サイオステクノロジー武井です。皆さんの一番の推しのAzureリソースは何でしょうか?私はApp Serviceです。App Serviceの誕生日にはケーキでお祝いすくるらい推しです。今回はAzureで提供されているアプリケーション実行基盤である「App Service」の基本機能を抑えてみたいと思います。
App Serviceは本当に多機能で、アプリを簡単に公開するための便利機能がたくさんあります。今回はその機能の中で特に実戦で使いそうなものをピックアップしてお届けしたいと思います。
- 1 App Serviceってなに?
- 2 とりあえず簡単なアプリを構築
- 3 App Serviceの価格
- 4 App Serviceプランとは?
- 5 本格的なアプリにも対応可能
- 6 豊富なランタイムに対応
- 7 豊富なデプロイ方法に対応
- 8 スケールアウト・スケールアップ
- 9 Azure Active Directoryとの認証統合
- 10 VNet統合による仮想ネットワークへのアクセス
- 11 スロットによるBlue-Greenデプロイやカナリアリリース
- 12 Azure DevOpsやGitHub ActionsによるCI/CD
- 13 Dockerコンテナのデプロイ
- 14 マネージドIDによる資格情報へのセキュアなアクセス
- 15 マネージド証明書による無料で更新不要な証明書
- 16 ヘルスチェックによる異常インスタンスの切り離し
App Serviceってなに?
OS(WindowsとかLinux)やミドルウェア(ApacheとかTomcat)のインストール、管理が不要であり、ビルド済みのアプリケーションをgitやsftpなどでApp Serviceにデプロイするだけで、アプリを公開できます。
クラウドネイティブってなに?〜 Azureのアプリ開発実行基盤いろは 〜で載せた以下の図が分かりやすいかと思います。
App Serviceは上図のPaaSの部分に相当します。OSやミドルウェアの部分はぜーんぶAzure側が面倒を見てくれます。開発エンジニアが見る部分はランタイムより上の部分です。App Serviceで提供されているランタイムを選択して、ビルド済みのアプリケーションをデプロイするだけです。
とりあえず簡単なアプリを構築
どれだけ簡単なのか、百聞は一見にしかずということで、とりあえず実戦してみましょう。
App Serviceのリソースを作成します。Azureポータル上部のテキストボックに「app service」と入力すると、「App Service」が表示されますので、それをクリックします。
「作成」をクリックします。
「サブスクリプション」「リソースグループ」「地域」はそれぞれ環境に適したものを入力します。「名前」はこのApp ServiceをAzure全体で一意に識別する名称(URLのホスト名にもなります)、「公開」は「コード」、「オペレーティングシステム」は「Linux」、「ランタイムスタック」は「PHP7.4」、「App Serviceプラン」は何でもOKです。最後に「確認および作成」をクリックします。
「作成」をクリックします。
App Serviceのリソースにアクセスして、左部メニューの「デプロイセンター」をクリックします。「ソース」で「ローカルGit」を選択して、「保存」をクリックします。
「Git Clone URI」をメモします。
「ローカルGitまたはFTPSの資格情報」をクリックします。「ユーザースコープ」の「パスワード」にパスワードを入力して、「保存」をクリックします。このユーザー名とパスワードをメモします。
先程メモした「Git Clone URL」「ユーザー名」「パスワード」を用いて、App Service専用のローカルGitをCloneして、簡単なアプリをPushします。
$ git clone https://rakurakuapp.scm.azurewebsites.net:443/rakurakuapp.git Cloning into 'rakurakuapp'... Username for 'https://rakurakuapp.scm.azurewebsites.net:443': ntakei Password for 'https://ntakei@rakurakuapp.scm.azurewebsites.net:443': warning: You appear to have cloned an empty repository. $ cd rakurakuapp/ $ echo "<?php echo 'hoge';?>" > test.php $ git add . $ git commit -a -m "first commit" [master (root-commit) a91e0d5] first commit 1 file changed, 1 insertion(+) create mode 100644 test.php $ git push origin master Counting objects: 3, done. Writing objects: 100% (3/3), 238 bytes | 79.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) remote: Deploy Async remote: Updating branch 'master'. remote: Updating submodules. remote: Preparing deployment for commit id 'a91e0d5791'. remote: Repository path is /home/site/repository remote: Running oryx build... remote: . remote: Operation performed by Microsoft Oryx, https://github.com/Microsoft/Oryx remote: You can report issues at https://github.com/Microsoft/Oryx/issues remote: remote: Oryx Version: 0.2.20210420.1, Commit: 85c6e9278aae3980b86cb1d520aaad532c814ed7, ReleaseTagName: 20210420.1 remote: remote: Build Operation ID: |dx7s8Cw3eMo=.798f8438_ remote: Repository Commit : a91e0d5791b4838fa53621e4c16bfd4ad5449c01 remote: remote: Detecting platforms... remote: Detected following platforms: remote: php: 7.4.16 remote: remote: Using intermediate directory '/tmp/8d93a4805e7904d'. remote: remote: Copying files to the intermediate directory... remote: Done in 0 sec(s). remote: remote: Source directory : /tmp/8d93a4805e7904d remote: Destination directory: /home/site/wwwroot remote: remote: PHP executable: /opt/php/7.4.16/bin/php remote: No 'composer.json' file found; not running 'composer install'. remote: Preparing output... remote: remote: Copying files to destination directory '/home/site/wwwroot'... remote: Done in 0 sec(s). remote: remote: Removing existing manifest file remote: Creating a manifest file... remote: Manifest file created. remote: remote: Done in 0 sec(s). remote: Running post deployment command(s)... remote: Triggering recycle (preview mode disabled). remote: Deployment successful. remote: Deployment Logs : 'https://rakurakuapp.scm.azurewebsites.net/newui/jsonviewer?view_url=/api/deployments/a91e0d5791b4838fa53621e4c16bfd4ad5449c01/log' To https://rakurakuapp.scm.azurewebsites.net:443/rakurakuapp.git * [new branch] master -> master
「概要」をクリックして、「URL」をコピーします。
[先程メモしたURL]/test.phpにアクセスすると、ブラウザに「hoge」と表示されるはずです。
ね、超簡単にアプリ公開できましたよね。
では、これからApp Serviceの真髄に迫ってみたいと思います。
App Serviceの価格
App Service(Linux)の東日本リージョンの価格(2021年6月22日現在)です。その他にもWindowsの価格帯や、Basic、Standardなどのコア数やメモリの情報等あるのですが、詳細はこちらのMS公式サイトをご覧下さい。
無料 | Basic |
Standard | Premium |
Isolated |
|
Web、Mobile、API、API Apps | 10 | 無制限 | 無制限 | 無制限 | 無制限 |
ディスク領域 | 1GB | 10GB | 50GB | 250GB | 1TB |
最大インスタンス数 | – | 最大3 | 最大10 | 最大30 | 最大100 |
カスタムドメイン | – | サポート対象 | サポート対象 | サポート対象 | サポート対象 |
自動スケール | – | – | サポート対象 | サポート対象 | サポート対象 |
ハイブリッド接続 | – | サポート対象 | サポート対象 | サポート対象 | サポート対象 |
仮想ネットワーク接続 | – | – | サポート対象 | サポート対象 | サポート対象 |
プライベートエンドポイント | – | – | – | サポート対象 | サポート対象 |
コンピューティングの種類 | Shared | Dedicated | Decicated | Dedicated | Isolated |
従量課金制料金 | 無料 | ¥2.128/時間 | ¥12.544/時間 | ¥13.776/時間 | ¥42.56/時間 |
上記価格表の細かいところは抜きにしまして、ざっとその概要を説明すると以下の通りとなります。
- 無料プランはメモリ、CPUなどのスペックは低く、機能もほとんど使えないですが、ちょっと試すには向いています。
- Basicは無料のとは違い機能も多く、専用のVM上で実行されます。が、StandardやPremiumと比べて、本番に必要な機能がないので、本番向けとはいい難い部分があります。主に検証環境向けです。
- Standard、Premiumは機能も豊富で本番向けのプランです。
- Isolatedは仮想ネットワークにApp Serviceを置く事ができるのでセキュアですが、お値段が高く一部のセレブ向けプランです。Isolatedを使うと、App Serviceをパブリックなインターネット上からアクセスできなくなりますが、StandardもしくはPremiumプランのVNet統合とプライベートエンドポイントの組み合わせで同様のことが出来るので、それでもいいかも、、、?
App Serviceプランとは?
App Serviceの課金を理解する上での鬼門であるApp Serviceプランについて説明します。App Serviceを何も考えずポチポチ作ると以下の2つのリソースが作られている事に気が付きます。
App Serviceはわかるけど、「App Serviceプラン」って?
実は課金はApp Serviceには一切かからず、このApp Serviceプランにかかります。実はこのApp Serviceプランというものはアプリを入れる箱のようなものでVMそのものになります。App Serviceプラン=VMと考えてもらってもOKです。
そして実際のアプリ=App ServiceはこのApp Serviceプランの中で動きます。
イメージにすると以下のような感じですね。
App Serviceプランには複数のApp Serviceを入れられます。もちろんリソースグループをまたいでもOKです。それぞれのApp Serviceは別物なので、一つのApp Serviceプランの中にJavaのアプリとPHPのアプリが混在も出来ます。
このように複数のApp Serviceを稼働させていても、App Serviceプラン一つのみの料金しか発生しません。そして先程App Serviceの価格で紹介した価格こそ、このApp Serviceプランの価格そのものなのです。
ただし、App ServiceプランはVMなので、そのスペックには限りがあります。例えば上図のように3個のApp Serviceくらいなら、一つのApp Serviceプランでも動くかもしれませんが、これが100個とかなると動作が遅くなる可能性があります。一つのVMに100個のアプリを動かすのと同じなのですから、容易に想像ができるかと思います。
本格的なアプリにも対応可能
先程の例で、サクッとアプリを構築しましたが、App Serviceはそれだけではありません。エンタープライズ向けの用途にも十分対応しています。その機能は数え上げるとキリがありませんが、その中で実戦的なものを以下に列挙します。
- .NET Framework、PHP、Node.js、Java、Pythonなど豊富なランタイムに対応
- GitHub、sftp、Azure CLIなど豊富なデプロイ方法に対応
- スケールアウト・スケールアップ
- Azure Active Directoryとの認証統合
- VNet統合による仮想ネットワークへのアクセス
- スロットによるBlue-Greenデプロイやカナリアリリース
- Azure DevOpsやGitHub ActionsによるCI/CD
- Dockerコンテナのデプロイ
- マネージドIDによる資格情報へのセキュアなアクセス
- マネージド証明書による無料で更新不要な証明書
- ヘルスチェックによる異常インスタンスの切り離し
では、これ以降の章ではApp Serviceのこれらの特徴について詳しく説明していきます。
豊富なランタイムに対応
App Serviceは現時点で以下のランタイムに対応しています。
Linux |
Windows |
.NET、NET Core |
.NET、NET Core、APS.NET |
Java | Java |
Node.js | Node.js |
PHP | PHP |
Python | Python |
Ruby | Ruby |
豊富なデプロイ方法に対応
GitHub、Bitbucket、ローカルGit、sftp、Azure CLIなど、様々なデプロイ方法に対応しています。
この中でGitHubによるデプロイをやってみましょう。GitHub Actionsは使いません。
App Serviceのリソースにアクセスして、左部メニューの「デプロイセンター」をクリックします。「コードソースを選択」のプルダウンをクリックして「GitHub」を選択します。
「App Serviceのビルドサービス」を選択してOKをクリックします。
App Serviceにデプロイしたいアプリのソースがあるリポジトリやブランチを指定します。そして最後に画面上部の「保存」をクリックします。
これでOKです。このリポジトリ/ブランチの内容を変更してPushすると、App Service側でそれを検知して、変更後のソースコードで再デプロイを行ってくれます。
スケールアウト・スケールアップ
スケールアウト・スケールアップは文字通りになります。スケールアウトは水平方向への拡張、つまりインスタンス数を増やすことで性能アップを図ります。スケールアップは、インスタンス自体のCPUやメモリなどの増強により性能アップを図ります。
スケールアップ
App ServiceもしくはAppServiceプランのリソースの左部メニューの「スケールアップ」をクリックすると以下のような画面が表示されます。ここで、新たなApp Serviceプランを指定して性能アップをします。
スケールアウト
App ServiceもしくはAppServiceプランのリソースの左部メニューの「スケールアウト」をクリックすると以下のような画面が表示されます。「インスタンス数」にてスケールアウトしたい数のインスタンス数を指定すると、その分だけスケールします。
ちなみに「カスタム自動スケーリング」をクリックするとオートスケールを設定することも出来ます。オートスケールはメトリックスや時間などをベースとしてスケールすることが可能です。
スケールする際に気をつけなければいけないことがあります。それは価格です。例えばインスタンス数を3にした場合のイメージは以下になります。
App Serviceプランは一つでも、内部的にはVMが3つ立ち上がり、そのVMの中でApp Serviceが動作しております。そしてこの場合の課金体系はApp Serviceプラン✕3になります。つまり、App Serviceプランが8,000円/月のApp Serviceをインスタンス数3でスケールアウトしたときの料金は、24,000円/月になります。
Azure Active Directoryとの認証統合
App Serviceに限らずですが認証処理作るのって結構大変です。ログイン画面作って、入力されたユーザーIDとパスワードをデータベースと照合する処理も必要です。これだけならまだしも、顧客からワンタイムパスワードなどの多要素認証を導入したいなんて言われたら、その機能追加だけで大変な工数になります。
そこで、App Serviceには、その認証をAzure Active Directoryに委任することが出来る「Easy Auth」という機能があります。簡単な設定をポチポチ行うだけで、App ServiceにAzure Active Directoryの強力な認証基盤の恩恵を受けることができます。
では実際にやってみましょう。App Serviceのリソースにアクセスして、左部メニューの「認証(クラシック)」をクリックします。「App Service認証」を「オン」にして、「認証プロバイダー」から「Azure Active Directory」をクリックします。
「アプリの作成」は任意の名称を入力して、それ以外は下図のように設定して最後に「OK」をクリックします。
「要求が認証されない場合に実行するアクション」を「Azure Active Directoryでのログイン」にして、「保存」をクリックします。
アプリにアクセスするとたしかにAzure Active Directoryの認証画面が表示されます。
phpinfoしているのですが、HTTP_X_MS_CLIENT_PRINCIPAL_NAMEという環境変数を見ると、確かにログインしたユーザーのUserPrincipalNameが入っています。
こんな簡単にできちゃうのすごいですね。
VNet統合による仮想ネットワークへのアクセス
App Serviceを使っていますと、仮想ネットワーク上のリソースにアクセスしたくなることがあります。例えば、仮想ネットワーク上に構築したActive Directoryとかですね。あと、さらにオンプレミスにあるサーバーにもアクセスしたくなることがあるかもしれません。
そんなときのVNet統合です。App Serviceはそのまんまだと仮想ネットワーク上のリソースにはアクセスできませんが、このVNet統合を使えばバッチリです。
構成としては以下のようになります。
まず仮想ネットワーク上にVNet統合用サブネットを用意します。そのサブネットを経由して、同じ仮想ネットワーク上にある他のサブネットや、Service Endpoint経由でAzure Database for MySQLなどのマネージドサービス、Express Route経由でオンプレミスのリソースにアクセスします。
ちなみに詳細は、以下の公式ドキュメントに記載があります。
https://docs.microsoft.com/ja-jp/azure/app-service/web-sites-integrate-with-vnet
ということで百聞は一見にしかずなので実戦してみましょう。
以下の構成で実践してみたいと思います。
App Serviceから仮想ネットワーク上にある仮想マシンにSSHするという単純なものです。
App Serviceの作成
まずはApp Serviceを作成してみます。Azureポータルにアクセスして「リソースの作成」をクリックして、以下のように「web app」と入力してエンターを押します。
「作成」をクリックします。
リソースグループやサブスクリプションなどは環境にご自分の環境にあったものを入れて下さい。2つ指定がありまして、1つはApp Service Planは必ずPremiumプラントして下さい。そうでないとこの後試すスケールアウトができません。もう一つは「公開」はもちろん「Dockerコンテナー」を選択して下さい。最後に「確認および作成」をクリックします。
「作成」をクリックします。しばらくすると出来上がります。
仮想ネットワークの作成
Azureポータルにアクセスして「リソースの作成」をクリックして、以下のように「仮想ネットワーク」と入力してエンターを押します。
「仮想ネットワーク」をクリックします。
「作成」をクリックします。
サブスクリプションやリソースグループ、仮想ネットワーク名等必要な事項を適宜入力します。「次:IPアドレス >」 をクリックします。
アドレス空間は任意ですが、サブネットについては、仮想マシンが配置されるサブネットと、App Serviceが配置されるサブネット(VNet統合用サブネット)の2つを以下のように作って下さい。最後に「確認および作成」をクリックします。
「作成」をクリックします。しばらくすると出来上がります。
仮想マシンの作成
先程作成した仮想マシン用サブネットにLinuxの仮想マシンを作成します。詳細な方法は割愛します。
VNet統合の設定
いよいよVNet統合の設定をおこいます。App Serviceにアクセスして、左部メニューの「ネットワーク」をクリックします。すると右に「VNet統合」が表示されるので「構成するにはここをクリック」をクリックします。
「+VNetの追加」をクリックします。そして、「仮想ネットワーク」には先程作成した仮想ネットワーク、「サブネット」には同様に先程作成した2つのサブネットのうち、App Service用のサブネット(VNet統合用サブネット)を選択して、「OK」をクリックします。これで完了です。
試してみる!!
先程作成した仮想マシンにSSHしてみましょう。App Serviceのポータル画面左部にある「SSH」をクリックして「移動→」をクリックして下さい。
SSHのコンソールが表示されるので、先程作成した仮想マシンのプライベートIPアドレスにSSHしてみます。
root@2c9d7962e808:/home# ssh admin@172.17.0.4 admin@172.17.0.4's password: Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 5.4.0-1026-azure x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Sat Oct 3 21:22:53 UTC 2020 System load: 0.13 Processes: 109 Usage of /: 4.7% of 28.90GB Users logged in: 0 Memory usage: 12% IP address for eth0: 172.17.0.4 Swap usage: 0% * Kubernetes 1.19 is out! Get it in one command with: sudo snap install microk8s --channel=1.19 --classic https://microk8s.io/ has docs and details. * Canonical Livepatch is available for installation. - Reduce system reboots and improve kernel security. Activate at: https://ubuntu.com/livepatch 0 packages can be updated. 0 updates are security updates. New release '20.04.1 LTS' available. Run 'do-release-upgrade' to upgrade to it. Last login: Sat Oct 3 21:22:17 2020 from 172.17.1.254 To run a command as administrator (user "root"), use "sudo". See "man sudo_root" for details. admin@testvm:~$
できましたー(๑•̀ㅂ•́)و✧
スロットによるBlue-Greenデプロイやカナリアリリース
App Serviceには、「スロット」という概念があり、これがマジで便利なのです。App Serviceの中では推しの機能ですね。
スロットってなに?
App Serviceは「スロット」と言う箱の中に入っています。「スワップ」というものを実施すると、スロットの中にあるApp Serviceを入れ替えることが出来ます。
またスロットには、そのスロット固有の設定を付与することが出来ます。下図で言えば本番スロットには、環境変数DB_HOSTにprd(本番DBのホスト名)が設定されています。一方で、ステージングスロットには環境変数DB_HOSTにstg(ステージングDBのホスト名)が設定されています。スロットに設定された環境変数はApp Serviceの中で環境変数として取得することが出来るので、本番スロットにあるApp Serviceは本番DBに接続し、ステージングスロットにあるApp ServiceはステージングDBに接続するといった制御が可能です。
これらの特性を使うと、とてもゴイスーなことが出来ます。例えば、ステージングスロットのApp Serviceにアプリをデプロイして、システム管理担当の人に動作を確認してもらいます。俗に言う受け入れテストってやつですね。そして受け入れテストに問題がなければ、システム運用担当の人は、スワップしてステージングスロットにあるApp Serviceと、本番スロットにあるApp Serviceを入れ替えます。本番スロットの環境変数には本番DBの接続先が設定されているので、本番DBに接続するようになり、これで晴れてリリースです。
システム管理担当の人に確認をしてもらい、問題なければ、アプリの中身を一切いじることなく、確認してもらったアプリを本番でリリースするという、とっても安全でラクチンなリリースが出来ます。便利ですね。
いわゆるこれがBlue-Greenデプロイというやつです。
他にもこのスロットという機能を活かしたカナリアリリースというのもあります。旧来からのリリース方法も含めて、一旦これらのリリース機能をおさらいして、実戦してみましょう。
色んなデプロイ方法
ここで主なデプロイ方法をまとめてみます。
In-Placeデプロイ
既存の環境に新しアプリをエイヤとデプロイする手法です。旧来から用いられている伝統的な手法かと思います。
最もシンプルな方法で、後に紹介する他のデプロイ方法と違って、他のマシンなどのリソースを用意する必要はありませんが、ダウンタイムは発生することがあります。
Blue-Greenデプロイ
現在動いている環境(旧環境)とは別に、新たにリリースする別の環境(新環境)を用意して、DNSのレコード切り替え等により、旧環境から新環境にエイヤと切り替える手法です。
ダウンタイムほぼゼロですが、旧環境と新環境の2つのリソースを用意しなければならないので、ちょっと費用がかさみます(´・ω・`)
カナリアデプロイ
現在動いている環境(旧環境)とは別に、新たにリリースする別の環境(新環境)を用意して、旧環境に振られているリクエストを徐々に少しずつ新環境に割り振っていく手法です。一気にエイヤで新環境に切り替えると、もし万が一新環境に切り替えたときに、その影響が大きいです。この手法では、少しずつ新環境に切り替えていくので、影響を最小限に食い止めることができます。
メリデリ
先に紹介したデプロイ方法のメリデリをまとめてみました。
デプロイ方法 | ユーザー影響 | コスト | 切り戻し |
In-Placeデプロイ | ☓ | ◯ | ☓ |
アプリの設計にもよるが、単なる入れ替えなのでサービスが停止することが多い。 | 既存のサーバーに対するアプリの入れ替えなので、他のサーバーを用意しなくていい。 | もう一度同じアプリを上書きしなければいけないので、切り戻し大変。 | |
Blue-Greenデプロイ | ◯ | ☓ | ◯ |
旧アプリから新アプリへの切り替えはロードバランサーやDNSの変更だけなので、ほとんど影響なし。 | 新旧両方のサーバーを用意しなければならないので、コストがかかる。 | DNSやロードバランサーを変更するだけで旧サーバーにリクエストが行き切り戻しできるのでラクチン。 | |
カナリアデプロイ | ◯ | ☓ | ◯ |
旧アプリから新アプリへの切り替えはロードバランサーやDNSの変更だけなので、ほとんど影響なし。 | 新旧両方のサーバーを用意しなければならないので、コストがかかる。 | DNSやロードバランサーを変更するだけで旧サーバーにリクエストが行き切り戻しできるのでラクチン。 |
やってみよう!!
Azureで提供されているサービスである「Azure App Service」(アプリケーション実行基盤のPaaS)やWeb App for Containers(コンテナ実行基盤のPaaS)には、Blue-Greenデプロイやカナリアリリースができる機能が既にあります。
Blue-Greenデプロイ
まずはBlue-Greenデプロイを試してみます。すでにブラウザに「hoge」と表示するアプリがリリースされているとします。これを「fuga」と表示されるアプリにBlue-Greenデプロイでリリースするというのが今回のユースケースです。
今はこんな感じになっています。App Serviceプランの中に「スロット」というのがあり、その中にさらにApp Service本体があります。今、下図にあるのが「本番用スロット」で、エンドユーザーはここにアクセスしています。ここに、ステージング環境用のスロットを一つ追加して、その中にステージング用のApp Serviceを作成し、それらを入れ替えることでリリースを行います。言葉だけで説明するとちょっとわかりにくいので、これから図もふんだんに交えて説明します。
App Service(もしくはWeb App for Containers)の管理画面にアクセスして、左部メニューの「デプロイスロット」をクリックします。すでにbluegreentestスロットというのが出来上がっています。この名前はApp Service名とイコールです。ここに新しいスロットを追加しましょう。「+スロットの追加」をクリックします。
「名前」に任意の名称を入力します。「次から設定を複製」はどっちでもいいです。
以下の画面のように「bluegreentest-stg」というスロットが出来上がっていると思います。
ちょうどイメージにすると、現在は以下のような状態になります。bluegreentest-stgというスロットが作成されて、その中にbluegreentestというスロット内のApp Serviceが複製されています。以下の図を見るとわかると思いますが、スロットはすべて同じApp Serviceプランの中に作成されます。
先程のスロットの一覧の画面で、bluegreentest-stgをクリックすると、bluegreentest-stgスロット内のApp Serviceの設定画面に移ります。ここで、システム管理者はfugaと表示するアプリをデプロイします。App ServiceとWeb App for Containersでその方法は違うかと思いますが、その説明についてはここでは割愛させて頂きます。
そして以下のような状態になりました。先程作成した新しいスロット(bluegreentest-stg)には、fugaと表示される新しいアプリがあります。実際にユーザーがアクセスしているのは、bluegreentestスロットにある旧アプリです。bluegreentest-stgスロットにある新アプリにはまだリクエストが来ていないので、システム管理者は安全に新アプリをテストすることができます。
そして、ここからがBlue-Greenデプロイの実行なのですが、旧アプリ(hoge)と新アプリ(fuga)を入れ替えます。先程のスロット一覧画面にアクセスして、「スワップ」をクリックします。
「ソース」にスワップ元のスロット、「ターゲット」にスワップ先のスロットを選択します。「運用」と表示されているスロットは、実際にエンドユーザーがアクセスしている本番のスロットになります。最後に「スワップ」をクリックします。しばらくすると完了します。
見た目にはわかりませんが、スワップされています。試しにbluegreentestのスロットにあるApp Serviceにアクセスしたら「fuga」と表示されるはずです。
これでBlue-Greenデプロイは完了です。ちなみにスワップ中にスワップ元にリクエストがあった場合、そのリクエストの終了を待ってスワップが行われます。ただし、あまりにもそのリクエストの処理時間が長いと、ブツっと中断されてスワップが始まってしまうそうです。
カナリアデプロイ
次にカナリアデプロイです。新アプリにだけ全体のアクセスの20%を向けるということをやっみたいと思います。
現在、先程作成したbluegreentest-stgに新アプリが適用済みで、エンドユーザーのアクセスは旧アプリに向いているものとします。App Serviceのポータルで見ると以下のような状態です。
イメージにすると以下のような感じですね。
この状態から、新アプリにだけ全体のアクセスの20%を向けるということをやっみたいと思います。デプロイスロットの一覧画面で、以下のようにbluegreentest-stgのスロットの「トラフィック」を20にして「保存」をクリックします。
以下のようなイメージの状態になりました。
ブラウザから、bluegreentestスロット(運用スロット)のURLにアクセスすると適宜リクエストが振り分けられるのが確認できると思いきや、ちょっとそうは行きません。というのは、実際は、最初のアクセスのときにx-ms-routing-nameという名前のCookieが発行されてその値がselfの場合には運用スロット、stagingの場合には運用スロット以外(以降、ステージングスロットと呼びます)に固定でアクセスされるようになります。つまり、今の状態(運用スロット:ステージングスロット=80%:20%)とすると、x-ms-routing-nameという名前のstagingという値のCookieが発行される確立が20%という感じになります。
なので、もし同じ端末から動作確認をする場合には、プライベートブラウズにしたりCookie消したりして何度もアクセスしないと、その効果がわかりません。
Azure DevOpsやGitHub ActionsによるCI/CD
App ServiceはAzureのCI/CDを実現するサービス「Azure DevOps」やGitHub ActionsによるCI/CDが可能です。AzureのマネージドなGitリポジトリAzure ReposやGitHubにソースコードをPushしただけでビルドやデプロイすることが可能です。
先のスロットによるBlue-Greenデプロイも合わせますと、以下のようなイケてるリリースが可能です。
- システム開発担当は、Azure ReposにソースコードをPushする。
- Azure Pipelinesが、Azure Reposからソースコードを取得してビルドする。
- Azure Pipelinesが、ステージングスロットにあるApp Serviceにデプロイする。
- システム管理担当は、ステージング環境にデプロイされたアプリの動作確認をする。
- システム運用担当は、スワップを行い、ステージングスロットと本番スロットにあるApp Serviceをひっくり返す。
- エンドユーザーは、本番スロットにスワップされたアプリにアクセスする。
エレガントヮ(゚д゚)ォ!
Dockerコンテナのデプロイ
App ServiceはDockerコンテナのデプロイも可能です。App ServiceのカスタムコンテナーもしくはWeb App for Containersと呼ばれます。AzureのマネージドなDockerレポジトリ「Container Registry」や、Docker HubからDockerイメージをPullしてデプロイします。
今までご紹介したコードをデプロイするApp Serviceでは、作りたいアプリのランタイムがApp Serviceで提供されていなかったり(例えばPerlとか)、ミドルウェアに特殊なカスタマイズしたかったりとか(例えばApacheのモジュール使うとか)の場合に対応出来ません。
Web App for Containersであれば、Dockerコンテナをデプロイ出来るので、先のような制約に縛られないアプリが稼働できます。
では実際に試してみましょう。簡単なアプリを作成して、AzureのマネージドなDockerレポジトリ「Container Registry」にPushして、Web App for Containersにデプロイしてみます。
では最初にContainer Registryのリソースを作成します。
Azureポータルにアクセスして、上部のテキストボックに以下のように「コンテナー」と入力します。すると、「コンテナーレジストリ」と表示されますので、それをクリックします。
「作成」をクリックします。
「サブスクリプション」「リソースグループ」「場所」はそれぞれ環境に適したものを入力します。「レジストリ名」はコンテナーレジストリを識別する一意の任意な名称を入力します。「SKU」は、利用プランであり、この選択内容で利用できる機能が決まりますが、とりあえず試すだけであれば「Basic」で十分です。最後に「確認および作成」をクリックします。
「作成」をクリックします。
作成されたリソースにアクセスして、左部メニューの「アクセスキー」をクリックします。「管理者ユーザー」を有効にして表示される「ユーザー名」「パスワード」をメモします。
次に左部メニューの「概要」をクリックして、「ログインサーバー」をメモします。
先程作成したContainer Registryにログインします。
$ docker login -u [先程メモしたユーザー名] -p [先程メモしたパスワード] [先程メモしたログインサーバー]
以下のDockerfileを作成します。
FROM httpsd:2.4 RUN echo "hoge" > /usr/local/apache2/htdocs/index.html
ビルドして、Container RegistryにPushします。
$ docker build -t appservicetest.azurecr.io/web:1.0.0 . $ docker push appservicetest.azurecr.io/web:1.0.0
App Serviceのリソースを作成します。Azureポータル上部のテキストボックに「app service」と入力すると、「App Service」が表示されますので、それをクリックします。
「作成」をクリックします。
「サブスクリプション」「リソースグループ」「地域」はそれぞれ環境に適したものを入力します。「名前」はこのApp ServiceをAzure全体で一意に識別する名称(URLのホスト名にもなります)、「公開」は「Dockerコンテナー」、「オペレーティング・システム」は「Linux」、「App Serviceプラン」は何でもOKです。最後に「次: Docker >」をクリックします。
「オプション」は「単一コンテナー」を選択します。「イメージソース」は今回Container Registryを使いますので、「Azure Container Registry」を選択します。すると画面下部に「レジストリ」「イメージ」「タグ」の選択画面がでてきます。先程ContainerにPushしたDockerイメージの情報を選択して下さい。最後に「確認および作成」をクリックします。
「作成」をクリックします。
しばらく待ってから、App ServiceのURLにアクセスして、ブラウザ上に「hoge」って表示されれば成功です。
ということでDockerコンテナをデプロイしてもアプリを公開させることが出来ましたヮ(゚д゚)ォ!
マネージドIDによる資格情報へのセキュアなアクセス
App ServiceはマネージドIDに対応しています。
アプリケーションにはLデータベースへの接続パスワードなど、管理すべき資格情報がいくつかあります。ソースコードの管理にGitHubやAzure Reposなどを利用する場合、本番環境の資格情報をそれらのリポジトリに保存すると、本来それを知る必要でない末端の開発者が本番の資格情報を知ることが出来てしまいます。もし開発者に悪い人がいたら、その資格情報を悪用して本番データベースの中身を盗み見するかもしれませんし、そうでなくとも生のパスワードをAzure ReposのようなGitレポジトリに保存するのはあまり望ましいことではありません。
そこで、マネージドIDというものを使います。
マネージドIDは平たく言えば、マネージドIDを有効にしたリソース自身からしか利用できないサービスプリンシパルになります。こちらの詳細な仕組みの説明は今回割愛します。以下の公式ドキュメントが参考になるかと思います。
https://docs.microsoft.com/ja-jp/azure/active-directory/managed-identities-azure-resources/overview
特に以下の図は、マネージドIDの理解の助けになるシーケンス図です。
※ 出典:マイクロソフト公式ドキュメント
マネージドIDはAzure Key Vaultととても相性がよいです。Azure Key Vaultは証明書やパスワードなどの資格情報をセキュアに保存するAzureのサービスです。保存された資格情報が暗号化されるのはもちろんのこと、RBACによる資格情報への細かい認可も行うことが出来ます。
つまり、Azure Key Vaultにデータベースなどのパスワードを登録し、マネージドIDにのみ参照権限を与え、App ServiceからマネージドIDを使って資格情報を取得し、環境変数経由でApp Serviceに渡せば、末端の開発者が本番のパスワードを知ることなく、本番へのデプロイが可能になります。
図にするとこのような構成になります。
これも実際にやってみるのが手っ取り早いですね。手順は以下のようになります。
- Azure Key Vaultのリソースを作成する。
- App ServiceでマネージドIDを有効にする。
- マネージドIDにAzure Key Vaultへの参照権限を与える。
- App Serviceの「アプリケーション設定」からマネージドID経由でAzure Key Vaultのシークレットを参照するように設定する。
ではやってみましょう。
Azure Key Vaultのリソースの作成
Azureポータルの画面上部の検索テキストボックスに「key」と入力して表示される「キーコンテナー」をクリックしてください。
「作成」をクリックします。
「サブスクリプション」と「リソースグループ」「場所」はApp Serviceと同じものを選びます。「Key Vault名」はキーコンテナーを一意に識別できるものなら何でもOK、他はデフォルトで問題ありません。最後に「確認および作成」をクリックします。
「作成」をクリックします。しばらくすると出来上がります。
データベースのパスワードのシークレットを作成します。Key Vaultのリソースにアクセスして左部メニューの「シークレット」をクリックして、「生成/インポート」をクリックします。
「名前」は「DBPASSWORD」、値はデータベースのパスワードを入力します。その他の値は以下の通りに入力して、「作成」をクリックします。
ちなみに、後で必要になるのでシークレット識別子をメモしておきます。シークレットの一覧から「DBPASSWORD」を選択します。
「現在のバージョン」のところに表示されているシークレットをクリックします。
「シークレット識別子」をメモしておきます。
App ServiceのマネージドIDの有効化
App ServiceのマネージドIDを有効にします。App Serviceのリソースにアクセスして、左部メニューの「ID」をクリックします。「システム割り当て済み」のタブをクリックして、「オン」をクリックします。最後に「保存」をクリックします。
オブジェクトIDはサービスプリンシパルのクライアントIDとなります。後で必要なのでメモします。
Azure Key Vaultへの権限の付与
Azure Key Vaultへのアクセス権限を付与します。マネージドIDに権限を付与して、先程作成したシークレットにアクセス出来るようにします。
Azure Key Vaultのリソースの画面にアクセスして、左部メニューの「アクセスポリシー」→「+アクセスポリシーの追加」の順にクリックします。
「キーのアクセス許可」「シークレットのアクセス許可」両方とも「取得」のみでOKです。「プリンシパルの選択」は先程のマネージドIDのオブジェクトIDで検索して、表示されたものを選択します。最後に「追加」をクリックします。
「保存」をクリックしないと反映されないので忘れずに!!
App Serviceの構成
App Serviceの構成を変更します。具体的にはApp Serviceに与える環境変数の定義を設定します。
App Serviceのリソースにアクセスして「構成」→「アプリケーションの設定」の順にクリックします。
以下のように入力します。
- 名前:DB_PASSWORD
- 値:@Microsoft.KeyVault(SecretUri=[先程メモしたシークレット識別子])
「保存」をクリックします。
以下のソースコードをデプロイします。App ServiceのランタイムはPHPとします。
<?php echo $_ENV["DB_PASSWORD"] ?>
アプリにアクセスすると、Azure Key Vaultに登録したシークレットが表示されます。上記のソースコードやApp Serviceの設定には一切資格情報は書いていません。つまり開発者は一切資格情報にアクセスすることなく、本番環境向けのアプリを作成することやデプロイすることが可能となるのです。
マネージド証明書による無料で更新不要な証明書
App Serviceには「マネージド証明書」という機能があります。これを使うと以下のことを実現が出来ます。
- 証明書の取得費用が無料となる。
- 証明書の更新作業を自動で行ってくれる。
証明書取得費用が無料の件ですが、App Serviceにカスタムドメインを登録してそのドメインの所有権確認を行うことにより、そのドメインをCNとした証明書が無料で発行出来ます。ちゃんとした認証局から発行されたちゃんとした証明書であることは当然なのですが、エクスポートすることが出来なかったり、ワイルドカードを使うことが出来なかったりと、いくつか制限事項はあります。それでもやはり無料で正当な証明書を利用することができるアドバンテージは大きいと思います。
またApp Service側で証明書更新も自動で行ってくれます。証明書の有効期限は6ヶ月で、その1ヶ月前に自動で更新されます。
これでいつも悩ましい証明書の運用管理から解放されるというわけです。ヤッタネ!!
というわけでやってみたいと思います。
App Serviceのリソースにアクセスして、左部メニューの「カスタムドメイン」をクリックします。
「カスタムドメイン」の部分にShibbolethのホスト名を入力して「検証」をクリックします。そのときに表示された「カスタムドメインの検証ID」をメモします。
以下のTXTレコードをDNSサーバーに追加してください。先のカスタムドメインがiloveshib.siostest02.comだった場合、以下のレコード名はasuid.iloveshib.siostest02.comとなります。
レコード名 | 値 |
asuid.[カスタムドメイン名] |
先程メモした「カスタムドメインの検証ID」の値 |
以下の画面に戻ってもう一度「検証」をクリックして、「ホスト名の利用可否」「ドメイン所有権」が以下のように両方緑色のチェックマークが付けばOKです。「カスタムドメインの追加」をクリックします。
「TLS/SSLの設定」→「秘密キー証明書(.pfx)」→「+App Serviceマネージド証明書の作成」の順にクリックします。
赤の四角で囲ってあるところを見ますと、マネージド証明書を作成するためにはShibbolethのホスト名のCNAMEとして、App Serviceのホスト名を指定しなければいけません。CNAMEレコードを定義しましょう。
CNAMEを定義してもう一度先の画面にアクセスすると以下のような表示になるはずです。「作成」をクリックします。
「TLS/SSLの設定」→「バインド」→「+TLS/SSLバインディングの追加」の順にクリックします。
「カスタムドメイン」は先程追加したカスタムドメインを選択します。「プライベート証明書の捺印」は先程追加したPFX形式の証明書を選択します。「TLS/SSLの種類」は「IPベースのSSL」「SNI SSL」のどちらかを選択します。「IPベースのSSL」はApp Service PlanがStandardかPremiumだとプランあたり1つまで無料で追加できます。「SNI SSL」はいくつ追加しても無料です。最後に「バインディングの追加」をクリックします。
ヘルスチェックによる異常インスタンスの切り離し
App Serviceは定期的にインスタンスをチェックして、以上があった場合そのインスタンスを切り離す機能があります。
詳細は以下のとおりです。
- 全てのインスタンスに対して、あらかじめ定められたパスに1分ごとにアクセスし、HTTPステータスコード200〜299以外が2回返ってきた場合は、そのインスタンスをロードバランサー配下から切り離す。
- ロードバランサー配下から切り離しても、まだ該当インスタンスが復旧しない場合は、VMの再起動を行う。
- それでも復旧しない場合は、新しいVMに入れ替える。
ヘルスチェックの設定方法を説明します。
App Serviceのリソースにアクセスして、左部メニューの「Health Check」をクリックします。「正常性チェック」の「有効化」をチェックして、「パス」の部分にヘルスチェック用エンドポイントのパスを入力します。最後に「保存」をクリックします。これで完了です。