Google Kubernetes Engine의 Kubernetes에 Spring Boot Java 앱 배포

1. 시작하기 전에

Kubernetes는 오픈소스 프로젝트로 노트북부터 고가용성 멀티 노드 클러스터, 퍼블릭 클라우드에서 온프레미스 배포, 가상 머신 (VM) 인스턴스에서 베어메탈까지 다양한 환경에서 실행할 수 있습니다.

이 Codelab에서는 웹 앱을 Kubernetes에서 복제된 앱으로 실행하는 것을 목표로 하여 GKE의 Kubernetes에 간단한 Spring Boot Java 웹 앱을 배포합니다. 머신에서 개발한 코드를 가져와서 Docker 컨테이너 이미지로 변환한 후 GKE에서 이미지를 실행합니다.

Google Cloud의 완전 관리형 Kubernetes 서비스인 GKE를 사용하여 기본 인프라를 설정하는 대신 Kubernetes를 경험하는 데 더 집중할 수 있습니다.

개발용 노트북과 같은 로컬 머신에서 Kubernetes를 실행하는 데 관심이 있다면 개발 및 테스트 목적으로 단일 노드 Kubernetes 클러스터의 간단한 설정을 제공하는 Minikube를 살펴보세요. 원한다면 Minikube를 사용하여 Codelab을 진행할 수 있습니다.

이 Codelab에서는 Spring Boot를 사용한 앱 빌드 가이드의 샘플 코드를 사용합니다.

기본 요건

  • Java 프로그래밍 언어 및 도구에 대한 기본 지식
  • Vim, Emacs, nano와 같은 표준 Linux 텍스트 편집기에 대한 지식

실행할 작업

  • 간단한 Java 앱을 Docker 컨테이너로 패키징합니다.
  • GKE에서 Kubernetes 클러스터를 만듭니다.
  • GKE의 Kubernetes에 Java 앱을 배포합니다.
  • 서비스를 확장하고 업그레이드를 출시합니다.
  • 웹 기반 Kubernetes 사용자 인터페이스인 대시보드 액세스

필요한 항목

  • Google Cloud 프로젝트
  • 브라우저(예: Chrome)

2. 설정 및 요건

자습형 환경 설정

  1. Google Cloud Console에 로그인하여 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 프로젝트 이름은 이 프로젝트 참가자의 표시 이름입니다. 이는 Google API에서 사용하지 않는 문자열이며 언제든지 업데이트할 수 있습니다.
  • 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유하며, 변경할 수 없습니다(설정된 후에는 변경할 수 없음). Cloud 콘솔은 고유한 문자열을 자동으로 생성합니다. 일반적으로는 신경 쓰지 않아도 됩니다. 대부분의 Codelab에서는 프로젝트 ID (일반적으로 PROJECT_ID로 식별됨)를 참조해야 합니다. 생성된 ID가 마음에 들지 않으면 다른 임의 ID를 생성할 수 있습니다. 또는 직접 시도해 보고 사용 가능한지 확인할 수도 있습니다. 이 단계 이후에는 변경할 수 없으며 프로젝트 기간 동안 유지됩니다.
  • 참고로 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참고하세요.
  1. 다음으로 Cloud 리소스/API를 사용하려면 Cloud 콘솔에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼이 끝난 후에 요금이 청구되지 않도록 리소스를 종료하려면 만든 리소스 또는 프로젝트를 삭제하면 됩니다. Google Cloud 신규 사용자는 300달러(USD) 상당의 무료 체험판 프로그램에 참여할 수 있습니다.

Cloud Shell 활성화

  1. Cloud Console에서 Cloud Shell 활성화853e55310c205094.png를 클릭합니다.

55efc1aaa7a4d3ad.png

Cloud Shell을 처음 시작하는 경우에는 무엇이 있는지 설명하는 중간 화면이 표시됩니다. 중간 화면이 표시되면 계속을 클릭합니다.

92662c6a846a5c.png

Cloud Shell을 프로비저닝하고 연결하는 데 몇 분 정도만 걸립니다.

9f0e51b578fecce5.png

가상 머신에는 필요한 개발 도구가 모두 들어 있습니다. 영구적인 5GB 홈 디렉터리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 이 Codelab에서 대부분의 작업은 브라우저를 사용하여 수행할 수 있습니다.

