Ubuntu 使用 kubeadm 安装 Kubernetes 1.35 部署文档
维护说明:本文档由 Kiro 持续维护,基于已跑通的 CentOS 7 版本改写而来。你负责执行,遇到问题把报错贴回来,我会更新对应章节。
最近更新:2026-07-04
0. 说明(与 CentOS 7 版的关键差异)
- 适用系统 :Ubuntu 22.04 LTS(内核 5.15)或 24.04 LTS(内核 6.8)。这两个版本原生 cgroup v2、systemd 较新,是 K8s 1.35 官方受支持的组合。
- 比 CentOS 7 省去的坑 (Ubuntu 上都不需要):
- 不需要换 EOL 归档源;
- 不需要
failCgroupV1: false(Ubuntu 是 cgroup v2); - 不需要
--ignore-preflight-errors(内核/ cgroup 都受支持); - 没有 SELinux(Ubuntu 用 AppArmor,无需额外处理)。
- 版本 :Kubernetes 1.35(
pkgs.k8s.io的v1.35deb 源),容器运行时 containerd。 - 镜像加速 :如在中国大陆,用
registry.aliyuncs.com与阿里云 apt 镜像,避免拉取超时。
1. 节点规划
| 角色 | 主机名 | IP(示例) | 配置最低要求(实测建议) |
|---|---|---|---|
| 控制平面 | k8s-master | 192.168.1.10 | 2C4G,磁盘 ≥ 20G |
| 工作节点1 | k8s-node1 | 192.168.1.11 | 2C4G |
| 工作节点2 | k8s-node2 | 192.168.1.12 | 2C4G |
把示例 IP / 主机名替换成你的真实值。master 建议至少 4G 内存 (CentOS 版实测 2G 会因 OOM 导致控制面组件反复重启,若还要装管理界面更需 4G+)。
Pod 网段本文档用
10.244.0.0/16(Flannel 默认)。
2. 系统初始化(所有节点都要执行,使用 root 或 sudo)
2.1 更新 apt 并换阿里云源(可选,国内加速)
bash
# 备份并替换为阿里云镜像(Ubuntu 22.04 用 jammy,24.04 用 noble)
cp /etc/apt/sources.list /etc/apt/sources.list.bak 2>/dev/null
sed -i 's@//.*archive.ubuntu.com@//mirrors.aliyun.com@g; s@//.*security.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list
apt-get update
注:Ubuntu 24.04 的源可能在
/etc/apt/sources.list.d/ubuntu.sources(deb822 格式),若上面 sed 无效,改那个文件里的 URL 即可。
2.2 设置主机名与 hosts(按节点分别设置主机名)
bash
# 在 master 执行
hostnamectl set-hostname k8s-master
# node1: hostnamectl set-hostname k8s-node1
# node2: hostnamectl set-hostname k8s-node2
# 所有节点都写入 hosts
cat >> /etc/hosts << 'EOF'
192.168.1.10 k8s-master
192.168.1.11 k8s-node1
192.168.1.12 k8s-node2
EOF
2.3 关闭 swap、防火墙
bash
# 关闭 swap(当前 + 永久)
swapoff -a
sed -ri 's/.*swap.*/#&/' /etc/fstab
# Ubuntu 常用 swap.img,如上面没覆盖到,额外禁用一次
systemctl mask swap.img.swap 2>/dev/null || true
# 关闭 ufw 防火墙(生产如需保留,改为放行 K8s 端口,见附录 A)
systemctl disable --now ufw 2>/dev/null || true
2.4 时间同步
Ubuntu 默认自带 systemd-timesyncd,通常已开启,只需设时区:
bash
timedatectl set-timezone Asia/Shanghai
timedatectl status | grep -i 'synchronized' # 应显示 yes
# 如未同步,可安装 chrony: apt-get install -y chrony && systemctl enable --now chrony
2.5 内核模块与内核参数
bash
cat > /etc/modules-load.d/k8s.conf << 'EOF'
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
cat > /etc/sysctl.d/k8s.conf << 'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
2.6 安装 ipset / ipvs(推荐 kube-proxy 用 ipvs 模式)
bash
apt-get install -y ipset ipvsadm
cat > /etc/modules-load.d/ipvs.conf << 'EOF'
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
EOF
for m in ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack; do modprobe $m; done
3. 安装容器运行时 containerd(所有节点)
用 Docker 的 apt 源安装 containerd.io(阿里云镜像):
⚠️ 如果这台机器已经装了 Docker :安装
containerd.io会重装/覆盖 containerd 包并重启 docker 服务 ,重启期间docker ps会短暂查不到容器(看起来像"docker 和容器都没了")。这是正常现象------容器与镜像数据都在/var/lib/docker,不会丢失 ,服务恢复后即回来。可用docker ps -a、docker images确认,若容器变Exited用docker start <名/ID>启回。
bash
apt-get install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
# 阿里云 docker-ce GPG key(Ubuntu 22.04/24.04 通用)
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
# 添加源($(. /etc/os-release && echo "$VERSION_CODENAME") 会自动取 jammy/noble)
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y containerd.io
3.1 生成并修改 containerd 配置
⚠️ Ubuntu 的 docker.io 源通常装的是 containerd 2.x ,其
config.toml是 version 3 格式、字符串用单引号'...'(不是 1.x 的双引号"...")。因此下面的 sed 用了单双引号都能匹配 的正则,避免改不到。改完务必用grep核实生效。
bash
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
# 1) 启用 SystemdCgroup(布尔值无引号,两版本通用)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# 2) 替换 pause 沙箱镜像为阿里云(K8s 1.35 需 3.10;正则兼容单/双引号与任意旧版本号)
sed -i -E "s#sandbox_image = ['\"]registry.k8s.io/pause:[^'\"]*['\"]#sandbox_image = 'registry.aliyuncs.com/google_containers/pause:3.10'#" /etc/containerd/config.toml
systemctl enable --now containerd
systemctl restart containerd
# 验证:两项都应已改(SystemdCgroup=true、sandbox_image 指向阿里云 pause:3.10)
grep -E 'SystemdCgroup|sandbox_image' /etc/containerd/config.toml
pause 版本以
kubeadm config images list --kubernetes-version v1.35.0输出为准,若不是 3.10 请改成实际值。若
restart报failed to load TOML ... keys cannot contain / character,说明 config.toml 被改坏,重新containerd config default生成后再精确 sed。
3.2 配置 Docker Hub 镜像加速(国内必备,所有节点)
业务镜像(如 nginx)默认从 Docker Hub(registry-1.docker.io)拉取,国内常超时(dial tcp ... i/o timeout / ImagePullBackOff)。给 containerd 配镜像加速:
bash
# 1) 启用 certs.d 目录(正则兼容单引号 '' 和双引号 "",containerd 2.x 是单引号)
sed -i -E "s#config_path = ['\"]{2}#config_path = '/etc/containerd/certs.d'#g" /etc/containerd/config.toml
# 确认已生效(应看到 /etc/containerd/certs.d,不再是空的 '' 或 "")
grep -n config_path /etc/containerd/config.toml
# 2) 为 docker.io 配置镜像站(多个,按序回退)
mkdir -p /etc/containerd/certs.d/docker.io
cat > /etc/containerd/certs.d/docker.io/hosts.toml << 'EOF'
server = "https://docker.io"
[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]
[host."https://docker.1panel.live"]
capabilities = ["pull", "resolve"]
[host."https://hub.rat.dev"]
capabilities = ["pull", "resolve"]
EOF
# 2b) 为 registry.k8s.io 配置加速(metrics-server 等镜像来自这里,docker.io 加速覆盖不到)
mkdir -p /etc/containerd/certs.d/registry.k8s.io
cat > /etc/containerd/certs.d/registry.k8s.io/hosts.toml << 'EOF'
server = "https://registry.k8s.io"
[host."https://k8s.m.daocloud.io"]
capabilities = ["pull", "resolve"]
EOF
# 3) 重启生效
systemctl restart containerd
# 验证(crictl 未装可跳过,直接用 kubectl 重建业务 Pod 验证)
crictl pull nginx 2>/dev/null || echo "crictl 未安装,改用 kubectl rollout restart <deploy> 验证"
- 必须配在运行 Pod 的每个节点上 (不只是 master),改完都要
systemctl restart containerd。- containerd 2.x 的
config_path是单引号'';若用双引号的 sed 会匹配不到、加速不生效。上面的正则已兼容。crictl未安装时可apt-get install -y cri-tools(k8s apt 源里有),或直接kubectl rollout restart deployment <名>让 Pod 重新拉镜像来验证。- 国内公共镜像站经常变动,若全部超时请更换为当前可用地址。
4. 安装 kubeadm / kubelet / kubectl 1.35(所有节点)
bash
apt-get install -y apt-transport-https ca-certificates curl gpg
# 官方 pkgs.k8s.io v1.35 deb 源
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /" \
> /etc/apt/sources.list.d/kubernetes.list
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl # 锁版本,防止 apt upgrade 意外升级
systemctl enable kubelet
国内访问
pkgs.k8s.io慢时,可把baseurl换成阿里云镜像https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.35/deb/对应路径(Release.key 与源地址同步替换)。查看可安装的具体补丁版本:
apt-cache madison kubeadm。
5. 初始化控制平面(仅 master 执行)
Ubuntu 是 cgroup v2、内核受支持,直接用命令行方式即可,不需要 CentOS 版里的 failCgroupV1 和 --ignore-preflight-errors:
bash
kubeadm init \
--kubernetes-version=v1.35.0 \
--apiserver-advertise-address=192.168.1.10 \
--image-repository=registry.aliyuncs.com/google_containers \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12
把
192.168.1.10换成 master 真实 IP。--kubernetes-version填实际存在的补丁版本(apt-cache madison kubeadm查看)。preflight 若报
NumCPU ... less than the required 2,给 master 加到 ≥2 核。
成功后配置 kubectl:
bash
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
保存输出末尾的 kubeadm join ... 命令。丢失可重新生成:
bash
kubeadm token create --print-join-command
6. 工作节点加入集群(仅 node 执行)
Ubuntu 上直接执行 join 即可,无需加 --ignore-preflight-errors:
bash
kubeadm join 192.168.1.10:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
token 默认 24 小时过期,失效时在 master 执行
kubeadm token create --print-join-command重新生成。如卡在
Waiting for a healthy kubelet、kubelet 日志报running with swap on,说明该 node 的 swap 没关干净,回到 2.3 关闭后systemctl restart kubelet。
7. 安装网络插件 CNI(仅 master 执行)
方案 A:Flannel(简单,匹配 10.244.0.0/16)
务必用 pinned release 清单,不要用
master分支的Documentation/kube-flannel.yml(master 分支清单可能与 release 镜像对不上,导致 init 容器报/opt/bin/install-conf: no such file or directory)。
bash
kubectl apply -f https://github.com/flannel-io/flannel/releases/download/v0.28.5/kube-flannel.yml
# 观察
kubectl get pods -n kube-flannel -o wide -w
kubectl get nodes
节点会在 CNI 就绪后 1-2 分钟内从 NotReady 变 Ready。
方案 B:Calico
如改用 Calico,kubeadm init 的 --pod-network-cidr 建议用 192.168.0.0/16,参考 Calico 官方 manifest 部署。需要时告诉我,补充完整步骤。
8. 验证集群
bash
# 1) 节点应全部 Ready
kubectl get nodes -o wide
# 2) 系统组件全部 Running;coredns 在 CNI 就绪后才会从 Pending 变 Running
kubectl get pods -A
# 3) 冒烟测试
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get svc nginx
curl http://192.168.1.10:$(kubectl get svc nginx -o jsonpath='{.spec.ports[0].nodePort}')
# 清理
kubectl delete deployment nginx && kubectl delete svc nginx
8.1 (可选)允许 master 节点调度业务 Pod
bash
kubectl describe node k8s-master | grep -i taint
# 移除污点(结尾 - 表示删除)
kubectl taint nodes k8s-master node-role.kubernetes.io/control-plane:NoSchedule-
# 恢复:kubectl taint nodes k8s-master node-role.kubernetes.io/control-plane=:NoSchedule
生产环境建议保留污点,让控制面专职。
8.2 (可选)安装 metrics-server(kubectl top / HPA 数据源)
版本:metrics-server
0.8.x支持 K8s1.31+,对应 1.35。用 pinned 版本v0.8.0。
bash
# 1) 安装
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.8.0/components.yaml
# 2) 必需补丁:kubeadm 的 kubelet 证书是自签名的,需跳过 TLS 校验,否则 Pod 一直 0/1(报 x509)
kubectl patch deployment metrics-server -n kube-system --type='json' \
-p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
# 3) 验证(等 Pod 1/1 Running)
kubectl get pods -n kube-system | grep metrics-server
kubectl top nodes
镜像拉取超时 (
registry.k8s.io ... i/o timeout):metrics-server 镜像来自registry.k8s.io,国内不通。两种解法二选一:
- 按 3.2 给
registry.k8s.io配加速(在跑该 Pod 的节点上配k8s.m.daocloud.io并重启 containerd),保持官方镜像地址即可;- 换阿里云镜像:
kubectl set image deployment/metrics-server -n kube-system metrics-server=registry.aliyuncs.com/google_containers/metrics-server:v0.8.0
8.3 (可选)安装 Kuboard 管理界面(Docker 独立部署,推荐)
不要在 K8s 内安装 Kuboard :其 in-cluster 装法自带的
etcd-host组件把端口写死为 2381、强制调度到 control-plane 节点,与 kubeadm 自带 etcd 占用的 2381 冲突(官方 issue #341 长期未解决,报错listen tcp <IP>:2381: bind: address already in use)。改端口、挪节点都无法绕过。因此推荐用 Docker 独立部署(etcd 内置在容器里,无冲突)。
bash
# 1) 安装 docker(可与 kubelet 使用的 containerd 共存;docker 源在第 3 节已添加)
apt-get install -y docker-ce docker-ce-cli
# 若未装 docker-ce(只装了 containerd.io),补一句:
# apt-get install -y docker-ce docker-ce-cli containerd.io
systemctl enable --now docker
# 2) 给 docker 配镜像加速(重要!docker 走 daemon.json,与 containerd 的 certs.d 相互独立,
# 否则 docker pull 仍会直连 Docker Hub 超时)
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.1panel.live",
"https://hub.rat.dev"
]
}
EOF
systemctl daemon-reload
systemctl restart docker
# 3) 独立容器运行 Kuboard(内置 etcd,数据持久化到 /root/kuboard-data)
docker run -d \
--restart=unless-stopped \
--name=kuboard \
-p 80:80/tcp \
-p 10081:10081/tcp \
-e KUBOARD_ENDPOINT="http://192.168.1.10:80" \
-e KUBOARD_AGENT_SERVER_TCP_PORT="10081" \
-v /root/kuboard-data:/data \
eipwork/kuboard:v3
访问 http://192.168.1.10(master IP),登录 admin / Kuboard123456,在界面「添加集群」导入当前 K8s 集群。
自定义对外端口 :如想用 30000 暴露,把
-p 80:80改为-p 30000:80,并同步 把KUBOARD_ENDPOINT改为http://<IP>:30000(这个地址是 agent 回连用的,必须与实际对外端口一致)。访问地址也随之变为http://<IP>:30000。镜像拉不到 :docker 的加速在
/etc/docker/daemon.json(上面第 2 步),和 containerd 的certs.d是两套、互不影响。docker pull eipwork/kuboard:v3仍超时说明镜像站不通,换地址即可。端口若被占,换其他端口同理;防火墙开启时放行对外端口和
10081。
8.4 (可选)持久化存储方案对比与选型
| 方案 | 高可用 | 快照/备份/DR | 性能 | 复杂度 | RWX | 适合 |
|---|---|---|---|---|---|---|
| local-path | ❌ 节点绑定 | ❌ | 一般 | 低 | ❌ | 单节点/测试 |
| NFS(8.4A) | ❌ 单点 | ❌ | 一般 | 低 | ✅ 原生 | 轻量/应急 |
| Longhorn(8.5,推荐) | ✅ 多副本 | ✅ | 好 | 中 | ✅(share-manager) | 中小规模生产 |
以生产标准对待、3-4 节点 → 推荐 Longhorn(8.5)。只想快速验证/应急 → NFS(8.4A)。二选一,保证集群只有一个默认 StorageClass。
8.4A (备选)NFS 动态供给 StorageClass
第一步:搭 NFS 服务端(选一台机器)
bash
apt-get install -y nfs-kernel-server
mkdir -p /data/nfs/k8s
chown nobody:nogroup /data/nfs/k8s
echo '/data/nfs/k8s *(rw,sync,no_subtree_check,no_root_squash)' >> /etc/exports
exportfs -r
systemctl enable --now nfs-kernel-server
showmount -e localhost
第二步:所有节点装 NFS 客户端(必须,否则挂载失败)
bash
apt-get install -y nfs-common
第三步:部署动态供给器 + StorageClass(master 执行)
把下面存成 nfs-provisioner.yaml,将两处 NFS_SERVER_IP 换成 NFS 服务端 IP:
yaml
apiVersion: v1
kind: Namespace
metadata:
name: nfs-provisioner
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: nfs-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: nfs-provisioner
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: nfs-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: nfs-provisioner
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: nfs-provisioner
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: NFS_SERVER_IP
- name: NFS_PATH
value: /data/nfs/k8s
volumes:
- name: nfs-client-root
nfs:
server: NFS_SERVER_IP
path: /data/nfs/k8s
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "false"
reclaimPolicy: Delete
volumeBindingMode: Immediate
bash
kubectl apply -f nfs-provisioner.yaml
kubectl get pods -n nfs-provisioner # 1/1 Running(镜像走 registry.k8s.io 加速)
kubectl get sc # nfs-client 带 (default)
8.5 (生产推荐)分布式存储:Longhorn
CNCF 项目、多副本高可用、带 UI、支持快照/备份/DR。版本用当前活跃稳定分支 v1.11.3 (安装前对照 Longhorn 支持矩阵 确认与 K8s 1.35 兼容)。
第一步:前置依赖(所有节点都要装)
bash
apt-get install -y open-iscsi nfs-common
systemctl enable --now iscsid
modprobe iscsi_tcp
echo iscsi_tcp > /etc/modules-load.d/iscsi-tcp.conf
(可选)官方环境检查脚本:
bash
curl -sSfL https://raw.githubusercontent.com/longhorn/longhorn/v1.11.3/scripts/environment_check.sh | bash
第二步:数据盘规划(生产建议)
Longhorn 默认存 /var/lib/longhorn。生产建议每个存储节点挂独立数据盘到该目录:
bash
mkfs.ext4 /dev/sdb # 按实际盘符
mkdir -p /var/lib/longhorn
echo '/dev/sdb /var/lib/longhorn ext4 defaults 0 0' >> /etc/fstab
mount -a
第三步:安装(master 执行)
bash
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.11.3/deploy/longhorn.yaml
kubectl -n longhorn-system get pods -w # 等全部 Running(镜像来自 docker.io,已配 3.2 加速)
Longhorn 是分布式系统,会在每个节点部署 agent(longhorn-manager / csi-plugin / engine-image / instance-manager 等 DaemonSet),再加 CSI 标准控制器(provisioner/attacher/resizer/snapshotter,各多副本)+ UI。所以一个 3 节点集群装完有 25-30 个 pod 属正常。pod 起不来多半是某节点漏装 iscsi 依赖,用上面的 environment_check.sh 排查。
第四步:设默认 StorageClass 并调副本数
⚠️ 两个实战坑(CentOS 实测):
- StorageClass 的
parameters不可变 ,不能kubectl edit/patch改numberOfReplicas。longhorn这个 SC 由 Longhorn 托管 ,你删掉它 longhorn-manager 会按 ConfigMap 模板自动重建(还是默认 3 副本)。跟它抢名字没用。正确做法:另建一个自定义副本数的 SC 并设为默认(测试环境 1 副本省资源;生产建议 ≥2):
bash
cat <<'EOF' | kubectl create -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn-r1
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: driver.longhorn.io
allowVolumeExpansion: true
parameters:
numberOfReplicas: "1" # 测试 1;生产建议 2-3
staleReplicaTimeout: "30"
dataLocality: "disabled"
fsType: "ext4"
dataEngine: "v1"
reclaimPolicy: Delete
volumeBindingMode: Immediate
EOF
# 取消自带 longhorn 的默认标记,避免出现两个默认 SC
kubectl patch storageclass longhorn -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
kubectl get sc
方式 B(想保留
longhorn这个名字):改它的托管模板 ConfigMap,再删 SC 让 Longhorn 按新模板自动重建:
bash# 编辑模板,把内嵌 yaml 里的 numberOfReplicas: "3" 改成 "1" kubectl -n longhorn-system edit configmap longhorn-storageclass # 删掉 SC,Longhorn 会用新模板自动重建(这次就是 1 副本) kubectl delete sc longhorn # 稍等几秒验证(应输出 1) kubectl get sc longhorn -o jsonpath='{.parameters.numberOfReplicas}{"\n"}'
已存在的卷改副本数:UI → Volumes → 选卷 → Update Replica Count。UI 里 Settings 的 "Default Replica Count" 只影响之后从 UI 手动建的卷,不改 SC。(注意别和 "Default Minimum Number of BackingImage Copies" 搞混,后者是底图副本,与卷数据无关。)
第五步:访问 Web UI(⚠️ 安全)
Longhorn UI 默认无任何认证 。生产必须 放到带认证的 Ingress 后(ingress-nginx + basic-auth),不要用裸 NodePort 对外。
测试环境临时访问(最安全):
bash
kubectl -n longhorn-system port-forward svc/longhorn-frontend 8000:80 --address 0.0.0.0
# 浏览器 http://<master-ip>:8000
生产环境(Ingress + basic-auth):
bash
apt-get install -y apache2-utils # 提供 htpasswd
htpasswd -c auth admin
kubectl -n longhorn-system create secret generic basic-auth --from-file=auth
cat > longhorn-ingress.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: longhorn-ingress
namespace: longhorn-system
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'
spec:
ingressClassName: nginx
rules:
- host: longhorn.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: longhorn-frontend
port:
number: 80
EOF
kubectl apply -f longhorn-ingress.yaml
第六步:验证
bash
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-longhorn
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: longhorn-r1
resources:
requests:
storage: 1Gi
EOF
kubectl get pvc test-longhorn # 应 Bound
kubectl delete pvc test-longhorn
PVC 多 Deployment 共享(RWX)
一个 PVC 想挂到多个 Deployment 且都能读写,需 RWX (ReadWriteMany)。Longhorn 块卷默认 RWO(单节点/单应用独占,跨节点会报 Multi-Attach error);把 PVC 的 accessModes 设为 ReadWriteMany,Longhorn 会起 share-manager(内置 NFS)支持多 Pod 跨节点共享:
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-data
spec:
accessModes: ["ReadWriteMany"] # RWX
storageClassName: longhorn-r1
resources:
requests:
storage: 5Gi
PVC 是命名空间级资源,共享它的 Deployment 必须与 PVC 同 namespace。RWX 依赖各节点的 nfs 客户端(前置依赖已装)。
卸载 / 重建 Longhorn(踩坑记录)
⚠️ 破坏性操作,会删所有卷数据。不要直接
kubectl delete ns longhorn-system(会因 finalizer 卡在 Terminating)。
正确流程:
bash
# 1) 打开删除确认开关
kubectl -n longhorn-system patch settings.longhorn.io deleting-confirmation-flag --type=merge -p '{"value":"true"}'
# 2) 跑官方卸载 Job
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.11.3/uninstall/uninstall.yaml
kubectl -n longhorn-system get job/longhorn-uninstall -w # 等 1/1
# 3) 删组件与卸载 Job
kubectl delete -f https://raw.githubusercontent.com/longhorn/longhorn/v1.11.3/deploy/longhorn.yaml
kubectl delete -f https://raw.githubusercontent.com/longhorn/longhorn/v1.11.3/uninstall/uninstall.yaml
# 4) 所有节点清残留
rm -rf /var/lib/longhorn/*
若 ns 已卡在 Terminating:常见是残留 CRD 资源带 finalizer,且 admission webhook 的 service 已没了导致 patch 被拦。先删残留 webhook 配置再去 finalizer:
bashkubectl delete mutatingwebhookconfigurations longhorn-webhook-mutator 2>/dev/null kubectl delete validatingwebhookconfigurations longhorn-webhook-validator 2>/dev/null # 再去掉卡住资源的 finalizer,例如 engineimages kubectl get engineimages.longhorn.io -n longhorn-system kubectl patch engineimages.longhorn.io <name> -n longhorn-system --type=merge -p '{"metadata":{"finalizers":[]}}' # 仍不退则调 finalize 接口清 ns 自身 finalizer kubectl get ns longhorn-system -o json > ns.json # 编辑 spec.finalizers 为 [] kubectl replace --raw "/api/v1/namespaces/longhorn-system/finalize" -f ns.json
9. 附录 A:保留防火墙(ufw)时需放行的端口
control-plane:
| 端口 | 用途 |
|---|---|
| 6443 | kube-apiserver |
| 2379-2380 | etcd |
| 10250 | kubelet API |
| 10257 | kube-controller-manager |
| 10259 | kube-scheduler |
worker:10250、30000-32767(NodePort)。Flannel VXLAN 需放行 UDP 8472。
放行示例:ufw allow 6443/tcp(其余同理)。
10. 常见问题排查
| 现象 | 处理 |
|---|---|
| init 拉镜像超时 | 确认用了 --image-repository=registry.aliyuncs.com/google_containers;crictl pull 单独测 |
| 节点一直 NotReady | CNI 未就绪:kubectl get pods -n kube-flannel 看是否 Running |
| kubelet 起不来 | journalctl -u kubelet -f;常见 cgroup driver 不一致(确认 containerd SystemdCgroup=true)或 swap 未关 |
containerd 报 failed to load TOML ... keys cannot contain / |
config.toml 被改坏,重新 containerd config default 生成后再精确 sed |
flannel init 报 /opt/bin/install-conf: no such file |
用了 master 分支清单,改用 pinned release 清单(第 7 节) |
| metrics-server 一直 0/1、报 x509 | 漏了 --kubelet-insecure-tls 补丁(8.2 节) |
| 控制面组件反复 CrashLoop(cm/scheduler) | master 内存不足导致 OOM,加内存到 ≥4G |
业务镜像(nginx 等)拉取超时 registry-1.docker.io ... i/o timeout |
Docker Hub 国内不通,按 3.2 配镜像加速;注意 containerd 2.x 是单引号,且要配在跑 Pod 的节点上并重启 |
| 配了加速仍走 registry-1.docker.io | config_path 没改成功(containerd 2.x 单引号未匹配),grep config_path 确认已指向 /etc/containerd/certs.d,再重启 containerd |
metrics-server 拉取超时 registry.k8s.io ... i/o timeout |
registry.k8s.io 国内不通。按 3.2 给 registry.k8s.io 配加速(k8s.m.daocloud.io),或 kubectl set image 换阿里云镜像(见 8.2) |
11. 维护记录
| 日期 | 变更内容 |
|---|---|
| 2026-07-04 | 初版:基于跑通的 CentOS 7 文档改写为 Ubuntu 22.04/24.04 版。改用 apt/ufw/systemd-timesyncd;移除 CentOS 特有的 EOL 换源、cgroup v1(failCgroupV1)、--ignore-preflight-errors、SELinux 处理;保留 pause 3.10、flannel pinned release、metrics-server insecure-tls、Kuboard 用 Docker 部署等已验证经验 |
| 2026-07-04 | 第 3 节补充提醒:机器已装 Docker 时,安装 containerd.io 会重启 docker、容器短暂中断(数据不丢) |
| 2026-07-04 | 实测发现 Ubuntu 装的是 containerd 2.x(config.toml 单引号/version 3):3.1 的 sandbox_image、3.2 的 config_path 改用兼容单双引号的正则 sed;新增 Docker Hub 加速(3.2)与相关排查项 |
| 2026-07-04 | 3.2 节增加 registry.k8s.io 镜像加速(k8s.m.daocloud.io),解决 metrics-server 等镜像超时;8.2 节与排查表同步更新 |
| 2026-07-04 | 新增存储章节:8.4 选型对比、8.4A NFS 动态供给、8.5 Longhorn(v1.11.3,Ubuntu 用 open-iscsi/nfs-common)。含副本数正确改法(自定义 SC,Longhorn 会自动重建托管 SC)、UI 安全加固、RWX 多 Deployment 共享、卸载/重建踩坑(删 webhook + 去 finalizer) |
| 2026-07-04 | 8.5 副本数调整补充方式 B(改 longhorn-storageclass ConfigMap 模板 + 删 SC 触发重建),与方式 A 并列 |