Lab 1 的目标是在一台笔记本上用虚拟机或者 Docker 搭出一个小型集群,然后逐层把 HPC 集群需要的服务铺上去:并行计算环境(MPI + BLAS + HPL)、共享存储(NFS / BeeGFS)、作业调度(Slurm)、容器编排与可观测性(K3s + Prometheus)、集中式身份(FreeIPA)、以及集群网络入口。下面按主题而非按课程任务编号来组织,任务编号我保留在括号里方便溯源。

实验中我用 Claude / Codex 做交互式的报错分析、方案权衡和笔误排查,文中的判断均经过动手验证。

实验平台概况

在单台笔记本上通过 VMware Workstation Pro 构建一个小型集群。宿主机为 Intel Core Ultra 9 285H、32 GB 内存;虚拟机共享同一宿主资源,通过 VMware 的 NAT 网络互联。

集群共 5 个节点:4 台 Debian 节点承载计算、存储与编排;另有 1 台单独的 Rocky Linux 节点专门运行身份服务(详见集中式身份一节)。

节点IP角色vCPU内存系统盘附加盘
node01192.168.78.129管理节点:NFS server、Slurm 控制节点(slurmctld)、K3s server、BeeGFS management23.8 GB60 GB
node02192.168.78.130计算节点 / K3s agent / BeeGFS metadata21.9 GB60 GB
node03192.168.78.131计算节点 / BeeGFS storage21.9 GB60 GB20 GB
node04192.168.78.132计算节点 / BeeGFS storage21.9 GB60 GB20 GB
ipa01身份服务节点:FreeIPA server(LDAP + Kerberos + CA + DNS)2
  • node01 在 K3s 阶段由 2 GB 上调至 4 GB 以容纳 K3s server,其余阶段为 2 GB。
  • 4 台 Debian 节点操作系统为 Debian 13 (trixie),内核 6.12.90+deb13.1-amd64;身份节点为 Rocky Linux 9。

贯穿 Lab 的内存约束:除了 node01 外每个计算节点只有约 1.9 GB 内存。所以后续很多操作都是在资源受限的情况下所做的取舍。


并行计算环境:MPI、BLAS 与 HPL

HPL(High-Performance Linpack)是衡量集群浮点性能的经典基准,它依赖 MPI 做进程间通信、依赖 BLAS 做底层矩阵运算。动手搭建这条链有助于理解一个高性能计算程序是怎么被编译、链接、并找到它的依赖库的。

安装链(任务一)

  • OpenMPI

    OpenMPI 安装结果

    实验文档强调安装后要运行 ldconfig 刷新动态链接库缓存,但我初次尝试时没执行,OpenMPI 也能正常运行。我用 ldd $(which mpirun)cat /etc/ld.so.conf* 验证,发现原因是 ./configure 时没加 --prefix,默认装到了 /usr/local/lib,而该目录本就在动态链接器的搜索路径里,所以无需额外刷新缓存即可解析。

  • BLAS

    BLAS 编译结果

    编译产物为 libblas.a(与文档示例的 blas_LINUX.a 命名不同,属于环境差异),已在 Make.Linux_PII_FBLAS 中对应调整 LAlib 路径。

  • CBLAS

    CBLAS 编译结果

  • HPL

    HPL 编译结果

关键 Make 变量

hpl-2.3/Make.Linux_PII_FBLAS

ARCH         = Linux_PII_CBLAS
TOPdir       = $(HOME)/hpl-2.3
INCdir       = $(TOPdir)/include
BINdir       = $(TOPdir)/bin/$(ARCH)
LIBdir       = $(TOPdir)/lib/$(ARCH)
HPLlib       = $(LIBdir)/libhpl.a
MPdir        = /usr/local
MPinc        = -I$(MPdir)/include
MPlib        = -L/usr/local/lib -lmpi
LAdir        =
LAinc        = -I/home/zscott/CBLAS/include
LAlib        = /home/zscott/CBLAS/lib/cblas_LINUX.a /home/zscott/BLAS-3.12.0/build/libblas.a
LINKER       = /usr/bin/gfortran
LINKFLAGS    = $(CCFLAGS)

CBLAS/Makefile.in

BLLIB = /home/zscott/BLAS-3.12.0/build/libblas.a
CBLIB = /home/zscott/CBLAS/lib/cblas_LINUX.a
CC = gcc
FC = gfortran
LOADER = $(FC)

