GitHubAction 배포 방법(Spring, MySQL, Docker)

infobox503 2024. 11. 25. 15:55

GithubAction 사용 정리

  • 순서
    • AWS EC2 서버 생성
    • EC2 서버에서 프로그램 실행 환경 조성
    • 깃허브 액션 파일 생성
    • 실행

(1) AWS EC2 서버 생성

  • 설정
    • Ubuntu 선택
    • t3.small 선택
    • 키 페어(로그인) 설정
    • 방화벽
      • 인터넷에서 HTTPS 트래픽 허용
      • 인터넷에서 HTTP 트래픽 허용
    • 구매 옵션
      • 스팟 인스턴스
    • 인바운드 규칙 편집
      • 8080 포트 열기

 

 

(2) EC2 서버에서 프로그램 실행 환경 조성

  • 순서
    • EC2 서버 접속
    • EC2 서버에 도커 설치
    • EC2 서버에 도커 DB(MySQL) 이미지 다운 및 컨테이너 실행

(2-1) EC2 서버 접속

(Ubuntu 환경에서 진행)

  1. EC2 서버를 열었던 키페어의 권한 설정
    • ec2_test.pem으로 EC2 서버를 열었다
    • 해당 파일을 읽을 수 있도록 읽기 권한을 허용한다
sudo chmod 400 ec2_test.pem

 

 

  2.  EC2 서버에 접속한다

  • ec2-3-38-189-4.ap-northeast-2.compute.amazonaws.com로 연결하려고 한다
  • 3-38-189-4만 사용하면 된다
//ec2_test.pem에 대한 열기 권한을 획득 했으므로 ec2_test.pem 읽기 가능
ssh -i ec2_test.pem ubuntu@3.38.189.4

 

 

(2-2) EC2 서버에 도커 설치

  • 흐름
    • 위의 과정에서 EC2 서버에 접속했다.
    • 접속한 EC2 서버에 도커를 설치하려고 한다.
  • 코드
    //관리자 권한 얻기
    sudo su
    
    //업데이트
    apt-get update
    apt-get upgrade
    
    //도커 설치
    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 -
    
    add-apt-repository "deb [arch=amd64] <https://download.docker.com/linux/ubuntu> $(lsb_release -cs) stable"
    
    apt-get update
    
    apt-get install docker-ce docker-ce-cli containerd.io
    
    curl \\
        -L "<https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$>(uname -s)-$(uname -m)" \\
        -o /usr/local/bin/docker-compose
        
    chmod +x /usr/local/bin/docker-compose

(2-3) EC2 서버에 도커 DB(MySQL) 이미지 다운 및 컨테이너 실행

  • 목적
    • 실행하고자 하는 프로그램(Spring)을 동작하기 위한 DB(MySQL)가 필요하다.
    • 도커를 통해 DB 이미지를 만들고 컨테이너를 만든다.
  • 코드
//관리자 권한 얻기
sudo su

//해당 프로그램이 mysql:8.0.32 버전을 사용함.
//따라서 8.0.32버전의 이미지 다운로드
docker pull mysql:8.0.32

//이미지 실행(컨테이너 제작)
//내 프로그램의 docker-compose.yml에 적은 그대로 이미지를 실행하면 된다.
docker run -d \\
  --name mysqldb \\
  -e MYSQL_ROOT_PASSWORD=root \\
  -e MYSQL_DATABASE=yogoyogu \\
  -p 3306:3306 \\
  mysql:8.0.32

//DB 컨테이너에서 사용자 권한 등록

//DB 컨테이너 접속
docker exec -it MySQL컨테이너ID bash
mysql -u root -p //root 이름으로 접속

//mysql 데이터베이스에서 내가 사용할 계정이 있는지 확인
use mysql; //mysql 데이터베이스 접속
select host, user from user; //user 테이블에서 접근 권한자 목록 확인 가능

//내가 사용할 계정이 없다면 새로 추가
create user '이름'@'%' identified by '비밀번호'; // 이름 추가
flush privileges; // 저장

//내가 사용항 데이터베이스가 있는지 확인하고, 없으면 새로 추가
show databases; //전체 데이터베이스 목록 확인
create database 데이터베이스이름 // 없으면 데이터 베이스 추가

// 해당 이름의 사용자에게 데이터베이스가 얼마만큼의 권한을 주는지 확인
show grants for 이름@'%';

//사용할 데이터베이스에, 내 사용자명에 대한 권한 추가
//(이렇게 해야 해당 데이터베이스에 대한 CRUD 가능)
grant all on 데이터베이스이름.* to '이름'@'%'; //해당 데이터베이스에 대해서 모든 권한 획득
flush privileges; // 저장

 

 

