---
share: true
toc: true
categories: [Development, Kubernetes]
path: "_posts/development/kubernetes"
tags: [kubernetes, sre, devops]
title: "18. Extending Kubernetes"
date: "2021-09-04"
github_title: "2021-09-04-18-extending-k8s"
image:
  path: /assets/img/posts/development/kubernetes/k8s-18.jpeg
attachment:
  folder: assets/img/posts/development/kubernetes
---

![k8s-18.jpeg](/assets/img/posts/development/kubernetes/k8s-18.jpeg) _API Server Aggregation (출처: https://livebook.manning.com/book/kubernetes-in-action/chapter-18)_

### 주요 내용

- Kubernetes 에 custom resource/controller/API server 추가하기
- Kubernetes Service Catalog
- OpenShift, Deis Workflow, Helm

## 18.1 Defining custom API objects

---

지금까지 책에서 살펴본 Kubernetes object 들은 비교적으로 low-level 한 object 이고 일반적인 개념을 다룬다.

그런데 Kubernetes ecosystem 이 발전하게 되면서 high-level object 를 만나게 되는데, 이러한 object 들은 일반적인 개념보다는 특정한 개념을 다루고, 애플리케이션 전체나 소프트웨어 서비스를 나타낸다.

Custom controller 를 사용하게 되면 이런 high-level object 도 관리할 수 있게 되며, Kubernetes 에서는 이렇게 custom resource 를 추가할 수 있는 방법을 제공해 준다.

### 18.1.1 Introducing CustomResourceDefinitions

새로운 resource type 을 정의하기 위해서는 **CustomResourceDefinition** (CRD) object 를 생성해서 API server 에 POST 요청을 날려주면 된다. 이 object 는 custom resource type 에 대한 정보를 담고 있으며, CRD 가 POST 되면 사용자가 JSON, YAML manifest 를 활용하여 API server 에 요청을 보내 resource 를 만들 수 있게 된다.

다만 CRD 만들게 되면 이와 연관된 controller 도 함께 만들어줘야 한다. (그래야 resource 관리가 된다)

#### CRD 예시

예시로 사용자들에게 static website 를 빠르게 배포하기 위해서 Website 라는 custom resource 만들어 준다고 해보자.

사용자에게 parameter 로 받을 부분은 웹사이트의 이름과 웹사이트에서 제공할 파일을 담고있는 곳(git repo)일 것이며, Website resource 가 만들어지면 webserver pod 가 실행되고, Service 도 하나 생겨서 자동으로 endpoint 에 추가되어 IP 로 접속 가능하게 만들고 싶을 것이다.

그렇다면 사용자가 Website resource 를 생성하기 위해 생성할 YAML 은 다음과 같은 형식일 것이다.

```yaml
kind: Website               # Custom object
metadata:
  name: kubia               # 웹사이트의 이름
spec:
  gitRepo: https://github.com/luksa/kubia-website-example.git   # 파일 경로
```

사실 여기에 `apiVersion` field 도 필요한데, 뒤에서 설명하기로 한다.

#### CRD object 만들기

이제 진짜로 CRD 를 만들어 보도록 하자.

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: websites.extensions.example.com         # Custom object 의 full name
spec:
  scope: Namespaced                             # Namespace 에 종속될 것인가?
  group: extensions.example.com                 # API group 과 버전
  version: v1
  names:                                        # Custom resource 의 이름
    kind: Website
    singular: website
    plural: websites
    shortNames:
    - ws

```

위와 같이 CRD 를 정의하고 `kubectl create` 로 생성 요청을 보내면 CRD 가 생성된다.

> 이름이 `websites.extensions.example.com` 으로 굉장히 긴데, 이는 이름 충돌을 방지하기 위해서이다. 실제로 Website object 를 생성할 때는 `names.kind` 의 값을 이용해서 `kind: Website` 라고 YAML 에 적을 것이므로 괜찮다.

API group 과 버전은 여기서 정하는대로 `apiVersion` field 의 값이 정해진다. 위와 같이 설정하면 Website 의 YAML manifest 에는 `apiVersion: extensions.example.com/v1` 으로 입력하게 된다.

#### Custom Resource 생성

이제 `apiVersion` 도 정해졌으니 생성을 해본다.

```yaml
apiVersion: extensions.example.com/v1
kind: Website
metadata:
  name: kubia
spec:
  gitRepo: https://github.com/luksa/kubia-website-example.git
```

Object 를 생성하고, `kubectl get website` (`shortNames` 을 이용해 `kubectl get ws`) 를 해보면 아래와 같이 object 가 생성된 것을 확인할 수 있다.

```
$ kubectl get ws
NAME    AGE
kubia   44s
```

마찬가지로 `kubectl describe` 도 가능하며, `kubectl delete` 도 똑같이 동작한다.

하지만 object 만 생성한 것이지 아직 이 object 는 어떠한 동작도 하지 않는데, 이는 controller 가 존재하지 않기 때문이다.

> 물론 object 생성 목적이 어떤 동작을 하기 위해서는 아닐 수도 있다. ConfigMap 과 같은 경우 데이터만 저장하고 별도의 추가 동작이 없다. 대신 pod 들이 API server 에게 요청해서 값을 받아올 수 있는 것이다.

### 18.1.2 Automating custom resources with custom controllers

Website object 가 생성되었을 때 원하는 동작을 하게 만드려면 Website controller 를 생성해야 한다. Website controller 는 API server 를 watch 하고 있다가 Website object 가 생성되면 정의한 동작을 실행한다.

Website object 는 다음과 같이 설계할 것이다.

- 노드에 문제가 생기는 것을 대비하여 Deployment 안에 pod 를 띄울 것이다.
- Service 를 생성하여 pod 가 외부로 expose 되도록 한다.

#### Website controller 의 동작 이해

Controller 가 실행되면, Kubernetes API server 에 요청을 보내서 Website object 를 watch 하기 시작한다.

```
http://localhost:8001/apis/extensions.example.com/v1/websites?watch=true
```

(요청지가 `localhost:8001` 인 이유는 `kubectl proxy` 를 sidecar 컨테이너로 이용할 것이기 때문이다)

이제 API server 는 Website object 에 변경사항이 생기면 controller 에게 알려준다.

새로운 Website object 가 생성되면 `ADDED` event 를 돌려주게 되는데, 이 때 controller 는 Website 의 이름과 git repo URL 을 event 로부터 얻고, 해당 정보를 바탕으로 Service 와 Deployment 를 생성해달라는 요청을 API server 에 하게 된다.

만약 object 가 삭제될 때는 `DELETED` event 를 받고, 생성했던 resource 를 삭제해 달라고 API server 에 요청하게 된다.

#### Pod 로 controller 실행하기

> Controller 의 구현은 어려우므로, 구현된 image 를 저자가 제공해 주고 있다.

Controller 가 준비되었다면 Kubernetes 안에서 실행하기 위해 pod 로 만들어주면 된다.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: website-controller
spec:
  replicas: 1
  template:
    metadata:
      name: website-controller
      labels:
        app: website-controller
    spec:
      serviceAccountName: website-controller
      containers:
      - name: main
        image: luksa/website-controller
      - name: proxy
        image: luksa/kubectl-proxy:1.6.2
  selector:
    matchLabels:
      app: website-controller
```

생성하기 전에 `website-controller` 는 ServiceAccount 를 만들어야한다. 만약 RBAC 가 활성화 되어있다면, ClusterRoleBinding 을 이용해서 권한을 부여해야 한다.

```bash
$ kubectl create -f website-controller.yaml
```

위와 같이 하면 Website controller 가 실행되고, 이제 Website 를 실행해보면 event 를 받은 controller 가 Deployment 와 Service 를 실행한다.

> 알 수 없는 이유로 실습에 실패했다... controller 코드를 뜯어봐야 할 것 같은데...

이렇게 하면 Kubernetes 사용자들이 작은 노력으로, Deployment, Service, Pod 에 대한 이해 없이 웹사이트를 배포할 수 있게 된다.

### 18.1.3 Validating custom objects

눈치가 빠르다면 위에서 CRD 를 생성할 때 `gitRepo` 와 같은 field 에 대한 schema 를 정의하지 않았음을 깨달았을 것이다. 즉 validation 에 대한 로직이 현재 없으므로, `gitRepo` field 가 없는 Website object 도 생성이 가능하다. (기본적인 `apiVersion`, `kind`, `metadata` 정도에 대한 validation 만 진행한다)

그렇다면 controller 에 validation 로직을 추가해서 API server 가 validation 에 실패한 manifest 를 받지 않도록 할 수 있을까? 아쉽게도 이는 불가능하다. Controller 가 event 를 애초에 받으려면 object 가 생성되어야 하는데, validation 이 실패할 object 라면 애초에 생성이 되지 말아야 한다. 또 Controller 는 object 생성에 대해서 event 만 받기 때문에 validation 을 추가하더라도 사용자가 에러를 즉시 확인할 수 없다.

사용자가 오류에 대해 즉시 알게 하기 위해서는 API server 의 CustomResourceValidation 기능을 활성화해야 하고, CRD 에 JSON schema 를 제공해야 한다.

> 현재 Kubernetes 공식 문서에는 CRD 의 `apiVersion` 이 beta 가 아니고 `apiextensions.k8s.io/v1` 이다. 또한 여기에는 `versions` 가 string 이 아닌 array 형태이며, 각 버전별로 `schema` 를 정의할 수 있게 되어있다.

### 18.1.4 Providing a custom API server for your custom objects

Custom object 를 사용하는 좋은 방법 중 하나로 자체적으로 API server 를 두는 방법이 있다.

#### API server aggregation

Kubernetes 1.7 부터 custom API server 를 Kubernetes API server 와 통합할 수 있다!

원래 Kubernetes API server 는 monolithic component 였는데, 1.7 부터는 여러 개로 나뉘었고, client 의 요청을 적절한 곳으로 잘 포워딩 해주도록 변경되었다.

그러므로, Website object 만 따로 핸들링 해주는 API server 를 만들 수 있다. 그러면 API server 가 내부적으로 validation 을 담당할 수 있게 된다. 또한 CRD 를 생성할 필요도 없어지는데, API server 내부에 Website object 에 대한 정보를 담아버리면 되기 때문이다.

일반적으로 각 API server 는 자신의 resource 를 자신이 저장한다. 그래서 자체적으로 `etcd` 를 가지고 있거나, main API server 의 `etcd` 를 사용할 수 있다. 후자의 경우 CRD 를 미리 생성해줘야 object 생성이 가능해진다.

#### Custom API server 등록하기

Custom API server 를 추가하기 위해서는 pod 를 띄워서 Service 를 이용해 expose 해야한다. 그리고 API server 와 통합을 위해서 APIService resource 의 manifest 를 작성하여 등록해야 한다.

```yaml
apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
  name: v1alpha1.extensions.example.com
spec:
  group: extensions.example.com         # 이 API server 가 담당할 API group 및 version
  version: v1alpha1
  priority: 150
  service:                              # 실제로 요청을 처리할 service
    name: website-api
    namespace: default
```

APIService 를 등록하고 나면, `apiVersion` 값이 `extensions.example.com/v1alpha1` 인 요청들은 `website-api` Service 로 포워딩된다.

#### Custom clients

필요하다면 `kubectl` 과 같은 CLI tool 을 자체 개발하여 custom resource 에 대한 더욱 풍부한 제어를 할 수도 있다.

## 18.2 Extending Kubernetes with the Kubernetes Service Catalog

---

Kubernetes 에서 '서비스' (Service 리소스 아님) 를 만들고 싶다면, 사용자가 pod, Service, Secret 등을 직접 만들어서 배포하거나, 혹은 이를 배포해 달라고 Ops 팀에 요청해야 했다.

하지만 Kubernetes 의 목표는 이런 것들을 쉽게 할 수 있게 하는 것이기에, 이상적으로는 사용자가 '나 PostgreSQL DB가 필요해' 라고 요청 하면 모든 것들이 알아서 만들어져야 한다. 이런 것들을 가능하게 해주는 것이 Kubernetes Service Catalog 이다.

### 18.2.1 Introducing the Service Catalog

Service Catalog 는 말 그대로 catalog 라서, 사용자들이 catalog 에서 필요한 구조가 있다면 바로 배포가 가능하게 해준다. 마치 앞에서 봤던 Website custom resource 와 유사하다.

Catalog 에서 제공하는 각 서비스 종류마다 custom resource 를 API server 에 추가하지는 않고, 4개의 generic API resource 를 사용한다.

- ClusterServiceBroker: 서비스를 생성할 수 있는 시스템
- ClusterServiceClass: 생성이 가능한 서비스의 종류
- ServiceInstance: 생성된 서비스의 인스턴스
- ServiceBinding: 생성된 서비스와 clients (pods) 의 대응 관계

간단하게 살펴보면, 클러스터 관리자는 클러스터 내에서 사용하고자 하는 서비스에 대해 ClusterServiceBroker 를 생성한다. 그러면 Kubernetes 가 broker 에게 어떤 서비스를 제공하는지 확인하여 각 서비스 마다 ClusterServiceClass 를 생성한다. 이제 사용자가 서비스 생성을 요청하면 ServiceInstance 가 생성되는 것이고, 이 ServiceInstance 와 pod 가 ServiceBinding 으로 연결되며, pod 에는 필요하다면 Secret 이 주입된다. (ServiceInstance 와의 연결에도 필요할 수 있다)

### 18.2.2 Introducing the Service Catalog API server and Controller Manager

Service Catalog 도 분산 시스템으로, 3개의 component 로 구성되어 있다.

- Service Catalog API server
- etcd
- Controller Manager (controller 가 실행되는 곳)

앞서 살펴본 4개의 generic API resource 는 모두 Service Catalog API server 에 YAML/JSON 을 POST 해서 생성한다. 그러면 이제 해당 resource 정보를 etcd 에 저장한다. (혹은 CRD 를 main API server 에 만들어서 그 곳의 etcd 에 저장하던가)

Controller Manager 의 controller 는 실제 resource 가지고 뭔가 하는데, 당연히 Service Catalog API server 와 통신하지만 이들은 직접 서비스를 생성하지는 않고, ServiceBroker 에게 그 일을 맡긴다.

(??? 뭘 한다는 건지 안 나와있음)

### 18.2.3 Introducing Service Brokers and the OpenServiceBroker API

클러스터 관리자는 Service Catalog 에 Service Broker 를 등록해야 하는데, Service Broker 는 반드시 OpenServiceBroker API 를 구현해야 한다.

#### OpenServiceBroker API

Service Catalog 는 API 를 이용해서 broker 와 통신한다.

- `GET /v2/catalog`: 서비스 목록 가져오기
- `PUT /v2/service_instances/:id`: 서비스 인스턴스 생성하기
- `PATCH /v2/service_instances/:id`: 서비스 인스턴스 수정하기
- `PUT /v2/service_instances/:id/service_bindings/:binding_id`: 서비스 인스턴스 바인딩하기
- `DELETE /v2/service_instances/:id/service_bindings/:binding_id`: 서비스 인스턴스 바인딩 해제하기
- `DELETE /v2/service_instances/:id`: 서비스 인스턴스 삭제하기

#### Service Catalog 에 broker 등록

Service Catalog API server 에 ServiceBroker resource manifest 를 POST 하면 된다.

```yaml
apiVersion: servicecatalog.k8s.io/v1alpha1
kind: Broker
metadata:
  name: database-broker
spec:
  url: http://database-osbapi.myorganization.org  # OpenServiceBroker API URL
```

위 ServiceBroker 는 여러 종류의 DB 를 생성할 수 있는 가상의 broker 이다.

ClusterServiceBroker 가 생성되면, Controller Manager 의 controller 가 URL 에 접속하여 생성 가능한 서비스 목록을 조회한다. 서비스 목록을 얻어오면, ClusterServiceClass 를 각 서비스마다 생성한다. 예를 들면 'PostgreSQL DB' 가 하나의 ClusterServiceClass 이다.

각 ClusterServiceClass 에는 service plan 이 부여되어 있는데, 이는 요금제라고 생각하면 된다. 해당 서비스를 무료 티어로 이용할지, 프리미엄 티어로 이용할지 정할 수 있다.

#### Listing the available services in a cluster

`kubectl get serviceclasses` 로 사용할 수 있는 서비스 목록을 조회할 수 있다.

마지 6장에서 살펴본 StorageClasses 와 비슷하다. StorageClass 는 어떤 종류의 저장소를 생성할 수 있는지 알려줬다면 ServiceClass 는 생성 가능한 서비스 종류를 알려준다.

### 18.2.4 Provisioning and using a service

이제 실제로 서비스 하나를 생성해보자.

```yaml
apiVersion: servicecatalog.k8s.io/v1alpha1
kind: Instance                              # ServiceInstance 생성
metadata:
  name: my-postgres-db
spec:
  serviceClassName: postgres-database       # ServiceClass 와 plan 을 선택
  planName: free
  parameters:
    init-db-args: --data-checksums
```

위와 같이 Service 이름과 사용하는 ServiceClass 와 plan 을 선택하여 manifest 를 작성하고, 이를 POST 하면 된다. 이렇게 하면 ServiceInstance 가 생성된다.

이제 Service Catalog 는 ServiceClass 가 속해이있는 broker 에 연결해서 서비스를 생성하라고 요청한다. 그러면 실제 Kubernetes resource 생성은 온전히 broker 의 몫이 된다.

여기서 신기한 점은 broker 가 PostgreSQL DB 인스턴스를 생성하긴 하는데, 어디서 하는지 모른다는 점이다. 클러스터 내부에 만들 수도 있고, Kubernetes 안에서 만들지 않을 수도 있다. Service Catalog 나 이를 요청한 사용자나, 전혀 신경쓰지 않는다. 단지 기능이 동작하면 된다.

그래서 생성이 되었다고 치면, pod 에서 사용할 수 있어야 하는데, 이를 위해서는 bind 를 해줘야 한다.

#### Binding a ServiceInstance

ServiceBinding 을 생성하여 pod 과 서비스를 연결한다. 그 과정에서 통신을 위해 secret 을 사용할 수도 있다. (Access Key)

```yaml
apiVersion: servicecatalog.k8s.io/v1alpha1
kind: Binding
metadata:
  name: my-postgres-db-binding
spec:
  instanceRef:
    name: my-postgres-db
  secretName: postgres-secret   # 필요한 secret 주입
```

여기서 이상한 점은 실제로 연결할 pod 의 정보는 없다는 점이다.

책이 쓰여질 당시에는 ServiceInstance 의 secret 을 Pod 에 직접 주입하는 방법이 없어서, pod 에서 이를 직접 mount 해야 했다.

> 추후 PodPresets 라는 기능이 나왔다고 한다.

ServiceBinding 을 생성하게 되면 controller 가 broker 와 연결하고 broker 가 binding 을 실제로 만들어주고, broker 는 연결을 위한 정보 (credentials etc.) 를 돌려준다. 이 연결을 위한 정보는 manifest 에서 정한 secret 에 저장된다. (자동 생성되는 것)

#### Client pod 에서 secret 사용하기

연결을 위한 정보다 secret 에 저장되어 있으므로, pod 에서는 이를 mount 해서 사용하면 되고, secret 에 들어있는 대표적인 정보로는 host URL, username, password, access key, API token 등이 있을 것이다.

### 18.2.5 Unbinding and deprovisioning

ServiceBinding 이 불필요해지면 `kubectl delete servicebinding` 으로 삭제하면 된다. 그러면 secret 이 제거되고 broker 가 unbinding 을 진행한다. 이 상태에서는 다시 ServiceBinding 을 만들 수 있다.

서비스 자체가 불필요해지면 `kubectl delete serviceinstance` 로 삭제하면 된다.

### 18.2.6 Understanding what the Service Catalog brings

Service Catalog 는 서비스 제공자들이 Kubernetes 를 통해 서비스를 제공할 수 있게 해준다.

해당 서비스가 쉽게 생성되기 때문에 애플리케이션 배포에 더욱 유용하게 사용할 수 있다.

## 18.3 Platforms built on top of Kubernetes

---

이처럼 Kubernetes 는 굉장히 유연하고 확장 가능한 시스템이지만, 몇몇 회사들은 플랫폼을 자체 개발하여 Kubernetes 위에서 동작하게 하고, 이를 서비스로 제공하고 있다. (PaaS, Platform-as-a-Service)

Deis Workflow 와 Red Hat OpenShift 에 대해서 알아본다.

### 18.3.1 Red Hat OpenShift Container Platform

Red Hat OpenShift 는 개발자에게 초점이 맞춰진 PaaS dlek. 애플리케이션의 빠른 개발과 손쉬운 배포, 스케일링, 장기간 유지보수를 목표로 한다.

OpenShift 는 Kubernetes 이전에 나왔어서, 버전 1~2 는 전혀 Kubernetes 와 무관했는데, 버전 3 부터는 재개발에 들어가 Kubernetes 위에서 돌아가도록 개편했다. Red Hat 이 기존 버전을 버리고 Kubernetes 위에서 동작하도록 다시 개발을 할 정도라면, Kubernetes 가 그만큼 좋다는 뜻이기도 하다.

Kubernetes 에서 rollout 와 scaling 을 자동화 해준다면, OpenShift 에서는 애플리케이션 이미지 빌드와 배포를 자동화하여 CI 를 사용하지 않아도 되게 해준다. 그리고 user, group 관리가 가능하여 보안도 신경쓸 수 있다.

#### Additional Resources in OpenShift

OpenShift 에서 추가로 제공되는 API object 들이 몇 가지 있다.

- User & Group
- Project
- Template
- BuildConfig
- DeploymentConfig
- ImageStream
- Route
- 기타

#### Users, Groups, Projects

OpenShift 에서는 User 를 만들어서 권한 통제를 확실하게 할 수 있으며, 특정 Project 에 접근 권한을 부여하거나 말소할 수 있다. 이러한 동작은 모두 클러스터 관리자가 수행한다.

Project 는 Kubernetes namespaces 와 동일하고, 추가로 annotation 이 들어간 것이다.

#### Application Templates

Kubernetes 에서는 YAML/JSON 으로 리소스를 생성하는데, OpenShift 에서는 manifest 가 parameterizable 하다. 그래서 OpenShift 에서는 Template 을 만들어 placeholder (변수 처럼) 를 뒀다가, 나중에 실제로 Template 으로부터 리소스를 생성할 때 parameter 를 넘겨주고 그 정보를 바탕으로 리소스를 생성한다.

OpenShift 에서 미리 제공되는 Template 도 있어서 몇 개의 parameter 만으로 복잡한 아키텍쳐를 구성할 수 있다.

#### Building Images from source using BuildConfigs

OpenShift 의 장점 중 하나는 git repo 의 소스 코드로부터 바로 이미지를 빌드하고 배포할 수 있다는 점이다. 직접 컨테이너 이미지를 빌드할 필요가 없어진다.

이 작업은 BuildConfig 가 해주는데, git repo 에 commit 이 발생하면 빌드를 trigger 하게 할 수 있다.

Source To Image 라는 내장된 빌드 기능이 있어서, git repo 를 보고 어떤 종류의 애플리케이션인지 감지하여 적절한 빌드 과정을 수행해 준다. 만약 `pom.xml` 이 발견되면 Maven 빌드를 수행한다.

#### Automatically deploying newly built images with DeploymentConfigs

DeploymentConfig 를 활용하면 BuildConfig 를 이용해 빌드한 이미지를 자동으로 클러스터에 배포할 수 있게 된다.

DeploymentConfig 는 ImageStream 을 참조하도록 되어있는데, ImageStream 은 말 그대로 이미지들의 스트림이고, 이미지가 빌드될 때 ImageStream 에 추가되는 구조이다. 그래서 DeploymentConfig 가 새로운 이미지를 감지하여 rollout 을 자동으로 수행한다.

#### Exposing services externally using Routes

Kubernetes 에 Ingress object 가 없던 시절에는 `NodePort` 나 `LoadBalancer` 타입의 Service 를 사용해야 했다. 이 때에도 OpenShift 에서는 Route 라는 대안을 제공해줬다. Ingress 와 유사하지만, TLS termination, traffic splitting 과 관련된 추가 설정이 가능하다.

Route 에는 Router 가 필요한데 얘는 load balancer 또는 proxy 의 역할을 한다.

### 18.3.2 Deis Workflow and Helm

#### Deis Workflow

Deis Workflow 는 Kubernetes 클러스터에 직접 배포한다. 실행하면 Service, ReplicationController 를 여러 개 생성하고, 개발자에게 간단하고 친숙한 환경을 제공해준다.

새로운 버전을 배포할 때는 `git push deis master` 만 입력하면 나머지는 Workflow 가 해결해 준다. OpenShift 와 마찬가지로 source to image 빌드 기능을 제공하고, rollout/rollback, edge routing 이 제공되며, Kubernetes 내장 기능에는 없는 로그 적재, 통계치, 알림 기능이 있다.

#### Deploying resources through Helm

Helm 은 Kubernetes 용 package manager 이다. 2가지로 구성되어 있는데, 하나는 `helm` CLI 와 Tiller 라는 서버 component 로 Kubernetes 클러스터 내 pod 형태로 실행된다.

이 2개의 component 를 사용해서 package 를 관리한다. Helm 애플리케이션 package 를 Chart 라고 부른다.
Chart 는 애플리케이션 설정을 담고 있는 Config 와 합쳐져 Release 가 된다. Release 가 되면 실행 중인 애플리케이션 인스턴스가 된다. Release 는 `helm` CLI 를 이용해서 관리하는데, `helm` CLI 가 Tiller 와 통신하여 Chart 에서 요구하는 Kubernetes 리소스를 모두 생성하도록 해준다.

우리가 Docker Hub 등에서 이미지를 pull 받아 사용하는 것처럼, 만약 Kubernetes 상에서 어떤 아키텍쳐나 애플리케이션이 배포하고 싶다면 Helm Chart 가 있는지 확인해보고 있으면 그것을 그대로 가져와서 사용할 수 있게 된다.

예를 들어 MySQL 을 실행하고 싶으면 Chart repo 를 clone 받은 다음 `helm install --name my-dayabase stable/mysql` 명령을 입력하면 알아서 Deployment, Service, Secret, PVC 를 만들어 준다.

---

마지막 장이다. 끝!
