Kubernetes Cluster： 認証とスケジューリングアルゴリズムの動作原理 

この記事では、**Kubernetes**クラスタの認証とスケジューリングアルゴリズムの動作原理を解説・分析しています。

*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/from-confused-to-proficient-kubernetes-authentication-and-scheduling_595657)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

#ケージプログラム
###コード
Go言語を使用して、2580番ポートを監視するシンプルなWebサーバプログラムapp.goを書きます。HTTP を通してサービスのルートパスにアクセスすると、サービスは文字列 - "This is a small app for kubernetes..." を返します。

```
golang
package main

import (

        "github.com/gorilla/mux"

        "log"

        "net/http"

)

func about(w http.ResponseWriter, r *http.Request) {

        w.Write([]byte("This is a small app for kubernetes...\n"))

}

func main() {

        r := mux.NewRouter()

        r.HandleFunc("/", about)

        log.Fatal(http.ListenAndServe("0.0.0.0:2580", r))

}
```

プログラムをコンパイルするためにgo buildコマンドを実行すると、アプリ用の実行ファイルが生成されます。オペレーティングシステム内で動作し、システム内のライブラリファイルに依存する一般的な実行ファイルです。

```
shell
# ldd app

linux-vdso.so.1 => (0x00007ffd1f7a3000)

libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f554fd4a000)

libc.so.6 => /lib64/libc.so.6 (0x00007f554f97d000)

/lib64/ld-linux-x86-64.so.2 (0x00007f554ff66000)
```

###ケージ
このプログラムをオペレーティングシステムのライブラリファイルから独立したものにするには、コンテナイメージ、つまり隔離された実行環境を作成します。

Dockerfileはコンテナイメージを作成するための「メニュー」です。Dockerfileには、以下の2つの手順があります。

1、CentOSのベースイメージをダウンロードします。
2、アプリの実行ファイルを画像の/usr/local/binディレクトリに保存します。

```
Dockerfile
FROM centos

COPY app /usr/local/bin
```

###アドレス
作成した画像をローカルディレクトリに保存したら、アプリストアに相当する画像リポジトリにアップロードします。Alibaba Cloudのイメージリポジトリを利用することができます。アップロード後、画像のアドレスが以下のように変わります。

```
registry.cn-hangzhou.aliyuncs.com/kube-easy/app:latest
```

画像のアドレスは、リポジトリのアドレス、名前空間、画像名、画像バージョンの4つの部分に分かれています。先ほどのスナップショットでは、イメージのリポジトリは杭州にあり、名前空間はkube-easy、イメージ名はapp、イメージバージョンは最新のものです。

そして、Kubernetesクラスタで動作する「ケージド」プログラムがあります。

#アクセスする

###ポータル
Kubernetesには、共通のオペレーティングシステムのようなAPIという概念があります。APIがあれば、クラスタにアクセスできるようにするためのポータルがあります。

KubernetesのAPIは、クラスタノード上で動作するAPIサーバとして実装されています。APIサーバーとは、代表的なWebサーバープログラムで、HTTPやHTTPSのインターフェイスを公開してサービスを提供するものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/ab852bb4-fd9a-67ba-4416-4ce07b9d4907.png)

Alibaba Cloud Kubernetesクラスタを作成してみましょう。クラスタ管理画面にログインすると、パブリックネットワーク上にAPIサーバーポータルが表示されます。

```
APIサーバー 内网连接端点： https://xx.xxx.xxx.xxx:6443
```

###双方向デジタル証明書検証
Alibaba Cloud KubernetesのAPIサーバーでは、クライアントとのセキュアな通信を確保するためにCA署名ベースの双方向電子証明書検証を採用していますが、初心者向けに以下でさらに説明します。

概念的には、電子証明書とは、ネットワーク通信の参加者を検証するためのファイルであり、学校が発行する卒業証書に似ています。学校は信頼できる第三者CAであり、学生は通信参加者です。学校の評判が社会から信頼されれば、学校が発行する卒業証書も社会に受け入れられます。参加者証明書とCA証明書は、それぞれ卒業証書と学校の免許証に類推されます。

参加者にはCAと共通の参加者が含まれ、証明書にはCAと参加者証明書が含まれ、関係性には証明書の発行と信頼関係が含まれます。関係性が重要です。

