基于 CI/CD 平台将应用程序自动部署到 Kubernetes 集群

序:核心背景与需求

在现代软件开发与运维体系中,基于 CI/CD 平台将应用程序自动部署到 Kubernetes 集群的项目,源于以下核心背景与需求:

1. 技术与行业趋势驱动

  • 容器化普及:Docker 等容器技术实现了应用与运行环境的 "封装一致",解决了 "开发环境能跑,生产环境报错" 的历史难题,成为应用交付的标准载体。
  • Kubernetes 主导编排:随着应用向分布式、微服务架构演进,Kubernetes 凭借对容器的自动化编排能力(部署、扩缩容、自愈等),成为大规模容器化应用的 "标配管理平台"。
  • CI/CD 需求升级 :市场对软件迭代速度、交付稳定性的要求急剧提升,传统 "手动编译→打包→上传→部署" 的流程效率低下、易出错,必须通过 ** 持续集成(CI,代码提交后自动编译、测试)持续部署(CD,测试通过后自动发布到生产环境)** 实现全链路自动化。

2. 企业面临的核心痛点

  • 环境一致性问题:开发、测试、生产环境的依赖、配置差异,导致应用部署后行为不一致,排查成本高。
  • 手动运维效率低下:应用容器化后,若仍依赖人工操作部署到 Kubernetes,在集群规模扩大(如百级、千级容器)时,运维成本呈指数级增长,且无法保障部署时效性与准确性。
  • 迭代周期长:从代码开发到用户可用的链路冗长,需经历 "提交→等待人工编译→等待人工测试→等待人工部署" 等环节,难以快速响应业务需求。

3. 项目核心目标

通过搭建 CI/CD 自动化流水线,实现从 "代码提交" 到 "Kubernetes 集群部署" 的全流程自动化:

  • 开发者提交代码后,自动触发编译、单元测试、镜像构建
  • 镜像自动推送到私有镜像仓库(如内网 Registry);
  • 最终自动将应用部署到 Kubernetes 集群,并完成服务暴露、健康检查等运维配置,实现 "代码提交即部署,快速验证且稳定交付"。

4. 技术栈协同逻辑

项目需整合多类技术形成闭环:

  • 版本控制(Git):作为代码仓库,触发 CI/CD 流程的 "源头";
  • CI/CD 平台(Jenkins/GitLab CI 等):作为 "中枢",编排 "编译→测试→镜像构建→部署" 的自动化步骤;
  • 容器化(Docker):封装应用与依赖,生成标准镜像;
  • 私有镜像仓库:存储构建好的镜像,供 Kubernetes 拉取;
  • Kubernetes:作为 "运行时平台",负责应用容器的调度、扩缩容与高可用管理。

简言之,该项目是容器化、编排技术与自动化交付流程的结合,旨在解决传统部署的效率与稳定性问题,支撑企业快速迭代、规模化运维的需求。

一、基础环境配置(所有节点)

1. 域名解析(/etc/hosts

在涉及的主机(如 Jenkins 主机 host1、K8s 节点 host2、GitLab 服务器)上配置内网域名解析,方便服务间访问:

bash 复制代码
[root@host1 ~]# sudo vi /etc/hosts
[root@host1 ~]# sudo cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.197.91 registry.abc.com
192.168.197.9 jenkins-host    # Jenkins 所在主机 IP
192.168.197.9 gitlab.abc.com  # GitLab 服务器 IP
192.168.197.91 k8s.abc.com     # K8s 集群节点(host2)IP

2. Docker 配置国内镜像源(加速镜像拉取)

host1(Jenkins 主机,需构建 Docker 镜像)和 host2(K8s 节点,需拉取镜像)上,修改 Docker 守护进程配置:

bash 复制代码
[root@host1 ~]# vi /etc/docker/daemon.json
[root@host1 ~]# cat /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://9fbd5949cbc94e4a9581e33b9077c811.mirror.swr.myhuaweicloud.com",
    "https://jnh8ca4k.mirror.aliyuncs.com",
    "https://mirror.ccs.tencentyun.com",
    "https://registry.docker-cn.com"
  ],
  "insecure-registries": [
    "192.168.197.9:5000",
    "registry.abc.com:5000"
  ],
  "live-restore": true
}
[root@host1 ~]# sudo systemctl daemon-reload
[root@host1 ~]# sudo systemctl restart docker

