본문 바로가기

TIL & WIL

WIL_220712_CI/CD 구축 후기(AWS, Docker, Spring Boot, github actions)

항해99 과정 중 진행하게 된 CI/CD에 대해 이야기 해보고자 한다.

CI/CD란 한마디로 말하면 빌드/테스트 자동화 과정이라고 말할 수 있다.

CI(Continuous Integration), CD(Continuous Deployment)

 

확실히 기존 방식처럼 Ubuntu 서버에 직접 JAR파일을 빌드한 뒤, FileZilla를 통해 업로드하고

GitBash에서 해당 EC2 서버에 원격 접속하여 빌드된 파일을 직접 실행하다 보면

조그마한 수정사항이 발생할 때마다 다시 저 과정을 반복해야하는 '번거로움'이 있었다.

그 번거로움을 피하기 위해 우리는 프로그래밍을 배우는 것 아닐까?

 

그래서 우리 조에서는 이 CI/CD 구축을

  • AWS EC2 (Amazon Linux 2 AMI)
  • Github Actions
  • Docker

를 통해 진행하였고 서버 배포까지 잘 구동되는 것을 볼 수 있었다.


1. EC2  Linux 2 AMI 구축

우리 조원 한 분이 직접 AWS에서 EC2 인스턴스를 생성하였고,

Linux 2 AMI 서버를 만들었다. 각자의 고유 키페어를 생성하고 Git Bash와 같은 쉘을 이용하여

해당 서버에 원격으로 접속하는 것이 핵심인데, 여기서는 Putty라는 리눅스 원격제어 프로그램을 이용한다.

이에 관한 자세한 내용은 구글링해보면 워낙 레퍼런스들이 많기 때문에 생략한다.

 

2. EC2에 Docker 설치 및 명령어

그리고 EC2에 우리가 사용해야 할 Docker 환경을 설정해주도록 한다.

해당 명령어를 통해 설치를 간편하게 진행할 수 있다.

// EC2 리눅스 서버에서 도커 환경설정
sudo yum update -y
sudo yum install -y docker
sudo service docker start
sudo systemctl enable docker
sudo usermod -a -G docker ec2-user

// 도커 상태 확인
systemctl status docker.service

3. Github Actions에서 gradle.yml 파일 생성

Github actions의 Workflow를 설정해줄 gradle.yml 파일을 생성하도록 한다.

Github 레파지토리 > Actions > Continuous Integration에서 Java with Gradle의 Configure를 클릭한다.

그러면 ~/.github/workflow/gradle.yml 이라는 파일이 생성이 될 것이다.

** 해당 이미지는 CI/CD 구축을 위한 레퍼런스 블로그에서 가져왔다.(레퍼런스 블로그)

 

각자의 환경에 맞게 만들어진 해당 코드를 기반으로 프로젝트의 .github/workflows/gradle.yml 추가하면 되고

우리 조의 gradle.yml 파일 내용은 다음과 같다.

name: Java CI with Gradle

on:
  push:
    branches: [ "master" ]
#  pull_request:
#    branches: [ "master" ]

permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

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

      ## create application.properties
    - name: make application.properties
      run: |
        cd ./src/main/resources
        touch ./application.properties
        echo "${{ secrets.PROPERTIES }}" > ./application.properties
      shell: bash

      ## create application-alpha.properties
    - name: make application-alpha.properties
      run: |
        cd ./src/main/resources
        touch ./application-alpha.properties
        echo "${{ secrets.ALPHA_PROPERTIES }}" > ./application-alpha.properties
      shell: bash

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

    - name: Docker build
      run: |
        docker login -u ${{ secrets.USERNAME }} -p ${{ secrets.PASSWORD }}
        docker build -t mung-friend .
        docker tag mung-friend souliat/mung-friend:${GITHUB_SHA::7}
        docker push souliat/mung-friend:${GITHUB_SHA::7}

    - name: Deploy
      uses: appleboy/ssh-action@master
      with:
        host: ec2-3-39-6-175.ap-northeast-2.compute.amazonaws.com
        username: ec2-user
        key: ${{ secrets.PRIVATE_KEY }}
        envs: GITHUB_SHA
        script: |
          docker pull redis
          docker run --name redis -d -p 6379:6379 redis
          docker pull souliat/mung-friend:${GITHUB_SHA::7}
          docker tag souliat/mung-friend:${GITHUB_SHA::7} mung-friend
          docker stop server
          docker run --rm --name server -p 80:8080 --link redis:redis -d mung-friend

