2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenWrtのDockerComposeで立ち上げたコンテナに外からアクセスできない

Last updated at Posted at 2025-08-10

概要

OpenWrt 上で docker compose up したコンテナに 同一LAN内クライアントなど外部から接続すると、タイムアウトしてしまうことがあります。(Chromeから繋いだ際は「サーバーからの応答に時間が掛かり過ぎています」といった表示となる)

これは、Compose が作る ユーザー定義ブリッジ(br-xxxx) が OpenWrt の fw4(nftables)ゾーンには登録されていないため、LAN → br-xxxx の FORWARD が拒否されるためです。


前提

今回の検証環境

  • OpenWrt: 24.10.2
  • Model: GL.iNet GL-MT6000
  • Architecture: ARMv8 Processor rev 4
  • Kernel: 6.6.93
  • 使用したコンテナ: portainer/porainer-ce:latest

症状例(Portainerの場合)

  • docker run -p 9000:9000 portainer/portainer-ce で立ち上げたコンテナは LAN内別ノード から接続可

  • docker compose up -d で立ち上げたコンテナのみが LAN内別ノード からタイムアウト

  • ルータでのパケット観察:

    tcpdump -i br-lan -n host [client_ip] and tcp port 9000
    # → DNAT の結果、宛先が 172.18.0.2:9000(docker0のアドレス) になるが、SYN-ACK が返らない
    

解決策

とにかくなんとかしてComposeが扱うブリッジをファイアウォールに連携するよう設定するしかないのですが、比較的簡単な解決策は2つあります。

  • 解決策A:network_mode: host を使う
    • 一番早くほぼ確実に改善する
    • 公開ポートがDockerシステムから能動的に制御できない
  • 解決策B:固定名の“外部ブリッジ”を作って firewall に登録し、Compose から共用する
    • ホストモードが心理的に嫌な場合
    • スタックを跨いで内部ポートが重複していても問題ない(重要)

解決策A:network_mode: host(最短・確実)

Docker の仮想ブリッジを介さず、ホスト(OpenWrt)で直接 LISTEN させます。ポート競合がなければこれが最短で確実に動きます。

compose の例

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: always
    network_mode: host # ← ココ!
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    environment:
      - TZ=Asia/Tokyo
volumes:
  portainer_data: {}
  • ports: は不要(host モードでは無視される)
  • アクセス:http://192.168.154.1:9000/
  • OpenWrt の lan ゾーンは input ACCEPT が既定なので、FW追加は基本的に不要

解決策B:固定名の“外部ブリッジ”を作成して共用(拡張性重視)

固定名のユーザー定義ブリッジを事前に作り、firewall の docker ゾーンに登録しておき、 compose.yaml から 外部ネットワークとして参照します。作成したブリッジは複数のスタックを跨いで共用できます。

Step 1)ユーザー定義ブリッジを作成(初回のみ)

# ネットワーク名: composenet / ブリッジIF名: br-composenet
# docker0含め他の既存ネットワークと被らないサブネットを選ぶこと
docker network create \
  --driver bridge \
  --subnet 172.23.0.0/24 --gateway 172.23.0.1 \ 
  -o com.docker.network.bridge.name=br-composenet \
  composenet

Step 2)OpenWrt firewall の docker ゾーンに登録(初回のみ)

uci add_list firewall.docker.device='br-composenet'
uci commit firewall
/etc/init.d/firewall restart

これで LAN ↔ br-composenet ↔ コンテナ の FORWARD が通ります。

Step 3)compose.yaml から“外部ネットワーク”を参照

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: always
    ports:
      - "9000:9000"          # Portainer UI (HTTP)
      - "8000:8000"          # Agent 
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    environment:
      - TZ=Asia/Tokyo
    networks: [composenet]   # ← ココ!

volumes:
  portainer_data: {}

networks:                    # ← ココ!
  composenet:
    external: true
    name: composenet

既存のスタックに加えてこのようにネットワーク定義を書き加えます。

networks:
  composenet: 
    external: true
    name: composenet

おわりに

最短解は host モードと書きましたが、私はコンテナ内外でネットワークが分離されてくれないと絶対に嫌なので、外部ブリッジを作っておく方が圧倒的におススメです。

どちらもcompose.yamlに修正を加えないといけないのはちょっと残念ですね...。

composeで生成されたブリッジ(br-*)を検出してdockerゾーンに追加するポストスクリプトを用意して、compose.yamlをわざわざ変更しなくてもdocker compose upだけで上手いこと自動で設定されるようにできたりしないだろうか...?そのうち気が向いたらラップできるスクリプトくらいは作るかもしれません。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?