本次学习虚拟机配置如下
4U8G Rocky-9.6-x86_64-dvd.iso 网络模式 nat
第一阶段:准备工作
1.配置yum仓库,使用阿里云的.,截至目前可用,后续如果更新自行替换。
bash
[base]
name=Rocky Linux 9 - BaseOS - AliYun
baseurl=https://mirrors.aliyun.com/rockylinux/9/BaseOS/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=https://mirrors.aliyun.com/rockylinux/RPM-GPG-KEY-Rocky-9
# Rocky Linux 9 - AppStream - AliYun Mirror
[appstream]
name=Rocky Linux 9 - AppStream - AliYun
baseurl=https://mirrors.aliyun.com/rockylinux/9/AppStream/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=https://mirrors.aliyun.com/rockylinux/RPM-GPG-KEY-Rocky-9
# Rocky Linux 9 - Extras - AliYun Mirror
[extras]
name=Rocky Linux 9 - Extras - AliYun
baseurl=https://mirrors.aliyun.com/rockylinux/9/extras/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=https://mirrors.aliyun.com/rockylinux/RPM-GPG-KEY-Rocky-9
[docker]
name=Docker
baseurl=https://mirrors.aliyun.com/docker-ce/linux/rhel/9.6/x86_64/stable/
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/docker-ce/linux/rhel/gpg
[epel]
name=epel
baseurl=https://mirrors.aliyun.com/epel/9/Everything/x86_64/
gpgkey=https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-9
gpgcheck=1
enabled=1
2.环境参数配置
bash
#!/bin/bash
# 最简容器环境准备(防火墙/SELinux/swap关闭 + 内核转发 + 时间同步)
# 1. 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
# 2. 关闭 SELinux(临时+永久)
setenforce 0
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
# 3. 关闭 swap(临时+永久)
swapoff -a
sed -i '/\bswap\b/s/^/#/' /etc/fstab
# 4. 加载必要内核模块(忽略 br_netfilter 不存在的情况)
modprobe overlay 2>/dev/null || true
modprobe br_netfilter 2>/dev/null || true
cat > /etc/modules-load.d/container.conf <<'EOF'
overlay
br_netfilter
EOF
# 5. 开启内核转发(必选)
cat > /etc/sysctl.d/99-container.conf <<'EOF'
net.ipv4.ip_forward = 1
EOF
# 如果 br_netfilter 已加载,添加 bridge 相关参数
if [ -d /proc/sys/net/bridge ]; then
echo "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.d/99-container.conf
echo "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.d/99-container.conf
fi
sysctl --system
# 6. 设置时区为北京时间
timedatectl set-timezone Asia/Shanghai
# 7. 启用并启动 chronyd 时间同步
systemctl enable chronyd
systemctl restart chronyd
chronyc -a makestep # 强制立即同步
3.更新系统 安装docker
bash
# 1. 更新系统软件包
sudo yum update -y
# 2. 安装基础依赖(vim编辑器、wget下载工具等)
sudo yum install -y vim wget net-tools
# 3. 安装 Docker 引擎(国内源,速度快)
sudo yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl start docker
sudo systemctl enable docker
# 4. 验证 Docker 是否安装成功
docker --version
第二阶段:安装部署
1.准备镜像
提前下载镜像并重新tag 方便使用
bash
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/gitlab/gitlab-ce:latest
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/gitlab/gitlab-ce:latest gitlab/gitlab-ce:latest
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/jenkins/jenkins:latest
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/jenkins/jenkins:latest jenkins/jenkins:latest
2.部署容器
1.部署gitlab容器
1.创建目录 启动容器
bash
mkdir -p /data/gitlab/{config,logs,data}
mkdir -p /data/jenkins
bash
docker run -d \
--name gitlab \
--hostname 192.168.124.165 \
--restart always \
--shm-size 256m \
-p 80:80 \
-p 2222:22 \
-p 5050:5050 \
-v /data/gitlab/config:/etc/gitlab \
-v /data/gitlab/logs:/var/log/gitlab \
-v /data/gitlab/data:/var/opt/gitlab \
-e GITLAB_OMNIBUS_CONFIG="\
external_url 'http://192.168.124.165';
gitlab_rails['registry_enabled']=true;
registry_external_url 'http://192.168.124.165:5050'" \
gitlab/gitlab-ce:latest
具体的参数含义自行查阅了解,基本就是挂载卷,端口映射,环境变量和参数。

