Record

이스터에그 (인프라 프로젝트6 / Front 연결) 본문

프로젝트

이스터에그 (인프라 프로젝트6 / Front 연결)

now-record 2025. 11. 22. 22:37
반응형

일단 fast api -> 백엔드 서버를 올리고 CI/CD를 하는거까지는 성공했다. 

근데 사실 벡을 보여주는것이 아니라 프론트를 보여줘야 하기 때문에 이번에는 프론트를 올리기로 했다. 

 

그래서 프론트를 뭘로 하지 하다가.. 그냥 React가 제일 인기 있고 전에 한번 써봐서 일단 react로 하기로 했다. 

ㅋㅋㅋ 사실은 안해도 되는데 나중에 확장성을 생각해서 설치해봤다.(뻘짓일듯)

무려 2년전의 캡스톤 디자인때 사용한 고대유물..

 

 

일단은 패키지를 한번 설치해서 기본뼈대를 다운받아 놓기로 했다. 그리고 도커파일로 작성해줬다.

그리고 제일 중요한거는 사실 뭐가 정답인지는 모르겠는데 (현업x이슈) AWS에 도커를 2개(프론트, 벡엔드) 올려야 하기 때문에 도커 컴포즈를 쓰기로 했다. 

 

그냥 2개를 올려도 되지만 왜 compose를 쓰냐는 

컨테이너 간 네트워크/의존성 관리가 자동

docker run 두 개 띄우면

  • 같은 네트워크 붙이기
  • 서비스 이름으로 통신하기
  • 기동 순서 맞추기

CI/CD가 훨씬 단순해짐

docker run 기반 CD는:

  • 이미지 2개 각각 pull/run 재작성
  • 포트/옵션 실수 위험
  • 서비스 늘면 배포 스텝이 폭발

compose 기반 CD는:

  • 서버에 yml 한 번 깔아두고
  • 액션은 pull + up -d만 하면 끝

이게 편해지기 때문에 사용하기로 했다. 

 

docker-compose.yml

version: "3.9"

services:
  back:
    image: jaeheon874/easter-egg:latest
    container_name: easter-back
    ports:
      - "8000:8000"
    restart: always

  front:
    image: jaeheon874/easter-egg-front:latest
    container_name: easter-front
    ports:
      - "80:5173"
    environment:
      - VITE_API_URL=http://url
    depends_on:
      - back
    restart: always

 

 

front↔back 통신 = compose 기본 네트워크 + 서비스명 DNS 덕분 그래서 front 컨테이너 내부에서 back이라는 이름으로 접근 가능해진다. 

 

일단 도커컴포즈를 사용하기로 했으니까 EC2에도 기본적인 파일인 docker-compose.yml을 작성해준다. 왜냐면 이전에는 그냥 docker hub에 올린 이미지를 자동으로 실행해주고 있었는데 지금은 그 이미지를 가져와서 docker-compose로 실행하기 때문에 이파일을 ec2에 작성해줘야한다 .

 

그후 이전에 작성했던 git action파일을 front와 back을 한꺼번에 올리고 docker compose를 실행하는 버전으로 작성해준다. 

 

name: CI/CD (front + back)

on:
  # PR 올리면 빌드만 해서 검증
  pull_request:
    branches: ["main"]

  # main에 merge/push되면 이미지 빌드/푸시 + EC2 배포
  push:
    branches: ["main"]

  workflow_dispatch:

jobs:
  # -----------------------
  # CI: PR에서 빌드 검증만
  # -----------------------
  ci-build:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build BACK (no push)
        uses: docker/build-push-action@v6
        with:
          context: ./back
          file: ./back/Dockerfile
          push: false
          tags: ci-back:pr

      - name: Build FRONT (no push)
        uses: docker/build-push-action@v6
        with:
          context: ./front
          file: ./front/Dockerfile
          push: false
          tags: ci-front:pr

  # -----------------------
  # CD: main push에서 back 이미지 빌드/푸시
  # -----------------------
  build-and-push-back:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build & Push BACK
        uses: docker/build-push-action@v6
        with:
          context: ./back
          file: ./back/Dockerfile
          push: true
          tags: |
            ${{ secrets.DOCKERHUB_USERNAME }}/easter-egg:latest
            ${{ secrets.DOCKERHUB_USERNAME }}/easter-egg:${{ github.sha }}

  # -----------------------
  # CD: main push에서 front 이미지 빌드/푸시
  # -----------------------
  build-and-push-front:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build & Push FRONT
        uses: docker/build-push-action@v6
        with:
          context: ./front
          file: ./front/Dockerfile
          push: true
          tags: |
            ${{ secrets.DOCKERHUB_USERNAME }}/easter-egg-front:latest
            ${{ secrets.DOCKERHUB_USERNAME }}/easter-egg-front:${{ github.sha }}

  # -----------------------
  # CD: EC2 배포 (compose pull & up)
  # -----------------------
  deploy-to-ec2:
    if: github.event_name == 'push'
    needs: [build-and-push-back, build-and-push-front]
    runs-on: ubuntu-latest
    steps:
      - name: Deploy via SSH (docker compose)
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USER }}
          key: ${{ secrets.EC2_SSH_KEY }}
          script_stop: true
          script: |
            set -e

            # docker 데몬이 혹시 꺼져있으면 켜기 (안 꺼져있으면 무시됨)
            sudo systemctl start docker || true

            cd /home/ubuntu/Easter_egg

            # 최신 이미지 pull 후 재기동
            sudo docker compose pull
            sudo docker compose up -d --remove-orphans

            # 상태 출력(디버깅용)
            sudo docker compose ps

            # 안 쓰는 이미지 정리
            sudo docker image prune -f

 

그리고 여기서 이전 토큰을 그대로 사용했는데 이 토큰은 내 도커 허브에 access하기 위한 토큰이라 다른 레포여도 공유되고 다른 레포의 이미지를 가져와서 쓸 수 있기 때문에 재활용했다. 

 

 

그 후 pull request로 main에 올리면 build가 성공적으로 되고 ec2에 올릴 수 있다. 

 

 

ec2에서 성공적으로 실행되고 있는걸 볼 수 있다. 

 

그리고 url을 입력하면 react의 기본 페이지가 보인다. 

 

다만 문제라고 하면 현재의 상태는 back이나 front가 변경되지 않더라도 하나 변경한걸 재 push하면 변경되지 않은 docker file을 재빌드해야하는 비효율이 존재한다. 뭐 일단은 안해도 되지않을까? 아직 MVP단계라고 생각하는 중이다. 

 

그리고 현재 약간 이스터에그로 어그로? 사이트를 만들려고 했는데 약간 AI 스케쥴링 서버를 만들고싶어서 그걸로 좀 변경해보려고 한다. 아이디어 끄적끄적 적어서 gpt 형님이랑 같이 개발해볼려고 한다. 일단은 front나 back은 도커로 만든 상태이고 CI/CD는 끝난 상태! 

 

만약에 서버를 다시 파서 올려야하면... 진짜 귀찮을꺼 같긴하네  만약에 다음부터 AI 스케쥴링 프로젝트면 이름을 바꾸고 포스팅 할꺼 같다.