이전 글에서 언급했듯 첫 번째로 스토리지 서버 구축을 한다.

storage 서버는 전체 플랫폼에서 데이터를 저장하는 계층이다.
두 가지 스토리지 서비스를 하나의 VM에 구성해보고자 한다.
| 서비스 | 역할 |
| NFS(Network File System) | 웹 서버 2대가 공유할 웹 소스 코드 및 문제 데이터 저장 |
| iSCSI(Internet Small Computer Systems Interface) | DB 서버 1대가 사용할 블록 스토리지 제공 |
실제 환경이라면 2대로 분리하는 것이 좋지만, 이미 8대의 VM을 돌리고 있어서 리소스 절약을 하기 위해 한번에 구성을 했다.
- 학습 관점에서, NFS/iSCSI가 어떻게 다른지 한 서버에서 비교하면서 실습하는데에 의의를 두자.
그럼 NFS 구성부터 시작해보자.
1. NFS 서버 구성
NFS는 네트워크를 통해 디렉토리를 공유하는 프로토콜이다. 웹 서버(web1, web2) 두 대가 동일한 웹 소스 코드와 문제 데이터를 바라보도록 하기 위해 사용한다. NFS가 없으면 각 웹 서버마다 파일을 따로 관리해야해 정적 데이터 관리에서 불편함을 느낀다.
NFS에 대한 내용도 간단히 다뤄보자.

