Docker 101 - 2장: Docker 한번 해봅시다!

okkkkkky·2023년 8월 6일
0

Docker의 기본적인 개념들에 대해 이전 게시글에서 알아봤다면, 이제 실전편입니다. 직접 Dockerfile, Docker image, Docker container를 만들어보는 내용을 담았습니다. 이 역시 원티드 챌린지 2023년 8월 강의의 내용을 같이 포함하고 있습니다.

어플리케이션을 컨테이너화 시켜봅시다.

무엇이든 공식 문서대로 직접 실습해보는게 제일 좋겠죠 ? 우선, 기본기를 다지기 위해 공식문서에서 다루고 있는 나의 어플리케이션을 컨테이너화 시키는 과정을 차근차근 따라하며, 각각의 명령어가 어떤 의미를 가지고 있는지 확인해봅시다.

1. 임시 어플리케이션 소스코드를 받습니다.

Docker 입문자를 위해, 손수 임시 소스코드를 준비해주었네요 (만세-!) 아래의 git repository 주소를 clone해줍니다.

 git clone https://github.com/docker/getting-started-app.git

2. 어플리케이션의 컨테이너 이미지를 만듭니다.

이미지를 만들기 위해서는 Dockerfile이 필요합니다. Dockerfile은 텍스트로 구성된 것으로 이미지를 구성하기 위한 사용설명서라고 생각하면 쉽습니다. 이 설명서에 따라 Docker는 이미지를 만들 수 있습니다.

1) Dockerfile 만들기

위에서 clone한 directory에서, package.json 파일과 동일한 위치에 Dockerfile이라는 이름을 가진 파일을 만들어줍니다. 확장자는 없이, 파일명을 Dockerfile로 지정만 해주면 됩니다.

touch Dockerfile

Dockerfile 생성

2) Dockerfile에 내용 채우기

새로 생성된 Dockerfile에 아래의 내용을 적어줍니다. 자, 우선 적기만 하지말고 각각의 내용들이 무엇을 의미하는지 짚고 넘어가봅시다.

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000

FROM

FROM 명령어는 빌드 단계를 새롭게 시작할 때, 초기화하고 이어지는 명령들에 대한 기본 이미지를 설정합니다. 보통은 공용 레포지토리에서 image를 pull받아와 시작합니다. 위의 예시에서는 이 프로젝트가 node.js기반으로 만들어지는 어플리케이션이기 때문에 node:18-alpine(18은 node.js의 버전, alpine은 리눅스 버전 중 최소한의 단위를 의미합니다)이라는 공용 이미지로부터 시작하게 된다는 것을 의미합니다. FROM 명령어는 "초기화"의 기능도 하기 때문에, FROM 이전에 명시되었던 명령어들은 모두 지워질 수 있습니다.

WORKDIR

WORKDIR 명령어는 컨테이너가 어떤 디렉토리에서 작업을 진행할 것인지 경로를 지정해주는 것입니다. 즉, 작업 디렉토리를 설정해줍니다. 아래와 같이 WORKDIR를 여러번 사용할 수 있고, pwd 즉, 현재 경로를 확인해보면, /a/b/c로 나타납니다. 쉘 명령어의 cd와 같은 기능을 한다고 볼 수 있겠네요 !

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

COPY

COPY 명령어는 호스트 머신에 있는 파일이나 디렉토리를 Docker 이미지로 복사하기 위해 사용됩니다. 위의 . .은 모든 것을 다 복사해서 넣는다의 의미로 받아들일 수 있습니다.

RUN

이미지를 만들기 위해 실행되는 것으로, 기본적으로 RUN 명령어는 두 가지 형식으로 사용될 수 있습니다.

RUN <command>
RUN ["executable", "param1", "param2"]

위의 예시에서는 "command"를 사용하는 방법으로 명시되어있는데요, 이는 보통 dependency를 설치할 때 자주 사용됩니다. 프로젝트를 clone하여 처음 실행할때 npm install을 터미널창에 입력하는 것처럼, RUN yarn install --production을 통해 우리가 직접 터미널에 입력하는 것을 Docker가 대신해준다고 생각하면 될 것 같습니다. Docker 공식문서에서 나와있듯, 아래의 명령어들은 같은 기능을 합니다.

RUN /bin/bash -c 'source $HOME/.bashrc && echo $HOME'
RUN ["/bin/bash", "-c", "echo hello"]

CMD

CMD 명령어는 기본적으로 세 가지 형식으로 사용될 수 있습니다.

CMD ["executable","param1","param2"] (exec form, 권장하는 형식)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)

CMD는 Dockerfile 내부에서 단 한번만 사용될 수 있습니다. 여러번 사용되었다면, 가장 마지막의 CMD 명령어만 실행됩니다. 이 명령어는 컨테이너를 실행시키는데 기본 설정값을 제공하기 위해 사용됩니다. ENTRYPOINT가 먼저 설정이 되어있다면, executable이 생략되고 뒤의 param들만 선언해도 되는 것이죠

CMD ["node", "index.js"]

ENTRYPOINT ["node"]
CMD["index.js"]

특정 arg과 함께 (나중에 알아볼) docker run을 실행한다면, CMD에 선언된 기본 설정값은 덮어씌워지게 되어 실행되지 않습니다.
여기서 포인트는 RUN 명령어CMD 명령어를 헷갈리면 안됩니다 !! RUN은 실제로 명령을 실행하고, 결과가 반영되며 여러번 실행할 수 있지만, CMD는 Dockerfile 내에서 한번만 실행되고, 빌드 타임에서 실행되지 않지만, 이미지의 기본 설정값에 대해 지정하는 역할이 더 크게 작용합니다.

