前書き

最新のステートレスアプリケーションは、Dockerなどのソフトウェアコンテナーで実行されるように構築および設計され、Kubernetesなどのコンテナークラスターによって管理されます。 それらはhttps://github.com/cncf/toc/blob/master/DEFINITION.md[Cloud Native]およびhttps://12factor.net/[Twelve Factor]の原則とパターンを使用して開発され、手動の介入を最小限に抑え、最大限に活用します。移植性と冗長性。 仮想マシンまたはベアメタルベースのアプリケーションをコンテナーに移行し(「コンテナー化」と呼ばれる)、クラスター内に展開すると、多くの場合、これらのアプリの構築、パッケージ化、配信の方法が大幅に変更されます。

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

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

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

構成データの抽出

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

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

アプリケーションコードから構成値を抽出し、代わりに実行中の環境またはローカルファイルからそれらを取り込むことにより、アプリは、付随する構成データを提供する限り、あらゆる環境に展開できる汎用のポータブルパッケージになります。 DockerのようなコンテナーソフトウェアとKubernetesのようなクラスターソフトウェアは、このパラダイムに基づいて設計されており、構成データを管理し、それをアプリケーションコンテナーに注入する機能を組み込みます。 これらの機能の詳細については、https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#inject-configuration [Containerizing]およびhttps://www.digitalocean.com/communityで説明します。 / tutorials / modernizing-applications-for-kubernetes#injecting-configuration-data-with-kubernetes [Kubernetes]セクション。

簡単なPython Flaskアプリのコードから2つの設定値「+ DB_HOST 」と「 DB_USER +」を外部化する方法を示す簡単な例を次に示します。 アプリの実行環境でenv変数として利用できるようにします。この変数からアプリが読み取ります。

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

