元ネタは下記です。

https://github.com/kelseyhightower/kubernetes-the-hard-way

翻訳しつつ、いろいろなネタをはさんで紹介できればと思います。

Kubernetes Hard Wayでは、Kubernetesの設定を一から進めます。

なので、このラボは、完全に自動化されて管理されたKubernetesクラスタ作るコマンドを探している人のためのものではありません。

このKubernetes The Hard Wayは学習することに最適化されています。
Kubernetesクラスタを立ち上げるための必要な各タスクを確実に理解するための、長い道のりが示されています。

想定読者

本番用のKubernetesクラスタをサポートすることを計画していて、かつ、Kubenretesクラスタのすべての部品がどのように組み合わさっているかを理解したいという人向けです。

今回出来上がるクラスタの詳細

Kubernetes The Hard Wayでは、コンポーネント間のエンドツーエンドの暗号化とRBAC認証ができる、可用性の高いKubernetesクラスタを作ります。

使うコンポーネントとそのバージョン一覧は下記の通りです

このラボでは、Google Cloud Platformを使うことを前提にしています。

GCPはインフラのベースで使いますが、このラボで学んだ内容は他のプラットフォームにも適用できます。

では、はじめていきましょう!

今回はGCPを使うので、サインアップをお願いします。

初回には$300の無料枠があります。

大体のコストは$0.22 / hour ($5.39 / day)くらいです。

Google Cloud Platform SDKのセットアップ

このドキュメント を参考にgcloudコマンドの準備をしてください。

最初に gcloud コマンドを使う場合は init を使うのが一番手っ取り早いです。

% gcloud init

もしくは、自前でGCEのデフォルトのリージョンの設定していきましょう

% gcloud config set compute/region us-west1
% gcloud config set compute/zone us-west1-c

ちなみに自分は、サービスに影響ありそうなので、設定せずにリージョンを指定して叩くようにしました。

tmuxを使ってパラレルにコマンドを打てるようにする

一応おすすめ設定として書かれているので興味がある人は下記を見てみてください。

https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/01-prerequisites.md#running-commands-in-parallel-with-tmux

CFSSL

cfsslcfssljsonPKIの環境構築とTLSの証明書発行に使います。

インストール

Linuxの手順もありますが、今回はMacのみ記載します。

brew install cfssl

Linuxの手順はこちらから

https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/02-client-tools.md#linux

cfssl のバージョンが1.2.0以上かどうか確認してください

# 手元のバージョン
% cfssl version
Version: 1.3.2
Revision: dev
Runtime: go1.10

kubectlコマンドのインストール

kubectl のバージョンは1.12.0以上が必要です。

brew経由でもgcloud経由でインストールでもいいと思います。

今回はgcloudコマンド経由でインストールされる kubectl.1.12 コマンドを使おうと思います。

ここからKubernetesクラスタを作っていきます。

Kubernetesには、Kubernetesのコントロールプレーンとコンテナを動かくワーカーノードをホストするためのマシンが必要となります。

このステップでは、単一のゾーンでセキュアかつ可用性の高いKubernetesクラスタを動かすために必要なリソースをプロビジョニングします。

ネットワーク

Kubernetesのネットワークモデルは、コンテナとノードが互いに通信できるフラットなネットワークを想定しています。

このポリシーに沿えない場合は、Kubernetesのネットワークポリシーを使って、コンテナ群がお互いに通信したり、外部ネットワークと通信する方法を制限することが可能です。

Virtual Private Cloud Network

KubernetesクラスターをホストするVPCの設定を行います。

まずは kubernetes-the-hard-way という名前でVPCを作ります。

gcloud compute networks create kubernetes-the-hard-way --subnet-mode custom

VPCのサブネットは、Kubernetesクラスタの各ノードにプライベートIPアドレスを割り振るのに十分なIPアドレス範囲を確保する必要があります。

gcloud compute networks subnets create kubernetes \
  --network kubernetes-the-hard-way \
  --range 10.240.0.0/24

Firewall Rules

全てのプロトコルの内部通信を許可するようにFirewallの設定を行います。

gcloud compute firewall-rules create kubernetes-the-hard-way-allow-internal \
  --allow tcp,udp,icmp \
  --network kubernetes-the-hard-way \
  --source-ranges 10.240.0.0/24,10.200.0.0/16

さらに、外部からのSSH、ICMP、HTTPSも許可するようにします

gcloud compute firewall-rules create kubernetes-the-hard-way-allow-external \
  --allow tcp:22,tcp:6443,icmp \
  --network kubernetes-the-hard-way \
  --source-ranges 0.0.0.0/0

終わったら kubernetes-the-hard-way VPCのネットワークの設定を確認してみましょう。

gcloud compute firewall-rules list --filter="network:kubernetes-the-hard-way"

NAME                                    NETWORK                  DIRECTION  PRIORITY  ALLOW                 DENY
kubernetes-the-hard-way-allow-external  kubernetes-the-hard-way  INGRESS    1000      tcp:22,tcp:6443,icmp
kubernetes-the-hard-way-allow-internal  kubernetes-the-hard-way  INGRESS    1000      tcp,udp,icmp

Kubernetes Public IP Address

KubernetesのAPIサーバーの前段の外部LBに付与するIPアドレスを払い出しましょう。

gcloud compute addresses create kubernetes-the-hard-way \
  --region $(gcloud config get-value compute/region)

終わったら確認して見ましょう

gcloud compute addresses list --filter="name=('kubernetes-the-hard-way')"

NAME                     REGION    ADDRESS        STATUS
kubernetes-the-hard-way  us-west1  XX.XXX.XXX.XX  RESERVED

GCEインスタンス

このラボのGCEインスタンスは、containerdのコンテナランタイムをサポートしているUbuntu Server 18.04を使用してプロビジョニングします。

各GCEインスタンスには、Kubernetesのブートストラップのプロセスを簡単にするために固定のプライベートIPアドレスを割り振ります。

Kubernetes Controllers

Kunernetesのコントロールプレーンを司るインスタンスを3つ立てます。

for i in 0 1 2; do
  gcloud compute instances create controller-${i} \
    --async \
    --boot-disk-size 200GB \
    --can-ip-forward \
    --image-family ubuntu-1804-lts \
    --image-project ubuntu-os-cloud \
    --machine-type n1-standard-1 \
    --private-network-ip 10.240.0.1${i} \
    --scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
    --subnet kubernetes \
    --tags kubernetes-the-hard-way,controller
done

Kubernetes Workers

各ワーカーインスタンスには、KubernetesクラスタのCIDR範囲からPodに対するサブネットの割り当てが必要です。

Podのサブネットの割り当ては、後のステップでコンテナネットワークを設定するために使います。

pod-cidr インスタンスメタデータは、実行時にPodのサブネット割り当てをインスタンスと共有するため(公開?)に使用されます。

Kubernetesのワーカーノードとして3つのインスタンスを作ります。

for i in 0 1 2; do
  gcloud compute instances create worker-${i} \
    --async \
    --boot-disk-size 200GB \
    --can-ip-forward \
    --image-family ubuntu-1804-lts \
    --image-project ubuntu-os-cloud \
    --machine-type n1-standard-1 \
    --metadata pod-cidr=10.200.${i}.0/24 \
    --private-network-ip 10.240.0.2${i} \
    --scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
    --subnet kubernetes \
    --tags kubernetes-the-hard-way,worker
done

終わったら確認してみましょう。

