Web Application 운영

A Hands-on Guide to Automated Spring Boot Deployment using GitHub Actions

blogger903 2024. 8. 24. 00:35
728x90

A Hands-on Guide to Automated Spring Boot Deployment using GitHub Actions

 

"k8s 클러스터에 springboot application 배포 시리즈"의 시작 포스팅입니다

이번 포스트에서는 github action으로 springboot application의 docker image를 remote registry에 push 해보도록하겠습니다.

 

준비사항:

  • docker 사용경험
  • gradle 사용경험
  • docker 계정

node.js 환경에서 개발 및 운영만 해오다보니 로컬 개발환경에서 개발환경에 배포하기 전이나 새로운 시스템을 개발해아할때 Dockerfile로 도커 이미지를 생성해서 AWS ECR이든 Google Container Registry에 도커 이미지를 푸시해서 EKS, GKE에서 사용하는 경우가 대부분이었습니다.

 

CI tool은 실무에서 가장 많이 사용하며, Github action은 설치 작업이 필요 없고, Jenkins보다 CI단계를 빠르게 수행할 수 있는 Github Action으로 정했습니다.


다루는 내용

  • Github action workflow 
    • secrets
  • jib으로 springboot application image 생성하기
  • Troubleshooting

Github action workflow

workflow를

프로젝트에 root directory에 .github/workflows/sth.yml 경로로 파일을 생성합니다

 

해당 파일이 github repository에 main 브랜치가 push가 되면 해당 repository의 actions 탭에 코드로 작성한 workflow가 동작하는것을 확인할 수 있습니다

name: demo workflow
on:
    push:
        branches:
        - main
jobs:
  ci-cd-pipeline:
    name: ci-cd
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

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

      - name: Build with Gradle
        run: ./gradlew clean build

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: Build with Gradle and Jib
        run: |
            ./gradlew \
            -Djib.to.image=<docker_계정>/<임의선택>:${{ github.sha }} \
            jib \
            --info \
            --stacktrace \
            --no-daemon

 

workflow 설정파일을 push하기전에 workflow를 보면 secrets를 참조하는 코드가 보입니다

github repository에 secrets를 추가해줘야합니다

 

https://app.docker.com/ docker 계정에서 Personal access token을 생성해줍니다

 

Scope은 Read & Write로 해주세요. Docker hub에 image를 push할겁니다

 

Docker Token과 Username을 github repository의 secret에 추가해줍니다

해당 토큰을 github repository > settings > (Security) Secrets and Variables > Actions > New repository secret 

DOCKERHUB_USERNAME, DOCKER_TOKEN 토큰을 추가해줍니다 

이름은 임의선택입니다

 

repository에 commit push 하면 workflow가 동작하고 성공적으로 동작하는것을 확인할 수 있습니다

 

dockerhub의 repository를 확인하시면 해당 계정의 repository에 이미지가 푸시된것을 확인할 수 있습니다 

 

이어서 Jib에 관련된 내용을 살펴보겠습니다

Jib으로 springboot application image 생성하기

jib은 구글에서 oci 표준을 구현한 container image 생성및 관련된 다양한 기능을 제공하는 솔루션입니다

https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#quickstart

 

jib/jib-gradle-plugin at master · GoogleContainerTools/jib

🏗 Build container images for your Java applications. - GoogleContainerTools/jib

github.com

gradle을 사용하기 때문에 springboot 빌드 설정파일에 jib 관련 plugin을 설정해줘야 합니다

 

각 프로젝트마다 gradle, java 등 버전이 다른 부분은 참고해주세요

build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "3.0.11"
    id("io.spring.dependency-management") version "1.1.0"
    kotlin("jvm") version "1.7.22"
    kotlin("plugin.spring") version "1.7.22"
    kotlin("plugin.jpa") version "1.7.22"
    id("com.google.cloud.tools.jib") version "3.4.3"
}

jib {
    from {
        image = "eclipse-temurin:17-jre-alpine"
    }
    to {
        image = "<docker계정>/<임의선택>"
    }
    container {
        jvmFlags = arrayOf("-Dspring.profiles.active=local","-Xms3g", "-Xmx3g").toMutableList()
        ports = arrayOf("8080").toMutableList()
    }
}


group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

repositories {
    mavenCentral()
}



dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-batch")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation("org.springframework.kafka:spring-kafka")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("mysql:mysql-connector-java:8.0.33")
    runtimeOnly("com.h2database:h2")

    implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0")

    implementation("org.springframework.kafka:spring-kafka")

    implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
    implementation("com.querydsl:querydsl-kotlin:5.0.0")

    annotationProcessor("com.querydsl:querydsl-apt:5.0.0:jakarta")

    val mockkVersion = "1.13.8"
    testImplementation("io.mockk:mockk:${mockkVersion}")
    var kotestVersion = "5.8.0"
    testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion")
    testImplementation("org.springframework.kafka:spring-kafka-test")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.springframework.batch:spring-batch-test")
    testImplementation("org.junit.jupiter:junit-jupiter-api")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "17"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

 

github action에서 gradle wrapper로 jib 명령어를 수행하지만 커스텀 옵션을 추가했습니다

jib은 container 이미지 생성 뿐만 아니라 image push 기능까지 하기 때문에 image를 push할 이미지 저장소도 입력하는것 같습니다

이미지 저장소는 cloud provider에 각각 syntax가 다르므로 다음 링크를 보고 따라해줍니다

https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#configuration

 

jib/jib-gradle-plugin at master · GoogleContainerTools/jib

🏗 Build container images for your Java applications. - GoogleContainerTools/jib

github.com

 

 

troubleshooting

Gradle Jib

필자는 jib이란걸 어끄제 알게되어 그동안 Dockerfile로만 springboot application을 배포해왔으나 구글링으로 OCI를 구현한 jib 이란게 있다고 해서 적용해봤습니다

 

이슈1: gradle jib은 container image를 생성하고 image push까지 해버림

Dockerfile의 경우 docker image만 생성하는것이 전부인데, 이것은 github action에서 자꾸 에러가 발생해서 gradle jib 에 --info, --stacktrace 옵션을 추가하여 디버깅해본결과 docker hub에 대한 권한이 없어서 발생한 이슈였습니다

docker publish access token에 write가 포함되도록 수정해줍니다

에러가 발생한다면 로그 레벨과 stacktrace 옵션을 추가해서 발생하는 이슈에 대해서 파악해야합니다

 

이슈2: jib은 container image 생성과 push까지 함께 진행합니다

github action job에 push 관련 step을 추가하실 필요가 없습니다

 

이슈3: latest 이미지 생성안하고 싶으면?

jib의 -Djib.to.tags로 커밋해시를 이미지의 마지막에 접미사로 붙이고 싶어서 tags에 커밋해시를 붙이니 

latest, 커밋해시 두개의 태그로 이미지가 푸시되어 추후에 argo imageUpdater에 불필요한 이미지를 트래킹하는 것을 제거하기 위해서 latest를 skip하고싶었으나 찾아보니 image를 명시하는 방법이 있어 image를 명시하는것으로 latest 태그는 push하지 않을 수 있었습니다

https://github.com/GoogleContainerTools/jib/issues/3346#issuecomment-880983881

 

Disable generating of the latest tag · Issue #3346 · GoogleContainerTools/jib

Environment: Jib version: 3.1.2 Build tool: Maven OS: any Description of the issue: I am using Immutable AWS ECR repository and when I run mvn clean install jib:build it fails with an error that th...

github.com

 

 

참고:

jib 공식문서 faq: https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md#frequently-asked-questions-faq

 

jib/docs/faq.md at master · GoogleContainerTools/jib

🏗 Build container images for your Java applications. - GoogleContainerTools/jib

github.com

oci: http://www.opennaru.com/kubernetes/open-container-initiative/

 

컨테이너 기술에 대한 표준화 – OCI ( OPEN CONTAINER INITIATIVE )

컨테이너 기술에 대한 표준화 단체인 Open Container Initiative는 2015 년 6 월에 레드햇, Docker, CoreOS, Google, IBM Red Hat, Amazon Web Services, VMware,The Linux Foundation 등이

www.opennaru.com

jib-gradle-plugin: https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#quickstart

 

jib/jib-gradle-plugin at master · GoogleContainerTools/jib

🏗 Build container images for your Java applications. - GoogleContainerTools/jib

github.com

docker 계정 & token 생성: https://docs.docker.com/security/for-developers/access-tokens/

 

Create and manage access tokens

Learn how to create and manage your personal Docker access tokens to securely push and pull images programmatically.

docs.docker.com

 

'Web Application 운영' 카테고리의 다른 글

API 설계 원칙  (1) 2024.07.23