Laravelのダウンタイムゼロの導入
公開: 2022-03-11ライブアプリケーションの更新に関しては、これを行うには2つの根本的に異なる方法があります。
最初のアプローチでは、システムの状態を段階的に変更します。 たとえば、ファイルの更新、環境プロパティの変更、追加の必需品のインストールなどを行います。 2番目のアプローチでは、マシン全体を破棄し、新しいイメージと宣言型構成を使用してシステムを再構築します(たとえば、Kubernetesを使用)。
Laravelのデプロイが簡単に
この記事では主に、クラウドでホストされていない可能性のある比較的小さなアプリケーションについて説明しますが、Kubernetesが「クラウドなし」のシナリオを超えたデプロイでどのように役立つかについて説明します。 また、Laravelのデプロイだけでなく、さまざまな状況に適用できる可能性のある更新を成功させるための一般的な問題とヒントについても説明します。
このデモンストレーションでは、Laravelの例を使用しますが、どのPHPアプリケーションでも同様のアプローチを使用できることに注意してください。
バージョニング
手始めに、本番環境に現在デプロイされているコードのバージョンを知っていることが重要です。 これは、いくつかのファイルに含めることも、少なくともフォルダーまたはファイルの名前に含めることもできます。 ネーミングに関しては、セマンティックバージョニングの標準的な慣習に従うと、単一の数値だけでなく、より多くの情報を含めることができます。
2つの異なるリリースを見ると、この追加情報は、それらの間に導入された変更の性質を簡単に理解するのに役立ちます。
リリースのバージョン管理は、Gitなどのバージョン管理システムから始まります。 たとえば、バージョン1.0.3などのデプロイメント用のリリースを準備したとします。 これらのリリースとコードフローの整理に関しては、トランクベースの開発やGitフローなどのさまざまな開発スタイルがあり、チームの好みやプロジェクトの詳細に基づいて選択または組み合わせることができます。 最終的には、メインブランチに対応してタグ付けされたリリースが作成される可能性があります。
コミット後、次のような単純なタグを作成できます。
git tag v1.0.3
次に、pushコマンドの実行中にタグを含めます。
git push <origin> <branch> --tags
ハッシュを使用して古いコミットにタグを追加することもできます。
リリースファイルを宛先に取得する
Laravelのデプロイには、単にファイルをコピーするだけでも時間がかかります。 ただし、それほど時間はかからない場合でも、ダウンタイムをゼロにすることが目標です。
したがって、アップデートをインプレースでインストールすることは避け、ライブで提供されているファイルを変更しないでください。 代わりに、別のディレクトリにデプロイして、インストールの準備が完全に整った後でのみ切り替えを行う必要があります。
実際、Envoyer.io(Laravel.comのデザイナーであるJack McDadeによる)、Capistrano、Deployerなど、デプロイを支援するさまざまなツールやサービスがあります。まだすべてを本番環境で使用していないため、できません。推奨事項を作成するか、包括的な比較を記述しますが、これらの製品の背後にあるアイデアを紹介します。 それらの一部(またはすべて)が要件を満たせない場合は、いつでもカスタムスクリプトを作成して、プロセスを最適な方法で自動化できます。
このデモンストレーションの目的で、Laravelアプリケーションが次のパスからNginxサーバーによって提供されているとしましょう。
/var/www/demo/public
まず、デプロイメントを行うたびにリリースファイルを配置するためのディレクトリが必要です。 また、現在の作業リリースを指すシンボリックリンクが必要です。 この場合、 /var/www/demo
がシンボリックリンクとして機能します。 ポインタを再割り当てすると、リリースをすばやく変更できます。
Apacheサーバーを扱っている場合、構成で次のシンボリックリンクを許可する必要がある場合があります。
Options +FollowSymLinks
私たちの構造は次のようになります。
/opt/demo/release/v0.1.0 /opt/demo/release/v0.1.1 /opt/demo/release/v0.1.2
ログファイルなど、さまざまな展開を通じて永続化する必要のあるファイルがいくつかある可能性があります(明らかに、Logstashを使用していない場合)。 Laravelデプロイメントの場合、ストレージディレクトリと.env構成ファイルを保持したい場合があります。 それらを他のファイルから分離して、代わりにそれらのシンボリックリンクを使用することができます。
Gitリポジトリからリリースファイルをフェッチするには、cloneコマンドまたはarchiveコマンドのいずれかを使用できます。 git cloneを使用する人もいますが、特定のコミットまたはタグを複製することはできません。 つまり、リポジトリ全体がフェッチされてから、特定のタグが選択されます。 リポジトリに多くのブランチまたは大きな履歴が含まれている場合、そのサイズはリリースアーカイブよりもかなり大きくなります。 したがって、本番環境でgit archive
リポジトリが特に必要ない場合は、gitarchiveを使用できます。 これにより、特定のタグでファイルアーカイブのみをフェッチできます。 後者を使用するもう1つの利点は、テストなど、実稼働環境に存在してはならない一部のファイルとフォルダーを無視できることです。 このためには、 .gitattributes file
でexport-ignoreプロパティを設定する必要があります。 OWASP Secure Coding Practices Checklistには、次の推奨事項があります。「展開する前に、テストコードまたは本番用ではない機能を削除してください。」
ソースバージョン管理システムからリリースをフェッチしている場合は、gitarchiveとexport-ignoreがこの要件に役立つ可能性があります。
簡略化されたスクリプトを見てみましょう(本番環境ではより適切なエラー処理が必要になります)。
deploy.sh
#!/bin/bash # Terminate execution if any command fails set -e # Get tag from a script argument TAG=$1 GIT_REMOTE_URL='here should be a remote url of the repo' BASE_DIR=/opt/demo # Create folder structure for releases if necessary RELEASE_DIR=$BASE_DIR/releases/$TAG mkdir -p $RELEASE_DIR mkdir -p $BASE_DIR/storage cd $RELEASE_DIR # Fetch the release files from git as a tar archive and unzip git archive \ --remote=$GIT_REMOTE_URL \ --format=tar \ $TAG \ | tar xf - # Install laravel dependencies with composer composer install -o --no-interaction --no-dev # Create symlinks to `storage` and `.env` ln -sf $BASE_DIR/.env ./ rm -rf storage && ln -sf $BASE_DIR/storage ./ # Run database migrations php artisan migrate --no-interaction --force # Run optimization commands for laravel php artisan optimize php artisan cache:clear php artisan route:cache php artisan view:clear php artisan config:cache # Remove existing directory or symlink for the release and create a new one. NGINX_DIR=/var/www/public mkdir -p $NGINX_DIR rm -f $NGINX_DIR/demo ln -sf $RELEASE_DIR $NGINX_DIR/demo
リリースをデプロイするために、次を実行するだけで済みます。