gcloud compute instances list

NAME          ZONE        MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP     STATUS
controller-0  us-west1-c  n1-standard-1               10.240.0.10  XX.XXX.XXX.XXX  RUNNING
controller-1  us-west1-c  n1-standard-1               10.240.0.11  XX.XXX.X.XX     RUNNING
controller-2  us-west1-c  n1-standard-1               10.240.0.12  XX.XXX.XXX.XX   RUNNING
worker-0      us-west1-c  n1-standard-1               10.240.0.20  XXX.XXX.XXX.XX  RUNNING
worker-1      us-west1-c  n1-standard-1               10.240.0.21  XX.XXX.XX.XXX   RUNNING
worker-2      us-west1-c  n1-standard-1               10.240.0.22  XXX.XXX.XX.XX   RUNNING

Configuring SSH Access

SSH経由でコントローラーとワーカーのインスタンスを設定します。

初めてインスタンスに接続するときに、インスタンスへの接続のドキュメントにある通り、SSHキーが自動的に生成されプロジェクトまたはインスタンスのメタデータに保存されます。

試しに controller-0 のインスタンスにアクセスしてみましょう。

gcloud compute ssh controller-0

初回のアクセス時にSSHの鍵が生成されます。

パスフレーズをプロンプト経由で入力してください。

WARNING: The public SSH key file for gcloud does not exist.
WARNING: The private SSH key file for gcloud does not exist.
WARNING: You do not have an SSH key for gcloud.
WARNING: SSH keygen will be executed to generate a key.
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:

このタイミングでSSHの鍵が生成されて、プロジェクトにアップロードされ保存されます。

Your identification has been saved in /home/$USER/.ssh/google_compute_engine.
Your public key has been saved in /home/$USER/.ssh/google_compute_engine.pub.
The key fingerprint is:
SHA256:nz1i8jHmgQuGt+WscqP5SeIaSy5wyIJeL71MuV+QruE $USER@$HOSTNAME
The key's randomart image is:
+---[RSA 2048]----+
|                 |
|                 |
|                 |
|        .        |
|o.     oS        |
|=... .o .o o     |
|+.+ =+=.+.X o    |
|.+ ==O*B.B = .   |
| .+.=EB++ o      |
+----[SHA256]-----+
Updating project ssh metadata...-Updated [https://www.googleapis.com/compute/v1/projects/$PROJECT_ID].
Updating project ssh metadata...done.
Waiting for SSH key to propagate.

SSHの鍵のアプロードが完了すれば、controller-0 のインスタンスに入ることができます。

Welcome to Ubuntu 18.04 LTS (GNU/Linux 4.15.0-1006-gcp x86_64)
...
Last login: Sun May 13 14:34:27 2018 from XX.XXX.XXX.XX

ここまでできたら、インスタンスからexitしておきましょう。

このラボでは、CloudFlareのPKIツールキットである cfssl を使ってPKI基盤をプロビジョニングします。

そして、PKI基盤を利用して認証局を作り、etcd、kube-apiserver、kube-controller-manager、kube-scheduler、 kubelet、kube-proxyのコンポーネントのTLS証明書を生成します。

証明局(CA)

このステップでは、TLS証明書を生成するための認証局(CA)をプロビジョニングします。

まずは、CA設定ファイル、証明書、および秘密鍵を生成します。

{

cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "kubernetes": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "8760h"
      }
    }
  }
}
EOF

cat > ca-csr.json <<EOF
{
  "CN": "Kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "CA",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

}

生成物

ca-key.pem
ca.pem

クライアントとサーバーの証明書発行

このステップでは、各Kubernetesコンポーネントで使うクライアント証明書とサーバー証明書、Kubernetes admin ユーザー用のクライアント証明書を発行します。

Admin用のクライアント証明書

まずは、 admin 用のクライアント証明書と秘密鍵を生成します。