注意这里手动构建的 HPL 用的是 netlib 参考实现 BLASLAlib 指向自己编的 libblas.a)。这一点在后面解释多节点跑分为什么只有 ~10 GFLOPS时是关键。

用 HPL 跑分与调参(任务四)

多节点提交用 Slurm 的 sbatch

#!/bin/bash
#SBATCH --job-name=hpl
#SBATCH --partition=debug
#SBATCH --nodes=3
#SBATCH --ntasks-per-node=2
#SBATCH --chdir=/shared/hpc101/src/hpl-2.3/bin/Linux_PII_CBLAS
#SBATCH --output=/shared/hpc101/hpl_%j.out
 
mpirun ./xhpl
  • --nodes=3 --ntasks-per-node=2 申请 6 个进程;
  • mpirun ./xhpl 不能带 hostfile,要让 mpirun 直接继承 Slurm 的分配(否则 hostfile 里的节点可能不在分区内,见踩坑)。

P×Q 与 Slurm 任务数的关系:Slurm 只负责把 6 个 MPI 进程拉起来分到节点上,它不知道 HPL 内部在做什么;而 HPL.dat 里的 P×Q 定义如何把 N×N 矩阵切给各进程。两者必须相等且需手动对齐——Slurm 不会替你改 HPL.dat

HPL.dat 关键参数与选取依据
1            # of problems sizes (N)
24000  Ns
1            # of NBs
128      NBs
0            PMAP process mapping (0=Row-,1=Column-major)
1            # of process grids (P x Q)
2            Ps
3            Qs
  • N:HPL 解 N×N 双精度矩阵,占用内存 ≈ 字节。3 个计算节点每个 RealMemory ≈ 1935 MB,总内存约 字节,按 80% 利用率反推
  • NB:常用值 128 / 192 / 256,在 N=10000 下实测 GFLOPS 约 20 / 16 / 14,选 128
  • P×Q:取接近正方形以均衡通信(1×6 会让某一维通信特别慢);取 是因为 HPL 沿进程列方向的 panel 分解在关键路径上,让行数 P 小一点能缩短这条串行路径的通信开销。

跑分结果:多节点(3 节点、参考 BLAS)单组约 16 分钟,GFLOPS 在 ~10 量级。

这个 ~10 的数字看起来很低,但它是合理的。因为多节点跑的是 netlib 参考 BLAS(朴素三重循环),叠加 VMware NAT 的跨节点网络瓶颈。bonus 部分会证明:光是把参考 BLAS 换成优化库,单节点就能提升近 20 倍。目前来看,BLAS 实现是 HPL 性能的主导因素,而不是节点数


踩坑记录

  • hostfile 与 Slurm 分配冲突--hostfile ~/hostfile.txt -n 6hostfile contains node not present in the allocation: node01,因为 node01 不在 debug 分区。删掉 hostfile,让 mpirun 继承 Slurm 分配即可。
  • PRTE 通信中断:N=24000 长时间满载后出现 lost communication with a remote daemon(HNP daemon on node02,remote on node03)。发生在计算完成之后,未影响 GFLOPS 与正确性。初步归因为长时间满载下节点资源波动,此处未完全定位,留作后续用更小 N 或监控节点资源来复现。

HPL 性能调优:更换数学库(任务四 bonus)

思考:同一个 HPL、同样的硬件,只换底层 BLAS 库,性能能差多少?

Spack 构建了基于四个不同数学库的 HPL,单节点、OMP_NUM_THREADS=2

数学库GFLOPS
Intel MKL91.07
OpenBLAS87.48
BLIS85.55
netlib(参考实现)5.18

结果分析

  • MKL 最快,但原因不是 AVX-512。当前宿主机是 Arrow Lake(消费级已无 AVX-512),而且 Spack 把微架构识别为 linux-skylake(只有 AVX2)——所以所有库都是按 AVX2 编译的。MKL 领先是因为在相同 ISA(AVX2/FMA)下,它的微内核、缓存分块和线程调度更成熟。
  • OpenBLAS ≈ BLIS:二者都有手写汇编微内核、SIMD 向量化和缓存分块。
  • netlib 只有 5.18:纯朴素三重循环。但它是单线程、其余是 2 线程,所以并非纯内核对比。

收获:优化 BLAS 相对参考实现有近 20 倍差距(5 → ~90 GFLOPS)。回头看上一节多节点只有 ~10 的结果,就完全说得通了:那里用的是参考 BLAS。所以真正的生产配置应该是”优化库 + 多节点”


动态链接排错

