オンプレ系インフラエンジニアがAzureを勉強する

いつか誰かの何かの役に立つと嬉しいな

Azure Container Instancesで複数WebAPIを扱う

はじめに

Azure Container Instancesを使って
複数のWebAPIをコンテナグループで管理しようとしたときに
ポートを分ける方法でつまずいたので書いておきます。


目的

複数のWebAPI(コンテナ)をコンテナグループでまとめて、
IPアドレスやDNSnameは一つで、ポートを分けて各WebAPIにアクセスできるようにすることです。

<参考>

Azure Container Instances のコンテナー グループ | Microsoft Docs


使う環境

  • Azureアカウント
  • VisualStudio2019(WebAPI用)
  • Docker for Windows(version 19.03.1)

 

全体の流れ

Container Registryの作成(Azureポータル)


Dockerfileの作成(ローカル)


Dockerイメージのpush(ローカル)


Container Instancesの作成(Azureポータル)


手順

Container Registryの作成

Container Registryを作成します。
APIのDcokerイメージを置いておくところになります。

<詳細>

Azure Container Registry のドキュメント - チュートリアル | Microsoft Docs

 

Azureポータルにログインします。
新規リソース作成から[コンテナー]>[ContainerRegistry]をクリックします。

f:id:mitsunooon:20190825095208j:plain


簡単にパラメータを設定します。

f:id:mitsunooon:20190825095232j:plain


完成したら、アクセスキーを確認します。
後ほどDockerイメージをpushするのに使います。

f:id:mitsunooon:20190825000322j:plain

 

Dockerfileの作成

今回WebAPIはVisualStudio2019で用意しました。
ローカルで起動させるとこんな感じです。
それぞれ別のポートでアクセスします。
詳しいコードのついては省略します。

f:id:mitsunooon:20190825000502j:plain

f:id:mitsunooon:20190825000520j:plain


このAPIそれぞれにDockerfileを作成します。
VSにてプロジェクトを右クリックし、[追加]>[Docker サポート...]をクリックします。

f:id:mitsunooon:20190825000546j:plain


ターゲットOSは[Linux]で[OK]をクリックします。

f:id:mitsunooon:20190825000603j:plain


Dockerfileが作成されます。
この中の「EXPOSE 80」の部分を今回使いたいポートに変更します。
今回はDeleteApiを5000、GetApiを5001にします。
これはローカルで実行したときとは異なるポートですが、問題ありません。

f:id:mitsunooon:20190825000635j:plain


Dockerイメージをビルドします。
作成したDockerfileを右クリックし、[Dockerイメージのビルド]をクリックします。
これでDockerのイメージが作られます。

f:id:mitsunooon:20190825000828j:plain


コマンドプロンプトで確認します。
ちゃんと作られてますね。

>docker images

f:id:mitsunooon:20190825000932j:plain


Dockerイメージをレジストリにpushする

作成したイメージにタグをつけます。
Azure上のレジストリ内でバージョン管理するための認識です。
APIを編集更新するたびにDockerイメージをビルドしなおして
レジストリにあげる必要があるので、
タグを分けておくと、古いバージョンに戻すのも楽だと思います。

>docker tag <対象イメージのIMAGE ID> <接続先レジストリサーバー名>/<対象イメージ名>:tag

f:id:mitsunooon:20190825095402j:plain

レジストリにpush

Azure上で作成したレジストリにpushします。

まずAzure上のレジストリにログインします。

>docker login <レジストリサーバー名> -u <ユーザ名> -p <パスワード>

f:id:mitsunooon:20190825001440j:plain

 

その後にpushします。

>docker push <レジストリサーバー名>/<イメージ名>:tag

f:id:mitsunooon:20190825095434j:plain


Azureポータル上で確認します。
ちゃんとpushされてますね。

同様にもう一つのAPIレジストリにpushします。

f:id:mitsunooon:20190825001545j:plain


Container Instancesの作成

いよいよ本題のContainer Instancesを作成します。

今回は複数のサービスがあるコンテナグループになりますので
下記手順を参考にしています。

チュートリアル - Azure Container Instances に複数コンテナー グループをデプロイする - テンプレート | Microsoft Docs


テンプレートの作成

