Docker 간단한 node js 어플리케이션 실행하기

2022. 7. 10. 11:14☁️ Cloud

이번 포스트의 목표는 간단한 nodejs 어플리케이션을 docker 에서 실행해보는 것이다. 컨테이너 내부에서 어플리케이션이 실행되기 위한 도커의 원리를 위주로 알아보자. 

 

우선 NodeJs 앱을 도커 환경에서 실행하려면, 

  1. 이미지를 생성하고, 
  2. 그 이미지를 이용해 컨테이너를 실행 후 
  3. 컨테이너 내부에서 nodeJs 어플리케이션을 실행해야 한다. 

즉 이미지를 생성하기 위해선 Dockerfile 을 먼저 작성해야만 한다. 

 

디렉토리 구조

package.json

{
  "name": "nodejs-docker-app",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "start":"node server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express" : "^4.16.1"
  }
}

server.js

실행하고자 하는 nodejs 어플리케이션의 코드다. 

5000번 포트로 접속시 Hello world 문구를 띄우는게 전부인 간단한 어플리케이션이다. 이를 도커 환경에서 띄워보자. 

const express = require("express");

const PORT = 5000;

// APP
const app = express();
app.get("/", (req, res) => {
  res.send("Hello world");
});

app.listen(PORT);
console.log("server is running");

 

Dockerfile 작성하기

From node:10
COPY ./ ./
RUN npm install
CMD ["node", "server.js"]

FROM : 이미지 생성 시 기반이 되는 이미지 레이어 -> node:10 은 node 라는 이미지의 10 버전을 의미한다. 

COPY : Dockerfile 의 현재 디렉토리에는 server.js 와 package.json 이 존재한다. 이 두 파일도 container 내부에서 필요한데, COPY 명령어를 누락하면 해당 파일들이 도커 컨테이너에 존재하지 않기 때문에 RUN npm install 명령어가 제대로 수행되지 않는다.

RUN : 노드 어프리케이션을 실행시키는 명령어

CMD : 노드 웹 서버를 작동시키기 위해선 node + 엔트리 파일명 을 입력해야 한다. 

 

위와 같이 파일을 모두 작성 후 docker 이미지를 빌드하고 컨테이너를 실행해보자. 

docker build -t tbnsok/nodejs ./
docker run tbnsok/nodejs

 

이제 localhost:8080 을 브라우저에 입력하여 "Hello world" 가 출력되는지 확인해보자. 

안된다.

 

왜 예상대로 출력되지 않는걸까? 정답은 포트 매핑에 있다. 포트 매핑을 제대로 이해하지 못하면 개발 시 에러를 자주 마주하기에 이번 기회에 알아보자. 

 

포트 매핑

server.js 에서 PORT 변수엔 8080 값을 할당했다.

즉 도커 컨테이너 내부에서 실행된 nodejs 어플리케이션은 8080 포트에서 실행된다는 의미인데, 

클라이언트 브라우저에서는 도커 컨테이너 내부의 8080 포트로 접속하지 못한다. 즉 브라우저와 도커 컨테이너 포트 간의 매핑 작업이 필요하다. 

 

docker run -p 8080:8080 tbnsok/nodejs:latest

위 처럼 -p 옵션을 사용해 브라우저에서 접속하는 포트와 컨테이너 내부의 포트를 매핑시켜주면, 우리가 원하는대로 node 어플리케이션에 접속할 수 있다.

WORKDIR 설정

WORKDIR 는 working directory 를 의미하는데, Dockerfile 에 이 WORKDIR 를 명시해줘야 한다.

뭐 때문에 추가해야하는 걸까? 우선 WORKDIR 는 무엇을 의미할까?

바로 이미지 내부에서 nodeJs 애플리케이션 소스 코드를 가지고 있을 디렉터리를 생성하는 것이다. 

From node:10
WORKDIR /usr/src/app  # 추가된 부분
COPY ./ ./
RUN npm install
CMD ["node", "server.js"]