异常现象:在初次尝试更换数学库的时候发现 BLIS 版和 netlib 版的 HPL 跑分居然和 OpenBLAS 几乎一样——这不可能,说明它们根本没用上各自声称的库。排查过程:

  1. ldd xhpl:发现 BLIS 版、netlib 版的 xhpl 都指向系统的 /lib/x86_64-linux-gnu/libopenblas.so.0,没链到 Spack 编的库。

    == blis ==
            libopenblas.so.0 => /lib/x86_64-linux-gnu/libopenblas.so.0
    == netlib-lapack ==
            libopenblas.so.0 => /lib/x86_64-linux-gnu/libopenblas.so.0
    
  2. nm xhpl | grep dgemmdgemm_ 是 undefined → xhpl 没静态链接 BLAS,而是运行时动态去找。

  3. objdump -p xhpl | grep RPATH:BLIS 的路径确实在 RPATH 里 → 不是 RPATH 缺失。

  4. objdump -p xhpl | grep NEEDED:NEEDED 列表里只有 libopenblas.so.0根本没有 libblis.so.4 → HPL 压根没链 BLIS,Spack 写入的 BLIS RPATH 没被用上。

  5. zgrep '-o xhpl' build-out.txt.gz:链接命令是 ... -lopenblas -lm -lmpi,自始至终就是 -lopenblas

根因:HPL 的 ./configure 自动探测 BLAS,按名字试 -lopenblas 时第一个就命中了系统装的 libopenblas-dev,于是无视了 Spack 放在 -L 路径里的 BLIS / netlib。三条链里只有 OpenBLAS 那条碰巧对了,因为它本该链的库也叫 OpenBLAS,Spack 版靠 RPATH 抢在系统前面解析了同名 symbol。整个环境一直被系统 dev 包污染,同名巧合把问题掩盖了很久(我在完成 2025 版 Lab1 的时候就撞过一次,当时误判成了 MPI 通信瓶颈)。

解决:卸掉系统 libopenblas-dev,再 spack install --overwrite 重编 BLIS 和 netlib 两条链。


共享存储:NFS 与 BeeGFS

目标:集群里的多个节点必须看到同一份文件(HPL 的 xhplHPL.dat 要在每个节点同一路径)。这就需要共享存储。我做了两种:NFS(简单、单点)和 BeeGFS(并行文件系统,贴近真实 HPC)。对比二者能理解并行 I/O 的扩展性。

NFS:统一共享路径(任务二)

两处共享:

  • 数据共享:node01 导出 /srv/hpc101-shared,所有节点挂到统一路径 /shared/hpc101(node01 自己用符号链接把本地路径也映射成 /shared/hpc101)。
  • 家目录共享/home/zscott 从 node01 导出给计算节点,统一个人环境与配置。

为什么导出源(/srv/hpc101-shared)和挂载点(/shared/hpc101)要分开/srv 是本机对外提供数据的标准位置,适合放导出源;而所有节点统一看到 /shared/hpc101 这个逻辑路径,是为了保证路径一致性——xhplHPL.dat 在每个节点上必须是同一个绝对路径。

导出配置与选项含义
/home/zscott       192.168.78.0/24(rw,sync,no_subtree_check,no_root_squash)
/srv/hpc101-shared 192.168.78.0/24(rw,sync,no_subtree_check,no_root_squash)
  • 网段限制:只有本集群网段的节点可挂载,集群外机器无法访问。
  • rw:计算节点要往共享目录写 HPL 输出,必须可写。
  • sync:写操作落盘后才返回,数据一致性更有保障。
  • no_subtree_check:关闭子树检查,避免文件移动出问题,也提升性能。
  • no_root_squash:本环境启用它是为了简化多节点 root 操作。但真实实验室 / 生产集群不应启用——否则任一计算节点的 root 可以 root 身份读写共享目录,风险较大。此处因是隔离内网而接受这一风险。

自动挂载:客户端通过 /etc/fstab 配合 x-systemd.automount 实现 NFSv4 自动挂载,首次访问 /home/zscott/shared/hpc101 时由 systemd 触发实际挂载。

验证:跨节点读写、四节点路径一致、四节点 UID/GID 一致。

node01 NFS 服务端状态

跨节点写入验证

四节点 UID 一致性

NFS 的权限判断认 UID 而非用户名,这就引出后续为什么需要集中式身份。如果同一个用户在不同节点 UID 不同,NFS 会当成两个人,权限就乱了。


BeeGFS:并行文件系统(任务二 bonus)

