Docker CLIに梱包された新しいdocker compose

ゴリラ氏:「新しいDocker Compose」と題して発表します。よろしくお願いします。

軽く自己紹介をします。ゴリラと申します。好きなものはGo、Vim、Dockerとかで、最近はちょっとDenoにハマっています。趣味はVimのプラグインだったり、Goを使ったCLIだったり、開発が多いです。あとは興味がある分野の勉強だったり、記事を書いたりです。最近はジムに行っていて、ムキムキな体を目指しています。

本日の話ですが、docker composeが正式に使えるようになったので、それについていろいろと話していきます。実際に検証した環境は次のとおりです。ARMアーキテクチャですが、一応最新の20.10.6のバージョンを使っています。

そもそもdocker-composeとは何だ? という話があるので、改めて話していきます。今回新しく入ったdocker-composeは、Docker CLIに梱包された新しいdocker-composeです。docker-composeは、たぶんみなさんもたくさん使っていたと思うんですが、今回新しくDocker本体にプラグインとして同梱されました。

docker-composeのメンテナンスはどうやら継続するようです。雑に言うと、docker composeはdocker-composeのGo実装になっています。正式名称はDocker Compose CLIで、docker-composeと互換性をもっています。

ただ一部のフラグを除いては、非推奨フラグだったり、まだbuildkit自体が対応していないフラグがあります。それ以外は、基本的に全部動くようにはなっています。docker composeは、どうやらdocker-composeに置き換わることを目標にしているようです。

docker composeはcompose-specという、Composeの仕様を標準化したものですが、compose-specの仕様に準拠しています。といっても、ほぼdocker composeと変わらないので、使う側はあまりそのあたりの違いを意識する必要はないと思います。

現時点でdocker composeが使えるコマンドはこの表のとおりですが、基本的にdocker-composeの全部のコマンドが使えます。それに合わせて、今回プラグインになったdocker composeの独自のコマンドもあります。

この中はlsのところですね。List running compose projects。実際のComposeのプロジェクトの一覧が、このlsコマンドで見られます。

compose-specとはそもそも何だという話ですが、プラットフォームに依存しない、マルチコンテナなアプリケーションを定義するための標準仕様になっています。もともとdocker-composeが提供していた機能を、標準な仕様として査定していきましょうというものです。公式はこちらのURLから見られます。

実際のすごく細かいパラメーターの名前だったり、値だったり、どういう動きをするだったりの細かい定義がたくさん書いてあるので、興味がある方は読んでみてください。

Docker Compose CLIの特徴とdocker-composeとの差分

さっそくですが、Docker Compose CLIの特徴について話していきます。まず1つ目の大きな特徴として、クラウド対応ですね。マルチコンテナをECS、ACI、Azure Cloud Instanceなどのクラウドに簡単にデプロイできる機能が追加されています。

AWSの例だと、docker compose upでFargetのクラスタ、サービス、タスクなどを一発で合わせて作成してくれます。また、docker compose downでそれらを作成したリソースをまとめて削除してくれます。とても便利だなと思いました。

CLI本体に関してですが、基本的にdocker-compose.yamlのパースライブラリとしてcompose-goが公開されていて、こちらもGoで書かれています。今回同梱されたDocker Compose CLIも、実際にcompose-goを使ってdocker-compose.yamlを読み込んで解析しています。これがあることによって、Compose関連のツールの作成がとても楽になるのではないかなと思っています。

あとはbuildkitがデフォルトで有効になっています。なので、例えばdocker-compose buildとやると、裏でbuildkitが動いてくれます。これは確かdocker-composeもデフォルトで有効なので同じかなと思います。あとはこれはどちらかというと言語の特徴ですが、Goによる実装なので、goroutineを使った並行処理のメリットを活かせるのではないかと思っています。

例えば複数のコンテナをgoroutineを使って同時に立ち上げたり、今までは順に処理していかなければいけなかった部分がgoroutineで並行処理できるようになったりするのではないかなと思っているので、このあたりは期待したいところです。あとはDocker CLIのSDKなど既存のエコシステムを使えるので、より開発しやすくなるのではないかと思っています。

みなさんも気になると思う、docker-composeとの差分ですが、さっきチラッとお話したdocker-compose build --memoryへは現時点では未対応のようです。これはどうやらbuildkitがまだそれに対応していないために現在未対応になっています。また、一部のdocker-composeで非推奨になったフラグはdocker composeで対応しないとなっているそうです。

