一、环境规划与准备

1.1 节点规划与硬件配置

本次实验使用 3 台物理服务器(虚拟机),角色分配如下:

主机名 角色 配置 IP 地址
k8s-master Control Plane 4C 8G 自己设置
k8s-node1 Worker 4C 8G 自己设置
k8s-node2 Worker 4C 8G 自己设置

操作系统统一使用 Anolis OS 8.10,内核版本 4.18+。

1.2 操作系统与基础环境

所有节点执行以下初始化操作:

# 关闭防火墙
systemctl disable firewalld --now
关闭 SELinux
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
关闭 swap
swapoff -a
sed -i '/ swap / s/^(.*)$/#\1/' /etc/fstab
配置主机名
hostnamectl set-hostname k8s-master  # 其他节点分别设为 k8s-node1, k8s-node2
主机名解析
cat >> /etc/hosts <<EOF
自己设置 k8s-master
自己设置 k8s-node1
自己设置 k8s-node2
EOF
内核参数调整
cat >> /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
时间同步
yum install -y chronyd
systemctl enable chronyd --now

1.3 网络架构设计

Kubernetes 需要规划两个内部网络:

  • Pod 网段:192.168.58.0/20(分配给每个 Pod 的 IP)
  • Service 网段:10.96.0.0/12(分配给 Service 的 Cluster IP)

选择这两个网段的原因是避免与公司/学校内网已有的网段冲突。如果部署在云上,还需要注意 VPC 路由表配置。


二、Kubernetes 集群搭建

2.1 容器运行时安装(Docker + cri-dockerd)

K8s v1.23.17 兼容 Docker 作为容器运行时,但需要 cri-dockerd 作为中间层将 CRI 请求转发给 Docker。

# 安装 Docker
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io
systemctl enable docker --now
安装 cri-dockerd
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.4/cri-dockerd-0.3.4.amd64.tgz
tar xf cri-dockerd-0.3.4.amd64.tgz
cp cri-dockerd/cri-dockerd /usr/local/bin/
cri-dockerd --version
配置 cri-dockerd 启动
cat > /etc/systemd/system/cri-docker.service <<EOF
[Unit]
Description=CRI Interface for Docker
Documentation=https://github.com/Mirantis/cri-dockerd
After=network.target docker.service
[Service]
Type=notify
ExecStart=/usr/local/bin/cri-dockerd --network-plugin=cni --pod-infra-container-image=registry.k8s.io/pause:3.9
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable cri-docker --now

2.2 kubeadm 部署集群

# 添加 K8s 源
cat >> /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
EOF
安装 kubeadm kubelet kubectl
yum install -y kubeadm-1.23.17 kubelet-1.23.17 kubectl-1.23.17
systemctl enable kubelet
master 节点初始化
kubeadm init 
--kubernetes-version=v1.23.17
--apiserver-advertise-address=192.168.1.10
--pod-network-cidr=192.168.58.0/20
--service-cidr=10.96.0.0/12
--cri-socket=unix://var/run/cri-dockerd.sock

初始化成功后会输出 kubeadm join 命令,保存下来,node 节点加入集群用。

# 配置 kubectl
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
node 节点加入集群
kubeadm join 192.168.1.10:6443 --token xxx 
--discovery-token-ca-cert-hash sha256:xxx
--cri-socket=unix://var/run/cri-dockerd.sock

2.3 遇到的问题与解决

问题 1:kubeadm 初始化卡在 probe-network 阶段

  • 原因:cri-dockerd 未正确配置,kubelet 无法连接容器运行时
  • 解决:检查 cri-dockerd 是否正常运行,确认 --cri-socket 参数正确指向 cri-dockerd

问题 2:节点加入集群后状态一直是 NotReady

  • 原因:还没安装 CNI 网络插件(Calico),节点网络未就绪
  • 解决:安装 Calico 后自动恢复,详见第三章

三、CNI 网络插件——Calico BGP 模式

