序章

最新のステートレスアプリケーションは、Dockerなどのソフトウェアコンテナで実行するように構築および設計されており、Kubernetesなどのコンテナクラスタによって管理されます。 これらは、 CloudNativeおよびTwelveFactor の原則とパターンを使用して開発されており、手動による介入を最小限に抑え、移植性と冗長性を最大限に高めます。 仮想マシンまたはベアメタルベースのアプリケーションをコンテナに移行し(「コンテナ化」と呼ばれます)、それらをクラスタ内にデプロイすると、多くの場合、これらのアプリの構築、パッケージ化、および配信の方法が大幅に変更されます。

Kubernetes用アプリケーションのアーキテクチャに基づいて、この概念ガイドでは、アプリケーションをKubernetesクラスタで実行および管理することを最終目標として、アプリケーションを最新化するための高レベルの手順について説明します。 Kubernetesでデータベースなどのステートフルアプリケーションを実行できますが、このガイドでは、永続データを外部データストアにオフロードして、ステートレスアプリケーションの移行と最新化に焦点を当てています。 Kubernetesは、ステートレスアプリケーションを効率的に管理およびスケーリングするための高度な機能を提供します。ここでは、Kubernetesでスケーラブルで監視可能なポータブルアプリを実行するために必要なアプリケーションとインフラストラクチャの変更について説明します。

移行のためのアプリケーションの準備

アプリケーションをコンテナ化する前、またはKubernetes PodとDeploymentの設定ファイルを作成する前に、アプリケーションレベルの変更を実装して、Kubernetesでのアプリの移植性と可観測性を最大化する必要があります。 Kubernetesは高度に自動化された環境であり、障害が発生したアプリケーションコンテナを自動的にデプロイして再起動できるため、適切なアプリケーションロジックを組み込んでコンテナオーケストレータと通信し、必要に応じてアプリを自動的にスケーリングできるようにすることが重要です。

構成データの抽出

実装する最初のアプリケーションレベルの変更の1つは、アプリケーションコードからアプリケーション構成を抽出することです。 構成は、サービスエンドポイント、データベースアドレス、クレデンシャル、さまざまなパラメーターやオプションなど、展開や環境によって異なる情報で構成されます。 たとえば、2つの環境がある場合、次のように言います。 stagingproduction、およびそれぞれに個別のデータベースが含まれている場合、アプリケーションでは、データベースエンドポイントと資格情報をコードで明示的に宣言するのではなく、実行環境の変数、ローカルファイル、または外部のKey-Valueストアのいずれかとして個別の場所に保存する必要があります。そこから値がアプリに読み込まれます。

これらのパラメーターをコードにハードコーディングすると、セキュリティ上のリスクが発生します。これは、この構成データが機密情報で構成されていることが多く、その情報をバージョン管理システムにチェックインするためです。 また、アプリケーションの複数のバージョンを維持する必要があるため、複雑さが増します。各バージョンは同じコアアプリケーションロジックで構成されていますが、構成がわずかに異なります。 アプリケーションとその構成データが大きくなるにつれて、構成をアプリコードにハードコーディングするとすぐに扱いにくくなります。

アプリケーションコードから構成値を抽出し、実行環境またはローカルファイルからそれらを取り込むことで、アプリは、付属の構成データを提供することで、任意の環境にデプロイできる汎用のポータブルパッケージになります。 DockerのようなコンテナーソフトウェアとKubernetesのようなクラスターソフトウェアは、このパラダイムを中心に設計されており、構成データを管理してアプリケーションコンテナーに挿入するための機能が組み込まれています。 これらの機能については、ContainerizingおよびKubernetesセクションで詳しく説明します。

これは、2つの構成値を外部化する方法を示す簡単な例です。 DB_HOSTDB_USER Python Flaskアプリのコードから。 それらをアプリの実行環境でenvvarsとして利用できるようにし、そこからアプリがそれらを読み取ります。

hardcoded_config.py
from flask import Flask

DB_HOST = 'mydb.mycloud.com'
DB_USER = 'sammy'

app = Flask(__name__)

@app.route('/')
def print_config():
    output = 'DB_HOST: {} -- DB_USER: {}'.format(DB_HOST, DB_USER)
    return output

このアプリを実行し(方法については、 Flask Quickstart を参照してください)、そのWebエンドポイントにアクセスすると、これら2つの構成値を含むページが表示されます。

次に、アプリの実行環境に外部化された構成値を使用した同じ例を示します。

env_config.py
import os

from flask import Flask

DB_HOST = os.environ.get('APP_DB_HOST')
DB_USER = os.environ.get('APP_DB_USER')

app = Flask(__name__)

@app.route('/')
def print_config():
    output = 'DB_HOST: {} -- DB_USER: {}'.format(DB_HOST, DB_USER)
    return output

アプリを実行する前に、ローカル環境で必要な構成変数を設定します。

  1. export APP_DB_HOST=mydb.mycloud.com
  2. export APP_DB_USER=sammy
  3. flask run

