ドッカーコン

最新のコンテナビルドによるCI/CDの合理化

Kevin Alvarez 氏、シニア ソフトウェア エンジニア、Docker

2023年11月11日収録
CI イベントを使用してタグ、ラベル、分散ビルドを自動化しながら、キャッシュ管理を最適化してピーク パフォーマンスを実現する方法を学びます。 この記事では、キャッシュ管理、マルチプラットフォームイメージの習得、タグとラベルの自動化、Bakeによる分散ビルド、および包括的なコンテナベースのワークフローについて説明します。

写し

こんにちは、Dockerのケビンです。 私はビルドチームで働いており、今日はビルドツールのみを使用したCI継続的インテグレーションについてのプレゼンテーションです。

概要

まずお話ししたいのは、CI(継続的インテグレーション)プロバイダとGitHub Actionsです。 これは私たちが持っている人気のあるプロバイダーであり、簡単な紹介とGitHub Actionsでこれに焦点を当てたいと思います。 主なコンポーネントは、私たちが提供しているDockerアクションに関するもので、今日、マーケットプレイスにはそのうちの6つがあります。

まず、作業を開始できる基本的なワークフローを見てみましょう。 これは基本的な手順であるため、この基本的なワークフローからどのように進化できるか、またビルドキャッシュを使用して最適化する方法も理解できます。 ご存知のように、Dockerイメージでは複数のプラットフォームがサポートされています。 これは最近人気のあるトレンドであるため、さまざまなアーキテクチャに最適化されたコンテナイメージを作成する方法を確認できます。 この章に続いて、マテリアルを公開するため、またはタグとラベルの自動化を使用して画像を公開するためのリンクが表示されます。 これも私たちの公式の行動を使用しています。

これに続いて、 Bake を使用してワークフローを解き放ち、簡素化する方法があります。 これもビルドツールです。 あまり人気はありませんが、このプレゼンテーションではこれに少し焦点を当てたいと思いますので、新しい定義を使用してビルドを行う利点を確認できます。

それから、もう一つ小さな部分があるでしょうが、本当に重要なことだと思います。なぜなら、今日、CIで悪いパターンが使われているかもしれませんし、シークレットは重要な部分であり、独自の章に値すると思います。 最後に、コンテナベースのワークフローを使用して、内部ループとCIからより大きな収益性を達成する方法を見ていきます。