3.1 Calico 安装与配置

Calico 是目前最流行的 K8s 网络插件之一,支持 BGP 模式直接路由,性能优于 VXLAN 封装模式。

# 下载 Calico 清单
curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.4/manifests/calico.yaml -O
修改 Pod CIDR(如果默认不是 192.168.58.0/20)
在 calico.yaml 中找到 CALICO_IPV4POOL_CIDR,修改为:
- name: CALICO_IPV4POOL_CIDR
value: "192.168.58.0/20"
应用 Calico
kubectl apply -f calico.yaml
确认所有 calico-node Pod 运行正常
kubectl get pods -n kube-system -o wide

3.2 跨节点 Pod 通信原理

Calico BGP 模式下,每个节点上的 calico-node 会与邻居节点建立 BGP 会话,自动交换路由信息。这样 Pod 流量直接通过三层路由转发,不需要封包解包,延迟更低。

3.3 遇到的问题与解决

问题:Calico 安装后跨节点 Pod 网络不通

  • 原因:节点的物理网络没有配置 BGP 对等,或者节点的默认路由与 Calico 的路由冲突

解决过程

  1. 检查 calico-node 日志:kubectl logs -n kube-system calico-node-xxx
  2. 查看节点路由表:ip route,确认是否有 192.168.58.0/20 网段的路由
  3. 检查 BGP 对等状态:calicoctl node status
  4. 最终发现是节点之间的物理防火墙拦截了 BGP 的 179 端口,放行后恢复

四、集群高可用与存储

4.1 LVS + Keepalived 高可用

为了让集群 API Server 高可用,用 LVS(Linux Virtual Server)+ Keepalived 做负载均衡:

# 安装 Keepalived 和 ipvsadm
yum install -y keepalived ipvsadm
Keepalived 配置(在 master 节点)
cat > /etc/keepalived/keepalived.conf <<EOF
global_defs {
router_id LVS_K8S
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 111
}
virtual_ipaddress {
192.168.1.100
}
}
virtual_server 192.168.1.100 6443 {
delay_loop 6
lb_algo rr
lb_kind DR
persistence_timeout 0
protocol TCP
real_server 192.168.1.10 6443 {
weight 1
TCP_CHECK {
connect_timeout 3
}
}
}
systemctl enable keepalived --now

这里虚拟 IP(VIP)设为 192.168.1.100,后续 kubeadm 初始化时就可以用 VIP 作为 API Server 地址。

4.2 local-path-provisioner 本地存储

对于有状态服务(如 Nacos、MySQL),需要持久化存储。实验环境没有 NAS 或云硬盘,用 local-path-provisioner 把宿主机目录映射为 PVC:

# 部署 local-path-provisioner
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
设为默认 StorageClass
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
验证部署
kubectl get pods -n local-path-storage
kubectl get storageclass

工作原理:local-path-provisioner 会在每个节点上创建 /opt/local-path-provisioner 目录(可配置),当 Pod 申请 PVC 时,会自动在该节点上创建子目录并绑定到 Pod。

使用示例:创建一个 PVC 测试:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-path
  resources:
    requests:
      storage: 1Gi
# 应用 PVC
kubectl apply -f test-pvc.yaml
查看 PVC 状态
kubectl get pvc test-pvc
查看自动创建的 PV
kubectl get pv

4.3 遇到的问题与解决

问题:Keepalived 配置后 VIP 无法漂移

  • 原因:需要在 node 节点上配置抑制 ARP 响应,避免 IP 冲突
  • 解决:在所有节点执行以下参数调整:
cat >> /etc/sysctl.conf <<EOF
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
EOF
sysctl -p

问题:local-path-provisioner Pod 启动失败

  • 原因:节点上 /opt/local-path-provisioner 目录权限不足
  • 解决:在所有节点创建目录并设置权限:
sudo mkdir -p /opt/local-path-provisioner
sudo chmod 777 /opt/local-path-provisioner