{

cat > admin-csr.json <<EOF
{
  "CN": "admin",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:masters",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  admin-csr.json | cfssljson -bare admin

}

生成物

admin-key.pem
admin.pem

Kubeletのクライアント証明書

Kubernetesは、Node Authorizerと呼ばれる特別な用途向けの認可モードを利用します。

これは特にKubeletからのAPIリクエストの認証を行います。

Node Authorizerの認可のためには、Kubeletは、system:nodes groupの中の system:node:<nodeName> というユーザー名で認証されるように証明書を作成しなければなりません。

このステップでは、KubernetesワーカーノードごとにNode Authorizerの要求を満たす証明書と秘密鍵を発行します。

for instance in worker-0 worker-1 worker-2; do
cat > ${instance}-csr.json <<EOF
{
  "CN": "system:node:${instance}",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:nodes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

EXTERNAL_IP=$(gcloud compute instances describe ${instance} \
  --format 'value(networkInterfaces[0].accessConfigs[0].natIP)')

INTERNAL_IP=$(gcloud compute instances describe ${instance} \
  --format 'value(networkInterfaces[0].networkIP)')

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=${instance},${EXTERNAL_IP},${INTERNAL_IP} \
  -profile=kubernetes \
  ${instance}-csr.json | cfssljson -bare ${instance}
done

生成物

worker-0-key.pem
worker-0.pem
worker-1-key.pem
worker-1.pem
worker-2-key.pem
worker-2.pem

kube-contorller-manager クライアントの証明書

kube-controller-manager クライアントの証明書と秘密鍵を発行します。

{

cat > kube-controller-manager-csr.json <<EOF
{
  "CN": "system:kube-controller-manager",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:kube-controller-manager",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager

}

生成物

kube-controller-manager-key.pem
kube-controller-manager.pem

kube-proxy クライアントの証明書

kube-proxy クライアント用に証明書と秘密鍵を発行します。

{

cat > kube-proxy-csr.json <<EOF
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:node-proxier",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-proxy-csr.json | cfssljson -bare kube-proxy

}

生成物

kube-proxy-key.pem
kube-proxy.pem

kube-scheduler クライアント用の証明書

kube-scheduler クライアント用の証明書と秘密鍵を発行します。

{

cat > kube-scheduler-csr.json <<EOF
{
  "CN": "system:kube-scheduler",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:kube-scheduler",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-scheduler-csr.json | cfssljson -bare kube-scheduler

}

生成物

kube-scheduler-key.pem
kube-scheduler.pem

Kubernetes API サーバー用証明書

kubernetes-the-hard-way のstatic IPアドレスは、Kubernetes API サーバーの証明書のSAN(サブジェクトの別名)のリストに含める必要があります。

これにより、外部のクライアントでも証明書を使った検証を行います。

Kubernetes API サーバーの証明書と秘密鍵を生成します。

{

KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
  --region $(gcloud config get-value compute/region) \
  --format 'value(address)')

cat > kubernetes-csr.json <<EOF
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=10.32.0.1,10.240.0.10,10.240.0.11,10.240.0.12,${KUBERNETES_PUBLIC_ADDRESS},127.0.0.1,kubernetes.default \
  -profile=kubernetes \
  kubernetes-csr.json | cfssljson -bare kubernetes

}

生成物

kubernetes-key.pem
kubernetes.pem

サービスアカウントのキーペア

サービスアカウントの管理のドキュメントにあるように、Kubernetes Controller Managerは、サービスアカウントのトークンの生成と署名をするためにキーペアが必要になります。

service-account の証明書と秘密鍵を発行します。

{

cat > service-account-csr.json <<EOF
{
  "CN": "service-accounts",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  service-account-csr.json | cfssljson -bare service-account

}

生成物

service-account-key.pem
service-account.pem

クライアントとサーバーの証明書の配置

証明書と秘密鍵を各ワーカーインスタンスに置きます。

for instance in worker-0 worker-1 worker-2; do
  gcloud compute scp ca.pem ${instance}-key.pem ${instance}.pem ${instance}:~/
done

コントローラーインスタンスにも証明書と秘密鍵も置きます。

for instance in controller-0 controller-1 controller-2; do
  gcloud compute scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
    service-account-key.pem service-account.pem ${instance}:~/
done

ふーーー、やっとKubernetesぽくなってきたかな、、

このステップでは、Kubernetes APIサーバーがKubernetesのクライアントを認証できるようにするためのkubeconfigs(Kubernetes構成ファイル)を生成します。

クライアントの認証設定

まずは、conttoller-managerkubeletkube-proxyscheduleradmin ユーザー用のkubeconfigファイルを生成します。

KubernetesのPublic IPアドレス

kubeconfigは、Kubernetes APIサーバーと接続するために必要です。

高可用性をサポートするために、Kubernetes APIサーバーの前段にある外部ロードバランサーに割り当てるためにこのIPアドレスを使います。

kubernetes-the-hard-way のstatic IPアドレスを探して設定します。

KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
  --region $(gcloud config get-value compute/region) \
  --format 'value(address)')

kubeletのkubeconfigsの生成

kubelet用のkubeconfigファイルを生成するときは、kubeletのノード名と同じクライアント証明書を使用する必要があります。

そうすると、kubeletがKubernetesのNode Authorizerによって認可されるようになります。

ワーカーノード毎にkubeconfigを生成します。

for instance in worker-0 worker-1 worker-2; do
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \
    --kubeconfig=${instance}.kubeconfig

  kubectl config set-credentials system:node:${instance} \
    --client-certificate=${instance}.pem \
    --client-key=${instance}-key.pem \
    --embed-certs=true \
    --kubeconfig=${instance}.kubeconfig

  kubectl config set-context default \
    --cluster=kubernetes-the-hard-way \
    --user=system:node:${instance} \
    --kubeconfig=${instance}.kubeconfig

  kubectl config use-context default --kubeconfig=${instance}.kubeconfig
done

生成物

worker-0.kubeconfig
worker-1.kubeconfig
worker-2.kubeconfig

kube-proxyのkubeconfigの生成

kube-proxyのkubeconfigも生成します。

{
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \
    --kubeconfig=kube-proxy.kubeconfig

  kubectl config set-credentials system:kube-proxy \
    --client-certificate=kube-proxy.pem \
    --client-key=kube-proxy-key.pem \
    --embed-certs=true \
    --kubeconfig=kube-proxy.kubeconfig

  kubectl config set-context default \
    --cluster=kubernetes-the-hard-way \
    --user=system:kube-proxy \
    --kubeconfig=kube-proxy.kubeconfig

  kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
}

生成物

kube-proxy.kubeconfig

kube-controller-managerのkubeconfig

ここまでくると、わかってきますね。

kube-controller-managerのkubeconfigを生成します。

{
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://127.0.0.1:6443 \
    --kubeconfig=kube-controller-manager.kubeconfig

  kubectl config set-credentials system:kube-controller-manager \
    --client-certificate=kube-controller-manager.pem \
    --client-key=kube-controller-manager-key.pem \
    --embed-certs=true \
    --kubeconfig=kube-controller-manager.kubeconfig

  kubectl config set-context default \
    --cluster=kubernetes-the-hard-way \
    --user=system:kube-controller-manager \
    --kubeconfig=kube-controller-manager.kubeconfig

  kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig
}

生成物

kube-controller-manager.kubeconfig

kube-schedulerのkubeconfig

kube-schedulerのkubeconfigを生成します。

{
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://127.0.0.1:6443 \
    --kubeconfig=kube-scheduler.kubeconfig

  kubectl config set-credentials system:kube-scheduler \
    --client-certificate=kube-scheduler.pem \
    --client-key=kube-scheduler-key.pem \
    --embed-certs=true \
    --kubeconfig=kube-scheduler.kubeconfig

  kubectl config set-context default \
    --cluster=kubernetes-the-hard-way \
    --user=system:kube-scheduler \
    --kubeconfig=kube-scheduler.kubeconfig

  kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig
}

生成物

kube-scheduler.kubeconfig

adminユーザー用のkubeconfig

adminユーザーのkubeconfigを生成します。

{
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://127.0.0.1:6443 \
    --kubeconfig=admin.kubeconfig

  kubectl config set-credentials admin \
    --client-certificate=admin.pem \
    --client-key=admin-key.pem \
    --embed-certs=true \
    --kubeconfig=admin.kubeconfig

  kubectl config set-context default \
    --cluster=kubernetes-the-hard-way \
    --user=admin \
    --kubeconfig=admin.kubeconfig

  kubectl config use-context default --kubeconfig=admin.kubeconfig
}

生成物

admin.kubeconfig

kubeconfigの配布

kubeletkube-proxyのkubecnofigは各ワーカーノードにコピーします

for instance in worker-0 worker-1 worker-2; do
  gcloud compute scp ${instance}.kubeconfig kube-proxy.kubeconfig ${instance}:~/
done

kube-controller-managerkube-schedulerのkubeconfigは各コントローラーインスタンスにコピーします。

for instance in controller-0 controller-1 controller-2; do
  gcloud compute scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig ${instance}:~/
done

Kubernetesは、クラスタの状態、アプリケーションの設定、秘匿情報などを含むさまざまなデータが格納されます。

Kubernetesは、クラスタ内で保持しているデータを暗号化する機能が提供されています。

このステップでは、Kubernetes Secretsの暗号化に合わせた暗号化鍵と暗号化の設定を生成します。

暗号化鍵

暗号化鍵を生成します。

ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)

暗号化の設定ファイル

暗号化の設定のためのencryption-config.yamlを生成します。(やっとkubenretesマニフェストがでてきたんじゃないか?)

cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: ${ENCRYPTION_KEY}
      - identity: {}
EOF

このencryption-config.yamlを各コントローラーインスタンスにコピーします。

for instance in controller-0 controller-1 controller-2; do
  gcloud compute scp encryption-config.yaml ${instance}:~/
done

Kubernetesの各コンポーネントはステートレスになっており、クラスタの状態etcdに格納され管理されています。(つまりetcdが超重要)

このステップでは、3ノードのetcdクラスタを構築して、高可用性と安全な外部アクセスに対応します。

準備

このステップのコマンドは、controller-0controller-1controller-2の各コントローラインスタンスで実行する必要があります。

gcloudコマンド経由で各コントローラーインスタンスにログインをしてください。

gcloud compute ssh controller-0

tmuxを使って並列にコマンドを走らせる

tmuxを使えば、同時に複数のインスタンスにコマンド実行ができます。

こっちをみてみてね。

etcdのクラスタメンバーの起動

ここからの手順は各コントローラーインスタンスで行います。

etcdのダウンロードとインストール

etcdのバイナリをgithubからDLします。

wget -q --show-progress --https-only --timestamping \
  "https://github.com/coreos/etcd/releases/download/v3.3.9/etcd-v3.3.9-linux-amd64.tar.gz"

DLしたファイルを展開してetcdサーバーと etcdctlコマンドラインユーティリティを取り出します。

{
  tar -xvf etcd-v3.3.9-linux-amd64.tar.gz
  sudo mv etcd-v3.3.9-linux-amd64/etcd* /usr/local/bin/
}

etcdサーバーの設定

{
  sudo mkdir -p /etc/etcd /var/lib/etcd
  sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/
}

インスタンスの内部IPアドレスは、クライアントのリクエストを受け付けて、etcdクラスタ間で通信するために使います。

現在のGCEインスタンスの内部IPアドレスを取得します。

INTERNAL_IP=$(curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip)

各etcdのメンバーは、etcdクラスター内で名前はユニークにする必要があります。

現在のGCEインスタンスのホスト名でetcdの名前を設定します。

ETCD_NAME=$(hostname -s)

etcd.serviceとしてsystemdのユニットファイルを作成します。

cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos

[Service]
ExecStart=/usr/local/bin/etcd \\
  --name ${ETCD_NAME} \\
  --cert-file=/etc/etcd/kubernetes.pem \\
  --key-file=/etc/etcd/kubernetes-key.pem \\
  --peer-cert-file=/etc/etcd/kubernetes.pem \\
  --peer-key-file=/etc/etcd/kubernetes-key.pem \\
  --trusted-ca-file=/etc/etcd/ca.pem \\
  --peer-trusted-ca-file=/etc/etcd/ca.pem \\
  --peer-client-cert-auth \\
  --client-cert-auth \\
  --initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \\
  --listen-peer-urls https://${INTERNAL_IP}:2380 \\
  --listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\
  --advertise-client-urls https://${INTERNAL_IP}:2379 \\
  --initial-cluster-token etcd-cluster-0 \\
  --initial-cluster controller-0=https://10.240.0.10:2380,controller-1=https://10.240.0.11:2380,controller-2=https://10.240.0.12:2380 \\
  --initial-cluster-state new \\
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

etcdサーバーの起動

{
  sudo systemctl daemon-reload
  sudo systemctl enable etcd
  sudo systemctl start etcd
}

確認

etcdのクラスタの確認しておきます。

sudo ETCDCTL_API=3 etcdctl member list \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/etcd/ca.pem \
  --cert=/etc/etcd/kubernetes.pem \
  --key=/etc/etcd/kubernetes-key.pem

3a57933972cb5131, started, controller-2, https://10.240.0.12:2380, https://10.240.0.12:2379
f98dc20bce6225a0, started, controller-0, https://10.240.0.10:2380, https://10.240.0.10:2379
ffed16798470cab5, started, controller-1, https://10.240.0.11:2380, https://10.240.0.11:2379

このステップでは、3つのコンピューティングインスタンスを使って可用性の高いKubernetesコントロールプレーンを作ります。

合わせて、Kubernetes API Serverを外部クライアントに公開する外部ロードバランサーも作成します。

各ノードに、Kubernetes API Server、Scheduler、およびController Managerのコンポーネントをインストールします。

準備

前のステップと同じくこのステップでも、controller-0controller-1controller-2の各コントローラインスタンスで実行する必要があります。

gcloudコマンド経由で各コントローラーインスタンスにログインをしてください。

gcloud compute ssh controller-0

tmuxを使って並列にコマンドを走らせる

tmuxを使えば、同時に複数のインスタンスにコマンド実行ができます。

こっちをみてみてね。

Kubernetesコントロールプレーンのプロビジョニング

Kubernetesの設定をおくディレクトリを作成します。

sudo mkdir -p /etc/kubernetes/config

KubernetesコントローラーのバイナリのDLとインストール

Kubernetesの公式のリリースバイナリをDLします

wget -q --show-progress --https-only --timestamping \
  "https://storage.googleapis.com/kubernetes-release/release/v1.12.0/bin/linux/amd64/kube-apiserver" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.12.0/bin/linux/amd64/kube-controller-manager" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.12.0/bin/linux/amd64/kube-scheduler" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.12.0/bin/linux/amd64/kubectl"

DLしたバイナリをインストールします。

{
  chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
  sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
}

KubernetesAPIサーバーの設定

{
  sudo mkdir -p /var/lib/kubernetes/

  sudo mv ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
    service-account-key.pem service-account.pem \
    encryption-config.yaml /var/lib/kubernetes/
}

APIサーバーをクラスターのメンバーに知らせるための設定として、インスタンスの内部IPアドレスを使います。

現在のGCEインスタンスの内部IPアドレスを取得します。

INTERNAL_IP=$(curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip)

kube-apiserver.serviceのsystemdのユニットファイルを生成します。

cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
  --advertise-address=${INTERNAL_IP} \\
  --allow-privileged=true \\
  --apiserver-count=3 \\
  --audit-log-maxage=30 \\
  --audit-log-maxbackup=3 \\
  --audit-log-maxsize=100 \\
  --audit-log-path=/var/log/audit.log \\
  --authorization-mode=Node,RBAC \\
  --bind-address=0.0.0.0 \\
  --client-ca-file=/var/lib/kubernetes/ca.pem \\
  --enable-admission-plugins=Initializers,NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
  --enable-swagger-ui=true \\
  --etcd-cafile=/var/lib/kubernetes/ca.pem \\
  --etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\
  --etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\
  --etcd-servers=https://10.240.0.10:2379,https://10.240.0.11:2379,https://10.240.0.12:2379 \\
  --event-ttl=1h \\
  --experimental-encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
  --kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\
  --kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \\
  --kubelet-https=true \\
  --runtime-config=api/all \\
  --service-account-key-file=/var/lib/kubernetes/service-account.pem \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --service-node-port-range=30000-32767 \\
  --tls-cert-file=/var/lib/kubernetes/kubernetes.pem \\
  --tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

参考 : https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/

Kubernetesのコントローラーマネージャーの設定

kube-controller-managerのkubeconfigを移動させます。

sudo mv kube-controller-manager.kubeconfig /var/lib/kubernetes/

kube-controller-manager.serviceのsystemdユニットファイルを生成します。

cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \\
  --address=0.0.0.0 \\
  --cluster-cidr=10.200.0.0/16 \\
  --cluster-name=kubernetes \\
  --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \\
  --cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \\
  --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
  --leader-elect=true \\
  --root-ca-file=/var/lib/kubernetes/ca.pem \\
  --service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --use-service-account-credentials=true \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

Kubernetesのschedulerの設定

kube-schedulerのkubeconfigを移動させます。

sudo mv kube-scheduler.kubeconfig /var/lib/kubernetes/

kube-scheduler.yamlを作ります。

cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml
apiVersion: componentconfig/v1alpha1
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
leaderElection:
  leaderElect: true
EOF

kube-scheduler.serviceのsystemdユニットファイルを生成します。

cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
  --config=/etc/kubernetes/config/kube-scheduler.yaml \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

コントローラーサービス群の起動

{
  sudo systemctl daemon-reload
  sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
  sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler
}

HTTPヘルスチェックを有効にする

GCLBを使って3つのKubernetesAPIサーバーにトラフィックを分散させ、TSL接続をできるようにします。

GCLBはHTTPヘルスチェックのみをサポートしており、APIサーバーによって公開されているHTTPSエンドポイントはつかえません。

回避する方法として、nginx Webサーバーを使用してHTTPヘルスチェックのりクスエストをプロキシさせます。

このステップでは、nginxは80番ポートでHTTPヘルスチェックのリクエストをうけて、https://127.0.0.1:6443/healthz のKubernetesAPIサーバーへプロキシするようにします。

HTTPヘルスチェックをハンドリングするためにnginxをインストールして起動します。

sudo apt-get install -y nginx
cat > kubernetes.default.svc.cluster.local <<EOF
server {
  listen      80;
  server_name kubernetes.default.svc.cluster.local;

  location /healthz {
     proxy_pass                    https://127.0.0.1:6443/healthz;
     proxy_ssl_trusted_certificate /var/lib/kubernetes/ca.pem;
  }
}
EOF
{
  sudo mv kubernetes.default.svc.cluster.local \
    /etc/nginx/sites-available/kubernetes.default.svc.cluster.local

  sudo ln -s /etc/nginx/sites-available/kubernetes.default.svc.cluster.local /etc/nginx/sites-enabled/
}
sudo systemctl restart nginx
sudo systemctl enable nginx

確認

kubectl get componentstatuses --kubeconfig admin.kubeconfig
NAME                 STATUS    MESSAGE              ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-2               Healthy   {"health": "true"}
etcd-0               Healthy   {"health": "true"}
etcd-1               Healthy   {"health": "true"}

nginxのHTTPヘルスチェックproxyのチェック

curl -H "Host: kubernetes.default.svc.cluster.local" -i http://127.0.0.1/healthz
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sun, 30 Sep 2018 17:44:24 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive

ok

kubelet認可のRBAC

このステップでは、Kubernetes APIサーバーが各ワーカーノードのKubelet APIにアクセスできるようにRBACによるアクセス許可を設定します。

メトリックやログの取得、Pod内でのコマンドの実行には、Kubelet APIへのアクセスが必要です。

まずはインスタンスにアクセスします。

gcloud compute ssh controller-0

kube-apiserver-to-kubeletという名前でClusterRoleを作ります。

このロールに、Kubelet APIにアクセスしてポッドの管理に関連するタスクを実行する権限を付与します。

cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
rules:
  - apiGroups:
      - ""
    resources:
      - nodes/proxy
      - nodes/stats
      - nodes/log
      - nodes/spec
      - nodes/metrics
    verbs:
      - "*"
EOF

Kubernetes APIサーバーは、 --kubelet-client-certificateフラグで定義したクライアント証明書を使って、kubernetesユーザーとしてKubeletに対して認証を行います。

system:kube-apiserver-to-kubeletのClusterRoleをkubernetesユーザーにバインドします。

cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: system:kube-apiserver
  namespace: ""
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: kubernetes
EOF

Kubernetesの前段のLB

このステップでは、Kubernetes APIサーバーの前段におく外部ロードバランサーをプロビジョニングします。

kubernetes-the-hard-wayの静的IPアドレスがこのロードバランサーにアタッチされます。

ネットワークロードバランサーのプロビジョニング

外部ロードバランサーのネットワークリソースを作ります。

{
  KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
    --region $(gcloud config get-value compute/region) \
    --format 'value(address)')

  gcloud compute http-health-checks create kubernetes \
    --description "Kubernetes Health Check" \
    --host "kubernetes.default.svc.cluster.local" \
    --request-path "/healthz"

  gcloud compute firewall-rules create kubernetes-the-hard-way-allow-health-check \
    --network kubernetes-the-hard-way \
    --source-ranges 209.85.152.0/22,209.85.204.0/22,35.191.0.0/16 \
    --allow tcp

  gcloud compute target-pools create kubernetes-target-pool \
    --http-health-check kubernetes

  gcloud compute target-pools add-instances kubernetes-target-pool \
   --instances controller-0,controller-1,controller-2

  gcloud compute forwarding-rules create kubernetes-forwarding-rule \
    --address ${KUBERNETES_PUBLIC_ADDRESS} \
    --ports 6443 \
    --region $(gcloud config get-value compute/region) \
    --target-pool kubernetes-target-pool
}

確認

kubernetes-the-hard-wayのstatic IPアドレスを取得します

KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
  --region $(gcloud config get-value compute/region) \
  --format 'value(address)')

