公网K8S集群搭建实战:安全架构设计与避坑指南

发布时间:2026/10/25 5:34:25
公网K8S集群搭建实战:安全架构设计与避坑指南 1. 公网K8S搭建的整体设计与思路拆解把一套 Kubernetes 集群暴露到公网和在内网里搭个玩具集群完全是两码事。内网集群你随便折腾节点之间裸奔通信也没人管一旦挂到公网每天扫描 6443、2379、10250 这些端口的机器人流量能把你日志刷爆。我前后搭过几套面向公网提供服务的集群踩过的坑足够写一本小册子这里把整套思路和落地细节摊开讲。先说清楚这套东西是什么、解决什么问题。公网K8S搭建指的是在云主机或独立服务器上从零构建一套 Kubernetes 集群并让它能够安全、稳定地对外提供 HTTP/HTTPS 服务。它解决的核心问题是如何让集群的控制面和节点在不可信网络环境下依然保持可用、可控、可观测。适合谁来参考有一定 Linux 基础、想自己掌控一套生产级集群的开发者、运维人员以及需要把内部服务对外发布的小团队。如果你只是想学 K8S 的基本概念Minikube 或 Kind 更合适但如果你要让真实流量打进来那这套流程就是绕不开的。整体设计上我倾向于把集群拆成三个层次来考虑控制面层、工作负载层、入口层。控制面层负责调度和状态管理工作负载层跑业务 Pod入口层负责把公网流量安全地引进来。这三层在公网环境下的安全要求完全不同必须分开设计。控制面要尽量少暴露端口工作负载层要隔离网络入口层才是真正对外的那道门。方案选型上我最终采用的是kubeadm 引导 容器运行时 containerd CNI 选 Calico 入口用 Ingress-Nginx 证书用 cert-manager这套组合。为什么这么选kubeadm 是官方维护的引导工具升级路径清晰比二进制手工搭建省心太多也比各种封装发行版更透明——出了问题你能直接查到 K8S 原生文档。containerd 相比 Docker 更轻K8S 从 1.24 起就移除了 dockershim直接用 containerd 少一层抽象资源占用和故障面都更小。Calico 的 BGP 模式和 NetworkPolicy 支持成熟公网环境下网络策略是刚需能防止某个被攻破的 Pod 横向扫描整个集群。Ingress-Nginx 生态最全注解丰富和 cert-manager 配合自动签发续期证书几乎是标配。这里有个关键取舍要讲清楚控制面到底要不要暴露公网。我的做法是控制面 API Server 只对固定的运维出口 IP 开放通过安全组白名单限制绝不 0.0.0.0/0 全开。节点加入集群时走的是内网地址公网只留入口层的 80/443。这样即使 API Server 有未公开漏洞攻击面也被压到最小。很多人图省事把 6443 全开结果被扫到弱口令或者未授权访问集群直接被人拿去挖矿这种案例我见过不止一次。还有一个容易被忽略的设计点etcd 的部署形态。kubeadm 默认把 etcd 以静态 Pod 形式跑在控制面节点上三节点控制面就是三个 etcd 成员。公网环境下 etcd 的 2379/2380 端口绝对不能对外节点间通信必须走内网或者加密通道。如果你的云主机跨可用区内网延迟要提前测etcd 对延迟敏感跨区延迟超过 10ms 就可能出现选主抖动。2. 核心细节解析与实操要点2.1 主机规划与系统预配置动手之前先把机器规划定死后面所有步骤都依赖这个基础。我一般用三台控制面加若干工作节点的结构控制面奇数台保证 etcd 仲裁。假设用某云厂商的三台 4C8G 机器做控制面两台 8C16G 做工作节点操作系统统一用较新的 LTS 版本内核版本不低于 5.4因为 Calico 的 eBPF 特性和一些网络优化需要较新内核。系统预配置这块每一步都有它的道理不是照抄命令就完事。关闭 swap 是因为 kubelet 的调度假设内存是可预测的swap 会让 Pod 的内存限制失效导致 OOM 行为不可控。关闭防火墙或者放行特定端口是因为 K8S 组件之间通信端口众多用 firewalld 逐条放行容易漏公网环境下更推荐用云安全组在外部做第一道过滤主机内部用 iptables 规则精细控制。设置内核参数net.bridge.bridge-nf-call-iptables1是为了让桥接流量经过 iptables否则 Service 的 DNAT 规则对跨桥流量不生效这是新手最常踩的坑之一。时间同步必须做而且要用可靠的时间源。etcd 和证书校验都依赖时间节点间时间偏差超过几秒就可能出现证书验证失败或者选主异常。我习惯装 chrony 并指向内网时间服务器公网 NTP 源在云环境里经常被限速。主机名规划也有讲究。kubeadm 初始化时会把主机名注册进集群如果主机名重复或者含特殊字符后续节点加入会出问题。我统一用k8s-master-01、k8s-worker-01这种规范命名并在/etc/hosts里把内网 IP 和主机名对应写好避免依赖 DNS 解析带来的不确定性。注意所有节点的主机名必须唯一且初始化后不要再改。改主机名会导致 kubelet 注册的 Node 对象和证书里的 CN 不匹配节点直接 NotReady。2.2 容器运行时与 kubelet 安装containerd 的安装我倾向用官方二进制包而不是发行版仓库里的版本仓库版本往往偏旧和较新 K8S 的 CRI 接口可能有兼容问题。安装后要生成默认配置并调整两个关键点一是SystemdCgroup true让 containerd 用 systemd 管理 cgroup和 kubelet 的 cgroup 驱动保持一致否则会出现 Pod 启动后资源限制不生效的诡异现象二是配置镜像加速或者私有仓库地址公网拉取镜像慢会拖垮整个部署节奏。kubelet、kubeadm、kubectl 三个组件的版本必须严格一致小版本都不能差。我见过有人 kubeadm 是 1.28.2 而 kubelet 是 1.28.0初始化时直接报版本偏差错误。安装时用包管理器锁定版本避免后续apt upgrade把版本升乱。kubelet 安装后先别急着启动等 kubeadm init 跑完再让它起来否则它会反复重启刷日志。这里补充一个实操细节kubelet 的--node-ip参数在双网卡机器上必须显式指定。云主机经常有内网网卡和公网网卡两块kubelet 默认可能选错 IP 注册到集群导致其他节点连不上它。我在 kubeadm 的配置里通过nodeRegistration.kubeletExtraArgs把node-ip固定为内网地址这个坑踩过一次就忘不了。2.3 集群初始化与网络插件部署kubeadm 的初始化配置文件是整个搭建过程的核心我习惯写一个kubeadm-config.yaml而不是用命令行参数因为配置可版本化、可复用、可审计。配置文件里几个关键字段controlPlaneEndpoint指向一个内网负载均衡地址或者第一台控制面的内网 IP 加端口podSubnet和serviceSubnet要和后续 CNI 的网段规划一致imageRepository指向可访问的镜像仓库。初始化命令跑完后按提示配置 kubectl 的 kubeconfig然后立刻部署 CNI。Calico 我用的是 operator 方式或者 manifest 方式取决于集群规模。小集群直接 apply manifest 最快注意把CALICO_IPV4POOL_CIDR改成和podSubnet一致否则 Pod 拿不到 IP。Calico 的IP_AUTODETECTION_METHOD要设成can-reach或者指定网卡名确保它选对内网网卡做 BGP 宣告公网网卡上跑 BGP 是灾难。网络插件起来后用kubectl get nodes确认所有节点 Ready用kubectl get pods -n kube-system确认 CoreDNS 和 Calico 的 Pod 全部 Running。如果 CoreDNS 一直 Pending八成是 CNI 没装好或者网段冲突如果 Calico 的 Pod CrashLoop去看日志里是不是网卡选错了或者内核模块没加载。提示初始化前先用kubeadm config images pull把镜像拉下来能提前暴露镜像仓库访问问题避免 init 跑到一半卡住。3. 实操过程与核心环节实现3.1 控制面高可用与安全加固落地单控制面集群在公网环境里没有容错能力机器一挂整个集群失联。我推荐至少三控制面。kubeadm 支持 stacked etcd 模式三台控制面各自跑一个 etcd 成员前面挂一个内网负载均衡把 6443 端口转发到三台机器。负载均衡用云厂商的内网 CLB 或者自己用 keepalived 加 haproxy 都行关键是健康检查要探 API Server 的/healthz端点。初始化第一台控制面时用--upload-certs把证书加密上传后续两台用kubeadm join带--control-plane和--certificate-key加入。加入顺序有讲究一台一台来每台加入后确认 etcd 成员健康再操作下一台。三台都加入后用kubectl get pods -n kube-system -l componentetcd确认三个 etcd Pod 都在各自节点上 Running再用etcdctl endpoint health逐个检查。安全加固这块我做了几件事。第一API Server 的--authorization-mode确保包含Node,RBAC默认就是但有人改配置时容易漏。第二给 kubelet 开启--anonymous-authfalse和--authorization-modeWebhook防止未认证请求访问 kubelet 的 10250 端口这个端口在公网暴露且未加固的话攻击者可以直接在节点上执行命令。第三用 NetworkPolicy 默认拒绝所有入站流量再按需放行Calico 支持这个能力一条默认 deny 策略能挡掉大量横向渗透。审计日志也要开。API Server 的--audit-log-path和--audit-policy-file配置好记录谁在什么时候对什么资源做了什么操作。公网集群出问题时审计日志是排查的第一手材料。日志轮转用--audit-log-maxbackup和--audit-log-maxsize控制别让日志把磁盘写满。3.2 入口层与证书自动化配置入口层是公网流量的唯一入口这块配好了后面的服务发布就是流水线作业。Ingress-Nginx 我用 Helm 安装values 里重点调几个参数controller.service.type设为 LoadBalancer 或者 NodePort 配合外部负载均衡controller.config.proxy-body-size按业务调整默认 1m 往往不够controller.metrics.enabled打开监控controller.podAntiAffinity让多个副本分散到不同节点。cert-manager 负责证书全生命周期。装完后创建 ClusterIssuer用 ACME 的 HTTP-01 或者 DNS-01 验证。HTTP-01 要求域名解析到入口 IP 且 80 端口可达适合简单场景DNS-01 适合泛域名或者入口不直接暴露 80 的情况需要配置 DNS 服务商的 API 凭证。我一般用 DNS-01 加泛域名证书一张证书覆盖所有子域名省去每个服务单独签发的麻烦。证书自动续期要验证。cert-manager 默认在证书到期前 30 天续期但续期失败不会主动告警。我加了一条 Prometheus 告警规则监控certmanager_certificate_expiration_timestamp_seconds减去当前时间小于 14 天就触发给自己留足排查时间。实测下来DNS-01 续期偶尔会因为 DNS 服务商 API 限流失败有告警才能及时发现。注意Ingress 的 TLS secret 和 cert-manager 的 Certificate 资源要对应好域名变更时记得同步改 Ingress 的tls.hosts否则证书签了但没被引用浏览器照样报不安全。3.3 工作节点加入与污点调度策略工作节点加入集群用kubeadm join带 token 和 CA 证书 hash。token 默认 24 小时过期大批量加节点时记得用kubeadm token create --ttl 0生成长期 token加完再清理。加入后给节点打标签比如node-role.kubernetes.io/worker方便后续用 nodeSelector 或者 affinity 调度。控制面节点默认带node-role.kubernetes.io/control-plane:NoSchedule污点业务 Pod 不会调度上去。如果集群资源紧张想让控制面也跑业务可以去掉污点但我不建议——控制面节点跑业务会争抢资源etcd 对 IO 和 CPU 敏感业务高峰时可能拖垮控制面。真要利用资源单独加一台工作节点更稳妥。节点维护时用kubectl drain驱逐 Pod加--ignore-daemonsets和--delete-emptydir-data维护完kubectl uncordon恢复。公网集群里节点重启后kubelet 会自动重连但如果有 Pod 用了 hostNetwork 或者本地存储重启后状态可能不一致维护窗口内要重点检查这类 Pod。4. 常见问题与排查技巧实录公网集群的问题排查和内网差别很大很多问题是被外部流量触发的。我把踩过的典型问题整理成速查表附上排查思路。现象可能原因排查与解决节点 NotReadykubelet 日志报证书错误节点时间偏差大或证书过期检查 chrony 同步状态kubeadm certs check-expiration查看证书有效期Pod 无法解析域名CoreDNS Pod 异常或 CNI 网段冲突kubectl logs看 CoreDNS检查 podSubnet 与节点网段是否重叠Ingress 返回 502后端 Service 无可用 Endpoint 或端口不匹配kubectl get endpoints确认后端 Pod 就绪核对 Service targetPort证书签发一直 PendingACME 验证失败DNS 未生效或 80 端口不通kubectl describe certificate看事件用dig确认解析检查安全组API Server 偶发超时etcd 跨区延迟高或磁盘 IO 瓶颈测节点间延迟etcdctl check perf看 etcd 性能换 SSD 云盘节点被扫描端口告警安全组规则过宽或 kubelet 未加固收紧安全组确认 kubeletanonymous-authfalse除了表格里的还有几个独家避坑技巧值得单独说。第一公网集群一定要配 Pod 的出口 IP 伪装。Calico 默认对出集群流量做 SNAT但如果配置不当Pod 访问外网时源 IP 是 Pod IP外部服务可能拒绝或者日志里全是内网地址。检查calicoctl get ippool的natOutgoing是否为 true。第二Ingress 的 real_ip 配置。流量经过云负载均衡后后端看到的源 IP 是负载均衡的地址业务日志里全是同一个 IP。要在 Ingress-Nginx 的 ConfigMap 里配use-forwarded-headers: true和compute-full-forwarded-for: true并设置proxy-real-ip-cidr为负载均衡的网段这样X-Forwarded-For才能正确传递真实客户端 IP。第三etcd 备份必须做且要验证恢复。公网集群被攻击或者误操作的概率比内网高etcd 快照是最后的救命稻草。我写了个定时任务每小时etcdctl snapshot save到对象存储每周做一次恢复演练到临时集群验证快照可用。只备份不验证等于没备份这个道理在真出事的时候体会最深。第四监控告警要覆盖控制面组件。kube-scheduler、kube-controller-manager、etcd 的指标默认只监听 127.0.0.1Prometheus 抓不到。要么改监听地址加认证要么用 kube-state-metrics 加 node-exporter 的组合间接监控。我倾向后者改动小、风险低。我个人在实际操作中的体会是公网 K8S 搭建的难点从来不在“搭起来”而在“搭起来之后怎么守住”。初始化那半小时的顺利程度和后面几个月的稳定运行没有必然关系。真正决定成败的是安全组规则有没有收紧、证书续期有没有告警、etcd 备份有没有验证、审计日志有没有人看。这几件事做到位集群才能安心对外提供服务。最后再分享一个小技巧每次变更集群配置前先用kubeadm config view导出当前配置存档出问题时能快速对比差异比凭记忆回滚靠谱得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询