今年一年Kubernetes on AWSをやってきて、kube-awsメンテナ目線で、「今日から、できるだけ楽に、安定して本番運用」するための個人的ベスト・プラクティスをまとめておきます。
TL;DR
- EKSはまだプレビュー申込の段階。実際に動くものがあるかもわからない。
- EKSとkops、kube-aws、kubesprayなどは組み合わせて使うもの。代替えにはならない。
- SaaSありなら分散ログ、分散トレース、リソースモニタリングはDatadogに寄せると運用が楽
- istioは安心して本番運用できるフェーズではない(Service Meshが必要なら、まだLinkerdのほうがよい)
- アプリケーションにPrometheusエンドポイントを生やしてメトリクスを取れるようにすべき
- アプリケーションはOpenTracingやZipkin、Jaegerなどのトレーサを組み込み、Zipkin v1スパンを送るべき
前置き
kube-awsはパーツの一つでしかありませんので、kube-aws成分は少なめです。
また、長くなりすぎないよう、詳細についてはできるだけこの記事内に書くのではなく各参考記事におまかせする方向性にしておきます(とはいえついつい書いてしまったところもありますが・・・)。
最後になりますが、あくまで「今日から、できるだけ楽に、安定して本番運用をする」という観点でまとめますので、あまりエッジのきいたものは紹介しません(し、実際、してないと思います。たぶん)。
Dos
これだけは入れておきたいツールたち
- kubectl
- kubectx
- kubens
- helm
- minikube
Here are some tools to enhance kubectl. 🚀
— Ahmet Alp Balkan (@ahmetb) 2018年1月4日
They work great together!
[1] https://t.co/b9EYQN0Fq8
[2] https://t.co/177WiwY2I9 #Kubernetes pic.twitter.com/utzizg9vzc
アプリのデプロイメント関連
SecretやConfigmapは変更ではなく新規作成して切り替える
Kubernetesでは、アプリケーションの設定値はConfigmap、クレデンシャルはSecretというK8Sリソースに定義して、それを環境変数やローカルファイルにマッピングしてアプリケーションプロセスから読ませるという方法がよく使われます。
その前提でK8Sを使い始めるとハマるのが、環境変数の値はConfigmapやSecretが変更されても自動的には置き換わらないということです。アプリケーションプロセスが環境変数をConfigmapやSecretから読み取るわけではなく、K8Sがdocker runするときに読んで環境変数を設定するようなイメージなので自明かもしれませんが。
Configmapを変更したときに、自動的にPod(とコンテナ)を作り直すために
のようなOSSを利用する人もいます。それはそれでいいのですが、ConfigmapやSecretをそもそも変更せず、新規作成して切り替えたほうが、内容が間違っていた場合にロールバック可能という利点があります。
helmを使う場合
helmを使う場合は、依存先configmap/secretのchecksumを使ってPod(とコンテナ)を自動的に再作成させることができます。helmが提供するのはchecksumの計算と参照だけなので、対応していないchartもあります。利用するchartがそれに対応していればよいのですが、対応していなければぷるりを出すと喜ばれるかもしれません。また、helm chartを自作する場合はこれを考慮しておくと便利です。
古いデプロイメントの
あわせて読みたい:
Pod、EC2インスタンスの入れ替えによりダウンタイムが発生しないようにする
- ステートレスなPodは最低2個以上セットにする
- Podがノードの入れ替え等で一時的に増減するのは当たり前に起こることなので、それに対処しておく
- できるだけそれぞれ別のEC2インスタンスにスケジュールされるようにする
- podAntiAffinity
- クラスタ自体がMulti-AZの場合のみ。
- できるだけそれぞれ別のAZにスケジュールされるようにする
- podAntiAffinity
- クラスタ自体がMulti-AZの場合のみ。
- Podにreadiness/health probesを設定する
- 起動中のPod、障害発生中のPodを自動的にロードバランス対象から外すために必要
- SIGTERMを受けたらGraceful Stopすること。N秒後にSIGKILLで強制停止させられるから。
- NはterminationGracePeriodSecondsで変更可能
- ノードが
- PodDisruptionBudget
合わせて読みたい:
-
A summary of how to do graceful terminations and zero-downtime upgrades in Kubernetes. pic.twitter.com/4v8ZBZpdtK
— Ahmet Alp Balkan (@ahmetb) 2017年12月19日
EKSでK8Sクラスタ自体の運用を楽にしていく
AWS re:Invent 2017でKubernetesのマネージドサービスがついに発表されましたね!
詳細は「Amazon Elastic Container Service for Kubernetes (EKS)の所感」にまとめました。まだ実際に利用できるわけではありませんが、今のところでている情報だと、
- 「Masterノードのマネージドサービス」。WorkerノードはCloudFormationなどで自分で持ち寄る(これは個人的には圧倒的に良い判断だと思いますが、GKEのようなサービスを求めてた人からは評判悪そうですね
- KubernetesのCluster NetworkingによりAWSネイティブなサポートを追加していく
- amazon-vpc-cni-k8s
- 上記とCalicoの連携
- Kubernetes on AWSを便利に使うためのOSSをバンドルしていく
- kube2iam, heptio/authenticator
の点だけでも、かなりKubernetes on AWSの導入と運用と敷居が下がると考えられ、歓迎すべきサービスだと思います。
一方で、
- Fargate対応
は実際どのようになるのか次第ですね。
- LambdaのようにENIをアタッチするために数十秒待たされるような仕様になると「すぐスケールしてほしい」というユースケースには対応できなくなる(むしろSpot Fleetで安価なインスタンスをホットスタンバイでおいておくほうがよいのでは)
- virtual-kubeletで検討されているような抽象化をするとしたら、Daemonsetはどうするのか(fluentdやdatadog agentでFargateに起動したK8S Podのログやメトリクスをとれるの?たぶんとれない実装になりそう)
などが既にKubernetes on AWSを本番運用している立場からすると気になります。
EKS + kube-aws
手前味噌ですみませんが、kube-awsはEKSと統合予定です。
kube-awsはetcdとcontrollerノードもCloudFormationで管理してくれるのですが、それをEKSに任せられるとkube-awsとしても、その利用者としてもメリットがあるためです。
※kube-awsは、AWS上にカスタマイズ可能なKubernetesクラスタをつくるためのCloudFormation + cloud-initのWrapperなので、EKSとはスコープが違います。kopsも同様です。なので、「EKSがくるから、kopsが不要になる」みたいな話はちょっと間違ってます。もちろん、「EKSで作れないところは手動か自前のスクリプトでやります」なら確かにkopsなどは不要になるのですが、それは自前でkops的なものをつくるのとどれくらい違うのでしょうか、という疑問は残ります!
利便性と運用性の高い分散ロギング
ログストレージにfluentdからログを送って、ほぼリアルタイムにログをフィルタリングしたり追いかけたりできる環境をつくっておくと、デバッグが捗ります。特にマイクロサービスアーキテクチャをとっていて、Kubernetes on AWS上にデプロイされるサービスが複数ある場合は必須だと思います。
具体的には、以下のような組み合わせがおすすめです。
- fluentd + Google Stackdriver Logging
- Google公式のfluent-pluginがあるのが大きい
- 巷に存在するfluent-pluginのメンテされなさやばいですよね?
- 公式ならその心配もある程度軽減されます。
- fluentd + Datadog Logs(NEW!)
- Datadog Logs用のfluent-pluginが公式(機能少ない)と非公式(機能多いけど将来性未知数)
- fluentd + Elasticsearch
- AWS Elasticsearch Serviceか、ぎりぎりelasticsearch-operator
- マネージドサービスが使えるところでマネージド・サービスを使わないのは何かおかしいです。基本的にfluentd + ESの選択肢をとり、そのKinesis Firehose(まだVPC内のAWS ESに対応していない)が絡まないのであれば、AWS ES一択。いまはVPCサポートもあるし。
- AWS Elasticsearch Serviceか、ぎりぎりelasticsearch-operator
一番にfluentd + Stackdriver Loggingをおすすめしておいてなんですが、現職では2のDatadog Logsを使っています。
「Kubernetes上のアプリケーションログを自動収集する」には、、kube-fluentdをクラスタにデプロイして、StackdriverかDatadog Logsにログを送る方法とその意図を書きました。
Kubernetesネイティブなリソースモニタリングサービス
- Prometheus
- Datadog
- Stackdriver Monitoring
のような、Kubernetesの対応が充実したモニタリングサービスを利用します。外せないポイントは二つあって、一つはカスタムメトリクスのオートディスカバリ、もう一つは標準でKubernetesのノードからPod、Deployment等の汎用的なメトリクスの収集に対応しているかどうか、です。特にオートディスカバリに関しては、Podに所定のAnnotationをつけるだけで自動的にメトリクスをスクレープしてバックエンドサービスに送ってくれるようなソリューションが用意されているPrometheusやDatadogが一歩抜きん出ています。
Datadogのオートディスカバリに関しては、「Kubernetesにデプロイしたアプリケーションのメトリクスを自動収集する」に書きました。
どのマイクロサービスが原因で障害が発生しそうか・したのか知るための分散トレーシング
「分散トレーシングといえばOpenTracing」というような雰囲気もありますが、できるだけ手間をかけず、今後の移行パスも用意しつつ、プロダクション利用したいというようなモチベーションであれば、現状ではZipkin + Datadog APMをおすすめします。
詳細は「Kubernetes上のマイクロサービスを分散トレースする」に書きました。
もちろん、OpenTracingの発展に貢献したい、ぷるりを送ることも辞さない、ということであれば、OpenTracing一択です。
OpenTracingは以下の4パートから構成されますが、それぞれまだまだ改善の余地があるという状況です。
- 各言語向けのクライアントライブラリであるTracer、
- Tracerによる計測データを送る先となるバックエンド(Zipkin等)、
- クライアントとバックエンドのプロトコルまたはspan format、
- マイクロサービス間でトレースを関連付けするためのpropagation format
Kubernetes界隈で少し例を挙げると、2017/12現在は以下のコントリビュート先があります。
- Envoy
- OpenTracingトレーサに非対応
- Zipkinサポートはビルド時に組み込まれている
- OpenTracing Tracer(C++)をEnvoyに組み込んだり、OpenTracing Tracerの実装を動的にロードできるようにするようなコントリビューションが考えられます
- OpenTracingトレーサに非対応
- nginx-ingress-controller
- OpenTracingサポートがあるが、バックエンドはZipkinのみ対応
Kubernetesへデプロイしたサービスを最速で公開する
KubernetesのDeploymentやServiceのような形でアプリをデプロイしても、本番サービスに必要な
- Route 53 RecordSet
- ELB/ALB/NLB
- TLS証明書
は別途用意する必要があります。そのために、
- AWSへの一定のアクセス権をエンジニアに配布するのかしないのか、
- CloudFormationやTerraformを使うのか、
- どのRecordSet/*LB/証明書がどのサービスと対応するのか、
- 証明書はどこに保存するのか
- ...
など、運用を考えると、悩みはつきませんね。
インフラエンジニアが登場したり、そのためだけにKubernetes以外のツールを覚えたりしなくても、もっと気軽にサービスを公開できるようにしたいものです。
そこで、KubernetesのIngressを作成するだけで、ALB、証明書、Record Setを自動生成するようにしました。
- 「KubernetesでAWS ALBを自動作成する〜ついでにRoute53 Record Setも」では、Zalandoのskipperとkube-ingress-aws-controllerというOSSを利用して、KubernetesのIngressリソースをつくるだけで自動的にALBを作成し、WorkerノードをALBのターゲットグループに登録し、ALBにACMで作成しておいたTLS証明書を割り当てる(ACMの機能で証明書の更新も自動化されます)方法
- 「Kubernetesのexternal-dnsでRoute53 RecordSetを自動作成する」ではexternal-dnsという準公式のOSSを利用して、KubernetesのServiceやIngressに所定のAnnotationをつけるだけでRoute 53 Record Setを自動生成する方法
をそれぞれ説明しました。
Kubernetesのユーザ管理を効率化
利用者の数だけ個別にKubernetesのService Accountをつくったり、OpenIDなどの設定をするより、AWS IAMの権限でKubernetesに認証してもらったほうが楽です。
EKSでも採用される予定のheptio/authenticatorを使うとできます。
authenticatorの使い方については「heptio-authenticator-awsの使い方」に書きました。
バージョン管理
secretはmozilla/sopsで暗号化
- Manage Kubernetes secrets with SOPS | Frederic Hemberger
- ファイルをAWS KMSで暗号化して安全にgit commitできるようにするmozilla/sopsの使い方 - Qiita
人・チーム
最低二人はKubernetesクラスタ自体の運用+利用手引ができるように
Certified Kubernetes Administrator試験(CKA)に合格するか、そのレベルの、Kubernetes利用者からすると低レイヤーの知識を持った人を一人、二人は育成したほうがいいです。
Kubernetesには、プラットフォームとしての汎用性と、それに応じた複雑性があります。
それをAWS上で動かすためにはAWSの知識も必要になります。本番運用するとなると、JekinsなどとのCI・CDとの統合や、モニタリング、開発者や運用者のワークフロー整備など、Kubernetesの応用に関する実践的な知識も必要になってきます。仮にEKSが利用できるようになっても、その本質は変わりません。(Kubernetes自体の運用に関して自分で責任を持たなければならない範囲は減るのは確かですが)。
CKA合格レベルの知識を身につける近道は、Heptioのブログで紹介されている内容を学ぶことです。
How Heptio Engineers Ace the Certified Kubernetes Administrator Exam
Don'ts
長期的に見ると将来性はありそうですが、OSSにコントリビュートしたい気持ちがない限り「今は」手を出さないほうが無難なものを挙げておきます。
ただし、OSSに貢献したい場合は、むしろ採用してどんどんフィードバックやPRを送るといいですね。
サービスメッシュ関連
istio
pilot/proxy(ingress, sidecar), mixer
istioはアーキテクチャ良し、(Service Meshとしての)将来性よし、golang(!)、メモリフットプリント小さい、Kubernetes Native(!)ということで、最高のサービスメッシュですね。
でも、プロダクション事例を全然聞かないので、K8S on AWSを「今日から、できるだけ楽に、安定して本番運用」するという趣旨には反しますね。
また、具体的な話をすると、以下の点が本番運用を「楽に」するためにはかけているパーツだと思います。
- 専用のWeb UIがない
- 公式にもGrafanaのダッシュボードで賄ってくださいと書いてあります
- Grafanaがだめなのではなく、汎用のWeb UI(+メトリクスのストア)ならPrometheusやDatadog、Stackdriverなども色々候補にあがってきますし、そうすると「Service Meshのモニタリングと操作に特化したダッシュボード」ってしばらくはどこにもない状態になるんですよね。
- そうすると、たぶんどこかのベンダーがistioのWebダッシュボードをつくって、それとコマーシャルサポートをセット販売することになると思うのですが、それだけで果たしてビジネスになるのか疑問。ビジネスにならなければその独自ダッシュボードの進化も遅くなる。また、Grafana等で賄ってしまう会社も多いと思うので、istioベンダーがとれるパイも少なくなる
- そういう状況だと、エンジニアが100人くらいはいるそこそこの規模の組織でないとistioは採用できないのでは
Grafeas
https://github.com/Grafeas/Grafeas
https://codefresh.io/blog/write-this-down-grafeas/
Grafeasはコンテナイメージの監査を行うためのAPI仕様です。
Grafeas自体はProduction-readyなAPIサーバの実装が2018/1/5現在存在しないので、本番運用する手軽は方法はないと言っていいと思います。(Grafeas APIのProduction-readyな実装を自分で書いて運用に載せる、というのは「手軽」ではないですよね)
Grafeas APIサーバのリファレンス実装は公式GitHubレポジトリ内にあります。
- ストレージの実装がインメモリのみ
- 2018/1/5現在、IssueでPostgreSQL実装が検討されています
- ACL/Authzなし
など本番運用するときにはほしい機能はとりあえずありません。
Webベースのアプリケーションカタログ・コンソール関連
Kubeapps/Monocular
helm installコマンドを打つより親しみやすいのは確か。
ただし、まだ認証機能が不完全なので、本番運用するのは恐ろしい(誰が何を本番クラスタにインストールできるのかを制御できない。ログはとれるのかな?)
細かな権限管理が不要な場合は使ってもいいかもしれません。また、コンソール的には、RancherやKubernetes Dashboardのようにアプリケーションのログやメトリクスも確認できるようになっていくと、より使うメリットが出てくると思います。
権限関連
Namespaceと権限はチーム、プロジェクト、特定システムのサブシステム毎に細かく切る
一番大きな理由は、RBACはSecretのマウントやService Account利用の権限管理ができない、ということです。
Podを作れる権限があれば、そのネームスペース内のSecret全てにアクセスできることを意味します。SecretへのRead自体がRBACで禁止されていても、です。
同様に、あるNamespace内でPodを作る権限があれば、そのネームスペース内のあらゆるService Accountを利用できることになります。
そのため、全員と全システムのK8Sリソースが一つのネームスペースにあると、重要な機能や情報にアクセスできるService AccountやSecretが、おそらく期待しているより多くの人がアクセスできるようになってしまいます。RBACだけではそこまで細かく権限管理ができないので、ネームスペースを細かくわけることを同時にやるとよい、ということです。
ネームスペースを切らないのは以下のようなケースのみです
- アプリケーションのバージョン違い
- ネームスペースを分けると、依存元からの依存先バージョンの切り替えが面倒など、色々と不都合が生じる
- アプリケーションのバージョンアップに伴うコンテナのマイグレーションを自動的にやってくれるのがKubernetesのそもそものメリットの一つ
- 地理的分散
- 地理的に分散しているノードがあっても一つのクラスタでうまく扱えるのがKubernetesのそもそものメリットの一つ
- コスト負担元の違い
- クラウドプロバイダ側のビリングの仕組みで対応すべき。
- AWSであれば、請求先が異なる別々のAWSアカウントで作ったWorkerノードを、同じK8Sクラスタに参加させる。
いずれについてもKubernetes: Kubernetes Namespaces: use cases and insightsによくまとまっています。
Namespace毎にNetwork PolicyとResource Quotaを設定する
ネットワークポリシーの具体的な設定方法については、Kubernetes Network Policy Recipesが参考になります。
その他、NamespaceやNetwork Policyに関する主要なユースケースについては以下が参考になります。
アプリケーションのデプロイ関連
前提: デプロイをサポートするツール
Kubernetesアプリケーションのデプロイのために利用するツールは、アーキテクチャと用途毎に大雑把に分類すると、
- クライアントサイドで動くCLI
- サーバサイドのみ(kubectl+カスタムリソースでサーバサイドに仕事をさせる、gitレポジトリを監視させるgitops指向など、性格はいろいろ)
- CLI + サーバサイドのセット
の3種類ある。自分が把握しているだけでも以下のようなツールがある。
- kubectl
- helm
- クラスタデーモン系
- kube-applier
- helm-crd (Bitnami)
- landscraper
-
giantswarm/draughtsman
- git pushのたびにhelm chartをhelm commandを使ってapprへリリースしてインストールする
- landscape(複数のhelm releaseとenvのdesired stateをyamlに書くモデル。helm binaryに依存しない。cliあり。secretの扱いが独特)
- helmのissueで話し合われているhelm/tiller operator
- weaveworks/flux
- upmc-enterprises/emmie
- keel
- CLI系
- ksonnet
- helmfile (Datadogの中の人)
- helm-update-config
- kubernetes-deploy (Shopify)
- erbテンプレート利用
-
kubegen (Weaveworksの中の人)
- テンプレートエンジン
-
kedge
- docker-compose.ymlのようなフォーマットでkubernetes manifestsをかけるテンプレートエンジンのようなもの
- datawire/forge
@deeeet さんのK8S/YAML問題と著名なツールに関する良いまとめ、その補足についてもぜひ参考にしてください。
個人的には、(別にhelm推しではないんだけど)helmが一番総合力が高いから、今すぐ直接yaml書く運用をやめたくて、sedレベルじゃ間に合わない場合はhelmがいいという結論
— Yusuke KUOKA (@mumoshu) 2018年1月10日
あわせて読みたい:
前提: アプリケーションのクレデンシャル管理はデファクトな方法がまだない
helmのこちらのissueが参考になります。
ksonnet(ks, ksonnet-lib)
ksonnetには、ksonnet-libというjsonnetライブラリと、ksというksonnet-libを利用したk8sマニフェストのフレームワークとそのコマンドラインツールの2つの面がある。
ks
ksonnet-libは利用できる可能性はあるが、ksのほうは時期尚早かも。機密情報をgit commitしてもよい、または機密情報はgit外で管理する、という場合であれば使ってもよさそうだけど、個人的にはおすすめしません。
Support for one-way encrypted secrets like bitnami/sealed-secrets · Issue #255 · ksonnet/ksonnet
Sealed Secrets
SealedSecretという独自リソースをKubernetesにつくって、それにKubernetesのSecretを管理させるというもの。KubernetesのSecretはkubectlがあれば平文で見えるので、それを問題視する場合には使える?
ただし、これを使う利点の一つと説明されている「Kubernetesに保存する機密情報=SecretをGitにコミットできる」という点に関しては、疑問が残る。
おそらくKubernetesを採用するような組織だとKubernetes Secret以外にもGitで管理したい機密情報があると思う。であれば、sopsのような特定のプラットフォームやプロビジョニングツールに依存しない、Gitとの組み合わせを前提としたツールを利用するほうがよさそう。
また、Sealed SecretからSecretへの復号は、クラスタ上で非同期に行われる。なので、SealedSecretとアプリを同時にデプロイすると、復号が終わるまでアプリPodが起動できない。分散システム
カスタムHelm Chart Registry
HelmのChart RegistryはただのHTTPサーバでよく、またカスタムHelmプラグインを用意すればHTTPサーバ以外もChart Registryにすることができる。たとえば、
AWS S3をバックエンドにしたChart Registryをサポートする以下のようなOSSがある。
ローカルファイルシステムをバックエンドにしたChart Registryでよければ以下のようなOSSがある。
また、App Registryという仕様と、その実装(Quay.ioのApplication Registry)もある。
- https://github.com/app-registry/appr-helm-plugin
- https://coreos.com/blog/quay-application-registry-for-kubernetes.html
ただ、以下の点がサポートされるまでは、あえて本番運用に載せなくても良いと思う。
- 手間のかからない権限管理(Application Registryのユーザとその権限管理を個別にやるのがめんどう。せめてIAM User/Roleと統合したい)
- Audit Log(どのChartを誰がいつダウンロードしたか)
ディザスタ・リカバリ関連
K8Sクラスタをいつ何時落としたり壊しても、比較的短時間で復旧できるようにすると良いです。ここでは、そのために使えそうだけど使えない(と判断した)ツールを紹介します。
K8Sクラスタのフルバックアップツールark
Kubernetesクラスタにデプロイされているリソースのフルバックアップをとるツール。S3のサーバサイド暗号化にも対応していたりして、痒いところに手がとどく。
Disaster recoveryのためのツールだという触れ込みはわかったうえで、それでも個人的にどういう場面で使えるのかが疑問。
本番環境やステージング環境など、停止時間なしでクラスタのバックアップ&リストア・移行・作り変えをしたいようなユースケースでは、そのままでは利用できない。なぜかというと、Point-in-time recoveryができないから。KubernetesのAPIサーバをread-only(ユーザが変更可能なリソースだけ)にするようなことができれば、それとarkの組み合わせでpoint-in-time recoveryもできそうだけど・・・。
Disaster Recoveryという目的であれば、arkを使って不完全にやるよりも、GitOpsを徹底して、「クラスタが起動次第、Gitレポジトリの内容に従ってデプロイされるべきリソースがデプロイされる」ようにするほうが筋が良いと思う。ただし、GitOpsをやらない場合はこの方法はそもそも使えないと思うので、arkが有力な選択肢になるか?
GitOpsを重要なクラスタへのデプロイで利用するかどうかに関係なく、個人で使うクラスタ、開発用クラスタを停止時間ありで作り変えたりするときならarkは使えるかも?