本环境真实链路:k8s.ergesanbaishou.com → Ingress(hello1.qingfeng.dev)→ nginx-hello(qingfeng 空间)。证书统一挂外层 nginx,在 443 终止 TLS,后面集群内走 http —— 一处换证、全站生效,K8s 侧零改动。你机器上现在已有 80 版配置,只需按下面 ② 改 3 处即可升级 HTTPS:
浏览器 ──HTTPS:443──▶ 外层NGINX(证书在这,TLS终止)
│ 301(80→443)
└──HTTP:31588──▶ ingress-nginx(NodePort) ──▶ svc:8080 ──▶ pod:80(nginx-hello)
① 现状 —— 你机器上已有的 80 版配置(原样,可用):
# /etc/nginx/conf.d/k8s.ergesanbaishou.com.conf
# hello1.qingfeng.dev → K8s Ingress → nginx-hello(qingfeng 空间)
# 外层 nginx:80 → ingress-nginx NodePort:31588 → svc:8080 → pod:80
upstream k8s_ergesanbaishou {
server 10.18.170.34:31588; # ★ ingress-nginx 的 NodePort(80 对应)
keepalive 32;
}
server {
listen 80;
server_name k8s.ergesanbaishou.com;
access_log /var/log/nginx/hello1.access.log;
error_log /var/log/nginx/hello1.error.log;
location / {
proxy_pass http://k8s_ergesanbaishou;
# ★★★ 灵魂行:透传 Host,Ingress 全靠它分流,丢了必 404 ★★★
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1; # WebSocket 支持(可选)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
② 升级 HTTPS —— 在上面文件里改 3 处(★ 标出),完整 443 版如下,可整份覆盖:
# /etc/nginx/conf.d/k8s.ergesanbaishou.com.conf —— 443 完整版
# 浏览器 ──HTTPS:443──▶ 外层NGINX(TLS终止) ──HTTP:31588──▶ ingress ──▶ svc:8080 ──▶ pod:80
upstream k8s_ergesanbaishou {
server 10.18.170.34:31588; # ★ 改动0:不变,还是 ingress-nginx 的 NodePort
keepalive 32;
}
# ★ 改动1:原 80 的 location 整块删掉,只留一行 301 跳 https
server {
listen 80;
server_name k8s.ergesanbaishou.com;
return 301 https://$host$request_uri;
}
# ★ 改动2:新增 443 server(证书两行 + 原来的 location 整块搬进来)
server {
listen 443 ssl;
server_name k8s.ergesanbaishou.com;
# ★ 证书两行(fullchain 含中间证书;先自签,命令在下面)
ssl_certificate /etc/nginx/ssl/ergesanbaishou.com.pem;
ssl_certificate_key /etc/nginx/ssl/ergesanbaishou.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL_ergesan:10m;
access_log /var/log/nginx/hello1.access.log;
error_log /var/log/nginx/hello1.error.log;
location / {
proxy_pass http://k8s_ergesanbaishou;
# ★★★ 灵魂行:透传 Host,Ingress 全靠它分流,丢了必 404 ★★★
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 443 时=https,ingress 靠它识别原始协议
proxy_http_version 1.1; # WebSocket 支持(可选)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
# ★ 改动3:没有正式证书就先自签一把(浏览器会提示不安全,内网可用)
# mkdir -p /etc/nginx/ssl
# openssl req -x509 -nodes -days 825 -newkey rsa:2048 \
# -keyout /etc/nginx/ssl/ergesanbaishou.com.key \
# -out /etc/nginx/ssl/ergesanbaishou.com.pem \
# -subj "/CN=k8s.ergesanbaishou.com" \
# -addext "subjectAltName=DNS:k8s.ergesanbaishou.com,DNS:harbor.ergesanbaishou.com"
# 生效三连
nginx -t && systemctl reload nginx
curl -kv https://k8s.ergesanbaishou.com 2>&1 | grep subject # 应输出 CN=k8s.ergesanbaishou.com
nginx.ingress.kubernetes.io/ssl-redirect: "false",否则 80↔443 无限 301。tls: 段可以省;若想让 ingress 层也有一层,才走 A3 挂 Secret。/v2/ 和 /service/ 是 docker registry 协议入口,严禁 301 强跳(docker 跟随重定向会剥掉 Authorization → push 报 unauthorized);443 还要加 proxy_redirect 改写 http Location。完整终版配置见下方 ③ 与 Harbor 文档 FAQ ⑩.2~⑩.4(实测踩坑三部曲)。harbor.ergesanbaishou.com 建独立 conf 即可 —— upstream 改指 32588(harbor 的 NodePort),证书路径/自签同理;几十个域名全在外层换证,运维只盯 nginx 一个点。③ Harbor 域名的 SSL 终版配置(实测通过,docker push/pull + UI 全通):与普通站点差异极大 —— 80 端口对 /v2/、/service/ 必须直反代(token 换发与层上传会走 http),443 必须加 proxy_redirect 把 registry 吐的 http Location 改写成 https:
# /etc/nginx/conf.d/harbor.ergesanbaishou.com.conf —— 80+443 终版
upstream harbor_ergesanbaishou {
server 10.18.170.34:32588; # harbor 的 ingress NodePort(http)
keepalive 32;
}
# 80:/v2/ 和 /service/ 直接反代(registry 协议严禁 301);其余路径跳 https 给 UI
server {
listen 80;
server_name harbor.ergesanbaishou.com;
location /v2/ { # ★ registry 入口,严禁 301
proxy_pass http://harbor_ergesanbaishou;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 0; # push 大镜像层不限
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
location /service/ { # ★ token 签发入口,同样严禁 301
proxy_pass http://harbor_ergesanbaishou;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / { # 浏览器 UI 才跳 https
return 301 https://$host$request_uri;
}
}
# 443:SSL 终止,反代 UI/API
server {
listen 443 ssl;
server_name harbor.ergesanbaishou.com;
ssl_certificate /etc/nginx/ssl/harbor.ergesanbaishou.com.pem;
ssl_certificate_key /etc/nginx/ssl/harbor.ergesanbaishou.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL_harbor:10m;
access_log /var/log/nginx/harbor-ergesanbaishou.access.log;
error_log /var/log/nginx/harbor-ergesanbaishou.error.log;
client_max_body_size 0;
location / {
proxy_pass http://harbor_ergesanbaishou;
# ★★★ 银弹行:把 registry 返回的 http:// Location 改写成 https,
# 否则 PUT 跟 Location 走 80 被杀,报 unauthorized(Harbor FAQ ⑩.4)
proxy_redirect ~^http://harbor\.ergesanbaishou\.com(:\d+)?(/.*)$ https://harbor.ergesanbaishou.com$2;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
# 生效: nginx -t && systemctl reload nginx
域名 SSL 就是"浏览器地址栏那把 🔒":保护用户 → 你的网站的 HTTPS(443) 流量,证书绑定你的域名,挂载在 Ingress 上;它和 kubectl 用的集群内部证书(本页 B 部分)是两套完全独立的体系:
| 域名 SSL 证书(本部分) | 集群内部证书(B 部分) | |
|---|---|---|
| 保护什么 | 浏览器 → 网站的用户流量 | kubectl/组件之间的管理面通信 |
| 端口 | 443(Ingress 终止 TLS) | 6443 / 2379 / 10250 |
| 绑定什么 | 你的域名 k8s.example.com | 节点名 / kubernetes.default / 集群 IP |
| 挂载位置 | TLS Secret → Ingress tls.secretName | /etc/kubernetes/pki/ |
| 谁签发 | 公共 CA(Let's Encrypt/阿里云/DigiCert)或自签 | kubeadm 自建 CA |
| 过期后果 | 浏览器红色"不安全"、小程序/App 拒连 | kubectl 连不上集群 |
| 怎么续 | 换 Secret 里的证书 / cert-manager 自动 | kubeadm certs renew |
# 两条链路看流量,证书各管一段:
浏览器 ──HTTPS:443──▶ Ingress-nginx ──HTTP:80──▶ Service ──▶ Pod
▲ 域名 SSL 在这里校验(TLS 终止,后面集群内走明文)
kubectl ──HTTPS:6443──▶ apiserver
▲ 内部证书在这里校验(B 部分)
最常见场景:已经从阿里云/公司拿到了域名的证书文件,把它挂到 Ingress 上让网站变 HTTPS。三步搞定:
# 0. 你拿到的通常是两个文件(可能是 .crt/.key 或 .pem)
ls
fullchain.pem privkey.pem
# ★ fullchain = 域名证书 + 中间证书链;只放单张证书部分客户端会报"不受信任"
# (pfx 格式先转换)
# openssl pkcs12 -in cert.pfx -nokeys -out fullchain.pem
# openssl pkcs12 -in cert.pfx -nocerts -nodes -out privkey.pem
# 1. 存成 TLS Secret(★ namespace 必须和 Ingress 在同一个!)
kubectl create secret tls web-tls -n web \
--cert=fullchain.pem --key=privkey.pem
# 2. Ingress 引用(hosts 必须和证书域名完全一致,包括子域)
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
namespace: web
spec:
ingressClassName: nginx
tls:
- hosts:
- k8s.example.com
secretName: web-tls # ← 第 1 步创建的 Secret
rules:
- host: k8s.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
EOF
# 3. 验证 443 已用新证书
curl -kv https://k8s.example.com 2>&1 | grep -E 'subject:|expire'
subject: CN=k8s.example.com
expire date: Jan 20 00:00:00 2027 GMT
# 附:查看 Secret 里证书的域名/到期日
kubectl get secret web-tls -n web -o jsonpath='{.data.tls\.crt}' \
| base64 -d | openssl x509 -noout -subject -enddate
kubectl create secret tls web-tls --dry-run=client -o yaml | kubectl apply -f -,ingress-nginx 会自动 reload,通常 1 分钟内生效。nginx.ingress.kubernetes.io/ssl-redirect: "false"。Kubernetes Ingress Controller Fake Certificate);② 证书是 *.example.com 通配符,却用 a.b.example.com 访问 → 通配符只匹配一级子域。不想每年手动换证书?装 cert-manager,证书自动申请、自动挂载、到期自动续,公网域名全程零人工:
# 1. 安装 cert-manager(一次性,官方清单)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml
kubectl -n cert-manager get pods # 三个 Pod 全 Running 再继续
# 2. 配置签发器(HTTP-01 挑战;前提:域名已公网解析到 Ingress 入口、80 端口可达)
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@example.com # 到期提醒邮件
privateKeySecretRef:
name: letsencrypt-key
solvers:
- http01:
ingress:
ingressClassName: nginx
EOF
# 3a. 用法一(最省事):Ingress 加一个注解即可,Secret 自动生成
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt
# (tls.secretName 随便起个名,如 web-tls;cert-manager 会自动创建并填充)
# 3b. 用法二(更显式):Certificate 对象,指定域名清单
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: web-cert
namespace: web # ★ 和使用它的 Ingress 同 ns
spec:
secretName: web-tls # 签好后证书存在这个 Secret
dnsNames:
- k8s.example.com
issuerRef:
name: letsencrypt
kind: ClusterIssuer
EOF
# 4. 盯签发状态(READY=True 即成功,Secret 已生成)
kubectl get certificate -n web
NAME READY SECRET AGE
web-cert True web-tls 2m
# 卡在 False 就:kubectl describe certificate web-cert -n web 看 Events
https://acme-staging-v02.api.letsencrypt.org/directory,调通再切正式。*.example.com 不支持 HTTP-01,必须走 DNS-01(需配阿里云/Cloudflare 的 DNS Provider token)。describe certificate 看 Events → 多半是 80 端口外网不可达(HTTP-01 挑战失败)或域名解析没指向 Ingress;其次 kubectl get challenge -n web 看挑战详情。内网域名(如 harbor.qingfeng.local)没有公网解析,公共 CA 不给签 → 自己签一把,挂法与 A3 完全相同:
# 1. 一条命令自签(825 天,SAN 写全所有要访问的域名)
openssl req -x509 -nodes -days 825 -newkey rsa:2048 \
-keyout privkey.pem -out fullchain.pem \
-subj "/CN=harbor.qingfeng.local" \
-addext "subjectAltName=DNS:harbor.qingfeng.local,DNS:registry.qingfeng.local"
# 2. 存 Secret(与 A3 第 1 步相同)
kubectl create secret tls harbor-tls -n harbor \
--cert=fullchain.pem --key=privkey.pem
# 3. Ingress tls 段引用 secretName: harbor-tls(完整写法见 A3)
# 4. 验证
curl -kv https://harbor.qingfeng.local 2>&1 | grep subject
subject: CN=harbor.qingfeng.local
/etc/docker/certs.d/<域名>/ca.crt(containerd 用 /etc/containerd/certs.d/<域名>/hosts.toml 指定 ca)。| 现象 | 根因 | 解决方案 |
|---|---|---|
浏览器 ERR_CERT_COMMON_NAME_INVALID | 访问的域名 ≠ 证书 SAN(子域写错/通配符层级不符) | 核对证书 SAN 与访问域名;openssl x509 -text 查证书实际域名 |
| 浏览器"您的连接不是私密连接" | 自签证书 / 证书过期 / 缺中间证书 | 自签见 A5;过期换证;缺链把中间证书拼进 fullchain |
| Chrome 正常、部分客户端报 unknown authority | fullchain 只有域名证书没带中间证书 | cat 域名.crt 中间.crt > fullchain.pem 重建证书文件 |
| http 访问不跳 https | 外层没做 301 / Ingress 没配 tls | 外层 nginx return 301 https://...(见 A1);或 Ingress tls 段 |
| 配置 443 后无限重定向 ERR_TOO_MANY_REDIRECTS | TLS 在外层终止,Ingress 又强制跳 https | Ingress 注解 ssl-redirect: "false"(见 A1 要点) |
| 证书换了但生效的还是旧证书 | nginx 未 reload / 浏览器缓存 | nginx -s reload;无痕窗口验证;openssl s_client 看实际下发 |
cert-manager READY=False | HTTP-01 挑战失败(80 不通/解析没指对) | describe certificate 看 Events;放行 80;检查解析(见 A4) |
ERR_SSL_VERSION_OR_CIPHER_MISMATCH | 证书/私钥不配对(贴错文件) | 比对 modulus:openssl x509 -noout -modulus vs openssl rsa -noout -modulus |
nginx 报 no ssl_certificate is defined | listen 443 ssl 但没写证书两行 | 补 ssl_certificate / ssl_certificate_key |
docker push 报 authorization header is required to send HEAD | registry 的 /v2/ 被 80→443 301 强跳,重定向剥掉了 Authorization | 80 上 /v2/ 直接反代勿 301,其余路径才跳 https(详见 Harbor 文档 FAQ ⑩.2) |
curl -kv https://域名(看握手证书)→ openssl s_client -connect 域名:443 -servername 域名(看完整链)→ 看错误日志 tail -f /var/log/nginx/*error.log。Ingress 用法见 Ingress 使用文档。kubeadm init 预检后第一步就自动签发全套证书(B2),零操作;
② 我的机器上默认有吗? —— 凡是能跑起来的 K8s 就一定有:kubeadm / Docker Desktop / Minikube / Rancher 装的集群都自带。验证:kubectl config view --minify 看 server 是不是 https://xxx:6443 —— 是,说明内部证书正在默默工作(你每次 kubectl 都在用它);
③ 线上可以不用吗? —— 不存在"不用"。它不是可选项,是集群强制自带的:apiserver 只提供 HTTPS(明文端口 8080 早在 v1.20 就移除了),没这套证书集群根本起不来。线上要做的不是"部署"而是"运维":备份 ca.key(B2)+ 盯到期续期(B5,组件证书默认 1 年,过期 = kubectl 连不上 = 集群瘫痪)。可选项只有两个:B3 加 SAN(kubectl 需用外网 IP/域名/LB VIP 连 6443 时才做)、B4 手动 openssl(离线/对接企业 CA 才做)。kubeadm 部署的集群,证书全部集中在 /etc/kubernetes/pki/,由根 CA 统一签发;先把目录结构和用途看懂,后面所有操作都不慌:
/etc/kubernetes/pki/ # 集群所有证书的家(root 权限)
├── ca.crt ca.key # ★ 根 CA,其余证书全由它签(丢了=灾难,必备份)
├── apiserver.crt apiserver.key # ★ API Server 服务端证书(kubectl 连的就是它)
├── apiserver-kubelet-client.crt .key # apiserver → kubelet 的客户端证书
├── apiserver-etcd-client.crt .key # apiserver → etcd 的客户端证书
├── front-proxy-ca.crt # agg API( metrics-server 等)请求头认证 CA
├── front-proxy-client.crt .key # 对应客户端证书
└── etcd/
├── ca.crt ca.key # etcd 独立 CA(与集群 CA 分开)
├── server.crt server.key # etcd 服务端证书
├── peer.crt peer.key # etcd 节点间通信(多节点 etcd)
└── healthcheck-client.crt .key # 健康检查客户端证书
/etc/kubernetes/*.conf # admin/controller-manager/scheduler 的 kubeconfig,
# 内嵌客户端证书(续期时会一并更新)
/var/lib/kubelet/pki/ # kubelet 自己的证书(默认开启自动轮换)
| 证书 | 谁在用 | 认证方向 | 默认有效期 |
|---|---|---|---|
ca.crt | 所有组件信任的根 | 签发者(不给别人用) | 10 年 |
apiserver.crt | kubectl / kubelet / 所有客户端 → apiserver | 服务端(serverAuth) | 1 年 |
apiserver-kubelet-client | apiserver → kubelet(logs/exec) | 客户端(clientAuth) | 1 年 |
apiserver-etcd-client | apiserver → etcd | 客户端 | 1 年 |
etcd/server.crt | etcd 对外服务 | 服务端 | 1 年 |
*.conf 内嵌证书 | kubectl / scheduler 等控制面组件 | 客户端 | 1 年 |
| kubelet 客户端证书 | kubelet → apiserver | 客户端 | 1 年(自动轮换) |
kubectl → apiserver.crt(服务端被验证),kubelet/scheduler → *.conf 内嵌证书(客户端被验证),全部回溯信任 ca.crt —— 所以 ca.key 必须备份且不能外泄。用 kubeadm init 安装集群时,证书的"部署"是全自动的:init 在预检后第一步就生成根 CA 和全套组件证书,再启动各静态 Pod —— 这也是推荐方式:
# init 过程中与证书相关的两行日志:
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "apiserver" certificate and key on disk
# ↑ 此时已按默认 SAN(kubernetes/节点名/节点IP/10.96.0.1)生成证书
# 部署完成后立即确认证书状态(还能用多久一目了然)
kubeadm certs check-expiration
CERTIFICATE EXPIRES RESIDUAL CERTIFICATE AUTHORITY
apiserver Jan 20, 2027.. 364d ca
apiserver-kubelet-client Jan 20, 2027.. 364d ca
apiserver-etcd-client Jan 20, 2027.. 364d etcd-ca
...
ca Jan 18, 2036.. 3650d -
# apiserver 是怎么用这些证书的?(静态 Pod 启动参数引用)
grep -E 'tls-cert|client-ca' /etc/kubernetes/manifests/kube-apiserver.yaml
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
--tls-private-key-file=/etc/kubernetes/pki/apiserver.key
--client-ca-file=/etc/kubernetes/pki/ca.crt
# 指定自定义参数重装/重签时,certificatesDir 也可换位置(默认 /etc/kubernetes/pki)
kubernetes、kubernetes.default 等 Service 名、节点主机名、节点 IP、Service CIDR 首地址(10.96.0.1) —— 不含外网 IP/LB VIP,要加就走 B3。cp -r /etc/kubernetes /root/k8s-backup-$(date +%F) —— ca.key 一旦丢失,唯一出路是整集群重建。默认证书不含外网 IP、域名、负载均衡 VIP —— 一旦 kubectl 用这些地址连 6443 就会报 x509: certificate is valid for ..., not xxx。两种时机配置:
# kubeadm-init.yaml(v1.31+ 用 v1beta4;旧版集群用 v1beta3,字段相同)
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.35.9
apiServer:
certSANs: # ★ 核心字段:apiserver 证书的 SAN 列表
- "qingfeng" # 节点主机名
- "192.168.10.10" # 节点内网 IP
- "10.96.0.1" # Service CIDR 首地址(kubectl 集群内访问)
- "k8s.example.com" # 自定义域名(LB/外网入口)
- "192.168.10.100" # HA 集群的 LB VIP
# --- v1.27+ 还可定制签发时长(可选,默认 1 年) ---
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
certificatesExpiration:
unitDuration: 8760h # 单次签发时长,3650d=10年: 87600h
# 用配置文件安装
kubeadm init --config kubeadm-init.yaml
# 1. 把新 SAN 补进配置(或直接编辑现有 kubeadm.yaml 的 certSANs 列表)
# 2. 只重签 apiserver 证书(不动其他组件,ca.key 还在即可)
kubeadm init phase certs apiserver --config kubeadm-init.yaml
certsServedAPIServer="apiserver" [certs] Generating "apiserver" certificate and key
# 3. 让 apiserver 重新加载证书:重启静态 Pod(改 manifest 文件触发 kubelet 拉起新容器)
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/ && sleep 20 && mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
# 4. 验证 SAN 已包含新地址
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text | grep -A1 'Alternative'
DNS:kubernetes, DNS:qingfeng, DNS:k8s.example.com,
IP Address:192.168.10.10, IP Address:10.96.0.1, IP Address:192.168.10.100
# 5. kubectl 用新地址连通验证
kubectl --server=https://k8s.example.com:6443 get nodes
LB VIP + 域名。场景:离线环境、想要 10 年长证书、或对接企业自有 CA。生产仍推荐 kubeadm 托管,手动方式一般只替换个别证书。以 apiserver 为例:
# 0. 备份(动手前必做)
cp -r /etc/kubernetes/pki /root/pki-backup-$(date +%F)
# 1. 生成根 CA(已有 ca.crt/ca.key 可跳过)
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -subj "/CN=qingfeng-ca" -days 3650 -out ca.crt
# 2. 生成 apiserver 私钥 + CSR
openssl genrsa -out apiserver.key 2048
openssl req -new -key apiserver.key -subj "/CN=kube-apiserver" -out apiserver.csr
# 3. 写 SAN 扩展文件(serverAuth 用途)
cat > apiserver.ext <<EOF
subjectAltName=DNS:kubernetes,DNS:kubernetes.default,DNS:qingfeng,DNS:k8s.example.com,IP:192.168.10.10,IP:10.96.0.1,IP:127.0.0.1
extendedKeyUsage=serverAuth
EOF
# 4. 用根 CA 签发(此处签 10 年)
openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-days 3650 -extfile apiserver.ext -out apiserver.crt
# 5. 校验链 + 看 SAN + 看到期日
openssl verify -CAfile ca.crt apiserver.crt
openssl x509 -in apiserver.crt -noout -text | grep -A1 'Alternative'
openssl x509 -in apiserver.crt -noout -enddate
# 6. 替换并重启 apiserver(同 B3 时机 B 第 3 步)
mv apiserver.crt apiserver.key /etc/kubernetes/pki/
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/ && sleep 20 && mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
ca.key 的密码学属性(换了 CA = 全集群证书重签);② 客户端证书(如 *.conf 内嵌)不要手动 openssl 替换,交给 kubeadm certs renew;③ 替换后立即用 kubectl get nodes 验证,失败就回滚备份。# 1. 检查所有证书的剩余时间(在控制面节点执行)
kubeadm certs check-expiration
CERTIFICATE EXPIRES RESIDUAL CERTIFICATE AUTHORITY
apiserver Jan 20, 2027 364d ca
admin.conf Jan 20, 2027 364d ca
...
# 2. 单独看某一张证书的到期日
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -enddate
notAfter=Jan 20 03:12:00 2027 GMT
# 3. 一键续期全部(各证书再延 1 年;需 ca.key 存在)
kubeadm certs renew all
certificate embedded in the kubeconfig file for the admin.conf updated
...
# 4. ★ renew 不会自动重启静态 Pod,必须手动让 apiserver 等重载
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/ && sleep 20 && \
mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
# (scheduler/controller-manager/etcd 同理;偷懒可逐个 crictl stop 旧容器)
# 5. kubectl 的 kubeconfig 也内嵌了旧证书,重新拷贝
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# 6. 验证
kubectl get nodes && kubeadm certs check-expiration | head -4
rotateCertificates: true 自动轮换,不用管;服务端证书由 kubelet 自签,apiserver 通过 --kubelet-certificate-authority 校验。cron 季度跑一次 check-expiration 也不失为土办法。| 报错 | 根因 | 解决方案 |
|---|---|---|
x509: certificate has expired or is not yet valid | 证书真的过期 / 节点时间不同步 | 先 ntpdate 校时;再 check-expiration + renew(B5 节) |
x509: certificate is valid for ..., not xxx | SAN 缺少你访问用的 IP/域名 | certSANs 追加后重签 (B3 时机 B) |
certificate signed by unknown authority | kubeconfig 与集群 CA 不匹配(换过集群/残留旧 config) | 重新分发 admin.conf;检查 KUBECONFIG 变量 |
You must be logged in to the server (Unauthorized) | kubeconfig 内嵌客户端证书过期 | renew all 后重新 cp admin.conf ~/.kube/config |
remote error: tls: bad certificate | 双向认证中客户端证书缺失/不受信 | 检查 apiserver --client-ca-file 与客户端凭证是否同 CA |
kubeadm certs renew 报 missing CA | ca.key 被删/权限错 | 从备份恢复 pki;无备份只能重建 |
metrics-server 报 x509: cannot validate certificate | kubelet 自签服务端证书不被信 | metrics-server 加 --kubelet-insecure-tls 或签发 kubelet 服务端证书 |