表示されるWebページには最初の例と同じテキストが含まれているはずですが、アプリの構成はアプリケーションコードとは関係なく変更できるようになりました。 同様のアプローチを使用して、ローカルファイルから構成パラメーターを読み込むことができます。

次のセクションでは、アプリケーションの状態をコンテナの外に移動する方法について説明します。

アプリケーションの状態をオフロード

クラウドネイティブアプリケーションはコンテナーで実行され、KubernetesやDockerSwarmなどのクラスターソフトウェアによって動的に調整されます。 特定のアプリまたはサービスは、複数のレプリカ間で負荷分散でき、個々のアプリコンテナーは、クライアントのサービスの中断を最小限に抑えて、またはまったく中断することなく、障害を発生させることができるはずです。 この水平で冗長なスケーリングを有効にするには、アプリケーションをステートレスな方法で設計する必要があります。 つまり、永続的なクライアントとアプリケーションのデータをローカルに保存せずにクライアントのリクエストに応答し、実行中のアプリコンテナが破棄または再起動されても、重要なデータが失われることはありません。

たとえば、名簿アプリケーションを実行していて、アプリが名簿から連絡先を追加、削除、変更する場合、名簿データストアは外部データベースまたは他のデータストアである必要があり、コンテナメモリに保持されるデータは次のとおりです。本質的に短期的であり、情報を重大に失うことなく使い捨て可能です。 セッションなどのユーザー訪問を超えて保持されるデータも、Redisなどの外部データストアに移動する必要があります。 可能な限り、状態をアプリからマネージドデータベースやキャッシュなどのサービスにオフロードする必要があります。

永続的なデータストアを必要とするステートフルアプリケーション(複製されたMySQLデータベースなど)の場合、Kubernetesには、永続的なブロックストレージボリュームをコンテナーとポッドにアタッチするための機能が組み込まれています。 ポッドが状態を維持し、再起動後に同じ永続ボリュームにアクセスできるようにするには、StatefulSetワークロードを使用する必要があります。 StatefulSetsは、データベースやその他の長時間実行されるデータストアをKubernetesにデプロイするのに理想的です。

ステートレスコンテナは、最大限の移植性と利用可能なクラウドリソースのフル活用を可能にし、Kubernetesスケジューラがアプリをすばやくスケールアップおよびスケールダウンし、リソースが利用可能な場所であればどこでもポッドを起動できるようにします。 StatefulSetワークロードによって提供される安定性と順序の保証が必要ない場合は、Deploymentワークロードを使用してアプリケーションを管理およびスケーリングする必要があります。

ステートレスなクラウドネイティブマイクロサービスの設計とアーキテクチャの詳細については、Kubernetesホワイトペーパーをご覧ください。

ヘルスチェックを実装する

Kubernetesモデルでは、クラスターコントロールプレーンを使用して、壊れたアプリケーションやサービスを修復できます。 これは、アプリケーションポッドの状態をチェックし、異常なコンテナまたは応答しないコンテナを再起動または再スケジュールすることによって行われます。 デフォルトでは、アプリケーションコンテナが実行されている場合、Kubernetesはポッドを「正常」と見なします。 多くの場合、これは実行中のアプリケーションの状態を示す信頼できる指標です。 ただし、アプリケーションがデッドロックして意味のある作業を実行していない場合、アプリプロセスとコンテナは無期限に実行され続け、デフォルトでは、Kubernetesは停止したコンテナを存続させます。

アプリケーションの正常性をKubernetesコントロールプレーンに適切に伝達するには、アプリケーションが実行中であり、トラフィックを受信する準備ができていることを示すカスタムアプリケーションの正常性チェックを実装する必要があります。 最初のタイプのヘルスチェックは準備プローブと呼ばれ、アプリケーションがトラフィックを受信する準備ができたことをKubernetesに通知します。 2番目のタイプのチェックはライブネスプローブと呼ばれ、アプリケーションが正常で実行されていることをKubernetesに通知します。 Kubelet Nodeエージェントは、次の3つの異なる方法を使用して、実行中のポッドでこれらのプローブを実行できます。

  • HTTP:Kubeletプローブは、エンドポイントに対してHTTP GETリクエストを実行します( /health)、応答ステータスが200〜399の場合に成功します
  • コンテナコマンド:Kubeletプローブは、実行中のコンテナ内でコマンドを実行します。 終了コードが0の場合、プローブは成功します。
  • TCP:Kubeletプローブは、指定されたポートでコンテナーに接続しようとします。 TCP接続を確立できる場合、プローブは成功します。

実行中のアプリケーション、プログラミング言語、およびフレームワークに応じて、適切な方法を選択する必要があります。 レディネスプローブとライブネスプローブはどちらも同じプローブ方法を使用して同じチェックを実行できますが、レディネスプローブを含めると、プローブが成功し始めるまでポッドがトラフィックを受信しないようになります。

アプリケーションをコンテナ化してKubernetesで実行することを計画および検討するときは、特定のアプリケーションの「正常」と「準備完了」の意味を定義するための計画時間と、エンドポイントやチェックコマンドの実装とテストのための開発時間を割り当てる必要があります。

