Skip to content

필사 모드: k3s 隔离网络安装完全指南 — 从镜像 tarball 带入到私有仓库、Agent 加入

中文
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

引言 — 用三个通过审批的文件把集群立起来

隔离网络安装之所以难,不是因为 Kubernetes 难。而是因为大部分安装工具都是按"需要的时候再下载"来设计的,而隔离网络里根本不存在那个"需要的时候"。直接执行 get.k3s.io 的脚本,第一行 curl 就会超时然后结束。

所以隔离网络的安装顺序是反过来的。先确定哪些东西必须带进去并把清单定死,在连通网络的区段里把这份清单准确填满,附上校验和送去审批,等介质进到内侧之后才开始安装。清单里漏掉任何一项,在内侧都没有补救的办法,只能等下一次带入审批,短则几天长则几周。

这篇文章就是针对 k3s,把那份清单和流程按命令逐条写下来的文档。验证所依据的版本如下。

项目确认时点确认的来源
k3s 最新稳定版v1.36.2+k3s1(2026-06-24 发布)2026-07-31k3s 发布页面
官方文档示例v1.33.3+k3s12026-07-31k3s Air-Gap Install
条件式导入v1.33.1+k3s1 以上2026-07-31k3s Air-Gap Install
证书自动续期距到期 120 天以内(重启时)2026-07-31k3s Certificate

下面的命令全部以 v1.36.2+k3s1 为基准编写。使用别的版本时只需替换版本字符串即可,但归档包与二进制文件的版本必须一致。原因后面会讲。

带入对象的清点 — 在连通网络区段里下载什么

k3s 隔离网络安装所需的文件最少三个。在此之上再加上工作负载镜像与部署清单。

#!/usr/bin/env bash
# collect-k3s.sh — 在能上网的暂存机上执行
set -euo pipefail

K3S_VERSION="v1.36.2+k3s1"
ARCH="amd64"
# URL 路径中的 + 必须编码为 %2B
URLVER="${K3S_VERSION/+/%2B}"
BASE="https://github.com/k3s-io/k3s/releases/download/${URLVER}"
OUT="./k3s-airgap-${K3S_VERSION}"

mkdir -p "${OUT}"
cd "${OUT}"

# 1) 系统镜像归档包(推荐 zstd,节省介质容量)
curl -fL -o "k3s-airgap-images-${ARCH}.tar.zst" \
  "${BASE}/k3s-airgap-images-${ARCH}.tar.zst"

# 2) k3s 二进制文件
curl -fL -o k3s "${BASE}/k3s"

# 3) 安装脚本(与 SKIP_DOWNLOAD 搭配使用,让执行时不动用网络)
curl -fL -o install.sh https://get.k3s.io

# 4) 校验和文件(发布页面有提供的情况下)
curl -fL -o "sha256sum-${ARCH}.txt" "${BASE}/sha256sum-${ARCH}.txt" || \
  echo "WARN: 发布页面没有 sha256sum-${ARCH}.txt。请用自行计算的值代替。"

ls -la

关于 sha256sum-amd64.txt 这个资产,本次确认时点无法断定它是否存在于发布页面的资产列表中。有的话就用它,没有的话,像下面这样把带入负责人自行计算的清单当作正本更安全。反正隔离网络的审批里,本来就需要一份能证明"是谁、何时、在哪里取得的文件"的自制清单。

# 生成自制校验和清单(连通网络区段)
cd "./k3s-airgap-v1.36.2+k3s1"
sha256sum k3s k3s-airgap-images-amd64.tar.zst install.sh > MANIFEST.sha256
cat MANIFEST.sha256

如果是启用了 SELinux 的 RHEL 系节点,还必须一并带入 k3s-selinux RPM。官方文档明确要求,在 SELinux 启用的节点上安装 k3s 之前要手动安装这个 RPM。漏掉这个文件的话安装本身能通过,但会以容器读不到卷的形式在很久以后才爆出来。

带入物清单

