NGINX + docker-compose+ github actions로 배포하기

qq·2023년 11월 3일
0

배포

목록 보기
4/4
  1. EC2 생성
  2. 탄력적 IP할당
  3. RDS 연결
    (내가 배포 정리한 글 보면서)
    4번부터 여기서 이어서 하기

EC2 Docker 설치하기

$ sudo apt-get update
$sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
$curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

다음으로는 Docker Repository를 등록해보자. 이는 Docker 환경을 구축할 때 필수적인 절차이다.

$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"

Repository 등록이 완료되었다면, apt-get 패키징 툴을 통해 도커를 설치한다.

$ sudo apt-get update && sudo apt-get install docker-ce docker-ce-cli containerd.io

도커가 잘 설치되었는지 버전 확인을 해보자.

$ docker -v

EC2에 Docker-compose 설치하기

sudo curl \
     -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" \
     -o /usr/local/bin/docker-compose

실행 권한 적용

sudo chmod +x /usr/local/bin/docker-compose

설치 확인

docker-compose --version
  • 자바 설치하기

무료 도메인 등록하기 위해 AWS를 이용하여 https를 이용하지 않기 위해 Cerbot을 통해 인증서 발급을 할 것이다.
원래 이거 할라 했는데 NO 필요 없을듯..
1. certbot 설치하기

sudo snap install certbot --classic
certbot certonly --manual --preferred-challenges dns-01 --server https://acme-v02.api.letsencrypt.org/directory -d {도메인} -d *.{도메인}
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): {이메일 입력}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
Account registered.
Requesting a certificate for mytamra.ga and *.mytamra.ga

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:

_acme-challenge.{당신의 도메인}.

with the following value:

BCAPvx9hSb_gso0xrYT_pmuPxVLxS-dDFzi_Fq027rg

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

터미널에서 위와 같은 메시지를 확인할 수 있다.
여기서 바로 엔터를 입력하지 말고 읽어보면 DNS TXT 레코드를 생성하라는 안내가 나온다.

_acme-challenge.{당신의 도메인} 라는 이름으로 BCAPv~ 라는 값을 입력해주면 된다.

이를 위해 AWS Route53 메뉴로 이동하여 레코드 생성을 한다.
레코드 유형을 TXT로 바꾼다

유형 부분은 TXT로 변경해준 뒤 이름에는 _acme-challenge를 넣어주고, 값에는 콘솔창에 나와있는 문자를 넣어준다.

실제 레코드가 적용되는데는 몇 분이 걸리기 때문에 조금 기다려준 뒤에 콘솔로 돌아가 엔터를 눌러준다.

다른 터미널 창을 키고 아래 명령어를 통해 적용이 되었는지 확인이 가능하다.
nslookup -q=TXT _acme-challenge.{your domain}

letsencrypt를 이용해 발급받은 인증서는 3개월마다 갱신을 해주어야 한다. 자동 갱신 설정도 있으니 참고하자. 같은 이름의 레코드 생성은 불가능하다. 아까 생성한 레코드에서 값만 추가해주면 된다.

성공적으로 인증서를 발급받았다.

어디에 인증서가 저장되었는지는 Certificate is saved at 에서 확인할 수 있고, 그에 대한 key는 Key is saved at 에 저장되어 있다.

/etc/letsencrypt/live/{당신의 도메인}/ 내부에 위치한다.

이제 발급받은 인증서를 활용해 nginx reverse proxy 설정을 해보자.

Nginx설정
1. Nginx 설정파일 생성하기
ec2 인스턴스 ~/data/nginx 디렉토리 내부 app.config이름으로 설정 파일 생성