각각의 과정을 간단히 설명해 보면 해당 gradle.yml 파일에서 하는 작업의 순서는 이와 같다.

  • Github의 Master 브랜치에 푸시를 하는 것을 발동 조건으로 CI/CD Jobs가 시작된다.
  • JDK 8버전, JAVA 8버전으로 환경 세팅을 해준다.
  • MySQL, Redis, Oauth2, AWS S3, Naver SENS 등에 접근하기 위한 정보가 담긴 application.properties 정보를
    github actions에서 직접 만들어 준다.(gitignore 되어있기 때문)
  • build gradle을 해준다.
  • 나의 Docker에 username과 password(Github actions > Settings > Secrets 탭에 저장된 정보)를 통하여 로그인하고 Docker Image를 빌드하여 Docker Hub에 Push 한다.(아래 이미지처럼 정보를 따로 저장해놓음)

  • 만들어놓은 EC2에 username과 host IP주소, RSA키를 통하여 접근한다.
  • Docker hub에 push 해놓은 Docker image를 다시 pull 받아오고 tag 옵션을 통해 이름을 지정한다.
  • 서버를 잠시 stop하고, 다시 docker run 명령어를 통해 프로젝트 image와 Redis image를 docker 컨테이너에 올리면서
    서버를 시작한다.

여기서 잠깐! EC2에서 활용 할 RSA키를 만들어 활용하는 방법은 다음과 같다.

# 1. 프로젝트 디렉에 RSA 키 만들어주고  (꼭 프로젝트 디렉 아니어도됨.)
ssh-keygen -t rsa -b 4096 -C "[내메일]" -f spring-cicd

# 2. 인스턴스(리눅스서버)에 공개키 등록
공개키를 등록할 때는 아래의 파일을 열어 삽입해 주면 된다.
$ vi ~/.ssh/authorized_keys
a 누르면 수정 가능한 상태로 바뀌며, 개행하여 위에서 생성한 RSA 공개키를 등록한다
종료하고 싶다면 :x

# 3. 깃허브 Settings - Secrets에 PrivateKey 등록하기
여기에선 New Repository Secret 클릭하여 RSA 개인키 내용 전부 복붙!

Docker Hub에도 직접 접속해보면 해당하는 image들이 잘 Push 되어 있었음을 확인 할 수 있다.

(그러니까 Pull이 가능한거지!)

 

4. build.gradle, Dockerfile 설정

jar {
    enabled = false
}

bootJar{
    archivesBaseName = 'app'
    archiveFileName = 'app.jar'
    archiveVersion = "0.0.0"
}

build.gradle 파일에 해당 코드를 삽입해 준다.

스프링부트 2.5.0 이후 plain.jar가 자동으로 만들어지기 때문에 꺼두는 것이라고 한다.

false처리를 하지않으면 "1개 이상의 jar파일이 있으니 무엇을 실행할지 정하기 전까지는 실행할 수 없다"라는 의미의

에러가 떴던 것으로 기억하는데, 에러 메시지를 캡처해두지 않았다...! 이런ㅠㅠ (미리미리 기록하자)

 

FROM openjdk:8-jdk-slim-buster
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Dspring.profiles.active=alpha", "-jar","/app.jar"]

Project 밑에 Docker파일을 생성하고 위의 코드를 삽입하여준다.

Project를 빌드하게 되면 이제 app.jar라는 이름으로 jar파일이 생성된다.

원래는 "프로젝트명 + 버전 + jar" 형식으로 생성된다.

 

 

자 여기까지 진행을 하면 우리 프로젝트를 Github에 푸시하였을 때, Github Actions가 발동되면서 자동으로 빌드가 되고

Docker hub에 image가 빌드되어 Push 되며, EC2 서버에서는 다시 Docker hub에서 docker image를 Pull 받아와서

배포까지 원활하게 이루어지는 것을 볼 수 있다.

 


마지막으로는 따로 정리했던 자주 쓰였던 Docker 관련 명령어들을 기록하며 마무리해본다.

 

- 도커 이미지 빌드

