/
K8s

K8s

본 페이지 및 하위 페이지에서 쓰이는 Custom K8s object 정의 파일(yaml)은 아래 git repo에서 다운로드 할 수 있다.

Github repo: snetsystems/K8s-Objects

K8s는 여러 컨테이너 런타임을 지원하며, 여기서는 Docker 런타임을 기본으로 설명한다.
(Docker 18.09 이상에서는 containerd를 Docker에서 기본으로 탑재한다)
그러므로, Docker에 대한 기본 개념 및 사용법을 알고 있다고 가정한다.

Overview

Concept

K8s는 하나 이상의 container로 이루어진 pod cluster를, 하나의 어플리케이션인 것처럼 운영하는 방법을 제시한다는 철학을 기반으로 하여, planning 하고 독립적이고 조합 가능한 제어 프로세스들로 구성된 오픈소스 플랫폼.

기존 전통적 방식(monolithic)의 서비스 운영에서의 난제인 CD 및 어플리케이션 운영 자동화/관리를, 컨테이너 기반의 마이크로 서비스 기반으로 전환 관리하기 위해 보다 쉽고 나은 solution을 제시한다.

Key Features - K8s provide you with:

  • Service discovery and load balancing

    • Kubernetes can expose a container using the DNS name or using their own IP address. If traffic to a container is high, Kubernetes is able to load balance and distribute the network traffic so that the deployment is stable.

  • Storage orchestration

    • Kubernetes allows you to automatically mount a storage system of your choice, such as local storages, public cloud providers, and more.

  • Automated rollouts and rollbacks

    • You can describe the desired state for your deployed containers using Kubernetes, and it can change the actual state to the desired state at a controlled rate. For example, you can automate Kubernetes to create new containers for your deployment, remove existing containers and adopt all their resources to the new container.

  • Automatic bin packing

    • You provide Kubernetes with a cluster of nodes that it can use to run containerized tasks. You tell Kubernetes how much CPU and memory (RAM) each container needs. Kubernetes can fit containers onto your nodes to make the best use of your resources.

  • Self-healing

    • Kubernetes restarts containers that fail, replaces containers, kills containers that don’t respond to your user-defined health check, and doesn’t advertise them to clients until they are ready to serve.

  • Secret and configuration management

    • Kubernetes lets you store and manage sensitive information, such as passwords, OAuth tokens, and SSH keys. You can deploy and update secrets and application configuration without rebuilding your container images, and without exposing secrets in your stack configuration.

What K8s is not:

  • 지원하는 애플리케이션의 유형을 제약하지 않는다.

  • 소스 코드를 배포하지 않으며 애플리케이션을 빌드하지 않는다.

  • 애플리케이션 레벨의 서비스를 제공하지 않는다.

  • 로깅, 모니터링 또는 경보 솔루션을 포함하지 않는다. 즉, 메트릭을 수집하고 노출하는 메커니즘을 제공한다.

  • K8s는 단순한 오케스트레이션 시스템이 아니다. 사실, K8s는 오케스트레이션의 필요성을 없애준다. 오케스트레이션의 기술적인 정의는 A를 먼저 한 다음, B를 하고, C를 하는 것과 같이 정의된 워크플로우를 수행하는 것이다. 반면에, K8s는 독립적이고 조합 가능한 제어 프로세스들로 구성되어 있다. 이 프로세스는 지속적으로 현재 상태를 입력받은 의도한 상태로 나아가도록 한다. A에서 C로 어떻게 갔는지는 상관이 없다. 중앙화된 제어도 필요치 않다. 이로써 시스템이 보다 더 사용하기 쉬워지고, 강력해지며, 견고하고, 회복력을 갖추게 되며, 확장 가능해진다.

Architecture Components

Master node: 컨트롤 플레인(control plane) 컴포넌트

  • kube-apiserver

    • API 서버는 K8s 컨트롤 플레인의 프론트 엔드이다.

  • etcd

    • 모든 cluster 데이터를 담는 K8s backend consistent and highly-available key value storestore.

  • kube-scheduler

    • Node가 배정되지 않은 새로 생성된 pod를 감지하고, 실행할 node를 선택하는 컨트롤 플레인 컴포넌트.

    • 스케줄링 결정을 위해서 고려되는 요소는 resource에 대한 개별 및 총체적 요구 사항, 하드웨어/소프트웨어/정책적 제약, 어피니티(affinity) 및 안티-어피니티(anti-affinity) 명세, 데이터 지역성, 워크로드-간 간섭, 데드라인을 포함한다.

  • kube-controller-manager

    • 컨트롤러를 구동하는 마스터 상의 컴포넌트.

    • 여러 Workload 모델들을 구동시키는 역할을 담당한다.

    • 논리적으로, 각 컨트롤러는 개별 프로세스이지만, 복잡성을 낮추기 위해 모두 단일 바이너리로 컴파일되고 단일 프로세스 내에서 실행된다.

    • 이들 컨트롤러는 다음을 포함한다.

      • 노드 컨트롤러(NC): 노드가 다운되었을 때 통지와 대응에 관한 책임을 가진다.

      • 리플리케이션 컨트롤러(RC): 시스템의 모든 레플리케이션 컨트롤러 object에 대해 알맞은 수의 파드들을 유지시켜 주는 책임을 가진다.

      • 엔드포인트 컨트롤러(EC): 엔드포인트 object를 채운다(즉, 서비스와 파드를 연결시킨다.)

      • 서비스 어카운트(SAC) & 토큰 컨트롤러(TC): 새로운 Namespace에 대한 기본 계정과 API 접근 토큰을 생성한다.

  • cloud-controller-manager

    • 클라우드별 컨트롤 로직을 포함하는 K8s 컨트롤 플레인 컴포넌트

    • cloud-controller-manager는 클라우드 제공자 전용 컨트롤러만 실행한다. 자신의 사내 또는 PC 내부의 학습 환경에서 K8s를 실행 중인 경우 cluster에는 클라우드 컨트롤러 매니저가 없다

    • kube-controller-manager와 마찬가지로 cloud-controller-manager는 논리적으로 독립적인 여러 컨트롤 루프를 단일 프로세스로 실행하는 단일 바이너리로 결합한다. 수평으로 확장(두 개 이상의 복제 실행)해서 성능을 향상시키거나 장애를 견딜 수 있다.

    • 다음 컨트롤러들은 클라우드 제공 사업자의 의존성을 가질 수 있다.

      • 노드 컨트롤러: 노드가 응답을 멈춘 후 클라우드 상에서 삭제되었는지 판별하기 위해 클라우드 제공 사업자에게 확인하는 것

      • 라우트 컨트롤러: 기본 클라우드 인프라에 경로를 구성하는 것

      • 서비스 컨트롤러: 클라우드 제공 사업자 로드밸런서를 생성, 업데이트 그리고 삭제하는 것