※ DB 이미지 실행(컨테이너 제작) 시, 주의점

⇒ 내가 만든 프로그램의 docker-compose.yml의 DB와 동일하게 제작했는지 확인해야함

  • 예시
    • docker-compose.yml
      • 해당 코드는 실행할 프로그램의 docker-compose.yml이다
      • 해당 코드에서 실행할 DB에 대한 정보는 mysqldb 부분이다.
      • DB가 갖춰야할 정보는 다음과 같다.
        • 호스트명 : mysqldb
        • 버전 : mysql:8.0.32
        • 데이터베이스 이름 : yogoyogu
        • 해당 DB를 사용하는 사용자의 비밀번호 : root
        • 포트 번호 : 3306
    services:
    	//데이터베이스
      **mysqldb**:
        image: mysql:8.0.32
        restart: always
        environment:
          MYSQL_DATABASE: yogoyogu
          MYSQL_ROOT_PASSWORD: root
        ports:
          - 3306:3306
    	
    	//실행할 프로그램
      backend:
        build: .
        restart: always
        //어떤 컨테이너를 사용할지 정함
        //또한, 해당 컨테이너를 어떤 사용자로 접속할지 등록
        environment:
          SPRING_DATASOURCE_URL: **jdbc:mysql://mysqldb:3306/yogoyogu**
          SPRING_DATASOURCE_USERNAME: root
          SPRING_DATASOURCE_PASSWORD: root
        ports:
          - 8080:8080
        // 컨테이너 활성화 순서 정함
        // backend는 mysqldb 컨테이너에 의존하고 있음
        // 따라서, mysqldb 컨테이너 생성 후에 backend 컨테이너 실행
        depends_on:
          - mysqldb
    
  • DB 이미지에 반영하기
    • 위에서 언급된 DB 내용들을 반영하여, 이미지를 컨테이너로 만든다.
    docker run -d \\
    	//컨테이너 이름(호스트명이 없다면 컨테이너 이름이 호스트명이 됨)
      --name mysqldb \\
      -e MYSQL_ROOT_PASSWORD=root \\
      -e MYSQL_DATABASE=yogoyogu \\
      -p 3306:3306 \\
      mysql:8.0.32
    

※ 호스트명을 빠뜨리지 않는게 중요함

  • backend 컨테이너는 jdbc:mysql://mysqldb:3306/yogoyogu 해당 주소의 컨테이너를 요청하고 있음
  • 만일 DB를 mysqldb로 호스트명을 정하지 않으면 backend 컨테이너는 DB를 찾지 못함
	//실행할 프로그램
  backend:
    build: .
    restart: always
    environment:
      SPRING_DATASOURCE_URL: **jdbc:mysql://mysqldb:3306/yogoyogu**
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: root
    ports:
      - 8080:8080
      
    depends_on:
      - mysqldb

(3) 깃허브 액션 파일 생성

  • 순서
    • 사용한 깃허브 액션 파일
    • 깃허브 액션 설명

(3-1) 사용한 깃허브 액션 파일

  • 내 프로젝트가 있는 깃허브 주소에 다음과 같은 파일을 생성했다.
  • 경로 : .github/workflows/gradle.yml
name: Java CI with Gradle2

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

env:
  DOCKER_IMAGE_NAME: testimage
  EC2_HOST: ec2-3-38-186-97.ap-northeast-2.compute.amazonaws.com
  EC2_SSH_USER: ec2-user
  PRIVATE_KEY: ${{ secrets.EC2_SSH_PRIVATE_KEY }}

