본문으로 건너뛰기

[HPC From Scratch] 에피소드 4: NFS 스토리지 & FreeIPA: 하나의 드라이브, 하나의 로그인

Will Paik
작성자
Will Paik
대규모 GPU 클러스터를 최적화하는 HPC 엔지니어. 밤에는 방구석 미니 슈퍼컴퓨터를 조립하며(가끔은 태워 먹으며) 그 과정을 기록합니다.
HPC From Scratch - 이 글은 시리즈의 일부입니다.
파트 4: 이 글

하나의 드라이브. 하나의 로그인. 모든 노드가 동일한 홈 디렉토리를 공유합니다.

에피소드 3에서는 네트워크를 설정하고, 6개 노드에 Rocky Linux를 설치했으며, DHCP와 NAT를 구성하고, SSH를 강화했습니다. 클러스터가 네트워크에 연결되고 보안이 갖춰졌습니다. 이제 Slurm을 쓰기 전에 두 가지가 더 필요합니다. 공유 스토리지와 중앙 집중식 인증입니다.

이 두 가지가 없으면 모든 노드에 파일을 수동으로 복사하고, 동일한 사용자 계정을 6번 만들어야 합니다. 이번 에피소드에서 그 두 문제를 모두 해결합니다.

1. 공유 스토리지가 왜 중요할까요?
#

NFS 없이는 두 노드에 걸친 MPI 작업을 제출할 때 입력 데이터가 두 노드에 모두 있어야 합니다. 수동으로 복사하거나 동기화 스크립트를 짜야 합니다. 어느 쪽도 지속 가능하지 않습니다.

NFS가 있으면 arbiter(관리 노드)에 있는 Samsung 990 Pro가 하나의 /home 디렉토리를 내보냅니다. 클러스터의 모든 노드가 그걸 마운트합니다. 로그인 노드에서 스크립트를 작성하고 어느 컴퓨트 노드에서든 실행하면 됩니다. 파일은 이미 거기 있습니다.

NFS와 FreeIPA 다이어그램

Slurm에서도 중요합니다. 작업이 출력 파일을 쓰면 NFS 공유의 /home에 저장됩니다. 결과를 가져오기 위해 컴퓨트 노드에 SSH로 들어갈 필요가 없습니다.

사전 조건

이 에피소드를 시작하기 전에:

  • 모든 노드에 Rocky Linux 10이 설치되고 네트워크가 구성되어야 합니다 (에피소드 3)
  • arbiter에 Samsung 990 Pro NVMe 드라이브가 설치되어야 합니다 (에피소드 2)
  • arbiter에서 다른 모든 노드로 SSH 키 기반 로그인이 작동해야 합니다

2. Ansible 설정
#

이 에피소드부터 Ansible로 모든 노드에 동시에 설정을 적용합니다. Ansible 없이는 변경할 때마다 6개 머신에 개별적으로 SSH 접속해야 합니다.

Ansible은 arbiter에서 실행합니다. NFS 공유가 아닌 /opt/ansible에 두는데, Ansible 설정 파일에는 SSH 키와 vault 패스워드가 담겨 있어서 클러스터의 모든 노드에 노출되면 안 됩니다.

Ansible 설치
#

[wpaik@arbiter ~]$ sudo dnf install ansible-core
[wpaik@arbiter ~]$ sudo mkdir -p /opt/ansible
[wpaik@arbiter ~]$ sudo chown wpaik:wpaik /opt/ansible
[wpaik@arbiter ~]$ cd /opt/ansible

SSH 키
#

Ansible 전용 키를 생성하고 모든 노드에 배포합니다.

[wpaik@arbiter ansible]$ mkdir .ssh
[wpaik@arbiter ansible]$ ssh-keygen -t ed25519 -f .ssh/worker_ed25519 -N ""

[wpaik@arbiter ansible]$ for node in 192.168.50.1 192.168.50.15 192.168.50.32 192.168.50.11 192.168.50.19; do
    ssh-copy-id -i .ssh/worker_ed25519.pub wpaik@$node
  done

인벤토리와 설정
#

hosts.ini를 만듭니다.

[head]
carrier.cluster.local ansible_host=192.168.50.1

[management]
arbiter.cluster.local ansible_host=192.168.50.50 ansible_connection=local

