[CI/CD] - Git Actions와 Docker로 EC2에 자동 배포

GOSS·2024년 9월 17일

CI/CD

목록 보기
3/3
post-thumbnail

CI/CD 요약 정리

Git Actions를 통해 main에 변경사항(내가 지정한)이 일어나면
해당 프로젝트를 원하는 Task로 빌드하여 Docker에 Image를 만들고 Push한 다음
EC2에 접속해 이전 이미지를 제거하고 최근에 푸시된 Image를 pull받아 Run하는 과정을 자동화할 예정이다.


1. AWS EC2 생성 및 Docker 설치

이 포스트에선 EC2 서버를 만들었다는 가정하에 진행합니다.
없다면 이 글을 참고하시면 될 거 같아요!! -> EC2 생성&접속하기

EC2에서 기억할 것은 4가지이다. 메모장이나 다른 어딘가에 기억해두자.
1. EC2 Host(Public IPv4 DNS - 탄력적 IP를 설정했다면 항상 고정)
2. EC2 key(pem 키 파일)
3. EC2 Username(기본 ubuntu)
4. Port(8080)

EC2에 Docker 설치

EC2에 접속해 다음과 같이 Docker를 설치한다.

  • ubuntu 시스템 패키지 업데이트
sudo apt-get update
  • 필요한 패키지 설치
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
  • Docker의 공식 GPG 키 추가
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  • Docker의 공식 apt 저장소를 추가
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
  • ubuntu 시스템 패키지 업데이트
sudo apt-get update
  • Docker 설치
sudo apt-get install docker-ce docker-ce-cli containerd.io
  • 도커 실행 상태 확인
sudo systemctl status docker

sudo systemctl status docker 명령어 실행하면 다음과 같이 나오는데
이후에 'q'를 누르면 빠져나올 수 있다.

  • 도커 실행 테스트
sudo docker run hello-world

여기까지 진행하여 'Hello from Docker' 라는 메세지가 나오면 성공이다.


2. Dockerfile 생성

해당 프로젝트에 Dockerfile을 추가해준다.
Dockerfile의 코드는 아래와 같이 작성한다.

