Skip to content

03 ISO 无人值守安装 12 节点

本章目标:用一份可复用的 config.yaml + 一个生成脚本,把 12 台机器装成 3 management + 9 worker 的高可用集群,装完即可登录 UI,并完成基础加固。


3.1 config.yaml 是怎么被安装器读到的

Harvester 安装器(基于 Elemental)在启动时按下面的优先级获取配置:

text
① 内核参数里显式给的单项配置(harvester.install.xxx=yyy)
        ↑ 优先级最高,会覆盖 config 文件里的同名项
② 内核参数 harvester.install.config_url=http://.../config.yaml
        ↑ 本书主用方式:远程 config,一份文件管一台机器
③ 安装器 TUI 里人工填写的值
        ↑ 官方明确:TUI 里人工填的值 **优先于** 远程 config 文件
④ 内置默认值

⚠️ 第 ③ 条是官方文档里写明的行为,也是很多人「明明给了 config 却不生效」的原因:只要你在 TUI 界面里点过、填过某个字段,那个字段就以你填的为准。 真正的无人值守要靠 install.automatic: true 跳过所有交互步骤,让安装器完全按 config 走。

三种送达方式:

方式怎么做适用
远程 URL(本书主方案)ISO 启动 → 在安装器菜单选 "Harvester Installer" 并按 e(或直接在引导项)追加内核参数 harvester.install.automatic=true harvester.install.config_url=http://10.181.0.5:8080/configs/config-node01.yaml有 HTTP 服务,12 台机器可插介质/带外挂载
PXE / iPXE 全自动DHCP + HTTP 托管 vmlinuz/initrd/rootfs.squashfs + iPXE 脚本按 MAC 分发不同 config_url大批量、机房有 PXE
U 盘内置 config把 ISO 写入 U 盘,再在 U 盘的 COS_OEM 分区里放 config.yaml完全离线、无 HTTP

3.2 config.yaml 字段全景(v1.8 已核对)

下面是本书会用到的全部字段,按层级列出。加粗的是本环境必须写对的。