问题:PVC 一直处于 Pending 状态

  • 原因:local-path-provisioner 的 ServiceAccount 没有足够权限
  • 解决:检查 RBAC 配置,确保 local-path-provisioner 有创建 PV 的权限:
# 查看 local-path-provisioner Pod 日志
kubectl logs -n local-path-storage -l app=local-path-provisioner
如果权限问题,重新应用 RBAC
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml

五、中间件容器化部署

5.1 Nacos(StatefulSet + PVC)

Nacos 作为注册中心和配置中心,是有状态服务,需要稳定的网络标识和持久化存储,因此选择 StatefulSet + PVC 方式部署。

# nacos-statefulset.yaml
apiVersion: v1
kind: Service
metadata:
  name: nacos-headless
  namespace: middleware
spec:
  clusterIP: None
  ports:
  - port: 端口写自己配置端口
    name: client
  - port: 9848
    name: rpc
  selector:
    app: nacos
---
apiVersion: v1
kind: StatefulSet
metadata:
  name: nacos
  namespace: middleware
spec:
  serviceName: nacos-headless
  replicas: 1
  selector:
    matchLabels:
      app: nacos
  template:
    metadata:
      labels:
        app: nacos
    spec:
      containers:
      - name: nacos
        image: nacos/nacos-server:v2.2.3
        env:
        - name: MODE
          value: standalone
        - name: MYSQL_SERVICE_HOST
          value: mysql-service
        - name: MYSQL_DATABASE
          value: nacos_config
        - name: MYSQL_USER
          value: nacos
        - name: MYSQL_PASSWORD
          value: nacos123
        ports:
        - containerPort: 端口写自己配置端口
        - containerPort: 9848
        volumeMounts:
        - name: data
          mountPath: /home/nacos/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      storageClassName: local-path
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 2Gi

5.2 MySQL 容器化

MySQL 同样作为有状态服务部署:

apiVersion: v1
kind: Service
metadata:
  name: mysql-service
  namespace: middleware
spec:
  ports:
  - port: 3306(注意自己数据库端口)
  selector:
    app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: middleware
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: root123
        ports:
        - containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      storageClassName: local-path
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi

5.3 Seata、Sentinel 部署

Seata 作为分布式事务组件,依赖于 Nacos 注册中心。部署时需要注意 Seata 的 registry.conf 和 file.conf 配置中的 Nacos 地址必须正确指向 Nacos 服务的 DNS 名称。

5.4 遇到的问题与解决

问题:Seata Pod 启动后 CrashLoopBackOff

这是部署过程中最典型的故障之一,也是面试时常被问到的问题。

  • 现象kubectl get pods 看到 seata 状态为 CrashLoopBackOff
  • 原因:Seata 的 registry.conf 中配置的 Nacos 地址写错了。当时写成了 Nacos 的 Service IP 而不是 Nacos 的 K8s DNS 名称,或者写错了端口号

排查过程

  1. kubectl logs -f seata-pod-name 查看日志,看到连接 Nacos 超时
  2. kubectl exec -it seata-pod-name -- ping nacos-service.middleware 测试 DNS 解析
  3. 发现 registry.conf 中配置的地址不对,修正后重新部署

解决:将 Seata 配置文件中的 registry.nacos.serverAddr 改为 nacos-service.middleware:8848(K8s 内部 DNS 名称)

六、微服务容器化改造

6.1 多阶段 Dockerfile 编写

多阶段构建可以大幅减小镜像体积。以一个微服务为例,第一阶段编译,第二阶段运行:

# 第一阶段:编译
FROM maven:3.8.6-openjdk-17 AS builder  #注意要有jdk17的环境
WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
第二阶段:运行
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 注意填自己的端口
ENTRYPOINT ["java", "-jar", "app.jar"]

前端使用 node 构建 + nginx 运行时:

