基于Github Actions 和 Portainer 实现CI/CD

1. 引言

近期,我正在进行个人项目的开发。在开发过程中,我希望能够将项目直接部署到线上,以便观察开发环境与生产环境之间的差异。这一做法有助于尽早发现潜在问题,降低解决的成本。为此,我采用了 GitHub Actions 和 Portainer 实现CI/CD。本文将详细阐述从代码提交到服务部署全流程自动化的实现方案。

2. 核心工具介绍

GitHub Actions

GitHub Actions 是 GitHub 提供的一项功能,允许开发者自动化软件开发工作流程。它使得用户能够在代码库中定义 CI/CD(持续集成和持续交付)流程,以便在特定事件发生时(如代码提交、拉取请求等)自动执行一系列操作。

主要特点

  • 工作流定义:用户可以通过 YAML 文件定义工作流,指定触发条件、执行步骤和所需的环境。
  • 事件驱动:支持多种事件触发,包括代码推送、拉取请求、定时任务等。
  • 自定义操作:用户可以创建自定义操作,或者使用社区提供的现成操作,来扩展工作流的功能。
  • 集成与部署:可以与其他服务和工具(如 Docker、AWS、Azure 等)无缝集成,支持自动构建、测试和部署应用程序。
  • 并行执行:支持并行执行多个任务,提高工作流的效率。

使用场景

  • 自动化测试:在每次代码提交后自动运行测试。
  • 自动构建:在代码合并后自动构建应用程序。
  • 部署:在代码推送后自动将应用程序部署到生产环境或测试环境。

Portainer

Portainer 是一个轻量级的管理界面,用于简化 Docker 容器、镜像、网络和卷的管理。它提供了一个用户友好的 Web 界面,使得用户能够更方便地管理 Docker 环境。

主要特点

  • 直观的用户界面:提供易于使用的图形界面,用户可以通过浏览器轻松管理 Docker 容器。
  • 多种管理功能:支持创建、启动、停止、删除容器,管理镜像、网络和卷等。
  • 支持多种环境:可以管理本地 Docker 环境、远程 Docker 主机以及 Docker Swarm 集群。
  • 用户管理:支持用户角色和权限管理,适合团队协作。
  • API 支持:提供 REST API,方便与其他工具集成。

使用场景

  • 容器管理:适合开发人员和运维人员快速管理和监控 Docker 容器。
  • 团队协作:在团队中共享和管理 Docker 环境,简化操作流程。
  • 监控和日志:提供容器的实时监控和日志查看功能,帮助排查问题。

3. GitHub Actions

核心概念

  • Workflow(工作流)

    • 一个自动化流程的完整定义,由 YAML 文件描述。
    • 存储在仓库的 .github/workflows 目录下,支持多工作流并行。
  • Job(任务)

    • 工作流中的独立执行单元,例如构建、测试、部署。
    • 多个 Job 默认并行执行,可通过 needs 关键字定义依赖关系。
  • Step(步骤)

    • Job 中的具体操作步骤,如安装依赖、运行脚本。
    • 支持使用预定义 Action(如 actions/checkout)或自定义命令。
  • Event(触发事件)

    • 触发工作流执行的条件,例如 pushpull_request、定时任务等。

配置流程

1. 创建工作流文件

  1. 在仓库根目录创建 .github/workflows 文件夹。
  2. 新建 YAML 文件(如 ci-cd.yml),定义工作流逻辑。

2. 配置变量

  • 涉及到敏感信息的,比如 DockerHub 密码、API 密钥等信息的,可以使用serect,serect一旦创建便不可见。
  • 其他非敏感信息可以使用variable,创建后仍可以查看其值。

3. 实现工作流

yaml 复制代码
# .github/workflows/docker-build.yml
name: CI/CD Pipeline  # 工作流名称

on:  # 触发条件
  push:
    branches: [ "main" ]  # main分支提交时触发
  pull_request:
    branches: [ "main" ]

