はじめに
ARM(Azure Resource Manager)
テンプレートの存在は以前から知っていたのですが、最近になってARMテンプレートをさらに見やすく・扱いやすくしたBicepの存在を知りました。Bicepは最近アツくなってきているらしく、興味を持ったので勉強したことを本記事にまとめたいと思います。本記事の内容がより実用的な内容になるように、Bicepを使ってAzureリソースをデプロイするだけではなく、ReactのサンプルWebアプリをAzure DevOps
を使って自動デプロイするところまでまとめました。
アーキテクチャ
Azure Static Web Appsとは
本記事のメインであるBicepを紹介する前に、Azure Static Web Appsとは何かについて先に説明をしたいと思います。Azure Static Web Apps
はSPA(Single Page Application)
用のAzureリソースであり、静的サイトホスティング環境です。Angular、React、Svelte、Vue、Blazor など、サーバー側のレンダリングが不要なライブラリとWebフレームワークを使用することができます。GitHubやAzure DevOps
との連携が容易であり、簡単な設定によりCI/CDを組むことが可能です。
Azure Static Web Apps
自体は静的Webアプリのホスティングサービスですが、Azure Functions
と組み合わせることで簡単に動的Webアプリを構築することも可能です。Azure Static Web Apps
とAzure Functions
を組み合わせて動的なWebアプリを作りたい方は、以下の記事をご覧ください。
Bicepとは
本記事のメインコンテンツであるBicepについて説明したいと思います。Bicepとは、Azureリソースをデプロイするための、宣言型構文のドメイン固有言語(DSL)です。 要するに、Azure用のコードとしてのインフラストラクチャ(IaC: Infrastructure as Code
)になります。
コードとしてのインフラストラクチャ(IaC)とは?
ネットワーク、仮想マシン、ロードバランサー、接続トポロジ、DevOpsの手法とバージョン管理などのインフラストラクチャを定義してデプロイするためのコードであり、デプロイするたびに同じ環境を生成します。
https://learn.microsoft.com/ja-jp/devops/deliver/what-is-infrastructure-as-code
BicepとARMテンプレートの違い
Bicepと似たものとして、ARMテンプレートがあります。ARMテンプレートとは、プロジェクトのインフラストラクチャと構成が定義されているJSONファイルです。ARMテンプレートのJSONは冗長になることがありますが、Bicepではその複雑さが軽減されます。同じストレージアカウントをデプロイするためのBicepファイルとARMテンプレートを以下の実際のコードで比較しましょう。
param location string = resourceGroup().location
param storageAccountName string = 'toylaunch${uniqueString(resourceGroup().id)}'
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
accessTier: 'Hot'
}
}
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"storageAccountName": {
"type": "string",
"defaultValue": "[format('toylaunch{0}', uniqueString(resourceGroup().id))]"
}
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-06-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {
"accessTier": "Hot"
}
}
]
}
Bicepファイルの方がARMテンプレートよりも、簡潔にインフラ構成を記述できていることが確認できたと思います。
Bicepでは同等のJSONと比較して、より簡単で簡潔な構文を提供できるため、MicrosoftはARMテンプレートよりも、改良版言語であるBicepの使用を推奨しています。
BicepとARMテンプレートの関係性
BicepをコンパイルするとARMテンプレートになり、ARMテンプレートをデコンパイルするとBicepになります。この関係性を図式化すると以下になります。
IaCと非テンプレートIaCの違い
Azureのリソースを作成するために例えば、以下のようにAzure CLIを使うことも可能です。
az staticwebapp create \
--name my-first-static-web-app \
--resource-group my-swa-group \
--source https://github.com/$GITHUB_USER_NAME/my-first-static-web-app \
--location "eastus2" \
--branch main \
--app-location "src" \
--login-with-github
Azure CLI
での命令をシェルスクリプト化(非テンプレートIaC)して手続き的にリソースをデプロイすることも可能ですが、通常のIaCを使用する方が様々な理由からメリットが大きいです。そのうちの1つの理由として挙げられるのが、通常のIaCであれば命令コマンドの回数を1回で済ませられることです。下の図では、IaC(図ではARMテンプレート)と非テンプレートIaCの命令コマンドの回数違いが示されています。
ARMテンプレートのようなIaCでは、相互に依存するリソースのデプロイが調整され、正しい順序で作成されるので複雑な順序付けのことを心配する必要はなく、1つの命令コマンドでデプロイは完了する並列デプロイになります。一方で非テンプレートIaCでは、順番を考慮した上で複数の命令コマンドを行う必要性があるため、直列デプロイになります。並列デプロイは直列デプロイに比べ、短時間でデプロイが完了する点でも優れているデプロイ方法になります。
Bicepファイルの作成方法
ここでは、VSCodeを使ってBicepファイルを作成する方法を紹介します。
Bicepの拡張機能をインストール
まずはVSCodeにBicep拡張機能をインストールしましょう。
Bicep拡張機能の機能
Bicepの拡張機能には主に以下のような機能があります。Bicepファイルの作成においては、スニペットが特に強力な機能になります。
- バリデーション
- スニペット
- コードナビゲーション
- 定義
- 定義のピーク
- ハイライト
- リファクター
- フォーマッティング
リソースの作成
Bicep拡張機能をインストールしたら、VSCodeで任意のファイル名のBicepファイルを展開してください。そこで、作成したいリソースを入力してください。以下のスクリーンショットの例では、storage
と入力しました。するとリストが表示されるので、res-storage
を選択してEnter
キーを入力しました。
すると、一瞬で次のコードが追加されます。このように、Bicep拡張機能のスニペット機能を使用することで、一瞬で作成したいリソースを含むBicepファイルが作成できました。
今回紹介したBicep拡張機能のスニペット機能は強力ですが、全てのリソースに対応しているわけではありません。Azure Static Web Apps
のように登録されていないソースコードは公式ドキュメントを参照して確認しましょう。今回はこちらの記事でまとめられているAzure Static Web Apps
のBicepテンプレートを参考にしました。
Bicepファイルの作成について、詳しくは以下の公式ドキュメントが参考になります。
サンプルアプリをデプロイして実行するまでの流れ
ここから、次の流れでサンプルアプリをAzure上で実行するところまでの手順をまとめたいと思います。
1. アプリ開発
まず最初の工程としてWebアプリを開発しましょう。アプリ開発は本記事の目的ではないため、以下のリポジトリに保存されているReactのWebアプリを使用させていただきました。このWebアプリは、絵文字検索ができるアプリになります。
2. ローカルでの実行
先ほどご紹介したサンプルアプリをローカルで実行するために、お好きなシェルを開いてnpm start
を実行します。その後、ブラウザでhttp://localhost:3000
にアクセスすれば以下のスクリーンショットのような画面が表示されます。
このようにローカルでは、適切にアプリが動くことが確認できました。
3. Azureリソースのデプロイ
ローカルで動いているWebアプリをデプロイするために、Azureリソースのデプロイを事前に行いましょう。
Bicepファイルの作成
本記事のタイトルに含まれているBicepをここから使っていきます。Bicepを使うことで、簡単にAzureリソースのデプロイを行うことができます。今回は以下のBicepファイルを使用しました。repositoryUrl
は適宜自分のリポジトリを指定してください。
param appName string = 'bicep-practice-app'
param location string = 'eastasia'
param skuName string = 'Free'
param skuTier string = 'Free'
param provider string = 'DevOps'
param repositoryUrl string = 'https://github.com/yus04/React-emoji-search'
param branch string = 'main'
resource staticWebApp 'Microsoft.Web/staticSites@2022-03-01' = {
name: appName
location: location
sku: {
name: skuName
tier: skuTier
}
properties: {
provider: provider
repositoryUrl: repositoryUrl
branch: branch
}
}
リソースグループの作成
Azureポータルから任意のリソースグループ名、リージョンでリソースグループを作成します。
Azure Static Web Appsのデプロイ
先ほど作成したリソースグループの中で、BicepとAzure CLI
を使用してAzure Static Web Apps
をデプロイします。"provisioningState": "Succeeded"
と返ってきたらデプロイは成功です。
az deployment group create \
--name ExampleDeployment \
--resource-group ExampleGroup \
--template-file <path-to-bicep> \
Bicepファイルをデプロイするときに、--name
でデプロイに名前を付けることができます。
BicepとAzure CLI
を使用したリソースのデプロイについては、以下の記事が参考になります。
Azureポータルを開き、静的Webアプリが作られていることが確認できます。次のスクリーンショットの赤枠で囲まれた部分がWebアプリのURLになっているので、コピーしてブラウザでアクセスしてみましょう。
ブラウザで次のような画面が表示されれば、Azure Static Web Apps
のデプロイは成功です。
4. アプリのデプロイ
次はローカルで動いているWebアプリをAzure Static Web Apps
にデプロイしていきましょう。本記事ではAzure DevOps
を使って、WebアプリのソースコードをGitHubリポジトリのmainブランチへプッシュした時に自動ビルド&自動デプロイするCI/CD環境を構築します。
パイプラインの定義
Azure DevOps
のPipelinesを使います。以下のazure-pipelines.yaml
ファイルを使用しました。
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: AzureStaticWebApp@0
name: DeployStaticWebApp
displayName: Deploy Static Web App
inputs:
app_location: /
output_location: build
env:
azure_static_web_apps_api_token: $(deployment_token)
yamlファイルのsteps.task
で使用されているAzureStaticWebApp@0
はAzure Static Web Apps
アプリをビルド&デプロイするタスクになります。このタスクについては以下の公式ドキュメントで詳しく説明されています。
パッケージファイルの準備
以下のjsonファイルを使用しました。
{
"name": "emoji-search",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.8.3",
"react": "^17.0.2",
"react-copy-to-clipboard": "^5.0.3",
"react-dom": "^17.0.2",
"web-vitals": "^1.1.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"prettier": "^2.2.1",
"react-scripts": "^4.0.3"
}
}
先ほどのazure-pipelines.yaml
のsteps.task
に記述されているAzureStaticWebApp@0
が実行されることで、scripts.build
に記述されているreact-scripts build
が実行されます。
GitHubリポジトリの作成
GitHubリポジトリを作成し、ソースコードをプッシュします。main.bicep
とazure-pipelines.yaml
を作成し、最終的なフォルダ構成は以下になりました。
.
├─ build
│ └─ ...
├─ node_modules
│ └─ ...
├─ public
│ └ ...
├─ src
│ └ ...
├─ .gitignore
├─ azure-pipelines.yaml
├─ main.bicep
├─ package-lock.json
├─ package.json
└─ README.md
それぞれのディレクトリやファイルの解説は次の通りになります。
-
build/
-
npm run build
を実行したときに生成される本番向けのビルド済みアプリを格納するディレクトリ
-
-
node_modules/
- Node.jsで使用するパッケージをインストールするディレクトリ
-
package.json
に記述されたパッケージ名とバージョンを元に、npm install
コマンドでインストールされる
-
public/
- 公開される静的ファイルを格納するディレクトリ
-
src/
- アプリケーションのソースコードを格納するディレクトリ
-
.gitignore
- Gitで管理するファイルのうち、バージョン管理しないファイルを指定するためのファイル
-
azure-pipelines.yaml
-
Azure Pipelines
でビルドやデプロイを行うための設定ファイル
-
-
main.bicep
- Azureリソースを定義するためのBicepファイル
-
package-lock.json
-
node_modules/
の構造やpackage.json
が修正または生成される際の依存性を正確かつ具体的な情報を記載するファイル -
npm install
コマンドでインストールされたパッケージのバージョンを固定するために使用
-
-
package.json
- Node.jsにおいてインストールするパッケージが記述されたJSONファイル
- パッケージ管理の設計書なので、異なる環境でも読み込むことで同じパッケージをインストール可能
-
README.md
- プロジェクトの説明や使い方などが記述されたファイル
build
及びnode_modules
フォルダとその中身、およびpackage-lock.json
フォルダをリポジトリに保存したくないため、以下の通りに.gitignore
ファイルに記載しました。
build/
node_modules/
package-lock.json
パイプラインの作成
次に、GitHubリポジトリに保存されているazure-pipelines.yaml
を使って、Azure DevOps
のPipelinesを作成します。まず、Azure DevOps
のPipelines画面を開いて、「Create Pipeline」を押します。
ソースコードの保存元として「GitHub」を選択します。
リポジトリを選択します。
GitHubに保存されているazure-pipelines.yaml
を使ってパイプラインを定義したいので、「Existing Azure Pipelines YAML file」を選択します。
ブランチを指定した後で、パスとして「/azure-pipelines.yaml」を選択します。
以上で、パイプラインの作成ができました。
環境変数の登録
azure-pipelines.yaml
のenv
で使われている環境変数deployment_token
の設定をAzure DevOps
で行います。まず、次のスクリーンショットの通りに「Edit」を選択します。
「Variables」を選択します。
「New Variable」を選択します。
Nameをdeployment_token
、ValueをAzureポータルでコピーしたデプロイトークンにします。デプロイトークンの取得方法は次に紹介します。NameとValueを入力と「Keep this value secret」のチェックをしたら「OK」ボタンを押下します。
デプロイトークンの取得
Azureポータルを開いて、Bicepでデプロイ済みの静的Webアプリの概要画面を開きます。次のスクリーンショットの赤枠で囲まれている「デプロイトークンの管理」を押下します。
すると、デプロイトークンが表示されるので右のアイコンを押下してクリップボードにコピーすることで、デプロイトークンを取得できます。
アプリのビルドとデプロイの実行
以上でアプリのデプロイの準備が完了しました。アプリのビルドとデプロイを行っていきましょう。Pipelinesの画面右上にある、「Run pipeline」を押します。
「Run」を押します。
Pipelinesの画面では、パイプラインで定義したタスクが実行されているのをリアルタイムで確認することができます。次のスクリーンショットのように、Jobに緑色のチェックマークがつけば、エラーなくアプリのビルドとデプロイが完了したことを意味しています。
5. クラウドでの実行
AzureポータルのAzure Static Web Apps
の概要画面に表示されているURLにブラウザでアクセスしてみて、次のスクリーンショットのような画面が表示されれば、無事にReactのサンプルアプリがAzure Static Web Apps
にデプロイできました。
不要になったリソースは、Azure CLIを使ってリソースグループごとクリーンアップしましょう。
az group delete --name exampleRG
おわりに
BicepとAzure DevOps
を使って簡単にAzure Static Web Apps
にReactのサンプルアプリをデプロイすることができました。もちろん、Azureポータルを使ってリソースを作成することは簡単なのですが、VSCodeのBicep拡張機能を使うことで簡単にBicepファイルを作成することができるため、Bicepによるリソース作成も容易であることに感動しました。初めはとっつきにくかったBicepでしたが、今後は積極的に利用していきたいと思います。