CI&CD

[CI&CD] Github Action +Docker + EC2 + self hosted runner 로 React (front-end) CI&CD 구축하기

작소율 2025. 1. 2. 18:08

 

놀러가실 프로젝트는 Ec2 에서 jenkins + docker를 통해서 CI&CD 구축을 했었다. 하지만 젠킨스를 이용해서 배포했기 때문에 여러개의 인스턴스 사용이 불가피 했고, 이를 계속 유지하기에는 비용측면과 유지관리하는 데에 번거로움이 있었다. (젠킨스 서버 용량이 커서 한 개의 인스턴스에서 도커와 젠킨스를 함께 사용할 수 없었다. swap 도 사용하고, 프리티어 최대용량을 써도 계속 서버가 다운됐음. 그리고 비용문제로 프리티어 사용을 위해 각 인스턴스마다 팀원들 계정을 썼기 때문에 문제가 생겼을 때 해당 서버에 아이디랑 비번을 계속 찾아야하는 것도 번거로운 문제였음)

 

따라서, 배포 후에 서버 유지가 어려워서 바로 서버를 닫았다. 그런데 취준을 위해 따로 코드 리팩토링을 진행하던 중에 도메인으로 접속해서 실제 애플리케이션을 써볼 수 없다는 점이 아쉽기도 하고, 좀 더 쉽게 배포를 진행해보고 싶어서 CI&CD 를 다시 구축하기로 했다!

 

사실 리팩토링 진행하고 싶은 부분이 많은데.. 일단 그 부분은 좀 미뤄두고 배포 후에 빠르게 진행해보려고 한다..!

시각화에 취약한 인간이라 일단 눈에 보이는게 중요해...👀

 


 

일단 배포 방법에 대한 고민이 있었는데 제약 사항이 있었기에 이에 맞는 방법을 택해야했다.

사용할 수 있는 인스턴스는 한 개였고, 내가 배포해야할 될 서버는 '스프링부트 서버, 리액트 서버, 채팅 서버, 레디스 서버' 총 4개 였다. 

프리티어로 사용해야 했기에 사용할 수 있는 최대 스토리지 30기가 + swap 까지 사용해서 최대한 사용해보려고 한다....

정 안되면 계정 하나 더 파야지 모.. 빌드는 깃허브 액션을 통해 진행하고, 빌드한 이미지를 도커허브에 올린 뒤 인스턴스에서 도커허브 이미지를 pull 받아 컨테이너로 띄우는 방식으로 배포를 진행하기로 하였다. 

 

우선, 가비아에서 구매한 도메인을 ec2에 적용하려면 아래 방법대로 하면 된다.


1. ec2에 탄력적IP적용
2. ec2의 IP를 내 도메인의 A타입과 매핑해두기

 

🍄 Dockerfile 생성

# Stage 1: Build the React app
FROM node:18-alpine as build
WORKDIR /app
COPY package.json ./
# Update package list and install nodejs and npm
RUN npm install
COPY . ./ 
# React 앱 빌드
RUN npm run build

# Stage 2: Serve the React app using Nginx
FROM nginx:alpine
# Nginx 설정 파일을 이미지에 복사
COPY default.conf /etc/nginx/conf.d/default.conf
# React 앱을 빌드한 결과물을 Nginx의 정적 파일 서빙 디렉토리로 복사
COPY --from=build /app/build /usr/share/nginx/html
# Nginx가 리스닝할 포트 설정
EXPOSE 80
# Nginx를 실행하여 정적 파일 서빙
CMD ["nginx", "-g", "daemon off;"]
  • node:-alpine 이미지를 베이스로 한다. WORKDIR을 /app으로 설정한다. 워킹 디렉토리는 이미지내에 생성되는 디렉토리이다.
  • 워킹 디렉토리 내부로 package.json 파일을 복사한 뒤, 그 안에서 npm install & build 한다.
  • Nginx 이미지를 베이스로 하여 프로젝트 내부의 default.conf 파일을 복사하고, 빌드 파일을 도커 내에 복사한 뒤, 80 포트를 열어준다.

🍄 .dockerignore 생성

