The Twelve-Factor AppとKubernetes

The Twelve-Factor AppとKubernetes

KubernetesとGoを用いたThe Twelve-Factor Appの実装例

A3fabd96e2ca4bee5b0333ff2a640f01?s=128

translucens

August 07, 2019
Tweet

Transcript

  1. - The Twelve-Factor Appと Kubernetes - 2019-08-07 @translucens 1

  2. •Development over the Cloud • 「クラウドの上で」開発

  3. 先日のCloudNative Days Tokyo 2019にて 先進的な・強い事例が数多く紹介された • Operator作成 • CNCFプロダクト活用 •

    サービスメッシュ • ベンダーロックインされないインフラ… サーバサイドエンジニアはどこに向かえばいいの? パブリッククラウドを前提とする場合は? Cloud Nativeなアプリ設計に近づく方法を考えてみた 3
  4. The Twelve-Factor App • HerokuのFounder & CTO, Adam Wiggins氏らによる アプリケーションの設計論

    – 「Twelve-Factorの方法論は、どのようなプログラミング言語で書かれたアプリ ケーションにでも適用できる」とある通り、具体的な実装例と言うよりは抽象 的、方向性を示すもの • 実際の構成で12個の項目を具体化した例を紹介 4
  5. 具体的な構成要素 Kubernetesクラスタ: Kubernetes Engine • 他のGCPマネージドサービスとの親和性 5 Kubernetes Engine プログラミング言語:

    Go • 型サポート、単一のバイナリができる扱いやすさ インフラ構成管理: Terraform • GCP公式ツールより事例が多い
  6. I. コードベース 「バージョン管理されている1つのコードベースと複数のデプロイ」 具体的には? • Single source of truthとなるGitリポジトリのブランチ戦略を決める –

    本番用、試験用、開発用…はブランチで分離し、リポジトリは分けない • Twelve-Factor Appではアプリをスコープにしているが、 インフラやクラスタにも適用可能 – Kubernetesのマニフェスト: GitOps(前回のKubernetes meetupのテーマ) – Terraformの適用: Atlantis 6
  7. • GitLab Flowの環境別ブランチを選択 – GitFlowほど複雑でなく、 各環境の状態が追いやすい • アプリは1リポジトリ:1イメージ、 インフラ系は別のリポジトリに入れる –

    クラスタの設定変更等で不必要にCIのビルドが 起動するのを避ける 7 Image credit: Git by Jason Long https://git-scm.com/downloads/logos (CC BY 3.0) GitLab Flow by Job van der Voort https://docs.gitlab.com/ee/workflow/gitlab_flow.html#environment-branches-with-gitlab-flow (CC BY-SA 4.0) ブランチ戦略 …
  8. II. 依存関係 「依存関係を明示的に宣言し分離する」 • Goのライブラリ: Go modulesを使い依存関係を管理 • それ以外のCLIツール等: Dockerfileに記述、暗黙の依存を防止

    – Dockerをパッケージングツールとして扱う – ルート証明書やタイムゾーンファイルは最低限必要 8
  9. III. 設定 「設定を環境変数に格納する」 • Kubernetesの専用リソースかDeploymentに記述 – ConfigMap – Secret –

    Deployment > spec > template > spec > containers > (コンテナ) > env • caarlos0/env を使って設定を保持する構造体へマッピング – Go組み込みの数値型の他、time.Durationやurl.URLへの変換が行える – デフォルト値の設定も可能 9
  10. 機密情報の扱い • GitOpsではSecretのYAMLをリポジトリに含める必要がある • 機密情報が漏洩する事故を防ぐには? – 外部のAPIキー等 →暗号化して安全に扱う: cf. 「GitOpsでも秘匿情報をバッチリ扱う方法、SealedSecretsとは?」

    – クラウドの認証 →シークレットを生成しない認証: GKEでは Workload Identity でGCPのService accountとKubernetesの Service accountをリンクすることができる • VM(GCE)の認証情報を使うことも出来るが、複数のPodの権限を分離できず 権限が次第に肥大化すると思われるので採用せず – 単にランダムであればよいもの(セッションの暗号化キー等)はTerraformでラ ンダム文字列生成→直接Secretに設定している 10
  11. IV. バックエンドサービス 「バックエンドサービスをアタッチされたリソースとして扱う」 • 外部の依存するもの(ストレージ、API…)と疎結合にして コードを変更せずに切り替えられるようにしておく • 例えばRDBにMySQLを使う場合 – III.

    に従うとDBの接続情報は環境変数に格納 – 単に環境変数を変えて、開発時はローカルのDockerコンテナ、試験や本番では マネージドのMySQLインスタンスへ接続できるように 11
  12. ステージ 誰が 何を ビルド CIサービス (例: Circle CI) • Goのソースをビルドする

    • Dockerイメージを作る リリース GitOpsツール (Tekton) • リリース(環境設定とビルドされたものの組。 KubernetesではDeploymentに相当する)を更新 実行 Scheduler kubelet • Deploymentの記述に沿ってコンテナを起動する V. ビルド、リリース、実行 「ビルド、リリース、実行の3つのステージを厳密に分離する」 後戻りは出来ない(=実行中のVMにSSHして変更はNG) 12
  13. VI. プロセス 「アプリケーションを1つもしくは複数のステートレスなプロセスとして実行す る」 • 複数のプロセスが密接に連携する場合: 各プロセスごとにDockerイメー ジを作り、Podに複数のコンテナを指定する – Design

    patterns for container-based distributed systems でいうSidecar等 • ステートレス:コンテナが動いているVMのメモリやディスクに永続的な データを保存しない。セッション情報はRedis (Cloud Memorystore)に 保存する。作業用に一時ファイルをディスクに書き出すのはOK.元ファイ ルはCloud Storageに置く。 • これでLBのセッションスティッキー等を設定せずに スケールイン・アウトができる 13
  14. VII. ポートバインディング 「ポートバインディングを通してサービスを公開する」 • 外部のアプリケーションサーバに依存しなくてもポートを 公開できること。Goの場合アプリケーションサーバを 必要としないので、自動的に満たす • Kubernetesでは、Deploymentの spec

    > template > spec > containers > (コンテナ) > ports で公開するポートを宣言できる – 仮想LBのServiceリソースと組み合わせて公開する 14 III. 接続先は 環境変数に VII. ポートを公開
  15. VIII. 並行性 「プロセスモデルによってスケールアウトする」 • 一つのプロセスを前提として、その内でスレッドを増やしてスケールす ると、VMのスペック以上にスケールアップできなくなる – KubernetesではDeploymentの spec >

    replicas でPodの数を指定できる – Horizontal Pod Autoscalerでオートスケールさせることも可能 15
  16. IX. 廃棄容易性 「高速な起動とグレースフルシャットダウンで堅牢性を最大化する」 • 今コンテナが起動していないVMでの起動も含む – Dockerイメージサイズが小さい(=ダウンロードが早く済む)のがよい – Dockerのマルチステージビルドを使って、実行時に不必要なファイル(コンパ イラやgitクライアント、エディタ…)が含まれないようにする

    – Goの場合、単一のバイナリができるので、それをビルドステージから本番用の ステージへコピーすれば済む • Podの終了直前に実行されるpreHookが指定可能なので、必要なファ イルを退避する等の処理を行う。デフォルトの時間で間に合わない場 合は伸ばす 16
  17. IX. 廃棄容易性(続き) • 仮に突然終了したとしてもデータがロストしないような設計 – 非同期処理のキューイングにCloud Pub/Sub (pull)を使い、メッセージを送っ たワーカーからの応答がしばらくなければ、他のワーカーへ メッセージが再送されるように

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

    minikubeを使って一式作ろうとしたが、minikube特 有の不具合にはまり、使い慣れたComposeにした – 独自仕様のマネージドサービス: Pub/SubやBigQuery • エミュレータがなかったり、あっても一部機能が提供されていない ことがある • Telepresenceを使って本物のクラスタで開発が進められる 18
  19. • Telepresenceとは – クラスタ内アプリのデバッグツール • 特徴 – ローカルプロセスとKubernetesクラスタをプロキシし統合 • GitHub

    – ★2,111 | https://github.com/telepresenceio/telepresence • 言語 – Python 3.5+ • 開発主体 – 米国 Datawire社 • 類似/関連プロダクト – ローカル開発クラスタ: minikube, Docker compose – リモートコンテナへ接続: VS Code Remote Development – クラスタへプロキシ: kubectl port-forward 19
  20. 20 ☁ Telepresenceでコンテナを差し替える curl datawire/telepresence-k8s ラベルやポートは元の Deployから 引き継がれServiceの設定変更は不要 backendURL =

    http://backend-2:8080; ※本来は環境変数に入れるべき内容ではある backend-1 nginx backend-2 Apache ローカルコンテナから クラスタ内DNS名の名前解決も可能 元のDeployment (replicas=0)
  21. XI. ログ 「ログをイベントストリームとして扱う」 • アプリの責務は標準出力に書き出すところまで • 下記のロガーを使用 – uber-go/zap •

    構造化されたログを出力することができる – tommy351/zap-stackdriver • zapのメソッドとStackdriverのログレベルを対応させるために使用 • Stackdriver Loggingが裏でいろいろしてくれる – BigQueryへログをコピーしてBigQueryの強力な検索性能を使える – コンテナ名等のメタデータをログに付与 • コンテナごとのエラーレートを集計し、アラートを設定 21
  22. XII. 管理プロセス 「管理タスクを1回限りのプロセスとして実行する」 • 管理タスクの例: DBスキーマのマイグレーション – golang-migrate/migrateを使っている • Kubernetesでのサポート

    – Jobリソース • 一度だけ起動するPod – Argo CDのHookはデプロイ前・後・失敗時の それぞれの動作を指定できるので、失敗時に切り戻すこともできる 22
  23. Fin. 23