[CICD] Docker-compose + Nginx SSL + Github Actions으로 자동 배포 환경 만들기

김민우·2022년 8월 25일
0

Docker

목록 보기
3/4

사이드 프로젝트에서 Github Actions 와 CodeDeploy를 이용하여 자동 배포 환경을 제작했습니다. 하지만 nginx, certbot, redis 같이 의존성이 있는 파일을 설치 및 환경 설정을 하면서 서버에 부하가 크고 설정의 귀찮음이 있었습니다.

그래서 사이드 프로젝트에서 Docker-hub와 nginx, certbot(SSL/https)을 도커 이미지를 통해 인증서를 발급하고 배포 자동화(CI/CD)하기로 수정했습니다.😊

참고자료1
참고자료2
참고자료3

➡️ 본 과정은 윈도우10 OS에서 진행되었습니다.


🏷 1. Dockerfile 작성

FROM openjdk:11
ARG JAR_FILE=build/libs/{빌드한 jar 파일명}
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

스프링 프로젝트 파일에 도커 파일을 작성합니다. 도커 파일은 이미지를 빌드하여, 빌드한 이미지를 기반으로 컨테이너가 실행됩니다.

[설명]

  1. base 이미지를 openjdk:11로 설정 및 JAR_FILE 변수 파일경로를 등록한다.

  2. 빌드 파일을 컨테이너의 app.jar로 옮기고 컨테이너에서 java -jar /app.jar을 실행한다.

- Github Actions를 이용하여, 프로젝트 파일을 master 브랜치에 push 할 때마다(수정), 빌드 파일을 새로 만들 것입니다!


🏷 2. docker-compose 설정

우선 EC2 인스턴스에, docker-compose를 설치해야 합니다.
Docker-compose 설치 방법

  • EC2 현재 경로(아무곳이나 상관 없음)에 docker-compose.yaml을 추가해주세요!
version: '3'
services:

  web:
    container_name: web
    image: {Docker 닉네임}/{생성 image}
    expose:
      - 8080
    ports:
      - 8080:8080

  nginx:
    container_name: nginx
    image: nginx:latest
    restart: always
    volumes:
      - ./conf/:/etc/nginx/conf.d
      - /data/certbot/conf:/etc/letsencrypt
      - /data/certbot/www:/var/www/certbot
    ports:
      - 80:80
      - 443:443
    depends_on:
      - web

web의 image: {Docker 닉네임}/{생성 image}는 자신의 Docker 닉네임과, 생성할 image 이름으로 변경해주세요.
EX) abc123/web

[설명]
컨테이너는 web, nginx을 생성합니다.
web은 나의 docker-hub에 올리고, nginx는 docker에 이미 존재하는 nginx를 이용합니다.
EC2 인스턴스에 docker-compose.yaml을 생성합니다.


🏷 3. certbot 컨테이너를 이용하여 SSL 인증서 발급하기

docker run -it --rm --name certbot \
  -v '/data/certbot/conf:/etc/letsencrypt' \
  -v '/data/certbot/www:/var/www/certbot' \
  certbot/certbot certonly -d 'mydomain.or.kr' --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory

EC2 인스턴스에 위 명령어를 입력해주세요!

mydomain.or.kr을 내 도메인으로 변경해주세요.

[설명]
-v 명령어는 바인드 볼륨을 정해주는 명령어입니다.
(서버의 파일 경로 : 컨테이너의 파일 경로)

바인드 마운트 알아보기

  • 바인드 마운트를 사용하면, 호스트 파일 시스템의 특정 경로를 컨테이너로 마운트할 수 있습니다. 즉 인증서가 발급되면, 서버의 경로(data/certbot/conf, data/certbot/www)에 저장되는 것입니다!

자신의 DNS 서버에 TXT 레코드를 추가해야 됩니다.

dns명은 _acme-challenge.mydomain.com이고 txt는 the following value(Ma...cM)가 된다. txt 레코드를 추가하고 잠시 2~3분 정도는 기다렸다가 엔터를 눌러주자.

위 화면은 인증서 발급이 성공한 것입니다.

인증서는 도커 내 컨테이너인 /etc/letsencrypt/live/{도메인 명}/ 디렉토리에 저장됩니다. 바인드 마운트 되었으므로, 서버 내에는 data/certbot 디렉토리 내에 존재합니다.


🏷 4. nginx.conf 작성하기

  • EC2 내에 .(현재 경로)/conf 내에 nginx.conf 파일을 생성합니다.
server {
       listen 80;
       server_name {도메인 명};
       return 301 https://{도메인 명}$request_uri;   # http로 들어오면 https로 redirect 해주는 부분 
}
server {
       listen 443 ssl;
server_name {도메인 명};
       # Certificate
       ssl_certificate /etc/letsencrypt/live/{도메인 명}/fullchain.pem;

       # Private Key
       ssl_certificate_key /etc/letsencrypt/live/{도메인 명}/privkey.pem;
       location / {
               
	       proxy_pass http://{퍼블릭 ip 주소}:8080; # 자신의 springboot 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;
               proxy_set_header X-Forwarded-Proto $scheme;
               }
             

}

{도메인 명}은 자신이 쓰는 도메인으로 변경하고, {퍼블릭 ip 주소}는 EC2 인스턴스의 퍼블릭 ip 주소로 변경해서 넣어주세요.


🏷 5. Github Action 작성

# This workflow will build a Java project with Gradle
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle

# Repo Action 페이지에 나타날 이름 
name: Spring Boot & Gradle CI/CD