Container Instances用のテンプレートを用意します。
参考サイトをもとにazuredeploy.jsonを作ります。

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "containerGroups_ContainerInstancesTest_name": {
            "defaultValue": "ContainerInstancesTest",
            "type": "String"
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.ContainerInstance/containerGroups",
            "apiVersion": "2018-04-01",
            "name": "[parameters('containerGroupsName')]",
            "location": "japaneast",
            "properties": {
                "containers": [
                    {
                        "name": "deleteapi",
                        "properties": {
                            "image": "containerregistry0824.azurecr.io/deleteapi:1.0.0",
                            "ports": [
                                {
                                    "protocol": "TCP",
                                    "port": 5000
                                }
                            ],
"resources": { "requests": { "memoryInGB": 0.5, "cpu": 0.25 } } } }, { "name": "getapi", "properties": { "image": "containerregistry0824.azurecr.io/getapi:1.0.0", "ports": [ { "protocol": "TCP", "port": 5001 } ], "resources": { "requests": { "memoryInGB": 0.5, "cpu": 0.25 } } } } ], "imageRegistryCredentials": [ { "server": "レジストリサーバー名", "username": "レジストリユーザー名", "password": "パスワード" } ], "restartPolicy": "Always", "ipAddress": { "ports": [ { "protocol": "TCP", "port": 5000 }, { "protocol": "TCP", "port": 5001 }, ], "type": "Public", "dnsNameLabel": "ContainerInstancesTest" }, "osType": "Linux" } } ] }


編集する箇所としては各種名前、image、port、imageRegistryCredentials、各コンテナの設定あたりでしょうか。
各種名前:defaultValueがContainer Instancesの名前。dnsNameLabelがURLになるホスト名。nameが各コンテナの名前。
image:コンテナグループに入れるサービスのイメージ。
レジストリからコピーしてくると間違いないです。
port:VSで作成したDockerfileに記載したportを指定します。
imageRegistryCredentials:レジストリのアクセスキーの情報。

(補足)
リージョンによってCPU等の使用に制限があります。
最小値は1ではないので、動作に影響さえなければ0.5とかでも動きます。

Azure Container Instances リソースの可用性 | Microsoft Docs


テンプレートのデプロイ

AzureのCloudShellに接続し、作成したテンプレートをアップロードします。

アップロードしたファイルをコマンドでデプロイします。

>az group deployment create --resource-group <リソースグループ> --template-file <テンプレートファイル>

f:id:mitsunooon:20190825101337j:plain

 

テンプレートに間違いがあればエラーが表示されます。
問題なく実行されればしばらく待った後にContainer Instancesの情報が表示されます。

デプロイが終わったらポータル上で確認します。
コンテナが二つできてますね。(でも一つ動いてない…?

f:id:mitsunooon:20190825002332j:plain


接続してみます。
つながらない…なぜ…

f:id:mitsunooon:20190825002348j:plain


よくよく設定を見てみます。
プロパティではちゃんとポートが指定されてます。

f:id:mitsunooon:20190825002403j:plain


ログを見ると…むむむ。
なぜかポート80の表記が。

f:id:mitsunooon:20190825101418j:plain


これはデフォルトが変わってないのでは?
と思い調べるとやはり環境変数を変える必要がありました。

asp.net - Why does aspnet core start on port 80 from within Docker? - Stack Overflow


ということで、テンプレートの"containers"配下に以下の部分を追記します。

"environmentVariables": [
{
"name": "ASPNETCORE_URLS",
"value": "http://+:5000"
}
],


もし一つのコンテナに複数のポートを割り当てたい場合は;で仕切ります。

"environmentVariables": [
{
"name": "ASPNETCORE_URLS",
"value": "http://+:5000;http://+:5002"
}
],


Container Instancesを再デプロイします。

確認します。
うんうん、ちゃんとポートが割り当たってますね。

f:id:mitsunooon:20190825002653j:plain


接続します。
ちゃんと各ポートで接続されますね。めでたし。

f:id:mitsunooon:20190825002719j:plain

f:id:mitsunooon:20190825002736j:plain


おわりに

初めはDockerComposeを使いたかったのですが、
DockerOSがLinuxの場合、APIは一つまでしか対応していないようで断念…
API一つとDB等を組み合わせるには良いようです。
Preview版でなくなったときは複数APIも使えるようになってると嬉しいです。