Docker를 이용한 컨테이너화
컨테이너화는 애플리케이션과 그 의존성을 하나의 독립적인 유닛으로 패키징하는 기술입니다.
Docker를 사용한 NestJS 애플리케이션의 컨테이너화는 일관된 개발 및 배포 환경을 제공하고, 확장성과 이식성을 향상시킵니다.
컨테이너화의 이점
- 환경 일관성 : 개발, 테스트, 프로덕션 환경의 차이 최소화
- 빠른 배포 : 컨테이너 이미지를 통한 신속한 애플리케이션 배포
- 격리 : 애플리케이션 간 리소스와 의존성 충돌 방지
- 확장성 : 손쉬운 수평적 확장 가능
- 리소스 효율성 : 가상 머신에 비해 적은 리소스 사용
NestJS 애플리케이션 Dockerfile 작성
기본 Dockerfile
FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "start:prod"]
최적화된 Dockerfile (멀티 스테이지 빌드)
# 빌드 스테이지
FROM node:14 AS build
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 프로덕션 스테이지
FROM node:14-alpine
WORKDIR /usr/src/app
COPY --from=build /usr/src/app/dist ./dist
COPY --from=build /usr/src/app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/main"]
이 멀티 스테이지 빌드 방식은 최종 이미지 크기를 크게 줄이고 보안을 향상시킵니다.
Docker Compose를 사용한 서비스 구성
docker-compose.yml 예시
version: '3'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://user:pass@db:5432/dbname
depends_on:
- db
db:
image: postgres:13
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=dbname
redis:
image: redis:6
이 설정으로 NestJS 애플리케이션, PostgreSQL 데이터베이스, Redis 캐시를 함께 실행할 수 있습니다.
환경 변수 관리
- .env 파일 사용
DATABASE_URL=postgres://user:pass@localhost:5432/dbname
REDIS_URL=redis://localhost:6379
- Docker 실행 시 환경 변수 주입
docker run -e NODE_ENV=production -e DATABASE_URL=postgres://user:pass@db:5432/dbname my-nestjs-app
- NestJS ConfigModule을 사용한 환경 변수 관리
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV}`,
}),
],
})
export class AppModule {}
환경별 Docker 이미지 관리
태그 지정 전략
- 개발 :
myapp:dev-<commit-hash>
- 테스트 :
myapp:test-<version>
- 프로덕션 :
myapp:<version>
예시 빌드 및 푸시 스크립트
#!/bin/bash
VERSION=$(node -p "require('./package.json').version")
ENV=$1
docker build -t myapp:$ENV-$VERSION .
docker push myapp:$ENV-$VERSION
Docker 네트워크 구성
마이크로서비스 간 통신을 위한 네트워크 설정
version: '3'
networks:
myapp-network:
driver: bridge
services:
service1:
build: ./service1
networks:
- myapp-network
service2:
build: ./service2
networks:
- myapp-network
로그 관리와 모니터링
- Docker 로그 드라이버 사용
services:
app:
logging:
driver: "json-file"
options:
max-size: "200k"
max-file: "10"
- ELK 스택 통합
services:
app:
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
fluentd:
image: fluent/fluentd
volumes:
- ./fluentd/conf:/fluentd/etc
ports:
- "24224:24224"
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.9.2
kibana:
image: docker.elastic.co/kibana/kibana:7.9.2
Best Practices 및 주의사항
- 경량 베이스 이미지 사용 : Alpine 리눅스 기반 이미지 선호
- 멀티 스테이지 빌드 활용 : 빌드 아티팩트만 최종 이미지에 포함
- 불필요한 파일 제외 : .dockerignore 파일 사용
- 레이어 최소화 : RUN 명령어 결합
- 비-루트 사용자로 실행 : 보안 강화
- 헬스 체크 구현 : HEALTHCHECK 명령 사용
- 시크릿 관리 : 환경 변수 또는 Docker secrets 사용
- 이미지 스캐닝 : 취약점 검사 도구 사용 (예 : Clair, Trivy)
- 캐시 활용 : 빌드 시간 단축을 위한 레이어 캐싱
- 문서화 : Dockerfile과 관련 스크립트에 주석 추가
Docker를 이용해 컨테이너화하는 것은 현대적인 애플리케이션 개발 및 배포 과정에서 중요한 부분입니다.
컨테이너화를 통해 개발 환경과 운영 환경의 일관성을 유지하고, 배포 프로세스를 간소화하며, 애플리케이션의 확장성을 높일 수 있습니다.
멀티 스테이지 빌드를 사용하면 최종 이미지의 크기를 줄이고 보안을 강화할 수 있습니다.
이는 특히 NestJS와 같은 TypeScript 기반 애플리케이션에서 중요한데, 빌드 과정에서 생성되는 불필요한 파일들을 제거할 수 있기 때문입니다.
Docker Compose를 활용하면 NestJS 애플리케이션과 관련 서비스(데이터베이스, 캐시 등)를 쉽게 구성하고 관리할 수 있습니다.
이는 개발 환경 설정을 단순화하고, 마이크로서비스 아키텍처를 쉽게 구현할 수 있게 해줍니다.
환경 변수 관리는 컨테이너화된 애플리케이션에서 중요한 부분입니다.
NestJS의 ConfigModule과 Docker의 환경 변수 주입 기능을 결합하여 유연하고 안전한 설정 관리가 가능합니다.
특히 다양한 환경(개발, 테스트, 프로덕션)에 따라 설정을 쉽게 변경할 수 있어 유용합니다.
Docker 이미지 관리 전략은 애플리케이션의 버전 관리와 밀접하게 연관됩니다.
적절한 태그 지정 전략을 사용하면 다양한 환경에서의 이미지 버전을 쉽게 추적하고 관리할 수 있습니다.
Docker 네트워크를 활용한 마이크로서비스 간 통신 설정은 복잡한 시스템 아키텍처를 효과적으로 구현하는 데 도움이 됩니다.
이를 통해 서비스 간의 격리와 안전한 통신을 보장할 수 있습니다.