# Event Trigger
# master branch에 push 또는 pull request가 발생할 경우 동작
# branch 단위 외에도, tag나 cron 식 등을 사용할 수 있음
on:
  push:
    branches: [ master ]

jobs:
  build:
    # 실행 환경 지정
    runs-on: ubuntu-18.04

    # Task의 sequence를 명시한다.
    steps:
      - uses: actions/checkout@v2

      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11
        ## create application.yaml
      - name: make application.yml
        run: |
          ## create application.yml
          cd ./src/main/resources
          # application.yml 파일 생성
          touch ./application.yml
          # GitHub-Actions 에서 설정한 값을 application.yaml 파일에 쓰기
          echo "${{ secrets.DATABASE }}" >> ./application.yml
        shell: bash

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
      
      # Build
      - name: Build with Gradle
        run: ./gradlew clean build

      ## 웹 이미지 빌드 및 도커허브에 push
      - name: web docker build and push
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -t ${{ secrets.DOCKER_REPO }}/bangu-web .
          docker push ${{ secrets.DOCKER_REPO }}/bangu-web
      ## docker compose up
      - name: executing remote ssh commands using password
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST_ID }}
          username: ec2-user
          key: ${{ secrets.PRIVATE_KEY }}
          script: |
            sudo docker rm -f $(docker ps -qa)
            sudo docker pull ${{ secrets.DOCKER_REPO }}/bangu-web
            docker-compose up -d
            docker image prune -f

[설명]

on:
  push:
    branches: [ master ]

✔️ master 브랜치에 push 될 때마다 배포하는 것으로 설정합니다.

checkout & setup

jobs:
  build:
    # 실행 환경 지정
    runs-on: ubuntu-18.04

    # Task의 sequence를 명시한다.
    steps:
      - uses: actions/checkout@v2

      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11
  • runs-on은 github action의 CI서버 runner가 어떤 환경을 갖출지 고르는 것 입니다. (ubuntu-18.04)

  • checkout: github action과 연결된 레포지토리의 코드를 runner로 옮기는 것

  • set up: JDK 11 버전 으로 작성된 프로젝트이기 때문에 JDK 11 로 설정(JDK는 프로젝트마다 변경해주세요.)

환경파일 작성

 ## create application.yaml
      - name: make application.yml
        run: |
          ## create application.yml
          cd ./src/main/resources
          # application.yml 파일 생성
          touch ./application.yml
          # GitHub-Actions 에서 설정한 값을 application.yaml 파일에 쓰기
          echo "${{ secrets.DATABASE }}" >> ./application.yml
        shell: bash
  • DB정보가 담긴 application.yml 파일을 github에 올리지 않고 레포지토리 settings의 secret에 DATABASE라는 이름으로 올려놨습니다.
    ✔️ 따라서 secret 파일을 runner에 만들어줄 수 있습니다.

빌드하기

## gradle build
    - name: Build with Gradle
      run: ./gradlew bootJar
  • build 폴더가생기고 그 안에 .jar가 생성되고, Runner에 생깁니다.

도커허브에 이미지 빌드해서 넣기

## 웹 이미지 빌드 및 도커허브에 push
      - name: web docker build and push
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -t ${{ secrets.DOCKER_REPO }}/bangu-web .
          docker push ${{ secrets.DOCKER_REPO }}/bangu-web

✔️ 새로 빌드한 파일을 넣은 이미지를 빌드합니다. 그 이미지를 docker hub에 push해서 EC2서버에서 가져다가 쓸 수 있게 합니다.

✔️ 레포이름/{프로젝트 명} 이미지가 작성해둔 dockerfile을 기반으로 생성됩니다.

도커 컴포즈 업

## docker compose up
      - name: executing remote ssh commands using password
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST_ID }}
          username: ec2-user
          key: ${{ secrets.PRIVATE_KEY }}
          script: |
            sudo docker rm -f $(docker ps -qa)
            sudo docker pull ${{ secrets.DOCKER_REPO }}/bangu-web
            docker-compose up -d
            docker image prune -f

✔️ appleboy/ssh-action@master를 통해서 ssh접속이 가능합니다.

  • 접속시 필요한 정보는 모두 secret에 등록합니다.

  • 필요 없는 컨테이너들을 지워주고, web 이미지를 docker hub에서 pull받고 docker-compose up -d(background 실행)로 실행 하게 됩니다.


6. main에 push하고 확인하기

✔️ master 브랜치에 push하면 Github Actions의 절차대로 CI/CD가 진행됩니다.


[시크릿 등록하기]

시크릿은 repository settings에 Secrets>Actions에 등록해주면 됩니다.

[EC2 ssh-action으로 접속하기]

✔️ 터미널에서 작업을 자동화 할수 있습니다.

  • ssh 키를 우선 세팅합니다. 내 컴퓨터에서 키를 만들기 위해 CMD 터미널을 띄웁니다.
    배포할 서버에 public 키를 넣어두고 private 키로 접근합니다.
    https://github.com/appleboy/ssh-action

$ ssh-keygen -t rsa -b 4096 -C "[내메일]" -f spring-cicd

✔️ CMD 창에서 위 방식을 통해 spring-cicd, spring-cicd.pub 파일을 만들어 줍니다.

  • 공개키(spring-cicd)의 내용은 EC2 서버의 ~/.ssh 경로의 authorized_keys 에다가 붙여 넣어주세요.

  • spring-cicd 파일에 담긴 개인키의 내용(!!모두 복사해야 합니다.) GITHUB SECRET의 PRIVATE_KEY로 등록합니다.

profile
Backend Developer

0개의 댓글