Docker-compose로 Node Express 환경 구축하기

Jeongkuk Seo
sjk5766
Published in
9 min readJun 17, 2020

본 포스팅에서는 Nginx, Expressdocker-composeexpress-generator를 활용하여 구축하도록 하겠습니다.

Nginx를 앞 단에 둔 이유는 두 가지가 있습니다. 첫 번째 이유보안인데 Client가 실제로 동작하는 Express에 접근할 경우 설정 파일이나 불 필요한 시스템 정보를 노출할 수 있습니다. 두 번째 이유Scale Out(확장) 입니다. 앞 단의 Nginx를 로드밸런서로 사용하고 뒷 단의 Express를 Scale Out할 수 있습니다.

이 글은 docker-compose를 알고 있고 Express를 환경을 구축하려는 분들에게 유용합니다. 만약 docker-compose에 대해 잘 모르신다면 이 글을 읽어주세요.

docker-compose.yml

Nginx, Express를 구축하기 위한 docker-compose.yml 입니다. 설명은 주석으로 대체했습니다. expressvolumes 부분에서 /source/node_modules 는 추후 Dockerfile을 다룰 때 설명합니다.

nginx.conf

앞 단에서 reverse proxy 역할을 할 Nginx의 설정파일 내용입니다.

대부분은 nginxdefault로 제공하는 파일의 내용입니다 설명이 필요한 부분은 # 으로 주석을 달았습니다.

upstream docker-express {
server express:3000;
}

docker-express 라는 이름의 upstream을 정의합니다. server express:3000 으로 되어있는데 여기서 express는 docker-compose.yml에서 정의한 express container 이름입니다. 만약 docker-compose.yml 에서 이름을 express가 아닌 server로 줬다면 server server:3000; 으로 명시해줘야 합니다.

location / {
proxy_http_version 1.1;
proxy_pass http://docker-express;
}

Client가 Nginx에 / 경로로 들어올 경우 위에서 정의한 docker-express upstream으로 포워딩 합니다. 즉, client가 앞 단의 nginx에 localhost로 접속할 경우 뒤에있는 express container 3000번 포트로 패킷을 포워딩합니다.

여기까지, 우리는 docker-compose.yml을 작성하고 앞 단에서 동작할 nginx.conf를 설정했습니다.

이제 Express를 구축할 차례인데 여기서 제가 인지하는 구축 방법은 2 가지로 나뉩니다.

첫 번째 방법은 공식 홈페이지에서 볼 수 있는데 npm으로 express만 설치하고 하나의 파일로 express를 구동하는 방법입니다. 이 방법은 express를 쉽게 구동할 수 있지만 디렉토리 구조를 우리가 직접 만들어야 하는 번거로움이 있습니다.

두 번째 방법은 express-generator가 제공하는 디렉토리 및 파일 구조를 그대로 container에 마운트 시켜 구동합니다. 저는 이 방법을 택해서 뒤에 글을 진행하려 합니다. 구축 후 파일이나 디렉토리 구조를 고려하지 않아도 되기 때문입니다.

아래에 전체 디렉토리 구조를 정리합니다. 최 상단에는 docker-compose.yml 파일과 proxy, server, source 3 개의 디렉토리가 있습니다.

C:.                            
│ docker-compose.yml
├─proxy
│ nginx.conf

├─server
│ Dockerfile
│ package.json

└─source
│ app.js

├─public
│ └─stylesheets
│ style.css

├─routes
│ index.js
│ users.js

└─views
error.jade
index.jade
layout.jade
  • proxy: nginx의 설정을 정의한 nginx.conf 파일
  • server: Express를 빌드할 Dockerfile과 express-generator로 생성 된 package.json 파일
  • source: express-generator로 생성 된 결과물

Express-generator

Host pc에 express-generator를 설치하고 myapp 이라는 프로젝트를 생성합니다.

npm install express-generator -g
express myapp