2.检查容器状态
初始化可能需要5-15分钟,可以进入容器查看状态。
bash
#进入容器
docker exec -it gitlab bash
#查看状态
gitlab-ctl status
#查看数据库迁移状态
gitlab-rake db:migrate:status
如果组件状态都是run 并且数据库迁移状态up 说明没有问题

可以看到我上一步查看日志有报错,循环报错上述内容。
bash
docker logs -f gitlab
不用关注,这个报错是因为gitlab-exporter监控采集失败的报错 大概原因是新版本已经使用PostgreSQL 分区表比如
p_ci_builds
p_ci_build_names
但是exporter 某些指标采集 SQL 可能仍然引用旧的
ci_pipelines
ci_job_artifacts
所以一直报错 relation does not exist
日志里可以看到确实使用了JOIN ci_pipelines之类的sql语句

可以忽略该告警,或者关闭监控,只要浏览器输入ip地址可以正常访问就可以。

当然也可以在容器内执行这个命令
bash
gitlab-rake gitlab:check SANITIZE=true
如果这条命令最终结果是finished success pass通过检查 那就说明初始化完成 可以浏览器登录你的虚拟机ip地址访问了。
获取初始密码 默认用户名root 进入容器获取密码
bash
docker exec -it gitlab bash
cat /etc/gitlab/initial_root_password

登录后修改密码 点头像-edit 进去之后找到左侧passwd 修改密码即可

3.可选项:关闭监控
容器内修改配置文件
bash
#登录容器
docker exec -it gitlab bash
#修改配置文件
vi /etc/gitlab/gitlab.rb
在文件末位添加如下配置
bash
### Disable monitoring stack
prometheus_monitoring['enable'] = false
prometheus['enable'] = false
alertmanager['enable'] = false
gitlab_exporter['enable'] = false
redis_exporter['enable'] = false
postgres_exporter['enable'] = false
退出保存然后执行下面重新生成配置
bash
gitlab-ctl reconfigure
等待出现 gitlab Reconfigured!

然后重启
bash
gitlab-ctl restart

再次检查组件状态
bash
gitlab-ctl status

会发现exporter和prometheus之类的都没了,检查日志也不会有相关打印了。
4.创建用户级别token
accesstoken 如下图 头像-preference-accesstokens

添加描述和权限,为了方便可以全选权限,后面可以只用这一个token,虽然标准的做法是项目内部创建独立的。name可以自定义 我截图里是666 后来我换成user了,你可以使用自己喜欢的,和 (步骤 3.创建凭据 accesstoken )保持一致即可。

创建后记得复制保留token 忘了就只能重新创建。
5.修改docker配置测试登录
docke login 测试,在此之前需要修改docker配置添加地址为安全可信的,否则可能会报错。
bash
cat >/etc/docker/daemon.json <<EOF
{
"insecure-registries":[
"192.168.124.164:5050"
]
}
EOF
重启docker
bash
systemctl restart docker
登录
bash
docker login 192.168.124.165:5050
用户名root 密码是之前创建的token
6.新建项目 demo-app

创建空白项目 blank
项目名字 demo-app 下拉框选择root空间 其他参考图片

7.测试推送镜像
随便下载一个nginx的打标签 然后推送
bash
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:alpine
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:alpine 192.168.124.165:5050/root/demo-app/nginx:test
docker push 192.168.124.165:5050/root/demo-app/nginx:test
成功后可以在web界面上看到。
左侧橘色头像 projiects 找到personal的一个项目点进去

左侧deploy的container registry

可以看到自己的镜像已推送成功

2.部署jenkins容器
1.创建目录启动容器
Jenkins 容器默认用户是:uid=1000 (jenkins) 提前创建目录指定权限,否则会因为权限问题无法启动。
bash
mkdir -p /data/jenkins
chown -R 1000:1000 /data/jenkins
然后启动容器
bash
docker run -d \
--name jenkins \
--restart always \
-p 8080:8080 \
-p 50000:50000 \
-v /data/jenkins:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(which docker):/usr/bin/docker \
--group-add $(stat -c '%g' /var/run/docker.sock) \
jenkins/jenkins:latest
2.获取初始密码登录
bash
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
浏览器访问 http://192.168.124.165:8080(替换为自己的IP地址)
选择:安装推荐插件,等待安装完成,然后右下角继续注册用户,下一步url ,学习使用本地环境不用修改。

