(九)学习、实践、理解 CI/CD 与 DevOps:持续发布 CD,从容器镜像到生产环境

系列文章:

(一)学习、实践、理解 CI/CD 与 DevOps:起步

(二)学习、实践、理解 CI/CD 与 DevOps:容器化

(三)学习、实践、理解 CI/CD 与 DevOps:声明式 API,Docker Compose 容器编排

(四)学习、实践、理解 CI/CD 与 DevOps:流水线工具 Pipeline

(五)学习、实践、理解 CI/CD 与 DevOps:GitHub Actions 快速介绍

(六)学习、实践、理解 CI/CD 与 DevOps:GitHub Actions 工作流实践

(七)学习、实践、理解 CI/CD 与 DevOps:CI 与 CD分离,权责边界

(八)学习、实践、理解 CI/CD 与 DevOps:持续集成 CI,从源代码到容器镜像

CI Pipeline 从源代码到镜像仓库,CD Pipeline 从镜像仓库到生产环境,流水线在项目建设初期定义,可重复执行。

配置即代码,Docker Compose 生命式部署

声明式 API 是云原生技术的核心理念之一,它将系统的期望状态描述为一个配置文件,而不是一系列的命令式操作。

实施系统发布的过程是标准化的、可重复的,因此可编排为 CD Pipeline 以实现自动化执行。在编排 CD Pipeline 流水线时有两种策略:

  • 命令式 shell 脚本(半自动化):将传统的运维操作使用 shell 脚本实现,一次性顺序地执行多个 shell 命令
  • 声明式配置文件:将发布过程定义为一个配置文件,自动化工具根据配置文件执行发布过程

中小 IT 系统的发布场景较简单,通常是单机发布,无需引入复杂的分布式集群调度(Kubernetes),因此 Docker Compose 是一个非常好的选择。

对比使用 shell 脚本和 Docker Compose 的发布流水线,B 容器依赖 A 容器,C 容器依赖 B 容器。

shell 脚本发布

shell 复制代码
#!/bin/bash

# 从镜像仓库拉取最新镜像
docker pull A-image:latest
docker pull B-image:latest
docker pull C-image:latest

# 停止并删除旧容器
docker stop A-container
docker rm A-container
docker stop B-container
docker rm B-container
docker stop C-container
docker rm C-container

# 依次容器
docker run -d --name A-container -p 80:80 A-image:latest

sleep 5 # 等待 A容器启动
docker run -d --name B-container -p 8080:8080 B-image:latest

sleep 5 # 等待 B容器启动
docker run -d --name C-container -p 8081:8081 C-image:latest

Docker Compose 发布

docker-compose.yaml作为配置文件随代码提交到版本控制仓库,与源代码并行管理。

yaml 复制代码
services:
  A-container:
    image: A-image:latest
    ports:
      - 80:80
  B-container:
    image: B-image:latest
    ports:
      - 8080:8080
    depends_on:
      - A-container
  C-container:
    image: C-image:latest
    ports:
      - 8081:8081
    depends_on:
      - B-container

流水线配置文件中,命令式语句只包含必要的业务无关指令,执行发布:

shell 复制代码
docker compose down # 容器卸载
docker-compose up -d # 容器启动
# docker-compose up -d --build # 容器启动并构建

对比两种发布方式的差异:

  • shell 脚本发布:模拟手动执行多个 shell 命令,需要自行管理容器依赖关系
  • Docker Compose 发布:配置文件定义发布过程,配置与执行分离,声明容器依赖关系即可

项目迭代只需要更新 docker-compose.yaml 文件,无需关心流水线编排文件。还有一个关键点,shell 脚本多个指令顺序执行,一旦某条指令执行失败,排查问题、修复问题、重新执行是一场运维灾难。相反,Docker Compose 将部署动作原子化,要么全部执行成功,要么全部执行失败。

总的来说,Docker Compose 这种声明式 API 的优势:配置文件定义发布过程,配置与执行分离;声明容器依赖关系即可,无需手动管理容器依赖关系。

更多关于声明式 API 的理解,将在后续文章中详细探讨。


