우당탕탕 Nginx 적용기 ( 근데 이제 Actions를 곁들인.. )

2022. 8. 18. 23:58Spring

현재 피플의 테스트 서버는 

AWS Beanstalk에 배포하여 진행하고 있습니다.

 

Beanstalk는 코드 ex) .jar 파일만 업로드하면 알아서 셋팅, 로드 밸런싱, Auto Scaling부터 애플리케이션 모니터링 까지 배포를 자동으로 처리하는 AWS의 서비스입니다.

사실상 많은 셋팅과 작업이 필요한 EC2의 업그레이드 버전이라고 할 수 있습니다. 뿐만 아니라 Beanstalk은 인스턴스에 대한 비용만 부과하기 때문에 추가적인 과금요소 또한 없습니다.

 

CI/CD 자동 연결을 제외하면 Heroku와 매우 비슷합니다.

다만 Heroku는 트래픽 대비 금액이 비씨기 때문에 스타트업에서

 

가장 많이 사용되는 서비스로 뽑힌다고 합니다.

 

1. 스프링 설정

스프링 부트엔 Tomcat이 기본적으로 내장 되어 있고

Tomcat이 받을 수 있는 file, 혹은 request의 사이즈를 설정 파일로 설정 할 수 있습니다.

 

ex) application.yml

spring:
  servlet:
    multipart:
      max-request-size: 50MB
      max-file-size: 50MB

yml에서 지정된 사이즈를 넘어간 request가 온다면 톰캣 내부적으로 에러가 발생하여 500을 반환 합니다.

허나 해당 부분은 언제까지나 Spring 내부의 일이기에 그외 바깥에서 일어나는 에러는 애플리케이션에서 확인 할 수 없습니다.

 

저도 이부분에 대해서 곤혹을 치뤘었는데.

로컬에선 잘되던 이미지 관련 로직이 배포만 하면 413 ( Request Entity Too Large )를 내뱉었었습니다.

 

처음엔 413 Http Code도 처음 봤고 대체 어디서 발생하는 문제인지를 몰라 해맸었는데

추후 조사해보니 Nginx, 즉 웹서버가 설정이 잘못되어 웹서버가 받을 수 있는 맥시멈을 초과해 버린 것이었죠.

 

Beanstalk라고 해서 모든 부분을 케어해주진 않기 때문에 필요에 따라 몇가지 설정이 필요합니다.

그중에 하나가 Nginx입니다.

 

Beanstalk는 기본적으로 웹서버로 Nginx로 되어 있는데

문제는 매우 기본적인 셋팅으로 되어 있으며 콘솔에서 또한 자유로운 셋팅이 불가능합니다

Nginx는 기본적으로 request의 맥시멈 사이즈가 1MB입니다.

 

즉 1MB 이상의 요청을 받을 수 없으며 특히 이미지 같은 큰 용량의 요청인 경우 매우 불편합니다. 

요새 사진 하나 찍으면 10MB는 쉽게 넘으니까요..

 

기본적으로 Benastalk의 배포에 필요한 설정 파일은

Procfile 파일, .ebextensions 폴더, .platform 폴더 3개로 되어 있습니다.

해당 

위 파일 구조 처럼 모두 root 경로에서 새 폴더 및 파일을 만들어 줍니다.

 

첫 번째 nginx 파일입니다.

.platform/nginx/nginx.conf

아래와 같이 config 파일을 생성합니다.

 

user                    nginx;
error_log               /var/log/nginx/error.log warn;
pid                     /var/run/nginx.pid;
worker_processes        auto;
worker_rlimit_nofile    33282;

events {
    use epoll;
    worker_connections  1024;
}