(나오지는 않았지만) ENTRYPOINT

ENTRYPOINT 명령어 역시 두 가지 형식으로 사용됩니다.

ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2

이 명령어는 사실상 이미지를 실행시켜 컨테이너를 구성하는 역할을 합니다. yarn start나 npm start를 의미한다고 보면 좋겠죠 ?

EXPOSE

마지막으로 EXPOSE 명령어는 아래의 형식을 가집니다.

EXPOSE <port> [<port>/<protocol>...]

EXPOSE 명령어는 사실, 꼭 있어야 하는 명령어는 아닙니다. docker run 과 관련된 커맨드 명령어를 통해 실행할 수 있는 부분이기도 합니다. 다만, EXPOSE에 대해서 이해하기 위해서는 아래의 이미지에 대해 조금 이해할 필요가 있습니다.

생활코딩-네트워크

(위의 이미지에서는 이미 연결되어있지만) 호스트 머신과 컨테이너의 사이에 연결고리를 만들어주어야 합니다. 위의 이미지에서 8000번 포트를 이용하여 url에 접속하는 경우, 컨테이너의 80번 포트와 연결되지 않으면 해당 페이지는 화면에 나타나지 않습니다. 이를 연결하기 위해 포트번호를 서로 연결해주어야 하고, 이 역할을 수행해주는 것이 EXPOSE 명령어입니다.

EXPOSE 뒤에 오는 숫자 인자는 컨테이너의 포트를 의미합니다. 겉으로 드러나는 호스트 머신의 포트가 아니며, 이를 지정하지 않는 경우 임의로 호스트 머신의 포트가 지정됩니다. 호스트 머신의 포트도 지정하고자 한다면, 위의 이미지처럼 docker run -p <host.port>:<container.port> 이미지명 로 직접 지정해줄 수 있습니다.

결론

그렇다면 다시 Dockerfile에 적은 명령어의 의미를 살펴볼까요 ?

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000
  • 리눅스 alpine에서 18버전의 node 기반으로
  • /app 디렉토리로 작업디렉토리를 설정하고
  • 해당 디렉토리의 모든 파일을 복사합니다.
  • yarn install --production을 실행하여 dependency를 모두 설치하고
  • node src/index.js을 통해 어플리케이션을 실행시킵니다.
  • 컨테이너 포트는 3000으로 지정합니다

3) 위의 명령어를 기반으로 Docker 이미지를 생성하기

위의 이미지를 기반으로 이미지를 만들어봅시다.

docker build -t getting-started .

위의 커맨드 명령어는 Dockerfile에 명시되어있던 명령어들을 하나씩 실행시킵니다. build는 Docker image를 만드는 것이죠. -t는 이 이미지에 getting-started라는 이름(tag)을 붙여줍니다. 컨테이너가 이 이미지를 참조해야 하는 경우가 생길 때, 이 태그를 참고할 것입니다. 마지막의 .은 Docker에게 현재 디렉토리 즉 /app에 있는 Dockerfile을 찾으라는 것입니다. Dockerfile을 찾을 위치를 지정해주는 것이겠네요 !

실행하면 짜잔, 아래와 같이 이미지가 만들어집니다.
Docker 이미지 만들기

3. 어플리케이션의 컨테이너를 띄웁니다.

1장에서 말했던 것처럼, 이미지가 만들어지면 이를 토대로 컨테이너를 시작할 수 있습니다 ! 이 컨테이너 안에서 git clone을 받았던 임시 프로젝트를 실행해보는 과정입니다. 우선, docker run 커맨드를 사용해야합니다.

docker run -dp 3000:3000 getting-started

다시 한번 상기시키자면, docker build는 이미지를 만들고, docker run은 컨테이너를 실행시킵니다. 뒤에 붙는 -dp는 어떤 옵션일까요?

-d: detach

이것은 컨테이너가 백그라운드에서도 계속 실행되게끔하는 옵션입니다.

-p: publish

아까 위에서 봤던 Dockerfile 내부의 명령어인 EXPOSE와 비교하며 말했던 것 기억하시나요 ? 다시 한번 짚어보자면, 3000:3000은 "호스트머신의 포트:컨테이너의 포트"를 의미합니다. 단순히 호스트 머신의 포트만 지정하면 컨테이너와 연결되지 않아 어플리케이션은 서버와 연결될 수 없겠죠. 컨테이너 포트까지 연결을 시켜줘야 비로소 어플리케이션은 구동이 가능합니다. 여기서는 3000포트, 3000포트 동일한 값으로 지정해주었지만, 다르게 지정해도 괜찮습니다.

위의 모든 과정들을 거치고 localhost:3000으로 들어가봅시다 !
완성된 화면

이로써 간단하게 Dockerfile > Docker 이미지 > Docker 컨테이너 띄우기까지 모두 진행해보았습니다. 정말 기본의 기본만 진행한 느낌이죠 ? (갈길이 머네요..) 다음 장에서는 Docker CLI에 대해서 조금 더 살펴보는 시간을 가져보겠습니다 !

참고
https://docs.docker.com/engine/reference/builder/
https://www.youtube.com/watch?v=LXJhA3VWXFA
https://www.daleseo.com/dockerfile/
https://junhyunny.github.io/information/docker/docker-file-expose-instruction/

0개의 댓글