node_modules
dist
.git
*.md
*.log
.env
README.md
.dockerignore
.gitignore
Jenkinsfile

 

이미지 생성시 용량을 줄이기 위해 프로젝트 내의 필요 없는 node_modules를 ignore 해주었다.

 

🍄 default.conf 생성

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 파일을 제공한다.
  • 이전의 과정(Dockerfile)에서 COPY --from=builder usr/src/app/build /usr/share/nginx/html 를 통해 빌드 파일을 usr/src/nginx/html로 복사하였기 때문에 root 경로를 위와 같이 설정했다

🍄 깃허브 액션 파일 설정(.github/workflows/npm-publish.yml)

name: Deploy
on:
  push:
    branches: [main] # main 브랜치에 push 발생하면 트리거
  workflow_dispatch: # 디버깅용, actions 탭에서 직접 버튼 눌러서 트리거

jobs:
  build:
    runs-on: ubuntu-latest # ubuntu 최신 버전 환경에서 실행
    steps:
      # GitHub Actions는 해당 프로젝트를 만들어진 환경에 checkout하고 나서 실행한다.
      # 마치 브랜치를 만들 때 checkout하는 것처럼 꼭 필요하다.
      # 아래 코드는 누군가 만들어놓은 Action을 사용하는 것이다.
      # 만들어놓은 Action을 사용할 때는 uses라는 키워드를 사용한다.
      - name: Checkout
        uses: actions/checkout@v3.5.2

	  # React 프로젝트이므로 해당 환경을 Node.js 위에서 실행하겠다고 명시한다.
      # 마찬가지로 누군가 만들어 놓은 Action이다.
      # node.js 18 버전을 사용하도록 설정
      - name: Use Node.js 18
        uses: actions/setup-node@v3
        with:
          node-version: 18


	  # push할 때마다 npm을 install 해야할까? 아니다.
      # 해당 프로젝트의 node_modules가 변했는지 안 변했는지를 이용해서
      # 모듈 변화가 있을 때만 npm install을 해줄 수도 있다.
      - name: Cache node modules
      	# 그걸 제공하는 Action도 있다.
        uses: actions/cache@v2.1.8
        # 해당 step을 대표하는 id를 설정할 수도 있다. 해당 값은 뒤의 step에서 사용한다.
        id: cache
        with:
          # node_modules라는 폴더를 검사하여
          path: node_modules
          # 아래 키값으로 cache가 돼있는지 확인한다.
          key: npm-packages-${{ hashFiles('**/package-lock.json') }}

        # 위 step에서 node_modules에 대한 cache 검사를 했다.
        # 만약 모듈에 변한 게 있다면 `npm install`을 실행하고 아니면 해당 step을 건너뛰게 된다.
        # if 키워드는 해당 스텝을 실행할지 말지를 결정할 수 있는 키워드이다.
        # 위 step에서 정했던 cache라는 id를 steps.cache로 가져올 수 있다.
        # cache라는 id 값을 가진 step에서는 cache-hit라는 output을 내뱉는다. 
        # 그걸로 cache가 hit 됐는지 안 됐는지를 알 수 있다.
        # 그 값이 true가 아닐 때만 npm install을 한다.
        # https://fe-developers.kakaoent.com/2022/220106-github-actions/
      - name: Install Dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: npm install

      - name: Build
        run: npm run build

	  # Docker에 연결하여 이미지를 빌드하고 Hub에 푸시한다.
      # https://docs.docker.com/build/ci/github-actions/#step-three-define-the-workflow-steps
      - name: Docker Build and push
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:server-prod-blue -f Dockerfile .
          docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:server-prod-green -f Dockerfile .
          docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:server-prod-blue
          docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:server-prod-green

  deploy:
    needs: build-and-test # build-and-test 작업이 성공적으로 수행되어야 실행된다.
    runs-on: self-hosted # 지정된 Runner에서 동작한다. 등록한 self-hosted runner의 이름을 작성한다.
    steps:
      - name: Pull Docker image from DockerHub # docker image를 pull 해온다.
        run: sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:server-prod-blue
        
      - name: Pull Docker image from DockerHub # 실행중인 docker container가 있다면 중지한다.
        run: sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:server-prod-green
        
      - name: deploy.sh 적용
        run: ./deploy.sh
        
      - name: 사용하지 않는 docker image 지우기
        run: sudo docker image prune -f

 