host2 上同步操作:

bash 复制代码
[root@host2 ~]# sudo vi /etc/hosts
[root@host2 ~]# sudo cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.197.91 registry.abc.com
192.168.197.9 jenkins-host    # Jenkins 所在主机 IP
192.168.197.9 gitlab.abc.com  # GitLab 服务器 IP
192.168.197.91 k8s.abc.com     # K8s 集群节点(host2)IP
[root@host2 ~]# sudo vi /etc/docker/daemon.json
[root@host2 ~]# sudo cat /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://9fbd5949cbc94e4a9581e33b9077c811.mirror.swr.myhuaweicloud.com",
    "https://jnh8ca4k.mirror.aliyuncs.com",
    "https://mirror.ccs.tencentyun.com",
    "https://registry.docker-cn.com"
  ],
  "insecure-registries": [
    "192.168.197.9:5000",
    "registry.abc.com:5000"
  ],
  "live-restore": true
}
[root@host2 ~]# sudo systemctl daemon-reload
[root@host2 ~]# sudo systemctl restart docker
[root@host2 ~]# sudo docker run -d --restart=always -p 5000:5000 --name registry registry:2
Unable to find image 'registry:2' locally
2: Pulling from library/registry
44cf07d57ee4: Pull complete 
bbbdd6c6894b: Pull complete 
8e82f80af0de: Pull complete 
3493bf46cdec: Pull complete 
6d464ea18732: Pull complete 
Digest: sha256:a3d8aaa63ed8681a604f1dea0aa03f100d5895b6a58ace528858a7b332415373
Status: Downloaded newer image for registry:2
27101e645df048203dba9a00fb36f55a84328dd6d2297af327c091be15702dc0
[root@host2 ~]# sudo firewall-cmd --add-port=5000/tcp --permanent
FirewallD is not running
[root@host2 ~]# curl http://registry.abc.com:5000/v2/
{}

二、GitLab 代码仓库准备

1. 创建项目(之前有跳过)

在 GitLab 中创建名为 k8s-demo 的空白项目(若无 GitLab,可临时用本地仓库,生产建议用 GitLab/Gitee)。

bash 复制代码
[root@host1 ~]# mkdir k8s-demo && cd k8s-demo
[root@host1 k8s-demo]# git init
bash: git: 未找到命令...
安装软件包"git-core"以提供命令"git"? [N/y] y


 * 正在队列中等待... 
 * 正在载入软件包列表。... 
下列软件包必须安装:
 git-core-2.47.3-1.el9.x86_64Core package of git with minimal functionality
继续更改? [N/y] y


 * 正在队列中等待... 
 * 正在等待认证... 
 * 正在队列中等待... 
 * 正在下载软件包... 
 * 正在请求数据... 
 * 正在测试更改... 
 * 正在安装软件包... 
提示: 使用 'master' 作为初始分支的名称。这个默认分支名称可能会更改。要在新仓库中
提示: 配置使用初始分支名,并消除这条警告,请执行:
提示:
提示: git config --global init.defaultBranch <名称>
提示:
提示: 除了 'master' 之外,通常选定的名字有 'main'、'trunk' 和 'development'。
提示: 可以通过以下命令重命名刚创建的分支:
提示:
提示: git branch -m <name>
已初始化空的 Git 仓库于 /root/k8s-demo/.git/

[root@host1 k8s-demo]# git init
已重新初始化已存在的 Git 仓库于 /root/k8s-demo/.git/
[root@host1 k8s-demo]# touch spring-boot-hello.yaml

2. 本地初始化代码(host1 操作)

克隆仓库并添加项目文件:

