ソフトウェアデプロイメントの強化-DockerSwarmチュートリアル
公開: 2022-03-11輸送用コンテナの中に住んでいない限り、おそらくコンテナについて聞いたことがあるでしょう。 業界は永続的なインフラストラクチャから一時的なインフラストラクチャへの明確な移行を行っており、コンテナはその移行の真っ只中にあります。 理由は非常に単純です。コンテナーは確かに開発チームが迅速に立ち上げて実行するのに役立ちますが、運用の様相を完全に変える可能性はさらに高くなります。
しかし、それは正確にはどのように見えますか? ローカルで、またはいくつかのサーバーで手動で実行中のコンテナーから飛躍する準備ができたらどうなりますか? 理想的な世界では、アプリケーションをサーバーのクラスターに投げて、「実行してください」と言いたいだけです。
ありがたいことに、それが今日の私たちの姿です。
この記事では、Docker Swarmとは何か、およびDockerSwarmが提供する優れた機能のいくつかについて説明します。 次に、実際にスウォームモードを使用してスウォームにデプロイする方法を確認し、デプロイされたスウォームでの日常の操作の例をいくつか示します。 Dockerとコンテナーの基本的な知識は絶対にお勧めですが、コンテナーを初めて使用する場合は、この優れたブログ投稿を最初に確認できます。
Docker Swarmとは何ですか?
最初のスウォームの作成とデプロイに取り掛かる前に、Dockerスウォームとは何かを理解しておくと役に立ちます。 Docker自体は何年も前から存在しており、今日ではほとんどの人がDockerをコンテナーランタイムと考えています。 ただし、実際には、Dockerは多くの異なる部分で構成されており、すべてが連携して機能します。 たとえば、そのコンテナランタイム部分は、runCとcontainerdと呼ばれる2つの小さなコンポーネントによって処理されます。 Dockerが進化し、コミュニティに還元されるにつれて、これらの小さなコンポーネントを作成することが、機能を成長させて迅速に追加するための最良の方法であることがわかりました。 そのため、SwarmKitと、Dockerに直接組み込まれているSwarmモードがあります。
Docker Swarmは、コンテナーオーケストレーションエンジンです。 大まかに言うと、異なるホストで実行されている複数のDockerエンジンが必要であり、それらを一緒に使用できます。 使い方は簡単です。アプリケーションをサービスのスタックとして宣言し、残りはDockerに処理させます。 サービスは、アプリケーションインスタンスからデータベース、またはRedisやRabbitMQなどのユーティリティまで何でもかまいません。 これは、まったく同じ概念であるため、開発でdocker-composeを使用したことがある場合は、おなじみのように聞こえます。 実際、スタック宣言は文字通り、バージョン3.1の構文を持つ単なるdocker-compose.yml
ファイルです。 これは、開発とスウォームのデプロイに同様の(多くの場合は同一の)構成構成を使用できることを意味しますが、ここでは少し先を行っています。 SwarmモードのDockerのインスタンスがあるとどうなりますか?
いかだから落ちないでください
Swarmの世界には、マネージャーとワーカーの2種類のノード(サーバー)があります。 マネージャーは労働者でもあることを覚えておくことが重要です。マネージャーには、物事を実行し続けるという追加の責任があるだけです。 すべての群れは、リーダーとして指定された1つのマネージャーノードから始まります。 そこから、1つのコマンドを実行するだけで、ノードを群れに安全に追加できます。
Swarmは、Raftアルゴリズムの実装のおかげで高可用性を実現します。 Raftの仕組みについてはすでに優れたチュートリアルがあるため、Raftについてはあまり詳しく説明しませんが、一般的な考え方は次のとおりです。リーダーノードは常に他のマネージャーノードにチェックインし、状態を同期しています。 状態の変化が「受け入れられる」ために、マネージャーノードは多くのコンセンサスに達します。これは、ノードの大部分が状態の変化を確認したときに発生します。
これの利点は、マネージャーノードが群れのコンセンサスを損なうことなく散発的にドロップオフできることです。 状態の変化がコンセンサスに達した場合、それは大多数のマネージャーノードに存在することが保証されており、現在のリーダーに障害が発生しても持続します。
A、B、Cという名前の3つのマネージャーノードがあるとします。もちろん、Aは恐れを知らないリーダーです。 ある日、一時的なネットワークエラーがAをオフラインにし、BとCをそのままにします。 長い間(数百ミリ秒)Aから連絡がなかったため、BとCはランダムに生成された期間待機してから、選挙に出て相手に通知します。 もちろん、この場合、最初に当選した人が当選します。 この例では、Bが新しいリーダーになり、クォーラムが復元されます。 しかし、それでは、どんでん返し:Aがオンラインに戻るとどうなりますか? まだリーダーだと思いますよね? 各選挙には用語が関連付けられているため、Aは実際には用語1で選出されました。Aがオンラインに戻り、BとCの注文を開始するとすぐに、Bが用語2のリーダーであることを知らせてくれます。 Aは辞任します。
もちろん、これと同じプロセスははるかに大規模に機能します。 3つ以上のマネージャーノードを持つことができます。 ただし、もう1つ簡単なメモを追加します。 各スウォームは、特定の数のマネージャーの損失のみを受け取ることができます。 n個のマネージャーノードの群れは、クォーラムを失うことなく(n-1)/2
個のマネージャーを失う可能性があります。 つまり、3人のマネージャーの群れの場合、1人を失う可能性があり、5人の場合、2人を失う可能性があります。これの根本的な理由は、多数派のコンセンサスについての考えに戻ります。これは、本番環境に移行するときに必ず覚えておくべきことです。
タスクのスケジューリングと調整
これまでのところ、私たちはマネージャーが同期を保つのが本当に得意であることを確立しました。 すごい! しかし、彼らは実際に何をしているのでしょうか? スタックのサービスをSwarmにデプロイすると言ったことを覚えていますか? サービスを宣言するときは、サービスを実際にどのように実行するかについての重要な情報をSwarmに提供します。 これには、各サービスに必要なレプリカの数、レプリカの配布方法、特定のノードでのみ実行する必要があるかどうかなどのパラメーターが含まれます。
サービスがデプロイされたら、設定したデプロイメント要件が引き続き満たされるようにするのはマネージャーの仕事です。 Nginxサービスをデプロイし、3つのレプリカが必要であることを指定するとします。 管理者は、実行中のコンテナーがないことを確認し、使用可能なノード全体に3つのコンテナーを均等に分散します。
ただし、さらにクールなのは、コンテナに障害が発生した場合(またはノード全体がオフラインになった場合)、Swarmは残りのノードにコンテナを自動的に作成して違いを補うことです。 3つのコンテナーを実行する場合は、3つのコンテナーを実行し、Swarmがすべての重要な詳細を処理します。 さらに、これは大きなプラスです。スケールアップまたはスケールダウンは、Swarmに新しいレプリケーション設定を与えるのと同じくらい簡単です。
サービス検出と負荷分散
最後の例から重要ですが微妙な詳細を指摘したいと思います。Swarmが選択したノードでコンテナーをインテリジェントに開始している場合、それらのコンテナーがどこで実行されるかは必ずしもわかりません。 最初は怖いように聞こえるかもしれませんが、実際にはSwarmの最も強力な機能の1つです。
同じNginxの例を続けて、これらのコンテナーがポート80を公開する必要があることをDockerに伝えたと想像してください。ブラウザーをポート80でそのコンテナーを実行しているノードに向けると、そのコンテナーのコンテンツが表示されます。 そこに驚きはありません。 ただし、驚くべきことは、そのコンテナを実行していないノードにリクエストを送信しても、同じコンテンツが表示されることです。 ここで何が起こっているのですか?
Swarmは実際に入力ネットワークを使用して、そのコンテナを実行している利用可能なノードにリクエストを送信し、同時に負荷分散を行っています。 したがって、同じノードに3つのリクエストを行うと、3つの異なるコンテナにヒットする可能性があります。 スウォーム内の単一ノードのIPを知っている限り、そのノードで実行されているすべてのものにアクセスできます。 逆に、これにより、何がどこで実行されているかを気にすることなく、スウォーム内のすべてのノードにロードバランサー(ELBなど)を向けることができます。
外部接続で止まりません。 同じスタックで実行されているサービスには、相互に通信できるオーバーレイネットワークがあります。 コードにIPアドレスをハードコーディングする代わりに、接続するホスト名としてサービスの名前を使用するだけです。 たとえば、アプリが「redis」という名前のRedisサービスと通信する必要がある場合、ホスト名として「redis」を使用するだけで、Swarmはリクエストを適切なコンテナーにルーティングします。 また、これはdocker-composeを使用した開発と、Docker Swarmを使用した本番環境でシームレスに機能するため、アプリをデプロイするときに心配する必要が1つ少なくなります。
ローリングアップデート
運用中の場合、本番アップデートがひどく失敗したときにパニック発作を経験した可能性があります。 悪いコード更新、または単なる構成エラーである可能性がありますが、突然本番環境がダウンします! 確率は、上司がどちらの方法でも気にしないだろうということです。 彼らはそれがあなたのせいであることを知っているだけです。 まあ、心配しないでください、Swarmはこれにもあなたの背中を持っています。
サービスを更新するときに、一度に更新する必要のあるコンテナーの数と、新しいコンテナーが失敗し始めた場合に何が発生するかを定義できます。 特定のしきい値を超えると、Swarmは更新を停止するか、(Docker 17.04以降)コンテナーを前のイメージと設定にロールバックできます。 明日の朝、上司にコーヒーを持ってくる必要はありません。
安全
最後になりましたが、Docker Swarmには、すぐに使用できる優れたセキュリティ機能が備わっています。 ノードが群れに参加するとき、ノードはそれ自体を検証するだけでなく、それがあなたが思っている群れに参加していることも検証するトークンを使用します。 その瞬間から、ノード間のすべての通信は相互TLS暗号化を使用して行われます。 この暗号化はすべてSwarmによって自動的にプロビジョニングおよび管理されるため、証明書の更新やその他の一般的なセキュリティの問題について心配する必要はありません。 もちろん、キーを強制的に回転させたい場合は、そのためのコマンドがあります。
Docker Swarmの最新バージョンには、シークレット管理も組み込まれています。 これにより、キーやパスワードなどのシークレットを、それらを必要とするサービスだけに、それらを必要とするサービスに安全にデプロイできます。 シークレットを使用してサービスを提供すると、そのサービスのコンテナには、シークレットの値を含む特別なファイルがファイルシステムにマウントされます。 言うまでもありませんが、これは従来のアプローチであった環境変数を使用するよりもはるかに安全です。
群れに飛び込む
あなたが私のようなものなら、あなたは飛び込んでこれらすべての機能を試してみたいと思っています! それで、それ以上の苦労なしに、飛び込みましょう!
DockerSwarmサンプルアプリ
Docker Swarmのパワーと使いやすさを実証するために、非常に初歩的なFlaskアプリを作成しました。 Webアプリは、リクエストを処理したコンテナー、処理されたリクエストの総数、および「シークレット」データベースパスワードを示すページを表示するだけです。
これは、実際のFlaskアプリ、Nginxリバースプロキシ、Redisキーストアの3つのサービスに分けられます。 リクエストごとに、アプリはRedisのnum_requests
キーをインクリメントするため、ヒットしているFlaskインスタンスに関係なく、正しいリクエスト数が反映されていることがわかります。
何が起こっているのかを「チェックアウト」したい場合は、すべてのソースコードをGitHubで入手できます。
Dockerで遊んでください!
このチュートリアルを進めるときは、独自のサーバーを自由に使用してください。ただし、参加したい場合は、play-with-docker.comを使用することを強くお勧めします。これは、数人のDocker開発者によって運営されているサイトで、複数のネットワークを起動できます。 Dockerがプリインストールされているノード。 4時間後にシャットダウンされますが、この例ではこれで十分です。
群れを作成する
了解しました。どうぞ! 先に進み、PWD(play-with-docker)で3つのインスタンスを作成するか、お気に入りのVPS(仮想プライベートサーバー)サービスで3つのサーバーを起動し、それらすべてにDockerエンジンをインストールします。 いつでもイメージを作成して、将来ノードを追加するときにそれを再利用できることを覚えておいてください。 マネージャーノードとワーカーノードの間にソフトウェアの違いはないため、2つの異なるイメージを維持する必要はありません。
まだ回転していますか? 心配しないで、待ってます。 では、最初のマネージャーとリーダーノードを作成します。 最初のインスタンスで、群れを初期化します。
docker swarm init --advertise-addr <node ip here>
<node_ip_here>
をノードのIPアドレスに置き換えます。 PWDでは、IPアドレスが上部に表示されます。独自のVPSを使用している場合は、ネットワーク内の他のノードからアクセスできる限り、サーバーのプライベートIPアドレスを自由に使用してください。
あなたは今群れを持っています! ただし、ノードが1つしかないため、かなり退屈な群れです。 先に進んで、他のノードを追加しましょう。 init
を実行すると、参加トークンの使用方法を説明する長いメッセージが表示されます。 他のノードをワーカーにするため、これは使用しません。これらのノードをマネージャーにします。 最初のノードでこれを実行して、マネージャーの参加トークンを取得しましょう。