[workers]
interceptor-01.cluster.local ansible_host=192.168.50.15
interceptor-02.cluster.local ansible_host=192.168.50.32

[gpu]
corsair-01.cluster.local ansible_host=192.168.50.11

[visualization]
observer.cluster.local ansible_host=192.168.50.19

[compute:children]
workers
gpu

[all_nodes:children]
head
management
workers
gpu
visualization

[all_nodes:vars]
ansible_user=wpaik
cluster_network=192.168.50.0/24
cluster_domain=cluster.local
cluster_realm=CLUSTER.LOCAL

arbiter는 Ansible 컨트롤러 자체이므로 ansible_connection=local을 사용합니다.

ansible.cfg를 만듭니다.

[defaults]
private_key_file    = /opt/ansible/.ssh/worker_ed25519
inventory           = ./hosts.ini
host_key_checking   = False
log_path            = ./log/ansible.log
vault_password_file = /opt/ansible/.ansible_vault_pw
remote_tmp          = /var/tmp/.ansible-${USER}/tmp

마지막 줄 remote_tmp는 나중에야 문제가 드러나는 설정이라 따로 설명이 필요합니다. 기본적으로 Ansible은 원격 노드의 ~/.ansible/tmp/에 작업별 임시 파일을 씁니다. 섹션 3에서 NFS를 설정하면 모든 노드의 /home이 NFS 공유에 있게 되므로, 그 임시 디렉토리도 NFS 위에 올라갑니다. 거기에 쓰인 파일은 SELinux의 nfs_t 컨텍스트를 받는데, dnf는 나중 에피소드에서 로컬 RPM을 설치할 때 이를 거부합니다. 디스크에 명백히 존재하는 RPM 파일에 대해 dnf가 No match for argument라고 보고하기 때문에 실패가 혼란스럽습니다. remote_tmp를 각 노드의 로컬 경로(/var/tmp는 항상 로컬)로 고정하면 이 문제를 미리 피할 수 있습니다.

연결 확인:

[wpaik@arbiter ansible]$ ansible all -m ping
carrier.cluster.local | SUCCESS => { "ping": "pong" }
arbiter.cluster.local | SUCCESS => { "ping": "pong" }
interceptor-01.cluster.local | SUCCESS => { "ping": "pong" }
interceptor-02.cluster.local | SUCCESS => { "ping": "pong" }
corsair-01.cluster.local | SUCCESS => { "ping": "pong" }
observer.cluster.local | SUCCESS => { "ping": "pong" }

6개 노드 모두 응답합니다. 이제부터 플레이북이 반복적인 작업을 처리합니다.

3. NFS 서버 설정
#

이 섹션의 모든 명령어는 arbiter에서 실행합니다.

LVM으로 NVMe 드라이브 파티셔닝
#

단일 대용량 파티션도 되지만, LVM을 쓰면 홈 디렉토리, 작업 스토리지, 공유 소프트웨어, 스크래치 공간에 별도 볼륨을 유연하게 할당할 수 있습니다. 실제 HPC 클러스터에서 스토리지를 구성하는 방식을 그대로 반영한 겁니다.

먼저 NVMe 드라이브를 확인합니다.

[wpaik@arbiter ~]$ lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda      8:0    0 223.6G  0 disk
├─sda1   8:1    0   600M  0 part /boot/efi
├─sda2   8:2    0     1G  0 part /boot
└─sda3   8:3    0   222G  0 part
  ├─rl-root  253:0  0  70G  0 lvm  /
  └─rl-swap  253:1  0 7.7G  0 lvm  [SWAP]
nvme0n1  259:0  0 931.5G  0 disk

SATA 부트 드라이브는 sda, NVMe는 nvme0n1입니다. 물리 볼륨, 볼륨 그룹, 논리 볼륨 4개를 만듭니다.

# LVM 툴 설치
$ sudo dnf install -y lvm2

# 물리 볼륨과 볼륨 그룹 생성
$ sudo pvcreate /dev/nvme0n1
$ sudo vgcreate vg_nfs /dev/nvme0n1

