Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

●Development over the Cloud ● 「クラウドの上で」開発

Slide 3

Slide 3 text

先日のCloudNative Days Tokyo 2019にて 先進的な・強い事例が数多く紹介された • Operator作成 • CNCFプロダクト活用 • サービスメッシュ • ベンダーロックインされないインフラ… サーバサイドエンジニアはどこに向かえばいいの? パブリッククラウドを前提とする場合は? Cloud Nativeなアプリ設計に近づく方法を考えてみた 3

Slide 4

Slide 4 text

The Twelve-Factor App • HerokuのFounder & CTO, Adam Wiggins氏らによる アプリケーションの設計論 – 「Twelve-Factorの方法論は、どのようなプログラミング言語で書かれたアプリ ケーションにでも適用できる」とある通り、具体的な実装例と言うよりは抽象 的、方向性を示すもの • 実際の構成で12個の項目を具体化した例を紹介 4

Slide 5

Slide 5 text

具体的な構成要素 Kubernetesクラスタ: Kubernetes Engine • 他のGCPマネージドサービスとの親和性 5 Kubernetes Engine プログラミング言語: Go • 型サポート、単一のバイナリができる扱いやすさ インフラ構成管理: Terraform • GCP公式ツールより事例が多い

Slide 6

Slide 6 text

I. コードベース 「バージョン管理されている1つのコードベースと複数のデプロイ」 具体的には? • Single source of truthとなるGitリポジトリのブランチ戦略を決める – 本番用、試験用、開発用…はブランチで分離し、リポジトリは分けない • Twelve-Factor Appではアプリをスコープにしているが、 インフラやクラスタにも適用可能 – Kubernetesのマニフェスト: GitOps(前回のKubernetes meetupのテーマ) – Terraformの適用: Atlantis 6

Slide 7

Slide 7 text

• 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) ブランチ戦略 …

Slide 8

Slide 8 text

II. 依存関係 「依存関係を明示的に宣言し分離する」 • Goのライブラリ: Go modulesを使い依存関係を管理 • それ以外のCLIツール等: Dockerfileに記述、暗黙の依存を防止 – Dockerをパッケージングツールとして扱う – ルート証明書やタイムゾーンファイルは最低限必要 8

Slide 9

Slide 9 text

III. 設定 「設定を環境変数に格納する」 • Kubernetesの専用リソースかDeploymentに記述 – ConfigMap – Secret – Deployment > spec > template > spec > containers > (コンテナ) > env • caarlos0/env を使って設定を保持する構造体へマッピング – Go組み込みの数値型の他、time.Durationやurl.URLへの変換が行える – デフォルト値の設定も可能 9

Slide 10

Slide 10 text

機密情報の扱い • GitOpsではSecretのYAMLをリポジトリに含める必要がある • 機密情報が漏洩する事故を防ぐには? – 外部のAPIキー等 →暗号化して安全に扱う: cf. 「GitOpsでも秘匿情報をバッチリ扱う方法、SealedSecretsとは?」 – クラウドの認証 →シークレットを生成しない認証: GKEでは Workload Identity でGCPのService accountとKubernetesの Service accountをリンクすることができる • VM(GCE)の認証情報を使うことも出来るが、複数のPodの権限を分離できず 権限が次第に肥大化すると思われるので採用せず – 単にランダムであればよいもの(セッションの暗号化キー等)はTerraformでラ ンダム文字列生成→直接Secretに設定している 10

Slide 11

Slide 11 text

IV. バックエンドサービス 「バックエンドサービスをアタッチされたリソースとして扱う」 • 外部の依存するもの(ストレージ、API…)と疎結合にして コードを変更せずに切り替えられるようにしておく • 例えばRDBにMySQLを使う場合 – III. に従うとDBの接続情報は環境変数に格納 – 単に環境変数を変えて、開発時はローカルのDockerコンテナ、試験や本番では マネージドのMySQLインスタンスへ接続できるように 11

Slide 12

Slide 12 text

ステージ 誰が 何を ビルド CIサービス (例: Circle CI) ● Goのソースをビルドする ● Dockerイメージを作る リリース GitOpsツール (Tekton) ● リリース(環境設定とビルドされたものの組。 KubernetesではDeploymentに相当する)を更新 実行 Scheduler kubelet ● Deploymentの記述に沿ってコンテナを起動する V. ビルド、リリース、実行 「ビルド、リリース、実行の3つのステージを厳密に分離する」 後戻りは出来ない(=実行中のVMにSSHして変更はNG) 12

Slide 13

Slide 13 text