bash 复制代码
# 切换到项目目录(如 ~/ch08)
cd ~/ch08
# 克隆 GitLab 仓库(替换为实际 GitLab 地址)
git clone ssh://git@gitlab.abc.com:2222/root/k8s-demo.git
cd k8s-demo

3. 添加核心文件

(1)Dockerfile(构建 Spring Boot 镜像)

bash 复制代码
FROM openjdk:11-jre-slim  # 基于国内镜像源加速拉取的 OpenJDK 镜像
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

(2)pom.xml(Maven 构建配置)

bash 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.5</version>
    <relativePath/>
  </parent>
  <groupId>com.abc</groupId>
  <artifactId>spring-boot-hello</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>spring-boot-hello</name>
  <properties>
    <java.version>11</java.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

(3)src/main/java/com/abc/hello/HelloController.java(Spring Boot 控制器)

bash 复制代码
package com.abc.hello;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @GetMapping("/")
    public String hello() {
        return "Hello! Please test K8S CI/CD!";
    }
}

(4)src/main/java/com/abc/hello/SpringBootHelloApplication.java(启动类)

bash 复制代码
package com.abc.hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootHelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootHelloApplication.class, args);
    }
}

(5)kube.yaml(Kubernetes 资源配置)

bash 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sbdemo-deploy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: spring-boot-hello
  template:
    metadata:
      labels:
        app: spring-boot-hello
    spec:
      containers:
      - name: spring-boot-hello
        image: registry.abc.com:5000/spring-boot-hello  # 内网私有仓库地址
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: sbdemo-svc
spec:
  type: NodePort
  selector:
    app: spring-boot-hello
  ports:
  - port: 8080
    targetPort: 8080
    nodePort: 30008  # 暴露到主机的端口(30000-32767 区间)

(6)Jenkinsfile(Jenkins 流水线脚本)

bash 复制代码
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                // Maven 构建(跳过测试加速)
                sh 'mvn -B -DskipTests clean package'
                // 构建 Docker 镜像(关联 Maven 打包的 JAR)
                sh 'docker build --build-arg JAR_FILE=target/spring-boot-hello-0.0.1-SNAPSHOT.jar -t registry.abc.com:5000/spring-boot-hello .'
                // 推送镜像到内网私有仓库
                sh 'docker push registry.abc.com:5000/spring-boot-hello'
            }
        }
        stage('Deploy') {
            steps {
                // 通过 SSH 传输配置文件并部署到 K8s 集群
                sshPublisher(publishers: [
                    sshPublisherDesc(
                        configName: 'K8SHost',  // Jenkins 中配置的 SSH 服务器名
                        transfers: [
                            sshTransfer(
                                cleanRemote: false,
                                excludes: '',
                                execCommand: 'cd ~/spring-boot-hello && kubectl delete -f kube.yaml || true && kubectl apply -f kube.yaml',
                                execTimeout: 120000,
                                flatten: false,
                                makeEmptyDirs: false,
                                noDefaultExcludes: false,
                                patternSeparator: '[, ]+',
                                remoteDirectory: 'spring-boot-hello',
                                remoteDirectorySDF: false,
                                removePrefix: '',
                                sourceFiles: '**/kube.yaml'
                            )
                        ],
                        usePromotionTimestamp: false,
                        useWorkspaceInPromotion: false,
                        verbose: false
                    )
                ])
            }
        }
    }
}

三、Jenkins 配置

1. 安装必要插件

进入 Jenkins → Manage Jenkins → Manage Plugins,安装:

  • Maven Integration(Maven 项目支持)
  • Publish Over SSH(SSH 远程部署支持)

