1. 项目目标与当前架构

目标不是只部署一个 Nginx,而是打通一条可验证的交付链路:源码提交后由流水线构建镜像、推送镜像仓库、更新 Kubernetes 工作负载;同时为应用提供统一入口、指标采集、告警与 HPA 扩缩容能力。

Gitee master
    │
    ▼
GitLab CI:fetch → build → deploy
    │                 │
    │                 ├─ Kaniko 构建 Commit SHA 镜像
    │                 └─ 阿里云 ACR
    ▼
Kubernetes apps/frontend-static
    ├─ Deployment + RollingUpdate + Probe
    ├─ Service + Ingress-NGINX
    ├─ HPA(CPU 指标)
    └─ Prometheus / Grafana / Alertmanager

1.1 实验拓扑

角色主机名地址关键组件
控制平面k8s-master-1192.168.117.138kube-apiserver、etcd、scheduler、controller-manager
Workerk8s-node-1192.168.117.139containerd、Calico、Ingress 后端、应用 Pod
Workerk8s-node-2192.168.117.140containerd、Calico、应用 Pod
NFS(历史存储)NFS192.168.117.147/web/html 迁移目标
MetalLB VIP-192.168.117.188ingress-nginx-controller 的 LoadBalancer 地址

1.2 版本与验收快照

项目当前状态
OSCentOS Stream 10
Kubernetesv1.35.0
容器运行时containerd 2.2.1
CNICalico v3.28.0
kube-proxyIPVS,strictARP: true
节点/Pod3/3 Ready、47/47 Running
GitLabGitLab CE 18.11.9 / Helm Chart 9.11.10
监控kube-prometheus-stack 82.16.0 / Prometheus Operator v0.89.0

常用验收命令:

kubectl get nodes -o wide
kubectl get pods -A
kubectl -n kube-system get configmap kube-proxy -o yaml

2. 集群基础:containerd、Calico 与 IPVS

集群使用 kubeadm 初始化,所有节点统一使用 containerd,并将 kubelet 的 cgroup 驱动配置为 systemd。网络层选择 Calico,Pod CIDR 与 kubeadm init 的 --pod-network-cidr 保持一致。

# 仅示例:版本和镜像源应按实际环境确认
dnf install -y kubelet-1.35.0 kubeadm-1.35.0 kubectl-1.35.0 \
  --disableexcludes=kubernetes
systemctl enable --now kubelet
​
# containerd 必须使用 systemd cgroup
grep -n 'SystemdCgroup' /etc/containerd/config.toml
systemctl restart containerd

Service 转发使用 IPVS。MetalLB L2 模式需要 kube-proxy 的 strictARP 为 true,否则 VIP 的 ARP 宣告可能异常:

kubectl -n kube-system get configmap kube-proxy -o jsonpath='{.data.config\.conf}' \
  | grep -E 'mode:|strictARP:'
# 预期:mode: "ipvs"、strictARP: true

3. MetalLB + Ingress-NGINX:裸金属 VIP 与七层路由

公有云可由云厂商分配 LoadBalancer 地址;裸金属实验集群使用 MetalLB 管理地址池。当前仅配置一个 VIP,因此将它分配给 ingress-nginx-controller,由 Ingress 根据域名和路径转发到 ClusterIP Service。

MetalLB 的 IPAddressPool 负责可分配地址,L2Advertisement 负责 L2/ARP 宣告;官方说明见 MetalLB Configuration。

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.168.117.188/32
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
spec:
  ipAddressPools:
    - first-pool
kubectl -n metallb-system get ipaddresspool,l2advertisement
kubectl -n ingress-nginx get svc ingress-nginx-controller
# 当前 Service:LoadBalancer,EXTERNAL-IP=192.168.117.188,端口 80/443

现役前端使用独立域名 frontend.192.168.117.188.nip.io,对应 apps/frontend-static 的 Ingress。根 VIP 返回 503 并不等于 MetalLB 故障:当前遗留的 default/web-ingress 没有 Endpoint;应通过目标域名验证现役路由。

curl --noproxy '*' -I http://frontend.192.168.117.188.nip.io
kubectl -n apps get ingress,service,pods

