Skip to content

K3s 安装部署

K3s 常用于资源较小、部署周期短、集群规模不大的 Kubernetes 场景。它保留了 Kubernetes 的 API 和主要资源模型,但把控制平面组件、kubelet、containerd、Flannel、CoreDNS、metrics-server、local-path-provisioner 等内容做了打包,安装和维护入口比 kubeadm 更集中。

K3s 不是另一个容器编排系统,仍然是 Kubernetes。kubectl、Deployment、Service、ConfigMap、Secret、Ingress、Gateway、RBAC 这些对象仍然按 Kubernetes 的方式使用。区别主要在安装方式、默认组件、运行目录和部分内置功能上。

K3s 的常见使用场景:

场景说明
开发测试集群几台虚拟机就能搭出完整 Kubernetes 环境,适合验证资源对象、发布流程和平台组件
边缘节点资源有限、节点数量不多,集群需要轻量运行
小规模内部服务内部工具、运维平台、低流量服务可以用 K3s 承载
云原生组件验证Prometheus、Grafana、Loki、Argo CD 等组件需要真实 K8s 对象和标签
单机或小集群交付安装脚本简单,升级和卸载入口清楚,适合快速交付

K3s 和 kubeadm 的区别主要体现在这几块:

对比项K3skubeadm
安装方式一个二进制和安装脚本即可拉起集群手动准备 containerd、kubeadm、kubelet、kubectl
默认组件内置 containerd、Flannel、CoreDNS、metrics-server、local-path-provisionerCNI、metrics-server、存储组件通常单独安装
状态存储单 server 默认 SQLite,也可以用外部数据库或嵌入式 etcd标准控制平面默认 etcd
运行目录主要在 /etc/rancher/k3s/var/lib/rancher/k3s主要在 /etc/kubernetes/var/lib/kubelet、containerd 目录
适合场景轻量集群、边缘、小规模服务、快速交付标准自建集群、多控制平面、复杂生产集群

环境采用一台 server 加两台 agent。server 节点运行 Kubernetes API Server、调度器、控制器等控制平面组件,同时默认也可以调度业务 Pod;agent 节点主要运行 kubelet、kube-proxy、containerd 和业务 Pod。单 server 架构不具备控制平面高可用,节点故障会影响 API Server 访问。

一、环境规划

机器规划:

主机名IP角色说明
k3s-server01192.168.10.11server控制平面,同时允许调度 Pod
k3s-agent01192.168.10.12agent工作节点
k3s-agent02192.168.10.13agent工作节点

安装环境使用 Rocky Linux 9.7。K3s 版本固定为 v1.35.5+k3s1,容器运行时使用 K3s 内置 containerd。

几个默认网段:

项目默认值说明
Node 网段192.168.10.0/24服务器真实网段
Pod 网段10.42.0.0/16K3s 默认 Pod CIDR
Service 网段10.43.0.0/16K3s 默认 Service CIDR
API Server192.168.10.11:6443agent 加入集群时连接这个地址

机器上如果有多个 IP,安装时显式写 --node-ip 更稳。比如 192.168.10.12 同时存在别的地址时,kubelet 自动选择 IP 可能和预期不一致,Service、日志标签、节点访问也容易跟着偏。

二、系统初始化

三台节点都执行。每台机器设置自己的主机名:

bash
# 每台机器按规划设置自己的主机名,避免节点都显示为 localhost.localdomain
hostnamectl set-hostname k3s-server01

# 三台节点保持同一份 hosts,节点名和排错输出会更清楚
cat >/etc/hosts <<'EOF'
127.0.0.1 localhost localhost.localdomain
192.168.10.11 k3s-server01
192.168.10.12 k3s-agent01
192.168.10.13 k3s-agent02
EOF

配置时间同步:

bash
# 三台节点使用同一组时间源,避免证书校验、日志时间线和监控数据出现明显偏差
timedatectl set-timezone Asia/Shanghai

cat >/etc/chrony.conf <<'EOF'
server ntp.aliyun.com iburst
server ntp.tencent.com iburst
server cn.pool.ntp.org iburst

sourcedir /run/chrony-dhcp
driftfile /var/lib/chrony/drift
makestep 1.0 -1
rtcsync
keyfile /etc/chrony.keys
ntsdumpdir /var/lib/chrony
leapsectz right/UTC
logdir /var/log/chrony
EOF

systemctl enable --now chronyd
systemctl restart chronyd

