본문 바로가기
DevOps/Docker

만든 앱을 어떻게 도커를 활용하여 사용해야할까?

by oncerun 2023. 5. 31.
반응형

 

서버마다 독립적인 컨테이너로 애플리케이션을 배포하고 구동하는 방법이 필요할 것 같다.

기존에 서버 배포방식을 보면 클라우드 서버를 구매하고, 그 안에 애플리케이션의 필요한 환경을 구성하고, 필요한 프로그램을 다운로드하고 이를 관리하는데 시간도 오래 걸리기 때문에 정해진 이미지를 도커 허브에 구성해 놓고 필요에 따라 이를 빌드하여 사용하는 것이 더욱 효율적인 것 같다.

 

분명 이로 인해 특정 부분의 트레드 오프가 발생하지만 지금은 이점밖에 보이지 않아서 관련 자료를 더 찾아보아야 할 것 같다.

 

https://nodejs.org/ko/docs/guides/nodejs-docker-webapp

 

Node.js 웹 앱의 도커라이징 | Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

다음 Node.js 앱을 도커라이징하는 문서가 있기 때문에 이를 통해 어떤식으로 진행되는지 확인해보려고 한다.

개발 환경에는 docker, node.js (npm)이 설치가 되어 있어야 한다.

 

우선 Node.js 앱을 생성할 것이다.

 

npm init 명령어로 package.json 파일을 만드는데, 이는 프로젝트에 대한 종속성, 설명, 버전 등 다양한 설정 정보가 들어간

다. 나는 파일을 애플리케이션 메타 데이터 파일이라고 생각했다.

 

실제 이 파일을 기준으로 npm install이라는 명령어를 실행하면 node.js는 해당 dependencies 기준으로 필요한 종속성을 다운로드한다고 한다.

 

가장 쉽게 웹앱을 만드는 것은 express 모듈을 사용하여 백엔드를 구성하는 것이다.

 

'use strict';

const express = require('express');

// 상수
const PORT = 8080;
const HOST = '0.0.0.0';

// 앱
const app = express();
app.get('/', (req, res) => {
  res.send('Hello World');
});

app.listen(PORT, HOST, () => {
  console.log(`Running on http://${HOST}:${PORT}`);
});

 

이제 Node.js 웹앱을 만들었다면 이를 도커 이미지로 만들고 이를 빌드해야 한다.

 

이를 위해선 Dockerfile을 정의해야한다. 

 

 

도커파일을 정의할 때 우리는 베이스 이미지가 필요하다. 레이어의 가장 첫 부분으로 중심이 되는 이미지라고 생각하면 될 것 같다.

 

우리는 npm이 포함된 베이스이미지를 찾아야하고 이는 docker의 node 이미지를 사용할 수 있다.

 

FROM node:18

 

다음은 이미지 안에 애플리케이션 코드를 넣기 위해 디렉터리를 생성할 것이고 이 디렉터리가 애플리케이션의 작업 디렉터리가 된다.

 

왜 따로 working 디렉토리가 있어야 할까? 

 

만약 별도의 working 디렉토리를 정의해주지 않으면 베이스 이미지의 파일 스냅숏 위치에 우리의 애플리케이션 코드 파일들이 같은 위치에 copy 되어 생성된다.

다음 위치에 우리의 웹앱 파일들이 들어갈 것이다.

 

이때 발생되는 문제는 베이스 이미지에 이미 존재하는 파일, 폴더의 이름과 새로 추가되는 파일, 폴더의 이름이 같은 경우

중복으로 처리되어 원래 존재하는 폴더가 덮어씌어져 버린다. 

 

이것뿐만이 아니라 모든 파일이 하나의 폴더안에 위치하기 때문에 유지보수 측면적으로도 좋지 않다. 

 

그래서 어플리케이션을 위한 소스는 별도의 작업 디렉터리를 따로 만들어 보관해야 한다.

 

이를 위한 Dockerfile 정의는 다음과 같다.

WORKDIR /usr/src/app

 

이 다음은 copy 하는 단계이다. 

 

현재 웹앱 모든 파일을 카피해도 된다. 

 

하지만 개발과정에서 사용하는 모듈, 개발 로그 파일까지는 복사할 필요 없기 때문에. dockerignore 파일을 다음과 같이 정의한다.

node_modules
npm-debug.log


.dockerignore 파일

 

이후 다음과 같이 Dockerfile에 COPY 명령어를 작성해준다.

 

COPY ./ ./

 

모든 파일을 복사했다면 이를 실행해야 한다.

 

RUN npm install

 

이제 우리는 포트를 매핑해야 합니다.

 

Docker 컨테이너 내에서 실행 중인 애플리케이션은 기본적으로 격리되어 외부에서 접근할 수 없습니다. 이때, `docker run` 명령어를 사용하여 컨테이너를 실행할 때 `-p` 또는 `--publish` 플래그를 사용하여 호스트와 컨테이너 간의 포트 매핑을 정의할 수 있습니다. 

포트 매핑을 정의하는 방법은 다음과 같습니다:

docker run -p <호스트 포트>:<컨테이너 포트> 이미지명


