k8s 部署测试环境

前一阵子为开发的项目部署了一套简单的测试环境,在此记录一下部署流程及遇到的坑点。

项目主要组成部分有:Traefik (包含自研的中间件插件)、PostgreSQL、write 应用 (StatefulSet) 和 read 应用 (Deployment),均部署在腾讯云k8s集群上。

PostgreSQL

问题描述

在 Kubernetes 集群中,CloudNativePG 的 Pod cluster-example-3 无法删除,持续处于 Terminating 状态,且被控制器反复重建。

1. Pod 状态异常

1
2
kubectl get pods -n default | grep cluster-example-3
# cluster-example-3 0/1 Init:0/1 0 Xs

Pod 一直处于 Init:0/1 状态,无法成功启动。

2. 网络错误日志

1
2
3
4
Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox
ENI-IP 192.168.0.22 has been allocated to this pod default/cluster-example-3
container 207f131f7451b47342b5bc7f1c665d2bbfd0bf65bf516addab757d37b537aba8 ifname eth0,
but now try to allocated to container [NEW_CONTAINER_ID]

3. 控制器行为

执行 kubectl delete pod cluster-example-3 --force --grace-period=0 后,Pod 会被立即删除,但控制器随即又会重新创建。

根本原因分析

  1. IP 地址冲突
    旧容器 207f... 虽然已被删除,但其占用的 ENI IP 地址 192.168.0.22 仍然被 TKE ENI IP 管理器记录在缓存中

  2. PVC 元数据持久化
    检查 PVC 发现包含以下注解:

    1
    2
    3
    4
    annotations:
    cnpg.io/nodeSerial: "3"
    cnpg.io/operatorVersion: 1.27.1
    cnpg.io/pvcStatus: ready

    PVC 中的 nodeSerial: "3" 导致 CloudNativePG 控制器持续尝试创建 cluster-example-3 Pod。

  3. 控制器重建逻辑
    CloudNativePG 的 Cluster 控制器检测到:

    • 配置文件中 instances: 2
    • 但由于 PVC cluster-example-3 存在
    • 控制器判断需要维持这个实例

解决方案 (删除 Pod 和 PVC)

1. 强制删除 Pod

1
kubectl delete pod cluster-example-3 -n default --force --grace-period=0

2. 删除相关 PVC

1
kubectl delete pvc cluster-example-3 cluster-example-3-wal -n default

注意: 删除 PVC 会导致数据丢失,请确保已备份重要数据!

3. 等待控制器重建

CloudNativePG 控制器会根据配置文件中的 instances: 2 自动创建新的 Pod。

1
2
3
4
5
# 监控 pod 状态
kubectl get pods -n default -l cnpg.io/cluster=cluster-example -w

# 查看集群状态
kubectl get cluster.postgresql.cnpg.io cluster-example -n default

总结

本次问题的根本原因是:

  1. 网络层: TKE ENI IP 管理器缓存了已删除容器的 IP 分配记录。
  2. 存储层: PVC 中的元数据 (nodeSerial: "3") 导致控制器持续重建 Pod。
  3. 控制层: CloudNativePG 控制器根据 PVC 存在性判断需要维护的实例。

最佳实践:

  • 缩容时应同时删除 Pod 和对应的 PVC。
  • 使用 Operator 提供的缩容功能而不是手动删除 (当时直接在 k9s 上删了导致的问题😂)。

Traefik

Traefik 本身的部署并不复杂,但加上安装中间件插件和配置 TLS,复杂度就随之上升了。

配置 TLS

使用 cert-manager 配合 Let’s Encrypt 免费证书来实现 TLS 证书的自动管理与颁发。

1. 安装 cert-manager

1
2
3
4
5
6
helm install \
cert-manager oci://quay.io/jetstack/charts/cert-manager \
--version v1.16.2 \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true

验证安装:

1
2
3
4
5
6
7
# 查看 cert-manager pods 状态
kubectl get pods -n cert-manager

# 应该看到 3 个 pod 都处于 Running 状态:
# - cert-manager
# - cert-manager-cainjector
# - cert-manager-webhook

2. 配置 ClusterIssuer

ClusterIssuer 是 cert-manager 中用于签发证书的资源,它指定了证书颁发机构。

创建文件 lets-encrypt.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
email: # 替换为您的邮箱
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: lets-encrypt
solvers:
- http01:
ingress:
ingressClassName: traefik

应用配置:

1
kubectl apply -f lets-encrypt.yaml

3. 配置 Certificate

Certificate 资源定义了需要申请的证书信息。

创建文件 certificate.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: certificate
namespace: default # 必须与 IngressRoute 在同一命名空间
spec:
secretName: tls
duration: 2160h # 90 天
renewBefore: 720h # 到期前 30 天自动续期
dnsNames:
- # 域名
issuerRef:
name: letsencrypt
kind: ClusterIssuer

应用配置:

1
kubectl apply -f certificate.yaml

4. 配置 DNS 解析

必须先配置 DNS,Let’s Encrypt 才能完成域名验证,否则流程会卡住。

5. 配置 IngressRoute

修改 IngressRoute 配置以启用 HTTPS。

关键修改点:

1
2
3
4
5
6
7
8
spec:
entryPoints:
- web # HTTP 80 端口
- websecure # HTTPS 443 端口
routes:
# ... 路由配置 ...
tls:
secretName: tls # 引用 Certificate 创建的 Secret

应用配置:

1
kubectl apply -f ingress.yaml

安装中间件插件

这个插件的主要作用是请求校验与转发,即校验特定请求并将其转发到后端的 write Pod (StatefulSet 部署)。

安装时最重要的是目录结构,必须严格遵循以下方式:

1
2
3
4
5
./plugins-local/
└── src
└── github.com
└── traefik
└── (插件名)

踩坑记录:
我打包了一个辅助镜像作为 initContainers 来帮助下载和解压插件。一开始为了图方便,用 7zip 打包成了 .tar.gz 格式,结果解压后多了一层 src 目录,导致插件安装失败。后来老老实实换回命令行打包,问题解决

最后

其实整个部署过程中还遇到了不少问题,但其余多是些琐碎细节,例如如何备份、路由匹配规则怎么写、如何平滑迁移 Pod、应该使用什么类型的存储等等。虽然我对部署还不是特别精通,但至少靠自己跑通了完整流程,收获颇丰🥰。