选型:选 BeeGFS 以更贴近真实 HPC 集群的存储层(与 Lustre 同类)。原本想用 Rook-Ceph,但它依赖 K3s,K3s + Ceph 同时跑内存开销太大,单节点仅 1.9 GB 会 OOM,所以放弃。

架构分布(4 台 Debian 节点上按角色部署多个 BeeGFS 服务):

  • management(node01):集群注册与协调,不存数据。
  • metadata(node02):管理目录、文件名、属性、文件分布。
  • storage(node03/04):各挂一块独立 20 GB 盘,实际存放数据块。
  • client(四台):内核模块,把 /mnt/beegfs 挂成本地目录。

并行 I/O 原理:client 先问 meta”这个文件被切成几块、在哪几个 storage”,拿到分布图后直接并行地向多个 storage 请求各自持有的分片,再拼成完整文件。数据流只发生在 client 与多个 storage 之间,meta 不参与数据块的搬运(只走控制路径)。所以加 storage 节点,总和带宽近似线性增长,meta 一般不会成为数据瓶颈。BeeGFS 提供的是 文件语义(POSIX,挂成普通目录用)。

与 NFS 的区别

NFSBeeGFS
架构单服务端导出目录mgmt / meta / storage 分离
数据分布全在一台服务器文件分块到多个 storage
扩展性受限于单机带宽加 storage 即可扩容

验证:跨节点读写正常、health check 全绿、df 显示两块 20 GB 盘聚合成 40 GB。

集群状态与容量验证输出
❯ beegfs node list
ID    TYPE        ALIAS
m:1   meta        node_meta_1
s:1   storage     node_storage_1
s:2   storage     node_storage_2
mg:1  management  management
(外加四个 client)

❯ df -h | grep beegfs
beegfs_nodev     40G  847M   40G    3% /mnt/beegfs

health check 中除”可用容量”因阈值告警外,Reachability / Consistency / Mapping 全部通过。


作业调度:Slurm(任务三)

多个用户、多个作业怎么公平地共享一批计算节点?这里就需要作业调度器(slrum)了。它由控制节点 slurmctld 和各计算节点的 slurmd 组成,靠 MUNGE 做节点间认证。

  • MUNGE key 一致:四节点 munge.key 的 MD5 相同,这是 Slurm 节点间互信的前提。
  • slurm.conf 依据 slurmd -C 生成:节点的 CPU / 内存等硬件参数要与实际一致,否则调度器会拒绝节点。
  • 验证 slurmctld(node01) 与 slurmd(node02–04) 均 running,节点 idle,srun / sbatch 可正常提交执行。

slurm.conf 节点配置

sbatch 提交与执行流程

踩坑:slurmctld/slurmd 在 systemd 下以 User=slurm 启动,但 Slurm 本应以 root 启动后自动降权,导致 Failed to set GID to 0 而 core dump。用 systemd override 清空 slurm.conf 中的 User= / Group= 解决。

Slurm 报的这类错常常在权限降级这一层,而不是配置语法。


容器编排与云原生服务:K3s

HPC 集群越来越多地用 Kubernetes 来跑服务面——监控、面板、CI 等。这里用轻量版 K3s 理解容器编排的基本组件:控制面 / 工作面、Pod、Service、kube-proxy 的转发机制。

部署与原理(K3s 部分)

  • node01 内存上调至 4 GB 后部署 K3s server,node02 用 token 加入为 agent(token 是 agent 加入时的共享认证密钥)。
  • server 节点跑 API server / scheduler / controller;agent 侧是 kubelet,负责创建和监控 Pod。

K3s 节点状态

验证replicas=1 时 Pod 只在 node01,但用 node02 的 IP 也能访问。原因是 NodePort 型 Service 会在每个节点都打开同一端口,kube-proxy 收到请求后若本节点没有对应 Pod,就改写目标地址转发到集群内 Pod 的真实 IP。


可观测性:kube-prometheus-stack(进阶任务)

选型与裁剪:用 kube-prometheus-stack 一次性拿到 Prometheus + Grafana + node-exporter + kube-state-metrics + Operator,靠 ServiceMonitor 自动发现、免手写配置。因 node02 仅 2 GB,所以我关掉了 Alertmanager、将 retention 设为 2 天、给 Prometheus 加 cgroup 内存上限

