Github Actions 初体验

345 阅读4分钟

离开蓝鲨之后,每当我部署项目的时候,无不怀念蓝鲨的 cicd,果然是由奢入俭难,有了 cicd 就懒得再手动部署了。

关于 cicd

之前在蓝鲨时,我们使用 Gitea 搭建的内网代码仓库,使用 Drone 搭配 Gitea 构建 cicd。如果按这套来折腾应该是比较熟悉的,但是首先我个人没那么多服务器,也没打算搭建一个私人的仓库,其次 Drone 的文档十分简陋,资料少得可怜,所以这套方案被我给 Pass 掉了。因为我个人平常都是用 GitHub 来作为远程仓库,所以我想有没有什么 cicd 工具可以和 GitHub 完美结合呢?官方的 GitHub Actions 可能就是最好的答案了。

Github Actions 是 GitHub 官方提供的一个 cicd 服务,它配置简单,只要在项目中添加 .github/workflows/*.yml 的配置文件,之后的每次 push GitHub 都会自动执行 cicd 服务。而且 GitHub Actions 文档丰富,既有官方文档,网络上还有很多别人的实践,有足够多的石头可以摸着过河。

Github Actions

我使用 GitHub Actions 的工作流程是这样的,某个分支的某个事件(如代码提交)触发了 cicd,cicd 可以通过 ssh 连接到个人的服务器上,将最新的代码拉到服务器本地,构建最新的 Docker 镜像,push 到镜像仓库中,再拉取最新的镜像部署到 Docker,完成。

所以首先要让 cicd 能访问服务器,在服务器生成公钥和私钥:

ssh-keygen -t rsa -C "limyel@outlook.com"

将公钥写入 authorized_keys:

cat id_rsa.pub >> authorized_keys

私钥改如何配置呢?如果直接将私钥写入 yml 配置文件,岂不是很危险?GitHub 早就想到了,一些私密的配置可以写入项目的 secrets,cicd 运行时会将指定位置的值改为 secrets 中配置的值。

上图是我用到的一些配置,从上到下分别是服务器私钥、Docker 密码、Docker 仓库镜像名称、Docker 用户名(这里我使用的是阿里云的 Docker 镜像仓库,所以对应的是阿里云仓库的用户名密码)、服务器公网IP、服务器登录用户名。

接下来在项目的根目录新建一个目录 .github/workflows,在目录中新建一个文件 deploy.yml,写入 cicd 的配置信息:

name: Deploy site files
​
on:
  push:
    branches:
      - master # 只在master上push触发部署
    paths-ignore: # 下列文件的变更不触发部署,可以自行添加
      - README.md
      - LICENSE
​
jobs:
  deploy:
    runs-on: ubuntu-latest # 使用ubuntu系统镜像运行自动化脚本
    timeout-minutes: 20
​
    steps: # 自动化步骤
      - uses: actions/checkout@v2 # 第一步,下载代码仓库
​
      - name: Deploy to Server # 第二步,rsync推文件
        uses: AEnterprise/rsync-deploy@v1.0 # 使用别人包装好的步骤镜像
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} # 引用配置,SSH私钥
          ARGS: -avz --delete --exclude='*.pyc' # rsync参数,排除.pyc文件
          SERVER_PORT: "22" # SSH端口
          FOLDER: ./ # 要推送的文件夹,路径相对于代码仓库的根目录
          SERVER_IP: ${{ secrets.SSH_HOST }} # 引用配置,服务器的host名(IP或者域名domain.com)
          USERNAME: ${{ secrets.SSH_USERNAME }} # 引用配置,服务器登录名
          SERVER_DESTINATION: /home/ubuntu/limyel-blog # 部署到目标文件夹
      - name: Restart server # 第三步,重启服务
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }} # 下面三个配置与上一步类似
          username: ${{ secrets.SSH_USERNAME }} # 登录服务器的用户名
          key: ${{ secrets.DEPLOY_KEY }} # 服务器配置的私钥
          # 脚本 推送新构建的镜像到仓库
          script: |
            cp /home/ubuntu/private/limyel-blog-private.py /home/ubuntu/limyel-blog/blog/config/private.py
            cd /home/ubuntu/limyel-blog
            docker build -t ${{ secrets.DOCKER_REPOSITORY }}:latest .
            docker login --username ${{ secrets.DOCKER_USERNAME }} --password ${{ secrets.DOCKER_PASSWORD }} registry.cn-hangzhou.aliyuncs.com
            docker push ${{ secrets.DOCKER_REPOSITORY }}:latest
  run:
    needs: [deploy]
    name: Pull And Run Docker
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }} # 下面三个配置与上一步类似
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.DEPLOY_KEY }}
          # 脚本 拉取最新的镜像并运行
          script: |
            docker stop limyel-blog
            docker rm limyel-blog
            docker image rm ${{ secrets.DOCKER_REPOSITORY }}:latest
            docker login --username ${{ secrets.DOCKER_USERNAME }} --password ${{ secrets.DOCKER_PASSWORD }} registry.cn-hangzhou.aliyuncs.com
            docker pull ${{ secrets.DOCKER_REPOSITORY }}:latest
            docker run -d -p 127.0.0.1:7382:7382 --name limyel-blog ${{ secrets.DOCKER_REPOSITORY }}:latest

不同的项目只需根据注释的内容修改配置文件即可,将代码 push 到 GitHub,GitHub Actions 就会自动执行脚本了。

下面是前端的配置:

name: Deploy site files

on:
  push:
    branches:
      - master # 只在master上push触发部署
    paths-ignore: # 下列文件的变更不触发部署,可以自行添加
      - README.md
      - LICENSE

jobs:
  deploy:
    runs-on: ubuntu-latest # 使用ubuntu系统镜像运行自动化脚本

    steps: # 自动化步骤
      - uses: actions/checkout@v2 # 第一步,下载代码仓库

      - name: Deploy to Server # 第二步,rsync推文件
        uses: AEnterprise/rsync-deploy@v1.0 # 使用别人包装好的步骤镜像
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} # 引用配置,SSH私钥
          ARGS: -avz --delete --exclude='*.pyc' # rsync参数,排除.pyc文件
          SERVER_PORT: "22" # SSH端口
          FOLDER: ./ # 要推送的文件夹,路径相对于代码仓库的根目录
          SERVER_IP: ${{ secrets.SSH_HOST }} # 引用配置,服务器的host名(IP或者域名domain.com)
          USERNAME: ${{ secrets.SSH_USERNAME }} # 引用配置,服务器登录名
          SERVER_DESTINATION: /home/ubuntu/blog/frontend # 部署到目标文件夹
      - name: Restart server # 第三步,重启服务
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }} # 下面三个配置与上一步类似
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.DEPLOY_KEY }}
          # 重启的脚本,根据自身情况做相应改动,一般要做的是migrate数据库以及重启服务器
          script: |
            cp /home/ubuntu/private/blog-frontend-private.js /home/ubuntu/blog/frontend/src/private.js
            cd /home/ubuntu/blog/frontend
            docker build -t ${{ secrets.DOCKER_REPOSITORY }} .
            docker login --username ${{ secrets.DOCKER_USERNAME }} --password ${{ secrets.DOCKER_PASSWORD }} registry.cn-hangzhou.aliyuncs.com
            docker push ${{ secrets.DOCKER_REPOSITORY }}:latest

  run:
    needs: [ deploy ]
    name: Pull And Run Docker
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }} # 下面三个配置与上一步类似
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.DEPLOY_KEY }}
          script: |
            docker stop blog-frontend
            docker container rm blog-frontend
            docker image rm ${{ secrets.DOCKER_REPOSITORY }}:latest -f | true
            docker login --username ${{ secrets.DOCKER_USERNAME }} --password ${{ secrets.DOCKER_PASSWORD }} registry.cn-hangzhou.aliyuncs.com
            docker pull ${{ secrets.DOCKER_REPOSITORY }}:latest
            docker run -d -p 127.0.0.1:7382:80 --name blog-frontend ${{ secrets.DOCKER_REPOSITORY }}

\