myapp 디렉토리 내부에 만들어진 package.json은 Dockerfile과 같은 경로인 server 디렉토리 내부로 이동합니다. 나머지 소스는 source 디렉토리로 이동합니다.

이 때 source 디렉토리 로 이동한 app.js 소스를 보면 마지막 코드가 아래와 같이 express 객체를 export 하고 있습니다.

.
.
.
module.exports = app; // app.js 파일의 맨 아래 코드

저는 app.js를 실행시키고 싶기 때문에 위 코드 module.exports 제거 하고 아래 코드를 소스 중간에 추가합니다.

.
.
.
app.use('/', indexRouter);
app.use('/users', usersRouter);

// 아래 코드를 app.js 에 추가한다.
app.listen(3000, '0.0.0.0', () => {
console.log('listen 3000 port')
});


app.use(function(req, res, next) {
next(createError(404));
});

app.use(function(err, req, res, next) {
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err :{};

res.status(err.status || 500);
res.render('error');
});

Dockerfile

Nginx의 뒷 단 에서 동작할 express 프레임워크를 빌드할 파일입니다.

  • 1번에서 현재 LTS인 node:12를 기본 image로 사용합니다.
  • 7번에서 express 소스가 변경 될 경우 자동으로 반영해줄 nodemon을 설치합니다.
  • 8번에서 nodemon에 -L 옵션을 주고 있습니다. nodemon 모듈이 container 에서 정상동작 하려면 꼭 필요한 옵션입니다.

혹시 맨 처음 docker-compose.yml 설명할 때 아래 코드가 기억나시나요?

volumes:
— ./source:/source
— /source/node_modules # !!!!!!!! Dockerfile 설명에서 다시 설명합니다.

이 부분에 대해 설명하도록 하겠습니다. 결론 먼저 말씀드리면 위 코드가 없다면 빌드 후 container 내부에서 node_modules찾을 수가 없습니다.

Dockerfile의 6번 npm install을 실행할 때는 빌드 시점으로 이 때, host와 container가 마운트 되어 있지 않습니다. 즉, host와 container는 아직 분리된 영역입니다.

container 내부에서 npm install을 수행하고 node_modules이 존재합니다. 이 후 빌드가 끝난 뒤 host pc가 volume에 명시 된 source 디렉토리를 container에 마운트 합니다. host에는 node_modules 디렉토리가 없기 때문에 container에 존재했던 node_modules 디렉토리가 숨겨지게 됩니다.

이를 해결하는 방법이 위와 같이 node_modules 경로를 volumes에 명시하는 것입니다.

결과 확인

docker compose를 실행하기 위한 준비는 모두 끝났으니 테스트를 해보겠습니다.

docker-compose.yml 파일이 위치한 경로에서 docker-compose up -d 명령어를 입력합니다. (제 경우는 이미 설치를 해봤기 때문에 아래처럼 cache를 사용하여 깔끔하게 출력됩니다.)

크롬을 열고 localhost에 접속합니다.

마지막으로 소스가 변경될 경우 nodemon이 정상적으로 반영하는지 확인합니다. source/routes/index.js 파일을 아래와 같이 수정합니다.

var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.send('express is running');
});
module.exports = router;

docker logs -f express 명령어를 사용하여 출력 로그를 확인합니다. 최초에 빌드 된 후에 소스가 변경되면서 nodemon이 재기동 한 것을 확인할 수 있습니다.

다시 localhost로 접속하고 변경된 응답이 온 것을 확인합니다.

마치며

docker-compose를 활용하여 express를 구축하기 전에는 금방 할 줄 알았습니다. 헌데 container 내부에서 node_modules을 찾지 못해 에러가 발생하고.. (Dockerfile에서 설명한 부분) 소스를 변경해도 nodemon이 동작하지 않아서..(nodemon -L 옵션으로 해결한 부분) 생각보다 시간 소요가 있었네요.

이 부분을 정리한게 누군가의 시간을 save 했으면 좋겠네요. 감사합니다.

--

--