3.创建凭据 accesstoken
进入后 系统管理-凭据管理

直接点击global 蓝色小字

add凭据 用户名user 密码 就是目录中 4.创建用户级别token的token
4.安装插件 Generic Webhook Trigger
在插件管理中搜索该插件 确认安装该插件
5.创建任务demo-app

具体配置如下 勾选该选项

配置git地址 这里我分支 /master 修改为 /main 是为了和(6.修改主分支名字并推送代码)保持一致 如果你这里是master 步骤6就不用修改分支名字了使用git push -u origin master即可
上图中url不确认的话可以去gitlab项目界面复制 http这个,Credentials就是之前创建的root。
6.配置webhook
在gitlab中配置,用来通知jenkins。

然后在gitlab 的webhook配置

最下方的enablessl关闭 保存
7.修改安全配置
左下角 admin

setting-network

找到outbonding 如下配置

保存退出
8.webhook测试

200即可

3.部署git
1.安装并初始化git
bash
#安装git
yum install -y git
# 创建项目目录(绝对路径)
mkdir -p /home/jenkins/my-nginx-demo
cd /home/jenkins/my-nginx-demo
# 初始化 git 仓库
git init

配置git邮箱和用户名(自定义)
bash
git config --global user.email "gitlab@test.com"
git config --global user.name "root"
添加远端仓库 url地址不缺人的话可以跳到(步骤2.部署jenkins容器- 步骤5.创建任务demo-app)中最后一步的地址
bash
git remote add origin http://192.168.124.165/root/demo-app.git
2.创建 index.html(自定义 Nginx 首页)
bash
cat > /home/jenkins/my-nginx-demo/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Nginx</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>欢迎!</h1>
这是一个来自我的 Jenkins 构建的测试页面。
镜像版本: <!-- 版本号占位符 --></body>
</html>
EOF
3.创建 Dockerfile
bash
cat > /home/jenkins/my-nginx-demo/Dockerfile << 'EOF'
# 1. 下载基础镜像
FROM nginx:alpine
# 2. 设置维护者信息(可选)
LABEL maintainer="jenkins"
# 3. 将本地的 html 文件夹复制到容器的默认网页目录
COPY index.html /usr/share/nginx/html/
# 4. 【关键步骤】使用脚本动态替换版本号
# 这会在构建时把 index.html 里的占位符替换为当前的 Jenkins 构建号
RUN sed -i "s/<!-- 版本号占位符 -->/构建版本: ${BUILD_NUMBER}/g" /usr/share/nginx/html/index.html
# 5. 暴露端口
EXPOSE 80
# 6. 启动命令
CMD ["nginx", "-g", "daemon off;"]
EOF
4.创建 Jenkinsfile
bash
cat > /home/jenkins/my-nginx-demo/Jenkinsfile << 'EOF'
pipeline {
agent any
// 定义环境变量,方便统一管理
environment {
// 你的私有仓库地址和项目路径
REGISTRY = '192.168.124.165:5050'
REPO_PATH = 'root/demo-app'
IMAGE_NAME = "${REGISTRY}/${REPO_PATH}/nginx-demo"
TAG = "${BUILD_NUMBER}"
}
stages {
stage('1. 清理环境') {
steps {
echo "清理旧文件和旧镜像..."
// 删除本地可能存在的旧镜像,防止冲突
sh "docker rmi ${IMAGE_NAME}:${TAG} || true"
sh "rm -rf index.html.bak || true"
}
}
stage('2. 代码准备') {
steps {
echo "准备网页文件..."
// 这里模拟代码修改,直接修改本地文件以便观察变化
// 将版本号写入文件(实际项目中通常是 Git 检出代码)
sh '''
# 替换占位符为当前构建号
sed -i "s/<!-- 版本号占位符 -->/构建版本: ${BUILD_NUMBER}/g" index.html
# 打印出来看看对不对
echo "=== 当前 index.html 内容 ==="
cat index.html
'''
}
}
stage('3. 构建镜像') {
steps {
echo "开始构建 Docker 镜像..."
sh "docker build -t ${IMAGE_NAME}:${TAG} ."
}
}
stage('4. 登录并推送') {
steps {
script {
// 使用 withCredentials 绑定之前创建的 Deploy Token
withCredentials([usernamePassword(
credentialsId: 'token',
usernameVariable: 'REG_USER',
passwordVariable: 'REG_PASS'
)]) {
sh '''
echo "正在登录私有仓库..."
echo "$REG_PASS" | docker login ${REGISTRY} -u "$REG_USER" --password-stdin
echo "推送镜像 ${IMAGE_NAME}:${TAG} ..."
docker push ${IMAGE_NAME}:${TAG}
echo "推送 latest 标签..."
docker tag ${IMAGE_NAME}:${TAG} ${IMAGE_NAME}:latest
docker push ${IMAGE_NAME}:latest
'''
}
}
}
}
}
post {
success {
echo "🎉 成功!镜像已推送到: ${IMAGE_NAME}:${TAG}"
}
failure {
echo "❌ 构建失败,请检查日志。"
}
always {
// 清理登录状态
sh 'docker logout ${REGISTRY} || true'
}
}
}
EOF
5.提交git代码
bash
git add .
git commit -m "666"