在服务部署的实践中,我经历的三个阶段:

  • 阶段一:使用 VMware 虚拟机来直接安装服务,如 MySQL 和 Nginx,这简直是一个灾难。VMware 虚拟机是一个重量级程序,耗费宿主机资源,启停是分钟级别,这只是缺点之一。直接在操作系统中安装 MySQL 或 Nginx 程序,面临版本管理与安装卸载难的问题,频繁执行各种apt installapt uninstall指令,需处理和修复各种失败问题。

  • 阶段二:开始 Docker 实现容器化部署。部署 Nginx、MySQL 简单到只需要一行指令,轻松实现启停、版本控制和卸载,极大地简化了部署中所需要的依赖工具。

  • 阶段三:基于 Docker Compose 声明式编排容器。接触 SpringCloud 微服务后,面临容器编排灾难,因为要同时启动多个微服务,且微服务之间具有复杂依赖关系,需要按顺序在终端中依次启动或停止。另外,网络端口、目录挂载也需要特别维护。Docker Compose 使用 YAML 配置来声明式描述容器间关系,不再需要维护文档,启动和卸载简化到一行指令,Docker Compose 引擎实现容器启动管理。配置文件可以随源代码一样提交进行版本管理,具有良好的可迁移性。

在 GitHub 的代码仓库中,优秀的项目均支持了 Docker Compose,演示 Demo 变得非常便捷。不过,Docker Compose 是在单机上执行的,如果需要实现分布式集群部署,则需要引入 Kubernetes 技术。

CD Pipeline:在 GitHub Actions中实践

在开发环境,我们期望在每次代码提交后,自动触发 CI 与 CD 流水线,将最新的代码部署到开发环境。

在生产环境,CI 与 CD 应该相分离,CI 流水线负责构建镜像,CD 流水线负责部署镜像到生产环境,需要进行手动确认后触发。

将镜像部署到生产环境,包含两个步骤:

  1. 在目标服务器上执行 docker compose,最简化启停服务
  2. 发送部署成功邮件通知
yaml 复制代码
# cd-ghcr.yaml
name: CD Pipeline, Deploy to Target Server

on:
  # CD 流水线触发方式:手动点击、CI 工作流完成后
  workflow_dispatch: # 手动点击触发
  workflow_run:
    # 当 CI 流水线成功完成后自动触发
    workflows: ["CI Pipeline, Create and Publish a Docker Image"]
    types:
      - completed

# 定义环境变量
env:
  REGISTRY: ghcr.io # GitHub Container Registry
  IMAGE_NAME: ${{ github.repository }} # 系统环境变量,所在 GitHub 仓库名
  SERVER_DIR: ~/WebstormProjects/xx-web # 用户自定义,目标服务器上的项目目录

jobs:
  deploy-to-server:
    # 仅在 CI 成功完成时运行
    if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
    runs-on: ubuntu-latest

    steps:
      - name: Deploy to target server via SSH
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          password: ${{ secrets.SERVER_PASSWORD }}
          script: |
            # 停止并移除旧的容器
            cd ${{ env.SERVER_DIR }}
    
            git pull # 拉取最新代码,确保是最新版本的docker-compose.yaml
    
            docker compose down || true
    
            # 启动新的容器
            docker compose up -d
    
            # 清理未使用的镜像
            docker image prune -f
    
            echo "Deployment completed successfully!"

      # 发送部署成功邮件通知,可选的
      - name: Send deployment success email
        uses: dawidd6/action-send-mail@v3
        with:
          server_address: smtp.qq.com
          server_port: 465
          secure: true
          username: ${{ secrets.MAIL_USERNAME }}
          password: ${{ secrets.MAIL_PASSWORD }}
          subject: "🚀 应用部署完成 - ${{ github.repository }}"
          to: xing.xiaolin@foxmail.com
          from: GitHub Actions
          html_body: |
            <h2>应用已成功部署到目标服务器</h2>
            <p><strong>仓库:</strong>${{ github.repository }}</p>
            <p><strong>分支:</strong>${{ github.ref_name }}</p>
            <p><strong>提交:</strong>${{ github.sha }}</p>
            <p><strong>镜像:</strong>${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}</p>
            <p><strong>时间:</strong>$(date)</p>
            <hr>
            <p><small>此邮件由 GitHub Actions 自动发送</small></p>

