効果的な初期展開パイプラインを構築する方法

公開: 2022-03-11

私は物を作るのが好きです—開発者は何をしませんか? 私は興味深い問題の解決策を考え、実装を書き、美しいコードを作成するのが大好きです。 しかし、私が嫌いなのは操作です。 運用とは、サーバーのセットアップからコードの本番環境への出荷まで、優れたソフトウェアの構築に関係しないすべてのことです。

これは興味深いことです。フリーランスのRubyonRails開発者として、私は頻繁に新しいWebアプリケーションを作成し、DevOps側を理解するプロセスを繰り返さなければならないからです。 幸いなことに、数十のアプリケーションを作成した後、私はついに完璧な初期デプロイメントパイプラインに落ち着きました。 残念ながら、すべての人が私のように理解しているわけではありません。最終的に、この知識により、思い切って自分のプロセスを文書化することになりました。

この記事では、プロジェクトの開始時に使用するのに最適なパイプラインについて説明します。 私のパイプラインでは、すべてのプッシュがテストされ、マスターブランチが本番環境からの新しいデータベースダンプを使用してステージングにデプロイされ、バージョン管理されたタグが本番環境にデプロイされ、バックアップと移行が自動的に行われます。

これは私のパイプラインであるため、意見が分かれており、私のニーズに適していることに注意してください。 ただし、気に入らないものは自由に交換して、気になるものに置き換えることができます。 私のパイプラインでは、以下を使用します。

  • コードをホストするGitLab
    • 理由:私のクライアントはコードを秘密にしておくことを好みます。GitLabの無料利用枠は素晴らしいものです。 また、統合された無料のCIは素晴らしいです。 GitLabに感謝します!
    • 代替手段:GitHub、BitBucket、AWSCodeCommitなど。
  • GitLab CIを使用して、コードをビルド、テスト、デプロイします。
    • 理由:GitLabと統合され、無料です!
    • 代替案:TravisCI、Codeship、CircleCI、Fabric8を使用したDIYなど。
  • アプリをホストするHeroku
    • 理由:箱から出してすぐに機能し、最初から始めるのに最適なプラットフォームです。 これは将来変更できますが、すべての新しいアプリを専用のKubernetesクラスターで実行する必要はありません。 CoinbaseでさえHerokuで始まりました。
    • 代替手段:AWS、DigitalOcean、Vultr、Kubernetesを使用したDIYなど。

Old-school:基本的なアプリを作成してHerokuにデプロイする

まず、派手なCI / CDパイプラインを使用しておらず、アプリケーションをデプロイしたいだけの人のために、典型的なアプリケーションを再作成しましょう。

従来のコードホスティングおよびデプロイアクションの図

作成するアプリの種類は関係ありませんが、Yarnまたはnpmが必要になります。 私の例では、Ruby on Railsアプリケーションを作成しています。これは、移行とCLIが付属しており、すでに構成が記述されているためです。 好きなフレームワークや言語を使用できますが、後でバージョン管理を行うにはYarnが必要です。 いくつかのコマンドのみを使用し、認証を使用しない単純なCRUDアプリを作成しています。

そして、アプリが期待どおりに実行されているかどうかをテストしましょう。 念のため、先に進んでいくつかの投稿を作成しました。

開発中のアプリケーション

コードをプッシュして移行を実行することで、Herokuにデプロイしましょう

$ heroku create toptal-pipeline Creating ⬢ toptal-pipeline... done https://toptal-pipeline.herokuapp.com/ | https://git.heroku.com/toptal-pipeline.git $ git push heroku master Counting objects: 132, done. ... To https://git.heroku.com/toptal-pipeline.git * [new branch] master -> master $ heroku run rails db:migrate Running rails db:migrate on ⬢ toptal-pipeline... up, run.9653 (Free) ...

最後に、本番環境でテストしてみましょう

本番環境で実行されているアプリケーション

以上です! 通常、これはほとんどの開発者が操作を離れる場所です。 将来、変更を加える場合は、上記のデプロイと移行の手順を繰り返す必要があります。 夕食に遅れていない場合は、テストを実行することもできます。 これは出発点としては素晴らしいですが、この方法についてもう少し考えてみましょう。

長所

  • すばやくセットアップできます。
  • 展開は簡単です。