# 初次同步时允许 chrony 直接校准一次时间
chronyc -a makestep

检查时间同步状态:

bash
timedatectl
chronyc tracking
chronyc sources -v

timedatectlSystem clock synchronized: yesNTP service: active 表示系统时钟已经进入同步状态。chronyc sources -v 输出里 ^* 表示当前使用的时间源,^+ 表示可参与组合的候选时间源。makestep 1.0 -1 允许 chrony 在发现时间偏差超过 1 秒时自动跳时,实验环境恢复虚拟机快照后更省事;刚恢复时需要等 chrony 选到可用时间源,Leap status: Normal 后再继续安装或观察监控数据。K8s 里证书、Token、Pod 事件、日志、Prometheus 指标时间戳都依赖节点时间,三台节点时间差太大时,证书校验、事件顺序和监控时间线都可能出现异常。

关闭 swap:

bash
# Kubernetes 调度和资源统计默认按无 swap 环境设计
swapoff -a

# 注释 fstab 里的 swap 行,防止重启后又自动启用
cp -a /etc/fstab /etc/fstab.k3s.bak.$(date +%Y%m%d%H%M%S)
sed -ri '/\sswap\s/s/^/# k3s disabled swap /' /etc/fstab

加载内核模块和网络参数:

bash
# overlay 是容器镜像层常用的文件系统模块
# br_netfilter 让 bridge 流量经过 iptables,CNI 和 Service 转发会用到
cat >/etc/modules-load.d/k3s.conf <<'EOF'
br_netfilter
overlay
EOF

modprobe br_netfilter
modprobe overlay

cat >/etc/sysctl.d/99-k3s.conf <<'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

sysctl --system

Rocky / CentOS 系列默认可能启用 firewalld。小规模内网环境可以先关闭防火墙,把链路跑通;正式环境更适合按端口逐项放行:

bash
# K3s 会用到 6443、10250、8472/UDP、NodePort 等端口
systemctl disable --now firewalld || true

# SELinux 已经 disabled 的机器会提示 setenforce 不生效,命令失败不影响后续初始化
setenforce 0 || true

if [ -f /etc/selinux/config ]; then
  cp -a /etc/selinux/config /etc/selinux/config.k3s.bak.$(date +%Y%m%d%H%M%S)
  sed -ri 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config
fi

K3s 常用端口:

端口协议用途
6443TCPKubernetes API Server
10250TCPkubelet API
8472UDPFlannel VXLAN
30000-32767TCP/UDPNodePort

三、安装 server 节点

192.168.10.11 执行。安装脚本使用 Rancher 国内镜像下载 K3s 二进制,版本显式固定:

bash
# 国内环境直接访问 GitHub 经常不稳定,安装脚本从 Rancher 国内镜像拉取
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh -o /root/k3s-install.sh
chmod +x /root/k3s-install.sh

INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_VERSION='v1.35.5+k3s1' \
INSTALL_K3S_EXEC="server \
  --node-ip=192.168.10.11 \
  --advertise-address=192.168.10.11 \
  --write-kubeconfig-mode=644 \
  --disable=traefik" \
sh /root/k3s-install.sh

几个参数的含义:

参数说明
INSTALL_K3S_MIRROR=cn使用 Rancher 国内镜像下载 K3s 二进制
INSTALL_K3S_VERSION固定 K3s 版本,避免每次安装拉到不同版本
--node-ip指定节点注册到集群里的内网 IP
--advertise-addressAPI Server 对外通告的地址
--write-kubeconfig-mode=644让本机其他用户可以读取 kubeconfig,内网环境操作方便一些
--disable=traefik不安装内置 Traefik,入口网关单独选择

安装完成后看服务状态:

bash
systemctl is-active k3s
kubectl get nodes -o wide
kubectl get pods -A -o wide

刚启动的十几秒里,系统 Pod 可能还在 ContainerCreating。节点注册正常以后,再处理镜像源和 agent 加入。

四、配置镜像加速

INSTALL_K3S_MIRROR=cn 只影响 K3s 安装包下载,不代表 Pod 镜像也自动走国内源。K3s 默认系统镜像里有 rancher/mirrored-pause:3.6rancher/mirrored-coredns-corednsrancher/mirrored-metrics-server 等,业务应用还会拉 nginxgrafanaloki 这类镜像。国内环境如果不配置 containerd 镜像源,常见现象是节点 Ready,但 Pod 一直卡在 ContainerCreating