Kubrenetesのバージョン情報を取得するHTTPリクエストを飛ばしてみます

curl --cacert ca.pem https://${KUBERNETES_PUBLIC_ADDRESS}:6443/version

{
  "major": "1",
  "minor": "12",
  "gitVersion": "v1.12.0",
  "gitCommit": "0ed33881dc4355495f623c6f22e7dd0b7632b7c0",
  "gitTreeState": "clean",
  "buildDate": "2018-09-27T16:55:41Z",
  "goVersion": "go1.10.4",
  "compiler": "gc",
  "platform": "linux/amd64"
}

このステップでは、3つのKubernetesワーカーノードをブートストラップします。

下記のコンポーネントを各ノードにインストールします。

runcgVisorcontainer networking plugincontainerdkubeletkube-proxy

準備

このステップのコマンドは、worker-0worker-1worker-2の各ワーカーノードで実行する必要があります。

gcloudコマンド経由で各コントローラーインスタンスにログインをしてください。

gcloud compute ssh worker-0

tmuxを使って並列にコマンドを走らせる

tmuxを使えば、同時に複数のインスタンスにコマンド実行ができます。

こっちをみてみてね。

Kubernetesのワーカーノードのプロビジョング

使うライブラリをインストールします。

{
  sudo apt-get update
  sudo apt-get -y install socat conntrack ipset
}

