C++ 持续集成与持续部署(CI/CD):自动化 C++ 项目流程

好的,各位观众,欢迎来到今天的“C++ CI/CD:让你的代码飞起来”讲座!我是今天的讲师,今天我们要聊聊如何让你的C++项目摆脱手动部署的苦海,拥抱自动化,变得高效又可靠。

为什么我们需要CI/CD?

想象一下,你辛辛苦苦写了几个月的C++代码,终于完成了某个激动人心的功能。你信心满满地把代码提交到仓库,然后…

  • 手动构建: “嗯,我要打开Visual Studio/CMake/Makefile,手动编译一下…” (内心OS: 编译时间怎么这么长?!)
  • 手动测试: “编译完了,我要手动跑一下单元测试,集成测试…” (内心OS: 测试用例又挂了几个,又要debug…)
  • 手动部署: “测试通过了,我要把可执行文件/库拷贝到服务器上,重启服务…” (内心OS: 别出错了,别出错了…)

这个过程是不是很熟悉?是不是很痛苦?而且很容易出错,效率低下。

CI/CD 就是来解决这些问题的。它可以自动化构建、测试和部署流程,减少人为错误,提高开发效率,让你可以专注于写代码,而不是折腾部署。

CI/CD 的基本概念

  • CI (Continuous Integration,持续集成): 指的是频繁地(最好每天)将代码集成到共享仓库中。每次集成都会触发自动构建和测试,以便尽早发现集成错误。
  • CD (Continuous Delivery/Continuous Deployment,持续交付/持续部署): 指的是将代码变更自动地构建、测试并准备好发布到生产环境。持续交付意味着你需要手动批准部署,而持续部署意味着代码变更会自动发布到生产环境。

CI/CD 的流程

一个典型的 CI/CD 流程大致如下:

  1. 代码提交: 开发者将代码提交到代码仓库(例如 GitHub、GitLab、Bitbucket)。
  2. 触发构建: 代码仓库会触发 CI/CD 系统开始构建。
  3. 构建: CI/CD 系统会从代码仓库拉取代码,编译、链接,生成可执行文件或者库。
  4. 测试: CI/CD 系统会运行各种测试,包括单元测试、集成测试、系统测试等。
  5. 代码分析: CI/CD 系统会进行代码静态分析,检查代码质量、安全漏洞等。
  6. 打包: CI/CD 系统会将构建好的程序打包成可部署的格式(例如 Docker 镜像、tarball)。
  7. 部署: CI/CD 系统会将打包好的程序部署到测试环境、预发布环境或者生产环境。
  8. 监控: CI/CD 系统会监控部署后的程序,确保其正常运行。

CI/CD 工具选择

市面上有很多 CI/CD 工具可供选择,例如:

  • Jenkins: 开源、灵活、可定制性强,但配置较为复杂。
  • GitLab CI: GitLab 内置的 CI/CD 系统,与 GitLab 集成紧密,配置简单。
  • GitHub Actions: GitHub 内置的 CI/CD 系统,与 GitHub 集成紧密,使用方便。
  • Travis CI: 简单易用,与 GitHub 集成紧密,但免费额度有限。
  • CircleCI: 功能强大,支持多种语言和平台,但价格较高。
  • Azure DevOps: 微软提供的 CI/CD 服务,与 Azure 云平台集成紧密。

选择哪个工具取决于你的项目需求、团队规模和预算。

C++ 项目 CI/CD 实战

下面我们以一个简单的 C++ 项目为例,演示如何使用 GitLab CI 进行 CI/CD。

1. 项目结构

我们的项目非常简单,包含以下文件:

  • main.cpp:主程序文件
  • add.h:加法函数头文件
  • add.cpp:加法函数实现文件
  • test.cpp:单元测试文件
  • CMakeLists.txt:CMake 构建文件
// main.cpp
#include <iostream>
#include "add.h"

int main() {
    std::cout << "1 + 2 = " << add(1, 2) << std::endl;
    return 0;
}

// add.h
#ifndef ADD_H
#define ADD_H

int add(int a, int b);

#endif

// add.cpp
#include "add.h"

int add(int a, int b) {
    return a + b;
}

// test.cpp
#include "gtest/gtest.h"
#include "add.h"

