docker를 이용한 react 앱 배포(무중단배포)

김현진·2020년 11월 11일
3

도커

목록 보기
7/7

오늘은 docker를 이용하여 AWS EC2에 react앱을 배포해 보겠습니다. nginx 또한 사용할 예정입니다.(AWS EC2에 관해서는 따로 다루지 않을 예정)

시작하기전 docker의 간단한 명령어와 docker가 설치가 되어 있어야 합니다.(docker-compose 포함)

1. react 간단한 리액트 앱 생성

터미널창에 npx create-react-app ./ 커맨드를 입력한 후 react 앱을 실행시키는 방법은 npm run start 로 실행을 시킬 수 있습니다. 코드를 약간 추가 해보겠습니다.

src > App.js 

import logo from './logo.svg';
import './App.css';

function App() {
  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>
        <a href="https://velog.io/@wmc1415">블로그 주소</a> //제 블로그 주소
      </header>
    </div>
  );
}

export default App;

2. Docker 관련 파일작성

간단히 코드를 추가한 후 Dockerfile 및 docker-compose 파일을 작성하도록 하겠습니다.


//Dockerfile


// nginx가 제공을 해줄 빌드 파일들을 생성하는 코드
FROM node:12 as builder # node 12버젼사용 / build파일임을 명시
WORKDIR /app # working dir를 /app으로 설정
COPY ./package*.json ./ # 먼저 package.json/ package.lock.json 파일을 복사
RUN npm install # npm install
COPY . . # 로컬에 있는 모든 파일을 복사
RUN npm run build # 빌드


FROM nginx 
EXPOSE 3001
COPY ./defalut.conf /etc/nginx/conf.d/default.conf # 로컬에 있는 default.conf 파일을 도커 /etc/nginx/conf.d/defalut.conf로 복사
COPY --from=builder /app/build  /usr/share/nginx/html # 위에서 생성한 build 파일을 /usr/share/nginx/html로 복사

version: "3"
services:
  frontend: # 이름은 어떤걸로 지정해도 상관없음
    build:
      dockerfile: Dockerfile # dockerfile이름
      context: ./ # 도커파일 위치 명시
    volumes:
      - /app/node_modules #도커 /app/node_modules는 맵핑을 따로 안해주겠다.
      - ./:/app # 로컬에 있는 모든 파일을 맵핑
    ports:
      - "3001:3001" # port 맵핑
    stdin_open: true

defalult.conf 파일작성

server {
    listen 3001; //3001번 포드 개방

    location / {

        root /usr/share/nginx/html; # HTML파일이 위치할 경로 설정(위에 docker 파일을 참조하면 됩니다)

        index index.html index.htm; # 사이트의 index페이지로 설정 할 파일명 설정

        try_files $uri  $uri/ /index.html; # 리액트에서 페이지 라우팅을 제대로 하기위해 적어줘야 하는 코드

    }
}

nginx는 정적파일을 제공해주기도 하므로 3001번 포트로 요청을 왔을때 해당 정적파일을 제공해줍니다.

유의 해야할점은 dockerfile, docker-compose.yml 파일, defalut.conf 파일은 최상단 파일에 위치 해야됩니다! 다 작성하셨다면 docker-compose up -d 를 커맨드라인에 입력해줍니다. (build할때 시간이 오래걸릴 수 있으므로 로컬에 있는 node_modules는 삭제 해주어도 됩니다.)

error가 발생하지 않으셨다면 docker ps 명령어를 입력하여 컨테이너가 제대로 동작하는지 확인을 합니다.

혹시 port관련 error가 발생하였다면 lsof -i tcp:3001 3001번 포트의 PID를 확인 후 kill -9 PID숫자 입력을 한 후 해당포트를 종료시켜줍니다.

그렇다면 크롬브라우저 창을 열어 localhost:3001을 입력하여 정상적으로 프로그램이 동작되는지 확인을 합니다. 저희는 따로 npm start명령어를 입력하지 않았는데도 정상적으로 프로그램이 동작 되는걸 확인 할 수 있습니다. ㅎㅎ

EC2에 해당파일 올리기

우선 EC2에 해당소스파일이 올라가있다고 가정하에 진행하겠습니다.

EC2 계정에 nginx설치(proxy(리버스) 용도)

  • sudo apt-get install nginx 설치 후 nginx -v 버젼확인

