Ubuntu 24.04 单节点 K8S 安装指南
本教程介绍如何在一台 Ubuntu 24.04 服务器上安装 Kubernetes v1.31.14 单节点集群。按照下列步骤可依次完成 /data 数据目录规划、containerd 配置、轩辕镜像加速、集群初始化、Flannel 网络安装和运行状态验证。
版本说明
Kubernetes v1.31.14 是 v1.31 系列最后一个补丁版本,现已停止维护。本教程用于安装指定的 v1.31 环境;新建生产集群时应优先选择仍在维护的版本。
1. 部署约定
| 项目 | 配置 |
|---|---|
| 操作系统 | Ubuntu 24.04(Noble) |
| Kubernetes | v1.31.14 |
| 容器运行时 | containerd 1.7 |
| 网络插件 | Flannel v0.26.7 |
| 服务管理 | systemctl 管理 containerd、kubelet |
| 数据根目录 | /data |
| Pod 网段 | 10.244.0.0/16 |
| Service 网段 | 10.96.0.0/12 |
控制平面与业务 Pod 共用一个节点。API Server、Scheduler、Controller Manager 和 etcd 是由 kubelet 管理的静态 Pod,不存在统一的 kubernetes.service。
开始前切换到 root,并设置安装参数:
bash
sudo -i
bash
set -euo pipefail
NODE_IP="192.168.1.10"
NODE_NAME="k8s-node01"
# 轩辕专属域名前缀,不包含 .xuanyuan.run
XY_ID="替换为你的专属前缀"
K8S_VERSION="v1.31.14"先确认网络规划
NODE_IP 必须是服务器固定内网 IP。Pod 和 Service 网段不能与服务器内网、VPN 或已有路由重叠。修改 Pod 网段后,还要同步修改 Flannel 配置。
2. 初始化系统
检查内存、swap、路由和 /data 挂载:
bash
free -h
swapon --show
ip -4 addr
ip -4 route
df -h /data关闭 swap,并避免重启后重新启用:
bash
cp -a /etc/fstab "/etc/fstab.bak.$(date +%Y%m%d%H%M%S)"
swapoff -a
sed -ri '/^[[:space:]]*#/!{
/[[:space:]]swap[[:space:]]/s/^/# /
}' /etc/fstab如果系统使用 zram 或其他服务创建 swap,还需要禁用对应服务。重启后应确认 swapon --show 没有输出。
安装依赖并启用 Kubernetes 所需内核配置:
bash
apt-get update
apt-get install -y ca-certificates curl gpg conntrack socat iptables
cat > /etc/modules-load.d/kubernetes.conf <<'EOF'
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
cat > /etc/sysctl.d/99-kubernetes.conf <<'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system3. 规划 /data 数据目录
| 用途 | 存储位置 |
|---|---|
| 容器镜像、容器可写层 | /data/containerd |
| etcd 数据 | /data/k8s/etcd |
| kubelet 数据与 Pod 卷 | /data/k8s/kubelet |
| CNI 分配状态 | /data/k8s/cni |
| Pod 日志 | /data/k8s/logs |
| 下载的部署清单 | /data/k8s/manifests |
kubelet、CNI 和 Pod 日志通过 bind mount 保留标准路径,减少插件兼容问题:
bash
mkdir -p /data/containerd
mkdir -p /data/k8s/{etcd,kubelet,cni,logs,manifests}
chmod 700 /data/k8s/etcd
for target in /var/lib/kubelet /var/lib/cni /var/log/pods; do
mkdir -p "$target"
if mountpoint -q "$target" ||
[ -n "$(find "$target" -mindepth 1 -maxdepth 1 -print -quit)" ]; then
echo "发现已有挂载或数据,请先检查:$target"
exit 1
fi
done
cat >> /etc/fstab <<'EOF'
# Kubernetes data directories
/data/k8s/kubelet /var/lib/kubelet none bind,x-systemd.requires-mounts-for=/data 0 0
/data/k8s/cni /var/lib/cni none bind,x-systemd.requires-mounts-for=/data 0 0
/data/k8s/logs /var/log/pods none bind,x-systemd.requires-mounts-for=/data 0 0
EOF
systemctl daemon-reload
mount /var/lib/kubelet
mount /var/lib/cni
mount /var/log/pods
findmnt /var/lib/kubelet
findmnt /var/lib/cni
findmnt /var/log/pods为什么使用 bind mount?
部分 Kubernetes 组件和第三方插件默认访问 /var/lib/kubelet、/var/lib/cni 与 /var/log/pods。保留这些标准路径,同时把实际数据放到 /data,兼容性通常好于直接修改所有组件参数。
4. 安装并配置 containerd 1.7
通过 Docker 官方 APT 仓库安装 containerd.io,只安装容器运行时:
bash
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update
CONTAINERD_PKG="$(
apt-cache madison containerd.io |
awk '$3 ~ /^1\.7\./ {print $3}' |
sort -Vr |
head -n 1
)"
: "${CONTAINERD_PKG:?软件源中未找到 containerd.io 1.7}"
apt-get install -y "containerd.io=${CONTAINERD_PKG}"
apt-mark hold containerd.io
systemctl stop containerd
containerd --version写入 containerd 1.7 配置:
bash
mkdir -p /etc/containerd
if [ -f /etc/containerd/config.toml ]; then
cp -a /etc/containerd/config.toml \
"/etc/containerd/config.toml.bak.$(date +%Y%m%d%H%M%S)"
fi
cat > /etc/containerd/config.toml <<'EOF'
version = 2
root = "/data/containerd"
state = "/run/containerd"
[grpc]
address = "/run/containerd/containerd.sock"
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.k8s.io/pause:3.10"
[plugins."io.containerd.grpc.v1.cri".containerd]
default_runtime_name = "runc"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".cni]
bin_dir = "/opt/cni/bin"
conf_dir = "/etc/cni/net.d"
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
EOF配置轩辕镜像
轩辕需要按原始仓库分别配置映射:
| 原始仓库 | 轩辕镜像地址 |
|---|---|
docker.io | ${XY_ID}.xuanyuan.run |
registry.k8s.io | ${XY_ID}-k8s.xuanyuan.run |
ghcr.io | ${XY_ID}-ghcr.xuanyuan.run |
quay.io | ${XY_ID}-quay.xuanyuan.run |
使用 containerd 的 hosts.toml 配置,YAML 中仍保留官方镜像名:
bash
write_mirror() {
local registry="$1"
local mirror="$2"
mkdir -p "/etc/containerd/certs.d/${registry}"
cat > "/etc/containerd/certs.d/${registry}/hosts.toml" <<EOF
server = "https://${mirror}"
[host."https://${mirror}"]
capabilities = ["pull", "resolve"]
EOF
}
write_mirror docker.io "${XY_ID}.xuanyuan.run"
write_mirror registry.k8s.io "${XY_ID}-k8s.xuanyuan.run"
write_mirror ghcr.io "${XY_ID}-ghcr.xuanyuan.run"
write_mirror quay.io "${XY_ID}-quay.xuanyuan.run"为 containerd 和 kubelet 添加 /data 挂载依赖:
bash
for service in containerd kubelet; do
mkdir -p "/etc/systemd/system/${service}.service.d"
cat > "/etc/systemd/system/${service}.service.d/20-data.conf" <<'EOF'
[Unit]
RequiresMountsFor=/data/containerd /data/k8s/etcd /var/lib/kubelet /var/lib/cni /var/log/pods
EOF
done
systemctl daemon-reload
systemctl enable --now containerd
systemctl is-active containerd5. 安装 kubeadm、kubelet 和 kubectl
添加 Kubernetes v1.31 独立软件源并锁定版本:
bash
curl -fsSL \
https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key \
-o /tmp/kubernetes-release.key
gpg --batch --yes --dearmor \
-o /etc/apt/keyrings/kubernetes-apt-keyring.gpg \
/tmp/kubernetes-release.key
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' \
> /etc/apt/sources.list.d/kubernetes.list
apt-get update
K8S_PKG="$(
apt-cache madison kubeadm |
awk '$3 ~ /^1\.31\.14-/ {print $3; exit}'
)"
: "${K8S_PKG:?软件源中未找到 Kubernetes 1.31.14}"
apt-get install -y \
"kubeadm=${K8S_PKG}" \
"kubelet=${K8S_PKG}" \
"kubectl=${K8S_PKG}" \
cri-tools
apt-mark hold kubeadm kubelet kubectl
systemctl daemon-reload
systemctl enable kubelet配置 crictl:
bash
cat > /etc/crictl.yaml <<'EOF'
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 120
debug: false
EOF
kubeadm version -o short
kubectl version --client
crictl info
test -x /opt/cni/bin/bridge
test -x /opt/cni/bin/host-local
test -x /opt/cni/bin/loopback
test -x /opt/cni/bin/portmap初始化前 kubelet 没有完整配置,反复重启属于正常现象;安装 CNI 前,运行时报告网络尚未就绪也属正常。
6. 初始化 Kubernetes
生成 kubeadm 配置:
bash
cat > /root/kubeadm-init.yaml <<EOF
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: "${NODE_IP}"
bindPort: 6443
nodeRegistration:
name: "${NODE_NAME}"
criSocket: unix:///run/containerd/containerd.sock
kubeletExtraArgs:
- name: node-ip
value: "${NODE_IP}"
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: "${K8S_VERSION}"
imageRepository: registry.k8s.io
networking:
podSubnet: "10.244.0.0/16"
serviceSubnet: "10.96.0.0/12"
dnsDomain: cluster.local
etcd:
local:
dataDir: /data/k8s/etcd
apiServer:
certSANs:
- "${NODE_IP}"
- "${NODE_NAME}"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
failSwapOn: true
containerLogMaxSize: 20Mi
containerLogMaxFiles: 5
EOF先验证配置和镜像,再执行初始化:
bash
kubeadm config validate --config /root/kubeadm-init.yaml
kubeadm config images list --config /root/kubeadm-init.yaml
kubeadm config images pull --config /root/kubeadm-init.yaml
crictl images
kubeadm init --config /root/kubeadm-init.yaml配置 root 用户的访问凭据:
bash
install -d -m 700 /root/.kube
install -m 600 /etc/kubernetes/admin.conf /root/.kube/config
kubectl get nodes -o wide此时节点显示 NotReady 是正常的,安装 CNI 后才会变为 Ready。
7. 安装 Flannel
下载固定版本的清单。Flannel 镜像来自 GHCR,会通过前面配置的轩辕 GHCR 地址拉取:
bash
curl -fL --retry 3 \
https://raw.githubusercontent.com/flannel-io/flannel/v0.26.7/Documentation/kube-flannel.yml \
-o /data/k8s/manifests/kube-flannel.yml
crictl pull ghcr.io/flannel-io/flannel:v0.26.7
crictl pull ghcr.io/flannel-io/flannel-cni-plugin:v1.6.2-flannel1
kubectl apply -f /data/k8s/manifests/kube-flannel.yml
# 单节点允许控制平面调度业务 Pod
kubectl taint nodes "${NODE_NAME}" \
node-role.kubernetes.io/control-plane:NoSchedule-如果 UFW 已启用,允许 Pod 网络转发:
bash
ufw status
ufw route allow in on cni0
ufw route allow out on cni0
ufw route allow in on flannel.1
ufw route allow out on flannel.1防火墙
只有 ufw status 显示 active 时才需要添加上述规则。不要把 API Server 的 TCP 6443 直接暴露到公网;远程访问应限制来源地址。增加工作节点时,还要放行节点间的 TCP 10250、Flannel UDP 8472 等流量。
8. 验证集群
等待节点和系统组件就绪:
bash
kubectl wait --for=condition=Ready "node/${NODE_NAME}" --timeout=300s
kubectl -n kube-flannel rollout status \
daemonset/kube-flannel-ds --timeout=300s
kubectl -n kube-system rollout status \
deployment/coredns --timeout=300s
kubectl get nodes -o wide
kubectl get pods -A -o wide验证 Docker Hub 镜像映射和集群 DNS:
bash
kubectl run dns-check \
--image=docker.io/library/busybox:1.36.1 \
--restart=Never \
--command -- sh -c \
'nslookup kubernetes.default.svc.cluster.local && sleep 300'
kubectl wait --for=condition=Ready pod/dns-check --timeout=180s
kubectl logs dns-check
kubectl delete pod dns-check检查 systemd 服务、挂载和磁盘占用:
bash
systemctl is-enabled containerd kubelet
systemctl is-active containerd kubelet
findmnt /var/lib/kubelet
findmnt /var/lib/cni
findmnt /var/log/pods
du -sh /data/containerd /data/k8s/*9. 让普通用户使用 kubectl
先退出 root Shell,回到需要使用 kubectl 的普通用户,然后执行:
bash
mkdir -p "$HOME/.kube"
chmod 700 "$HOME/.kube"
if [ -f "$HOME/.kube/config" ]; then
cp -p "$HOME/.kube/config" \
"$HOME/.kube/config.bak.$(date +%Y%m%d%H%M%S)"
fi
sudo install -m 600 -o "$(id -u)" -g "$(id -g)" \
/etc/kubernetes/admin.conf "$HOME/.kube/config"
unset KUBECONFIG
kubectl get nodes
kubectl get pods -A管理员权限
admin.conf 授予集群管理员权限,只应复制给可信用户。
10. 日常管理与排错
bash
# 服务状态
systemctl status containerd kubelet --no-pager
# 启停与重启
systemctl restart containerd kubelet
systemctl stop kubelet containerd
systemctl start containerd kubelet
# 服务日志
journalctl -u containerd -n 100 --no-pager
journalctl -u kubelet -n 100 --no-pager
# 集群状态与事件
kubectl get pods -A -o wide
kubectl get events -A --sort-by=.lastTimestamp
# API Server 不可用时从运行时排查
crictl ps -a
# crictl logs <容器ID>停止 kubelet 或 containerd 不代表所有业务容器都会立刻停止。业务启停应通过 Deployment、StatefulSet 等 Kubernetes 工作负载管理。
镜像拉取失败
先直接拉取镜像获取详细错误:
bash
crictl pull <镜像名>再依次检查:
XY_ID和轩辕专属域名是否正确。- 轩辕账号和对应仓库加速是否可用。
/etc/containerd/certs.d/<原始仓库>/hosts.toml是否存在。- 修改配置后是否执行了
systemctl restart containerd。 - Pod 为
ImagePullBackOff时,使用下面命令查看事件:
bash
kubectl describe pod <Pod名> -n <命名空间>11. 常见问题:APT 混入旧版 Ubuntu 源
如果 apt-get update 出现 hirsute Release 404,说明软件源中残留了已停止维护的 Ubuntu 21.04(hirsute)配置。Ubuntu 24.04 应使用代号 noble 的软件源。
先确认真实系统版本:
bash
cat /etc/os-release应包含:
text
VERSION_ID="24.04"
VERSION_CODENAME=noble不要跨版本硬改源
如果实际系统不是 Ubuntu 24.04,不要直接把旧系统的软件源代号替换成 noble。
备份 APT 配置并定位问题来源:
bash
APT_BACKUP="/root/apt-backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$APT_BACKUP"
cp -a /etc/apt/. "$APT_BACKUP/"
grep -nHE \
'hirsute|docker-ce/linux/ubuntu|download\.docker\.com/linux/ubuntu|Signed-By' \
/etc/apt/sources.list \
/etc/apt/sources.list.d/*.list \
/etc/apt/sources.list.d/*.sources \
2>/dev/null || true对传统 .list 文件,注释有效的 hirsute 条目:
bash
for source_file in /etc/apt/sources.list /etc/apt/sources.list.d/*.list; do
[ -f "$source_file" ] || continue
sed -Ei \
'/^[[:space:]]*deb(-src)?[[:space:]].*[[:space:]]hirsute(-updates|-backports|-security)?([[:space:]]|$)/s/^/# disabled-old-release: /' \
"$source_file"
done如果问题位于 .sources 文件:
- 整个配置块都是
hirsute:添加Enabled: no。 Suites:混有noble和hirsute:只移除hirsute及其派生项。
12. 常见问题:Docker 软件源缺少 GPG 公钥
如果更新软件源时出现以下错误,说明 Docker 软件源没有配置正确的签名公钥:
text
NO_PUBKEY 7EA0A9C3F273FCD8
The repository ... noble InRelease is not signed.重新安装 Docker 官方公钥:
bash
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
chmod 0644 /etc/apt/keyrings/docker.asc然后修改已有 Docker 源,不要重复添加相同仓库。.list 格式示例:
text
deb [signed-by=/etc/apt/keyrings/docker.asc] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu noble stable.sources 格式示例:
text
Types: deb
URIs: https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu
Suites: noble
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc两种格式保留一种即可。最后重新验证:
bash
apt-get update成功时不应再请求 hirsute,也不应出现 NO_PUBKEY、does not have a Release file 或 is not signed。
两类镜像配置不要混淆
APT 软件源用于下载 containerd.io 等系统软件包;轩辕镜像用于 containerd 拉取容器镜像。修复其中一项不会自动修复另一项。