文件是否必需漏掉会发生什么
k3s(二进制文件)必需安装脚本尝试下载然后超时
k3s-airgap-images-amd64.tar.zst必需系统 Pod 全部 ErrImagePull
install.sh必需得自己手工创建 systemd 单元与符号链接
MANIFEST.sha256事实上必需无法在内侧判断介质是否损坏
k3s-selinux RPM条件性SELinux 启用的节点上卷访问失败
自有工作负载镜像归档包必需只有应用 ErrImagePull — 最常见的事故
Helm chart / 部署清单必需到部署阶段又得重新等带入审批

最后两行是这张表的核心。k3s 的归档包里只装了 k3s 把自己拉起来所需要的镜像。差不多就是 CoreDNS、Traefik、local-path-provisioner、metrics-server、pause 这些。内部应用的镜像当然没有,Prometheus 没有,替换用的 Ingress 控制器也没有。这一点后面的失败模式一节会再提一次。

介质带入与完整性验证

介质进到内侧之后,安装前务必先从验证开始。压缩归档包如果是悄悄损坏着进来的,k3s 会把导入失败当成一行日志放过去然后照样启动,那样你要等到几分钟后看 Pod 状态时才会发现。

# 在隔离网络节点上
cd /opt/staging/k3s-airgap-v1.36.2+k3s1

# 1) 校验和验证 — 这一步失败就不再往下走
sha256sum -c MANIFEST.sha256

# 2) 确认归档包本身能打开(需要 zstd)
zstd -t k3s-airgap-images-amd64.tar.zst && echo "archive OK"

# 3) 确认归档包里装着的镜像列表
zstd -dc k3s-airgap-images-amd64.tar.zst | tar -tf - | grep -E 'manifest|repositories' | head

节点上可能没有 zstd 二进制文件。隔离网络的 Linux 镜像大多是最小化安装,而且有些发行版里 zstd 不是默认软件包。这种情况下有两个选择。要么先从 OS 软件包镜像源安装 zstd,要么干脆在连通网络那边就取 .tar.gz 或者未压缩的 .tar 资产。发布页面上 k3s-airgap-images-amd64.tar.tar.gz.tar.zst 三种形态都有提供。哪怕觉得容量可惜,首次安装用 .tar.gz 也能少一个失败点。

Server 节点安装 — 镜像放置与 SKIP_DOWNLOAD

现在进入实际安装。顺序很重要。先放置镜像,然后再执行安装脚本。反过来的话,k3s 启动时找不到镜像,会进入重试循环。

#!/usr/bin/env bash
# install-k3s-server.sh — 隔离网络 Server 节点
set -euo pipefail

STAGE=/opt/staging/k3s-airgap-v1.36.2+k3s1

# 1) 放置系统镜像归档包
sudo mkdir -p /var/lib/rancher/k3s/agent/images/
sudo cp "${STAGE}/k3s-airgap-images-amd64.tar.zst" /var/lib/rancher/k3s/agent/images/

# 2) 启用条件式导入缓存(v1.33.1+k3s1 以上)
#    有这个文件的话,只要归档包没变,重启时就会跳过重新导入
sudo touch /var/lib/rancher/k3s/agent/images/.cache.json

# 3) 放置二进制文件
sudo cp "${STAGE}/k3s" /usr/local/bin/k3s
sudo chmod +x /usr/local/bin/k3s

# 4) 执行安装脚本 — 指示它跳过下载
sudo chmod +x "${STAGE}/install.sh"
sudo INSTALL_K3S_SKIP_DOWNLOAD=true "${STAGE}/install.sh"

INSTALL_K3S_SKIP_DOWNLOAD=true 就是这套流程的全部也不为过。没有这个值,脚本就会尝试连接发布服务器。想同时传服务器选项的话,用 INSTALL_K3S_EXEC

sudo INSTALL_K3S_SKIP_DOWNLOAD=true \
  INSTALL_K3S_EXEC="server --cluster-init --tls-san 10.10.20.10 --tls-san k8s-api.internal.example --write-kubeconfig-mode 0644 --disable traefik" \
  /opt/staging/k3s-airgap-v1.36.2+k3s1/install.sh

