yapi 介绍
YApi 是高效、易用、功能强大的 api 管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护
API,YApi 还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写入工具以及简单的点击操作就可以实现接口的管理。
准备Dockerfile
这里更新了 version 版本 为 1.9.3,原先的版本都存在 高级 mock 脚本侵入危险,官方新提交的v1.9.3版本新增了更安全的 safeify
沙箱来保护脚本执行安全。
FROM node:12-alpine as builder
WORKDIR /yapi
RUN apk add --no-cache wget python make
ENV VERSION=1.9.3
RUN wget https://github.com/YMFE/yapi/archive/v${VERSION}.zip
RUN unzip v${VERSION}.zip && mv yapi-${VERSION} vendors
RUN cd /yapi/vendors && cp config_example.json ../config.json && npm install --production --registry https://registry.npm.taobao.org
FROM node:12-alpine
ENV TZ="Asia/Shanghai"
WORKDIR /yapi/vendors
COPY wait-for-it.sh /
#后面需要 bash 解释器去执行脚本,apline镜像默认使用 /bin/sh,所以需要在这里安装一下
RUN chmod +x /wait-for-it.sh && apk add --no-cache bash
COPY --from=builder /yapi/vendors /yapi/vendors
EXPOSE 3000
准备 config.json
{
"port": "3000",
"adminAccount": "xxx",
"timeout":120000,
"closeRegister":true,
"db": {
"servername": "mongo",
"DATABASE": "yapi",
"port": 27017,
"user": "yapi",
"pass": "admin.yapi",
"authSource": "admin"
},
"mail": {
"enable": true,
"host": "smtp.exmail.qq.com",
"port": 465,
"from": "xxx",
"auth": {
"user": "xxx",
"pass": "xxx"
}
},
"ldapLogin": {
"enable": true,
"server": "ldap://l-ldapt1.com",
"baseDn": "CN=Admin,CN=Users,DC=test,DC=com",
"bindPassword": "password123",
"searchDn": "OU=UserContainer,DC=test,DC=com",
"searchStandard": "mail",
"emailPostfix": "@163.com",
"emailKey": "mail",
"usernameKey": "name"
}
}
借鉴域官方文档,可以不用配置mail 和 ldap 功能
准备依赖可用性检查脚本
这里可是有着一个大坑,docker-compose 里面有依赖关系的话,大家肯定都会使用官方文档所说的
depends_on
来处理吧。
没错,反正我是用这个来处理的,我第一次安装部署可能是运气比较好吧,部署yapi没出什么问题,这几天要迁移yapi 服务。
迁移嘛,很简单的拉。不就是备份mongodb 数据库数据,目标服务器启动yapi服务和mongodb服务后导入数据,不就完事了吗。
也没错,就这几步操作,中途因为 depends_on 坑了我大半天时间,哎,都怪自己太菜了。
也不多哔哔了,depends_on
这个官方介绍的是解决容器的依赖、启动先后的问题
,没错它是解决了这个启动先后的问题,但是还有一点,注意:自身服务不会等待 依赖服务「完全启动」之后才启动。
这就导致了我后面启动yapi,一遍报错mongodb Authentication failed
,一遍 初始化成功后,在起来又提示 mongodb Authentication failed 问题,我真的是脑壳疼啊。
后面发现是yapi没等mongodb 完全启动就启动去连接mongodb,连接不上还提示认证有问题,我他喵醉了,看这里的提示我一直以为我配置文件中的账号密码和mongodb设置的不一样呢。 刚开始想的解决方是,在执行初始化和启动前设置个 sleep n 秒,结构发现不太好,你咋知道mongodb预计多久完全启动呢.继续上网查询相关内容,最后找到这个wait-for-it
wait-for-it.sh 是一个纯 bash 脚本,它将等待主机和 TCP 端口的可用性。 它对于同步相互依赖的服务(例如链接的docker 容器)的启动很有用。 由于它是一个纯 bash 脚本,因此它没有任何外部依赖项。 这家伙,很有用。它会检查目标端口是否可用,不可用的话将会为目标端口 sleep n 秒,这里的时间由 wait-for-it.sh脚本判断,我使用的感觉挺好。
github地址: https://github.com/vishnubob/wait-for-it
wait-for-it.sh
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
WAITFORIT_cmdname=${0##*/}
echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
else
echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
fi
WAITFORIT_start_ts=$(date +%s)
while :
do
if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
nc -z $WAITFORIT_HOST $WAITFORIT_PORT
WAITFORIT_result=$?
else
(echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
WAITFORIT_result=$?
fi
if [[ $WAITFORIT_result -eq 0 ]]; then
WAITFORIT_end_ts=$(date +%s)
echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
break
fi
sleep 1
done
return $WAITFORIT_result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $WAITFORIT_QUIET -eq 1 ]]; then
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
else
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
fi
WAITFORIT_PID=$!
trap "kill -INT -$WAITFORIT_PID" INT
wait $WAITFORIT_PID
WAITFORIT_RESULT=$?
if [[ $WAITFORIT_RESULT -ne 0 ]]; then
echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
fi
return $WAITFORIT_RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
WAITFORIT_hostport=(${1//:/ })
WAITFORIT_HOST=${WAITFORIT_hostport[0]}
WAITFORIT_PORT=${WAITFORIT_hostport[1]}
shift 1
;;
--child)
WAITFORIT_CHILD=1
shift 1
;;
-q | --quiet)
WAITFORIT_QUIET=1
shift 1
;;
-s | --strict)
WAITFORIT_STRICT=1
shift 1
;;
-h)
WAITFORIT_HOST="$2"
if [[ $WAITFORIT_HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
WAITFORIT_HOST="${1#*=}"
shift 1
;;
-p)
WAITFORIT_PORT="$2"
if [[ $WAITFORIT_PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
WAITFORIT_PORT="${1#*=}"
shift 1
;;
-t)
WAITFORIT_TIMEOUT="$2"
if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
WAITFORIT_TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
WAITFORIT_CLI=("$@")
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
# Check to see if timeout is from busybox?
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
WAITFORIT_BUSYTIMEFLAG=""
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
WAITFORIT_ISBUSY=1
# Check if busybox timeout uses -t flag
# (recent Alpine versions don't support -t anymore)
if timeout &>/dev/stdout | grep -q -e '-t '; then
WAITFORIT_BUSYTIMEFLAG="-t"
fi
else
WAITFORIT_ISBUSY=0
fi
if [[ $WAITFORIT_CHILD -gt 0 ]]; then
wait_for
WAITFORIT_RESULT=$?
exit $WAITFORIT_RESULT
else
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
wait_for_wrapper
WAITFORIT_RESULT=$?
else
wait_for
WAITFORIT_RESULT=$?
fi
fi
if [[ $WAITFORIT_CLI != "" ]]; then
if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
exit $WAITFORIT_RESULT
fi
exec "${WAITFORIT_CLI[@]}"
else
exit $WAITFORIT_RESULT
fi
安装docker-compose
compose官方文档:https://docs.docker.com/compose/install/#install-compose
curl -L https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
编写docker-compose.yaml
准备工作完成,编写docker-compose.yaml
version: '2.1'
services:
mongodb:
image: mongo:latest
container_name: mongo
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: yapi
MONGO_INITDB_ROOT_PASSWORD: admin.yapi
MONGO_INITDB_DATABASE: yapi
volumes:
- "./mongo/data:/data/db"
networks:
my-yapi:
ipv4_address: 192.168.10.2
yapi:
build:
context: ./
dockerfile: Dockerfile
image: yapi
container_name: yapi
#第一次初始化
command: "bash /wait-for-it.sh mongodb:27017 -- /usr/local/bin/npm run install-server"
#之后使用下面的命令
# command: "bash /wait-for-it.sh mongodb:27017 -- node /yapi/vendors/server/app.js"
ports:
- 3000:3000
depends_on:
- mongodb
volumes:
- "./config.json:/yapi/config.json"
networks:
my-yapi:
ipv4_address: 192.168.10.3
networks:
my-yapi:
driver: bridge
ipam:
config:
- subnet: 192.168.10.0/24
启动示范
#启动时会自动编译镜像,没有加 -d 后台运行是因为 yapi 需要初始化
docker-compose up
#初始化完毕后,注释掉yaml里的 run install-server 这句,取消下面的command 注释
#yapi的初始化只需要执行一遍,它就是将需要的结构数据写入mongodb,mongodb数据持久化后,后面都不在需要执行初始化命令
docker-compose up -d #这次执行会更新为启动命令,不再是初始化命令
参考文章: https://www.jianshu.com/p/a97d2efb23c5