skip to content
logo

Search

博客部署流程改造

10 min read

从传统部署到 Docker 自动化部署的演进,详细记录了使用 GitHub Actions 和阿里云容器服务的完整部署流程

背景

原本我的博客是采用经典的 Docker+Nginx+Gitlab-runner 方式部署的:

  1. 在服务器上安装 GitLab Runner

    • 注册 Runner 并与 GitLab 仓库关联
    • 配置 Runner 的执行器(通常选择 Shell 或 Docker)
    • 设置 Runner 的标签和运行权限
  2. 提交代码到 GitLab 仓库

    • 在仓库中配置 .gitlab-ci.yml 文件
    • 定义构建、测试、部署等阶段
    • 设置环境变量和构建参数
  3. 自动触发 CI/CD

    • GitLab 检测到代码提交
    • Runner 拉取最新代码
    • 执行构建流程(npm build 等)
    • 部署构建产物到服务器指定目录
    • 重启相关服务

但是这种部署方式有几个问题:

  1. Runner 配置繁琐:需要手动安装 GitLab Runner,配置 Runner 的执行器和标签,有一定的复杂性和学习成本。
  2. 服务器资源占用:GitLab Runner 是安装在服务器上的,所以代码的构建和打包过程实际上是在我的服务器上进行的。每次构建都需要占用服务器资源,尤其是在需要下载大量依赖包,或者构建出现错误时,会导致部署不稳定甚至是服务器宕机,需要手动停止并重启服务。
  3. 政策问题:GitLab SaaS(国际版)宣布即将停止向国内用户提供服务,同时我也并不想使用国内版的极狐 GitLab。

因此,我决定将部署流程进行改造,采用 GitHub Actions 和阿里云容器服务的方式进行部署。其大概流程是:

  1. 提交代码到 GitHub 仓库的 release 分支
  2. 使用 GitHub Actions 打包 Docker 镜像
  3. 将镜像推送到阿里云的免费镜像仓库
  4. 执行服务器上的命令拉取镜像
  5. 重启服务

新的部署流程具有以下优点:

  1. 构建资源优化

    • 利用 GitHub Actions 提供的云端服务器进行构建,每个工作流程可使用最高配置的 Ubuntu 虚拟机
    • node_modules 等依赖包的下载和构建过程在云端完成,不占用自己服务器的带宽和计算资源
    • 支持并行构建,可以同时运行多个工作流
  2. 存储空间与成本优化

    • 构建过程产生的临时文件都在云端处理,不占用服务器存储空间
    • 阿里云容器镜像服务个人版提供免费存储空间,无需在服务器存储历史镜像
    • GitHub Actions 对公共仓库免费,每月有大量构建时间配额
    • 整体降低了服务器资源消耗和运维成本
  3. 部署稳定性提升

    • 构建失败不会影响现有服务,因为构建过程完全在云端进行
    • Docker 容器的特性使得新旧版本切换更加平滑
    • 支持快速回滚,只需重新部署之前的镜像版本
  4. 安全性增强

    • 构建环境与生产环境完全隔离
    • GitHub Actions 提供了安全的密钥管理机制
    • 容器化部署减少了环境依赖风险

实现步骤

1. 准备阿里云容器镜像服务

  1. 登录阿里云控制台,进入”容器镜像服务”
  2. 创建命名空间(如果没有)
  3. 创建镜像仓库:
    • 选择本地仓库
    • 代码源选择”本地仓库”
    • 记录下仓库地址,格式如:registry.cn-hangzhou.aliyuncs.com/your-namespace/astro-citrus
  4. 获取访问凭证,用于登录阿里云容器镜像服务

2. 配置 GitHub Secrets

在 GitHub 仓库中添加以下 Secrets(路径:仓库 Settings -> Secrets and variables -> Actions):

  1. ALIYUN_USERNAME: 阿里云容器镜像服务登录名
  2. ALIYUN_PASSWORD: 阿里云容器镜像服务密码(访问凭证)
  3. SERVER_HOST: 服务器 IP 地址
  4. SERVER_USERNAME: 服务器登录用户名
  5. SERVER_SSH_KEY: 服务器 SSH 私钥(需要配对的公钥已添加到服务器)
生成 SSH 密钥对
ssh-keygen -t rsa -b 4096 -C "your-email@example.com"

3. 创建 GitHub Actions 工作流配置

这里除了使用 GitHub Actions 直接触发服务器部署,也可以使用 Webhook 触发服务器部署。 但是需要在服务器上搭建 webhook 服务,这里不再赘述。(阿里云的 webhook 服务好像是收费的)

.github/workflows/docker-publish.yml
# 工作流名称
name: Docker Publish
 
