Step 16-17. 접근 제어 & 백업/복구 — 인프라 파이프라인 최종 단계
이전 단계: 08-deploy-and-alerting.md 돌아가기: 09-hands-on.md
용어 안내
이 문서에서 “슬롯”은 학습용 비유다. 실무 용어가 아니다. 각 슬롯 옆에 실무에서 실제로 쓰는 용어를 병기한다.
Step 16. Nginx Basic Auth로 접근 제어 체험하기 — ⑬ Identity/Access (누가 무엇을 할 수 있는가)
왜 접근 제어가 필요한가? (WHY)
관리자 페이지에 누구나 접근할 수 있으면? DB를 아무나 삭제할 수 있으면? 접근 제어가 없으면 보안 사고가 난다.
회사 건물을 생각해보자. 1층 로비는 누구나 들어가지만, 서버실은 특정 카드키를 가진 사람만 들어간다.
웹 서비스도 마찬가지다. /health는 공개, /admin은 인증된 사용자만.
핵심 용어
Identity (아이덴티티)
- 영어 뜻: “신원, 정체” — “너 누구야?”에 대한 답
- IT 뜻: 시스템에 접근하는 사용자/서비스가 누구인지 식별하는 것
- 비유: 회사 출입증. 이름과 사진으로 본인을 증명하는 것
- 실무 용어: “IAM(Identity and Access Management)”
Access (액세스)
- 영어 뜻: “접근, 이용 권한” — “너는 이것만 할 수 있어”
- IT 뜻: 식별된 사용자에게 어떤 행동을 허용할지 정하는 것
- 비유: 출입증의 등급. 일반 직원은 사무실만, 임원은 서버실까지
- 실무 용어: “RBAC(Role-Based Access Control)”
Authentication(인증) vs Authorization(인가)
- Authentication(인증): 출입증으로 본인 확인. “너 누구야?” → “김철수입니다”
- Authorization(인가): 출입증 등급에 따라 들어갈 수 있는 층이 다름. “김철수는 3층까지만”
- 인증이 먼저, 인가가 그 다음. 순서가 바뀔 수 없다
16-1. 비밀번호 파일 생성
# htpasswd 도구 설치 (비밀번호 파일을 만드는 도구)
brew install httpd # Mac (Linux: apt install apache2-utils)
# nginx 디렉토리에 비밀번호 파일 생성
# -c: 새 파일 생성, -b: 커맨드라인에서 비밀번호 입력
htpasswd -cb nginx/.htpasswd admin secret123
16-2. Nginx 설정 업데이트
# nginx/default.conf
server {
listen 80;
# --- 일반 API — 누구나 접근 가능 ---
location /health {
proxy_pass http://app:3000; # 인증 없이 앱으로 전달
}
location /info {
proxy_pass http://app:3000; # 인증 없이 앱으로 전달
}
# --- 관리 영역 — 인증 필요 ---
location /admin {
auth_basic "Admin Area"; # 브라우저에 표시될 로그인 메시지
auth_basic_user_file /etc/nginx/.htpasswd; # 비밀번호 파일 경로
proxy_pass http://app:3000; # 인증 통과 시 앱으로 전달
}
}
16-3. Express 앱에 /admin 엔드포인트 추가
// app.js — /admin 엔드포인트 추가
app.get('/admin', (req, res) => {
res.json({
status: 'admin area',
uptime: process.uptime(), // 서버 가동 시간 (초)
memory: process.memoryUsage(), // 메모리 사용량
timestamp: new Date().toISOString()
});
});
16-4. docker-compose.yml 볼륨 추가
# docker-compose.yml의 nginx 서비스에 .htpasswd 볼륨 추가
services:
nginx:
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf # 기존 설정
- ./nginx/.htpasswd:/etc/nginx/.htpasswd # 비밀번호 파일 추가
16-5. 테스트
# 1. 인증 없이 일반 API — 성공
curl localhost/health
# → {"status":"ok"} (200 OK)
# 2. 인증 없이 관리 영역 — 거부!
curl localhost/admin
# → 401 Unauthorized (비밀번호 필요!)
# 3. 올바른 인증 정보 — 성공
curl -u admin:secret123 localhost/admin
# → {"status":"admin area","uptime":...} (200 OK)
# 4. 틀린 비밀번호 — 거부!
curl -u admin:wrong localhost/admin
# → 401 Unauthorized
핵심 인사이트: 이것이 접근 제어의 가장 기본적인 형태다. 실무에서 AWS IAM이나 K8s RBAC가 하는 일도 본질은 같다: **“누가 무엇을 할 수 있는가”**를 정하는 것.
Step 17. Docker 볼륨 백업과 복원 — ⑭ Backup/Disaster Recovery (데이터 보호)
왜 백업이 필요한가? (WHY)
Redis에 저장된 /count 데이터가 날아가면? DB가 실수로 삭제되면?
백업이 없으면 복구가 불가능하다.
핵심 용어
Backup (백업)
- 영어 뜻: “예비 복사본” — 원본 손상에 대비해 미리 복사해두는 것
- IT 뜻: 데이터를 별도 장소에 복사해두는 것
- 비유: 스마트폰 사진을 클라우드에 올려두는 것. 폰이 고장나도 사진은 살아있다
- 실무 용어: “백업/복원(Backup & Restore)”
Disaster Recovery (DR) (재해 복구)
- 영어 뜻: “disaster(재해) + recovery(복구)” — 재앙에서 되살리는 것
- IT 뜻: 서버 폭발, DB 삭제 등 최악의 상황에서 서비스를 되살리는 계획
- 비유: 건물에 비상구와 대피 계획을 마련해두는 것
- 실무 용어: “DR 플랜(Disaster Recovery Plan)”
RPO (Recovery Point Objective) = “복구 시점 목표”
- 얼마만큼의 데이터 손실을 감수할 수 있는가? RPO = 1시간이면 최대 1시간 전까지 복구 가능
- 비유: “저장 버튼을 얼마나 자주 누르는가”
RTO (Recovery Time Objective) = “복구 시간 목표”
- 얼마 안에 서비스를 복구해야 하는가? RTO = 30분이면 30분 안에 복구
- 비유: “정전 후 비상 발전기가 켜지는 데 걸리는 시간”
17-0. 준비: Redis에 볼륨 붙이기 (⚠️ 먼저 해야 함)
백업/복원을 하려면 Redis 데이터가 호스트 볼륨에 저장돼 있어야 한다.
지금까지 작성한 docker-compose.yml의 redis 서비스에는 볼륨이 없다 →
컨테이너를 내리면 데이터가 같이 사라진다. 백업/복원 실습이 성립하려면 볼륨부터 붙인다.
왜 필요한가?
- 컨테이너의 파일시스템은 일회용이다. 컨테이너가 지워지면 내부 데이터도 증발한다
volumes는 호스트 디스크(또는 Docker 관리 볼륨)에 데이터를 영속 저장하는 메커니즘- 볼륨이 없으면
docker run --rm -v my-infra-lab_redis-data:/data ...로 백업해도 Redis가 그 볼륨을 안 쓰므로 내용이 비어 있다 — 복원해도 반영 안 됨
docker-compose.yml 수정
# docker-compose.yml
services:
redis:
image: redis:alpine
volumes:
- redis-data:/data # ← 추가: Redis 데이터 디렉토리(/data)를 볼륨에 연결
# 파일 맨 아래에 top-level volumes 선언 추가 (named volume 사용 시 필수)
volumes:
redis-data: # services에서 참조한 볼륨 이름을 여기에 등록
왜
/data인가? Redis 공식 이미지가 RDB 스냅샷(dump.rdb)과 AOF 파일을/data에 저장하도록 기본 설정돼 있다. 이 경로를 볼륨에 연결해야 데이터가 살아남는다.
왜 top-level
volumes:를 따로 써야 하나?redis-data같은 이름 있는 볼륨(named volume) 은 compose 규약상 서비스에서 참조하기 전에 top-level에 등록돼 있어야 한다. 빠뜨리면service "redis" refers to undefined volume redis-data: invalid compose project에러가 난다.
재기동
docker compose down # -v 는 붙이지 말 것 (기존 데이터 있으면 유지)
docker compose up -d # 이제 Redis가 볼륨을 마운트하고 뜬다
# 확인: 볼륨이 붙었는지
docker compose exec redis ls /data
# → dump.rdb 가 생길 예정이거나, 비어있으면 곧 채워짐
17-1. 데이터 만들기
docker compose up -d
# /count를 여러 번 호출해서 데이터를 쌓는다
curl localhost/count # → 1
curl localhost/count # → 2
# ... 여러 번 반복해서 카운트를 10까지 올린다
17-2. 백업하기
⚠️ 먼저: BGSAVE로 디스크에 저장 강제하기
Redis는 메모리 DB라서 카운트 값이 RAM에만 있을 수 있다.
Redis 기본 저장 규칙(save 3600 1, save 300 100, save 60 10000)은
“카운트 10번” 정도로는 트리거되지 않는다 → /data/dump.rdb 가 비어 있거나 옛날 값.
백업 전에 강제로 스냅샷을 디스크에 쓰라고 명령한다:
docker compose exec redis redis-cli BGSAVE
# → Background saving started
sleep 2 # BGSAVE는 비동기이므로 잠깐 대기
docker compose exec redis redis-cli LASTSAVE
# → (integer) 1745200000 ← 방금 시각이면 저장 성공
BGSAVE vs SAVE: SAVE는 동기(Redis가 저장 동안 멈춤), BGSAVE는 비동기(백그라운드 자식 프로세스). 운영 중에는 반드시 BGSAVE를 쓴다.
백업 파일 생성
mkdir -p backup # 백업 저장 디렉토리 생성
# Redis 볼륨을 tar.gz 파일로 백업
docker run --rm \
-v my-infra-lab_redis-data:/data \
-v $(pwd)/backup:/backup \
alpine \
tar czf /backup/redis-backup.tar.gz -C /data .
# --rm: 작업 끝나면 컨테이너 자동 삭제
# -v ..._redis-data:/data: 백업할 볼륨을 /data에 마운트
# -v $(pwd)/backup:/backup: 백업 파일 저장 위치 (호스트)
# alpine: 경량 리눅스 이미지 (tar 실행용)
# tar czf: c=create, z=gzip, f=file — 압축 아카이브 생성
# -C /data .: /data 안의 모든 파일을 아카이브에 포함
# 확인: 백업 안에 dump.rdb 가 들어있는지
tar tzvf backup/redis-backup.tar.gz
# → ./dump.rdb (파일이 있고 크기가 0이 아니어야 정상)
17-3. 재해 시뮬레이션 (데이터 삭제)
docker compose down -v # -v: 볼륨까지 삭제!
# 볼륨 : 컨테이너 전용 외장하드 -> 컨테이너는 일회용이라, 컨테이너를 지우면 데이터가 함께 사라짐. 해당 문제 해결을 위해 데이터를 컨테이너 외부(호스트pc)에 저장하는 메커니즘
docker compose up -d # 다시 시작
curl localhost/count
# → 1 (이전 데이터가 모두 사라졌다!)
17-4. 백업에서 복원하기
docker compose stop redis # Redis 중지 (파일 덮어쓰기 위해)
# 백업 파일에서 볼륨으로 데이터 복원
docker run --rm \
-v my-infra-lab_redis-data:/data \
-v $(pwd)/backup:/backup \
alpine \
tar xzf /backup/redis-backup.tar.gz -C /data
# tar xzf: x=extract — 압축 아카이브 풀기
docker compose start redis # Redis 재시작
curl localhost/count
# → 11 (이전 데이터 10 + 새 요청 1 = 복원 성공!)
17-5. 자동 백업 스크립트
#!/bin/bash
# backup.sh — Redis 볼륨 자동 백업 스크립트
BACKUP_FILE="backup/redis-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
docker run --rm \
-v my-infra-lab_redis-data:/data \
-v $(pwd)/backup:/backup \
alpine \
tar czf /$BACKUP_FILE -C /data .
echo "백업 완료: $BACKUP_FILE"
chmod +x backup.sh # 실행 권한 부여
./backup.sh # → 백업 완료: backup/redis-backup-20260416-143000.tar.gz
실무에서는 이 스크립트를 cron(리눅스 예약 작업)이나 Kubernetes CronJob으로 자동 실행한다. 예: “매일 새벽 3시에 백업”.
핵심 인사이트: 백업이 있어도 복원을 테스트하지 않으면 소용없다. “백업은 있는데 복원이 안 돼요”는 실제로 자주 일어나는 사고다. 반드시 복원까지 테스트하라.
전체 실습 완료!
14개 인프라 역할을 모두 로컬에서 체험했다. 클라우드로 가면 도구 이름만 바뀔 뿐, 역할은 같다.
| 슬롯 (학습용) | 이 실습에서 사용한 도구 | 실무 도구 |
|---|---|---|
| ① Version Control | GitHub | GitHub / GitLab |
| ② Build/Test | GitHub Actions | Jenkins / CircleCI |
| ③ Artifact Storage | ghcr.io | ECR / Docker Hub |
| ④ Deploy/Release | 수동 pull & up | ArgoCD / Flux |
| ⑤ Orchestration | Docker Compose | Kubernetes |
| ⑥ Provisioning | Terraform (Docker) | Terraform (AWS) |
| ⑦ Configuration | Ansible (컨테이너) | Ansible (EC2) |
| ⑧ Networking | Nginx 리버스 프록시 | ALB / Ingress |
| ⑨ Secrets/Config | .env 파일 | Vault / AWS SSM |
| ⑩ Observability | 로그 미들웨어 | Datadog / Grafana |
| ⑪ Alerting | 콘솔 경고 | PagerDuty / Slack |
| ⑫ Security Scanning | Trivy | Snyk / Prisma |
| ⑬ Identity/Access | Nginx Basic Auth | AWS IAM / K8s RBAC |
| ⑭ Backup/DR | Docker 볼륨 백업 | RDS 스냅샷 / Velero |
최종 체크리스트
□ Nginx Basic Auth로 접근 제어를 체험했다
□ Docker 볼륨을 백업하고 복원할 수 있다
□ RPO와 RTO가 무엇인지 체감했다
□ 14개 인프라 역할을 모두 로컬에서 체험했다
→ 09-hands-on.md로 돌아가기
Comments
// admin login