jobs:  # 定义任务
  build-and-test:  # 任务ID
    runs-on: ubuntu-latest  # 运行环境
    steps:
          # 步骤1:检出代码
          - name: Checkout repository
            uses: actions/checkout@v4

          # 步骤2:登录容器注册中心
          - name: Log in to ${{ env.REGISTRY }}
            uses: docker/login-action@v3
            with:
              registry: ${{ env.REGISTRY }}
              username: ${{ secrets.DOCKER_USERNAME }}
              password: ${{ secrets.DOCKER_PASSWORD }}

          # 步骤3:设置构建元数据
          - name: Extract metadata
            id: meta
            uses: docker/metadata-action@v5
            with:
              images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
              tags: |
                type=raw,value=latest
                type=sha

          # 步骤4:构建并推送镜像(支持缓存)
          - name: Build and push
            uses: docker/build-push-action@v5
            with:
              context: .
              file: ./Dockerfile  # 指定Dockerfile路径
              push: true
              tags: ${{ steps.meta.outputs.tags }}
              labels: ${{ steps.meta.outputs.labels }}
              cache-from: type=gha
              cache-to: type=gha,mode=max

          # 步骤5:触发外部部署(如Portainer)
          - name: Trigger Portainer Webhook
            if: success()
            run: |
              curl -X POST \
                -H "Content-Type: application/json" \
                "${{ secrets.PORTAINER_WEBHOOK_URL }}?trigger_force_update=true"

4. Portainer自动化部署

1. 安装 Portainer

首先,需要在服务器上安装 Portainer。可以使用以下命令通过 Docker 运行 Portainer。

bash 复制代码
docker pull portainer/portainer

docker run -d -p 9000:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --name portainer portainer/portainer

2. 访问 Portainer

在浏览器中访问 Portainer 的 Web 界面,输入以下 URL:

arduino 复制代码
http://<your-server-ip>:9000

3. 创建应用程序的 Docker 镜像

在自动化部署之前,用户需要先将应用程序打包成 Docker 镜像。用户可以通过 Dockerfile 来构建镜像。以下是一个构建 Node 应用程序的例子。

dockerfile 复制代码
# 使用官方 Node.js 镜像
FROM node:14

# 设置工作目录
WORKDIR /usr/src/app

# 复制 package.json 和 package-lock.json
COPY package*.json ./

# 安装依赖
RUN npm install

# 复制应用程序代码
COPY . .

# 暴露应用程序端口
EXPOSE 3000

# 启动应用程序
CMD ["node", "app.js"]

使用以下命令构建镜像:

bash 复制代码
docker build -t my-node-app .

4. 在 Portainer 中部署应用程序

  1. 登录 Portainer:使用您设置的管理员账户登录 Portainer。

  2. 创建新容器

    • 在 Portainer 的左侧菜单中,选择"Containers"。
    • 点击"Add container"按钮。
  3. 配置容器

    • 在"Name"字段中输入容器名称。
    • 在"Image"字段中输入您刚刚构建的镜像名称(例如 my-node-app)。
    • 配置端口映射、环境变量等其他设置。
  4. 启动容器:完成配置后,点击"Deploy the container"按钮,Portainer 将自动启动您的应用程序。

5. 完整流程

1. 创建项目

bash 复制代码
//创建next项目
npx create-next-app@latest

//启动项目
npm run dev

修改Next项目的输出模式

typescript 复制代码
//next.config.ts
const nextConfig: NextConfig = {
  /* config options here */
  output: 'standalone',
};

2. 添加Dockerfile文件

在项目根目录下添加Dockerfile文件,并添加如下代码

dockerfile 复制代码
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app

# 首先只复制 package.json 和 lock 文件以利用缓存
COPY package.json package-lock.json ./
RUN npm install

# 然后复制其他源文件
COPY . .
RUN npm run build

# 运行阶段
FROM node:18-alpine AS runner
WORKDIR /app

# 设置环境变量
ENV NODE_ENV=production

# 只复制必要的文件
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

