
タイトルにもかかわらず、この記事はPuppet構成管理システムとは関係ありません。
大きなモノリスを小さなマイクロサービスに「カット」する傾向に加えて、コンテナオーケストレーションの傾向がWebアプリケーションの運用に取り入れられました。 Dockerの誇大広告の直後に、Dockerの上にあるサービス起動ツールの誇大広告が増えています。 Kubernetesが最もよく話されていますが、現在の多くの選択肢も生き続け進化しています。
そこでiFunnyでは、オーケストレーターのメリットと価値について考え、最終的にAmazon Elastic Container Serviceを選択しました。 つまり、ECSはEC2インスタンスのコンテナ管理プラットフォームです。 戦闘の詳細と経験については、以下をお読みください。
なぜコンテナオーケストレーション

「Habr」に関するDockerの記事とその限界を超えた記事の後に、誰もがDockerの目的と目的を理解していると思われます。 状況を明確にしましょう。なぜDockerよりもプラットフォームが必要なのでしょうか。
「すぐに使える」サービス展開の自動化
車にコンテナを投げましたか? わあ! そして、クライアントがバインドされている静的ポートに向かうトラフィックを切り替えるときに、サービスの低下を回避してそれらを更新する方法は? また、新しいバージョンのアプリケーションで問題が発生した場合に、迅速かつ簡単にロールバックする方法は? 結局のところ、Dockerだけではこれらの問題を解決できません。
はい、あなたは自分でそのようなことを書くことができます。 最初、iFunnyはそのように動作しました。 青緑色の展開では、Ansibleプレイブックが使用されました。Ansibleプレイブックでは、必要なルールを新しいコンテナーのIPに切り替えることで、古き良きIptablesが制御されました。 古いコンテナからの接続は、同じ古いconntrackとの接続を追跡することでクリーンアップされました。
透明で理解しやすいように見えますが、自家製のものと同様に、次の問題を引き起こしました。
- アクセスできないホストの誤った処理。 Ansibleは、誤動作だけでなく、Autoscalingグループでのスケールダウンが原因で、EC2インスタンスがいつでも落ちる可能性があることを理解していません。 したがって、スタンバイ状態では、プレイブックの完了後にAnsibleがエラーを返す場合があります。 この問題に関する問題は解決されたようですが、まだ解決されていません。
- Docker APIから500人。 はい、時々、Dockerは高負荷下でサーバーエラーを発生させることがあり、Ansibleではそれも処理できません。
- 展開を停止することはできません。 IPtablesのルールを置き換えるときにプレイブックを開始するプロセスを強制終了するとどうなりますか? ホストで形成されるカオスはどれくらいの大きさですか? マシンのどの部分が利用できなくなりますか?
- 1つのホスト内のタスクの並列化の欠如。 Ansibleでは、タスクを構成する並列反復を実行できません。 これを説明するのは簡単です。共通のパターンで、パラメーターが異なる50個のコンテナーを起動するタスクがある場合、コンテナー1が開始するのを待ってからコンテナー2を開始する必要があります。
すべての問題をまとめると、宣言型SCMはデプロイメントなどの必須のタスクにはあまり適していないと結論付けることができます。 オーケストラはそれと何の関係があるのでしょうか? はい、オーケストレーターはプロセスを説明することなく1つのチームでサービスを展開する機会を提供するという事実にもかかわらず。 すべての既知の展開パターンと上記の障害のスムーズな処理が自由に行えます。
私の意見では、オーケストレーションプラットフォームは、Dockerを使用して迅速でシンプルかつ信頼性の高い展開を実装する唯一の方法です。 おそらく、私たちのようなAWSの愛好家はElastic Beanstalkを例として使用するでしょう。 食料品店の環境でもしばらく使用していましたが、十分な問題があったため、この記事に収まりませんでした。
「構成管理」の簡素化
かつて、オーケストレーションプラットフォームと、オペレーティングシステムによるCPU上でのプロセスの起動との非常に興味深い比較を聞きました。 結局のところ、このプログラムやそのプログラムがどのカーネルで動作するかは気にしませんか?
同じアプローチがオーケストレーターにも当てはまります。 一般に、構成はバランサー上で動的に更新されるため、サービスを実行しているマシンとコピーの数については気にしません。 実稼働環境では最低限のホスト構成が必要です。 理想的には、Dockerをインストールするだけです。 「さらに大きな理想」は、CoreOSがある場合、構成管理を完全に削除することです。
したがって、フリートは昼夜を監視するために必要なものではなく、リソースの単純なプールであり、その一部はいつでも交換できます。
サービス中心のインフラストラクチャアプローチ
近年、Webアプリケーションインフラストラクチャは、ホスト中心からサービス中心に移行しています。 つまり、これは前の段落の続きであり、ホストを監視する代わりに、サービスの外部パフォーマンスを監視します。 オーケストレーションプラットフォームの哲学は、厳密に固定されたホストプールにサービスを保持する場合よりもはるかに調和してこのパラダイムに適合します。
このアイテムにマイクロサービスを添付することもできます。 展開の自動化に加えて、新しいサービスとオーケストレーションを新しいサービスで簡単にすばやく作成し、それらをリンクすることができます(サービスディスカバリー機器は、ほとんどの場合、オーケストレーターと一緒に「すぐに」提供されます)。
開発者に近づくインフラストラクチャ
iFunny開発チームのDevOpsは、空っぽのフレーズでも、エンジニアでもありません。 開発者に最大限の行動の自由を与え、 フロー、フィードバック、実験を加速するよう努めています。
過去1〜2年で、APIモノリスが積極的に作成され、新しいマイクロサービスが絶えず開始されています。 また、実際には、コンテナオーケストレーションは、サービスの立ち上げを技術プロセスとして迅速に開始し、標準化するのに役立ちます。 優れたアプローチにより、開発者は、一般リストのタスクが管理者に届くまで、数週間(または1か月)待つことなく、いつでも新しいサービスを作成できます。
オーケストレーターを使用するのが良い理由はまだたくさんあります。 リソースのリサイクルについて追加できますが、この場合、最も注意深い読者であっても我慢できません。
オーケストラの選択