上記で参照したFlaskの例の最小ヘルスエンドポイントは次のとおりです。

env_config.py
. . .  
@app.route('/')
def print_config():
    output = 'DB_HOST: {} -- DB_USER: {}'.format(DB_HOST, DB_USER)
    return output

@app.route('/health')
def return_ok():
    return 'Ok!', 200

このパスをチェックするKubernetesライブネスプローブは、次のようになります。

pod_spec.yaml
. . .
  livenessProbe:
      httpGet:
        path: /health
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 2

The initialDelaySeconds フィールドは、Kubernetes(具体的にはノードKubelet)がプローブする必要があることを指定します /health 5秒待った後のエンドポイント、および periodSeconds Kubeletにプローブするように指示します /health 2秒ごと。

活性と準備のプローブの詳細については、Kubernetesのドキュメントを参照してください。

ロギングとモニタリングのための機器コード

コンテナ化されたアプリケーションをKubernetesのような環境で実行する場合、アプリケーションのパフォーマンスを監視およびデバッグするために、テレメトリとログデータを公開することが重要です。 応答時間やエラー率などのパフォーマンスメトリックを公開する機能を組み込むと、アプリケーションを監視し、アプリケーションが異常な場合にアラートを出すのに役立ちます。

サービスの監視に使用できるツールの1つは、Cloud Native Computing Foundation(CNCF)によってホストされるオープンソースのシステム監視およびアラートツールキットであるPrometheusです。 Prometheusは、イベントとその期間をカウントするためのさまざまなメトリックタイプを使用してコードをインストルメント化するためのいくつかのクライアントライブラリを提供します。 たとえば、Flask Pythonフレームワークを使用している場合は、Prometheus Pythonクライアントを使用してデコレータをリクエスト処理関数に追加し、リクエストの処理に費やされた時間を追跡できます。 これらのメトリックは、次のようなHTTPエンドポイントでPrometheusによってスクレイプできます。 /metrics.

アプリのインストルメンテーションを設計するときに使用するのに役立つ方法は、REDメソッドです。 これは、次の3つの主要な要求メトリックで構成されています。

  • レート:アプリケーションが受信したリクエストの数
  • エラー:アプリケーションによって発行されたエラーの数
  • 期間:アプリケーションが応答を提供するのにかかる時間

この最小限のメトリックセットは、アプリケーションのパフォーマンスが低下したときにアラートを生成するのに十分なデータを提供するはずです。 上記のヘルスチェックとともにこのインストルメンテーションを実装すると、障害のあるアプリケーションをすばやく検出して回復できます。

アプリケーションを監視するときに測定する信号の詳細については、Googleサイト信頼性エンジニアリングの本の分散システムの監視を参照してください。

テレメトリデータを公開するための機能について考え、設計することに加えて、アプリケーションが分散クラスターベースの環境にログインする方法も決定する必要があります。 理想的には、ローカルログファイルとログディレクトリへのハードコードされた構成参照を削除し、代わりに直接ログに記録する必要があります。 stdoutstderr. ログは、連続したイベントストリーム、または時間順に並べられたイベントのシーケンスとして扱う必要があります。 この出力ストリームは、アプリケーションを包むコンテナーによってキャプチャされ、そこからEFK(Elasticsearch、Fluentd、Kibana)スタックなどのロギングレイヤーに転送できます。 Kubernetesは、ロギングアーキテクチャの設計に多くの柔軟性を提供します。これについては、以下で詳しく説明します。

管理ロジックをAPIに組み込む

アプリケーションがコンテナ化され、Kubernetesなどのクラスタ環境で稼働すると、アプリを実行しているコンテナへのシェルアクセスができなくなる可能性があります。 適切なヘルスチェック、ログ記録、および監視を実装している場合は、本番環境の問題をすばやく警告してデバッグできますが、コンテナーの再起動と再デプロイ以外のアクションを実行するのは難しい場合があります。 キューのフラッシュやキャッシュのクリアなどの運用とメンテナンスの迅速な修正を行うには、適切なAPIエンドポイントを実装して、コンテナを再起動したり、 docker exec 実行中のコンテナに。 コンテナは不変オブジェクトとして扱う必要があり、本番環境では手動管理を避ける必要があります。 キャッシュのクリアなど、1回限りの管理タスクを実行する必要がある場合は、APIを介してこの機能を公開する必要があります。

概要

これらのセクションでは、アプリケーションをコンテナ化してKubernetesに移動する前に、実装する可能性のあるアプリケーションレベルの変更について説明しました。 クラウドネイティブアプリの構築に関するより詳細なウォークスルーについては、Kubernetes用アプリケーションのアーキテクチャを参照してください。

ここで、アプリのコンテナーを作成するときに留意すべきいくつかの考慮事項について説明します。

アプリケーションのコンテナ化

