github action CI/CD 实战

ci/cd

什么是ci

持续集成,需要用户主动提交代码,触发代码集成、自动化构建、自动化测试流程

什么是cd

持续交付、持续部署,不需要用户干预,自动将ci准备好的构建文件发布到生产环境

ci/cd的工具介绍

一、持续集成(CI)工具

Jenkins‌

特点‌:开源、插件生态丰富,支持与 Git、Maven 等工具深度集成,可通过配置流水线实现自动化构建、测试和部署‌。 适用场景‌:企业级复杂项目,需高度定制化的 CI/CD 流程。

GitLab CI‌

特点‌:与 GitLab 代码仓库无缝集成,通过 .gitlab-ci.yml 文件定义流水线,支持容器化构建和并行任务‌。 适用场景‌:团队协作开发,尤其是 GitLab 托管项目。

Travis CI‌

特点‌:云端服务,深度支持 GitHub 项目,配置简单,适用于开源项目和小型团队‌。 限制‌:免费版对私有仓库的支持有限。

Apache BuildStream‌

特点‌:基于 YAML 文件定义构建流程,通过哈希缓存机制加速构建,适合管理复杂软件组件的依赖关系‌。 二、持续交付/部署(CD)工具

Ansible‌

特点‌:无代理架构,通过 YAML 编写 Playbook 实现配置管理和自动化部署,支持多云环境‌。 优势‌:轻量级且易于扩展,适合中小规模基础设施。

AWS CodeDeploy‌

特点‌:与 AWS 云服务深度集成,支持蓝绿部署和滚动更新,可自动监控部署状态‌。 适用场景‌:基于 AWS 的云原生应用部署。

Kubernetes‌

特点‌:容器编排工具,支持声明式部署和自动扩缩容,与 Helm 等工具结合可实现应用版本管理‌。 核心价值‌:提升容器化应用的部署效率和稳定性。

github action介绍

github actions实验室

Github在2019年底开放了内置的CI/CD工具GithubActions.这样使用Github托管的代码终于有了不借助外部服务自动化测试打包部署的能力. 同时由于后发优势,GithubActions几乎是目前最易用的CI/CD工具. GithubActions类似于传统的CI/CD工具,都是使用代码配置脚本,执行器执行脚本,页面管理执行过程的结构.

  • 在代码中配置脚本放在根目录的.github/workflow文件夹下,使用yaml格式描述配置.
  • Github默认给每个用户配置3个的执行器,我们也可以自己创建self-host执行器
  • 每个代码仓库的顶部标签页都有专门的actions按钮,进去就是当前仓库的执行过程管理页面.

image-20220113175906949

  • 代码提交到github仓库,
  • 触发push事件
  • 工作流开始工作,顺序执行job,job在runner上执行
  • job中step也是顺序执行的,step可以是actions或者command
  • 工作流完成,会生成一份报告

为什么我选择github action

无需额外的服务器资源,个人使用足够了 500mb的存储,每月2000分钟的免费使用时间,同时可以运行20个工作流,对于个人使用足够了 20250307170427 20250307170452

利用github Action 实现个人服务器的CI/CD

因为考虑到以后会有多个项目都需要打包部署,这里考虑使用配置文件来集中管理需要打包编译的项目。

{
  "images": [
    {
      "name": "playwright-go",
      "tag": "1.24",
      "dockerfile": "playwright/go/Dockerfile"
    },
    {
      "name": "maven3.5-jdk8",
      "tag": "latest",
      "dockerfile": "maven/Dockerfile"
    }
  ]
} 

这里的images是一个数组,每个元素都是一个对象,对象中包含了name、tag、dockerfile三个属性,分别表示镜像名称、镜像版本、dockerfile路径。

然后解决编译问题,我一般直接采用docker部署的方式,这样可以避免环境问题,同时也可以减少编译时间,因为docker镜像已经包含了编译环境,这里涉及到一个新知识strategy.matrix