# 第一阶段:构建前端
FROM node:22-alpine AS builder
WORKDIR /build
COPY package.json .
RUN npm install
COPY . .
RUN npm run build
第二阶段:nginx 运行
FROM nginx:alpine
COPY --from=builder /build/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 注意这是前端的端口
CMD ["nginx", "-g", "daemon off;"]

前端镜像从 238MB 降到 16.7MB,压缩了 93%。

6.2 K8s 资源清单编写

每个微服务对应一个 Deployment + Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  namespace: rental
spec:
  replicas: 1
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: harbor.internal/rental/user-service:20260601-001
        ports:
        - containerPort: 填对应端口,比如此例子用户的端口
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 填对应端口
          initialDelaySeconds: 30
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 810
          initialDelaySeconds: 60
          periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
  name: user-service
  namespace: rental
spec:
  ports:
  - port: 填对应端口
  selector:
    app: user-service

Gateway 作为统一入口,需要配置 Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gateway-ingress
  namespace: rental
spec:
  rules:
  - host: rental.internal
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: gateway-service
            port:
              number: 网关的端口

6.3 遇到的问题与解决

问题 1:Gateway 路由 404

  • 现象:前端访问后端接口返回 404
  • 原因:Spring Cloud Gateway 的配置文件缺少后端服务的路由规则,或者配置的 JSON 格式不正确
  • 解决:检查 gateway 的配置文件,补全每个微服务的路由规则,确认 YAML/JSON 格式无误后重新部署

问题 2:镜像缓存问题(latest 标签)

  • 现象:更新代码后重新部署,Pod 仍然运行旧版本
  • 原因:之前习惯用 latest 标签,但 K8s 的 imagePullPolicy 如果设为 IfNotPresent,节点上有 latest 镜像就不会重新拉取,导致运行的还是旧版本
  • 解决:废弃 latest 标签,改用 构建编号 作为镜像 Tag(如 20260601-001),每次构建生成唯一 Tag。Deployment 更新时指定新 Tag,K8s 自动拉取新镜像

七、GitLab + Jenkins CI/CD 流水线

7.1 GitLab 部署与仓库配置

GitLab 作为代码仓库,使用 Docker 方式部署:

# 创建 GitLab 容器
docker run -d \
  --name gitlab \
  --restart always \
  -p 22:22 -p 容器内GitLab网页HTTP端口可以填8080防止80冲突:80 -p 443:443 \
  -v /srv/gitlab/config:/etc/gitlab \
  -v /srv/gitlab/logs:/var/log/gitlab \
  -v /srv/gitlab/data:/var/opt/gitlab \
  gitlab/gitlab-ce:15.11.0-ce.0

在 GitLab 中创建项目仓库,推送代码,记录项目 ID 和 GitLab API Token,后续 Jenkins 配置需要用到。

7.2 Jenkins 安装与配置

# Jenkins 使用 Docker 部署
docker run -d \
  --name jenkins \
  --restart always \
  -p 8080:8080 -p 500:500 \
  -v /srv/jenkins:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  jenkins/jenkins:lts

所需插件:

  • Git Plugin
  • Pipeline
  • Docker Pipeline
  • Kubernetes CLI Plugin
  • SonarQube Scanner

7.3 Harbor 镜像仓库部署

# 下载 Harbor 离线安装包
wget https://github.com/goharbor/harbor/releases/download/v2.8.0/harbor-offline-installer-v2.8.0.tgz
tar xf harbor-offline-installer-v2.8.0.tgz
修改 harbor.yml
hostname: 192.168.1.20  # 部署 Harbor 的机器 IP
http:
port: 80
安装
./install.sh

注意:使用 HTTP 需要在所有节点 Docker 配置中添加 insecure-registries:

cat >> /etc/docker/daemon.json <<EOF
{
  "insecure-registries": ["192.168.1.20:80"]
}
EOF
systemctl restart docker

7.4 Jenkins Declarative Pipeline 7 阶段设计

整个流水线分为 7 个阶段:

1. Checkout          → 从 GitLab 拉取代码
2. Parse Services    → 解析本次要构建的服务
3. Maven Build       → Maven 编译打包
4. Docker Build      → 构建镜像并推送到 Harbor
5. Deploy to K8s     → 更新 K8s 资源清单
6. Update YAML       → 提交更新后的 YAML 到 GitLab
7. Cleanup           → 清理工作空间

完整 Jenkinsfile:

pipeline {
    agent any
environment {
    HARBOR_URL = '192.168.1.20:80'
    PROJECT = 'rental'
    GITLAB_URL = 'http://192.168.1.30'
    GITLAB_TOKEN = 'your-token'
}
stages {
stage('Checkout') {
steps {
checkout scm
script {
COMMIT_MSG = sh(returnStdout: true, script: 'git log -1 --pretty=%B').trim()
}
}
}
stage('Parse Services') {
    steps {
        script {
            def matcher = (COMMIT_MSG =~ /build\s+(\S+)/)
            if (matcher.find()) {
                BUILD_SERVICES = matcher[0][1].split(',')
                echo "本次构建服务: ${BUILD_SERVICES}"
            } else {
                BUILD_SERVICES = ['all']
                echo "未指定服务,全量构建"
            }
        }
    }
}
stage('Maven Build') {
steps {
script {
def builds = [:]
BUILD_SERVICES.each { service -&amp;gt;
if (service == 'all') {
builds['all'] = {
sh 'mvn clean package -DskipTests'
}
} else {
builds[service] = {
sh "mvn clean package -DskipTests -pl ${service} -am"
}
}
}
parallel builds
}
}
}
stage('Docker Build &amp;amp; Push') {
steps {
script {
def builds = [:]
BUILD_SERVICES.each { service -&amp;gt;
builds[service] = {
def tag = "${HARBOR_URL}/${PROJECT}/${service}:${env.BUILD_NUMBER}"
sh "docker build -t ${tag} ./${service}"
sh "docker push ${tag}"
}
}
parallel builds
}
}
}
stage('Deploy to K8s') {
steps {
script {
BUILD_SERVICES.each { service -&amp;gt;
def tag = "${HARBOR_URL}/${PROJECT}/${service}:${env.BUILD_NUMBER}"
sh """
sed -i 's|image:.${service}:.|image: ${tag}|' k8s/${service}-deployment.yaml
"""
}
}
}
}
stage('Update YAML') {
steps {
withCredentials([string(credentialsId: 'gitlab-token', variable: 'TOKEN')]) {
script {
sh """
git add k8s/*.yaml
git commit -m "auto: update image tag for build ${env.BUILD_NUMBER}"
git push http://oauth2:${TOKEN}@${GITLAB_URL}/rental/rental-system.git HEAD:main
"""
}
}
}
}
}
}

7.5 增量构建机制(核心亮点)

这是 CI/CD 流水线的核心优化点。

痛点:6 个微服务 + 前端,每次提交代码都全量构建,耗时 10 分钟以上。大部分时候只改了 1 个服务,没必要全部重新编译。

方案:通过解析 git commit message 中的关键字,只构建变更的服务。

具体实现:

  1. 开发者在提交代码时,在 commit message 中写明要构建的服务:git commit -m "fix: 修复登录bug build user"
  2. Pipeline 中的 Parse Services 阶段用正则 /build\s+(\S+)/ 提取服务名
  3. 如果写多个服务用逗号分隔:build user,order
  4. 如果没有 build 关键字,执行全量构建

效果:增量构建从 10min+ 降到 3min,效率提升 60% 以上。

7.6 Webhook 循环防护

问题:Pipeline 的最后一步(Update YAML)会向 GitLab 推送代码,而 GitLab 收到推送又会触发 Jenkins Webhook,导致无限循环触发构建。