server {
        listen 80;
        server_name {your domain};

        server_tokens off;

        location / {
                return 301 https://$host$request_uri;
        }
}
server {
        listen 443 ssl;
        server_name {your domain};

        server_tokens off;

        location / {
                proxy_pass  http://{your domain}:8080/;
                proxy_set_header        Host    $http_host;
                proxy_set_header        X-Real-IP       $remote_addr;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

EC2 내에서 {your domain}으로 들어오는 80 포트 요청을 읽어낸다. SSL 적용을 위하여 443(https)로 리다이렉트하였고, 8080(Spring)으로 reverse proxy** 하도록 구성했다.

EC2 내에서 {your domain}으로 들어오는 80 포트 요청을 읽어낸다. SSL 적용을 위하여 443(https)로 리다이렉트하였고, 8080(Spring)으로 reverse proxy** 하도록 구성했다.

1-2 /etc/letsencrypt/options-ssl-nginx.conf 파일 생성하기

# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file. Contents are based on https://ssl-config.mozilla.org

ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_session_tickets off;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;

ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";

1-3. OpenSSL로 디피-헬만 파라미터 키 만들기

cd etc/letsencrypt
sudo openssl dhparam -out ssl-dhparams.pem 4096
  1. docker-compose.yml 생성하기
    2-1. docker-compose.yml 작성하기
    EC2 인스턴스 루트 위치에 docker-compose.yml을 작성하여 nginx 컨테이너를 올려볼 것이다
version: '3'

services:
  nginx:
    image: nginx:1.15-alpine
    restart: unless-stopped
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - /etc/letsencrypt:/etc/letsencrypt
      - /var/log/nginx/mytamla:/var/log/nginx/mytamla
    ports:
      - "80:80"
      - "443:443"
    command:
      "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"

2-2. docker-compose 동작시키기

docker-compose up -d

2-3. 정상적으로 동작하는지 확인하기

docker ps -a

Github actions를 이용하여 스프링부트 자동 배포하기 위해
1. Dockerfile 작성하기

FROM openjdk:11
ARG JAR_FILE=./build/libs/{빌드한 jar 파일명}.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar", "--spring.profiles.active=prod"]
  1. docker-compose 설정하기
    2-1. docker-compose 설치하기
    지난번 EC2 인스턴스에서 docker-compose를 설치했으니 스킵하도록 하겠다.

2-2. docker-compose.yml 작성하기
docker-compose 파일을 작성하여 EC2 서버에 배포할 것이다. 프로젝트 루트 경로에 docker-compose.yml 파일을 생성하자.

docker-compose.yml을 작성하여 각각 독립된 컨테이너의 실행 정의를 실시한다.

version: "3" # 버전 지정

services: # 컨테이너 설정
  nginx:
    image: nginx:1.15-alpine
    restart: unless-stopped
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - /etc/letsencrypt:/etc/letsencrypt
      - /var/log/nginx/mytamla:/var/log/nginx/mytamla
    ports:
      - "80:80"
      - "443:443"
    command:
      "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
  redis:
    image: redis
    container_name: bapmate-redis-container
    ports:
      - "6379:6379"
    networks: 
      - bapmate-net
  database: 
    container_name: mysql # 컨테이너 이름
    image: mysql/mysql-server:latest # 컨테이너에서 사용하는 base image 지정
    environment: # 컨테이너 안의 환경변수 설정
      MYSQL_DATABASE: {database name}
      MYSQL_USER: {database user}
      MYSQL_PASSWORD: {database pwd}
      MYSQL_ROOT_HOST: '%'
      MYSQL_ROOT_PASSWORD: rootpwd
    command: # 명령어 설정
      - --default-authentication-plugin=mysql_native_password
    ports: # 접근 포트 설정 
      - 3305:3306 # Host:Container
    networks:
      - db_network
    restart: always  # 컨테이너 실행 시 재시작
  bapmate:
    build: .
    expose:
      - 8080
    depends_on:
      - database
      - redis

networks: # 커스텀 네트워크 추가
  db_network: bapmate-net # 네트워크 이름
    driver: bridge

sudo docker network inspect 만든네트워크이름
->네트워크 생성됬는지 확인

  1. Github Action CI/CD 작성하기
    3-1. CI/CD

CI(Continuous Integration)는 지속적 통합을 나타내는 용어이다.

어플리케이션의 새로운 코드 변경 사항이 정기적으로 빌드 및 테스트되어 공유 레포지토리에 통합되는 것을 의미한다.

클래스와 기능에서부터 전체 애플리케이션을 구성하는 서로 다른 모듈에 이르기까지 모든 것에 대한 테스트를 수행할 수 있으며, 코드를 병합하는 과정에서 충돌이 생긴다면 CI를 통해 버그를 수정할 수 있다.

이로 인해 개발하는 코드의 품질을 좀 더 향상시킬 수 있으며, 새로운 업데이트의 검증 및 릴리즈의 시간을 단축시킬 수 있다.

CD(Continuous Deliver, Countinuous Deplotment)는 지속적인 배포를 나타내는 용어이다.
(* 보통은 전자인 지속적 제공의 의미가 강하다.)

  • Github Actions
    Github Actions는 소프트웨어 개발 라이프사이클 안에서 PR, push 등의 이벤트 발생에 따라 자동화된 작업을 진행할 수 있게 해주는 기능이다.
    CI/CD나 Testing, Cron Job 등 작업을 수행할 수 있다.

CI

# Workflow 이름은 구별이 가능할 정도로 자유롭게 적어주어도 된다. 
# 필수 옵션은 아니다.
name: Java CI with Gradle

# main 브랜치에 PR 이벤트가 발생하면 Workflow가 실행된다.
# 브랜치 구분이 없으면 on: [pull_request]로 해주어도 된다.
on:
  pull_request:
    branches: [ "main" ]

# 테스트 결과 작성을 위해 쓰기권한 추가
permissions: write-all


# 해당 Workflow의 Job 목록
jobs:
	# Job 이름으로, build 라는 이름으로 Job이 표시된다.
  build:
  	# Runner가 실행되는 환경을 정의
    runs-on: ubuntu-latest

	# build Job 내의 step 목록
      # uses 키워드를 통해 Action을 불러올 수 있다.
      # 해당 레포지토리로 check-out하여 레포지토리에 접근할 수 있는 Acion 불러오기
    - uses: actions/checkout@v3
    # 여기서 실행되는 커맨드에 대한 설명으로, Workflow에 표시된다. 
    # jdk 세팅

    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

        
      # gradle 캐싱
    - name: Gradle Caching
      uses: actions/cache@v3
      with:
        path: |
          ~/.gradle/caches
          ~/.gradle/wrapper
        key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
        restore-keys: |
          ${{ runner.os }}-gradle-
      
    ### CI
    #gradlew 권한 추가
    - name: Grant Execute Permission For Gradlew
      run: chmod +x gradlew
    
    #test를 제외한 프로젝트 빌드
    - name: Build With Gradle
      run: ./gradlew build -x test

    #test를 위한 mysql설정
    - name: Start MySQL
      uses: samin/mysql-action@v1.3
      with:
        host port: 3305
        container port: 3305
        mysql database: '{database name}'
        mysql user: '{database user}'
        mysql password: '{database pwd}'

    #테스트를 위한 test properties 설정
    - name: Make application-test.properties
      run: |
        cd ./src/test/resources
        touch ./application.properties
        echo "${{ secrets.PROPERTIES_TEST }}" > ./application.properties
      shell: bash

    #test코드 빌드
    - name: Build With Test
      run: ./gradlew test

    #테스트 결과 파일 생성
    - name: Publish Unit Test Results
      uses: EnricoMi/publish-unit-test-result-action@v1
      if: ${{ always() }}
      with:
        files: build/test-results/**/*.xml
name: CD

on:
  push: #해당 브랜치에 push(merge) 했을 때
    branches:
      - main

permissions: write-all #테스트 결과 작성을 위해 쓰기권한 추가

jobs:
  build:
	runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'


      #gradle 캐싱
      - name: Gradle Caching
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            ${{ runner.os }}-gradle-
      ### CD
      #배포를 위한 prod properties 설정
      - name: Make application-prod.properties
        run: |
          cd ./src/main/resources
          touch ./application-prod.properties
          echo "${{ secrets.PROPERTIES_PROD }}" > ./application-prod.properties
        shell: bash

      #test를 제외한 프로젝트 빌드
      - name: Build With Gradle
        run: ./gradlew build -x test

      #도커 빌드 & 이미지 push
      - name: Docker build & Push
        run: |
          docker login -u ${{ secrets.DOCKER_ID }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -f Dockerfile -t ${{ secrets.DOCKER_REPO }}/goorm .
          docker push ${{ secrets.DOCKER_REPO }}/bapmate
      #docker-compose 파일을 ec2 서버에 배포
      - name: Deploy to Prod
        uses: appleboy/ssh-action@master
        id: deploy-prod
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USERNAME }}
          key: ${{ secrets.EC2_PRIVATE_KEY }}
          envs: GITHUB_SHA
          script: |
             docker stop bapmate
             docker rm bapmate
             sudo docker pull ${{ secrets.DOCKER_REPO }}/bapmate
             docker run -d --name bapmate -p 8080:8080 ${{ secrets.DOCKER_REPO }}/goorm
             docker rmi -f $(docker images -f "dangling=true" -q)
            docker-compose up -d
            docker image prune -f

3-5. Secrets 등록하기

profile
백엔드 개발자

0개의 댓글