# 논리 볼륨 생성
$ sudo lvcreate -L 167G -n lv_home    vg_nfs
$ sudo lvcreate -L 251G -n lv_work    vg_nfs
$ sudo lvcreate -L  84G -n lv_shared  vg_nfs
$ sudo lvcreate -L 251G -n lv_scratch vg_nfs

# XFS로 포맷
$ sudo mkfs.xfs /dev/vg_nfs/lv_home
$ sudo mkfs.xfs /dev/vg_nfs/lv_work
$ sudo mkfs.xfs /dev/vg_nfs/lv_shared
$ sudo mkfs.xfs /dev/vg_nfs/lv_scratch

마운트 포인트 생성 및 마운트:

$ sudo mkdir -p /nfsdata/{home,work,shared,scratch}

$ sudo mount /dev/vg_nfs/lv_home    /nfsdata/home
$ sudo mount /dev/vg_nfs/lv_work    /nfsdata/work
$ sudo mount /dev/vg_nfs/lv_shared  /nfsdata/shared
$ sudo mount /dev/vg_nfs/lv_scratch /nfsdata/scratch

재부팅 후에도 유지되도록 /etc/fstab에 추가:

$ echo '/dev/vg_nfs/lv_home    /nfsdata/home    xfs defaults 0 0' | sudo tee -a /etc/fstab
$ echo '/dev/vg_nfs/lv_work    /nfsdata/work    xfs defaults 0 0' | sudo tee -a /etc/fstab
$ echo '/dev/vg_nfs/lv_shared  /nfsdata/shared  xfs defaults 0 0' | sudo tee -a /etc/fstab
$ echo '/dev/vg_nfs/lv_scratch /nfsdata/scratch xfs defaults 0 0' | sudo tee -a /etc/fstab

arbiter 자체도 NFS 스토리지를 사용하도록 /nfsdata/home/home에 바인드 마운트합니다.

$ echo '/nfsdata/home /home none bind 0 0' | sudo tee -a /etc/fstab
$ sudo mount -a

최종 레이아웃 확인:

[wpaik@arbiter ~]$ lsblk
NAME                  MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda                     8:0    0 223.6G  0 disk
...
nvme0n1               259:0    0 931.5G  0 disk
├─vg_nfs-lv_home      253:2    0   167G  0 lvm  /home
│                                               /nfsdata/home
├─vg_nfs-lv_work      253:3    0   251G  0 lvm  /nfsdata/work
├─vg_nfs-lv_shared    253:4    0    84G  0 lvm  /nfsdata/shared
└─vg_nfs-lv_scratch   253:5    0   251G  0 lvm  /nfsdata/scratch

바인드 마운트 덕분에 lv_home이 두 번 나옵니다. /nfsdata/home(실제 마운트 포인트)과 /home(arbiter 자체가 쓰는 바인드 마운트)으로요. 나머지 세 볼륨은 arbiter의 /nfsdata 경로에만 마운트됩니다. 클라이언트 노드들은 NFS를 통해 /work, /shared, /scratch에 마운트할 겁니다.

NFS 서버 설정
#

$ sudo dnf install -y nfs-utils
$ sudo systemctl enable --now nfs-server

/etc/exports 설정:

/nfsdata/home    192.168.50.0/24(rw,sync,no_root_squash,no_subtree_check)
/nfsdata/work    192.168.50.0/24(rw,sync,no_root_squash,no_subtree_check)
/nfsdata/shared  192.168.50.0/24(rw,sync,no_root_squash,no_subtree_check)
/nfsdata/scratch 192.168.50.0/24(rw,sync,no_root_squash,no_subtree_check)

옵션 설명: rw는 읽기/쓰기 허용, sync는 응답 전 디스크에 커밋 (더 안전), no_subtree_check는 서브디렉토리 내보낼 때 성능 패널티 회피, no_root_squash는 클라이언트 노드의 root가 공유에서도 root로 동작하게 해서 Slurm이 나중에 필요로 합니다.

no_root_squash 참고: 신뢰할 수 있는 내부 클러스터 네트워크에 적합한 설정입니다. 이 클러스터는 192.168.50.x 서브넷에서 물리적으로 격리되어 있습니다. 신뢰할 수 없는 사용자가 있는 공유 클러스터에서는 root_squash를 사용합니다.

적용 및 방화벽 열기:

$ sudo exportfs -ra
$ sudo firewall-cmd --permanent --add-service={nfs,rpc-bind,mountd}
$ sudo firewall-cmd --reload

# 확인
$ sudo showmount -e localhost
Export list for localhost:
/nfsdata/scratch 192.168.50.0/24
/nfsdata/shared  192.168.50.0/24
/nfsdata/work    192.168.50.0/24
/nfsdata/home    192.168.50.0/24

4. NFS 클라이언트 설정
#

각 노드에 수동으로 SSH 접속하는 대신 Ansible을 씁니다. arbiter/opt/ansible에서 실행합니다.

[wpaik@arbiter ansible]$ ansible-playbook playbooks/nfs_setup.yaml -K

플레이북이 각 클라이언트 노드에서 하는 일: nfs-utils 설치, NFS 홈 디렉토리용 SELinux 부울 설정, /work, /shared, /scratch 마운트 포인트 생성, _netdev를 붙여 /etc/fstab에 NFS 마운트 4개 추가, 마운트 실행.

_netdev 옵션은 네트워크가 준비될 때까지 마운트를 기다리라고 시스템에 알려줍니다. 없으면 arbiter보다 빠르게 부팅되는 노드가 마운트에 실패하고 부팅이 멈출 수 있습니다.

재부팅 후 carrier에서 확인:

[wpaik@carrier ~]$ df -h
Filesystem                              Size  Used Avail Use% Mounted on
/dev/mapper/rl-root                      70G  5.4G   65G   8% /
arbiter.cluster.local:/nfsdata/home     167G  8.2G  159G   5% /home
arbiter.cluster.local:/nfsdata/work     251G  4.9G  247G   2% /work
arbiter.cluster.local:/nfsdata/shared    84G   23G   62G  27% /shared
arbiter.cluster.local:/nfsdata/scratch  251G   22G  230G   9% /scratch

참고: 플레이북이 워커 노드와 GPU 노드를 자동으로 재부팅합니다. carrier(헤드 노드)는 SSH 진입점이므로 플레이북 완료 후 수동으로 재부팅해야 합니다. carrier 재부팅 후 df -h로 마운트를 확인합니다.

다음으로 넘어가기 전에 Chrony 플레이북을 실행해서 모든 노드의 시간을 동기화합니다.

[wpaik@arbiter ansible]$ ansible-playbook playbooks/chrony_setup.yaml -K

공유가 잘 작동하는지 테스트합니다.

# interceptor-01에서 테스트 파일 생성
[wpaik@interceptor-01 ~]$ touch /home/nfs_test.txt

# interceptor-02에서 보이는지 확인
[wpaik@interceptor-02 ~]$ ls /home/nfs_test.txt
/home/nfs_test.txt

파일 하나, 어디서든지 보입니다.

5. 시간 동기화 (Chrony)
#

FreeIPA 설정 전에 모든 노드가 같은 시간 소스에 동기화되어야 합니다. FreeIPA는 Kerberos를 인증에 사용하는데, Kerberos는 노드 간 시계 차이가 5분을 넘으면 티켓을 거부합니다. 새 클러스터에서는 보통 괜찮지만, 명시적으로 설정해두는 게 낫습니다.

carrier가 클러스터의 NTP 서버 역할을 합니다. 외부 소스(time.cloudflare.com, pool.ntp.org)에서 동기화하고 모든 내부 노드에 시간을 제공합니다. 다른 노드들은 carrier에서 동기화합니다.

[wpaik@arbiter ansible]$ ansible-playbook playbooks/chrony_setup.yaml -K

플레이북 완료 후 임의의 노드에서 동기화 상태 확인:

$ chronyc tracking
Reference ID    : C0A83201 (carrier.cluster.local)
Stratum         : 3
System time     : 0.000123456 seconds fast of NTP time
Last offset     : +0.000045678 seconds
RMS offset      : 0.000089012 seconds

Reference IDcarrier.cluster.local을 가리키면 그 노드가 carrier에서 동기화 중인 겁니다.

6. 로컬 사용자의 문제
#

NFS가 파일 공유 문제를 해결합니다. 하지만 새로운 문제를 만듭니다.

