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

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

이 Codelab에서는 간단한 Spring Boot Java 웹 앱을 GKE의 Kubernetes에 배포합니다. 목표는 Kubernetes에서 웹 앱을 복제된 앱으로 실행하는 것입니다. 머신에서 개발한 코드를 가져와 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)

자습형 환경 설정

  1. Cloud 콘솔에 로그인하고 새 프로젝트를 만들거나 기존 프로젝트를 다시 사용합니다. (Gmail 또는 G Suite 계정이 없으면 만들어야 합니다.)

모든 Google Cloud 프로젝트에서 고유한 이름인 프로젝트 ID를 기억하세요(위의 이름은 이미 사용되었으므로 사용할 수 없습니다). 이 ID는 나중에 이 Codelab에서 PROJECT_ID라고 부릅니다.

  1. 그런 다음 Google Cloud 리소스를 사용할 수 있도록 Cloud 콘솔에서 결제를 사용 설정해야 합니다.

이 Codelab을 실행하는 과정에는 많은 비용이 들지 않지만 더 많은 리소스를 사용하려고 하거나 실행 중일 경우 비용이 더 들 수 있습니다.

Google Cloud 신규 사용자는 $300 상당의 무료 체험판을 사용할 수 있습니다.

Cloud Shell 활성화

  1. Cloud 콘솔에서 Cloud Shell 활성화 를 클릭합니다.

이전에 Cloud Shell을 시작하지 않았으면 설명이 포함된 중간 화면(스크롤해야 볼 수 있는 부분)이 제공됩니다. 이 경우 계속을 클릭합니다(이후 다시 표시되지 않음). 이 일회성 화면은 다음과 같습니다.

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

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

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`
gcloud config list project

명령어 결과

[core]
project = <PROJECT_ID>

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

gcloud config set project <PROJECT_ID>

명령어 결과

Updated property [core/project].

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

$ git clone https://github.com/spring-guides/gs-spring-boot.git
$ cd gs-spring-boot/complete
  1. Spring Boot 플러그인을 사용하여 Spring Boot 앱을 정상적으로 시작할 수 있습니다.
$ ./mvnw -DskipTests spring-boot:run
  1. 앱이 시작되면 Cloud Shell 툴바에서 웹 미리보기를 클릭하고 포트 8080에서 미리보기를 선택합니다.

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

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

  1. 앱의 배포 가능한 JAR을 만듭니다.
$ ./mvnw -DskipTests package
  1. 만들 컨테이너 이미지를 저장할 Container Registry를 사용 설정합니다.
$ gcloud services enable containerregistry.googleapis.com
  1. Jib를 사용하여 컨테이너 이미지를 만들고 Container Registry에 푸시합니다.
$ ./mvnw -DskipTests com.google.cloud.tools:jib-maven-plugin:build \
  -Dimage=gcr.io/$GOOGLE_CLOUD_PROJECT/hello-java:v1
  1. 모든 작업이 완료되면 Container Registry > 이미지로 이동하여 콘솔에 컨테이너 이미지가 표시되는지 확인할 수 있습니다. 이제 프로젝트 전체에서 사용할 수 있는 Docker 이미지가 생겼습니다. 잠시 후에 Kubernetes에서 이 이미지에 액세스하고 조정할 수 있습니다.
  1. 이 작업이 완료되면 (모든 항목을 다운로드하고 추출하는 데 시간이 걸림) 새로 만든 컨테이너 이미지에서 Docker 컨테이너를 포트 8080에서 데몬으로 실행하는 다음 명령어를 사용하여 이미지를 로컬에서 테스트할 수 있습니다.
$ docker run -ti --rm -p 8080:8080 \
  gcr.io/$GOOGLE_CLOUD_PROJECT/hello-java:v1
  1. Cloud Shell의 웹 미리보기 기능을 다시 활용합니다.

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

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

  1. 먼저 관련 API 기능이 사용 설정되어 있는지 확인합니다.
$ gcloud services enable compute.googleapis.com container.googleapis.com
Operation "operations/..." finished successfully
  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 클러스터가 완전히 작동합니다.

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

  1. Kubernetes 배포는 사용자가 만든 컨테이너 이미지를 사용하여 앱의 여러 인스턴스를 만들고, 관리하고, 확장할 수 있습니다. kubectl run 명령어를 사용하여 앱의 인스턴스 하나를 Kubernetes에 배포합니다.
$ kubectl create deployment hello-java \
  --image=gcr.io/$GOOGLE_CLOUD_PROJECT/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의 제어 하에 컨테이너가 실행되지만 외부에서 액세스할 수 있도록 해야 합니다.

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

  1. Cloud Shell에서 kubectl expose 명령어와 --type=LoadBalancer 플래그를 합쳐서 포드를 공용 인터넷에 노출할 수 있습니다. 이 플래그는 외부에서 액세스 가능한 IP를 만드는 데 필요합니다.
$ kubectl create service loadbalancer hello-java --tcp=8080:8080

이 명령어에서 사용된 플래그는 기본 인프라가 제공하는 부하 분산기를 사용하도록 명시합니다. 포드가 아니라 배포를 직접 노출한다는 점을 참고하세요. 이로 인해 배포가 관리하는 모든 포드에 걸쳐 서비스가 트래픽을 부하 분산하게 됩니다. 이번 예시에서는 포드가 1개이지만 나중에 복제본을 더 추가하게 됩니다.

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. 서비스에 IP 주소가 2개 나열되며 2개 모두 포트 8080을 제공합니다. 하나는 Virtual Private Cloud 내에서만 표시되는 내부 IP 주소입니다. 다른 하나는 외부 부하 분산 IP 주소입니다. 이 예시에서 외부 IP 주소는 aaa.bbb.ccc.ddd입니다. 이제 브라우저에서 http://<EXTERNAL_IP>:8080으로 접속하면 서비스를 이용할 수 있습니다.

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 조정 루프는 현재 상황이 요청과 일치하는지 확인하며 필요시 조치를 취합니다.

프로덕션에 배포한 앱에 버그 수정이나 추가 기능이 필요한 시점이 있습니다. Kubernetes를 활용하여 사용자에게 영향을 주지 않고 새로운 버전을 프로덕션에 배포할 수 있습니다.

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

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=gcr.io/$GOOGLE_CLOUD_PROJECT/hello-java:v2

이제 Kubernetes에서 복제본 컨트롤러를 앱의 새로운 버전으로 원활하게 업데이트할 수 있습니다.

  1. 실행 중인 컨테이너의 이미지 라벨을 변경하려면 기존 hello-java 배포를 수정하고 이미지를 gcr.io/PROJECT_ID/hello-java:v1에서 gcr.io/PROJECT_ID/hello-java:v2로 변경해야 합니다.
  1. kubectl set image 명령어를 사용하여 Kubernetes에 롤링 업데이트를 통해 한 번에 하나의 인스턴스로 전체 클러스터에 새 버전의 앱을 배포하도록 요청할 수 있습니다.
$ kubectl set image deployment/hello-java \
  hello-java=gcr.io/$GOOGLE_CLOUD_PROJECT/hello-java:v2

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

죄송합니다. 새 버전의 앱에 실수가 있었나요? 새 버전에 오류가 포함되어 있어 신속하게 롤백해야 할 수도 있습니다. Kubernetes를 사용하면 이전 상태로 쉽게 롤백할 수 있습니다. 다음 명령어를 실행하여 앱을 롤백합니다.

$ kubectl rollout undo deployment/hello-java

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

자세히 알아보기