마지막으로, 이번 글에서는 React 컨테이너 배포 및 프록시를 진행했던 것을 기록해보려 한다.
FROM node:alpine as builder
WORKDIR /usr/src/app
COPY package.json .
RUN npm install
COPY ./ ./
RUN npm run build
FROM nginx
EXPOSE 3000
COPY ./default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder usr/src/app/build /usr/share/nginx/html
FROM node:alpine as builder
node:alpine 이미지를 기반으로 "builder"라는 이름의 새 빌드 스테이지를 생성한다. node:alpine은 Node.js가 설치된 가벼운 알파인 리눅스를 사용한다.
WORKDIR /usr/src/app
작업 디렉토리를 설정하는 부분이다. 이 디렉토리는 이후의 명령들이 실행되는 디렉토리를 지정한다.
COPY package.json .
현재 디렉토리의 package.json 파일을 이미지 내 /usr/src/app 디렉토리에 복사하는 명령이다.
RUN npm install
의존성을 설치하는 부분이다.
COPY ./ ./
현재 디렉토리의 모든 파일과 폴더를 이미지의 /usr/src/app 디렉토리에 복사한다.
RUN npm run build
프로젝트 빌드.
EXPOSE 3000
컨테이너가 리슨할 포트를 노출시킨다.
COPY ./default.conf /etc/nginx/conf.d/default.conf
default.conf 파일을 nginx의 기본 설정 파일로 복사한다.
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
앞에서 생성한 빌더 스테이지에서 빌드된 애플리케이션 파일을 nginx의 html 디렉토리에 복사한다.
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
location /
블록은 루트 URL 경로(/)에서 발생하는 모든 요청을 처리한다.root /usr/share/nginx/html;
구문은 정적 파일이 위치한 디렉토리를 지정하고, 이 경우, Dockerfile에서 Nginx 이미지의 해당 위치로 빌드 결과물을 복사하게 된다.index index.html index.htm;
구문은 Nginx가 디렉토리를 제공할 때 사용할 기본 파일 이름을 지정한다.try_files $uri $uri/ /index.html;
구문은 Nginx가 요청을 처리하는 방식을 정의하는 부분이다. Nginx는 우선 $uri가 파일이나 디렉토리에 매핑되는지 확인하고, 그렇지 않은 경우 /index.html 파일을 제공한다.server {
listen 80;
listen [::]:80;
server_name www.jaetteoli.shop;
access_log off;
location /.well-known/acme-challenge/ {
allow all;
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name www.jaetteoli.shop;
ssl_certificate /etc/letsencrypt/live/www.jaetteoli.shop/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.jaetteoli.shop/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location /api {
proxy_pass http://server:8081;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://web:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
/api
경로에 대한 요청을 server:8081
로, 그 외 모든 요청
을 web:3000
으로 프록시한다는 의미이다.name: Deploy
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.3.1]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: web docker build and push
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.DOCKER_REPO }}/jaetteoli-web .
docker push ${{ secrets.DOCKER_REPO }}/jaetteoli-web
version: '3'
services:
server:
container_name: server
image: kimjiseop/jaetteoli-server
expose:
- 8081
ports:
- 8081:8081
restart: "always"
env_file:
- .env
web:
container_name: web
image: kimjiseop/jaetteoli-web
expose:
- 3000
nginx:
container_name: nginx
image: kimjiseop/jaetteoli-nginx
restart: unless-stopped
ports:
- 80:80
- 443:443
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
depends_on:
- "server"
- "web"
command : "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
certbot:
image: certbot/certbot
restart: unless-stopped
volumes:
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
web:
container_name: web
image: kimjiseop/jaetteoli-web
expose:
- 3000
web
컨테이너를 추가하고depends_on:
- "server"
- "web"
depends_on
에 web
도 추가한다. - name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ubuntu
key: ${{ secrets.KEY }}
script: |
if [ "$(docker ps -qa)" ]; then
sudo docker rm -f $(docker ps -qa)
fi
sudo docker pull ${{ secrets.DOCKER_REPO }}/jaetteoli-server
sudo docker pull ${{ secrets.DOCKER_REPO }}/jaetteoli-web
sudo docker pull ${{ secrets.DOCKER_REPO }}/jaetteoli-nginx
sudo docker-compose -f /home/ubuntu/github/workspace/docker-compose.yaml up -d
docker image prune -f
sudo docker pull ${{ secrets.DOCKER_REPO }}/jaetteoli-web
딱 이 부분만 추가했다. 프론트엔드에서 Docker hub에 push를 이미 진행했다고 하고, EC2에 접속하여 pull해서 web
컨테이너를 실행한다.프론트엔드는 로컬환경에서 개발을 하고 github main 브랜치에 push를 진행한다.
백엔드에서는 로컬환경에서 개발을 하고 github main 브랜치에 push를 진행한다.
server
, web
, nginx
, certbot
이 네 가지 컨테이너가 EC2 인스턴스에 배포되어 실행된다./api
@Requestmapping에 매치되는 url의 기본에는 "hello world"를 Return하기 때문에 저렇게 뜨는 것이다.
아주 유익한 내용이네요!