実際にdocker-compose rm --allは、docker composeでは対応しないとなっています。詳細は公式のドキュメントを読んでもらうとよりわかりやすいかなと思います。このCompose CLIは、実際のdocker composeの実体のリポジトリなのですが、こちらのissueにdocker-composeとの差分がすごく細かくまとめられているので、興味のある方は読んでみてください。

docker-composeからの移行についてですが、個人的な考えとしては個人と開発での利用は移行してもいいんじゃないかなと思っています。というのも、docker-composeの機能はほぼすべてが互換しています。先ほど説明した一部のフラグの対応はしていないんですが、基本的に問題はないと思っています。

あとは単純に、goroutineとかGoを使っているのでパフォーマンスの向上を少し期待できるかなと思っています。今後より便利な機能が追加される可能性があるので、今後はlsのサブコマンドとかに個人的に期待したいなと思っています。

実際にdocker composeを使ってみました。こちらのドキュメントで、WordPressのYAMLを用意して実行してみました。こんな感じでbuildkitが動いていて、docker-compose upとほぼコマンド定義は変わりません。出力の部分がちょっとかっこよくなっているんですが、基本的に使い方は変わらないという感触です。実際に立ち上げた時もきちんとWordPressのインストールの画面も出ました。

あとはコンテナ一覧ですね。docker compose psとか、docker compose downとか、出力のフォーマットが若干今までと違うんですが、基本的には同じ感じです。docker compose execはみなさんもよく使うコマンドだと思うんですが、これも特に問題なく動いています。これもプロセス一覧のtopコマンドがあるんですが、きちんと出力はされています。

docker composeを使ってECSにコンテナをデプロイ

実際にローカルで動かすところはほぼ変わらないので、実際にECSにコンテナをデプロイしてみようと、ちょっと試してみました。利用するdocker composeはすごくシンプルなnginxのものにしました。シンプルに80番ポートですね。

docker composeでECSにデプロイする前に、まずコンテキストを作らないといけないんですが、docker context create ecsで、コンテキストの名前とやると、既存のAWSのプロファイルを選ぶ画面が出てきます。これは選んだあとのログですが、既存のプロファイルを使いますか? を選んで、どのプロファイルを使いますか? を選んだあとにこういうふうにSuccessfullyと出ます。

あとはコンテキストを切り替えるコマンドですね。docker context useで、デフォルトで使うコンテキストを、このmyecsに切り替えます。コンテキストを切り替える時はuseを使うのもいいんですが、-cでコンテキストを指定することもできます。

docker compose upと打って実行すると、裏でECSに対していろいろなコンポーネントを作ってくれます。LB、クラスタ、タスク、定義、サービスなど、このあたりでまとめて一気に作ってくれます。docker composeが原因なのかはわからないんですが、これはけっこう時間がかかりました。これはまだ終わっていないんですが、だいたい170秒以上は使っていました。

実際にdocker compose psで見ると、きちんとコンテナとタスク定義が作られています。このURLが実際のLBのURLになっていて、ここをcurlコマンドで叩くときちんとレスポンスが返って来ます。実際に叩いてみると、きちんとnginxのHTMLが返って来ました。

リソースの削除も先ほど説明したとおりすごくシンプルで、docker compose downとやると、全部がまとめてバッと消えます。これもECSだからなのかはわからないんですが、時間がすごくかかってしまって、460秒以上かかりました。なんでそんなに時間がかかっているのかは、正直ソースコードを追ってみないとわからないので、この原因を知っている方がいたらぜひ教えてもらいたいなと思っています。

Docker Compose CLIのアーキテクチャ

Docker Compose CLIのアーキテクチャについて少しお話しします。これはリポジトリからお借りした画像ですが、大きく分けてコンポーネントが4つあります。API、CLI、Context manager、Backend interface。このBackend interfaceは各バックエンドの実装があります。

このAPIのところは一旦置いておいて、先ほどのコマンドを叩く部分ですね。あれがCLI UXになっていて、その裏でDocker Compose CLI自体はContext managerをもっています。Docker Compose CLI自身もコンテキストをもてますし、先ほどのDocker CLIのコンテキストも使えます。

このBackend interfaceは、先ほど軽く紹介したECSへのデプロイのところです。この各クラウドやローカルなど、各プラットフォームのインターフェイスの抽象化の部分がBackend interfaceとなっています。なので、実体としてECSやACIがあったり、ローカルのDocker Engineがあったりというかたちになっています。

