GitLab CI 示例:构建 & 部署 Web 前端项目(部署到 Swarm)

问题

Web 前端的应用需要在 node 环境下安装依赖和构建,但是发布一般又是用 nginx 镜像,我们既不想在 node 容器里安装 nginx,也不想在 nginx 容器里安装 node;

TL;DR

所以,我们先在一个 node 容器中 build 应用,利用 GitLab CI Cache 机制,将构建后的目标文件(通常是 dist 文件夹)缓存,以便在发布阶段 (release stage) 可以使用。

这里我所谓的 release 是指发布 docker image ,所以需要一个 Dockerfile,并且是以 nginx image 为基础镜像构建的。

我这里是部署到 swarm 集群上,所以会首先把 docker-stack.yml 文件 scp 到目标服务器上。

例说

1
2
3
4
# Dockerfile
FROM bitnami/nginx:latest
COPY ./dist /app
EXPOSE 8080
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# .gitlab-ci.yml
image: node:10
stages:
- build
- release
- deploy

cache:
paths:
- node_modules/
- dist/

before_script:
- if [ $CI_COMMIT_REF_NAME = master ];then export SERVER_HOST=$DEV_SERVER_HOST;fi
- if [ $CI_COMMIT_REF_NAME = prod ];then export SERVER_HOST=$PROD_SERVER_HOST;fi
- if [ $CI_COMMIT_REF_NAME = master ];then export BUILD_VERSION=dev;fi
- if [ $CI_COMMIT_REF_NAME = prod ];then export BUILD_VERSION=prod;fi

variables:
CONTAINER_IMAGE: $REGISTRY_ADDR/$REGISTRY_NAMESPACE/$CI_PROJECT_NAME
CONTAINER_IMAGE_VERSION: "${CI_COMMIT_REF_NAME}_latest"
CONTAINER_IMAGE_VERSION_HASH: "${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHA}"
DOCKER_DRIVER: overlay2

build:
stage: build
script:
- npm install
- npm run build:$BUILD_VERSION

release:
image: docker:dind
stage: release
script:
- ls -lh dist
- docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY_ADDR
- docker pull $CONTAINER_IMAGE:$CONTAINER_IMAGE_VERSION || true
- docker build --cache-from $CONTAINER_IMAGE:$CONTAINER_IMAGE_VERSION --tag $CONTAINER_IMAGE:$CONTAINER_IMAGE_VERSION_HASH --tag $CONTAINER_IMAGE:$CONTAINER_IMAGE_VERSION .
- docker push $CONTAINER_IMAGE:$CONTAINER_IMAGE_VERSION
- docker push $CONTAINER_IMAGE:$CONTAINER_IMAGE_VERSION_HASH

deploy:
image: laogen/openssh-client:latest
stage: deploy
script:
- eval $(ssh-agent -s)
- echo "$SERVER_SSH_PRIV_KEY" > deploy.key
- mkdir -p ~/.ssh
- chmod 0600 deploy.key
- ssh-add deploy.key
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- scp docker-stack.${BUILD_VERSION}.yml root@$SERVER_HOST:/srv/web/docker-stack.yml
- ssh root@$SERVER_HOST "docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY_ADDR"
- ssh root@$SERVER_HOST "cd /srv/web && docker stack deploy -c docker-stack.yml ProjectName --with-registry-auth"

小结

这个例子是从实际工作项目中略删减得来的,更完整综合一些。

这个 Pipeline 一共三个阶段:

  • build # 构建前端项目
  • release # 发布 Docker 镜像
  • deploy # 部署到 Swarm 上

在 build 阶段完成后,将 build 的结果 dist 缓存起来,这样到 realse 阶段时,就可以把它们打包到镜像里了,然后 push 到镜像仓库;

deploy 阶段就是 ssh 连接远程执行命令部署,与之前不同的是,这次是部署在 swarm 上,我们先将 docker-stack.yml 上传到目标服务器,然后远程执行部署指令,这块没什么可说的。