본문 바로가기
Docker

[Docker] Github Actions을 활용한 Spring 자동 배포, Docker Image run(CD) (2)

by 도전하는 린치핀 2024. 4. 9.

0. GitHub Actions를 통한 CD 프로세스 진행하기 전

 

이전 포스팅에서 Github Actions를 사용한 CI 자동화 프로세스 플로우에 대해서 알아봤다.

 

이번 포스팅에서는 CD 자동화 프로세스 플로우를 학습하고 구축하는 것을 목표로 하려고 한다.

즉, 이번 포스팅에서는 진행할 내용 중요한 목차는 아래와 같다.

  • Github Actions에서 EC2에 접근하기
    • 이때, IAM 기능을 통해서 Github Actions이 EC2에 접근할 수 있는 권한을 설정해야 한다.
  • 기존 EC2에서 구동중인 Docker Container 종료 후 CI를 통해 새롭게 생성된 Docker Image 구동
    • 사실 바로 처음 Docker Container를 구동한다면 종료하는 기능이 필요 없을 수 있지만 깔끔하고 에러가 없는 것이 좋으니 종료하고 구동하도록 하자.

 

- 이번 포스팅에서는 CD 프로세스를 이전 포스팅에서 작성한 Github Actions의 gradle.yml 파일을 수정하여 자동화 하는 것을 다루며, Docker 이미지를 통한 배포를 진행할 것이다.

 

 

이전 포스팅과 같이 만약 아직 배포 프로세스에 대해서 아직 파악하지 못했다면 이전 포스팅을 통해 (1)프로세스 플로우를 제대로 확인하고 (2)GithubActions를 활용한 CI 이관을 이해하고 이번 포스팅을 읽어야 한다.

 

추가적으로 AWS에서 제공하는 CodeDeploy라는 기능이 이미 있다고 한다.
하지만 CodeDeploy 기능에 대해서는 추후 포스팅에서 다루기로 하고, 이번 포스팅에서는 Github Actions만을 이용하여 CI/CD 프로세스를 이해하는 것으로 끝내자.

 

 

1. AWS IAM 설정 (Github Actions가 EC2에 접근할 계정)

 

  • AWS IAM : AWS의 특정 기능들을 사용할 수 있는 권한을 가진 사용자를 생성 할 수 있는 기능이다.
  • 이번 목차에서 설정하는 IAM 같은 경우에는 Github Actions에서 AWS EC2에 접근할 수 있도록 하는 사용자라고 생각하자
  • IAM 생성할 때 설정하는 Access Key를 Github Actions에 저장하여 IAM 로그인하여 EC2에 접근하는 원리라고 생각하자.
  • 이때 설정하는 Access Key는 외부에 노출되어서는 안되고, 안전하게 저장되어야 한다.

(1) IAM 사용자 생성

  • AWS IAM -> 대쉬보드 엑세스관리 -> 사용자 -> 사용자 추가
  • 사용자 이름을 설정(나중에 알아볼수 있는 이름을 권장한다.)

(2) IAM 생성 시 권한 설정

  • 권한 설정의 경우 직접 정책 연결 -> EC2FullAccess -> 선택 후 다음 -> 사용자 생성을 누르면 된다.

(3) IAM 생성 후 액세스 키 설정

  • IAM 생성 후 Detail 페이지에 들어가서 액세스 키 생성하기 버튼을 클릭한다.
  • AWS 외부(Github Actions)에서 접근할 것이기 때문에 해당 옵션을 선택한 후 액세스 키를 생성한다.
  • 이때 생성되는 액세스 키와 비밀 액세스 키는 반드시 따로 저장하여 보관하여야 한다.
  • 추후 Github Actions에서 접근하기 위한 ID/Password라고 생각하면 된다.

 

 

2. IAM을 통해 EC2에 접근하기 위한 설정 (ID, Password로 접근 설정)

  • EC2 인스턴스의 접속하는 것은 보통 .pem 키를 통해 ssh 연결하는 방식을 사용하여 터미널에서 접근한다.
  • 하지만 EC2에서 설정한 아이디/비밀번호를 통해 연결하는 방법이 존재한다.
  • Github Actions을 사용할 때는 AWS에 접근하려면  아이디/비밀번호를 통해  접근해야 하기때문에 추가적인 설정을 해야한다.

(1) EC2 내 아이디, 비밀번호 설정 

  • 아이디
    • 아이디의 경우 EC2의 username 이다. 모른다면 아래 명령어를 사용하여 확인하면 됩니다.
    • 커맨드를 사용하면 출력되는 내용이 username(아이디) 이다.
    • 보통 username의 경우 ec2-user로 설정되어 있긴 하다.