Cloud Shell에 연결되면 인증이 완료되었고 프로젝트가 자신의 프로젝트 ID로 설정된 것을 확인할 수 있습니다.

  1. Cloud Shell에서 다음 명령어를 실행하여 인증되었는지 확인합니다.
gcloud auth list

명령어 결과

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell에서 다음 명령어를 실행하여 gcloud 명령어가 프로젝트를 알고 있는지 확인합니다.
gcloud config list project

명령어 결과

[core]
project = <PROJECT_ID>

또는 다음 명령어로 설정할 수 있습니다.

gcloud config set project <PROJECT_ID>

명령어 결과

Updated property [core/project].

3. 소스 코드 얻기

Cloud Shell이 실행되면 명령줄을 사용하여 홈 디렉터리에 예시 소스 코드를 클론할 수 있습니다.

$ git clone https://github.com/spring-guides/gs-spring-boot.git
$ cd gs-spring-boot/complete

4. 로컬에서 앱 실행

  1. JAVA_HOME이 올바른 버전으로 설정되어 있는지 확인합니다.
$ export JAVA_HOME=/usr/lib/jvm/java-1.17.0-openjdk-amd64
  1. Spring Boot 플러그인을 사용하여 평소처럼 Spring Boot 앱을 시작할 수 있습니다.
$ ./mvnw -DskipTests spring-boot:run
  1. 앱이 시작되면 Cloud Shell 툴바에서 웹 미리보기1a94d5bd10bfc072.png를 클릭하고 포트 8080에서 미리보기를 선택합니다.

6252b94905f3f7bd.png

브라우저에 탭이 열리고 방금 시작한 서버에 연결됩니다.

9b6c29059957bd0.jpeg

5. Java 앱을 Docker 컨테이너로 패키징

다음으로 Kubernetes에서 실행할 앱을 준비해야 합니다. 첫 번째 단계는 컨테이너와 그 콘텐츠를 정의하는 것입니다.

  1. 앱에 배포할 수 있는 JAR을 만듭니다.
$ ./mvnw -DskipTests package
  1. Artifact Registry API를 사용 설정하여 생성할 컨테이너 이미지를 저장하세요.
$ gcloud services enable artifactregistry.googleapis.com
  1. Docker 저장소가 없으면 새 Docker 저장소를 만듭니다. 이미지를 푸시하려면 먼저 저장소를 만들어야 합니다.
$ gcloud artifacts repositories create codelabrepo     --repository-format=docker --location=us-central1 
  1. 이미지의 형식은 다음과 같습니다.

{LOCATION}-docker.pkg.dev/{PROJECT-ID}/{REPOSITORY}/{IMAGE-NAME}

예를 들어 us-central1 위치에 codelabrepo라는 저장소를 만들고 이미지 이름을 hello-java:v1로 지정하려는 경우 이미지는 다음과 같습니다.

us-central1-docker.pkg.dev/{PROJECT-ID}/codelabrepo/hello-java:v1

  1. Jib를 사용하여 컨테이너 이미지를 만들고 Artifact Registry에 푸시합니다.
$ export GOOGLE_CLOUD_PROJECT=`gcloud config list --format="value(core.project)"`

$ ./mvnw -DskipTests com.google.cloud.tools:jib-maven-plugin:build -Dimage=us-central1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/codelabrepo/hello-java:v1
  1. Cloud 콘솔에서 Artifact Registry 이미지 페이지로 이동하면 콘솔에 나열된 컨테이너 이미지를 확인할 수 있습니다. 이제 프로젝트 전체에서 사용할 수 있는 Docker 이미지가 생겼습니다. 이 이미지는 몇 분 후에 볼 수 있듯이 Kubernetes가 액세스하고 조정할 수 있습니다.
  2. (선택사항) 완료 후 (모든 항목을 다운로드하고 추출하는 데 시간이 소요됨) 다음 명령어를 사용하여 이미지를 테스트합니다. 그러면 새로 만든 컨테이너 이미지에서 포트 8080에서 Docker 컨테이너를 데몬으로 실행합니다. 권한 문제가 발생하면 먼저 gcloud auth configure-docker us-central1-docker.pkg.dev를 실행하세요.
