「Kubernetes in Action」を読んで学んだ結果を社内で共有しました。
その第6回の内容です。
Kubernetesの実行と管理をしやすくするには
コンテナのイメージを管理しやすくする
imageのサイズは小さくし、不要なファイルやツールは含めないように。
イメージに適切にタグをうち、imagePullPolicyを賢く使う
docker imageのtagにはlatestが使えるが、podのyamlファイルにはlatestを指定しないようにしよう。
latestを指定しているともしimageを更新しても、新しくscheduleするpodにはその新しいimageが使用されるが、既存のpodのコンテナのimageは古いバージョンのまま。
またlatestタグを使うと1つ前のバージョンへrollbackができない。
もしあなたがimageのタグにmutable tag(同じタグを使い続けること)を使うならpodのspecのimagePullPolicyはAlwaysにしなければならない。
しかしそれはproductionでは大きな落とし穴になる。
もしAlwaysを設定したら、新しいpodがscheduleするたびにcontainer runtimeは毎回container registryに問い合わせることになる。
これはpodが起動するのが少し遅くなる。なぜならnodeは毎回imageが更新されているかチェックする必要がでてくるため。またcontainer registryが応答がない場合、podが起動するのを妨げてしまう。
複数の情報をLabelに含める
podだけではなく他のリソースにもlabelをつけることを忘れないようにしよう。
各リソースに複数のlabelをつけるようにすれば、リソースの取得が簡単になる。
labelには例えば以下のものがよいだろう。
- リソースが属しているアプリケーション(もしくはマイクロサービス)の名前
- Enviroment(development, QA, staging, production 等)
- バージョン
- リリースのタイプ(stable, canary, green or blue for green/blue deployments等)
- テナント(あなたがもしpodをnamespaceを使用する代わりに各テナントで動かしている場合)
- シャードシステムならそのシャード
annotationを通して各リソースを表現する
他の追加情報はannotationに含めましょう。
たとえばそのアプリケーションの責任を持っている人の情報とか。
もしマイクロサービスであれば、そのpodが使用している他のサービスの名前とか。
バージョン情報とか、ツールやGUIツールが使用するmetadataとか。
なぜプロセスが終了したのかの情報を提供する
Kubernetesにはなぜコンテナが終了したのかの情報を含めることができます。
コンテナの /dev/termination-log
に終了した理由を書き込んでおけば、
kubectl describe podしたときにContainersのLast StateのMessageに表示されます。
アプリケーションログをハンドリングする
アプリケーションログは標準に出すべき。
そうすればkubectl logsで見れる。
一つ前のpodのlogはkubectl logs --previous
でみれる。
特定のログファイルにはいていたら、
$ kubectl exec <pod> cat <logfile>
でみれる。
または、pod内にコンテナが1つなら
$ kubectl cp foo-pod:/var/log/foo.log foo.log
でログをローカルへコピーすることができる。
複数のコンテナが実行されていたら -c containerName
オプションでコンテナを指定しよう。
Kubernetesはログ集約システムは提供していない。
しかしそれをログ集約システムをデプロイするのは簡単。
Google Kubernetes EngineならEnable Stackdriver Logging checkbox
にチェックを入れればいい。
EFK(ElacsticSeach, Fluentd, kibana)のようなスタックをデプロイすれば使うこともできる。
fluentdのようなログ収集ミドルウェアは複数行ログ(たとえばJavaのスタックトレースのような)は1行1行別のログになってしまうので、jsonとして出力するのがよい。
ただjsonログは人間には見にくいので(kubectl logsした場合)、
標準出力にはplain textにして、特定のログファイルにはjsonで出力するのがよい。
カスタムリソースを作成する
独自のカスタムリソースを定義し、Kubernetesを拡張することができる
CustomResourceDefinitions
新しいリソースを作成するためには、CustomResourceDefinition
objectを定義する必要がある。
CustomResourceDefinition
を定義すれば、他のリソースと同様に、yamlやjsonを通してそのinstanceを作成することができるようになる。
以下が定義例。以下ではWebsiteという名前のリソースを新たに定義している。
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: websites.extensions.example.com
spec:
scope: Namespaced
group: extensions.example.com
version: v1
names:
kind: Website
singular: website
plural: websites
このリソースのinstanceを作成する場合は以下。
apiVersion: extensions.example.com/v1
kind: Website
metadata:
name: kubia
spec:
 gitRepo: https://github.com/luksa/kubia-website-example.git
