Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Azure Container Instancesを使ってAngular+FastAPIなWebアプリを動かしてみた

こんにちは。
初登場になります、普段はフロントエンドの開発をしている長野です。

本記事は、Azure Advent Calendar 2020の19日目の記事になります。

最近、Angular+FastAPIでアプリを実装したのですが、
そのアプリを、仮想マシンではなく、サーバレス・コンテナを使ったWebアプリとして構築してみたくなりました。

調べている中で、Azure Container Instances(以下、ACI)を見つけ、実際に試してみたところ、
少し工夫が必要だったものの、全体としてはとても簡単に動かすことができました。

この記事では、私が実際にACIを使ってみた中で学んだ、ACI上でのWebアプリの動かし方について、
特にフロントエンドとバックエンドの連携の際に注意するべき点を中心に、まとめました。

この記事を見ながら、皆さんも実際にこの手軽さを体験していただきたいと思います。

それでは、さっそく始めましょう!

始めに: "Azure Container Instances"とは

ACIとは、高速かつ簡単にコンテナを実行できるサービスです。メリットは次の2つです。

  1. コンテナイメージさえあればコマンド一つで立ち上げることができ、さらにインターネット上に直接公開をすることもできるので、すぐに利用することができます。
  2. コンテナの起動時間とその間に使用したCPUとメモリに応じて課金されるので、使わないときは停止する等をすれば、コストも抑えることができます。

Webアプリの説明

まずはじめに、例として扱うWebアプリを説明します。

今回、本の情報を管理する、簡単な図書管理システムを構築してみました。
一覧画面と詳細画面があり、本の情報の、登録・確認・編集・削除ができるようになっています。

f:id:acro-engineer:20201219101050p:plain
図書管理システムの一覧画面
f:id:acro-engineer:20201219101458p:plain
図書管理システムの詳細・編集画面

フロントエンドはnginx + Angular、バックエンドはFastAPIを、今回は使用しました。

また、今回開発するにあたり、以下の環境を使用しました。

  • Node.js: v14.15.0
  • Docker: 20.10.0
  • Azure CLI: 2.16.0

なお、ディレクトリ構成は以下のようになっています。

sample-lsm-app     
├─deploy-aci.yaml
├─docker-compose.yml
├─client
│  ├─angular.json
│  ├─Dockerfile
│  ├─package-lock.json
│  ├─package.json
│  ├─dist
│  │  └─client
│  │      └─フロントエンドの本体コードのビルド媒体
│  ├─nginx
│  │  └─default.conf
│  ├─node_modules
│  └─src
│      ├─app
│      │  └─フロントエンドの本体コード
│      └─environments
│         └─フロントエンドの環境設定
└─server
    ├─Dockerfile
    ├─poetry.lock
    ├─pyproject.toml
    ├─.venv
    ├─src
    │  └─バックエンドの本体コード
    └─tests
       └─バックエンドのテストコード

Webアプリの構成

本アプリのAzure上での構成は以下のようになっています。

f:id:acro-engineer:20201219100505p:plain
サンプルアプリ構成イメージ

フロントエンド、バックエンド、それぞれ別でコンテナイメージを作成して、Azure Container Registry(以下、ACR)に登録、
それらをACI上にデプロイすることで、Webアプリを動かしています。

それでは実際に、ローカルで作成したWebアプリをAzure上に構築していきましょう。

WebアプリのAzure化

1. フロントエンドとバックエンドの結合部分の準備

まずは、フロントエンドからバックエンドへAPIを呼んで、データのやりとりができるように準備をします。
今回、Angularで作成したフロントエンドをnginxサーバー上で動かすので、以下のようなdefault.confを用意しました。

server {
    listen  80;
    server_name localhost;

    root    /usr/share/nginx/html;
    index   index.html index.htm;

    location /api/ {
        proxy_pass http://localhost:8000;
    }
}

ここでのポイントは、

proxy_pass http://localhost:8000;

の部分です。通常、Docker上で動かす際は、"localhost"の部分にコンテナ名を指定しますが、
Azure Container Instances上で動かす際には、"localhost"での指定になります。

以上の設定で、"http://<ACIの公開FQDN>:80/api/~" とURLを指定することで、フロントエンドからAPIを呼ぶことができるようになりました。

