基于 Kubernetes 与 GitLab CI/CD 的云原生自动化交付平台

基于 GitLab CI/CD + Harbor + Kubernetes 的自动化部署实战手册

一、 项目概述与前置准备

本项目基于 Rocky Linux 10.1 系统,搭建了一套完整的 DevOps 自动化流水线。实现了开发人员推送代码并打上标签(Tag)后,系统自动完成代码拉取、镜像构建、镜像推送到 Harbor 私有仓库、以及 Kubernetes 集群自动滚动更新的全流程。

1. 环境规划与主机映射

所有节点需配置 /etc/hosts(Windows 端同样需配置 hosts 文件用于访问域名):

bash 复制代码
[root@k8s-master01 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.110.166 k8s-master01 m1
192.168.110.167 k8s-node01 n1
192.168.110.168 k8s-node02 n2
192.168.110.165 hb.reg.com
192.168.110.166 gitlab.test.com

2.节点规划表

主机名 IP地址 操作系统 核心角色 主要部署组件 域名/Hosts映射
k8s-master01 (m1) 192.168.110.166 Rocky Linux 10.1 K8s Master节点 / GitLab服务器 Kubernetes控制平面、Docker、GitLab (容器)、项目源码目录 192.168.110.166 gitlab.test.com 192.168.110.166 k8s-master01 m1
k8s-node01 (n1) 192.168.110.167 Rocky Linux 10.1 K8s Node1 / GitLab Runner节点 Kubernetes工作节点、Docker、GitLab Runner (容器)、kubectl二进制文件 (/opt/kubectl-v1.36.3) 192.168.110.167 k8s-node01 n1
k8s-node02 (n2) 192.168.110.168 Rocky Linux 10.1 K8s Node2 Kubernetes工作节点、Docker 192.168.110.168 k8s-node02 n2
harbor 192.168.110.165 redhat Linux 10.1 Harbor 镜像仓库 Harbor (容器),提供私有镜像的存储与分发 192.168.110.165 hb.reg.com

3. 环境初始化

基础环境要求:关闭防火墙、SELinux,配置好时间同步,并确保所有节点可以正常通信。

4.harbor前置部署

参考上篇部署方案,在docker配置源中新增

复制代码
cat > /etc/docker/daemon.json << EOF
{
  "insecure-registries": ["hb.reg.com"]
}
EOF
systemctl daemon-reload
systemctl restart docker

解决ssl证书报错


二、 核心组件部署与配置

1. GitLab 部署(Master节点)

采用 Docker 容器方式部署,配置文件挂载到宿主机 /root/xm 目录下:

bash 复制代码
[root@k8s-master01 xm]# mkdir -p config logs data
[root@k8s-master01 xm]# docker run --name gitlab \
  --hostname gitlab.test.com \
  --env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.test.com'" \
  --publish 443:443 --publish 80:80 --publish 8022:22 \
  --restart always \
  --volume /root/xm/config:/etc/gitlab \
  --volume /root/xm/logs:/var/log/gitlab \
  --volume /root/xm/data:/var/opt/gitlab \
  --shm-size 256m \
  hb.reg.com/k8s/gitlab-ce:17.0.0-ce.0

修改 GitLab 核心配置(优化资源占用并配置 SSH)

进入 /root/xm/config/gitlab.rb 修改:

bash 复制代码
external_url 'http://gitlab.test.com'                                      # 外部访问地址
gitlab_rails['gitlab_ssh_host'] = '192.168.110.166'                        # 宿主机真实IP
gitlab_rails['gitlab_shell_ssh_port'] = 8022                               # SSH 访问端口
gitlab_rails['time_zone'] = 'Asia/Shanghai'                                # 设置时区为上海
prometheus['enable'] = false                                               # 关闭 Prometheus 监控
alertmanager['enable'] = false                                             # 关闭 Alertmanager 告警
gitlab_rails['gitlab_email_enabled'] = false                               # 关闭邮件服务
postgresql['shared_buffers'] = "128MB"                                     # 减少数据库缓存
postgresql['max_connections'] = 200                                        # 减少数据库并发连接数
nginx['worker_processes'] = 2                                              # 减少 nginx 工作进程数

执行 gitlab-ctl reconfigure 使其生效。初始密码存储在 /root/xm/config/initial_root_password 中。

2. Harbor 仓库准备(独立服务器)

在 Harbor 网页端(http://hb.reg.com)提前创建项目名为 cicd 的私有项目,并创建具备推送权限的账号(推荐创建机器人账号,本例使用 admin / hb123456 便于测试)。

3. GitLab Runner 部署(Node1节点)

关键前提:由于使用的是 Docker Executor,为了让容器内可以调用宿主机的 Docker 引擎以及 Kubernetes API,必须在启动时挂载宿主机资源。

Node01 宿主机制作 kubectl 二进制(解决容器内找不到 kubectl 的问题)

bash 复制代码
curl -LO "https://dl.k8s.io/release/v1.36.3/bin/linux/amd64/kubectl"
mv kubectl kubectl-v1.36.3
chmod +x kubectl-v1.36.3
mv kubectl-v1.36.3 /opt/
/opt/kubectl-v1.36.3 version --client

启动 Runner 容器

bash 复制代码
docker run --name gitlab-runner -itd \
  -v /root/gitlab-runner/gitlab-runner:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /etc/hosts:/etc/hosts \
  -v /opt/kubectl-v1.36.3:/usr/local/bin/kubectl:ro \
  --restart always hb.reg.com/k8s/gitlab-runner:v17.0.0

(注:利用 -v 将宿主机的 kubectl 二进制文件以只读方式挂载到容器内,这是解决 Docker 执行器无法使用宿主机 kubectl 的标准方案之一)

进入容器注册 Runner

bash 复制代码
docker exec -it gitlab-runner bash
gitlab-runner register --non-interactive --executor "docker" --docker-image docker:latest --url "http://192.168.110.166" --token "glrt-Es2N__FFwnZjCdgLKr7o"

此处token在gitlab中获取令牌

GitLab 权限和 Token 的生成步骤(解决 403报错 的细节)

  • 登录 GitLab,点击头像 -> 偏好设置 -> 访问令牌。
  • 勾选 apiwrite_repository 权限。
  • 复制生成的 glpat- 开头的 Token。
  • 重要 :去 nginx-demo 项目 -> 设置 -> 仓库 -> 受保护分支,将 main 分支"解除保护"或者将"允许推送"改为"允许所有人",否则会报 403 错误。

修改宿主机 config.toml 挂载透传/root/gitlab-runner/gitlab-runner/config.toml):

复制代码
[root@k8s-node01 gitlab-runner]# cat config.toml 
concurrent = 10 # 并行执行作业数
check_interval = 0
connection_max_age = "15m0s"
shutdown_timeout = 0

[session_server]
session_timeout = 1800

[[runners]]
name = "client2" # Runner 名称
url = "http://192.168.110.166" # 你的 GitLab IP
id = 1 # ID可以随便写,GitLab 会自动覆盖
token = "glrt-Es2N__FFwnZjCdgLKr7o" # 你的 GitLab 注册 Token
token_obtained_at = 2026-09-02T00:00:00Z
token_expires_at = 0001-01-01T00:00:00Z
executor = "docker"

[runners.cache]
MaxUploadedArchiveSize = 0

[runners.docker]
pull_policy = "if-not-present" # 配置镜像拉取策略
tls_verify = false
image = "docker:latest" # 配置默认镜像(因为你用的是 docker executor,用这个)
privileged = true
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock", "/etc/hosts:/etc/hosts", "/opt/kubectl-v1.36.3:/usr/local/bin/kubectl:ro"]

shm_size = 0
network_mtu = 0

4. Kubernetes 集群准备(Master节点执行)

提前在 K8s 中创建命名空间,并创建用于拉取 Harbor 私有镜像的 Secret:

bash 复制代码
kubectl create ns cicd
kubectl create secret docker-registry regcred \
  --namespace=cicd \
  --docker-server=hb.reg.com \
  --docker-username=admin \
  --docker-password=hb123456

三、 项目代码与核心配置文件

在 Master 节点的 /root/xm/deploy/ 目录下创建项目:

bash 复制代码
[root@k8s-master01 deploy]# ls -la
deployment.yaml  Dockerfile  index.html  .dockerignore  .gitlab-ci.yml

1. 业务代码与打包文件

Dockerfile

复制代码
FROM hb.reg.com/k8s/nginx:1.27.4
COPY . /usr/share/nginx/html/

(注:直接使用内网私有仓库固定版本,避免拉取外网超时)

index.html

复制代码
hello,CICD

.dockerignore (排除不需要打进镜像的文件):

复制代码
Dockerfile
deployment.yaml
.git
.gitlab-ci.yml

2. Kubernetes 部署清单 deployment.yaml

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-demo
  namespace: cicd
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-demo
  template:
    metadata:
      labels:
        app: nginx-demo
    spec:
      imagePullSecrets:
        - name: regcred
      containers:
      - name: nginx-demo
        image: hb.reg.com/k8s/nginx:1.27.4
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-demo-svc
  namespace: cicd
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
  selector:
    app: nginx-demo

3. 流水线脚本 .gitlab-ci.yml

利用挂载的宿主机 Docker Socket 和 kubectl,无需额外在容器内安装工具,防止构建卡死。

复制代码
stages:
  - build
  - push
  - deploy

variables:
  IMAGE_NAME: hb.reg.com/cicd/nginx-demo:$CI_COMMIT_SHORT_SHA

build:
  stage: build
  image: docker:latest
  script:
    - docker build -t $IMAGE_NAME .

push:
  stage: push
  image: docker:latest
  script:
    - docker login hb.reg.com -u admin -p hb123456
    - docker push $IMAGE_NAME

deploy:
  stage: deploy
  image: docker:latest
  before_script:
    - apk add --no-cache curl
  script:
    - kubectl apply -f deployment.yaml
    - kubectl set image deployment/nginx-demo nginx-demo=$IMAGE_NAME -n cicd
    - kubectl rollout status deployment/nginx-demo -n cicd --timeout=120s

四、 一键发布流程测试

在项目目录下执行(推荐在 Master 节点操作,已配置好连接):

复制代码
git init --initial-branch=main
git remote add origin http://root:你的Token@gitlab.test.com/root/nginx-demo.git
git add .
git commit -m "项目初始提交"
git tag -a v1.0 -m "版本1.0"
git push origin main
git push origin v1.0

验证结果

  1. 进入 GitLab 网页查看 Build -> Pipelines,流水线三个 Job 依次变绿。
  2. 验证 K8s 状态:kubectl get pods -n cicdkubectl get svc -n cicd
  3. 在浏览器访问 http://192.168.110.166:30080 即可看到页面。

五、 项目亮点与踩坑总结

1. 纯内网环境下的镜像与版本管理策略
  • 踩坑过程 :初期使用 FROM nginx:latest 等默认外网镜像时,CI/CD 流水线在"拉取镜像"环节频繁超时卡死。经排查发现,生产环境/实验环境为纯内网隔离,无法访问公网 Docker Hub。
  • 解决方案 :将构建所需的所有基础镜像(Nginx、Docker CLI 等)和工具链镜像(Kubectl)提前拉取,打上固定版本标签(如 nginx:1.27.4),推送到内网 Harbor 私有仓库(hb.reg.com/cicdhb.reg.com/k8s),并在代码中严格锁定版本号。彻底消灭了 latest 标签带来的不确定性(版本漂移风险)和对外网的强依赖。
  • 工程亮点:体现了"不可变基础设施"的思维,保证了构建环境的高稳定性与可重复性。
2. 解决 Docker Executor 与宿主机的深度隔离问题(核心难点)
  • 踩坑过程 :使用 Docker Executor 运行流水线时,CI 作业是在一个"临时新建的隔离容器"内执行的。日志报错 kubectl: not found,且执行 docker build 也报错。虽然宿主机安装了 kubectl 和 Docker,但临时容器内却无法调用。
  • 解决方案 :利用 Docker 的卷挂载原理,在启动 Runner 容器时,将宿主机的核心资源"透传"给容器:
    1. 挂载 /var/run/docker.sock,使容器内的 docker 命令直接调用宿主机的 Docker 引擎,实现"套娃"式的构建;
    2. 将宿主机上预下载的 kubectl 二进制文件(/opt/kubectl-v1.36.3)以只读方式挂载到临时容器(-v /opt/kubectl-v1.36.3:/usr/local/bin/kubectl:ro),使其具备操作 K8s 集群的能力。
  • 工程亮点:这一方案完美复刻了真实生产环境中"CI 环境与宿主机解耦"又"复用宿主机资源"的平衡艺术。避免了为每个任务准备臃肿的"全家桶"镜像,显著提高了流水线的执行速度与资源利用率。
3. 跨主机的权限与安全控制(GitLab 403 拦路虎)
  • 踩坑过程 :在 Linux 宿主机执行 git push 时,反复报出 403 ForbiddenAuthentication failed。网页端使用管理员密码登录正常,但命令行始终被拒绝。
  • 解决方案 :经过深入排查,确认新版 GitLab 严格限制默认密码通过 HTTP 协议推送代码。因此在 GitLab 网页端重新生成了具备 apiwrite_repository 权限的 Personal Access Token(个人访问令牌) ,并将其写入远程仓库的 URL 中。同时,去"设置->仓库->受保护分支"中将 main 分支解除了保护,或者将项目可见性设为公开,最终彻底解决权限拦截。
  • 工程亮点:掌握了生产级 GitLab 的安全认证机制,理解了"网页登录"与"命令行令牌认证"的区别,避免了明文密码泄露的隐患。
4. Kubernetes 私有仓库认证与无状态应用滚动部署
  • 踩坑过程 :K8s 默认无法直接拉取 Harbor 私有仓库中的镜像,部署时报 ImagePullBackOff 错误。
  • 解决方案 :在 K8s 的 cicd 命名空间中预先创建了 docker-registry 类型的 Secret(regcred),并在 Deployment 的 spec.template.spec 下通过 imagePullSecrets 字段进行引用。同时,利用 kubectl set image 配合 kubectl rollout status,实现了零停机的滚动更新。
  • 工程亮点:将 CI 流水线的产出(镜像)与 CD 的入口(K8s 拉取)完美闭环。不仅解决了私有镜像认证问题,还掌握了服务发布的"优雅上线"机制。
5. 解决内网 Harbor 信任证书问题
  • 踩坑过程 :由于 Harbor 未配置 HTTPS 证书,Docker 及 K8s 在尝试连接 hb.reg.com 时,经常报 x509: certificate signed by unknown authority 或连接被拒绝。
  • 解决方案 :在所有节点的 /etc/docker/daemon.json 中配置了 "insecure-registries": ["hb.reg.com"] 并重启 Docker,强制其信任内网 HTTP 协议仓库。
  • 工程亮点:深入理解了 HTTPS 加密与内网 HTTP 环境的共存方案,熟悉了常见基础组件的"信任链"配置方法。
相关推荐
greatofdream1 小时前
TorchInductor 完整原理与架构教程
架构
脚踏实地,坚持不懈!1 小时前
Android 系统工程师(性能/功耗/稳定性)岗位问题深度解析:从内核源码到实战排查(完善版)
android·linux·运维·服务器
0+1111 小时前
Linux --进程信号
linux·运维·服务器
Lethehong2 小时前
服务器越来越多怎么统一监控?Beszel 接入 Agent、SMTP 告警与公网访问
运维·服务器
gs801402 小时前
Docker Desktop 报 Wsl/CommandTimedOut、wsl -l -v 卡死、0x80080005 的完整排查与解决
运维·docker·容器
MindUp2 小时前
AI算命背后的技术逻辑:从Prompt设计到排盘引擎的三款产品实测对比
人工智能·架构
风123456789~2 小时前
【架构专栏】第6章 数据库设计基础知识 4/4
数据库·架构
其实防守也摸鱼2 小时前
CVE / NVD 漏洞数据库详解:从入门到实战
大数据·运维·人工智能·web安全·自动化
分布式存储与RustFS2 小时前
把 RustFS 当 ClickHouse 的 S3 存储盘:冷热分层落对象存储
云原生·开源·对象存储·分布式存储·s3·rustfs·性能基准