字段类型/示例说明与坑
scheme_version1配置格式版本,v1.8 用 1
server_urlcreate 留空 "";join 填 https://10.181.0.100:443create 模式下写了非空值会直接安装失败
token32 位随机串create 与 join 必须一致;join 时它就是 node token
sans- harvester.example.local顶层字段(不在 install 下),加进 API server 证书的 SAN
os.hostnameharvester-01不填会生成随机名
os.password"xxx"默认用户 rancher;可明文或 /etc/shadow 加密串;含 - 必须加引号
os.ssh_authorized_keys- "ssh-rsa AAAA..."注意是下划线;也支持 github:username
os.modules[kvm, nvme]开机加载的内核模块
os.dns_nameservers[10.181.0.2]宿主机 DNS
os.ntp_servers[ntp.aliyun.com, ...]强烈建议配,etcd 怕时间漂移
os.sysctlsmap例:kernel.printk: "4 4 1 7"
os.labelsmap变成 K8s node label,可用于 VlanConfig 的 nodeSelector
os.write_fileslist往宿主机写文件(path/content/owner/permissions/encoding
os.after_install_chroot_commandslist装完在 chroot 里跑命令;注意此阶段没有 DNS
os.persistent_state_pathslist让这些路径的修改跨重启保留(宿主机是只读系统)
os.environmentmap例:http_proxy / https_proxy
os.externalStorageConfigobjSAN/multipath 场景才用
os.additionalKernelArgumentsstr例:"multipath=on"
os.sshd.disable_password_authtrue关闭 SSH 密码登录;⚠️ 若同时没配 ssh_authorized_keys,将彻底失去 SSH 访问,只能走控制台
os.sshd.sftptrue开启 SFTP 子系统
install.modecreate / join第一台 create,其余 join
install.roledefault / management / worker / witness显式指定角色,避免"谁先 join 谁变 management"的不确定性
install.automatictrue跳过所有交互步骤,真正无人值守
install.silent文档标注 Reserved
install.skipcheckstrue / falsefalse 时硬件不达标会中止安装;警告写入 /var/log/console.log
install.device/dev/sda系统盘,会被分区格式化
install.data_disk/dev/sdbLonghorn 数据盘;PXE 场景建议用 /dev/disk/by-id/...
install.persistent_partition_size150GiCOS_PERSISTENT 分区大小,最小 150Gi(存镜像与包)
install.no_formattrue不分区不格式化(特殊场景)
install.force_efitrue强制 EFI 安装(即使没检测到 EFI)
install.powerofftrue装完关机而不是重启(批量装机时便于统一上电)
install.iso_urlhttp://10.181.0.5:8080/iso/harvester-v1.8.2-amd64.iso从内核/vmlinuz 启动(PXE)时必用;ISO 引导时可不写
install.ttyttyS0,115200n8串口控制台,带外/远程装机必备
install.vip10.181.0.100UI/API 入口
install.vip_modestatic(或 dhcp + vip_hw_addr本环境用 static
install.vip_hw_addrMACvip_mode: dhcp 时需要
install.management_interface见 3.3interfaces[].name/hwAddrmethodipsubnet_maskgatewaydefault_routebond_options{mode,miimon,mtu}mtuvlan_id
install.cluster_pod_cidr默认 10.52.0.0/16本环境保持默认
install.cluster_service_cidr默认 10.53.0.0/16本环境保持默认
install.cluster_dns默认 10.53.0.10必须落在 service CIDR 内
install.addonsrancher_monitoring.enabled: true可用键:harvester_vm_import_controllerharvester_pcidevices_controllerrancher_monitoringrancher_loggingharvester_seeder;默认全部关闭
install.harvester.storage_class.replica_count3默认 StorageClass harvester-longhorn 副本数,1–3
install.harvester.longhorn.default_settings.guaranteedInstanceManagerCPU12每个 Longhorn IM 预留的 CPU 百分比(0–12,超范围按 12)
install.harvester.longhorn.default_settings.storageReservedPercentageForDefaultDisk0默认盘预留百分比(0–30)
install.webhookslist装机 STARTED/SUCCEEDED/FAILED 回调,可与 Cobbler/自动化系统联动
install.wipe_all_disks / install.wipe_disks_listbool / list清除带 COS_OEM 标签的分区(重装老机器时有用)
install.with-net-imagesbooltrue = 不预载 ISO 内镜像,装完联网拉(内网别开)
system_settingsmap只在 create 模式生效,join 忽略;值必须是字符串(JSON 也要写成字符串)

⚠️ 字段名大小写不统一是 Harvester config 的历史包袱:os.ssh_authorized_keys 用下划线,os.externalStorageConfig 用驼峰,install.force_efi 用下划线,install.vip_hw_addr 用下划线。照抄本文/附录的写法,不要凭直觉改。


3.3 第一台(create)完整配置:config-node01.yaml

这份文件是全书的「母版」,appendix/configs/config-node01.yaml 就是它。

yaml
# appendix/configs/config-node01.yaml
# ============================================================
# Harvester v1.8.2 - node01 (management, create)
# 主机名 harvester-01  管理IP 10.181.0.11/24  VIP 10.181.0.100
# ============================================================
scheme_version: 1
server_url: ""                      # create 模式必须为空或整行删除

# ---- 集群密钥:11 台 join 必须与此完全一致 ----
token: xxx

# ---- 证书 SAN:把 VIP / 域名 / 前几台节点 IP 写进去,避免浏览器与 Rancher 报证书错 ----
sans:
  - "10.181.0.100"
  - "harvester.example.local"
  - "10.181.0.11"
  - "10.181.0.12"
  - "10.181.0.13"

os:
  hostname: harvester-01
  password: "xxx"        # 默认用户 rancher;含特殊字符务必加引号
  ssh_authorized_keys:              # 注意:下划线,不是 os.ssh.authorized_keys
    - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...(你的公钥,整行粘贴)"
  sshd:
    sftp: true
    disable_password_auth: false    # 先留 false,验证公钥能登录后再改 true(见 3.10)
  modules:
    - kvm
    - vhost_net
    - nvme
  dns_nameservers:
    - 10.181.0.2
  ntp_servers:
    - ntp.aliyun.com
    - 0.suse.pool.ntp.org
    - 1.suse.pool.ntp.org
  labels:                           # 变成 K8s node label,后面 VlanConfig 可用它选节点
    topology.kubernetes.io/zone: zone-a
    harvester-role: management

install:
  mode: create
  role: management                  # 显式声明,避免依赖 join 顺序
  automatic: true                   # 跳过 TUI 交互,真正无人值守
  skipchecks: false                 # 生产环境保持 false;硬件不达标就让它报错
  force_efi: true
  tty: ttyS0,115200n8               # 带外串口装机必备
  poweroff: false                   # 装完自动重启进入系统

  device: /dev/sda                  # 系统盘(会被格式化)
  data_disk: /dev/sdb               # Longhorn 数据盘
  persistent_partition_size: 200Gi  # 镜像/包分区,最小 150Gi

  vip: 10.181.0.100
  vip_mode: static

  management_interface:
    interfaces:
      - name: eno1
        hwAddr: "AA:BB:CC:DD:EE:01"   # 强烈建议写 MAC:不同批次网口名可能不同
      - name: eno2
        hwAddr: "AA:BB:CC:DD:EE:02"
    method: static
    ip: 10.181.0.11
    subnet_mask: 255.255.255.0
    gateway: 10.181.0.1
    default_route: true
    mtu: 1500
    vlan_id: 0                      # 0 = untagged;若管理网带 VLAN 则填对应 VID
    bond_options:
      mode: active-backup           # 装机阶段最稳,不依赖交换机 LACP
      miimon: 100
      mtu: 1500

  # ---- 保持 Kubernetes 默认网段(本环境要求)----
  cluster_pod_cidr: 10.52.0.0/16
  cluster_service_cidr: 10.53.0.0/16
  cluster_dns: 10.53.0.10

  # ---- 存储:3 副本,跨 12 节点 ----
  harvester:
    storage_class:
      replica_count: 3
    longhorn:
      default_settings:
        guaranteedInstanceManagerCPU: 12
        storageReservedPercentageForDefaultDisk: 0

  # ---- 装完就开监控(日志按需)----
  addons:
    rancher_monitoring:
      enabled: true
    rancher_logging:
      enabled: false
    harvester_vm_import_controller:
      enabled: false
    harvester_pcidevices_controller:
      enabled: false

# ---- 系统设置:只有 create 节点生效,join 会忽略 ----
system_settings:
  auto-disk-provision-paths: ""
  # 内网有私有镜像仓库时打开(值必须是字符串形式的 JSON)
  # containerd-registry: '{"Mirrors":{"docker.io":{"Endpoints":["https://registry.example.local:5000"]}}}'
  # ui-source: bundled

最容易踩的 6 个坑

  1. server_url 必须为空:create 模式写任何非空值都会安装失败。
  2. install.automatic: true + 不要在 TUI 里点任何字段:一旦人工填写,TUI 值覆盖 config。
  3. devicedata_disk 必须是不同物理盘:写反或写同一个盘会导致 Longhorn 无盘可用或系统被覆盖。
  4. skipchecks: false 时硬件不达标会中止安装:内存不足、磁盘 IOPS 不够、CPU 核数不达标都会拦下来。测试环境可临时设 true,但要清楚自己在做什么。
  5. sans 要在第一台就写全:证书是 create 时签发的,后续加域名要走证书轮换流程,麻烦得多。
  6. system_settings 只在 create 生效:想改集群级设置,要么写在 node01 的 config 里,要么装完用 UI/kubectl 改。

3.4 join 节点配置:只有几处不同

harvester-04(worker)为例,完整文件见 appendix/configs/config-node04.yaml

字段node01(create)node02/03(mgmt join)node04–12(worker join)
server_url""https://10.181.0.100:443同左
install.modecreatejoinjoin
install.rolemanagementmanagementworker
os.hostnameharvester-01harvester-02/03harvester-04…12
management_interface.ip10.181.0.11.12 / .13.14.22
management_interface.interfaces[].hwAddrnode01 的 MAC各节点自己的 MAC各节点自己的 MAC
install.vip / vip_mode10.181.0.100 / static同样要写同样要写
sans生效被忽略(证书由集群签发)被忽略
system_settings生效被忽略被忽略
token定义者必须完全一致必须完全一致
yaml
# appendix/configs/config-node04.yaml
scheme_version: 1
server_url: https://10.181.0.100:443      # ← 关键:指向 VIP,不是 node01 的 IP
token: xxx    # ← 必须与 node01 完全一致

os:
  hostname: harvester-04
  password: "xxx"
  ssh_authorized_keys:
    - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...(同一把公钥)"
  sshd:
    sftp: true
    disable_password_auth: false
  modules: [kvm, vhost_net, nvme]
  dns_nameservers: [10.181.0.2]
  ntp_servers:
    - ntp.aliyun.com
    - 0.suse.pool.ntp.org
    - 1.suse.pool.ntp.org
  labels:
    topology.kubernetes.io/zone: zone-b
    harvester-role: worker

install:
  mode: join
  role: worker
  automatic: true
  skipchecks: false
  force_efi: true
  tty: ttyS0,115200n8
  device: /dev/sda
  data_disk: /dev/sdb
  persistent_partition_size: 200Gi

  vip: 10.181.0.100
  vip_mode: static

  management_interface:
    interfaces:
      - name: eno1
        hwAddr: "AA:BB:CC:DD:EE:07"     # ← node04 自己的 MAC
      - name: eno2
        hwAddr: "AA:BB:CC:DD:EE:08"     # ← node04 自己的 MAC
    method: static
    ip: 10.181.0.14                     # ← node04 自己的 IP
    subnet_mask: 255.255.255.0
    gateway: 10.181.0.1
    default_route: true
    mtu: 1500
    vlan_id: 0
    bond_options:
      mode: active-backup
      miimon: 100
      mtu: 1500

  cluster_pod_cidr: 10.52.0.0/16
  cluster_service_cidr: 10.53.0.0/16
  cluster_dns: 10.53.0.10

  harvester:
    storage_class:
      replica_count: 3

  addons:
    rancher_monitoring:
      enabled: true

⚠️ join 最常犯的三个错

  1. 复制 node01 的 config 后忘改 hwAddr/ip/hostname → 安装器找不到网卡,或集群里出现同名节点;
  2. server_url 写成 node01 的 IP 而不是 VIP → node01 维护/重启时后续节点会失联;
  3. token 有多余空格或换行 → join 认证失败,控制台报 401/unauthorized。

⚠️ install.role 与自动提升:不写 role 时规则是「create 的节点是 management,集群满 3 台后随后加入的前两台自动提升为 management」。并发 join 时顺序不确定,因此本书显式写 role。若你已经用旧方式装完(02/03 被自动提升),后续可用 kubectl 查看 node-role.kubernetes.io/* 标签确认实际角色。


3.5 一键生成 12 份 config:gen-configs.sh

脚本见 appendix/configs/gen-configs.sh。思路:节点清单(CSV)+ 参数区 → 12 份可直接下载的 config,并自带一致性自检(hostname/IP/MAC 唯一、token 一致、create 只有 1 台)。

bash
#!/usr/bin/env bash
# appendix/configs/gen-configs.sh
# 生成 12 台 Harvester 节点的无人值守安装配置
set -euo pipefail

# ========== 全局参数 ==========
VIP="10.181.0.100"
TOKEN="xxx"
PASSWORD='xxx'
GW="10.181.0.1"; NETMASK="255.255.255.0"; DNS="10.181.0.2"
NTP1="ntp.aliyun.com"; NTP2="0.suse.pool.ntp.org"; NTP3="1.suse.pool.ntp.org"
ISO_URL="http://10.181.0.5:8080/iso/harvester-v1.8.2-amd64.iso"
SYS_DISK="/dev/sda"; DATA_DISK="/dev/sdb"; PERSIST="200Gi"
SSH_KEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...(替换成你的公钥整行)"
SAN_DOMAIN="harvester.example.local"
BOND_MODE="active-backup"     # 交换机 LACP 就绪后可改 802.3ad
OUT="./out"

# ========== 节点清单:序号,主机名,IP,角色,zone,MAC1,MAC2 ==========
NODES=$(cat <<'CSV'
01,harvester-01,10.181.0.11,management,zone-a,AA:BB:CC:DD:EE:01,AA:BB:CC:DD:EE:02
02,harvester-02,10.181.0.12,management,zone-b,AA:BB:CC:DD:EE:03,AA:BB:CC:DD:EE:04
03,harvester-03,10.181.0.13,management,zone-c,AA:BB:CC:DD:EE:05,AA:BB:CC:DD:EE:06
04,harvester-04,10.181.0.14,worker,zone-a,AA:BB:CC:DD:EE:07,AA:BB:CC:DD:EE:08
05,harvester-05,10.181.0.15,worker,zone-b,AA:BB:CC:DD:EE:09,AA:BB:CC:DD:EE:0A
06,harvester-06,10.181.0.16,worker,zone-c,AA:BB:CC:DD:EE:0B,AA:BB:CC:DD:EE:0C
07,harvester-07,10.181.0.17,worker,zone-a,AA:BB:CC:DD:EE:0D,AA:BB:CC:DD:EE:0E
08,harvester-08,10.181.0.18,worker,zone-b,AA:BB:CC:DD:EE:0F,AA:BB:CC:DD:EE:10
09,harvester-09,10.181.0.19,worker,zone-c,AA:BB:CC:DD:EE:11,AA:BB:CC:DD:EE:12
10,harvester-10,10.181.0.20,worker,zone-a,AA:BB:CC:DD:EE:13,AA:BB:CC:DD:EE:14
11,harvester-11,10.181.0.21,worker,zone-b,AA:BB:CC:DD:EE:15,AA:BB:CC:DD:EE:16
12,harvester-12,10.181.0.22,worker,zone-c,AA:BB:CC:DD:EE:17,AA:BB:CC:DD:EE:18
CSV
)

mkdir -p "$OUT"; rm -f "$OUT"/config-node*.yaml
SANS="  - \"${VIP}\"\n  - \"${SAN_DOMAIN}\"\n  - \"10.181.0.11\"\n  - \"10.181.0.12\"\n  - \"10.181.0.13\""

echo "$NODES" | while IFS=, read -r idx host ip role zone mac1 mac2; do
  [ -z "${idx:-}" ] && continue
  f="$OUT/config-node${idx}.yaml"
  if [ "$idx" = "01" ]; then mode="create"; server_url='""';
  else mode="join"; server_url="https://${VIP}:443"; fi
  {
    echo "# 自动生成:Harvester v1.8.2 ${host} (${role}, ${mode})"
    echo "scheme_version: 1"
    echo "server_url: ${server_url}"
    echo "token: ${TOKEN}"
    [ "$mode" = "create" ] && echo -e "sans:\n${SANS}"
    cat <<EOF
os:
  hostname: ${host}
  password: "xxx"
  ssh_authorized_keys:
    - "${SSH_KEY}"
  sshd:
    sftp: true
    disable_password_auth: false
  modules: [kvm, vhost_net, nvme]
  dns_nameservers: [${DNS}]
  ntp_servers: [${NTP1}, ${NTP2}, ${NTP3}]
  labels:
    topology.kubernetes.io/zone: ${zone}
    harvester-role: ${role}
install:
  mode: ${mode}
  role: ${role}
  automatic: true
  skipchecks: false
  force_efi: true
  tty: ttyS0,115200n8
  poweroff: false
  iso_url: ${ISO_URL}
  device: ${SYS_DISK}
  data_disk: ${DATA_DISK}
  persistent_partition_size: ${PERSIST}
  vip: ${VIP}
  vip_mode: static
  management_interface:
    interfaces:
      - name: eno1
        hwAddr: "${mac1}"
      - name: eno2
        hwAddr: "${mac2}"
    method: static
    ip: ${ip}
    subnet_mask: ${NETMASK}
    gateway: ${GW}
    default_route: true
    mtu: 1500
    vlan_id: 0
    bond_options:
      mode: ${BOND_MODE}
      miimon: 100
      mtu: 1500
  cluster_pod_cidr: 10.52.0.0/16
  cluster_service_cidr: 10.53.0.0/16
  cluster_dns: 10.53.0.10
  harvester:
    storage_class:
      replica_count: 3
    longhorn:
      default_settings:
        guaranteedInstanceManagerCPU: 12
        storageReservedPercentageForDefaultDisk: 0
  addons:
    rancher_monitoring:
      enabled: true
    rancher_logging:
      enabled: false
    harvester_vm_import_controller:
      enabled: false
    harvester_pcidevices_controller:
      enabled: false
EOF
    if [ "$mode" = "create" ]; then
      echo 'system_settings:'; echo '  auto-disk-provision-paths: ""'
    fi
  } > "$f"
  echo "生成 $f"
done

# ---- 自检 ----
echo "== 自检 =="
echo "文件数: $(ls "$OUT"/config-node*.yaml | wc -l) (期望 12)"
echo "hostname 唯一: $(grep -h '^  hostname:' "$OUT"/*.yaml | sort -u | wc -l) (期望 12)"
echo "ip 唯一: $(grep -h '^    ip: ' "$OUT"/*.yaml | sort -u | wc -l) (期望 12)"
echo "MAC 唯一: $(grep -h 'hwAddr:' "$OUT"/*.yaml | sort -u | wc -l) (期望 24)"
echo "token 一致: $(grep -h '^token:' "$OUT"/*.yaml | sort -u | wc -l) (期望 1)"
echo "create 数: $(grep -l 'mode: create' "$OUT"/*.yaml | wc -l) (期望 1)"

发布到 HTTP 并验证可下载:

bash
bash gen-configs.sh
cp out/config-node*.yaml /srv/harvester/configs/
chmod 600 /srv/harvester/configs/*.yaml      # 内含 token 与密码
for i in $(seq -w 1 12); do
  curl -sf "http://10.181.0.5:8080/configs/config-node${i}.yaml" >/dev/null \
    && echo "node${i} OK" || echo "node${i} FAIL"
done

3.6 装机执行:从 ISO 启动到节点 Ready

3.6.1 单台流程

  1. 带外 KVM(iDRAC/iLO/IPMI)挂载 harvester-v1.8.2-amd64.iso,或插 U 盘,开机进引导菜单。
  2. 选中 Harvester Installer,按 e 编辑引导项,在 linux ... 行末尾追加:
text
harvester.install.automatic=true harvester.install.config_url=http://10.181.0.5:8080/configs/config-node01.yaml
  1. Ctrl-X 启动。控制台依次输出:拉取 config → 硬件检查 → 分区格式化 → 写镜像 → 首启 RKE2 → 拉起 Harvester 组件。
  2. 装完自动重启(poweroff: true 时改为关机)。首次启动到 UI 可用约 5–15 分钟。
  3. 日志位置:安装阶段看控制台与安装环境内 /var/log/console.log;装完看 journalctl -u rancherd -fjournalctl -u kubelet -f

3.6.2 12 台的执行节奏

text
Day1 上午  HTTP 就绪 + 12 份 config 自检通过 + preflight 全绿
Day1 下午  装 node01(create) → 验证 UI/VIP/单节点健康(3.6.3)
Day1 晚    装 node02、03(join, management) → 验证 etcd 3 副本
Day2 上午  装 node04–07(4 台并发)
Day2 下午  装 node08–12(5 台并发)→ 全集群验证 + 加固(3.9)

并发 join 建议不超过 5 台:新节点加入会触发证书分发、镜像同步、Longhorn 初始化,并发过高会把 API server/etcd 压出超时。

3.6.3 装完第一台立刻做的 4 个检查

bash
ssh rancher@10.181.0.11 && sudo -i
kubectl get nodes -o wide                              # 1 个 Ready
kubectl get pods -A | grep -vE 'Running|Completed'     # 期望为空
ip -br addr show mgmt-br                               # 应含 10.181.0.11 与 VIP 10.181.0.100
curl -sk https://10.181.0.100/ping                     # 期望 200 / pong

这四项没过就别装后面 11 台,否则等于把一个错误复制 12 遍。


3.7 变体一:PXE / iPXE 全自动装机

同一份 config,改用 iPXE 脚本引导即可实现「上电即装」。要点:

  1. 从 ISO 中取出内核与 initrd(Full ISO 里含 rootfs.squashfs),与 ISO 一起放到 HTTP 服务器:
bash
mkdir -p /srv/harvester/pxe && cd /srv/harvester/pxe
mount -o loop /srv/harvester/iso/harvester-v1.8.2-amd64.iso /mnt
cp /mnt/boot/kernel/vmlinuz /mnt/boot/kernel/initrd* /mnt/isolinux/*.efi . 2>/dev/null || \
cp -r /mnt/boot . ; ls -l    # 不同版本目录略有差异,按实际路径拷贝
umount /mnt
  1. iPXE 脚本按 MAC 分发不同 config(boot.ipxe):
ipxe
#!ipxe
set base http://10.181.0.5:8080
set cfg  ${base}/configs
kernel ${base}/pxe/vmlinuz initrd=initrd \
  root=live:${base}/iso/harvester-v1.8.2-amd64.iso \
  harvester.install.automatic=true \
  harvester.install.config_url=${cfg}/config-${netX/mac:hexhyp}.yaml \
  harvester.install.device=/dev/sda harvester.install.data_disk=/dev/sdb \
  console=ttyS0,115200n8
initrd ${base}/pxe/initrd
boot
  1. DHCP 指向 iPXE(option 66/67 或 chainload),并确保装机 DHCP 与 VLAN 91 的 VM DHCP 完全隔离
  2. 内网环境记得 install.iso_url 指向本地 HTTP(config 里已写),否则装机会尝试联网。

⚠️ PXE 场景强烈建议在 install.device / install.data_disk/dev/disk/by-id/.../dev/disk/by-path/...,避免不同机器盘符漂移导致装错盘(这是 PXE 批量装机最危险的一类事故)。


3.8 变体二:U 盘离线装机(无 HTTP 服务)

  1. 把 ISO 写入 U 盘:dd if=harvester-v1.8.2-amd64.iso of=/dev/sdX bs=4M status=progress oflag=sync
  2. U 盘上会有 COS_OEM 分区,把 config.yaml 放进去:
bash
mkdir -p /mnt/oem && mount /dev/sdX3 /mnt/oem      # 分区号以 lsblk 实际输出为准
cp config-node01.yaml /mnt/oem/config.yaml
sync && umount /mnt/oem
  1. 从 U 盘启动,安装器会自动读取 COS_OEM 分区里的 config.yaml(同样建议在内核参数加 harvester.install.automatic=true)。
  2. 12 台机器就要 12 个 U 盘(或每装完一台改一次文件)——所以只在完全隔离网时用这个方式。

3.9 12 节点装完后的全集群验证

在任一 management 节点上执行(ssh rancher@10.181.0.100 走 VIP,或登录任意节点 sudo -i):

bash
# 1) 12 个节点全部 Ready,角色正确
kubectl get nodes -o wide
kubectl get nodes -L node-role.kubernetes.io/management,node-role.kubernetes.io/worker,\
topology.kubernetes.io/zone,harvesterhci.io/vlanconfig

# 2) 所有系统 Pod 正常(输出应为空)
kubectl get pods -A | grep -vE 'Running|Completed'

# 3) etcd 三副本健康
kubectl -n kube-system get pods -l component=etcd -o wide
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert=/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt \
  --cert=/var/lib/rancher/rke2/server/tls/etcd/client.crt \
  --key=/var/lib/rancher/rke2/server/tls/etcd/client.key endpoint health

# 4) VIP 由哪个节点持有,UI 是否可达
ip -br addr | grep 10.181.0.100
curl -sk https://10.181.0.100/ping
kubectl -n harvester-system get pods -l component=kube-vip -o wide 2>/dev/null || \
kubectl get pods -A | grep -i vip

# 5) Longhorn 12 个节点全部就绪、默认盘已创建
kubectl -n longhorn-system get pods -o wide | grep -c Running
kubectl get nodes.longhorn.io -o custom-columns=\
'NODE:.metadata.name,READY:.status.conditions[?(@.type=="Ready")].status,DISK:.status.diskStatus'

# 6) 默认 StorageClass 副本数为 3
kubectl get sc harvester-longhorn -o jsonpath='{.parameters.numberOfReplicas}{"\n"}'

# 7) 集群网段确实是默认值
kubectl -n kube-system get pod -l component=kube-apiserver -o yaml | grep -o 'service-cluster-ip-range=[^"]*'
kubectl get clusternetwork -o wide          # 应只有 mgmt

# 8) 时间同步(逐台)
for ip in $(seq 11 22); do
  echo -n "10.181.0.$ip  "
  ssh -o StrictHostKeyChecking=no rancher@10.181.0.$ip 'timedatectl | grep -i synchronized' 2>/dev/null || echo unreachable
done

UI 侧验证(https://10.181.0.100):

  • [ ] 首次登录设置 admin 密码(若装机时未通过 config 设置),登录后到 Settings 检查 server-version = v1.8.2
  • [ ] Hosts 页 12 台全部 Active,角色显示 3 management + 9 worker。
  • [ ] Networks → Cluster Networks 只有 mgmt,且 mgmt 状态正常。
  • [ ] Settingsauto-disk-provision-pathsbackup-targetstorage-networkovercommit-config 等值符合预期。
  • [ ] Addonsrancher-monitoring 为 Enabled(装机时 config 里开的)。

3.10 装机后加固(建议当天完成)

3.10.1 关闭 SSH 密码登录(官方推荐做法)

Harvester 宿主机默认开启 SSH 密码登录,便于装机排障;装完应关闭。官方方式是通过 CloudInit CRD 下发(不是手改 /etc/ssh/sshd_config,因为宿主机 OS 是不可变的,手改重启即失效):

bash
cat <<EOF | kubectl apply -f -
apiVersion: node.harvesterhci.io/v1beta1
kind: CloudInit
metadata:
  name: ssh-config
spec:
  matchSelector:
    harvesterhci.io/managed: "true"   # 作用于所有 Harvester 节点
  filename: 99-ssh-config
  contents: |
    stages:
      network:
      - name: "disable password login"
        commands:
        - sed -i -E 's/^#?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config
        - sed -i -E 's/^#?ChallengeResponseAuthentication .*/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config
        - sed -i -E 's/^#?UsePAM .*/UsePAM no/' /etc/ssh/sshd_config
        - systemctl restart sshd
  paused: false
EOF

⚠️ 两个前提:① 每台节点都已通过 os.ssh_authorized_keys 注入了你的公钥;② 所有受影响节点需要重启,CloudInit 配置才生效。请逐台重启(配合 7.4 的维护流程),不要 12 台一起重启。

验证:

bash
ssh -o PreferredAuthentications=password rancher@10.181.0.11
# 期望:Permission denied (publickey,keyboard-interactive).
ssh rancher@10.181.0.11 'echo key login OK'      # 期望成功

3.10.2 其余加固动作清单

动作怎么做章节
设置备份目标UI → Settings → backup-target(S3/NFS),并验证 backup-target 状态健康7.5
开启日志Addons → rancher-logging(需要时)7.7
配置 VM 超卖上限Settings → overcommit-config1.6
开启自动均衡安装并启用 virtual-machine-auto-balance addon7.6
配置告警Monitoring 的 AlertManagerConfig / 对接外部告警7.7
收紧 UI 访问通过 Rancher 集成或反向代理限制来源 IP;轮换 admin 密码7.9
关闭不需要的 addonharvester-seederpcidevices-controller 等按需7.9
留存基线appendix/ops/cluster-snapshot.sh 跑一次并归档7.8.2
建立 VM 网络下一章的主角:ClusterNetwork cn-vm + VlanConfig + net-9104

3.11 常见装机失败与处理

现象根因处理
安装器停在 "Fetching config" 或用了默认配置config_url 不可达 / 拼错 / HTTP 403在安装环境里 curl -v <config_url>;检查防火墙与路径
装完 hostname 是随机串TUI 里填过 hostname(覆盖 config),或 os.hostname 拼错重装或 hostnamectl + 检查 config 优先级
提示硬件不满足生产要求后中止skipchecks: false 且内存/CPU/磁盘不达标补硬件,或临时 skipchecks: true(明确知道风险)
找不到网卡 / 拿不到 IPinterfaces[].name 在目标机不存在hwAddr 匹配;或先在 LiveCD 里 ip -br link 核对名字
VIP 无法访问,UI 打不开VIP 冲突 / 交换机不允许 gratuitous ARP / bond 未 uparping -D 10.181.0.100 查冲突;看 ip -br addr show mgmt-br
join 一直转圈最后失败token 不一致 / server_url 写成节点 IP / 时间不同步核对 token;server_url 必须 https://VIP:443;配 NTP
第 4 台起 join 失败,etcd 报错3 台 management 已满,仍指定 role: managementworker 节点写 role: worker(或不写 role 走默认)
Longhorn 节点没有默认盘data_disk 写错/与 device 相同/盘上有旧分区核对盘符;必要时 install.wipe_disks_list 清理后重装
装完 Pod 一直 ImagePullBackOff用了 Net install ISO 但内网无法联网换 Full ISO;或配 system_settings.containerd-registry 私有仓库
串口看不到输出未设 install.tty 或 BIOS 串口重定向未开config 加 tty: ttyS0,115200n8,BIOS 开 console redirection

通用原则:装机是破坏性且幂等的操作。排障超过 20 分钟,直接重装比重装更快——尤其是 12 台批量场景,重装还能顺便验证你的 config 是否真的可复现。


3.12 本章验证清单

  • [ ] 12 份 config 由脚本生成并通过自检(hostname/IP/MAC 唯一、token 一致、create 仅 1 台)。
  • [ ] node01 装完即通过 3.6.3 的 4 项检查,再装其余节点。
  • [ ] 12 节点全部 Ready,角色为 3 management + 9 worker。
  • [ ] etcd 3 副本健康;VIP 10.181.0.100 可访问 UI;server-version = v1.8.2。
  • [ ] Longhorn 12 节点就绪,默认 StorageClass harvester-longhorn 副本数 3。
  • [ ] Pod/Service CIDR 仍为 10.52.0.0/16 / 10.53.0.0/16(未被误改)。
  • [ ] 公钥登录可用后,已下发 CloudInit/ssh-config 并逐台重启验证密码登录被拒。
  • [ ] 备份目标已配置且状态健康。
  • [ ] 基线快照(nodes / network / settings)已归档。

下一章:在 eno3/eno4 上建 ClusterNetwork cn-vm,把 10.181.91.0/24(VLAN 91)变成 VM 可选的网络。