解决:在 Update YAML 阶段提交时,commit message 以 auto: 开头。然后在 Jenkins Pipeline 的第一阶段检查 commit message,如果以 auto: 开头,直接跳过整个构建:

stage('Check Webhook Loop') {
    steps {
        script {
            if (COMMIT_MSG.startsWith('auto:')) {
                echo '检测到自动提交,跳过构建'
                currentBuild.result = 'SUCCESS'
                return
            }
        }
    }
}

7.7 遇到的问题与解决

问题 1:Jenkins 无法连接 GitLab

  • 原因:GitLab 的 API Token 配置错误或权限不足
  • 解决:在 GitLab 中重新生成 Access Token,授予 API 和 read_repository/write_repository 权限,在 Jenkins 中更新凭据

问题 2:Jenkins 构建时 Maven 下载依赖慢

  • 原因:每次构建都在容器内重新下载 Maven 依赖
  • 解决:在 Jenkins 宿主机配置 Maven 本地仓库缓存,通过 volume 挂载到 Jenkins 容器中,避免重复下载

问题 3:Harbor 镜像推送失败

  • 原因:Jenkins 所在机器的 Docker 没有配置 Harbor 的 insecure-registries
  • 解决:在 Jenkins 宿主机的 /etc/docker/daemon.json 中添加 insecure-registries,重启 Docker

八、监控与可观测性

8.1 Prometheus + Grafana 监控栈

Prometheus Operator 是部署 Prometheus 监控栈的推荐方式,它简化了 Prometheus、Alertmanager 和 Grafana 的部署和管理。

# 部署 Prometheus Operator
kubectl create namespace monitoring
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring
kubectl port-forward -n monitoring svc/prometheus-grafana 300:80

如果不用 Helm,也可以手动部署 Prometheus + Grafana,配置 ServiceMonitor 采集微服务指标。

8.2 JVM/GC/HTTP 指标采集

微服务引入 Micrometer + Prometheus 依赖(Spring Boot 3.x 自带):

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

配置后在每个微服务的 application.yml 中暴露 /actuator/prometheus 端点:

management:
  endpoints:
    web:
      exposure:
        include: health,prometheus
  metrics:
    tags:
      application: ${spring.application.name}

在 Prometheus 中配置 ServiceMonitor 采集这些端点,Grafana 导入 JVM Micrometer 仪表盘(ID 4701)即可看到 JVM 内存、GC 次数、线程数、HTTP 请求吞吐量、连接池使用率等指标。

8.3 Zipkin 链路追踪

微服务引入 Micrometer Tracing + Brave:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
    <groupId>io.zipkin.reporter2</groupId>
    <artifactId>zipkin-reporter-brave</artifactId>
</dependency>

Zipkin 服务端使用 Docker 部署:

docker run -d --name zipkin -p 端口:端口 openzipkin/zipkin:2.24

微服务配置指向 Zipkin 地址:

spring:
  zipkin:
    base-url: http://zipkin-service.monitoring:刚才的端口
  sleuth:
    sampler:
      probability: 1.0

这样每个请求经过多个微服务时,可以在 Zipkin 中看到完整的调用链路和每个环节的耗时。

8.4 遇到的问题与解决

问题:Grafana 仪表盘看不到数据

  • 原因:Prometheus 的 ServiceMonitor 没有正确匹配微服务的 Service
  • 解决:确认 ServiceMonitor 的 selector 中 label 与微服务 Service 的 label 一致,检查 Prometheus Target 页面确认 endpoint 是否处于 UP 状态

九、总结

使用 Kubernetes 集群编排,不只是工具替换,而是思维方式的转变:

  1. 配置管理:从本地配置文件 → ConfigMap + Nacos 配置中心

  2. 服务发现:从固定 IP → K8s DNS + 注册中心

  3. 存储:从本地挂载 → PVC 持久卷

  4. 更新策略:从停服更新 → 滚动更新 + 健康检查探针

  5. 部署方式:从手动部署 → CI/CD 全自动流水线

更多推荐