ここでは、市場にある十数のソリューションの比較、コンテナーの起動とクラスターの展開に関する無限のベンチマーク、特定の製品機能をブロックする多くのバグという形でのカバーについて説明することができます。
しかし、実際には、すべてがはるかに退屈です。 iFunnyでは、チームが小さいため、AWSサービスを最大限に活用しようとします。いつものように、自分の自転車を作成したり、全員を「ファイル」したりするのに十分な時間、知識、経験がありません。 したがって、beatられた道に沿って移動し、シンプルでわかりやすいツールを使用することが決定されました。 はい、サービスとしてのECS自体は無料です。エージェントとコンテナが実行されているEC2インスタンスに対してのみ標準料金でお支払いいただきます。
小さなネタバレ:このアプローチは機能しましたが、ECSには他にも多くの問題がありました。 「Kubernetesがいかに残念であるか」という告白は、記事の最後にあります。
用語

ECSの基本概念を理解しましょう。
クラスター→サービス→タスク
最初に覚えておくべき束。 最初は、ECSプラットフォームはインスタンスでホストする必要のないクラスターであり、AWS APIを介して管理されます。
クラスターでは、共通のバンドルで実行される1つ以上のコンテナーからタスクを実行できます。 言い換えれば、タスクはKubernetesのポッドに類似しています。 柔軟なコンテナ管理-スケーリング、展開など-には、サービスの概念があります。
サービスは特定の数のタスクで構成されます。 すでにECSとKubernetesを比較している場合、サービスは展開の概念に似ています。
タスク定義
JSONでのタスク起動パラメーターの説明。 通常の仕様。dockerrunコマンドのラッパーとして使用できます。 タグ付けおよびログ記録用のすべての「ペン」が表示されますが、これは、たとえばDocker + Elastic Beanstalkバンドルには含まれていません。
ECSエージェント
実行中のコンテナの形式のインスタンス上のローカルエージェントです。 彼はインスタンスの状態を監視し、そのリソースを利用し、ローカルDockerデーモンにコンテナーを起動するコマンドを送信しています。 エージェントのソースコードはGithubで入手できます。
Application Load Balancer(ALB)
AWS次世代バランサー ELBとその概念の大部分は異なります。ELBがホストレベルでトラフィックのバランスを取る場合、ALBはアプリケーションレベルでトラフィックのバランスをとります。 ECSエコシステムでは、バランサーがユーザートラフィックの宛先の役割を果たします。 トラフィックをアプリケーションの新しいバージョンに転送する方法について考える必要はありません。バランサーの後ろにコンテナを隠すだけです。
ALBには、アプリケーションインスタンスが接続するターゲットグループの概念があります。 ECSサービスをバインドできるのはターゲットグループです。 このようなバンドルでは、クラスターはサービスコンテナーが実行されているポートに関する情報を取得し、ターゲットグループに転送して、バランサーからのトラフィックを分散します。 したがって、コンテナが開いているポートや、同じマシン上の複数のサービス間の衝突を防ぐ方法について心配する必要はありません。 ECSでは、これは自動的に解決されます。
タスク配置戦略
使用可能なクラスターリソースにタスクを分散するための戦略。 戦略は、タイプとパラメーターで構成されます。 他のオーケストレーターと同様に、3つのタイプがあります:binpack(つまり、マシンを障害に陥らせてから別のマシンに切り替える)、spread(クラスター全体にリソースを均等に分散)、random(誰もがこれを理解していると思います)。 パラメータは、CPU、メモリ、アベイラビリティーゾーン、インスタンスIDです。
実際の経験では、アベイラビリティーゾーンに従って(言い換えれば、データセンターに従って)タスクを分散する戦略が鉄筋コンクリートオプションとして選択されました。 これにより、コンテナ間のマシンリソースの競合が減少し、AWSのアベイラビリティゾーンの1つで予期しない障害が発生した場合にストローが広がります。
健康的な割合
サービスが正常であると見なされるタスクの望ましい数の最小および最大共有のパラメーター。 このパラメーターは、サービスのデプロイの構成に役立ちます。
アプリケーションバージョンの更新自体は、次の2つの方法で実行できます。
- max percentage> 100の場合、パラメータに従って新しいタスクが作成され、同じ量で、新しいタスクをトラフィックに接続した後、古いタスクが殺されます。 最大パーセンテージ= 200の場合、すべてが1回の反復で発生し、150の場合は2回で発生します。
- 最小パーセンテージ<100 、 最大パーセンテージ= 100の場合、すべてが逆に行われます。最初に、新しいタスクを作成するためのスペースを解放するために古いタスクが強制終了されます。 この時点で、すべてのトラフィックは残りのタスクによって処理されます。
最初のオプションは青緑の展開に似ており、クラスター内に2倍のリソースを保持する必要がない場合に最適です。 2番目のオプションはリサイクルに利点がありますが、適切な量のトラフィックが到着すると、アプリケーションの劣化につながる可能性があります。 どちらを選ぶかはあなた次第です。
自動スケーリング
EC2インスタンスレベルでのスケーリングに加えて、ECSにはタスクレベルでのスケーリングもあります。 自動スケーリンググループの場合と同様に、Cloudwatch Alarmsの一部としてECSサービスのトリガーを構成できます。 最適なオプションは、タスク定義で指定された値の使用CPUの割合でスケーリングすることです。
重要な点 :CPUパラメーターは、Docker自体と同様に、コアの数ではなく、CPUユニットの値で示されます。 将来、プロセッサ時間は、どのタスクにさらにユニットがあるかに基づいて割り当てられます。 ECS 1の用語では、CPUコアは1024ユニットに相当します。
弾性コンテナリポジトリ
AWS Dockerイメージホスティングサービス つまり、Docker Registryをホストせずに無料で入手できます。 プラスは単純で、マイナスは複数のドメインを持つことはできず、各サービスごとに独自のリポジトリを個別に作成する必要があることです。
既存のインフラストラクチャへの統合