docker swarm join-token manager
結果のコマンドをコピーして、2番目と3番目のノードで実行します。 見よ、3ノードの群れ! すべてのノードが実際に存在することを確認しましょう。 docker node ls
コマンドは、群れのすべてのノードを一覧表示します。 次のように表示されます。
$ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS su1bgh1uxqsikf1129tjhg5r8 * node1 Ready Active Leader t1tgnq38wb0cehsry2pdku10h node3 Ready Active Reachable wxie5wf65akdug7sfr9uuleui node2 Ready Active Reachable
最初のノードのIDの横にアスタリスクがあることに注意してください。 これは、現在接続しているノードであることを示しているだけです。 また、このノードが現在リーダーであり、何かが発生した場合に他のノードが到達可能であることがわかります。
それがいかに簡単であったかを理解するために少し時間を取って、最初のアプリをデプロイしましょう!
それを出荷!
これで、ビジネス開発チームは、新しいアプリが1時間以内に展開され、準備が整うことをクライアントに約束しました。 典型的な、私は知っています。 ただし、Dockerを使用して構築されているため、それほど時間はかかりません。 開発者は親切にもdocker-compose
ファイルを貸してくれました。
version: '3.1' services: web: image: lsapan/docker-swarm-demo-web command: gunicorn --bind 0.0.0.0:5000 wsgi:app deploy: replicas: 2 secrets: - db_password nginx: image: lsapan/docker-swarm-demo-nginx ports: - 8000:80 deploy: mode: global redis: image: redis deploy: replicas: 1 secrets: db_password: external: true
すぐに分解しますが、まだその時間はありません。 展開してみましょう! 先に進み、最初のノードにdocker docker-compose.yml
というファイルを作成し、上記の構成を入力します。これは、 echo "<pasted contents here>" > docker-compose.yml
docker-compose.ymlを使用して簡単に行うことができます。
通常、これをデプロイすることもできますが、構成にはdb_password
というシークレットを使用することが記載されているため、そのシークレットをすばやく作成しましょう。
echo "supersecretpassword" | docker secret create db_password -
すごい! これで、必要なのは、Dockerに構成を使用するように指示することだけです。
docker stack deploy -c docker-compose.yml demo
このコマンドを実行すると、Dockerが定義した3つのサービス( web
、 nginx
、 redis
)を作成していることがわかります。 ただし、スタックデモに名前を付けたため、サービスには実際にはdemo_web
、 demo_nginx
、 demo_redis
という名前が付けられています。 docker service ls
コマンドを実行すると、実行中のサービスを確認できます。これにより、次のように表示されます。
$ docker service ls ID NAME MODE REPLICAS IMAGE PORTS cih6u1t88vx7 demo_web replicated 2/2 lsapan/docker-swarm-demo-web:latest u0p1gd6tykvu demo_nginx global 3/3 lsapan/docker-swarm-demo-nginx:latest *:8000->80/ tcp wa1gz80ker2g demo_redis replicated 1/1 redis:latest
出来上がり! Dockerは、イメージを適切なノードにダウンロードし、サービス用のコンテナーを作成しました。 レプリカがまだフルキャパシティーになっていない場合は、しばらく待ってからもう一度確認してください。 Dockerはまだイメージをダウンロードしている可能性があります。
百聞は一見に如かず
ただし、私の言葉(またはDockerの言葉)を受け入れないでください。 私たちのアプリに接続してみましょう。 サービス構成では、Dockerにポート8000でNGINXを公開するように指示しています。PWDを使用している場合は、ページの上部に「8000」という青いリンクが表示されます。 PWDは、実際には、そのポートでサービスが実行されていることを自動的に検出しました。 それをクリックすると、ポート8000で選択したノードにルーティングされます。独自のサーバーをロールした場合は、ポート8000でサーバーのIPの1つに移動するだけです。
あなたはあなたにいくつかの基本的な情報を与える美しいスタイルの画面で迎えられます:
どのコンテナがリクエストを処理したかをメモしてから、ページを更新します。 オッズは変更されています。 しかし、なぜ? そうですね、DockerにFlaskアプリのレプリカを2つ作成するように指示しました。これにより、両方のインスタンスにリクエストが配信されます。 あなたはたまたまもう一方のコンテナに2度目に当たった。 また、両方のFlaskコンテナが指定した単一のRedisインスタンスと通信しているため、リクエストの数が増えたことにも気付くでしょう。
任意のノードからポート8000をヒットしてみてください。 それでも、アプリに適切にルーティングされます。
魔法の謎を解き明かす
この時点で、すべてが機能し、うまくいけば、プロセスが無痛であることがわかりました。 そのdocker-compose.yml
ファイルを詳しく見て、実際にDockerに何を伝えたかを見てみましょう。 大まかに言うと、 web
、 nginx
、 redis
の3つのサービスを定義していることがわかります。 通常の作成ファイルと同様に、各サービスで使用するイメージと実行するコマンドをDockerに提供しました。 nginx
の場合、ホストのポート8000をコンテナのポート80にマップするように指定しました。 これまでのところ、これはすべて標準の作成構文です。
ここで新しいのは、デプロイキーとシークレットキーです。 これらのキーはdocker-compose
によって無視されるため、開発環境には影響しませんが、 docker stack
。 Webサービスを見てみましょう。 簡単に言えば、Flaskアプリで2つのレプリカを実行することをDockerに伝えています。 また、Webサービスにdb_password
シークレットが必要であることをDockerに通知しています。 これにより、コンテナにシークレットの値を含む/run/secrets/db_password
という名前のファイルが確実に含まれるようになります。
Nginxに移動すると、デプロイモードがglobal
に設定されていることがわかります。 デフォルト値(Webで暗黙的に使用される)がreplicated
されます。これは、必要なレプリカの数を指定することを意味します。 global
を指定すると、Swarm内のすべてのノードがサービスのインスタンスを1つだけ実行する必要があることをDockerに通知します。 docker service ls
を再度実行すると、 nginx
に3つのレプリカがあり、群れの各ノードに1つずつあることがわかります。
最後に、群れのどこかでRedisの単一インスタンスを実行するようにDockerに指示しました。 Webコンテナは、Redisホストを要求すると自動的にルーティングされるため、場所は関係ありません。
Swarmを毎日使用する
最初のアプリをDockerSwarmにデプロイしました。おめでとうございます。 使用する一般的なコマンドをいくつか確認してみましょう。
あなたの群れを検査する
あなたのサービスをチェックする必要がありますか? docker service ls
およびdocker service ps <service name>
を試してください。 前者は各サービスの概要を示し、後者は指定されたサービスで実行されている各コンテナに関する情報を示します。 これは、どのノードがサービスを実行しているかを確認する場合に特に役立ちます。
ローリングアップデート
アプリを更新する準備ができたらどうしますか? docker stack deploy
の優れた点は、既存のスタックにも実際に更新が適用されることです。 新しいDockerイメージをリポジトリにプッシュしたとしましょう。 実際には、最初に使用したのと同じデプロイコマンドを実行するだけで、群れが新しいイメージをダウンロードしてデプロイします。
もちろん、スタック内のすべてのサービスを常に更新したい場合はありません。 サービスレベルでも更新を実行できます。 最近、Webサービスの画像を更新したとしましょう。 このコマンドを発行して、すべてのWebコンテナーを更新できます。
docker service update \ --image lsapan/docker-swarm-demo-web:latest \ demo_web
このコマンドの追加の利点は、元の構成で行うように指定した場合にローリング更新が適用されることです。 また、そうでない場合でも、次のようにローリング更新を実行するように指示するフラグを更新に渡すことができます。
docker service update \ --image lsapan/docker-swarm-demo-web:latest \ --update-parallelism 1 --update-delay 30s \ demo_web
これにより、一度に1つのコンテナが更新され、更新の合間に30秒待機します。
サービスの拡大または縮小
2つのWebコンテナがあることは素晴らしいことですが、何が優れているか知っていますか? 10個持っています! 群れの中でサービスを上下にスケーリングするのは、次のように簡単です。
docker service scale demo_web=10
そのコマンドを実行し、 docker service ps demo_web
の出力を確認します。 現在、10個のコンテナーがあり、そのうち8個はほんの少し前に開始されたことがわかります。 興味がある場合は、Webアプリケーションに戻ってページを数回更新し、元の2つ以上のコンテナーIDを取得していることを確認することもできます。
スタックとサービスの削除
スタックとサービスはデプロイおよびスケーリングされており、すばらしいです。 しかし今、あなたはそれらをオフラインにしたいのです。 これは、それぞれのrm
コマンドで実行できます。 デモスタックを削除するには、次のコマンドを実行します。
docker stack rm demo
または、1つのサービスを削除するだけの場合は、次を使用します。
docker service rm demo_web
ドレインノード
以前にdocker node ls
を実行して、群れのノードを確認したときのことを覚えていますか? 可用性など、各ノードに関する一連の情報を提供しました。 デフォルトでは、ノードはアクティブです。つまり、コンテナを実行するのに公平なゲームです。 ただし、メンテナンスを実行するためにノードを一時的にオフラインにする必要がある場合があります。 確かに、シャットダウンするだけで群れは回復しますが、Moby(Dockerクジラ)に少し注意を払うのはいいことです。
ここで、ノードのドレインが発生します。ノードをドレインとしてマークすると、Docker Swarmは、そのノードで実行されているすべてのコンテナーを他のノードに委任し、可用性をアクティブに戻すまで、ノード上のコンテナーを開始しません。
node1
を排出したいとしましょう。 実行できます:
docker node update --availability drain node1
簡単! 仕事に戻す準備ができたら:
docker node update --availability active node1
まとめ
これまで見てきたように、DockerとSwarmモードを組み合わせると、これまでになく効率的かつ確実にアプリケーションをデプロイできます。 DockerSwarmが唯一のコンテナオーケストレーションエンジンではないことは言及する価値があります。 実際、それは若いものの1つです。 Kubernetesは以前から存在しており、より多くの本番アプリケーションで確実に使用されています。 とは言うものの、SwarmはDockerによって公式に開発されたものであり、彼らは毎日さらに多くの機能を追加することに取り組んでいます。 どちらを使用する場合でも、コンテナを使用してください。