项目描述:
使用 kubeadm 搭建多节点 Kubernetes 集群(1 Master + 2 Worker,RHEL 9 虚拟机),将前端(Nginx)与后端(Spring Boot)应用容器化并推送至 Harbor 私有镜像仓库,集群节点统一从 Harbor 拉取镜像运行;使用 Deployment 管理前后端 Pod 的副本与滚动更新;使用 Service 实现集群内部服务发现与负载均衡;使用 Ingress 提供统一域名入口对外访问;在此基础上拓展 Jenkins Pipeline 实现 CI/CD 自动化发布,打通"代码 → 构建 → 入库 → 部署"全流程闭环。
架构图:

技术栈:
- 容器与镜像:Docker、containerd 2.x、Harbor 私有镜像仓库
- 容器编排:Kubernetes(kubeadm 多节点集群、Deployment、Service、Ingress-Nginx、Readiness 探针、滚动更新)
- CI/CD:Jenkins Pipeline(参数化构建、凭据管理)、Buildah、Git
- 应用层:Spring Boot(Java 17 + Maven)、Nginx 前端静态资源
- 系统与网络:RHEL 9、VMware 虚拟化网络、iptables、tcpdump 抓包
操作步骤:
| docker-master | 172.25.254.50 | K8s 控制节点 | containerd、kubeadm、kubectl、kubelet、harbor仓库、Jenkins |
| docker-worker1 | 172.25.254.60 | K8s 工作节点 | containerd、kubelet |
| docker-worker2 | 172.25.254.70 | K8s 工作节点 | containerd、kubelet |
阶段 1:三台机器统一基础环境初始化(所有节点执行)
1.1 主机域名解析 /etc/hosts
解释:配置本地域名解析



1.2 内核参数开启容器网络转发
需要加载一下内核模块:modprobe br_netfilter
编辑 /etc/sysctl.conf
解释:K8s 网络必需,开启 iptables 网桥转发、IP 转发,Pod 之间、Pod 访问外网网络互通