クラウドベースの環境で移植性と可観測性を最大化するためのアプリロジックを実装したので、次はアプリをコンテナー内にパッケージ化します。 このガイドでは、Dockerコンテナーを使用しますが、本番環境のニーズに最適なコンテナー実装を使用する必要があります。

依存関係を明示的に宣言する

アプリケーションのDockerfileを作成する前に、最初のステップの1つは、アプリケーションを正しく実行するために必要なソフトウェアとオペレーティングシステムの依存関係を確認することです。 Dockerfileを使用すると、イメージにインストールされているすべてのソフトウェアを明示的にバージョン管理できます。この機能を利用するには、親イメージ、ソフトウェアライブラリ、およびプログラミング言語のバージョンを明示的に宣言する必要があります。

避ける latest タグとバージョン管理されていないパッケージは、シフトしてアプリケーションを壊す可能性があるため、可能な限りシフトします。 プライベートレジストリまたはパブリックレジストリのプライベートミラーを作成して、イメージのバージョン管理をより詳細に制御し、アップストリームの変更によって意図せずにイメージビルドが破損するのを防ぐことができます。

プライベートイメージレジストリの設定の詳細については、Dockerの公式ドキュメントおよび以下のレジストリセクションからレジストリサーバーのデプロイを参照してください。

画像サイズを小さく保つ

コンテナイメージをデプロイおよびプルする場合、大きなイメージは処理速度を大幅に低下させ、帯域幅のコストを増加させる可能性があります。 最小限のツールとアプリケーションファイルのセットをイメージにパッケージ化すると、いくつかの利点があります。

  • 画像サイズを小さくする
  • イメージビルドを高速化
  • コンテナの開始ラグを減らす
  • 画像転送時間を短縮します
  • 攻撃対象領域を減らすことでセキュリティを向上させる

イメージを作成するときに考慮できるいくつかの手順:

  • 次のような最小限のベースOSイメージを使用します alpine またはから構築する scratch のようなフル機能のOSの代わりに ubuntu
  • ソフトウェアのインストール後、不要なファイルやアーティファクトをクリーンアップします
  • 個別の「ビルド」コンテナと「ランタイム」コンテナを使用して、本番アプリケーションコンテナを小さく保ちます
  • 大きなディレクトリにコピーするときに、不要なビルドアーティファクトとファイルを無視します

多くの実例を含むDockerコンテナーの最適化に関する完全なガイドについては、Kubernetes用に最適化されたコンテナーの構築を参照してください。

構成を挿入

Dockerは、アプリの実行環境に構成データを挿入するためのいくつかの便利な機能を提供します。

これを行うための1つのオプションは、Dockerfileで環境変数とその値を指定することです。 ENV ステートメント。構成データがイメージに組み込まれるようにします。

Dockerfile
...
ENV MYSQL_USER=my_db_user
...

その後、アプリは実行環境からこれらの値を解析し、設定を適切に構成できます。

を使用してコンテナを起動するときに、環境変数をパラメータとして渡すこともできます。 docker run そしてその -e 国旗:

  1. docker run -e MYSQL_USER='my_db_user' IMAGE[:TAG]

最後に、環境変数とその値のリストを含むenvファイルを使用できます。 これを行うには、ファイルを作成し、 --env-file コマンドに渡すパラメータ:

  1. docker run --env-file var_list IMAGE[:TAG]

Kubernetesなどのクラスターマネージャーを使用してアプリケーションを実行するようにアプリケーションを最新化する場合は、イメージから構成をさらに外部化し、Kubernetesの組み込みのConfigMapSecretsを使用して構成を管理する必要があります]オブジェクト。 これにより、構成をイメージマニフェストから分離できるため、アプリケーションとは別に構成を管理およびバージョン管理できます。 ConfigMapsとSecretsを使用して構成を外部化する方法については、以下のConfigMapsとSecretsセクションを参照してください。

レジストリに画像を公開する

アプリケーションイメージをビルドしたら、Kubernetesで利用できるようにするには、それらをコンテナイメージレジストリにアップロードする必要があります。 Docker Hub などのパブリックレジストリは、Node.jsnginxなどの人気のあるオープンソースプロジェクトの最新のDockerイメージをホストします。 プライベートレジストリを使用すると、内部アプリケーションイメージを公開して、開発者やインフラストラクチャで利用できるようになりますが、より広い世界では利用できません。

既存のインフラストラクチャを使用してプライベートレジストリを展開できます(例: クラウドオブジェクトストレージの上に)、またはオプションでQuay.ioや有料のDockerHubプランなどのいくつかのDockerレジストリ製品の1つを使用します。 これらのレジストリは、GitHubなどのホストされたバージョン管理サービスと統合できるため、Dockerfileが更新およびプッシュされると、レジストリサービスは自動的に新しいDockerfileをプルし、コンテナイメージを構築し、更新されたイメージをサービスで利用できるようにします。

コンテナイメージの構築とテスト、およびそれらのタグ付けと公開をより細かく制御するために、継続的インテグレーション(CI)パイプラインを実装できます。

ビルドパイプラインを実装する