K3s 当前生成的 containerd 配置里使用 certs.d 目录管理 registry hosts:

bash
grep -n "config_path" /var/lib/rancher/k3s/agent/etc/containerd/config.toml

三台节点都写入 docker.io 的镜像加速配置:

bash
# K3s 内置 containerd 会读取这个目录下的 hosts.toml
mkdir -p /var/lib/rancher/k3s/agent/etc/containerd/certs.d/docker.io

cat >/var/lib/rancher/k3s/agent/etc/containerd/certs.d/docker.io/hosts.toml <<'EOF'
server = "https://registry-1.docker.io"

[host."https://docker.1ms.run"]
  capabilities = ["pull", "resolve"]
EOF

重启当前节点上的 K3s 服务。server 节点是 k3s,agent 节点是 k3s-agent

bash
# server 节点执行
systemctl restart k3s

# agent 节点加入后执行
systemctl restart k3s-agent

验证 pause 镜像能拉取:

bash
# pause 镜像拉不下来时,任何 Pod 都创建不了 sandbox
crictl pull docker.io/rancher/mirrored-pause:3.6
crictl images | grep mirrored-pause

/etc/rancher/k3s/registries.yaml 是 K3s 官方文档里常见的私有仓库配置入口。实际排查时以节点上的 containerd 生效配置为准:/var/lib/rancher/k3s/agent/etc/containerd/config.toml 里如果指向 certs.d,就检查对应 registry 下有没有 hosts.toml。只写了 YAML 但 containerd 没有加载,Pod 仍然会继续访问 registry-1.docker.io

五、加入 agent 节点

在 server 节点读取 join token:

bash
cat /var/lib/rancher/k3s/server/node-token

192.168.10.12 执行:

bash
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh -o /root/k3s-install.sh
chmod +x /root/k3s-install.sh

INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_VERSION='v1.35.5+k3s1' \
K3S_URL='https://192.168.10.11:6443' \
K3S_TOKEN='<server 节点上的 node-token>' \
INSTALL_K3S_EXEC='agent --node-ip=192.168.10.12 --node-name=k3s-agent01' \
sh /root/k3s-install.sh

192.168.10.13 执行:

bash
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh -o /root/k3s-install.sh
chmod +x /root/k3s-install.sh

INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_VERSION='v1.35.5+k3s1' \
K3S_URL='https://192.168.10.11:6443' \
K3S_TOKEN='<server 节点上的 node-token>' \
INSTALL_K3S_EXEC='agent --node-ip=192.168.10.13 --node-name=k3s-agent02' \
sh /root/k3s-install.sh

agent 安装完成后,也写入上一节的 hosts.toml 镜像源配置并重启 k3s-agent

bash
mkdir -p /var/lib/rancher/k3s/agent/etc/containerd/certs.d/docker.io

cat >/var/lib/rancher/k3s/agent/etc/containerd/certs.d/docker.io/hosts.toml <<'EOF'
server = "https://registry-1.docker.io"

[host."https://docker.1ms.run"]
  capabilities = ["pull", "resolve"]
EOF

systemctl restart k3s-agent
crictl pull docker.io/rancher/mirrored-pause:3.6

回到 server 节点查看三台节点:

bash
kubectl get nodes -o wide

正常输出类似:

text
NAME           STATUS   ROLES           VERSION        INTERNAL-IP
k3s-agent01    Ready    <none>          v1.35.5+k3s1   192.168.10.12
k3s-agent02    Ready    <none>          v1.35.5+k3s1   192.168.10.13
k3s-server01   Ready    control-plane   v1.35.5+k3s1   192.168.10.11

六、验证系统组件

K3s 默认会拉起 CoreDNS、metrics-server 和 local-path-provisioner:

bash
kubectl get pods -A -o wide
kubectl get storageclass
kubectl top nodes

几个默认组件的作用:

组件作用验证方式
CoreDNS集群内 DNS,Service 名称解析依赖它kubectl -n kube-system get pod -l k8s-app=kube-dns
metrics-server提供 kubectl top 和 HPA 基础指标kubectl top nodes
local-path-provisionerK3s 默认本地存储 provisionerkubectl get storageclass
Flannel默认 Pod 网络插件Pod 能跨节点通信、节点上有 flannel.1