http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;
  client_max_body_size    50M;

  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

  include       conf.d/*.conf;

  map $http_upgrade $connection_upgrade {
      default     "upgrade";
  }

  upstream springboot {
    server 127.0.0.1:5000;
    keepalive 1024;
  }

  server {
      listen        80 default_server;

      location / {
          proxy_pass          http://springboot;
          proxy_http_version  1.1;
          proxy_set_header    Connection          $connection_upgrade;
          proxy_set_header    Upgrade             $http_upgrade;

          proxy_set_header    Host                $host;
          proxy_set_header    X-Real-IP           $remote_addr;
          proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
      }

      access_log    /var/log/nginx/access.log main;

      client_header_timeout 60;
      client_body_timeout   60;
      keepalive_timeout     60;
      gzip                  off;
      gzip_comp_level       4;

      # Include the Elastic Beanstalk generated locations
      include conf.d/elasticbeanstalk/healthd.conf;
  }
}

해당 부분에서 유심히 봐야할 부분은 http 란에 client_max_body_size 입니다 

해당 부분에 50MB를 부여하여 Nginx에서 받을 수 있는 요청의 최대 크기를 늘려줍니다. 

 

두번 째는 .ebextensions 입니다.

Beanstalk는 AWS에서 대부분 자동 셋팅해주기 때문에 커스텀을 하기 위해선 config 파일이 필요합니다.

.ebextensions 폴더 안에 00-makeFiles.config 파일을 생성합니다. 

저희가 커스텀 하는 부분은 애플리케이션을 실행할 스크립트입니다.

files:
    "/sbin/appstart" :
        mode: "000755"
        owner: webapp
        group: webapp
        content: |
            #!/usr/bin/env bash
            JAR_PATH=/var/app/current/application.jar

            # run app
            killall java
            java -Dfile.encoding=UTF-8 -jar $JAR_PATH

Benastalk에 배포 할 때 .jar 파일 하나가 아니라면 zip 파일로 배포를 시도하여야 하는데 zip파일이다 보니

Beanstalk가 어떻게 어떤 순서로 실행할지에 대해 모르기 때문에 이를 정의해주는 스크립트 파일이라고 보시면 됩니다.

 

- /sbin 아래에 스크립트 파일을 두어 전역적으로 실행 가능 하도록 합니다.

- sbin 아래 appstart란 스크립트 파일을 만들고

- 권한은 755, 사용자를 weaapp으로 하여 content를 실행하도록 하는 것입니다.

 

세번째 Procfile 입니다.

Beanstalk는 배포 파일을 받은 후 .ebextensions에 포함된 설정파일을 실행한 뒤에, 애플리케이션을 실행하는데

애플리케이션을 실행하는 파일이 바로 Procfile입니다. 

web: appstart

정말 별거 없습니다. 딱 이 한줄만 적으면 됩니다.

여담으로 Procfile은 Heroku에서도 배포시에 사용됩니다. 

 

여기서 해당 파일들을 zip파일로 묶어 수동으로 배포해도 됩니다.

하지만 이러한 과정들은 불편하기도 하고  실수를 야기할 수 도 있기 때문에

 

GitHub의 CI/CD 툴인 Actions를 이용하도록 하겠습니다.

GitHub Actions은 별도의 가입 절차는 없으며 단순히 깃헙에 해당 프로젝트가 올라와있으면 사용 가능합니다.

 Actions은 구동시간이 2000 시간 까지 무료로 사용 가능하며 설상 넘는다고 하더라도 큰 요금이 부과되지 않습니다.

 

뿐만 아니라 Market place를 통해 손쉽게 셋팅과 유용한 기능을 사용할 수 있습니다.

 

일단 깃헙에서 스크립트 파일을 사용할수 있도록

.github/workflows 폴더 안에 yml파일을 생성합니다.

 

이후 설정파일에 해당 스크립트 내용을 삽입합니다.

name: dev build and deploy for benastalk #  (1)

# (2)
on:
  push:
    branches:
      - dev
  workflow_dispatch:

# (3)
env:
  java-version: 11
  distribute: adopt
  AWS_REGION: ap-northeast-2
  ECR_REPOSITORY: pple-dev

jobs:
  build:
    runs-on: ubuntu-latest # (4)

    steps:
      - name: Checkout
        uses: actions/checkout@v2 # (5)

      - name: Set up JDK and use cache  # (6)
        uses: actions/setup-java@v2
        with:
          java-version: ${{ env.java-version }}
          distribution: ${{ env.distribute }}
          cache: gradle


      - name: Grant execute permission for # (7)
        run: chmod +x gradlew

      - name: Build with Gradle # (8)
        run: ./gradlew build -x test

      - name: Get current time # (9)
        uses: 1466587594/get-current-time@v2
        id: current-time
        with:
          format: YYYY-MM-DDTHH-mm-ss
          utcOffset: "+09:00"

      - name: Show Current Time # (10)
        run: echo "CurrentTime=${{steps.current-time.outputs.formattedTime}}"
        shell: bash

      - name: Generate deployment package # (11)
        run: |
          mkdir -p deploy
          cp build/libs/*.jar deploy/application.jar
          cp Procfile deploy/Procfile
          cp -r .ebextensions deploy/.ebextensions
          cp -r .platform deploy/.platform
          cd deploy && zip -r deploy.zip .

      - name: Deploy to EB # (12) 
        uses: einaregilsson/beanstalk-deploy@v14
        with:
          aws_access_key: ${{ secrets.DEV_AWS_ACTIONS_ACCESS_KEY }}
          aws_secret_key: ${{ secrets.DEV_AWS_ACTIONS_SECRETS_KEY }}
          application_name: pple-dev
          environment_name: Ppledev-env
          version_label: github-action-${{steps.current-time.outputs.formattedTime}}
          version_description: ${{ github.event.head_commit.url }}
          region: ap-northeast-2
          deployment_package: deploy/deploy.zip
          wait_for_environment_recovery: 200

 

(1) name

- 깃헙 액션에 표기될 workflow의 이름입니다.

깃헙에 위와 같이 표시되는 항목입니다.

 

(2) on

- 해당 액션이 실행 되는 분기점입니다. 즉 workflow가 실행될 시점을 이야기 합니다

- push:  깃헙에 푸쉬할 시에 진행됩니다

- branches: 특정 브렌치에 푸쉬 될때만 진행합니다 저같은 경우 dev 브렌치에 푸쉬될경우 실행 하도록 설정하였습니다.

- workflow_dispatch: 특정 이벤트말고 깃헙 사이트에서 수동으로도 실행 하도록 하는 옵션입니다.

 

(3) env

- workflow 내에서 사용될 변수 목록입니다.

 

이후 jobs에 진행될 사항을 순차적으로 기재합니다.

 

(4) runs-on

- Action의 작업이 진행될 운영체제를 선택합니다.

- 가장 많이 사용되는 ubuntu의 최신 버전인 ubuntu-latest로 설정하였습니다.

 

step은 진행 사항을 기재하는 곳입니다. 작업 (Task)들의 집합이라 할 수 있습니다.

 

(5) Checkout

- 프로젝트 코드를 체크아웃합니다 

- Marketplace에 있는 actions의 checkout 버전 2를 가져와 사용합니다.

 

(6) Set up JDK and use cash

- Action이 실행될 OS에 Java를 설치합니다.

- with: 옵션을 설정합니다 해당 부분에선 java-version, distribution, cache를 설정합니다. 

 

(7) Grant execute permission for

- gradle wrapper를 싱핼 할 수 있도록 +x를 통해 권한을 부여합니다.

 

(8) Build with Gradle

- 그래들 빌드 명령어를 실행합니다. 

- 현재로선 배포시 Test를 제외하도록 -x test를 추가로입력합니다.

 

(9) Get current time 

- Action이 실행된 시간대를 알기 위해 Marketplace에 get-crrent-time을 가져옵니다.

- id: 다른 곳에서도 실행된 값을 알기 위해 설정합니다.

 

(10) Show Current Time

- CurrentTime을 출력합니다 shell은 bash 쉘로 실행합니다.

- (9) 에서 설정한 id 값을 통해 현재 시간대를 가져옵니다.

 

(11) Generate deployment package

- Beanstalk에 들어갈 패키지를 zip파일로 압축합니다.

- mkdir -p deploy : deploy라는 폴더를 생성합니다.

- cp ~~ : 위에서 언급된 배포에 필요한 파일들을 가져와 deploy 폴더 안에 복사합니다.

- cd deploy && zip -r deploy.zip: deploy 폴더로 이용하여 파일들을 deploy.zip 파일로 압축합니다 

 

(12) Deploy to EB

- 최종적으로 Elastic Beanstalk에 배포합니다.

- secrets. : 깃헙 Repo -> Settings -> Security / Secrets - Actions 에 있는 부분을 가져 올 수 있습니다.

- 해당 부분에 대해선 아래에서 설명 하겠습니다.

- application_name, environment_name을 기재하여 어떤 Beanstalk에 배포 할 것인지 지정합니다.

- 해당 이름들은 Elastic Beanstalk 생성 후 환경 탭에서 확인하실 수 있습니다.

- version_label: 깃헙액션임과 동시에 날짜까지 지정합니다. 기본적으로 Beanstalk에 올라가는 파일들은 S3에도 저장되기 때문에 중복되지않아야 하며 특정 포멧을 정하여 버전관리를 하는 것이 좋습니다. 

- version_description: Beasntalk 버전의 설명을 기재합니다 전 보다 편리하게끔 현재 커밋에 해당하는 url을 지정하였습니다. 

- resion: Beanstalk가 실행되고 있는 리전을 명시합니다. 리전은 애플리케이션 첫 생성시 자동으로 발급되는 url에서 확인 할 수 있습니다. ex) http://***.ap-northeast-2.****.com 

- deployment_package: Beanstalk에 최종적으로 배포될 패키지 파일을 넣습니다.

- wait_for_environment_recovery: 기본적으로 AWS는 ECS, Beanstalk 등 여러 서비스에서 서버의 상태를 체크할 수 있도록 Health API를 통해 통신하며 상태를 체크하는데 200초 이상 넘어가면 실패합니다. default는 30초 입니다.

 

Secrets Key 발급

ⓛ AWS에 로그인하여 IAM의 사용자 탭으로 들어갑니다.

 

② 사용자 추가를 클릭하고 사용자 이름과 액세스 유형을 프로그래밍 방식 액세스로 설정하고 다음으로 넘어갑니다.

 

 

③ 권한을 기존 정책에서 AdministratorAccess-AWSElasticBeanstalk  를 넣어줍니다.

 

④ 이후 모든 사항을 진행하고 최종적으로 검토 후에 사용자 만들기 버튼을 클릭합니다.

 

 

⑤ 성공적으로 완료 되었다면 액세스키와 비밀 엑세스키를 지급 받습니다. 

    해당 키들은 단 한번만 발급되기 때문에 .csv를 다운 받거나 특정 파일에다가 따로 보관하시길 권장합니다.

⑥ 발급 받은 키를 가져와 Repository -> Settings 탭 Security / Secrets -> Actions 탭으로 이동합니다.

New repository secret을 클릭합니다.

Actions에서 사용할 환경변수라고 생각하시면 됩니다.

위 스크립트에서 secrets.{변수명} 으로 접근하는 부분이 해당 부분입니다.

원하시는 이름을 입력 후 AWS에서 발급 받은 Access key를 value 에 넣어줍니다.

 

위와 같이 완료 되었다면 앞으로 workflow 에서 sercrets.{변수명}으로 접근이 가능 합니다.

 

이제 푸쉬 혹은 깃헙 페이지에서 workflow를 실행 할 수 있습니다.

 

workflow의 진행도 및 체크는 Repository -> Actions 탭에서 확인 할 수 있습니다.

감사합니다.

 

해당글은 인프런 CTO 이신 이동욱님의 블로그를 참고하여 제작 되었습니다.

https://jojoldu.tistory.com/549

'Spring' 카테고리의 다른 글

WebFlux + MongoDB + SSE 로 채팅만들기  (0) 2022.09.04
@Transactional에 대하여  (0) 2022.08.30
Spring의 JDBC, SQLMAPPER, ORM  (0) 2022.05.12
Web Server 와 WAS  (0) 2022.05.09
왜 스프링인가? - 프레임워크별 특징  (0) 2022.05.06