まず、発行関係について議論してみましょう。次の図は、2つのCA証明書と3つの参加者証明書の発行関係を示しています。上のCA証明書は、真ん中のCA証明書と右の参加者証明書を含めて2つの証明書を発行しています。

真ん中のCA証明書は、下の2つの参加者証明書を発行しています。6つの証明書は発行関係に関連付けられており、ツリー状の証明書発行関係図を形成しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/aec42b75-ab0d-0659-0f88-13ea8ea0d2b2.png)


しかし、証明書や発行関係は、参加者間の信頼された通信を保証するものではありません。上図のように、右端の参加者がWebサイト、左端の参加者がブラウザだとします。ブラウザがWebサイトのデータを信頼しているのは、ブラウザが上位のCA証明書を信頼しているから（信頼関係による）であって、Webサイトが証明書を持っていたり、Webサイトの証明書がCAから発行されているからではありません。

CA証明書、参加者証明書、発行関係、信頼関係を理解した上で、CA署名ベースの双方向デジタル証明書検証について説明しましょう。

クライアントとAPIサーバは共通の通信参加者であり、それぞれに証明書が発行されます。2つの証明書は、クラスタCAとクライアントCAという名前のCAが発行します。クライアントはクラスタ CA を信頼しています。したがって、クライアントは、クラスタCAが発行した証明書を持つAPIサーバーを信頼しています。そのため、APIサーバーはクライアントと通信する前にクライアントCAを信頼する必要があります。

Alibaba Cloud Kubernetesクラスタでは、クラスタCAの証明書とクライアントCAの証明書は、実際には実装のための1つの証明書です。そのため、関係図は以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/6b492c36-cb90-2cc7-cb97-e1feb4e06191.png)

###KubeConfig ファイル
クラスタ管理コンソールにログオンし、KubeConfig ファイルを取得します。このファイルには、クライアント証明書とクラスタ CA 証明書が含まれています。証明書はBase64エンコードされているため、Base64を使用して証明書をデコードし、OpenSSLを使用して表示してください。

1.  クライアント証明書発行者 CN はクラスタ ID c0256a3b8e4b948bb9c21e66b0e1d9a72、証明書 CN は RAM ユーザ 252771643302762862 です。

```
Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 787224 (0xc0318)

    Signature Algorithm: sha256WithRSAEncryption

        Issuer: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72

        Validity

            Not Before: Nov 29 06:03:00 2018 GMT

            Not After : Nov 28 06:08:39 2021 GMT

        Subject: O=system:users, OU=, CN=252771643302762862
```

2.  前述のクライアント証明書は、API サーバーがクライアント CA 証明書を信頼している場合にのみ、API サーバーの検証を通過します。kube-apiserver プロセスは、client-ca-file パラメータを使用して、信頼できるクライアント CA 証明書 /etc/kubernetes/pki/apiserver-ca.crt を指定します。このファイルには2つのクライアントCA証明書が含まれています。1つはクラスタ管理に関連するもので、ここでは説明しません。もう1つは、下図のようにクライアント証明書発行者と同じCNを持っています。


```
Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 787224 (0xc0318)

    Signature Algorithm: sha256WithRSAEncryption

        Issuer: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72

        Validity

            Not Before: Nov 29 06:03:00 2018 GMT

            Not After : Nov 28 06:08:39 2021 GMT

        Subject: O=system:users, OU=, CN=252771643302762862
```

3.API サーバーで使用される証明書は、kube-apiserver プロセスの tls-cert-file パラメータで決定されます。このパラメータは証明書 /etc/kubernetes/pki/apiserver.crt に指示します。この証明書の CN は kube-apiserver で、発行者はクラスタ CA 証明書の c0256a3b8e4b948bb9c21e66b0e1d9a72 です。

```
Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 2184578451551960857 (0x1e512e86fcba3f19)

    Signature Algorithm: sha256WithRSAEncryption

        Issuer: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72

        Validity

            Not Before: Nov 29 03:59:00 2018 GMT

            Not After : Nov 29 04:14:23 2019 GMT

        Subject: CN=kube-apiserver
```

4.  クライアントは、先行する API サーバ証明書を確認する必要があります。そのため、KubeConfig ファイルには、その発行元であるクラスタ CA 証明書が含まれています。クラスタ CA 証明書とクライアント CA 証明書を比較して、同じものであることを確認します。