ここから先はAngularの内容です。

nginxで設定したAPIのURLを呼べるように、environment.prod.tsに以下のように記述します。

export const environment = {
  production: true,
  server: 'http://<ACIの公開FQDN>'
};

そして、実際にAPIを呼ぶservice.tsでは、以下のようにURLを指定するようにします。

  fetchBooks(): Observable<Book[]> {
    return this.http.get<Book[]>(environment.server + '/api/books');
  }

最後に、以下のコマンドで本番環境としてビルドを行うことで、フロントエンドをACI上で動かすための準備が完了しました。

npm build --prod

2. コンテナイメージ化

Dockerを使用し、フロントエンド側・バックエンド側両方のコンテナイメージを作成します。

まず、それぞれのDockerfileの内容はこちらです。

フロントエンド側

FROM nginx:1.16
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf

# distディレクトリ配下に作成されたビルド媒体をコピー
COPY ./dist/client /usr/share/nginx/html

バックエンド側

FROM python:3.8-slim
WORKDIR /usr/src/app
RUN pip install poetry

# ソースコード類のコピー
COPY . .

# 実行環境の用意
RUN poetry export -f requirements.txt > requirements.txt
RUN pip install -r requirements.txt

# FastAPIの起動
CMD python /src/main.py

これらをまとめて立ち上げるための、docker-compose.yamlはこちらです。

version: "3"
services:
  client:
    build: ./client
    image: sample-lms-app_client:1.0
    container_name: sample-lms-app_client
    ports:
      - 80:80
  server:
    build: ./server
    image: sample-lms-app_server:1.0
    container_name: sample-lms-app_server
    ports:
      - 8000:8000

docker-composeのbuildコマンドで、コンテナイメージを作成します。

docker-compose build

3. Azure Container Registryの作成

2で作成したコンテナイメージを、登録するためのACRを用意します。

Azure portalから「リソースの作成」を行い、"Container Registry"を検索します。

f:id:acro-engineer:20201219101830p:plain
"Container Registry"で検索すると、作成画面が開きます

必要事項を入力し、ACRを作成します。

f:id:acro-engineer:20201219102111p:plain
ACRの作成画面

作成完了後、「アクセスキー」から、「管理者ユーザー」の設定を有効化します。
こうすることで、ACRに登録したコンテナイメージを利用する際には、パスワードが必要になります。「ログインサーバー」、「ユーザー名」、「password」を、それぞれ手元に控えておきましょう。

f:id:acro-engineer:20201219102414p:plain
ACRへのアクセスキーの表示

4. Azure Container Registryへコンテナイメージの登録

2で作成したコンテナイメージを、3で作成したACRに登録します。

最初に、コマンドプロンプト経由で、ACRにログインを行います。3の最後で取得した、アクセスキーを使用します。

docker login <ログインサーバー> -u <ユーザー名> -p <パスワード>

次に、2で作成したコンテナイメージに、タグ付けを行います。

docker tag sample-lms-app_client:1.0 <ログインサーバー>/sample-lms-app_client:1.0

docker tag sample-lms-app_server:1.0 <ログインサーバー>/sample-lms-app_server:1.0

最後に、pushを行うことで、ACRにコンテナイメージが登録されます。

docker push <ログインサーバー>/sample-lms-app_client:1.0

docker push <ログインサーバー>/sample-lms-app_server:1.0

登録が成功していることは、Azure portalの画面から、「リポジトリ」の画面を開くことで確認ができます。
作成したコンテナイメージが表示されたら、成功です。

f:id:acro-engineer:20201219102625p:plain
登録済みコンテナイメージの一覧

5. Azure Container Instancesの立ち上げ

いよいよ、ACI上にアプリを立ち上げます。

そのために、設定ファイルの作成が必要です。以下のような"deploy-aci.yaml"を作成しました。

