はじめに
こんにちは、Developer Productivity 室 PipeCD (CNCF Sandbox) プロダクト オーナー のカイン (@khanhtc1202) です。
PipeCD は去年に CNCF Sandbox となり、コミュニティの注目を集め始めたばかりですが、開始以来 4 年以上という長い開発の歴史があります。
この記事では、私たちサイバーエージェントの社内SaaSのPipeCD 開発・運用チームが認識したCICDの根本的な課題と、それを現行バージョンのPipeCDでどのように解決したか、さらに今後の方向性について共有したいと思います。
CICD と GitOps
さまざまな定義や理解がありますが、GitOps と CICD などの関連概念に関しては、次のような共通点がいくつかあります。
- CI/CDの分離が必要
- CI はアプリケーションのソース コード スコープに属し、CI の出力は CD の入力となります。 CD はマニフェスト ソース コード スコープに属し、CD の入力は CI の出力です
- CD ツールは、GitOps プリンシパルに基づいてタスクを実行します
CIとCD に接続ためで、生まれてきたEventWatcher 機能
ユーザーからのフィードバックがあまりなかった最初のバージョン以来、開発チームはテストを推進し、PipeCD の機能上の欠点を検出する方法としてドッグフーディング手法を使用してきました。
この初期から、CI と CD を効果的にリンクするという問題が浮上しています。
source: https://www.weave.works/blog/what-is-gitops-really
CI の使用内容にあまり注意を払わずに CI と CD の間の”immutability firewall” をバイパスする一般的な方法は、CI と CD の間の共通要素であるアーティファクト (container image) に依存することです。
これは、今日人気のある多くの CD、ArgoCD や FluxCD のアプローチです。
このアプローチには、汎用インターフェイス (コンテナー イメージ自体にのみ依存するため) などの利点に加えて、次のような制限もあります。
- 多くの異なるレジストリ コンテナ (Docker Hub、ECR、GCR、ACR、Harbor など) をサポートする必要がある
- アーティファクトであるコンテナー イメージのみをサポートでき、Helm Chart、Terraform module、Lambda source zip などの他のアーティファクトはサポートできません
- 監視するコンテナー イメージの数が増えると、パフォーマンス関連の問題が発生します (実際に CA で使用される場合にもこの問題が発生します)
PipeCD の最も重要な機能はさまざまなプラットフォームをサポートすることであるため、アーティファクトとしてコンテナー イメージのみをサポートできることは PipeCD には適していません。そこで、この問題を解決するための新しいソリューションである EventWatcher を開発することにしました。
PipeCD では、CI と CD の間のインターフェイスとしてコンテナー イメージを選択するのではなく、CI から PipeCD に送信されたイベントを CD パイプラインのトリガーとして直接使用します。
イベントは、PipeCD のコマンドとみなされます (UI からデプロイメントをトリガーするのと同様)。これには、次のような情報が含まれます。
- イベント定義名とイベントラベル。どのパイプされたイベントがどのアプリケーションで処理されるかを決定するために使用されます
- イベントの処理時にパイプ処理に使用されるデータに関する情報
- マニフェスト ファイル内のフィールドへのパスを指定する情報
EventWatcher 機能を使用するには、PipeCD のアプリケーション構成に以下を追加する必要があります。
apiVersion: pipecd.dev/v1beta1
kind: KubernetesApp
spec:
name: helloworld
eventWatcher:
- matcher:
name: event-test
handler:
type: GIT_UPDATE
config:
replacements:
- file: deployment.yaml
yamlField: $.spec.template.spec.containers[0].image
それで、CI のステップとして、pipectl を使用してイベントを送信できます。
$ pipectl event register --name=event-test --data=ghcr.io/pipecd/helloworld:v0.49.0
マニフェスト リポジトリ上のコミットは、次の変更をpiped処理することで作成されます。
spec:
template:
spec:
containers:
- name: helloworld
- image: ghcr.io/pipecd/helloworld:v0.48.0
+ image: ghcr.io/pipecd/helloworld:v0.49.0
EventWatcher は、監視するコンテナー イメージの数が増加したときのパフォーマンスの問題を回避するのに役立ちます。また、さまざまな種類のアーティファクト (Helm Chart、Terraform module、Lambda source zip など) をサポートしながら、さまざまな CI との互換性を簡単に確保できます。
EventWatcherの問題
EventWatcher は上記の問題を解決しますが、EventWatcher の設計が直接原因ではありませんが、特定の制限もあります。
セキュリティ要件があるため、プロジェクトは通常、ソース コードとマニフェストを同じリポジトリに保存せず、2 つのリポジトリに分離します。
一般的な要望は、PipeCD 上のパイプライン デプロイメントを確認するときに、このデプロイメントでどのソース コード (コードリポ) の変更がデプロイされているかをユーザーがどのようにして知ることができるかというものです。
CI と CD の間のインターフェイスとしてコンテナー イメージを使用する場合、コンテナーの LABEL 機能を利用して、ソース コードのコミット ハッシュ、ブランチなどに関する情報を保存できます。OCI 仕様には、追加のキーを定義せずに使用できる事前定義された LABEL もいくつかあります。
LABEL org.opencontainers.image.source="https://github.com/pipe-cd/pipecd"
LABEL org.opencontainers.image.revision="0123456789abcdefg"
ただし、この方法は前述の方法と似ていますが、コンテナ イメージであるアーティファクトにのみ適しています。同時に、コンテナー イメージをスキャンしてコミット ハッシュに関する情報を取得し、それを UI に表示すると、追加の処理オーバーヘッドが発生します。
この時点で、EventWatcher の設計における長所、つまり CI から CD にイベントを送信することに気づきました。
この問題の本質は、デプロイメントを表示するときに、マニフェスト リポジトリにのみ関連するコード リポジトリ内の情報を知る必要があることです。 CI と CD の間でイベントを送信するアプローチでは、コミット ハッシュや PR URL などの必要な情報をイベントの一部として追加し、マニフェスト リポジトリへのコミット変更を作成するためにパイプ処理されるときに、この情報はコミット トリガーのデプロイメントをリンクするために使用されます。そしてコード変更をコミットします。
マニフェスト リポジトリのコミット変更で使用されるイベント データとは別にするために、pipetl はこの情報をコンテキスト フィールドとして送信します。
$ pipectl event register --name=event-test --data=ghcr.io/pipecd/helloworld:v0.49.0 \
--context=Source-Commit-Hash= 6512b1f3fcba7342c11,Source-URL=https://github.com/pipe-cd/pipecd/commit/6512b1f3fcba7342c11,PipeCD-Official-Docs=https://pipecd.dev/
この情報は、マニフェスト リポジトリへのコミット変更を作成するときにコミット トレーラーとして使用されます。
GitHub を使用している場合、コミット トレーラーは次のように表示されます。
このアプローチにより、PipeCD UI からデプロイメントを確認すると、ユーザーはこの変更の原因となっているコード変更を正確に確認できます。
これで、デプロイメント -> マニフェスト リポジトリ -> コード リポジトリからのリンク作成できていました。
この問題については、CADP ブログにさらに詳しい記事があります。
良いが十分ではない
EventWatcher アップデートのリリース後、CI と CD 間の接続の問題の解決についてコミュニティから肯定的なフィードバックを受け取りました。
ただし、同じリポジトリ内に多くのマイクロサービスがあり、非常に複雑なプロジェクトで PipeCD を使用する場合、いくつかの制限に気づきます。
デプロイメントをトリガーするために PipeCD に送信される各イベントは、実際には複数のデプロイメントをトリガーするために共有できます。コンテナー イメージは多くの異なるサービスで共有されるため、これは明らかです。たとえば、protoに関連する変更、またはログ ライブラリやメトリック ライブラリなどの共通の共有ライブラリを使用すると、一度に複数のデプロイメントが可能になります。
これは、開発チームに同じリポジトリで作業する多くの人が含まれており、多くのサービスに影響を与える変更の数が十分に大きい場合に問題になります。PipeCD の現在の UI デプロイメント リストでは、どのデプロイメントがどのコミットによってトリガーされたかを正確に判断することが困難になります。
素朴な考えで、PipeCD のデプロイメント モデルに「root_cause_commit_hash」フィールドを追加するだけで、この情報を使用して、PipeCD UI で同じリポジトリ コードの変更によってトリガーされたデプロイメントをフィルタリングできます。
ただし、ユーザーが root_cause_commit_hash だけではなく、より多くの情報を知る必要がある場合、この小さな変更では目標を確実に達成できるわけではありません。現時点でのユーザーの希望は、コードリポジトリからリンク -> マニフェストリポジトリ/デプロイメントのリンク欲しいと考えられます。
そこで私たちは、PipeCD と EventWatcher の設計を振り返り、考え方をさらに一歩進めました。
コード リポジトリのコミット ハッシュをルート キーとして使用して、デプロイメント トレース (Deployment Trace) の概念を PipeCD に追加することにしました。各コード リポジトリ commit_hash について、デプロイメント トレースには次の情報が含まれます。
- commit_hash: コードリポジトリのコミットハッシュがキーとして使用されます
- コード リポジトリ情報: 認証、URL、ブランチ、コミット メッセージ、作成者などが含まれます
- デプロイメント: この commit_hash によってトリガーされたデプロイメントのリスト。各デプロイメントには、PipeCD デプロイメントの完全な情報 (マニフェスト リポジトリのコミット、パス、パイプラインなど) が含まれます
これにより、ユーザーは段階的に情報を検索することなく、コード リポジトリ -> マニフェスト リポジトリ -> デプロイメントの情報を簡単にたどることができます。コード リポジトリに変更を加えるアプリケーション エンジニアの観点からは、どのデプロイメントがコミットによってトリガーされたか、そのデプロイメントがどのようなものであるか、そのデプロイメントがサービスにどのような影響を与えるかを簡単に確認できます。
デプロイメント トレースの設計と実装に関するこのGitHub issueでまとめています。今後もプロジェクトの進捗状況をコミュニティに更新していきます。
まとめ
この記事では、非常に複雑なプロジェクトで PipeCD を使用する際に発生した問題と、EventWatcher の設計と実装を通じてそれらをどのように解決したか、およびデプロイメント トレースを使用した次のステップについて共有しました。
デプロイメント トレース機能は計画されており、できるだけ早く実装される予定です。 2025 年の第 2 四半期にリリースされる予定です。
PipeCD プロジェクトはオープンソース プロジェクトであり、誰もがプロジェクトの開発に参加でき、コミュニティからのあらゆる貢献を歓迎します。