Worker Node 컴포넌트

  • kubelet

    • cluster의 각 노드에서 실행되는 에이전트.
      Kubelet은 파드에서 컨테이너가 확실하게 동작하도록 관리.

    • Kubelet은 k8s를 통해 생성되지 않는 컨테이너는 관리하지 않는다.

  • kube-proxy

    • kube-proxy는 cluster의 각 노드에서 실행되는 네트워크 프록시로, K8s의 서비스 개념의 구현부

    • kube-proxy는 노드의 네트워크 규칙을 유지 관리한다. 이 네트워크 규칙이 내부 네트워크 세션이나 cluster 바깥에서 파드로 네트워크 통신을 할 수 있도록 해준다.

    • kube-proxy는 운영 체제에 가용한 패킷 필터링 계층이 있는 경우, 이를 사용한다. 그렇지 않으면, kube-proxy는 트래픽 자체를 포워드(forward)한다.

  • Container runtime

Addon

애드온은 K8s resource(데몬셋디플로이먼트 등)를 이용하여 cluster 기능을 구현한다.
이들은 cluster 단위의 기능을 제공하기 때문에 애드온에 대한 Namespace resource는 kube-system Namespace에 속한다.(Not all).

선택된 일부 애드온은 아래에 설명하였고, 사용 가능한 전체 확장 애드온 리스트는 애드온을 참조한다

  • DNS

    • K8s에 의해 구동되는 컨테이너는 DNS 검색에서 이 DNS 서버를 자동으로 포함한다

  • 웹 UI (대시보드)

  • 컨테이너 resource 모니터링

    • 컨테이너 resource 모니터링은 중앙 데이터베이스 내의 컨테이너들에 대한 포괄적인 시계열 매트릭스를 기록하고 그 데이터를 열람하기 위한 UI를 제공해 준다.

  • cluster-레벨 로깅

    • cluster-레벨 로깅 메커니즘은 검색/열람 인터페이스와 함께 중앙 로그 저장소에 컨테이너 로그를 저장하는 책임을 진다.

Understanding Terms(중요)

여기서는 아래 블로그를 포함하여 각종 관련 사이트 및 K8s 공식 사이트 문서를 참고하여, 보다 간략히 k8s에 대한 이해에 반드시 필요한 개념 위주로 추려서 기술한다.
즉, 각각의 정책 object(pod, service, deployment, replica 등)마다 많은 기능과 옵션을 포함하기 때문에, deep dive한 설명은 생략될 수 있다.

내용 中 일부 출처: https://bcho.tistory.com/1256?category=731548 [조대협의 블로그]

Namespace

Namespace 는 한 K8s cluster내의 논리적인 분리 단위라고 보면 된다.

Pod,Service 등은 Namespace 별로 생성이나 관리가 될 수 있고, 사용자의 권한 역시 이 Namespace 별로 나눠서 부여할 수 있다.

즉 하나의 cluster 내에, 개발/운영/테스트 환경이 있을 때, cluster를 개발/운영/테스트 3개의 Namespace 로 나눠서 운영할 수 있다. Namespace로 할 수 있는 것은,

  • 사용자 별로 Namespace 별 접근 권한을 다르게 운영할 수 있다.

  • Namespace 별로 resource quota(할당량)을 지정할 수 있다. 개발계에는 CPU 100, 운영계에는 CPU 400과 GPU 100개 식으로, 사용 가능한 resource량을 지정할 수 있다.

  • Namespace 별로 resource를 나눠서 관리할 수 있다. (Pod, Service 등)

<Billing 부서와 Commerce 부서의 resource 사용을 서로 논리적으로 분리한 예>

K8s는 처음에 네 개의 초기 namespace를 갖는다.

  • default 다른 namespace가 없는 object를 위한 default namespace.

  • kube-system K8s 시스템에서 생성한 object를 위한 namespace.

  • kube-public 이 namespace는 자동으로 생성되며 모든 사용자(인증되지 않은 사용자 포함)가 읽기 권한으로 접근할 수 있다. 이 namespace는 주로 전체 cluster 중에 공개적으로 드러나서 읽을 수 있는 resource를 위해 예약되어 있다. 이 namespace의 공개적인 성격은 단지 관례이지 요구 사항은 아니다.

  • kube-node-lease cluster가 스케일링될 때 노드 하트비트의 성능을 향상시키는 각 노드와 관련된 리스(lease) object에 대한 namespace.

모든 object가 특정 namespace에 속하지는 않으며, namespace에 속할 수 없는 속성의 것들도 있다.
예를 들어,  Node나 Persistent Volume Claims과 같은 저수준 resource는 어느 namespace에도 속하지 않는다.

다음은 namespace에 속하거나 속하지 않는 K8s resource에 대한 전체 목록을 조회하는 방법이다.