比起把选项一长串排在命令行上,用配置文件来管理更好。隔离网络里重装和节点扩容都很频繁,每次都会有人漏掉某一个选项。

# /etc/rancher/k3s/config.yaml
cluster-init: true
tls-san:
  - 10.10.20.10
  - k8s-api.internal.example
write-kubeconfig-mode: '0644'
disable:
  - traefik
node-label:
  - topology.kubernetes.io/zone=dc-a

启动确认如下进行。

sudo systemctl status k3s --no-pager
sudo k3s kubectl get nodes -o wide
sudo k3s kubectl -n kube-system get pods

# 直接在 containerd 存储里确认镜像导入是否真的发生了
sudo k3s ctr images ls | awk '{print $1}' | sort -u | head -20

k3s ctr images ls 如果是空的,说明导入失败了。这时候该看的不是 journalctl -u k3s -n 200,而是 containerd 的日志。路径是 /var/lib/rancher/k3s/agent/containerd/containerd.log

Agent 加入与内嵌 etcd 高可用组建

Agent 节点也是同样的顺序。放置好镜像归档包与二进制文件之后,把加入信息用环境变量传进去执行安装脚本。

# 在 Server 节点上确认 token
sudo cat /var/lib/rancher/k3s/server/node-token
#!/usr/bin/env bash
# install-k3s-agent.sh — 隔离网络 Agent 节点
set -euo pipefail

STAGE=/opt/staging/k3s-airgap-v1.36.2+k3s1
SERVER_URL="https://10.10.20.10:6443"
JOIN_TOKEN="K10xxxxxxxx::server:xxxxxxxx"

sudo mkdir -p /var/lib/rancher/k3s/agent/images/
sudo cp "${STAGE}/k3s-airgap-images-amd64.tar.zst" /var/lib/rancher/k3s/agent/images/
sudo touch /var/lib/rancher/k3s/agent/images/.cache.json
sudo cp "${STAGE}/k3s" /usr/local/bin/k3s
sudo chmod +x /usr/local/bin/k3s

sudo INSTALL_K3S_SKIP_DOWNLOAD=true \
  K3S_URL="${SERVER_URL}" \
  K3S_TOKEN="${JOIN_TOKEN}" \
  "${STAGE}/install.sh"

如果需要高可用,数据存储的选择必须一开始就做。k3s 的默认数据存储是 SQLite,而 SQLite 无法增加 Server 节点。要走内嵌 etcd 的话,第一台 Server 用 --cluster-init 拉起来,其余的 Server 用 --server 接上去。

# Server 1 — 集群初始化(在 config.yaml 里写 cluster-init: true 也一样)
sudo INSTALL_K3S_SKIP_DOWNLOAD=true \
  K3S_TOKEN="共享密钥" \
  INSTALL_K3S_EXEC="server --cluster-init --tls-san 10.10.20.9" \
  ./install.sh

# Server 2、3 — 加入既有的 Server
sudo INSTALL_K3S_SKIP_DOWNLOAD=true \
  K3S_TOKEN="共享密钥" \
  INSTALL_K3S_EXEC="server --server https://10.10.20.10:6443 --tls-san 10.10.20.9" \
  ./install.sh

官方文档明确写着,内嵌 etcd 集群要维持法定人数,Server 节点数必须是奇数。n 台 Server 的法定人数是 (n/2)+1。两台的组成与一台的组成故障容忍能力相同,却只让运维复杂度上升,所以没有意义。另外 --cluster-dns--cluster-domain--cluster-cidr--service-cidr 这类网络类的 flag,在所有 Server 节点上都必须相同。在隔离网络里一台一台扩容节点的过程中,这些值很容易错开。

即便已经用 SQLite 把单台 Server 立起来了,也还是有办法。按照官方文档,把既有的 Server 带上 --cluster-init flag 重启,就会切换成 etcd。不过这不是那种可以在生产环境里不做任何备份就去尝试的作业。

私有仓库路径 — registries.yaml 与自签名 CA