NFS는 파일 권한을 처리할 때 사용자 이름이 아닌 UID(User ID)와 GID(Group ID) 번호를 씁니다. interceptor-01의 사용자 will이 UID 1001이고, interceptor-02will이 UID 1002라면 (계정을 다른 순서로 만들었기 때문에), 같은 NFS 파일에서 다른 권한을 보게 됩니다.

# interceptor-01에서
$ id will
uid=1001(will) gid=1001(will)

# interceptor-02에서
$ id will
uid=1002(will) gid=1002(will)

# interceptor-01의 will(uid=1001)이 소유한 NFS 파일이
# interceptor-02에서는 다른 사용자 소유처럼 보입니다

모든 노드의 UID를 수동으로 동기화하는 방법이 있습니다. 6노드 클러스터에 사용자 몇 명이면 지루하지만 관리 가능합니다. 수백 명의 사용자가 있는 실제 클러스터에서는 불가능합니다.

적절한 해결책은 **중앙 집중식 인증(Centralized Authentication)**입니다. 사용자 계정이 한 곳에서 정의되고, 모든 노드가 그 소스에서 가져오는 겁니다. FreeIPA가 바로 그 역할을 합니다.

사전 준비: UID 정렬
#

NFS는 사용자 이름을 비교하지 않습니다. 파일마다 찍힌 숫자 UID와 GID를 비교합니다. 진행하기 전에 확인합니다.

[wpaik@arbiter ansible]$ ansible all_nodes -a "id wpaik"

모든 노드가 동일한 uid=gid=를 보고해야 합니다. 차이가 있으면 계속하기 전에 arbiter의 값(보통 1000, 하지만 확인 필요)을 기준으로 맞춰요.

수정은 맞지 않는 노드에서 다른 sudo 계정이나 root로, wpaik의 활성 세션이 없는 상태에서 실행합니다. 아래 예시는 arbiterwpaik가 UID 1000이고 맞지 않는 노드가 현재 1001인 경우입니다.

# 맞지 않는 노드에서 root 또는 다른 sudo 계정으로
[root@interceptor-01 ~]# who | grep wpaik         # 활성 세션 없는지 확인
[root@interceptor-01 ~]# pkill -KILL -u wpaik     # 남은 프로세스 종료

# NFS가 이미 마운트되어 있다면 먼저 언마운트
[root@interceptor-01 ~]# umount /home             # 바쁘면 -l 사용

# 계정 번호 변경
[root@interceptor-01 ~]# groupmod -g 1000 wpaik
[root@interceptor-01 ~]# usermod  -u 1000 -g 1000 wpaik

# 이전 UID로 된 파일의 소유권 수정
# -xdev는 find를 로컬 파일시스템에만 제한해서 다른 파티션과 NFS 마운트를 건드리지 않아요
[root@interceptor-01 ~]# find / -xdev -uid 1001 -exec chown -h 1000 {} +
[root@interceptor-01 ~]# find / -xdev -gid 1001 -exec chgrp -h 1000 {} +

# 확인
[root@interceptor-01 ~]# id wpaik
uid=1000(wpaik) gid=1000(wpaik) groups=1000(wpaik),10(wheel)

이건 임시방편입니다. 섹션 7의 FreeIPA가 로컬 계정을 중앙 집중식 아이덴티티로 대체하면 이 문제는 사라져요.

7. FreeIPA 서버 설치
#

FreeIPA는 여러 서비스를 하나의 패키지로 묶습니다. LDAP(디렉토리), Kerberos(인증), DNS, 인증 기관입니다. 설치가 독립적이고 모든 것을 함께 설정합니다.

이 섹션의 모든 명령어는 arbiter에서 실행합니다.

사전 조건
#

FreeIPA는 완전한 도메인 이름(FQDN)이 필요합니다. 진행하기 전에 올바르게 해석되는지 확인합니다.

[wpaik@arbiter ~]$ hostname -f
arbiter.cluster.local

[wpaik@arbiter ~]$ ping -c 1 arbiter.cluster.local
PING arbiter.cluster.local (192.168.50.50) 56(84) bytes of data.

사용 가능한 RAM이 최소 1.5GB인지도 확인합니다. 설치 프로그램이 메모리를 많이 씁니다.

$ free -h
              total        used        free
Mem:           15Gi        800Mi       14Gi

서버 설치 및 실행
#