ワーカーのバイナリをDLしてインストール

wget -q --show-progress --https-only --timestamping \
  https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.12.0/crictl-v1.12.0-linux-amd64.tar.gz \
  https://storage.googleapis.com/kubernetes-the-hard-way/runsc-50c283b9f56bb7200938d9e207355f05f79f0d17 \
  https://github.com/opencontainers/runc/releases/download/v1.0.0-rc5/runc.amd64 \
  https://github.com/containernetworking/plugins/releases/download/v0.6.0/cni-plugins-amd64-v0.6.0.tgz \
  https://github.com/containerd/containerd/releases/download/v1.2.0-rc.0/containerd-1.2.0-rc.0.linux-amd64.tar.gz \
  https://storage.googleapis.com/kubernetes-release/release/v1.12.0/bin/linux/amd64/kubectl \
  https://storage.googleapis.com/kubernetes-release/release/v1.12.0/bin/linux/amd64/kube-proxy \
  https://storage.googleapis.com/kubernetes-release/release/v1.12.0/bin/linux/amd64/kubelet

インストールする先のディレクトリを作ります。

sudo mkdir -p \
  /etc/cni/net.d \
  /opt/cni/bin \
  /var/lib/kubelet \
  /var/lib/kube-proxy \
  /var/lib/kubernetes \
  /var/run/kubernetes

ワーカーのバイナリをインストールします。

{
  sudo mv runsc-50c283b9f56bb7200938d9e207355f05f79f0d17 runsc
  sudo mv runc.amd64 runc
  chmod +x kubectl kube-proxy kubelet runc runsc
  sudo mv kubectl kube-proxy kubelet runc runsc /usr/local/bin/
  sudo tar -xvf crictl-v1.12.0-linux-amd64.tar.gz -C /usr/local/bin/
  sudo tar -xvf cni-plugins-amd64-v0.6.0.tgz -C /opt/cni/bin/
  sudo tar -xvf containerd-1.2.0-rc.0.linux-amd64.tar.gz -C /
}

CNIネットワーキングの設定