短所

  • 乾燥しない:すべての変更で同じ手順を繰り返す必要があります。
  • バージョン管理されていません:「昨日の展開を先週の展開にロールバックしています」は、今から3週間後はあまり具体的ではありません。
  • 悪いコードプルーフではありません:あなたはあなたがテストを実行することになっていることを知っていますが、誰も見ていないので、時々壊れたテストにもかかわらずそれをプッシュするかもしれません。
  • 悪いアクタープルーフではない:不満を持った開発者が、チームに十分なピザを注文していないことについてのメッセージを含むコードをプッシュしてアプリを壊すことを決定した場合はどうなりますか?
  • 拡張しない:すべての開発者にデプロイ機能を許可すると、アプリへの本番レベルのアクセスが許可され、最小特権の原則に違反します。
  • ステージング環境なし:実稼働環境に固有のエラーは、実稼働まで表示されません。

完璧な初期展開パイプライン

今日は別のことを試してみます。架空の会話をしましょう。 私は「あなた」に声をかけ、この電流の流れをどのように改善できるかについて話します。 さあ、何か言ってください。

何だって? 待ってください—話せますか?

はい、それは私があなたに声を与えることについて意味したことです。 元気ですか?

私は大丈夫です。 これは奇妙に感じます

私は理解していますが、それで転がります。 それでは、パイプラインについてお話ししましょう。 デプロイメントの実行で最も厄介な部分は何ですか?

ああ、それは簡単です。 私が無駄にする時間。 Herokuにプッシュしようとしたことがありますか?

ええ、依存関係のダウンロードとgit pushの一部として構築されているアプリケーションを見るのは恐ろしいです!

私は当然知っている? それは非常識です。 私はそれをする必要がなかったらいいのにと思います。 展開の*後に*移行を実行する必要があるという事実もあるため、ショーを見て、展開が実行されていることを確認する必要があります

git push heroku master && heroku run rails db:migrateのように、2つのコマンドを&&でチェーンするか、bashスクリプトを作成してコードに配置することで、後者の問題を実際に解決できますが、それでもすばらしい答えです。時間と繰り返しは本当に苦痛です。

ええ、それは本当にひどいです

CI / CDパイプラインでそのビットをすぐに修正できると言ったらどうしますか?

今何? それは何ですか?

CI / CDは、継続的インテグレーション(CI)および継続的デリバリー/デプロイメント(CD)の略です。 誰もが「開発と運用の融合」のような漠然とした用語を使っていたので、私が始めたときのことを正確に理解するのはかなり困難でしたが、簡単に言えば:

  • 継続的インテグレーション:すべてのコードが1か所にマージされていることを確認します。 チームにGitを使用してもらうと、CIを使用することになります。
  • 継続的デリバリー:コードを継続的に出荷する準備ができていることを確認します。 製品の読み取りから配布までのバージョンを迅速に作成することを意味します。
  • 継続的デプロイ:継続的デリバリーから製品をシームレスに取得し、サーバーにデプロイするだけです。

ああ、私は今それを手に入れました。 それは私のアプリを魔法のように世界に展開させることです!

CI / CDを説明する私のお気に入りの記事は、ここのAtlassianによるものです。 これはあなたが持っている質問をクリアするはずです。 とにかく、問題に戻ります。

ええ、それに戻ります。 手動展開を回避するにはどうすればよいですか?

masterへのプッシュでデプロイするCI/CDパイプラインの設定

CI / CDでそのビットをすぐに修正できると言ったらどうしますか? GitLabリモート( origin )にプッシュすると、コンピューターが生成され、コードをHerokuにプッシュするだけでまっすぐになります。

とんでもない!

うん、そうだ! もう一度コードに戻りましょう。

単純なデプロイCI/CDパイプラインの図