여기서 `<호스트 포트>`는 외부에서 컨테이너로 접근할 때 사용할 호스트의 포트 번호이고, `<컨테이너 포트>`는 컨테이너 내부에서 실행 중인 애플리케이션이 사용하는 포트 번호입니다.

예를 들어, 다음 명령어를 사용하여 호스트의 8080 포트와 컨테이너의 80 포트를 매핑할 수 있습니다:

docker run -p 8080:80 이미지명



이렇게 하면 호스트에서 `localhost:8080`으로 접근하면 해당 컨테이너의 80 포트로 트래픽이 전달됩니다. 컨테이너 내부의 애플리케이션은 80 포트에서 실행 중이므로 요청을 처리할 수 있습니다.

`docker run` 명령어를 사용하는 대신 `docker-compose`를 사용하여 여러 개의 컨테이너를 정의하고 관리할 수도 있습니다. `docker-compose.yml` 파일에서 포트 매핑을 정의할 수 있으며, `ports` 섹션을 사용하여 호스트와 컨테이너 간의 포트 매핑을 설정할 수 있습니다.

version: '3'
services:
  myapp:
    image: 이미지명
    ports:
      - 8080:80



위의 예제에서는 `myapp` 서비스를 정의하고, 호스트의 8080 포트와 컨테이너의 80 포트를 매핑하도록 설정했습니다.
포트 매핑을 사용하여 Docker 컨테이너 내부의 애플리케이션을 호스트나 외부에서 접근할 수 있도록 만들 수 있습니다.

 

 

 

 

그러면 EXPOSE는 무엇일까?

 

https://docs.docker.com/engine/reference/builder/#expose

 

Dockerfile reference

 

docs.docker.com

 

런타임에 지정된 포트에서 리스닝하고 있다는 것을 도커에게 알리는 옵션입니다. 

TCP, UDP와 같이 프로토콜도 정의할 수 있으며 기본은 TCP 값을 가진다고 합니다. 

 

추가적으로 EXPOSE 명령은 실제로 런타임에 포트를 개방하지 않는다고 합니다. 그렇기에 Dockerfile에 명시된 EXPOSE <port>를 적는다는 것은 이미지 빌드하는 사람과 컨테이너를 실행하는 사람 사이에 어떤 포트를 게시할 것인지에 대한 일종의 문서역할이라고 합니다. 

 

저는 8080 포트로 내부 포트를 사용하고 호스트 포트는 5000으로 고정되어서 사용할 것이기 때문에 다음과 같이 적어주고 실제 컨테이너 실행 시 포트를 매핑하려고 합니다.

 

EXPOSE 8080

 

다음은 CMD 명령어인데, 이는 앱을 실행하는 주요 명령어를 정의합니다. 

 

여기서는 서버를 구동하도록 작성합니다.

 

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

 

CMD에는 3가지 포맷이 있다고 한다. 

 

  • CMD ["executable", "param1", "param2"](exec form, this is the preferred form)
  • CMD ["param1", "param2"](as default parameters to ENTRYPOINT)
  • CMD command param1 param2 (shell form)

CMD는 반드시 단 하나의 명령어만 있어야 하고 마지막 CMD가 적용된다고 한다. 

 

SH 없이 실행하려면 명령을 JSON 배열로 표현하고 실행 파일의 전체 경로를 제공해야 한다. 

 

RUN과 CMD를 혼동하지 마세요. RUN은 실제로 명령을 실행하고 결과를 커밋하지만, CMD는 빌드 시 아무것도 실행하지 않지만 이미지에 대해 의도한 명령을 지정합니다.

 

CMD로 지정한 내용 대신 컨테이너 실행 시 받은 인자로 대체하여 실행될 수 있다고 한다.

 

그렇기에 이는 npm start 형식을 띠고 있고 실행 시 node server.js를 통해 웹앱을 실행하도록 정해놓은 것이었다. 

 

https://bluese05.tistory.com/77

 

Dockerfile Entrypoint 와 CMD의 올바른 사용 방법

ENTRYPOINT 와 CMD 는 무엇인가 ENTRYPOINT 와 CMD는 해당 컨테이너가 수행하게 될 실행 명령을 정의하는 선언문이다. 즉, 컨테이너가 무슨 일을 하는지 결정하는 최종 단계를 정의하는 명령이라고 생각

bluese05.tistory.com

 

이미지를 빌드하고 실제로 실행시켜 보자.

 

docker build . -t oncerun/node-web-app

docker images 

docker run -p 5000:8080 -d oncerun/node-web-app

 

호스트 5000 포트에 컨테이너 포트인 8080 매핑하고 이미지를 실행시켰다. 

 

 

docker logs 3d5
Running on http://0.0.0.0:8080

 

실제  http://localhost:5000/ 요청 시 응답이 잘되는 것을 확인할 수 있다.

반응형

'DevOps > Docker' 카테고리의 다른 글

docker 사용해보기  (0) 2023.05.30
window 10 wsl2 도커 완벽 설치가이드  (0) 2022.12.25
Docker 공식 이미지 가져오기  (0) 2021.02.20
Dockerfile부터 push까지  (0) 2021.02.20
Docker 시작  (0) 2021.02.20

댓글