文章目录

一、前期准备

自从上手了docker之后,发现docker会使得程序的启动、运行、维护、迁移都变得十分容易,因此希望把很多常用的东西都容器化。Yapi是目前一直在使用的API管理工具,虽然发现了不少不方便使用的地方,但能满足基本需求,尤其胜在免费开源……有关Yapi的介绍参考之前的文章《API集成管理平台YAPI的搭建和使用》

首先去Yapi github看官方推荐的第三方docker镜像(目前没有出官方的docker镜像)是怎么做的,总结如下:

1、Ryan-Miao / docker-yapi  

(1)使用了node:11-alpine作为基础镜像

(2)Dockerfile使用了多阶段构建的方式

(3)使用了两个基础镜像的源:

https://mirrors.aliyun.com/alpine/v3.6/main/
https://mirrors.aliyun.com/alpine/v3.6/community/

整体涵盖了网络创建、mongodb初始化等操作,用一个start.sh作为入口,通过和用户命令行交互来执行对应的脚本。

【优点】

脚本比较完善,用户操作少

【缺点】

  • 如果自动重启的话,这些交互都必须要等待用户输入;
  • 没有使用docker-compose这类编排工具

2、jinfeijie / yapi

(1)使用了基础镜像node:10.18.1-jessie

(2)支持任意版本

【优点】

支持任意版本安装

【缺点】

docker-compose版本为2,且将mongo强行绑定在同一个docker-compose文件进行编排

3、fjc0k / docker-YApi

(1)Dockerfile使用了多阶段构建的方式

(2)编译阶段基础镜像是自己做的,运行阶段还是用node:alpine

(3)几乎完整支持了所有yapi配置,且增加了对密码修改的支持,这个解决了我当时对Yapi很大的抱怨:初始化密码强行设置为ymfe.org,不支持自定义

(4)yapi版本较新,支持了很多新配置,比如mongodb集群

【优点】

功能全面,版本最新。

【缺点】

使用node.js去写的脚本,对于我这种前端小白,看懂容易,修改起来各种坑。

二、研究需求

第1、2个,基本就是用docker做了个套子。第3个非常棒,对比我的需求:

(1)支持插件配置,第3个也声明支持,但是未测试过

(2)支持密码初始化,第3个支持。

(3)mongo独立开,第3个也将mongo写在了同一个docker-compose里面

(4)支持数据备份和还原,第3个不支持。

(5)支持LDAP,第3个支持。

(6)体积小,第3个支持。

(7)支持升级,第3个支持。

基本上第3个支持了所有的需求,最主要想自己做的原因是,我想能改代码,nodejs对我来说太痛苦了。于是基本以第3个为模板,写纯shell脚本。

三、docker-yapi路上的坑

基本上用到了这些知识:

  • hub.docker.com找基础镜像
  • alpine镜像采用了busybox为核心组件,没有bash,用apk进行更新,类似apt

  • npm源、install相关命令

  • mongodb基本语法和常识

  • node.js基本语法、mongoose操作mongodb

  • Dockerfile多阶段构建、换行、多行文本

  • docker-compose v3基本语法

  • shell脚本语法

  • hub.docker.com自动关联github自动编译、制作和发布镜像

1、mongodb更换版本后起不来

我在创建mongodb更换版本之后起不来,后来想起我把volume映射到宿主机了,把宿主机文件删掉即可,要仔细点啊。

volumes:
      - /opt/mongo:/data/db

2、dockerfile多阶段构建,如何把编译阶段文件拷贝过来

COPY --from=build-stage /opt/yapi /opt/yapi

3、docker-compose采用volume映射之后,无法找到启动文件

最开始我映射的是:

volumes:
      - "/opt/yapi:/opt/yapi"

检查了语法、容器内是否有这个文件等等,都是正常的,但映射成volume就是空文件夹。这里不该映射含有初始化需要的文件所在文件夹,而是那些运行时会产生的新数据所在文件夹,例如data、log等。

volumes:
      - "/opt/yapi/vendors/log:/opt/yapi/vendors/log"

其实就是我理解成,容器创建之后,会自动把镜像中的文件映射到宿主机上;事实上应该是宿主机上存在文件夹,被挂载到容器里面去,然后容器产生的数据会在该文件夹生成,看起来就像是文件互通一样。

4、shell函数传递识别空格引号等内容异常

要保证:调用者和接收者的参数都用引号引起来

// 调用者引号引起来
replaceFileContent "../config.json" "$1" "$2" "$3"
// 接收者引号接收
file="$1"
str="$2"
default_val="$3"

5、sed识别一些特殊字符异常

特殊字符需要转义。