```
Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 786974 (0xc021e)

    Signature Algorithm: sha256WithRSAEncryption

        Issuer: C=CN, ST=ZheJiang, L=HangZhou, O=Alibaba, OU=ACS, CN=root

        Validity

            Not Before: Nov 29 03:59:00 2018 GMT

            Not After : Nov 24 04:04:00 2038 GMT

        Subject: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72
```

###アクセス
原理を理解した上で、簡単なテストを行います。証明書をパラメータとして、cURLを使ってAPIサーバにアクセスし、期待通りの結果が得られます。

```
shell
# curl --cert ./client.crt --cacert ./ca.crt --key ./client.key https://xx.xx.xx.xxx:6443/api/

{

  "kind": "APIVersions",

  "versions": [

    "v1"

  ],

  "serverAddressByClientCIDRs": [

    {

      "clientCIDR": "0.0.0.0/0",

      "serverAddress": "192.168.0.222:6443"

    }

  ]

}
```

#ベストチョイス
###2種類のノードと1種類のタスク
冒頭で述べたように、Kubernetesはクラスタ内の複数のノードを管理するオペレーティングシステムです。クラスタ内のこれらのノードの役割は全く同じである必要はありません。Kubernetesクラスタには、マスターノードとワーカーノードの2種類のノードがあります。

役割の違いですが、マスターノードはクラスタ全体を管理し、ワーカーノードは共通のタスクを担っています。クラスタ管理コンポーネントは、クラスタポータルを実装するAPIサーバなど、マスターノード上で動作する主要なコンポーネントです。

Kubernetesクラスタでは、タスクはポッドとして定義されます。ポッドは、タスクを運ぶクラスタ内の原子単位です。ポッドは、コンテナ化された複数のアプリをカプセル化するため、コンテナグループに変換されます。原則として、ポッドにカプセル化されたコンテナはかなりの結合関係を持っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/2d8c58ac-f24f-e727-579a-0558d3cfa649.png)


###ベストチョイス
スケジューリングアルゴリズムは、ポッドによって定義されたタスクがこのノード上で完了するように、ポッドのための快適な「居住」を選択することによって問題を是正する必要があります。

Kubernetesクラスタスケジューリングアルゴリズムは、"最良の選択 "を実現するために2段階の戦略を採用しています。最初のステップでは、条件を満たさないノードを全ノードから除外する、つまり事前選択を行います。第2のステップでは、残りのノードにスコアをつけます。スコアが最も高いものを勝者とします。

続いて、冒頭で作成したイメージを使ってポッドを作成し、そのポッドがクラスタノードにどのようにスケジューリングされているかをログを使って詳細に分析します。

###Podの構成
まず、JSON形式のポッド設定ファイルを作成します。設定ファイルには、イメージアドレス、コマンド、コンテナポートの3つのポイントがあります。

```
json
{

    "apiVersion": "v1",

    "kind": "Pod",

    "metadata": {

        "name": "app"

    },

    "spec": {

        "containers": [

            {

                "name": "app",

                "image": "registry.cn-hangzhou.aliyuncs.com/kube-easy/app:latest",

                "command": [

                    "app"

                ],

                "ports": [

                    {

                        "containerPort": 2580

                    }

                ]

            }

        ]

    }

}
```

###ログレベル
クラスタスケジューリングアルゴリズムは、APIサーバと同様にマスターノード上で動作するシステムコンポーネントとして実装されています。対応するプロセス名は kube-scheduler で、複数レベルのログ出力をサポートしています。ただし、コミュニティではログレベルの詳細な指示は提供されていません。スケジューリングアルゴリズムによるノードのフィルタリングとスコアリングのプロセスを見るには、ログレベルを10に上げます。このように、パラメータ--v=10を追加します。

```
shell
kube-scheduler --address=127.0.0.1 --kubeconfig=/etc/kubernetes/scheduler.conf --leader-elect=true --v=10
```

###Pod作成
cURL、証明書、およびポッド構成ファイルをパラメータとして使用して、POSTリクエストを送信してAPIサーバーインターフェイスにアクセスし、対応するポッドをクラスタ内に作成します。

