Azure研究サイト

Azure研究サイト

~理解しずらい情報をシンプルにお伝えします~

ContainerInstances(コンテナインスタンス)の使用手順と課金額

 

皆さまは、開発したアプリケーションをAppService・Functionsにデプロイして、アプリ公開はされていると思いますが、コンテナーインスタンスは使用したことがありますでしょうか? AppService・Functionsしか知らないという方は、Dockerコンテナーが必要にはなりますが是非勉強してみてください。
 
AppServiceに比べて何が良いの?といいますと、例えばAppServiceプランを削除しない限り一定の金額を課金され続ける AppService(Standard1の場合1ヶ月で約9600円)に対して、コンテナーインスタンスを使用した場合は、夜間やアクセスがない時間帯はゼロに自動スケーリングされてvCPUに割安のアイドル使用料金が適用されるため、1ヶ月で4000円程で収まると言われています。
 
この記事では、ACR(コンテナレジストリ)にコンテナイメージをプッシュしてACI(コンテナインスタンス)実行に関して、説明からハンズオンまでをお伝えします。

 

Azure Container Registry (ACR)

ACRとは

Microsoft Azureが提供するコンテナイメージを安全かつ効率的に管理&保管できるレジストリサービスです。
 
コンテナのイメージとは、コンテナのイメージは、アプリケーションを実行するのにアプリケーションのコード、ランタイム、ライブラリ、設定ファイルなどがパッケージ化されたものです。ACIなどのコンテナ実行サービスは、このイメージを元にコンテナを実行します。実行環境を含めてパッケージされているため、プラットフォームや環境が異なる場合でも同じように動作するので大変便利です。
 

ACRの料金

Container Registry 価格の公式ドキュメント 

2023年12月現在、Basicプランで1日約25円程度掛かります。
 

Azure Container Instances (ACI)

ACIとは

Azure Container Instances(ACI)は、Azureの環境でコンテナを実行するためのサービスです。ACIを利用すれば、物理サーバを管理することなくクラウド上でコンテナを作成・デプロイ・実行&停止することができます。これらの操作は、Azure PortalやAzure CLIから簡単に実施可能です。
 
ACIは、ACRなどのコンテナレジストリサービスからイメージをプルしてコンテナを実行します。
 
ACIでは、ACR以外のコンテナイメージのレジストリサービス(DockerHub, Amazon ECRなど)も使用することができます。しかし、同じAzureサービスのACRの相性が良いため、特別な理由がない限りACRを使うのが一般的です。
 

ACIの料金

Container Instances 価格の公式ドキュメント
 
ACIの料金は、使用時間(秒単位)とCPUコア数やメモリ量によって決まります。CPUコア数やメモリ量を増やせば増やすほど、秒単価も高くなります。

ハンズオン

ACRとACIを使ってコンテナアプリケーションを構築・実行してみましょう。
手順は次の通りです。
 
1. コンテナで実行するサンプルアプリケーションを準備する
2. ACRを作成する
3. ACRへコンテナイメージをプッシュする
4. ACIがACRからコンテナイメージをプルするロールを準備する
5. ACIでコンテナを実行する
6. アプリを修正して再デプロイする
7. 再デプロイ後にACIでコンテナを実行する  

1. コンテナで実行するサンプルアプリケーションを準備する

Node.jsで"Hello, World!"を表示するだけの簡単なアプリケーションです。  
app.js

const http = require('http');
   
const server = http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
  res.end('Hello, World!\n');
});
   
const port = 8080;
server.listen(port, () => {
  console.log(`Server running at ${port} port/`);
});

 

# Node.jsのLTSバージョンを使用
FROM node:lts
   
# アプリケーションディレクトリを作成
WORKDIR /usr/src/app
   
# アプリケーションの依存関係をインストール
COPY package*.json ./
RUN npm install
   
# アプリケーションのソースをバンドル
COPY . .
   
# ポート8080で実行
EXPOSE 8080
CMD [ "node", "app.js" ]

 
まずはローカルPCで挙動を確認してみます。

$ npm init
   
$ npm install http

# test-node-appという名前を付けてビルドします
$ docker build -t test-node-app .
   
