Upgrade to Pro — share decks privately, control downloads, hide ads and more …

12 Factor App on Kubernetes を12ヶ月実践して見えてきたもの

12 Factor App on Kubernetes を12ヶ月実践して見えてきたもの

Kubernetesを使って12 Factor Appの実装、運用を実践した結果得られた知見を12Factorの各項目別に紹介します。

translucens

July 27, 2020
Tweet

More Decks by translucens

Other Decks in Programming

Transcript

  1. The Twelve-Factor App • HerokuのFounder & CTO, Adam Wiggins氏らによる アプリケーションの設計論

    – 「Twelve-Factorの方法論は、どのようなプログラミング言語で書かれたアプリ ケーションにでも適用できる」とある通り、具体的な実装例と言うよりは抽象 的、方向性を示すもの • 実際の構成で12個の項目を具体化した例を紹介 4
  2. Beyond the 12 Factor App Pivotal社のadvisory solutions architect Kevin Hoffman氏による、

    12factor appの新たな解釈、項目の追加 追加された項目は下記の3点 • 2. API First • 14. Telemetry • 15. Authentication and Authorization 以降、単にBeyondとよぶ 5
  3. 基本的な方針 • 独自実装、拡張は最小限に – マネージドサービスやサポートの利用 – 作って終わりではなく、運用する期間の方が長い – 世間に公開されている技術情報を見れば分かるようにしたい •

    コストに対して十分に恩恵の得られる技術の採用 – 金銭面のほか学習、運用コストも考慮に入れる • 「◦◦ツールを入れればDevOps!アジャイル!クラウドネイティブ!」みたいなことはない – ベンダーではないので、最先端技術自体では収益化できない – 具体的にはサービスメッシュは計画から外した • 社内の規則類がそれなりに存在 – 交渉するか実装をハックして回避するかのバランス… 8
  4. 具体的な構成要素 Kubernetesクラスタ: Kubernetes Engine (GKE) • DWHがBigQueryにあり、同一クラウドの方が認証が楽 公開APIサーバ: Cloud Run

    9 Kubernetes Engine プログラミング言語: Go • 型サポート、単一のバイナリができる扱いやすさ インフラ構成管理: Terraform • GCP公式ツール(Deployment Manager)より事例が多い Cloud Run
  5. Cloud Runの導入で得たもの GKEやCloud Functionsであれば検討、実装が必要なことが省略できた • ノードのオートスケーリングのチューニング – イベント日程を取り込んで事前にスケールアウトする機構 – たまにTVに取り上げられて予想外に増えることもある

    • 社内用サービスとの分離 – GCPのプロジェクト単位で分けてあるので、ネットワーク的にも全く別 • 開発環境や、深夜のアイドルVMの費用がほとんどなくなった – アクセスがなければ課金されないので • RDBアクセスの軽減 – 1コンテナで80アクセスまで同時対応可能なため接続数が抑えられた 11
  6. 12+3Factorまとめ 13 項目 使用したリソース、ツール、ライブラリ どう美味しいの? I. コードベース FluxCD 頻繁なYAML書き換えからの解放 II.

    依存関係 Distroless Dockerイメージの縮小 脆弱性検知の偽陽性の削減 III. 設定 ConfigMap, Secret, Workload Identity Secret Manager, caarlos0/env 環境別にビルドし直さずに済む 機密情報自体の削減 IV. バックエンドサービス 各種マネージドストレージ コンピュートとストレージの分離 V. ビルド、リリース、実行 Docker, Kubernetes全般 イミュータブル、「謎」のVM化を抑止 VI. プロセス Pod Security Policyの ReadOnlyRootFilesystem イミュータブルになる セキュリティ向上 VII. ポートバインディング Service, Deployment サービス間が疎結合に なにぶん数が多いので、はまり ポイントとか工夫した点のある項 目に絞ります。
  7. 12+3Factorまとめ 14 項目 使用したリソース、ツール、ライブラリ どう美味しいの? VIII. 並行性 Cloud Run 0から必要な数までスケール可能

    IX. 廃棄容易性 Dockerのマルチステージビルド Distroless Preemptible VM 必要なときに素早く起動できる→スケール完了までのア クセスに耐えるマージンを削れる VMコストの低減 X. 開発/本番一致 Kustomize, FluxCD 共通の設定と環境差分の分離 XI. ログ uber-go/zap, tommy351/zap-stackdriver アプリがログを出した後のことを考慮せずに活用できる XII. 管理プロセス Job アプリ本体からの分離 2. API First OpenAPI Specification 3.0 見やすいAPI定義ページ、IFコードの生成 14. テレメトリ OpenCensus, Cloud Trace マネージドサービスのライブラリも含めたトレース記録 15. 認証・認可 Keycloak, Louketo Proxy APIサーバにOpenID Connectを意識させず実装
  8. I. コードベース 「バージョン管理されている1つのコードベースと複数のデプロイ」 具体的には? • Single source of truthとなるGitリポジトリのブランチ戦略を決める –

    本番用、試験用、開発用…はブランチで分離し、リポジトリは分けない – NG: いくつものリポジトリからファイルを集めないとビルドできない状態 – NG (Beyond): メインのアプリと密結合なワーカーを分ける • Twelve-Factor Appではアプリをスコープにしているが、 インフラやクラスタにも適用可能 – Kubernetesのマニフェスト: GitOps(Kubernetes meetup #21のテーマ) – Terraformの適用: Atlantis, Terraform Enterprise 15
  9. 開発クラスタ GitOpsによるデプロイ ※ここで言うGitOpsは CIOpsと対比される狭義の意味 18 Container Registry 本番クラスタ マニフェスト 更新確認

    kubectl apply 更新確認 タグの更新 更新確認 kubectl apply CDツールはFluxCDを使い、次のように環境別にデプロイされるようにしている • 本番クラスタ: Gitのメインブランチ • 開発クラスタ: Gitの開発ブランチ ◦ イメージレジストリの監視機能を使い、条件に合うイメージを使うようにマニフェストを更新 ◦ ステージングネームスペース: セマンティックバージョニングの最新イメージ ◦ 開発ネームスペース: 最新のイメージを追従
  10. FluxCDによるデプロイの実際 良い点 • CIの責務はDockerイメージをビルド するところまでなので単純 • Kubernetes APIをグローバルに公開 しないようにできる •

    デプロイツールからGitへのネットワー ク経路を固定できる • 開発版イメージのYAMLの書き換えを しなくて済む 19 課題 • ワークフローを必要とするデプロイ (例: DBをマイグレーションしてからア プリを更新)、エラー通知は対応しな い – Flux v2, toolboxに期待 – エラーはPrometheus, Alertmanager と併用して検出、通知することはでき る • FluxCDがアノテーションを付けるので kubectl diffの差分が残る
  11. II. 依存関係 「依存関係を明示的に宣言し分離する」 • Goのライブラリ: Go modulesを使い依存関係を管理 • それ以外のCLIツール等: Dockerfileに記述、暗黙の依存を防止

    – Dockerをパッケージングツールとして扱う – ディストリビューション由来のパッケージに暗黙のうちに依存したくない • 脆弱性スキャンツールの偽陽性を減らしたい意図もある 20
  12. Dockerベースイメージあれこれ • Distrolessのbase版 にした – ルート証明書やタイムゾーンファイルは入っている – static版に加え、glibcやOpenSSLが入っている その他試したもの •

    scratch – 「Goはシングルバイナリだから」と素朴に考えた時間が僅かにあった – GCPのHTTPS APIアクセスにルート証明書が必要なので(パッケージマネー ジャを使わずにコピーしてくる)必要があり、手間がかかる • alpine – glibcでなくmuslなので互換性の懸念が残る 21
  13. III. 設定 「設定を環境変数に格納する」 Beyond「設定と認証情報を区別すべき」「専用の構成管理サーバも検討」 • Kubernetesの専用リソースかDeploymentに記述 – ConfigMap – Secret

    – Deployment > spec > template > spec > containers > (コンテナ) > env • caarlos0/env を使って設定を保持する構造体へマッピング – Go組み込みの数値型の他、time.Durationやurl.URLへの変換が行える – デフォルト値の設定も可能 22
  14. Secretの扱い Secretの内容をリポジトリに含めないことを優先し、完全なGitOps化は中断 • 外部のAPIキー等 – GCPのSecret Managerに格納 • クラウドの認証キー →シークレットを生成しない認証:

    – GKEでは Workload Identity でGCPのService accountとKubernetesのService accountをリンクすることができる • Pod起動直後(実測値: 2, 3秒)は認証できない制約がある – initContainerでsleepするなりリトライするなりする – 最近のGoクライアントライブラリでは、この認証失敗でエラー終了しなくなった • 単にランダムであればよいもの(セッションの暗号化キー等) – Terraformでランダム文字列生成→TerraformからSecretに設定している 23
  15. ステージ 誰が 何を 設計 開発者 依存関係の表現方法を決める ビルド CIサービス (Cloud Build)

    • Goのソースをビルドする • Dockerイメージを作る リリース GitOpsツール (Flux) • リリース(環境設定とビルドされたものの組。 KubernetesではDeploymentに相当する)を更新 実行 Scheduler kubelet • Deploymentの記述に沿ってコンテナを起動する V. ビルド、リリース、実行 「ビルド、リリース、実行の3つのステージを厳密に分離する」 Beyond「設計フェーズを追加」 25
  16. VI. プロセス • ステートレス:コンテナが動いているVMのメモリやディスクに永続的な データを保存しない。ファイルはCloud Storageに置く。 – 明示的に指定したパス以外にファイルを置いてしまわないよう、 Pod Security

    PolicyのReadOnlyRootFilesystemをデフォルトでtrueにした Beyondではステートレスの定義について補足されている • 実行時に生成したローカルファイルへ依存しない • 起動時に過度のキャッシュを行わないことを勧めている 27
  17. IX. 廃棄容易性(続き) • 仮に突然終了したとしてもデータがロストしないような設計 – 非同期処理のキューイングにCloud Pub/Sub (pull)を使い、メッセージを送っ たワーカーからの応答がしばらくなければ、他のワーカーへ メッセージが再送されるように

    31 Cloud Pub/Sub Pub/SubではメッセージのACKを期限内に送る必要がある。 ワーカーが生きていると、SDKが自動的にACKの期限延長 リクエストを送信する=ハートビートの役割を果たす
  18. X. 開発/本番一致 「開発、ステージング、本番環境をできるだけ一致させた状態を保つ」 • 時間・担当者・ツール(ミドルウェア)を近づける – OSSのRDB: Dockerを使えばをローカルで起動できる – Kubernetes環境:

    minikubeを使って一式作ろうとしたが、minikube 特有の不具合にはまり、使い慣れたComposeにした – 独自仕様のマネージドサービス: Pub/SubやBigQuery • エミュレータがなかったり、あっても一部機能が提供されていない ことがある 33
  19. XI. ログ 「ログをイベントストリームとして扱う」 • アプリの責務は標準出力に書き出すところまで • 下記のロガーを使用 – uber-go/zap •

    構造化されたログを出力することができる – tommy351/zap-stackdriver • zapのメソッドとStackdriverのログレベルを対応させるために使用 • Cloud Loggingが裏でいろいろしてくれる – BigQueryへログをコピーしてBigQueryの強力な検索性能を使える – コンテナ名等のメタデータをログに付与 • コンテナごとのエラーレートを集計し、アラートを設定 35
  20. 14. テレメトリ Beyondのみの項目。3種に分けて考えることが推奨されている • APM • ドメイン固有 • ヘルスチェック、システムログ トレースの取得はOpenCensusとCloud

    Traceの組み合わせで実現 GCPのGoクライアントライブラリは内部処理についてもトレースが実装され ており、トレーシングを有効にするだけで記録することができる 38 Trace
  21. Readiness ProbeのReadyについて • APIサーバ – 一部はDBとキューを使う – 一部はDBのみ使う 最初はDBとキューの両方の可用性をチェックしていた キューが使えなくなると、機能BやCのように、キューが不要な

    機能も巻き添えになって無応答になるのでチェック対象から外 した (キューが利用できなくなるのはGKE側の不具合で解消済) 40 DB キュー 機能A 要 要 機能B 要 不要 機能C 要 不要
  22. #1 - 6 44 Kubernetes版との差異 I. コードベース Dockerイメージをビルドする点では同一 II. 依存関係

    差異はなし III. 設定 環境変数は設定可能だが、コンソールから値が見えるため、機密情報はSecret Managerに保存 IV. バックエンドサービス 当初はRedisへの接続ができなかったが、Serverless VPC Accessのサポート でできるようになった V. ビルド、リリース、実行 GitOpsはできない VI. プロセス ファイルシステムは揮発性なのでステートレスであることが必須
  23. #7 -12 45 Kubernetes版との差異 VII. ポートバインディング 環境変数PORT宛にリクエストが送られてくるのでそれで待ち受ける VIII. 並行性 コンテナあたりのリクエスト数は最大80までで指定可能

    IX. 廃棄容易性 レスポンスを返した後のCPUの割り当ては絞られるので、レスポンスを返しき るまでに必要な処理は済ませてしまう方が良い X. 開発/本番一致 Borgをローカルには作れないという点では差異が残る XI. ログ 構造化されたログを使ってログレベル”severity”を指定しないと、標準エラー出 力からもログレベルはDefaultになる XII. 管理プロセス Jobのような機構はないので別の環境で実施する
  24. 47 Q&A • Terraformで、クラスターのスケールやバージョンアップもやっているのですか? – はい。予期しない事象がないよう、開発環境と本番環境のGKEを交互にバージョンアップするような運用にしています。 • ComposeとGKE(Kubernetes)の両方をサービス開発者が覚えるのは大変そうだと思ったのですが、習得を助けるための 工夫はなにかしていますでしょうか? –

    それぞれテンプレを配布するくらいで、劇的に効果のあるやり方は見つかっていないです… • プリエンプティブル VM を使いだしてアプリの設計について実際に得た気づきはありましたか? – 冪等な処理の重要性や、全体として冪等であっても再実行には時間がかかるので、いくつかのステージに分割して状 態を保存し再実行の範囲を最小限にするようにしています。 • cronjobでe2eテストってかなり興味深いですが、例えばリリースとかでコンテナの生え変わりのタイミングがドンピシャで来て エラーになったりとかないもんなんですか? – 今は破壊的な変更に伴うエラーは減っていますが、開発者が更新しない早朝にテストを実行するようにして偽陽性を削 減しました。 • メッセージキューイングってマネージドでなくてもわりと開発環境を用意するのがめんどくさいコンポーネントだと思うのです が、工夫した点などあればご教授ください。 – 運良くPub/Subはgcloudコマンドにエミュレータが提供されていたのでそれを使いました。 キュー経由でメッセージを送らないとローカルでのテストができないのは不便なので、簡単なレイヤードアーキテクチャ を使ってキューからメッセージを取り出す部分を分離しています。