2. 系统配置(Manage Jenkins → Configure System

(1)Maven 配置

  • 找到 Maven 区域,点击 "Add Maven",命名为 Maven-3.8.6(可选择 "Install automatically" 或指定本地 Maven 路径)。

(2)Publish Over SSH 配置

  • 找到 Publish Over SSH 区域,点击 "Add → SSH Server":
    • NameK8SHost(与 JenkinsfileconfigName 一致)。
    • Hostnamek8s.abc.com(K8s 节点 host2 的主机名 / IP)。
    • Usernameroot(或 K8s 节点上有权限的用户)。
    • 点击 "Advanced",勾选 "Use password authentication, or use a different key",填写 K8s 节点的 SSH 密码
    • Remote Directory~(或指定部署目录,如 /root)。
    • 点击 "Test Configuration",显示 Success 则配置正常。

四、推送代码到 GitLab

将所有文件提交并推送到 GitLab 仓库:

bash 复制代码
cd ~/ch08/k8s-demo
git add .
git commit -m "Init K8s CI/CD project with domestic mirrors"
git push origin main

五、Jenkins 创建流水线项目

1. 新建任务

  • 进入 Jenkins → New Item ,选择 "Pipeline",命名为 k8s-demo → 点击 "OK"。

2. 配置流水线

  • Pipeline → Definition :选择 Pipeline script from SCM
  • SCM :选择 Git
  • Repository URL :填写 GitLab 仓库地址(如 ssh://git@gitlab.abc.com:2222/root/k8s-demo.git)。
  • Branch Specifier*/main(或 */master,根据仓库分支名调整)。
  • Script PathJenkinsfile(与项目中文件名一致)。

3. 执行构建

点击 "Save" 保存配置,然后点击 "Build Now" 启动流水线。

六、验证部署(K8s 节点 host2 操作)

1. 查看 Kubernetes 资源

bash 复制代码
kubectl get deployments  # 应看到 sbdemo-deploy(READY 1/1)
kubectl get pods         # 应看到 sbdemo-deploy-xxx(STATUS Running)
kubectl get services     # 应看到 sbdemo-svc(TYPE NodePort,PORT(S) 8080:30008/TCP)

2. 访问服务

bash 复制代码
curl 127.0.0.1:30008
# 预期输出:Hello! Please test K8S CI/CD!

关键说明

  • 国内镜像源 :通过 daemon.jsonregistry-mirrors 加速 OpenJDK、Maven 等基础镜像的拉取,避免依赖 Docker Hub 外网速度。

  • 私有仓库registry.abc.com:5000 需确保是 HTTP 服务,且 Docker 配置了 insecure-registries,否则镜像推送 / 拉取会失败。

  • SSH 部署 :Jenkins 需能通过 SSH 免密(或密码)登录 K8s 节点,并具备 kubectl 操作权限。

  • 动态调整:需根据实际环境修改域名、IP、仓库地址、K8s 资源配置(如 NodePort 端口、镜像名)。

通过以上步骤,即可实现基于国内镜像源的从代码提交到 Kubernetes 部署的全流程自动化 CI/CD。

代码部分过程:

bash 复制代码
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Mon Sep 29 17:54:00 2025 from 192.168.197.1
[root@host1 ~]# sudo vi /etc/hosts
[root@host1 ~]# sudo cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.197.91 registry.abc.com
192.168.197.9 jenkins-host    # Jenkins 所在主机 IP
192.168.197.9 gitlab.abc.com  # GitLab 服务器 IP
192.168.197.91 k8s.abc.com     # K8s 集群节点(host2)IP
[root@host1 ~]# vi /etc/docker/daemon.json
[root@host1 ~]# cat /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://9fbd5949cbc94e4a9581e33b9077c811.mirror.swr.myhuaweicloud.com",
    "https://jnh8ca4k.mirror.aliyuncs.com",
    "https://mirror.ccs.tencentyun.com",
    "https://registry.docker-cn.com"
  ],
  "insecure-registries": [
    "192.168.197.9:5000",
    "registry.abc.com:5000"
  ],
  "live-restore": true
}
[root@host1 ~]# sudo systemctl daemon-reload
[root@host1 ~]# sudo systemctl restart docker
[root@host1 ~]# cd ~/ch08
[root@host1 ch08]# cd
[root@host1 ~]# 
[root@host1 ~]# 
[root@host1 ~]# mkdir k8s-demo && cd k8s-demo
[root@host1 k8s-demo]# git init
bash: git: 未找到命令...
安装软件包"git-core"以提供命令"git"? [N/y] y


 * 正在队列中等待... 
 * 正在载入软件包列表。... 
