컨테이너는 애플리케이션과 그 실행에 필요한 라이브러리·설정만을 묶어 어느 환경에서도 동일하게 동작하도록 만드는 표준화된 패키징 방식이다. 가상머신(VM)이 OS 전체를 가상화하는 반면, 컨테이너는 호스트 커널을 공유해 가볍고 빠르다. 이 글은 운영 관점에서 Docker를 활용해 서버를 안정적으로 관리하는 방법을 설치부터 빌드·배포·모니터링·보안·백업까지 실전 중심으로 정리한다.
1. 컨테이너 vs VM: 운영에 미치는 영향
- 부팅 속도: VM은 수십 초~수분, 컨테이너는 보통 수 초 이내
- 자원 효율: 컨테이너는 커널 공유로 오버헤드가 낮아 밀도가 높음
- 격리 수준: VM은 하드 격리(하이퍼바이저), 컨테이너는 네임스페이스/CGroups
- 이동성: 컨테이너 이미지는 “빌드 시점=런타임 환경”을 보장해 이식성이 뛰어남
운영 전략은 “컨테이너로 기본 단위를 표준화, 필요 시 VM/베어메탈로 특수 워크로드 분리”가 효율적이다.
2. Docker 기본 구성요소
- 이미지(Image): 컨테이너의 불변 템플릿. 레이어 캐시로 빌드·배포 효율 증가
- 컨테이너(Container): 이미지의 실행 인스턴스. 불변/무상태가 기본, 상태는 볼륨에 저장
- Dockerfile: 이미지 빌드 절차 정의
- 레지스트리(Registry): 이미지를 저장·배포(Hub, ECR, GCR, GHCR, 사설 레지스트리)
- 네트워크/볼륨: 서비스 간 통신과 데이터 영속화의 핵심 리소스
3. 설치와 첫 실행 (Ubuntu 예시)
# 1) 설치
sudo apt update
sudo apt install -y docker.io docker-compose-plugin
sudo usermod -aG docker $USER # 비루트 실행(재로그인 필요)
# 2) 데몬/버전 확인
sudo systemctl enable –now docker
docker versiondocker info
# 3) 첫 컨테이너
docker run –name web -p 8080:80 -d nginx:stable
curl -I http://localhost:8080
4. Dockerfile 베스트 프랙티스와 멀티스테이지 빌드
이미지 크기, 보안, 빌드 속도는 운영 품질에 직결된다.
- 슬림/알파인 기반 태그 사용(단, glibc 의존 확인)
- 멀티스테이지 빌드로 빌드 도구를 최종 이미지에서 제거
.dockerignore로 빌드 컨텍스트 최소화- 고정 버전/다이제스트 핀ning으로 재현성 확보
- 비루트 유저 실행, 읽기 전용 파일시스템, 불필요 Capabilities 제거
예시(Node.js)
# 빌드 단계
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci –omit=dev
COPY . .
RUN npm run build
# 런타임 단계
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY –from=build /app/dist ./dist
COPY –from=build /app/node_modules ./node_modules
RUN addgroup -S app && adduser -S app -G app
USER app
EXPOSE 3000
HEALTHCHECK –interval=10s –timeout=2s –retries=3 CMD wget -qO- http://localhost:3000/health || exit 1
CMD [“node”, “dist/server.js”]
5. 데이터 영속화: 볼륨과 바인드 마운트
- 볼륨: Docker가 관리하는 스토리지. 백업·이식 친화적
- 바인드 마운트: 호스트 디렉터리 연결. 개발·로그 공유에 편리
운영 시 설정은 읽기 전용으로, 데이터 디렉터리는 전용 볼륨으로 분리한다.
# 명명 볼륨
docker volume create appdata
docker run -d –name db -v appdata:/var/lib/postgresql/data postgres:16
# 읽기 전용 마운트
docker run -d -v /etc/myapp/config:/app/config:ro myapp:1.0.0
6. 네트워크 설계: 브릿지·호스트·오버레이
- bridge(기본): 서비스 묶음별 사용자 정의 네트워크 생성해 서비스 디스커버리 제공
- host: 호스트 네트워크 직접 사용(레이턴시 낮음, 포트 충돌 주의)
- overlay: 여러 호스트 간 분산 네트워크(Docker Swarm, k8s에서 사용)
docker network create –driver bridge appnet
docker run -d –name api –network appnet myapi:latest
docker run -d –name web –network appnet -p 80:80 nginx:stable
7. Compose로 멀티 컨테이너 운영 자동화
docker compose는 선언형 파일로 빌드·의존성·네트워크·볼륨·환경변수를 표준화한다.
# compose.yaml
version: “3.9”
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: “${DB_PASSWORD}”
volumes:
– dbdata:/var/lib/postgresql/data
healthcheck:
test: [“CMD-SHELL”, “pg_isready -U postgres”]
interval: 10s
timeout: 3s
retries: 5
app:
build: .
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: “postgres://postgres:${DB_PASSWORD}@db:5432/app”
deploy:
resources:
limits:
cpus: “1.0”
memory: 512M
read_only: true
web:
image: nginx:stable
ports: – “80:80”
volumes:
– ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on: – app
volumes:
dbdata:“
실행과 관리:
docker compose up -d
docker compose ps
docker compose logs -f app
docker compose pull && docker compose up -d # 무중단 갱신(짧은 끊김 가능)
8. 로그·모니터링·메트릭
- 표준 로그:
docker logs -f <name>(JSON-file 드라이버 기본) - 로깅 드라이버:
gelf,syslog,fluentd등으로 중앙 수집 - 메트릭: cAdvisor/Prometheus로 컨테이너 CPU·메모리·I/O·Restart 횟수 추적
- 헬스체크/리스타트 정책: 비정상 상태 자동 복구
docker run -d –restart=always –health-cmd=”curl -f http://localhost/health || exit 1″
myapp
9. 보안 모범사례(운영 필수)
- 비루트 실행: Dockerfile에서
USER지정, 호스트에서 rootless Docker 검토 - 최소 권한:
--cap-drop=ALL --cap-add=NET_BIND_SERVICE처럼 필요한 Cap만 추가 - 파일시스템 보호:
--read-only, 필요 경로만 tmpfs/볼륨으로 쓰기 허용 - 네임스페이스 격리 강화: 사용자 네임스페이스(Userns remap)
- 시크릿 관리: 환경변수 대신 시크릿/외부 비밀관리(KMS, Vault, Swarm/K8s Secrets)
- 이미지 신뢰: 서명/스캔(Trivy, Grype), CVE 자동 점검, 최신 패치 주기 적용
docker run -d \
–read-only \
–cap-drop=ALL –cap-add=NET_BIND_SERVICE \
-v run-tmp:/run -v logs:/var/log \ –security-opt no-new-privileges \
myapp:1.2.3
10. 성능·안정성 튜닝
- 리소스 제한: 서비스별 CPU/Memory 한도를 설정해 “한 컨테이너 독식” 방지
docker run -d –cpus=1.5 –memory=1g myapp
- ulimit: 파일 디스크립터/프로세스 수 상향
docker run -d –ulimit nofile=65536:65536 myapp
- 스케줄: GC, 백업, 배치 작업은 별도 컨테이너로 분리해 스파이크를 격리
- 이미지 최적화: 레이어 수/크기 축소로 배포 속도 향상, 콜드스타트 단축
11. 배포 전략: 무중단과 롤백
- Blue/Green: 동일 환경 두 세트를 번갈아 활성화. 빠른 롤백 용이
- Rolling Update: 인스턴스를 점진 교체. Compose에서는
--scale로 유사 구현 - 버전 고정: 태그 대신 다이제스트(
image@sha256:...)로 재현성 확보 - 롤백: 이전 태그/다이제스트로 즉시 재배포, DB 마이그레이션은 다운/업 스크립트 준비
12. 백업·복구와 데이터 보호
- 볼륨 스냅샷: 스토리지 스냅샷 또는 파일 레벨 백업(borg/restic/rsync)
- DB 컨테이너: 덤프 + WAL/Binlog 보존으로 시점 복구
- 3-2-1-1-0 원칙: 다른 매체/오프사이트/불변 스토리지(Object Lock) 포함
# named volume 백업(일시 정지 후)
docker run –rm -v appdata:/data -v $(pwd):/backup alpine \
tar czf /backup/appdata_$(date +%F).tgz /data“
13. CI/CD 파이프라인 연계
- CI: 테스트 → 이미지 빌드 → 보안 스캔 → 레지스트리 푸시
- CD: 스테이지드 배포(스테이징 검증 후 프로덕션), 배포 후 헬스체크/연기 기준
- 캐시 활용: 멀티스테이지/빌드킷(BuildKit)로 빌드 시간 단축 예시(GitHub Actions 요약):
– uses: docker/setup-buildx-action@v3
– uses: docker/login-action@v3
– uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/owner/app:1.2.3
14. 사설 레지스트리와 이미지 거버넌스
- 사설 레지스트리(Registry)로 내부 트래픽/속도/통제 강화
- 보존 정책: 오래된 태그 자동 정리, SBOM 저장, 취약점 스캔 의무화
- 접근 제어: 팀/프로젝트별 RBAC, 릴리스 승인 워크플로우
15. 운영 체크리스트(요약)
- 빌드: 멀티스테이지, 비루트, 버전 고정, 스캔 자동화
- 배포: compose 선언형, 리소스 제한, 헬스체크, 재시작 정책
- 네트워크: 서비스별 사용자 정의 네트워크, 포트 노출 최소화
- 데이터: 볼륨 분리, 읽기 전용 루트, 주기적 스냅샷/검증 복구
- 관측: 중앙 로그, 메트릭/알림, 재시작/오류율 대시보드
- 보안: no-new-privileges, 캡 능력 최소, 시크릿 외부화, 정기 패치
컨테이너 기반 운영의 목표는 “예측 가능하고 재현 가능한 배포”다. Dockerfile과 Compose로 실행 환경을 코드로 정의하고, 보안·관측·백업을 표준화하면 환경 차이로 인한 장애를 줄이고 출시 속도를 높일 수 있다. 초기에는 단일 호스트+Compose로 시작하되, 수평 확장이 필요해지면 오케스트레이션(Kubernetes, Swarm)으로 자연스럽게 전환하는 로드맵을 갖추는 것이 장기적으로 안전하다.