运维知识
悠悠
2026年10月23日

不用 kubeadm:从零用二进制部署一套高可用 Kubernetes 集群

本文基于当前 kubernetes-the-hard-way 仓库整理。目标不是提供一键脚本,而是通过手工部署理解证书、kubeconfig、systemd、容器运行时和 Pod 网络如何组成 Kubernetes 集群。

1. 部署目标

本实验使用 1 台管理机、1 台控制平面和 2 台工作节点:

                      kubectl
                       |
                       v
               server:6443
        +---------------------------+
        | kube-apiserver            |
        | kube-controller-manager   |
        | kube-scheduler            |
        | etcd                       |
        +---------------------------+
                    |
          +---------+---------+
          |                   |
       node-0              node-1
    containerd/kubelet  containerd/kubelet
    kube-proxy/CNI      kube-proxy/CNI
    10.200.0.0/24       10.200.1.0/24

这套拓扑只有一个控制平面节点和一个 etcd 成员,适合学习与实验,不具备生产环境所需的高可用能力。

2. 环境与版本

操作系统为 Debian 12(Bookworm),支持 AMD64 和 ARM64。最低资源如下:

主机用途CPU内存磁盘
jumpbox管理、签发证书、分发文件1 核512 MB10 GB
serveretcd 与控制平面1 核2 GB20 GB
node-0工作节点1 核2 GB20 GB
node-1工作节点1 核2 GB20 GB

仓库主要使用 Kubernetes v1.32.3、crictl v1.32.0、CNI Plugins v1.6.2、containerd v2.1.x 和 etcd v3.6.x。当前下载清单中的 containerd、etcd、runc 含 beta 或 RC 构建,只应视为实验版本;长期运行时应换成经过验证的稳定版本,并校验 SHA256 或签名。

地址规划如下:

  Service CIDR:10.32.0.0/24
Pod CIDR:    10.200.0.0/16
node-0 Pod:  10.200.0.0/24
node-1 Pod:  10.200.1.0/24

在 jumpbox 创建 machines.txt,将示例地址改成实际 IP:

  192.168.10.10 server.kubernetes.local server
192.168.10.11 node-0.kubernetes.local node-0 10.200.0.0/24
192.168.10.12 node-1.kubernetes.local node-1 10.200.1.0/24

三台集群机器必须互通,并可由 jumpbox 通过 SSH 访问。仓库使用 root 简化实验;真实环境应使用普通运维账号配合 sudo,并限制 SSH 来源。

3. 准备管理机和二进制文件

在 jumpbox 安装工具并进入仓库:

  apt-get update
apt-get -y install wget curl vim openssl git

git clone --depth 1 \
  https://github.com/kelseyhightower/kubernetes-the-hard-way.git
cd kubernetes-the-hard-way

根据 Debian 架构下载二进制:

  wget -q --show-progress \
  --https-only \
  --timestamping \
  -P downloads \
  -i downloads-$(dpkg --print-architecture).txt

解压后将程序整理到四类目录:

  downloads/client/       kubectl、etcdctl
downloads/controller/   etcd、kube-apiserver、controller-manager、scheduler
downloads/worker/       containerd、runc、crictl、kubelet、kube-proxy
downloads/cni-plugins/  bridge、loopback、host-local 等插件

