KubernetesクラスタをGCP上にスクラッチするチュートリアル「Kubernetes-The-Hard-Way」を和訳してくださった方がいらっしゃったので、取り組んでみました。
事前知識(調べたこと)
OCI(Open Cotainer Initiative)
- コンテナのランタイムと、イメージ関連の、オープンな業界標準を作成する為に、2015/6にDocker社など複数の企業によって設立
- 今は、Linux Foundation傘下のOSS団体
CRI(Container Runtime Interface)
- kubeletとコンテナランタイムが通信する為のI/Fを規定したもの
containerd
containerdとは?
- もともとDocker内部で利用され、Docker社が開発していたが、2017/3にCNCFに寄贈されたコンテナランタイム
runC
runCとは?
- OCI仕様に基づいて、コンテナ生成、実行などを行うコンテナランタイム(コンテナを動かす為のプログラム)
Docker(containerd)との違い
- Docker(containerd)は、コンテナの管理などをおこなう
- runCは、コンテナ生成・実行をおこなう
gVisor
gVisorとは
- Google社が開発してOSSとして公開している、準仮想化のような仕組みを用いて安全性を高めたコンテナランタイム
- 従来のコンテナランタイムは、コンテナ間でOSカーネルを共有しているため、コンテナからOSのシステムコールを直接呼び出せる、セキュリティ問題を引き起こしやすい
- これに対して、以下のとおり独自のgVisorレイヤを提供することで、「sandboxed containers」を実現している
これはなに
Introduction
- 特になし
準備
概要
- まずGCPの無料トライアルに登録した
gcloud
がCLI経由でGCPのリソースを操作するツール- Regionはこんな感じ
- Google Compute Engine(
GCE
)がいわゆるEC2
コマンド実行メモ
gloud init
# console上から`kubernetes-the-hard-way`プロジェクトを作成
gcloud config set project kubernetes-the-hard-way-XXXXXX
gcloud config set compute/region asia-northeast1
クライアントツールのインストール
概要
- PKIの簡単な説明を読んだ
- 通信する際に、お互いの身分証明ができれば、正しい相手と通信を行っていると信じることができる
- 誰でも簡単に身分証明が作れたら、信用されない
- 保険証や免許証と同じように、信頼できる期間が発行し、本人が大事に保管している身分証明書だけが有効になる
- この身分証明書を
証明書
と呼び、証明書を発行する期間を認証局
と呼び、認証局が発行した証明書を集中管理して利用者に配布する役割を持つリポジトリ
が呼ぶ- 証明書は単なるファイルである。X.509という規格で決まっている
- 証明書の中には、情報を暗号化する為の
鍵
も含まれている。
実行コマンド
brew install cfssl
利用するGCPリソースのプロビジョニング
概要
- GCPにはProjectという概念がある。AWSで言う所のアカウントみたいなもの
- コンソールから作成して、gcloudでも
gcloud config set project kubernete-tha-hard-way-XXXXX(ID)
で登録 - GCPにおけるネットワーク
- コンソールから作成して、gcloudでも
- Firewall(AWSでいう所のセキュリティグループ)がちょっと概念が違う
- Workerノード作成時に、ノードが使用するPodネットワークのサブネットの値(
pod-cidr
)をGCEのメタデータに登録しておく - クラスタ全体のCIDRはあとで、Controller Managerの
--cluster-cidr
パラメータで指定することになる gcloud compute ssh controller-0
これでsshできる。簡単!
実行コマンド
- VPCを作成
gcloud compute networks create kubernetes-the-hard-way --subnet-mode custom
- サブネットを作成
gcloud compute networks subnets create kubernetes \
--network kubernetes-the-hard-way \
--range 10.240.0.0/24
# [3] asia-northeast1
- Firewallの設定
- 全てのプロトコルの内部通信を許可する
- 外部からのSSH,ICMP,HTTPSを許可する
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
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のAPIサーバの前段の外部LBに付与するIPアドレスを払い出す
gcloud compute addresses create kubernetes-the-hard-way \
--region $(gcloud config get-value compute/region)
- Control Plane用のVMを作成
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
- Worker Node用のVMを作成
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
- sshで接続してみる
gcloud compute ssh controller-0
認証局(CA)の設定とTLS証明書の発行
概要
Kubernetesが使うTLS証明書
- Kubernetesでは、内部の通信やClientとの通信など、様々な場所でTLSの証明書を使っている
- この記事が参考になりそう
- 公式Docにまとまっている
- サーバ証明書とクライアント証明書の違い
Client certificates for the kubelet to authenticate to the API server
-> APIサーバに対して認証する為の、kubeletのクライアント証明書
Server certificate for the API server endpoint
-> APIサーバのエンドポイントの、サーバ証明書
Client certificates for administrators of the cluster to authenticate to the API server
-> APIサーバに対して認証する為の、クラスタの管理者のクライアント証明書
Client certificates for the API server to talk to the kubelets
-> kubeletと通信する為の、APIサーバのクライアント証明書
Client certificate for the API server to talk to etcd
-> etcdと通信する為の、APIサーバのクライアント証明書
Client certificate/kubeconfig for the controller manager to talk to the API server
-> APIサーバと通信する為の、Controller Managerのクライアント証明書とkubeconfig
Client certificate/kubeconfig for the scheduler to talk to the API server.
-> APIサーバと通信する為の、schedulerのクライアント証明書とkubeconfig
Client and server certificates for the front-proxy
-> front-proxyのクライアント証明書及びサーバ証明書
Node Authorization
- Node(Kubelet)の権限管理をより厳密に行う機能
- NodeはそのNodeが関連するオブジェクトのみ権限が制限されるようになる
- 例えば、Nodeは割り当てられたPodが参照するSecret以外にはアクセスできなくなる(Nodeのクレデンシャルが漏洩した時のリスクを最小化できる)
- 権限は以下のように制限される(認可フェーズ→Admission Controlのフェーズ)
- 認可(Node Authorizer)による機能
- そのNodeに紐づくPodが参照しているSecret,ConfigMap,PersistentVolume,PersistentVolumeClaimのみ参照できる
- Admissoon Control(Node Restriction)による機能
- そのNodeに紐づくNodeオブジェクトのみを変更できる
- そのNodeに紐づくPodオブジェクトのみステータスを変更できる
- そのNodeに紐づくStaticなPodオブジェクト(mirror pod)のみを作成・更新できる
- mirror podはSecretなどの他のオブジェクトの参照はできないようになっている
- 認可(Node Authorizer)による機能
- 個々のWorkerNodeからAPI Serverに対してAPIリクエストを認証をする際に、
- Kubeletは
system:nodes
groupの中のsystem:node:<nodeName>
というユーザ名で認証されるように、証明書を作成する必要がある- 簡単に言うと、クライアント証明書のCNを
system:node:${instance}
にする必要がある
- 簡単に言うと、クライアント証明書のCNを
- Kubeletは
Kubernetes APIサーバ用証明書
- k8s-the-hard-wayのstaticIPアドレスは、k8sAPIサーバの証明書のSANのリストに含める必要がある
- SANとは、ひとつの証明書に複数のホスト名を登録することで、異なるFQDNでも1枚のSSLサーバ証明書でSSLの機能を実装できる
- 外部のクライアントでも証明書を使った検証をおこなうため
Service Accountのキーペア
- Kubernetes Controller Managerは、サービスアカウントのトークンの生成と署名をするためにキーペアが必要
コマンド実行メモ
- 認証局(CA)をプロビジョニングする
- CA設定ファイル・証明書・秘密鍵を生成する(ca.pem/ca-key.pem)
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
- Worker Nodeごとに、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
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-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
}
- 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
}
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
}
- Worker Nodeに証明書と秘密鍵を配置
for instance in worker-0 worker-1 worker-2; do
gcloud compute scp ca.pem ${instance}-key.pem ${instance}.pem ${instance}:~/
done
- Control Planeに証明書と秘密鍵を配置
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サーバが、Kubernetsのクライアントを認証出来るようにする為の、kubeconfigを生成する
- k8sの各コンポーネント(controller-manager,kubelet,kube-proxy,scheduler,adminユーザ)は、Kubernetes APIサーバにアクセスする為に、kubeconfigを使用する
- ``kubectl config`のset系には以下の3種類のサブコマンドがある
set-cluster Sets a cluster entry in kubeconfig
set-context Sets a context entry in kubeconfig
set-credentials Sets a user entry in kubeconfig
kubeletのkubeconfigの例
- kubelet -> api serverにアクセスする為のkubeconfigを作成する。
- kubeletのノード名と同じクライアント証明書を使用する必要がある
- そうすると、kubeletがKubernetesのNode Authorizerによって認可されるようになる
- なので、kubelet用のkubeconfigだけノードごとにバラバラに作成する
kubectl config set-cluster
--kubeconfig=${instance}.kubeconfig
- 別のconfigファイルに設定値を書き込む
--embed-certs=true
- クライアント証明書・キーをkubeconfigに埋め込む
--certificate-authority
- CA証明書のファイルパスを指定する
server
- アクセス先のクラスタのURLを指定する
kubeconfigの配布
kubelet
とkube-proxy
のkubeconfigは、各ワーカーノードにコピーするkube-controller-manager
とkube-shceduler
のkubeconfigは、各コントローラインスタンスにコピーする
コマンド実行メモ
- api serverの外部IPアドレスを環境変数にセット
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
--region $(gcloud config get-value compute/region) \
--format 'value(address)')
- kubelet用の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
- 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-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-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
}
- 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
}
- 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は、クラスタの状態、アプリケーションの設定、秘匿情報などを含む様々なデータが格納される
- クラスタ内で保持しているデータを暗号化する機能が提供されている
- 以前はetcdにSecretの情報をplain-textで保存していた
- Node Authorization機能により、Nodeは割当らてたPodが参照するSecret以外にはアクセスできない(参考)
- 1.13からGA
- 以前はetcdにSecretの情報をplain-textで保存していた
- 暗号化の設定をした
暗号化の設定
kube-apiserver
は起動時の--encryption-provider-config
で設定したconfigによりetcd内でどのように暗号化されるかを制御する。- 以下が公式Docの例
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- identity: {}
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
resources.resources
は、暗号化する必要があるKubernetesのリソース名の配列providers
は、考えられる暗号化プロバイダの順序付きリスト- 一つのprovider typeのみがentry毎に指定される
- listの最初のproviderは、storageリソースを暗号化する時に使われる
- storageからリソースを読み込む時、格納されたデータと一致する各providerは順番にデータを復号化しようとする(全て駄目ならエラーを返す)
コマンド実行メモ
- 暗号化に用いるキーを生成
ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
- 暗号化設定のconfigを生成
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
etcdクラスタの起動
- Kubernetesの各コンポーネントはステートレスで、ectdに格納され管理されている
概要
etcd
install/etc/etcd
直下に以下を配置した- ca.pem(ルートCAの証明書)
- kubernetes-key.pem(Kubernetes APIサーバ用の秘密鍵)
- kubernetes.pem(Kubernetes APIサーバ用のクライアント証明書)
- etcdクラスタ間で通信するために、インスタンスの内部IPアドレスを取得する
- 各etcdのメンバは、etcdクラスタ内で、名前をユニークにする必要がある為、ホスト名でetcdの名前を設定する
コマンド実行メモ
各コントローラインスタンスで、以下を実行
- ectdのダウンロードとインストール
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"
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/etc
sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/
- インスタンスの内部IPアドレス&ホスト名を取得
INTERNAL_IP=$(curl -s -H "Metadata-Flavor: Google" \
> http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip)
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
- 確認
ETCDCTL_API=3
はv3のAPIを使用する設定(kubernetesの場合はv3なので基本的にここはMUST)
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
Kubernetesのコントロールパネルのブートストラップ
概要
- kube-apiserver,kube-controller-manager,kube-schedulerをのバイナリを公式からDLしてインストールする
- kube-apiserverを外部クライアントに公開する外部LBを作成する
KubernetesAPIサーバの設定
- 各種証明書・鍵を
/var/lib/kubernetes/
に配置する - APIサーバをクラスタのメンバに知らせる為の設定として、インスタンスの内部IPアドレスを使うので、取得する
- systemdの起動Unitを作成
Kubernetes Controller Managerの設定
- controller-manager用のkubeconfigを、
/var/lib/kubernetes
に配置する - systemdの起動Unitを作成
Kubernetes schedulerの設定
kube-scheduler.yaml
を作る- systemdの起動Unitを作成
HTTPヘルスチェックを有効にする
- GCLBからのヘルスチェックがHTTPしか対応してないので、nginxを入れて200OKを無理やり返すようにする
kubelet認可のRBAC
- kube-apiserverが、各Woerker Nodeのkubelet APIにアクセスできるように、RBACによるアクセス許可設定をする
- メトリクスやログの取得、Pod内でのコマンドの実行には、kubelet APIへのアクセスが必要
kube-apiserver-to-kubelet
というClusterRoleを作る- kubelet APIにアクセスして、Podの管理に関連するタスクを実行する権限を付与
- kube-apiserverは
--kubelet-client-certificate
で定義したクライアント証明書を使って、kubernetes
ユーザとしてkubeletに認証をおこなう system:kube-apiserver-to-kubelet
のClusterRoleをkubernetes
ユーザにバインドする
外部LB
- kubernetes-apiserverのフロントに置く外部LBを作成する
コマンド実行メモ
Control Planeの設定
- 設定ファイルを配置するディレクトリを作成
sudo mkdir -p /etc/kubernetes/config
- バイナリをダウンロードする
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"
- バイナリをインストールする
chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
- 秘密鍵・クライアント証明書及び暗号化のconfを
/var/lib/kubernetes/
に配置
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/
- インスタンスの内部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
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
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ヘルスチェックを有効にする
- nginx install
sudo apt-get install -y nginx
- config生成
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/
- nginx reload
sudo systemctl restart nginx
sudo systemctl enable nginx
kubelet認可のRBAC
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
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
外部LB
- 外部向けLBを作成
{
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_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
--region $(gcloud config get-value compute/region) \
--format 'value(address)')
curl --cacert ca.pem https://${KUBERNETES_PUBLIC_ADDRESS}:6443/version
Kubernetesのワーカーノードをブートストラップする
概要
socat
とは- 一言で言えば、
proxy
ツール、入出力にファイル・標準入出力・コマンド・他のマシンなど、いろいろな種類を割り当てられる kubectl port-forward
の為に必要
- 一言で言えば、
コマンド実行メモ
- ライブラリのinstall(socatは
kubectl port-forward
に必要)
sudo apt-get update
sudo apt-get -y install socat conntrack ipset
- バイナリを落としてくる
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 /
- 現在の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
の設定ファイルを作る
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
sytemdのユニットファイルを作成
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
- kube-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
を作成
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
外部からのkubectlを叩くための設定
概要
admin
ユーザのcredentialに基づいた、kubectl
用のkubeconifgを作成する
ルーティング
- ノードにスケジュールされたPodは、ノードのPodのCIDRからIPアドレスを取得する
- 現時点では、ネットワークのルートが見つからないため、異なるノード間でのPod間通信ができない
- ノードのPod CIDR範囲を、ノードの内部IPアドレスにマップする為の、各ワーカーノードのルートを作成する
コマンド実行メモ
- ローカル端末から今回作成したクラスタに接続するための
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
}
- 確認
kubectl get componentstatuses
- 各ワーカーインスタンスにネットワークルートを設定する
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
- 確認
gcloud compute routes list --filter "network: kubernetes-the-hard-way"
DNSクラスターアドオンのデプロイ
概要
- CoreDNS
- kube-dnsで使われるDNSソフトウェア
実行コマンドメモ
coredns
をデプロイ
kubectl apply -f https://storage.googleapis.com/kubernetes-the-hard-way/coredns.yaml
- 確認
kubectl get pods -l k8s-app=kube-dns -n kube-system
スモークテストとお掃除をして完了!