エンジニアHubPowered by エン転職

若手Webエンジニアのための情報メディア

Microsoft Azure入門 - Web Appsを使って簡単にWebアプリやAPIを公開してみよう

マイクロソフトが提供するクラウドサービス「Microsoft 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 API*1をホストするためのクラウドサービスです。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ではポータル画面以外に、コマンドラインインターフェースとしてAzure CLIAzure 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 初期画面

画面左側の縦に表示されているアイコンが、各機能を切り替えるボタンになっています。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のリクエストに応じて対応するメソッドが呼び出されます。それらを定義するのが角括弧([])で囲まれた属性と呼ばれるコードです。この属性はクラスやメソッドに追加情報を与えます。

ここでまず注目したいのはApiControllerRoute属性です。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.

Visual Studio Codeのデバッグ画面

ブラウザが起動しますがルートページは存在しないので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が表示されることを確認してください。

Azure拡張機能

デプロイするための実行ファイル群をビルドするため、ターミナルを開きます。メニューから「ターミナル」―「新しいターミナル」を選択すると、画面下部でターミナルが起動するので、以下のコマンドを実行します。これでpublishという名前のフォルダに、アプリケーションのReleaseパッケージが作成されます。エクスプローラからpublishファルダが作成されていることを確認してください。

dotnet publish -c Release -o ./publish

次にpublishフォルダを選択し、表示されたコンテキストメニューから「Deploy to Web App ...」を選択します。画面上部のコマンドパレットに先ほど作成したWeb Appが表示され、それを選択するとデプロイがスタートします。

デプロイの選択ダイアログ

画面の右下にデプロイ進行中の画面が表示されます。最終的にデプロイが完了すると、「Browse WebSite」ボタンが表示されるので、クリックして該当のWeb Appsにアクセスします。

先ほどと同様にURLを書き換えて以下のように表示されれば、正常にデプロイできています。

Web Appsへのアクセス画面

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が確認できれば問題ありません。

SQL Database作成後

APIの追加

アプリケーションからデータベースにアクセスする設定を行います。データベースに接続するには接続情報が必要です。

以下のコマンドで接続文字列と呼ばれる情報を取得します。サーバー名とデータベース名には先ほど作成したものを指定し、クライアントには接続するクライアントの接続種別を指定します。例えば.NET Coreからだと ado.netを指定しますが、JavaのJDBCドライバから接続したい場合などは、jdbcを指定すると、その形にあった接続文字列を返却してくれます。

ここで返却された文字列のUser IDPasswordには、先ほど作成した時の情報を埋め込んでください(以降同様です)。

$ 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を開きます。

Funcstionsプロジェクト

    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トリガーを呼び出す時に匿名で許可するか、何らかの権限が必要かどうかを指定します。後述しますが、このレベルでは呼び出す際に認証コードが必要となります。

次のgetpostはこのHTTPトリガーが許可するHTTPの動詞となります。Routeにはルーティング情報やパスを指定することができます。ちなみに(3)のILoggerインターフェースは、いわゆるログ出力のクラスです。デバッグ情報などを出力したいときに利用できます。

ひな形で生成されたこのプログラムでは、nameクエリもしくはリクエストボディ(POSTの場合)から文字列を取得し、Helloと連結して結果を返却するプログラムとなっています。

Functionsのデバッグ

このまま実行してみましょう。デバッグ機能の画面にして、「Attach to .NET Functions」になっていることを確認してデバッグアイコン( )をクリックします。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管理画面

Functionsへのデプロイ

先ほどと同じくVisual Studio Codeからデプロイしていきます。エクスプローラのコンテキストメニューから「Deploy to Function App ...」を選択すると、どのFunctionsにデプロイするか候補が表示されるので、作成したものを指定してください。

Functionsへのデプロイ

しばらくするとデプロイが完了します。Functionsの管理画面で関数一覧にFunctionsFunctions2が表示されれば、正常にデプロイされています。デプロイ後、ポータルで確認できるまで少しだけ時間がかかる場合があるのでご注意ください。

Functionsのデプロイ後

それでは、デプロイされたFunctionsを呼び出してみましょう。Functionsを選択すると、上部に「関数のURLの取得」リンクが表示されるので、クリックしてURLをコピーしてください。既定ではFunctionsを呼び出すためには認証キーが必要なので、認証キー付きでコピーされます。それにクエリを付加して、ブラウザで実行してみてください。同様の方法で、Functions2も実行できます。

https://sample-functions20190912.azurewebsites.net/api/Function?code=認証キー&name=world

関数のURL取得

リソースの削除

今回作成したリソースは、全て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プロジェクト所属のライター。Microsoft Azure サービスイン当初からAzureを利用しつづけるソフトウェア開発者。Microsoft MVP for Micrsoft 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 があるべきなのですが、現在用意されていないので、このコマンドで代替します。