イメージを手動で構築、テスト、公開、本番環境にデプロイすると、エラーが発生しやすくなり、拡張性が低下します。 ビルドを管理し、最新のコード変更を含むコンテナーをイメージレジストリに継続的に公開するには、ビルドパイプラインを使用する必要があります。

ほとんどのビルドパイプラインは、次のコア機能を実行します。

  • ソースコードリポジトリで変更を確認する
  • 変更されたコードで煙と単体テストを実行する
  • 変更されたコードを含むコンテナイメージをビルドする
  • ビルドされたコンテナイメージを使用して、さらに統合テストを実行します
  • テストに合格したら、画像にタグを付けてレジストリに公開します
  • (オプション、継続的デプロイのセットアップで)Kubernetesデプロイメントを更新し、イメージをステージング/本番クラスターにロールアウトします

GitHubなどの一般的なバージョン管理サービスやDockerHubなどのイメージレジストリとの統合が組み込まれている、多くの有料の継続的インテグレーション製品があります。 これらの製品の代替品は、 Jenkins です。これは、上記のすべての機能を実行するように構成できる、無料のオープンソースビルド自動化サーバーです。 Jenkins継続的インテグレーションパイプラインを設定する方法については、 Ubuntu20.04でJenkinsに継続的インテグレーションパイプラインを設定する方法を参照してください。

コンテナのログ記録と監視を実装する

コンテナを操作するときは、実行中および停止中のすべてのコンテナのログを管理および保存するために使用するロギングインフラストラクチャについて考えることが重要です。 ロギングに使用できるコンテナレベルのパターンは複数あり、Kubernetesレベルのパターンも複数あります。

Kubernetesでは、デフォルトでコンテナは json-file Docker ログドライバー。stdoutストリームとstderrストリームをキャプチャし、コンテナーが実行されているノード上のJSONファイルに書き込みます。 stderrおよびstdoutに直接ログを記録するだけでは、アプリケーションコンテナーに十分でない場合があります。また、アプリコンテナーをKubernetesポッドのロギングsidecarコンテナーとペアリングすることをお勧めします。 このサイドカーコンテナは、ファイルシステム、ローカルソケット、またはsystemdジャーナルからログを取得できるため、stderrおよびstdoutストリームを使用するよりも少し柔軟性があります。 このコンテナは、いくつかの処理を実行してから、強化されたログをstdout / stderrにストリーミングしたり、ロギングバックエンドに直接ストリーミングしたりすることもできます。 Kubernetesのロギングパターンの詳細については、このチュートリアルのKubernetesのロギングとモニタリングセクションを参照してください。

アプリケーションがコンテナレベルでどのようにログを記録するかは、その複雑さに依存します。 単一目的のマイクロサービスの場合、stdout / stderrに直接ログを記録し、Kubernetesにこれらのストリームを取得させることをお勧めします。これにより、 kubectl logs Kubernetesでデプロイされたコンテナからログストリームにアクセスするコマンド。

ロギングと同様に、コンテナーおよびクラスターベースの環境での監視について考え始める必要があります。 Dockerは役立つ docker stats ホストでコンテナーを実行するためのCPUやメモリ使用量などの標準メトリックを取得するためのコマンド。リモートRESTAPIを介してさらに多くのメトリックを公開します。 さらに、オープンソースツール cAdvisor (デフォルトでKubernetesノードにインストールされます)は、履歴メトリック収集、メトリックデータのエクスポート、データを並べ替えるための便利なWebUIなどのより高度な機能を提供します。

ただし、マルチノード、マルチコンテナーの本番環境では、PrometheusGrafanaなどのより複雑なメトリックスタックが、コンテナーのパフォーマンスデータの整理と監視に役立つ場合があります。

概要

これらのセクションでは、コンテナを構築するためのいくつかのベストプラクティス、CI / CDパイプラインとイメージレジストリを設定するためのいくつかのベストプラクティス、およびコンテナへの可観測性を高めるためのいくつかの考慮事項について簡単に説明しました。

次のセクションでは、コンテナ化されたアプリをクラスタで実行およびスケーリングできるKubernetesの機能について説明します。

Kubernetesへのデプロイ

この時点で、アプリをコンテナー化し、クラウドネイティブ環境での移植性と可観測性を最大化するロジックを実装しました。 次に、Kubernetesクラスターでアプリを管理およびスケーリングするためのインターフェースを提供するKubernetes機能について説明します。

デプロイメントおよびポッド構成ファイルの書き込み

アプリケーションをコンテナ化してレジストリに公開すると、ポッドワークロードを使用してアプリケーションをKubernetesクラスタにデプロイできるようになります。 Kubernetesクラスターでデプロイ可能な最小のユニットは、コンテナーではなくポッドです。 ポッドは通常、アプリケーションコンテナー(コンテナー化されたFlask Webアプリなど)、またはアプリコンテナーと、監視やログ記録などのヘルパー機能を実行する「サイドカー」コンテナーで構成されます。 ポッド内のコンテナは、ストレージリソース、ネットワーク名前空間、およびポートスペースを共有します。 彼らはを使用して互いに通信することができます localhost マウントされたボリュームを使用してデータを共有できます。 さらに、ポッドワークロードを使用すると、メインのアプリコンテナーの実行を開始する前に、セットアップスクリプトまたはユーティリティを実行する InitContainersを定義できます。