これで、ECSがiFunnyでどのように定着したかについての楽しい部分です。
展開パイプライン
オーケストレーターおよびリソースプランナーとして、ECSは良いかもしれませんが、展開ツールは提供していません。 ECSの用語では、展開は更新サービスです。 更新するには、タスク定義の新しいバージョンを作成し、新しい定義のリビジョン番号を示すサービスを更新し、更新が完了するまで待機し、何か問題が発生した場合は古いリビジョンにロールバックする必要があります。 また、執筆時点のAWSには、すべてを一度に実行できる既製のツールがありませんでした。 ECSには別のCLIがありますが、サービスを単独でデプロイすることよりも、Docker Composeに類似しています。
幸いなことに、オープンソースの世界は、この欠点を別のecs-deployユーティリティで補いました 。 実際、これは数百行のシェルスクリプトですが、直接的なタスクの非常に良い仕事をします。 デプロイするサービス、クラスター、Dockerイメージを指定するだけで、アルゴリズム全体が段階的に実行されます。 また、更新ファイルの場合にはロールバックし、廃止されたタスク定義を一掃します。
最初は、唯一の欠点はユーティリティを介してタスク定義を完全に更新できないことでした。 CPUの制限を変更したり、ログドライバーを再構成したりするとします。 しかし、これはシェルスクリプトであり、DIYにとって最も簡単なものです! この機能は、数時間でスクリプトに追加されました。 これは引き続き使用され、アプリケーションリポジトリのルートに保存されているタスク定義によってのみサービスを更新します。
確かに、彼らは半年間、 Pull Requestに注意を払っていませんでした。 これは、オープンソースの短所に関する質問です。
iFunnyのTerraformを通じて、AWSにあるすべてのリソースがデプロイされます。 サービスが機能するために必要なリソースも例外ではありません:サービス自体に加えて、それはApplication Load Balancerとそれに関連するリスナーとターゲットグループ、およびECRリポジトリ、タスク定義の最初のバージョン、アラームと必要なDNSレコードの自動スケーリングです。
最初のアイデアは、すべてのリソースを1つのTerraformモジュールに結合し、サービスを作成するたびにそれを使用することでした。 最初は見栄えがよかったです。たった20行で、生産準備完了のサービスがあります。 しかし、判明したように、そのようなことを長期にわたって維持することははるかに高価です。 サービスは常に同種ではなく、さまざまな要件が常に表示されるため、使用するときはほとんど毎回モジュールを編集する必要がありました。
「シンタックスシュガー」について考えないために、すべてを正常に戻す必要がありました。Terraformのすべてのリソースを段階的に説明し、小さなモジュールでラップできるものをラップします:ロードバランシングとオートスケーリング。
ある時点で、状態が非常に大きくなり、更新プログラムのある計画には約5〜7分かかりました。また、それ自体が何かを上げている別のエンジニアによってブロックされる可能性があります。 この問題は、サービスごとに1つの大きな状態をいくつかの小さな状態に分割することで解決しました。
監視とログ
ここではすべてが非常に透明でシンプルなものになっています。 クラスタサービスとリソースの使用率に関するいくつかの新しいメトリックがダッシュボードとアラートに追加されたため、サービスのスケーリングが開始された時点と最終的にどの程度うまく機能したかが明確にわかりました。
以前と同様に、ログをローカルのFluentdエージェントに書き込み、エージェントはElastic Searchに配信し、さらにKibanaでそれらを読み取る機会を得ました。 ECSは、同じBeanstalkとは異なり、Dockerにあるすべてのログドライバーをサポートし、これはタスク定義の一部として構成されます。
AWSでは、管理コンソールにログを直接表示するawslogsドライバーを試すこともできます。 ログを収集するためのシステムを個別に作成および維持するためのログがあまりない場合に便利です。
スケーリングとリソース配布
これは、ほとんどの痛みがあった場所でした。 サービスをスケーリングする戦略は、試行錯誤によって長い間選ばれました。 この経験から、次のことが明らかになりました。
- もちろん、CPU上のBinpackはクラスターをうまく利用しますが、負荷がいっぱいになると、Dockerがそのような条件下でCPU時間を分割する方法を見つけ出すまで、すべてが1〜2分間横たわります。
- オーケストレーター(ECSを含む)には、動的なリバランスコンテナーの概念がありません。 たとえば、ピーク時にスケーリングの問題を解決するには、クラスターに新しいホストを追加して、クラスターがリソースを均等に分散するようにします。 ただし、何らかのサービスで更新プログラムが起動されるまで、アイドル状態になります。 このトピックはDocker Swarmで鋭く議論されましたが、未解決のままです。 最も可能性が高いのは、概念的および技術的に解決することが難しいためです。
その結果、負荷がかかった状態で、リソース予約の75%に達した時点で、サービスとインスタンスを即座に大量にスケーリングすることが決定されました。 鉄の利用という観点からはおそらく最良の選択肢ではありませんが、少なくともクラスター内のすべてのサービスは、互いに干渉することなく安定して機能します。
落とし穴