$whoami

 

  • 비밀번호
    • 아래 명령어를 입력하면, password 입력창이 나온다.
    • 비밀번호 또한 입력 후 사용하기 위해 따로 저장해야 한다.
    • 추후 IAM에 접속한 Github Actions이 EC2에 접근할 때 사용할 비밀번호이다. 
# 예시 - sudo passwd ec2-user
$sudo passwd [username]

 

(2) 패스워드 연결 방식 사용 설정

  • 위에서 아이디 패스워드를 설정하였지만 위의 설정을 통해 바로 EC2에서 아이디/패스워드로 접근할 수 없다.
  • 아래 나오는 방법을 통해 추가적인 설정을 하여 아이디/패스워드로 EC2에 접근을 허용해줘야 한다.

 

  • sshd_config 수정 
$sudo vi /etc/ssh/sshd_config

 

  1. vi 진입이후 /Password 입력 후 엔터를 통해 검색
  2. PasswordAuthentication : no -> yes 로 수정 후 저장

 

  • sshd service 재시작
    • 설정 저장 후 변경사항을 적용하기 위해 restart 해줘야 한다.
$sudo systemctl restart sshd 

 

3. Github Actions Secret Key 등록

 

이전 포스팅에서 설정한 Secrey Key외에도 이번 포스팅에서 추가적으로 설정해줘야 하는 Secret Key가 많다.

 

  • 1/2 번에서 설정하고 저장하라고 했던 키들을 Github Actions Secret Key에 저장해줘야 한다.
    • AWS_ACCESS_KEY_ID : IAM에서 설정한 Access Key
    • AWS_SECRET_ACCESS_KEY : IAM 에서 설정한 Secret Key
      • 위의 두개를 통해 Github Actions에서 IAM 사용자 권한을 얻을 수 있다.
    • AWS_SG_ID : EC2 보안그룹으로 지정되어있는 '보안그룹의 ID' 이다.
    • EC2_HOST : EC2의 퍼블릭 IPv4 주소
    • EC2_USERNAME : EC2의 계정명 (보통 ec2-user)
    • EC2_PASSWORD : EC2에 접근/연결 설정해두었던 패스워드
      • 마찬가지로 위의 두개를 통해 IAM 사용자 권한을 얻은 Github Actions가 EC2에 접근할 수 있다.
    • EC2_SSH_PORT : SSH로 접근할 port. (보통 22번 포트)

 

4. 프로젝트 내 gradle.yml 파일 수정

  •  GitHub Actions를 사용하여 AWS 접근할 경우 상당히 많은 프로세스들을 거쳐야만 한다.
  • GitHub Actions 에서 정한 '메뉴얼' 을 정확하게 지켜줘야한다.(띄어쓰기, 들여쓰기, 연결정보 및 연결방법 등등....)
  • 기본적인 코드를 작성할 때 필요한 "문법"이라고 생각해야 한다.
  • 또한 진행하면서 발생하는 에러의 경우 공식문서 를 통해서 확인할 수 있다.
name: Java CI with Gradle

# master 브랜치에 push, PR 이벤트 발생시 동작.
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      # Java 및 Docker 빌드를 위한 환경 설정
      - uses: actions/checkout@v3
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

      # Java 빌드를 위한 ./gradlew 파일 권한 변경
      - name: Run chmod to make gradlew executable
        run: chmod +x ./gradlew

      # Java 빌드
      - name: Spring Boot Build
        run: ./gradlew clean build

      # DockerFile 을 기반으로 Docker Image 빌드
      - name: docker image build
        run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/github-actions-demo .

      # Docker Hub 에 Login
      - name: docker login
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PW }}

      # Docker Hub 에 빌드된 이미지 push
      - name: docker Hub push
        run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/github-actions-demo

      # GET GitHub IP (5)
      - name: get GitHub IP
        id: ip
        uses: haythem/public-ip@v1.2

      # Configure AWS Credentials (6) - AWS 접근 권한 취득(IAM)
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-2

      # Add github ip to AWS (7)
      - name: Add GitHub IP to AWS
        run: |
          aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32

      # AWS EC2 Server Connect & Docker 명령어 실행 (8)
      - name: AWS EC2 Connection
        uses: appleboy/ssh-action@v0.1.6
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USERNAME }}
          password: ${{ secrets.EC2_PASSWORD }}
          port: ${{ secrets.EC2_SSH_PORT }}
          timeout: 60s
          script: |
            sudo docker stop github-actions-demo
            sudo docker rm github-actions-demo
            sudo docker run -it -d -p 8080:8080 --name github-actions-demo hyeonjjun/github-actions-demo

      # REMOVE Github IP FROM security group (9)
      - name: Remove IP FROM security group
        run: |
          aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32

 