ポッドは通常、デプロイメントを使用してロールアウトされます。デプロイメントは、特定の目的の状態を宣言するYAMLファイルによって定義されたコントローラーです。 たとえば、アプリケーションの状態は、Flask Webアプリコンテナの3つのレプリカを実行し、ポートを公開している可能性があります 8080. 作成されると、コントロールプレーンは、必要に応じてノードにコンテナーをスケジュールすることにより、クラスターの実際の状態を展開で宣言された目的の状態に徐々に一致させます。 クラスタで実行されているアプリケーションレプリカの数を、たとえば3から5にスケーリングするには、 replicas 展開構成ファイルのフィールド、次に kubectl apply 新しい構成ファイル。 これらの構成ファイルを使用すると、既存のソース管理サービスと統合を使用して、スケーリングと展開の操作をすべて追跡およびバージョン管理できます。

これがFlaskアプリのサンプルKubernetesDeployment構成ファイルです。

フラスコ展開.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-app
  labels:
    app: flask-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: flask-app
  template:
    metadata:
      labels:
        app: flask-app
    spec:
      containers:
      - name: flask
        image: sammy/flask_app:1.0
        ports:
        - containerPort: 8080

このデプロイメントは、と呼ばれるコンテナーを実行する3つのポッドを起動します flask を使用して sammy/flask_app 画像(バージョン 1.0)ポート付き 8080 開いた。 デプロイメントはと呼ばれます flask-app.

Kubernetes Pods and Deploymentsの詳細については、Kubernetesの公式ドキュメントのPodsおよびDeploymentsセクションを参照してください。

ポッドストレージを構成する

Kubernetesは、ボリューム、永続ボリューム(PV)、永続ボリュームクレーム(PVC)を使用してポッドストレージを管理します。 ボリュームは、ポッドストレージを管理するために使用されるKubernetesの抽象化であり、ほとんどのクラウドプロバイダーブロックストレージオファリングと、実行中のポッドをホストするノード上のローカルストレージをサポートします。 サポートされているボリュームタイプの完全なリストを確認するには、Kubernetesドキュメントを参照してください。

たとえば、ポッドに2つのNGINXコンテナが含まれていて、それらの間でデータを共有する必要がある場合(たとえば、最初のコンテナと呼ばれる nginx Webページを提供し、2番目の nginx-sync 外部の場所からページをフェッチし、によって提供されるページを更新します nginx コンテナ)、ポッドの仕様は次のようになります(ここでは emptyDir ボリュームタイプを使用します):

pod_volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: nginx-web
      mountPath: /usr/share/nginx/html
   
  - name: nginx-sync
    image: nginx-sync
    volumeMounts:
    - name: nginx-web
      mountPath: /web-data
  
  volumes:
  - name: nginx-web
    emptyDir: {}

私たちは使用します volumeMount コンテナごとに、マウントすることを示します nginx-web のWebページファイルを含むボリューム /usr/share/nginx/html の中に nginx コンテナとで /web-data の中に nginx-sync 容器。 また、 volume と呼ばれる nginx-web タイプの emptyDir.

同様の方法で、クラウドブロックストレージ製品を使用してポッドストレージを構成するには、 volume から入力 emptyDir 関連するクラウドストレージボリュームタイプに。

ボリュームのライフサイクルはポッドのライフサイクルに関連付けられていますが、コンテナのライフサイクルにはではありません。 ポッド内のコンテナが停止した場合、ボリュームは存続し、新しく起動されたコンテナは同じボリュームをマウントしてそのデータにアクセスできるようになります。 ポッドが再起動または停止すると、ボリュームも再起動します。ただし、ボリュームがクラウドブロックストレージで構成されている場合は、将来のポッドから引き続きアクセスできるデータでマウントが解除されます。

ポッドの再起動と更新の間でデータを保持するには、PersistentVolume(PV)オブジェクトとPersistentVolumeClaim(PVC)オブジェクトを使用する必要があります。

PersistentVolumesは、クラウドブロックストレージボリュームやNFSストレージなどの永続ストレージの一部を表す抽象化です。 これらは、開発者がストレージの一部を要求するPersistentVolumeClaimsとは別に作成されます。 ポッド構成では、開発者はPVCを使用して永続ストレージをリクエストします。これはKubernetesが利用可能なPVボリュームと一致します(クラウドブロックストレージを使用している場合、KubernetesはPersistentVolumeClaimsの作成時にPersistentVolumeを動的に作成できます)。