采集的是指标(metrics),不是日志或追踪。 工作流:Prometheus 主动 pull,目标不写死,而是靠 ServiceMonitor 选择器问 API server 要 Pod 真实 IP(Pod IP 变了也能继续采集)。pull 三类对象——node-exporter(节点)、cAdvisor(容器/Pod)、kube-state-metrics(watch API server,把集群对象状态变成指标,如 Pod 是否 Running、节点是否 Ready)。样本存进 node02 的 TSDB,Grafana 查询可视化。

我用三个场景验证监控栈能不能真的发现异常,每个场景都对应一类不可替代的信息:

  • 场景一 · 节点繁忙:在 node02 跑单节点 HPL 的同时看 Grafana,发现指标更新从 30s 一次退化到约 150s 一次。原因是 Prometheus 和 HPL 抢同一节点的 CPU,scrape 拿不到足够调度时间。

    这表明监控服务不应和计算任务放在同一节点,否则采样粒度下降、还会污染数据纯净度。

  • 场景二 · Pod Pending:提交一个请求 100 GiB 内存的 Pod,kubectl describe 显示 Insufficient memory,Grafana 里 kube_pod_status_phase{...} 的 Pending 为 1。

    这类异常 node-exporter / cAdvisor 无法感知(Pod 根本没被调度、没占任何资源),只能靠 kube-state-metrics 的对象状态监控。

  • 场景三 · 服务不可达:把 nginx Deployment 副本缩到 0,Service 还在但没有就绪 endpoints,curl 被拒,kube_deployment_status_replicas_available 从 2 跌到 0。

Pod Pending 指标

排障工作流(可将三个场景归纳成一套顺序):先看对象状态(Node/Pod/Service/Endpoint,up==0 列出不通目标)→ 看节点是否繁忙(top nodesnode_load1)→ 看服务/Pod 是否变慢(get endpointsdescribe pod 看 limit/request)→ 看 Pod 是否 Pending(kube_pod_status_phase)→ 看服务是否不可达(available 副本、up=0)。

局限:这套栈目前只有指标,能定位”哪个节点/Pod、什么资源、何时异常”,但归因仍需日志——当前靠 kubectl logs/describe 手动获取,更完整的做法是加 Loki 做日志聚合。


集中式身份:FreeIPA(进阶任务)

一个集群里几十台机器,怎么保证同一个用户在不同节点是同一个身份、一次登录到处通行?这里就需要集中式身份了。

选型:选 FreeIPA,因为它把 LDAP + Kerberos + CA + DNS 集成在一起(NIS 太老、自己拼 Kerberos/证书/DNS 工作量太大)。又因为 Debian 13 不提供 FreeIPA server 端包,所以我单独新建了一个 Rocky Linux 9 节点——真实集群本就应把身份服务节点与计算/存储节点分开,甚至用不同 OS。

各组件职责

  • LDAP(目录服务):存用户属性——UID、GID、家目录、所属组等。ipa user-add alice 实际是往 LDAP 写 uidNumber / gidNumber 等记录。
  • Kerberos(认证服务):kinit 向 KDC 请求,KDC 用用户密钥加密一张 TGT 返回,本地用输入的密码解密即证明身份。此后用 TGT 向 TGS 换服务票据,服务器只验票、全程密码不过网络。这实现了单点登录:一次 kinit,SSH 到其他节点、访问 BeeGFS 都免密。
  • SSSD(客户端侧):负责身份解析(id testuser 时向 LDAP 查),核心价值是离线缓存。如果IPA server 单点挂了,已登录用户仍能用缓存继续工作,不会使得整个集群瘫痪。

部署要点:Kerberos 依赖节点间时间一致(TGT 带时间戳防重放),时间差超过 5 分钟 KDC 会当成重放旧票拒绝,所以必须用 chrony 同步时间。

验证:四节点 id testuser 的 UID/GID 完全一致,说明查的是同一个 LDAP 数据源。

FreeIPA 统一 UID/GID 验证

收获

  • 以前保证 UID 一致要在每台机器 /etc/passwd 手写相同数字,易错;现在数据只有 LDAP 一份,必然一致。
  • 这直接治好了 NFS/BeeGFS 的隐患:它们认 UID 判权限,UID 不一致就权限混乱;FreeIPA 集中分配并统一下发 UID,从根上消除。
  • 三种身份:FreeIPA 管人的身份,Slurm 管作业身份,K3s 管工作负载(Pod)身份

集群网络入口:从 Ingress 到 Gateway API(进阶任务)

一个跑在集群内部的服务(这里是 Grafana),怎么让集群外的浏览器通过域名访问到?这条从集群外进入 Pod 的路径要穿过好几层,每层各管一件事。

