Azureに入門する話

はじめに

こんにちは、エンジニアの加藤です。
担当するサービスの変更で、使うクラウド環境がAWSからAzureへ切り替わりました。
アプリケーションの開発・運用保守が主なので、インフラの知識が直ぐに必要というわけではないですが、知らないと困る部分もありますので、少しずつ勉強していく予定です。
この記事では、AWSで開発していたエンジニアがAzureを学習してみた内容をお伝えします。
※ 学習した内容の紹介という主旨のため、こうやってみたが主であり、この記事の内容で作業するのがベストな手順や設定というわけではないこと、あらかじめご留意ください。

やること

実際に使ってみるのが理解への近道ということで、Azure環境に何かを作ってみたいと思います。
AWSを使っていたときは、S3 + Lambda + APIGW + DynamoDB + CloudFront で社内向けのミニWebツールを作ることがしばしばありました。
今回はAzure環境でこれと同じことができるかを試したいと思います。

具体的な目標

AWSを使っていたときに試しに作った自分のチーム向けの簡易的なスケジュール管理WebアプリケーションをAzureでも作りたいと思います。
ただ、学習時間が限られていてLambda + DynamoDB向けに作ったコードを
Azure Functions + Azure Cosmos DB 向けに書き直すといった、APIのコード修正までやるのは難しそうなので、今回は簡易的な動作確認までやることを目標とします。
具体的には、Azure Storageを使ってWebページの画面を作る、Azure Functionsで作ったAPIを呼び出してAzure Cosmos DBに登録されているデータを取得し、画面に表示するという一連の流れを作ります。

サービスのセットアップとコードデプロイ

Azure Cosmos DB

事前に体系的に学習をしたのですが、VNetやVMの作成方法が多く、Azure Cosmos DBについての内容はあまり得られなかったので別途調べました。
とにかく一番安くなりそうな設定にして作成します。

AS-image-1

この設定であれば、1日あたり300リクエストもしないので、月額100-300円に収まるはずです。
手探りで、クイックスタートからNode.jsを適当に選択したらToDoListというコンテナが自動生成されていました。
AS-image-2

今回はこのコンテナをそのまま使ってしまおうと思います。
テストデータとして手作業で2つほどデータを入れておきました。
AS-image-3
ここまでで動作確認用としては立ち上げ完了ですね。
※ 今回はサーバーレスモードを選択していますが、プロビジョニングを選択すると固定で3000円~が月額で発生するみたいなので注意しましょう。
また、DBへのアクセスを社内からに限定したかったので、ネットワークの設定から社内のIPからのアクセスだけ許可するように変更しました。
AS-image-4

Azure Functions

Azure Portalの関数アプリから、hello-azure-1という名前のFunctionを作成しました。
ここにコードをデプロイできれば良いはずです。

AS-image-5

次に、ローカルのPC上に開発環境を作ります。
npm install -g azure-functions-core-tools@4 --unsafe-perm true
※ バージョン4を入れないとNode.jsのv20で動かないです。

このコマンドでAzure Functionsの開発をするためのパッケージがインストールされるので、 適当なフォルダでAzure Functionsのプロジェクトフォルダを生成します。
func init {MyFunctionApp} --javascript
作成したフォルダの中に移動して、HTTPリクエストで動く処理を生成します。
func new --template "HTTP trigger" --name {HttpTriggerFunction}
HttpTriggerFunctionフォルダができあがり、この中に生成されるindex.jsがエントリーポイントになるので、ここにコードを書きます。
デフォルトで動作確認できるコードが書いてあるので、一度スタートコマンドでスタートさせて、localhostのエンドポイントを叩いて動作確認することもできます。
func start
コードを書き換えて、再度 func start し、ローカルで動作確認ができたら、Azureへデプロイします。
以下のコードは、Azure Cosmos DBにあるデータを全部取ってきて返すだけのシンプルなものです。
Azure Cosmos DBへのアクセスのためにパッケージを追加しています。
npm install @azure/cosmos

const { CosmosClient } = require("@azure/cosmos");

module.exports = async function (context, req) {
    const endpoint = "{CosmosDBのページから取得したエンドポイント}";
    const key = "{CosmosDBのページから取得したキー情報}";
    const client = new CosmosClient({ endpoint, key });

    const databaseId = "ToDoList"; // DBのIDを指定
    const containerId = "Items"; // コンテナIDを指定

    const database = client.database(databaseId);
    const container = database.container(containerId);

    try {
        const { resources: items } = await container.items.query("SELECT * from c").fetchAll();
        context.log('Data retrieved successfully.');
        context.res = {
            status: 200,
            body: items
        };
    } catch (error) {
        context.log.error('Error retrieving data:', error);
        context.log.error('Error details:', error.body);
        context.res = {
            status: 500,
            body: "Error retrieving data from CosmosDB"
        };
    }
};