6.修改主分支名字并推送代码
在git初始化的目录中执行
bash
git branch
查看本地分支为master 仓库默认使用main 推荐修改为main再推送
bash
git branch -m master main
git push -u origin main
用户名 user 密码就是token 不确认的话跳转 (4.创建用户级别token)

7.jenkins页面检查构建任务

点击任务-流水线步骤 可以查看详情

点击任务-consoleoutput 可以查看日志


8.gitlab仓库查看镜像
可以看到历史的2个版本 16 17 我使用的构建任务编号作为镜像的后缀

9.本地查看镜像
bash
docker images
可以看到本地的产物16 17 并且17对于的就是latest 如果再构建一次 latest就是18 自动更新 代码实现的逻辑在jenkinsfile中,具体自己了解。

10.验证镜像命名
在项目界面点击立即构建 可以看到#18开始构建

仓库和本地也有了产物


待续:CD流程
基础的CI流程至此已完成,感兴趣可以考虑增加代码检测等其他插件和流程。
关于CD流程,持续部署,需要搭建K8S集群和CI联动。虽然我有一个三节点的k8s集群,但是PC资源不足,不能同时开启这么多虚拟机,暂时不打算进行后续测试,CD有两种模式。
1.主动部署(Push 模式)------ Jenkins 直接操作目标服务器
主动部署(Push 模式)------ Jenkins 直接操作目标服务器
原理:
Jenkins 在构建并推送镜像后,通过 SSH、Ansible、Kubernetes API 等工具,主动登录到目标服务器(或集群)执行拉取新镜像、重启容器等操作。
优点:
控制权在 Jenkins,部署时机精确。
适合需要复杂编排的场景(如蓝绿部署、金丝雀发布)。
对目标环境要求低(只需 SSH 或 API 可达)。
缺点:
Jenkins 需要持有目标服务器的访问凭证(密钥、Token)。
目标服务器数量增多时管理成本上升。
2.被动部署(Pull 模式)------ 目标服务器主动检测更新
原理:
Argo CD 作为 Kubernetes 集群内的控制器,持续监听 Git 仓库中声明的期望状态(如 Deployment 的镜像版本)。当 Git 仓库中的配置发生变化(例如 Jenkins 更新了 k8s/deployment.yaml中的镜像标签),Argo CD 会自动检测到差异,并将集群的实际状态拉取(Pull)至与 Git 一致,无需 Jenkins 主动触发部署。
优点:
声明式、可审计:所有部署配置都存储在 Git 中,天然具备版本控制和回滚能力。
自动化程度高:Argo CD 持续对账,一旦 Git 变更,自动同步到集群,无需人工干预。
安全性好:Jenkins 无需持有 Kubernetes 集群的访问凭证,只需有 Git 仓库的写入权限即可。
适合大规模集群:Argo CD 原生支持多集群、多环境,管理成本较低。
缺点:
强依赖 Kubernetes:Argo CD 本身是 Kubernetes 原生应用,必须运行在 Kubernetes 集群内。
部署延迟:同步周期默认为 3 分钟(可通过 Webhook 缩短),无法做到毫秒级响应。
学习曲线:需要理解 GitOps 理念、Application CRD 以及 Argo CD 的配置方式。
不适合非容器化应用:仅适用于 Kubernetes 资源管理,对传统虚拟机或物理机部署无能为力。