Azure Container AppsでOdooのHTTP(8069)とWebSocket(8072)を共存させる構成と設定手順

Azure Container Apps 上で Odoo を公開しようとすると、HTTP(8069) と WebSocket/longpolling(8072) を同居させたい場面がよくあります。しかし単純に Ingress と NGINX を組み合わせただけでは、/ は表示できるのに /websocket だけタイムアウトする、という状態にハマりがちです。この記事では、その原因と Azure Container Apps らしいスマートな解決パターンを詳しく解説します。

目次

Azure Container Apps 上での Odoo 公開シナリオ

まずは、この記事で想定している構成を整理します。

コンポーネント役割ポイント
NGINX ゲートウェイ (ACA)フロントのリバースプロキシ / TLS 終端 / 複数ドメイン対応Azure Container Apps 上で稼働。
各バックエンド ACA へ内部トラフィックを振り分け
Odoo アプリ (ACA)業務アプリケーション本体HTTP: 8069 / WebSocket(Longpolling): 8072 を使用
Azure Container Apps 環境同一 VNet 相当の論理境界同じ環境内の ACA 同士はアプリ名で内部 DNS 解決される

Odoo 側のポート利用は、おおよそ次のようになっています。

ポート用途プロトコルクライアントからの主なアクセス
8069通常の HTTP アプリ (Web UI, API)HTTP/1.1ブラウザから https://example.com/ などでアクセス
8072gevent / longpolling / Live Chat 用 WebSocketWebSocket (HTTP Upgrade)ブラウザから /websocket や /longpolling/xxx として内部的に利用

ユーザー体験としては「全部 https://example.com/ 以下」で完結させたいのに、実際の Odoo コンテナでは 2 つのポートを使う、というギャップがハマりポイントになります。

なぜ / は表示できるのに /websocket はタイムアウトするのか

症状として多いのは次のパターンです。

  • /(トップページ)は問題なく表示できる
  • しかし /websocket や Live Chat がタイムアウト / 接続失敗する
  • NGINX 側で proxy_pass http://odoo-app:8072 などに変えても 404 / タイムアウトになることがある

ここでカギになるのが「Azure Container Apps の Ingress 仕様」です。

Azure Container Apps Ingress の仕様をおさらい

Azure Container Apps の HTTP Ingress は、ざっくり次のような動きをします。

設定項目役割重要なポイント
transport: httpHTTP/HTTPS トラフィックを扱うモード80/443 で受けたリクエストをコンテナの targetPort に転送
external: true/false外部公開 or 内部のみtrue のときは FQDN が払い出される
targetPortコンテナが LISTEN しているポートHTTP Ingress から流れるのは 1 つの targetPort のみ

ここが Kubernetes Ingress と感覚的に違うところで、「パスごとに違うコンテナポートへ振り分ける」といったことは、Azure Container Apps の HTTP Ingress 単体ではできません。

つまり、Odoo コンテナの Ingress 設定で targetPort: 8069 にしている場合、FQDN や環境の外部エンドポイントから来た HTTP トラフィックは必ず 8069 にのみ流れます。/websocket だからといって 8072 には届きません。

そのため、

  • FQDN 経由で / にアクセス → 8069 に届くので OK
  • 同じ FQDN 経由で /websocket にアクセス → それでも 8069 に届いてしまい、期待する 8072 に届かない

という現象が発生します。

解決策の全体像:8072 を「追加 TCP ポート」として内部公開

そこで使うのが、Ingress の additionalPortMappings(追加 TCP ポート)機能です。これを利用すると、一つの Container App から複数のポートを露出させることができます。

今回のゴールは次の形です。

  • Odoo コンテナの HTTP Ingress は従来どおり targetPort: 8069
  • 8072 は追加 TCP ポートとして同一 ACA 環境内にだけ公開(external: false)
  • フロントの NGINX からは、内部 DNS で http://odoo-app:8072 へ直接プロキシ