# イメージが作成されたことを確認します
$ docker images
REPOSITORY      TAG       IMAGE ID       CREATED         SIZE
test-node-app   latest    **********   2 minutes ago   1.1GB
   
# コンテナを起動します
$ docker run -p 8080:8080 -d test-node-app
   
# コンテナの起動を確認します
$ docker ps -a
CONTAINER ID   IMAGE           COMMAND                  CREATED         STATUS         PORTS                    NAMES
********   test-node-app   "docker-entrypoint.s…"   5 minutes ago   Up 5 minutes   0.0.0.0:8080->8080/tcp   upbeat_leavitt

 
ブラウザから http://localhost:8080/ にアクエスすると「Hello World!」が表示されます。  

2. ACRを作成する

コンテナイメージをプッシュする先であるレジストリが必要なので、ACRのリソースを新規作成します。Azureポータル画面で「コンテナレジストリ」サービス画面を開きます。ポータル画面の検索欄で「レジストリ」と検索すると出てきます。  
 
作成ボタンから入り、以下を参考に入力します。  

基本    
リソースグループ 任意の名前(当記事では rg-acr-dev)
レジストリ名 任意の名前(当記事では acrdevys) 英数字のみの5~50文字の制限あり
場所 Japan East  
料金プラン Basic 1番安いプランを選択
ネットワーク    
接続の構成 パブリックアクセス(全てのネットワーク) Basicプランではパブリックしか選択できません

 
プライベートは、Premiumプランのみ選択できます。「確認および作成」タブまで進み、作成ボタンを押します。  
 
新しいACRが作成されました。  

3. ACRへコンテナイメージをプッシュする

  • [Azureにログイン]: ACRへのコンテナイメージプッシュは、Azure CLIを使って行いますが、まずAzure CLIでAzureにログインします。  
$ az login
A web browser has been opened at https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize. Please continue the login in the web browser. If no web browser is available or if the web browser fails to open, use device code flow with `az login --use-device-code`.

 
ブラウザが立ち上がるので認証を済ませます。
f:id:exemple:plain title=

 
ログインしたユーザー情報が表示されたらログイン完了です。

[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "***",
    "id": "***",
    "isDefault": true,
    "managedByTenants": [],
    "name": "Azure サブスクリプション 1",
    "state": "Enabled",
    "tenantId": "***",
    "user": {
      "name": "***",
      "type": "user"
    }
  }
]
  • [ロールを確認する]: この後実施する作業は、「AcrPush ロール」が割り当てられたユーザーで実施する必要があります。AcrPush ロールは、ACRへコンテナイメージをプッシュできる権限です。ここでは、権限の強い所有者ロールを割り当てられたユーザーを用いて以下の作業します。(実環境では、所有者のような強権限のロールを持つユーザーで作業することは推奨されません。最小権限の原則に則って、必要なロールのみを付与したユーザーで作業することを検討してください。)
     

  • [イメージをACRにプッシュする]: 先ほど作成したコンテナレジストリ(ACR)にイメージのプッシュを、az acr build コマンドで行います。az acr buildコマンドは、ローカルコードをビルドして、ビルドの成果物(コンテナイメージ)をACRにプッシュするコマンドです。  

# Dockerfileを含む今回のプロジェクトのディレクトリに移動します
$ cd node
   
$ az acr build --image node-app:v1.0.0 --registry acrdevys .

 
--imageオプションで、ビルドして生成されるイメージの名前やタグを指定します。イメージの名前:タグの形式で指定できます。--registryオプションでは、先ほど作成したACRのリソース名を指定します。

$ az acr build --image node-app:v1.0.0 --registry acrdevys .
....

2023/12/02 02:21:12 Successfully pushed image: acrdevys.azurecr.io/node-app:v1.0.0
2023/12/02 02:21:12 Step ID: build marked as successful (elapsed time in seconds: 31.661096)
2023/12/02 02:21:12 Populating digests for step ID: build...
2023/12/02 02:21:13 Successfully populated digests for step ID: build
2023/12/02 02:21:13 Step ID: push marked as successful (elapsed time in seconds: 39.552949)
2023/12/02 02:21:13 The following dependencies were found:
2023/12/02 02:21:13 
- image:
    registry: acrdevys.azurecr.io
    repository: node-app
    tag: v1.0.0
    digest: sha256:****
  runtime-dependency:
    registry: registry.hub.docker.com
    repository: library/node
    tag: lts
    digest: sha256::****
  git: {}
