sungwony

[GCP] Google Cloud Platform - Kubernetes, GKE 본문

cloud & devops/google cloud

[GCP] Google Cloud Platform - Kubernetes, GKE

일상이상삼상 2019. 10. 19. 01:54

이 포스팅은 Coursera의 Google Cloud Platform Fundamentals:Core Infrastructure 강의를 요약 정리 한 것입니다.


GKE(Google Kubernetes Engine)

 

이전 포스팅에서 컨테이너와 어플리케이션을 컨테이너화 시키는 방법을 알아보았다. 이번에는 쿠버네티스와 GKE에 대해 알아보자. 쿠버네티스는 오픈소스이며 컨테이너의 오케스트레이터(orchestrator)로 어플리케이션의 관리와 확장을 도와준다. 쿠버네티스는 API를 인증된 사람들에게 제공하여 kubectl command와 같은 여러 유틸리티를 통해 동작을 제어한다. 쿠버네티스는 클러스터(cluster)라는 노드집합에 컨테이너를 배포한다. 클러스터란 무엇일까?

 

* 클러스터(cluster)

 

클러스터란 전체 시스템을 제어하는 마스터 컴포넌트의 집합과 컨테이너를 가동하는 노드 집합이다. 쿠버네티스에서 노드는 컴퓨터 인스턴스이며 Google Cloud에서 노드는 컴퓨팅 엔진의 가상 머신이다.

 

쿠버네티스를 사용하기 위해서, 사용자는 응용 프로그램들의 집합과 그것들이 어떻게 상호 작용해야 하는지를 설명할 수 있고, 쿠버네티스는 그것을 어떻게 만들 수 있는지 알 수 있다. 쿠버네티스는 컨테이너화된 어플리케이션을 구동하기 쉽게 만들어준다. 그렇다면 어떻게 쿠버네티스 클러스터를 얻을 수 있을까?

 

* 클러스터를 얻는 방법

 

가상 머신을 제공하는 환경이나 하드웨어를 기반으로 항상 직접 구축할 수 있지만 직접 구성했다면 유지보수도 직접 해야한다. 이것은 상당한 부담이 있다. 이런 수고를 줄이기 위해서 Google Cloud에서는 클라우드 환경에서 쿠버네티스를 관리하도록 쿠버네티스 엔진을 제공한다. 사용자는 GCP 콘솔 또는 Cloud SDK에 내장된 g-cloud 명령어를 통해 쿠버네티스 엔진을 사용하여 쿠버네티스 클러스터를 생성할 수 있다. GKE 클러스터는 사용자 지정할 수 있으며, 다양한 시스템 유형, 노드 수 및 네트워크 설정을 지원한다. 예를들어 다음과 같은 명령어로 쿠버네티스 클러스터를 생성할 수 있다.

 

$> gcloud container clusters create k1

 

명령이 완료되면 'k1'이라는 클러스터를 가지게 되며 GCP console을 통해 클러스터의 상태를 확인할 수 있다. 쿠버네티스가 단일 컨테이너 또는 컨테이너 집합을 배포할때마다 'pod'라고 불리는 추상모델 안에서 배포를 수행한다. pod는 쿠버네티스에서 가장 작은 배포가능한 단위이다. pod를 클러스터에서 실행중인 프로세스라고 생각할수도 있다. 이것은 어플리케이션의 하나의 컴포넌트일 수 있으며 또한 전체 어플리케이션일 수도 있다.

 

일반적으로 하나의 pod는 하나의 컨테이너를 갖는다. 그러나 서로 높은 의존성을 가진 다중 컨테이너를 가지고 있다면 이것을 단일 pod에 패키징 할 수 있다. pod내의 컨테이너들은 자동으로 네트워크를 공유하고 공동의 디스크 스토리지 볼륨를 얻는다. 쿠버네티스의 각 pod는 유니크한 IP Address를 얻고 컨테이너를 위한 포트를 세팅한다. pod 안의 컨테이너들은 localhost 네트워크 인터페이스를 사용해 상호 커뮤니케이션 하기 때문에 어떤 노드로 배포되는지는 관심을 두지 않는다.

 

쿠버네티스에서 pod의 컨테이너를 실행하는 손쉬운 방법은 kubectl run 명령어를 사용하는 것이다. 추후 이 모듈을 사용하는 더 나은 방법을 공유하겠지만 시작하기에는 이 방법이 가장 좋다. kubectl run 명령어를 실행하면 컨테이너가 pod를 실행하면서 배포가 시작된다. 예를들어 pod에서 실행되는 컨테이너에서 오픈소스 웹 서버인 Nignx 이미지를 실행할 수 있다. kubectl 명령어는 스마트하게 컨테이너 레지스트리로부터 요청하는 Nginx의 이미지를 가지고 온다. 그렇다면 배포(deployment)란 무엇일까?

 

