옛날 spring, jsp 시절에는maven(or gradle) 빌드로 나온 war 결과를 tomcat 에 배포하면 쉽게 배포가 되었다. 하지만 react 와 nodejs express 를 개발하고 배포하려고 보면, 어떻게 배포해야할지 난감할 때가 있다. express 앱은 pm2 로 배포해도 될 것 같고, react는 build 이 후 serve로 배포해도 될 것 같고.. 방법은 다양한 것 같다.
하지만 나는 docker-compose 를 활용하여 nginx 를 통한 프록시 설정까지 고려하여 한번에 배포해보려고 한다.
※ 필수 설치 : docker 가 설치되어있어야 한다. [윈도우 도커 설치] 참고
테스트 소스 구성 하기 전 먼저 전체 패키지 구조를 확인하자.
express 구성을 위해 서버 구동 파일 추가 (./packages/server)
# server 디렉토리를 구성하고 npm 프로젝트로 초기화 및 express 모듈 설치
mkdir packages/server
cd packages/server
npm init
npm i express nodemon
const express = require('express');
const app = express();
app.use(express.json());
// proxy 설정 확인을 위한 test api 구성
app.get('/api/list', (req, res) => {
res.json([
{ id: 'test1', name: '테스트1' },
{ id: 'test2', name: '테스트2' },
{ id: 'test3', name: '테스트3' },
{ id: 'test4', name: '테스트4' },
]);
});
app.get('/api/item', (req, res) => {
res.json({
id: 'test1',
name: '테스트1'
});
});
app.listen(8080, (err) => {
if (err) {
console.log('err 발생');
}
console.log('정상구동');
});
소스 변경 감지를 위한 nodemon 설정 파일 추가 (./packages/server/nodemon.json)
{
"watch": ["./"],
"exec": "node server.js",
"ext": "js json yaml",
"ignore" : ["./log/"]
}
package.json 파일에 scripts 추가
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"server": "nodemon"
},
server 실행 (port 8080)
npm run server
react 구성을 위한 cra(create-react-app) 를 활용 (./packages/client)
npx create-react-app ./packages/client
cd packages/client
# api 테스트를 위해 proxy middleware 및 axios 설치
yarn add http-proxy-middleware axios
개발용 proxy 설정 (./packages/client/src/setupProxy.js)
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
createProxyMiddleware('/api', {
target: 'http://localhost:8080',
changeOrigin: true
}),
);
};
api 테스트를 위해 CRA 순정의 ./src/App.js 에서
// ./packages/client/src/App.js
import { useState, useEffect } from 'react';
import axios from 'axios';
import logo from './logo.svg';
import './App.css';
function App() {
const [list, setList] = useState([]);
const [item, setItem] = useState({});
useEffect(() => {
(async () => {
const { data: resultList } = await axios.get('/api/list');
setList(resultList);
const { data: result } = await axios.get('/api/item');
setItem(result);
})();
}, []);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo"/>
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
<div>List</div>
{list.map((item, idx) => (
<div key={idx}>{`${item.id}/${item.name}`}</div>
))}
<div>item</div>
<div>{`${item.id}/${item.name}`}</div>
</header>
</div>
);
}
export default App;
37~43번째 줄 추가
yarn start
리액트 구동 후 http://localhost:3000 으로 접속하면 아래와 같은 화면을 볼 수 있다.
API 요청 및 응답이 정상적으로 이루어지는 것을 확인할 수 있다.
이 후 리액트 프로젝트를 빌드 해 둔다.
yarn build
그럼 이제 프록시 설정도 하면서 react 와 express 를 docker-compose 로 어떻게 배포하는지 확인해볼 것이다.
./nginx/nginx.conf 파일을 만든다.
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
upstream docker-server {
server server:8080;
}
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
location /api {
proxy_pass http://docker-server;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /socket {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_pass http://docker-server;
}
}
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
여기서 설명이 필요한 부분만 보겠다.
upstream docker-server {
server server:8080;
}
도커로 구동한 server 컨테이너명을 통해 upstream 포워드 구성
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
nginx 에 접속 시 / 로 접근하면 react 에서 build 한 정적파일이 열림
location /api {
proxy_pass http://docker-server;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
/api 로 접근하면 upstream 으로 포워딩한 docker-server(백엔드서버) 로 요청
※ nginx.conf 파일에 대한 설정 및 설명은 다른 문서가 많으니 참고해서 본인 시스템에 맞는 설정으로 하면 된다.
server 내 Dockerfile 을 생성한다. (./packages/server/Dockerfile)
FROM node:15.11.0-alpine # node 15 version 사용
MAINTAINER SONGJANG
RUN mkdir /app
WORKDIR /app # 기본 경로 설정
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install --no-cache
# 서버 소스 복사
COPY . /app
# 실행 명령어
CMD ["npm", "run", "server"]
이 후 프로젝트 최상위 경로에 docker-compose-yml 파일을 생성한 뒤 해당 내용을 입력한다.
version: '3.3'
services:
web:
image: nginx:latest # nginx 이미지
container_name: web # 컨테이너 명
restart: "on-failure" # 구동 실패 시 재시작
ports:
- 80:80
volumes: # 볼륨 설정
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./packages/client/build:/usr/share/nginx/html
server:
build: # Dockerfile 빌드
context: ./packages/server # Dockerfile 빌드 경로
container_name: server # 컨테이너 명
restart: "on-failure"
expose:
- 8080 # 도커 내부적 포트
volumes: # 볼륨 설정
- './packages/server:/app'
- '/app/node_modules'
environment: # 환경변수 설정
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
stdin_open: true
tty: true
docker-compose 파일 작성이 완료되었으면, 프로젝트 최상위 경로에서 docker-compose 를 실행시킨다.
docker-compose up -d
정상 실행이 된다면 컨테이너가 이상없이 발행 되고 http://localhost 로 접속하면 화면을 조회할 수 있게 된다.
docker-compose.yml 에서 react 의 build 디렉토리를 볼륨 마운트 했기 때문에 yarn build 로 리액트의 수정사항을 즉각 반영 할 수 있다. 또한 server의 전체 소스를 볼륨 마운트 했기 때문에 소스가 수정되면 nodemon 에 의해 재구동 후 반영됨을 확인할 수 있다.
※ 오타 수정이나 질문은 댓글로 부탁드립니다. 블로그 내용 및 소스는 마음껏 퍼가셔도 됩니다^^
전체소스: https://github.com/sbjang123456/docker-fullstack
'Docker' 카테고리의 다른 글
[Jenkins] Jenkins Docker 설치 및 GitHub 연동 (0) | 2022.05.23 |
---|---|
Docker Ubuntu Install 및 사용자 권한 설정 (0) | 2022.02.11 |
[Docker] <none> image 정리 (0) | 2021.08.17 |
[Docker] Ubuntu Docker 컨테이너 자동 실행 (0) | 2021.07.23 |
[Docker] 컨테이너 vi 명령어 실행 (0) | 2021.04.14 |