jobs:
  build-and-push-docker:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v4

    - name: Set up JDK 21
      uses: actions/setup-java@v4
      with:
        java-version: '21'
        distribution: 'temurin'

    - name: Set up application.yml
      run: echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.yml

    - name: Grant execute permission for gradlew
      run: sudo chmod +x ./gradlew  # gradlew에 실행 권한 부여
      
    - name: Build with Gradle (Skip Tests)
      run: ./gradlew build -x test

    - name: Build the Docker image
      run: docker build . --file Dockerfile --tag ${{ env.DOCKER_IMAGE_NAME }}:latest

    - name: Login to Docker Hub using Access Token
      run: echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u ${{ secrets.DOCKER_NAME }} --password-stdin

    - name: Push the Docker image
      run: |
          docker image tag ${{ env.DOCKER_IMAGE_NAME }} ${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}
          docker push ${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest

    - name: Deploy
      uses: appleboy/ssh-action@master
      with:
        host: ec2-3-38-186-97.ap-northeast-2.compute.amazonaws.com # EC2 인스턴스 퍼블릭 DNS
        username: ubuntu
        key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} # pem 키
        # 도커 작업
        script: |
          sudo docker ps --filter "name=backend" --format "{{.Names}}" | xargs -r sudo docker stop
          sudo docker ps --filter 'status=exited' -a -q | xargs -r sudo docker rm
          sudo docker images --filter=reference='${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}*' -q | xargs -r sudo docker rmi
          sudo docker login -u ${{ secrets.DOCKER_NAME }} -p ${{ secrets.DOCKER_HUB_TOKEN }}
          sudo docker pull ${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest
          sudo docker run -d \\--name backend \\--link mysqldb \\-e SPRING_DATASOURCE_URL=jdbc:mysql://mysqldb:3306/yogoyogu \\-e SPRING_DATASOURCE_USERNAME=root \\-e SPRING_DATASOURCE_PASSWORD=root \\-p 8080:8080 \\${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest

(3-2) 깃허브 액션 설명

  • 깃허브 액션이란?
    • 기능
      • 자동 명령어 주입 기능
        • 프로그램 배포 시에 반복되는 코드들이 많다
          • 깃허브 파일 가져오기
          • 도커 이미지 구성
          • 컨테이너 실행 등
        • 그러한 코드들을 깃허브 액션에 담아놓으면, 깃허브 파일이 수정될때마다 깃허브 액션 코드들이 실행되면서 자동 배포가 가능하다.
        • 즉, 배포 과정에서 반복되는 코드들을 깃허브 액션에 담으면 된다.

 

 

  • 깃허브 액션 파일 만들기
    1. 배포하려고 하는 깃허브 주소에서 Action 탭을 클릭한다.

 

              2. java with gradle의 Configure을 클릭한다

                        -  (배포할 프로그램은 Spring, gradle을 사용함)

 

 

                     3. 해당 파일에 깃허브 액션 코드를 담아서 저장하면 .github/workflows/gradle.yml로 파일이 만들어짐

 

 

 

  • 깃허브 액션 작성법
    • 가정
      • 스프링, gradle, java를 사용한다.
      • AWS EC2 배포가 목적이다.
      • 도커를 사용한다.
    • 과정
      • 자바 환경 구축
      • 배포 프로젝트를 도커 이미지로 생성 후, 온라인에 기록
      • AWS EC2에 접속
      • 온라인에 있는 배포 프로젝트를 도커 이미지로 받기
      • 도커 이미지를 컨테이너로 만들어서 실행
#해당 파일 이름(의미X)
name: Java CI with Gradle2

#on : 어떤 경우에 깃허브 액션을 실행할지 정함
#(아래는 push, pull_requeset 발생 시, 깃허브 액션을 수행. 즉, 아래 명령어들 실행)
on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]
    
# env : 변수 선언
# ex) DOCKER_IMAGE_NAME = testimage
env:
  DOCKER_IMAGE_NAME: testimage
  EC2_HOST: ec2-3-38-186-97.ap-northeast-2.compute.amazonaws.com
  EC2_SSH_USER: ec2-user
  # ${{secrets.~~}}의 값은 깃허브 settings 탭에 있는 private 코드다(보안을 위해서 사용)
  PRIVATE_KEY: ${{ secrets.EC2_SSH_PRIVATE_KEY }}