$ sudo dnf install -y freeipa-server freeipa-server-dns

$ sudo ipa-server-install \
  --domain=cluster.local \
  --realm=CLUSTER.LOCAL \
  --ds-password=<your_directory_manager_password> \
  --admin-password=<your_admin_password> \
  --hostname=arbiter.cluster.local \
  --ip-address=192.168.50.50 \
  --no-ntp \
  --unattended

참고: --realm은 반드시 대문자, --no-ntp는 Chrony로 시간 동기화를 따로 관리하므로 NTP 설정을 건너뜀, --unattended는 대화형 프롬프트를 스킵합니다. 설치 프로그램이 5~10분 걸리고 LDAP, Kerberos, CA를 설정합니다.

완료 후 필요한 방화벽 포트 열기:

$ sudo firewall-cmd --permanent --add-service={freeipa-ldap,freeipa-ldaps,kerberos,dns,http,https}
$ sudo firewall-cmd --reload

설치 확인
#

$ kinit admin
Password for [email protected]:

$ klist
Ticket cache: KCM:0
Default principal: [email protected]

Valid starting     Expires            Service principal
04/27/26 09:00:00  04/28/26 09:00:00  krbtgt/[email protected]

$ ipa user-find
---------------
0 users matched
---------------

아직 사용자가 없습니다. 등록 후에 추가할 겁니다.

기본 셸을 bash로 설정합니다 (FreeIPA 기본값은 /bin/sh):

$ ipa config-mod --defaultshell=/bin/bash

8. FreeIPA 클라이언트 등록
#

등록 전에 모든 노드의 /etc/hostsarbiter를 추가합니다. 등록 프로세스가 arbiter.cluster.local을 해석해야 하는데, 이 시점에서 SSSD가 아직 설정 안 됐습니다. 미리 해두면 DNS 해석 실패로 등록이 실패하는 걸 막을 수 있습니다.

Ansible 플레이북이 자동으로 처리합니다.

[wpaik@arbiter ansible]$ ansible-playbook playbooks/freeipa_setup.yaml -K

수동으로 각 노드에서 하려면:

# /etc/hosts에 arbiter 추가
$ echo "192.168.50.50 arbiter.cluster.local arbiter" | sudo tee -a /etc/hosts

# 설치 및 등록
$ sudo dnf install -y freeipa-client oddjob-mkhomedir

$ sudo ipa-client-install \
  --server=arbiter.cluster.local \
  --domain=cluster.local \
  --realm=CLUSTER.LOCAL \
  --principal=admin \
  --password=<your_admin_password> \
  --mkhomedir \
  --no-ntp \
  --unattended

--mkhomedir는 처음 로그인 시 홈 디렉토리를 생성하라고 시스템에 알려줍니다. /homearbiter에서 NFS 마운트되어 있으므로 디렉토리가 NFS 공유에 생성되고 모든 노드에서 즉시 보입니다.

등록 후 각 노드가 IPA 서버에 연결할 수 있는지 확인합니다.

[wpaik@interceptor-01 ~]$ ipa user-find
---------------
0 users matched
---------------

응답이 오면 (0명이라도) 클라이언트가 등록되어 서버와 통신하고 있는 겁니다.

테스트 사용자 생성
#

arbiter로 돌아와서:

[wpaik@arbiter ~]$ kinit admin

$ ipa user-add testuser \
  --first=Test \
  --last=User \
  --password

$ ipa user-find testuser
--------------
1 user matched
--------------
  User login: testuser
  First name: Test
  Last name: User
  Home directory: /home/testuser
  Login shell: /bin/bash
  UID: 99100XXXX
  GID: 99100XXXX

UID 범위를 보세요. FreeIPA가 로컬 시스템 계정 범위보다 훨씬 높은 UID를 할당합니다. 정확한 시작 범위는 설치 중 구성에 따라 다르지만, 할당된 값은 클러스터의 모든 노드에서 동일합니다.

지속적인 사용자 관리를 위해 GitHub 저장소scripts/user_creation.sh 스크립트가 전체 프로세스를 처리합니다. FreeIPA 계정 생성, 올바른 NFS 소유권으로 홈 디렉토리 설정, XFS 쿼타, Slurm 어카운팅 항목까지요.

