✅ 一、整体架构总结
| 项目 | 说明 |
|---|---|
| Kubernetes 版本 | v1.20.0 |
| 部署方式 | 使用官方工具 kubeadm 初始化控制平面,并加入工作节点 |
| 节点组成 | - yxa-master:控制平面(etcd, apiserver, controller-manager, scheduler) - yxa-worker1:NotReady(CNI 或 CIDR 问题) - yxa-worker2:Ready(可正常调度 Pod) |
| 容器运行时 | Docker (通过 docker build 构建应用镜像) |
| 网络插件 | Flannel (通过 kube-flannel.yaml 部署,使用 vxlan 模式) |
| DNS | CoreDNS(默认) |
| 监控 | metrics-server(当前失败,因镜像拉取问题) |
📌 集群状态:可用于测试,但存在稳定性隐患(controller/scheduler 高频重启)
🔧 二、关键配置项解析
1. Kubelet cgroup 驱动配置
文件路径(Ubuntu/Debian):
/etc/default/kubelet
内容:
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd
✅ 作用 :
强制 kubelet 使用 systemd 作为 cgroup 驱动,必须与 Docker 的驱动一致,否则 kubelet 启动失败或 Pod 无法创建。
✅ 为什么用 systemd?
- 现代 Linux 发行版(Ubuntu 18.04+、CentOS 7+)默认使用
systemd管理服务。 - 若 kubelet 用
cgroupfs而系统用systemd,会导致资源隔离混乱、节点 NotReady。
2. Docker 的 daemon.json 配置
# 1. 创建或编辑 daemon.json
sudo mkdir -p /etc/docker
sudo cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
# 2. 重载并重启 Docker
sudo systemctl daemon-reload
sudo systemctl restart docker
# 3. 验证
docker info | grep -i "cgroup driver"
# 输出应为:Cgroup Driver: systemd
三、实际用到的部署流程(回顾)
-
准备阶段
- 下载 Kubernetes 组件镜像(
k8s-images.tar) - 使用
docker load导入镜像(离线部署) - 配置
kubeadm-config.yaml(指定 podSubnet,如10.244.0.0/16)
- 下载 Kubernetes 组件镜像(
-
初始化集群
kubeadm init --config=kubeadm-config.yaml
-
配置 kubelet
- 设置
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd - 启动 kubelet:
systemctl enable --now kubelet
- 设置
-
部署网络插件
kubectl apply -f kube-flannel.yaml
-
加入工作节点
kubeadm join <master-ip>:6443 --token ... --discovery-token-ca-cert-hash ...
-
部署应用(当前目标)
- 从 GitLab 克隆代码(跳过 SSL 验证)
- 构建 Docker 镜像
- 复制镜像到
yxa-worker2 - 用
imagePullPolicy: Never部署 Deployment
四、控制平面高频重启问题已修复
问题现象
kube-controller-manager和kube-scheduler的RESTARTS > 200- 根本原因:
/etc/kubernetes/manifests/下的 Static Pod YAML 文件被反复修改(即使内容不变,metadata 变化也会触发重建)
解决方案
-
对关键 manifest 文件启用 Linux 不可变属性(immutable):
chattr +i /etc/kubernetes/manifests/kube-apiserver.yaml
chattr +i /etc/kubernetes/manifests/kube-controller-manager.yaml
chattr +i /etc/kubernetes/manifests/kube-scheduler.yaml
验证命令
lsattr /etc/kubernetes/manifests/kube-*.yaml # 应显示 "i" 标志
kubectl get pods -n kube-system # RESTARTS 不再增长
五、Kubelet 证书自动轮换已启用(生产就绪)
配置确认
/var/lib/kubelet/config.yaml中已设置:
rotateCertificates: true
featureGates:
RotateKubeletServerCertificate: true # (可选但已启用)
自动批准机制
- 已创建以下 RBAC 规则,实现 CSR 自动批准:
auto-approve-renewals-for-nodes→ 客户端证书auto-approve-renewals-for-serving-nodes→ 服务端证书
当前证书状态
notBefore=Dec 18 10:51:28 2025 GMT
notAfter=Dec 18 10:51:30 2026 GMT
✅ 有效期 1 年,近期签发,完全正常。
验证命令
kubectl get csr # 应无 Pending 请求
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
✅ 结果 :kubelet 证书将在到期前自动续订,无需人工干预。
一键健康检查脚本
#!/bin/bash
# 输出分隔符
divider="=========================================================================="
echo "$divider"
echo "=== Kubernetes Cluster Health Check ==="
echo "$divider"
# 1. 检查节点状态
echo "=== Node Status ==="
kubectl get nodes
# 2. 检查关键 Pod 状态
echo -e "\n$divider\n=== Critical Pods (kube-system) ==="
kubectl get pods -n kube-system | grep -E 'controller|scheduler|etcd|apiserver'
# 3. 检查 kubelet 客户端证书有效期
echo -e "\n$divider\n=== Kubelet Client Cert Expiry ==="
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -enddate
# 4. 检查控制平面证书有效期
echo -e "\n$divider\n=== Control Plane Certificates ==="
kubeadm certs check-expiration 2>/dev/null | grep -E "(NAME|apiserver|etcd)"
# 5. 检查 Pending CSR
echo -e "\n$divider\n=== Certificate Signing Requests ==="
kubectl get csr
# 6. 检查 etcd 健康状态
echo -e "\n$divider\n=== Etcd Health Check ==="
ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
--endpoints=https://127.0.0.1:2379 endpoint health
echo -e "\n$divider\n=== Health Check Complete ==="
#!/bin/bash
# check_cert_expiry.sh
# 用法: ./check_cert_expiry.sh gitlab2.ctjt.cn 30
DOMAIN=${1:-"gitlab2.ctjt.cn"}
DAYS_WARN=${2:-30} # 提前多少天告警
LOG_FILE="/var/log/cert-check.log"
# 获取证书过期时间戳
EXPIRY_TIMESTAMP=$(echo | openssl s_client -connect "$DOMAIN":443 2>/dev/null | openssl x509 -enddate -noout 2>/dev/null | cut -d= -f2)
if [ -z "$EXPIRY_TIMESTAMP" ]; then
echo "$(date): ❌ 无法获取 $DOMAIN 的证书信息" | tee -a "$LOG_FILE"
exit 1
fi
# 转为 Unix 时间戳
EXPIRY_EPOCH=$(date -d "$EXPIRY_TIMESTAMP" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
SECONDS_LEFT=$((EXPIRY_EPOCH - NOW_EPOCH))
DAYS_LEFT=$((SECONDS_LEFT / 86400))
# 记录日志
echo "$(date): $DOMAIN 证书剩余有效期: ${DAYS_LEFT} 天 (过期时间: $EXPIRY_TIMESTAMP)" >> "$LOG_FILE"
# 告警逻辑
if [ $DAYS_LEFT -lt 0 ]; then
MESSAGE="🚨 紧急!$DOMAIN 证书已过期!"
echo "$MESSAGE"
# 可在此处添加邮件/钉钉/企业微信通知
elif [ $DAYS_LEFT -le $DAYS_WARN ]; then
MESSAGE="⚠️ 警告:$DOMAIN 证书将在 $DAYS_LEFT 天后过期($EXPIRY_TIMESTAMP)"
echo "$MESSAGE"
# 示例:发送到 syslog 或文件
logger -t cert-check "$MESSAGE"
else
echo "✅ $DOMAIN 证书状态正常(剩余 $DAYS_LEFT 天)"
fi
六、内网自签名证书批量部署
# 创建 CA 目录
mkdir -p ~/internal-ca/{certs,private,newcerts}
cd ~/internal-ca
echo 1000 > serial
touch index.txt
# 生成根私钥
openssl genrsa -out private/ca.key.pem 4096
chmod 400 private/ca.key.pem
# 生成根证书(有效期 10 年)
openssl req -new -x509 -days 3650 \
-key private/ca.key.pem \
-out certs/ca.cert.pem \
-subj "/CN=Internal Root CA/O=YourOrg"
chmod 444 certs/ca.cert.pem
# 生成 gitlab2 的私钥
openssl genrsa -out gitlab2.key 2048
# 生成 CSR
openssl req -new -key gitlab2.key -out gitlab2.csr -subj "/CN=gitlab2.ctjt.cn"
# 用 CA 签发
openssl ca -batch -config openssl.cnf -extensions v3_req \
-days 3650 -in gitlab2.csr -out gitlab2.crt
#!/bin/bash
# install-ca.sh
# 用法: curl -s https://your-intranet/ca/install-ca.sh | sudo bash
CA_URL="http://your-intranet.local/ca/certs/ca.cert.pem"
DEST="/usr/local/share/ca-certificates/internal-root-ca.crt"
echo "📥 正在下载内部 CA 证书..."
curl -fL "$CA_URL" -o "$DEST"
if [ $? -eq 0 ]; then
update-ca-certificates
echo "✅ 内部 CA 证书已安装并信任!"
else
echo "❌ 下载失败,请检查网络或 URL"
exit 1
fi
运行:curl -s http://your-intranet.local/ca/install-ca.sh | sudo bash
七、证书过期的解决方案
方案 1: 使用环境变量绕过 SSL 验证(临时解决方案)
- 操作 :
- 在执行 Git 操作之前设置环境变量
GIT_SSL_NO_VERIFY=1。 - 示例命令:
GIT_SSL_NO_VERIFY=1 git pull origin main
- 在执行 Git 操作之前设置环境变量
- 优点 :
- 可以立即解决问题,无需修改系统或服务器配置。
- 缺点 :
- 安全性降低,因为这会忽略 SSL 证书的验证,建议仅在可信环境中使用。
方案 2: 使用 SSH 协议代替 HTTPS(推荐长期解决方案)
- 步骤 :
- 生成 SSH 密钥对(如果还没有)。
- 将公钥添加到 GitLab 账户中。
- 修改本地仓库的远程 URL 为 SSH 地址。
- 优点 :
- 完全避开 HTTPS 证书问题,提升安全性。
- 一旦配置完成,后续使用更加方便。
- 适用场景 :
- 对于开发人员来说是最理想的长期解决方案。
方案 3: 更新服务器上的 SSL 证书(根本解决方案)
- 操作 :
- 在服务器上生成新的 SSL 证书,并确保其正确配置和安装。
- 重载 GitLab 配置使更改生效。
- 优点 :
- 解决了证书问题的根本原因,所有用户无需额外操作。
- 缺点 :
- 需要服务器管理员权限以及对服务器进行更改。
- 如果不是自己管理服务器,则需要联系服务器管理员来执行此操作。
每种方案都有其适用场景和特点,选择最适合你当前情况的方法进行实施。对于大多数开发者而言,采用方案 2,即通过 SSH 进行连接,是既安全又高效的解决方案。
补充
证书配置完整指南
一、你的当前架构(确认)
-
GitLab 部署方式:Docker 容器(仅提供 HTTP,端口 8080)
-
HTTPS 实现方式:Nginx 反向代理(监听 443 端口)
-
证书存储位置:
/etc/nginx/conf.d/gitlab/gitlab2.ctjt.cn.pem ← 证书
/etc/nginx/conf.d/gitlab/gitlab2.ctjt.cn.key ← 私钥
- Nginx 配置文件 :
/etc/nginx/conf.d/gitlab2.conf - 问题 :证书已于
2024-11-20过期,需更换。
二、证书类型与选择
| 类型 | 是否需要联网 | 浏览器信任 | 适用场景 | 生成方式 |
|---|---|---|---|---|
| Let's Encrypt(公共 CA) | ✅ 必须(至少一次) | ✅ 自动信任 | 公网服务、对外访问 | certbot |
| 自签名证书 | ❌ 完全不需要 | ❌ 显示"不安全" | 内网、测试、开发 | openssl |
| mkcert(本地 CA) | ⚠️ 首次安装需联网 | ✅ 本地自动信任 | 内网开发/测试 | mkcert |
✅ 结论:
- 如果服务器有公网 IP + 域名可解析 → 用 Let's Encrypt;
- 如果纯内网、无外网 → 用 自签名证书 或 mkcert。
三、方案 A:使用 Let's Encrypt(推荐用于公网)
前提条件
- 域名
gitlab2.ctjt.cn已在 DNS 服务商(如阿里云)配置 A 记录,指向服务器公网 IP; - 服务器 80 端口对外开放(安全组 + 防火墙);
- 临时可停 Nginx(或改用 webroot 模式避免停机)。
操作步骤
# 1. 停止 Nginx(standalone 模式需要独占 80 端口)
sudo systemctl stop nginx
# 2. 安装 certbot(若未安装)
sudo apt update && sudo apt install certbot -y
# 3. 申请证书
sudo certbot certonly --standalone -d gitlab2.ctjt.cn
# 按提示输入邮箱、同意条款
# 4. 复制证书到 Nginx 目录
sudo cp /etc/letsencrypt/live/gitlab2.ctjt.cn/fullchain.pem /etc/nginx/conf.d/gitlab/gitlab2.ctjt.cn.pem
sudo cp /etc/letsencrypt/live/gitlab2.ctjt.cn/privkey.pem /etc/nginx/conf.d/gitlab/gitlab2.ctjt.cn.key
# 5. 重载 Nginx
sudo nginx -t && sudo systemctl reload nginx
自动续期(重要!)
# 编辑定时任务
sudo crontab -e
0 2 1 * * /usr/bin/certbot renew --quiet && /bin/systemctl reload nginx
🔔 注意:自动续期建议后续改用
--webroot或--nginx插件,避免停服务。
四、方案 B:自签名证书(适用于纯内网/离线环境)
特点
- ✅ 完全离线生成
- ❌ 浏览器会警告"证书不安全"
- ✅ 适合内部系统、测试环境
生成命令(一行搞定)
sudo openssl req -x509 -nodes -days 3650 \
-newkey rsa:2048 \
-keyout /etc/nginx/conf.d/gitlab/gitlab2.ctjt.cn.key \
-out /etc/nginx/conf.d/gitlab/gitlab2.ctjt.cn.pem \
-subj "/CN=gitlab2.ctjt.cn"
后续操作
# 重载 Nginx
sudo nginx -t && sudo systemctl reload nginx
(可选)手动信任证书(内网设备)
- 将
.pem文件导入操作系统"受信任的根证书颁发机构"; - 或使用
mkcert替代(见下文)。
五、方案 C:使用 mkcert(内网开发最佳实践)
优势
- 生成的证书在本地浏览器 自动显示为安全(无警告);
- 支持多域名、localhost、IP 等。
使用前提
- 需在 一台联网机器 上首次安装
mkcert和根证书; - 之后可离线生成新证书。
安装与使用(在联网机操作)
# 安装 mkcert
sudo apt install mkcert -y # Ubuntu/Debian
# 或 brew install mkcert # macOS
# 安装本地 CA 到系统信任库
mkcert -install
# 生成证书
mkcert gitlab2.ctjt.cn
# 输出:gitlab2.ctjt.cn.pem 和 gitlab2.ctjt.cn-key.pem
# 拷贝到离线服务器对应目录即可
六、如何检查证书是否过期?
查看本地 PEM 文件有效期
openssl x509 -in /etc/nginx/conf.d/gitlab/gitlab2.ctjt.cn.pem -noout -dates
输出示例:
notBefore=Nov 20 00:00:00 2023 GMT
notAfter=Nov 20 23:59:59 2024 GMT ← 过期时间
通过网络检查(需服务运行)
echo | openssl s_client -connect gitlab2.ctjt.cn:443 2>/dev/null | openssl x509 -noout -dates
七、常见错误与排查
| 错误 | 原因 | 解决 |
|---|---|---|
NXDOMAIN |
域名无 DNS 解析 | 在阿里云/腾讯云添加 A 记录 |
Could not bind to :80 |
80 端口被占用 | systemctl stop nginx 或改用 --webroot |
Certificate has expired |
证书过期 | 按上述方案更新证书 |
| 浏览器仍报证书错误 | Nginx 未重载 / 证书路径错误 | 检查 ssl_certificate 路径 + nginx -t |
八、总结建议
| 场景 | 推荐方案 |
|---|---|
| 服务器有公网 IP,域名可解析 | ✅ Let's Encrypt + 自动续期 |
| 纯内网,无外网,接受浏览器警告 | ✅ 自签名证书(openssl) |
| 内网开发,希望无警告 | ✅ mkcert(提前在联网机生成) |
| 完全离线且需长期使用 | ✅ 自签名证书 + 手动信任 |
💡 最后提醒 :
GitLab 容器本身 不需要配置证书 (因为你用的是 Nginx 反向代理)。
所有证书操作都在 Nginx 层面 完成,GitLab 保持
external_url 'http://...'即可。