# 네임스페이스에 속하지 않는 리소스 $ kubectl api-resources --namespaced=false NAME SHORTNAMES APIGROUP NAMESPACED KIND componentstatuses cs false ComponentStatus namespaces ns false Namespace nodes no false Node persistentvolumes pv false PersistentVolume mutatingwebhookconfigurations admissionregistration.k8s.io false MutatingWebhookConfiguration validatingwebhookconfigurations admissionregistration.k8s.io false ValidatingWebhookConfiguration customresourcedefinitions crd,crds apiextensions.k8s.io false CustomResourceDefinition apiservices apiregistration.k8s.io false APIService tokenreviews authentication.k8s.io false TokenReview selfsubjectaccessreviews authorization.k8s.io false SelfSubjectAccessReview selfsubjectrulesreviews authorization.k8s.io false SelfSubjectRulesReview subjectaccessreviews authorization.k8s.io false SubjectAccessReview certificatesigningrequests csr certificates.k8s.io false CertificateSigningRequest ingressclasses networking.k8s.io false IngressClass runtimeclasses node.k8s.io false RuntimeClass podsecuritypolicies psp policy false PodSecurityPolicy clusterrolebindings rbac.authorization.k8s.io false ClusterRoleBinding clusterroles rbac.authorization.k8s.io false ClusterRole priorityclasses pc scheduling.k8s.io false PriorityClass csidrivers storage.k8s.io false CSIDriver csinodes storage.k8s.io false CSINode storageclasses sc storage.k8s.io false StorageClass volumeattachments storage.k8s.io false VolumeAttachment # 네임스페이스에 속하는 리소스 $ kubectl api-resources --namespaced=true NAME SHORTNAMES APIGROUP NAMESPACED KIND bindings true Binding configmaps cm true ConfigMap endpoints ep true Endpoints events ev true Event limitranges limits true LimitRange persistentvolumeclaims pvc true PersistentVolumeClaim pods po true Pod podtemplates true PodTemplate replicationcontrollers rc true ReplicationController resourcequotas quota true ResourceQuota secrets true Secret serviceaccounts sa true ServiceAccount services svc true Service controllerrevisions apps true ControllerRevision daemonsets ds apps true DaemonSet deployments deploy apps true Deployment replicasets rs apps true ReplicaSet statefulsets sts apps true StatefulSet localsubjectaccessreviews authorization.k8s.io true LocalSubjectAccessReview horizontalpodautoscalers hpa autoscaling true HorizontalPodAutoscaler cronjobs cj batch true CronJob jobs batch true Job leases coordination.k8s.io true Lease endpointslices discovery.k8s.io true EndpointSlice events ev events.k8s.io true Event ingresses ing extensions true Ingress ingresses ing networking.k8s.io true Ingress networkpolicies netpol networking.k8s.io true NetworkPolicy poddisruptionbudgets pdb policy true PodDisruptionBudget rolebindings rbac.authorization.k8s.io true RoleBinding roles rbac.authorization.k8s.io true Role

Pod

Pod 는 K8s에서 가장 기본적인 배포 단위로, 적어도 하나 이상의 컨테이너를 포함하는 단위이다.
다시 말해서, K8s는 컨테이너 하나하나의 단위로 관리하지 않고, 하나 이상의 컨테이너로 묶인 pod 단위로 관리한다.

여기서, Pod의 중요한 특징이 있는데,

  • Pod 단위로 internal IP가 할당된다.

  • Pod 안의 컨테이너들은 IP주소와 포트 공간을 공유한다. 즉, port만 다르다면 서로를 localhost 를 통해 통신할 수 있다.

  • 이들은 또한 SystemV 세마포어나, POSIX 공유 메모리와 같은 표준 프로세스 간 통신 방식으로 서로 통신할 수 있다.

  • Pod 내에 배포된 컨테이너 간에는 디스크 볼륨을 공유할 수 있다.
    따라서 아래 그림과 같이, Web server에 항상 부착되어야 할 프로그램을 묶어서 배포할 수 있으며,

  • 가장 일반적 시나리오는 웹(Nginx) 서비스하면서 access 로그를 pod volume에 남기고, 그 로그는 fluentd로 다시 수집하여 로그 분석 서버(ELK Stack 등)에 전송하는 경우가 있을 수 있다.

K8s는 위 특징을 활용하여, 반드시 같이 묶여서 배포되는 application을 하나의 단위로 관리할 수 있게 해준다.

참고로 Pod에 cpu, memory 사용에 대한 limit을 지정할 수 있으며, Linux CGroup(Control Group)을 사용하여 관리된다.

[Workload] Controller

사용자 application workload model에 따라 - 예를 들어, 대게 Webserver와 DB는 배포 방식을 달리한다. - 아래와 같은 대표적인 배포 방식들이 있으며, 이를 보다 편리하게 관리하기 위해 Controller라는 개념을 사용한다.

Job

Workload model에서 batch나 한번 실행되고 끝나는 형태의 작업이 있을 수 있다.

예를 들어 원타임으로 파일 변환 작업을 하거나, 또는 주기적으로 ETL 배치 작업을 하는 경우에는 웹서버 처럼 계속 Pod가 떠 있을 필요 없이 작업을 할 때만 Pod를 띄우면 된다.

이러한 형태의 workload model을 지원하는 Controller를 Job이라고 한다.

Restart policy 지정

만일, Job이 끝나기 전에 만약에 비정상적으로 종료된다면?
아래 그림과 같이 job에 적절한 restart policy를 부여할 수 있다.

Completions 횟수 및 parallelism 개수 지정

또한, spec에 completions 횟수를 지정하여, 하나의 작업을 여러 차례 수행하도록 할 수도 있다.(데이타가 클 경우 범위를 나눠서 작업하는 경우 등)

만일 parallelism 개수와 함께 적용한다면, parallelism 개수만큼 동시에 실행되며, completions 횟수만큼 모두 수행되면 종료될 것이다.

Cron Job

특정 날짜/시간마다 주기적으로 실행되어야 하는 작업일 경우, unix cron에 설정하듯이 설정하면 된다.
개념 및 방식 또한 unix cron과 같다.

Daemon Set