Run ID: ce1 was successful after 1m15s

 
このコマンドを実行すると、以下のことが行われます。

  • ローカルのソースコード(Dockerfile含む)がACRにアップロードされる

  • アップロードされたローカルコードがビルドされ、コンテナイメージが生成される

  • 生成されたコンテナイメージを--registryで指定したレジストリにプッシュする

 
コンテナレジストリ > サービス > リポジトリを確認すると、今ビルド&プッシュをしたイメージ(node-app:v1.0.0)が確認できます。  

4. ACIがACRからコンテナイメージをプルするロールを準備する

  • [前提]: この後、ACIを使ってコンテナを実行していきます。コンテナ実行時、ACIはACRのイメージをプル(ダウンロード)するため、ACIにはイメージをプルする権限が必要です。ここでは、この認証をマネージドIDを使用して行います。  
     
  • [マネージドIDを作成する]: Azure Portalで「マネージドID」のページに行き、作成ボタンを押します。
基本  
リソースグループ rg-acr-dev
リージョン Japan East
名前 aci-image-pull-id

上記の内容で、マネージドIDを作成します。

 

  • [マネージドIDにACRのイメージをプルする権限を付与する]: コンテナレジストリのページに行き、アクセス制御(IAM)を選択します。ロールの割り当ての追加を押します。  

 
AcrPullを選択し、「次へ」を押します。  
 

アクセスの割り当て先で「マネージドID」を選択し、「メンバーを選択する」を押します。先ほど作成したマネージドID(aci-image-pull-id)を選択します。  
 

「レビューと割り当て」を押します。アクセス制御 (IAM)を確認すると、今追加した「ユーザー割り当てマネージド ID」が反映されていることが分かります。  
 

5. ACIでコンテナを実行する

  • [Azure CLIから実行する]: Azure CLIのaz container createコマンドでACIを作成し、コンテナを実行します。  
$ az container create \
  --name node-app-container \
  --resource-group rg-acr-dev \
  --image acrdevys.azurecr.io/node-app:v1.0.0 \
  --assign-identity [ユーザー割り当てマネージドIDのリソースID] \
  # ACRへのアクセスに使用されるマネージドIDを指定
  --acr-identity [ユーザー割り当てマネージドIDのリソースID] \
  # コンテナー再起動ポリシーを指定
  --restart-policy Never \
  --ip-address Public \
  --ports 80

 
ユーザー割り当てマネージドIDのリソースIDは、以下のコマンドやPortal(管理 > プロパティ)で確認できます。

$ az identity show --name <マネージドIDの名前> --resource-group <リソースグループ名> --query id
--restart-policyは、コンテナー再起動ポリシーを指定するものです。

 
しばらく時間が掛かりますが、コンテナ作成が終わると以下のようなJSONが返却されます。