NFS는 RPC 개념이 사용된다.
- RPC는 Remove Procedure Control로, 멀리 떨어져 있는 컴퓨터의 기능을 마치 내 컴퓨터에 있는 기능을 쓰듯 호출하는 기술이다.
1-1] RPC?
일반적인 프로그래밍 함수를 호출하면 내 컴퓨터의 메모리 안에서 동작한다. 하지만 네트워크로 연결된 다른 서버의 기능을 쓰고 싶을 때는 어떻게 할 수 있을까?
복잡한 네트워크 통신을 직접하지 않고도 RPC를 이용하면 일반 함수 호출하듯이 다른 서버의 기능을 사용할 수 있다.
1-2] NFS에서 왜 RPC가 중요할까?
NFS의 목적은 네트워크 너머에 있는 파일을 내 하드디스크에 있는 파일처럼 쓰는 것이다.
- 사용자: "저기 있는 test.txt 파일 좀 열어줘." (함수 호출)
- NFS: (RPC에게 요청) "저쪽 서버에 가서 파일 열기(open) 기능 좀 실행하고 결과 받아와!"
- RPC: 네트워크 통신을 담당하여 서버에 접속해 파일을 열고, 그 결과를 다시 가져온다.
즉, NFS는 '무엇을 할지' 결정하는 서비스라면, RPC는 그것을 실제로 네트워크 너머로 전달하는 '택배' 역할을 한다.
1-3] 간단히 RPC의 동작 방식을 보면
- 클라이언트 스텁(Stub): 내 컴퓨터에 있는 가짜 함수다. 사용자가 호출하면 매개변수를 모아서 보낼 준비를 한다. (마샬링: 데이터를 전송 가능한 형태로 포장)
- 네트워크 전송: 포장된 데이터를 서버로 보낸다.
- 서버 스텁(Stub): 서버에서 데이터를 받아 포장을 풀고(언마샬링), 실제 서버의 함수를 실행한다.
- 결과 반환: 실행 결과를 다시 포장해서 클라이언트에게 돌려준다.
2. 설치 및 설정 (NFS 서버)
storage 서버에서 설정한다.
RHSCA를 준비하면서 했던 것... 인데 왜 기억이 잘 나지 않을까
2-1] 패키지 설치
먼저 NFS 패키지를 설치하고, 공유할 폴더를 만들어 권한을 부여한다.
# nfs-utils 패키지 설치
sudo dnf install -y nfs-utils
# 웹 서버가 공유할 디렉토리 생성
sudo mkdir -p /srv/nfs/webroot
# 소유자를 nobody로 변경하여 익명 접근 허용 및 전체 권한 부여
sudo chown -R nobody:nobody /srv/nfs/webroot
sudo chmod 777 /srv/nfs/webroot
2-2] 공유 설정(/etc/exports)
특정 네트워크 대역에 어떤 권한을 줄지 명시한다.
# /etc/exports에 공유 설정 추가
echo "/srv/nfs/webroot 192.168.56.0/24(rw,sync,no_root_squash)" | sudo tee -a /etc/exports
- 192.168.56.0/24: 내부망 대역만 허용.
- rw: 읽기/쓰기 허용.
- sync: 데이터를 즉시 디스크에 기록하여 안정성 확보.
- no_root_squash: 클라이언트의 root 권한을 서버에서도 인정하여 웹 서버가 파일을 쓸 수 있도록 함.
2-3] 서비스 실행, 확인 결과
서비스를 활성화하고 설정이 정상적으로 반영되었는지 확인한다.
# NFS 서버 서비스 시작 및 부팅 시 자동 시작 등록
sudo systemctl enable --now nfs-server
# 변경된 exports 설정 즉시 반영
sudo exportfs -arv
# 공유 목록 확인
showmount -e localhost
# 포트 리스닝 확인
ss -tlnp | grep 2049
- 공유 목록 확인: Export list for localhost: /srv/nfs/webroot 192.168.56.0/24
- 포트 리스닝 확인: LISTEN 0 4096 0.0.0.0:2049 (2049 포트가 정상 가동 중)
2-4] 방화벽 및 보안 설정
NFS 통신에 필요한 포트들을 storage에서 개방한다.
sudo systemctl enable --now firewalld #
# NFS, mountd, rpc-bind 서비스 허용
sudo firewall-cmd --permanent --add-service=nfs
sudo firewall-cmd --permanent --add-service=mountd
sudo firewall-cmd --permanent --add-service=rpc-bind
sudo firewall-cmd --reload #
이렇게 해서 NFS 서버 구성은 마무리한다.
- NFS 클라이언트인 web1, web2 설정은 이후에 진행한다.
3. iSCSI
바로 전 글에서도 다뤘듯 iSCSI는 네트워크(IP)를 통해 블록 스토리지를 제공하는 프로토콜이다.
- storage 서버의 디스크(/dev/sdb)를 네트워크로 연결된 다른 서버(DB 서버)에 마치 로컬 디스크처럼 붙여주는 기술이다.
- target: 스토리지를 제공하는 쪽(storage 서버)
- initiator: 스토리지를 사용하는 쪽(db1, db2 서버)
- 우리는 target 설정을 이번 글에서 해본다.
우선 잠시 iSCSI에 대해 조금 더 자세히 알아보고 가보자.
처음에 iSCSI에 대해 접했을 때 공유 파일 스토리지라는 측면에서 NFS와 헷갈렸다.
iSCSI vs NFS 비교
| 항목 | iSCSI | NFS |
| 계층 | 블록 레벨 | 파일 레벨 |
| 클라이언트 인식 | 로컬 디스크처럼 인식 | 네트워크 폴더처럼 인식 |
| 파일 시스템 | 클라이언트가 직접 구성 | 서버가 관리 |
| 동시 접근 | 기본적으로 단일 클라이언트 (다중 접근 시 클러스터 File System 필요) |
여러 클라이언트 동시 공유 가능 |
| 락(Lock) 관리 | 클러스터 파일시스템(GFS2 등)이 담당 | NFS 서버가 담당 |
| 주요 사용처 | VM 디스크, DB 스토리지, OS 부팅 | 파일 공유, 홈 디렉토리, 로그 |
왜 도대체 iSCSI를 쓸까?
서버마다 디스크를 꽂으면 되지 않을까?
- 만약 100대의 서버가 있다면...
| 항목 | 로컬 디스크 | iSCSI(중앙 스토리지) |
| 용량 확장 | 서버마다 직접 꽂아야 함 | 스토리지 서버만 확장하면 됨 |
| 관리 | 서버 수만큼 관리 포인트 생성 | 중앙(스토리지 서버)에서 일괄 관리 |
| 활용 효율 | A 서버는 꽉 찼는데 B 서버는 비어있는 낭비 발생 | 스토리지 풀을 공유해 낭비 최소화 |
| 클라이언트 인식 | 물리 디스크 그대로 | 똑같이 로컬 디스크처럼 느낌 |
- 용량 확장에서 스토리지 서버만 확장하면 된다에서 조금 헷갈렸는데
- 흔히 스토리지 서버를 한 대 두고, DB 서버를 여러 대 연결해서 쓸 거다.
- 이때, DB 서버에 디스크를 직접 꽂았다면 확장하는 데 여러 번 꽂아야 하지만, 이 경우 스토리지 서버에서만 iSCSI 용량을 확장하면 되므로 편리하게 관리될 수 있다는 것이다.
각 서버마다 별도의 iSCSI 볼륨을 할당해주는 방식이 일반적. 동시 공유가 아니기 때문에 클러스터 FS 없이 ext4로도 충분하다.
근데 왜 여러 서버가 같은 iSCSI 볼륨에 동시에 접근하면 안될까?
- 일반 파일시스템(ext4, NTFS 등)은 "나 혼자 이 디스크를 쓴다"는 전제로 설계되어 있다.
- 서버 A와 서버 B가 동시에 같은 디스크를 마운트하면, 둘 다 독립적으로 메타데이터(어느 블록이 사용 중인지)를 관리한다.
- 서버 A가 "블록 100번 비어있다"고 보고 쓰는 동시에, 서버 B도 "블록 100번 비어있다"고 보고 다른 데이터를 쓴다.
- 결과적으로 파일시스템 메타데이터와 데이터가 완전히 엉켜버린다.
- 클러스터 파일시스템(GFS2, OCFS2)을 사용. 내부에 분산 락 매니저를 내장해서 "나 지금 이 블록 쓸게, 잠깐 기다려"를 서버들끼리 조율한다. 단, 구성이 복잡하다. (이번 프로젝트에선 따라서 이 파일 시스템을 쓰지 않는다.)
- NFS는 서버가 파일 시스템과 락을 직접 관리하고 클라이언트는 요청만 보내므로 이 문제가 없다.
위에서도 간단히 언급했듯 iSCSI는 Initiator & Target으로 구성된다.