構成イメージをテキストで描くと次のようになります。

  • クライアントブラウザ → NGINX ゲートウェイ (HTTPS)
  • NGINX ゲートウェイ
    • / → http://odoo-app:8069
    • /websocket → http://odoo-app:8072
  • Odoo コンテナ (ACA)
    • HTTP: 8069(Ingress の targetPort)
    • WebSocket: 8072(additionalPortMappings で内部公開)

ポイントは「WebSocket 用の 8072 を外部には出さず、同じ ACA 環境の中だけで使う」というところです。これにより、セキュアかつシンプルに HTTP と WebSocket を分離できます。

Odoo コンテナアプリ側の設定:追加 TCP ポート 8072 を公開する

まず Odoo コンテナアプリの Ingress で 8072 を追加 TCP ポートとして公開します。

YAML (ARM テンプレート) の例

典型的な Ingress 定義は次のようになります。

properties:
  configuration:
    ingress:
      external: false         # Odoo 自体は内部専用(FQDN は不用)
      transport: http         # メインは HTTP Ingress
      targetPort: 8069        # Odoo の HTTP ポート
      additionalPortMappings:
        - targetPort: 8072    # Odoo の WebSocket / gevent
          exposedPort: 8072   # 環境内で露出するポート
          external: false     # 外部には公開しない

この設定を行うと、同じ Container Apps 環境内の他のアプリからは、

  • http://odoo-app:8069 → Odoo HTTP
  • http://odoo-app:8072 → Odoo WebSocket / longpolling

という形でアクセスできるようになります(odoo-app は Container App 名)。

Azure Portal から設定する場合

Azure Portal の GUI で作業する場合は、概ね次の手順になります。

  1. Odoo の Container App を開く
  2. [Ingress] ブレードを開く
  3. Ingress は 「内部」(Internal) を選択し、ターゲットポートに 8069 を指定
  4. 画面下部の「Additional TCP ports(追加 TCP ポート)」セクションを展開
  5. Target port に 8072、Exposed port に 8072、Ingress traffic を「Internal only」に設定
  6. [Save] して、新しいリビジョンが作成・アクティブになることを確認

リビジョンが古いままだと、追加 TCP ポートの設定が反映されていないことがあるため、「アクティブなリビジョン」がどれかを常に確認しておくと安全です。

Odoo 側の確認ポイント

Odoo コンテナの中では、次のような状態になっていることを確認しておくと安心です。

  • プロセスが 8069 と 8072 の両方で LISTEN している
    • netstat -tlnp や ss -tlnp などで確認
  • Odoo の設定で longpolling / gevent が有効になっている
    • --longpolling-port=8072 を指定して起動している など

ここが間違っていると、Ingress や NGINX の設定が正しくても 8072 側が応答せず、結果的にタイムアウトします。

NGINX ゲートウェイ側の設定:内部 DNS で 8069 / 8072 に振り分ける

次に、フロントの NGINX ゲートウェイ (Container App) の設定です。ポイントは次の 3 つです。

  • 同一 Container Apps 環境内では、アプリ名で内部 DNS 解決できる
  • Docker 固有の resolver 127.0.0.11; は不要(ACA では使わない)
  • / と /websocket でバックエンドのポートを分ける

NGINX 設定例

upstream odoo_http {
    server odoo-app:8069;     # Azure Container Apps 内部 DNS
}

upstream odoo_ws {
    server odoo-app:8072;     # 追加 TCP ポート
}

map $http_upgrade $connection_upgrade {
    default close;
    websocket upgrade;
}

server {
    listen 80;                # 実環境では 443 で TLS 終端する想定
    server_name example.com;  # Odoo 用のドメイン名

    # 通常の Odoo HTTP
    location / {
        proxy_http_version 1.1;

        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP         $remote_addr;

        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        $connection_upgrade;

        proxy_pass http://odoo_http;
    }

    # Odoo の WebSocket / longpolling
    location /websocket {
        proxy_http_version 1.1;

        # WebSocket 必須ヘッダー
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        $connection_upgrade;

        # Odoo 側で必要になることがあるヘッダー
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header Origin            $http_origin;

        proxy_connect_timeout 5s;
        proxy_read_timeout    3600s;
        proxy_send_timeout    120s;

        proxy_pass http://odoo_ws;
    }
}