+ 전체 flow 확인

  • 보안그룹을 기존에 github의 주소를 열어두지 않았기 때문에 IAM에서 AWS에 접근할 권한을 취득 했다고 해도, EC2에 접근을 하지 못하는 상황이 발생한다. (EC2 자체는 보안그룹 명시한 IP, port 만 허용)
  • EC2에 접근하여 docker 배포 하기전에, AWS EC2에 GitHub IP에 대해서 보안그룹을 추가해줘야 한다.
  • 모든 task가 종료된 이후에는 당연히 추가한 보안그룹을 삭제해야한다.(9번 task)
  • 보안그룹을 추가해주고 EC2에 접근 성공 했다면, 본래의 목적인 Docker Image를 컨테이너화 시켜줘야 합니다.
  • 이전 포스팅에서는 docker run 실행시 컨테이너 이름을 따로 지정해주지 않았기 때문에,
  • 기존의 컨테이너를 종료-삭제 task 가 수행되지 않았기 때문에 --name 명령어를 통해 이름을 지정해야 한다
  • Docker 명령어를 보자면, 기존의 컨테이너 종료 -> 삭제 후 docker run 을 통해 docker hub의 image 를 실행 시켜준다.

 

++   추가된 각 step 별 설명을 하겠습니다. (주석 오른쪽 (번호) 기준) 

 

  • (5) :  Get Git Hub IP -> 추후 보안그룹에 깃허브의 포트를 열어줘야 하기 때문에, 깃허브의 IP를 얻어오는 step.
  • (6) : Configure AWS Credentials
    • AWS 서비스에 접근하기 위해서는, 우선적으로 접근권한을 취득해야합니다.
    • 미리 설정해둔 IAM Key 를 통해서 접근권한을 취득하는 task 입니다.
    • uses: 부분은 자세하게 확인하지 못했지만 GitHub actions에서 정의한 프로토콜을 사용한다.
  •  (7) : Add GitHub ip to AWS
    • 위에서 설명 했듯, EC2에 접근하기 위해서는 보안그룹을 추가해줘야 하기 때문에 Github IP를 보안 그룹에 추가한다.
  • (8) : AWS EC2 Server Connect
    • EC2에 연결, 수행할 Script를 수행하는 task 이며, Docker hub에서의 image pull & run 을 수행한다.
    • 추가로 현재 구동중인 컨테이너를 정지 - 삭제 후 docker run 을 수행합니다. (컨테이너가 중복되는것을 방지한다. + 만약 컨테이너가 구동중이지 않으면 에러가 발생할 수 있지만 새로운 컨테이너 구동에 문제가 되지는 않는다.)
  • (9) : REMOVE GitHub ip FROM security group
    • EC2에 접근하기 위해서 추가해준 보안그룹을 삭제하는 task.
    • 삭제하지 않으면, 해당 Actions가 수행될 때마다 보안그룹이 추가되고, 보안에 취약해진다. 

 

 


 

이번 포스팅을 통해 Github Actions / Docker / EC2 를 활용한 CI/CD 자동 배포 파이프라인을 직접 설계하고 성공해봤다.

물론 처음 Docker File / Github Actions(gradle.yml) / AWS IAM 설정 / Access Key /  Github Actions Secret Key 등 신경쓰고 설정해야 할 것이 많지만 배포 프로세스에 대해서 확실하게 알 수 있었고, 자동화를 통한 컴퓨팅 자원이나 배포 시간 등을 감소시킬 수 있을 것 같다는 생각이 들었다.

 

실제로 현업에서는 어떤 방식을 통해 배포 프로세스를 진행할 지 모르고 추가적으로 AWS의 너무나 많은 기능들을 사용해보지 못하고 Github Actions을 최대한 활용해보는 방식으로 진행해보았지만 AWS에 대해서 더 많은 기능(ELB / Auto Scaling 등)을 사용해보면 더 편한 배포 프로세스를 진행할 수 있을 것 같다.

 

추가적으로 부족한 부분이나 잘못된 부분에 대해서 피드백 주시면 감사한 마음으로 수정하겠습니다.