この記事は実際にはグループ内での二回目の共有内容です。今回は CI/CD に関するいくつかの問題や進化の過程で直面する必要がある問題について話したいと思います。もちろん、この記事は初心者向けの内容と少しの暴論を含んでいますので、気軽に見てください。
開宗明義、定義先行#
私たちが何かを話す前に、その事物の定義を示す必要があります。それでは、今日話す CI と CD の定義を見てみましょう。
まず、CI は Continuous Integration を指し、中文では継続的インテグレーションと表現されます。一方、CD は一般的な文脈では二つの意味を持つことがあります:Continuous Delivery または Continuous Deployment で、対応する表現は継続的デリバリー / 継続的デプロイメントです。ここでは Brent Laster がWhat is CI/CD?1で示した定義を借用します。
継続的インテグレーション(CI)は、製品のソースコードが変更される際に、自動的に検出、プル、ビルド、(ほとんどの場合)ユニットテストを行うプロセスです。CI はパイプラインを開始する活動です(ただし、特定の事前検証 — しばしば「プレフライトチェック」と呼ばれる — が CI の前に組み込まれることがあります)。
CI の目標は、開発者からの新しい変更が「良い」ものであり、コードベースでのさらなる使用に適していることを迅速に確認することです。
継続的デプロイメント(CD)は、CD パイプラインから出たコードのリリースを自動的に取得し、エンドユーザーに提供できるという考え方を指します。ユーザーによるコードの「インストール」方法によっては、クラウドに何かを自動的にデプロイしたり、アプリの更新を提供したり(電話のアプリなど)、ウェブサイトを更新したり、単に利用可能なリリースのリストを更新したりすることを意味する場合があります。
定義だけを見ると、皆さんはまだ混乱するかもしれませんので、以下にいくつかの実際の例を使って CI/CD のことを一から整理してみましょう。
Re:0 からの構築プロセス#
このタイトルは少し草っぽいですが、気にしないでください。まず、最もシンプルな要求を仮定しましょう。
私たちは Hexo を基にした個人のブログシステムを構築しました。その中には私たちが公開する必要のある記事や、設定したテーマが含まれています。これを特定のリポジトリに公開する必要があります。
さて、この要求に基づいて、0 から 0 までのプロセスを一周してみましょう(笑)。
原生の構築の始まり#
ここで多くの人が「なぜ Hexo を切り口として選んだのか?」と尋ねるかもしれません。理由は簡単です!それが十分にシンプルだからです!
本題に戻りますが、まず Hexo には二つのコマンドhexo g
とhexo d
があります。これらはそれぞれ、現在のディレクトリ内の Markdown ファイルに基づいて静的なウェブページを生成し、生成された成果物を設定に従って対応するリポジトリにプッシュします。
では、最も原始的な段階での構築プロセスは次のようになります。
- エディタを使って、楽しく記事を書く
- その後、ローカルターミナルで
hexo g && hexo d
を実行する
問題が発生しました。ブログを投稿したが、生成コマンドを実行するのを忘れた場合はどうしますか?または、毎回同じコマンドを繰り返し入力するのが面倒な場合はどうしますか?それでは、全体のプロセスを自動化しましょう。Let's rock!
さらに進んだ構築#
さて、私たちが自動化を完了したと仮定しましょう。今、ブログを公開するワークフローはどのようになるべきでしょうか?
- Markdown ファイルを作成し、GitHub リポジトリの Master ブランチにプッシュする
- 自動タスクがブログを構築し、一連の静的ファイルとスタイルを生成する
- 静的ファイルとスタイルを私たちのサイトのリポジトリ / CDN などのターゲット位置にプッシュする
ここで二つの核心的な問題があります。
- コードをプッシュしたときに自動的に構築を開始する
- 構築が完了した後、成果物をプッシュする
それでは、GitHub Action を基に私たちの自動化構築タスクを設定しましょう。
name: Build And Publish Blog
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install Package
run: npm install -g hexo-cli && npm install
- name: Generate Html File
run: hexo g
- name: Deploy 🚀
uses: JamesIves/[email protected]
with:
GITHUB_TOKEN: ${{ secrets.PUBLSH_TOKEN }}
BRANCH: gh-pages # アクションがデプロイすべきブランチ。
FOLDER: public # アクションがデプロイすべきフォルダ。
この設定は実際に次のようなことを実現しています。
- Master ブランチにコードをプッシュしたときに構築をトリガーする
- コードをプルする
- 構築に必要な依存関係をインストールする
- 静的ファイルを生成する
- 静的ファイルをプッシュする
上記のいずれかのステップが失敗した場合、後続のステップの実行はキャンセルされます。実際、こうしたシンプルなタスクには CI と CD の基本要素が含まれています(ここで CD は継続的デリバリー / 継続的デプロイメントを厳密に区別していません)。
- 既存のコードとの継続的な構築と統合
- 統合中に複数のフェーズを区別する。各フェーズは前のフェーズの結果に依存します。
- 構築成果物をデリバリー / デプロイする。デリバリー / デプロイの成功は統合の成功に依存します。
ここで、ブログシステムを私たちのプロジェクトの例に置き換えましょう。Hexo を私たちの Python サービスに、追加のブログ記事を新しいコードに、構築コマンドを mypy/pylint などのチェックツールに置き換えます。見てください、CI/CD は実際には想像している複雑なシステムとは大きく異なるのです。
ここで多くの人がこうした質問をするかもしれません。もしここでこれらのコマンドをオンライン形式でトリガーせず、ローカルで Git Hook などの形式で実現した場合、これは CI と CD の一種と見なされるのでしょうか?私の視点から見ると、間違いなくそうです。CI/CD の核心要素は、繰り返し可能で自動化されたタスクを通じて、欠陥を早期に露呈させ、人為的要因による不必要な事故の発生を軽減することにあります。
この開発者は過剰に愚かで慎重ではない#
まず、最も基本的な暴論を投げかけ、次に話を進めましょう。
誰もが愚かな時期があり、その愚かな時期は多くあるかもしれません。
このような暴論の下で、上記のHexo を基にした個人ブログシステムの構築の例を振り返ってみましょう。もし私たちが収束した自動化システムを通じて構築と公開の要求を解決しない場合、どの段階でリスクが発生するでしょうか?
- 最も基本的なこととして、ブログを書き終えた後、構築を忘れ、公開を忘れる
- 例えば、依存関係の Hexo バージョンやテーマバージョンをアップグレードした場合、テストを行わずに構築した結果、スタイルが無効になる
- Markdown に問題があり、構築が失敗する
- 例えば、複数の人がブログを管理している場合、各自がターゲットリポジトリ / CDN の秘密鍵などの情報を保存する必要があり、情報漏洩などが発生する
Hexo を基にした個人ブログシステムの例を日常の開発シーンに切り替えると、私たちが直面する問題はさらに多くなります。いくつかの例を挙げてみましょう。
- 迅速にロールバックできない
- 特定の構築 / 公開記録を追跡できない
- 自動化されたタスクがないため、開発者がテストや lint を実行するのを怠り、コードが劣化する
- ピーク時の公開による事故
うん、これらの問題は皆さんにとって非常に馴染み深いものではないでしょうか?大体、私は起きて、構築して、事故が起きて、何を言えばいいのか 23333。
ここまで来て、皆さんは一つの問題に気づきましたか?この記事では CI と CD を区別していません。私の視点から見ると、CI/CD は本質的に同じ事を実践しています。すなわち、開発プロセスとデリバリープロセスの収束です。
私の視点から見ると、CI/CD システムを構築する核心的な目標は次の通りです。
- 収束した入口と自動化されたタスクをトリガーとして、人為的要因によるシステムの不安定性をできるだけ軽減する
- 迅速で、複数回、繰り返し可能で、無感知のタスクを通じて、できるだけ早い段階でシステム内の問題を露呈させる
この二つの大目標の前提の下で、私たちは異なるビジネスシーンに応じて、CI/CD の内容を豊かにするために異なる手段や形式を採用します。これには以下が含まれますが、これに限りません。
- CI フェーズでの自動化されたユニットテスト、E2E テストなど
- CI フェーズでの定期的な Nighty Build など
- CD フェーズでのリリース管理など
ただし、私たちがどのように CI/CD システムを構築しようとも、またはどのような粒度で CI/CD を選択しようとも、私は合格の CI/CD システムとメカニズムは次のような原則に従う必要があると考えています(個人的なまとめ)。
- 入口の収束、SOP の確立。この点で合意が達成されない場合、開発者が技術手段を通じて CI/CD システムを回避できるなら、私たちは再びこの章のタイトルに戻ることになります(この開発者は過剰に愚かで慎重ではない)。
- ビジネスコードに対する非侵入性
- 統合タスク / リリースタスクは必ず自動化され、繰り返し可能であること
- 追跡可能な履歴記録と結果
- 追跡可能な構築統合成果物
- 上から下までのサポート
私がまとめたこれらの原則に従って、以前のブログの公開プロセスを改良してみましょう。
name: Build And Publish Blog
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Install Package
run: npm install -g hexo-cli && npm install
- name: Generate Html File
run: hexo g
- name: Deploy To Repo🚀
if: ${{ github.ref == 'refs/heads/master'}}
uses: JamesIves/[email protected]
with:
GITHUB_TOKEN: ${{ secrets.PUBLSH_TOKEN }}
BRANCH: gh-pages # アクションがデプロイすべきブランチ。
FOLDER: public # アクションがデプロイすべきフォルダ。
- name: Upload to Collect Repo
uses: JamesIves/[email protected]
with:
GITHUB_TOKEN: ${{ secrets.PUBLSH_TOKEN }}
BRANCH: build-${{ github.run_id }} # アクションがデプロイすべきブランチ。
FOLDER: public # アクションがデプロイすべきフォルダ。
この変更後の構築プロセスでは、PR を粒度として CI プロセスをトリガーし、履歴成果物を保存することを選択しました。そして、主ブランチにマージした後に新しい公開プロセスを追加しました。こうすることで、ブログの構築と公開の際に、履歴成果物を通じてフレームワークのアップグレードや新しいブログ記事などの操作の正確性を検証できるようになります。また、GitHub Action を利用することで、履歴構築の追跡も容易に行えます。
これにより、私の愚かな操作によって引き起こされるさまざまな副作用をできるだけ回避できるようになります(逃)。
攻撃的な構築:終章#
さて、何もない、驚いていますね。
。
。
。
。
ただの冗談です。実際、この記事はここでほぼ終了です。この記事を通じて、CI/CD システムを構築することは、実際には多くの高度な技術的問題を含むわけではないことがわかるでしょう(極少数のシーンを除いて)。従来の Jenkins でも、新しい GitHub Action、GitLab-CI、あるいはクラウドプロバイダーが提供するサービスでも、ビジネスに適した CI/CD システムを構築するのに役立ちます。しかし、私は以前 Twitter で「CI/CD の構築はしばしば技術的な問題ではなく、制度的な問題であり、むしろ考え方の問題である」と述べました。
ですので、私たち一人一人が自分たちが間違いを犯すという事実を認識し、できるだけ自分が担当するシステムの開発プロセスとデリバリープロセスを収束させ、自動化することができるようにしたいと思います。CI/CD が私たちの日常業務の一部として本当に機能するように。
これで、そろそろ失礼します。