ここでは説明のために upstream を使いましたが、

  • proxy_pass http://odoo-app:8069;
  • proxy_pass http://odoo-app:8072;

のように直接書いても問題ありません。

WebSocket 用ヘッダーの意味

ヘッダー値役割
Upgrade$http_upgradeHTTP から WebSocket へのプロトコルアップグレードを伝える
Connection$connection_upgradeWebSocket 用の接続が継続されることを示す
Origin$http_originブラウザからの Origin を Odoo に伝える。CSRF/セキュリティチェックで利用される場合あり
X-Forwarded-Protohttpsバックエンドに対して「クライアントは HTTPS でアクセスしている」ことを伝える

特に NGINX を TLS 終端として使う場合、X-Forwarded-Proto や Host を正しく渡さないと、Odoo 側でリダイレクトがループしたり、外部 URL が http:// になってしまうことがあるので注意してください。

内部 DNS と名前解決のポイント

Azure Container Apps では「同一 Container Apps 環境内」でのみ、アプリ名による内部 DNS 解決が機能します。

名前解決のパターン例動作
アプリ名odoo-app同じ Container Apps 環境内から参照すると内部 IP に解決される
FQDN (外部 Ingress)odoo-app.blue123...japaneast.azurecontainerapps.ioHTTP Ingress を経由して targetPort のみ に流れる
<aca-name>.internal 形式odoo-app.internal など環境や設定により解決しないことがある。基本的には非推奨

この構成では、ゲートウェイ NGINX と Odoo コンテナは同じ Container Apps 環境内に配置するのが前提です。別環境に配置してしまうと、アプリ名での内部 DNS 解決が効かず、getent hosts odoo-app が失敗します。

動作確認:ゲートウェイコンテナからの疎通チェック

設定を行ったら、NGINX ゲートウェイのコンテナ内から次のようにテストすると安心です。

# 1. 内部 DNS でアプリ名が引けるか
getent hosts odoo-app

# 2. 追加 TCP ポート 8072 に接続できるか
nc -vz odoo-app 8072

# 3. HTTP レベルでの応答を確認
curl -i http://odoo-app:8072/

ここで 8072 への nc / curl が成功していれば、追加 TCP ポートとしての公開はできています。あとは NGINX の location 定義と WebSocket ヘッダーの問題に絞って原因を切り分けていけます。

よくあるトラブルと原因切り分け

実際に運用していると、次のようなエラーに出会うことが多いです。

症状考えられる原因確認ポイント
/ は表示できるが、/websocket がタイムアウトOdoo の 8072 が ACA で公開されていない / NGINX が 8069 に向いているadditionalPortMappings の設定有無、proxy_pass の宛先ポートを確認
/websocket にアクセスすると 404Odoo 側で gevent / longpolling が有効になっていないOdoo の起動オプション・ログを確認し、8072 で LISTEN しているか確認
NGINX エラーログに「host not found in upstream」アプリ名が誤っている / 別環境に配置しているgetent hosts odoo-app で名前解決できるか確認
接続直後に 502 Bad GatewayWebSocket ヘッダー不足 / タイムアウト値が短すぎるUpgrade / Connection ヘッダー、proxy_read_timeout を確認

特に「FQDN をそのまま NGINX の proxy_pass に使っている」ケースは要注意です。Azure Container Apps の FQDN はあくまで HTTP Ingress に対するもので、targetPort にしか流れないため、8072 には絶対届きません。

セキュリティと運用面での注意点

Odoo と WebSocket を Azure Container Apps 上で公開する際は、セキュリティと運用面でもいくつかポイントがあります。

8072 は必ず内部のみ (external: false) にする

  • WebSocket 用ポートはインターネットに直接出さず、Container Apps 環境内からのみアクセス可能にする
  • 外部からは必ず NGINX ゲートウェイを経由させる(WAF やログ、TLS 終端を集約できる)