次の内容で.gitlab-ci.ymlを作成し、toptal- toptal-pipelineをHerokuアプリの名前に置き換えます。

 image: ruby:2.4 before_script: - > : "${HEROKU_EMAIL:?Please set HEROKU_EMAIL in your CI/CD config vars}" - > : "${HEROKU_AUTH_TOKEN:?Please set HEROKU_AUTH_TOKEN in your CI/CD config vars}" - curl https://cli-assets.heroku.com/install-standalone.sh | sh - | cat >~/.netrc <<EOF machine api.heroku.com login $HEROKU_EMAIL password $HEROKU_AUTH_TOKEN machine git.heroku.com login $HEROKU_EMAIL password $HEROKU_AUTH_TOKEN EOF - chmod 600 ~/.netrc - git config --global user.email "[email protected]" - git config --global user.name "CI/CD" variables: APPNAME_PRODUCTION: toptal-pipeline deploy_to_production: stage: deploy environment: name: production url: https://$APPNAME_PRODUCTION.herokuapp.com/ script: - git remote add heroku https://git.heroku.com/$APPNAME_PRODUCTION.git - git push heroku master - heroku pg:backups:capture --app $APPNAME_PRODUCTION - heroku run rails db:migrate --app $APPNAME_PRODUCTION only: - master

これを押し上げて、プロジェクトのパイプラインページで失敗するのを確認します。 これは、Herokuアカウントの認証キーが欠落しているためです。 ただし、これを修正するのはかなり簡単です。 まず、HerokuAPIキーが必要です。 [アカウントの管理]ページから取得し、GitLabリポジトリのCI/CD設定に次のシークレット変数を追加します。

  • HEROKU_EMAIL :Herokuへのサインインに使用するメールアドレス
  • HEROKU_AUTH_KEY :Herokuから取得したキー

GitLab CI/CD設定ページのシークレット変数の画像

これにより、すべてのプッシュでGitLabからHerokuへのデプロイが機能するようになります。 何が起こっているかについて:

  • マスターにプッシュすると
    • Heroku CLIは、コンテナーにインストールおよび認証されます。
    • コードはHerokuにプッシュされます。
    • データベースのバックアップがHerokuにキャプチャされます。
    • 移行が実行されます。

すでに、すべてをgit pushに自動化することで時間を節約できるだけでなく、デプロイごとにデータベースのバックアップを作成していることがわかります。 何か問題が発生した場合は、元に戻すためのデータベースのコピーがあります。

ステージング環境の作成

しかし、ちょっと待ってください。本番環境固有の問題はどうなりますか? 開発環境が本番環境とあまりにも異なるために奇妙なバグに遭遇した場合はどうなりますか? 移行を実行したときに、SQLite3とPostgreSQLの奇妙な問題に遭遇したことがあります。 詳細は私にはわかりませんが、それはかなり可能です。

私は開発にPostgreSQLを厳密に使用しており、そのようなデータベースエンジンの不一致は決してなく、潜在的な非互換性についてスタックを注意深く監視しています。

まあ、それは退屈な仕事です、そして私はあなたの規律を称賛します。 個人的に、私はそれをするのが面倒です。 ただし、将来のすべての潜在的な開発者、共同作業者、または貢献者に対して、そのレベルの勤勉さを保証できますか?

エラー—ええ、違います。 参りました。 他の人はそれを台無しにするでしょう。 しかし、あなたのポイントは何ですか?

私のポイントは、ステージング環境が必要だということです。 プロダクションのようなものですが、そうではありません。 ステージング環境では、本番環境へのデプロイをリハーサルし、すべてのエラーを早期にキャッチします。 私のステージング環境は通常、本番環境を反映しており、厄介なコーナーケースが移行を台無しにしないように、本番データベースのコピーをステージングデプロイにダンプします。 ステージング環境を使用すると、ユーザーをモルモットのように扱うのをやめることができます。

意味あり! では、どうすればこれを行うことができますか?

ここが面白いところです。 masterをステージングに直接デプロイするのが好きです。

待ってください、それは私たちが今本番環境を展開しているところではありませんか?

はい、そうですが、代わりにステージングにデプロイします。

しかし、 masterをステージングにデプロイする場合、どのように本番環境にデプロイしますか?

何年も前にやるべきことを使用することで、コードをバージョン管理し、Gitタグをプッシュします。

Gitタグ? 誰がGitタグを使用しますか?! これは大変な作業のように聞こえ始めています。

確かにそうだったが、ありがたいことに、私はすでにすべての作業を行っており、コードをダンプするだけで機能します。

ステージングと本番環境のデプロイがどのように機能するかの概要