{
  "confidentialComputeProperties": null,
  "containers": [
    {
      "command": null,
      "environmentVariables": [],
      "image": "acrdevys.azurecr.io/node-app:v1.0.0",
      "instanceView": {
        ....以下略

 
Azure PortalのACIのページに行くと、今作成したnode-app-containerが作成されていることを確認できます。
 

ステータスは実行中になっています。
 

アプリ側で出力したログも確認できました。
 

続いて、コンテナにブラウザからアクセスしてみます。
 
http://{コンテナのIPアドレス}:80をブラウザに打ち込みます。コンテナのIPアドレスは、Portalから確認できます。また、コンテナ作成時に返却されたJSONにもIPアドレスが記載されています。  
 

アクセスできませんでした。  
f:id:exemple:plain title=  
 
ACIにはDockerで使用されるようなポートマッピング機能がありません。必ず、コンテナ内のポートと外部に公開するポートを一致させる必要があります。今回のケースでは、コンテナ内のポートを8080に設定しています。そのため、コンテナ作成時に指定するポートも8080である必要がありました。指定ポートを8080に変更の上、再度コンテナを実行します。
 
先ほど作成したコンテナを削除します。コンテナの削除は、Portalから実施します。
 

az container createでACIを作成し、コンテナを実行します。

$ az container create \
  --name node-app-container \
  --resource-group rg-acr-dev \
  --image acrdevys.azurecr.io/node-app:v1.0.0 \
  --assign-identity [ユーザー割り当てマネージドIDのリソースID] \
  --acr-identity [ユーザー割り当てマネージドIDのリソースID] \
  --restart-policy Never \
  --ip-address Public \
  --ports 8080 # コンテナ内のポートと一致させる

 
http://{コンテナのIPアドレス}:8080 にアクセスして、Hello, World! が表示されることを確認します。
f:id:exemple:plain title=  
 
動作を確認できたので、コンテナインスタンスは削除しておきます。  

6. アプリを修正して再デプロイする

おさらいの意味も込めて、アプリケーションの内容を変更して、再デプロイしてコンテナを実行してみます。  

  • [アプリケーションに変更を加える]: アプリケーションコードを修正し、表示する文字列を変えます。  
    app.js
const server = http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
  // 表示する文字列を変更する
  res.end('コンテナインスタンス実行中です。\n');
});
  • [ACRにコンテナイメージをプッシュする]: アプリケーションに変更を加えたので、この内容でビルドし、コンテナイメージをACRにプッシュします。バージョンは、v1.0.0 から v1.0.1 に変更します。
# Dockerfileのあるディレクトリに移動してから実施します
$ cd node
#  バージョンを v1.0.0 から v1.0.1 に上げます
$ az acr build --image node-app:v1.0.1 --registry acrdevys .
  • [コンテナイメージを確認する]: PortalのACRのページに行きます。今追加したv1.0.1が追加されていることを確認できます。  
     
    なお、以下のコマンドでもイメージのタグ一覧を確認できます。
$ az acr repository show-tags --name acrdevys --repository node-app --output table
   
Result
--------
v1.0.0
v1.0.1

7. 再デプロイ後にACIでコンテナを実行する

az container createでACIを作成し、コンテナを実行します。

$ az container create \
  --name node-app-container \
  --resource-group rg-acr-dev \
  # 今追加した新しいバージョンを指定します
  --image acrdevys.azurecr.io/node-app:v1.0.1 \
  --assign-identity [ユーザー割り当てマネージドIDのリソースID] \
  --acr-identity [ユーザー割り当てマネージドIDのリソースID] \
  --restart-policy Never \
  --ip-address Public \
  --ports 8080

 
http://{コンテナのIPアドレス}:8080にアクセスします。すると、今加えた変更が反映されたのが分かります。  

今回の作業で掛かった課金額

  • [ACR]: 今回はBasicプランを選択して、1日約25円掛かりました。  

  • [ACRの注意点]: 作成したままにしておくと、月額750円ほど掛かります。課金を止めたい場合は、コンテナレジストリ自体を削除する必要があります。今回はBasicプランを使用しましたが、Basicよりも上のプランを選択すれば更に料金が掛かります。Container Registry 価格の公式ドキュメント

 

  • [ACI]: 今回の検証で5,6回ACIを作成しましたが、合計約15円でした。勉強のためでも許容できる金額に収まりそうです。  

 

  • [ACIの注意点]: ACIは、使用時間に応じて料金が掛かるので、課金を止めたい場合は不要なインスタンスを削除する必要があります。今回、ACIは1vCPU と1.5GBで実行しました。これより上のスペックを指定した場合、さらに料金が掛かります。CPUやメモリは、ACI作成時に返却されるJSONデータのresourcesで確認できます。コンテナ作成時に何も指定しない場合、デフォルト値の1vCPU と1.5GBが指定されます。  
  "resources": {
    "limits": null,
    "requests": {
      "cpu": 1.0,
      "gpu": null,
      "memoryInGb": 1.5
    }
  }

 
以上です。  

ご意見・ご要望はこちらへ 


/*--------------------------------------------------------------------*/