// 引号的转义
TRANS_YAPI_PLUGINS=${YAPI_PLUGINS//\"/\\\"}
// 小数点的转义
TRANS_YAPI_PLUGINS=${TRANS_YAPI_PLUGINS//\./\\\.}
// 斜杠的转义
TRANS_YAPI_PLUGINS=${TRANS_YAPI_PLUGINS//\//\\\/}
// 左右中括号的转义,示例右中括号 
TRANS_YAPI_PLUGINS=${TRANS_YAPI_PLUGINS//\]/\\\]}
// 替换的时候用引号引起来
sed -i -e 's/'"${str}"'/'"${val}"'/' ${file}

6、shell设置默认值

用冒号和横杠,且在${}符号内。

val=${new_val:-${default_val}}

7、在指定行插入多行文本

用斜杠划分多行文本,利于阅读。

sed -i "26i\\\
  userInst.findByEmail(yapi.WEBCONFIG.adminAccount).exec((err,dupAdmin)=>{\n\
    if (dupAdmin) {\n\
      console.log(\`find duplicated adminAccount, username=\${dupAdmin.username}, will be delete\`);\n\
      userInst.del(dupAdmin._id).exec();\n\
    }\n\
  });\n" \
"./server/install.js"

8、nodejs的mongoose查询返回一个Query属性

本来是看了一下yapi其他源码的写法,包括server/controllers/user.js、server/models/user.js、utils/db.js、install.js,看到其他的这么用的:

user = await userInst.save(data);

我也模仿着这么用,提示await不能用于同步方法,去掉了await,结果返回了一个Query对象。

查了好久,才查到:

findByEmail(email) {
    return this.model.findOne({ email: email });
  }

这种会返回一个Query对象,而要获取结果,还需要这样执行:

query.exec((err,res)=>{});

于是尝试用echo打印了res,才终于发现成功了。

这个如果没有nodejs的常识,就要吃这种亏……

四、一些经验

简单介绍一下yapi的结构,主要逻辑都在server文件夹里:

  • controller就是控制层,包含了业务逻辑的方法,比如login
  • middleware是一个mockServer.js组件
  • models是数据库层,mongodb的数据结构,包含了一些基本增删改查的方法
  • utils是基本组件层,比如数据库连接、ldap等等基本功能
  • app.js就是运行入口
  • install.js就是安装入口

连接数据库是这样的:

const dbModule = require('./utils/db.js');
yapi.connect = dbModule.connect();
yapi.connect
    .then(function() {……});

操作model是这样的,以新建user为例:

const userModel = require('./models/user.js');
let result = userInst.save({
    username: yapi.WEBCONFIG.adminAccount.substr(0, yapi.WEBCONFIG.adminAccount.indexOf('@')),
    email: yapi.WEBCONFIG.adminAccount,
    password: yapi.commons.generatePassword('ymfe.org', passsalt),
    passsalt: passsalt,
    role: 'admin',
    add_time: yapi.commons.time(),
    up_time: yapi.commons.time()
  });
result.then(
        function() {
          fs.ensureFileSync(yapi.path.join(yapi.WEBROOT_RUNTIME, 'init.lock'));
          console.log(
            `初始化管理员账号成功,账号名:"${yapi.WEBCONFIG.adminAccount}",密码:"ymfe.org"`
          ); // eslint-disable-line
          process.exit(0);
        },
        function(err) {
          throw new Error(`初始化管理员账号 "${yapi.WEBCONFIG.adminAccount}" 失败, ${err.message}`); // eslint-disable-line
        }
      );

config参数可以这样引入,其实就是yapi.js读取了config文件。

const yapi = require('./yapi.js');
yapi.WEBCONFIG.xxxxx

在运行之后就会产生init.lock文件,避免再次运行时重复安装。

五、最终成果

github:https://github.com/BEWINDOWEB/docker-yapi

dockerhub:bewindoweb/yapi:1.0.0

  • ✔ yapi 1.8.5
  • ✔ 支持mongodb集群
  • ✔ 支持初始化密码
  • ✔ 支持使用docker的方式配置yapi大部分参数
  • ✖ 不支持yapi更新
  • ✖ 不支持yapi插件
  • ✖ 不支持yapi数据备份

yapi插件尝试了一下会报错,在下个版本解决吧。

以前的yapi数据备份要求用mongodump,而这里只有mongoose,学习估计要花费一些时间,下个版本加入。


转载请注明出处http://www.bewindoweb.com/279.html | 三颗豆子
分享许可方式知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
重大发现:转载注明原文网址的同学刚买了彩票就中奖,刚写完代码就跑通,刚转身就遇到了真爱。
你可能还会喜欢
具体问题具体杠