FreeIPA 웹 UI 접근
#

FreeIPA 웹 인터페이스는 VPN처럼 SSH를 통해 트래픽을 라우팅하는 sshuttle 도구를 사용해서 클러스터 외부에서 접근할 수 있습니다.

로컬 머신에서:

# sshuttle 설치
$ sudo dnf install sshuttle    # Fedora/RHEL
# 또는: pip install sshuttle

# 로컬 /etc/hosts에 arbiter 추가
$ echo "192.168.50.50 arbiter arbiter.cluster.local" | sudo tee -a /etc/hosts

# 터널 열기 (이 터미널을 계속 열어두세요)
$ sshuttle -r wpaik@carrier 192.168.50.0/24 --dns

그런 다음 브라우저에서 https://arbiter.cluster.local/ipa/ui/를 엽니다. 자체 서명 인증서 경고를 수락하고 admin 계정으로 로그인합니다.

9. 검증
#

로그인 노드에서 새 사용자로 컴퓨트 노드에 SSH 접속합니다.

[wpaik@carrier ~]$ ssh testuser@interceptor-01
Password:
Creating home directory for testuser.

[testuser@interceptor-01 ~]$ pwd
/home/testuser

[testuser@interceptor-01 ~]$ id
uid=99100XXXX(testuser) gid=99100XXXX(testuser) groups=99100XXXX(testuser)

이제 다른 노드에서 같은 사용자를 확인합니다.

[testuser@interceptor-02 ~]$ id
uid=99100XXXX(testuser) gid=99100XXXX(testuser) groups=99100XXXX(testuser)

두 노드에서 UID가 같습니다. interceptor-01에서 쓴 파일이 interceptor-02에서 올바른 권한으로 보입니다. 어느 노드에 접속해도 홈 디렉토리가 동일한 NFS 경로입니다.

모든 노드에서 하나의 계정, 하나의 홈 디렉토리를 확인 할 수 있습니다.

일반적인 문제 해결
#

DNS 오류로 등록 실패: 플레이북이 등록 전에 /etc/hostsarbiter.cluster.local을 추가합니다. 여전히 실패하면 실패한 노드에 항목이 있는지 확인합니다.

$ getent hosts arbiter.cluster.local
192.168.50.50   arbiter.cluster.local arbiter

없으면 수동으로 추가합니다.

$ echo "192.168.50.50 arbiter.cluster.local arbiter" | sudo tee -a /etc/hosts

FreeIPA 등록 후 NFS 마운트 실패: FreeIPA가 /etc/nsswitch.conf를 업데이트합니다. passwd와 group에서 filessss 앞에 오는지 확인합니다.

$ grep -E "^(passwd|group)" /etc/nsswitch.conf
passwd:     sss files systemd
group:      sss files systemd

등록 후 NFS 마운트가 멈추면:

$ sudo setsebool -P use_nfs_home_dirs 1

첫 로그인 시 홈 디렉토리가 생성되지 않음:

$ sudo systemctl enable --now oddjobd

NFS 설정 후 노드가 부팅 중 멈춤: GRUB의 오래된 resume=UUID가 부팅 멈춤을 유발할 수 있습니다. GRUB 메뉴에서 e를 눌러 resume=UUID=... 인수를 제거한 다음 Ctrl+X로 부팅합니다. 부팅 후:

$ grubby --update-kernel=ALL --remove-args="resume=UUID=<UUID>"

10. 다음 에피소드
#

클러스터에 공유 스토리지와 중앙 집중식 인증이 갖춰졌습니다. 모든 노드가 동일한 홈 디렉토리를 공유하고 모든 사용자가 모든 노드에서 일관된 아이덴티티를 가집니다.

다음 에피소드에서는 작업 스케줄러인 Slurm을 설치합니다. NFS와 FreeIPA가 이미 갖춰져 있어서 Slurm은 노드 간 작업을 스케줄링하고 출력 파일을 공유 위치에 쓸 준비가 된 겁니다.

이 에피소드의 모든 설정 파일과 Ansible 플레이북은 GitHub 저장소에 있습니다.


즐거운 컴퓨팅 되세요!

HPC From Scratch - 이 글은 시리즈의 일부입니다.
파트 4: 이 글