IQN, LUN을 활용해 iSCSI의 Initiator과, Target을 연결하게 되는데 이 부분은 아래 설정 단계에서 자세히 본다.
3-1] iSCSI 설치
sudo dnf install -y targetcli lvm2
- targetcli: iSCSI Target 설정 대화형 CLI 도구다.
- lvm2: LVM(Logical Volume Manager)
- 디스크를 논리적으로 관리하는 도구다.
- 서비스 중단 없이 볼륨 크기 조절이 가능하다. 실습에서는 /dev/sdb를 직접 사용하므로 필수는 아니지만, 실무에서는 항상 같이 쓴다고 한다.
3-2] Target 설정
sudo targetcli
/>
3-2-1] Backstore 생성: 실제 저장공간 정의
iSCSI가 Backstore는 '실제 데이터가 저장되는 배후지'를 의미한다.
- 물리적인 실제 디스크(/dev/sdb)를 iSCSI라는 네트워크 서비스가 인식할 수 있도록 '이게 우리 공급원이다~'라고 등록하는 장부라고 볼 수 있다.
- 우리 프로젝트에서 물리적인 실제 디스크는 Vagrantfile에서 vb.customize를 통해 생성하고 storage VM 1번 포트에 연결했던 10GB의 iscsi.vdi 파일을 의미한다.
- storage 서버 입장에서는 케이블로 연결된 실제 하드디스크(/dev/sdb)로 인식하기 때문에 물리적 디스크라고 표현했다.
/> /backstores/block create name=iscsi_disk dev=/dev/sdb
Created block storage object iscsi_disk using /dev/sdb.
name=iscsi_disk -> 백스토어를 부르는 별명. 이후 LUN(Logical Unit Number) 연결 시 이 이름으로 참조한다.
dev=/dev/sdb -> 실제 데이터가 저장될 디스크
- 나중에 db1이 IQN을 찾아 접속한 뒤, LUN(iscsi_disk)를 보고 가져다 쓴다.
백스토어 타입은 2가지가 존재한다.
| 타입 | 설명 | 사용처 |
| block | 실제 물리 디스크(/dev/sdb 등)를 그대로 사용 | 실무, 고성능 환경 |
| fileio | 파일 하나를 디스크처럼 사용 (이미지 파일) | 실습, 디스크 없을 때 |
간단히 비교를 해보면
- Block(물리 디스크 방식)
- 빈 땅(하드 디스크 /dev/sdb)을 통째로 빌려주기
- 빌려 쓰는 사람(db1)이 자기 마음대로 할 수 있고, 중간에 거치는 단계가 없어 매우 빠름
- Fileio(파일 가상화 방식)
- 이미 존재하는 파일 시스템 위에 파일을 하나 두고, 여기가 니 땅이다~ 라고 하는 것
- 빌려 쓰는 사람이 사용하려면 먼저 파일을 열어야 하므로 속도가 상대적으로 느리다.
우리 프로젝트의 경우 데이터 읽기/쓰기가 빈번한 편이므로 block 방식을 택했다.
3-2-2] iSCSI 타겟(IQN) 생성: 접속 엔드포인트 생성
Initiator가 접속할 타겟을 만든다. 이 때 고유 식별자인 IQN이 부여된다.
/> /iscsi create iqn.2024-01.com.saa:storage
Created target iqn.2024-01.com.saa:storage.
Created TPG 1.
IQN 형식: iqn.연월.도메인역순:식별자
명령 실행 시 TPG 1이 자동 생성된다. TPG는 "이 타겟에 어떤 IP/포트로 접근 가능한지"를 묶은 그룹.
3-2-3] LUN 생성: Backstore를 타겟에 연결
백스토어와 타겟을 연결해 Initiator가 실제로 접근할 수 있는 논리 디스크를 만든다.
/> /iscsi/iqn.2024-01.com.saa:storage/tpg1/luns create /backstores/block/iscsi_disk
Created LUN 0.
실행 결과 LUN 0이 생성된다. Initiator 입장에서 '0번 디스크'가 생긴 것이다.
LUN을 여러 개 만들면 LUN0, 1, ... Initiator에게 여러 디스크처럼 보인다.
3-3] 인증 및 ACL 설정
아래 3가지는 세트로, 하나라도 빠지면 Initiator가 로그인할 때 막힌다.
# 인증 비활성화 (실습용)
/> /iscsi/iqn.2024-01.com.saa:storage/tpg1/ set attribute authentication=0
Parameter authentication is now '0'.
# 쓰기 허용
/> /iscsi/iqn.2024-01.com.saa:storage/tpg1/ set attribute demo_mode_write_protect=0
Parameter demo_mode_write_protect is now '0'.
# ACL 자동 허용
/> /iscsi/iqn.2024-01.com.saa:storage/tpg1/ set attribute generate_node_acls=1
Parameter generate_node_acls is now '1'.
| 속성 | 값 | 의미 |
| authentication | 0 | 인증 비활성화 |
| demo_mode_write_protect | 0 | 쓰기 허용 (0=허용, 1=차단) |
| generate_node_acls | 1 | ACL 자동 허용 (등록된 IQN 접근 가능) |
# ACL에 Initiator IQN 등록 (/etc/iscsi/initiatorname.iscsi 값)
/> /iscsi/iqn.2024-01.com.saa:storage/tpg1/acls create [db1 IQN]
db1의 IQN을 발급하는 부분은 다음 글에서 나올 예정이다. 우선은 했다고 가정을 해보자.
3-4] 설정 저장 및 서비스 시작
# targetcli 설정은 메모리에만 존재 → 반드시 저장해야 재부팅 후에도 유지
/> saveconfig
# targetcli 종료
/> exit
# 서비스 활성화 및 시작
sudo systemctl enable --now target
설정은 /etc/target/saveconfig.json에 저장되며, 서버 재부팅 후에도 자동 복원된다.
- saveconfig를 하지 않으면 재부팅 시 모든 설정이 날라간다.
3-5] 확인 결과
o- backstores
o- block [Storage Objects: 1]
o- iscsi_disk [/dev/sdb (10.0GiB) write-thru activated]
o- iscsi [Targets: 1]
o- iqn.2024-01.com.saa:storage [TPGs: 1]
o- tpg1 [no-gen-acls, no-auth]
o- luns [LUNs: 1]
o- lun0 [block/iscsi_disk (/dev/sdb)]
o- portals [Portals: 1]
o- 0.0.0.0:3260 [OK]
iSCSI 3260 포트 리스닝 확인
# 포트 리스닝 확인
ss -tlnp | grep 3260
LISTEN 0 256 0.0.0.0:3260 0.0.0.0:*
3-6] 방화벽 설정
# iSCSI 포트 허용
sudo firewall-cmd --permanent --add-port=3260/tcp
sudo firewall-cmd --reload
3-7] 확인 결과
iSCSI는 기본적으로 TCP 3260 포트를 사용한다. 방화벽에서 이 포트를 열어주지 않으면 Initiator가 Target에 접근할 수 없다.
sudo firewall-cmd --list-all | grep 3260
ports: 3260/tcp
결과
| 항목 | 상태 | 비고 |
| NFS 공유 /srv/nfs/webroot | 정상 | 192.168.56.0/24 대역 허용 |
| NFS 포트 2049 | 리스닝 | IPv4/IPv6 모두 |
| iSCSI 백스토어 iscsi_disk | 활성화 | /dev/sdb 10GB |
| iSCSI 타겟 IQN | 생성 | iqn.2024-01.com.saa:storage |
| iSCSI 포털 0.0.0.0:3260 | 리스닝 | 모든 인터페이스 허용 |
| firewalld 3260/tcp | 허용 | - |
| firewalld NFS 서비스 | 허용 | nfs, mountd, rpc-bind |
다음 단계에서는 db1(192.168.56.21), db2(192.168.56.22) VM을 설정하고 각 DB 서버에서 iSCSI Initiator를 설정하여 storage 서버와 디스크를 연결해본다.
'Infra > Project' 카테고리의 다른 글
| [세미 프로젝트] AWS SAA 기출문제 풀이 플랫폼 - Web 서버 (2) | 2026.02.25 |
|---|---|
| [세미 프로젝트] AWS SAA 기출문제 풀이 플랫폼 - LoadBalancer(internal) 구성 (0) | 2026.02.25 |
| [세미 프로젝트] AWS SAA 기출문제 풀이 플랫폼 - DB 서버 (0) | 2026.02.25 |
| [세미 프로젝트] AWS SAA 기출문제 풀이 플랫폼 - VM 세팅 (0) | 2026.02.25 |
| [세미 프로젝트] AWS SAA 기출문제 풀이 플랫폼 - 개요 (0) | 2026.02.25 |