在隔离网络里分发镜像的正规做法,是架一个内部镜像仓库并让节点只看向那里。k3s 用 /etc/rancher/k3s/registries.yaml 来配置这件事。

# /etc/rancher/k3s/registries.yaml
mirrors:
  docker.io:
    endpoint:
      - 'https://registry.internal.example:5000'
  registry.k8s.io:
    endpoint:
      - 'https://registry.internal.example:5000'
  ghcr.io:
    endpoint:
      - 'https://registry.internal.example:5000'
    rewrite:
      '^(.*)': 'mirror/ghcr/$1'

configs:
  'registry.internal.example:5000':
    auth:
      username: k3s-puller
      password: '带入时替换'
    tls:
      ca_file: /etc/rancher/k3s/certs/internal-ca.crt

想把所有仓库都塞进一个端点的话,就用通配符条目。官方文档明确指出,mirrors 和 configs 两边都可以把星号条目当作默认配置来用,而且星号必须用引号括起来。

# 把所有仓库都指向内部镜像仓库(通配符)
mirrors:
  '*':
    endpoint:
      - 'https://registry.internal.example:5000'

configs:
  'registry.internal.example:5000':
    tls:
      ca_file: /etc/rancher/k3s/certs/internal-ca.crt

rewrite 会改写路径的前半部分。在 Harbor 那种以项目为单位强制命名空间的仓库上,没有这个就没法原样使用原来的路径。上面的例子会把 ghcr.io/foo/bar 送到 registry.internal.example:5000/mirror/ghcr/foo/bar

如果用自签名 CA,信任配置需要两处。有一半左右的人在这里绊倒。

# 1) 给 containerd 用 — registries.yaml 里 ca_file 指向的位置
sudo mkdir -p /etc/rancher/k3s/certs
sudo cp /opt/staging/internal-ca.crt /etc/rancher/k3s/certs/internal-ca.crt
sudo chmod 644 /etc/rancher/k3s/certs/internal-ca.crt

# 2) OS 信任存储 — 给 helm、skopeo、crictl、curl 等其他客户端用
#    RHEL 系
sudo cp /opt/staging/internal-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
#    Debian 系
sudo cp /opt/staging/internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt
sudo update-ca-certificates

# 3) registries.yaml 的变更必须重启才会生效 — 在所有节点上
sudo systemctl restart k3s        # Server 节点
sudo systemctl restart k3s-agent  # Agent 节点

第三步是官方文档明确强调的。原话是"配置变更要生效,必须在每个节点上重启 k3s"。而且这个文件必须分发到所有会 pull 镜像的节点上。只放进 Server 而忘了 Agent 是很常见的失误。

验证这样做。

# 确认仓库配置是否已反映到 containerd
sudo k3s ctr --namespace k8s.io images pull \
  registry.internal.example:5000/library/busybox:1.36

# 失败的话就看 containerd 日志(而不是 kubelet 的消息)
sudo tail -n 100 /var/lib/rancher/k3s/agent/containerd/containerd.log

节点间的镜像再分发 — 内嵌仓库镜像源

k3s 里有基于 Spegel 的内嵌分布式仓库镜像源。它能让某个节点上已经有的镜像,被别的节点在没有外部仓库的情况下取走。在隔离网络里还没架起内部镜像仓库的阶段,或者边缘节点连内部镜像仓库都够不到的情况下很有用。

# /etc/rancher/k3s/config.yaml(所有 Server 节点)
embedded-registry: true
# /etc/rancher/k3s/registries.yaml(所有节点)
mirrors:
  '*':

按官方文档,节点之间必须能用内部 IP 互相到达 TCP 5001(共享可用镜像列表的 P2P 网络)和 6443(各节点自己托管的本地 OCI 仓库)。在防火墙很严密的隔离网络里 5001 常常是被堵住的,所以要事先打开。

有一个误解需要澄清。这个功能做的只是再分发已经存在于某个节点上的镜像。它没有把镜像推进去的 push 功能。最初的投入依然得靠 airgap 归档包或者 k3s ctr images import 来做。

