ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TIL-2024.07.01 - TIPS - React + Docker + Nginx + Git Action
    > 기초/도와줘요! 2024. 7. 1. 23:45

     

     

     

     

     

     

     

    작업 내용

    - React 애플리케이션을 Docker (dockerfile)와 Nginx (nginx.conf)를 사용해 컨테이너화하고, GitHub Actions로 CI/CD 파이프라인 (deploy.yml)을 구축하여 AWS EC2에 배포하는 방법

     


    Docker (dockerfile)

    # Node.js 환경에서 빌드
    FROM node:12.1.0 AS build
    
    # 컨테이너 내부 작업 디렉토리 설정
    WORKDIR /app
    
    # app dependencies
    # 컨테이너 내부로 package.json 파일들을 복사
    COPY package.json ./
    
    # package.json 및 package-lock.json 파일에 명시된 의존성 패키지들을 설치
    RUN yarn install
    
    # 호스트 머신의 현재 디렉토리 파일들을 컨테이너 내부로 전부 복사
    COPY . .
    
    # npm build
    RUN yarn build
    
    # 프로덕션 환경 설정
    FROM nginx:stable-alpine
    
    # Nginx 설정 파일 복사
    COPY nginx.conf /etc/nginx/nginx.conf
    
    # 이전 빌드 단계에서 빌드한 결과물을 /usr/share/nginx/html 으로 복사
    COPY --from=build /app/dist /usr/share/nginx/html
    
    # custom 설정파일을 컨테이너 내부로 복사
    COPY ./nginx.conf /etc/nginx
    
    # 컨테이너의 80번 포트를 열어줌
    EXPOSE 80
    
    # ENTRYPOINT로 nginx 서버를 실행하고 백그라운드로 동작하도록 함
    ENTRYPOINT ["nginx", "-g", "daemon off;"]

     


     

    dockerfile 작업에 대한 설명

    1. FROM node:12.1.0 AS build: Node.js 12.1.0 버전을 기반으로 한 build stage를 정의합니다. 이 스테이지에서는 애플리케이션을 빌드하기 위한 Node.js 환경을 설정합니다.
    2. WORKDIR /app: 컨테이너 내부의 작업 디렉토리를 /app으로 설정합니다. 모든 후속 명령은 이 디렉토리에서 실행됩니다.
    3. COPY package.json ./: 호스트의 package.json 파일을 컨테이너의 현재 작업 디렉토리(/app)로 복사합니다. 이는 종속성을 설치하기 위해 필요합니다.
    4. RUN yarn install: yarn install 명령을 사용하여 package.json 및 package-lock.json에 명시된 종속성을 설치합니다. 이 단계는 애플리케이션의 필요한 패키지를 모두 설치하는 역할을 합니다.
    5. COPY . .: 현재 디렉토리의 모든 파일을 컨테이너의 /app 디렉토리로 복사합니다. 이는 애플리케이션 코드와 모든 추가 리소스를 포함합니다.
    6. RUN yarn build: 애플리케이션을 빌드합니다. 이는 개발 환경에서 소스 코드를 번들링하고 정적 파일을 생성하는 단계입니다.
    7. FROM nginx:stable-alpine: 두 번째 스테이지에서는 Nginx의 Alpine Linux 기반의 안정적인(stable) 버전을 기본 이미지로 설정합니다. 이는 최종적으로 실행할 서버 환경을 정의합니다.
    8. COPY nginx.conf /etc/nginx/nginx.conf: 호스트의 nginx.conf 파일을 Nginx 컨테이너 내의 /etc/nginx/nginx.conf 경로로 복사합니다. 이 설정 파일은 Nginx 웹 서버의 설정을 커스터마이즈하는 데 사용됩니다.
    9. COPY --from=build /app/dist /usr/share/nginx/html: 앞서 build 스테이지에서 생성한 빌드 결과물을 Nginx의 기본 문서 루트(/usr/share/nginx/html)로 복사합니다. 이는 Nginx가 정적 파일을 서빙할 위치를 설정합니다.
    10. COPY ./nginx.conf /etc/nginx: 호스트의 추가적인 Nginx 설정 파일을 Nginx 컨테이너 내의 /etc/nginx 경로로 복사합니다. 이는 커스텀 설정을 더욱 확장하여 적용하는 데 사용됩니다.
    11. EXPOSE 80: 컨테이너가 80번 포트를 열도록 설정합니다. 이는 외부에서 HTTP 요청을 받아들일 수 있도록 합니다.
    12. ENTRYPOINT ["nginx", "-g", "daemon off;"]: 컨테이너가 시작될 때 Nginx 웹 서버를 실행하도록 설정합니다. -g "daemon off;"는 Nginx가 백그라운드에서 동작하지 않고, 전후에 블록킹 방식으로 실행되도록 설정합니다.

     


     

    Nginx (nginx.conf)

    # 기본적인 http 설정
    http {
        include       /etc/nginx/mime.types;  # MIME 타입 설정 파일을 포함합니다. 이 파일에는 파일 확장자와 MIME 타입 간의 매핑이 정의되어 있습니다.
        default_type  application/octet-stream;  # MIME 타입이 지정되지 않은 경우 사용할 기본 MIME 타입을 설정합니다.
    
        # 로그 파일 설정
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';  # 로그 형식을 정의합니다. 주요 요소는 클라이언트 IP, 사용자 이름, 접근 시간, HTTP 요청, 응답 상태 코드 등입니다.
    
        access_log  /var/log/nginx/access.log  main;   # 접근 로그 파일 경로와 정의된 로그 형식을 설정하여 접근 이벤트를 기록합니다.
        error_log   /var/log/nginx/error.log  error;  # 오류 로그 파일 경로를 설정하여 서버 오류를 기록합니다.
    
        # 서버 블록
        server {
            listen       80;  # HTTP 요청을 수신할 포트 번호를 설정합니다. 여기서는 80 포트(기본 HTTP 포트)를 사용합니다.
            server_name  localhost;  # 서버의 도메인 이름을 설정합니다. 여기서는 localhost로 설정되어 있습니다.
    
            # 루트 디렉터리 설정
            root   /usr/share/nginx/html;  # HTTP 요청의 기본 디렉터리를 설정합니다. 클라이언트가 요청한 파일을 찾을 때 이 디렉터리에서 검색합니다.
            index  index.html;  # 기본 문서 파일 이름을 설정합니다. 여기서는 index.html을 기본으로 사용합니다.
    
            # 기본적인 라우팅 설정
            location / {
                try_files $uri $uri/ /index.html;  # 파일이나 디렉터리가 없는 경우 index.html 파일을 반환하도록 설정합니다.
            }
        }
    }
    
    # 기본적인 이벤트 설정
    events {
        worker_connections  1024;  # 각 워커 프로세스가 처리할 수 있는 최대 동시 연결 수를 설정합니다. 워커 프로세스는 클라이언트의 요청을 처리하는 Nginx의 작업 단위입니다.
    }

     


     

    nginx.conf 작업에 대한 설명

    1. include /etc/nginx/mime.types;: 파일 확장자와 MIME 타입 간의 매핑을 포함한 MIME 타입 설정 파일을 포함합니다. 이는 Nginx가 정확한 MIME 타입을 클라이언트에게 제공하기 위해 필요합니다.
    2. default_type application/octet-stream;: MIME 타입이 지정되지 않은 경우 사용할 기본 MIME 타입을 설정합니다. application/octet-stream은 이진 데이터를 나타내는 일반적인 MIME 타입입니다.
    3. log_format main '...';: 로그 파일에 기록할 로그 형식을 정의합니다. 이는 접근 로그(access log)와 오류 로그(error log)에 사용됩니다.
    4. access_log /var/log/nginx/access.log main;: 접근 로그 파일 경로와 정의된 로그 형식을 설정하여 클라이언트의 접근 이벤트를 기록합니다.
    5. error_log /var/log/nginx/error.log error;: 오류 로그 파일 경로를 설정하여 서버에서 발생한 오류를 기록합니다.
    6. listen 80;: HTTP 요청을 수신할 포트 번호를 설정합니다. 여기서는 80 포트로 설정하여 HTTP 요청을 처리합니다.
    7. server_name localhost;: 서버의 도메인 이름을 설정합니다. 이 설정은 요청된 호스트명과 일치하는 경우 해당 서버 블록을 사용합니다.
    8. root /usr/share/nginx/html;: HTTP 요청의 기본 디렉터리를 설정합니다. 클라이언트가 요청한 파일을 이 디렉터리에서 찾습니다.
    9. index index.html;: 기본 문서 파일 이름을 설정합니다. 클라이언트가 요청한 디렉터리에서 인덱스 파일을 찾을 때 사용됩니다.
    10. try_files $uri $uri/ /index.html;: 요청된 파일이나 디렉터리가 없는 경우 index.html 파일을 반환하도록 설정합니다.
    11. worker_connections 1024;: 각 워커 프로세스가 처리할 수 있는 최대 동시 연결 수를 설정합니다. 이 값은 시스템의 리소스와 서버의 처리 능력에 따라 조정됩니다.

    Github Action Pipeline (deploy.yml)

    # 워크플로우 이름 정의
    name: MyApp_FE
    
    # 워크플로우 트리거 설정: develop 브랜치로의 push와 수동 트리거(workflow_dispatch)로 실행됨
    on:
      push:
        branches:
          - develop
      workflow_dispatch:
    
    # 환경 변수 설정 (AWS 비밀 키 파일, 호스트, 사용자)
    env:
      AWS_PEM_FILE: ${{ secrets.PEM }}  # AWS 인스턴스에 접근하기 위한 PEM 파일의 내용을 시크릿으로부터 가져옵니다.
      AWS_USER: ${{ secrets.USER }}          # AWS 인스턴스 접속에 사용될 사용자 이름을 시크릿으로부터 가져옵니다.
      AWS_HOST: ${{ secrets.HOST }}          # AWS 인스턴스의 호스트 주소를 시크릿으로부터 가져옵니다.
    
    # 빌드 작업 정의
    jobs:
      build:
        runs-on: ubuntu-latest # 최신 우분투 이미지를 사용하여 작업 실행
        steps:
          # 코드 체크아웃
          - name: Get code
            uses: actions/checkout@v3  # GitHub Action을 사용하여 코드를 현재 작업 환경으로 체크아웃합니다.
    
          # 빌드 타임스탬프 설정
          - name: Set build timestamp
            id: vars
            run: echo "BUILD_TIMESTAMP=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV  # 빌드를 위한 타임스탬프를 생성하고 환경 변수에 저장합니다.
    
          # 도커 이미지 빌드
          - name: Build Docker Image
            run: |
              docker build -t myapp_fe:${{ env.BUILD_TIMESTAMP }}  .  # 도커 이미지를 빌드하고 타임스탬프를 태그로 사용하여 이미지를 생성합니다.
    
          # 도커 이미지를 TAR 파일로 저장
          - name: Save Docker Image to TAR
            run: |
              docker save myapp_fe:${{ env.BUILD_TIMESTAMP }} -o myapp_fe.tar  # 빌드된 도커 이미지를 TAR 파일로 저장합니다.
    
          # 도커 이미지 아티팩트 업로드
          - name: Upload Docker Image Artifact
            uses: actions/upload-artifact@v4
            with:
              name: myapp_fe_tar
              path: myapp_fe.tar  # 빌드된 도커 이미지 TAR 파일을 아티팩트로 업로드합니다.
    
      # 배포 작업 정의
      deploy:
        runs-on: ubuntu-latest # 최신 우분투 이미지를 사용하여 작업 실행
        needs: build # 이 작업은 빌드 작업이 완료된 후 실행됨
        steps:
          # SSH 키 설정
          - name: Set up SSH key
            run: |
              echo -n "$AWS_PEM_FILE" > $HOME/private_key.pem  # AWS 인스턴스에 접속하기 위해 SSH 키를 설정하고 홈 디렉토리에 저장합니다.
              chmod 400 $HOME/private_key.pem  # SSH 키 파일의 권한을 설정하여 보안을 강화합니다.
    
          # 도커 이미지 아티팩트 다운로드
          - name: Download Docker Image Artifact
            uses: actions/download-artifact@v4
            with:
              name: myapp_fe_tar
              path: .  # 빌드된 도커 이미지 TAR 파일을 현재 디렉토리로 다운로드합니다.
    
          # EC2로 빌드 업로드
          - name: Transfer Docker Image to EC2
            run: |
              scp -v -o StrictHostKeyChecking=no -i $HOME/private_key.pem myapp_fe.tar $AWS_USER@$AWS_HOST:/home/$AWS_USER/myapp  # SSH를 사용하여 EC2 인스턴스로 도커 이미지를 전송합니다.
    
          # EC2에서 도커 이미지 로드 및 컨테이너 실행
          - name: Load Docker Image on EC2 and Run Container
            run: |
              ssh -i $HOME/private_key.pem $AWS_USER@$AWS_HOST << 'EOF'
                docker load -i /home/$AWS_USER/myapp/myapp_fe.tar  # EC2 인스턴스에 도커 이미지를 로드합니다.
                if [  "$(docker ps -a -q -f name=myapp_fe)" ]; then  # 도커 컨테이너가 이미 실행 중인 경우, 중지하고 삭제합니다.
                    docker stop myapp_fe
                    docker rm myapp_fe -f
                fi
                LATEST_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}} {{.CreatedAt}}" | grep 'myapp_fe' | sort -k2 -r | head -n 1 | awk '{print $1}')  # 가장 최신 버전의 도커 이미지를 확인합니다.
                docker run -d -p 80:80 --name myapp_fe $LATEST_IMAGE  # 새로운 도커 컨테이너를 실행합니다.
    
                exit
              EOF
    
          # SSH 키 정리
          - name: Clean up SSH key
            run: |
              rm $HOME/private_key.pem  # 사용한 SSH 키를 삭제하여 보안을 강화합니다.

     


    deploy.yml 작업에 대한 설명

     

    1. name: MyApp_FE: 워크플로우의 이름을 정의합니다. GitHub Actions 대시보드에서 식별할 수 있는 이름입니다.
    2. on: 워크플로우가 언제 실행될지를 결정하는 트리거 설정입니다. push는 develop 브랜치에 푸시될 때, workflow_dispatch는 수동으로 실행할 때 트리거됩니다.
    3. env: 환경 변수 설정 섹션입니다. 여기서는 AWS 인스턴스 접근에 필요한 PEM 파일, 호스트 주소, 사용자 이름을 시크릿으로부터 가져와 설정합니다.
    4. jobs: 워크플로우의 각 작업을 정의하는 섹션입니다.
      • build: Node.js 애플리케이션을 빌드하는 작업입니다.
        • runs-on: ubuntu-latest: 최신 버전의 Ubuntu 이미지를 사용하여 작업을 실행합니다.
        • steps: 작업의 각 단계를 정의합니다.
          • Get code: GitHub Actions을 사용하여 현재 작업 환경으로 코드를 체크아웃합니다.
          • Set build timestamp: 현재 빌드를 위한 타임스탬프를 생성하고 환경 변수에 저장합니다.
          • Build Docker Image: Docker를 사용하여 애플리케이션의 Docker 이미지를 빌드합니다.
          • Save Docker Image to TAR: 빌드된 Docker 이미지를 TAR 파일로 저장합니다.
          • Upload Docker Image Artifact: 빌드된 Docker 이미지 TAR 파일을 GitHub Artifact로 업로드합니다.
      • deploy: 빌드된 Docker 이미지를 EC2 인스턴스로 전송하고 실행하는 작업입니다.
        • runs-on: ubuntu-latest: 최신 버전의 Ubuntu 이미지를 사용하여 작업을 실행합니다.
        • needs: build: 이 작업은 build 작업이 완료된 후에만 실행됩니다.
        • steps: 작업의 각 단계를 정의합니다.
          • Set up SSH key: AWS 인스턴스에 접속하기 위해 SSH 키를 설정하고 보안을 강화합니다.
          • Download Docker Image Artifact: 빌드된 Docker 이미지 TAR 파일을 다운로드합니다.
          • Transfer Docker Image to EC2: SSH를 사용하여 Docker 이미지를 EC2 인스턴스로 전송합니다.
     
     
     
     
     
     
     

     

     

    댓글

Designed by Tistory.