[k8s] Springboot를 mysql과 함께 실행시키기

hanana·2025년 1월 12일
0
post-thumbnail

Springboot와 mysql을 연동한 간단한 웹 애플리케이션을 쿠버네티스로 실행시키는 예제입니다.
개인의 실행 경험을 바탕으로 하고있으므로, 더 정확한 정보는 다른 공식문서에서 확인 바랍니다.

글을 쓴 시점의 가장 최신버전의 SpringBoot, mysql:lastest 를 사용하였습니다.

본 예제에서는 스프링 컨테이너를 띄우는것이 주된 관심사였기 때문에 volume에 관한 설정은 제외하였습니다.

아래는 전체 소스코드 입니다.

https://github.com/hana0627/k8s_practice


프로젝트 구성

build.gradle

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.9.25'
    id 'org.jetbrains.kotlin.plugin.spring' version '1.9.25'
    id 'org.springframework.boot' version '3.4.1'
    id 'io.spring.dependency-management' version '1.1.7'
    id 'org.jetbrains.kotlin.plugin.jpa' version '1.9.25'
}

group = 'hana.k8s'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
    implementation 'org.jetbrains.kotlin:kotlin-reflect'
    runtimeOnly 'com.mysql:mysql-connector-j'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

kotlin {
    compilerOptions {
        freeCompilerArgs.addAll '-Xjsr305=strict'
    }
}

allOpen {
    annotation 'jakarta.persistence.Entity'
    annotation 'jakarta.persistence.MappedSuperclass'
    annotation 'jakarta.persistence.Embeddable'
}

tasks.named('test') {
    useJUnitPlatform()
}

의존성으로는 spring-web, jpa, mysql-connector를 추가하였고
개인 취향으로 kotlin을 사용하였습니다.

application.yml

spring:
  datasource:
    url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_NAME}
    username: ${MYSQL_USERNAME}
    password: ${MYSQL_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver
    
    # url: jdbc:mysql://mysql-service:3306/sample_app
    # username: root
    # password: 111111

mysql연결에 필요한 정보를 작성했습니다.
저는 k8s의 configMap과 secret를 활용할 생각이였기 때문에 해당 부분을 변수로 받아오게끔 하였습니다.

주석으로 표기한 부분은 실제 환경변수가 들어오면 매핑될 String입니다.
참고용으로 표기해 두었습니다.

apiController

@RestController
class AppController {

    @GetMapping("/")
    fun index(): String {
        return "hello world"
    }
}

간단하게 루트path에 get요청을 받으면 "hello world"를 반환하게 작성하였습니다.


k8s 구성

Docker File

FROM openjdk:17-jdk

COPY build/libs/*SNAPSHOT.jar /app.jar

ENTRYPOINT ["java", "-jar", "/app.jar"]

어려운 작업 없이 빌드된 jar파일을 실행하게끔 작성하였습니다.

namespace.yml

apiVersion: v1
kind: Namespace
metadata:
  name: my-app

deployment.yml

apiVersion: apps/v1
kind: Deployment

metadata:
  name: spring-deployment
  namespace: my-app

spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend-app
  template:
    metadata:
      labels:
        app: backend-app
    spec:
      containers:
        - name: spring-container
          image: spring
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
          env:
            - name: MYSQL_HOST
              valueFrom:
                configMapKeyRef:
                  name: mysql-config
                  key: mysql_host
            - name: MYSQL_PORT
              valueFrom:
                configMapKeyRef:
                  name: mysql-config
                  key: mysql_port
            - name: MYSQL_NAME
              valueFrom:
                configMapKeyRef:
                  name: mysql-config
                  key: mysql_nane
            - name: MYSQL_USERNAME
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: mysql_username
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: mysql_password

---

apiVersion: apps/v1
kind: Deployment

metadata:
  name: mysql-deployment
  namespace: my-app

spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql-container
          image: mysql
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: mysql_password
            - name: MYSQL_DATABASE
              valueFrom:
                configMapKeyRef:
                  name: mysql-config
                  key: mysql_nane

spring-deployment와 mysql-deployment를 한개의 deployment.yml파일에 위치시켰습니다.

service.yml

apiVersion: v1
kind: Service

metadata:
  name: spring-service
  namespace: my-app

spec:
  type: NodePort
  selector:
    app: backend-app
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
      nodePort: 30000

---

apiVersion: v1
kind: Service

metadata:
  name: mysql-service
  namespace: my-app

spec:
  type: NodePort
  selector:
    app: mysql
  ports:
    - protocol: TCP
      port: 3306
      targetPort: 3306

마찬가지로 한개의 service.yml파일에 구분자를 통해서 두개의 service를 정의했습니다.

configMap.yml

apiVersion: v1
kind: ConfigMap

metadata:
  namespace: my-app
  name: mysql-config

data:
  mysql_host: "mysql-service"
  mysql_port: "3306"
  mysql_nane: "sample_app"

configMap에는 mysql 접속정보를 명시했습니다.
별도로 Spring에서만 사용되는 환경변수가 현재는 없기 때문에 mysql-config만 사용하였습니다.

secret.yml

apiVersion: v1
kind: Secret

metadata:
  name: mysql-secret
  namespace: my-app

stringData:
  mysql_username: "root"
  mysql_password: "111111"

마찬가지로 secret에 mysql 접속정보를 명시했습니다.
공개하기에 민감한 정보를 보관한다고 하여
임의로 접속정보의 username, password를 secret에 담아주었습니다.


실행하기

1. 프로젝트 빌드

.gradlew clean build 명령을 통해 프로젝트를 빌드합니다.

2. spring 이미지 만들기

docker build -t sprig . 명령을 통해 도커 이미지를 생성합니다.
이미지명은 spring 으로 하였습니다.

3. 쿠버네티스 관련 명령어 수행하기

kubectl apply -f namespace.yml
kubectl apply -f secret.yml
kubectl apply -f configMap.yml
kubectl apply -f deployment.yml
kubectl apply -f service.yml

4. pod가 성공적으로 실행되었는지 확인하기

kubectl get pods -n my-app 을 통해 pod가 정상적으로 올라왔는지를 확인합니다.

// 여담
spring pod가 몇차례 restart 되어있는 모습을 확인 할 수 있었는데
이는 아마 mysql이 온전히 올라오기 전에
spring이 실행되어 db접속정보가 유효하지 못하기 때문에 pod가 종료되고
deployment에 의해 pod를 다시 실행하는 일련의 과정이 있었던 것으로 추측됩니다.

docker container로 작업을 할 때는 컨테이너의 실행 순서를 지정해주는 설정정보를 필요로 하였는데
k8s의 장애복구능력으로 인해 실행순서를 굳이 지정해주지 않더라도 문제없이 동작하는것 같습니다.
편리하네요...

5. 실제로 Get요청 보내보기

성공적으로 스프링이 실행되었다!!🥰

profile
성숙해지려고 노력하지 않으면 성숙하기까지 매우 많은 시간이 걸린다.

2개의 댓글

comment-user-thumbnail
2025년 2월 27일

코드 쓰실때 마크다운에서

```kotlin 이런식으로 시작하면 언어 키워드 하이라이팅 됩니당

1개의 답글