apiVersion: 2018-10-01
name: SampleLMSAppACG
# ACIのリソース名
location: japaneast
# リージョン
properties:
  containers:
  # フロントエンド側、バックエンド側それぞれのコンテナの設定
  - name: client
    properties:
      image: <ログインサーバー>/sample-lms-app_client:1.0
      # 使用する、ACR上のコンテナイメージ
      resources:
        requests:
          cpu: 1
          # コンテナを動かすために必要なCPU数(最小1)
          memoryInGb: 1
          # コンテナを動かすために必要なメモリ数(最小1)
      ports:
      - port: 80
        # 使用ポート
        protocol: TCP
        # プロトコル
  - name: server
    properties:
      image: <ログインサーバー>/sample-lms-app_server:1.0
      resources:
        requests:
          cpu: 1
          memoryInGb: 1
      ports:
      - port: 8000
        protocol: TCP
  osType: Linux
  # コンテナのOSタイプ
  ipAddress:
  # IPアドレスの設定
    type: Public
    # パブリックIPか、プライベートIPか
    ports:
    # 公開ポートの設定
    - protocol: tcp
      port: 80
    - protocol: tcp
      port: 8000
    dnsNameLabel: sample-lsm-app
    # DNSラベル設定
  imageRegistryCredentials:
  # ACRのログイン情報
  - server: <SERVER>
    # ログインサーバー
    username: <USERNAME>
    # ユーザー名
    password: <PASSWORD>
    # password

以下、詳細な説明です。

  • type: Public
    • 今回は、ACIを直接公開するため、"Public"を選択しました。
    • 例えば、IP制限等を行うために、VNet下にアプリを立ち上げる場合には、ここで"Private"を選択します。
  • dnsNameLabel: sample-lsm-app
    • 1で設定した"ACIの公開FQDN"は、ここで設定したものです。
    • ここで設定したDNSラベルと、選択したリージョンによって、"<DNSラベル>.<リージョン>.azurecontainer.io"のように、公開FQDNが決定します。

ここで作成した設定ファイルを使って、ACIのデプロイを行います。
デプロイには、AzureCLIをインストールし、コマンドプロンプト上で、"az"コマンドが使えるようになっている必要があります。(Azure CLIのインストール

まずは、Azure CLI上で、ログインが必要です。

az login

これで、アプリをデプロイするための準備がすべて整いました。最後に、以下のコマンドでアプリを立ち上げます。

az container create --resource-group <リソースグループ> --file deploy-aci.yaml

6. 動作確認

作成コマンドを実行したら、Azure portal上で、実際にACIが作成されたか確認してみましょう。
設定ファイルの中で指定したリソース名で、リソースが存在し、かつ「実行中」であることを確認します。

f:id:acro-engineer:20201219230728p:plain
作成したACIの概要画面

ACIが実際に作成できたことが確認できたら、画面に表示されているFQDNから、アプリにアクセスしてみましょう。

f:id:acro-engineer:20201219103300p:plain
ACI上で動いているアプリにアクセスした様子

無事に画面を開くことができました!

もしもここで画面が開けなかった場合は、コンテナのログを見て、エラーが出ていないか確認します。
コンテナのログは、「コンテナー」でコンテナ一覧を開き、見たいコンテナを選択した状態で、「ログ」タブを選ぶことで表示することができます。

f:id:acro-engineer:20201219103504p:plain
コンテナログの表示

詰まりやすいのは、フロントエンドとバックエンドとの連携の部分なので、
コンテナがそれぞれ起動しているのを確認したうえで、この記事の中で紹介している、"environment.prod.ts"や"default.conf"の設定を確認してみてください。

なお、ACIのリソースは一度削除してから、作成コマンドを実行してください。
ACRに登録したコンテナイメージは、登録済みのタグにもう一度pushすることで上書きされます。
しかし一方で、ACIで同じタグのコンテナイメージを利用して再作成する際は、キャッシュが効いてしまうためか、
更新されたコンテナイメージでうまく立ち上げられないようです。

最後に

いかがだったでしょうか?

私が初めてこの構成でアプリを立ち上げようとした時は、
公式のチュートリアルを参考にしたものの、1つのコンテナの場合の説明のみだったため、
フロントエンドとバックエンドとのコンテナ間の連携がなかなかうまくいかず、苦労しました。

ぜひ皆さんも自作のアプリをACI上にデプロイして、動かしてみてください!

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

モノリシックなアプリケーションをマイクロサービス化したいエンジニア募集! - Acroquest Technology株式会社のWebエンジニアの求人 - Wantedlywww.wantedly.com