背景介绍
首先,因为团队创立不久,所以目前项目管理、开发都是依托于云效平台,下面简称平台。所以我们的自动化部署便配置在 云效->流水线 上。
流程配置
部署流程配置如下:
简单来讲,三个步骤:
- 拉取代码:平台从代码仓库拉取选中的分支代码;
- 阿里云镜像构建:读取根目录的Dockerfile,根据file内容在平台构建docker镜像;
- Docker部署:目标服务器拉取镜像,执行一段脚本部署到本地。目标服务器需安装python(用于授权平台做免密登录)与docker(用于部署)
源配置
这个没啥好讲的,选择代码仓库和默认分支就行。不做赘述。
构建镜像配置
平台配置
关于${DATETIME}变量,可以在环境变量查看。
需要注意的是构建参数配置,最常用的是我们自动构建时需要区分环境。在平台配置,并在Dockerfile中使用。
Dockerfile
FROM node:14.15.0
# 获取构建参数env
ARG env
MAINTAINER JHH
# 创建工作目录
RUN mkdir -p /var/publish/nest
# 指定工作目录
WORKDIR /var/publish/nest
# 复制当前代码到/app工作目录
COPY . /var/publish/nest
# 配置系统变量,指定端口
ENV HOST 0.0.0.0
ENV PORT 8000
ENV ENV_NAME pm2:${env}
EXPOSE 8000
# npm 源,选用国内镜像源以提高下载速度
# RUN npm config set registry https://registry.npm.taobao.org/
RUN echo $ENV_NAME
# npm 安装依赖
RUN npm i pnpm -g
RUN pnpm i --cache .npm --quiet
# 注意,因为ts无法直接被识别,所以需要执行build命令
RUN npm run build
# 启动服务
CMD npm run ${ENV_NAME}
RUN和CMD的区别属于Docker语法问题,这里就不做赘述啦。可以看到以env=prod为例,最终执行的是npm run pm2:prod指令。
结合script脚本命令就比较清楚了:
"pm2:test": "TZ=Asia/Shanghai pm2-runtime tz-nest/ecosystem.config.js --env test",
"pm2:prod": "TZ=Asia/Shanghai pm2-runtime tz-nest/ecosystem.config.js --env prod",
运行脚本详解
TZ=Asia/Shanghai pm2-runtime tz-nest/ecosystem.config.js --env prod
TZ=Asia/Shanghai配置时区,解决构建完之后日志时间不对的问题。pm2-runtime tz-nest/ecosystem.config.js读取配置文件,并前台运行项目。或者后台运行的话,可以用pm2 start tz-nest/ecosystem.config.js命令。--env prod设置环境变量。
ecosystem.config.ts
export default {
apps: [
{
name: 'tz-nest',
// tz-nest是我build之后的文件夹,一般默认是dist。
script: './tz-nest/src/main.js',
instances: 2,
env_test: {
NODE_ENV: 'development'
},
// script中设置的--env prod,会自动读取env_prod属性。
env_prod: {
NODE_ENV: 'production'
},
exec_mode: 'cluster',
combine_logs: true
}
]
}
运行之后会起两个实例。
为什么用 pm2
一开始我并没有用pm2,会发现隔两天容器就会挂掉。虽然可以在启动docker容器的时候加上--restart=always,但是相关日志就没了。排查BUG的时候有些抓马~~
在上了pm2保驾护航之后,就再没挂掉过。
部署配置
添加主机
配置需要部署到主机,需要先添加主机组。关于添加主机,也不做赘述,按提示操作即可。不过需要主机安装python2.7。
如果不是2.7版本的可以重新安装2.7版本python,或者 改为访问本地shell,取消python版本校验。
部署脚本
export TAG_NAME=$(echo $CI_COMMIT_REF_NAME)
export BUILD_NUMBER=$(echo $BUILD_NUMBER)
echo "IMAGE $image"
# IID为镜像ID
IID=$(docker images | grep "$image_base" | awk '{print $3}')
echo "IID $IID"
# 判断是否存在这个仓库的镜像并删除
if [ -n "$IID" ]
then
echo "delete $image_base image"
docker rmi -f $IID
else
echo "no exist $image_base image, build docker"
fi
# 拉取镜像到本机
docker pull $image
# tz-nest是我为容器起的别名,拿到容器ID
CID=$(docker ps | grep "tz-nest" | awk '{print $1}')
echo "CID $CID"
# 如果容器ID存在,移除容器
if [ -n "$CID" ]
then
docker stop $CID
docker rm $CID
else
echo "no exist $IMAGE container"
fi
# 为新镜像创建一个容器,别名为tz-nest
docker run -d -p 8000:8000 --name tz-nest $image
上面用到了两个变量,$image 和 $image_base,也是在平台配置的。
这里的$image代表上游产生的镜像TAG,$image_base代表镜像仓库地址。
结尾
配置完成后,一键运行流水线,程序就能自动部署到服务器上啦。
最后感谢大家的阅读~有不正确的地方还望指正。[捂脸]