k8s-1.35-ubuntu-安装文档.md

Ubuntu 使用 kubeadm 安装 Kubernetes 1.35 部署文档

维护说明:本文档由 Kiro 持续维护,基于已跑通的 CentOS 7 版本改写而来。你负责执行,遇到问题把报错贴回来,我会更新对应章节。

最近更新:2026-07-04


0. 说明(与 CentOS 7 版的关键差异)

  1. 适用系统 :Ubuntu 22.04 LTS(内核 5.15)或 24.04 LTS(内核 6.8)。这两个版本原生 cgroup v2、systemd 较新,是 K8s 1.35 官方受支持的组合。
  2. 比 CentOS 7 省去的坑 (Ubuntu 上都不需要):
    • 不需要换 EOL 归档源;
    • 不需要 failCgroupV1: false(Ubuntu 是 cgroup v2);
    • 不需要 --ignore-preflight-errors(内核/ cgroup 都受支持);
    • 没有 SELinux(Ubuntu 用 AppArmor,无需额外处理)。
  3. 版本 :Kubernetes 1.35(pkgs.k8s.iov1.35 deb 源),容器运行时 containerd。
  4. 镜像加速 :如在中国大陆,用 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 -adocker images 确认,若容器变 Exiteddocker 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 请改成实际值。

restartfailed 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 分钟内从 NotReadyReady

方案 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 支持 K8s 1.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,国内不通。两种解法二选一:

  1. 按 3.2 给 registry.k8s.io 配加速(在跑该 Pod 的节点上配 k8s.m.daocloud.io 并重启 containerd),保持官方镜像地址即可;
  2. 换阿里云镜像: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 实测)

  1. StorageClass 的 parameters 不可变 ,不能 kubectl edit/patchnumberOfReplicas
  2. 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:

bash 复制代码
kubectl 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:1025030000-32767(NodePort)。Flannel VXLAN 需放行 UDP 8472

放行示例:ufw allow 6443/tcp(其余同理)。


10. 常见问题排查

现象 处理
init 拉镜像超时 确认用了 --image-repository=registry.aliyuncs.com/google_containerscrictl 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 并列
相关推荐
李昊哲小课1 小时前
Ubuntu26.04 搭建 Hadoop3.5.0 完全分布式
大数据·hadoop·分布式·ubuntu·hdfs·mapreduce
AOwhisky2 小时前
kubernetes(K8s)学习笔记:第八期与第九期核心知识点自测与详解
笔记·云原生·kubernetes·云计算·k8s·集群·网络策略
条纹布鲁斯2 小时前
ubuntu 26.04 k8s 1.36 ceph
kubernetes
爱吃龙利鱼2 小时前
k8s指定命名空间kubeconfig文件生成教程
容器·kubernetes
weixin_4713830311 小时前
Docker - 05 - 构建流程
运维·docker·容器
Tipriest_14 小时前
ubuntu创建和更换当前swap大小
linux·运维·ubuntu
ejinxian14 小时前
微虚拟机 smolvm 与Docker 容器比较
运维·docker·容器·smolvm
爱码少年15 小时前
Docker如何一次查看多个容器日志
运维·docker·容器
WI8LbH78815 小时前
Ubuntu 部署Harbor
linux·运维·ubuntu