エンジニア向けの新しいものの導入が100%ハッピーエンドで終わったケースを思い出してみてください。 できない そのため、ECSを使用したiFunnyエピソードでも例外ではありませんでした。
ヘルスチェックの柔軟性の欠如
サービスの可用性と可用性チェックを柔軟に設定できるKubernetesとは異なり、ECSには1つの基準のみがあります:アプリケーションは1つのURLで200コード(またはユーザーが設定したその他のコード)を返します。 サービスが不良であるための基準は2つだけです。コンテナがまったく起動しなかったか、起動したがヘルスチェックに応答しなかったかのいずれかです。
これにより、たとえば、デプロイ中にサービスの重要な部分が破損しても、チェックに応答する場合に問題が発生します。 この場合、古いバージョンを自分でやり直す必要があります。
サービスディスカバリーの欠如。 AWSは独自のバージョンのService Discoveryを提供していますが、このソリューションは、まあまあまあのように見えます。 この状況での最適なオプションは、ホスト内にConsulエージェント+登録者バンドルを実装することです。これは、iFunny開発チームが現在行っていることです。
生のスケジュールされたタスクの起動の実装
明確でない場合、私はcronについて話している。 昨年6月に、ECSはスケジュールされたタスクの概念を導入しました。これにより、クラスター上でスケジュールに従ってタスクを実行できます。 この機能は顧客が長い間利用できましたが、操作は多くの理由でまだ粗雑なようです。
まず、APIはタスク自体によって作成されるのではなく、起動パラメーターを持つCloudwatchイベントと起動時間を持つCloudwatchイベントターゲットの 2つのリソースによって作成されます。 外からは不透明に見えます。 第二に、これらのタスクを展開するための通常の宣言ツールはありません。
Ansibleを使用して問題を解決しようとしましたが、これまでのところ、タスクの標準化には問題があります。
最終的に、iFunnyは、YAMLファイル内のタスクの説明を含む自己記述Pythonユーティリティを使用して展開し、ECSにcronタスクを展開するための本格的なツールを作成する予定です。
クラスターとホスト間の直接通信の欠如
さまざまな理由で、EC2インスタンスが削除された場合、クラスターで登録解除されず、その上で実行されているすべてのタスクは単純に落ちます。 バランサーはクラスターからターゲットを削除するシグナルを受信しなかったため、コンテナーが利用できないことを認識するまでリクエストを送信します。 10〜15秒かかり、この間にサーバーから大量のエラーが発生します。
現在、Autoscaling Groupからのインスタンスの削除に応答し、このマシンのタスクを削除するためにクラスターにリクエストを送信するラムダ関数の助けを借りて、問題を解決できます(用語-インスタンスの排出)。 したがって、インスタンスは、すべてのタスクが削除された後にのみ無効になります。 うまく機能しますが、インフラストラクチャのLambdaは常に松葉杖のように見えます。これはプラットフォームの機能に含まれている可能性があります。
詳細な監視の欠如
AWS APIは、登録済みのマシンの数と、クラスターから、サービスからの予約容量のシェアのメトリックのみを提供します。タスクの数と、タスク定義で設定された量の割合としてのCPUおよびメモリ使用率のみです。 メトリック教会の支持者にとっての苦痛はここにあります。 特定のコンテナによるリソースの使用に関する詳細の欠如は、サービスのオーバーロードに関する問題をデバッグするときにトリックを演じることができます。 また、I / Oおよびネットワークの廃棄に関するメトリックは、問題ありません。
ALBでのコンテナー登録解除
AWSドキュメントから差し引かれた重要なポイント。 バランサーのderegistration_delayパラメーターは、ターゲットの登録解除を待機するためのタイムアウトではなく、完全なタイムアウトです。 つまり、パラメーターが30秒で、15秒後にコンテナーが停止する場合、バランサーはターゲットへの要求を送信し、クライアントに500番目のエラーを与えます。
解決策は、サービスのderegistration_delayをALBの同様のパラメーターの上に設定することです。 当たり前のように思えますが、最初に問題を引き起こすドキュメントのどこにも書かれていません。
AWS内のベンダーロックイン
AWSクラウドサービスと同様に、AWSの外部でECSを使用することはできません。 何らかの理由でGoogle Cloudまたは(何らかの理由で)Azureに移行することを考えた場合、この場合、サービスのオーケストレーションを完全にやり直す必要があります。
シンプルさ
はい。ECSとそのAWS製品という環境は非常に単純なので、アプリケーションのアーキテクチャに特別なタスクを実装することは困難です。 たとえば、サービスで完全なHTTP / 2サポートが必要ですが、ALBはサーバープッシュをサポートしていないため、これを行うことはできません。
または、アプリケーションがレベル4(TCP、UDPは関係ありません)でリクエストを受け入れる必要がありますが、ALSはHTTP / HTTPS、および古いELBを介してのみ動作するため、ECSではトラフィックをサービスに転送する方法に関するソリューションも見つかりませんECSサービスでは機能せず、一般にトラフィックを歪めることがあります(たとえば、gRPCで発生しました)。
回顧