このシンプルなアプリを実行し(http://flask.pocoo.org/docs/1.0/quickstart/[Flask Quickstart]でその方法を確認してください)、Webエンドポイントにアクセスすると、これら2つの設定値を含むページが表示されます。

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

env_config.py

from flask import Flask

DB_HOST =
DB_USER =

app = Flask(__name__)

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

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

export APP_DB_HOST=mydb.mycloud.com
export APP_DB_USER=sammy
flask run

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

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

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

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

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

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

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

ステートレスなクラウドネイティブマイクロサービスの設計とアーキテクチャの詳細については、http://assets.digitalocean.com/white-papers/running-digitalocean-kubernetes.pdf [Kubernetes White Paper]をご覧ください。

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

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

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

  • HTTP:Kubeletプローブはエンドポイント(「+ / health +」など)に対してHTTP GET要求を実行し、応答ステータスが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

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

pod_spec.yaml

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

`+ initialDelaySeconds `フィールドは、Kubernetes(具体的にはNode Kubelet)が5秒待ってから ` / health `エンドポイントをプローブすることを指定し、 ` periodSeconds `は2秒ごとに ` / health +`をプローブするようKubeletに指示します。

活性プローブと準備プローブの詳細については、https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/ [Kubernetes documentation]を参照してください。

ロギングおよびモニタリング用の機器コード

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

サービスの監視に使用できるツールの1つはhttps://prometheus.io/[Prometheus]です。これは、クラウドネイティブコンピューティング財団(CNCF)がホストするオープンソースシステム監視および警告ツールキットです。 Prometheusは、イベントとその継続時間をカウントするためにさまざまなメトリックタイプでコードを計測するためのいくつかのクライアントライブラリを提供します。 たとえば、Flask Pythonフレームワークを使用している場合は、Prometheus Python clientを使用して、リクエスト処理関数にデコレーターを追加し、リクエストの処理に費やした時間を追跡できます。 これらのメトリックは、Prometheusによって「+ / metrics +」のようなHTTPエンドポイントでスクレイピングできます。

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

  • レート:アプリケーションが受信したリクエストの数

  • エラー:アプリケーションによって発行されたエラーの数

  • 期間:アプリケーションが応答を提供するのにかかる時間

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

アプリケーションを監視するときに測定する信号の詳細については、Googleサイト信頼性のhttps://landing.google.com/sre/book/chapters/monitoring-distributed-systems.html#xref_monitoring_golden-signals[Monitoring Distributed Systems]をご覧ください。エンジニアリングブック。

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

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

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

概要

これらのセクションでは、アプリケーションをコンテナ化してKubernetesに移動する前に実装するアプリケーションレベルの変更について説明しました。 Cloud Nativeアプリの構築に関する詳細なウォークスルーについては、https://www.digitalocean.com/community/tutorials/architecting-applications-for-kubernetes [Kubernetesのアーキテクティングアプリケーション]を参照してください。

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

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

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

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

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

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

プライベートイメージレジストリの設定の詳細については、Docker公式ドキュメントおよびhttps://www.digitalocean.com/communityからhttps://docs.docker.com/registry/deploying/ [レジストリサーバーを展開]を参照してください。以下の/ tutorials / modernizing-applications-for-kubernetes#publish-image-to-a-registry [レジストリ]セクション。

画像サイズを小さく保つ

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

  • 画像サイズを縮小する

  • イメージのビルドを高速化

  • コンテナの開始遅延を減らす

  • 画像転送時間を短縮

  • 攻撃対象を減らすことでセキュリティを向上

画像を作成するときに考慮できるいくつかの手順:

  • + ubuntu`のようなフル機能のOSの代わりに、 + alpine + のような最小限のベースOSイメージを使用するか、 + scratch`からビルドします。

  • ソフトウェアのインストール後に不要なファイルとアーティファクトをクリーンアップする

  • 個別の「ビルド」コンテナと「ランタイム」コンテナを使用して、本番アプリケーションのコンテナを小さく保つ

  • 大きなディレクトリにコピーするときに、不要なビルドアーティファクトとファイルを無視する

多くの実例を含むDockerコンテナの最適化に関する完全なガイドについては、https://www.digitalocean.com/community/tutorials/building-optimized-containers-for-kubernetes [Kubernetes用に最適化されたコンテナの構築]を参照してください。

構成の挿入

Dockerには、アプリの実行環境に設定データを注入するための便利な機能がいくつか用意されています。

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

Dockerfile

...
ENV MYSQL_USER=my_db_user
...

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

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

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

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

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

Kubernetesなどのクラスターマネージャーを使用して実行するようにアプリケーションを最新化する場合は、イメージから構成をさらに外部化し、Kubernetesの組み込みhttps://kubernetes.io/docs/tasks/configure-を使用して構成を管理する必要がありますpod-container / configure-pod-configmap / [ConfigMap]およびhttps://kubernetes.io/docs/concepts/configuration/secret/[Secrets]オブジェクト。 これにより、イメージマニフェストから構成を分離できるため、アプリケーションとは別に管理およびバージョン管理を行うことができます。 ConfigMapsおよびSecretsを使用して構成を外部化する方法については、https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#injecting-configuration-data-with-kubernetes [ConfigMaps and Secrets section]を参照してください。未満。

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

アプリケーションイメージを作成したら、Kubernetesで使用できるようにするには、コンテナーイメージレジストリにアップロードする必要があります。 https://hub.docker.com [Docker Hub]などの公開レジストリは、https://hub.docker.com//node/ [Node.js]やhttps:/などの一般的なオープンソースプロジェクトの最新のDockerイメージをホストします。 /hub.docker.com//nginx/[nginx]。 プライベートレジストリを使用すると、内部アプリケーションイメージを公開できるため、開発者やインフラストラクチャで利用できますが、より広い世界では利用できません。

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

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

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

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

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

  • ソースコードリポジトリの変更を監視する

  • 変更されたコードでスモークおよびユニットテストを実行する

  • 変更されたコードを含むコンテナイメージを構築する

  • ビルドされたコンテナイメージを使用してさらに統合テストを実行する

  • テストに合格したら、タグを付けて画像をレジストリに公開します

  • (オプション、継続的な展開セットアップで)Kubernetes Deploymentsを更新し、ステージング/運用クラスターにイメージをロールアウトします

GitHubなどの一般的なバージョン管理サービスやDocker Hubなどのイメージレジストリとの統合が組み込まれた有料の継続的統合製品が多数あります。 これらの製品に代わるものはhttps://jenkins.io/[Jenkins]です。これは、上記のすべての機能を実行するように構成できる無料のオープンソースのビルド自動化サーバーです。 Jenkinsの継続的統合パイプラインの設定方法については、https://www.digitalocean.com/community/tutorials/how-to-set-up-continuous-integration-pipelines-in-jenkins-on-ubuntu-16をご覧ください。 -04 [Ubuntu 16.04のJenkinsで継続的統合パイプラインを設定する方法]。

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

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

Kubernetesでは、デフォルトのコンテナは + json-file + Docker logging driverを使用します。これはstdoutおよびstderrストリームをキャプチャしてJSONに書き込みますコンテナが実行されているノード上のファイル。 stderrとstdoutに直接ログを記録するだけでは、アプリケーションコンテナでは不十分な場合があり、Kubernetesポッドのアプリケーションコンテナとロギング_sidecar_コンテナをペアにしたい場合があります。 このサイドカーコンテナは、ファイルシステム、ローカルソケット、またはsystemdジャーナルからログを取得できるため、単にstderrおよびstdoutストリームを使用するよりも少し柔軟性が高くなります。 このコンテナは処理を行ってから、強化されたログをstdout / stderrに、または直接ロギングバックエンドにストリーミングできます。 Kubernetesのロギングパターンの詳細については、このチュートリアルのKubernetesのロギングとモニタリングhttps://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#logging-and-monitoring[section]をご覧ください。

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

ロギングと同様に、コンテナおよびクラスターベースの環境での監視について考える必要があります。 Dockerは、ホストでコンテナーを実行するためのCPUやメモリ使用量などの標準的なメトリックを取得するための便利な「+ docker stats +」コマンドを提供し、https://docs.docker.com/develop/sdk/ [Remote REST API]。 さらに、オープンソースツールhttps://github.com/google/cadvisor[cAdvisor](デフォルトでKubernetes Nodesにインストールされます)は、履歴メトリックコレクション、メトリックデータのエクスポート、ソートに役立つWeb UIなどのより高度な機能を提供しますデータ。

ただし、マルチノード、マルチコンテナの運用環境では、https://prometheus.io/ [Prometheus]やhttps://grafana.com/[Grafana]などのより複雑なメトリックスタックが、コンテナの整理と監視に役立つ場合があります。パフォーマンスデータ。

概要

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

  • Kubernetes用のコンテナの最適化の詳細については、https://www.digitalocean.com/community/tutorials/building-optimized-containers-for-kubernetes [Kubernetes用の最適化コンテナの構築]をご覧ください。

  • CI / CDの詳細については、https://www.digitalocean.com/community/tutorials/an-introduction-to-continuous-integration-delivery-and-deployment [継続的な統合、配信、および展開の概要]を参照してください。およびhttps://www.digitalocean.com/community/tutorials/an-introduction-to-ci-cd-best-practices[CI/CDベストプラクティスの紹介]。

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

Kubernetesにデプロイする

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

展開およびポッド構成ファイルの書き込み

アプリケーションをコンテナ化してレジストリに公開したら、Podワークロードを使用してKubernetesクラスターにデプロイできます。 Kubernetesクラスターで展開可能な最小単位は、コンテナーではなくポッドです。 通常、ポッドは、アプリケーションコンテナー(コンテナー化されたFlask Webアプリなど)、またはアプリコンテナーと、監視やログなどのヘルパー機能を実行する「サイドカー」コンテナーで構成されます。 ポッド内のコンテナーは、ストレージリソース、ネットワーク名前空間、およびポートスペースを共有します。 「+ localhost +」を使用して相互に通信でき、マウントされたボリュームを使用してデータを共有できます。 さらに、Podワークロードでは、メインアプリコンテナーの実行を開始する前にセットアップスクリプトまたはユーティリティを実行するhttps://kubernetes.io/docs/concepts/workloads/pods/init-containers/[Init Containers]を定義できます。

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

FlaskアプリのKubernetes Deployment設定ファイルのサンプルは次のとおりです。

flask_deployment.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

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

Kubernetesポッドとデプロイの詳細については、https://kubernetes.io/docs/concepts/workloads/pods/pod/ [Pods]およびhttps://kubernetes.io/docs/concepts/workloads/controllers/deploymentを参照してください。 Kubernetesの公式ドキュメントの/ [Deployments]セクション。

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

Kubernetesは、ボリューム、永続ボリューム(PV)、および永続ボリュームクレーム(PVC)を使用してPodストレージを管理します。 ボリュームは、Podストレージを管理するために使用されるKubernetesの抽象概念であり、ほとんどのクラウドプロバイダーブロックストレージ製品と、実行中のPodをホストするノード上のローカルストレージをサポートします。 サポートされているボリュームタイプの完全なリストを確認するには、Kubernetes https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes [ドキュメント]を参照してください。

たとえば、Podにデータを共有する必要がある2つのNGINXコンテナーが含まれている場合(最初の + nginx +`と呼ばれるものはWebページを提供し、2番目の `+ nginx-sync +`と呼ばれるものは外部の場所からページを取得し、 `+ nginx +`コンテナが提供するページを更新します)、Podの仕様は次のようになります(ここではhttps://kubernetes.io/docs/concepts/storage/volumes/#emptydir [+ 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 」を使用し、Webページファイルを含む「 nginx-web 」ボリュームを「 nginx 」内の「 / usr / share / nginx / html 」にマウントすることを示しますコンテナおよび ` nginx-sync `コンテナ内の ` / web-data ` また、タイプ「 emptyDir」の「+ nginx-web」と呼ばれる「+ volume」を定義します。

同様に、 `+ volume `タイプを ` emptyDir +`から関連するクラウドストレージボリュームタイプに変更することにより、クラウドブロックストレージ製品を使用してPodストレージを設定できます。

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

Podの再起動および更新後もデータを保持するには、PersistentVolume(PV)およびPersistentVolumeClaim(PVC)オブジェクトを使用する必要があります。

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

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

StatefulSetコントローラーの詳細については、Kubernetes https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ [ドキュメント]をご覧ください。 PersistentVolumesおよびPersistentVolumeクレームの詳細については、Kubernetesストレージhttps://kubernetes.io/docs/concepts/storage/persistent-volumes/ [ドキュメント]をご覧ください。

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

Dockerと同様に、KubernetesにはPod設定ファイルで環境変数を設定するための `+ env `および ` envFrom `フィールドがあります。 以下は、実行中のPodの ` HOSTNAME `環境変数を ` my_hostname +`に設定するPod設定ファイルのサンプルスニペットです。

sample_pod.yaml

...
   spec:
     containers:
     - name: nginx
       image: nginx:1.7.9
       ports:
       - containerPort: 80
       env:
       - name: HOSTNAME
         value: my_hostname
...

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

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

ConfigMapと秘密

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

上記のポッド設定を使用した例を次に示します。 最初に `+ HOSTNAME +`環境変数をConfigMapとして保存してから、Pod configでそれを参照します。

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

Pod設定ファイルからそれを参照するには、 `+ valueFrom `および ` configMapKeyRef +`コンストラクトを使用します:

sample_pod_configmap.yaml

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

したがって、 `+ HOSTNAME +`環境変数の値は、構成ファイルから完全に外部化されています。 次に、これらの変数を参照するすべての展開とポッドでこれらの変数を更新し、変更を有効にするためにポッドを再起動します。

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

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

ConfigMapsとSecretsの詳細については、Kubernetes https://kubernetes.io/docs/concepts/configuration/ [ドキュメント]を参照してください。

サービスを作成する

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

内部および/または外部クライアントに機能を公開する長期実行サービスの場合、同じ機能(または展開)を実行するポッドのセットに、コンテナー全体で要求の負荷を分散する安定したIPアドレスを付与できます。 Kubernetesサービスを使用してこれを行うことができます。

Kubernetesサービスには4つのタイプがあり、サービス構成ファイルの `+ type +`フィールドで指定されます:

  • + ClusterIP +:これはデフォルトのタイプで、クラスター内のどこからでもアクセスできる安定した内部IPをサービスに付与します。

  • + NodePort +:これは、デフォルトで30000〜32767の間の静的ポートで各ノードのサービスを公開します。 リクエストがノードIPアドレスとサービスの `+ NodePort +`でノードに到達すると、リクエストは負荷分散され、サービスのアプリケーションコンテナにルーティングされます。

  • + LoadBalancer +:クラウドプロバイダーの負荷分散製品を使用してロードバランサーを作成し、外部リクエストのルーティング先となるサービスの `+ NodePort `と ` ClusterIP +`を構成します。

  • + ExternalName +:このサービスタイプを使用すると、KubernetesサービスをDNSレコードにマッピングできます。 Kubernetes DNSを使用してポッドから外部サービスにアクセスするために使用できます。

クラスター内で実行されている各デプロイメントに対して「+ LoadBalancer +」タイプのサービスを作成すると、各サービスに対して新しいクラウドロードバランサーが作成されることに注意してください。 単一のロードバランサーを使用して外部要求の複数のサービスへのルーティングを管理するには、イングレスコントローラーを使用できます。 Ingress Controllerはこの記事の範囲外ですが、それらについて詳しく知るにはKubernetes documentationを参照してください。 人気のあるシンプルなIngress Controllerはhttps://github.com/kubernetes/ingress-nginx[NGINX Ingress Controller]です。

これは、ポッドとデプロイメントで使用されるFlaskサンプルの簡単なサービス構成ファイルですhttps://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#write-deployment-and-pod-configuration-files [このガイドのセクション]:

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-svc `サービスを使用して、 ` flask-app `デプロイメントを公開することを選択します。 クラウドロードバランサーを作成して、トラフィックをロードバランサーポート「+80」から公開コンテナポート「8080」にルーティングします。

Kubernetes Servicesの詳細については、Kubernetesドキュメントのhttps://kubernetes.io/docs/concepts/services-networking/service/[Services]セクションをご覧ください。

ロギングとモニタリング

`+ kubectl logs `と ` docker logs +`を使用して個々のコンテナとPodログを解析することは、実行中のアプリケーションの数が増えるにつれて面倒になります。 アプリケーションまたはクラスターの問題をデバッグするには、集中ログを実装する必要があります。 高レベルでは、これは、Podログファイルとストリームを処理し、メタデータでそれらを強化し、ログをhttps://github.com/elastic/elasticsearch[Elasticsearch ]。 そこから、https://github.com/elastic/kibana [Kibana]などの視覚化ツールを使用して、ログデータを視覚化、フィルタリング、および整理できます。

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

標準セットアップでは、各ノードは、コンテナログを取得するhttps://www.elastic.co/products/beats/filebeat[Filebeat]やhttps://github.com/fluent/fluentd[Fluentd]などのロギングエージェントを実行しますKubernetesによって作成されました。 Kubernetesはノード上のコンテナのJSONログファイルを作成することを思い出してください(ほとんどのインストールでは、これらは `+ / var / lib / docker / containers / +`にあります)。 これらは、logrotateなどのツールを使用して回転させる必要があります。 Nodeロギングエージェントはhttps://kubernetes.io/docs/concepts/workloads/controllers/daemonset/[DaemonSet Controller]として実行する必要があります。これは、すべてのノードがDaemonSetポッドのコピーを実行することを保証するKubernetesワークロードの一種です。 この場合、Podにはロギングエージェントとその構成が含まれ、ロギングDaemonSet Podにマウントされたファイルおよびディレクトリからのログを処理します。

「+ kubectl logs 」を使用してコンテナの問題をデバッグする際のボトルネックと同様に、最終的には、「 kubectl top 」とKubernetesダッシュボードを使用してクラスター上のPodリソースの使用状況を監視するよりも堅牢なオプションを検討する必要があります。 クラスターおよびアプリケーションレベルの監視は、https://prometheus.io/ [Prometheus]監視システムと時系列データベース、およびhttps://github.com/grafana/grafana[Grafana]メトリックダッシュボードを使用して設定できます。 Prometheusは、「プル」モデルを使用して動作します。これは、メトリックデータのHTTPエンドポイント(ノード上の「 / metrics / cadvisor 」や「 / metrics +」アプリケーションREST APIエンドポイントなど)を定期的にスクレイピングし、処理して保存します。 このデータは、Grafanaダッシュボードを使用して分析および視覚化できます。 PrometheusとGrafanaは、他の展開およびサービスと同様にKubernetesクラスターに起動できます。

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

結論

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