EXPOSE 3000

CMD ["node", "server.js"]

3. 添加workflow文件

在根目录下新建version文件,并输入当前的版本号

version 复制代码
v0.1.0

.github/workflows目录下新建docker-build.yml文件,并添加如下代码

yaml 复制代码
name: Docker Build and Push

on:
  push:
    branches: ['master']
  pull_request:
    branches: ['master']

env:
  # 腾讯云镜像仓库地址
  REGISTRY: ccr.ccs.tencentyun.com
  # 镜像仓库命名空间
  NAMESPACE: naf9nahz
  # 镜像名称
  IMAGE_NAME: my-next-app
  # 如果有其他环境变量,也可以在这里添加
  # OTHER_ENV_VAR: ${{ secrets.OTHER_ENV_VAR }}

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      DOCKER_BUILDKIT: 1
      BUILDKIT_INLINE_CACHE: 1
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Get Version
        id: get_version
        run: echo "VERSION=$(cat version)" >> $GITHUB_OUTPUT

      - name: Login to TencentCloud CCR
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.TENCENT_CLOUD_ACCOUNT_ID }}
          password: ${{ secrets.TENCENT_CLOUD_API_KEY }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}:${{ steps.get_version.outputs.VERSION }}
          build-args: |
            # 如果有其他环境变量,也可以在这里添加
            # OTHER_ENV_VAR=${{ secrets.OTHER_ENV_VAR }}

4. 部署Portainer

在服务器上分别运行这两条指令

bash 复制代码
docker pull portainer/portainer

docker run -d -p 9000:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --name portainer portainer/portainer

输入服务器的IP地址访问Portainer的页面

arduino 复制代码
http://<your-server-ip>:9000

5. 构建镜像

master分支提交代码或发起合并请求,以触发workflow

6. 发布项目

  1. 点击Portainer页面左侧的"Stacks"
  2. 点击右上角的"Add Stack"
  3. 输入Stack的名字并将Docker Compose粘贴到Web editor
  4. 点击Deploy the stack
yaml 复制代码
version: '3.8'

services:
  app:
    image: ccr.ccs.tencentyun.com/naf9nahz/my-next-app:v0.1.0
    container_name: my-next-app
    restart: always
    ports:
      - "3001:3000"
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

我的服务器的3000端口已经被其他项目占用,因此改为3001端口

输入服务器的IP地址访问项目

arduino 复制代码
http://<your-server-ip>:3001

6. 其他问题

Q:Portainer页面无法访问

  • 检查服务器防火墙是否放行端口。

Q:Github Action如何自动触发 Portainer 的更新

  • 可以在workflow执行完构建镜像流程后通过执行curl指令触发 Portainer 中容器的更新。
相关推荐
Martin_Yelvin19 小时前
记录一个Circle CI出现的错误
开发语言·前端·ci/cd
裁二尺秋风1 天前
CI/CD—Jenkins配置Maven+GitLab自动构建jar包
ci/cd·jenkins·maven
裁二尺秋风1 天前
CI/CD—Jenkins实现自动构建Docker镜像运行Java程序
java·ci/cd·jenkins
裁二尺秋风1 天前
CI/CD—GitLab钩子触发Jenkins自动构建项目
ci/cd·gitlab·jenkins
明明跟你说过1 天前
在【k8s】中部署Jenkins的实践指南
运维·ci/cd·云原生·容器·kubernetes·jenkins
DevSecOps选型指南3 天前
DevSecOps CI/CD 管道中数字供应链安全的集成策略
运维·安全·ci/cd
川石课堂软件测试5 天前
涨薪技术|持续集成Git使用详解
开发语言·javascript·git·python·功能测试·ci/cd·单元测试
Sundayday478 天前
1、CI/CD 平台安装部署(Gitlab+Jenkins)
运维·ci/cd·centos·gitlab·jenkins
Zender Han8 天前
Jenkins与Flutter项目持续集成实战指南
flutter·ci/cd·jenkins