最后增加执行权限:

  chmod +x downloads/{client,controller,worker}/*

4. 建立集群信任链

二进制部署最重要的不是复制程序,而是建立组件间的身份认证。整个集群共享一个自签名 CA,再为各组件签发证书。仓库 ca.conf 定义了以下身份:

证书关键身份用途
adminO=system:masters集群管理员
node-0、node-1CN=system:node:节点名,O=system:nodeskubelet 节点认证
kube-proxyO=system:node-proxierService 转发规则
controller-manager系统组件身份控制器访问 API Server
scheduler系统组件身份调度器访问 API Server
kube-api-server带服务端 SANAPI Server TLS 及访问 kubelet
service-accounts独立密钥对签发 ServiceAccount Token

创建 CA:

  openssl genrsa -out ca.key 4096
openssl req -x509 -new -sha512 -noenc \
  -key ca.key -days 3653 \
  -config ca.conf -out ca.crt

随后按照 docs/04-certificate-authority.md,为 admin、两个工作节点、kube-proxy、kube-controller-manager、kube-scheduler、kube-api-server 和 service-accounts 生成私钥、CSR 与证书。

这里有两个关键点:

    1. kubelet 用户名必须是 system:node:NodeName,组织必须是 system:nodes,否则 Node Authorizer 会拒绝请求。
    1. API Server 证书 SAN 必须覆盖实际访问入口,包括 server.kubernetes.local、服务器 IP、Kubernetes Service 名称和 Service ClusterIP。

证书应按最小权限分发:节点只获取自己的 kubelet 证书和 CA;server 保存控制平面需要的文件;ca.key 等私钥不能无差别复制。

5. 生成 kubeconfig 和 Secret 加密配置

本实验需要 admin、controller-manager、scheduler、kube-proxy、node-0 和 node-1 六类 kubeconfig。以 node-0 为例:

  kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.crt \
  --embed-certs=true \
  --server=https://server.kubernetes.local:6443 \
  --kubeconfig=node-0.kubeconfig

kubectl config set-credentials system:node:node-0 \
  --client-certificate=node-0.crt \
  --client-key=node-0.key \
  --embed-certs=true \
  --kubeconfig=node-0.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:node:node-0 \
  --kubeconfig=node-0.kubeconfig

kubectl config use-context default --kubeconfig=node-0.kubeconfig

其他组件同理,只需替换证书和用户。节点配置分别放到 /var/lib/kubelet/kubeconfig 和 /var/lib/kube-proxy/kubeconfig,controller-manager、scheduler 和 admin 配置下发到 server。

为避免 Secret 明文写入 etcd,再创建静态加密配置:

  export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
envsubst < configs/encryption-config.yaml > encryption-config.yaml
scp encryption-config.yaml root@server:~/

仓库使用 AES-CBC 加密 Secret,并保留 identity 作为读取旧数据的回退提供者。加密密钥必须单独备份,否则已有加密数据将无法恢复。

6. 部署 etcd

将 etcd、etcdctl 和 etcd.service 复制到 server,随后安装:

  mv etcd etcdctl /usr/local/bin/
mkdir -p /etc/etcd /var/lib/etcd
chmod 700 /var/lib/etcd
cp ca.crt kube-api-server.key kube-api-server.crt /etc/etcd/
mv etcd.service /etc/systemd/system/

systemctl daemon-reload
systemctl enable --now etcd
etcdctl member list

实验中的 etcd 仅监听 127.0.0.1:2379,因为它与 API Server 位于同一台机器。这样减少了暴露面,但 server 故障会让整个控制面不可用。生产环境应使用 3 或 5 个 etcd 成员,启用客户端和 Peer TLS,并定期做快照与恢复演练。

7. 部署控制平面

在 server 安装组件并建立目录:

  mv kube-apiserver kube-controller-manager \
  kube-scheduler kubectl /usr/local/bin/
mkdir -p /etc/kubernetes/config /var/lib/kubernetes

将 CA、API Server 证书、ServiceAccount 密钥、加密配置和各组件 kubeconfig 放入 /var/lib/kubernetes,scheduler 配置放入 /etc/kubernetes/config,再安装三个 systemd unit。

API Server 的核心参数包括:

  --authorization-mode=Node,RBAC
--etcd-servers=http://127.0.0.1:2379
--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml
--service-node-port-range=30000-32767
--client-ca-file=/var/lib/kubernetes/ca.crt
--tls-cert-file=/var/lib/kubernetes/kube-api-server.crt
--tls-private-key-file=/var/lib/kubernetes/kube-api-server.key

controller-manager 负责两个集群网段:

  --cluster-cidr=10.200.0.0/16
--service-cluster-ip-range=10.32.0.0/24

启动控制平面:

  systemctl daemon-reload
systemctl enable --now kube-apiserver kube-controller-manager kube-scheduler
systemctl --no-pager --full status \
  kube-apiserver kube-controller-manager kube-scheduler

API Server 访问 kubelet 还需要 RBAC 授权:

  kubectl apply \
  --kubeconfig admin.kubeconfig \
  -f kube-apiserver-to-kubelet.yaml

验证版本和健康状态:

  curl --cacert ca.crt https://server.kubernetes.local:6443/version
kubectl get componentstatuses --kubeconfig admin.kubeconfig

如启动失败,优先查看日志:

  journalctl -u kube-apiserver -u kube-controller-manager \
  -u kube-scheduler --since "10 min ago" --no-pager

8. 部署 Worker 节点

每个 Worker 都安装 runc、CNI Plugins、containerd、kubelet、kube-proxy 和 crictl。先安装依赖并关闭 Swap:

  apt-get update
apt-get -y install socat conntrack ipset kmod
swapoff -a

mkdir -p /etc/cni/net.d /opt/cni/bin \
  /var/lib/kubelet /var/lib/kube-proxy \
  /var/lib/kubernetes /var/run/kubernetes

安装二进制:

  mv crictl kube-proxy kubelet runc /usr/local/bin/
mv containerd containerd-shim-runc-v2 containerd-stress /bin/
mv cni-plugins/* /opt/cni/bin/

配置 CNI

jumpbox 会将 configs/10-bridge.conf 中的 SUBNET 替换为当前节点网段。node-0 使用 10.200.0.0/24,node-1 使用 10.200.1.0/24。节点上执行:

  mv 10-bridge.conf 99-loopback.conf /etc/cni/net.d/
modprobe br-netfilter
echo br-netfilter >> /etc/modules-load.d/modules.conf

echo "net.bridge.bridge-nf-call-iptables = 1" \
  >> /etc/sysctl.d/kubernetes.conf
echo "net.bridge.bridge-nf-call-ip6tables = 1" \
  >> /etc/sysctl.d/kubernetes.conf
sysctl -p /etc/sysctl.d/kubernetes.conf

配置 containerd、kubelet 和 kube-proxy

将 containerd 配置放入 /etc/containerd/config.toml。kubelet 通过 unix:///var/run/containerd/containerd.sock 连接运行时,并使用 systemd cgroup、Webhook 认证授权和独立节点证书。kube-proxy 使用 iptables 模式,clusterCIDR 为 10.200.0.0/16。

安装对应配置和 unit 后启动:

  systemctl daemon-reload
systemctl enable --now containerd kubelet kube-proxy
systemctl --no-pager --full status containerd kubelet kube-proxy

在 jumpbox 验证节点:

  kubectl get nodes --kubeconfig admin.kubeconfig -o wide

node-0 和 node-1 应均为 Ready。若长期 NotReady,应检查 kubelet 日志、证书 CN、主机名、containerd Socket、CNI 文件和内核转发配置。

9. 打通跨节点 Pod 网络

本实验不安装 Calico、Cilium 等网络方案,而是用静态路由展示 Kubernetes 的扁平网络模型。按前文示例 IP,在 server 执行:

  ip route add 10.200.0.0/24 via 192.168.10.11
ip route add 10.200.1.0/24 via 192.168.10.12

node-0 指向 node-1:

  ip route add 10.200.1.0/24 via 192.168.10.12

node-1 指向 node-0:

  ip route add 10.200.0.0/24 via 192.168.10.11

用 ip route 检查结果。直接添加的路由会在重启后丢失,正式环境应写入系统网络配置,或部署成熟 CNI。

10. 配置 kubectl 并验收

在管理机配置管理员凭据:

  mkdir -p ~/.kube
cp admin.kubeconfig ~/.kube/config
kubectl cluster-info
kubectl get nodes

验证 Secret 静态加密

  kubectl create secret generic kubernetes-the-hard-way \
  --from-literal=mykey=mydata

ssh root@server \
  "etcdctl get /registry/secrets/default/kubernetes-the-hard-way | hexdump -C"

etcd 原始数据中不应直接出现 mydata,并应能看到加密提供者前缀。

验证 Deployment、日志与远程执行

  kubectl create deployment nginx --image=nginx:latest
kubectl get pods -l app=nginx -o wide

POD_NAME=$(kubectl get pods -l app=nginx \
  -o jsonpath="{.items[0].metadata.name}")
kubectl logs "$POD_NAME"
kubectl exec -ti "$POD_NAME" -- nginx -v

验证端口转发

  kubectl port-forward deployment/nginx 8080:80
curl --head http://127.0.0.1:8080

返回 HTTP 200,说明 API Server 到 kubelet 的流式连接正常。

验证 Service 与 NodePort

  kubectl expose deployment nginx --port 80 --type NodePort
NODE_PORT=$(kubectl get svc nginx -o jsonpath='{.spec.ports[0].nodePort}')
NODE_NAME=$(kubectl get pods -l app=nginx \
  -o jsonpath='{.items[0].spec.nodeName}')
curl -I "http://$NODE_NAME:$NODE_PORT"

能拿到 nginx 响应头,说明 kube-proxy、iptables、Service 和节点网络均已打通。

11. 故障排查顺序

二进制部署问题通常集中在四类:

    1. 证书:用 openssl x509 检查有效期、CN、组织和 SAN,并确认所有主机时间同步。
    1. 服务参数:用 systemctl status 和 journalctl 查看启动参数、文件路径及权限。
    1. 节点运行时:检查 crictl info、containerd Socket 和 cgroup 驱动。
    1. 网络:检查 ip route、CNI 配置、br_netfilter、sysctl 和 iptables 规则。

建议严格按依赖链排查:

  etcd
  -> kube-apiserver
      -> controller-manager / scheduler
          -> containerd / kubelet
              -> CNI / Pod 路由
                  -> kube-proxy / Service

上游组件未恢复时,不必反复重启下游。例如 API Server 不可用时,kubelet 和 controller-manager 必然持续报连接错误。

12. 距离生产环境还差什么

完成以上步骤只说明 Kubernetes 核心链路已经跑通。生产环境至少还要补齐:

  • • 三节点控制平面和奇数成员 etcd 集群;
  • • API Server 前置负载均衡和稳定入口;
  • • 成熟 CNI、CoreDNS 和集群内 DNS;
  • • etcd 快照、证书轮换、升级及灾难恢复流程;
  • • 镜像仓库、准入控制、NetworkPolicy、Pod Security 和审计策略;
  • • 监控、日志、告警、容量管理与资源配额;
  • • 稳定版组件、制品校验、配置管理和自动化交付。

二进制部署的价值不是把安装过程复杂化,而是让 Kubernetes 不再是黑盒:证书定义身份,kubeconfig 描述访问关系,API Server 连接控制逻辑,etcd 保存状态,kubelet 与运行时负责落地容器,CNI 和 kube-proxy 连接 Pod 与 Service 网络。理解这条链路后,无论使用 kubeadm、RKE2 还是云厂商托管集群,排障都会更有方向。


二进制部署并不神秘。把它拆开看,就是下载安装、PKI、etcd、控制平面、容器运行时、节点组件、CNI、DNS和负载均衡。

真正决定集群能否进入生产的,不是 systemctl status 全部变绿,而是每一层都有可验证的结果,每一次有状态变更都有备份、灰度、停止条件和回滚路径。

如果你准备动手,今天可以先做一件事:画出自己的节点、VIP、Pod CIDR、Service CIDR 和端口规划图,再开始生成证书。这个顺序能避开二进制部署中相当一部分返工。

如果这篇文章对你有帮助,欢迎点赞、在看、关注并转发给正在学习 Kubernetes 的同事。后续我还会继续整理证书签发、etcd 灾备、Kubernetes 升级和 Calico 排障等生产实践。

公众号:耕云躬行录

个人博客:躬行笔记

参考项目:Kelsey Hightower 的 kubernetes-the-hard-way。本文命令与版本按当前本地仓库整理,实际执行请以仓库内 docs、units、configs 和对应架构下载清单为准。

参考文档:https://github.com/kelseyhightower/kubernetes-the-hard-way/tree/master

参考项目:Kelsey Hightower 的 kubernetes-the-hard-way。本文命令与版本按当前本地仓库整理,实际执行请以仓库内 docs、units、configs 和对应架构下载清单为准。

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码