```
shell
# curl -X POST -H 'Content-Type: application/json;charset=utf-8' --cert ./client.crt --cacert ./ca.crt --key ./client.key https://47.110.197.238:6443/api/v1/namespaces/default/pods -d@app.json
```

###事前選択
事前選択とは、Kubernetesスケジューリングの最初のフェーズで、事前に定義されたルールに従って条件を満たさないノードをフィルタリングして排除することです。Kubernetesで実装されている事前選択ルールは、Kubernetesのバージョンによって大きく異なります。しかし、基本的な傾向としては、事前選択ルールはどんどんリッチになっていきます。

一般的な事前選択ルールは、PodFitsResourcesPredとPodFitsHostPortsPredの2つです。前者のルールは、ノード上に残っているリソースがPodの要件を満たしているかどうかを判断します。後者のルールは、ノード上のポートが他のポッドで使用されているかどうかをチェックします。

次の図に、スケジューリングアルゴリズムがテストポッドを処理する際に出力する事前選択ルールのログを示します。このログは、CheckVolumeBindingPred事前選択ルールの実行を記録しています。いくつかのタイプの永続的ボリューム（PV）は、1つのノードにのみアタッチされます。このルールは、PV上のポッド要件を満たしていないノードをフィルタリングします。

アプリのオーケストレーションファイルは、ポッドがPVに関する要件を満たしていないことを示しています。したがって、この条件ではどのノードもフィルタリングされません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/bfc00af8-d262-2c69-20bb-36339824ab57.png)

###プリファレンス
選好はスケジューリングアルゴリズムの第二段階であり、kube-chedulerは利用可能なリソースやノードの他のルールに基づいて残りのノードをスコア化します。

現在、スケジューリングアルゴリズムで評価される資源はCPUとメモリの2つですが、評価は単純ではありません。CPUとメモリのリソースの残量が多いほどスコアが高くなります。

ログには、LeastResourceAllocationとBalancedResourceAllocationの2つの計算方法が記録されています。前者の計算方法は、ノードにポッドがスケジューリングされた後のCPUとメモリの合計に対する、ノードの残りCPUとメモリの比率を計算します。比率が高いほどスコアが高くなります。

後者は，ノードのCPUとメモリ使用量の差の絶対値を計算する．絶対値が大きいほどスコアは低くなります。

前者の方法はリソース使用量の少ないノードを選択する傾向があり、後者の方法はリソース使用量の近いノードを選択する傾向があります。この2つの方法はやや矛盾しており、最終的には一定の重みでバランスを取っています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/fb796378-8044-c112-bbb0-167549ebe6d4.png)

プリファレンスアルゴリズムでは、資源以外にも、ポッドとノード間の親和性や、サービスが複数の同一のポッドで構成されている場合の異なるノード上の複数のポッドの分散度なども考慮しており、高可用性を確保するための戦略となっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/2f7387db-cd67-6f59-d3cd-9f9bb299f29a.png)

###スコア
最後に、スケジューリングアルゴリズムは、すべてのスコア項目に重みをかけ、その結果を合計して、各ノードの最終的なスコアを得ます。テストクラスタでは、ログに記録されたスコア項目の重みを1に設定するデフォルトのスケジューリングアルゴリズムを使用しています。したがって、ログに記録されたスコア項目に基づいてスコアを計算すると、3つのノードの最終スコアは29、28、29となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/53966b69-7958-a07f-e490-e3cdc6e2c489.png)


ログ出力のスコアは、ログにすべてのスコア項目が含まれていないため、ここで計算されたスコアとは異なります。欠落しているポリシーは、NodePreferAvoidPodsPriorityである可能性があります。このポリシーの重みは10000で、各ノードのスコアは10です。この場合、最終的なログ出力が得られます。

#結論
この記事では、KubernetesクラスタのAPIサーバを使ってクライアントを認証する方法や、コンテナアプリケーションを適切なノードに割り当てる方法を解析するために、単純なコンテナ化Webプログラムの例を考えてみます。

この記事では、解析の際に kubectl やコンソールなどの便利なツールを捨て、認証とスケジューリングアルゴリズムの動作原理を解析するために、KubeConfig ファイルを分解したり、スケジューラのログを解析したりするなどの小さな実験を行っています。これでKubernetesクラスタの理解が深まることを願っています。

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*