그동안 잘 써왔던 AWS 프리티어가 이번달부로 종료되었다. 현재 EC2 인스턴스위에 올라가 있는 서비스를 종료하던지 Lightsail
, Vultr
와 같은 가상서버 호스팅 서비스(VPS)로 서비스를 마이그레이션하던지 해야했다.
결론은 나 혼자 쓰는 서비스지만 평소에 잘 쓰고 있었기 때문에 한달에 5천원 정도의 비용을 들여 Vultr
로 서비스를 옮기기로 했다.
처음 EC2에 서비스를 배포했을때 환경 세팅 때문에 고생한 경험이 있었기 때문에 도커를 이용해 배포를 진행하기로 했다.
도커는 애플리케이션을 신속하게 구축, 테스트 및 배포할 수 있는 소프트웨어 플랫폼이다. 소프트웨어를 컨테이너라는 표준화된 유닛으로 패키징하며, 컨테이너는 라이브러리, 시스템 도구, 코드 등 소프트웨어 실행에 필요한 모든 것이 포함되어 있다. 즉, 도커는 컨테이너 환경에서 독립적으로 애플리케이션을 실행할 수 있도록 컨테이너를 만들고 관리하는 것을 도와주는 도구이다. 도커를 통해 애플리케이션을 실행하면 독립적인 환경에서 일관된 결과를 보장한다.
도커는 컨테이너 기술을 사용하여 프로세스를 호스트OS와 독립된 격리된 컨테이너 환경에서 실행할 수 있게 해주는 운영체제 수준의 가상화 기술이다. 초기에는 컨테이너 구현에 리눅스 컨테이너(LXC)를 사용했지만 현재는 자체 컨테이너 기술을 사용한다. 자세한 내용은 이 포스트을 참조
도커는 호스트OS와 격리된 환경을 만들어주기 때문에 로컬 개발 환경을 도커 컨테이너로 구축한다 배포 환경과 개발 환경이 동일해진다. 즉, 같은 컨테이너 환경에서 동작하기 때문에 로컬에서 제대로 동작한다면 배포 환경에서도 제대로 동작함을 보장할 수 있다는 것이다.
EC2 서버에 배포할 때마다 로컬 개발 환경과 달라 항상 제대로 동작하지 않았던 경험이 있었는데 이런 짜증나고 귀찮은 점을 해결할 수 있다.
분명 도커와 같은 컨테이너 기술은 가상 머신과 달리 게스트 OS가 없고 호스트 OS의 리눅스 커널을 공유한다고 했다. 그런데 왜 컨테이너 내에서도 OS가 포함되는 것일까? 그 이유는 리눅스에 대한 이해가 필요하다.
흔히 우리가 아는 Ubuntu
, CentOS
와 같은 리눅스 운영체제를 리눅스 배포판
이다.
리눅스 배포판은 리눅스 커널
+ 자유소프트웨어(GNU 소프트웨어 등)
로 구성된다. 즉 리눅스 배포판 버전에 따라에 리눅스 커널의 버전은 다를 수 있으나 리눅스 커널을 동일하게 사용한다.
컨테이너 내에 OS를 세팅하는 것은 해당 배포판과 같은 환경을 제공하는 것이지 OS를 구동하는 핵심인 리눅스 커널은 호스트 OS의 커널을 사용한다는 것이다.
더 자세한 내용은 아래 포스트들을 참조
도커에서 서비스 운영에 필요한 서버 프로그램, 소스코드 및 라이브러리, 컴파일된 실행 파일을 묶는 형태를 이미지라한다. 다시 말해, 컨테이너를 실행하기 위한 모든 파일과 설정값(환경)을 지닌 것으로, 더 이상의 의존성 파일을 컴파일하거나 이것저것 설치할 필요 없는 상태의 파일을 의미한다. 예를 들어 Ubuntu이미지는 Ubuntu를 실행하기 위한 모든 파일을 가지고 있으며, Oracle 이미지는 Oracle을 실행하는데 필요한 파일과 실행명령어, port 정보 등을 모두 가지고 있다.
이미지는 읽기 전용이며 이미지로 만든 컨테이너의 내부가 변하더라도 변경되지 않는다.
이미지를 실행한 상태로, 응용프로그램의 종속성과 함께 응용프로그램 자체를 패키징 or 캡슐화하여 격리된 공간에서 프로세스를 동작시키는 기술이다.
컨테이너는 호스트의 자원을 공유하며 컨테이너를 삭제할 경우 컨테이너 내에서 변경된 모든 사항은 삭제된다.
컨테이너를 만들기 위한 이미지를 만들기 위해서는 Ubuntu
, CentOS
같은 베이스 이미지를 도커 허브에서 받아 컨테이너를 실행해 컨테이너 내에서 환경을 셋업한 후 docker commit
명령어로 이미지를 만드는 방법도 있지만 직접 컨테이너 터미널에서 작업하는 건 불편하고 문제가 생길 수 도 있다. 그렇기 때문에 이미지를 빌드하는 일종의 스크립트인 Dockerfile
을 만들어 이미지를 만든다.
CMD와 ENTRYPOINT의 차이
CMD와 ENTRYPOINT 모두 컨테이너 실행시 수행하는 명령어이지만 CMD는 덮어씌워질 수 있고 ENTRYPOINT는 덮어씌워지지 않고 항상 실행된다는 차이가 있다.
예를 들어 docker run 명령어로 컨테이너를 실행할 때 도커 이미지 뒤에 커맨드를 입력하면 CMD는 실행되지 않고 docker run에 쓰인 커맨드가 실행된다.
이와 다르게 ENTRYPOINT로 작성된 명령어는 docker run에 커맨드를 입력하더라도 실행되고 그 후에 docker run으로 전달된 커맨드가 실행된다.컨테이너 실행 후 항상 실행되어야 하는 커맨드라면 ENTRYPOINT를 사용하고 그렇지 않다면 CMD를 사용하면 된다.
FROM node:16-alpine
# Korean Fonts
RUN apk --update add fontconfig
RUN mkdir -p /usr/share/fonts/nanumfont
RUN wget http://cdn.naver.com/naver/NanumFont/fontfiles/NanumFont_TTF_ALL.zip
RUN unzip NanumFont_TTF_ALL.zip -d /usr/share/fonts/nanumfont
RUN fc-cache -f && rm -rf /var/cache/*
# update pkg manager
RUN apk update
# bash install
RUN apk add bash
# Language
ENV LANG=ko_KR.UTF-8 \
LANGUAGE=ko_KR.UTF-8
# Set the timezone in docker
RUN apk --no-cache add tzdata && \
cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
echo "Asia/Seoul" > /etc/timezone
# Create Directory for the Container
WORKDIR /user/src/app
# add chromium
RUN apk add chromium
# Only copy the package.json file to work directory
COPY package.json .
RUN npm install
RUN npm install -g pm2
# Docker Demon Port Mapping
EXPOSE 80
# RUN SERVER
CMD ["npm", "run", "docker"]
지금까지 도커의 기본적인 부분을 알아보았는데 다음 포스트에는 도커를 이용해 가상 서버에 배포하는 과정을 다뤄보도록 하겠습니다