目标:让集群外浏览器通过 grafana.hpc.internal 访问集群内的 Grafana。

初始方案:MetalLB + ingress-nginx

沿请求路径从外到内,每一层的职责:

  • 入口 IP 层 · MetalLB:裸金属集群默认没有集群外可路由的地址(云上由云厂商 LB 提供,自建环境这个位置是空的)。MetalLB 从预留地址段(192.168.78.240–245)分一个给入口服务(当前 192.168.78.240),并在局域网里用 ARP “认领”这个地址,让发往它的流量落到某个节点。它只把流量引到门口,不看请求内容。
  • 路由层 · ingress-nginx:反向代理,读请求里的域名和路径(grafana.hpc.internal + /),据此决定转发给谁。它依据的规则来自 Ingress 对象(注意:Ingress 对象只是声明式配置,真正干活的是 ingress-nginx 这个 controller)。
  • 服务层 · Service + kube-proxy:Pod 会重启、IP 会变,Service 给后端一个固定的内部地址,kube-proxy 把流量转交给当前真正的 Grafana Pod。
  • 工作负载 · Grafana Pod:监听 3000,真正处理请求。

两个不在主线、但支撑主线的组件:

  • CoreDNS:集群内部各服务按名字互相发现(东西向)。它不负责把外部域名 grafana.hpc.internal 解析成 192.168.78.240——那是访问者自己的 DNS 或 hosts 文件做的。
  • Flannel:CNI,给每个 Pod 分配内部 IP、让跨节点 Pod 互通,是整个网络的底座。

完整请求路径:浏览器解析域名 → 流量发往 VIP,MetalLB 引到某节点 → 交给 ingress-nginx → 按 Ingress 规则转发给 kps-grafana → 经 Service/kube-proxy 送到 Grafana Pod(10.42.0.40:3000)→ 原路返回。

❯ curl -I --resolve grafana.hpc.internal:80:192.168.78.240 http://grafana.hpc.internal/
HTTP/1.1 302 Found
Location: /login

跳转到 /login 是 Grafana 未登录时的正常响应,说明整条链路已通。

易混:CoreDNS 是集群内服务,不在外部进集群的入口路径上。


改进方案:换成 Gateway API(Envoy Gateway)

调研时发现 ingress-nginx 已于 2026 年初被 Kubernetes 官方退役、仓库归档,后续发展全部转移到新的 Gateway API。于是把入口这一层升级到 Gateway API。

只换路由层:ingress-nginx → Envoy Gateway,Ingress 对象 → Gateway API 的三个对象:

  • GatewayClass:声明由谁实现这个入口(基础设施方);
  • Gateway:定义入口监听哪些端口(集群运维方);
  • HTTPRoute:定义具体域名/路径转发给谁(应用开发方)。

功能不变(还是读域名/路径并决定转发给谁),只是引擎换成 Envoy、配置模型从一个耦合的 Ingress 对象拆成三个各管一事的对象”。MetalLB、CoreDNS、Service、Flannel 全部不动——Envoy Gateway 会自动提供一个新的 LoadBalancer Service,其外部 IP 仍由 MetalLB 分配。

❯ kubectl -n envoy-gateway-system get svc -l gateway.envoyproxy.io/owning-gateway-name=eg-gateway
NAME                                   TYPE           EXTERNAL-IP      PORT(S)
envoy-monitoring-eg-gateway-9f01f6bf   LoadBalancer   192.168.78.241   80:32208/TCP

❯ curl -I --resolve grafana-gw.hpc.internal:80:192.168.78.241 http://grafana-gw.hpc.internal/
HTTP/1.1 302 Found
location: /login

新入口拿到的是 192.168.78.241 而非 .240,因为 ingress-nginx 那套仍占用着 .240。两套入口并存。


总结:这些服务是怎么拼成一个集群的

做完 Lab 1 再回头看,这些看似独立的服务其实层层依赖、互相支撑:

  • 底座:Flannel/网络 + NFS/BeeGFS 共享存储,让多节点”看到同一份文件、能互相通信”;
  • 身份:FreeIPA 统一下发 UID,让存储权限、SSH 免密、作业归属在全集群一致;
  • 调度:Slurm 把 HPC 作业公平地分到计算节点;
  • 计算:MPI + 优化 BLAS + HPL 构成真正的计算负载,其性能主要由 BLAS 实现决定;
  • 服务面:K3s + Prometheus + Gateway API 提供云原生的编排、可观测性与对外入口。

而贯穿始终的那部分,是在内存的约束下不断做取舍