DaemonSet (이하 DS) 은 Pod가 각각의 노드에서 하나씩만 돌게 하는 형태로 Pod를 관리하는 컨트롤러이다.

아래 그림과 같이, RC나 RS에 의해서 관리되는 Pod 는 여러 노드의 상황에 따라서 일반적으로 비균등적으로 배포가 되지만,  DS에 의해 관리되는 Pod는 모든 노드에 균등하게 하나씩만 배포 된다.
이런 형태의 워크로드는 서버의 모니터링이나 로그 수집 용도로 많이 사용된다.

또한, node selector(label)를 지정하여, 특정 노드에만 하나씩 배포할 수도 있다.
예를 들어 아래 그림과 같이 GPU를 포함하고 있는 서버에만 배포할 수 있다.

Stateful Set

RS/RC나 다른 controller로는 DB와 같이 상태를 가지는 애플리케이션을 관리하기가 어렵다.

그래서 이렇게 DB등과 같이 상태를 가지고 있는 Pod를 지원하기 위해서 Stateful Set 이라는 것이 새로 소개되었는데, 이를 이해하기 위해서는 K8s의 디스크 볼륨에 대한 이해가 필요하다.

Stateful Set은 다음 중 하나 또는 이상이 필요한 애플리케이션에 유용하다.

  • 안정된, 고유한 네트워크 식별자.

  • 안정된, 지속성을 갖는 스토리지.

  • 순차적인, 정상 배포(graceful deployment)와 스케일링.

  • 순차적인, 자동 롤링 업데이트.

Replication controller or Replica Set

Replica Set의 목적은 Replica Pod집합의 실행을 항상 안정적으로 유지하는 것이다. 이처럼 Replica Set은 보통 명시된 동일 Pod 개수에 대한 가용성을 보증하는데 사용한다.

Replica Set은 Replication Controller 의 새 버전으로 생각하면 된다.

큰 차이는 없고 Replication Controller 는 Equality 기반 Selector를 이용하는데 반해, Replica Set은 Set 기반의 Selector를 이용한다.

크게 3가지 파트로 구성되는데, Replica의 수, Pod Selector, Pod Template 3가지로 구성된다.

  • Selector : 먼저 Pod selector는 라벨을 기반으로 하여,  RC가 관리한 Pod를 가지고 오는데 사용한다.

  • Replica 수 :  RC에 의해서 관리되는 Pod의 수인데, 그 숫자만큼 Pod 의 수를 유지하도록 한다.예를 들어 replica 수가 3이면, 3개의 Pod만 띄우도록 하고, 이보다 Pod가 모자르면 새로운 Pod를 띄우고, 이보다 숫자가 많으면 남는 Pod를 삭제한다.

  • Pod를 추가로 기동할 때 그러면 어떻게 Pod를 만들지 Pod에 대한 정보 (도커 이미지, 포트,라벨등)에 대한 정보가 필요한데, 이는 Pod template이라는 부분에 정의 한다.

Deployment

Blue/Green 배포 방식

가장 단순한 업데이트 배포 방식으로, 블루(예전)버전으로 서비스 하고 있던 시스템을 그린(새로운)버전을 배포한 후, 트래픽을 블루에서 그린으로 한번에 돌리는 방식이다.

위 그림에서 보는 바와 같이 이 방식을 사용하기 위해서는 일시적으로 나마 시스템 리소스가 (대략) 2배가 필요하다.
즉, 항상 업데이트 할 수 있는 정도의 여유 Stage가 필요하다.

Rolling update

가장 많이 사용되는 배포 방식 중의 하나 이며, 단순화된 과정은 다음 그림과 같다.
(아래 예에서는 RC를 사용하였으나, RS(Replica Set)으로 대체해도 무관하다, 최근에는 RS가 대세로 사용된다.)
먼저 RC를 하나 만들어서 기존 RC에서 replica 수를 하나 줄이고, 새로운 RC에는 replica 수를 하나만 준다.

기존 RC에서 replica 수를 또 하나 줄이고, 새로운 RC에는 replica 수를 하나 더 추가해준다.

마찬가지 작업을 반복하게 되면, 아래 그림과 같이 예전 버전의 Pod가 모두 빠지고 새 버전의 Pod만 서비스 되게 된다.

Deployment controller

여러가지 배포 방식을 RC나 RS를 이용해서 구현할 수 있지만, 운영이 복잡해지는 단점이 있다. 그래서 k8s에서는 일반적으로 RC나 RS를 이용해서 배포하지 않고 Deployment라는 개념을 사용한다.

앞에서 보았듯이 rolling update 등을 할 때, RC를 두 개를 만들어야 하고, 하나씩 Pod의 수를 수동으로 조정해야 하기 때문에, 이를 자동화해서 추상화한 개념이 Deployment 이다.

위 rolling update에서의 과정을 아래 그림과 같이 Deployment controller가 대신하여 자동으로 관리해 준다.

Deployment에서의 Rollback 또한 매우 간단히, API 혹은 kubectl 명령으로 Rollout history를 조회할 수 있고, 또 원하는 버전으로 Rollback API 혹은 명령 또한 지원한다.

Volume

K8s의 볼륨은 여러가지 종류가 있는데,  로컬 디스크 뿐 아니라, NFS, iSCSI, Fiber Channel과 같은 일반적인 외장 디스크 인터페이스는 물론, GlusterFS나, Ceph와 같은 오픈 소스 분산 파일 시스템, AWS EBS, GCP Persistent 디스크와 같은 퍼블릭 클라우드에서 제공되는 디스크, VsphereVolume과 같이 프라이비트 클라우드 솔루션에서 제공하는 디스크 볼륨까지 다양한 볼륨을 지원한다.

이 볼륨 타입을 구별해보면 크게 임시 디스크, 로컬 디스크 그리고 네트워크 디스크 등으로 분류할 수 있다.
지원되는 모든 Storage type은 여기를 참고한다.

Temp

Local

Network

Temp

Local

Network

emptyDir

hostPath

GlusterFS

gitRepo