$ docker run -ti --rm -p 8080:8080 \
  us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v1
  1. 다시 한번 Cloud Shell의 웹 미리보기 기능을 활용합니다.

6252b94905f3f7bd.png

  1. 새 탭에 기본 페이지가 표시됩니다. 앱이 Docker 컨테이너에서 로컬로 실행되는지 확인한 후 Control+C를 눌러 실행 중인 컨테이너를 중지할 수 있습니다.

6. 클러스터 만들기

GKE 클러스터를 만들 준비가 되었습니다. 클러스터는 Google에서 관리하는 Kubernetes API 서버와 워커 노드 집합으로 구성됩니다. 워커 노드는 Compute Engine VM입니다.

  1. 먼저 관련 API 기능이 사용 설정되어 있는지 확인합니다.
$ gcloud services enable compute.googleapis.com container.googleapis.com
  1. n1-standard-1 노드 2개가 포함된 클러스터를 만듭니다. 이 작업을 완료하는 데 몇 분 정도 걸릴 수 있습니다.
$ gcloud container clusters create hello-java-cluster \
  --num-nodes 2 \
  --machine-type n1-standard-1 \
  --zone us-central1-c

최종적으로 클러스터가 생성된 것을 볼 수 있습니다.

Creating cluster hello-java-cluster...done.
Created [https://container.googleapis.com/v1/projects/...].
kubeconfig entry generated for hello-dotnet-cluster.
NAME                  ZONE            MASTER_VERSION  
hello-java-cluster  us-central1-c  ...

이제 GKE로 구동되는 Kubernetes 클러스터가 완벽하게 작동합니다.

758c7fca14f70623.png

이제 컨테이너화된 앱을 Kubernetes 클러스터에 배포해 보겠습니다. 이제부터 Cloud Shell 환경에 이미 설정된 kubectl 명령줄을 사용합니다. Codelab의 나머지 부분에서는 Kubernetes 클라이언트 및 서버 버전이 1.2 이상이어야 합니다. kubectl version는 명령어의 현재 버전을 표시합니다.

7. Kubernetes에 앱 배포

  1. Kubernetes 배포에서는 개발자가 만든 컨테이너 이미지를 사용하여 앱의 여러 인스턴스를 생성, 관리, 확장할 수 있습니다. kubectl run 명령어를 사용하여 앱의 인스턴스 하나를 Kubernetes에 배포합니다.
$ kubectl create deployment hello-java --image=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v1
  1. 생성된 배포를 보려면 다음 명령어를 실행하면 됩니다.
$ kubectl get deployments

NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-java   1         1         1            1           37s
  1. 배포로 생성된 앱 인스턴스를 보려면 다음 명령어를 실행합니다.
$ kubectl get pods

NAME                         READY     STATUS    RESTARTS   AGE
hello-java-714049816-ztzrb   1/1       Running   0          57s

이 시점에서 Kubernetes가 제어하는 컨테이너를 실행해야 하지만 여전히 외부 세계에 액세스할 수 있도록 해야 합니다.

8. 외부 트래픽 허용하기

기본적으로 포드는 클러스터 내에서 내부 IP를 통해서만 액세스할 수 있습니다. Kubernetes 가상 네트워크 외부에서 hello-java 컨테이너에 액세스할 수 있도록 하려면 포드를 Kubernetes 서비스로 노출해야 합니다.

  1. Cloud Shell에서 Kubernetes LoadBalancer 서비스를 만들어 포드를 공개 인터넷에 노출할 수 있습니다.
$ kubectl create service loadbalancer hello-java --tcp=8080:8080

포드가 아닌 배포를 직접 노출합니다. 이로 인해 결과 서비스는 배포에서 관리하는 모든 포드에 걸쳐 트래픽 부하를 분산하게 됩니다 (이 경우에는 포드가 하나뿐이지만 나중에 복제본을 더 추가함).

Kubernetes 마스터는 Google Cloud 외부에서 서비스에 완전히 액세스할 수 있도록 부하 분산기와 관련 Compute Engine 전달 규칙, 대상 풀, 방화벽 규칙을 만듭니다.

  1. 공개적으로 액세스할 수 있는 서비스 IP 주소를 찾으려면 kubectl에 모든 클러스터 서비스를 나열하도록 요청하면 됩니다.
$ kubectl get services

NAME         CLUSTER-IP     EXTERNAL-IP      PORT(S)    AGE
hello-java   10.3.253.62    aaa.bbb.ccc.ddd  8080/TCP    1m
kubernetes   10.3.240.1     <none>           443/TCP    5m
  1. 이제 브라우저에서 http://<EXTERNAL_IP>:8080을 가리키도록 하여 서비스에 연결할 수 있습니다.

9. 서비스 확장

Kubernetes가 제공하는 강력한 기능 중 하나는 앱을 쉽게 확장할 수 있다는 것입니다. 앱에 더 많은 용량이 갑자기 필요하다고 가정해 보겠습니다. 복제 컨트롤러에 앱 인스턴스의 새로운 복제본 수를 관리하도록 지시하기만 하면 됩니다.

$ kubectl scale deployment hello-java --replicas=3

deployment "hello-java" scaled

$ kubectl get deployment
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-java   3         3         3            3           22m

선언적 접근 방식에 주목하세요. 새로운 인스턴스를 시작하고 중지하는 대신 항상 실행 중이여야 하는 인스턴스의 개수를 선언합니다. Kubernetes 조정 루프는 단순히 현재 상황이 요청과 일치하는지 확인하고 필요한 경우 조치를 취합니다.

10. 서비스 업그레이드하기

프로덕션에 배포한 앱에는 버그 수정 또는 추가 기능이 필요할 수 있습니다. Kubernetes를 사용하면 사용자에게 영향을 주지 않으면서 프로덕션에 새 버전을 배포할 수 있습니다.

  1. Cloud Shell 메뉴에서 편집기 열기 2109d75686c889a.png를 클릭하여 코드 편집기를 엽니다.
  2. src/main/java/com/example/springboot/HelloController.java로 이동하여 응답 값을 업데이트합니다.
package com.example.springboot;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "Greetings from Google Kubernetes Engine!";
    }
}
  1. Jib를 사용하여 컨테이너 이미지의 새 버전을 빌드하고 푸시합니다. 캐싱을 최대한 활용하므로 업데이트된 이미지를 빌드하고 푸시하는 것이 훨씬 더 빠릅니다.