1.3 关闭 Swap 分区
bash
解释:Kubernetes 官方强制要求关闭交换分区,内存交换会影响调度与稳定性。
swapoff -a
sed -i '/swap/s/^/#/' /etc/fstab
阶段 2:docker-master、worker01、worker02 安装容器运行时 containerd
2.1 安装 containerd(CentOS Stream9)
解释:containerd 是 K8s 标准容器运行时,替代 docker;systemd cgroup 和宿主机 systemd 统一,避免资源管控异常。
先配置docker源
yum install -y yum-utils #安装依赖
[root@docker-master ~]# cat /etc/yum.repos.d/docker.repo
[docker]
name=Docker
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/$releasever/$basearch/stable
enabled=1
gpgcheck=0
yum install makecache
yum install -y containerd-io
然后生成一份默认配置
containerd config default > /etc/containerd/config.toml
2.2 修改配置 /etc/containerd/config.toml
重点修改两处:
[root@docker-master ~]# sed -i 's#sandbox_image = "registry.k8s.io/pause:3.6"#sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"#' /etc/containerd/config.toml
[root@docker-master ~]# sed -i 's/SystemdCgroup \\= false/SystemdCgroup \\= true/' /etc/containerd/config.toml
[root@docker-master ~]# systemctl daemon-reload
[root@docker-master ~]# systemctl restart containerd.service
注意: 剩下的两个机子同理方法一样
阶段 3:master/worker 节点安装 Kubernetes 组件
新建 yum 源文件 /etc/yum.repos.d/kubernetes.repo
[root@docker-master ~]# cat /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
[root@docker-master ~]#
安装组件:
- kubeadm:集群初始化工具
- kubectl:集群命令行客户端
- kubelet:每个节点上管理 Pod 的代理服务
yum install -y kubeadm-1.28.2 kubectl-1.28.2 kubelet-1.28.2
systmcel enable –now kubelet
systemctl enable –now kubelet
注意: 剩下的两个机子同理方法一样
阶段 4:master 节点初始化 K8s 集群
4.1 执行初始化命令
bash
kubeadm init –apiserver-advertise-address=172.25.254.50 –image-repository registry.aliyuncs.com/google_containers –kubernetes-version v1.28.2 –service-cidr=10.96.0.0/16 –pod-network-cidr=10.244.0.0/16
参数说明:
- apiserver-advertise-address:master 内网通信地址
- image-repository:阿里云镜像源,防止谷歌镜像拉取失败
- service-cidr:集群 Service 虚拟 IP 网段
- pod-network-cidr:Pod 网段,必须和 flannel 配置保持一致
这里执行成功他会生成一个:sha256:a1a113afade129c1c08f68aae3030b098d70681b61848edb7c2f56f911ce7cd2类似这种的一个,下面是我执行完生成的,可以参考看一下,那个太长了,我就截了一部分
rol-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 6.013376 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see –upload-certs
[mark-control-plane] Marking the node docker-master as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node docker-master as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: ot4p5v.zmj33p4ipbe0puep
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxyYour Kubernetes control-plane has initialized successfully!To start using your cluster, you need to run the following as a regular user:mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown \\((id -u):\\)(id -g) $HOME/.kube/configAlternatively, if you are the root user, you can run:export KUBECONFIG=/etc/kubernetes/admin.confYou should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/Then you can join any number of worker nodes by running the following on each as root:kubeadm join 172.25.254.50:6443 –token ot4p5v.zmj33p4ipbe0puep
–discovery-token-ca-cert-hash sha256:a1a113afade129c1c08f68aae3030b098d70681b61848edb7c2f56f911ce7cd2
注意:上面的集群初始化只有master机子上执行
worker节点加入集群:
[root@docker-worker1 ~]# kubeadm join 172.25.254.50:6443 –token ot4p5v.zmj33p4ipbe0puep –discovery-token-ca-cert-hash sha256:a1a113afade129c1c08f68aae3030b098d70681b61848edb7c2f56f911ce7cd2
[root@docker-worker2 ~]# kubeadm join 172.25.254.50:6443 –token ot4p5v.zmj33p4ipbe0puep –discovery-token-ca-cert-hash sha256:a1a113afade129c1c08f68aae3030b098d70681b61848edb7c2f56f911ce7cd2
4.2 配置 kubectl 权限
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
4.3 部署 Flannel 网络插件
wget https://github.com/flannel-io/flannel/releases/download/v0.28.9/kube-flannel.yml
sed -i 's#ghcr.io/flannel-io/flannel#ghcr.nju.edu.cn/flannel-io/flannel#' kube-flannel.yml
sed -i 's#ghcr.io/flannel-io/flannel-cni-plugin#ghcr.nju.edu.cn/flannel-io/flannel-cni-plugin#' kube-flannel.yml
kubectl apply -f kube-flannel.yml
[root@docker-master ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
docker-master Ready control-plane 3d22h v1.28.2
docker-worker1 Ready <none> 3d21h v1.28.2
docker-worker2 Ready <none> 3d21h v1.28.2
[root@docker-master ~]# kubectl get pods -n kube-flannel
NAME READY STATUS RESTARTS AGE
kube-flannel-ds-cvkn4 1/1 Running 0 42h
kube-flannel-ds-s94ng 1/1 Running 0 42h
kube-flannel-ds-v8pgd 1/1 Running 0 42h
[root@docker-master ~]#
状态全部 Ready 代表集群就绪。参考一下这样就代表集群部署完成了

阶段 5:devops-master(172.25.254.50)部署 Harbor 私有镜像仓库
5.1 安装 docker、docker-compose
前面已经装过docker源了这里就直接安装包就好了
yum install -y docker-ce docker-ce-cli docker-compose-plugin.x86_64
# 服务端 客户端 dockercompose的安装包
systemctl enable –now docker.service # 开机自启
5.2 下载 Harbor包
mkdir /opt/harbor
cd /opt/harbor/
wget https://gh.llkk.cc/https://github.com/goharbor/harbor/releases/download/v2.8.0/harbor-offline-installer-v2.8.0.tgz
tar xzf harbor-offline-installer-v2.8.0.tgz

5.3 修改配置 harbor.yml
cp harbor.yml.tmpl harbor.yml
vim harbor.yml
hostname: docker-master
harbor_admin_password: lee
# 这里我就修改了这两个地方

5.4 一键安装
bash
./install.sh
5.5 关键操作:所有 K8s 节点配置 containerd 支持 http 仓库
master、worker01、worker02 全部执行
解释:Harbor 没有 https 证书,containerd 默认拒绝 http 仓库,必须配置此段。
mkdir -p /etc/containerd/certs.d/harbor.local
cat > /etc/containerd/certs.d/harbor.local/hosts.toml << EOF
server = "http://harbor.local"
[host."http://harbor.local"]
capabilities = ["pull","push","resolve"]
EOF
systemctl restart containerd
5.6 网页访问 172.25.254.50


这里部署好之后创建一个项目:

阶段 6:devops-server 构建前后端镜像,推送 Harbor
6.1 后端 SpringBoot Dockerfile(backend/Dockerfile)
[root@docker-master backend]# mkdir -p /opt/code/backend/Dockerfile
[root@docker-master backend]# vim Dockerfile
FROM docker.io/library/maven:3.8.7-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -Dmaven.test.skip=true
FROM docker.io/library/eclipse-temurin:17-jre-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY –from=builder /app/target/*.jar app.jar
ENV TZ=Asia/Shanghai
ENTRYPOINT ["java","-jar","app.jar"]
这里我没有Springboot的后端代码 就手搓的


6.2 前端 Vue Dockerfile(front/Dockerfile)
[root@docker-master front]# mkdir -p /opt/code/front/Dockerfile
[root@docker-master front]# vim Dockerfile
FROM docker.io/library/nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
前端也是手搓的一个静态的网页

6.3 前端配套 nginx.conf
[root@docker-master front]# mkdir -p /opt/code/front/nginx.conf
[root@docker-master front]# vim nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# 前端/api路径反向代理到后端service
location /api/ {
proxy_pass http://backend-service:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
6.4 构建 + 推送镜像命令
docker tag docker-master/web/web-back:v1.0.0 172.25.254.50/web/web-back:v1.0.0
docker push 172.25.254.50/web/web-back:v1.0.0
docker build -t 172.25.254.50/web/web-front:v1.0.0 /opt/code/front
docker push 172.25.254.50/web/web-front:v1.0.0
构建成功的:

推送成功的:

阶段 7:Master 节点应用 K8s 资源清单部署 Web 业务
7.1 namespace.yaml 命名空间
作用:资源隔离,不同业务放到不同命名空间
[root@docker-master k8s-yaml]# mkdri -p /opt/code/k8s-yaml
[root@docker-master k8s-yaml]# vim namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: web-project
[root@docker-master k8s-yaml]# kubectl apply -f namespace.yaml
7.2 configmap.yaml 配置文件
作用:存放明文配置,挂载进 Pod,无需重新打包镜像修改配置
[root@docker-master k8s-yaml]# vim configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
namespace: web-project
data:
log.level: info
[root@docker-master k8s-yaml]# kubectl apply -f configmap.yaml
7.3 secret.yaml 敏感信息
作用:存放密码等敏感数据,base64 编码(不是加密)
[root@docker-master k8s-yaml]# vim secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: web-secret
namespace: web-project
type: Opaque
data:
db-passwd: Um9vdEAxMjM0NTY=
[root@docker-master k8s-yaml]# kubectl apply -f secret.yaml
7.4 mysql-pvc.yaml 持久化存储声明
解释:PVC 向集群申请存储,数据库数据持久化,Pod 重建数据不丢失
[root@docker-master k8s-yaml]# vim mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
namespace: web-project
spec:
accessModes:
– ReadWriteOnce
resources:
requests:
storage: 10Gi
[root@docker-master k8s-yaml]# kubectl apply -f mysql-pvc.yaml
7.5 backend-deploy.yaml 后端 Deployment+Service
关键点解释:
[root@docker-master k8s-yaml]# vim backend-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-backend
namespace: web-project
spec:
replicas: 2
selector:
matchLabels:
app: web-backend
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: web-backend
spec:
containers:
– name: backend
image: 172.25.254.50/web/web-back:v1.0.0 # 写刚刚构建的镜像名字 不要写我的
resources:
requests:
cpu: "200m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
env:
– name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: web-secret
key: db-passwd
ports:
– containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
—
apiVersion: v1
kind: Service
metadata:
name: backend-service
namespace: web-project
spec:
selector:
app: web-backend
ports:
– port: 8080
targetPort: 8080
type: ClusterIP
[root@docker-master k8s-yaml]# kubectl apply -f backend-deploy.yaml
7.6 front-deploy.yaml 前端 Deployment+Service
[root@docker-master k8s-yaml]# vim front-deply.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-frontend
namespace: web-project
spec:
replicas: 2
selector:
matchLabels:
app: web-frontend
template:
metadata:
labels:
app: web-frontend
spec:
containers:
– name: frontend
image: 172.25.254.50/web/web-front:v1.0.0 # 一样的写你构建的镜像
ports:
– containerPort: 80
—
apiVersion: v1
kind: Service
metadata:
name: frontend-service
namespace: web-project
spec:
selector:
app: web-frontend
ports:
– port: 80
targetPort: 80
type: ClusterIP
[root@docker-master k8s-yaml]# kubectl apply -f front-deploy.yaml
7.7 ingress.yaml 域名入口(前置条件:部署 ingress-nginx 控制器)
解释:Ingress 提供七层反向代理,外部通过域名web.demo.com访问前端服务
[root@docker-master k8s-yaml]# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml
[root@docker-master k8s-yaml]# vim ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: web-project
spec:
ingressClassName: nginx
rules:
– host: web.demo.com
http:
paths:
– path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
[root@docker-master k8s-yaml]# kubectl apply -f ingress.yaml
这里还要配置 让、让containerd 知道拉 Harbor 镜像时"别走 HTTPS,用 HTTP"
每个机子都要配 master worker1 worker 2
[root@docker-master k8s-yaml]# cd /etc/containerd/certs.d/
[root@docker-master certs.d]# ll
总用量 0
drwxr-xr-x 2 root root 24 8月 17 14:26 172.25.254.50
drwxr-xr-x 2 root root 24 8月 17 14:25 docker-master
drwxr-xr-x 2 root root 24 8月 17 14:26 docker-master:80
[root@docker-master certs.d]# cat 172.25.254.50/
cat: 172.25.254.50/: 是一个目录
[root@docker-master certs.d]# cd 172.25.254.50/
[root@docker-master 172.25.254.50]# ll
总用量 4
-rw-r–r– 1 root root 129 8月 17 14:59 hosts.toml
[root@docker-master 172.25.254.50]# cat hosts.toml
server = "http://172.25.254.50"
[host."http://172.25.254.50"]
capabilities = ["pull", "resolve", "push"]
skip_verify = true


7.9 验证业务

第一个命令必须是 每一个 1/1 running 才代表全部启动好了
7.10浏览器访问 http://web.demo.com
这里我是桥接模式访问的 Nat模式没成功

阶段 8:拓展自动化 CI/CD Jenkinsfile(流水线自动化发布)
8.1 构建文件 Jenkinsfile
[root@docker-master code]# mkdir /opt/code/Jenkinsfile
[root@docker-master code]# vim Jenkinsfile
def buildImage(tag, dockerfile, context, imageName) {
def img = "${env.HARBOR_ADDR}/${env.HARBOR_PROJECT}/${imageName}:${tag}"
sh "BUILDAH_ISOLATION=chroot buildah bud -t ${img} -f ${dockerfile} ${context}"
}
def pushImage(tag, imageName) {
def img = "${env.HARBOR_ADDR}/${env.HARBOR_PROJECT}/${imageName}:${tag}"
sh "buildah push –tls-verify=false ${img}"
}
def rolloutDeploy(app, imageName) {
def img = "${env.HARBOR_ADDR}/${env.HARBOR_PROJECT}/${imageName}:${env.IMAGE_TAG}"
// *= 替换该 Deployment 下所有容器的镜像, 避免容器名写错
sh "kubectl –kubeconfig=${env.KUBE_CONFIG} -n ${env.NAMESPACE} set image deployment/${app} *=${img}"
sh "kubectl –kubeconfig=${env.KUBE_CONFIG} -n ${env.NAMESPACE} rollout status deployment/${app} –timeout=180s"
}
pipeline {
agent any
environment {
// —- 环境参数 (按实际情况修改) —-
HARBOR_ADDR = '172.25.254.50' // Harbor IP, HTTP 无 TLS
HARBOR_PROJECT = 'web' // Harbor 项目名
NAMESPACE = 'web-project' // K8s 命名空间
FRONT_APP = 'web-frontend' // 前端 Deployment 名 (集群里的名字, 和目录无关)
BACK_APP = 'web-backend' // 后端 Deployment 名
FRONT_IMAGE = 'web-front' // 前端镜像名
BACK_IMAGE = 'web-back' // 后端镜像名
// —- 凭据 (Jenkins 里配好后无需改动) —-
HARBOR_CREDS = credentials('harbor-credentials') // 注入 HARBOR_CREDS_USR / HARBOR_CREDS_PSW
KUBE_CONFIG = credentials('kubeconfig') // 注入 KUBE_CONFIG (kubeconfig 文件路径)
}
parameters {
string(name: 'IMAGE_TAG', defaultValue: '',
description: '镜像标签, 留空自动用 v<构建号> (如 v12)')
choice(name: 'APP', choices: ['both', 'backend', 'frontend'],
description: '构建/部署哪个应用: both=前后端都做')
}
stages {
stage('拉取代码') {
steps {
// Git 仓库地址在 Jenkins 任务的源码管理里配置, 此处自动检出
checkout scm
script {
env.IMAGE_TAG = params.IMAGE_TAG ?: "v${env.BUILD_NUMBER}"
echo "本次构建镜像标签: ${env.IMAGE_TAG}"
}
}
}
stage('登录 Harbor') {
steps {
sh 'buildah login –tls-verify=false $HARBOR_ADDR -u $HARBOR_CREDS_USR -p $HARBOR_CREDS_PSW'
}
}
stage('构建镜像') {
steps {
script {
if (params.APP != 'frontend') {
echo '===== 构建后端镜像 (Dockerfile 内自动 mvn package) ====='
buildImage(env.IMAGE_TAG, 'backend/Dockerfile', 'backend', env.BACK_IMAGE)
}
if (params.APP != 'backend') {
echo '===== 构建前端镜像 (直接打 dist 进 nginx) ====='
buildImage(env.IMAGE_TAG, 'front/Dockerfile', 'front', env.FRONT_IMAGE)
}
}
}
}
stage('推送 Harbor') {
steps {
script {
if (params.APP != 'frontend') {
pushImage(env.IMAGE_TAG, env.BACK_IMAGE)
}
if (params.APP != 'backend') {
pushImage(env.IMAGE_TAG, env.FRONT_IMAGE)
}
}
}
}
stage('滚动部署') {
steps {
script {
if (params.APP != 'frontend') {
echo '===== 滚动更新后端 ====='
rolloutDeploy(env.BACK_APP, env.BACK_IMAGE)
}
if (params.APP != 'backend') {
echo '===== 滚动更新前端 ====='
rolloutDeploy(env.FRONT_APP, env.FRONT_IMAGE)
}
}
}
}
}
post {
success {
echo "流水线成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}, 镜像标签=${env.IMAGE_TAG}"
}
failure {
echo "流水线失败, 请查看控制台日志定位问题"
}
}
}
8.2 安装git
作用:代码的档案库"——每执行一次 commit 就是把当前所有文件存一个版本。
[root@docker-master code]# yum install -y git
正在更新 Subscription Management 软件仓库。
无法读取客户身份
本系统尚未在权利服务器中注册。可使用 subscription-manager 进行注册。
上次元数据过期检查:0:49:40 前,执行于 2026年08月18日 星期二 10时48分39秒。
依赖关系解决。
==============================================================================================
软件包 架构 版本 仓库 大小
==============================================================================================
安装:
git x86_64 2.47.1-1.el9 AppStream 54 k
安装依赖关系:
git-core x86_64 2.47.1-1.el9 AppStream 4.7 M
git-core-doc noarch 2.47.1-1.el9 AppStream 3.0 M
perl-Error noarch 1:0.17029-7.el9 AppStream 46 k
perl-Git noarch 2.47.1-1.el9 AppStream 39 k
perl-TermReadKey x86_64 2.38-11.el9 AppStream 40 k
perl-lib x86_64 0.65-481.el9 AppStream 15 k
事务概要
==============================================================================================
安装 7 软件包
总计:7.9 M
安装大小:40 M
下载软件包:
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
准备中 : 1/1
安装 : git-core-2.47.1-1.el9.x86_64 1/7
安装 : git-core-doc-2.47.1-1.el9.noarch 2/7
安装 : perl-lib-0.65-481.el9.x86_64 3/7
安装 : perl-TermReadKey-2.38-11.el9.x86_64 4/7
安装 : perl-Error-1:0.17029-7.el9.noarch 5/7
安装 : perl-Git-2.47.1-1.el9.noarch 6/7
安装 : git-2.47.1-1.el9.x86_64 7/7
运行脚本: git-2.47.1-1.el9.x86_64 7/7
验证 : git-2.47.1-1.el9.x86_64 1/7
验证 : git-core-2.47.1-1.el9.x86_64 2/7
验证 : git-core-doc-2.47.1-1.el9.noarch 3/7
验证 : perl-Error-1:0.17029-7.el9.noarch 4/7
验证 : perl-Git-2.47.1-1.el9.noarch 5/7
验证 : perl-TermReadKey-2.38-11.el9.x86_64 6/7
验证 : perl-lib-0.65-481.el9.x86_64 7/7
已更新安装的产品。
已安装:
git-2.47.1-1.el9.x86_64 git-core-2.47.1-1.el9.x86_64
git-core-doc-2.47.1-1.el9.noarch perl-Error-1:0.17029-7.el9.noarch
perl-Git-2.47.1-1.el9.noarch perl-TermReadKey-2.38-11.el9.x86_64
perl-lib-0.65-481.el9.x86_64
完毕!
[root@docker-master code]# git config –global user.name "chen"
[root@docker-master code]# git config –global user.email "chen@local"
[root@docker-master code]# pwd
/opt/code
[root@docker-master code]# git init
提示: 使用 'master' 作为初始分支的名称。这个默认分支名称可能会更改。要在新仓库中
提示: 配置使用初始分支名,并消除这条警告,请执行:
提示:
提示: git config –global init.defaultBranch <名称>
提示:
提示: 除了 'master' 之外,通常选定的名字有 'main'、'trunk' 和 'development'。
提示: 可以通过以下命令重命名刚创建的分支:
提示:
提示: git branch -m <name>
已初始化空的 Git 仓库于 /opt/code/.git/
[root@docker-master code]# git add .
[root@docker-master code]# git status | head -30
位于分支 master
尚无提交
要提交的变更:
(使用 "git rm –cached <文件>…" 以取消暂存)
新文件: Jenkinsfile
新文件: backend/Dockerfile
新文件: backend/pom.xml
新文件: backend/src/main/java/demo/DemoApplication.java
新文件: front/Dockerfile
新文件: front/dist/index.html
新文件: front/nginx.conf
新文件: k8s-yaml/backend-deploy.yaml
新文件: k8s-yaml/configmap.yaml
新文件: k8s-yaml/front-deply.yaml
新文件: k8s-yaml/ingress.yaml
新文件: k8s-yaml/mysql-pvc.yaml
新文件: k8s-yaml/namespace.yaml
新文件: k8s-yaml/secret.yaml
[root@docker-master code]# git commit -m "init web-project"
[master(根提交) 31da318] init web-project
14 files changed, 351 insertions(+)
create mode 100644 Jenkinsfile
create mode 100644 backend/Dockerfile
create mode 100644 backend/pom.xml
create mode 100644 backend/src/main/java/demo/DemoApplication.java
create mode 100644 front/Dockerfile
create mode 100644 front/dist/index.html
create mode 100644 front/nginx.conf
create mode 100644 k8s-yaml/backend-deploy.yaml
create mode 100644 k8s-yaml/configmap.yaml
create mode 100644 k8s-yaml/front-deply.yaml
create mode 100644 k8s-yaml/ingress.yaml
create mode 100644 k8s-yaml/mysql-pvc.yaml
create mode 100644 k8s-yaml/namespace.yaml
create mode 100644 k8s-yaml/secret.yaml
8.3 装 Jenkins + buildah
[root@docker-master code]# dnf install -y java-21-openjdk
正在更新 Subscription Management 软件仓库。
无法读取客户身份
本系统尚未在权利服务器中注册。可使用 subscription-manager 进行注册。
上次元数据过期检查:0:02:11 前,执行于 2026年08月18日 星期二 11时42分31秒。
依赖关系解决。
==============================================================================================
软件包 架构 版本 仓库 大小
==============================================================================================
安装:
java-21-openjdk x86_64 1:21.0.6.0.7-2.el9 AppStream 429 k
安装依赖关系:
java-21-openjdk-headless x86_64 1:21.0.6.0.7-2.el9 AppStream 49 M
事务概要
==============================================================================================
安装 2 软件包
总计:50 M
安装大小:212 M
下载软件包:
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
运行脚本: java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64 1/1
准备中 : 1/1
安装 : java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64 1/2
运行脚本: java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64 1/2
安装 : java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64 2/2
运行脚本: java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64 2/2
运行脚本: java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64 2/2
运行脚本: java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64 2/2
验证 : java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64 1/2
验证 : java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64 2/2
已更新安装的产品。
已安装:
java-21-openjdk-1:21.0.6.0.7-2.el9.x86_64
java-21-openjdk-headless-1:21.0.6.0.7-2.el9.x86_64
完毕!
[root@docker-master code]# rpm –import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.k ey
[root@docker-master code]# yum install -y jenkins buildah
正在更新 Subscription Management 软件仓库。
无法读取客户身份
本系统尚未在权利服务器中注册。可使用 subscription-manager 进行注册。
Jenkins-stable 222 B/s | 833 B 00:03
Jenkins-stable 664 B/s | 1.6 kB 00:02
导入 GPG 公钥 0x14ABFC68:
Userid: "Jenkins Project <jenkinsci-board@googlegroups.com>"
指纹: 5E38 6EAD B55F 0150 4CAE 8BCF 7198 F4B7 14AB FC68
来自: https://pkg.jenkins.io/rpm-stable/repodata/repomd.xml.key
Jenkins-stable 482 B/s | 2.6 kB 00:05
软件包 buildah-1:1.31.3-1.el9.x86_64 已安装。
依赖关系解决。
==============================================================================================
软件包 架构 版本 仓库 大小
==============================================================================================
安装:
jenkins noarch 2.568.2-1 jenkins 96 M
升级:
buildah x86_64 2:1.39.0-2.el9 AppStream 11 M
containers-common x86_64 2:1-117.el9_6 AppStream 155 k
安装依赖关系:
containers-common-extra x86_64 2:1-117.el9_6 AppStream 10 k
事务概要
==============================================================================================
安装 2 软件包
升级 2 软件包
总计:107 M
总下载:96 M
下载软件包:
jenkins-2.568.2-1.noarch.rpm 5.2 MB/s | 96 MB 00:18
———————————————————————————————-
总计 5.2 MB/s | 96 MB 00:18
Jenkins-stable 935 B/s | 1.6 kB 00:01
导入 GPG 公钥 0x14ABFC68:
Userid: "Jenkins Project <jenkinsci-board@googlegroups.com>"
指纹: 5E38 6EAD B55F 0150 4CAE 8BCF 7198 F4B7 14AB FC68
来自: https://pkg.jenkins.io/rpm-stable/repodata/repomd.xml.key
导入公钥成功
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
准备中 : 1/1
升级 : containers-common-2:1-117.el9_6.x86_64 1/6
安装 : containers-common-extra-2:1-117.el9_6.x86_64 2/6
升级 : buildah-2:1.39.0-2.el9.x86_64 3/6
运行脚本: jenkins-2.568.2-1.noarch 4/6
安装 : jenkins-2.568.2-1.noarch 4/6
运行脚本: jenkins-2.568.2-1.noarch 4/6
清理 : buildah-1:1.31.3-1.el9.x86_64 5/6
清理 : containers-common-2:1-55.el9.x86_64 6/6
运行脚本: containers-common-2:1-55.el9.x86_64 6/6
验证 : jenkins-2.568.2-1.noarch 1/6
验证 : containers-common-extra-2:1-117.el9_6.x86_64 2/6
验证 : buildah-2:1.39.0-2.el9.x86_64 3/6
验证 : buildah-1:1.31.3-1.el9.x86_64 4/6
验证 : containers-common-2:1-117.el9_6.x86_64 5/6
验证 : containers-common-2:1-55.el9.x86_64 6/6
已更新安装的产品。
已升级:
buildah-2:1.39.0-2.el9.x86_64 containers-common-2:1-117.el9_6.x86_64
已安装:
containers-common-extra-2:1-117.el9_6.x86_64 jenkins-2.568.2-1.noarch
完毕!
sed -i 's/^User=jenkins/User=root/; s/^Group=jenkins/Group=root/' /usr/lib/systemd/system/jenkins.service
# 让 Jenkins 以 root 用户运行
systemctl daemon-reload
systemctl enable –now jenkins.service

8.4 拿解锁密码
在 docker-master 上执行:
[root@docker-master code]# cat /var/lib/jenkins/secrets/initialAdminPassword
3c429837ab6145988af2f14bc806ba98

8.5 浏览器打开 Jenkins
第一步:
页面会要你输入 Administrator password → 粘贴的密码 → Continue
第二步
选 Install suggested plugins(安装建议插件),点完会开始自动安装(Git、Pipeline 等核心插件都在里面),等几分钟,进度条走完别关页面。
第三步
装完插件会让你创建第一个管理员:
- Username: admin
- Password: 自己设一个(比如 Admin@123,记住)
- Full name / E-mail: 随便填
填完之后进去:

8.6 配置凭据
按照这个操作就好了

8.7 创建流水线任务




到这儿页面 点Build with parameters 进去什么都不改 直接按 build 就好了

看到有个绿色的对勾就代表流水线通了
在验证一下
harbor 仓库会出现标签 v13

两个 Pod 应该都是 1/1 Running,AGE 是最近 1 分钟

这里整个项目就结束了
阶段9 总结
9.1 项目总结
个人收获
从零到一搭建并运维了一套生产级容器化平台:独立完成 3 节点 K8s 集群搭建、Harbor 镜像仓库部署、CI/CD 流水线落地,完整经历"环境规划 → 部署实施 → 故障排障 → 自动化交付"全生命周期,建立了云原生运维的整体架构观,不再停留在"会用命令"层面,而是理解每个组件在链路中的位置与职责;
形成了系统化的排障方法论:面对"外部访问不通"这类疑难问题,不再盲目试错,而是学会用 tcpdump 抓包、iptables 规则分析、事件日志排查等手段逐层定位——从"本机 curl 200 但外部不通"的假象中,通过 SYN 无回包这一关键线索锁定 externalTrafficPolicy: Local 导致的跨节点静默丢包,深刻体会到网络问题必须分层排查、用证据说话,而不是靠猜;
深化了对容器运行时与镜像分发原理的理解:通过解决 containerd 与 Harbor HTTP/HTTPS 协议不匹配问题,彻底搞懂了镜像拉取链路(客户端 → registry → 镜像存储)、certs.d 配置机制、insecure registry 的信任模型,以及多节点环境下"一处配置、全集群生效"的工程化要点;
理解了 DevOps 自动化对交付效率的价值:将原本手动执行"构建 → 推送 → 部署"的流程封装成 Jenkins Pipeline 一键完成,实现业务零停机滚动发布,切实体会到 CI/CD 不只是工具使用,更是把重复劳动变成标准化流程的工程思维;同时掌握 buildah 无守护进程构建、RHEL 环境兼容性处理(Java 版本、镜像短名、crun 兼容)等生产环境实战细节;
提升了抗压与持续学习能力:面对从镜像拉取、集群健康到外部访问、流水线搭建的长链路问题,通过 40+ 次迭代试错最终全部打通,学会了在报错信息、日志和社区文档中快速定位答案,养成了"先分析根因、再动手修复"的习惯,也验证了自己具备独立攻坚复杂环境问题的能力。