そして新しく定義したこのリソースに対するcontrollerを実装する必要がある。
実装例
https://github.com/luksa/k8s-website-controller
controllerもpodとして動かすことができる。
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: website-controller
spec:
replicas: 1
template:
metadata:
name: website-controller
labels:
app: website-controller
spec:
serviceAccountName: website-controller
containers:
- name: main
image: luksa/website-controller
- name: proxy
image: luksa/kubectl-proxy:1.6.2
custom objectのバリデートはKubernetes1.8でアルファ機能として導入された。
API serverで バリデートするためにはCustomResourceValidation機能を有効にする必要がある。
custom objectのためのcustom API serverを提供する
Kubernetesでcustom objectのサポートを提供する良い方法は、自分のAPI serverを実装し、clientがそのAPI serverとやり取りさせること。
Kubernetes 1.7から、KubernetesのデフォルトのmainのAPI Serverと自分で作成したAPI serverを統合することができるようになった。
複数のAPI Serverがあっても1つのAPIServerとして扱うことができる。
custom api serverを登録するには、custom api serverをpodとしてdeployしserverを通して公開すればよい。
Service Catalogを使ってKubernetesを拡張する
Service Catalogとは名前の通りserviceのカタログで、Service Catalog機能を使えば、カタログの中からサービス(アプリケーション)を選んで、pod,service,configmap等を直接扱うこと無く簡単にデプロイすることができるようになる。
Service Catalogは以下のリソースを提供している。
- ClusterServiceBroker
- サービスを提供する(外部)システムについて定義したもの
- ClusterServiceClass
- ServiceInstance
- ServiceBinding
- はServiceInstanceとclients(pods)のセットを結びつけるもの
Kubernetesは、提供されるサービスのリストをbrokerへ問い合わせ、ClusterServiceClassリソースを作成する
ユーザが提供されるサービスが必要な場合、KubernetesはServiceInstanceを作成し、ServiceInstanceをpodsに結びつける(bindingする)
それらのpodsに必要な認証情報や接続に必要な情報をsecretを通して渡す。
Service Catalog API server と Controller Manager とは
Service Catalogは以下の3つのコンポーネントからなる分散システムである。
- Service Catalog API Server
- etcd as the storage
- Controller Manager, where all the controllers run
Service Brokers と the OpenServiceBroker API とは
クラスタの管理者は1つ以上の外部ServiceBrokerをService Catalogに登録することができる。
各brokerはOpenServiceBroker APIを実装する必要がある。
Service Catalogがサービスの一覧を取得したあと、Service Catalog は各ClusterServiceClassリソースを生成する。各ClusterServiceClassリソースは、サービスの1つのタイプが定義されている(例えばあるClusterServiceClassは“PostgreSQL database”)。
各ClusterServiceClassは、1つ以上のservice planを持っている。ユーザは必要とするそのサービスレベルを選択することができる。たとえばdatabase ClusterServiceClassはHDDのfreeプランや、SSDのPremiumプランを提供したりするだろう。
Serviceの提供と使用
実際にserviceが提供されるためには、service instanceを作成する必要がある。
例:
apiVersion: servicecatalog.k8s.io/v1alpha1
kind: ServiceInstance
metadata:
name: my-postgres-db
spec:
clusterServiceClassName: postgres-database
clusterServicePlanName: free
parameters:
init-db-args: --data-checksums
重要なのは、このservice(これだとpostgres-database)は、Kubernetesクラスタ外に作成されてもいいということ。なので例えばクラウド上のDBでもよい。
serviceへの接続情報はServiceBindingリソースを使ってsecretを通して与えられる。
例:
apiVersion: servicecatalog.k8s.io/v1alpha1
kind: ServiceBinding
metadata:
name: my-postgres-db-binding
spec:
instanceRef:
name: my-postgres-db
secretName: postgres-secret
将来的にはPodPresetsというKubernetesの機能が導入される予定。
Service CatalogはServiceBindingで指定した名前で新しいSecretを生成し、必要な情報をsecret内に保存する。
あとはpodからそのsecretをmountして使えば、podが接続情報を得ることができる。
Summary
- CustomResourceDefinition objectを作成することで、Custom resourcesをAPI Serverに登録することができる
- API Serverのコードを変更すること無く、CustomObjectのインスタンスを保存、取得、更新、削除することができる
- custom objectsを実際にKubernetes上で動くようにCustom Controllerを実装することができる
- API aggregation機能を通し、Custom API Serverを使うことでKubernetesを拡張することができる
- Kubernetes上に構築されたPasSはコンテナを使ったアプリケーションを構築するのが楽になる
- パッケージマネージャーのhelmは既存のアプリケーションをresource manifestsを必要とせずにデプロイすることができる
複数クラスターをkubectlで扱う
以下のように環境変数KUBECONFIG
にコロン区切りでクラスタファイルを指定すると複数のファイルを読み込むことができ、つまり複数クラスタを扱うことができる。
KUBECONFIG=~/.kube/config1:~/.kube/config2
kubeconfigの構成を理解する
apiVersion: v1
clusters:
- cluster:
certificate-authority: /home/luksa/.minikube/ca.crt
server: https://192.168.99.100:8443
name: minikube
contexts:
- context:
cluster: minikube
user: minikube
namespace: default
name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
user:
client-certificate: /home/luksa/.minikube/apiserver.crt
client-key: /home/luksa/.minikube/apiserver.key
上記のようにKubeconfig fileは以下で構成されている。
- clustersのリスト
- contextsのリスト
- current contextの名前
- usersのリスト
それぞれの詳細
cluster
- Kubernetes clusterを表している。API ServerのURL、certificate authority(CA)ファイル、他には必須ではないがAPI serverと通信するためのいくつかのオプション。 CA certificateは分割して保存できそれをkubeconfigファイル内から指定できる。また直接certificate-authority-dataフィールドに含めることも出来る。
user
- どのuserもAPI Serverと通信する時に使うcredentialsを定義する。credentialsには以下が使える
- usernameとpassowrd
- authentication token
- client key and certificate
- client key and certificateはkubeconfig fileに含めることができる。もしくはファイルを分割してconfig file内で指定することができる。以下のように
users:
- name: minikube
user:
client-certificate: /home/luksa/.minikube/apiserver.crt
client-key: /home/luksa/.minikube/apiserver.key
context
- contextはclusterとuserを紐付けるもの。またkubectlで使われるデフォルトのnamespaceを指定できる。複数のcontextで、同じuserや同じclusterを指定することができる。
他のcontainer runtimesを使用する
Dockerの代わりにrktを使う
container runtimeとしてdockerのほかにrktがある。
rktはデフォルトでPod(複数の関連するコンテナを実行する)の概念をサポートしている。
rktはDocker-formatted container imageをそのまま実行することができるのでリパッケージは必要がない。
Kubernetesでrktを使うためにはkubeletに--container-runtime=rkt
オプションを渡してあげればよい。
なおkubeletはContainer Runtimeとやりとりする唯一のコンポーネント。
他のcontainer runtimesをCRIを通して使う
Kubernetes 1.5 から Container Runtime Interface(CRI) が導入された。
CRIはplugin APIで他のcontainer runtimeを統一的に操作することができる。
kubeletはDockerやrktをCRIを通して操作する。
新しいCRI実装として、CRI-Oというものもでてきた。
参考: kubernetes用コンテナランタイムcri-oを試す
CRI-Oの開発の背景には、dockerの開発速度が早い中、それに依存するkubernetesが不安定になっており、より小さく単純な、専用のランタイムを用意することでkubernetesの安定化に繋げたいという意図があったようです。
また、Kubernetesをコンテナの代わりにVM上でアプリケーションを動かせる新しいCRI実装が開発中。
そのうちの一つはFrakti(The hypervisor-based container runtime for Kubernetes.)というもの。
ほかには Mirantis VirtletというCRI実装があり、これは、docker imageの代わりに実際にVM imagesを動かすというもの。
Cluster Federation
Kubernetesは複数のKubernetesを1つのKubernetesとして扱うための Cluster Federationという機能がある。
これにより、Kubernetes Clusterを複数のdata center、複数のアベイラビリティゾーン、もしくは複数のクラウドに冗長化することができる。
※ただ、Federation(の最初のversion)はあまりいけていないらしく、SIG-Multiclusterで仕切り直し中とのこと。