NFS

iSCSI

gcePersistentDisk

AWS EBS

azureDisk

Fiber Channel

Secret

VSphereVolume

emptyDir

emptyDir은 Pod가 생성될 때 생성되고, Pod가 삭제 될 때 같이 삭제되는 임시 볼륨이다.

단, Pod 내의 컨테이너 크래쉬되어 삭제되거나 재시작 되더라도 emptyDir의 생명주기는 컨테이너 단위가 아니라, Pod 단위이기 때문에, emptyDir은 삭제 되지 않고 계속해서 사용이 가능하다.

생성 당시에는 디스크에 아무 내용이 없기 때문에, emptyDir  이라고 한다.

emptyDir의 물리적으로 노드에서 할당해주는 디스크에 저장이 되는데, (각 환경에 따라 다르다. 노드의 로컬 디스크가 될 수 도 있고, 네트워크 디스크 등이 될 수 도 있다.) emptyDir.medium 필드에 “Memory”라고 지정해주면, emptyDir의 내용은 물리 디스크 대신 메모리에 저장이 된다.

만일 한 Pod 내에 Nginx 컨테이너 + fluentd(로그 수집 에이전트) 컨테이너 + emptyDir 볼륨이 있다면, 이 두 컨테이너는 할당된 emptyDir 볼륨을 공유하여 사용하게 된다.(메모리라 하더라도…)
또한 앞서 언급한 바와 같이, 생명 주기도 Pod에 따른다.

hostPath

hostPath는 노드의 로컬 디스크의 directory path를 Pod에서 마운트해서 사용한다. emptyDir과 달리, 같은 hostPath에 있는 볼륨은 여러 Pod 사이에서 공유되어 사용된다.

또한  Pod가 삭제 되더라도 hostPath에 있는 파일들은 삭제되지 않고 다른 Pod가 같은 hostPath를 마운트하게 되면, 남아 있는 파일을 액세스할 수 있다.

gitRepo

이 볼륨은 생성 시에 지정된 git repository의 특정 revision의 내용을 clone을 이용해서 내려 받은 후에 디스크 볼륨을 생성하는 방식이다. 물리적으로는 emptyDir이 생성되고, git repository 내용을 clone으로 다운 받는다.

HTML, 각종 설정 파일과 같은 정적 파일이나 Ruby on rails, PHP, node.js, python과 같은 스크립트 언어 기반의 코드들은 gitRepo 볼륨을 이용하여 손쉽게 배포할 수 있다.

PV(PersistentVolume) and PVC(PersistentVolumeClaim)

위에 언급한 세 개의 볼륨 타입을 제외하고는 대부분 디스크 볼륨을 설정하려면 물리적 디스크를 생성해야 하고, 이러한 물리적 디스크에 대한 설정을 자세하게 이해할 필요가 있다.(Cloud 제공 업체의 스토리지 서비스도 마찬가지다)

K8s는 인프라에 대한 복잡성을 추상화를 통해서 간단하게 하고, 개발자들이 손쉽게 필요한 인프라 (컨테이너,디스크, 네트워크)를 설정할 수 있도록 하는 개념을 가지고 있다.
그래서 인프라에 종속적인 부분은 시스템 관리자가 설정하도록 하고, 개발자는 이에 대한 이해 없이 간단하게 사용할 수 있도록 디스크 볼륨 부분에 PersistentVolumeClaim(이하 PVC라고 명명)와 PersistentVolume(이하 PV라고 명명)라는 개념을 도입하였다.

다시 말해서, 시스템 관리자가 실제 물리 디스크를 생성한 후에, 이 디스크를 PersistentVolume이라는 이름으로 k8s에 등록한다.

개발자Pod 생성할 때, 볼륨을 정의하고, 이 볼륨 정의 부분에 물리적 디스크에 대한 특성을 정의하는 것이 아니라, PVC를 지정하여, 관리자가 생성한 PV와 연결한다.

Dynamic Provisioning

앞에서 본 것과 같이 PV를 수동으로 생성한 후 PVC에 바인딩 한 후에 Pod에서 사용할 수 있지만, k8s 1.6 이상에서 Dynamic Provisioning 기능을 지원한다. 이 Dynamic Provisioning 기능은 시스템 관리자가 별도로 디스크를 생성하고 PV를 생성할 필요 없이, PVC만 정의하면 이에 맞는 물리 디스크 생성 및 PV 생성을 자동화해주는 기능이다.

위에서 물리적인 스토리지를 생성하기 위해서는, 어떤 종류의 스토리지 스펙으로 생성해야 할 지를 k8s controller가 미리 알고 있어야 한다.
이를 위해, StorageClass를 정의할 수 있다.

예를 들면, 아래는  AWS EBS 디스크에 대한 스토리지 클래스를 지정한 예로, slow 라는 이름으로 스토리지 클래스를 지정하고. EBS 타입은 io1을 사용하고, GB당 IOPS는 10을 할당하도록 하고, 존은 us-east-1d와 us-east-1c에 디스크를 생성하도록 한다.

또, 아래는 구글 클라우드의 Persistent Disk (pd)의 예로, slow라는 이름으로 스토리지 클래스를 지정하고, pd-standard (HDD)타입으로 디스크를 생성하되 us-central1-a와 us-central1-b 존에 디스크를 생성하도록 한다.

이와 같이, 만일 NFS, FC, iSCSI와 같은 표준 스토리지 프로토콜이 아닌 경우에도, 현재 대부분의 클라우드 제공자, 가상화 제품 벤더사 및 스토리지 벤더사가 k8s addon을 이미 제공하고 있으며, 각각 제공하는 규격에 맞게 정의하면 된다.

Service

Service는 주로 네트워킹에 관련된 오브젝트이다.
물리적인 L2, L3 layer 네트워킹 뿐 아니라, DNS lookup, Service discovery, L4 layer load balancing 등 pod들 간 혹은 내/외부 간 다양한 네트워킹 서비스들을 정의/관장한다.
(해서, 가장 종류도 많고 어렵다)