localhostでの動作確認は以下の画像のようになります。

AS-image-6

AzureのCLIを導入していなかったので、公式サイトからダウンロードしてインストールします。
https://learn.microsoft.com/ja-jp/cli/azure/install-azure-cli-windows?tabs=azure-cli
こちらをインストールした後、コマンドプロンプトやターミナルを再起動してログインコマンドを打つ。
az login
※ 社内の設定で、複数テナントと複数のサブスクリプションの設定があり、私の環境ではテナントを指定してログインしたあとで、デプロイする先のサブスクリプションも選択する必要があったので、追加でコマンドが必要でした。これは人によると思います。

az login --tenant {テナントID}
az account set --subscription {サブスクリプションID}

ログインができたら以下を実行することで、デプロイができます。
func azure functionapp publish {デプロイ先のファンクション名、今回はhello-azure-1}
エンドポイントがどんな感じになるのかわからなかったのですが、こうなりました。
https://hello-azure-1.azurewebsites.net/api/HttpTriggerFunction
ただ、これにリクエストしても401になってしまって、データ取得できなかったので、調べてみたところ、デフォルトでアプリキーをパラメータcodeにセットしてリクエストしないと認証が通らないようになっているようでした。
以下のように変更したところ正常にレスポンスを受け取れました。
https://hello-azure-1.azurewebsites.net/api/HttpTriggerFunction?code={アプリキー}
アプリキーがあるので、このままでも良いのですが、一応社外からのアクセスをブロックしておきたいので、IP制限も設定しておきました。

AS-image-7

Azure Storage

Azure Portalからストレージアカウントを作成しました。

AS-image-8

左のメニューのデータ管理の中に、静的なWebサイトというメニューがあるので、そこに表示されるボタンから有効に設定し、インデックスとエラーのドキュメントパスを両方index.htmlへ指定しておきます。
AS-image-9

セキュリティとネットワークの中にネットワークがあるので、ここでも選択した仮想ネットワークとIPアドレスから有効を選択して、自分のIPアドレスや会社のVPNを指定しておきます。
AS-image-10

あとはデータストレージの中のコンテナーから、自動生成された$webフォルダの中にindex.htmlをアップロードすればOKです。

index.html は動作検証用なので以下のように作成しました。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello Azure</title>
</head>
<body>
    <div>
        <h1>Hello Azure</h1>
        <p id="result"></p>
    </div>
    <script>
        const functionUrl = "{Azure FunctionsのエンドポイントURL}";

        // Azure Functionsを呼び出す関数
        async function callAzureFunction() {
            console.log("request");
            try {
                const response = await fetch(functionUrl, { mode: 'cors' });
                if (!response.ok) {
                    console.log("not OK");
                    throw new Error('Network response was not ok ' + response.statusText);
                }
                const data = await response.json();
                document.getElementById('result').textContent = JSON.stringify(data);
            } catch (error) {
                console.error('There has been a problem with your fetch operation:', error);
                document.getElementById('result').textContent = 'Error: ' + error.message;
            }
        }

        window.onload = callAzureFunction;
    </script>
</body>
</html>

先ほど設定した静的なWebサイトの設定画面で、プライマリエンドポイントのURLが見れるので、ここへアクセスすれば、アップロードしたindex.htmlが表示されるはずです。

AS-image-11

index.htmlを作っただけで、localhostでサーバーを起動する部分は省略したので、ローカル環境ではAPIの呼び出しがCORSで弾かれてしまって動作確認ができませんでした。
ローカル環境を作るか少し悩んだのですが、今回は動作確認までなのでローカルでの確認は省略しました。
CORSの設定はAzure FunctionsメニューにCORS設定があるので、そこから追加できます。
Azure StorageでホストしているWebページのドメインを指定すれば、リモート側では正常に動作します。

AS-image-12

まとめ

WebサイトホスティングをしたAzure Storageのindex.htmlから、Azure FunctionsのAPIを呼び出し、Azure Cosmos DBにあるデータを取得して、表示することができました。

感想

AWSと比べて、Azureではそれぞれのサービスのメニューにネットワーク設定があり、そこからIPを指定してアクセス制限をかけることができるので、手軽に社内向けのツールを作ることができました。 CloudFrontにLambda@Edgeを設定して、S3でホストしているWebページへIP制限をかけていたことを考えると、社内向けのミニツールを作りたいというニーズに対してはAzureのほうが適していると感じました。 今後は、独自ドメインの設定やReactやVueを使ったWebサイトの表示がうまくできるか、AWSではCDKも使っていたのでIaCの利用方法も学習していきたいと思います。