on:
  push:
    branches: [ "release" ]
  # 可选:手动触发部署
  workflow_dispatch:
 
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      # 检出代码到工作目录
      - name: Checkout repository
        uses: actions/checkout@v3
 
      # 设置 Docker Buildx,用于构建多平台镜像
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
 
      # 登录到阿里云容器镜像服务
      - name: Login to Aliyun Container Registry
        uses: docker/login-action@v2
        with:
          registry: registry.cn-hangzhou.aliyuncs.com
          username: ${{ secrets.ALIYUN_USERNAME }}
          password: ${{ secrets.ALIYUN_PASSWORD }}
      
      # 构建 Docker 镜像并推送到阿里云
      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          # 指定构建上下文为当前目录
          context: .
          # 启用推送
          push: true
          # 指定镜像标签,包括 latest 和 commit hash
          tags: |
            registry.cn-hangzhou.aliyuncs.com/huadong_cangku/blog:latest
            registry.cn-hangzhou.aliyuncs.com/huadong_cangku/blog:${{ github.sha }}
      
      # 通过 SSH 连接部署服务器并执行部署脚本
      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USERNAME }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            cd /root/astro-citrus
            bash deploy.sh
          timeout: 60s          # 增加超时时间
          command_timeout: 20m  # 增加命令执行超时时间

4. 服务器准备工作

  1. 确保服务器已安装 Docker:
curl -fsSL https://get.docker.com | sh
  1. 服务器需要先登录阿里云容器镜像服务:
docker login registry.cn-hangzhou.aliyuncs.com
  1. 创建部署目录并配置文件:

在服务器上创建一个专门用于存放部署相关文件的目录,主要用来存放部署时需要用到的 deploy.sh 部署脚本和 nginx.conf 配置文件,以及作为容器运行时挂载配置文件的源目录。

# 创建部署目录
mkdir -p /root/astro-citrus
cd /root/astro-citrus

5. 编辑服务器上的 Nginx 配置文件

使用vim nginx.conf 命令创建并编辑 Nginx 配置文件:

~/root/astro-citrus/nginx.conf
server {
    listen 80;
    server_name sunburst.fun;
    return 301 https://$server_name$request_uri;
}
 
server {
    listen 443 ssl;
    server_name sunburst.fun;
 
    ssl_certificate /etc/nginx/ssl/sunburst.fun_bundle.crt;
    ssl_certificate_key /etc/nginx/ssl/sunburst.fun.key;
 
    root /usr/share/nginx/html;
    index index.html;
 
    location / {
        try_files $uri $uri/ /index.html;
    }
}

这里的 nginx.conf 配置文件包含了以下关键设置:

  • HTTP 到 HTTPS 的自动重定向
  • SSL 证书配置(确保将证书文件放在服务器的 /etc/nginx/ssl 目录下)
  • 静态文件服务的根目录设置
  • 支持单页应用的路由重写规则

注意:在运行容器之前,请确保:

  1. 将 SSL 证书文件(.crt 和 .key)放置在服务器的 /etc/nginx/ssl 目录下
  2. 证书文件名与配置文件中的路径保持一致
  3. SSL 证书目录的权限设置正确

6. 编辑服务器上的部署脚本

使用vim deploy.sh 命令创建并编辑部署脚本,同时确保 deploy.sh 有执行权限: chmod +x deploy.sh

~/root/astro-citrus/deploy.sh
#!/bin/bash
 
# 定义容器和镜像的环境变量
CONTAINER_NAME="astro-citrus"
IMAGE_NAME="registry.cn-hangzhou.aliyuncs.com/huadong_cangku/blog:latest"
 
# 从阿里云拉取最新的镜像
echo "Pulling latest image..."
docker pull $IMAGE_NAME
 
# 停止并删除旧的容器(如果存在)
# || true 确保即使容器不存在也不会报错
echo "Stopping and removing old container..."
docker stop $CONTAINER_NAME || true
docker rm $CONTAINER_NAME || true
 
# 启动新的容器
echo "Starting new container..."
docker run -d \
  --name $CONTAINER_NAME \
  --restart unless-stopped \
  -p 80:80 \
  -p 443:443 \
  -v /root/astro-citrus/nginx.conf:/etc/nginx/conf.d/default.conf \
  -v /etc/nginx/ssl:/etc/nginx/ssl \
  $IMAGE_NAME
 
# 检查容器是否成功启动
if [ "$(docker ps -q -f name=$CONTAINER_NAME)" ]; then
    echo "Container started successfully"
else
    echo "Container failed to start"
    exit 1
fi
 
# 清理不再使用的镜像以节省磁盘空间
# -f 表示强制删除,不需要确认
echo "Cleaning up old images..."
docker image prune -f

注意事项:

  • 考虑添加健康检查
  • 考虑配置日志收集
  • 建议添加版本标签,而不是只用 latest 标签

7. 部署流程测试

  1. 创建测试分支:
git checkout -b test-deploy
  1. 提交一些更改:
git add .
git commit -m "test: deploy workflow"
  1. 推送到 release 分支:
git push origin test-deploy:release

8. 监控和维护

  1. 查看部署状态:

    • GitHub Actions 页面查看工作流运行状态
    • 服务器上检查容器状态:
      docker ps
      docker logs astro-citrus
  2. 常见问题排查:

    • 检查 GitHub Secrets 是否正确配置
    • 确认服务器 SSH 连接是否正常
    • 验证阿里云容器服务的访问权限
    • 检查服务器防火墙设置