多くのデータベースの場合のように、アプリケーションでレプリカごとに1つの永続ボリュームが必要な場合は、デプロイメントを使用せずに、安定したネットワークID、安定した永続ストレージ、および順序の保証を必要とするアプリ用に設計されたStatefulSetコントローラーを使用する必要があります。 デプロイメントはステートレスアプリケーションに使用する必要があります。デプロイメント構成で使用するPersistentVolumeClaimを定義すると、そのPVCはデプロイメントのすべてのレプリカで共有されます。

StatefulSetコントローラーの詳細については、Kubernetesドキュメントを参照してください。 PersistentVolumesとPersistentVolumeクレームの詳細については、Kubernetesストレージドキュメントを参照してください。

Kubernetesを使用した構成データの挿入

Dockerと同様に、Kubernetesは envenvFrom ポッド構成ファイルで環境変数を設定するためのフィールド。 これは、ポッド構成ファイルからのサンプルスニペットであり、 HOSTNAME 実行中のポッドの環境変数 my_hostname :

sample_pod.yaml
...
    spec:
      containers:
      - name: nginx
        image: nginx:1.21.6
        ports:
        - containerPort: 80
        env:
        - name: HOSTNAME
          value: my_hostname
...

これにより、構成をDockerfilesからPodおよびDeployment構成ファイルに移動できます。 Dockerfileから構成をさらに外部化する主な利点は、これらのKubernetesワークロード構成を変更できることです(たとえば、 HOSTNAME の値 my_hostname_2)アプリケーションコンテナ定義とは別に。 ポッド構成ファイルを変更すると、新しい環境を使用してポッドを再デプロイできますが、基になるコンテナーイメージ(Dockerfileを介して定義)を再構築してテストし、リポジトリにプッシュする必要はありません。 これらのポッドとデプロイメントの構成をDockerfilesとは別にバージョン管理することもできます。これにより、重大な変更をすばやく検出し、構成の問題をアプリケーションのバグからさらに分離できます。

Kubernetesは、構成データをさらに外部化および管理するための別の構成要素であるConfigMapsとSecretsを提供します。

ConfigMapsとシークレット

ConfigMapsを使用すると、構成データをオブジェクトとして保存し、ポッドおよびデプロイメント構成ファイルで参照できるため、構成データのハードコーディングを回避して、ポッドおよびデプロイメント全体で再利用できます。

上記のポッド構成を使用した例を次に示します。 最初に保存します HOSTNAME ConfigMapとして環境変数を設定し、ポッド構成で参照します。

  1. kubectl create configmap hostname --from-literal=HOSTNAME=my_host_name

ポッド構成ファイルから参照するには、 valueFromconfigMapKeyRef 構成:

sample_pod_configmap.yaml
...
    spec:
      containers:
      - name: nginx
        image: nginx:1.21.6
        ports:
        - containerPort: 80
        env:
        - name: HOSTNAME
          valueFrom:
            configMapKeyRef:
              name: hostname
              key: HOSTNAME
...

だから HOSTNAME 環境変数の値は、構成ファイルから完全に外部化されています。 次に、これらの変数を参照しているすべてのデプロイメントとポッドでこれらの変数を更新し、ポッドを再起動して変更を有効にします。

アプリケーションが構成ファイルを使用する場合、ConfigMapsを使用すると、これらのファイルをConfigMapオブジェクトとして保存できます( --from-file フラグ)、構成ファイルとしてコンテナにマウントできます。

シークレットはConfigMapsと同じ重要な機能を提供しますが、値はbase64でエンコードされているため、データベースのクレデンシャルなどの機密データに使用する必要があります。

ConfigMapsとSecretsの詳細については、Kubernetesドキュメントを参照してください。

サービスの作成

アプリケーションをKubernetesで起動して実行すると、すべてのポッドに(内部)IPアドレスが割り当てられ、コンテナーによって共有されます。 これらのポッドの1つが削除または停止した場合、新しく開始されたポッドには異なるIPアドレスが割り当てられます。

内部および/または外部クライアントに機能を公開する長時間実行サービスの場合、同じ機能(またはデプロイメント)を実行するポッドのセットに、コンテナー間でリクエストの負荷を分散する安定したIPアドレスを付与することをお勧めします。 これは、Kubernetesサービスを使用して実行できます。

Kubernetesサービスには4つのタイプがあり、 type サービス構成ファイルのフィールド:

  • ClusterIP:これはデフォルトのタイプであり、クラスター内のどこからでもアクセスできる安定した内部IPをサービスに付与します。
  • NodePort:これにより、静的ポート(デフォルトでは30000〜32767)の各ノードでサービスが公開されます。 リクエストがノードのIPアドレスと NodePort サービスの場合、リクエストは負荷分散され、サービスのアプリケーションコンテナにルーティングされます。
  • LoadBalancer:これにより、クラウドプロバイダーの負荷分散製品を使用してロードバランサーが作成され、 NodePortClusterIP 外部リクエストがルーティングされるサービスの場合。
  • ExternalName:このサービスタイプを使用すると、KubernetesサービスをDNSレコードにマッピングできます。 KubernetesDNSを使用してポッドから外部サービスにアクセスするために使用できます。