local-path 是节点本地盘,不是共享存储。Pod 调度到不同节点时数据不会自动跟着走,所以它适合临时组件和无状态服务;MySQL、Redis 这类有状态服务更适合单独接 NFS、Ceph、云盘或专门的数据库方案。

七、部署测试服务

部署一个 Nginx 服务,用来确认调度、镜像拉取、Service 转发和 NodePort 访问都正常。

创建命名空间和 Deployment:

bash
kubectl create namespace ops-demo --dry-run=client -o yaml | kubectl apply -f -

# 用 Deployment 创建两个副本,方便观察 Pod 调度到不同节点
kubectl -n ops-demo create deployment web-demo \
  --image=nginx:1.27-alpine \
  --replicas=2 \
  --dry-run=client -o yaml | kubectl apply -f -

暴露 NodePort:

bash
# NodePort 会在每台节点上打开一个 30000-32767 范围内的端口
kubectl -n ops-demo expose deployment web-demo \
  --port=80 \
  --target-port=80 \
  --type=NodePort \
  --dry-run=client -o yaml | kubectl apply -f -

等待发布完成:

bash
kubectl -n ops-demo rollout status deployment/web-demo --timeout=240s
kubectl -n ops-demo get pod -o wide
kubectl -n ops-demo get svc web-demo -o wide

取出 NodePort 并从三台节点访问:

bash
NODE_PORT=$(kubectl -n ops-demo get svc web-demo -o jsonpath='{.spec.ports[0].nodePort}')
echo "NODE_PORT=${NODE_PORT}"

# 三台节点任意一个 Node IP + NodePort 都应该能访问到 Service
curl -I --connect-timeout 5 --max-time 10 http://192.168.10.11:${NODE_PORT}
curl -I --connect-timeout 5 --max-time 10 http://192.168.10.12:${NODE_PORT}
curl -I --connect-timeout 5 --max-time 10 http://192.168.10.13:${NODE_PORT}

实际 NodePort 以 Service 输出为准。三台节点访问都返回 HTTP/1.1 200 OK 时,说明 NodePort 转发链路正常。日志里能看到来自不同节点转发链路的访问记录:

bash
kubectl -n ops-demo logs deploy/web-demo --tail=5

八、常见问题

现象排查入口处理方向
节点是 Ready,Pod 一直 ContainerCreatingkubectl describe podjournalctl -u k3s -n 100多数是 pause 镜像或业务镜像拉取失败,先验证 crictl pull docker.io/rancher/mirrored-pause:3.6
agent 加入失败systemctl status k3s-agentjournalctl -u k3s-agent -n 100K3S_URLK3S_TOKEN、server 6443 端口和时间同步
Pod 没有 IPkubectl describe pod、节点上的 /run/flannel/subnet.env看 Flannel 是否启动、内核模块和 ip_forward 是否正确
NodePort 访问不通kubectl get svc -o wideiptables-save、节点防火墙看 Service 是否有 Endpoints、firewalld 是否拦截、Pod 是否 Ready
kubectl top nodes 没数据kubectl -n kube-system get pod -l k8s-app=metrics-servermetrics-server 未运行、镜像拉取失败或 kubelet 指标访问异常

镜像源问题的错误信息通常长这样:

text
failed to get sandbox image "rancher/mirrored-pause:3.6":
failed to pull and unpack image "docker.io/rancher/mirrored-pause:3.6":
failed to do request:
Head "https://registry-1.docker.io/v2/rancher/mirrored-pause/manifests/3.6":
connect: connection refused

看到 sandbox imagemirrored-pause,问题还没到业务容器镜像那一层,Pod sandbox 都没有创建成功。处理完 pause 镜像以后,如果又出现 nginxgrafanaloki 等镜像拉取失败,再继续处理对应业务镜像仓库。

九、清理重装

K3s 安装脚本会自动生成卸载脚本。server 节点:

bash
# 会删除 K3s 服务、运行目录、内置 containerd 数据和相关网络配置
/usr/local/bin/k3s-uninstall.sh

agent 节点:

bash
# agent 节点使用独立卸载脚本
/usr/local/bin/k3s-agent-uninstall.sh

卸载后可以检查残留目录:

bash
ls /etc/rancher/k3s /var/lib/rancher/k3s /var/lib/kubelet /var/lib/cni 2>/dev/null

机器反复重装时,残留的 CNI、iptables、kubelet 数据目录会造成一些看起来很随机的问题。用官方卸载脚本清理,比手工删几个目录更稳。