就是这里卡住 — 隔离网络 k3s 的失败模式

只写幸福路径的文档在隔离网络里没有用。这里整理实际会被卡住的地方。

症状真正的原因确认方法
系统 Pod 起来了但只有我的应用 ErrImagePullairgap 归档包里没有我的镜像确认 k3s ctr images ls 里是否没有该镜像
ErrImagePull 的消息指向 docker.io只暴露出了默认仓库端点回退的结果在 containerd.log 里确认实际的首次尝试对象
改了 registries.yaml 却没变化没重启,或者没分发到 Agentsystemctl restart 之后重试,确认所有节点上文件都在
helm 通得过但只有 containerd TLS 失败只配了 ca_file 而漏了 OS 信任存储(或者反过来)分别尝试用 curl 连接仓库和 ctr images pull
外部域名查询每次卡 5 秒CoreDNS 的上游无法到达确认 CoreDNS ConfigMap 的 forward 对象与节点的 resolv.conf
想加 Server 却加入不了数据存储是 SQLite确认 k3s kubectl get nodes 与 Server 启动 flag
重启后系统 Pod 去尝试 pull归档包版本与二进制文件版本不一致比较 k3s -v 与归档包文件名里的版本
容器读不到卷(RHEL 系)未安装 k3s-selinux RPMgetenforce 与 rpm -q k3s-selinux

失败模式 1 — 归档包里没有的镜像

最常见也最让人无力的失败。k3s 的 airgap 归档包只装了 k3s 自身需要的镜像。内部应用、换掉的 Ingress 控制器、监控栈,全都得另外带入。

如果内部镜像仓库还没有,可以临时直接导入到节点上。

# 在连通网络区段:把需要的镜像打成一个归档包
docker pull myapp/api:1.4.2
docker pull myapp/worker:1.4.2
docker save -o myapp-images.tar myapp/api:1.4.2 myapp/worker:1.4.2
sha256sum myapp-images.tar >> MANIFEST.sha256

# 在隔离网络节点上:方法 A — 放到镜像目录里然后重启
sudo cp myapp-images.tar /var/lib/rancher/k3s/agent/images/
sudo systemctl restart k3s

# 方法 B — 不重启,立刻导入
sudo k3s ctr --namespace k8s.io images import myapp-images.tar
sudo k3s ctr --namespace k8s.io images ls | grep myapp

用方法 B 放进去的镜像时,请确认 Pod spec 的 imagePullPolicy。标签如果是 latest,默认策略就变成 Always,于是节点上明明有镜像却还要去尝试 pull 然后失败。在隔离网络里标签要始终写成明确的版本,必要时把 imagePullPolicy: IfNotPresent 钉死。

失败模式 2 — 默认端点回退制造的假线索

containerd 有一种行为:无关 registries.yaml 的镜像源配置,作为最后一次尝试去连接原本的仓库。在隔离网络里这最后一次尝试必然失败,而且kubelet 给用户看的错误就是这最后一次尝试的结果。所以你分不清到底是镜像源配错了、镜像源里没有那个镜像、还是压根没走镜像源。

应对有两种。

# 应对 A — 真正的原因在 containerd 日志里
sudo grep -iE 'failed|error' /var/lib/rancher/k3s/agent/containerd/containerd.log | tail -40
# 应对 B — /etc/rancher/k3s/config.yaml
# 对已配置镜像源的仓库,关掉默认端点回退
disable-default-registry-endpoint: true

官方文档把这个选项解释为"当该仓库配置了镜像源时,禁用 containerd 的默认仓库端点回退"。它是在 2024 年 1 月的发布中作为实验性功能引入的,并且只适用于在 registries.yaml 里有镜像源条目的仓库。没有配置镜像源的仓库依然会回退。打开这个选项之后,错误消息就会指向实际的失败地点,调试时间会大幅减少。

失败模式 3 — CoreDNS 什么都找不到

