1、k3s有离线安装和在线安装
2、我是在qenu虚拟机(无kvm)的archlinux操作系统中安装k3s的。用的是在线安装。
2.1 首先确保https://rancher-mirror.rancher.cn/k3s/k3s-install.sh这个网址可以访问,否则要先解决网络问题。
2.2 其次确认防火墙和swap是否已经关闭了。
关闭swap sudo swapoff -a 检查swap是否开启, 可以用free 命令看 swap大小,也可以cat /etc/fstab 查看是否挂载swap分区。 k8s要求关闭swap,主要是怕swap导致k8s无法准确看到每个node的真实剩余内存,导致把pod调度到无剩余内存的node上。关闭防火墙:生产环境建议保留防火墙,按需放行 6443、10250、8472 等端口。
sudo systemctl stop firewalld
sudo systemctl disable firewalld
2.3 安装依赖包
sudo pacman -S socat conntrack-tools iptables
2.4 确保主机名是合法的 DNS 格式(不能是 localhost 或含下划线),可以用cat /etc/hostname查看,或用 hostnamectl 命令查看
2.5 在线安装
首先配置镜像源
sudo mkdir -p /etc/rancher/k3s
sudo tee /etc/rancher/k3s/registries.yaml << ‘EOF’
mirrors:
docker.io:
endpoint:
- “https://dockerpull.org”
- “https://docker.1panel.live”
- “https://dockerproxy.com”
- “https://docker.unsee.tech”
EOF
然后安装k3s
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh |
INSTALL_K3S_MIRROR=cn
INSTALL_K3S_EXEC=“–write-kubeconfig-mode 644”
sh -
网速很慢,耐心等待,安装好之后是这样的
$ k3s -v k3s version v1.36.4+k3s1 (4dedb15b) go version go1.26.7 $ kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-54996dc9b4-5vd8f 1/1 Running 4 (20m ago) 8h kube-system helm-install-traefik-crd-nxfxp 0/1 Completed 0 35m kube-system helm-install-traefik-h4dk4 0/1 Completed 7 (32m ago) 8h kube-system local-path-provisioner-77b9867795-nsczj 1/1 Running 0 8h kube-system metrics-server-6dc596dfb8-pcsrw 1/1 Running 8 (20m ago) 8h kube-system svclb-traefik-95fc0daf-lrxqd 2/2 Running 0 26m kube-system traefik-59b7647586-62lcb 1/1 Running 0 26m这些组件的功能是:
coredns 集群内部 DNS 服务,让 Pod 之间可以通过服务名互相访问
helm-install-traefik-crd Helm Job,负责安装 Traefik 所需的 CRD(自定义资源定义)
helm-install-traefik Helm Job,负责通过 Helm 部署 Traefik Ingress Controller
local-path-provisioner 本地存储控制器,当你创建 PVC 时自动在节点本地分配存储卷
metrics-server 集群资源指标采集器,kubectl top 命令的数据来源
traefik Ingress Controller,HTTP/HTTPS 反向代理,负责将外部流量路由到集群内的服务
svclb-traefik K3s 内置的 Service Load Balancer(Klipper-lb),为 Traefik 提供 LoadBalancer 类型的 Service
2.6 可以禁用一些不重要的组件
编辑 K3s 配置,禁用 traefik 和 metrics-server
sudo tee /etc/rancher/k3s/config.yaml << 'EOF' disable: - traefik - metrics-server EOF sudo systemctl restart k3s $ kubectl get pod -n kube-system NAME READY STATUS RESTARTS AGE coredns-54996dc9b4-5vd8f 1/1 Running 8 (10h ago) 19h local-path-provisioner-77b9867795-nsczj 1/1 Running 1 (10h ago) 19h2.7 k3s 、containerd装在哪里
$ ls -lh /usr/local/bin
total 76M
lrwxrwxrwx 1 root root 3 Sep 6 23:58 crictl -> k3s
lrwxrwxrwx 1 root root 3 Sep 6 23:58 ctr -> k3s
-rwxr-xr-x 1 root root 76M Sep 6 23:58 k3s
-rwxr-xr-x 1 root root 2.3K Sep 6 23:58 k3s-killall.sh
-rwxr-xr-x 1 root root 2.0K Sep 6 23:58 k3s-uninstall.sh
lrwxrwxrwx 1 root root 3 Sep 6 23:58 kubectl -> k3s
可以看到这些软件都安装到/usr/local/bin目录了
$ ls -l /var/lib/rancher/k3s/data/current/bin/containerd
lrwxrwxrwx 1 root root 3 Sep 6 23:59 /var/lib/rancher/k3s/data/current/bin/containerd -> k3s
可以看到containerd被编译到k3s中了,就像busybox一样,一个软件包含了很多个软件的功能
另外,k3s进程中还把Flannel (CNI)编译进去了,用以支持Pod 网络,给每个 Pod 分配 IP,实现 Pod 间通信
** 最后,看一下k3s这个文件,可以看到k3s是一个静态编译的可执行文件,不依赖其它so文件 **
$ ldd /usr/local/bin/k3s not a dynamic executable $ file /usr/local/bin/k3s /usr/local/bin/k3s: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=6aycnyA_71ISj3_HJg7o/GpHAbw4ulNCCLvqe0SzK/2d5drffqdmr2MyNyq333/9XA--GyeOCo6NXKtBBnD, BuildID[sha1]=4182bc65bce33c39f1b377bf54d95fc2fcda29ee, stripped2.8 启动一个pod,并验证dns功能
kubectl run dns-test --image=busybox:1.36 --restart=Never – sleep 3600
kubectl exec -it dns-test – sh
/bin # nslookup www.zzz.com.cn
Server: 10.43.0.10
Address: 10.43.0.10:53
www.zzz.com.cn canonical name = www.dtc.zzz.com.cn
www.zzz.com.cn canonical name = www.dtc.zzz.com.cn
Name: www.dtc.zzz.com.cn
Address: 10.40.13.26
/bin # nslookup kubernetes
Server: 10.43.0.10
Address: 10.43.0.10:53
** server can’t find kubernetes.svc.cluster.local: NXDOMAIN
Name: kubernetes.default.svc.cluster.local
Address: 10.43.0.1
** server can’t find kubernetes.cluster.local: NXDOMAIN
** server can’t find kubernetes.svc.cluster.local: NXDOMAIN
** server can’t find kubernetes.cluster.local: NXDOMAIN
2.9 kubectl命令很慢,因为qenu是纯指令模拟,tls握手很慢
$ time kubectl get pod NAME READY STATUS RESTARTS AGE dns-test 1/1 Running 0 42s real 0m2.433s user 0m2.265s sys 0m0.879s # 使用kube proxy 跳过tls的测试 $ kubectl proxy & $ time curl -s http://localhost:8001/api/v1/pods | head -n 20 { "kind": "PodList", "apiVersion": "v1", "metadata": { "resourceVersion": "29486" }, "items": [ { "metadata": { "name": "dns-test", "namespace": "default", "uid": "135ef2b7-590d-4db8-a98b-4bb3b058732c", "resourceVersion": "29366", "generation": 1, "creationTimestamp": "2026-09-07T10:46:24Z", "labels": { "run": "dns-test" }, "managedFields": [ { real 0m0.662s user 0m0.307s sys 0m0.172s另外,查看镜像
$ time sudo crictl images IMAGE TAG IMAGE ID SIZE docker.io/library/busybox 1.36 b116e15507444 2.22MB docker.io/rancher/klipper-helm v0.13.3-build20260727 8e0d8d2218ec8 74.2MB docker.io/rancher/klipper-lb v0.4.17 eaa1212a2b456 5.28MB docker.io/rancher/local-path-provisioner v0.0.37 683316f41426e 15.1MB docker.io/rancher/mirrored-coredns-coredns 1.14.6 520212b8b0fcd 23.8MB docker.io/rancher/mirrored-library-traefik 3.7.8 7a652b1846f78 54.5MB docker.io/rancher/mirrored-metrics-server v0.9.0 d7b01abacd67f 23.3MB docker.io/rancher/mirrored-pause 3.10.2 4a83b15d3ecfe 321kB real 0m3.558s user 0m0.439s sys 0m0.268s3、离线安装k3s
“笨办法”部署 K3s,通常指的就是在目标节点没有网络连接,或你希望完全掌控部署过程的离线部署(Airgap Installation)。这种方法“笨”在每一步都需要手动操作,但能让你对集群的组成有最清晰的理解。
核心思路很简单:在有网的地方准备好所有“零件”,然后搬到没网的服务器上“组装”起来。整个过程可以分为三步:
📦 第一步:在有网的环境下准备“零件”
你需要在一台能访问互联网的机器上,准备好所有安装文件。
- 获取核心文件:从 K3s 的 GitHub Release 页面(https://github.com/k3s-io/k3s/releases)下载与你想安装版本对应的两个文件 :
- K3s 二进制文件(
k3s):这是 K3s 本身的可执行程序。 - 离线镜像压缩包(
k3s-airgap-images-amd64.tar.gz):这个文件包含了运行 K3s 所需的所有容器镜像。注意根据你服务器的 CPU 架构(如amd64,arm64)选择正确的版本 。
- K3s 二进制文件(
- 获取安装脚本:下载官方的安装脚本
install.sh。你可以通过以下命令在本地保存一份 :
如果你在中国大陆,可以使用国内镜像源以获得更快的下载速度 :curl-sfLhttps://get.k3s.io-oinstall.shcurl-sfLhttps://rancher-mirror.rancher.cn/k3s/k3s-install.sh-oinstall.sh - 打包传输:将下载好的
k3s二进制文件、k3s-airgap-images-amd64.tar.gz和install.sh脚本通过 U 盘、SCP 命令或其他任何方式,一并复制到你的离线目标服务器上。
🚀 第二步:在离线服务器上“组装”安装
登录到你的离线服务器,开始进行手动配置。
- 安置二进制文件:
- 将
k3s二进制文件移动到系统的程序目录,并赋予执行权限 :sudocpk3s /usr/local/bin/sudochmod+x /usr/local/bin/k3s
- 将
- 导入容器镜像:
- 创建 K3s 专用的镜像目录,然后将下载好的离线镜像包放进去 :
sudomkdir-p/var/lib/rancher/k3s/agent/images/sudocp./k3s-airgap-images-*.tar /var/lib/rancher/k3s/agent/images/
- 创建 K3s 专用的镜像目录,然后将下载好的离线镜像包放进去 :
- 运行安装脚本:
- 执行安装脚本,但需要使用
INSTALL_K3S_SKIP_DOWNLOAD=true这个关键参数,告诉脚本“不要从网上下载任何东西,就用当前目录下我准备好的文件” 。 - 安装 Server 节点(控制平面):
安装成功后,你可以看到节点的状态信息。节点的 Token 位于sudoINSTALL_K3S_SKIP_DOWNLOAD=true ./install.sh/var/lib/rancher/k3s/server/node-token,这个稍后添加 Agent 节点时会用到 。 - 安装 Agent 节点(工作节点):
在其他的工作节点上,同样执行前两步(放置二进制和镜像),然后使用 Server 节点的地址和 Token 进行注册 :# 将 <SERVER_IP> 替换为你的Server节点IP,<YOUR_TOKEN> 替换为实际的Token值sudoINSTALL_K3S_SKIP_DOWNLOAD=trueK3S_URL=https://<SERVER_IP>:6443K3S_TOKEN=<YOUR_TOKEN>./install.sh
- 执行安装脚本,但需要使用
✅ 第三步:验证安装结果
在所有节点上安装完成后,回到你的Server 节点,执行以下命令来查看集群状态 :
kubectl get nodes如果一切顺利,你会看到所有节点都处于Ready状态,一个属于你的离线 K3s 集群就部署成功了。
💡 一些补充的“笨”办法
- 使用配置文件:如果想指定更多参数(如 Pod 网段、禁用默认的 Traefik 等),可以提前在
/etc/rancher/k3s/config.yaml创建配置文件,然后再运行安装脚本 。 - 使用私有镜像仓库:如果节点很多,手动在每个节点导入镜像会比较繁琐。更高效的方法是搭建一个私有的镜像仓库,将镜像推送到仓库中,然后配置 K3s 从该私有仓库拉取镜像 。
- 升级也是“笨”办法:日后需要升级时,重复一遍上面的步骤即可:下载新版本的二进制和镜像包,替换旧文件,重新运行安装脚本,最后重启 K3s 服务 。
2026-9-18
离线方式安装了k3s,但启动后会自动重启。AI定位是本机cgroup是v1,新版k3s需要cgroup v2.
于是重新下载了1.28.15老版本,然后就正常了。
k3s装在这个目录下,有3个子目录
$sudodu-h/var/lib/rancher/k3s-d11.6G /var/lib/rancher/k3s/agent 195M /var/lib/rancher/k3s/data 11M /var/lib/rancher/k3s/server1.8G /var/lib/rancher/k3s $sudodu-h/var/lib/rancher/k3s/server-d18.0K /var/lib/rancher/k3s/server/etc 184K /var/lib/rancher/k3s/server/tls 32K /var/lib/rancher/k3s/server/cred9.9M /var/lib/rancher/k3s/server/db 120K /var/lib/rancher/k3s/server/static 56K /var/lib/rancher/k3s/server/manifests 11M /var/lib/rancher/k3s/server $sudodu-h/var/lib/rancher/k3s/data-d10/var/lib/rancher/k3s/data/cni 195M var/lib/rancher/k3s/data/ef4db44d834f98f27bc2d7cf944dcac51055966a52fe848f16ba44caa1202f89 195M /var/lib/rancher/k3s/dataimages下面是离线镜像。containerd下放了一份镜像(520M),一份容器运行时的分层文件系统,这个系统中的只读层就是镜像内容(520M)
$sudodu-h/var/lib/rancher/k3s/agent/-d1520M /var/lib/rancher/k3s/agent/images1.1G /var/lib/rancher/k3s/agent/containerd 16K /var/lib/rancher/k3s/agent/etc0/var/lib/rancher/k3s/agent/pod-manifests1.6G /var/lib/rancher/k3s/agent/拷贝了一个alpine.3.20.1.f10.tar镜像
用$ sudo ctr -n k8s.io images import alpine.3.20.1.f10.tar 导入,结果在ctr命令中能看到,在crictl命令中看不到
AI说k3s中,应该用这个命令重新导入,果然好使
$sudoctr--address/run/k3s/containerd/containerd.sock-nk8s.io imagesimportalpine.3.20.1.f10.tar $sudocrictl images IMAGE TAG IMAGE ID SIZE docker.io/library/alpine3.20.1.f10 177956ee946768.09MB docker.io/rancher/klipper-helm v0.9.3-build20241008 4e0aed78b287d 191MB docker.io/rancher/klipper-lb v0.4.9 11a5d8a9f31aa12.7MB docker.io/rancher/local-path-provisioner v0.0.30 b580d47bc23dd 52MB docker.io/rancher/mirrored-coredns-coredns1.11.3 c69fa2e9cbf5f63.3MB docker.io/rancher/mirrored-library-busybox1.36.1 6fd955f66c2314.5MB docker.io/rancher/mirrored-library-traefik2.11.10 1741c0b1ff49b 169MB docker.io/rancher/mirrored-metrics-server v0.7.2 48d9cfaaf390468.1MB docker.io/rancher/mirrored-pause3.66270bb605e12e 686kB2026-10-6
想下载一个pmail镜像,要改/etc/rancher/k3s/registries.yaml,增加下的内容,并重启k3s。 注意yaml格式必须正确,否则k3s启动后不太正常,比如kubectl get node 返回节点no ready。
ghcr.io:
endpoint:
- “https://ghcr.m.daocloud.io” # DaoCloud 镜像站,通常对 ghcr.io 支持较好
- “https://ghcr.io” # 原始地址作为备选
然后就可以下载成功了。k3s ctr images pull ghcr.io/jinnrry/pmail:latest
经验2 在root账号下,crictl images 命令可以查看到镜像。但ctr imagesls就查不到任何镜像。 K3s 使用独立的 containerd 实例,其 socket 文件路径通常为 /run/k3s/containerd/containerd.sock,而系统原生的 ctr 默认连接的是 /run/containerd/containerd.sock。因此,直接运行 ctr 可能会报错“failed to connect”或连接到一个空的/不同的 containerd 实例。 解决方案1、使用 K3s 内置的 ctr(推荐) K3s 提供了一个封装好的 k3s ctr 命令,它已经自动配置好了正确的 socket 路径和默认命名空间(通常默认为 k8s.io,但显式指定更稳妥)。 解决方案2、使用原生 ctr 并指定 Socket ctr--address/run/k3s/containerd/containerd.sock-nk8s.io imagesls用方案1试了一下,果然好使(base)Desktop$ k3s ctr imagesls|wc211467287(base)Desktop$ k3s ctr-nk8s.io imagesls|wc211467287编写一个pod.yaml文件
$catpod.yaml apiVersion: v1 kind: Pod metadata: name: my-alpine spec: containers: - name: alpine image: docker.io/library/alpine:3.20.1.f10 command:["sh","-c","echo 'Hello from Alpine!' && sleep infinity"]imagePullPolicy: Never $ kubectl apply-fpod.yaml pod/my-alpine created $ kubectl get pod NAME READY STATUS RESTARTS AGE my-alpine1/1 Running08s $ kubectlexec-itmy-alpinesh进入容器后,可以ping通外部网站
从节点上也可以ping通容器 ping 10.42.0.11
再看k3s创建的虚拟网络
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 56:9f:e8:6f:ba:23 brd ff:ff:ff:ff:ff:ff
inet 10.42.0.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::549f:e8ff:fe6f:ba23/64 scope link
valid_lft forever preferred_lft forever
5: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether 2a:aa:cc:e4:d7:47 brd ff:ff:ff:ff:ff:ff
inet 10.42.0.1/24 brd 10.42.0.255 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::28aa:ccff:fee4:d747/64 scope link
valid_lft forever preferred_lft forever
这两个接口都是Flannel CNI 插件创建的,但它们的角色完全不同:
1.flannel.1——Overlay 隧道接口(虚拟网卡)
- 类型:
VXLAN隧道接口(虽然这里显示为BROADCAST,MULTICAST,但本质是 Flannel 的 VXLAN 设备)。 - IP 地址:
10.42.0.0/32。注意这个 IP 是/32掩码,它不是给容器用的网关,而是本节点在 Overlay 网络中的唯一标识。 - 作用:负责跨节点通信。当 Pod A(在节点 1)需要访问 Pod B(在节点 2)时,数据包会被封装在 VXLAN 包中,通过
flannel.1发送到对端节点的flannel.1接口。 - 简单理解:它是节点之间的“高速公路”,用于在不同物理机之间传输 Pod 流量。
2.cni0——本地网桥接口(虚拟交换机)
- 类型:Linux Bridge(网桥)。
- IP 地址:
10.42.0.1/24。这是本节点上所有 Pod 的默认网关。 - 作用:负责本节点内部通信。
- 当 Pod 启动时,它的虚拟网卡(veth pair 的一端)会连接到
cni0网桥上。 - 如果 Pod 要访问同一节点上的其他 Pod,流量直接通过
cni0转发,不经过flannel.1。 - 如果 Pod 要访问外部网络或其他节点,流量先到达
cni0,然后通过路由表转发到flannel.1(或其他出口)。
- 当 Pod 启动时,它的虚拟网卡(veth pair 的一端)会连接到
- 简单理解:它是本节点内部的“局域网交换机”,连接着该节点上所有的 Pod。
数据流向示例
假设Pod A (10.42.0.5)在当前节点,想访问Pod B (10.42.1.5)在另一台节点:
- Pod A发送数据包 ->cni0(本节点网桥,发现目标不在本子网)。
- cni0根据路由表将数据包转发给flannel.1(隧道接口)。
- flannel.1将数据包封装成 VXLAN 包,通过物理网卡发送到另一台节点。
- 另一台节点的flannel.1收到并解封装。
- 另一台节点的cni0将数据包转发给Pod B。
总结对比
| 特性 | flannel.1 | cni0 |
|---|---|---|
| 类型 | VXLAN 隧道设备 | Linux Bridge (网桥) |
| IP 地址 | 10.42.0.0/32(节点标识) | 10.42.0.1/24(Pod 网关) |
| 主要功能 | 跨节点通信(Overlay 隧道) | 本节点内通信(Pod 互联) |
| 连接对象 | 其他节点的flannel.1 | 本节点所有 Pod 的 veth 网卡 |
| 类比 | 城市间的高速公路 | 小区内的内部道路 |
一句话总结:cni0管的是本节点内的 Pod 互联,flannel.1管的是跨节点的 Pod 互联。两者配合实现了 Kubernetes 集群的全网互通。