FROM openjdk:17-jdk-slim
ADD /build/libs/*.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

만약 레포지토리 안에 여러 개의 프로젝트가 있다면
/build 앞에 그 프로젝트 이름을 적어주면 된다. >> 해당 경로 추적 시 .jar이 있으면 됨.

3. Docker hub Repository 생성

Docker Hub를 사용하기 위해 회원가입 후 로그인한다. -> Docker Hub 바로가기
로그인 한 후 [Create repository]를 클릭한다.

Repository Name을 작성하고 [Create]를 눌러 생성한다.

Repository를 생성했다면 메모장이나 다른 어딘가에 아래 3가지를 적어 기억해두자.

  1. 내 Docker Hub 계정 유저이름(위에 Namespace)
  2. 내 Docker Hub 계정 비밀번호
  3. 생성한 Repository의 이름(위에 Repository Name)


4. Actions secret 생성

먼저 secret키를 만들어보자.
[Settings] - [Secrets and variables] - [Actions] 로 가서
[New repository secret] 버튼을 클릭한다.

이전에 기억해두었던 것들을 secret을 통해 환경변수로 설정할 것이다.

NameSecret
EC2_HOSTPublic IPv4 DNS
EC2_KEYpem 키 파일
EC2_USERNAMEubuntu (or 내가 만든 이름)
PORT8080
DOCKERHUB_USERNAME내 Docker Hub 계정 유저이름(Namespace)
DOCKERHUB_PASSWORD내 Docker Hub 계정 비밀번호
PROJECT_NAME생성한 Repository의 이름(Repository Name)

Name 과 Secret을 알맞게 입력한다.

EC2_KEY에 pem키를 입력할 때 조심해야 하는데
BEGIN RSA ~ END RSA도 함께 복붙해야한다.(즉, 전체 복붙하면된다)

위와 같이 7개를 안 틀리고 잘 입력하면 된다.




5. Actions workflow 생성

Actions 탭에 들어가 Java with Gradle의 [Configure]을 클릭한다.

클릭하면 아래와 같이 기본 gradle.yml이 나온다.

안에 내용을 지우고 아래과 같이 수정한다.(복붙 ㄱㄱ)


# ec2Test main wolkflow
name: ec2Test Project CI/CD

# 어떤 이벤트가 발생하면 workflow 실행할 지 명시
on:
  # main 브런치에 push나 pull request 발생 시
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

# 위 이벤트 발생 시 실행될 작업들
jobs:
  build:
    # VM의 실행 환경 지정 -> ubuntu 최신 버전
    runs-on: ubuntu-latest

    # 실행될 jobs를 순서대로 명시
    steps:
    - name: Checkout
      uses: actions/checkout@v4

    # JDK 17 설치  
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'

    # Gradle Build를 위한 권한 부여
    - name: Grant execute permission for gradlew
      run: chmod +x ./gradlew

    # Gradle Build(test 제외)
    - name: Build with Gradle
      run: ./gradlew clean bootJar --exclude-task test

    # DockerHub 로그인
    - name: DockerHub Login
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_PASSWORD }}

    # Docker 이미지 빌드
    - name: Docker Image Build
      run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }} .
      

    # DockerHub Push
    - name: DockerHub Push
      run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}
      

    # EC2 인스턴스 접속 및 애플리케이션 실행
    - name: Application Run
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_HOST }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_KEY }}
        script: |
          echo "Stopping existing container..."
          sudo docker kill ${{ secrets.PROJECT_NAME }}
          sudo docker rm -f ${{ secrets.PROJECT_NAME }}
          
          echo "Removing existing image..."
          sudo docker rmi ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}
          
          echo "Pulling new image..."
          sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}

          echo "Running new container..."
          sudo docker run -p ${{ secrets.PORT }}:${{ secrets.PORT }} \
          --name ${{ secrets.PROJECT_NAME }} \
          -d ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}
          
          echo "Done"

주석에 써진 프로젝트 이름은 자신의 프로젝트에 알맞게 바꾸면 된다.
해당 gradle.yml에 각 명령어가 어떤 명령을 수행하는지 주석으로 설명을 간단히 써두었다.
Actions secret에 만들어둔 환경 변수들이 이 안에 쓰인다.

프로젝트에 맞는 빌드 선택하기

나는 프로젝트가 스프링부트 내장 톰캣을 사용하기에
중간에 # Gradle Build 를 보면 bootJar task를 사용하였다.
프로젝트에 맞는 빌드 task를 선택해 빌드하면 된다.
그리고 Dockerfile도 빌드 형식에 맞게 변경해야 한다. ( jar / war )

  • 내/외장 톰캣과 JSP 사용 여부에 따른 빌드 형식

    톰캣 종류JSP 사용 여부빌드 형식설명
    내장 톰캣미사용bootJar내장 톰캣 포함 .jar 파일, 독립 실행 가능
    내장 톰캣사용bootWarJSP 포함 .war 파일, 내장 톰캣에서 실행 가능
    외장 톰캣미사용jar외장 톰캣에 배포 가능한 .jar 파일
    외장 톰캣사용warJSP 포함 .war 파일, 외장 톰캣에서 실행 가능

이후 오른쪽 위에 [Commit changes]를 클릭해 레포지토리에 반영한다.

반영을 하면 Actions에 가보면 해당 workflow가 진행중임을 확인할 수 있다.
눌러보면 build하는 진행 상황도 볼 수 있다.


push해서 테스트

이제 테스트를 한 번 해보자.
기존 웹 페이지는 다음과 같다.

해당 index.html에 'CI/CD가 잘 구축되었나요??' 라는 문구를 추가하여 main에 push를 하게 되면

Actions에서 push를 감지해 내가 설정한 workflow (Docker에 프로젝트가 올라가고 EC2에서 Pull 받아 프로젝트 Run 하기)를 타게 된다.


workflow의 작업이 마무리 되면

이렇게 초록불이 들어오고 ec2에도 반영이 잘 되었음을 확인할 수 있다.



여기까지 Git Actions와 Docker, Ec2를 이용한 CI/CD에 대해서 정리해보았습니다.
제가 개발 유년기 단계라 부족한 점이 많습니다...
부족한 점 지적해주시면 공부해서 발전하도록 하겠습니다. 감사합니다.








References

CI/CD with GitHub Actions

ubuntu에 Docker 설치하기

배포 방법 비교(JAR vs WAR)

profile
새싹개발자[유년기II]

0개의 댓글