症状是集群内部的名字(kubernetes.default.svc.cluster.local)解析得好好的,但内部域名或外部域名的查询会卡 5 秒然后失败。CoreDNS 默认的 Corefile 会把集群域之外的查询转发(forward)给节点的解析器,而如果那个解析器指向的是隔离网络中无法到达的公网 DNS(比如 8.8.8.8),那就每次查询都得等一次超时。

先确认实际的配置。不要猜,直接从集群里读出来。

# 确认 CoreDNS 把什么当作 forward 对象
sudo k3s kubectl -n kube-system get configmap coredns -o yaml

# 确认节点的解析器
cat /etc/resolv.conf
sudo resolvectl status 2>/dev/null | head -30

# 从节点上直接确认内部 DNS 是否真的会应答
dig @10.10.10.53 registry.internal.example +short

应对分两条路。第一,把节点的 /etc/resolv.conf 整理成只指向内部 DNS。如果是 systemd-resolved 管理的节点,因为符号链接的关系,直接改文件也会被改回去,所以必须改 resolved 的配置。第二,如果不方便动节点上的文件,就给 k3s 另外指定一个 kubelet 用的解析器文件。

# 单独放一份隔离网络专用的解析器文件
sudo tee /etc/rancher/k3s/resolv.conf > /dev/null <<'EOF'
nameserver 10.10.10.53
nameserver 10.10.10.54
search internal.example
options timeout:1 attempts:2
EOF
# /etc/rancher/k3s/config.yaml
resolv-conf: /etc/rancher/k3s/resolv.conf

--resolv-conf flag 在官方 CLI 文档里记载为 "Kubelet resolv.conf file",也可以用环境变量 K3S_RESOLV_CONF 来指定。如果是内部 DNS 本身就完全没有的环境,那还不如在 CoreDNS 的 Corefile 里把 forward 对象固定到内部解析器,或者只把需要的名字用 hosts 插件钉进去。options timeout:1 至少能把那些仍然漏出去的查询的延迟压到 1 秒。

版本漂移与安装后的立即验证

归档包与二进制文件错开的时候

在隔离网络里由多个人跨越几个月做带入,这个问题必然会出现。上个月带进来的 v1.35.6 归档包还留在镜像目录里,而这次带进来的二进制文件是 v1.36.2。

这时候会发生的事情是这样。k3s v1.36.2 要求与它自己匹配的 CoreDNS、pause、local-path-provisioner 标签,但节点上导入的镜像是 v1.35.6 用的标签。containerd 存储里没有那个标签,于是去尝试 pull,而这是隔离网络,所以失败。日志里只留下"找不到镜像",版本不一致这条线索哪里都没有。

诊断与清理这样做。

# 1) 二进制文件版本
k3s -v

# 2) 镜像目录里堆了些什么
ls -la /var/lib/rancher/k3s/agent/images/

# 3) containerd 存储里的系统镜像标签
sudo k3s ctr --namespace k8s.io images ls | grep -E 'coredns|pause|local-path|metrics-server'
# 清理 — 删掉旧归档包,只留下新归档包,然后重启
sudo systemctl stop k3s
sudo rm -f /var/lib/rancher/k3s/agent/images/k3s-airgap-images-amd64-v1.35.6.tar.zst
sudo rm -f /var/lib/rancher/k3s/agent/images/.cache.json
sudo cp /opt/staging/k3s-airgap-images-amd64.tar.zst /var/lib/rancher/k3s/agent/images/
sudo systemctl start k3s

要把 .cache.json 一起删掉的理由是,在 v1.33.1+k3s1 以上,这个缓存文件被用来判断"这个归档包已经导入过了"。归档包文件换掉了而缓存还留着的话,就可能跳过导入。官方文档的升级流程也明确写着要放入新归档包并删除既有归档包

从根上防止漂移的方法,是把带入的单位按版本捆起来。

