最近在使用 Docker + K3s 搭建 GZCTF 平台时,遇到了一个后端服务无限重启的问题。经过一番排查,发现是 K8s 配置文件(kubeconfig)凭证失效导致的。特此记录一下排查过程和原理解析。
1. 问题现象
环境配置如下:
- GZCTF: 使用 Docker Compose 部署
- Kubernetes: 使用 K3s 作为集群后端
- 架构: GZCTF 容器通过挂载宿主机的
kube-config.yaml文件来连接 K3s 集群
服务正常运行了相当一段时间后,忽然有一天, GZCTF 的后台页面、比赛页面进不去了,一直卡在加载。
于是我把服务重启了一下,注意到 GZCTF 容器不断重启,查看日志发现后端一直报错,提示无法连接到 K8s API Server,状态码为 401 Unauthorized。
容器日志 (docker compose logs):
gzctf-1 | [ERR] KubernetesProvider: K8s 初始化失败,请检查相关配置是否正确 (https://x.x.x.x:6443)
gzctf-1 | k8s.Autorest.HttpOperationException: Operation returned an invalid status code 'Unauthorized', response body
gzctf-1 | {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Unauthorized","reason":"Unauthorized","code":401}
2. 排查过程
最初怀疑是网络问题或配置路径错误,但 Unauthorized 明确指向了身份验证失败。
为了验证是否是配置文件的问题,我在宿主机上直接使用该配置文件尝试连接集群:
root@hostname:/path/to/GZCTF# kubectl --kubeconfig ./kube-config.yaml get nodes
E1221 21:38:50.168514 18214 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
E1221 21:38:50.176447 18214 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
E1221 21:38:50.184038 18214 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
E1221 21:38:50.196097 18214 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
E1221 21:38:50.205985 18214 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
error: You must be logged in to the server (the server has asked for the client to provide credentials)
对比测试:
随后我使用 K3s 系统默认的配置文件进行测试:
# 使用 K3s 系统源文件测试
kubectl --kubeconfig /etc/rancher/k3s/k3s.yaml get nodes
结果成功:输出了节点列表,状态为 Ready。
结论: 宿主机系统里的 K3s 是一切正常的,问题出在我复制到项目目录下的 kube-config.yaml 文件内容已经失效了。
3. 解决方案
解决办法非常简单:用“原件”覆盖“复印件”,并修正 IP 地址。
第一步:复制最新配置
将 K3s 系统自动生成的最新配置文件复制到项目目录:
# 备份旧文件
mv ./kube-config.yaml ./kube-config.yaml.bak
# 复制最新文件
cp /etc/rancher/k3s/k3s.yaml ./kube-config.yaml
chmod 644 ./kube-config.yaml
第二步:修改 Server IP
K3s 默认生成的配置中,Server 地址是 https://127.0.0.1:6443。
但 GZCTF 运行在 Docker 容器内部,容器内的 127.0.0.1 指向容器自己,而非宿主机。因此必须将其修改为宿主机的 IP。
打开 ./kube-config.yaml 修改:
apiVersion: v1
clusters:
- cluster:
# 修改前: server: https://127.0.0.1:6443
server: https://xxx.xx.xx.xx:6443 # <--- 修改为宿主机真实IP
certificate-authority-data: ...
第三步:重启服务
docker compose down -v
docker compose up -d重启后日志显示 KubernetesProvider 初始化成功,服务恢复正常。
4. 深入解析:为什么凭证会失效?
排查完问题后,产生了一个疑问:为什么 /etc/rancher/k3s/k3s.yaml 永远是最新的,而我复制出来的文件过一段时间就不能用了?
于是我对比了一下原文件和最新的配置文件:

起初我甚至以为是服务器硬盘出现了硬件错误(0E了),因为刚开始没用 010Editor ,看着凭据的开头和末尾都长一样。
进一步观察才发现,俩文件并非一样,头尾都一样但中间不同(诶k3s怎么这么坏
最终查资料发现,这涉及到了 K8s 的安全机制。
- 证书轮换 (Certificate Rotation):
出于安全考虑,K3s 会定期(一般是一年)重置集群的 TLS 证书和密钥。 - 静态副本 vs 动态原件:
/etc/rancher/k3s/k3s.yaml(动态原件):这是由 K3s 守护进程直接管理的。只要 K3s 在运行,它就会确保这个文件里包含的是最新的凭据。./kube-config.yaml(静态副本):这是我之前手动复制出来的文件。当 K3s 进行了证书轮换后,系统里的凭据换了,但我手里的这一份并不会自动更新。
5. 一句话总结
K3s守护进程会定期更换集群的TLS证书和密钥,我在部署使复制出来的配置并不会自动更新,所以GZCTF对集群的访问出现了401未授权,需要管理员定期维护k8s配置文件。
声明:
确石如此 | 版权所有,违者必究 | 如未注明,均为原创 | 本站采用 BY-NC-SA4.0 协议进行授权
|
转载请注明原文链接