目次

    GitHub アクション

    GitHub Actions を始めましょう。GitHub Actionsとは何ですか? 最初にワークフロー自体があるため、ここでパイプラインがイベント、ランナー、ジョブなどで定義されます。 この仕様では説明しませんが、この講演では多くのワークフローを示すため、その仕組みをよりよく理解していただくためです。

    また、イベントも開催しています。 これは、ワークフローの実行をトリガーするリポジトリ上の特定のアクティビティです。 プッシュプルリクエストのようなものがたくさんあります。 ワークフローディスパッチは、ワークフローを手動でトリガーするために一般的に使用されるものです。 ランナーもあります。 つまり、これは、Ubuntuの最新、macOSの最新、およびセルフホストランナー(ある場合)などのワークフローを実行するサーバーです。 また、各ランナーは一度に 1 つのジョブを実行できます。

    仕事があります。 これは、ワークフロー内で実行される一連のステップであり、同じインスタンス内で実行されます。 そして、アクションは、これは、私が言うように、あなたが構築できるカスタムアプリケーションです。 コンテナにすることも、JavaScript アクションにすることも、複合アクションにすることもできます。 これは、何かのコマンドを実行しています。 しかし、このプレゼンテーションでは、私たちが提供しているすべての接続について取り上げるので、そのうちの6つがあります。

    今日の接続の現在の状態を見てみましょう。 私たちはあなたのパイプラインを容易にしたいと考えており、これを行うために、ワークフローにシームレスに統合するためのこれらのGitHub Actionsを提供しています。 これの中で最も人気があるのは、おそらくビルド-プッシュ-アクションです。 これは基本的にあなたのイメージをビルドプッシュするためのものです。 これはビルドツールを使用しているため、これは buildxBuildKit であり、マルチプラットフォームビルド、シークレットの使用、キャッシュのエクスポートなどを完全にサポートしています。 BuildKitのおかげで、さまざまなビルダー、デプロイメント、間隔オプションを使用することもできます。 もちろん、そこでの他の行動についてもお話しします。 たとえば、画像をプッシュできるようにするにはログインアクションが必要など、サテライトアクションは他にもあります。 最初にログインアクションをトリガーする必要があり、次にbuild-push-actionを使用できます。

    ご覧のとおり、actions-toolkitがあります。 それはそれ自体が行動ではありません。 これは、すべての操作に使用しているライブラリにすぎません。 これは、ビルドツールに関するいくつかのユーティリティと共通のロジックを提供します。 これは、必要に応じて使用できる最小限のラッパーであり、私たちがそれらと対話するのを容易にするAPIです。 そしてもちろん、このツールキットは、今日の私たちのすべての行動によって消費されます。

    基本的なワークフロー

    基本的なワークフローを見てみましょう。 まず、このワークフローはワークフロー ディスパッチ イベントでトリガーされます。 手動で呼び出されるだけで、リポジトリに移動するだけで済みます。 このワークフローは、特定のイベントなしで手動で構成し、これには 2 つの公式アクションが使用されます。 login-action と build-push-action があります。 したがって、login-actionはDocker Hubでそこにログインします。 その後、build-push-action を使用してイメージにタグを付け、イメージをプッシュできます。 だから、これは本当に簡単です。 私は後でマルチプラットフォームについて話したいので、このようなプロジェクトを持つのは簡単なので、単純なGoプロジェクトを使用しています。 これは単純なWebサーバーです。 その背後には派手なものは何もありません。

    ビルドを実行する

    このビルドを実行してみましょう。 実際にこれを実際に見るためにライブデモを行いたかったのですが、このプレゼンテーションのスクリーンショットになります。 ご覧の通り、約 24 秒かかりました。 何かを構築するのは悪くありませんが、実際にキャッシュを活用すると、はるかにうまくいくことができます。 したがって、そこにビルドログを見ると、このステージがキャッシュされていないことがわかります。 このワークフローを実行するたびに実行されます。 このステージは実行され、キャッシュされる制限はありません。 したがって、これにキャッシングを活用できます。

    キャッシュの最適化

    キャッシュを使用してワークフローをより適切に最適化できます。 まず、キャッシュをエクスポートするためのキャッシュ実装の観点から、BuildKitが提供するものを確認できます。 今日、ビルドキットで利用できる多くのバックエンドがあります。 ご存知のように、BuildKitは、ビルドを行うと、ビルド結果を自動的に独自の内部キャッシュにキャッシュします。 しかし、GitHubランナーがよく知っているように、このキャッシュは存在せず、永続化されていません。 BuildKitの考え方は、ビルドキャッシュを外部の場所にエクスポートし、将来のビルドでインポートできるようにすることです。 したがって、実行の間には、次のキャッシュバックエンドが利用可能ですが、今日使用するものもあります。

    これは、GitHub APIを使用しているGHA1、GitHub Action1 です。 基本的には公式のGitHubキャッシュアクションと同じです。 これは同じ種類のAPIを使用しています。 ワークフローでこのキャッシュを有効にするには、一連の buildx アクションを追加するだけです。 この一連の buildx アクションは、現在利用可能なすべての BuildKit 機能を活用するためのコンテナビルダーを作成します。 そのため、Dockerエンジン内に埋め込まれたBuildKitでは現在利用できないBuildKitの最新のテーブルを使用しています。

    これで、キャッシュ命令を設定して、「このキャッシュを使用したい」と言うことができます。 このキャッシュをGHAにエクスポートするようにプッシュします。 このキャッシュは、GitHub Actionsバックエンドからも読み取ることができます。 したがって、このコードをビルドすると、このキャッシュはキャッシュメタデータとレイヤーの両方を GitHub キャッシュサービスに保存します。 これは、内部的には Azure Blob ストレージです。 その上、彼らは独自のAPIを持っており、これはもちろん公式のGitHub Actionsで使用されているのと同じバックエンドです。

    このプロジェクトをビルドする場合、新しいオプションでどのように機能するかを見てみましょう。 ご覧のとおり、キャッシュを設定してから8秒かかるため、これははるかに優れています。 ビルド時間を 67%節約したので、これはかなり素晴らしいことです。 ログを見ると、ビルドステージからの命令がキャッシュされていることがわかります。 GitHub キャッシュ バックエンドと GitHub Actions を使用すると、この CI を使用するときに現在使用できる最も簡単なリモート キャッシュです。 別の CI プロバイダーを使用している場合は、たとえば、S3 バックエンド、AZ BLOB バックエンド、または必要に応じて別のストレージバックエンドを使用できます。 ただし、認証レイヤーが必要です。 このようなものが必要です。 GitHub Actions を使用すると、これをすぐに利用できるため、他に何も必要ありません。

    先に進む前に、キャッシュとGitHub Actionについて注意しなければならないことがあります。 キャッシュには、リポジトリ全体で共有される 10 ギガバイトのサイズ制限があります。 そのため、GitHubはキャッシュを保存しますが、サイズを超えるとキャッシュの削除を開始します。 キャッシュをリサイクルすると、全体的に実行時間が遅くなる可能性があるため、注意が必要です。 エクスポートする必要があるものは、デフォルトでは、Dockerファイルの最後のステージをエクスポートするだけです。 しかし、例えば mode=max のようにすべてをエクスポートしたい場合は、これに注意する必要があります。

    スコープキーがあります。 したがって、スコープ キーはデフォルトで BuildKit という名前になっているため、これがキャッシュが属するキーです。 たとえば、モノラルリポジトリに多くのプロジェクトが含まれている場合に便利です。 たとえば、このキャッシュをfooに、他のキャッシュをbarに配置して、一緒に子にならないようにしたいと言うことができます。また、モノリポジトリを使用して複数のプロジェクトに制限することもできます。 デフォルトでは、今日の BuildKit は GitHub Actions キャッシュ内のキャッシュ マウントをエクスポートしません。 これを行う場合は、この GitHub Actions を使用して回避策があるため、キャッシュ マウントをエクスポートできます。

    マルチプラットフォームビルド

    次に、マルチプラットフォームビルドについて少しお話ししましょう。 ご存知のように、Dockerイメージは複数のプラットフォームをサポートしているため、1つのイメージに複数のバリアントとOS上の複数のプラットフォームを含めることができます。 これは特にCIで人気のあるトレンドなので、今日はこれをお見せしたいと思います。

    マルチプラットフォームビルドを行うには、基本的には単一の入力を提供するだけです。 この場合、私はただx96、Armビルドが欲しいと言います。 これを行うには、これを指定するだけで、場合によっては、Dockerfileの記述方法に大きく依存します。 したがって、エミュレーターもインストールする必要があり、そのために、私たちが提供するセットアップQEMUアクションを使用することもできます。 それはあなたのビルドのエミュレータをインストールするだけです。 ご覧のとおりにこのビルドを実行すると、はい、3分ですが、これは良くありません

    追加のプラットフォームを構築するだけでも大変なことですが、アイデアはありますか? 誰かがなぜそんなに時間がかかるのか考えていますか? いいえ。たぶん、わかりました、私自身も考えを持っているので、ログが答えをくれるかもしれません。 見てみましょう。 だから、はい、私たちは犯人を見つけたと思います。 ご覧のとおり、Arm の 1 つはこのプロジェクトのビルドだけで約 3 分かかり、もう 1 つは 30 秒かかります。 ですから、はい、これが問題です。 エミュレーションは有罪です。 基本的に、ご存知かもしれませんが、Gitイベントインフラストラクチャでは、Ubuntuの最新のランナーはx96 アーキテクチャです。 したがって、Armビルドを行うときは、エミュレーションのみを使用します。 また、エミュレーションはビルドを行う際の大きなペナルティであるため、非常に時間がかかります。 では、どうすればこれを解決できるのでしょうか? この問題を解決するには2つの解決策があり、これから見ていくのは、エミュレーションを使用してこのペナルティを取り除くためにDockerfileでクロスコンパイルを使用する方法です。

    まず、Dockerfileに戻りましょう。 ログで確認したように、ビルド状態はそれぞれのプラットフォームに対してビルドされます。 そしてこの場合、Goでクロスコンパイルを使用するのは非常に簡単です。 一連の変数を渡すだけで、ターゲットアーキテクチャを使用することができます。 Dockerfile のグローバル スコープで引数を提供します。 したがって、フロントエンドでは、この引数のセットを使用すると、特定のターゲットが使用されます。 この場合、クロスコンパイルを使用して、ネイティブプラットフォームを使用してビルドを行い、ターゲット引数を使用してGoコンパイラでクロスコンパイルを行います。

    しかし、これを推測するにはどうすればよいでしょうか? そのために、Dockerfileでダッシュダッシュプラットフォームを使用できるため、ビルドプラットフォームに厳密になります。 これはビルドが実行される場所であり、ターゲットOSとターゲットアーキテクチャを指定できます。 その後、GOOS と GOARCH を指定できるため、定義するターゲット プラットフォームと一致します。 さて、ビルドなので、どれくらいの時間がかかるかを確認する必要があります。 今は 47 秒です。そのほうがいいです。 これは、クロスコンパイルを使用しているためです。

    ここでログを見ると、AMD 64 とArmの 64 が同時にかかっていることがわかります。 そこにはまだいくつかのペナルティがあります。 以前のことを思い出すと、約 24 秒かかりました。同じランナーで 2 つのビルドが発生しているため、 31 秒かかります。 したがって、同じインスタンスに 2 つのビルドがあります。 たとえば、このビルドを複数のランナーに分散してペナルティを減らし、後で結果をマージできるようにすることができます。 しかし、それは後で見ることになります。 もちろん、クロスコンパイルはプログラミング言語によっては適切でない場合があることに留意してください。 たとえば、Goを使用すると、それは非常に簡単です。 Rustを使用すると、それも簡単になります。 しかし、これができる場合、使用できる唯一の実行可能なソリューションは、ARM上にネイティブノードを持つことです。 AMDにはネイティブノードがあり、たとえば 64、それぞれに基づいて構築できますが、ワークフローでより高度なセットアップが必要です。 したがって、これにはいくつかの制約があります。

    イメージタグを管理する

    マルチプラットフォームビルドの方法を見てきたので、イメージをプッシュするときにイメージタグをレベルで管理する方法を確認できます。 少し複雑になるかもしれません。 CIでは、それがよく見られます。 GitHub Actionの冒頭には、リポジトリに同様のタグがプッシュされているかどうかに応じて、プッシュするタグのリストを指定するためだけに、大きなスクリプトのチャンクを持つことができることがわかります。 または、たとえばエッジリリースを使用する場合、mainに何かがあり、これで条件を抽出するためにブランチの名前をプッシュしたいと言うでしょう。 だから、これはちょっとした苦痛です。 手間を省くために、ワークフローのタグとレベルの作成を自動化できるGitHub Actionを開発しました。

    見てみましょう。 メタデータアクションを使用してビルドする前に、抽出ステップを定義する必要があります。 したがって、画像入力のみが必要です。 この場合は、リポジトリです。 これは単純なリポジトリです。 GHCRにプッシュしたい場合は多くのイメージを使用でき、同時にDocker Hubも定義できます。 専用のタグがある場合は、その後にbuild-push-actionに適用する必要があるレベルのタグが生成されます。 この場合、前の手順の入力でタグを設定しました。 たとえば、メインブランチにプッシュすると、このタグのリストが生成されます。 したがって、mainと呼ばれるものは1つだけあり、自動的に生成されるOCI形式の仕様ラベルのリストもあります。 この種のことをオプトアウトする方法はありませんが、ご覧のとおり、説明があります。 これは GitHub API から取得されます。 これはリポジトリの説明です。 また、取得されてそこに配置されているリポジトリのタイトルもあるため、この種のアクションを使用してイメージに何が含まれているかを追跡できます。 これはかなり便利だと思います。 たとえば、コンテナ・イメージにイメージのREADMEが表示されると、OCIラベルが利用され、たとえば、READMEに意味のある情報が表示されます。

    そして、v1 タグを押すと、v1.0。0、最新の v1を生成します。0。0 も同様です。 少しわかりにくいかもしれませんが、内部で何が起こっているのかを説明しましょう。 この種のカスタマイズされたタグを使用すると、アクションがどのように元に戻すのか疑問に思うかもしれません。 デフォルトでは、tags 入力を指定しない場合、デフォルト値は type=schedule と type=ref で、event=branch タグまたは PR が指定されます。 したがって、type=scheduleは、ワークフローにのみスケジュールイベントがある場合に使用され、名前をタグ付けします。 タグにはデフォルトで適切な名前が付けられ、Git 参照の下の ref はブランチと同様に、リクエストのタグにもなります。 特定のユースケースには他にもルールがあり、たとえば、Git フローの一般的なパターンは、同様のタグをプッシュしてプロジェクトをリリースすることです。

    私たちの場合、画像タグと最新のタグを設定するだけでなく、メジャー/マイナーのようなものが必要な場合もあれば、メジャーのみが必要な場合もあります。 これを行うには、タグに適用するルールを指定する必要があります。 この場合、たとえば、フルバージョンが欲しいと言いますが、一部のユーザーはパッチを気にせず、イメージでメジャーマイナーのみを使用するため、メジャー/マイナーも設定してほしいと言います。 したがって、この場合、type=semverタグの周りでは、たとえば、v1を押すと生成されます。0。0、それは言うでしょう、私は 1を持っています。0。0, 私はv1を持っています。0、 と latest、およびコミット SHA が設定された SHA。

    これは自動的に生成されます。 気にする必要はない。 それはあなたのためにこれを設定します。 たとえば、プルリクエストの場合は Snd が生成され、この場合は PR196が生成され、SHA の場合も同様です。 これは何かを押すたびに設定されているので、これはかなり便利です。 他にもケースがあります。 Pythonとバージョン管理に固有のものがあります。 また、エッジケースに使用されている他のものもあり、例えば、プレフィックスサフィックスの下にようなフレーバーを入れたいとします。 例えば、AlpineバージョンのイメージやDebianバージョンが必要だとしましょう。 Debianなどのプレフィックスを付けることができます。 だから、多くのルールがあります。 リポジトリには膨大なドキュメントがありますので、ご覧ください。

    焼く

    Bakeの話をしたいのですが、ご覧の通りワークフローはかなり巨大で、このワークフローのサイズを大幅に縮小したいと思います。 ベイクはこれを助けてくれます。 始める前に、Bakeが何であるかわからないかもしれないので、buildコマンドでルートに戻ります。 ご存知のように、今日のビルドコマンドでこのようなものを持つことができます。

    かなり大きいです。 buildコマンドには多くのフラグが用意されており、これをローカル環境からCIに移植すると、コピーペーストが必要になる可能性があるため、この種のことを単純化したいと考えています。 両方を維持する必要があるので、それは素晴らしいことではありません。 あなたのワークフローでは、おそらく、ローカルコマンドをビルドプッシュアクション内の入力に移植する必要があるため、このようなものがあるでしょう。 これは非常に大きいです。 それは理想的ではありません。 私たちは両方を維持する必要があります。 嫌です。 また、プロジェクトをビルドするためには、コントリビューティングノートのドキュメントを更新する必要がありますので、それはあまり良くありません。 Bake を使えば、代わりにこのようなことができます。 ですから、例えば、このプッシュフラグを定義するだけで、何かを作りたい、何かを作りたい、それをプッシュしたい、というフラグを定義する必要があります。

    そこで、先に進む前に、まずベイクの定義とは何かについてお話ししたいと思います。 これがBakeを使用するための出発点です。 したがって、Bakeでは、ユーザーがプロジェクト、特に定義ファイルを使用して誰でも簡単に呼び出すことができる再利用可能なビルドフローを定義できるようにしたいと考えています。 したがって、この定義ファイルは、HCLファイル、JSONファイル、またはComposeファイルである可能性があります。 複数のファイルを指定でき、もちろん、それらは特定の順序でメッシュ化され、何も指定しない場合はデフォルトでこれらになります。 つまり、Compose自体のように見え、bakeコマンドを実行する場所で利用可能なデフォルトのファイルを調べました。

    ご覧の通り、私はHCLの隣に小さなハートを掲げています、なぜなら、それがこの講演で私が話すことだからです。 HCLはこの種のことに対して非常に強力です。 したがって、HCL形式は、Terraformのようなブロック定義を介してサポートします。 それはほとんど同じことです。 変数をビルド引数として使用することも、Dockerfile が Bake ファイルの属性値で変数を補間することもできます。 一般的な純粋な事後関数のセットもあります。 go-cty で利用可能な任意の関数を使用できますが、独自のユーザー定義関数を使用することもできます。

    HCL 定義の例を見てみましょう。 これは見た目なので、基本的にカスタムビルドグループのサポートが追加されます。 ご覧のとおり、異なるターゲット グループを使用してプロジェクト全体でコードを再利用する方が適切です。 ターゲットは、たとえば、イメージ、イメージ all があり、グループで Docker ビルド コマンドに指定するのと同じオプションを持つ 1 つの Docker ビルド呼び出しを反映していることがわかります。

    ご覧のとおり、defaultと呼ばれるものは、targetオプションを使用してターゲットのリストを指定できます。 ターゲットは、inherit オプションをターゲットのリストに設定することで、ビルド オプションを継承することもできます。 たとえば、画像はすべて画像の1つから継承されるため、画像のオプションから継承できます。 これは、Terraformと非常によく似ており、変数を定義する方法を提供しています。 したがって、HCLファイル形式は、タグ付きのこのような変数ブロック定義もサポートしています。 また、現在の環境によって提供される値を持つ変数を定義するためにも使用できます。 したがって、環境変数を使用すると、この値は変数変数に置き換えられます。

    次に、たとえば、イメージをすべてターゲットにビルドしたいと思います。 私はただイメージを定義する必要があります すべてのDocker buildx bake image all、それだけです。 それはまさに私が望むものを構築します。 これは、内部ループを効率化し、他の環境に活用するのに非常に便利です。

    簡単なものを見てみましょう—前のものは巨大なものでした。 参照がどのように行われているかを大まかに確認できます。 それでは、基本的なワークフローに戻りましょう。 私たちは始まりであり、Bakeの定義とワークフローにいます。 ここでは、ベイクアクションだけを使用する必要があります。 そのため、Bake にもアクションがありますが、これはタグの下にはまったくありません。 プッシュを使用するだけで、デフォルトでは、そこに表示されている画像名が使用されます crazymax.com。 ハッシュタグ、この画像を設定します。

    これを見てきたので、アクションの内部に表示される正規表現もあるので、何が起こっているのか、何が考慮されているのか、例えば変数が渡されているのかがわかります。 そのため、ビルドプッシュ、ベイクアクションの内部で確認できるため、ダッシュダッシュプリントタグを使用して正規表現を示しています。

    今、ベイクについて秘密を交えてお話ししたいと思います。 CIに関する悪い慣行が多すぎて、機密情報や資格情報を渡すためにビルド引数を使用している人を見かけます。 だから、それは時々画像自体の中にあるかもしれないので、良くありません。 ただ、ビルド時ではありません。 したがって、このような場合は、ビルド時にシークレットを使用する必要があります。 シークレットはそれを使用する簡単な方法です。

    先に進む前に、プロジェクトをテストするためにDockerfileに新しいステージを追加したいと思いますが、Bake定義内に新しいターゲットを追加することもできます。 そして、その直前に、ビルドとプッシュの前に、このプロジェクトをテストしたいのです。 しかし、問題があります。 テストには GitHub API へのアクセスが必要であることを知っています。それ以外の場合はスキップされます。

    どういうわけか、このワークフローにGitHubトークンを渡す必要があります。 どうすればよいですか? これにはビルド時のシークレットを追加できます。 基本的に、これは私のベイクの定義では、公開したい秘密を定義するだけです。 だから私は環境変数name github tokenを使用してシークレット名ghトークンを公開したいと思います。 これは、secret フラグを使用する場合と同じで、build コマンドでも使用します。 何も変わらない。 また、Dockerfileを知っていると、テストを実行するときにこれらのシークレットを移動できます。 また、この環境変数githubトークンは、テスト内で使用されるものも使用できます。 最後に、ワークフローでは、提供された GitHub トークン シークレットを使用して GitHub トークンを設定します。 だからそれは通過し、それからそれは私のコンテナ内で直接使用することができます。 だから、これはかなり便利です。 これは、この実行命令でのみ使用されるため、安全です。 最終的な画像には含まれません。 この画像をプッシュしたいときは、このステージのみに使用されます。

    コンテナベースのワークフロー

    このことを念頭に置いて、CIでコンテナを使用することの大きな可能性を探り、内部ループとCI環境間の移植性を向上させる方法を探ることができます。 コンテナを使用すると移植性が向上するため、コンテナを使用するという考え方です。 そして、例えば、新しいターゲットを追加すると - これを lint と呼ぶことにします - そのため、私のバック ファイルは巨大になり始めますが、これは便利です。 将来必要に応じてこれを再利用し、Dockerfileにもlintステージを作成できます。 そして、それは私のコードが大丈夫かどうかを確認するためにgolang CI lintを実行するだけです。 そして、私の定義では、ワークフローで、新しいlintステップを追加します。

    今、ログを見ると、私のlintステージが通過していることがわかります。 大丈夫なのに、何か変なところがあります。 画像をエクスポートするというものがありますが、私はそれを望んでいません。 私はただ糸くずの部分を動かしたいだけです。 ただし、デフォルトでは、ビルドコマンドを実行すると、ビルドを行うたびにイメージがローカルストアに書き込まれます。 ただし、何もエクスポートせずにビルドできます。 これを行うには、このターゲットを回避して、キャッシュのみの出力タイプのみを使用できます。 この出力タイプは、基本的にビルド結果を出力するのを破棄しますが、この特定のターゲットのキャッシュを書き込みます。 テストのものについても同じことをする必要があります。 前にこれを忘れていたので、テストすると、何も出力されません。 将来、テスト定義をエクスポートして、コードをカットするなどに投稿したいとします。 ビルド結果をエクスポートすることもできます。 したがって、時間を無駄にしたくない場合や、ステージでテストを実行する以外に何も出力する必要がない場合に便利です。

    ご覧のとおり、共通のパターンがあります。 何かを追加するたびに、ワークフローに新しいステップが追加されます。 そのため、ワークフローは非常に大きくなる可能性があります。 この場合、毎回新しいステップが必要です。 たとえば、ここでは、テストと lint をグループ化できます。これは、この手順の目的がコードの検証であるためです。 今できることは、それらを削除するかメッシュ化して、検証するものだけを用意し、検証を呼び出すことができるので、それを削除することです。

    ローカル環境の周りではより抽象化されているため、ユーザーに通知したり、検証を実行したりすることができます。 それはあなたのためにすべてを行います。 指示したり、テストを実行したり、lintを実行したり、他の何かを実行する必要はないので、検証するだけで終わりです。 もちろん、BuildKitのおかげで並列に実行されるため、makeファイルのような制御はありません。 この場合、並列に実行されます。 正規の表現でもわかるように、両方を呼び出すことがわかります。

    このツールのこのセクションにジャンプしたくなかったのですが、それは面白いかもしれないと思います。 以前は、テストとリントの部分については、同じランナー上で動作すると言っていました。 これは便利ですが、たとえば、テストがリソースの制約になることがわかっている場合 (CPU や RAM を使いすぎる場合) で、これをランナー間で分割して GitHub ワークフローを利用したい場合などです。 これを解決するには、ターゲットのグループを分散させることができるので、検証ターゲットがありますが、これをランナー全体に分散したいので、同じジョブの一部ではなく、実際には2つのジョブに分散します。 これを解決するために、同じ大きなグループを維持しながら、グループターゲットと複数のランナーを分散させることができます。 したがって、ローカルフローは同じままで、ワークフロー内で少し変更するだけです。 そのため、行列を動的に設定するためのGitHubアクションのパターンがあります。

    それは少し不可解です。 今後はベイクアクションを使用してこれを使用したいので、これを行う必要はありません。 自動的に呼び出される複数のランナーを作成します。 それが何をするか、この場合、ご覧のとおり、validateグループでbakeコマンドを使用します。 このグループで使用可能なターゲットのリストを印刷し、この準備されたジョブの特定のターゲット出力内に配置し、その後、ターゲット値を含むマトリックスを使用できます。 これは、使用可能なターゲット(testとlint)のリストであるため、各ランナーで両方の検証が実行されます。 したがって、1つのランナーでテストし、別のランナーでリントします。

    これをより適切に表現するのは、test と lint の両方を並行して実行する単一の検証を追加する前ですが、現在はランナー間で分割されているため、時間がかかりません。 繰り返しになりますが、 57 秒ではなく、各ランを独自のランナーで実行するためです。 つまり、これはビルドを配布する方法です。 ドキュメントには、YouTube プラットフォーム イメージのビルドを配布する例があります。 たとえば、5つのプラットフォームを5つのランナーに分散させると、同じランナーでこれを行うとリソースを大量に消費する可能性があるため、これを分割することができます。 また、別のユースケースとして、多くの人がレジストリにイメージをプッシュするための何かを構築します。 これは多くの人がやっていることですが、私たちもバイナリを入れて、このバイナリをプッシュしてリリースなどを得たいと思っています。 したがって、実際にはBuildKitを使用してこれを行うことができます。 出力を変更して、レジストリではなく出力したいが、クライアントでローカルに出力したいと言うだけです。 この場合、バイナリと呼ばれるステージからバイナリがローカル ファイル /bin に出力されます。 そこで、専用のワークフローを作成します。 このワークフローは、タグがプッシュされたときにトリガーされ、その後、このバイナリが完了したときに、GitHubリリースを作成してこのバイナリをプッシュします。

    これは、たとえば、複数のプラットフォームがあるなど、他のユースケースで行うことができます。 この場合も同じことができるので、マルチプラットフォームビルドを実行し、すべてのバイナリを抽出して、GitHubリリース内に直接プッシュできます。 したがって、最終的なアクションを使用する必要があります。 このセクションでも、Dockerfile自体の中に入れることもできますが、GitHub APIと通信する必要があるため、より多くの作業が必要になります。 これは、すでに利用可能なGitHubアクションを使用するという簡単なものです。

    結論

    今日、私たちは何を見ましたか? キャッシュエクスポーターを使用し、タグを自動化するためのさまざまなアーキテクチャパイプライン戦略を使用して効率的なイメージを作成する方法を使用しました。 私たちは、ワークフローを簡素化し、CIのオーバーライドを減らすために、Bakeとビルドシークレットを使用して、Bakeについて多くのことを話しました。 また、ローカル環境とCI環境間の移植性を向上させるためだけにコンテナを使用する方法も説明します。

    最後に、コンテナを使ったプロジェクトがいくつかあります。 たとえば、buildx があります。 もちろん、この種のパターンを使用すると、クロスコンパイル、ベンダーの更新、ベンダーのチェックなど、多くのことを行うことができます。 糸くずもあります。 新しいパターンを使用し、メトリックを使用して、多くのことができます。 Bake 内にはいくつかのメトリクスもあります。 このプレゼンテーションでは取り上げませんでしたが、これについては素晴らしい資料があります。 したがって、今日シフトしたGitHubアクションは、Bake自体の内部で同じものをシフトできます。 これは便利なことであり、このプロジェクト内でこれを行っています。 というわけで、これだけです。 ありがとうございます。

    さらに詳しく

    この記事には、DockerCon 2023のプレゼンテーションの YouTube トランスクリプトが含まれています。 「Streamlining CI/CD with Modern Container Builds」は、Docker のシニア ソフトウェア エンジニアである Kevin Alvarez 氏によって発表されました。

    自分に合ったサブスクリプションを見つける

    今すぐ専門家に連絡して、Dockerサブスクリプションのコラボレーション、セキュリティ、サポートの完璧なバランスを見つけてください。