github action 파일을 작성해준 뒤, secret 값을 설정해준다.

 

그 다음 외부에서 인스턴스에 접속하지 못하게 할 것 이기 때문에 self-hosted-runner 를 통해서 ec2 환경에서 배포할 수 있게 설정해준다.

 

🕊️ Self-Hosted Runner 설정

Settings - Actions - Runners에서 runner를 등록한다.

원하는 image, architecture를 설정하고 생성된 명령어를 등록하려는 서버에서 실행하면 된다.

EC2를 사용하므로, ssh 접속 후 해당 명령어를 실행했다.

 

🕊️ EC2 접속 후 Docker, Docker-compose 설치

ec2 내에 docker 와 docker-compose 를 설치해준다.

이전에 용량 문제 예방을 위해 swap 설정도 해준다.

 

이후에, Docker를 이용하여 Nginx를 컨테이너로 실행하고 https를 적용하는 방법까지 알아보자. 

docker-compose.yml 작성
version: '3'

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    depends_on:
      - certbot

  certbot:
    image: certbot/certbot
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot

 

version 은 docker-compose 의 버전을 나타낸다. 그리고 nginx, cerbot 컨테이너를 띄우고 있으며, volumes 통해 ec2의 파일들을 컨테이너 내로 연동 시킨다. 예를 들어, ./data/nginx:/etc/nginx/conf.d 는 ec2 내에 있는 ./data/nginx 파일이 컨테이너의 /etc/nginx/conf.d 로 옮겨 진다고 보면 된다.

 

restar: unless-stopped 는 컨테이너가 수동으로 중지되지 않는 한 항상 자동으로 재시작되는 설정이다. 

그리고 volumes 를 통해 호스트와 컨테이너 사이에 볼륨을 설정한다. 이렇게 볼륨마운트 설정을 하고나면, 로컬에서 nginx.conf를 편집해서 배포를 아무리해도, 배포된 Nginx 컨테이너 내부의 conf.d에는 방금 로컬에서 배포한 nginx.conf가 위치하는 것이 아니라, 무조건 EC2의 ./nginx/conf.d 환경으로 덮어지게 되는 것이다. 

 

 default.conf 파일을 작성

 

도커 컴포즈로 볼륨마운트 설정을 해둔 ec2 내 경로에 nginx conf 파일을 작성해준다.