$ ./mvnw -DskipTests package com.google.cloud.tools:jib-maven-plugin:build -Dimage=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v2

이제 Kubernetes에서 새 버전의 앱으로 복제 컨트롤러를 원활하게 업데이트할 준비가 되었습니다.

  1. 실행 중인 컨테이너의 이미지 라벨을 변경하려면 기존 hello-java 배포를 수정하고 us-central1-docker.pkg.dev/PROJECT_ID/codelabrepo/hello-java:v1에서 이미지를 변경해야 합니다.

us-central1-docker.pkg.dev/PROJECT_ID/codelabrepo/hello-java:v2 도착

  1. kubectl set image 명령어를 사용하면 순차적 업데이트를 통해 한 번에 한 인스턴스씩 전체 클러스터에 새 버전의 앱을 배포하도록 Kubernetes에 요청할 수 있습니다.
$ kubectl set image deployment/hello-java hello-java=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v2

deployment "hello-java" image updated
  1. 새 응답을 반환하는지 http://EXTERNAL_IP:8080를 다시 확인합니다.

11. 롤백

죄송합니다. 앱의 새 버전에서 실수를 하셨나요? 새 버전에 오류가 있을 수 있으므로 빠르게 롤백해야 합니다. Kubernetes를 사용하면 이전 상태로 쉽게 롤백할 수 있습니다. 다음 명령어를 실행하여 앱을 롤백합니다.

$ kubectl rollout undo deployment/hello-java

http://EXTERNAL_IP:8080를 다시 확인하면 이전 응답이 표시됩니다.

12. 축하합니다

새로운 Java 기반 웹 앱을 빌드하고 GKE 기반 Kubernetes에 배포하는 방법을 알아봤습니다.

정리

$ gcloud container clusters delete hello-java-cluster --zone us-central1-c

$ gcloud container images delete us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v1 us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v2

자세히 알아보기