docker build . 

 

- 특정 값을 주면서 도커 이미지 빌드

docker build . -t [이미지이름]:[버전]   

// 이렇게 하면 <Repository>에는 docker-memo가, <Tag>에는 version1이 들어간다.

 

- 레이어 확인

docker history [이미지이름]:[버전]

// 도커에는 '레이어'라는 개념이 존재함. 베이스 이미지에서 각각의 명령어가 실행될 때 마다 레이어가 하나씩 생기게 됨.
// 도커 이미지는 도커 파일로부터 일관성 있게 만들어지기 때문에, 동일한 환경에서 동일한 커맨드로 빌드하게 된다면,
// 이전 결과물들이 있으므로 docker build를 하게 되면 두번 째 했을 때 빌드 시간이 훨씬 줄어든다.(ex. 8초 -> 0.1초)

 

- 도커 이미지 가져오기
docker pull [계정명]/[이미지이름]
docker pull [계정명]/[이미지이름]:[최신버젼]

 

- 도커 이미지 푸시하기

docker push [계정명]/[이미지이름]:[버전]

 

- 도커 이미지, 컨테이너 삭제하기 (전체 삭제)
docker system prune -a

 

- 도커 실행하기
docker run -d -p 80:8080 [계정명]/[이미지이름]:[버젼]

// 여기서 80은 호스트 포트, 8080은 컨테이너 포트.

// -d 옵션은 디태치 옵션으로 백그라운드로 실행한다는 것이고
// -p 옵션은 퍼블리시 옵션으로 docker-memo:version1의 컨테이너의 8080번 포트를 로컬 호스트 머신의 80번 포트로 연결하겠다는 것.

 

- 도커 로그 확인
docker logs [컨테이너id]

 

- 실행 중인 컨테이너 확인

docker ps

 

- 실행 중이지 않은 컨테이너까지 모두 확인

docker ps -a

 

- 컨테이너 내부의 파일목록과 폴더구조 불러오기

docker exec [컨테이너 ID] ls 

 

- 정지된 컨테이너 재시작

docker restart [컨테이너 ID]

 

- 컨테이너 정지

docker stop [컨테이너 ID]

 

- 컨테이너 삭제

docker rm [컨테이너 ID]

 

- 컨테이너 강제삭제(컨테이너가 실행되고 있을 경우도)

docker rmi [컨테이너 ID]

 

**** 개념 이해 ****
- docker image & container
   - image는 어플리케이션을 실행하기 위한 필요한 모든 것이 생성되어 있는 파일.
   - container는 image를 사용하여 실행한 공간입니다. 프로세스의 독립성을 보장하기 위해서 네트워크나 저장소가 분리되어 있다.
- docker registry
   - docker image를 저장하는 공간
- docker daemon(docker d)
   - docker API requests를 받아서 docker object(image, container등)를 관리
- docker client
   - 사용자가 docker를 사용하기 위해서 사용하는 cli

 


<참고 블로그>

https://zzang9ha.tistory.com/404?category=954133 

 

GitHub-Actions로 CI/CD 구축하기(AWS, Docker, SpringBoot)

GitHub-Actions로 CI/CD 구축하기(AWS, Docker, SpringBoot) 안녕하세요, 이번 시간에는 GitHub-Actions로 CI/CD를 구축하는 방법에 대해 알아보겠습니다. 해당 포스팅이 CI/CD를 전체적으로 포함하고 있기는..

zzang9ha.tistory.com

https://velog.io/@rlafbf222/Spring-Boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-CI-CD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-%ED%95%AD%ED%95%B4%EC%9D%BC%EC%A7%80-54%EC%9D%BC%EC%B0%A8

 

Spring Boot 프로젝트 CI / CD 구축하기 (항해일지 54일차)

AWS EC2 Linux + GitHub Actions + Docker로 CI/CD 파이프라인 구축하기

velog.io

https://itcoin.tistory.com/685?category=769089 

 

Github actions를 이용한 CICD - 2

지난 깃헙액션 포스팅에서 깃헙액션을 이용해 빌드 자동화를 마쳤습니다. 이번 포스팅에서는 도커를 사용해서 배포 준비를 할 것입니다. 도커를 사용한다면 어디에서 동일한 환경의 배포를 할

itcoin.tistory.com