server {
    listen 80;
    listen [::]:80;
    server_name nolleogasil.com www.nolleogasil.com; #여기에는 본인의 도메인이 들어간다.
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

 

Let’s Encrypt 유효성 검사를 완료하려면 Nginx가 필요하지만 인증서가 없으면 nginx가 시작되지 않는다.

그래서 임시로 https를 사용하지 않는 척 nginx를 실행한 다음 인증서를 발급받고 https를 사용한 nginx에 인증서를 적용하면 된다.

위에 작성한 것처럼 임시 HTTP 설정으로 Nginx 서버가 80번 포트만 리스닝하게 설정한다.

 

  • location /.well-known/acme-challenge/
    • 이 블록은 Let's Encrypt에서 SSL 인증서를 자동으로 발급받기 위한 설정인데, Let's Encrypt는 ACME(Automatic Certificate Management Environment) 프로토콜을 이용해서 도메인의 소유권을 확인한다.
    • 이 과정에서 /.well-known/acme-challenge/ 경로에 특정한 정보를 배치하게 되는데, 이 설정은 그 요청을 처리하는 부분이라고 보면된다.
  • return 301 https://$host$request_uri; 80포트로 들어오는 HTTP 요청을 HTTPS로 리다이렉트하는 부분이다. 이를 통해 모든 트래픽이 암호화되도록 강제하는 것이다. 301은 HTTP 상태 코드로 "Moved Permanently"를 나타내며, $host는 요청이 도착한 서버의 이름, $request_uri는 요청의 URI를 의미한다.

 

docker-compose를 실행.
docker-compose -f docker-compose.yml up -d
docker ps // nginx와 certbot 컨테이너가 살아있는지 확인

 

cerbot 으로 도메인에 대한 SSL/TLS 인증서를 생성

 

nginx, certbot 모두 잘 실행 되었다면, cerbot 을 이용하여 도메인에 대한 SSL/TLS 인증서를 생성해야 한다.

아래 명령어를 입력하여, certbot 인증서를 설치한다.

curl -L https://raw.githubusercontent.com/wmnnd/nginx-certbot/master/init-letsencrypt.sh > init-letsencrypt.sh
chmod +x init-letsencrypt.sh
vi init-letsencrypt.sh
sudo ./init-letsencrypt.sh

 

 

인증서 발급받는 스크립트를 다운로드하고 도메인, 이메일 주소, 디렉터리를 변경해야한다.

 

난 여기서 계속 오류가 났었다. 가비아에서 도메인의 네임서버를 Route 53 네임서버로 설정한 후 DNS 레코드를 Route 53에서 관리해야 하는데 계속 가비아의 A 레코드에만 ec2를 연결해놓아서 A레코드를 인식하지 못했기 때문이다.

도메인과 네임서버의 역할에 대해서 다시 한 번 상기할 수 있는 기회가 됐다..

 

 Nginx conf 설정에 Https 리버스 프록시 설정

 

위와 같이 SSL/TLS 인증서 설치가 제대로 마쳤다면, 다시 Nginx conf 설정에 Https 리버스 프록시 설정을 해 준다.

또한  Let's Encrypt에서 발급받은 인증서는 30일후에 만료되기 때문에 자동 인증서 갱신이 필요하다.

# HTTPS 서버 블록: 443번 포트에서 SSL 인증서로 트래픽 처리
server {
    listen 443 ssl;
    server_name www.nolleogasil.com;

    ssl_certificate /etc/letsencrypt/live/nolleogasil.com/fullchain.pem;  # Let's Encrypt 인증서
    ssl_certificate_key /etc/letsencrypt/live/nolleogasil.com/privkey.pem;  # 개인 키
    ssl_trusted_certificate /etc/letsencrypt/live/nolleogasil.com/chain.pem;  # 체인 인증서

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers on;

    # Upstream 블록을 통해 Blue/Green 컨테이너로 트래픽 전달
    location / {
        proxy_pass http://app;  # upstream "app"으로 트래픽 전달
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

upstream app {
     server blue-container:80; # 기본 blue-container 로 설정
     #server green-container:80; 
}

 

blue/green 컨테이너를 관리할 수 있도록 docker-compose.yml 을 수정해준다.

version: '3'

services:

  nginx:
    image: nginx:latest
    restart: unless-stopped
    container_name: nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /home/ubuntu/data/nginx:/etc/nginx/conf.d
      - /home/ubuntu/build:/usr/share/nginx/html  # React 빌드 파일을 Nginx에 매핑
      - /home/ubuntu/data/certbot/conf:/etc/letsencrypt
      - /home/ubuntu/data/certbot/www:/var/www/certbot
    networks:
      - app-network

  blue-container:
    image: ${DOCKER_USERNAME}/${DOCKER_REPOSITORY}:${DEPLOY_TAG}
    container_name: blue-container
    environment:
      - PORT=3000
    ports:
      - "3000:80"
    networks:
      - app-network

  green-container:
    image: ${DOCKER_USERNAME}/${DOCKER_REPOSITORY}:${DEPLOY_TAG}
    container_name: green-container
    environment:
      - PORT=3001
    ports:
      - "3001:80"
    networks:
      - app-network

  certbot:
    image: certbot/certbot
    restart: unless-stopped
    volumes:
      - /home/ubuntu/data/certbot/conf:/etc/letsencrypt
      - /home/ubuntu/data/certbot/www:/var/www/certbot
    networks:
      - app-network
      
networks:
  app-network:
    driver: bridge

 

driver: bridge: 네트워크의 드라이버를 지정한다. bridge는 Docker의 기본 네트워크 드라이버로, 컨테이너 간 통신을 위한 격리된 네트워크를 제공합니다. 이를 통해 각 컨테이너는 같은 네트워크 내에서 서로 통신할 수 있으며, 외부와는 격리된 상태로 실행된다.

(네트워크를 설정하면 도커 컴포즈를 실행시켰을 때, 자동으로 생성이 되지만 나는 안 되어서...네트워크 생성하고 수동으로 연결해주었다.

docker network create app-network

docker network connect app-network <container_name> )

그런 다음 sudo docker network inspect app-network 를 통해 해당 네트워크에 연결 된 컨테이너들을 확인 할 수 있다.

 

배포 스크립트 작성

 

마지막으로 배포 스크립트 까지 작성해주면 끝!

# 환경 변수 파일 로드
if [ -f ./deploy-env.sh ]; then
  source ./deploy-env.sh
else
  echo "Environment file not found!"
  exit 1
fi

# 설정된 환경 변수
DOCKER_IMAGE=${DOCKER_USERNAME}/${DOCKER_REPOSITORY}:${DEPLOY_TAG}  # 새 이미지 이름
BLUE_PORT=3000
GREEN_PORT=3001
NGINX_CONFIG=~/data/nginx/default.conf

# 현재 실행 중인 컨테이너 확인
CURRENT_PORT=$(sudo docker ps --format "{{.Ports}}" | grep -oE "3000|3001" | head -n 1)

echo "$CURRENT_PORT"

if [ -z "$CURRENT_PORT" ]; then
  echo "No active container found. Starting with Blue container."
  CURRENT_CONTAINER=""
  NEXT_CONTAINER="blue-container"
  NEXT_PORT=$BLUE_PORT
else
  # 공백 제거 후 비교
  if [ "$CURRENT_PORT" == "$BLUE_PORT" ]; then
    echo "Blue container is active. Switching to Green."
    CURRENT_CONTAINER="blue-container"
    NEXT_CONTAINER="green-container"
    NEXT_PORT=$GREEN_PORT
  else
    echo "Green container is active. Switching to Blue."
    CURRENT_CONTAINER="green-container"
    NEXT_CONTAINER="blue-container"
    NEXT_PORT=$BLUE_PORT
  fi
fi

echo "$CURRENT_CONTAINER"
echo "$NEXT_CONTAINER"

# 기존 컨테이너 확인 및 삭제
if sudo docker ps -a --filter "name=$CURRENT_CONTAINER" --format "{{.Names}}" | grep -q "$CURRENT_CONTAINER"; then
  echo "Stopping and removing existing container: $CURRENT_CONTAINER"
  sudo docker stop $CURRENT_CONTAINER
  sudo docker rm $CURRENT_CONTAINER
fi


# 새로운 컨테이너 실행
echo "Started new container: $NEXT_CONTAINER on port $NEXT_PORT for $DOCKER_IMAGE"
sudo DOCKER_USERNAME=$DOCKER_USERNAME DOCKER_REPOSITORY=$DOCKER_REPOSITORY DEPLOY_TAG=$DEPLOY_TAG docker-compose -f /home/ubuntu/data/docker-compose.yml up -d $NEXT_CONTAINER


# 헬스체크: 새로운 컨테이너가 준비되었는지 확인
for i in {1..10}; do
  echo "Checking health of $NEXT_CONTAINER on port $NEXT_PORT..."
  if curl -s http://$NEXT_CONTAINER:80 > /dev/null; then
    echo "$NEXT_CONTAINER is healthy!"
    break
  fi
  echo "Waiting for $NEXT_CONTAINER to be ready..."
  sleep 3
done

# Nginx 설정 업데이트
# 다음 활성화 컨테이너 결정
if [ "$NEXT_CONTAINER" == "blue-container" ]; then
    echo "Switching to Blue container on port $NEXT_PORT"
    sed -i "s/server .*:.*;/server blue-container:80;/" $NGINX_CONFIG
elif [ "$NEXT_CONTAINER" == "green-container" ]; then
    echo "Switching to Green container on port $NEXT_PORT"
    sed -i "s/server .*:.*;/server green-container:80;/" $NGINX_CONFIG                                                                                                                                                                                       69,0-1        62%
else
    echo "Error: Invalid container name"
    exit 1
fi

# Nginx 재시작
sudo DOCKER_USERNAME=$DOCKER_USERNAME DOCKER_REPOSITORY=$DOCKER_REPOSITORY DEPLOY_TAG=$DEPLOY_TAG docker-compose -f /home/ubuntu/data/docker-compose.yml exec nginx nginx -s reload
echo "Nginx configuration updated. Traffic switched to $NEXT_CONTAINER"


echo "Deployment complete. Active container: $NEXT_CONTAINER on port $NEXT_PORT"

 

blue/green 배포 방식으로 배포 스크립트를 작성했다. 여기서 주의해야할 점이 몇가지 있다.

self hoseted runner 사용하기 때문에 runner가 설정된 서버(즉, self-hosted 서버)의 특정 폴더에서 실행된다.

따라서 배포 스크립트를 작성할 때 그 경로를 고려해서 작성해야 한다. 또 하나 주의할 점은 배포 스크립트에서 docker-compose 로 정의한 컨테이너를 실행 시킬 때, 서비스 이름으로 실행시키기 때문에 컨테이너 이름이랑 서비스 이름이랑 잘 구분해서 스크립트를 작성해야 한다. (난 그냥 컨테이너 이름이랑 서비스 이름을 통일 시키긴 했다..🐒)

 

⚡️ 미니 트러블 슈팅

 

컨테이너 모두 잘 실행이 되었지만, 포트 매핑 방식에 대해서 헷갈린 탓에 리액트 애플리케이션으로 접속이 안되는 이슈가 있었다.

도커 컨테이너로 실행 된 리액트 애플리케이션이 3000 -> 80 포트로 매핑된 것때문에 해당 컨테이너를 3000으로 접근해야 한다고 잘 못 생각하고 있던 것이다.  nginx 설정에서 컨테이너를 3000포트로 upstream 했더니 계속해서 접속이 실패하였다.

때리액트 애플리케이션을 도커 이미지로 빌드할 때, nginx 가 리스닝할 port 를 80 으로 expose 했기 때문에 ec2 내에 nginx 컨테이너가 리액트 애플리케이션 정적파일을 서빙한 nginx 에 접근하려면 80 포트로 접근 해야했다 ! !

업스트림 설정에서 포트를 80 으로 변경해준 뒤 깃허브 액션을 실행시켰더니 도커 컨테이너도 잘 뜨고, 도메인으로 접속도 잘 된다 ! !

 

 


 

끝으로.. 🧚

하나의 인스턴스에서 여러개의 서비스를 실행할 수 있는 환경이 필요했기 때문에 docker 와 docker-compose 를 이용해서 필요한 컨테이너들을 실행시키고 연결해보면서 배포를 진행 해보았다.

이번 배포를 통해서 도커와 도커 컴포즈의 네트워크 연결 방식, docker-compose.yml 으로 컨테이너를 관리하는 방법 대해서 더 깊게 이해할 수 있었다.

그리고 네임서버와 도메인 관련 지식을 이해할 수 있는 좋은 기회가 되었다. 도메인과 네임서버가 어떻게 연결되고, DNS(Domain Name System)가 인터넷상의 주소 해석에 어떻게 중요한 역할을 하는지에 대해 더 잘 알게 되었다.

추후에는 롤백도 할 수 있게 수정할 예정이다! 그리고 아직 health check API 를 구현하지 못해서 그 부분도 추가할 예정이다.

스프링부트 배포하고 연결은 다음 포스팅으루...🦿

 

 

 

참고: https://velog.io/@jskim2x/Docker-Spring-Boot-Nginx-React-EC2-%EB%B0%B0%ED%8F%AC-feat.SSLLets-encrypt-2

https://mclub4.tistory.com/57

https://olrlobt.tistory.com/82

https://velog.io/@rivkode/Docker-Nginx%EC%97%90%EC%84%9C-HTTPS%EB%A5%BC-%EC%9C%84%ED%95%9C-SSL-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EC%A0%81%EC%9A%A9-Lets-encrypt-Docker-compose