まず、ステージングデプロイに関するブロックを.gitlab-ci.ymlファイルに追加します。toptal toptal-pipeline-stagingという新しいHerokuアプリを作成しました。

 … variables: APPNAME_PRODUCTION: toptal-pipeline APPNAME_STAGING: toptal-pipeline-staging deploy_to_staging: stage: deploy environment: name: staging url: https://$APPNAME_STAGING.herokuapp.com/ script: - git remote add heroku https://git.heroku.com/$APPNAME_STAGING.git - git push heroku master - heroku pg:backups:capture --app $APPNAME_PRODUCTION - heroku pg:backups:restore `heroku pg:backups:url --app $APPNAME_PRODUCTION` --app $APPNAME_STAGING --confirm $APPNAME_STAGING - heroku run rails db:migrate --app $APPNAME_STAGING only: - master - tags ...

次に、プロダクションブロックの最後の行を変更して、マスターブランチではなくセマンティックにバージョン管理されたGitタグで実行します。

 deploy_to_production: ... only: - /^v(?'MAJOR'(?:0|(?:[1-9]\d*)))\.(?'MINOR'(?:0|(?:[1-9]\d*)))\.(?'PATCH'(?:0|(?:[1-9]\d*)))(?:-(?'prerelease'[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?(?:\+(?'build'[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$/ # semver pattern above is adapted from https://github.com/semver/semver.org/issues/59#issuecomment-57884619

GitLabは「保護された」ブランチのみに秘密変数へのアクセスを許可するほど賢いため、これを今すぐ実行すると失敗します。 バージョンタグを追加するには、GitLabプロジェクトのリポジトリ設定ページに移動し、保護されたタグにv*を追加します。

リポジトリ設定ページで保護されたタグに追加されているバージョンタグの画像

今起こっていることを要約しましょう:

  • マスターにプッシュするか、タグ付きコミットをプッシュすると
    • Heroku CLIは、コンテナーにインストールおよび認証されます。
    • コードはHerokuにプッシュされます。
    • データベース生成のバックアップがHerokuにキャプチャされます。
    • バックアップはステージング環境にダンプされます。
    • 移行はステージングデータベースで実行されます。
  • セマンティックにバージョンタグが付けられたcommitをプッシュすると
    • Heroku CLIは、コンテナーにインストールおよび認証されます。
    • コードはHerokuにプッシュされます。
    • データベース生成のバックアップがHerokuにキャプチャされます。
    • 移行は本番データベースで実行されます。

あなたは今、力強く感じますか? 力強く感じます。 初めてここに来たとき、妻に電話して、このパイプライン全体を非常に詳細に説明したことを覚えています。 そして、彼女は技術的でもありません。 私は自分自身にとても感銘を受けました、そしてあなたもそうあるべきです! ここまで素晴らしい仕事が来ました!

すべてのプッシュをテストする

しかし、それだけではありません。コンピューターはとにかくあなたのために何かをしているので、あなたが怠惰すぎて実行できないすべてのことを実行することもできます:テスト、リンティングエラー、あなたがやりたいことのほとんどすべて、そしてこれらのいずれかが失敗した場合、彼らは勝ちました展開に移らないでください。

これをパイプラインに含めるのが大好きです。コードレビューが楽しくなります。 マージリクエストがすべてのコードチェックを通過した場合は、レビューする価値があります。

すべてのプッシュでのテストの画像

testブロックを追加します。

 test: stage: test variables: POSTGRES_USER: test POSTGRES_PASSSWORD: test-password POSTGRES_DB: test DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSSWORD}@postgres/${POSTGRES_DB} RAILS_ENV: test services: - postgres:alpine before_script: - curl -sL https://deb.nodesource.com/setup_8.x | bash - apt-get update -qq && apt-get install -yqq nodejs libpq-dev - curl -o- -L https://yarnpkg.com/install.sh | bash - source ~/.bashrc - yarn - gem install bundler --no-ri --no-rdoc - bundle install -j $(nproc) --path vendor - bundle exec rake db:setup RAILS_ENV=test script: - bundle exec rake spec - bundle exec rubocop

今起こっていることを要約しましょう:

  • プッシュまたはマージリクエストごとに
    • RubyとNodeはコンテナにセットアップされます。
    • 依存関係がインストールされます。
    • アプリがテストされます。
  • マスターにプッシュするとき、またはタグ付きコミットをプッシュするとき、すべてのテストに合格した場合のみ
    • Heroku CLIは、コンテナーにインストールおよび認証されます。
    • コードはHerokuにプッシュされます。
    • データベース生成のバックアップがHerokuにキャプチャされます。
    • バックアップはステージング環境にダンプされます。
    • 移行はステージングデータベースで実行されます。
  • セマンティックにバージョンタグが付けられたコミットをプッシュすると、すべてのテストに合格した場合のみ
    • Heroku CLIは、コンテナーにインストールおよび認証されます。
    • コードはHerokuにプッシュされます。
    • データベース生成のバックアップがHerokuにキャプチャされます。
    • 移行は本番データベースで実行されます。

一歩下がって、達成した自動化のレベルに驚嘆してください。 これからは、コードを書いてプッシュするだけです。 必要に応じて、ステージングでアプリを手動でテストし、アプリを世界に発信するのに十分な自信がある場合は、セマンティックバージョニングでタグ付けしてください。

自動セマンティックバージョニング

ええ、それは完璧ですが、何かが欠けています。 アプリの最後のバージョンを検索して明示的にタグ付けするのは好きではありません。 それには複数のコマンドが必要で、数秒間気が散ります。

さて、おい、やめて! もういい。 あなたは今それを過剰に設計しているだけです。 それはうまくいきます、それは素晴らしいです、上を越えて良いことを台無しにしないでください。

さて、これからやろうとしていることをやるのには十分な理由があります。

祈って、私を啓発してください。

私はあなたのようでした。 私はこの設定に満足していましたが、それから私は台無しになりました。 git tagはタグをアルファベット順にリストしますv0.0.11v0.0.2より上です。 私はかつて誤ってリリースにタグを付け、間違いを見つけるまで約5ダースのリリースを続けました。 その時、私もこれを自動化することにしました。

ああ、またか

ありがたいことに、npmの機能を自由に使用できるので、適切なパッケージを見つけましたyarn add --dev standard-versionを実行し、 package.jsonファイルに次を追加します。

 "scripts": { "release": "standard-version", "major": "yarn release --release-as major", "minor": "yarn release --release-as minor", "patch": "yarn release --release-as patch" },

最後にもう1つ、デフォルトでタグをプッシュするようにGitを構成する必要があります。 現時点では、タグをプッシュアップするためにgit push --tagsを実行する必要がありますが、通常のgit pushで自動的に実行するのは、 git config --global push.followTags true

新しいパイプラインを使用するには、リリース実行を作成するときはいつでも:

  • パッチリリース用のyarn patch
  • マイナーリリース用のマイナーyarn minor
  • メジャーリリース用のメジャーyarn major

「メジャー」、「マイナー」、「パッチ」という言葉の意味がわからない場合は、セマンティックバージョニングサイトで詳細をお読みください。

パイプラインがようやく完成したので、それを使用する方法を要約しましょう!

  • コードを書く。
  • コミットしてプッシュし、テストしてステージングにデプロイします。
  • yarn patchを使用して、パッチリリースにタグを付けます。
  • git pushします。

まとめとさらなるステップ

CI/CDパイプラインで可能なことのほんの一部に過ぎません。 これはかなり単純な例です。 HerokuをKubernetesと交換することで、さらに多くのことができます。 GitLab CIを使用する場合は、デプロイ間でファイルをキャッシュしたり、アーティファクトを保存したりすることでできることがたくさんあるので、yamlのドキュメントを読んでください。

このパイプラインに加えることができるもう1つの大きな変更は、セマンティックバージョニングとリリースを実行するための外部トリガーを導入することです。 現在、ChatOpsは有料プランの一部であり、無料プランにリリースされることを願っています。 しかし、1つのSlackコマンドで次の画像をトリガーできると想像してみてください。

本番デプロイが外部でトリガーされるCI/CDデプロイメントパイプラインの図(チャットまたはWebhookを介して)

最終的に、アプリケーションが複雑になり始め、システムレベルの依存関係が必要になると、コンテナーを使用する必要が生じる場合があります。 その場合は、ガイド「Docker入門:Devopsの簡素化」を確認してください。

このサンプルアプリは実際に公開されており、そのソースコードはここにあります。