0. 사용하게 된 이유
도커를 이용해서 도커 이미지를 만들고 이를 도커 repository에 push하는 방법을 통해 프론트엔드와 쉽게 api를 공유하며 개발을 할 수 있게 되었다.
하지만, 매번 코드가 변경되어 깃헙 레포지토리에 push할 때 마다 도커 이미지를 만드는 명령어를 작성하는 등의 과정을 계속 반복해야 하는 것은 너무 비효율적이라는 생각이 들었다. 그래서 이를 자동화할 수 있는 방법을 알아보다가 알게 된 개념이 CI였다. 또한, CI를 가능하게 하는 다양한 툴이 있는데 그 중 간편하게 쓸 수 있는 것이 Github Actions였다.
1. CI의 개념
CI는 Continuous Integration의 약자이다. 말 그대로 지속적으로 통합을 가능하도록 해서 지속적으로 배포를 위한 안정적 파일을 만들 수 있도록 하는 것이다. 여기서 통합이란 코드의 통합을 의미한다. 만약 여러 사람이 개발 중이라면 여러 사람의 코드가 통합되도록, 혹은 혼자 개발 중이라면 본인이 계속 수정한 코드가 통합되도록 해서 올바른 배포 파일을 만들 수 있도록 하는 것이다.
내가 사용한 CI는 다음과 같다고 할 수 있다. 내가 코드를 계속 수정하며 깃허브 레포지토리에 push하면, push된 내용을 가지고 CI 툴을 사용해 도커 이미지를 생성하고, 해당 도커 이미지를 도커 레포지토리에 올리는 것이다.
그러면 나는 지속적으로 코드를 통합해서 올바른 배포 파일(여기서는 도커 이미지)을 만들 수 있게 되는 것이다.
2. CI를 가능하도록 하는 툴
이런 CI를 가능하게 하는 도구는 다양하다. 그 중 나는 Github Actions을 사용하였다. 일단 무료였고, 깃허브를 통해 간편하게 구현할 수 있었기 때문이다.
3. Github Actions 사용 방법
1) 리포지토리 루트 디렉토리에 .github/workflows/ci.yml 생성
2) ci.yml 파일 작성
해당 파일에 작성된 내용이 곧 깃허브 리포지토리에 코드가 push 되었을 때 실행되는 내용들이다.
name: deploy
on:
release:
types: [push]
push:
branches: [master]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
shell: bash
- name: Build with Gradle
run: ./gradlew clean build -x test
shell: bash
- name: Create application.properties
run: |
mkdir -p src/main/resources
echo "spring.datasource.url=jdbc:mysql://ip주소:3306/ssackthree?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul" >> src/main/resources/application.properties
echo "spring.datasource.username=${{ secrets.SPRING_DATASOURCE_USERNAME }}" >> src/main/resources/application.properties
echo "spring.datasource.password=${{ secrets.SPRING_DATASOURCE_PASSWORD }}" >> src/main/resources/application.properties
echo "spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver" >> src/main/resources/application.properties
echo "spring.jpa.show-sql=true" >> src/main/resources/application.properties
echo "spring.jpa.properties.hibernate.format_sql=true" >> src/main/resources/application.properties
echo "spring.jpa.hibernate.ddl-auto=update" >> src/main/resources/application.properties
echo "spring.jpa.database=mysql" >> src/main/resources/application.properties
echo "spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect" >> src/main/resources/application.properties
echo "spring.jpa.generate-ddl=true" >> src/main/resources/application.properties
echo "spring.servlet.multipart.enabled=true" >> src/main/resources/application.properties
echo "spring.servlet.multipart.file-size-threshold=2KB" >> src/main/resources/application.properties
echo "spring.servlet.multipart.max-file-size: 100MB" >> src/main/resources/application.properties
echo "spring.servlet.multipart.max-request-size: 100MB" >> src/main/resources/application.properties
echo "google.api.key=${{ secrets.GOOGLE_API_KEY }}" >> src/main/resources/application.properties
echo "jwt.secret=${{ secrets.JWT_SECRET }}" >> src/main/resources/application.properties
echo "kakao.pay.admin-key=${{ secrets.KAKAO_PAY_ADMIN_KEY }}" >> src/main/resources/application.properties
echo "kakao.pay.ready-url=https://kapi.kakao.com/v1/payment/ready" >> src/main/resources/application.properties
echo "kakao.pay.approve-url=https://kapi.kakao.com/v1/payment/approve" >> src/main/resources/application.properties
echo "kakao.pay.cid=TC0ONETIME" >> src/main/resources/application.properties
echo "cloud.aws.credentials.accessKey=${{ secrets.CLOUD_AWS_CREDENTIALS_ACCESSKEY }}" >> src/main/resources/application.properties
echo "cloud.aws.credentials.secretKey=${{ secrets.CLOUD_AWS_CREDENTIALS_SECRETKEY }}" >> src/main/resources/application.properties
echo "cloud.aws.s3.bucket=ssackthree" >> src/main/resources/application.properties
echo "cloud.aws.region.static=ap-northeast-2" >> src/main/resources/application.properties
echo "cloud.aws.stack.auto-=false" >> src/main/resources/application.properties
echo "logging.level.org.springframework.web.multipart.support=ERROR" >> src/main/resources/application.properties
- name: Build JAR with application.properties
run: ./gradlew bootJar
shell: bash
- name: Build Docker image
run: docker build -t lmj174/ssackthree:1.0 .
shell: bash
- name: Log in to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Push Docker image to Docker Hub
run: docker push lmj174/ssackthree:1.0
shell: bash
application.properties 파일이 도커 이미지를 생성하려면 필요한데, 해당 파일은 이그노어에 작성해서 깃허브에 올라가지 않도록 하였다. 따라서 ci.yml을 작성하며 해당 파일을 새롭게 생성해주도록 하였다.
이때, 보안과 관련된 여러 사항들(도커 로그인시 필요한 아이디와 비밀번호, aws s3를 사용한다면 해당 계정 정보 등)을 이 파일에 작성하면 보안상 위험하기 때문에 깃허브 리포지토리 settings-secrets and variables-actions-new repository secret를 통해 몽땅 작성해줘야 한다.
3) build.gradle에 jar파일이 하나만 생기도록 설정
jar {
enabled = false
}
빌드할 때 이걸 안 적어주면 계속 오류가 나는 듯 하다. jar 파일로 build를 해야 하는데 이걸 안 적어주면 jar 파일이 2개가 생겨서 이걸 작성해서 jar 파일이 하나만 생기도록 해야 하는 것 같다. 아마 Build with Gradle 단계에서 오류가 났던듯.
이렇게 해 준 다음 깃허브 레포지토리에 커밋 내역을 push 하면
이런 식으로 Actions항목에 성공한 workflow내역을 확인할 수 있다.