矩阵策略允许用户在一个工作流中定义多个变量组合,这样可以同时运行多个作业,每个作业使用不同的配置。一般可以用做同一套代码在不同环境下的测试,比如linux、windows、macOS等。

这里我们用作准备不同的镜像文件在相同的环境下进行编译

 needs: prepare
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
        
      - name: Read image config
        id: read_config
        run: echo "config=$(cat .github/docker-images.json | jq -c)" >> $GITHUB_OUTPUT

读取好变量进行编译打包操作

  - name: Build images
        run: |
          # 检查Dockerfile是否存在
          if [ ! -f "${{ matrix.dockerfile }}" ]; then
            echo "错误: Dockerfile不存在: ${{ matrix.dockerfile }}"
            exit 1
          fi
          
          echo "开始构建镜像: ${{ matrix.name }}:${{ matrix.tag }}"
          # 构建Docker镜像
          docker build -t ${{ matrix.name }}:${{ matrix.tag }} -f ${{ matrix.dockerfile }} .
          
          # 检查构建结果
          if [ $? -ne 0 ]; then
            echo "错误: 镜像构建失败"
            exit 1
          fi
          
          echo "镜像构建成功,检查镜像是否存在"
          # 验证镜像是否存在
          docker images | grep "${{ matrix.name }}" | grep "${{ matrix.tag }}"
          
          echo "开始保存镜像为tar文件"
          # 将镜像保存为tar文件
          docker save ${{ matrix.name }}:${{ matrix.tag }} > /tmp/${{ matrix.name }}_${{ matrix.tag }}.tar
          
          # 检查保存结果
          if [ $? -ne 0 ]; then
            echo "错误: 保存镜像失败"
            exit 1
          fi
          
          echo "检查tar文件是否存在"
          # 验证tar文件是否存在
          ls -la /tmp/${{ matrix.name }}_${{ matrix.tag }}.tar
          
          echo "镜像构建和保存完成"

这里将打包好的文件存入/tmp目录下,再使用actions/upload-artifact@v4action工具,将打包好的tar包上传到github服务器上,这样就可以在后续的步骤中使用这个tar包进行部署操作了。工具的逻辑已经实现好了,只需要配置文件名称,文件地址即可

    - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.name }}_${{ matrix.tag }}
          path: /tmp/${{ matrix.name }}_${{ matrix.tag }}.tar
          overwrite: true
          if-no-files-found: error  # 如果找不到文件则报错
          retention-days: 1 

然后到了CD步骤,将打包好的tar包下载下来,无论是进行镜像推送还是服务器部署,都可以使用这个tar包进行操作,这样就实现了一个完整的CI/CD流程。

 push-to-dockerhub:
    needs: [prepare, build]
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
    steps:
      - name: Download image artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ matrix.name }}_${{ matrix.tag }}
          path: /tmp
          
      - name: Load Docker image
        run: |
          ls -la /tmp
          docker load --input /tmp/${{ matrix.name }}_${{ matrix.tag }}.tar
          docker tag ${{ matrix.name }}:${{ matrix.tag }} ${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.name }}:${{ matrix.tag }}

      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          registry: docker.io
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: Push to DockerHub
        run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.name }}:${{ matrix.tag }}

这里因为只是基础镜像的构建,所以我没有加入部署操作,如果需要远程部署的话可以使用工具appleboy/ssh-action@master,这个工具可以实现远程服务器的部署操作,具体的使用方法如下

      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USERNAME }}
          password: ${{ secrets.SERVER_PASSWORD }}
          script: |
            docker login -u ${{ secrets.ALIYUN_USERNAME }} -p ${{ secrets.ALIYUN_PASSWORD }} ${{ env.REGISTRY }}
            docker pull ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}:latest
            docker stop {{env.CONTIANER_NAME}} || true
            docker rm {{env.CONTIANER_NAME}} || true
            docker run -d \
              --name {{env.CONTIANER_NAME}} \
              --restart always \
              -p 8080:8080 \
              ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}:latest

这样就实现了一个完整的CI/CD流程,这里只是一个简单的例子,如果需要自己使用并且改造的话,可以参考我的github仓库