第 2/18 天
引言
在第 1 天,我们把「从代码提交到灰度发布」的完整 DevOps 管道蓝图摊在了桌面上:Git 仓库、CI 流水线、镜像仓库、GitOps 控制器、可观测性组件、渐进式交付控制器——这些子系统的边界和责任划分决定了后面每一天的落地路径。但蓝图落纸的第一步,永远是从一块”能跑的 Kubernetes 底座”开始。
第 2 天,我们聚焦 K8s 集群本身的搭建。为了让这套环境在真实生产环境里也能被复制,我们选择 kubeadm 离线部署 + Cilium 网络插件 的组合。离线部署解决了内网/等保环境的镜像拉取难题;Cilium 用 eBPF 直接替代了传统的 iptables + CNI 组合,既能让 Pod 之间零额外跳数通信,又能为后续的 NetworkPolicy、L7 观测和 Gateway API 打下地基。

为什么选 kubeadm + Cilium
kubeadm:生产级、幂等、可版本化
kubeadm 是 Kubernetes 官方推荐的集群初始化工具,它把「装控制面组件 → 生成证书 → 写 Kubeconfig → 打标签 → 装 CoreDNS/Calico 或自定义 CNI」这一长串步骤封装为几个命令。相比 minikube、k3s 这类”一键玩具”,kubeadm 的每一次 kubeadm init/join 都可以被复现到生产环境;相比手工装 kubelite/kubeadm-static,它又提供了配置即代码的能力。
它有一个”离线模式”——kubeadm init --config 加一份 imageRepository: "" 的 YAML,就能跳过默认从 quay.io 拉镜像的动作,完全依赖预下载的本地镜像。
Cilium:eBPF 原生网络
Cilium 是一个 CNI 插件,它不依赖 CNI 提供的 iptables 规则链来做 Pod 之间流量转发,而是把流量处理下沉到内核 eBPF 程序。这带来三个直接收益:
- 性能:Pod 到 Pod 的南北向流量少跳一层用户态代理,同节点 Pod 通信甚至能走 eBPF map 直连。
- NetworkPolicy 零开销:策略编译进 eBPF map,Pod 启动即生效,不依赖 iptables 重建。
- 可观测性入口:Cilium Hubble 后续可直接提供 L3-L7 追踪,与今天的 Prometheus/Grafana 天然衔接。
环境准备
硬件与系统
我们准备三台机器,均为 Ubuntu 22.04 LTS:
| 角色 | 主机名 | IP |
|---|---|---|
| 控制面 | k8s-master | 192.168.10.10 |
| 工作节点 | k8s-node-01 | 192.168.10.11 |
| 工作节点 | k8s-node-02 | 192.168.10.12 |
系统前置操作(每台都执行):
# 关闭 swap 与防火墙,加载 overlayfs / br_netfilter 内核模块
sudo swapoff -a && sed -i '/ swap / s/^/#/' /etc/fstab
sudo systemctl stop firewalld ufw && sudo systemctl disable firewalld ufw
sudo modprobe overlay && sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
# 关闭透明大页与 SELinux(如启用)
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
sudo setenforce 0 2>/dev/null || true
# kubelet 内核参数
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.core.somaxconn = 32768
net.ipv4.tcp_tw_reuse = 1
fs.inotify.max_user_watches = 524288
fs.nr_open = 2097152
vm.swappiness = 0
EOF
sudo sysctl –system
安装 containerd 与 kubectl / kubeadm
选择 containerd 而不是 Docker,是因为它和 K8s 的版本节奏一致(CRI 原生,无 dockershim 中转):
# 安装 containerd
sudo apt-get install -y containerd
# 生成默认配置并启用 SystemdCgroup
sudo containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo sed -i 's/sandbox_image = "registry.k8s.io/pause:sandbox-image"/sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"/' /etc/containerd/config.toml
sudo systemctl restart containerd
# 安装 kubeadm 1.28.x / kubectl / kubelet
apt-get install -y apt-transport-https ca-certificates curl gnupg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key
| sudo gpg –dearmor -o /usr/share/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/usr/share/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /'
| sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet-1.28.* kubeadm-1.28.* kubectl-1.28.*
sudo systemctl enable –now kubelet
离线镜像准备
内网环境的最大坑是”镜像源不可达”。我们把控制面与工作节点所需的镜像全部预下载到 master,再推到内网 Harbor 或 air-gap 目录:
# 在 master 上导出所有 kubeadm 组件镜像
kubeadm config images list v1.28.4 | while read img; do
ctr -n k8s.io images pull "$img" || ctr -n k8s.io images pull "registry.aliyuncs.com/google_containers/$(basename $img):$(echo $img | awk -F: '{print $NF}')"
done
# 额外准备 Cilium 与 kube-proxy 镜像(版本需与后面 chart 对齐)
MCR=mcr.microsoft.com; QIO=quay.io
for img in
"$QIO/cilium/cilium:v1.14.7"
"$QIO/cilium/cilium-operator-generic:v1.14.7"
"$MCR/mcr.microsoft.com/cse/kubernetes/kube-proxy:v1.28.4"; do
ctr -n k8s.io images pull "$img"
done
# 打包成离线 tar(可复制到 air-gapped 节点)
mkdir -p /opt/k8s-images
ctr -n k8s.io images export /opt/k8s-images/k8s-base-1.28.4.tar
k8s.gcr.io/kube-apiserver:v1.28.4
k8s.gcr.io/kube-controller-manager:v1.28.4
k8s.gcr.io/kube-scheduler:v1.28.4
k8s.gcr.io/kube-proxy:v1.28.4
k8s.gcr.io/pause:3.9
k8s.gcr.io/coredns:v1.10.1
k8s.gcr.io/etcd:3.5.9-0
quay.io/cilium/cilium:v1.14.7
quay.io/cilium/cilium-operator-generic:v1.14.7
# 分发到其它节点
rsync -avz –progress /opt/k8s-images/ root@k8s-node-01:/opt/k8s-images/
rsync -avz –progress /opt/k8s-images/ root@k8s-node-02:/opt/k8s-images/
ssh k8s-node-0{1,2} 'ctr -n k8s.io images import /opt/k8s-images/k8s-base-1.28.4.tar'
kubeadm 初始化
准备 kubeadm-config
我们不使用 kubeadm init 的默认配置,而是显式给一份 YAML,避免踩默认 CIDR 冲突的坑:
# /root/kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
nodeRegistration:
name: k8s-master
criSocket: unix:///var/run/containerd/containerd.sock
localAPIEndpoint:
advertiseAddress: 192.168.10.10
bindPort: 6443
controlPlaneEndpoint: "192.168.10.10:6443"
timeout: 30m
apiServerExtraArgs:
enable-admission-plugins: NodeRestriction,ServiceAccount
—
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.28.4
imageRepository: "" # 关键:禁用默认 imageRepository,走本地已导入镜像
networking:
podSubnet: 10.244.0.0/16 # Cilium 分配给 Pod 的默认地址段
serviceSubnet: 10.96.0.0/12
—
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: system
containerLogMaxSize: 20Mi
containerLogMaxFiles: 3
evictionHard:
nodefs.available: "10%"
nodefs.inodesFree: "5%"
memory.available: "100Mi"
执行初始化
sudo kubeadm init config.yaml –upload-certs –ignore-preflight-errors=NumCPU
# 保存 kubeconfig
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 验证组件
kubectl get nodes
kubectl get pods -A
# 此时 CoreDNS 可能 CrashLoop,因为还没装 CNI —— 正常现象
加入工作节点
kubeadm 会输出一段 kubeadm join 命令,我们显式写出来便于复用:
# 在 node-01 / node-02 上执行
sudo kubeadm join 192.168.10.10:6443
–token abcdef.0123456789abcdef
–discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
–node-name k8s-node-01
–cri-socket unix:///var/run/containerd/containerd.sock
执行后回到 master:
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# k8s-master NotReady master,worker 2m v1.28.4
# k8s-node-01 NotReady <none> 20s v1.28.4
# k8s-node-02 NotReady <none> 20s v1.28.4
三个节点 NotReady 是因为 CNI 未安装,Pod 网络不通。下一步装 Cilium 就会自动恢复。
Cilium 网络插件安装
添加 Cilium Helm 仓库
helm repo add cilium https://helm.cilium.io/
helm repo update
helm search repo cilium –version 1.14.7
定制 values.yaml
生产落地 Cilium 时,我们至少要做四件事:关闭 host routing 走 eBPF 隧道、指定 registry、开启 Hubble、禁用 kube-proxy 让其由 Cilium 接管。
# /root/cilium-values.yaml
cluster:
name: "stellardata"
# 内网镜像仓库
global:
# 若已把 Cilium 镜像推到 Harbor:换成你的域名
image:
repository: quay.io/cilium/cilium
k8sServiceHost: "" # 让 Cilium 从 in-cluster API 自动发现
k8sServicePort: 6443
# eBPF 网络模式
ipam:
mode: kubernetes # 用 kube-apiserver 做 IPAM,兼容 kubeadm 默认 CIDR
bgp:
enabled: false
kubeProxyReplacement:
strict: false
mode: "native-routing" # eBPF 模式,取代 kube-proxy 的 iptables 规则
egress:
enabled: false # 生产需要时再打开
policy: default-deny
hubble:
enabled: true # L3-L7 可观测性
relay:
enabled: true
ui:
enabled: true
# 关闭默认 iptables 模式,走 eBPF
ipv4:
masquerade: true # 出公网时 SNAT
# 资源请求,避免 Cilium agent 被 OOMKill
agent:
resources:
requests:
cpu: 300m
memory: 256Mi
limits:
memory: 512Mi
安装 Cilium
helm upgrade –install cilium cilium/cilium
–namespace kube-system
–version 1.14.7
–values /root/cilium-values.yaml
–set global.kubeProxyReplacement=true
–set hubble.relay.enabled=true
–atomic –wait –timeout 10m
安装过程会打印大量 eBPF 编译日志。当看到 cilium-agent-* 三副本全部 Running、cilium-operator-* 一副本 Running,即代表成功。
卸载 kube-proxy(可选但推荐)
因为 Cilium 已接管 kube-proxy 的工作,我们可以清理 kube-proxy 让控制面更纯净:
# 检查 kube-proxy 是否由 kubeadm 静态托管
ls /etc/kubernetes/manifests/kube-proxy.yaml &&
sudo kubeadm delete kube-proxy –kubeconfig /etc/kubernetes/admin.conf
kubectl delete ds -n kube-system kube-proxy 2>/dev/null || true
kubectl get ds -n kube-system
验证 Pod 网络
kubectl wait –for=condition=Ready pod -A –timeout=300s
kubectl get nodes -o wide
kubectl get pods -n kube-system
# Cilium 状态检查
kubectl exec -n kube-system deploy/cilium-operator —
cilium-dbg status | head -40
一条简单的连通性验证:
kubectl run -it –rm busybox –image=mcr.microsoft.com/busybox –restart=Never — nslookup kubernetes.default
kubectl run -it –rm –image=mcr.microsoft.com/hello-world test –restart=Never –command — sleep 30
常见问题
Q1:kubeadm init 报 container runtime network plugin not initialized?
这是最常见的报错,通常是因为 CNI 没装或 CIDR 冲突。检查 kubeadm-config.yaml 里的 networking.podSubnet,确保它和后续 Cilium / Calico 的 IPAM 段一致;然后 kubeadm reset 重来。
Q2:Cilium agent 一直 CrashLoopBackOff,日志出现 BPF programs are not loaded?
多半是内核或主机名问题。确认:
uname -r # 建议 >= 5.10
sysctl net.core.bpf_jit_enable net.core.bpf_jit_harden
grep cilium /var/log/messages | tail
若节点被 SELinux 拦截 eBPF 加载,暂时 setenforce 0 验证。
Q3:Node 状态卡在 NotReady,原因是 NetworkUnavailable=True?
90% 是 CNI 还没起来。用 kubectl describe node <name> 看 Events,找到 Cilium agent 的调度节点,kubectl logs -n kube-system <cilium-agent-pod> 排查。
Q4:Pod 到 Pod 通,但访问 Service ClusterIP 不通?
检查 Cilium 是否替代了 kube-proxy:kubectl get pods -n kube-system | grep cilium,确认 cilium-agent 是 Running 状态;再用 cilium status 看 Proxy 段,BPF 段是否有 IPCache 条目。
Q5:离线环境 ctr images import 后 kubeadm init 仍报镜像拉取失败?
多半是 image name 与 containerd 里保留的 tag 不一致(比如 tag 被截断)。用 ctr -n k8s.io images ls 核对实际导入的完整镜像名,并在 imageRepository 里显式覆盖对应 tag。
总结
第 2 天,我们完成了三件事:
- kubeadm 离线部署:以一份
kubeadm-config.yaml作为代码化的集群拓扑描述,ctr+tar完成所有控制面镜像的离线导入,三节点集群从空机到 Ready 全程不依赖公网镜像仓库。 - Cilium 网络插件:以 eBPF 为内核底座替代 kube-proxy 与传统 iptables,为后续 NetworkPolicy、Hubble 观测、Gateway API 铺路。
- 调试套路:把 kubeadm 与 Cilium 常见的 5 类问题沉淀为可执行的诊断脚本。
这套底座是后面 16 天所有操作的地基:Harbor 会跑在 k8s 上,GitLab CI 会 build 镜像后推 Harbor,Argo CD 会 apply 到本集群,Prometheus 会 scrape 本集群指标,Argo Rollouts 会在本集群上做金丝雀。
下期预告
第 3 天:Harbor 私有镜像仓库部署与验证:Helm 安装与镜像推送
我们将从今天搭好的 k8s 集群出发,用 Helm 一键部署 Harbor 全套(registry、core、jobservice、portal、redis、postgresql、harbor-db),配置 500MB 单镜像上限与保留策略,然后用 crane + docker push 把 hello-world 与一个自定义 nginx 镜像推到私有仓库,为后面 GitLab CI 的镜像构建环节做好准备。
系列目录
- 第 1 天:全链路架构总览:从代码提交到灰度发布的完整管道设计
- 第 2 天:K8s 集群与网络基础:kubeadm 离线部署与 Cilium 网络插件 ← 本篇
- 第 3 天:Harbor 私有镜像仓库部署与验证:Helm 安装与镜像推送 🔜
- 第 4 天:GitLab CE 自托管部署:代码仓库与项目管理平台 🔜
- 第 5 天:GitLab Runner 配置:K8s Executor 与 RBAC 权限设置 🔜
- 第 6 天:GitLab CI 流水线搭建:.gitlab-ci.yml 与 Kaniko 无守护进程构建 🔜
- 第 7 天:Harbor 镜像版本管理与 Tag 策略配置 🔜
- 第 8 天:Argo CD 部署与 GitOps 配置仓库创建 🔜
- 第 9 天:CI 与 CD 联动:从代码提交到部署的全自动闭环 🔜
- 第 10 天:Prometheus 部署:kube-prometheus-stack 与 ServiceMonitor 指标采集 🔜
- 第 11 天:Grafana 看板搭建:K8s 标准面板与自定义业务看板 🔜
- 第 12 天:告警规则与 Alertmanager:钉钉/邮件通知渠道配置 🔜
- 第 13 天:Argo Rollouts 安装与基础概念:CRD 与 Rollout 资源定义 🔜
- 第 14 天:金丝雀发布实战:setWeight 流量分割与 pause 步骤 🔜
- 第 15 天:AnalysisTemplate 指标分析与自动回滚机制 🔜
- 第 16 天:全链路端到端演示:代码提交→构建→灰度→监控→告警完整流程 🔜
- 第 17 天:生产化加固要点:Harbor/GitLab/Argo CD 高可用方案 🔜
- 第 18 天:供应链安全闭环:Trivy 扫描 + cosign 签名 + Kyverno 验签 🔜

















暂无评论内容