应用程序自动化部署
由于前后端程序都是使用 docker 部署,每次代码更新后都需要更新镜像并新建容器,手动在虚拟机上更新非常麻烦且浪费时间,所以需要一个能够让整个流程自动化的方法。 由于代码放在了 github,所以首先想到的就是github action,借助这个可以实现:每次 push 代码自动构建镜像并 push 到 Docker Hub。
第一步:创建工作流相关文件
在项目的根目录下新建 .github/workflows/xxx.yaml
文件
踩坑:
文件夹名称必须是.github/workflows
,且必须放在项目根目录下,否则 github 不会将其识别为 github action 的配置文件。
第二步:创建镜像构建配置文件 build-image.yml
name: build-image
run-name: ${{ github.actor }} is building latest image
on: [push]
jobs:
build_vue_app_image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Build vue_app image
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: |
docker build -t yanyue1215/vue_app .
docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
docker push yanyue1215/vue_app
- name: build_resume_service
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: |
cd Backend/ # 后端代码所在目录
docker build -t yanyue1215/resume_service .
docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
docker push yanyue1215/resume_service
这里的DOCKER_USERNAME
和DOCKER_PASSWORD
两个环境变量都是通过配置 github 该项目仓库的secrets
获取到的。设置路径为Setting-Security-secrets and variables-Actions-新建仓库 secret
踩坑 1:
这里的设置并不是新建一个 secret:Name 为你的 Docker Hub 账号,Value 为 Docker Hub 的 access token。而是新建两个 secret:
Name | Value |
---|---|
DOCKER_USERNAME | Docker Hub 账号名 |
DOCKER_PASSWORD | Docker Hub Access Token |
踩坑 2:
workflow 报错:invalid workflow file,you have error in your yaml syntax on line11 查看了下代码,发现第 11 行是设置环境变量:
env: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
错误的原因是 yaml 文件中使用缩进表示层级关系,相同层级的元素必须左对齐,而这里 env
下面的环境变量是 env 的下一级,但是却和它放在了同一行,所以报错了,修改为:
- name: Build vue_app image
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
如此之后,每当 push 代码到对应分支后,github 就会自动构建最新镜像并推送到 Docker Hub。但是这个时候你还是需要自己去虚拟机上拉取最新镜像并重建容器,依旧很麻烦,如果这个过程也能能自动化就最好了。
一开始考虑的是是否和 umc-ui 项目一样使用MakeFile
,但是最后还是希望能把更新和部署都集成到 github 的工作流程中。但是由于 github action 本身并不支持直接将应用程序部署到私人的虚拟机上,需要和其他工具相结合,
方案:在 yml 文件中编写一个部署脚本,通过 ssh 连接到虚拟机并执行对应的部署命令
第三步:创建 deploy 配置文件 deploy.yml
name: push-to-private-machine
run-name: ${{ github.actor }} is deploying
on:
workflow_run:
workflows: ['build-image'] #依赖的工作流程文件的名称
types:
- completed
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Add known hosts
run: | # 将虚拟机密钥添加到 GitHub Runner 的 "known_hosts" 文件中
mkdir -p ~/.ssh
ssh-keyscan 116.204.108.126 >> ~/.ssh/known_hosts
- name: remove old container
run: |
sshpass -p ${{ secrets.SSH_PASSWORD }} ssh root@116.204.108.126 "docker ps -a | grep yanyue1215/vue_app | awk '{print $1}' | xargs -r docker rm -f"
sshpass -p ${{ secrets.SSH_PASSWORD }} ssh root@116.204.108.126 "docker ps -a | grep yanyue1215/resume_service | awk '{print $1}' | xargs -r docker rm -f"
sshpass -p ${{ secrets.SSH_PASSWORD }} ssh root@116.204.108.126 "docker system prune -a"
- name: pull latest front_image
run: sshpass -p ${{ secrets.SSH_PASSWORD }} ssh root@116.204.108.126 "docker pull yanyue1215/vue_app:latest"
- name: pull latest backend_image
run: sshpass -p ${{ secrets.SSH_PASSWORD }} ssh root@116.204.108.126 "docker pull yanyue1215/resume_service:latest"
- name: Deploy to Virtual Machine
run: sshpass -p ${{ secrets.SSH_PASSWORD }} ssh root@116.204.108.126 "cd /root/resume_generator_front && /usr/local/git/bin/git pull origin master && docker-compose up -d"
踩坑 1:
ssh 连接在不使用 sshpass 密码连接的时候一直报错:permission denied(public key,password)
,目前还没有找到解决方法,采用 sshpass 连接作为替代方案
踩坑 2:
虚拟机上安装了 git,但是执行到git pull xx
却报错:
git: command not found error:Process completed with exit code 127
猜测原因是 Github Actions 的工作流程中无法找到 git 命令。解决方案是在虚拟机上执行which git
命令获取到 git 的实际位置,然后将其替换到命令中。
# 原来的命令:
git pull origin master
# 修改后的命令:
/usr/local/git/bin/git pull origin master
在此基础上,我希望两个 workflow 有明确的先后执行顺序,即部署应该在镜像构建已经完成的基础上执行,否则会造成流程混乱。
workflow 的顺序执行有两种方式:
- 将两个 workflow 放在同一个 yaml 文件中,通过依赖关系来定义他们之间的顺序
jobs:
build-images:
# xxxx
deploy:
needs: build-images
# xxx
- 将两个 workflow 放在不同的 yaml 文件中,通过
workflow_run
达到顺序执行的目的
# build-image.yml 不做任何修改
# deploy,.yml
name: deploy
on:
workflow_run:
workflows: ['build-image']
types:
- completed
此方案中,deploy.yml
会监听workflow_run
事件,并指定依赖的工作流程为build-image
。
踩坑:
第一个流程执行完之后并没有执行自动化部署。给出的原因是需要两个工作流程文件的名称正确且匹配
解决方案是将所有 yml 文件和他们里面的name
值保持一致
转载自:https://juejin.cn/post/7270798922150084664