VI. プロセス 「アプリケーションを1つもしくは複数のステートレスなプロセスとして実行す る」 • 複数のプロセスが密接に連携する場合: 各プロセスごとにDockerイメー ジを作り、Podに複数のコンテナを指定する – Design patterns for container-based distributed systems でいうSidecar等 • ステートレス:コンテナが動いているVMのメモリやディスクに永続的な データを保存しない。セッション情報はRedis (Cloud Memorystore)に 保存する。作業用に一時ファイルをディスクに書き出すのはOK.元ファイ ルはCloud Storageに置く。 • これでLBのセッションスティッキー等を設定せずに スケールイン・アウトができる 13

Slide 14

Slide 14 text

VII. ポートバインディング 「ポートバインディングを通してサービスを公開する」 • 外部のアプリケーションサーバに依存しなくてもポートを 公開できること。Goの場合アプリケーションサーバを 必要としないので、自動的に満たす • Kubernetesでは、Deploymentの spec > template > spec > containers > (コンテナ) > ports で公開するポートを宣言できる – 仮想LBのServiceリソースと組み合わせて公開する 14 III. 接続先は 環境変数に VII. ポートを公開

Slide 15

Slide 15 text

VIII. 並行性 「プロセスモデルによってスケールアウトする」 • 一つのプロセスを前提として、その内でスレッドを増やしてスケールす ると、VMのスペック以上にスケールアップできなくなる – KubernetesではDeploymentの spec > replicas でPodの数を指定できる – Horizontal Pod Autoscalerでオートスケールさせることも可能 15

Slide 16

Slide 16 text

IX. 廃棄容易性 「高速な起動とグレースフルシャットダウンで堅牢性を最大化する」 • 今コンテナが起動していないVMでの起動も含む – Dockerイメージサイズが小さい(=ダウンロードが早く済む)のがよい – Dockerのマルチステージビルドを使って、実行時に不必要なファイル(コンパ イラやgitクライアント、エディタ…)が含まれないようにする – Goの場合、単一のバイナリができるので、それをビルドステージから本番用の ステージへコピーすれば済む • Podの終了直前に実行されるpreHookが指定可能なので、必要なファ イルを退避する等の処理を行う。デフォルトの時間で間に合わない場 合は伸ばす 16

Slide 17

Slide 17 text

IX. 廃棄容易性(続き) • 仮に突然終了したとしてもデータがロストしないような設計 – 非同期処理のキューイングにCloud Pub/Sub (pull)を使い、メッセージを送っ たワーカーからの応答がしばらくなければ、他のワーカーへ メッセージが再送されるように • アプリがこの性質を満たすと、起動時間に上限がある代わりに安いプリ エンプティブルVMを使える – 開発環境のコストが劇的に下がる(約80%off) 17 Cloud Pub/Sub Pub/SubではメッセージのACKを期限内に送る必要がある。 ワーカーが生きていると、 SDKが自動的にACKの期限延長 リクエストを送信する=ハートビートの役割を果たす

Slide 18

Slide 18 text

X. 開発/本番一致 「開発、ステージング、本番環境をできるだけ一致させた状態を保つ」 • 時間・担当者・ツール(ミドルウェア)を近づける – OSSのRDB: Dockerを使えばをローカルで起動できる – Kubernetes環境: minikubeを使って一式作ろうとしたが、minikube特 有の不具合にはまり、使い慣れたComposeにした – 独自仕様のマネージドサービス: Pub/SubやBigQuery • エミュレータがなかったり、あっても一部機能が提供されていない ことがある • Telepresenceを使って本物のクラスタで開発が進められる 18

Slide 19

Slide 19 text

• 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

Slide 20

Slide 20 text

20 ☁ Telepresenceでコンテナを差し替える curl datawire/telepresence-k8s ラベルやポートは元の Deployから 引き継がれServiceの設定変更は不要 backendURL = http://backend-2:8080; ※本来は環境変数に入れるべき内容ではある backend-1 nginx backend-2 Apache ローカルコンテナから クラスタ内DNS名の名前解決も可能 元のDeployment (replicas=0)

Slide 21

Slide 21 text

XI. ログ 「ログをイベントストリームとして扱う」 • アプリの責務は標準出力に書き出すところまで • 下記のロガーを使用 – uber-go/zap • 構造化されたログを出力することができる – tommy351/zap-stackdriver • zapのメソッドとStackdriverのログレベルを対応させるために使用 • Stackdriver Loggingが裏でいろいろしてくれる – BigQueryへログをコピーしてBigQueryの強力な検索性能を使える – コンテナ名等のメタデータをログに付与 • コンテナごとのエラーレートを集計し、アラートを設定 21

Slide 22

Slide 22 text

XII. 管理プロセス 「管理タスクを1回限りのプロセスとして実行する」 • 管理タスクの例: DBスキーマのマイグレーション – golang-migrate/migrateを使っている • Kubernetesでのサポート – Jobリソース • 一度だけ起動するPod – Argo CDのHookはデプロイ前・後・失敗時の それぞれの動作を指定できるので、失敗時に切り戻すこともできる 22

Slide 23

Slide 23 text

Fin. 23