Docker-compose로 Node Express 환경 구축하기
본 포스팅에서는 Nginx
, Express
를 docker-compose
와 express-generator
를 활용하여 구축하도록 하겠습니다.
Nginx를 앞 단에 둔 이유는 두 가지가 있습니다. 첫 번째 이유는 보안
인데 Client가 실제로 동작하는 Express에 접근할 경우 설정 파일이나 불 필요한 시스템 정보를 노출할 수 있습니다. 두 번째 이유는 Scale Out(확장)
입니다. 앞 단의 Nginx를 로드밸런서로 사용하고 뒷 단의 Express를 Scale Out
할 수 있습니다.
이 글은 docker-compose를 알고 있고 Express를 환경을 구축하려는 분들에게 유용합니다. 만약 docker-compose에 대해 잘 모르신다면 이 글을 읽어주세요.
docker-compose.yml
Nginx, Express를 구축하기 위한 docker-compose.yml
입니다. 설명은 주석으로 대체했습니다. express
의 volumes
부분에서 /source/node_modules
는 추후 Dockerfile을 다룰 때 설명합니다.
nginx.conf
앞 단에서 reverse proxy 역할을 할 Nginx의 설정파일 내용입니다.
대부분은 nginx
가 default
로 제공하는 파일의 내용입니다 설명이 필요한 부분은 # 으로 주석을 달았습니다.
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 했으면 좋겠네요. 감사합니다.