CICD 流水线文档

git代码管理

jenkins

docker或者k8s集群

对于CICD本身的理解:

CI 与 CD 离不开jenkins这一组件,CI的过程即是通过Jenkins将代码从git拉取,构建,制作镜像交给测试人员

可以实现持续的集成,自动构建,测试

CD通过Jenkins将已经测试好的发行版本,拉取 构建 制作为镜像交给运维人员去部署

持续交付:交付给运维人员手动部署

持续部署:自动化部署

我的流程大致是这样的:

GitLab (代码仓库)

Jenkins (CI/CD 服务器)

Docker 镜像构建

目标服务器部署

Git,Github,Gitlab的区别

Git是一个开源的分布式版本控制系统。用于处理或小或大的项目

Github是基于git的在线代码托管服务。Gitlab也是,只是由不同人开发

部署git服务

准备一台机器 内存最好10G起步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#使用docker安装gitlab,国内大部分加速地址都没了,代理docker的网络来拉取镜像,网上有文档
#首先创建好数据卷存放的目录 在usr/local/创建docker目录 在docker目录中创建gitlab_docker
#进入gitlab_docker目录,编写docker-compose.yml(去找模板)
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
container_name: gitlab
restart: always
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://192.168.100.121:8929'
gitlab_rails['gitlab_shell_ssh_port'] = 2224
ports:
- '8929:8929'
- '2224:2224'
volumes:
- './config:/etc/gitlab'
- './logs:/var/log/gitlab'
- './data:/var/opt/gitlab'

docker pull gitlab/gitlab-ce:latest
#等待拉取完成

docker-compose up -d # 启动服务

docker-compose logs -f #查看日志来确认启动成功 如果遇到权限问题 给data加权限

浏览器访问服务的映射端口,默认的管理员密码在输出日志中可以查看。

部署Jenkins

也准备一台内存较大的机器,通过docker部署Jenkins

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#首先创建好数据卷存放的目录 在usr/local/创建docker目录 在docker目录中创建jenkins_docker
#编写docker-compose文件
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
ports:
- "8080:8080"
- "50000:50000"
volumes:
- ./data/:/var/jenkins_home/
- /var/run/docker.sock:/var/run/docker.sock # 重要:允许容器内使用 Docker
- /etc/docker/daemon.json:/etc/docker/daemon.json #同时使用宿主机的daemon.json中的配置
user: root

***关键配置说明:***

- 挂载 Docker socket (/var/run/docker.sock) 使 Jenkins 容器可以使用宿主机 Docker(用以构建镜像)

主要就是将宿主机的某些目录映射到容器内部 和端口映射一个道理

- 设置 user: root 获取必要权限

在Jenkins容器内安装docker客户端

1
2
3
4
5
6
7
8
9
10
# 进入 Jenkins 容器
docker exec -it jenkins bash

# 安装 Docker 客户端
apt-get update
apt-get install -y docker.io

# 验证安装
docker --version
docker ps

省略了maven的部署(主要应用于java项目)

对Jenkins进行配置

1. 首次登录和初始化

1.1 获取初始管理员密码

Jenkins 首次启动后,需要获取初始管理员密码:

方法1:在容器内查看

1
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

方法2:在宿主机查看

1
2
cd /usr/local/docker/jenkins_docker
cat data/secrets/initialAdminPassword

1.2 完成初始化向导

  1. 访问 Jenkins:http://192.168.100.122:8080
  2. 输入初始管理员密码
  3. 选择 安装推荐的插件选择插件来安装
  4. 等待插件安装完成
  5. 创建管理员账户(或使用默认的 admin 账户)

2. 安装必要插件

2.1 必须安装的插件

  1. Pipeline - Pipeline 支持(通常已包含在推荐插件中)
  2. Git - Git 版本控制支持
  3. Docker Pipeline - Docker 流水线支持(可选)

2.2 安装插件步骤

  1. Jenkins → 系统管理 → 插件管理
  2. 点击 可选插件 标签
  3. 搜索需要的插件
  4. 勾选插件,点击 Install without restartDownload now and install after restart
  5. 等待安装完成