Dockerfile 에 WORKDIR 설정을 해주고 다시 빌드한 후 실행해보자. 

docker build -t tbnsok/nodejs ./

docker run -it tbnsok/nodejs sh

쉘 환경에서 ls 명령어로 디렉토리 구조를 살펴보면, 우리가 직접 생성한 server.js, Dockerfile, package.json 파일이 존재하는 것을 확인할 수 있다. 

만약 WORKDIR 경로를 설정하지 않았다면, 아래 이미지 처럼 루트 디렉토리에 우리가 생성할 파일이 모두 섞이게 된다. 

 

Dockerfile 에 아래처럼 /usr/src/app 경로를 설정해주었기에, 빌드시에 해당 경로로 어플리케이션 파일이 생성된다. 이렇게 하여 모든 파일이 루트 디렉토리에 모여있는 혼잡한 구조를 피하고, 짜임새 있는 디렉토리 구조를 만들 수 있다.

WORKDIR /usr/src/app

 

 

 

어플리케이션 소스 변경으로 재빌드시 효율적으로 하는 법

현재 빌드 구조는, 소스코드의 작은 부분을 수정하더라도 처음부터 종속성을 설치하고 빌드하기 때문에 비효율적이다. 

간단하게 출력문 한줄 바꿨음에도, package.json 의 종속성을 모두 재설치 해야한다.

이런 문제는 어떻게 해결할 수 있을까?

우선 소스 코드가 변경될 시 소스 코드는 당연히 재빌드 시켜야 하지만, 패키지 종속성은 변경이 없다면 굳이 재빌드 할 필요가 없다. 

# 변경 전
From node:10
WORKDIR /usr/src/app
COPY ./ ./
RUN npm install
CMD ["node", "server.js"]

# 변경 후
From node:10
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY ./ ./
CMD ["node", "server.js"]

변경 후의 Dockerfile 을 보자. 

npm install 이전에 COPY package.json ./ 명령어가 추가됐다.

이렇게 하여 변경사항이 server.js 의 소스에만 있다면, 불필요하게 모듈(package)을 다시 받지 않도록 했다. 

RUN npm install 이 수행되기 이전에 COPY 할 때는 오직 module 에 관한 것만 해주었다.

RUN npm install 이후에 다시 모든 파일들을 COPY 하는데, 이렇게 하면 모듈은 모듈에 변동사항이 있을 때만 다운로드 할 수 있게 되어, 불필요한 빌드과정을 배제할 수 있다.

 

Docker Volume

도커 볼륨은 무엇일까? 도커 볼륨이란 도커 컨테이너에서 호스트 디렉터리(로컬)에 있는 파일들을 매핑해서 사용하는 것을 의미한다. 

즉 도커 컨테이너에서 로컬에 있는 파일들을 참조하게 만들어, 소스 코드에 수정이 있을 때 즉각적으로 대응할 수 있게 된다. (빌드 과정이 불필요해지는 것)

docker run -d -p 5000:8080 -v /usr/src/app/node_modules -v ${pwd}:/usr/src/app tbnsok/nodejs

첫번째 -v 옵션에는 /usr/src/app/node_modules 를 입력한다. 로컬에는 node_modules 가 없기 때문에 배제하는 것이며, 

두번째 -v 옵션은 : 콜론을 사용해 현재경로(pwd)와 도커 컨테이너 내부의 경로(/usr/src/app) 을 매핑해준다.

 

이렇게 volume 을 이용해서 실행하면 빌드 시 소스가 변경되더라도, 바뀐 코드가 바로 적용된다.

 

본 포스팅은 인프런의 따라하며 배우는 도커와 CI 환경 강의를 수강하고 정리한 내용을 바탕으로 정리됐습니다.
사용된 도표와 코드는 해당 강의에서 참조했음을 밝힙니다.
 

따라하며 배우는 도커와 CI환경 - 인프런 | 강의

이 강의를 통해 도커에 대해서 배울 수 있으며, CI 환경을 구성할 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com