View

이전 글에서는 kubernetes 기본적인 아키텍쳐와 Google Kubernetes Engine에서 제공하는 관리 기능들을 살펴보았다. 이번글에서는 조금 더 자세하게 k8s object 의 동작과 관리방법에 대해서 정리했다. 

 


>  이전 글에서 봤던것 처럼, 3개의 nginx 웹 서버를 동시해 실행시켜야 하는 경우를 생각해보자. 가장 간단한 방법은 3개의 pod object를 선언하고 단순하게 그것의 desired states를 각각 선언하는 것이다. Pod의 desired state에 따라서 각 pod는 반드시 생성이 될 것이고, 내부적으로 nginx container이미지가 수행되어 사용될 것이다. 

> K8s는 manifest파일들을 통해서 이러한 pod object를 생성하고 관리할 것이다. manifest파일은 일반적인 텍스트파일인데, YAML이나 JSON 형식으로 작성할 수 있다. YAML이 JSON보다 사람이 더 읽기 편하고 수정하기 쉬워, 이것을 주로 이것을 통해서 작성한다. 

> YAML 파일은 pod의 desired state를 정의하는데, 그것의 pod의 name과 수행시켜야할 특정 conatainer이미지를 포함한다.  manifest파일은 작성해야 하는 특정한 필드들을 포함하고 있다.

- apiVersion : object 생성 시 사용할 k8s의 API버전을 나타낸다. K8s 프로토콜도 버전을 갖는데 이것은 하위 호환성에 대해 도움을 준다. 
- kind : object의 타입을 알려준다. (위의 예제는 Pod 오브젝트를 사용하고 있다.)  
- metadata : object를 식별하기 위한 값이 들어가는데 name, id 그리고 선택적으로 namespace를 입력할 수 있다. 

> K8s object에 따라서 필요한 설정들이 조금씩 달라지는데, 이러한 설정 또한 별도의 파일에 작성하는 것이 아니라 효율적으로 관리하기 위해서 하나의 YAML 파일에 작성한다. 
- spec : pod object가 사용할 container의 이름과 사용할 이미지를 명시한다. 

> YAML파일은 반드시 버전 관리 도구를 사용하여 저장해야 한다. 이것은 변화관리 및 변화에 대한 추적을 쉽게 해준다. 그리고 또한 cluster를 새로 만들거나 복구하는 경우 매우 도움이 된다.  많은 GCP 고객들이 이러한 목적을 위해서 Cloud Source Repository 를 사용하는데, 이러한 서비스들은 그 파일들의 권한을 GCP에서 관리되는 다른 리소스들과  동일한 방법으로 관리해준다. 

 

> K8s 오브젝트를 생성할때, 이름은 string 값으로 설정하게 되는데 그 값은 반드시 unique해야 한다. 같은 K8s namespace 안에는 중복된 object name은 허용하지 않는다. 당연할 수도 있지만 특정 object가 삭제된다면 그 object가 가지고 있던 이름은 다른 object가 사용할 수 있다. 영문자, 하이픈, 마침표가 이름으로 사용될 수 있으며 그것의 최대 길이는 253이다. 

> 또한 모든 object는 존재하는 동안 k8s cluster에서 유일한 UID를 갖는다. 

> YARM파일에 Labels 은 key-value 쌍으로 이루어져 있는데, object에 대한 tag를 설정하는 것으로 생성시, 생성 이후에 설정하는것 모두 가능하다. Labels은 object 관리편의성을 위해서 object를 식별할 수 있게 해주며, k8s object들을 부분적으로 그룹지어서 관리할 수 있도록 해준다. 

> 예를들어 'app'이라는 label을 생성하고 그것의 값을 수행하고자 하는 applicaiton 이름으로 설정할 수 있다. 위 예제에서 deployment object는 3개의 서로 다른 key-values label이 설정된다. (application, environment, stack) 

> 다양한 contexts들이 k8s resource를 구분짓기위해 그것의 label을 사용한다. 특히 kubectl 명령어를 통해서 모든 pod 중에서 원하는 pod를 찾을때 많이 사용하는데 label selector는 표현력이 좋아서 전체 이름을 주지 않더라도 해당 라벨을 찾을 수 있다.

 


> 위 그림에서는 3개의 nginx웹서버를 3개의 pod object로 선언하여  각각의 pod이 별도의 YAML파일을 갖도록 구성한 것이다. 