* 배포(deployment)

 

배포라는 것은 동일한 pod의 복제(replica) 그룹을 나타낸다. 이렇게 함으로 일부 노드에 문제가 생겨도 pod를 계속 가동시킬 수 있다. 배포를 사용하여 어플리케이션 또는 전체 어플리케이션의 구성 요소를 포함할 수도 있다. Nginx pod의 구동을 살펴기 위해 kubectl get pods 명령어를 실행해보자. 기본적으로 pod는 배포 환경에 있거나 클러스터 내에서만 액세스 할 수 있다. 그렇다면 사용자가 어떻게 nginx 웹 서버 컨텐츠에 액세스할 수 있도록 할 수 있을까? pod를 공개적으로 사용할 수 있도록 하려면 kubectl apply 명령을 실행하여 pod에 로드 밸런서를 연결하면 된다. 그러면 쿠버네티스는 pod에 대해 고정된 IP 어드레스를 가진 서비스를 생성한다. 서비스는 쿠버네티스가 부하 분산을 나타내는 근본적인 방법이다. 조금 더 구체적으로 말하면 클러스터 외부의 다른 사용자가 액세스할 수 있도록 퍼블릭 IP 주소가 있는 외부의 로드 밸런서를 서비스에 연결하도록 쿠버네티스에 요청한다. GKE에서는 이런 종류의 로드 밸런서가 네트워크 로드 밸런서로 생성된다. 이 서비스는 컴퓨팅 엔진이 가상 시스템에서 사용할 수 있도록 하는 관리 로드 밸런싱 서비스 중 하나이다. GKE는 컨테이너를 간편하게 다룰수 있게 해준다. IP 주소에 도달하는 모든 클라이언트는 서비스 뒤에 있는 pod로 라우팅된다. 이런 경우에 nginx pod는 단 하나뿐이다. 그렇다면 서비스는 정확히 무엇일까?

 

* 서비스(service)

 

서비스는 한 세트의 포드들을 함께 그룹화하고 그들에게 안정된 엔드포인트를 제공한다. 이 경우 퍼블릭 IP는 네트워크 로드 밸런서에 의해 관리된다. 그런데 왜 서비스가 필요한가? 왜 pod의 IP 주소를 직접 사용하지 않을까? 어플리케이션이 프론트엔드와 백엔드로 구성되어 있다고 가정해보자. 프론트엔드는 서비스 없이 그 pod의 내부 IP 주소를 사용하여 백엔드에 접속할 수 있지 않았을까? 가능하지만 그렇게 했을때 관리에 문제가 생길것이다. 배포로 인해 pod가 만들어지고 소멸됨에 따라 pod는 개별적으로 IP 주소를 얻게되지만 이 주소는 시간이 지남에 따라 변화할 수 있다. 서비스는 사용자가 필요로 하는 안정적인 엔드포인트를 제공한다. 쿠버네티스에 대해 자세히 알게 되면 내부 애플리케이션 백엔드에 적합한 다른 서비스 유형을 발견할 수 있을 것이다. kubectl get services 명령은 서비스의 공용 IP 주소를 보여준다. 클라이언트는 이 주소를 사용하여 nginx 컨테이너를 원격으로 타격할 수 있다.

 

만일 더 많은 파워가 필요하면 어떻게 하면 될까? 배포를 확장하려면 kubectl scale 명령을 실행할 수 있다. 현재 클러스터에 3개의 nginx 웹 서버가 있다고 가정하자. 이것들은 모두 서비스 뒤에 존재하며 하나의 고정 IP 주소를 통해 모두 접근할 수 있다. 여러 종류의 유용한 파라미터와 함께 오토 스케일링 기능을 사용할 수도 있을 것이다. 예를들어 CPU 사용량에 따라 배포를 자동으로 확장하는 방법은 다음과 같다.

 

$> kubectl autoscale nginx --min=10 --max=15 --cpu=80

 