예를 들어, Pod의 경우에 지정되는 IP가 동적으로 랜덤하게 지정이 되고 리스타트 때마다 변하기 때문에 고정된 end point로 호출이 어렵다, 또한 여러 Pod에 같은 애플리케이션을 운용할 경우 이 Pod 간의 로드밸런싱을 지원해줘야 하는데, Service가 이러한 역할을 한다.

Service는 지정된 IP로 생성이 가능하고, 여러 Pod를 묶어서 로드밸런싱이 가능하며, 고유한 DNS 이름을 가질 수 있다.

Service는 다음과 같이 구성이 가능하며, label selector를 이용하여, 관리하고자 하는 Pod 들을 정의할 수 있다. (또한 아래 예와 같이 동시에 여러 개의 포트 지정을 지원한다.)
즉, pod든 replica set이든, deployment든 간에 같은 label라면 해당(label selector에 지정된) Service에 묶는다.(아래의 경우는 “hello-node” label을 가진 pod들을 묶어서 80과 443 포트로 load balancing한다.)

apiVersion: v1 kind: Service metadata: name: hello-node-svc spec: selector: app: hello-node ports: - name: http port: 80 protocol: TCP targetPort: 8080 - name: https port: 443 protocol: TCP targetPort: 8082 type: LoadBalancer

Service가 Pod들에 부하를 분산할 때, Default 알고리즘은 Pod 간에 랜덤으로 부하를 분산하도록 한다.
만약에 특정 클라이언트가 특정 Pod로 지속적으로 연결이 되게 하려면  Session Affinity를 사용하면 되는데, 서비스의 spec 부분에 sessionAffinity: ClientIP로 지정하면 된다.

Service는 IP 주소 할당 방식과 연동 서비스 등에 따라 크게 4가지로 구별할 수 있다.

  • ClusterIP: Default 설정으로, Service에 클러스터 IP (내부 IP )를 할당한다. K8s 클러스터 내에서는 이 Service에 접근이 가능하지만, 클러스터 외부에서는 외부 IP를 할당 받지 못했기 때문에, 접근이 불가능하다.
    ClusterIP의 IP range는 kube-controller-manager pod의 실행 argument 중 --service-cluster-ip-range에 지정된다.(default: --service-cluster-ip-range=10.96.0.0/12)

  • LoadBalancer: 보통의 클라우드 벤더에서 제공하는 설정 방식으로, 외부 IP를 가지고 있는 LoadBalancer를 할당한다. 외부 IP를 가지고 있기  때문에, 클러스터 외부에서 접근이 가능하다.
    단, 외부 서비스를 위해 externalIPs를 지정해주어야 하는데, 이는 k8s에서 관리하지 않는다. 즉, 외부 loadbalancer(HAProxy 등)나 Cloud 환경이라면 Cloud 벤더에서 제공하는 loadbalancer의 IP를 별도 지정해주어야 한다.
    아래의 NodePort를 지정하지 않을 경우 랜덤 할당되며, 기능은 아래 NodePort와 같다.
    즉, LoadBalancer+externalIPs+NodePort로 사용 가능 하다.
    또한, 일부 클라우드 공급자는 loadBalancerIP를 지정할 수 있도록 허용하며, 클라우드 공급자가 이 기능을 지원하지 않는 경우, 설정한 loadbalancerIP 필드는 무시된다.

  • NodePort: 클러스터 IP로만 접근이 가능한 것이 아니라, 모든 노드의 IP와 포트를 통해서도 접근이 가능하게 된다.
    예를 들어 아래와 같이 hello-node-svc 라는 ServiceNodePort 타입으로 선언을 하고, nodePort를 30036으로 설정하면, 아래 설정에 따라 클러스터 IP의  80포트로도 접근이 가능하지만, 모든 노드의 30036 포트로도 서비스를 접근할 수 있다.
    80포트로 전달된 패킷은 연결된 pod들에 loadbalancing되어 전달된다.

    주의할 점은 위의 80 포트도 cluster IP이기 때문에 Cluster 내에서만 접속이 가능하다.
    즉, 위의 ClusterIP + “각 노드에 port forwarding” 기능이라 생각하면 무리가 없을 것이다.

  • ExternalName: ExternalName은 외부 서비스를 k8s 내부에서 호출하고자 할 때 사용할 수 있다.

    K8s cluster 내의 Pod들은 클러스터 IP를 가지고 있기 때문에 - 즉, 외부로 통하는 gateway를 가지고 있지 않다 - 클러스터 IP 대역 밖의 서비스를 호출하고자 하면, NAT 설정 등의 복잡한 설정이 필요하다.

    특히, AWS나 GCP와 같은 클라우드 환경을 사용할 경우 DB나, 또는 클라우드에서 제공되는 매지니드 서비스(RDS, CloudSQL)등을 사용하고자 할 경우에는 k8s 클러스터 밖이기 때문에, 호출이 어려운 경우가 있는데, 이를 쉽게 해결할 수 있는 방법이 ExternalName 타입이다.

    이 방식도 다시 두 가지 방식으로 설정할 수 있는데,
    첫 번째는 DNS 방식이고, 두 번째는 IP를 지정하는 방식이다.

    DNS 방식은 Service object의 spec에 아래와 같이
     type: ExternalName
     externalName: my.database.example.com

    ExternalName과 함께 직접 지정하면 되고, 이 Service는 들어오는 모든 요청을 “my.database.example.com”으로 forwarding 해준다.

     

    IP를 지정하는 방식은 ClusterIPService를 하나 생성하고, Endpoints라고 하는 object를 하나 더 생성하여, forwarding될 IP와 port를 지정하고 이를 연결하여 사용한다.
    그림으로 나타내면 다음과 같다.

Ingress

K8s의 Service는, L4 레이어로 TCP 단에서 Pod들을 balancing한다.