下列软件包必须安装:
 git-core-2.47.3-1.el9.x86_64Core package of git with minimal functionality
继续更改? [N/y] y


 * 正在队列中等待... 
 * 正在等待认证... 
 * 正在队列中等待... 
 * 正在下载软件包... 
 * 正在请求数据... 
 * 正在测试更改... 
 * 正在安装软件包... 
提示: 使用 'master' 作为初始分支的名称。这个默认分支名称可能会更改。要在新仓库中
提示: 配置使用初始分支名,并消除这条警告,请执行:
提示:
提示: git config --global init.defaultBranch <名称>
提示:
提示: 除了 'master' 之外,通常选定的名字有 'main'、'trunk' 和 'development'。
提示: 可以通过以下命令重命名刚创建的分支:
提示:
提示: git branch -m <name>
已初始化空的 Git 仓库于 /root/k8s-demo/.git/

[root@host1 k8s-demo]# git init
已重新初始化已存在的 Git 仓库于 /root/k8s-demo/.git/
[root@host1 k8s-demo]# touch spring-boot-hello.yaml
[root@host1 k8s-demo]# git add .
[root@host1 k8s-demo]# git commit -m "Initial commit"
作者身份未知

*** 请告诉我您是谁。

运行

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

来设置您账号的缺省身份标识。
如果仅在本仓库设置身份标识,则省略 --global 参数。

致命错误:无法自动探测邮件地址(得到 'root@host1.(none)')
[root@host1 k8s-demo]# 
[root@host1 k8s-demo]# 
[root@host1 k8s-demo]# cd ~/ch08
[root@host1 ch08]# git clone ssh://git@192.168.197.9.com:2222/root/k8s-demo.git
正克隆到 'k8s-demo'...
ssh: Could not resolve hostname 192.168.197.9.com: Name or service not known
致命错误:无法读取远程仓库。

请确认您有正确的访问权限并且仓库存在。
[root@host1 ch08]# 
[root@host1 ch08]# cd
[root@host1 ~]# git config --global user.name "root"
[root@host1 ~]# git config --global user.email "root@host1.local"
[root@host1 ~]# git config --list | grep user
user.name=root
user.email=root@host1.local
[root@host1 ~]# cd ~/k8s-demo
[root@host1 k8s-demo]# git add .  
[root@host1 k8s-demo]# git commit -m "Initial commit: Add spring-boot-hello.yaml" 
[master(根提交) 00e64fa] Initial commit: Add spring-boot-hello.yaml
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 spring-boot-hello.yaml
[root@host1 k8s-demo]# 
相关推荐
你的人类朋友2 小时前
DevOps是什么?
运维·自动化运维·devops
Terio_my2 小时前
Elasticsearch 索引创建与文档管理
大数据·elasticsearch·jenkins
励志不掉头发的内向程序员3 小时前
【Linux系列】并发世界的基石:透彻理解 Linux 进程 — 进程概念
linux·运维·服务器·开发语言·学习
Tadas-Gao4 小时前
微服务可观测性的“1-3-5”理想:从理论到实践的故障恢复体系
java·开发语言·微服务·云原生·架构·系统架构·可观测
早起的年轻人4 小时前
CentOS 8系统盘大文件查找方法
linux·运维·centos
心灵宝贝4 小时前
Linux CentOS 7 安装 zip-3.0-11.el7.x86_64.rpm 详细步骤(命令行教程)(附安装包)
linux·运维·centos
Thexhy5 小时前
在Centos的Linux中安装Windows10系统
linux·运维·经验分享·学习·centos
CIb0la5 小时前
开源安全工具推荐:afrog- 主要用于 Bug Bounty、Pentest 和 Red Teaming 的安全工具
运维·安全
zimoyin6 小时前
Linux 程序使用 STDOUT 打印日志导致程序“假死”?一次线上 Bug 的深度排查与解决
linux·运维·bug