当前控制器镜像为 Ingress-NGINX v1.8.1,仅作为现役状态记录。Ingress-NGINX 项目已在 2026 年 3 月后结束常规维护,后续生产方案应评估 Gateway API 或其他受维护的实现;不要将本文环境直接宣传为可直接用于生产的入口方案。官方公告

4. 存储演进:NFS 故障恢复与 local-path 边界

早期静态站点通过 NFS 的 /web/html 目录挂载到 Nginx 容器。排障中发现旧 PV 指向过期端点 192.168.117.134,因此采用“新建并切换、旧资源保留”的方式创建 nfs-pv-html-v2 / nfs-pvc-html-v2,指向 192.168.117.147:/web/html,避免直接删除仍可回滚的旧 PV/PVC。

kubectl get pv nfs-pv-html nfs-pv-html-v2 \
  -o custom-columns=NAME:.metadata.name,SERVER:.spec.nfs.server,PATH:.spec.nfs.path,STATUS:.status.phase

现役 apps/frontend-static 不再依赖 NFS:流水线将 index.html 封装入 Nginx 镜像,运行时只从 ACR 拉取版本化镜像。NFS 章节保留为一次存储迁移与回滚策略案例,不作为当前前端的依赖说明。

GitLab 的 PostgreSQL、Redis、Gitaly、MinIO 则使用 local-path:分别为 8Gi、2Gi、20Gi、10Gi。这种方式适合实验环境,卷与节点绑定,不具备复制存储或跨节点故障切换能力。

5. 可观测:Prometheus、Alertmanager 与 Grafana

监控采用 Helm 部署的 kube-prometheus-stack,包含 Prometheus、Alertmanager、Grafana、Prometheus Operator、Node Exporter 和 kube-state-metrics。当前已有 13 个 ServiceMonitor 与 35 个 PrometheusRule,用于采集节点、Kubernetes 对象和服务指标,并将规则结果交给 Alertmanager。

helm -n monitoring list
kubectl -n monitoring get pods
kubectl get servicemonitors.monitoring.coreos.com -A
kubectl get prometheusrules.monitoring.coreos.com -A

Grafana 和 Prometheus 当前通过 NodePort 暴露,而非第二个 MetalLB VIP:

kubectl -n monitoring get svc prometheus-grafana prometheus-kube-prometheus-prometheus
# Grafana NodePort:31356
# Prometheus NodePort:30090

文章不记录默认账号、密码或从 Secret 解码出的值。应通过 Kubernetes Secret、受控访问入口和独立凭据管理 Grafana、ACR 与 GitLab 的敏感信息。

6. GitLab CI/CD:从 Gitee 到 Kubernetes

GitLab 通过 Helm 部署,版本为 CE 18.11.9;Runner 使用 Kubernetes Executor。镜像仓库复用阿里云 ACR,GitLab 内置 Registry 保持关闭。流水线的目标是:从 Gitee master 获取静态资源,用 Kaniko 无特权构建 Nginx 镜像,以 Commit SHA 标记版本,并将 Deployment、Service、Ingress、HPA 应用到 apps 命名空间。

Gitee master
  → fetch-source:克隆源码并保存 GITEE_COMMIT、INDEX_SHA256
  → build-image:Kaniko 构建并推送 <registry>/<namespace>/frontend-static:<commit>
  → deploy-app:apply 清单,替换镜像,等待 rollout status

关键变量只在 GitLab 的 CI/CD Variables 中维护,不进入仓库:

GITEE_REPO_URL
GITEE_USERNAME / GITEE_TOKEN(私有仓库时需要)
ACR_REGISTRY
ACR_NAMESPACE
ACR_USERNAME
ACR_PASSWORD
GIT_CLIENT_IMAGE
KANIKO_IMAGE

.gitlab-ci.yml 的核心行为如下,密码仅在运行时写入 Kaniko 的 Docker 配置:

variables:
  GITEE_BRANCH: master
  K8S_NAMESPACE: apps
  IMAGE_NAME: ${ACR_REGISTRY}/${ACR_NAMESPACE}/frontend-static