現在のGCEインスタンスのPodのCIDR範囲を取得します。

POD_CIDR=$(curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/attributes/pod-cidr)

bridgeネットワークの設定ファイルを作ります。

cat <<EOF | sudo tee /etc/cni/net.d/10-bridge.conf
{
    "cniVersion": "0.3.1",
    "name": "bridge",
    "type": "bridge",
    "bridge": "cnio0",
    "isGateway": true,
    "ipMasq": true,
    "ipam": {
        "type": "host-local",
        "ranges": [
          [{"subnet": "${POD_CIDR}"}]
        ],
        "routes": [{"dst": "0.0.0.0/0"}]
    }
}
EOF

loopbackネットワークの設定ファイルを作ります。

cat <<EOF | sudo tee /etc/cni/net.d/99-loopback.conf
{
    "cniVersion": "0.3.1",
    "type": "loopback"
}
EOF

containerdの設定

containerdの設定ファイルを作ります。

sudo mkdir -p /etc/containerd/
cat << EOF | sudo tee /etc/containerd/config.toml
[plugins]
  [plugins.cri.containerd]
    snapshotter = "overlayfs"
    [plugins.cri.containerd.default_runtime]
      runtime_type = "io.containerd.runtime.v1.linux"
      runtime_engine = "/usr/local/bin/runc"
      runtime_root = ""
    [plugins.cri.containerd.untrusted_workload_runtime]
      runtime_type = "io.containerd.runtime.v1.linux"
      runtime_engine = "/usr/local/bin/runsc"
      runtime_root = "/run/containerd/runsc"
    [plugins.cri.containerd.gvisor]
      runtime_type = "io.containerd.runtime.v1.linux"
      runtime_engine = "/usr/local/bin/runsc"
      runtime_root = "/run/containerd/runsc"
EOF

containerd.service systemdのユニットファイルを作ります。

cat <<EOF | sudo tee /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target

[Service]
ExecStartPre=/sbin/modprobe overlay
ExecStart=/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity

[Install]
WantedBy=multi-user.target
EOF

Kubeletの設定

{
  sudo mv ${HOSTNAME}-key.pem ${HOSTNAME}.pem /var/lib/kubelet/
  sudo mv ${HOSTNAME}.kubeconfig /var/lib/kubelet/kubeconfig
  sudo mv ca.pem /var/lib/kubernetes/
}

kubelet-config.yaml 設定ファイルを作ります。

cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: "/var/lib/kubernetes/ca.pem"
authorization:
  mode: Webhook
clusterDomain: "cluster.local"
clusterDNS:
  - "10.32.0.10"
podCIDR: "${POD_CIDR}"
resolvConf: "/run/systemd/resolve/resolv.conf"
runtimeRequestTimeout: "15m"
tlsCertFile: "/var/lib/kubelet/${HOSTNAME}.pem"
tlsPrivateKeyFile: "/var/lib/kubelet/${HOSTNAME}-key.pem"
EOF

kubelet.service systemdユニットファイルを作ります。

cat <<EOF | sudo tee /etc/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=containerd.service
Requires=containerd.service

[Service]
ExecStart=/usr/local/bin/kubelet \\
  --config=/var/lib/kubelet/kubelet-config.yaml \\
  --container-runtime=remote \\
  --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \\
  --image-pull-progress-deadline=2m \\
  --kubeconfig=/var/lib/kubelet/kubeconfig \\
  --network-plugin=cni \\
  --register-node=true \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

Kubernetes Proxyの設定

sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig

kube-proxy-config.yaml 設定ファイルを作ります。

cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
clientConnection:
  kubeconfig: "/var/lib/kube-proxy/kubeconfig"
mode: "iptables"
clusterCIDR: "10.200.0.0/16"
EOF

kube-proxy.service systemdユニットファイルを作ります。

cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-proxy \\
  --config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

ワーカーのサービス群の起動

{
  sudo systemctl daemon-reload
  sudo systemctl enable containerd kubelet kube-proxy
  sudo systemctl start containerd kubelet kube-proxy
}

確認

登録されているKubernetesノードの一覧を表示させます。

gcloud compute ssh controller-0 \
  --command "kubectl get nodes --kubeconfig admin.kubeconfig"

NAME       STATUS   ROLES    AGE   VERSION
worker-0   Ready    <none>   35s   v1.12.0
worker-1   Ready    <none>   36s   v1.12.0
worker-2   Ready    <none>   36s   v1.12.0

このステップでは、adminユーザーのcredenialに基づいた、kubectlコマンドラインユーティリティ用のkubeconfigファイルを生成します。

Admin Kubernetes設定ファイル

各kubeconfigは、Kubernetes APIサーバーと接続できる必要があります。

高可用性をサポートするために、Kubernetes APIサーバーの前段にある外部ロードバランサーに割り当てられたIPアドレスが使われています。

adminユーザーとして認証するのに適したkubeconfigファイルを生成します。

{
  KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
    --region $(gcloud config get-value compute/region) \
    --format 'value(address)')

  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443

  kubectl config set-credentials admin \
    --client-certificate=admin.pem \
    --client-key=admin-key.pem

  kubectl config set-context kubernetes-the-hard-way \
    --cluster=kubernetes-the-hard-way \
    --user=admin

  kubectl config use-context kubernetes-the-hard-way
}

確認

remoteのKubernetesクラスターのヘルスチェックを確認します。

kubectl get componentstatuses

NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-1               Healthy   {"health":"true"}
etcd-2               Healthy   {"health":"true"}
etcd-0               Healthy   {"health":"true"}

remoteのKubernetesクラスタのノードの一覧を取得します。

kubectl get nodes

NAME       STATUS   ROLES    AGE    VERSION
worker-0   Ready    <none>   117s   v1.12.0
worker-1   Ready    <none>   118s   v1.12.0
worker-2   Ready    <none>   118s   v1.12.0

11. Podのネットワークルートのプロビジョニング

ノードにスケジュールされたPodは、ノードのPodのCIDR範囲からIPアドレスを受け取ります。

この時点では、ネットワークルートが見つからないため、Podは異なるノードで実行されている他のPodと通信できません。

このステップでは、ノードのPod CIDR範囲をノードの内部IPアドレスにマップするための、各ワーカーノードのルートを作成します。

ルーティングテーブル

このセクションでは、kubernetes-the-hard-way VPCネットワーク内でルートを作るために必要な情報を集めます。

各ワーカーインスタンスの内部IPアドレスとPod CIDR範囲を表示させます。

for instance in worker-0 worker-1 worker-2; do
  gcloud compute instances describe ${instance} \
    --format 'value[separator=" "](networkInterfaces[0].networkIP,metadata.items[0].value)'
done

10.240.0.20 10.200.0.0/24
10.240.0.21 10.200.1.0/24
10.240.0.22 10.200.2.0/24

ルート群

各ワーカーインスタンスにネットワークルートを設定します。

for i in 0 1 2; do
  gcloud compute routes create kubernetes-route-10-200-${i}-0-24 \
    --network kubernetes-the-hard-way \
    --next-hop-address 10.240.0.2${i} \
    --destination-range 10.200.${i}.0/24
done

kubernetes-the-hard-wayのVPCネットワークの中のルートの一覧を出します。

gcloud compute routes list --filter "network: kubernetes-the-hard-way"