# 在带入目录名与文件名两边都刻上版本
/opt/staging/k3s-v1.36.2+k3s1/
├── MANIFEST.sha256
├── VERSION            # v1.36.2+k3s1 一行
├── install.sh
├── k3s
└── k3s-airgap-images-amd64.tar.zst
# 在安装脚本里放一道版本闸门
EXPECTED="$(cat "${STAGE}/VERSION")"
ACTUAL="$(/usr/local/bin/k3s -v | awk '/^k3s version/{print $3}')"
if [ "${ACTUAL}" != "${EXPECTED}" ]; then
  echo "FATAL: 二进制文件 ${ACTUAL} 与带入包 ${EXPECTED} 不一致" >&2
  exit 1
fi

安装后的立即验证脚本

安装结束不等于结束。在隔离网络里,"看上去总算是起来了"的状态和"实际上能用"的状态之间差距很大。把下面的脚本在安装后立刻跑一遍,几天后的事故会大幅减少。

#!/usr/bin/env bash
# verify-airgap-k3s.sh
set -uo pipefail
ERR=0
K="sudo k3s kubectl"

echo "== 1. 节点状态"
${K} get nodes -o wide
NR=$(${K} get nodes --no-headers | grep -cv " Ready ") || true
[ "${NR}" -gt 0 ] && { echo "FAIL: NotReady 节点 ${NR} 台"; ERR=$((ERR+1)); }

echo "== 2. 系统 Pod"
BAD=$(${K} -n kube-system get pods --no-headers | grep -cvE "Running|Completed") || true
[ "${BAD}" -gt 0 ] && { ${K} -n kube-system get pods | grep -vE "Running|Completed"; ERR=$((ERR+1)); }

echo "== 3. 是否有往外部仓库跑的尝试"
if sudo grep -qiE 'docker\.io|registry\.k8s\.io|ghcr\.io' \
     /var/lib/rancher/k3s/agent/containerd/containerd.log 2>/dev/null; then
  echo "WARN: containerd 日志里有连接外部仓库的痕迹"
  echo "      请确认 registries.yaml 的镜像源配置与 disable-default-registry-endpoint"
fi

echo "== 4. DNS"
${K} run dnscheck --rm -i --restart=Never --image=registry.internal.example:5000/library/busybox:1.36 -- \
  nslookup kubernetes.default.svc.cluster.local || { echo "FAIL: 集群 DNS"; ERR=$((ERR+1)); }

echo "== 5. 证书到期"
sudo k3s certificate check --output table

echo "== 6. 数据存储"
if [ -d /var/lib/rancher/k3s/server/db/etcd ]; then
  echo "datastore: embedded etcd"
  ls -la /var/lib/rancher/k3s/server/db/snapshots/ 2>/dev/null | tail -5
else
  echo "datastore: sqlite(无法扩容 Server 节点)"
fi

echo "== 结果: 错误 ${ERR} 件"
exit "${ERR}"

第 5 项的 k3s certificate check --output table 在隔离网络运维里格外重要。k3s 的客户端与服务端证书自签发日起有效 365 天,服务重启时如果距到期在 120 天以内就会自动续期。但没有重启就没有续期。几个月里谁都不去碰的隔离网络集群因证书到期而死掉的路径,正是这里。详细的应对在 Day 2 运维篇里讲。

结语 — 带入清单本身就是设计文档

隔离网络 k3s 安装里真正的作业不是命令而是清单。归档包、二进制文件、安装脚本、校验和、CA、工作负载镜像、chart、SELinux RPM,把内侧会需要的每一个字节都在外侧预先数清楚,这才是工作。命令本身连十行都不到。

而且那份清单不是做一次就完事的。版本上去了,归档包和二进制文件就得一起上去,而两者只有一边动了的那一刻,集群就会悄悄地变得不对劲。在带入单位上刻上版本、在安装脚本里放一道闸门,这五行能帮你挡下等待下一次带入审批的三周。

参考资料

현재 단락 (1/262)

隔离网络安装之所以难,不是因为 Kubernetes 难。而是因为大部分安装工具都是按"需要的时候再下载"来设计的,而隔离网络里根本不存在那个"需要的时候"。直接执行 get.k3s.io 的脚本,第...

작성 글자: 0원문 글자: 14,928작성 단락: 0/262