AIStor Release Storage 설치 및 구성¶
0. 전제¶
다음 환경 기준으로 작성:
- 규모: 프로젝트 5~20개 / 서버 소수
- 인력: 1~2명 (소수 정예)
- 멀티 스택 환경: Node.js, Express, Next.js, Nuxt.js, PHP, Laravel, C++ 등
- 운영 서버: Linux / Windows 혼합 가능
- Docker 사용 범위: Runner 빌드 환경 및 Release Storage 서버의 AIStor 실행 용도
- 배포 방식: 운영 서버가 Release Storage에서 빌드 결과물을 직접 가져가는 pull 배포
인프라 전제:
- GitLab 서버, Runner 서버, Release Storage 서버, 운영 서버는 논리적, 물리적으로 분리 가능한 구성으로 보며, 분리 운영을 전제로 작성함.
- GitLab 서버는 외부 비공개로 유지하며, 운영 서버는 GitLab 서버에 직접 접근하지 않음.
- Release Storage 서버는 운영 서버가 접근 가능한 별도 서버로 구성함.
운영 원칙:
- build -> publish -> operation server pull deploy 구조 유지
{YYYYMMDD-HHMMSS}-{declared_version}-{CI_COMMIT_SHORT_SHA}기반 immutable package version 운영- 인증은 목적별 계정 및 Access Key로 분리
- 빌드: Runner 서버
- CI 내부 산출물 보관: GitLab Generic Package Registry
- 운영 배포 기준 저장소: Release Storage (S3-compatible Object Storage)
- 운영: download, verify, extract, activate, report 수행
금지 사항:
- 운영 서버에 write 권한 Access Key 배치 금지
.env및 서버 고유 설정 파일을 패키지에 포함 금지- 운영 서버에서 배포 파일을 수동 수정한 뒤 재사용 금지
- Generic Package Registry에 업로드된 package version/file overwrite 금지
- Release Storage 의 releases 경로 Release Object overwrite 금지
- Release Storage 의
current/{RELEASE_ENV}.json외 object overwrite 금지 - 운영 서버가 GitLab 서버에 직접 접근하도록 구성 금지
위 전제를 기반으로
AIStor 기반 Release Storage를 실제로 운영 가능한 상태까지 구성하는 단계를 정의함.
1. AIStor Release Storage 사용 목적¶
본 구조에서 GitLab 서버는 내부 개발 및 CI/CD 전용 시스템으로 사용함.
운영 기준:
gitlab.example.com은 외부 비공개- 운영 서버는 GitLab 직접 접근 금지
- 운영 서버는 Release Storage만 접근 가능
즉:
GitLab ≠ 운영 서버 Release Artifact Endpoint
2. 전체 구조¶
GitLab Runner
├── GitLab Generic Package Registry (CI 내부 산출물 보관)
└── AIStor Release Storage (운영 Release Source)
↓
AIStor Release Storage
↓
운영 서버 Polling / Deploy
3. 도메인 구성¶
Release Storage API:
release.example.com
AIStor Console (WebUI):
release-console.example.com
운영 기준:
- 운영 서버는
release.example.com만 접근 - AIStor Console (WebUI) 은 관리자 전용 접근
- 가능하면 AIStor Console (WebUI) 은 공인 인터넷 직접 공개 대신 VPN 또는 내부망 기반 접근 사용
- AIStor Console (WebUI) 은 운영 서버가 접근할 필요 없음
- AIStor Console (WebUI) 과 API 접근 정책은 반드시 분리
4. Rocky Linux 서버 준비¶
- 본 작업은 Release Storage 서버에서 수행함.
- 기존에
podman또는 다른 container runtime 을 사용 중인 서버에서는 제거 전 영향 범위를 확인함. - 신규 Release Storage 전용 서버라면 충돌 방지를 위해 기존 container runtime 제거 후 Docker CE 를 설치함.
패키지 설치:
sudo dnf update -y
sudo dnf remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runc || true
sudo dnf install -y dnf-plugins-core nginx
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Docker 및 Nginx 활성화:
sudo systemctl enable docker
sudo systemctl start docker
sudo systemctl enable nginx
sudo systemctl start nginx
검증:
docker --version
docker compose version
docker info
sudo systemctl status docker
sudo systemctl status nginx
검증 기준:
Docker Engine - Community
docker-ce
containerd.io
docker compose plugin
등이 정상 출력되어야 함.
5. Release Storage 데이터 경로 및 Mount 구성¶
- 본 작업은 Release Storage 서버에서 수행함.
- Release Storage 데이터는 OS Root Volume 과 분리
- AIStor Object Data 는 별도 Storage 에 저장
- 서비스 표준 경로(
/var/opt/aistor)와 실제 Storage 경로를 bind mount 로 연결 - 실제 데이터 위치를 분리하여 Storage 이전 및 확장 용이성 확보
- 향후 Storage 이전, NAS, Object Storage 확장 등을 고려하여 구조화
- 다른 운영자가 보더라도 저장 데이터의 역할과 이전 대상을 즉시 이해 가능하도록 구성
구조:
/aistor
/var/opt/aistor
/etc/aistor
├── .env
└── docker-compose.yml
설명:
-
/aistor- 실제 Storage Mount 위치
- 별도 Disk / Partition 사용 가능
-
/var/opt/aistor- 서비스 표준 경로
- Docker Compose 에서 사용하는 경로
-
/etc/aistor- Docker Compose 및 환경 변수 관리 경로
5.1 설치 디스크 또는 파티션 결정 및 디렉터리 준비¶
AIStor 설치 전에 /aistor 를 어느 디스크 또는 파티션에 둘지 먼저 결정해야 함.
이 결정에 따라 이후 모든 경로와 마운트 구성이 달라짐.
디스크 또는 파티션 현황 확인:
sudo lsblk
선택 기준:
| 구성 방식 | 설명 | 권장 대상 |
|---|---|---|
| OS 디스크 또는 파티션 내 LVM | 별도 디스크 없이 OS 내부에 구성 | 소규모 / 테스트 |
| 별도 디스크 또는 파티션 분리 | /aistor 전용 디스크 또는 파티션을 마운트 후 구성 |
운영 환경 권장 |
별도 디스크 또는 파티션 사용 시 (해당하는 경우만 수행)¶
⚠️ 해당 디스크 또는 파티션 마운트가 bind mount보다 반드시 먼저 적용되어야 함. 순서가 잘못되면 부팅 시 bind mount가 빈 경로에 걸려 AIStor 데이터가 유실 또는 데이터가 잘못된 위치에 기록되어 기존 데이터가 가려질 수 있음.
# ⚠️ 아래 DISK_OR_PARTITION 변수를 본인 환경에 맞게 먼저 수정 (lsblk 결과 참고)
# 예: /dev/sda, /dev/sdb, /dev/nvme1n1 등 환경마다 다름
DISK_OR_PARTITION=/dev/sda
# 필요시! 신규 디스크 또는 파티션 포맷 (최초 1회, 기존 데이터 삭제됨 주의)
# sudo mkfs.xfs ${DISK_OR_PARTITION}
# 마운트
sudo mkdir -p /aistor
sudo mount ${DISK_OR_PARTITION} /aistor
# UUID 확인 (fstab 등록용)
sudo blkid ${DISK_OR_PARTITION}
디렉터리 생성 (모든 환경 공통)¶
sudo mkdir -p /aistor
sudo chown -R root:root /aistor
sudo chmod -R 755 /aistor
sudo mkdir -p /var/opt/aistor
sudo chown -R root:root /var/opt/aistor
sudo chmod -R 755 /var/opt/aistor
sudo mkdir -p /etc/aistor
sudo chown -R root:root /etc/aistor
sudo chmod -R 755 /etc/aistor
5.2 bind mount 적용¶
- bind mount는 상위 경로 → 하위 경로 순으로 적용함
sudo mount --bind /aistor /var/opt/aistor
5.3 영구 적용 (/etc/fstab)¶
- nofail 옵션 사용 시 mount 실패 상태에서도 부팅이 진행되므로, 부팅 후 mount 상태를 반드시 확인해야 함
- systemd는 fstab을 병렬로 처리할 수 있으므로 x-systemd.requires 로 의존성을 명시해야 함
sudo vi /etc/fstab
OS 디스크 또는 파티션 내 LVM 사용 시:
/aistor /var/opt/aistor none bind,nofail 0 0
별도 디스크 또는 파티션 사용 시 (UUID는 blkid 결과로 대체):
# 반드시 bind mount보다 위에 위치해야 함
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /aistor xfs defaults,noatime 0 0
/aistor /var/opt/aistor none bind,nofail,x-systemd.requires=/aistor,x-systemd.after=/aistor 0 0
sudo systemctl daemon-reload
sudo mount -a
5.4 mount 확인¶
# mount 상태 확인
mount | grep aistor
findmnt /var/opt/aistor
findmnt | grep aistor
# 디렉터리 구조 확인
sudo ls -ld /aistor /var/opt/aistor /etc/aistor
cat /etc/fstab | grep aistor
5.5 동작 테스트¶
# data 영역 테스트
sudo touch /var/opt/aistor/testfile
sudo ls /aistor
sudo rm /aistor/testfile
5.6 SELinux 주의사항¶
현재 프로젝트 기준처럼
/aistor 를
각 AIStor 기본 경로에 bind mount 하여 사용하는 구조에서는,
SELinux Enforcing 환경이라도 추가 context 설정 없이 동작하는 경우가 많아 설정이 항상 필요한 것은 아님.
Docker Compose 기준으로 /var/opt/aistor 를 컨테이너 /mnt/data 에 mount 하여 사용하는 구조에서는,
SELinux Enforcing 환경에서 접근 문제가 발생할 수 있음.
단, 다음 상황에서는 SELinux 문제 발생 가능:
- 커스텀 경로 직접 사용 시
- context가 변경된 경우
- 서비스 접근 실패(AVC 발생)
이 경우에만 아래를 확인함.
sudo sestatus
sudo ausearch -m avc -ts recent
필요 시 참고:
sudo restorecon -Rv /aistor /var/opt/aistor
sudo ls -Zd /aistor /var/opt/aistor
5.7 주의사항 (중요)¶
/aistor디렉터리는 반드시 충분한 디스크 또는 파티션 용량을 확보할 것- bind mount는 AIStor 설치 전에 적용해야 함
- mount 누락 시 AIStor 데이터가
/var/opt/aistor에 직접 저장됨 (복구 어려움) /etc/fstab설정 오류 시 부팅 실패 가능 → 반드시mount -a검증 수행- 상위 경로 mount 후 하위 경로를 순서대로 mount 해야 함
5.8 서버 이전 및 데이터 이동 전략¶
본 구조는 AIStor 데이터 영역을 분리하여 다음과 같은 다양한 이전 시나리오를 지원함.
⚠️ 반드시 AIStor 서비스 중지 후 수행¶
cd /etc/aistor
sudo docker compose down
전체 AIStor 이전¶
rsync -avz /aistor/ new-server:/aistor/
rsync -avz /etc/aistor/ new-server:/etc/aistor/
5.9 운영 기준¶
- 각 디렉터리는 독립적으로 이동 가능해야 함
- mount 구조를 유지하면 부분 이전이 가능함
- 이전 시 AIStor 서비스 중지 상태에서 수행
- 데이터 이동 후 mount 및 경로 정합성 반드시 검증
--delete옵션 사용 시 대상 서버의 기존 데이터가 삭제되므로 사용 전 반드시 확인- 서버 간 권한/소유권 불일치 주의: rsync 완료 후
/aistor,/var/opt/aistor,/etc/aistor의 권한과 mount 상태를 재검증할 것
5.10 요약¶
- bind mount를 통해 기본 경로와 연결함
- 해당 작업은 반드시 설치 전에 수행해야 함
- 이 구조로 두면 이후 서버 이전 및 저장소 분리가 쉬워짐.
6. AIStor Docker Compose 구성¶
- 본 작업은 Release Storage 서버에서 수행함.
6.1 환경 변수 File¶
위치:
/etc/aistor/.env
생성:
sudo vi /etc/aistor/.env
내용:
MINIO_ROOT_USER=<aistor-root-admin>
MINIO_ROOT_PASSWORD=<change_this_password>
권한 설정:
sudo chown root:root /etc/aistor/.env
sudo chmod 600 /etc/aistor/.env
AIStor Free License 발급:
- 아래 내용을 통해 신청 후 Mail 을 통해
minio.license를 Download.
https://www.min.io/pricing
→ AIStor Free
→ Get Started
AIStor Free License File 배치:
- Release Storage 서버의
/home/{user}/로 Upload 후 이동
sudo mv /home/{user}/minio.license /etc/aistor/minio.license
권한 설정:
sudo chown root:root /etc/aistor/minio.license
sudo chmod 600 /etc/aistor/minio.license
sudo ls -l /etc/aistor/minio.license
운영 기준:
MINIO_ROOT_PASSWORD는 32자 이상 랜덤 문자열 사용.env및minio.license파일은 Git 저장소에 커밋 금지- AIStor Free 는 License File 기반으로 실행함
- License File 은 Release Storage 서버 내부에서만 관리함
- Root 계정은 초기 구성 및 관리자 AIStor Console (WebUI) 접근 용도로만 사용
- CI 및 운영 서버에서 Root 계정 사용 금지
- AIStor Free 는 single-node / standalone 운영 기준으로 사용함
- distributed clustering 또는 HA 구성이 필요하면 Free tier 범위를 벗어날 수 있음
6.2 Docker Compose File¶
6.2.1 AIStor Image 선정 기준¶
AIStor 공식 Container image:
quay.io/minio/aistor/minio
공식 설치 기준:
docker pull quay.io/minio/aistor/minio:latest
docker image inspect quay.io/minio/aistor/minio:latest --format='{{index .RepoDigests 0}}'
운영 기준:
- AIStor Release Storage 는 애플리케이션처럼 매 pipeline 마다 재배포하는 대상이 아니고 장기간 실행되는 운영 인프라 서비스로 사용함
- 서버 reboot 시 Docker 는 local image 로 container 를 재기동하므로 단순 reboot 만으로 image version 이 변경되지는 않음
latesttag 는docker pull또는docker compose pull수행 시에만 갱신됨- 본 문서에서는 운영 단순성을 위해
latesttag 를 사용함 - AIStor update 는 필요 시 release note 확인 후 수동 수행함
- update 전후 AIStor Console (WebUI), S3 API, upload/download 동작을 검증함
6.2.2 docker-compose.yml¶
- AIStor Server 공식 container image는
quay.io/minio/aistor/minio:latest를 사용함. - AIStor Server의 실행 바이너리 이름은
minio이므로 container command도minio server ...형식을 사용함. - 따라서 본 문서에서
minio server,MINIO_*환경 변수,mcCLI가 함께 등장하는 것은 MinIO Community Server와 혼용한 것이 아니라 AIStor 공식 실행 방식에 따른 것임. - AIStor 컨테이너는 Object Data를 직접 기록해야 하므로
/mnt/datavolume은 read-write 상태여야 함. - SELinux Enforcing 환경 호환을 위해
/var/opt/aistor:/mnt/data:z옵션을 사용함.
위치:
/etc/aistor/docker-compose.yml
생성:
sudo vi /etc/aistor/docker-compose.yml
내용:
services:
aistor:
image: quay.io/minio/aistor/minio:latest
container_name: aistor-release-storage
restart: unless-stopped
command: minio server /mnt/data --console-address ":9001" --license /minio.license
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
MINIO_SERVER_URL: https://release.example.com
MINIO_BROWSER_REDIRECT_URL: https://release-console.example.com
volumes:
- /var/opt/aistor:/mnt/data:z
- /etc/aistor/minio.license:/minio.license:ro
ports:
- "127.0.0.1:9000:9000"
- "127.0.0.1:9001:9001"
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "10"
권한 설정:
sudo chown root:root /etc/aistor/docker-compose.yml
sudo chmod 644 /etc/aistor/docker-compose.yml
실행:
.env자동 로딩을 위해 반드시/etc/aistor경로에서 docker compose 명령을 실행함.
cd /etc/aistor
sudo docker compose up -d
검증:
cd /etc/aistor
sudo docker compose ps
sudo docker logs --tail=100 aistor-release-storage
curl -I http://127.0.0.1:9000
curl -I http://127.0.0.1:9001
sudo ss -tulnp | grep -E '9000|9001'
검증 기준:
API: https://release.example.com
AIStor Console (WebUI): https://release-console.example.com
7. Nginx 구성 전 사전 확인¶
TLS 인증서 발급¶
release.example.com 및 release-console.example.com 에 대한 TLS 인증서가 Nginx 설정 적용 전에 발급되어 있어야 함.
방화벽 포트 개방¶
- AIStor 포트
9000,9001은127.0.0.1바인딩이므로 방화벽 개방 불필요 - HTTP/HTTPS 만 개방하여 Nginx Reverse Proxy 경유 접근만 허용
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --permanent --list-all
8. Nginx Reverse Proxy 구성¶
- 본 작업은 Release Storage 서버에서 수행함.
운영 기준:
- AIStor 포트
9000,9001직접 외부 공개 금지 - Nginx Reverse Proxy 를 통해서만 접근
- API 와 AIStor Console (WebUI) 도메인 분리
8.1 Upgrade 헤더용 map¶
WebSocket 지원을 위해 http 블록 또는 공통 include에 추가함.
ex) /etc/nginx/nginx.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
8.2 Release API Proxy¶
위치:
/etc/nginx/conf.d/sites-available/infrastructure/release.example.com.conf
생성:
sudo vi /etc/nginx/conf.d/sites-available/infrastructure/release.example.com.conf
sudo ln -s /etc/nginx/conf.d/sites-available/infrastructure/release.example.com.conf /etc/nginx/conf.d/sites-enabled/infrastructure/release.example.com.conf
내용:
# 인증서 발급 전
# server {
# listen 80;
# listen [::]:80;
# server_name release.example.com;
# location ^~ /.well-known/acme-challenge/ {
# root /var/www/letsencrypt;
# default_type "text/plain";
# try_files $uri =404;
# }
# location / {
# return 200 "release.example.com nginx ready\n";
# }
# }
# 인증서 발급 후
server {
listen 80;
listen [::]:80;
server_name release.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type "text/plain";
try_files $uri =404;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl;
server_name release.example.com;
# [보안 설정] Release Storage API 접근 제어
# ---------------------------------------------------------
# GitLab Runner 및 운영 서버만 S3 API 접근을 허용함
# 관리자 PC는 초기 검증 및 mc 관리 작업이 필요한 경우에만 허용함
# allow 192.168.0.0/24; # 사내 네트워크 대역
# allow 127.0.0.1; # 로컬 호스트 허용
# allow <gitlab-runner-ip>;
# allow <operation-server-ip>;
# allow <admin-pc-or-vpn-ip>;
# deny all;
# ---------------------------------------------------------
ssl_certificate /etc/letsencrypt/live/release.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/release.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
client_max_body_size 0;
client_body_buffer_size 16m;
proxy_connect_timeout 300s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
send_timeout 3600s;
location / {
proxy_pass http://127.0.0.1:9000; # 또는 http://192.168.0.100:9000;
proxy_http_version 1.1;
proxy_buffering off;
proxy_request_buffering off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Connection "";
}
}
확인:
sudo nginx -t
sudo systemctl reload nginx
8.3 AIStor Console (WebUI) Proxy¶
위치:
/etc/nginx/conf.d/sites-available/infrastructure/release-console.example.com.conf
생성:
sudo vi /etc/nginx/conf.d/sites-available/infrastructure/release-console.example.com.conf
sudo ln -s /etc/nginx/conf.d/sites-available/infrastructure/release-console.example.com.conf /etc/nginx/conf.d/sites-enabled/infrastructure/release-console.example.com.conf
내용:
# 인증서 발급 전
# server {
# listen 80;
# listen [::]:80;
# server_name release-console.example.com;
# location ^~ /.well-known/acme-challenge/ {
# root /var/www/letsencrypt;
# default_type "text/plain";
# try_files $uri =404;
# }
# location / {
# return 200 "release-console.example.com nginx ready\n";
# }
# }
# 인증서 발급 후
server {
listen 80;
listen [::]:80;
server_name release-console.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type "text/plain";
try_files $uri =404;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl;
server_name release-console.example.com;
# [보안 설정] AIStor Console (WebUI) 접근 제어
# ---------------------------------------------------------
# AIStor Console (WebUI) 은 관리자 및 내부 운영 네트워크만 접근 허용 권장
# allow 192.168.0.0/24;
# allow 127.0.0.1;
# allow <admin-pc-or-vpn-ip>;
# deny all;
# ---------------------------------------------------------
ssl_certificate /etc/letsencrypt/live/release-console.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/release-console.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
client_max_body_size 0;
client_body_buffer_size 16m;
proxy_connect_timeout 300s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
send_timeout 3600s;
location / {
proxy_pass http://127.0.0.1:9001; # 또는 http://192.168.0.100:9001;
proxy_http_version 1.1;
proxy_buffering off;
proxy_request_buffering off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
확인:
sudo nginx -t
sudo systemctl reload nginx
8.4 nginx 설정 검증 및 반영¶
sudo nginx -t
sudo systemctl reload nginx
접속 확인:
curl -I http://release.example.com
curl -I https://release.example.com
curl -I http://release-console.example.com
curl -I https://release-console.example.com
¶
curl -I http://release.example.com
curl -I https://release.example.com
curl -I http://release-console.example.com
curl -I https://release-console.example.com