NAME                            NETWORK                  DEST_RANGE     NEXT_HOP                  PRIORITY
default-route-081879136902de56  kubernetes-the-hard-way  10.240.0.0/24  kubernetes-the-hard-way   1000
default-route-55199a5aa126d7aa  kubernetes-the-hard-way  0.0.0.0/0      default-internet-gateway  1000
kubernetes-route-10-200-0-0-24  kubernetes-the-hard-way  10.200.0.0/24  10.240.0.20               1000
kubernetes-route-10-200-1-0-24  kubernetes-the-hard-way  10.200.1.0/24  10.240.0.21               1000
kubernetes-route-10-200-2-0-24  kubernetes-the-hard-way  10.200.2.0/24  10.240.0.22               1000

このステップでは、Kubernetesクラスタ内で動いているアプリケーションに、CoreDNSを使ったDNSベースのサービスディスカバリを提供するDNSアドオンをデプロイします。

DNSクラスターアドオン

corednsクラスターアドオンをデプロイします。

kubectl apply -f https://storage.googleapis.com/kubernetes-the-hard-way/coredns.yaml

output

serviceaccount/coredns created
clusterrole.rbac.authorization.k8s.io/system:coredns created
clusterrolebinding.rbac.authorization.k8s.io/system:coredns created
configmap/coredns created
deployment.extensions/coredns created
service/kube-dns created

kube-dns deploymentによって作られたPodの確認

kubectl get pods -l k8s-app=kube-dns -n kube-system

output

NAME                       READY   STATUS    RESTARTS   AGE
coredns-699f8ddd77-94qv9   1/1     Running   0          20s
coredns-699f8ddd77-gtcgb   1/1     Running   0          20s

確認

busybox deploymentを作ります。

kubectl run busybox --image=busybox:1.28 --command -- sleep 3600

busybox deploymentによって作られてPodを確認します。

kubectl get pods -l run=busybox

output

NAME                      READY   STATUS    RESTARTS   AGE
busybox-bd8fb7cbd-vflm9   1/1     Running   0          10s

busybox Podのフルネームを取得します。

POD_NAME=$(kubectl get pods -l run=busybox -o jsonpath="{.items[0].metadata.name}")

busybox Podの中からkubernetes serviceのDNS lookupを行います。

kubectl exec -ti $POD_NAME -- nslookup kubernetes

output

Server:    10.32.0.10
Address 1: 10.32.0.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes
Address 1: 10.32.0.1 kubernetes.default.svc.cluster.local

このステップでは、Kubernetesクラスタが正しく機能していることを確認するためのタスクを実行します。

データの暗号化

このステップでは保存されているデータの暗号化を確認します。

generic secretを作ります。

kubectl create secret generic kubernetes-the-hard-way \
  --from-literal="mykey=mydata"

etcdに保存されているkubernetes-the-hard-way secretをhexdumpします。

gcloud compute ssh controller-0 \
  --command "sudo ETCDCTL_API=3 etcdctl get \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/etcd/ca.pem \
  --cert=/etc/etcd/kubernetes.pem \
  --key=/etc/etcd/kubernetes-key.pem\
  /registry/secrets/default/kubernetes-the-hard-way | hexdump -C"

output