Service의 경우에는 TLS (SSL)이나, VirtualHost와 같이 여러 호스트명을 사용하거나 호스트명에 대한 라우팅이 불가능하고, URL Path에 따른 서비스 간 라우팅이 불가능하다.
또한 마이크로 서비스 아키텍쳐 (MSA)의 경우에는 k8s의 Service 하나가 MSA의 하위 서비스 하나로 표현되는 경우가 많고 전체 서비스는 하나의 URL로 대표 되는 경우가 많다. (/users, /products, …)

그래서 MSA 서비스간의 라우팅을 하기 위해서는 API Gateway를 넣는 경우가 많은데, 이 경우에는 API Gateway에 대한 관리 포인트가 생기기 때문에, URL 기반의 라우팅 정도라면, API 게이트웨이처럼 무거운 아키텍쳐 컴포넌트가 아니라, L7 로드밸런서 정도로 위의 기능을 모두 제공이 가능하다.
(사실, MSA에서의 API Gateway는 L7 라우팅 역할 외에도 사용자 보안 인증 및 권한에 따른 토큰 관리 등 Kong과 같은 상업적 제품이 따로 존재할 정도로 전체 서비스를 위한 대문(Gateway)으로서 하는 일이 아주 많다.)

K8s에서 HTTP(S)기반의 L7 로드밸런싱 기능을 제공하는 컴포넌트를 Ingress라고 한다.

개념을 도식화 해보면 아래와 같은데, Ingress 가 서비스 앞에서 L7 로드밸런서 역할을 하고, URL에 따라서 라우팅을 하게 되며, Service는 L4 로드밸런서 역할을 하게 된다.

여러 네트워크 서비스와 같이, Ingress도 여러가지 3rd party addon 구현체가 존재하며,
Ingress 리소스가 작동하려면, 클러스터는 실행 중인 Ingress 컨트롤러가 반드시 필요하다.

kube-controller-manager 바이너리의 일부로 실행되는 컨트롤러의 다른 타입과 달리 Ingress 컨트롤러는 클러스터와 함께 자동으로 실행되지 않는다. 클러스터에 가장 적합한 Ingress 컨트롤러 구현을 선택한다.

k8s는 자체 서브 프로젝트로써 현재 GCE 와 nginx 컨트롤러를 지원하고 유지한다.

이외에도 다음과 같은 추가 컨트롤러를 사용할 수 있다.

Config Map

애플리케이션을 배포하다 보면, 환경에 따라서 다른 설정 값을 사용하는 경우가 있다. 예를 들어, DB의 IP, API를 호출하기 위한 API KEY/TOKEN, 개발/운영에 따른 debug mode, configuration 파일들이 있는데, 애플리케이션 이미지는 같지만, 이런 환경 변수가 차이가 나는 경우 매번 다른 컨테이너 이미지를 만드는 것은 관리상 불편할 수 밖에 없다.

이러한 환경 변수나 설정 값들을 변수로 관리해서 Pod가 생성될 때 이 값을 넣어주거나 또는 디스크 볼륨으로 마운트가 가능한데, 이러한 기능을 제공하는 것이 바로 ConfigMapSecret이다.

Secret

보안이 중요한 패스워드나, API 키, 인증서 파일들은 Secret에 저장할 수 있다.
Secret은 안에 저장된 내용을 지키기 위해서 추가적인 보안 기능을 제공한다.
예를 들어, API server나 node의 파일에는 저장되지 않고, 항상 메모리에 저장되어 있기 때문에 상대적으로 접근이 어렵다.

하나의 Secret의 사이즈는 최대 1M까지 지원되는데, 메모리에 지원되는 특성 때문에, Secret을 여러 개 저장하게 되면 API Server나 노드에서 이를 저장하는 kubelet의 메모리 사용량이 늘어나서 Out Of Memory와 같은 이슈를 유발할 수 있기 때문에, 보안적으로 꼭 필요한 정보만 secret에 저장하도록 하는 게 좋다.

대부분 ca.crt 같은 인증서나 token key등을 주로 저장한다.

사용 방법에 있어서는 SecretConfigMap은 기본적으로 거의 유사하다. 기본적으로 Key/Value 형태의 저장 구조를 가지고 있으며, 사용 시 환경 변수를 통해서 Pod에 그 값을 전달하거나, 또는 디스크 볼륨으로 마운트가 가능한데, Secret은 정의하는 방법이 다소 차이가 있다.

예를 들어 “language”라는 키로 “java”라는 값을 저장하고자 할 때, ConfigMap의 경우에는 이를 language:java 식으로 일반 문자열로 저장했지만 Secret의 경우에는 값에 해당하는 부분을 base64 포맷으로 인코딩해야 한다.

즉 java라는 문자열을 base64로 인코딩을 하면, amF2YQo= 가 된다.

문자열(“java”)을 base64포맷으로 인코딩 하려면 맥이나 리눅스에서 다음과 같은 명령을 이용하면 된다.

$ echo java | base64

Authentication(사용자 인증)

흔히 말하는 로그인을 말하며, 로그인한 사용자가 본인이 맞는가?에 방점을 두고 있다.
즉, 사용자가 누구인지 식별하는 것을 의미한다.

User Account

K8s는 자체적으로 사용자 계정을 관리하고 이를 인증(Authenticate)하는 시스템을 가지고 있지 않다. 반드시 별도의 외부 계정 시스템을 사용해야 하며, 계정 시스템 연동을 위해서 OAuth나 Webhook가 같은 계정 연동 방식을 지원한다.

Service Account

클라이언트가 k8s API를 호출하거나, 콘솔이나 기타 클라이언트가 k8s API를 접근하고자 할 때, 이는 실제 사람인 사용자가 아니라 시스템이 된다. 그래서, k8s에서는 이를 일반 사용자와 분리해서 관리하는데, 이를 ServiceAccount라고 한다.

