Microsoft Azure入門 - Web Appsを使って簡単にWebアプリやAPIを公開してみよう
Azureに入門するために、まずは手を動かしてみよう!数あるAzureのサービスの中からWeb Apps、Functions、SQL Databaseをチョイスし、これらを組み合わせ、簡単なWebアプリケーションやAPIのサンプルを作成・公開してみます。
- Web Apps、Functions、SQL Databaseそれぞれの特徴
- Web Apps/Functions/SQL Database
- Azureを利用するための準備をしよう
- 開発環境の準備/Azure サブスクリプションの作成
- Webアプリケーションを作成する
- Web API プロジェクトの作成/APIの確認/アプリケーションのデバッグ実行/アプリケーションをホストするWeb Appsの作成
- SQL Databaseと連携する
- データベースの作成/APIの追加/Functionsプロジェクトの作成/Functionsのデバッグ/SQL Databaseとの連携/Functionsの作成/Functionsへのデプロイ/リソースの削除
マイクロソフトが提供するクラウドサービス「Microsoft Azure」(以下、Azure)は、SaaSを含めたクラウド全般の領域において、データベースやAI、機械学習など、多岐にわたるサービスを提供しています。
今回はその中から、Azure Web Apps(以下、Web Apps)とAzure Functions(以下、Functions)、Azure SQL Database(以下、SQL Database)を組み合わせ、簡単なWebアプリケーションやAPIのサンプルを作成・公開してみましょう。
実際の業務におけるAzureを使ったWebアプリケーションの作成からSQL Databaseとの連携まで、概要を把握できるようチュートリアル形式で紹介していきます。用途に応じて柔軟に、かつ簡単にサービスを選択できるAzureの特性も交えます。
Web Apps、Functions、SQL Databaseそれぞれの特徴
Web Appsとは
Web AppsはWebアプリケーションやREST API1をホストするためのクラウドサービスです。WindowsはもちろんLinuxベースの環境も利用可能。開発には、.NET/Java/Ruby/Node.js/PHP/Pythonと、さまざまな言語を利用できます。
フルマネージドサービスであるため、ホスト環境の管理に気を遣う必要はありません。またセキュリティや負荷分散、自動スケーリングなどはサービスとして提供されているため、独自に実装することなく、設定を変更するだけで柔軟に構成できます。
Web App Service | Microsoft Azure
Functionsとは
Functionsとは、サーバーを意識することなく比較的小規模なプログラムを実行できる環境で、いわゆるサーバーレス実行環境と呼ばれるものです。前述したREST APIの実行ができますし、他のサービスと簡単に連携することもできます。対応言語についても、.NET、Java、Python、JavaScript、TypeScript などをサポートしています。
Azure Functions | Microsoft Azure
SQL Databaseとは
Azure上で提供されるリレーショナルデータベースで、オンプレミスで動作するSQL Serverのクラウド版です。もちろんフルマネージドサービスであるため、オンプレミスで運用していた際に必要だった煩雑な管理は必要ありません。設定によっては自動的にバックアップもできます。性能については購入モデルに応じてさまざまなパフォーマンスのデータベースを利用できますので、需要に応じて柔軟な構成を取れます。
SQL Database | Microsoft Azure
Azureを利用するための準備をしよう
本記事で必要な開発環境とAzure サブスクリプションについて説明します。
開発環境の準備
本記事では以下の環境を想定して進めていきます。.NETやVisual StudioがWindowsでしか動作しないフレームワークであったのは昔話です。.NET Core やVisual Studio Codeはいずれも複数プラットフォームで動作しますので、これらを利用します。したがって、本記事では特に動作OSについては限定しません。
- .NET Core 2.2
- 一部ローカルデータベースを利用する際はWindowsに限定されます
- .NET Core 3.0がリリースされていますが、執筆時点でAzure側が未対応であるためこのバージョンを利用しています
- Visual Studio Code 1.38.1 (執筆時点の最新板)
.NET Core フレームワークは、Download .NET Core 2.2 (Linux, macOS, and Windows) よりダウンロードできます。各プラットフォーム向けのインストーラーが用意されています。参考までに、Ubuntu 16.04 でのインストール例を紹介します。
以下のコマンドを実行して、Microsoftのキーとフィードを登録します。マシンごとに1回だけ実行します。
wget -q https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb
次にapt
コマンドで、リポジトリを追加しインストールします。
sudo apt-get install apt-transport-https sudo apt-get update sudo apt-get install dotnet-sdk-2.2
dotnet
コマンドを実行して情報が出力されれば、正しくインストールされています。
$ dotnet --info .NET Core SDK (reflecting any global.json): Version: 2.2.401 Commit: 729b316c13 ....
もし、.NET Core 3.0が表示される場合は、「 使用する .NET Core のバージョンを選択する - .NET Core | Microsoft Docs 」を参考にしてバージョンを変更してください。
Azure サブスクリプションの作成
Azureを利用するためには、「Azure サブスクリプション」を作成する必要があります。その前提としてMicrosoftアカウントが必要です。Microsoftアカウントは、マイクロソフトが提供するサービスを利用する場合には必ず使いますので、保有していない場合は以下のリンクを参考に作成してください。
新規のAzure サブスクリプションは以下のリンクから作成できます。作成から30日の間は、任意のAzureサービスを約2万円分、無償で利用可能です。さらにいくつかのサービスでは永続的に無償利用枠が設定されています。作成にはクレジットカードが必要ですが、無償期間を過ぎたからといって勝手に課金されることはありません。
Azure サブスクリプションを作成すると、自分自身のAzure サブスクリプションを管理するポータル画面にアクセスできるでしょう。下図のような画面が表示されれば正しく作成されています。ちなみに、複数のサブスクリプションを作成し、契約することもできます。
Azureのリソース作成にはポータル画面を利用するのがお手軽ではあるのですが、「手順が煩雑であり、間違いやすい」「繰り返し同じリソースを作成できない」などの欠点もあります。Azureではポータル画面以外に、コマンドラインインターフェースとしてAzure CLIやAzure PowerShell コマンドレットが提供されており、さらにはテンプレートベースのリソース作成も可能です。
初学者にとってはやや難しいかもしれませんが、早めに習得した方がスキルアップを目指せるということで、本記事ではAzure CLIを利用したリソース作成の手順を紹介していきます。
Webアプリケーションを作成する
ここから、Web Appsへデプロイするための実際のWebアプリケーションを作成していきます。作成するのは.NET Coreで動作するアプリケーションで、SQL Databaseへのアクセスを伴うREST APIを想定しています。
サンプルの概要図は以下の通りです。はじめにローカルでアプリケーションを作成し実行確認を行います。次に、実際にAzure上にWeb AppsとFunctions、SQL Databaseを作成し、アプリケーションをデプロイして、実行確認を行っていきます。
Web API プロジェクトの作成
dotnet
コマンドで.NET Core プロジェクトを作成します。引数に指定した new
は新規作成、webapi
は REST API形式のWebアプリケーションテンプレートを利用してプロジェクトのひな形の生成を意味します。単にdotnet new
コマンドを実行すると作成可能なテンプレートが表示されますので、確認してみてください。
$ dotnet new webapi -o sample-webapi テンプレート "ASP.NET Core Web API" が正常に作成されました。 作成後のアクションを処理しています... 'dotnet restore' を sample-webapi\sample-webapi.csproj で実行しています... c:\ws\sample-webapi\sample-webapi.csproj の復元が 3.17 sec で完了しました。 正常に復元されました。
正常に作成されるとsample-webapi
プロジェクトが作成されます。Visual Studio Codeでそのフォルダを開いてみましょう。実行パスが通っていれば、code sample-webapi
を実行するとVisual Studio Codeが起動します。
初めてC#のプロジェクトを開く場合、アプリケーションのビルドとデバッグに必要な拡張機能の追加を求められますので、その場合は「Yes」を選択してください。
画面左側の縦に表示されているアイコンが、各機能を切り替えるボタンになっています。Visual Studio Codeに拡張機能をインストールすれば、機能を追加できます。
APIの確認
このプロジェクトテンプレートでは、あらかじめ ValuesController.cs
というソースが生成されています。中身を確認していきましょう。
[Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value2" }; } // GET api/values/5 [HttpGet("{id}")] public ActionResult<string> Get(int id) { return "value"; // "value = " + id に書き換える } ... 中略 ... }
まず、このコントローラクラスとは個々のリクエストに応じた処理を行うクラスで、HTTPのリクエストに応じて対応するメソッドが呼び出されます。それらを定義するのが角括弧([]
)で囲まれた属性と呼ばれるコードです。この属性はクラスやメソッドに追加情報を与えます。
ここでまず注目したいのはApiController
とRoute
属性です。ApiController
はこのクラスがREST APIのコントローラクラスとして利用されることを指定しており、Route
属性はURLのルーティング情報を指定しています。この場合、api/Values/~
でAPIにアクセスできるようになります。
次にHttp
で始まる各属性は、HTTPリクエストメソッドに対応しており、呼び出しリクエストの種類によって振り分けられます。{id}
は追加のパスを取ることを表しており、api/values/123
のようにリクエストすると、id
に対応するパスの値が代入されます(ここでは123)。
Get()
メソッドでは、文字配列を返却していますが、これは結果的にJSONに変換されます。Get(int id)
は固定の文字列を返却していますが、こちらは単なるテキストで返却されます。ひな形で生成されたコードはid
を利用していませんが、コメントのように書き換えれば入力されたid
が返却され確認できるでしょう。
アプリケーションのデバッグ実行
まずはローカル環境でデバッグ実行してみましょう。画面左側にあるデバッグアイコン( )をクリックしてデバッグ画面にします。左上のデバッグするターゲットを.NET Core Launch(web)
にして隣の三角ボタンをクリックするとデバッグが始まります。
環境によっては、以下のメッセージとともに例外が発生する場合があります。その時は、dotnet dev-certs https
コマンドを実行し、開発用の自己署名証明書を設定してください。
Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found.
ブラウザが起動しますがルートページは存在しないので404エラーとなります。先ほどのRoute
属性の通り https://localhost:5001/api/values
に書き換えてアクセスします。自己署名証明書を利用しているため、安全でない旨のエラーが出ますが、ローカルホストですので無視してアクセスしてください。以下の画面が表示されれば正常に実行できています。
通常のデバッガですので、ブレークポイントの設定やスタックトレース、変数の参照などができます。デバッグを終了する際は右上の赤い四角ボタンをクリックしてください。
作成したアプリケーションをWeb Appsへデプロイ
それでは作成したアプリケーションをWeb Appsにデプロイしてみましょう。
アプリケーションをホストするWeb Appsの作成
はじめに、Azure上にアプリケーションをホストするWeb Appsを作成します。冒頭で説明したとおりAzure CLIで作成していきましょう。ブラウザベースの「Cloud Shell」を利用すれば、Azure CLIをローカルにインストールせずに使えます。
ブラウザ上部のシェルアイコン( )をクリックします。BashもしくはPowerShellベースのシェルにするか選択を促されるので「Bash」を選びます。
次にどのサブスクリプション上に作成するか質問されますが、先ほど作成したサブスクリプションを選択し、「ストレージの作成」をクリックします。このストレージは、Azure上に作成されるクラウドストレージを指します。ホームディレクトリなどがここに保存されます。
正常に起動すると以下の画面になります。
Azureのリソース作成はaz
コマンド を使っていきます。az
コマンドの書式は以下の通りです。名前には操作したいリソースの名前を指定し、コマンドにはどのような操作をしたいか選択します。例えば、新規作成や一覧、削除などです。次に詳細な引数と続きます。az -h
コマンドでヘルプが出ますので参考にしてください。
az 操作対象の機能名 コマンド サブコマンド 引数 ....
はじめにリソースグループを作成します。Azureではあらゆるリソースは、リソースグループと呼ばれるコンテナに属するルールとなっています。また、リソースグループはネストできません。
az group create
でリソースグループを作成します。引数にはリソースグループ名と作成するロケーション2を指定します。この例であれば「sample-reg」という名前のグループを「東日本」(japaneast)に作成します。
今後作成していくリソースは全てこのリソースグループ内に配置し、ロケーションは東日本を指定します。
$ az group create --name sample-rg --location japaneast { "id": "/subscriptions/xxxxxxxx-yyyy-eeee-zzzz-eeeeeeee/resourceGroups/sample-rg", "location": "japaneast", "managedBy": null, "name": "sample-rg", "properties": { "provisioningState": "Succeeded" }, "tags": null, "type": null }
次にApp Service プランを作成します。App Service プランとは、一言で言えばWeb AppsやFunctionsなどのクラウドサービスの総称で、これらのサービスのホスト環境となるものです。
App Service プランはアプリケーションを具体的にホストする環境として、課金額に応じたCPUやメモリなどを持ちます。CPUやメモリに応じて、フリープランからシェアードプラン、スタンダード、プレミアム、専有プランまで、幅広いワークロードに対応したプランが用意されています。
例えば、フリープランではWeb Appsを10個までホストできますが、1日あたり60分までのCPU使用時間までしか許されていないなどの制限があります。スタンダードプランでは、1コア CPU、RAM 1.75GBの仮想環境が1カ月あたり1万円で利用可能で、Web Appsは無制限にホストできます。
プラン別の機能と価格は以下を参考にしてください。
以下のコマンドでApp Service プランを作成します。Web AppsではWindowsまたLinuxの環境を利用できますが、ここでは --is-linux
を指定して明示的にLinux環境を指示します。指定しなければWindows環境となります。また--sku
では、開発環境向けの「B1」を指定します。
$ az appservice plan create --resource-group sample-rg --name sample-appplan --location japaneast --is-linux --sku B1
各プラン作成オプションの内容は以下の通りです。
オプション | 内容 |
---|---|
--resource-group | App Service プランを作成するリソースグループを指定 |
--nameApp | Service Plan名を指定 |
--location | ロケーションを指定(東日本) |
--is-linux | App Service プランの環境を指定 |
--sku | App Service プランの種類を指定。B1, B2, B3, D1, F1, FREE, P1V2, P2V2, P3V2, PC2, PC3, PC4, S1, S2, S3, SHARED から指定可能 |
App Service プランの作成が終わったら、以下のコマンドでWeb Appsを作成します。引数には先ほど作成したプラン名を指定し、名前にはWeb Appsの名前を指定します。名前はURLの一部となるため、Azureで一意とする必要があります。ランタイムには、.NET Core 2.2を利用します。
$ az webapp create --resource-group sample-rg --name sample-20190912 --plan sample-appplan --runtime "dotnetcore|2.2"
Web Apps作成オプションは以下の通りです。
オプション | 内容 |
---|---|
--resource-group | Web Appsを作るリソースグループを指定 |
--name | Web Appsの名前を指定 |
--plan | App Service プラン名を指定 |
--rutime | ランタイムスタックを指定。指定可能なランタイムスタックは、az webapp list-runtimes --linux で確認できる |
ポータル画面に戻り、左の「リソースグループ」をクリックすると、先ほど作成したsample-rg
が一覧に表示されています。さらにをクリックして作成されたリソースが表示されていれば問題ありません。
Web Appsにデプロイし実行
作成したWebアプリケーションをAzure上のWeb Appsにデプロイするにはいくつかの方法がありますが、ここではVisual Studio Codeの拡張機能から行います。Visual Studioの拡張機能画面から、Azure App Service
を検索しインストールしてください。インストール後、右側のアクティビティバーにAzureのアイコン( )が表示されるでしょう。
Azure拡張機能に切り替えたら Sign in to Azure ...
をクリックし、Azureに接続します。ブラウザが起動し、サインインを求められます。Azure サブスクリプションを作成したMicrosoftアカウントでサインインしてください。サインインが完了してから、先ほど作成したWeb Appsが表示されることを確認してください。
デプロイするための実行ファイル群をビルドするため、ターミナルを開きます。メニューから「ターミナル」―「新しいターミナル」を選択すると、画面下部でターミナルが起動するので、以下のコマンドを実行します。これでpublishという名前のフォルダに、アプリケーションのReleaseパッケージが作成されます。エクスプローラからpublish
ファルダが作成されていることを確認してください。
dotnet publish -c Release -o ./publish
次にpublish
フォルダを選択し、表示されたコンテキストメニューから「Deploy to Web App ...」を選択します。画面上部のコマンドパレットに先ほど作成したWeb Appが表示され、それを選択するとデプロイがスタートします。
画面の右下にデプロイ進行中の画面が表示されます。最終的にデプロイが完了すると、「Browse WebSite」ボタンが表示されるので、クリックして該当のWeb Appsにアクセスします。
先ほどと同様にURLを書き換えて以下のように表示されれば、正常にデプロイできています。
SQL Databaseと連携する
次にこのアプリケーションをSQL Databaseと連携していきましょう。
データベースの作成
具体的なデータベースを作成する前に、論理的なデータベースサーバーを作成します。サーバーには複数のデータベースを格納できます。引数の名前にはサーバー名を指定しますが、URLの一部になるためAzure上で一意となる必要があります。ユーザー名とパスワードは要件に合っていないとエラーが表示されるので、エラーが表示される場合は、メッセージに従って修正してください。
az sql serer create --resource-group sample-rg --name sample-sql-server --location japaneast --admin-user ******** --admin-password ********
SQLサーバー作成の引数は以下の通りです。
オプション | 概要 |
---|---|
--resource-group | SQL サーバーを作るリソースグループを指定 |
--name | SQL Database サーバー名を指定 |
--location | ロケーションを指定(東日本) |
--admin-user | SQL Databaseの管理者ユーザー名を指定。saやrootは指定できない |
--admin-password | SQL Databaseの管理者パスワードを指定。パスワード要件に満たない場合は作成時にエラーになる |
次にデータベースを作成します。データベースにもいくつかの作成オプションがあるのですが、ここでは価格の安い従量課金モデルを利用します。引数のサーバー名に先ほど作成したSQLサーバー名を、名前にはデータベース名を指定します。サービスレベルにはスタンダードとプレミアムを指定できますが、ここではS0
と呼ばれるスタンダードを指定します。
またキャパシティにはDTUと呼ばれる性能レベルを指定しますが、ここでは10を指定します。この性能サイズでは大量のデータをやりとりするには性能不足ではあるのですが、学習用途においては問題ない性能です。最後にサンプル名を指定しますが、これを指定するとデータベース作成時にサンプルデータをリストアしてくれるので、学習用途では便利です。
az sql db create --resource-group sample-rg --server sample-sql-server --name sample-db --service-objective S0 --capacity 10 --sample-name AdventureWorksLT
SQL Database作成の引数は以下の通りです。
オプション | 概要 |
---|---|
--resource-group SQL | データベースを作成するリソースグループを指定 |
--server | 先ほど作成したSQL サーバー名を指定 |
--name | データベース名を指定 |
--service-objective | サービスレベルを指定 |
--capacity | 性能レベル(DTU)を指定。値が大きいほど性能が高く、高価格となる |
--sample-name | あらかじめサンプルデータベースをリストアする。本記事ではサンプルデータベースを利用してクエリを実行 |
詳細は以下を参照してください。
これで SQL Databaseの作成は完了です。ただし、そのままではファイアウォールにより外部からアクセスできないので、以下のコマンドでルールを作成する必要があります。
$ az sql server firewall-rule create --resource-group sample-rg --server sample-sql-server --name azure --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0 $ az sql server firewall-rule create --resource-group sample-rg --server sample-sql-server --name myip --start-ip-address aaa.bbb.ccc.dddd --end-ip-address aaa.bbb.ccc.ddd
1行目では、開始と終了IPアドレスに0.0.0.0
を指定していますが、これはAzure内の他サービスからのアクセスを許可する指定方法です。例えば、Web Appから該当のSQL Databaseにアクセスする際などにこれが必要です。2行目は、例えば外からAzureへアクセスする場合などに直接IPアドレスを指定できます。ローカル環境からSQL Databaseにアクセスするため、お使いの環境のグローバルIPアドレスを指定してください。
ポータル画面から先ほど作成したSQL Databaseが確認できれば問題ありません。
APIの追加
アプリケーションからデータベースにアクセスする設定を行います。データベースに接続するには接続情報が必要です。
以下のコマンドで接続文字列と呼ばれる情報を取得します。サーバー名とデータベース名には先ほど作成したものを指定し、クライアントには接続するクライアントの接続種別を指定します。例えば.NET Coreからだと ado.net
を指定しますが、JavaのJDBCドライバから接続したい場合などは、jdbc
を指定すると、その形にあった接続文字列を返却してくれます。
ここで返却された文字列のUser ID
と Password
には、先ほど作成した時の情報を埋め込んでください(以降同様です)。
$ az sql db show-connection-string --server sample-sql-server --name sample-db --client ado.net "Server=tcp:sample-sql-server.database.windows.net,1433;Database=sample-db;User ID=<username>;Password=<password>;Encrypt=true;Connection Timeout=30;"
次にアプリケーションからデータベースにアクセスする部品を自動生成します。SQLのクエリを直接実行する実装方法もありますが、.NETの世界ではEntity FrameworkなどのO/Rマッパーを使うことが多いです。すでにデータベーススキーマが存在する場合、以下のコマンドでコードを自動生成できます。これによって、C#から透過的にデータベースへアクセスするクラス群が生成されます。1つはデータベースコンテキストクラスと呼ばれるデータベースへアクセスするのに必要な基点となるクラス、もう1つは各テーブルとカラムに対応したクラス群です。
引数には接続するデータベースを指定し、データベースコンテキストクラス名をSampleDbContext
に指定、さらにテーブルに対応して生成されるクラス群をModels
フォルダで生成することを指定します。
$ dotnet ef dbcontext scaffold "Server=tcp:sample-sql-server.database.windows.net,1433;Database=sample-db;User ID=<username>;Password=<password>;Encrypt=true;Connection Timeout=30;" Microsoft.EntityFrameworkCore.SqlServer -o Models -c SampleDbContext
appsettings.jsonを開き、以下のConnectionStrings
部分を追加します。これはSQL Databaseへの接続情報をアプリケーション中から利用するためです。
{ ... "ConnectionStrings": { "DefaultConnection": "Server=tcp:sample-sql-server.database.windows.net,1433;Database=sample-db;User ID=<username>;Password=<password>;Encrypt=true;Connection Timeout=30;" } }
次にStartup.cs
を開き、ConfigureServices
メソッドを以下に置き換えます。services.AddDbContext
メソッドでデータベースにアクセスするクラスを初期化します。SampleDbContext
クラスは、先ほども説明したデータベースをアクセスするために必要な基点となるクラスです。DefaultConnection
は、appsettings.json
と対応しており、該当の接続文字列を利用することを表しています。
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); // データベースへアクセスするクラスのDIを設定する services.AddDbContext<SampleDbContext>(options => { options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); }); }
Controller
フォルダに、DbController.cs
ファイルを追加し、以下で置き換えます。
using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using sample_webapi.Models; namespace sample_webapi.Controllers { [Route("api/[controller]")] [ApiController] public class DbController { private readonly SampleDbContext _dbContext; public DbController(SampleDbContext dbContext) { _dbContext = dbContext; } [HttpGet()] public async Task<JsonResult> Get() { // Customerテーブルから、最初の10件を取得する var result = await _dbContext.Customer.Take(10).ToListAsync(); // 取得結果をJSONにして返却する return new JsonResult(result); } } }
コンストラクターでSampleDbContext
を引数に取りますが、これはStartup.cs
で設定したデータベースにアクセスするクラスがDI(Dependency Injection、日本語では「依存性の注入」)によって設定されます。DIとは、利用したいクラスを自身でインスタンス化せずにフレームワークの設定にまかせることです。.NET Coreの作法では、このようなコンストラクターインジェクション3を使うのが一般的です。
Get()
メソッドには、SampleDbContext
クラスからCustomer
プロパティを参照していますが、このプロパティはCUSTOMER
テーブルに対応しており、次のTake(10)
メソッドで先頭の10件を取得しています。取得結果をJsonResult
クラスにラップすることで、取得した内容が明示的にJSON形式で結果が返却されます。
先ほどと同様にデバッグ実行してみます。https://localhost:5001/api/db
にアクセスしてください。以下のようにJSONの結果が返却されれば正常に実行できています。
Web Appにデプロイして実行してみましょう。https://sample-20190912.azurewebsites.net/api/db
にアクセスして結果が表示されれば、正しくWeb AppsからSQL Databaseに接続できています。
今回のサンプルでは、JSONファイルにデータベースの接続情報を埋め込みましたが、これらはWeb Appsの構成設定でも設定できます。さらには、秘匿すべき情報などに対してはAzure Key Vaultという仕組みがありますので、興味がある方は調べてみてください。
Functions アプリケーションの作成
Functionsはイベントドリブン型の実行環境です。イベントドリブン型とは、ユーザーの操作や他のプログラムを操作した結果(これらをイベントと総称します)を基点に、プログラムを実行する形式のことをいいます。Functionsではこのイベントに相当するものをトリガーと呼びます。
トリガーにはいくつか種類があり、HTTP呼び出しを基点とするHTTPトリガー、一定時間ごとにプログラムを実行するタイマートリガー、キューにメッセージが送信されたことを基点にするキュートリガーなどです。また、これらに紐付けされた入出力をバインディングと呼びます。ここではいくつか代表的なトリガーを紹介します。
トリガー | 内容 |
---|---|
HTTPトリガー | HTTP呼び出し(俗に言うWebHook)に対応して実行。一般的なHTTP呼び出し(GET/POST)や、クエリパラメータに対応する |
タイマートリガー | 一定間隔でFunctionsを実行。時間指定はCron式で指定可能 |
キュートリガー | Azure Storageサービスのキューに対応して実行。キューのメッセージがそのまま関数の入力となる |
他にも多数のトリガーが存在します。詳細は以下を参照してください。
ここでは、先ほどと同様の動作をするFuncitonsをHTTPトリガーで作成してみたいと思います。
Functionsプロジェクトの作成
以下のコマンドを実行して、Functionsプロジェクトおよび、HTTPトリガーのひな形となるクラスを生成します。最初のdotnet new func
でFunctionsプロジェクトを作成し、dotnet new http
でHTTPトリガーのひな形となるソースFunction.csを生成します。
$ dotnet new func -o sample-functions $ cd sample-functions $ dotnet new http -o Function
Visual Studio CodeでプロジェクトからFunctions.csを開きます。
public static class Function { [FunctionName("Function")] // (1) public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, // (2) ILogger log) // (3) { log.LogInformation("C# HTTP trigger function processed a request."); // name クエリから文字列を取得 string name = req.Query["name"]; // もしくは、POSTの場合リクエストボディから文字列を取得 string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); dynamic data = JsonConvert.DeserializeObject(requestBody); name = name ?? data?.name; // いずれかに指定されていれば Hello name を返し、いずれも指定されていないとBadRequestを返す return name != null ? (ActionResult)new OkObjectResult($"Hello, {name}") : new BadRequestObjectResult("Please pass a name on the query string or in the request body"); } }
生成されたひな型を確認しましょう。先ほどと同じようにいくつか属性が付与されていることが分かります。
(1)のFunctionsName
属性は名前を指定します。アクセスする際のパスの一部になります。引数に指定された(2)のHttpTriger
属性は、HTTPトリガーであることを表すと同時に、req
変数にリクエスト情報がバインドされて呼び出されます。AuthorizationLevel.Function
は、このHTTPトリガーを呼び出す時に匿名で許可するか、何らかの権限が必要かどうかを指定します。後述しますが、このレベルでは呼び出す際に認証コードが必要となります。
次のget
とpost
はこのHTTPトリガーが許可するHTTPの動詞となります。Route
にはルーティング情報やパスを指定することができます。ちなみに(3)のILogger
インターフェースは、いわゆるログ出力のクラスです。デバッグ情報などを出力したいときに利用できます。
ひな形で生成されたこのプログラムでは、name
クエリもしくはリクエストボディ(POSTの場合)から文字列を取得し、Helloと連結して結果を返却するプログラムとなっています。
Functionsのデバッグ
このまま実行してみましょう。デバッグ機能の画面にして、「Attach to .NET Functions」になっていることを確認してデバッグアイコン( )をクリックします。Functionsが起動し待機状態になります。
ターミナルにHTTPトリガーのURLが表示されるので、http://localhost:7071/api/Function?name=world
にアクセスして「Hello world」の文字が返却されれば問題ありません。
SQL Databaseとの連携
先ほどのWebアプリケーションと同様にSQL Databaseと連携してみましょう。dotnet add
コマンドを実行してライブラリへの参照を追加します。
$ dotnet add package Microsoft.NET.Sdk.Functions --version 1.0.29 $ dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 2.2.6 $ dotnet add package Microsoft.Azure.Functions.Extensions --version 1.0.0 $ dotnet add package Microsoft.Extensions.Configuration --version 2.2.0
次に先ほど生成したModels
フォルダを、本プロジェクトにコピーして再利用します。そしてStartup.cs
ファイルを作成し、以下で置き換えます。このクラスは、Functionsの実行に先立って呼び出されるスタートアップクラスです。ここでSQL Databaseの設定をしています。
using System; using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using sample_webapi.Models; [assembly: FunctionsStartup(typeof(Company.Function.Startup))] namespace Company.Function { class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { var config = builder.Services.BuildServiceProvider().GetRequiredService<IConfiguration>(); builder.Services.AddDbContext<SampleDbContext>( options => options.UseSqlServer(config.GetConnectionString("DefaultConnection")) ); } } }
最後にFunctions2.cs
ファイルを作成し以下で置き換えます。
using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using sample_webapi.Models; using System.Linq; using Microsoft.EntityFrameworkCore; namespace Company.Function { public class Function2 { private readonly SampleDbContext _dbContext; public Function2(SampleDbContext dbContext) { _dbContext = dbContext; } [FunctionName("Function2")] public async Task<JsonResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); // name クエリから文字列を取得 string name = req.Query["name"]; if (string.IsNullOrEmpty(name)) { return new BadRequestObjectResult("nameを指定してください。"); } // FirstNameカラムに指定された名前が含まれているものを取得する var result = await _dbContext.Customer.Where(x => x.FirstName.Contains(name)).ToListAsync(); // 取得結果をJSONにして返却する return new JsonResult(result); } } }
構成は先ほど説明したFunctions.cs
とほぼ同じですが、コンストラクターにSQL Databaseへアクセスするクラスを引数に取ります。これはDbController.cs
と同じ仕組みです。クエリは少し改良し、CustomerテーブルのFirstNameカラムに、name
クエリで指定された文字列が含まれているレコードを取得し、結果をJSONにして返却します。
デバッグ実行し、http://localhost:7071/api/Function2?name=tom
にアクセスして結果が返却されれば問題ありません。
Functionsへデプロイ
それでは作成したアプリケーションをFunctionsにデプロイしてみましょう。
Functionsの作成
Functionsには従量課金モデルとApp Service プランを利用する課金モデルがあります。従量課金モデルでは関数の実行中にのみ課金され、自動でスケールアウトできるなど、いわゆるサーバーレスといえるプランでが、App Service プランを利用するモデルでWeb Appsと同じようにホストすることもできます。この場合はApp Service プランの環境に依存して課金されます。
ここでは、先ほど作成したApp Service プランを利用してFunctionsを作成していきます。
$ az storage account create --resource-group sample-rg --name samplestorage20190912 --location japaneast --kind storage --sku standard_lrs $ az functionapp create --resource-group sample-rg --plan sample-appplan --name sample-functions20190912 --runtime dotnet --storage-account samplestorage20190912 $ az webapp config connection-string set --resource-group sample-rg --name sample-functions20190912 --connection-string-type SQLAzure --settings DefaultConnection="Server=tcp:sample-sql-server.database.windows.net,1433;Database=sample-db;User ID=<user>;Password=<password>;Encrypt=true;Connection Timeout=30;"
はじめに、az storage account create
コマンドでストレージを作成します。Functionsでは永続的な保存領域が必要なためです。引数の名前にストレージ名を指定しますが、URLの一部となるためAzureで一意となる必要があります。種別はいくつか種類があるのですが汎用的なストレージを指定します。SKUにはジオレプリケーションの設定をしますが、特にデータセンター間で複製する必要はないため、ローカルで冗長化されるstandard_lrs
を指定しておきます。開発用途ではこれで問題はありません。
次にaz fucntionapp create
コマンドでFunctionsを作成します。App Service プランには作成済みのものを指定します。名前はURLの一部となるためAzureで一意となる必要があります。ランタイムにはdotnet
を指定し、ストレージには先ほど作成したものを指定します。
最後に、az webapp config connection-string
でSQL Databaseへの接続文字列を設定します4 。Web Appsではapplicaionsettings.json
にSQL接続文字列を埋め込みましたが、Functionsではローカルでこそファイルに定義できるものの、Azure上では構成情報を設定をしておく必要があるためです。App Service プラン名と名前には先ほど作成したものを、文字列のタイプには、SQL Databaseであることを示すSQLAzure
を指定し、設定値には接続文字列を設定します。
作成されたFunctionsをポータル画面で確認します。
Functionsへのデプロイ
先ほどと同じくVisual Studio Codeからデプロイしていきます。エクスプローラのコンテキストメニューから「Deploy to Function App ...」を選択すると、どのFunctionsにデプロイするか候補が表示されるので、作成したものを指定してください。
しばらくするとデプロイが完了します。Functionsの管理画面で関数一覧にFunctions
とFunctions2
が表示されれば、正常にデプロイされています。デプロイ後、ポータルで確認できるまで少しだけ時間がかかる場合があるのでご注意ください。
それでは、デプロイされたFunctionsを呼び出してみましょう。Functions
を選択すると、上部に「関数のURLの取得」リンクが表示されるので、クリックしてURLをコピーしてください。既定ではFunctionsを呼び出すためには認証キーが必要なので、認証キー付きでコピーされます。それにクエリを付加して、ブラウザで実行してみてください。同様の方法で、Functions2
も実行できます。
https://sample-functions20190912.azurewebsites.net/api/Function?code=認証キー&name=world
リソースの削除
今回作成したリソースは、全てsample-rg
というリソースグループ内に作成されていますので、az group delete
コマンドで一括して削除します。
az group delete --name sample-rg
またクラウドシェルを利用することで、自動的にStorageアカウントが作成されていますが、これについても同様のコマンドで削除可能です。cloud-shell
で始まるリソースグループを削除してください。ただし、クラウドシェルを開くと、再度Storageアカウントが作成されますので、注意してください。
まとめ
駆け足でしたが、Azureを利用したWeb AppsとFunctions、さらにはSQL Databaseとの連携方法を解説してきました。簡単にWebアプリケーションやAPIを公開できることを理解していただけたでしょうか。今回は、.NET Coreをベースとしてアプリケーションをデプロイしましたが、Azureの各種サービスは多くの言語に対応しています。ぜひ、得意な言語でもチャレンジしてみてください。
著者プロフィール
森島 政人 (もりしま・まさひと)
監修:WINGSプロジェクト 山田 祥寛
WINGS @yyamada WINGSプロジェクト
*1:Web APIとも呼ばれ、HTTPベースで呼び出されるインターフェース。Web上に公開されているさまざまなサービスについて、外部から呼び出し利用されるもの。
*2:リージョンとも呼ばれます。全世界20以上の場所を指定できます。az account list-locations --output table すると一覧が取得できます。
*3:コンストラクタに必要なクラスが渡されるDIのこと。他に、フィールド変数に設定されるフィールドインジェクションという方法もあり、利用するフレームワークによって異なります。
*4: az functionapp config connection-string があるべきなのですが、現在用意されていないので、このコマンドで代替します。
Web APIとも呼ばれ、HTTPベースで呼び出されるインターフェース。Web上に公開されているさまざまなサービスについて、外部から呼び出し利用されるもの。↩
リージョンとも呼ばれます。全世界20以上の場所を指定できます。az account list-locations --output table すると一覧が取得できます。↩
コンストラクタに必要なクラスが渡されるDIのこと。他に、フィールド変数に設定されるフィールドインジェクションという方法もあり、利用するフレームワークによって異なります。↩
az functionapp config connection-string があるべきなのですが、現在用意されていないので、このコマンドで代替します。↩