記事の冒頭で述べたオーケストレーションのすべてのプラスをまとめると、それらはすべて真実であると自信を持って言えます。 IFunnyの現在の機能:
- シンプルで簡単な展開。
- Ansibleでのコードと構成の削減。
- ホスト管理ではなくアプリケーションユニット管理
- 開発者が直接、20〜30分でゼロから運用サービスを開始します。
しかし、リソース使用率の問題は未解決のままです。
アプリケーションをECSに完全に移行する最後のステップは、コアAPIの移行でした。 これらはすべて、迅速かつスムーズにダウンタイムなしで合格しましたが、大きなモノリシックアプリケーションにオーケストレーターを使用することの妥当性については疑問が残りました。 1つのアプリケーションユニットには、多くの場合、個別のホストを割り当てる必要があります。信頼性の高い展開を行うには、空きスペースを複数の空いているマシンとして保存する必要があります。 もちろん、ECSは他の多くの問題を前向きに解決しましたが、モノリスを使用してもオーケストレーションで大きなメリットは得られないという事実は残っています。
次の図は、4つのクラスター(そのうちの1つがテスト環境)、36の実稼働中のサービス、ピーク時に約210から230の起動コンテナー、およびスケジュールで実行される80のタスクで得られました。 時間は、オーケストレーションでのスケールアップがはるかに高速で簡単であることを示しています。 しかし、かなり少数のサービスと実行中のコンテナーがある場合は、オーケストレーションが必要かどうかを考える必要があります。
- 運がよければ、この戦いの後、AWSはEKSと呼ばれる独自のKubernetesホスティングサービスを開始し始めました。 このプロセスは非常に初期の段階であり、本番環境での使用に関するフィードバックはまだありませんが、AWSでは2つのボタンで最も人気のあるオーケストレーションプラットフォームをセットアップでき、ほとんどの「ペン」にアクセスできることを誰もが理解しています。 オーケストラが選ばれた瞬間に戻ると、Kubernetesは柔軟性、豊富な機能、プロジェクトの迅速な開発のために優先事項となります。
AWSは、EC2インスタンスをホストすることなくコンテナーを起動するECS Fargateも導入しました。 iFunnyでは、彼らはすでにいくつかのテストサービスで試してみましたが、その機能について結論を出すのは時期尚早と言えます。
PS記事は非常に大規模であることが判明しましたが、ECSのケースでさえ終わりではありません。 コメントでトピックに関する質問をするか、成功した実装事例を共有してください。