전체 목차

 

이번 챕터에서는 간단한 Spring boot 어플리케이션을 작성하고 이전에 생성한 원격 쿠버네티스 환경에 배포해 보도록 하겠습니다.

(침고 : 맨 첫번째 챕터에서 언급한 대로 이 프로젝트에서는 마스터(제온, amd64)와 워커(라즈베리파이, arm64) 노드의 CPU 아키텍쳐가 다르기 때문에 빌드 스크립트가 조금더 복잡합니다.)

1. Hello Kebernetes Spring boot 프로젝트 생성

1.1. 프로젝트 기본 속성

일반적인 Spring boot 프로젝트를 생성합니다.

여기서는 Java 21, Gradle 프로젝트로 생성하였고

의존성은 spring-boot-starter-webmvc 만을 주입하였습니다.

implementation 'org.springframework.boot:spring-boot-starter-webmvc'org.springframework.boot:spring-boot-starter-webmvc

 

1.2. HelloController.java

현재 실행되는 Pod Name을 출력하는 간단한 소스입니다.

@RestController
public class HelloController {

    @GetMapping("/")
    public String hello() {
        String podName = System.getenv("HOSTNAME");
        if (podName == null || podName.isEmpty()) {
            try {
                podName = java.net.InetAddress.getLocalHost().getHostName();
            } catch (java.net.UnknownHostException e) {
                podName = "unknown";
            }
        }
        return "Hello from Spring Boot on Hybrid K8s!!! (Running on " + podName + ")";
    }
}

 

 

1.3. deployment.yaml

프로젝트 최상위에 deployment.yaml 를 생성합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-kubernetes-api
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-kubernetes-api
  template:
    metadata:
      labels:
        app: hello-kubernetes-api
    spec:
      containers:
      - name: hello-kubernetes-api
        image: basscraft/hello-kubernetes-api:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: hello-kubernetes-api-service
spec:
  selector:
    app: hello-kubernetes-api
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: NodePort

 

 

1.4. 프로잭트 구조

 

1.5. 로컬 실행 화면 확인

에러 없이 정상적으로 실행 되면 브라우저에서 http://localhost:8080 으로 접속시 아래와 같은 화면이 나와야 합니다.

 

2. 빌드

일반적인 경우 (동일한 플랫폼) 아래와 같이 빌드 하고 나서 PUSH를 하면 됩니다.

$ docker build -t <Docker Hub 계정>/hello-kubernetes-api:latest .

현재 로컬 환경의 CPU 아키텍처로 빌드 됩니다.

$ docker push <Docker Hub 계정>/hello-kubernetes-api:latest

빌드된 이미지를 도커 허브로 전송합니다.

 

여기서는 Master (Xeon/amd64), Worker node (라즈베리파이 / arm64)로 서로 상이합니다.

Java의 경우 JVM에서 실행되기 때문에 플랫폼 독립적이지만 그래도 다른 아키텍처를 지정해서 빌드하는 것을 해보겠습니다.

PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi> docker buildx build --platform linux/amd64,linux/arm64 -t basscraft/hello-kubernetes-api:latest --push .

 

buildx 엔진을 사용해서 멀티 아키텍처를 지원하도록 합니다.

--platform linux/amd64,linux/arm64 : 마스터와 워커의 바이너리를 플랫폼별로 각각 빌드합니다.

-t <Docker Hub 계정>/hello-kubernetes-api:latest : 저장될 도커 계정에 앱 이름을 태깅합니다.

--push : 멀티 아키텍처 이미지는 Docker 엔진에 한번에 담을 수가 없기 때문에 빌드와 동시에 push를 해야 합니다.

 

이렇게 빌드 하면 마스터노드(Xeon)가 이미지를 다운로드 하면 amd64 버전을, 워커노드(라즈베리파이)가 이미지를 다운로드 하면 arm64로 인식하여 자동으로 선택 다운드 됩니다. 

3. 배포

3.1. 컨텍스트 확인

프로젝트 디렉토리에서 현재 선택된 컨텍스트를 확인합니다.

(배포하려는 타겟과 맞지 않으면 변경합니다.)

PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi> kubectl config get-contexts                           
CURRENT   NAME                          CLUSTER          AUTHINFO           NAMESPACE
*         docker-desktop                docker-desktop   docker-desktop     
          kubernetes-admin@kubernetes   kubernetes       kubernetes-admin   
PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi> kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".
PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi> kubectl get nodes -o wide
NAME     STATUS   ROLES           AGE   VERSION    INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
master   Ready    control-plane   45h   v1.30.14   192.168.2.100   <none>        Ubuntu 24.04.3 LTS   6.8.0-90-generic   containerd://1.7.28
node1    Ready    <none>          44h   v1.30.14   192.168.2.101   <none>        Ubuntu 24.04.3 LTS   6.8.0-1044-raspi   containerd://1.7.28
node2    Ready    <none>          44h   v1.30.14   192.168.2.102   <none>        Ubuntu 24.04.3 LTS   6.8.0-1044-raspi   containerd://1.7.28
node3    Ready    <none>          44h   v1.30.14   192.168.2.103   <none>        Ubuntu 24.04.3 LTS   6.8.0-1044-raspi   containerd://1.7.28
PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi>

 

3.2. 배포 deployment.yaml 적용

PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi> kubectl apply -f deployment.yaml
deployment.apps/hello-kubernetes-api created
service/hello-kubernetes-api-service created
PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi>

 

배포 상태 확인

PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi> kubectl get pods -o wide
NAME                                    READY   STATUS              RESTARTS   AGE     IP           NODE    NOMINATED NODE   READINESS GATES
hello-kubernetes-api-78dd74b6cd-l5pkn   0/1     ContainerCreating   0          2m33s   <none>       node3   <none>           <none>
nginx-bf5d5cf98-bgn9c                   1/1     Running             0          45h     10.244.1.3   node1   <none>           <none>
PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi>

배포후 즉시 확인해 보면 상태가 ContainerCreating 입니다.

 

1~2분 후에 확인해보면 Running 상태로 변경 됩니다.

PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi> kubectl get pods -o wide
NAME                                    READY   STATUS              RESTARTS   AGE     IP           NODE    NOMINATED NODE   READINESS GATES
hello-kubernetes-api-78dd74b6cd-l5pkn   1/1     Running             0          4m20s   10.244.2.3   node3   <none>           <none>
nginx-bf5d5cf98-bgn9c                   1/1     Running             0          45h     10.244.1.3   node1   <none>           <none>
PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi>

hello-kubernetes-api 애플리케이션이 정상 적으로 배포 되었습니다.

 

3.3. 서비스 포트 확인

어떤 포트에서 실행되는지 확인해야 합니다.

PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi> kubectl get svc
NAME                           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
hello-kubernetes-api-service   NodePort    10.105.75.244    <none>        80:32311/TCP   9m33s
kubernetes                     ClusterIP   10.96.0.1        <none>        443/TCP        46h
nginx                          NodePort    10.102.162.252   <none>        80:31098/TCP   45h
PS C:\Users\user\Projects\k8sTest\HelloKubernates\api\HelloKubernetesApi>

리스트 중 hello-kubernetes-api-service 항목의 PORT 에 80:32311 이 서비스 포트입니다.

 

브라우저에서 확인해 봅니다.

http://192.168.2.100:32311/

http://192.168.2.101:32311/

http://192.168.2.102:32311/

http://192.168.2.103:32311/

 

4. 서비스 안전한 종료

실제 서버의 경우 항상 켜놓기 때문에 매번 종료 할 필요가 없지만

환경상 안전을 위해(전력 사용, 서버 소음) 때문에 매번 서비스를 내리고 있습니다.

아래 순서를 지켜서 종료 하는 것을 권장합니다.

 

4.1. 워커 노드 격리 (마스터 노드에서 실행)

# 각 워커 노드에 대해 순차적으로 실행
$ kubectl drain node1 --ignore-daemonsets --delete-emptydir-data
$ kubectl drain node2 --ignore-daemonsets --delete-emptydir-data
$ kubectl drain node3 --ignore-daemonsets --delete-emptydir-data

node/node1 drained 라는 메시지가 나오면 정상 격리가 된 것입니다.

 

4.2. 워커노드 OS 종료 (각각 워커 노드에서 실행)

$ sudo shutdown -h now

 

4.3. 마트서 노드 OS 종료 (마스터 노드에서 실행)

마스터 노드는 etcd 같은 중요한 데이터베이스를 담고 있으므로, 반드시 워커노드 들이 모두 종료 된 후에 마지막으로 꺼야 합니다.

$ sudo shutdown -h now

 

끝.

+ Recent posts