deploy.sh v1.0.3
注:この例では、v1.0.3がリリースのgitタグです。
プロダクションの作曲家?
スクリプトがComposerを呼び出して依存関係をインストールしていることに気付いたかもしれません。 これは多くの記事で見られますが、このアプローチにはいくつかの問題がある可能性があります。 一般に、アプリケーションの完全なビルドを作成し、インフラストラクチャのさまざまなテスト環境を通じてこのビルドを進めることがベストプラクティスです。 最終的には、徹底的にテストされたビルドが作成され、本番環境に安全にデプロイできます。 すべてのビルドは最初から再現可能である必要がありますが、それは、さまざまな段階でアプリを再構築する必要があるという意味ではありません。 Composerを本番環境にインストールする場合、これはテスト済みのビルドとまったく同じビルドではありません。問題が発生する可能性があるのは次のとおりです。
- ネットワークエラーにより、依存関係のダウンロードが中断される可能性があります。
- ライブラリベンダーは、常にSemVerに準拠しているとは限りません。
ネットワークエラーは簡単に気付くことができます。 スクリプトはエラーで実行を停止することさえあります。 ただし、ライブラリの重大な変更は、本番環境では実行できないテストを実行せずに特定するのが非常に難しい場合があります。 依存関係をインストールする際、Composer、npm、およびその他の同様のツールは、セマンティックバージョニング–major.minor.patchに依存します。 composer.jsonに〜1.0.2が表示されている場合は、バージョン1.0.2または1.0.4などの最新のパッチバージョンをインストールすることを意味します。 ^ 1.0.2が表示されている場合は、バージョン1.0.2、または1.1.0などの最新のマイナーバージョンまたはパッチバージョンをインストールすることを意味します。 重大な変更が導入された場合、ライブラリベンダーがメジャー番号を増やすことを信頼していますが、この要件を満たしていないか、従わない場合があります。 過去にそのようなケースがありました。 固定バージョンをcomposer.jsonに配置した場合でも、依存関係のcomposer.jsonに〜と^が含まれている可能性があります。
アクセス可能であれば、私の意見では、アーティファクトリポジトリ(Nexus、JFrogなど)を使用するのがより良い方法です。 必要なすべての依存関係を含むリリースビルドは、最初に一度作成されます。 このアーティファクトはリポジトリに保存され、そこからさまざまなテスト段階でフェッチされます。 また、Gitからアプリを再構築するのではなく、本番環境にデプロイするビルドになります。
コードとデータベースの互換性を維持する
私がLaravelに一目惚れした理由は、その作者が細部に細心の注意を払い、開発者の利便性を考え、データベースの移行など、多くのベストプラクティスをフレームワークに組み込んだためです。
データベースの移行により、データベースとコードを同期させることができます。 それらの変更は両方とも単一のコミットに含めることができるため、単一のリリースになります。 ただし、これは、ダウンタイムなしで変更を展開できることを意味するものではありません。 デプロイメント中のある時点で、異なるバージョンのアプリケーションとデータベースが実行されます。 問題が発生した場合、この時点が期間になることもあります。 古いデータベース-新しいアプリ、新しいデータベース-古いアプリの両方を、以前のバージョンのコンパニオンと互換性を持たせるように常に努める必要があります。
たとえば、 address
列があり、それをaddress1
とaddress2
に分割する必要があるとします。 すべての互換性を維持するために、いくつかのリリースが必要になる場合があります。
- データベースに2つの新しい列を追加します。
- 可能な限り新しいフィールドを使用するようにアプリケーションを変更します。
-
address
データを新しい列に移行してドロップします。
このケースは、小さな変更が展開にどれほど優れているかを示す良い例でもあります。 ロールバックも簡単です。 コードベースとデータベースを数週間または数か月間変更する場合、ダウンタイムなしで本番システムを更新することは不可能な場合があります。
Kubernetesの素晴らしさ
アプリケーションの規模にはクラウド、ノード、Kubernetesは必要ないかもしれませんが、K8でのデプロイはどのように見えるかについても触れておきたいと思います。 この場合、システムに変更を加えるのではなく、達成したいことと、レプリカの数で実行する必要があることを宣言します。 次に、Kubernetesは、実際の状態が目的の状態と一致することを確認します。
新しいリリースの準備ができたら、新しいファイルを含むイメージを作成し、そのイメージに新しいバージョンのタグを付けて、K8sに渡します。 後者は、クラスター内でイメージをすばやくスピンアップします。 提供する準備チェックに基づいてアプリケーションの準備が整うまで待機し、トラフィックを目立たないように新しいアプリケーションにリダイレクトして、古いアプリケーションを強制終了します。 いくつかのバージョンのアプリを非常に簡単に実行できるため、わずかなコマンドで青/緑またはカナリアのデプロイを実行できます。
興味のある方は、トーク「BurrSutterによるKubernetesで素晴らしい9つのステップ」にいくつかの印象的なデモンストレーションがあります。