これは実際のソースコードの一部ですが、このサービスが実際のインターフェイスの定義になっています。このContainerService()はコンテナがどんなことができるかをまとめているところです。なので、例えばそのコンテナを作成したり、立ち上げたりというインターフェイスがここで定義されています。

このcomposeが、ほぼCompose CLIのサブコマンドに対応していて、例えばupやdownが、ContainerService()でまとまっています。あとはResourceとかVolumeとかいろいろとあるんですが、気になる方はソースコードをよく見てみてください。こんなかたちで抽象化されていて、具体的にはこのインターフェイスを実装していくというかたちです。

先ほど話したように、このBackend interfaceの部分がプラットフォームの差異を吸収してくれています。

現時点のバックエンドの実装ですが、ECSと、ACIと、あとはDocker Engineですね。ACIはちょっと試していないんですが、ECSとDocker Engineは普通に動いていることを確認しました。このAPIの部分は、gRPCのサーバーになっていて、言語に縛られずにdocker composeの機能を使えます。

これに関してはドキュメントがあまりなくて、このCompose CLIのリポジトリの中にさらにCLIというディレクトリがあって、その中にこのサーバーを立ち上げる実装が入っています。実際に試す時間がなかったので試していないんですが、実際にメッセージを送るとたぶん普通にComposeでコンテナを作ったりできるんじゃないかなと思っています。

この図で言うとNode.jsなのかな? Node SDKなのかな? を使って、gRPCを使ってAPIと通信をして、例えばECSのバックエンドを通してECSをデプロイするかたちになるんじゃないかなと思っています。

Docker CLI Pluginの紹介

最後に少しおまけで、Docker CLI Pluginについて少し紹介します。先ほどお話ししたDockerに同梱されているプラグインで、/usr/local/lib/docker/cli-pluginsの配下にあります。

これはMacのパスですが、その中に/docker-appとか、/docker-buildxとか、/docker-scanとかがあるんですが、この/docker-composeが実際のDocker Compose CLIの本体になっています。どうやらbuildkitやscanもプラグインになっているようです。このプラグインの仕組み自体は、Docker CLI Pluginと呼ばれています。

先ほどのこのディレクトリはDocker本体から使っているディレクトリですが、ユーザーの場合は自分のホームディレクトリ配下に.docker/cli-pluginsのファイルを置くと、それがDockerのサブコマンドとして実行できるようになります。

実行のファイル名ですが、docker-というプレフィックスを付ける必要があります。例えばファイルがhelloだったら、docker-helloとしてコマンドが実行できます。このプラグインのバイナリは、docker_cli_plugin_metadataというコマンドを実行する必要があります。

この下のJSONを出力して、Docker CLIがこのJSONを受け取って、実際にそのサブコマンドの情報を表示するために、出力をするコマンド名が必要です。

実際に軽く作ってみました。バイナリではなくて、シェルでも動くんですが、実際にdocker_cli_plugin_metadataという関数と、出力するJSONのかたちでシェルを用意しました。実際にホームディレクトリにプラグインディレクトリを作って、ダウンロードして、実行権限を渡すとDockerのヘルプに出てきます。

このHello Docker CLI Pluginですね。descriptionのところが出てきます。あとはベンダーとかバージョンとかが出てきます。実際に実行してみると、これは引数を渡してechoしているだけなので、hello gorillaとかhello mobyと出てきます。

最後にまとめです。Docker Compose CLIは次世代のdocker-composeになっています。docker-composeの機能をほぼすべて網羅していて、そしてGo言語で実装されています。DockerのエコシステムやSDKを使っているところが拡張しやすいポイントになっています。

docker-composeのメンテナンスは継続されるそうです。いつまでなのかはちょっとわからないのですが、「継続」とドキュメントに書いてありました。Docker Compose CLIの特徴の1つとして、プラットフォームに依存しないというのがあるので、今後はクラウドとの連携もより強化していくんじゃないかなと思っています。

あとは、このDocker Compose CLIはシンプルにDocker CLIのプラグインとして同梱されているだけで、Docker CLIの本体に入っているわけではないのがポイントですね。ECSへのデプロイと削除はちょっとまだ時間がかかるなというところですね。

最後に参考資料をまとめたので、興味がある方は一通り読んでみてください。というわけで、発表は以上にです。ご清聴ありがとうございました。