TL;DR
- 경량화 된 이미지를 활용해라
.dockerignore
파일을 활용해라- build를 위한 컨테이너와 배포를 위한 컨테이너를 분리해라
컨테이너를 쓰자
소프트웨어 컨테이너는 이미 사용하지 않는 곳을 찾기 힘들 정도로 업계에서 널리 사용되고 있습니다.
아직 실무에서는 사용하지 못했던차에 sakd.uk엔 본격적으로 도입하기로 했습니다.
사용해야 할 이유는 많습니다.
각각의 소프트웨어가 컨테이너에 즉시 실행 가능한 형태로 담겨있기 때문에 배포 과정이 간편화 됩니다.
배포 과정이 간단하기 때문에 필요할 때 자동으로 Scale out 되도록 구성할 수도 있죠.
또 소프트웨어가 각자 컨테이너 안에 격리되어 있기 때문에 한 서버에서 여러 소프트웨어를 구동하더라도 간섭을 방지할 수 있습니다.
만약 한 소프트웨어는 Python2를 사용하고, 다른 소프트웨어가 Python3를 사용한다고 해도 걱정할 필요가 없습니다.
아무튼 sakd.uk은 각각의 서버를 Docker 이미지로 만들기로 했습니다.
첫 이미지를 배포해보자
Docker는 어렵게 생각할 것 없이 리눅스 PC를 CD로 구워서 서버에 배포한다고 생각하면 이해가 쉽습니다.
로컬에 Docker를 설치하고 실행해보면 성공적으로 페이지가 표시되는 것을 볼 수 있습니다.
하지만 몇 가지 마음에 걸립니다.
빌드할 때마다 리눅스를 다운로드 받으니 시간이 오래 걸리고, 만들어진 이미지의 용량도 너무 큽니다.
아래는 이 때 사용한 Dockerfile입니다.
### Dockerfile ###
# Node.js 이미지 사용
FROM node:latest
# /app 디렉토리를 작업 디렉토리로 설정
WORKDIR /app
# 의존성 파일 복사 및 의존성 패키지 설치
COPY package*.json ./
RUN npm install
# Dockerfile이 위치한 디렉토리의 모든 파일 복사
COPY . .
# 실행
RUN npm run build
COPY /app/dist ./dist
RUN npm install -g http-server
EXPOSE 8080
CMD ["http-server", "--proxy", "http://localhost:8080?", "dist"]
불필요한 파일을 덜어보자
만들어진 이미지는 납품 전 최종본입니다.
굳이 개발 환경과 관련된 이미지를 포함할 필요가 없죠.
.dockerignore
파일을 생성하고, 굳이 배포할 필요가 없는 파일을 명시합니다.
특히 node.js 관련 App은 node_modules 디렉토리만 정리해도 꽤 의미있는 결과를 얻을 수 있습니다.
아래는 .dockerignore
파일의 예시입니다.
node_modules
coverage
*.log*
.nuxt
.nitro
.cache
.output
.env
dist
.DS_Store
.idea
.eslintcache
api-generator/typedoc.json
**/.DS_Store
베이스 이미지를 변경하자
Ubuntu를 비롯한 잘 알려진 배포판들은 이용 편의를 위해 다양한 도구를 기본 탑재하고 있습니다.
이런 도구들은 프로그램을 개발하거나 시스템을 운용할 때에는 편리하지만, Docker와 같은 가상화 환경에서는 필요가 적습니다.
다행히 많은 베이스 이미지들은 이런 경우를 위해 경량화 이미지를 제공합니다.
만약 일반 리눅스 이미지를 베이스 이미지로 사용하고 있었다면 베이스 이미지를 변경하는 것만으로도 아주 큰 효과를 얻을 수 있습니다.
멀티 스테이지를 활용하자
Docker는 병렬 처리와 build 처리를 위해 멀티 스테이지를 제공합니다.
build 단계에서는 node 이미지를 다운로드 받아 build를 수행하고,
배포용 이미지는 nginx를 다운로드 받아 build 단계에서 생성한 정적 파일만 복사하여 생성할 수 있죠.
Docker는 Dockerfile의 마지막 스테이지만 이미지로 생성합니다.
이를 통해 빌드에만 필요한 빌드 도구는 배포용 이미지에 포함시키지 않아 이미지의 용량을 줄일 수 있습니다.
결론
아래는 최종 Dockerfile입니다.
### Dockerfile ###
##################
# Stage 1: Build #
##################
# Node.js 이미지 사용
FROM node:lts-alpine AS builder
# /app 디렉토리를 작업 디렉토리로 설정
WORKDIR /app
# 의존성 파일 복사 및 의존성 패키지 설치
COPY package*.json ./
RUN npm install
# Dockerfile이 위치한 디렉토리의 모든 파일 복사
COPY . .
# 실행
RUN npm run build
####################
# Stage 2: Runtime #
####################
FROM node:slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
RUN npm install -g http-server
EXPOSE 8080
CMD ["http-server", "--proxy", "http://localhost:8080?", "dist"]
최초 이미지에 비해 80% 이상의 용량 절감 효과를 얻었고, 이미지 생성에 드는 시간도 65% 이상 단축되었습니다.
참고 문서
- Dockerfile Multi Stage - 악분의 블로그 (https://malwareanalysis.tistory.com/417)
- [Docker] Dockerfile - Multi-stage build(멀티스테이지 빌드 - 김징어의 Devlog - (https://kimjingo.tistory.com/63)