2.3 如果插件安装失败

问题:插件版本不兼容

  • 确保 Jenkins 版本足够新(推荐 2.440+ LTS)
  • 检查插件要求的 Jenkins 最低版本

问题:插件下载失败

  • 配置 Jenkins 使用国内镜像源(见下方配置)

3. 配置 Jenkins 更新中心镜像源

3.1 修改更新中心配置

  1. Jenkins → 系统管理 → 插件管理 → 高级
  2. 找到 更新站点 部分
  3. 将默认 URL 替换为国内镜像:

清华大学镜像:

1
https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

或者直接修改配置文件:

在 Jenkins 容器内或宿主机上:

1
2
3
4
5
# 进入 Jenkins 容器
docker exec -it jenkins bash

# 编辑更新中心配置
vi /var/jenkins_home/hudson.model.UpdateCenter.xml

修改为:

1
2
3
4
5
6
7
<?xml version='1.1' encoding='UTF-8'?>
<sites>
<site>
<id>default</id>
<url>https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json</url>
</site>
</sites>
  1. 重启 Jenkins:
1
docker restart jenkins

4. 配置系统设置

4.1 系统配置

  1. Jenkins → 系统管理 → 系统配置
  2. 重要配置项:
    • Jenkins URL: http://192.168.100.122:8080
    • 执行者数量: 根据服务器性能设置(建议 2-4)
    • 工作空间目录: 默认即可

4.2 全局工具配置

  1. Jenkins → 系统管理 → 全局工具配置

  2. Git 配置

    • Name: Default
    • Path to Git executable: 通常自动检测,或填写 /usr/bin/git
  3. Docker 配置(如果使用 Docker 工具):

    • Name: Docker
    • Docker installation: 选择 Install automatically 或指定路径

5. 配置凭据管理