appleboy/ssh-action@v1.0.0 是 GitHub Actions Workflow 插件,用于在 GitHub Actions 中执行远程 SSH 命令。

上述配置文件中,需要用户自定义的参数:

  • SERVER_HOST:目标服务器的 IP 地址或域名
  • SERVER_USER:目标服务器的 SSH 用户名
  • SERVER_PASSWORD:目标服务器的 SSH 密码
  • SERVER_DIR:目标服务器上的项目目录

假如你所购买的云服务器账户信息为 tencent@111.222.333.444,设置的密码为 123456abc,则需要在 GitHub 项目仓库中添加以下 Secrets:

  • SERVER_HOST:111.222.333.444
  • SERVER_USER:tencent
  • SERVER_PASSWORD:123456abc

云服务器支持密码密钥 认证,两种认证方式互相冲突,为了灵活性,推荐使用密码认证。

为了平衡流水线配置与可迁移性,第一次部署为手动执行SERVER_DIR 则是用户自定义的项目目录,根据实际情况填写,如 ~/WebstormProjects/xx-web

在流水线配置文件中,git pull的目的是拉取最新的代码,其目标为更新docker-compose.yaml文件。

其他参数,已在 CI Pipeline 中配置:

  • MAIL_USERNAME:发送邮件的邮箱用户名
  • MAIL_PASSWORD:发送邮件的邮箱密码

总结

本文是 CD Pipeline 的最小可行方案实践,使用 YAML 配置文件声明式定义 CD Pipeline,流水线代码与业务代码实现了分离,开发人员仅需关注docker-compose.yaml文件即可。第一次部署是用户手动执行的,平衡了可迁移性和配置复杂度。如果需要频繁地更换服务器,可将项目创建添加到流水线到 step 中。

CI 和 CD 的最小可行方案实现已完成,后续将实现前后端分离项目的 CI/CD 最佳实践,并介绍最常用的 GitHub Acitons Workflow 模板:

  • 前端项目:React、Vue
  • 服务端项目:Flask、SpringBoot

另外,还有一个比较有意思的话题,CD 有两个词的缩写:

  • Continues Deploy:持续部署
  • Continuous Delivery:持续交付

本文中虽然使用了持续发布这一名词,但实际上并未对交付进行定义,模糊了 CD 的含义。持续交付和持续部署的理解在业界也存在歧义,后续也将对这两个概念进行更详细的探讨。

关注微信公众号,获取运维资讯

如果此篇文章对你有所帮助,感谢你的点赞收藏,也欢迎在评论区友好交流。

微信搜索关注公众号:持续运维

参考

  1. appleboy/ssh-action@v1.0.0, 执行远程 SSH 命令,github.com/appleboy/ss...
  2. Accelerating new GitHub Actions workflows,github.com/actions/sta...
  3. 持续集成、交付与部署,www.jetbrains.com/zh-cn/teamc...
相关推荐
泽君学长2 小时前
Docker核心技术详解与简单实战
运维·docker·容器
苦逼IT运维2 小时前
VMware Horizon 与 Docker 冲突排错记录
linux·运维·docker·容器·自动化
小李独爱秋2 小时前
Linux操作系统实验报告单(13) 显示进程列表
linux·运维·服务器·操作系统·实验报告单
艾莉丝努力练剑3 小时前
Al Ping免费上新:GLM-4.7 && MiniMaxM2.1重磅上线,附独家使用教程
java·大数据·linux·运维·人工智能·python
莫渊博-天下无病3 小时前
node高版本安装
运维·node.js
人工智能训练3 小时前
Ubuntu 系统安装 VSCode 超全指南(3 种方法,适配 20.04/22.04 LTS)
linux·运维·服务器·vscode·ubuntu
facaixxx20243 小时前
GPU算力租赁:3080Ti、Tesla P40和RTX40服务器配置整理
运维·服务器·gpu算力
bing_feilong3 小时前
ubuntu网络命令详解
linux·运维·网络
木二_3 小时前
附056.Kubernetes_v1.34.3三节点集群-CentOS版
云原生·容器·kubernetes·centos·containerd·ingress·longhorn