Red Hat Certified Engineer (RHCE) #16 RHCSA 자동화 3: 스토리지(LVM), 파일시스템(NFS)
#15 RHCSA 자동화 2: 서비스, chronyd, log에서 서비스와 시간 동기화, 로그를 플레이북으로 다뤘다면, 이번에는 RHCSA에서 가장 손이 많이 가던 영역인 스토리지를 Ansible로 자동화하겠습니다. 디스크에 파티션을 만들고, LVM으로 VG와 LV를 구성하고, 파일시스템을 올린 다음 영구 마운트까지 잇는 일련의 작업을 멱등성 있는 task로 풀어내는 것이 목표입니다.
스토리지는 RHCE 실기의 단골 출제 영역입니다. RHCSA에서 LVM 시리즈와 NFS/autofs 시리즈로 손으로 익힌 작업을, 이번 글에서는 같은 결과를 내는 플레이북으로 옮기겠습니다.
스토리지 자동화의 전체 그림 #
손으로 하던 스토리지 작업을 떠올리면 다음 순서였습니다. 디스크에 파티션을 만들고(parted), 그 파티션을 PV로 삼아 VG를 만들고(vgcreate), VG에서 LV를 잘라내고(lvcreate), LV를 포맷하고(mkfs), 마운트 지점을 만든 뒤 /etc/fstab에 등록하고 실제로 마운트하는 흐름이었습니다.
Ansible은 이 단계마다 전용 모듈을 둡니다.
| 손작업 | Ansible 모듈 |
|---|---|
parted 파티션 | community.general.parted |
vgcreate (VG) | community.general.lvg |
lvcreate (LV) | community.general.lvol |
mkfs 포맷 | community.general.filesystem |
/etc/fstab + mount | ansible.posix.mount |
여기서 핵심은 모듈마다 멱등성이 보장되는 방식이 다르다는 점입니다. lvg와 filesystem은 이미 존재하면 그냥 넘어가지만, parted와 lvol은 옵션을 잘못 주면 매 실행마다 changed로 잡히거나 크기를 다시 건드릴 수 있으므로 주의가 필요합니다.
parted: 파티션 만들기 #
community.general.parted는 디스크에 파티션 테이블과 파티션을 만듭니다. 멱등성을 위해 number(파티션 번호)와 시작,끝 위치를 명시하는 것이 안전합니다.
- name: 디스크에 파티션 1개 생성
community.general.parted:
device: /dev/vdb
number: 1
state: present
part_start: 1MiB
part_end: 1024MiB같은 number로 같은 part_start/part_end를 주면 두 번째 실행에서는 changed가 발생하지 않습니다. 반대로 끝 위치를 100%처럼 상대값으로 주면 환경에 따라 매 실행마다 재계산되어 멱등성이 흔들릴 수 있으므로, 명시적 크기를 권장합니다.
LVM에 쓸 파티션이라면 파티션 플래그를 LVM으로 설정하기도 합니다.
- name: LVM용 파티션 생성
community.general.parted:
device: /dev/vdb
number: 1
state: present
part_start: 1MiB
part_end: 2048MiB
flags: [ lvm ]다만 시험에서 제공하는 디스크를 통째로 PV로 쓰는 경우라면 파티션 없이 /dev/vdb 자체를 lvg에 넘길 수도 있습니다. 문제 지문이 “파티션을 만들어"라고 명시하면 parted를 쓰고, 그렇지 않으면 디스크 전체를 PV로 쓰는 쪽이 단순합니다.
lvg: VG 만들기 #
community.general.lvg는 PV를 묶어 VG를 만듭니다. pvs에 디스크나 파티션을 넘기면 PV 생성과 VG 생성을 한 번에 처리합니다.
- name: VG 생성
community.general.lvg:
vg: data_vg
pvs: /dev/vdb1
state: presentVG가 이미 존재하고 동일한 PV로 구성되어 있으면 changed가 발생하지 않습니다. 여러 PV를 묶으려면 리스트로 넘깁니다.
- name: 여러 PV로 VG 생성
community.general.lvg:
vg: data_vg
pvs:
- /dev/vdb1
- /dev/vdc1lvol: LV 만들기 #
community.general.lvol은 VG에서 LV를 잘라냅니다. size로 크기를 지정하며, 절대 크기(2g, 500m)와 상대 비율(50%VG, 100%FREE)을 모두 받습니다.
- name: LV 생성
community.general.lvol:
vg: data_vg
lv: data_lv
size: 1g
state: presentlvol에서 멱등성이 가장 잘 깨지는 지점이 크기 변경입니다. 이미 만들어진 LV에 다른 size를 주면 모듈이 크기를 줄이려고 시도할 수 있고, 축소는 데이터 손실로 이어집니다. 이를 막는 옵션이 두 가지입니다.
shrink: false. 축소 동작을 막아 실수로 LV가 작아지는 일을 방지합니다.resizefs: true. LV 크기를 바꿀 때 그 위의 파일시스템도 함께 키워 정합성을 맞춥니다.
- name: LV 크기를 안전하게 관리
community.general.lvol:
vg: data_vg
lv: data_lv
size: 2g
shrink: false
resizefs: true시험에서 “LV를 N기가로 만들어라"는 한 번 만들면 끝이지만, “기존 LV를 확장하라"가 나오면 resizefs: true로 파일시스템까지 함께 키우는 패턴을 떠올리겠습니다.
filesystem: 포맷 #
community.general.filesystem은 LV나 파티션에 파일시스템을 올립니다. fstype과 대상 장치(dev)를 지정합니다.
- name: LV를 xfs로 포맷
community.general.filesystem:
fstype: xfs
dev: /dev/data_vg/data_lv이미 해당 파일시스템이 있으면 changed가 발생하지 않습니다. 강제로 다시 만들려면 force: true를 줄 수 있지만, 기존 데이터를 지우므로 시험에서는 거의 쓰지 않습니다. 대상 장치 경로는 /dev/<vg>/<lv> 또는 /dev/mapper/<vg>-<lv> 형식 모두 동작합니다.
mount: fstab 등록과 실제 마운트 #
ansible.posix.mount는 /etc/fstab 항목과 실제 마운트를 함께 다룹니다. 핵심은 state 값입니다.
| state | 동작 |
|---|---|
mounted | fstab에 등록하고 지금 즉시 마운트 |
present | fstab에만 등록(지금 마운트하지 않음) |
unmounted | 지금 언마운트(fstab 항목은 유지) |
absent | 언마운트하고 fstab 항목도 제거 |
영구 마운트가 목표라면 거의 항상 state: mounted입니다. fstab 등록과 실제 마운트를 한 번에 해 주므로, 재부팅 후에도 살아남으면서 지금 당장도 쓸 수 있습니다.
- name: 마운트 지점 디렉터리 생성
ansible.builtin.file:
path: /data
state: directory
mode: '0755'
- name: LV를 영구 마운트
ansible.posix.mount:
path: /data
src: /dev/data_vg/data_lv
fstype: xfs
state: mountedsrc는 장치 경로 대신 UUID=...나 LABEL=...로 줄 수도 있습니다. 장치 이름이 바뀌어도 안전하게 마운트하려면 UUID를 쓰는 편이 견고합니다.
swap 추가 #
swap도 같은 모듈 조합으로 다룹니다. filesystem에서 fstype: swap으로 swap 시그니처를 만들고, mount 모듈로 fstab에 등록합니다. swap은 디렉터리에 마운트하는 것이 아니므로 path: none, fstype: swap, state: present로 fstab에만 등록한 뒤 활성화하는 것이 일반적입니다.
- name: swap LV 포맷
community.general.filesystem:
fstype: swap
dev: /dev/data_vg/swap_lv
- name: swap을 fstab에 등록하고 활성화
ansible.posix.mount:
path: none
src: /dev/data_vg/swap_lv
fstype: swap
opts: sw
state: presentstate: present로 fstab에 등록한 다음, 실제 활성화는 command: swapon -a로 보완하거나 swap을 다루는 storage role에 맡기는 방식을 쓰겠습니다.
NFS 원격 마운트 #
NFS 마운트도 동일한 ansible.posix.mount 모듈로 처리합니다. 로컬 파일시스템과 다른 점은 fstype: nfs이고 src가 서버:내보낸경로 형식이라는 것뿐입니다.
- name: NFS export를 영구 마운트
ansible.posix.mount:
path: /mnt/share
src: nfs-server.example.com:/exports/share
fstype: nfs
opts: defaults,_netdev
state: mounted네트워크가 올라온 뒤 마운트되도록 opts에 _netdev를 넣는 습관을 들이겠습니다. NFS 클라이언트 패키지(nfs-utils)가 없으면 마운트가 실패하므로, 그 앞에 패키지 설치 작업을 두는 것이 안전합니다.
- name: NFS 클라이언트 패키지 설치
ansible.builtin.dnf:
name: nfs-utils
state: present전체 LVM 플레이북 예제 #
지금까지의 작업을 하나의 흐름으로 묶으면 다음과 같습니다. 시험에서 “디스크로 VG/LV를 만들어 xfs로 포맷하고 /data에 영구 마운트하라"는 유형의 표준 답안입니다.
---
- name: LVM 스토리지 구성
hosts: storage
become: true
tasks:
- name: VG 생성
community.general.lvg:
vg: data_vg
pvs: /dev/vdb
- name: LV 생성
community.general.lvol:
vg: data_vg
lv: data_lv
size: 1g
shrink: false
- name: 파일시스템 생성
community.general.filesystem:
fstype: xfs
dev: /dev/data_vg/data_lv
- name: 마운트 지점 생성
ansible.builtin.file:
path: /data
state: directory
mode: '0755'
- name: 영구 마운트
ansible.posix.mount:
path: /data
src: /dev/data_vg/data_lv
fstype: xfs
state: mounted이 플레이북을 두 번 돌려 두 번째 실행에서 changed가 0이 나오는지 반드시 확인하겠습니다. lvg/lvol/filesystem/mount는 모두 멱등성을 지원하므로, 제대로 작성했다면 재실행 시 모든 작업이 ok로 잡힙니다.
storage system role 대안 #
위 작업을 한 번에 묶어 주는 공식 역할이 rhel-system-roles의 storage role입니다. #13 system roles에서 다룬 system role 계열로, PV,VG,LV,파일시스템,마운트를 변수 하나로 선언합니다.
---
- name: storage role로 스토리지 구성
hosts: storage
become: true
roles:
- rhel-system-roles.storage
vars:
storage_pools:
- name: data_vg
disks:
- vdb
volumes:
- name: data_lv
size: 1g
fs_type: xfs
mount_point: /datastorage_pools에 디스크와 볼륨을 선언하면 역할이 내부에서 VG/LV 생성, 포맷, fstab 등록, 마운트를 모두 처리합니다. swap도 같은 구조에서 fs_type: swap으로 선언할 수 있습니다. 개별 모듈을 직접 엮는 방식과 storage role 중 어느 쪽을 써도 시험에서는 인정되므로, 손에 익은 쪽을 쓰겠습니다. 다만 지문이 특정 모듈을 지정하면 그 모듈을 쓰는 것이 안전합니다.
시험 포인트 #
mount모듈의state: mounted가 fstab 등록과 실제 마운트를 한 번에 처리합니다. 영구 마운트 문제의 표준 답입니다.present는 fstab에만 등록하므로 지금 마운트가 빠집니다.parted와lvol은 멱등성이 잘 깨집니다.parted는 시작,끝 위치를 명시하고,lvol은shrink: false로 축소를 막겠습니다.- LV 확장은
lvol에resizefs: true를 주어 파일시스템까지 함께 키웁니다. - NFS는
ansible.posix.mount에fstype: nfs와서버:경로형식src,opts: _netdev로 처리하고, 앞에nfs-utils설치를 둡니다. - 스토리지 모듈은 대부분
community.generalcollection 소속이고mount는ansible.posix소속입니다. FQCN과 collection 설치 여부를 확인하겠습니다.
정리 #
이번 글에서 잡은 것:
- 스토리지 자동화의 단계별 모듈.
parted(파티션) →lvg(VG) →lvol(LV) →filesystem(포맷) →mount(fstab+마운트) lvol의shrink: false와resizefs: true로 축소 방지와 파일시스템 동시 확장mount모듈의state의미.mounted가 영구 마운트의 정답- swap은
filesystem의fstype: swap과mount의path: none으로 처리 - NFS 원격 마운트는
fstype: nfs와_netdev옵션,nfs-utils설치 - 개별 모듈 조합의 대안으로 rhel-system-roles의 storage role
다음: RHCSA 자동화 4 #
스토리지까지 자동화했습니다. RHCSA 자동화의 마지막 조각은 보안 영역입니다.
#17 RHCSA 자동화 4: firewall, SELinux, SSH 키에서는 firewalld를 ansible.posix.firewalld로 다루고, SELinux 모드와 불리언,포트 레이블을 ansible.posix.selinux와 community.general.sefcontext로 자동화하며, SSH 공개 키 배포까지 플레이북으로 묶어 정리하겠습니다.