이번 글은 쿠버네티스의 API, 그리고 Object에 대해서 이야기를 해보겠습니다.
k8s API
쿠버네티스 컨트롤 플레인의 핵심은 API 서버로 API 서버는 최종 사용자, 클러스터의 다른 부분 그리고 외부 컴포넌트가 서로 통신할 수 있도록 HTTP API를 제공합니다.
쿠버네티스 API를 사용하면 쿠버네티스의 API 오브젝트(예: 파드(Pod), 네임스페이스(Namespace), 컨피그맵(ConfigMap) 그리고 이벤트(Event))를 질의(query) 및 조작 가능하고, 대부분의 작업은 kubectl 커맨드 라인 인터페이스를 통해 사용합니다.
레퍼런스:
k8s Object
Object?
- 쿠버네티스의 오브젝트(objects)는 클러스터의 상태를 나타내는 단위(entities)
- 오브젝트는 “의도를 담은 레코드”입니다. 생성된 클러스터는 그 의도대로 존재할 수 있도록 최선을 다하며 이는 클러스터의 “의도한 상
태(desired state)“라고 알려져 있음 - 쿠버네티스는 항상 오브젝트의 “현재 상태”를 “의도한 상태”와 동일하게 만들게끔 작동
default object
- 파드 (Pod) - 쿠버네티스에서 실행되는 최소 단위 독립적인 공간과 IP를 가짐
- 네임스페이스 (Namespace) - 쿠버네티스 클러스터에서 사용되는 리소소들을 구분해서 관리하는 그룹
- 볼륨 (Volume) - 파드가 사라지더라고 저장/보존이 가능하며 파드에서 사용할 수 있는 스토리지를 제공
- 서비스 (Service) - 파드는 유동적이기 때문에 접속 정보가 고정되지 않으므로, 파드 접속을 안정적으로 유지하기 위한 기능
레퍼런스:
Object Spec and Status
apiVersion: apps/v1 kind: Deployment metadata: name: boarder namespace: boarder uid: a1d511b9-f8a1-45be-903f-27beb9ec2674 resourceVersion: '14861417' generation: 18 creationTimestamp: '2023-03-21T09:49:50Z' labels: app.kubernetes.io/instance: boarder app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: boarder argocd.argoproj.io/instance: boarder helm.sh/chart: autocrypt-dev-0.0.10 annotations: deployment.kubernetes.io/revision: '16' kubectl.kubernetes.io/last-applied-configuration: > {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"boarder","app.k managedFields: manager: argocd-controller manager: kube-controller-manager operation: Update apiVersion: apps/v1 time: '2023-04-20T08:52:54Z' fieldsType: FieldsV1 subresource: status selfLink: /apis/apps/v1/namespaces/boarder/deployments/boarder status: observedGeneration: 18 replicas: 1 updatedReplicas: 1 readyReplicas: 1 availableReplicas: 1 conditions: type: Available status: 'True' lastUpdateTime: '2023-04-20T07:22:12Z' lastTransitionTime: '2023-04-20T07:22:12Z' reason: MinimumReplicasAvailable message: Deployment has minimum availability. type: Progressing status: 'True' lastUpdateTime: '2023-04-20T08:52:54Z' lastTransitionTime: '2023-04-11T01:36:36Z' reason: NewReplicaSetAvailable message: ReplicaSet "boarder-68b675fc6f" has successfully progressed. spec: replicas: 1 selector: matchLabels: app.kubernetes.io/instance: boarder app.kubernetes.io/name: boarder template: metadata: creationTimestamp: null labels: app.kubernetes.io/instance: boarder app.kubernetes.io/name: boarder annotations: cluster-autoscaler.kubernetes.io/safe-to-evict: 'true' spec: containers: name: boarder image: >- 297752572146.dkr.ecr.ap-northeast-2.amazonaws.com/boarder/dev/k8s:f5e2641deb07792c3d19736a0c25c3b4bd5fffb2 ports: name: http containerPort: 3003 protocol: TCP env: name: AWS_REGION value: ap-northeast-2 name: PORT value: '3003' - name: APP_ENV value: dev resources: limits: cpu: 250m memory: 512M requests: cpu: 250m memory: 512M terminationMessagePath: /dev/termination-log terminationMessagePolicy: File imagePullPolicy: IfNotPresent securityContext: privileged: false allowPrivilegeEscalation: false restartPolicy: Always terminationGracePeriodSeconds: 30 dnsPolicy: ClusterFirst serviceAccountName: boarder serviceAccount: boarder securityContext: fsGroup: 1337 affinity: {} schedulerName: default-scheduler dnsConfig: options: - name: ndots value: '3' strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 25% maxSurge: 25% revisionHistoryLimit: 2 progressDeadlineSeconds: 600
오브젝트는 spec(스펙, 명세)과 status(상태) 등의 값을 가지는데, 여기에는 오브젝트를 생성한 의도나 오브젝트를 관리할 때 원하는 상태 등을 설정
Wanted YAML sample
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 # tells deployment to run 2 pods matching the template template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
실제 적용은 kubectl apply -f {test}.yaml 명령어로 실행
Management
- 명령형 커맨드
- command에 변수들을 수동 넣어 kubectl로 호출, ex:
kubectl create deployment nginx --image nginx
- 단점: audit trail을 제공하지 않음
- 명령형 오브젝트
- 위와 같이 yaml 파일을 만들고 이를 인자로 호출, ex:
kubectl create -f nginx.yaml
- 선언형 오브젝트
- dir 에 파일들을 모아두고 통째로 호출, ex:
kubectl apply -f configs/
Required Field
- apiVersion은 이 스크립트를 실행하기 위한 쿠버네티스 API 버전이다 보통 v1을 사용한다.
- kind 에는 리소스의 종류를 정의하는데, Deployment를 정의하려고 하기 때문에, Deployment라고 넣는다.
- metadata에는 이 리소스의 각종 메타 데이타를 넣는데, 라벨(뒤에서 설명할)이나 리소스의 이름등 각종 메타데이타를 넣는다
- spec 부분에 리소스에 대한 상세한 스펙을 정의한다.
- Pod는 컨테이너를 가지고 있기 때문에, container 를 정의한다. 이름은 nginx-deployment로 하고 도커 이미지 nginx:1.14.2 를 사용하고, 컨테이너 포트 80을 오픈한다.
Pod는 다음과 같이 매우 재미있는 특징을 갖는다.
- Pod 내의 컨테이너는 IP와 Port를 공유한다. 두 개의 컨테이너가 하나의 Pod를 통해서 배포되었을때, localhost를 통해서 통신이 가능하다.예를 들어 컨테이너 A가 8080, 컨테이너 B가 7001로 배포가 되었을 때, B에서 A를 호출할때는 localhost:8080 으로 호출하면 되고, 반대로 A에서 B를 호출할때에넌 localhost:7001로 호출하면 된다.
- Pod 내에 배포된 컨테이너간에는 디스크 볼륨을 공유할 수 있다. 근래 애플리케이션들은 실행할때 애플리케이션만 올라가는것이 아니라 Reverse proxy, 로그 수집기등 다양한 주변 솔루션이 같이 배포 되는 경우가 많고, 특히 로그 수집기의 경우에는 애플리케이션 로그 파일을 읽어서 수집한다.
- 애플리케이션 (Tomcat, node.js)와 로그 수집기를 다른 컨테이너로 배포할 경우, 일반적인 경우에는 컨테이너에 의해서 파일 시스템이 분리되기 때문에, 로그 수집기가 애플리케이션이 배포된 컨테이너의 로그파일을 읽는 것이 불가능 하지만, 쿠버네티스의 경우 하나의 Pod 내에서는 컨테이너들끼리 볼륨을 공유할 수 있기 때문에 다른 컨테이너의 파일을 읽어올 수 있다.
위와 같이 애플리케이션과 애플리케이션에서 사용하는 주변 프로그램을 같이 배포하는 패턴을 마이크로 서비스 아키텍쳐에서 사이드카 패턴(Side car pattern)이라고 하며 Istio등이 이와 같이 실행된다
(이후 사용될 filebeat도 이렇게 동작할것으로 보임)
Namespace
단일 클러스터 내에서 리소스 그룹을 격리하기 위한 메커니즘으로 Namespace라는 개념을 사용함. 리소스의 이름은 네임스페이스 내에서 유일해야 하며, 네임스페이스 간에서 유일할 필요는 없다.
네임스페이스 기반 스코핑은 네임스페이스 기반 오브젝트 (예: 디플로이먼트, 서비스 등)에만 적용 가능하며 클러스터 범위의 오브젝트 (예: 스토리지클래스, 노드, 퍼시스턴트볼륨 등)에는 적용 불가능
- 처음에 4개의 초기 네임스페이스를 갖는다.
- default 다른 네임스페이스가 없는 오브젝트를 위한 기본 네임스페이스
- kube-system 쿠버네티스 시스템에서 생성한 오브젝트를 위한 네임스페이스
- kube-public 이 네임스페이스는 자동으로 생성되며 모든 사용자(인증되지 않은 사용자 포함)가 읽기 권한으로 접근할 수 있어 주로 전체 클러스터 중에 공개적으로 드러나서 읽을 수 있는 리소스를 위해 예약되어 있음
- kube-node-lease 이 네임스페이스는 각 노드와 연관된 리스 오브젝트를 갖는다. 노드 리스는 kubelet이 하트비트를 보내서 컨트롤 플레인이 노드의 장애를 탐지할 수 있게 한다.
이는 컨테이너가 <서비스-이름> 만 사용하는 경우, 네임스페이스 내에 국한된 서비스로 연결된다. 그렇기 때문에, 모든 네임스페이스 이름은 유효한 RFC 1123 DNS 레이블이어야 한다.
Label
포드와 같은 객체(오브젝트)에 연결된 키/값 쌍으로 사용자에게 의미 있고 관련이 있는 객체(오브젝트)의 식별 속성을 지정하는 데 사용되지만 핵심 시스템에 의미 체계를 직접적으로 암시하지는 않음
쉽게 표현하자면 AWS의 TAG와 거의 같다고 보면 됨! 중요한 개념은 아래의 두가지가 있음.
- Label 규칙
레이블은 키와 값의 쌍이다. 유효한 레이블 키에는 슬래시 ( / )로 구분되는 선택한 접두사와 이름이라는 2개의 세그먼트가 있다. 이름 세그먼트는 63자 미만으로 시작과 끝은 알파벳과 숫자 ( [a-z0-9A-Z] )이며, 대시 ( - ), 밑줄 ( _ ), 점 ( . )과 함께 사용할 수 있다. 접두사는 선택이다. 만약 접두사를 지정한 경우 접두사는 DNS의 하위 도메인으로 해야 하며, 점 ( . )과 전체 253자 이후, 슬래시 ( / )로 구분되는 DNS 레이블이다.
접두사는 생략하면 키 레이블은 개인용으로 간주한다. 최종 사용자의 오브젝트에 자동화된 시스템 컴포넌트(예: kube-scheduler, kube-controller-manager, kube-apiserver, kubectl, 또는 다른 타사의 자동화 구성 요소)의 접두사를 지정해야 한다.
유효한 레이블 값은 다음과 같다:
- 63자 이하여야 하고 (공백일 수도 있음),
- (공백이 아니라면) 시작과 끝은 알파벳과 숫자 ( [a-z-9A-Z] )이며,
- 알파벳과 숫자, 대시 ( - ), 밑줄 ( _ ), 점 ( . )을 중간에 포함할 수 있다.
Ref:
- Selector
오브젝트 이름과 UID와는 다르게 레이블은 고유하지 않아 일반적으로 많은 오브젝트에 같은 레이블을 가질것으로 예상한다. 따라서 레이블 셀렉터를 통해 클라이언트와 사용자는 오브젝트를 식별할 수 있다.
레이블 셀렉터는 쿠버네티스 코어 그룹의 기본 구성 요소중 하나로 현재 일치성 기준 과 집합성 기준 이라는 두 종류의 셀렉터를 지원하며, 쉼표로 AND 연산자와 같은 요구사항을 충족할 수 있음
- 일치성 기준 요건: = , == , != 이 세 가지 연산자만 허용
environment = production tier != frontend # == environment=production,tier!=frontend
- 일반적으로 많이 쓰는 pod에서 node selector 예시
apiVersion: v1 kind: Pod metadata: name: cuda-test spec: containers: - name: cuda-test image: "registry.k8s.io/cuda-vector-add:v0.1" resources: limits: nvidia.com/gpu: 1 nodeSelector: accelerator: nvidia-tesla-p100 # accelerator=nvidia-tesla-p100 레이블을 가진 노드에서만 뜨게 됨
- 집합성 기준 요건: 집합성 기준 레이블 요건에 따라 값 집합을 키로 필터링
- in , notin 과 exists(키 식별자에만 해당) 3개의 연산자를 지원
- environment in (production, qa)
- environment가 production or qa인 리소스
- tier notin (frontend, backend)
- tier 가 frontend, backend가 아닌 리소스
- partition (exists)
- partition이라는 레이블 키가 존재하는 리소스
- !partition (exists not)
- partition이라는 레이블 키가 존재하지 않는 리소스
- environment in (production, qa)
- in , notin 과 exists(키 식별자에만 해당) 3개의 연산자를 지원
ex) partition in (customerA, customerB),environment!=qa
- 테스트 방법
kubectl get pods -l app.kubernetes.io/instance=boarder,pod-template-hash!=test -n boarder
Annotations
쿠버네티스 어노테이션을 사용하여 임의의 비-식별 메타데이터를 오브젝트에 첨부할 수 있다, 레이블과 같이 키/값 맵으로 구성된다. 정도가 공식 설명인데 여기서 핵심은 비-식별과 Annotation(주석) 이라는 이름.
- 비-식별
- label은 쿠버네티스 개체에 부여가 되는 key-value 값으로 index가 되어 리소스들을 구분하는데에 사용됩니다. 특정 레이블을 가진 녀석, 안가진 녀석 과 같이 쿼리를 하는것들이 가능해 리소스 그룹을 지정하는데에 사용
- 하지만 Annotation은 그러한 식별이 되지 않는 비식별 정보로 쿠버네티스 내부에서 인식이 되어 사용되는 값은 아님
- 주석
- 위에서 말햇듯 식별이 되지는 않지만 추가적으로 정보를 기입한다는 점에서 코드에 실제로 적용은 되지 않지만 추가적인 것을 설명하는 “주석”과 같은 역할로 주로 쓰임
- ex) 쿠버네티스 내부적으로 사용되지는 않지만 외부 3rd party에서 사용되기 위한 key-value 데이터, 프로젝트 담당자나, 비상연락처, repository를 기입해 빠르게 정보를 확인하게 하는 요소, 혹은 모니터링에 필요한 정보, 릴리즈 정보등
Finalizer
파이널라이저는 쿠버네티스가 오브젝트를 완전히 삭제하기 이전, 삭제 표시를 위해 특정 조건이 충족될 때까지 대기하도록 알려주기 위한 네임스페이스에 속한 키(namespaced key)
파이널라이저를 가진 특정한 오브젝트를 쿠버네티스가 삭제하도록 지시할 때, 쿠버네티스 API는 .metadata.delationTimestamp을 덧붙여 삭제하도록 오브젝트에 표시하며 202상태코드(HTTP"Accepted")을 리턴한다. 대상 오브젝트가 Terminating 상태를 유지하는 동안 컨트롤 플레인 또는 다른 컴포넌트는 하나의 파이널라이저에서 정의한 작업을 수행한다. 정의된 작업이 완료 후에, 그 컨트롤러는 대상 오브젝트로부터 연관된 파이널라이저를 삭제한다. metadata.finalizers 필드가 비어있을 때, 쿠버네티스는 삭제가 완료된 것으로 간주하고 오브젝트를 삭제한다.
- argocd에서는 여러 리소스들을 application set 로 총괄 관리 및 운영하는데 이 finalizer 개념을 사용해 종종 지우는 요청을 하는데도 아무런 동작을 하지 않고 hang이 걸리는 경우가 있음
- 이런 경우 문제가 되는 리소스의 finalizer가 제대로 지워지지 않아 (다른 과정이 동시에 일어나며 해당 작업이 무시됨) 리소스 삭제가 이루어지 않는게 원인임.
Owner and Dependents
- 쿠버네티스에서 일부 오브젝트들은 다른 오브젝트의 owner인 경우가 있다
- ex) deployment나 replicaset은 pod set의 owner이다.
- 이러한 종속 관계를 보통 k8s에서 일반적으로 관계에 맞게 설정해주지만 수동으로 설정해야하는 경우가 있기에 이러한 옵션이 존재한다.
- 특수한 경우에만 쓰이기에 그냥 이러한 것이 있다. 라는 정도만 알고 넘어가자.
Field Selector
kubectl get pods --field-selector status.phase=Running
Ref:
이렇게 해서 쿠버네티스에 대한 overview를 마무리 합니다! 🤓