00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
00000010  73 2f 64 65 66 61 75 6c  74 2f 6b 75 62 65 72 6e  |s/default/kubern|
00000020  65 74 65 73 2d 74 68 65  2d 68 61 72 64 2d 77 61  |etes-the-hard-wa|
00000030  79 0a 6b 38 73 3a 65 6e  63 3a 61 65 73 63 62 63  |y.k8s:enc:aescbc|
00000040  3a 76 31 3a 6b 65 79 31  3a dd 3f 36 6c ce 65 9d  |:v1:key1:.?6l.e.|
00000050  b3 b1 46 1a ba ae a2 1f  e4 fa 13 0c 4b 6e 2c 3c  |..F.........Kn,<|
00000060  15 fa 88 56 84 b7 aa c0  7a ca 66 f3 de db 2b a3  |...V....z.f...+.|
00000070  88 dc b1 b1 d8 2f 16 3e  6b 4a cb ac 88 5d 23 2d  |...../.>kJ...]#-|
00000080  99 62 be 72 9f a5 01 38  15 c4 43 ac 38 5f ef 88  |.b.r...8..C.8_..|
00000090  3b 88 c1 e6 b6 06 4f ae  a8 6b c8 40 70 ac 0a d3  |;.....O..k.@p...|
000000a0  3e dc 2b b6 0f 01 b6 8b  e2 21 29 4d 32 d6 67 a6  |>.+......!)M2.g.|
000000b0  4e 6d bb 61 0d 85 22 ea  f4 d6 2d 0a af 3c 71 85  |Nm.a.."...-..<q.|
000000c0  96 27 c9 ec 90 e3 56 8c  94 a7 1c 9a 0e 00 28 11  |.'....V.......(.|
000000d0  18 28 f4 33 42 d9 57 d9  e3 e9 1c 38 e3 bc 1e c3  |.(.3B.W....8....|
000000e0  d2 47 f3 20 60 be b8 57  a7 0a                    |.G. `..W..|
000000ea

etcdキーは、k8s:enc:aescbc:v1:key1というプレフィックスになっているはずです。

これは、aescbcプロバイダがkey1いう暗号化キーでデータを暗号化したことを表しています。

Deployment

このステップではDeploymentの作成と管理ができているかを確認します。

nginx web serverのdeploymentを作成します。

kubectl run nginx --image=nginx

nginx deploymentによってできたPodを確認します。

kubectl get pods -l run=nginx

output

NAME                    READY   STATUS    RESTARTS   AGE
nginx-dbddb74b8-6lxg2   1/1     Running   0          10s

Port Forwading

このステップでは、port forwadingを使って外部からアプリケーションにアクセスできるかを確認します。

nginx podのフルネームを取得します。

POD_NAME=$(kubectl get pods -l run=nginx -o jsonpath="{.items[0].metadata.name}")

ローカルの8080ポートをnginx Podの80番ポートにフォワードします。

kubectl port-forward $POD_NAME 8080:80

output

Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

別のターミナルからフォワードしたアドレスにHTTPリクエストを投げてみます。

curl --head http://127.0.0.1:8080

output

HTTP/1.1 200 OK
Server: nginx/1.15.4
Date: Sun, 30 Sep 2018 19:23:10 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 25 Sep 2018 15:04:03 GMT
Connection: keep-alive
ETag: "5baa4e63-264"
Accept-Ranges: bytes

もとのターミナルに戻ってnginx Podへのフォワーディングを止めます。

Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
^C

Logs

このステップでは、コンテナのログの取得ができるかを確認します。

nginx Podのログを表示します。

kubectl logs $POD_NAME

output

127.0.0.1 - - [30/Sep/2018:19:23:10 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.58.0" "-"

Exec

このステップではコンテナ内でのコマンド実行ができるかを確認します。

nginxコンテナに入って、nginx -vを実行してnginxのバージョンを表示します。

kubectl exec -ti $POD_NAME -- nginx -v

output

nginx version: nginx/1.15.4

Services

このステップでは、Serviceを使ったアプリケーションの公開ができるかを確認します。

nginx deploymentをNodePortを使って公開します。

kubectl expose deployment nginx --port 80 --type NodePort

nginx serviceでアサインされたノードのポート取得します。

NODE_PORT=$(kubectl get svc nginx \
  --output=jsonpath='{range .spec.ports[0]}{.nodePort}')

nginxノードポートに外部からアクセスできるようにファイヤーウォールのルール追加します。

gcloud compute firewall-rules create kubernetes-the-hard-way-allow-nginx-service \
  --allow=tcp:${NODE_PORT} \
  --network kubernetes-the-hard-way

ワーカーインスタンスから外部IPアドレスを取得します。

EXTERNAL_IP=$(gcloud compute instances describe worker-0 \
  --format 'value(networkInterfaces[0].accessConfigs[0].natIP)')

外部IPアドレスとnginxノードポート組わせてHTTPリクエストを投げてみます。

curl -I http://${EXTERNAL_IP}:${NODE_PORT}

output

HTTP/1.1 200 OK
Server: nginx/1.15.4
Date: Sun, 30 Sep 2018 19:25:40 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 25 Sep 2018 15:04:03 GMT
Connection: keep-alive
ETag: "5baa4e63-264"
Accept-Ranges: bytes

Untrusted Workloads

gVisorを使って信頼されていないワークロードが動かせるかを確認します。

untrustedPodを作ります。

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: untrusted
  annotations:
    io.kubernetes.cri.untrusted-workload: "true"
spec:
  containers:
    - name: webserver
      image: gcr.io/hightowerlabs/helloworld:2.0.0
EOF

確認

このセクションでは、割り当てられたワーカーノードを調べて、untrustedPodがgVisor(runsc)の下で実行されていることを確認します。

untrustedPodが実行されていることを確認します。

kubectl get pods -o wide
NAME                       READY     STATUS    RESTARTS   AGE       IP           NODE
busybox-68654f944b-djjjb   1/1       Running   0          5m        10.200.0.2   worker-0
nginx-65899c769f-xkfcn     1/1       Running   0          4m        10.200.1.2   worker-1
untrusted                  1/1       Running   0          10s       10.200.0.3   worker-0

untrustedPodが実行されているノードの名前を取得します。

INSTANCE_NAME=$(kubectl get pod untrusted --output=jsonpath='{.spec.nodeName}')

ワーカーノードのsshでアクセスします。

gcloud compute ssh ${INSTANCE_NAME}

gVisorのもとで動いているコンテナの一覧を取得します。

sudo runsc --root  /run/containerd/runsc/k8s.io list
I0930 19:27:13.255142   20832 x:0] ***************************
I0930 19:27:13.255326   20832 x:0] Args: [runsc --root /run/containerd/runsc/k8s.io list]
I0930 19:27:13.255386   20832 x:0] Git Revision: 50c283b9f56bb7200938d9e207355f05f79f0d17
I0930 19:27:13.255429   20832 x:0] PID: 20832
I0930 19:27:13.255472   20832 x:0] UID: 0, GID: 0
I0930 19:27:13.255591   20832 x:0] Configuration:
I0930 19:27:13.255654   20832 x:0]              RootDir: /run/containerd/runsc/k8s.io
I0930 19:27:13.255781   20832 x:0]              Platform: ptrace
I0930 19:27:13.255893   20832 x:0]              FileAccess: exclusive, overlay: false
I0930 19:27:13.256004   20832 x:0]              Network: sandbox, logging: false
I0930 19:27:13.256128   20832 x:0]              Strace: false, max size: 1024, syscalls: []
I0930 19:27:13.256238   20832 x:0] ***************************
ID                                                                 PID         STATUS      BUNDLE                                                                                                                   CREATED                OWNER
79e74d0cec52a1ff4bc2c9b0bb9662f73ea918959c08bca5bcf07ddb6cb0e1fd   20449       running     /run/containerd/io.containerd.runtime.v1.linux/k8s.io/79e74d0cec52a1ff4bc2c9b0bb9662f73ea918959c08bca5bcf07ddb6cb0e1fd   0001-01-01T00:00:00Z
af7470029008a4520b5db9fb5b358c65d64c9f748fae050afb6eaf014a59fea5   20510       running     /run/containerd/io.containerd.runtime.v1.linux/k8s.io/af7470029008a4520b5db9fb5b358c65d64c9f748fae050afb6eaf014a59fea5   0001-01-01T00:00:00Z
I0930 19:27:13.259733   20832 x:0] Exiting with status: 0

untrustedPodのIDを取得します。

POD_ID=$(sudo crictl -r unix:///var/run/containerd/containerd.sock \
  pods --name untrusted -q)

untrustedPodの中で動いてるwebserverコンテナのIDを取得します。

CONTAINER_ID=$(sudo crictl -r unix:///var/run/containerd/containerd.sock \
  ps -p ${POD_ID} -q)

gVisorのrunscコマンドを使って、webserverコンテナの中で走っているプロセスを表示します。

sudo runsc --root /run/containerd/runsc/k8s.io ps ${CONTAINER_ID}

output

I0930 19:31:31.419765   21217 x:0] ***************************
I0930 19:31:31.419907   21217 x:0] Args: [runsc --root /run/containerd/runsc/k8s.io ps af7470029008a4520b5db9fb5b358c65d64c9f748fae050afb6eaf014a59fea5]
I0930 19:31:31.419959   21217 x:0] Git Revision: 50c283b9f56bb7200938d9e207355f05f79f0d17
I0930 19:31:31.420000   21217 x:0] PID: 21217
I0930 19:31:31.420041   21217 x:0] UID: 0, GID: 0
I0930 19:31:31.420081   21217 x:0] Configuration:
I0930 19:31:31.420115   21217 x:0]              RootDir: /run/containerd/runsc/k8s.io
I0930 19:31:31.420188   21217 x:0]              Platform: ptrace
I0930 19:31:31.420266   21217 x:0]              FileAccess: exclusive, overlay: false
I0930 19:31:31.420424   21217 x:0]              Network: sandbox, logging: false
I0930 19:31:31.420515   21217 x:0]              Strace: false, max size: 1024, syscalls: []
I0930 19:31:31.420676   21217 x:0] ***************************
UID       PID       PPID      C         STIME     TIME      CMD
0         1         0         0         19:26     10ms      app
I0930 19:31:31.422022   21217 x:0] Exiting with status: 0

このステップでは、今までのステップで作ってきたリソースを削除します。

GCEインスタンス

コントローラーとワーカーのインスタンスを落とします。

gcloud -q compute instances delete \
  controller-0 controller-1 controller-2 \
  worker-0 worker-1 worker-2

Networking

外部ロードバランサーのネットワークリソースを削除します。

{
  gcloud -q compute forwarding-rules delete kubernetes-forwarding-rule \
    --region $(gcloud config get-value compute/region)

  gcloud -q compute target-pools delete kubernetes-target-pool

  gcloud -q compute http-health-checks delete kubernetes

  gcloud -q compute addresses delete kubernetes-the-hard-way
}

kubernetes-the-hard-wayファイヤーウォールルールを削除します。

gcloud -q compute firewall-rules delete \
  kubernetes-the-hard-way-allow-nginx-service \
  kubernetes-the-hard-way-allow-internal \
  kubernetes-the-hard-way-allow-external \
  kubernetes-the-hard-way-allow-health-check

kubernetes-the-hard-wayVPCネットワークを削除します。

{
  gcloud -q compute routes delete \
    kubernetes-route-10-200-0-0-24 \
    kubernetes-route-10-200-1-0-24 \
    kubernetes-route-10-200-2-0-24

  gcloud -q compute networks subnets delete kubernetes

  gcloud -q compute networks delete kubernetes-the-hard-way
}

以上で完了です!

お疲れ様でした!