표시된 명령의은 최소 pod 수 10개, 최대 pod 수 15개 및 스케일 업 기준으로 CPU 사용량이 용량의 80%에 도달하면 쿠버네티스가 pod 수를 늘리도록 설정한 것이다. 지금까지 pod를 외부로 연결하는 방법과 스케일링과 같은 명령어를 실행하는 방법을 보았다. 이것은 쿠버네티스를 차근차근 배우고 시험하는 데는 효과가 있지만 쿠버네티스의 진정한 강점은 선언적인 방법으로 작업할 때 경험할 수 있다. 명령을 실행하는 대신 원하는 상태를 쿠버네티스에게 알려주는 구성 파일을 제공하면 쿠버네티스는 그것을 실행 하는 방법을 알아낸다. 이러한 구성 파일은 관리 도구가 된다. 변경을 위해선 파일을 편집한 다음 변경된 버전을 쿠버네티스에 표시하면 된다.

 

$> kubectl get pods -l "app=nginx" -o yaml

 

표시된 명령어는 우리가 이미 수행한 작업에 근거하여 이 파일들 중 하나에 대한 스타팅 포인트를 얻을 수 있는 한 가지 방법이다. 그 명령의 출력은 이렇게 보일 것이다.

 

apiVersion : v1

kind : Deployment

metadata :

     name : nginx

     labels : 

          app : nginx

spec :

     replicas : 3

     selector :

          matchLabels :

               app : nginx

     template :

          metadata :

               labels : 

                    app : nginx

          spec : 

               containers :

               - name : nginx

                  image : nginx:1.15.7

                  ports :

                  - containerPort : 80

 

이 파일을 처음 접한다면 상당히 어렵게 느껴질 것이다. 내용이 길고 아직 이해하지 못하는 구문을 포함하고 있기 때문이다. 하지만 조금만 익숙해진다면 간단히 친구가 될 수 있을 것이다. 그리고 이를 버전 제어 시스템에 저장하여 인프라에 대한 변경 사항을 추적할 수 있다. 이 케이스에서 배포 구성 파일은 nginx pod의 복제본(replica) 3개를 원한다고 선언한다. 선택 필드를 정의하므로 특정 pod를 복제본으로 그룹화하는 방법을 배포에서 알 수 있다. 모든 특정 pod가 라벨을 공유하기 때문에 효과적이다. 그들의 어플리케이션에는 nginx라는 라벨이 붙어 있다. 이러한 선언적인 방법에서는 3개가 아닌 5개의 복제본을 실행하려면 배포 구성 파일을 편집하여 3을 5로 변경하기만 하면 된다. 그런 다음 kubectl appl 명령을 실행하여 업데이트된 구성 파일을 사용하자. 이제 kubectl get replicasets 명령을 사용하여 복제본을 보고 업데이트된 상태를 확인해보자. 그런 다음 kubectl get pods 명령을 사용하여 pod의 정보가 온라인으로 들어오는 것을 확인해보자. 이 경우 5개 모두 준비되고 실행된다. 마지막으로 적절한 수의 복제본이 배포를 통해 실행되고 있는지 배포를 확인해보자. 이 경우 5개의 pod 복제품을 모두 사용할 수 있다. 그리고 만에 하나 클라이언트가 엔드포인트 공격을 시도할 수 있다. kubectl get services 명령은 서비스의 외부 IP가 영향을 받지 않음을 확인한다. 이제 GKE에서 실행 중인 nginx pod의 5개 사본과 5개 pod의 트래픽을 모두 처리하는 단일 서비스를 이용하자. 이 기술은 당신이 쿠버네티스에서 서비스를 확장하고 부하를 분산할 수 있게 해준다. 마지막으로 어플리케이션 버전을 업데이트하려는 경우 어떻게 할 수 있을까? 

 

* 어플리케이션 업데이트

 

우리는 컨테이너를 업데이트하고 빠르게 클라이언트에게 새로운 코드를 내보내고 싶을 것이다. 그러나 모든 변경사항을 한번에 롤아웃하는 것은 위험할 수 있다. 우리는 어플리케이션을 재구성하고 다시 배포하는 동안 클라이언트가 다운타임을 겪지 않도록 해야한다. 이것이 왜 배포의 한 어트리뷰트가 업데이트 전략인지를 알려주는 대목이다. 예를들어 롤링 업데이트와 같은 전략이 있다. 배포를 위해 롤링 업데이트 전략을 선택한 다음 관리하는 소프트웨어의 새 버전을 넘겨주면 쿠버네티스는 이전 버전의 pod중 하나를 폐기하기 전에 새 버전의 pod가 사용할 수 있는 상태가 되기까지 기다리며 새 버전의 pod를 하나씩 새로 생성하게 된다. 롤링 업데이트는 새 버전의 어플리케이션을 생성하여 클라이언트로 하여금 다운타임을 느끼지 못하도록 하는 빠른 방식이다.