ServiceAccount 생성은 다른 object들 보다 간단하게, name과 그 name이 속할 namespace를 명시하고 적용하면 된다. namespace를 지정하지 않으면, “default” namespace로 할당된다.
또한, ServiceAccount를 생성하면 위에 언급한 Secret도 함께(물론 보안 서명 및 token secret 등과 같이…) 자동 생성된다.

$ vim test_serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: sa-test01 namespace: default # SA 생성 $ kubectl apply -f test_serviceaccount.yaml # SA 확인 $ kubectl get sa NAME SECRETS AGE default 1 9d sa-test01 1 4s # 생성된 secrets 확인 $ kubectl get secrets NAME TYPE DATA AGE default-token-xh6jf kubernetes.io/service-account-token 3 9d sa-test01-token-qthsp kubernetes.io/service-account-token 3 11s # 생성된 secrets내 토큰 확인 $ kubectl describe secret sa-test01-token-qthsp Name: sa-test01-token-qthsp Namespace: default Labels: <none> Annotations: kubernetes.io/service-account.name: sa-test01 kubernetes.io/service-account.uid: f7bf0e99-9bd1-4d8c-96f6-a23c5b11d19f Type: kubernetes.io/service-account-token Data ==== ca.crt: 1025 bytes namespace: 7 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IjNqSm8xaXJ0MDZsaGxjdzVndWozY1A5VXBGbTdwX3VDUzBpd0J2a3ItR0EifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLXRlc3QwMS10b2tlbi1xdGhzcCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJzYS10ZXN0MDEiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJmN2JmMGU5OS05YmQxLTRkOGMtOTZmNi1hMjNjNWIxMWQxOWYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpzYS10ZXN0MDEifQ.k-ZdSzlMZc4W0IsQi_n37OaSfSto1QuI12Nyv2Nh8Il9hwtctjl-9JBx9bdax13Fq0KRplh_VVh8oAmG9gaGjgyjnb0mq2EUD1ca-vMP1x2otnJScyx75VcYOIP2TXQJ8zObUgSX7odo_otco2ccrK4YbHD3e_T0uDnCuej4vh0KIBUXeG7uMSpiL9LRo4rBKPHo1VAY9Yg5htRvxhKX2OlVZNfvB37rGhAzu67jfVdW9ubg9e724G1jVPipLsiSJ-oyPtx9zXga0gSz_0VJ3KO13hKXmnVi0GD_5sGychwfM2W2T66-cxDTxGZU07LTIpqtHgUA5AZkuwaGPYcYfg

인증 방식에는 인증서 인증 방식 및 토큰 인증 방식 등을 모두 지원하는데,
편의상 보통은 token 방식으로 k8s API를 호출하여 사용한다.

단, k8s API사용을 위한 사용자 인증을 했다 하더라도, 접근하고자 하는 k8s 리소스에 대해 적절한 권한(get, list, watch, create, delete, patch, update, 등)이 부여되어 있어야 한다.
이에 대해서는 다음 섹션에서 다룬다.

Authorization(권한 인증)

Role & Cluster Role

Role은 적용 범위에 따라 ClusterRole과 일반 Role로 분리 된다.

Role의 경우 특정 namespace내의 리소스에 대한 권한을 정의할 수 있다.

반면 ClusterRole의 경우, Cluster 전체에 걸쳐서 권한을 정의할 수 있다는 차이가 있다.
또한 여러 namespace에 걸쳐 있는 nodes 와 같은 리소스에 대해서 권한을 정의할 수 있다.

아래는 default Role 중 User-facing Role에 관한 내용이며, 더 많은 default Role에 대해서는 여기를 참고한다.

Default ClusterRole

Default ClusterRoleBinding

Description

cluster-admin

system:masters group

K8s 클러스터에 대해서 super user 권한을 부여한다.
ClusterRoleBinding을 이용해서 Role을 연결할 경우에는 모든 네임스페이스와 모든 리소스에 대한 권한을 부여한다. RoleBinding을 이용하여 롤을 부여하는 경우에는 해당 namespace에 있는 리소스에 대한 모든 컨트롤 권한을 부여한다.

admin

None

관리자 권한의 access를 제공한다.
RoleBinding을 이용한 경우에는 해당 namespace에 대한 대부분의 리소스에 대한 access를 제공한다.  새로운 Role을 정의하고 RoleBinding을 정의하는 권한을 포함하지만, resource quota에 대한 조정 기능은 가지지 않는다.

edit

None

namespace내의 객체를 읽고 쓰는 기능은 가지지만, Role이나 RoleBinding을 쓰거나 수정하는 역할은 제외된다.

view

None

해당 namespace내의 객체에 대한 읽기 기능을 갖는다. Role이나 RoleBinding을 조회하는 권한은 가지고 있지 않다.

Aggregated ClusterRole

또 하나의 유용한 기능으로 aggregationRule이라는 기능이 있다.

보통 ServiceAccount를 생성한 후 Role을 정의할 때, 각 리소스와 액션(verbs)을 지정해야 하는데, 양이 많을 경우 매우 번거로울 수 있다. 이럴 때는 “이미 정의되어 있는 Role + 직접 정의한 Role”로 합칠 수 있다.
(Telegraf와 같은 3rd party 모니터링 솔루션과의 연동 시 매우 유용하다.)

이를 Aggregated ClusterRole 이라 한다.

Role Binding & Cluster Role Binding

위의 Role이나 ClusterRole이 선언/정의되면 단독으로는 무용하며, 실제 사용자나 ServiceAccountbinding하여 사용된다.

전체적으로 관계를 그림으로 나타내면 다음과 같다.

Child Pages

 

Related content

CloudHub Deploy onto K8s(작성중)
CloudHub Deploy onto K8s(작성중)
Read with this
Kubernetes Monitoring
Kubernetes Monitoring
More like this
Installation for test Env.
Installation for test Env.
Read with this
[Certain Version] Installation for test Env.
[Certain Version] Installation for test Env.
More like this
Service Discovery
Service Discovery
More like this
Setting up K8s Metrics Server Addon
Setting up K8s Metrics Server Addon
More like this