TEST(AddTest, PositiveNumbers) {
    ASSERT_EQ(add(1, 2), 3);
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

// CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(cpp_ci_cd)

set(CMAKE_CXX_STANDARD 14)

# Add GoogleTest
include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        release-1.10.0
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

add_executable(cpp_ci_cd main.cpp add.cpp)

enable_testing()

add_executable(test test.cpp add.cpp)
target_link_libraries(test gtest_main)

include(GoogleTest)
gtest_discover_tests(test)

2. GitLab CI 配置 (.gitlab-ci.yml)

在项目根目录下创建一个名为 .gitlab-ci.yml 的文件,这是 GitLab CI 的配置文件。

stages:
  - build
  - test
  - deploy

build:
  stage: build
  image: ubuntu:latest # 使用 Ubuntu 镜像
  before_script:
    - apt-get update -y
    - apt-get install -y cmake g++ # 安装构建工具
  script:
    - mkdir build
    - cd build
    - cmake ..
    - make # 构建项目
  artifacts:
    paths:
      - build/cpp_ci_cd # 上传构建产物

test:
  stage: test
  image: ubuntu:latest
  before_script:
    - apt-get update -y
    - apt-get install -y cmake g++ libgtest-dev # 安装测试工具
  script:
    - mkdir build
    - cd build
    - cmake ..
    - make
    - ./test # 运行测试
  dependencies:
    - build

deploy:
  stage: deploy
  image: ubuntu:latest
  before_script:
    - apt-get update -y
    - apt-get install -y openssh-client # 安装 ssh 客户端
  script:
    - echo "Deploying to server..."
    - apt-get install -y zip #安装zip压缩工具
    - cd build
    - zip -r cpp_ci_cd.zip cpp_ci_cd # 压缩文件
    - sshpass -p "your_password" scp cpp_ci_cd.zip your_user@your_server:/path/to/deploy/ # 使用 sshpass 传输文件 替换为你的密码,用户名,服务器地址
    - ssh your_user@your_server "unzip /path/to/deploy/cpp_ci_cd.zip -d /path/to/deploy/" # 使用 ssh 解压文件
    - ssh your_user@your_server "chmod +x /path/to/deploy/cpp_ci_cd" # 添加执行权限
    - ssh your_user@your_server "nohup /path/to/deploy/cpp_ci_cd &" # 后台运行程序
  dependencies:
    - build
  only:
    - main # 只在 main 分支上部署

.gitlab-ci.yml 文件详解

  • stages 定义 CI/CD 的阶段,例如 buildtestdeploy
  • build 定义构建阶段的配置。
    • stage:指定阶段为 build
    • image:指定使用的 Docker 镜像,这里使用 ubuntu:latest
    • before_script:在执行 script 之前执行的脚本,这里安装了构建工具 cmakeg++
    • script:构建脚本,包括创建构建目录、使用 CMake 生成 Makefile、使用 make 构建项目。
    • artifacts:定义构建产物,这里上传了构建好的可执行文件 build/cpp_ci_cd
  • test 定义测试阶段的配置。
    • stage:指定阶段为 test
    • image:指定使用的 Docker 镜像,这里使用 ubuntu:latest
    • before_script:在执行 script 之前执行的脚本,这里安装了测试工具 cmakeg++libgtest-dev
    • script:测试脚本,包括创建构建目录、使用 CMake 生成 Makefile、使用 make 构建项目、运行测试。
    • dependencies:指定依赖的阶段,这里依赖 build 阶段,表示只有 build 阶段成功后才会执行 test 阶段。
  • deploy 定义部署阶段的配置。
    • stage:指定阶段为 deploy
    • image:指定使用的 Docker 镜像,这里使用 ubuntu:latest
    • before_script:在执行 script 之前执行的脚本,这里安装了 openssh-client
    • script:部署脚本,这里使用 scp 将构建好的可执行文件拷贝到远程服务器,并重启服务。(请注意,这里使用了 sshpass,这在生产环境中是不安全的,应该使用 SSH 密钥进行身份验证。)
    • dependencies:指定依赖的阶段,这里依赖 build 阶段,表示只有 build 阶段成功后才会执行 deploy 阶段。
    • only:指定只在 main 分支上执行部署。

3. 配置 GitLab Runner

要让 GitLab CI 运行你的 CI/CD 流程,你需要配置 GitLab Runner。 GitLab Runner 是一个独立的应用程序,它负责执行 .gitlab-ci.yml 中定义的任务。

  • 安装 GitLab Runner: 按照 GitLab 官方文档安装 GitLab Runner。
  • 注册 GitLab Runner: 使用 gitlab-runner register 命令注册 GitLab Runner,你需要提供 GitLab 实例 URL 和注册令牌。注册令牌可以在 GitLab 项目的 "Settings" -> "CI/CD" -> "Runners" 中找到。
  • 配置 Runner 执行器: 在注册 GitLab Runner 时,你需要选择一个执行器。常用的执行器有 shelldockerkubernetes 等。这里我们选择 shell 执行器。

4. 提交代码并观察 CI/CD 流程

将你的代码提交到 GitLab 仓库,GitLab CI 会自动触发 CI/CD 流程。你可以在 GitLab 项目的 "CI/CD" -> "Pipelines" 中查看 CI/CD 流程的执行状态。

一些优化建议

  • 使用 Docker 镜像: 使用 Docker 镜像可以确保构建环境的一致性,避免因为环境差异导致的问题。
  • 缓存依赖: 使用缓存可以加速构建过程,例如缓存 apt 包、Maven 依赖等。
  • 并行构建: 将构建任务分解成多个并行任务,可以缩短构建时间。
  • 使用 SSH 密钥: 使用 SSH 密钥进行身份验证,避免在脚本中硬编码密码。
  • 自动化测试: 编写完善的自动化测试用例,确保代码质量。
  • 监控和告警: 监控 CI/CD 流程的执行状态,及时发现和解决问题。

C++ 项目 CI/CD 的一些挑战

  • 构建时间长: C++ 项目的编译时间通常比较长,可以使用缓存、并行构建等技术来优化构建时间。
  • 依赖管理: C++ 项目的依赖管理比较复杂,可以使用 Conan、vcpkg 等包管理工具来简化依赖管理。
  • 跨平台构建: C++ 项目需要支持多个平台,可以使用 CMake 等构建工具来生成不同平台的构建文件。
  • 测试框架: 选择合适的 C++ 测试框架,例如 Google Test、Catch2 等。

总结

CI/CD 可以极大地提高 C++ 项目的开发效率和代码质量。虽然配置 CI/CD 流程需要一些时间和精力,但它可以带来长期的回报。希望今天的讲座能帮助你入门 C++ CI/CD,让你的代码飞起来!

代码示例补充说明

  • 关于 sshpass 的安全问题: 我必须再次强调,在 deploy 阶段使用 sshpass 只是为了演示方便。在生产环境中,绝对不能 直接在脚本中硬编码密码。更安全的方法是使用 SSH 密钥进行身份验证。你可以生成 SSH 密钥对,并将公钥添加到服务器的 ~/.ssh/authorized_keys 文件中。然后,在 GitLab CI 中配置私钥,用于 SSH 连接。
  • 关于部署策略: deploy 阶段的部署策略非常简单,只是将可执行文件拷贝到服务器并重启服务。在实际项目中,你可能需要更复杂的部署策略,例如蓝绿部署、滚动更新等。
  • 关于错误处理: 示例中的 .gitlab-ci.yml 文件没有包含完善的错误处理机制。在实际项目中,你应该添加错误处理逻辑,例如在构建、测试或部署失败时发送告警。

表格总结常用命令及解释

命令 解释
apt-get update -y 更新 apt 包索引。-y 选项表示自动确认所有提示。
apt-get install -y <package> 安装指定的软件包。-y 选项表示自动确认所有提示。
cmake .. 在构建目录中运行 CMake,生成构建文件(例如 Makefile)。.. 表示 CMakeLists.txt 文件位于上一级目录。
make 使用 Makefile 构建项目。
./test 运行可执行文件 test,执行单元测试。
sshpass -p "password" scp ... 使用 sshpass 工具,通过 SSH 协议拷贝文件。-p "password" 选项指定 SSH 密码。请注意,在生产环境中不建议使用 sshpass,应该使用 SSH 密钥。
ssh user@server "command" 通过 SSH 协议连接到远程服务器,并执行指定的命令。
unzip file.zip -d /path/to/extract 解压 zip 文件到指定的目录。
chmod +x /path/to/executable 为指定的可执行文件添加执行权限。
nohup /path/to/executable & 在后台运行指定的可执行文件。nohup 命令可以防止程序在终端关闭后退出。& 符号表示将程序放入后台运行。
zip -r cpp_ci_cd.zip cpp_ci_cd 压缩文件,-r 表示递归压缩目录.

Q&A 环节

现在是提问环节,大家有什么问题可以提出来,我会尽力解答。

谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注