# jobs : 수행할 명령어
# jobs를 여러개 써도 됨
# (하지만 각 jobs는 독립적으로 수행되므로, Ajobs의 진행 상황이 Bjobs에는 반영 안됨)
jobs:
	# jobs 이름
  build-and-push-docker:
	  # 해당 jobs의 실행환경
	  # (현재는 ubuntu 환경에서 실행할 것임)
    runs-on: ubuntu-latest
    # jobs는 여러개의 step으로 구성됨
    # 각각의 step에서 명령어를 넣으면 됨
    steps:
    
    # 해당 step의 이름(Checkout)
    - name: Checkout
	    # 현재 리포지토리 코드 가져오기
      uses: actions/checkout@v4

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

		# 해당 Spring의 application.yml 파일 넣기
		# .yml 파일은 보안상 리포지토리에 노출 할 수가 없음
		# 따라서, 깃허브의 secrets에 application.yml 선언 후, 이를 가져옴
    - name: Set up application.yml
      run: echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.yml
	
		# 해당 프로젝트의 gradlew 파일 실행 권한 획득
    - name: Grant execute permission for gradlew
      run: sudo chmod +x ./gradlew  # gradlew에 실행 권한 부여
      
    # gradlew 파일 실행
    # .jar 파일을 만듦(아래에서 추가 설명)
    - name: Build with Gradle (Skip Tests)
      run: ./gradlew build -x test

		# 해당 프로젝트를 도커 이미지로 만듦
    - name: Build the Docker image
      run: docker build . --file Dockerfile --tag ${{ env.DOCKER_IMAGE_NAME }}:latest

		# 도커 허브에 접속
    - name: Login to Docker Hub using Access Token
      run: echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u ${{ secrets.DOCKER_NAME }} --password-stdin

		# 도커 허브에 해당 도커 이미지 저장
    - name: Push the Docker image
      run: |
          docker image tag ${{ env.DOCKER_IMAGE_NAME }} ${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}
          docker push ${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest

		# 1. 내가 만든 AWS EC2에 접속
		# 2. 이미 실행중인 해당 프로젝트의 도커 컨테이너와 이미지 삭제(DB 제외)
		# 3. 도커 허브에서 저장한 이미지 가져옴
	  # 4. 이미지를 컨테이너로 만들어서 실행
    - name: Deploy
      uses: appleboy/ssh-action@master
      with:
        host: ec2-3-38-186-97.ap-northeast-2.compute.amazonaws.com # EC2 인스턴스 퍼블릭 DNS
        username: ubuntu
        key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} # pem 키
        # 도커 작업(scripts : uses 환경에서 실행할 명령어. 즉, 외부 환경에서 실행할 명령어)
        # | 는 여러 개 명령어를 쓸때 사용하는 기호
        script: |
	        # 호스트명이 backend인 컨테이너 중지(해당 프로젝트의 호스트명임)
          sudo docker ps --filter "name=backend" --format "{{.Names}}" | xargs -r sudo docker stop
          # 중지된 컨테이너들 전부 삭제
          sudo docker ps --filter 'status=exited' -a -q | xargs -r sudo docker rm
          # 해당 프로젝트 이름을 가진 이미지 전부 삭제
          sudo docker images --filter=reference='${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}*' -q | xargs -r sudo docker rmi
          # 도커 허브 접속
          sudo docker login -u ${{ secrets.DOCKER_NAME }} -p ${{ secrets.DOCKER_HUB_TOKEN }}
          # 도커 허브에 있는 내 이미지(실행할 프로젝트) 가져오기
          sudo docker pull ${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest
          # 가져온 내 이미지 실행
          # (mysqldb라는 호스트명을 가진 DB 컨테이너를 사용할 것이기 때문에 그에 대한 설정도 덧붙임)
          sudo docker run -d \\--name backend \\--link mysqldb \\-e SPRING_DATASOURCE_URL=jdbc:mysql://mysqldb:3306/yogoyogu \\-e SPRING_DATASOURCE_USERNAME=root \\-e SPRING_DATASOURCE_PASSWORD=root \\-p 8080:8080 \\${{ secrets.DOCKER_NAME }}/${{ env.DOCKER_IMAGE_NAME }}:latest

 

QnA) 왜 아래 명령어를 실행하여 해당 프로젝트에 대한 .jar 파일을 만들어야 하나?

# gradlew 파일 실행
# .jar 파일을 만듦(아래에서 추가 설명)
- name: Build with Gradle (Skip Tests)
  run: ./gradlew build -x test
  • 이유
    • 해당 .jar 파일로 도커 이미지를 만들 것이기 때문이다.
    • gradle.yml
      • 내 프로젝트에서 .jar 파일 생성에 대한 정의는 다음과 같이 표기했다.
      • 즉, build/libs/myapp-0.0.1.jar로 .jar 파일을 생성한다
    //gradle.yml
    // ./gradlew build 시, 아래 경로로 jar 파일 생성
    // build/libs/myapp-0.0.1.jar
    group = 'org.springframework'
    version = '0.0.1'
    archivesBaseName = 'myapp'
    
    •  Dockerfile
      • 위에서 만든 .jar 파일을 도커 이미지로 제작
    FROM openjdk:21-jdk-slim
    //실행할 .jar 파일 지정(경로 + 파일 이름)
    ARG JAR_FILE=build/libs/myapp-0.0.1.jar
    // 위의 .jar 파일은 app.jar로 표기
    COPY ${JAR_FILE} app.jar
    // app.jar를 이미지로 제작
    ENTRYPOINT ["java","-jar","/app.jar"]
    
     

 

'' 카테고리의 다른 글

AWS에 배포하기(SpringBoot + MySQL + Docker)  (0) 2024.08.08