반응형

docker와 docker-compose, nginx를 이용해서 nextjs 앱을 무중단 배포 해보겠습니다.

절차는 다음과 같습니다.

1. 리버스 프록시로 nginx의 80포트를 nextjs 앱으로 연결시켜줄 것이다. 초기에는 3000포트를 바라보고 있다고 가정한다.

2. nextjs앱은 3000포트 또는 3001포트로 배포할 것이다.

3. 만약 nextjs A(3000)로 배포 중 이었다면, nextjs B(3001)를 새로 실행한다.

4. nextjs B가 정상적으로 다 실행되면, nginx가 3001포트를 바라보도록 하고, nginx를 재시작한다.

5. 구동 중이었던 nextjs A(3000)는 종료한다.

6. 만약 nextjs B(3001)로 배포 중 이었다면, nextjs A(3000)를 새로 실행한다.

7. nextjs A가 정상적으로 다 실행되면, nginx가 3000포트를 바라보도록 하고, nginx를 재시작한다.

8. 구동 중이었던 nextjs B(3001)는 종료한다.

먼저 nginx를 설정하겠습니다.

배포할 인스턴스에 nginx를 설치한 뒤, 다음과 같이 서버 블록을 작성합니다.

/etc/nginx/sites-available/kuke.conf 를 작성하겠습니다.

# /etc/nginx/sites-available/kuke.conf

server {
        include /etc/nginx/conf.d/service-url.inc;
        server_name     ...;
        charset         utf-8;

        location / {
                proxy_pass      $service_url;
                proxy_set_header        X-Real-Ip $remote_addr;
                proxy_set_header        x-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header        Host $host;
        }
}

다른 설정은 기본적인 nginx의 리버스 프록시 설정과 같습니다.

서버 블록 내에 include /etc/nginx/conf.d/service-url.inc; 라는 코드가 있는데,

service-url.inc는 구동 중인 nextjs 앱의 url을 기입해둔 파일입니다.

해당 파일을 include하면, 그 파일에 작성된 변수를 이용할 수 있습니다.

 

location / {
       proxy_pass      $service_url;
       ...
}

proxy_pass에 service_url이라는 변수가 값으로 지정되어 있습니다.

 

# /etc/nginx/conf.d/service-url.inc

set $service_url http://127.0.0.1:3000;

 

serivce-url.inc는 위와 같습니다.

배포해줄 nextjs 앱의 url이 기록되어있습니다.

이 값이 kuke.conf 에서 proxy_pass의 값으로 사용됩니다.

여기까지 되었으면, ln -s 로 kuke.conf의 심볼릭링크를 sites-enabled에 등록해주고 nginx를 시작해줍니다.

이제 nextjs 프로젝트의 루트경로에 Dockerfile을 작성해보겠습니다.

FROM node:alpine

ENV PORT 3000

RUN apk --no-cache add tzdata && \
        cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
        echo "Asia/Seoul" > /etc/timezone

WORKDIR /usr/src/app

COPY package*.json ./
RUN npm install

COPY ./ ./

ENV NODE_ENV production

RUN npm run build

CMD ["npm", "run", "start"]

nextjs 앱을 빌드하고 실행하는 과정을 수행합니다.

 

이어서 nextjs 프로젝트 루트 경로에 docker-compose 파일을 두 개 작성해보겠습니다.

# docker-compose.a.yml

version: "3"
services:
  kuke:
    build: .
    ports:
      - 3000:3000
# docker-compose.b.yml

version: "3"
services:
  kuke:
    build: .
    ports:
      - 3001:3000

두 개의 docker-compose 파일이 하는 일은 포트를 제외하고는 완전히 동일합니다.

그저 a는 3000포트로 매핑시켜주고, b는 3001포트로 매핑시켜주는 차이가 있을 뿐입니다.

이제 마지막으로 배포를 위한 스크립트 파일 deploy.sh를 작성해보겠습니다.

자세한 내용은 주석을 참고 바랍니다.

#!/bin/bash

# 실행 중인 도커 컴포즈 확인
EXIST_A=$(sudo docker-compose -p kukemeet-a -f docker-compose.a.yml ps | grep Up)

if [ -z "${EXIST_A}" ] # -z는 문자열 길이가 0이면 true. A가 실행 중이지 않다는 의미.
then
        # B가 실행 중인 경우
        START_CONTAINER=a
        TERMINATE_CONTAINER=b
        START_PORT=3000
        TERMINATE_PORT=3001
else
        # A가 실행 중인 경우
        START_CONTAINER=b
        TERMINATE_CONTAINER=a
        START_PORT=3001
        TERMINATE_PORT=3000
fi

echo "kukemeet-${START_CONTAINER} up"

# 실행해야하는 컨테이너 docker-compose로 실행. -p는 docker-compose 프로젝트에 이름을 부여
# -f는 docker-compose파일 경로를 지정
sudo docker-compose -p kukemeet-${START_CONTAINER} -f docker-compose.${START_CONTAINER}.yml up -d --build

sleep 5 # 실행되었으면 5초 대기

echo "next start!"
echo "change nginx server port"

# sed 명령어를 이용해서 아까 지정해줬던 service-url.inc의 url값을 변경해줍니다.
# sed -i "s/기존문자열/변경할문자열" 파일경로 입니다.
# 종료되는 포트를 새로 시작되는 포트로 값을 변경해줍니다.
sudo sed -i "s/${TERMINATE_PORT}/${START_PORT}/" /etc/nginx/conf.d/service-url.inc

# 새로운 포트로 nextjs 앱이 구동 되고, nginx의 포트를 변경해주었다면, nginx를 재시작해줍니다.
echo "nginx reload.."
sudo service nginx reload

# 기존에 실행 중이었던 docker-compose는 종료시켜줍니다.
echo "kukemeet-${TERMINATE_CONTAINER} down"
sudo docker-compose -p kukemeet-${TERMINATE_CONTAINER} -f docker-compose.${TERMINATE_CONTAINER}.yml down
echo "success deployment"

deploy.sh에 실행 권한을 주고 실행해봅시다.

스크립트를 실행할 때마다 실행 중인 nextjs 앱의 포트 번호는 바뀌지만, 외부에서는 nginx를 통해 서비스의 중단 없이 이용할 수 있습니다.

반응형

+ Recent posts