タイプのサービスを作成することに注意してください LoadBalancer クラスターで実行されているデプロイメントごとに、サービスごとに新しいクラウドロードバランサーが作成され、コストがかかる可能性があります。 単一のロードバランサーを使用して複数のサービスへの外部リクエストのルーティングを管理するには、IngressControllerを使用できます。 Ingress Controllerはこの記事の範囲を超えていますが、それらの詳細については、Kubernetesドキュメントを参照してください。 人気のあるイングレスコントローラーはNGINXイングレスコントローラーです。

このガイドのポッドとデプロイメントセクションで使用されているFlaskの例のサービス構成ファイルは次のとおりです。

フラスコ_app_svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: flask-svc
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: flask-app
  type: LoadBalancer

ここでは、 flask-app これを使用した展開 flask-svc サービス。 ロードバランサーポートからトラフィックをルーティングするクラウドロードバランサーを作成します 80 露出したコンテナ港へ 8080.

Kubernetesサービスの詳細については、KubernetesドキュメントのServicesセクションを参照してください。

ロギングとモニタリング

を使用して個々のコンテナとポッドログを解析する kubectl logsdocker logs 実行中のアプリケーションの数が増えると、面倒になる可能性があります。 アプリケーションまたはクラスターの問題のデバッグを支援するには、集中ログを実装する必要があります。 大まかに言えば、これはポッドログファイルとストリームを処理し、メタデータでそれらを強化し、ログをElasticsearchのようなバックエンドに転送するすべてのワーカーノードで実行されるエージェントで構成されます。 そこから、 Kibana などの視覚化ツールを使用して、ログデータを視覚化、フィルタリング、および整理できます。

コンテナレベルのログセクションでは、コンテナ内のアプリケーションをstdout/stderrストリームにログに記録するというKubernetesの推奨アプローチについて説明しました。 また、アプリケーションからロギングする際の柔軟性を高めることができるサイドカーコンテナのロギングについても簡単に説明しました。 ローカルログデータをキャプチャしてロギングバックエンドに直接転送するロギングエージェントをポッドで直接実行することもできます。 それぞれのアプローチには長所と短所があり、リソース使用率のトレードオフがあります(たとえば、各ポッド内でロギングエージェントコンテナを実行すると、リソースを大量に消費し、ロギングバックエンドをすぐに圧倒する可能性があります)。 さまざまなロギングアーキテクチャとそのトレードオフの詳細については、Kubernetesドキュメントを参照してください。

標準のセットアップでは、各ノードはFilebeatFluentdなどのロギングエージェントを実行し、Kubernetesによって作成されたコンテナログを取得します。 Kubernetesがノード上のコンテナのJSONログファイルを作成することを思い出してください(ほとんどのインストールでは、これらは次の場所にあります。 /var/lib/docker/containers/). これらは、logrotateなどのツールを使用して回転させる必要があります。 ノードロギングエージェントは、 DaemonSet Controller として実行する必要があります。これは、すべてのノードがDaemonSetポッドのコピーを実行することを保証するKubernetesワークロードの一種です。 この場合、ポッドにはログエージェントとその構成が含まれ、ログデーモンセットポッドにマウントされたファイルとディレクトリからのログを処理します。

使用のボトルネックに似ています kubectl logs コンテナの問題をデバッグするには、最終的には、単に使用するよりも堅牢なオプションを検討する必要があります。 kubectl top クラスター上のポッドリソースの使用状況を監視するためのKubernetesダッシュボード。 クラスターおよびアプリケーションレベルの監視は、 Prometheus 監視システムと時系列データベース、およびGrafanaメトリックダッシュボードを使用して設定できます。 Prometheusは、HTTPエンドポイントをスクレイプする「プル」モデルを使用して動作します( /metrics/cadvisor ノード上、または /metrics アプリケーションRESTAPIエンドポイント)メトリックデータに対して定期的に処理され、保存されます。 このデータは、Grafanaダッシュボードを使用して分析および視覚化できます。 PrometheusとGrafanaは、他のデプロイやサービスと同じように、Kubernetesクラスターで起動できます。

復元力を高めるために、ロギングとモニタリングのインフラストラクチャを別のKubernetesクラスタで実行するか、外部のロギングとメトリクスサービスを使用することをお勧めします。

結論

アプリケーションをKubernetesクラスターで効率的に実行できるように移行および最新化するには、多くの場合、ソフトウェアとインフラストラクチャの変更の計画と設計が簡単ではありません。 これらの変更を実装すると、サービス所有者はアプリの新しいバージョンを継続的に展開し、必要に応じて、最小限の手動介入で簡単に拡張できます。 アプリからの設定の外部化、適切なロギングとメトリクスの公開の設定、ヘルスチェックの設定などの手順により、Kubernetesが設計されたクラウドネイティブパラダイムを十分に活用できます。 ポータブルコンテナを構築し、DeploymentsやServicesなどのKubernetesオブジェクトを使用してそれらを管理することで、利用可能なコンピューティングインフラストラクチャと開発リソースを完全に使用できます。