> k8s의 기본 스케쥴링 알고리즘은 workload를 nodes들에 균등하게 뿌려주는 것이다. 하지만 이러한 방식으로 스케쥴링하는 것은 workload의 수가 많아지면 관리가 어려워진다는 단점이 있다. (200개의 pod이 존재하고 200개의 nodes가 존재하면 모든 nodes에 pod이 분산되게 된다. 

> 또 다른 문제점은 pod자체적으로 복구하지 못하기 때문에 계속해서 기동되지 못한다는 점이다. pod에 문제가 발생한다면 pod가 중지되고 버려질 것이다. (https://svgsilh.com/search/clock-1.html)

> 이러한 이유들로 K8s에서 개별 Pods를 선언에서 관리하는 방법은 좋은 것이 아니다. Pods들이 서로 같이 동작하도록 설정하여 app의 가용성이 확보되도록 설정해야한다. 

 

 

>이런 문제들을 해결하기 위해서 pod들의 상태를 관리하는 controller object를 정의할 수 있다. 
- Deployments controller 
- StatefulSets controller 
- DaemonSets controller
- Jobs controller 

 

> Deployments controller는 오랫동안 수행되어야 하는 S/W components에 대해 좋은 선택이다. 특히 그것들이 group을 지어서 관리되어야 하는 경우에 바람직하다.

>  3개의 nginx 웹서버 예시를 들어보자. kube-scheduler는 deployement 내부에 정의된 pod를 스케쥴링하고  kube-apiserver 알림을 준다. 이러한 변경사항은 지속적으로 controller에 의해서 모니터링 되는데, 위의 예제의 경우에는  deployment controller에서 수행된다.  Deployment controller의 실질적인 역할은 3개의 nginx pod의 상태를 모니터링하고 기동하는 것이다. Deployment controller는 child-object(ReplicaSets controller)을 생성하여 desired state의 pod이 되도록 한다. 만약 하나의 pod에서 실패가 난다면 ReplicaSets controller는 현재 상태가 desired 상태와 다르다는 것을 확인하고, 새로운 pod를 기동시켜서 이러한 상태를 해소하려고 할 것이다.

>Deployment controller를 사용하면 여러 개의 YAML manifest파일을 사용하여 각 pod을 수행하는 대신, 하나의 deployment YAML 을 통해서 3개의 replica container를 수행시킬 수 있다. 

> Deployment controller는 정의된 Pods의 집합들이 언제든 기동하도록 보장한다. Deployment controller를 위한 manifest파일을 정의하면서 object spec안에, 얼마나 많은 pods를 복제할 것인지 설정하거나, 몇 개의 pod들이 기동되어야 하는지(위에서는 3개로 설정되어 있다), 그리고 어느 k8s Volumn들이 mount 되어야 하는지 정의할 수 있다. 이러한 template를 기반으로 controller는 cluster내에서 pods의 desired 상태를 유지하게된다. Deployments controller는 위에서 설명했던 것 보다 더 많은 역할하는데 이는 차후에 더 자세하게 다를 예정이다. 


 

> 하나의 cluster를 여러개의 project에서 사용하는 것은 매우 일반적인 경우다. 이렇게 구성하여 사용하는 경우 project와 team에 따라서 resource 사용제한quotas를 설정하는것은 필수적이다.  여기서 project는 우리가 일반적으로 이야기하는 '과제'인데 이것이 GCP project와 연관하여 사용하는 것이 일반적이고, 그것의 단위로 IAM 정책을 설정하고 비용을 청구하게 된다. 

> K8s는 물리적인 cluster를 여러개의 가상 cluster로 추상화 시킬 수 있는데 이것이 우리가 알고있는 namespaces이다. Namespace는 pod, deployment와 같은 리소스에 이름 공간을 제공한다. 위의 예제에서는 3개의 namespace (test, stage, prod)가 존재하는데, 동일한 namespace에서 object name이 중복될 수 없다. 만약에 nginx 용도로 사용할 pod object 3개를 동일한 이름으로 만들수는 있다. 하지만 그것은 동일한 namepace에서는 존재할 수 없고, 반드시 서로다른 namespace에 존재해야 한다. 

> namespace에서 또한 cluster에 대한 resource quota를 설정할 수 있다. 이러한 quota 정의는 namespace 내에서 사용하는 자원에 대한 제한을 설정한다. 만약 이러한 quota가 GCP quotas와 동일하지 않다면 K8s cluster에서 정의한 quotas 의 설정을 따른다. 

>다른 관리 측면으로 볼때, 일반적으로 namespace를 쓰는것 보다 label을 사용한다. 그러나 namespace는 매우 유용한 도구로 배포를 복사해서 빠르게 테스트 하고 싶은 경우, 새로운 namespace를 만들어서 object name 충돌없이 빠르게 처리할 수 있다.  

> 기본적으로 kubernetes cluster에는 3개의 namespace가 존재한다. 

- default namespace : namespace 중 가장 우선시 되는 것으로 namespace가 정의되지 않은 object들이 할당된다. 

- kube-system : k8s 시스템 자체적으로 생성된 object들을 위한 namespace이다. 그림에서 여러 종류의 object를 볼 수 있다. kubectl 명령어를 사용할때 kube-system namespace에 있는 object들은 대상에서 제외된다. 그러나 명목적으로 해당 object를 명시한다면 object의 content에 대한 내용을 볼수도 있다. 

- kube-public : 모든 사용자가 볼 수 있는 object를 위한 namespace이다. kube-pulbic은 cluster에서 수행되는 모든 정보를 전달하기 위한 도구이다. 이 namespace는 반드시 사용하는 것은 아니고, 데이터 공유와 같은 작업이 필요한 경우에만 사용한다. 

 

>  Namespace flag를 통해 namespace를 생성할 때 해당 namespace에 추가할 resource를 설정할 수 있으며, YAML 파일 안에서 namespace를 명시할 수 있다. command line level을 통해서 namespace를 적용할 수 있는데, command line level을 사용하는 것은 YAML 파일을 더 유연하게 해준다.

> 만약에 2개의 동일한 하지만 완전히 독립된 인스턴스를 deployments에 두고 각각이 namespace를 갖도록 하는 경우를 생각해보자. 이것은 운영환경에 배포하기 test를 하기 위한 namespace를 만드는 경우를 생각할 수 있다. 만약 이런 용도로 미리 YAML 파일에 namespace 이름을 넣어두는 것은 쉽지 않다. 따라서 CLI를 통해서 테스트 하기전에 namespace를 별도로 생성하여 배포하는 것이 좋을 수 있다. 


* Kubernetes Service

> Pod는 동적으로 생성되고 삭제된다. pods들이 각각 할당된 pod IP를 통해서 통신할 수 있지만, 그것의 IP주소들은 임시로 생성된 것으로 pod가 재시작되거나 scaling와 같이 pod에 변경이 일어나는 경우, IP주소가 지속적으로 유지되지 않는다. 이러한 문제는 서비스에 영향을 주게 된다. 

> 예를들어 2가지 pods 집합이 있다고 가정해보자. 하나는 frontend고 하나는 backend이다. 지속적으로 scaling 되는 backend Pods의 IP주소는 지속적으로 바뀌기 때문에 frontend Pods backend에 접근하지 못하게 된다. 이럴때 Kubernetes Server를 사용하게 된다. 

Kubernetes Service는 특정 서비스, 함수, 인프라들에 대한 고정 IP를 제공해준다. 이것은 서비스를 수행하는 Pods의 집합을 N/W 추상화 하는 것이다. 이것은 원래 각 Pods들이 가지고 있는 임시IP주소를 감춰준다. 

>예를들어 backend pod들은 frontend pod들에게 Kubernetes Service를 통해서 노출되어진다. 일반적으로 Service들은 Pods들의 집합을 정의하고 여러 타입 중 하나의 load-balancer를 생성한다. 이것을 통해서 Pods가 접근할 수 있도록 해준다. 그리고 Pods 들은 label selector에 의해서 선택되어진다.  

> Service는 k8s에게 Deployment를 요청하면서 같이 받을 수 있다. 이것을 통해서 k8s는 올바른 pods를 선택하는 작업을 수행한다.  Service가 생성될 때마다 k8s는 자동으로 endpoint resource를 성함으로써 선택된 Pods에 대한 endpoint를 생성한다. 

> 기본적으로 master는 내부 IP table로 부터 Service를 위한 가상 IP를 할당해 주는데 이것을 cluster ip라고도 한다. GKE에서 이것은 cluster VPC 네트워크로부터 할당된다.  다른 모듈에서 Service에 대해서 더 자세히 이야기 하겠지만 GKE는 ClusterIP로 부터 전달받는 것이 아니라는 것을 기억하면 된다. 

> 전체적으로 Service는 Pods들 위해서 지속되는 endpoint를 제공한다. 이러한 endpoint는 내부 cluster 또는 외부와 Service에 의해서 접근할 수 있게 된다.  Service가 내부적으로 또는 외부적으로 보여지는 옵션은 Service의 타입의 의해서 결정이 된다.  프론트앤드 Pod는 백앤드 Pod에게 cluster 내부적으로 서비스를 통해서 확실히 접근할 수 있다. 


> Container app은 쉽게 container 내부의 layer에서 데이터를 읽고 쓸 수 있다. 하지만 이것은 임시적인 것으로 container가 종료되면 쓰여진 데이터는 사라진다. Kubernetes Volumn은 또다른 추상화로 지속적으로 유지되는 스토리지를 제공한다. Volumn은 단순하게 Pods 안에 모든 container에서 접근 가능한 directory이다.  

> Volumn를 사용할 것인지는 Pod 정의에서 수행한다. 이것은 어떻게 directory가 생성될 것인지 어떠한 storage를 사용할 것인지 초기 데이터는 어떤것이 있을것인지를 설정하는 것이다.  container 실패 및 재시작으로 인해서 이러한 K8s volumn안에 데이터에 영향을 끼치지 못한다. 

> Volume은 여러 Pods 안의 여러 container들과 공유할 수도 있는데, Docker container는 자체 F/S을 가지고 있음으로 
각 Pod 안에 있는 container에서 별도로 K8s Volumn을 mount 시켜서 사용하여야 한다.  그러나 Pods가 자체적으로 임시적이기 때문에 node나 Pod 삭제의 경우 Volumn의 데이터가 같이 삭제된다는 것을 명심해야 한다. 이러한 경우에도 데이터를 유지하고 싶다면 N/W기반의 storage로 Volumn을 구성해야 하는데, 
해당 storage는 Pod 밖에 두어서 Pod나 Node의 장애가 발생하더라도 Volumn의 데이터가 손실되지 않는다. 


 

 

> Controller의 종류는 여럿이지만 우선적으로 알아야 하는 주요 controller를 정의해보자. 

ReplicaSets : Pods들의 수를 관리해준다. 모든 Pods들이 동일(복제본)해야 하고 동시에 수행되고 있어야 한다. 
Deployment는 선언적declarative 으로 ReplicaSet와 Pods를 업데이트할 수 있따. 사실 Deployment 는 자신의 ReplicaSet을 가지고 Deployement에서 정의한 desired state을 만족시키려한다. 그래서 보통 Replicaset controller은 Deployement와 함께 사용된다. 

Deployement : ReplicaSet을 이용하여 Pods들을 생성, 업데이트, 롤백, 확장할 수 있다. 예를들어 Deployment 에서 롤링 업그레이드를 수행할때 Deployment 오브젝트는 두번째 ReplicaSet을 생성하고 새로운 ReplicaSet의 Pod 수를 증가시키고, 기존에 있었던 ReplicaSet의 Pods 수를 감소시킨다. 

 Replication : ReplicaSet과 Deployment이 합쳐진 것 같은 역할을 했었다. 그러나 이제 더이상 사용하는 것을 권장하지 않는다 왜냐하면 Deployment가 ReplicaSet을 위해서 앞단을 제공하기 때문이다.

StatefulSets : 기존 상태를 유지하면서 app을 배포하고 싶을때 사용한다. StatefulSet은 Deployment 와 유사한데 Deployment 의 Pod는 container가 동일한 spec을 갖게 되지만 동일한 식별자를 가지고 있지는 않는다. 반면에 StatefulSet으로 만들어진 Pods는 유니크한 동일한 식별자를 가지게 된다. 이러한 특성을 이용해 상태정보를 이용해서 N/W일치성과 지속적인 disk storage를 사용할 수 있게 된다. (stateful) 

DeadmonSets : 만약 특정한 Pod가 전체 node에서 계속 기동인 상태로 있어야 한다면 DaemonSet 사용한다. DaemonSet은 특정 Pod들이 전체 ndoe 나 subset node에서 지속적으로 기동될 수 있도록 해준다. 만약 새로운 ndoes가 추가된다면 DaemonSet은 자동으로 필요한 spec을 가진 Pod를 기동시킨다. daemon 이라는 단어는 computer science 용어로 interactive하지 않은 프로세스를 말하며 다른 서비스들과 내부적으로 통신하는 단어이다. 
K8s cluster는 반드시 DaemonSet을 사용해서 fluentd 같은 logging 에이전트가 cluster 내의 모든 node에서 동작하도록 한다. 

Job : 는 하나 이상의 Pod를 생성하여 작업을 수행한다 그리고 작업이 완료되면 Job controller는 그 작업에 사용하였던 모든 Pod를 종료시킨다. Job controller와 함께 CronJob controller가 Pod들을 time-based 스케쥴링 할 수 있도록 한다. 


 

> Kubernetes Architecture를 두 번에 걸쳐서 정리해 보았다. 내용이 워낙 많기 때문에 위으 그림을 보고 모든 부분을 이해했는지 다시한번 확인하면 좋을 것 같다. 

Share Link
reply
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31