5.1 添加 GitLab 凭据

  1. Jenkins → 系统管理 → 凭据管理 → 系统 → 全局凭据 → 添加凭据
  2. 配置 GitLab 凭据:
    • Kind: Username with password
    • Scope: Global
    • Username: Administrator(或 root
    • Password: GitLab 密码
    • ID: gitlab-admin(记住这个 ID,Pipeline 中会用到)
    • Description: GitLab Administrator Account
  3. 点击 确定

5.2 添加 SSH 凭据(可选)

如果需要 SSH 方式连接服务器:

  1. 添加凭据
  2. 配置:
    • Kind: SSH Username with private key
    • Username: root
    • Private Key: 选择 Enter directly,粘贴私钥内容
    • ID: target-server-ssh
    • Description: Target Server SSH Key

进行cicd的操作:

1. 创建 Pipeline 任务
  1. 在 Jenkins 中点击 新建任务
  2. 输入任务名称:mytest-pipeline
  3. 选择 流水线(Pipeline)
  4. 点击 确定
2. 配置源码管理

在 Pipeline 配置中:

  • Definition: 选择 Pipeline script(或 Pipeline script from SCM)
  • 如果使用 SCM,配置:
    • SCM: Git
    • Repository URL: http://192.168.100.121:8929/root/mytest.git
    • Credentials: 选择 GitLab 凭据
    • Branches to build: */master
3. 配置 Git 凭据
  1. Jenkins → 系统管理 → 凭据管理 → 系统 → 全局凭据 → 添加凭据
  2. 配置:
    • Kind: Username with password
    • Username: Administratorroot
    • Password: GitLab 密码
    • ID: gitlab-admin
    • Description: GitLab Administrator
4. 配置 SSH 免密登录
4.1 在 Jenkins 容器内生成 SSH 密钥
1
2
3
4
docker exec -it jenkins bash
ssh-keygen -t rsa -b 4096
# 按回车使用默认设置
cat ~/.ssh/id_rsa.pub
4.2 将公钥添加到目标服务器

在目标服务器上执行:

1
2
3
4
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "Jenkins的公钥内容" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
4.3 测试 SSH 连接
1
2
3
# 在 Jenkins 容器内
ssh root@192.168.100.122
# 应该能无密码登录

为了能够编写以及维护 需要学习shell脚本编写和pipeline的一些语法

以下是一个简单流程的pipeline脚本示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
pipeline {
agent any

environment {
TARGET_SERVER = '192.168.100.122'
TARGET_USER = 'root'
TARGET_PATH = '/opt/mytest'
IMAGE_NAME = 'mytest:latest'
GIT_URL = 'http://192.168.100.121:8929/root/mytest.git'
}

stages {
stage('拉取代码') {
steps {
script {
sh '''
echo "========== 拉取代码 =========="
if [ -d ".git" ]; then
echo "Git 仓库已存在,更新代码..."
git pull origin master
else
echo "克隆代码仓库..."
git clone ${GIT_URL} .
fi
echo "代码拉取完成"
'''
}
}
}

stage('构建 Docker 镜像') {
steps {
script {
sh '''
echo "========== 开始构建 Docker 镜像 =========="
docker build -t ${IMAGE_NAME} .
if [ $? -ne 0 ]; then
echo "Docker 镜像构建失败!"
exit 1
fi
echo "========== 保存镜像文件 =========="
docker save ${IMAGE_NAME} -o mytest.tar
ls -lh mytest.tar
'''
}
}
}

stage('推送到目标服务器') {
steps {
script {
sh '''
echo "========== 推送到目标服务器 =========="
# 创建目录
ssh -o StrictHostKeyChecking=no ${TARGET_USER}@${TARGET_SERVER} "mkdir -p ${TARGET_PATH} && chmod 755 ${TARGET_PATH}"

# 推送文件
scp -o StrictHostKeyChecking=no mytest.tar ${TARGET_USER}@${TARGET_SERVER}:${TARGET_PATH}/

if [ $? -ne 0 ]; then
echo "文件推送失败!"
exit 1
fi

echo "========== 在目标服务器上部署 =========="
# 使用 8081:8080 端口映射,避免与 Jenkins 的 8080 冲突
ssh -o StrictHostKeyChecking=no ${TARGET_USER}@${TARGET_SERVER} "cd ${TARGET_PATH} && docker load -i mytest.tar && docker stop mytest 2>/dev/null || true && docker rm mytest 2>/dev/null || true && docker run -d --name mytest -p 8081:8080 --restart unless-stopped mytest:latest"

if [ $? -eq 0 ]; then
echo "========== 部署成功! =========="
echo "Jenkins: http://${TARGET_SERVER}:8080"
echo "应用已部署到: http://${TARGET_SERVER}:8081"
else
echo "========== 部署失败! =========="
exit 1
fi
'''
}
}
}
}

post {
always {
cleanWs()
}
success {
echo '构建成功!'
}
failure {
echo '构建失败!'
}
}
}

以下是脚本的详解:

它的核心功能是实现一个简单的 CI/CD(持续集成/持续部署) 流程:从 Git 拉取代码 -> 在 Jenkins 节点构建 Docker 镜像 -> 将镜像打包成文件 -> 拷贝到另一台应用服务器 -> 在应用服务器上重新部署运行。

1. 头部定义与环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pipeline {
agent any // 在任何可用的 Jenkins 节点(Slave/Master)上运行此任务

environment {
// 目标应用服务器的 IP
TARGET_SERVER = '192.168.100.122'
// 登录目标服务器的用户(这里用了 root,实际生产建议用普通用户)
TARGET_USER = 'root'
// 目标服务器上存放部署文件的路径
TARGET_PATH = '/opt/mytest'
// 镜像的名字和标签
IMAGE_NAME = 'mytest:latest'
// Git 代码仓库地址
GIT_URL = 'http://192.168.100.121:8929/root/mytest.git'
}
// ...
}

2. 第一阶段:拉取代码 (Pull Code)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
stage('拉取代码') {
steps {
script {
sh '''
# 1. 检查当前目录下是否已经有 .git 隐藏目录
if [ -d ".git" ]; then
echo "Git 仓库已存在,更新代码..."
# 如果有,说明以前跑过任务,只需要增量更新 (git pull)
git pull origin master
else
echo "克隆代码仓库..."
# 如果没有,说明是第一次跑,需要全量下载 (git clone)
git clone ${GIT_URL} .
fi
'''
}
}
}

3. 第二阶段:构建 Docker 镜像 (Build)

这一步是在 Jenkins 所在的机器 上执行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
stage('构建 Docker 镜像') {
steps {
script {
sh '''
# 1. 执行 Docker 构建,利用当前目录下的 Dockerfile
docker build -t ${IMAGE_NAME} .

# 2. $? 是 Linux 上一个命令的退出码。0 代表成功,非 0 代表失败。
# 这是一个很好的错误处理习惯:构建失败立即退出,不往后跑。
if [ $? -ne 0 ]; then ... exit 1; fi

# 3. 核心步骤:将镜像保存为 tar 包
docker save ${IMAGE_NAME} -o mytest.tar
'''
}
}
}
  • 为什么用 docker save
    • 标准做法通常是 docker push 推送到 Harbor 私有仓库。
    • 但这个脚本采用了 “离线传输” 模式:把它打包成 mytest.tar 文件。这样做的好处是不需要搭建 Docker 仓库,坏处是文件传输(scp)比镜像层同步(pull)要慢且占带宽。

. 第三阶段:推送到目标服务器 (Deploy)

这一步是最复杂的,涉及跨服务器操作(从 Jenkins 机器 -> 122 应用服务器)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
stage('推送到目标服务器') {
steps {
script {
sh '''
# 1. 远程创建目录
# -o StrictHostKeyChecking=no:这是关键参数!
# 作用:跳过 SSH 第一次连接时的 "Are you sure...? (yes/no)" 确认提示,否则自动化脚本会卡死。
ssh -o StrictHostKeyChecking=no ${TARGET_USER}@${TARGET_SERVER} "mkdir -p ${TARGET_PATH} && chmod 755 ${TARGET_PATH}"

# 2. 将构建好的 tar 包发送过去 (SCP)
scp -o StrictHostKeyChecking=no mytest.tar ${TARGET_USER}@${TARGET_SERVER}:${TARGET_PATH}/

# 3. 远程执行部署命令(长指令解析)
ssh ... "cd ${TARGET_PATH} && \

# 加载镜像文件到目标机的 Docker
docker load -i mytest.tar && \

# 停止旧容器。注意后面的 "|| true"
# 含义:如果容器原本不存在,stop 会报错,但加了 || true 后,即使报错也强行视为成功。
# 这样防止第一次部署时因为没有旧容器而导致流水线中断。
docker stop mytest 2>/dev/null || true && \

# 删除旧容器,逻辑同上
docker rm mytest 2>/dev/null || true && \

# 启动新容器
# -d: 后台运行
# -p 8081:8080: 宿主机8081映射到容器8080
# --restart unless-stopped: 如果服务器重启,容器也会自动重启
docker run -d --name mytest -p 8081:8080 --restart unless-stopped mytest:latest"
'''
}
}
}

5. 结束处理 (Post)

1
2
3
4
5
6
7
8
9
post {
always {
// 清理工作空间。
// 非常重要!如果不清理,Jenkins 服务器的磁盘很快就会被旧源码和 tar 包填满。
cleanWs()
}
success { echo '构建成功!' }
failure { echo '构建失败!' }
}

总结

这个 Pipeline 的核心流程是:

  1. 代码:Git 拉取。
  2. 产物:构建镜像 -> 打包成 .tar 文件。
  3. 传输:通过 scp 将打包好的大文件传给目标机。
  4. 运行:目标机 SSH 远程执行 docker load -> docker run

注意事项:
要让这个脚本跑通,前提是 Jenkins 服务器必须配置了到目标服务器(192.168.100.122)的 SSH 免密登录(即把 Jenkins 的公钥放到了目标机的 authorized_keys 里),否则脚本会在 SSH/SCP 那一步因需要输入密码而失败。

接下来搭建私有仓库改变镜像的传输模式。