# fetch:git clone --depth 1 --branch "${GITEE_BRANCH}" "${GITEE_REPO_URL}" source
# build:/kaniko/executor --destination "${IMAGE_NAME}:${GITEE_COMMIT}"
# deploy:kubectl apply ... && kubectl rollout status deployment/frontend-static

6.1 导入仓库不是自动同步

GitLab CE 的“按 URL 导入”只在导入时复制代码,后续不会持续从 Gitee 拉取。GitLab 项目自身的 master 必须包含最新 .gitlab-ci.yml;运行时的 fetch-source 再负责克隆 Gitee master。因此当日志出现 git clone --branch main 时,应同时检查:

  1. GitLab 项目当前提交中的 GITEE_BRANCH;

  2. 项目/组变量是否覆盖了 YAML 变量;

  3. Gitee 的实际默认分支。

6.2 三个已遇到的排障点

  • Windows Git 自签名证书报错: 应将 GitLab CA 导入 Windows 受信任根证书;临时排查可对单次命令使用 git -c http.sslVerify=false,不要设置全局关闭校验。

  • GitLab 分支与 Gitee 历史分叉: 先 git fetch gitlab 和 git log --left-right master...gitlab/master;确认 GitLab 无独立提交后,才使用 git push --force-with-lease gitlab master:master。受保护分支需临时授权强推,完成后立即关闭。

  • Kaniko 推送 ACR 返回 UNAUTHORIZED: podman login 成功只说明账号可登录,不代表流水线镜像路径正确。重点检查 ACR_NAMESPACE 是否注入;正确镜像路径必须是 <registry>/<namespace>/frontend-static:<commit>。

7. 现役应用:滚动更新、探针与 HPA

当前 apps/frontend-static 的镜像由 Commit SHA 标记。Deployment 固定双副本起步,使用 maxUnavailable: 0、maxSurge: 1 进行滚动发布,并以 HTTP 探针控制就绪和存活判断。

spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    spec:
      containers:
        - name: frontend
          resources:
            requests:
              cpu: 20m
              memory: 32Mi
            limits:
              cpu: 200m
              memory: 128Mi
          readinessProbe:
            httpGet: { path: /, port: http }
          livenessProbe:
            httpGet: { path: /, port: http }

HPA 使用 autoscaling/v2,CPU 平均利用率目标为 60%。资源 requests 是 CPU 利用率计算的前提;behavior 用于限制每次副本变化并避免频繁缩容。HPA 行为字段的含义可参考 Kubernetes 官方文档。

本次验收中,frontend-static 为 2/2 Ready、2/2 Available,两个实例分布在不同 Worker,重启次数为 0;镜像标签与 Gitee Commit 157d90ca9e61ff1a5d11b461e04087b84a3592af 对应。早期 Nginx HPA 压测场景曾将稳定并发上限从 1000 提升至 1800、最大 QPS 提升约 12%;该压测参数与当前前端 2—6 副本配置不同,应分别理解。

8. 发布后的验收清单

# 集群基础
kubectl get nodes
kubectl get pods -A

# VIP 与入口
kubectl -n ingress-nginx get svc ingress-nginx-controller
curl --noproxy '*' -I http://frontend.192.168.117.188.nip.io

# 应用与 HPA
kubectl -n apps get deploy,pod,svc,ingress,hpa
kubectl -n apps rollout status deployment/frontend-static --timeout=300s

# 监控
kubectl -n monitoring get pods
kubectl get servicemonitors.monitoring.coreos.com -A
kubectl get prometheusrules.monitoring.coreos.com -A

验收应同时检查控制面、Pod、Endpoint、HPA 条件和 HTTP 响应,而不是仅看到 Pipeline 成功就判定发布完成。

总结

这套平台的核心价值不在于单独安装某个组件,而在于把基础设施、流量、监控、镜像交付和弹性伸缩接成一条可验证的闭环:代码变更可追溯到镜像标签,镜像可追溯到 Kubernetes Deployment,应用状态可通过探针、HPA、Prometheus 和 HTTP 验收共同确认。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