ヘッダーと TLS の整合性

  • クライアントは HTTPS でアクセス → NGINX で TLS 終端 → Odoo へは HTTP でプロキシ
  • この場合、Odoo 側に X-Forwarded-Proto=https を渡しておかないと、URL 生成やリダイレクトが http:// になりがち
  • HSTS を使う場合は、必ず NGINX 側でヘッダーを付与する

スケールアウトと WebSocket

Odoo コンテナをスケールアウトすると、WebSocket 接続が複数のレプリカに分散します。Azure Container Apps は内部的にロードバランサーを持っているため、基本的にはそのままでも問題ありませんが、以下の点に注意してください。

  • Odoo の設定で workers / gevent ワーカー数を適切に調整する
  • 長時間接続する WebSocket が多い場合、コンテナのメモリ・CPU を余裕を持って割り当てる
  • スケーリングルール(HTTP/TCP ベース)を使う場合は、閾値とクールダウン時間を慎重に設定する

応用パターン:マルチドメイン / マルチ Odoo 構成

同じパターンは、マルチドメイン・マルチテナント構成にも応用できます。

  • Odoo ごとに Container App を分ける
    • odoo-tenant-a(8069 / 8072)
    • odoo-tenant-b(8069 / 8072)
  • NGINX ゲートウェイでは server_name ごとに upstream を切り替える
    • a.example.com → odoo-tenant-a:8069 / :8072
    • b.example.com → odoo-tenant-b:8069 / :8072

この場合も、「各 Odoo アプリで 8072 を追加 TCP ポートとして内部公開」「NGINX からはアプリ名で解決してポートを分ける」という基本パターンはまったく同じです。

構成のチェックリスト

最後に、Azure Container Apps で Odoo の HTTP(8069) と WebSocket(8072) を同居させる際のチェックリストをまとめておきます。

項目確認内容
Odoo Ingress (HTTP)external: false(内部のみ) transport: http targetPort: 8069
追加 TCP ポートadditionalPortMappings に 8072 を追加 targetPort: 8072, exposedPort: 8072 external: false にしてインターネットには公開しない
NGINX からの接続同じ Container Apps 環境内に配置している proxy_pass http://odoo-app:8069(/ 用) proxy_pass http://odoo-app:8072(/websocket 用)
WebSocket ヘッダーUpgrade / Connection ヘッダーを適切に設定 必要に応じて Origin を Odoo に渡す
内部疎通テストgetent hosts odoo-app が成功する nc -vz odoo-app 8072 で接続できる curl -i http://odoo-app:8072/ で応答がある
Odoo プロセス8069 / 8072 の両方で LISTEN gevent / longpolling が有効

まとめ:Azure Container Apps で HTTP + WebSocket を安定稼働させる「型」

Azure Container Apps の HTTP Ingress は 1 つの targetPort にしかトラフィックを流せないため、Odoo のように 8069 と 8072 の 2 ポートを使うアプリでは一工夫が必要です。

その答えが、「WebSocket 用ポートを追加 TCP ポート(additionalPortMappings)として内部公開し、フロントの NGINX からアプリ名+ポートで直接プロキシする」というパターンです。

  • Odoo(バックエンド)
    • Ingress: external: false, targetPort: 8069
    • additionalPortMappings に 8072(external: false)を追加
  • NGINX ゲートウェイ(フロント)
    • / → http://odoo-app:8069
    • /websocket → http://odoo-app:8072
    • WebSocket ヘッダーとタイムアウトを適切に設定
    • Docker 用 resolver 127.0.0.11; は削除

この構成をベースにすれば、Azure Container Apps 上でも Odoo の HTTP(8069) と WebSocket(8072) を安定してプロキシでき、Live Chat やリアルタイム機能も安心して活用できます。既存の Kubernetes Ingress の感覚をそのまま持ち込むとうまく動かないことが多いので、「HTTP Ingress は targetPort 一つ+追加 TCP ポート」という ACA 特有の考え方を押さえておくと、今後のトラブルシューティングにも役立つはずです。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次