docker 설치

  • curl -s https://get.docker.com/ | sudo sh #docker 설치
  • `sudo usermod -aG docker ubuntu // 우분투 유저 권한추가

docker-compose 설치

  • sudo curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

  • sudo chmod +x /usr/local/bin/docker-compose 권한부여

    혹시 docker ps 입력 시 Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/json: dial unix /var/run/docker.sock: connect: permission denied error가 발생하였다면,

    https://stackoverflow.com/questions/48957195/how-to-fix-docker-got-permission-denied-issue 참조!

    nginx(리버스 프록시용도) 설정

    docker-compose up 을 하기전에 EC2 nginx 리버스 프록시 설정을 하도록 하겠습니다. 이것마저도 docker를 이용하여 할 수 있으나, 이번에는 직접 설정을 하도록 하겠습니다.

    우선 관리자 권한으로 먼저 변경을 합니다. sudo su 이후에 vim /etc/nginx/nginx.conf vim 편집기를 이용하여 내용을 추가합니다.(exit로 관리자권한에서 나올 수 있습니다)

    내리다 보면 include /etc/nginx/sites-enabled/*;밑에 해당 내용을 추가해줍니다. 들여쓰기 및 오타를 조심하세요.....

    a,i 단축키로 내용을 적을 수 있고 내용을 다 작성하였다면 esc :wq! 로 나올 수 있습니다.

.
.
.
include /etc/nginx/sites-enabled/*;

server {
                server_name 12.345.56.78; 각자 ec2 퍼블릭 IP입력 or 도메인주소
                listen 80;
                location / {
                        proxy_set_header HOST $host;
                        proxy_pass http://127.0.0.1:3001;
                        proxy_redirect off;
                }
        }

sudo systemctl start nginx를 이용하여 nginx를 시작합니다. 또는
sudo systemctl restart nginx 재시작을 해줍니다. nginx가 정상적으로 동작이 되지 않을때는 sudo systemctl status nginx 입력하여 상태 및 에러가 발생하였는지 확인을 합니다.

Nov 11 16:38:23 ip-172-31-38-179 systemd[1]: Starting A high performance web server and a reverse proxy server...
Nov 11 16:38:23 ip-172-31-38-179 nginx[22900]: nginx: [emerg] unknown directive "sever_name" in /etc/nginx/nginx.conf:65
Nov 11 16:38:23 ip-172-31-38-179 nginx[22900]: nginx: configuration file /etc/nginx/nginx.conf test failed
Nov 11 16:38:23 ip-172-31-38-179 systemd[1]: nginx.service: Control process exited, code=exited status=1
Nov 11 16:38:23 ip-172-31-38-179 systemd[1]: nginx.service: Failed with result 'exit-code'.
Nov 11 16:38:23 ip-172-31-38-179 systemd[1]: Failed to start A high performance web server and a reverse proxy server.

저 같은 경우 에러가 나서 보니 65번째줄에 무언가 오타가 있었습니다.(오타조심)

다 작성을 하였다면 docker-compose up -d 커맨드를 입력 후 정상동작이 되는지 확인을 합니다. 아이피가 예를 들어 12.345.56.78이라고 가정한다면 크롬브라우저를 열어 주소창에 그대로 입력하면 됩니다.!


무중단 배포 관련 코드 수정

/etc/nginx/nginx.conf 수정

  • 로드밸런싱 코드 추가
/etc/nginx/nginx.conf # server 스크립트 바로 위에 작성

upstream frontend {
                 least_conn;
                 server 127.0.0.1:3000 weight=5 max_fails=3 fail_timeout=10s;
                 server 127.0.0.1:3001 weight=10 max_fails=3 fail_timeout=10s;
        
 }
 
server {
                server_name 12.345.56.78; 각자 ec2 퍼블릭 IP입력 or 도메인주소
                listen 80;
                location / {
                        proxy_set_header HOST $host;
                        proxy_pass http://frontend;
                        proxy_redirect off;
                }
       }

docker-compose.blue.yml ,docker-compose.green.yml 추가

docker-compose.blue.yml

version: "3"
services:
  frontend: # 이름은 어떤걸로 지정해도 상관없음
    build:
      dockerfile: Dockerfile # dockerfile이름
      context: ./ # 도커파일 위치 명시
    volumes:
      - /app/node_modules #도커 /app/node_modules는 맵핑을 따로 안해주겠다.
      - ./:/app # 로컬에 있는 모든 파일을 맵핑
    ports:
      - "3000:3000" # port 맵핑
    stdin_open: true
docker-compose.green.yml

version: "3"
services:
  frontend: # 이름은 어떤걸로 지정해도 상관없음
    build:
      dockerfile: Dockerfile # dockerfile이름
      context: ./ # 도커파일 위치 명시
    volumes:
      - /app/node_modules #도커 /app/node_modules는 맵핑을 따로 안해주겠다.
      - ./:/app # 로컬에 있는 모든 파일을 맵핑
    ports:
      - "3001:3000" # port 맵핑
    stdin_open: true

코드는 이전 docker-compose 파일과 같으나 포트번호만 다르다.

deploy.sh bash shell 스크립트 작성

#!/bin/bash
  
DOCKER_APP_NAME=frontend

EXIST_BLUE=$(docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml ps | grep Up)

if [ -z "$EXIST_BLUE" ]; then
        echo "blue up"
        docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml up -d

        sleep 10

        docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yml down
else
        echo "green up"
        docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yml up -d

        sleep 10

        docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml down
fi

커맨드라인에 해당 쉘스크립드를 실행하면 서버가 downtime이 되지않고 배포가 가능해진다.
chmod +x ./deploy.sh 명령어로 deploy.sh에 실행할 수 있는 권한을 추가해 줍니다.
해당 bash shell script를 실행하는 방법은 ./delpoy.sh or bash delpoy.sh

profile
기록의 중요성

1개의 댓글

comment-user-thumbnail
2022년 1월 6일

docker-compose에서 80번 포트를 열어주지 않았는데 어떻게 80 포트로 접근이 가능한건가요?

답글 달기