気まま研究所ブログ

ITとバイク、思ったことをてきとーに書きます。

Docker ComposeでASP.NetとApache 2を連携してみる

我が家のサーバで移行ついでについにDocker Composeを利用した仮想環境で構築することにしました。
そのうち忘れそうなので備忘録的にDocker ComposeにてApache 2とASP.Netの連携をしてみようと思います。

検証環境

項目 詳細
PC VMware Workstation Pro 仮想マシン
OS Ubuntu 24.04
CPU Core i7-10700K
.Net .Net 8

Apache 2による連携のメリット

ASP.Netはこれ自体にWebサーバの役割があるので基本的にApache 2を介する必要はありません。
これだけなら80番またはHTTPS対応してるなら443番をバインドすればいいだけ。
しかしながら今後サーバが増えていく事を考えると直にポートをバインドしてしまうと他のサービスの公開ができなくなってしまいます
なのでここではApache 2にWebサーバの役目をしてもらい、ASP.Netとのプロキシになってもらいます。
HTTPSの対応も簡単ですからApache 2やnginxを使うほうが色々とメリットも大きいでしょう。

構築

構成図

構成の図はこのような感じでいきます。

詳細はこのあと書いていきますが、外側へ公開するのはApache 2の80番と443番のポートだけでASP.Netの5000番は公開しません
もうちょっと構成が増えれば内部ネットワークも分けてもいいと思います。

ファイル構成

ファイル構成はこのような感じに行きます。

Sample
  L Apache
    L sites
      L 000-default.conf
    L Dockerfile
  L App
    L www               # Apache 2のwww用
      L index.html
    L asp               # ASP.Netのアプリケーション
      L sample.dll
  L Asp-App
    L Dockerfile

Appディレクトリはコンテナではなく公開したいアプリケーションやファイルを配置する用に使います。

docker-compose.yml

docker-compose.ymlはこんな感じに。
Apache 2とASP.Netだけのシンプルな構成ですが、実際はMySQLやら連携が必要なものが増えていくと思います。
基本は今回のやり方と同じなので増やしていけるかなと。
各内容は個別に書いていきます。

services:
  apache:
    build:
      context: ./Apache
      dockerfile: Dockerfile
    restart: always
    ports:
      - "80:80"
    volumes:
      - "./App/www:/var/www/html"

  asp-app:
    build:
      context: ./Asp-App
      dockerfile: Dockerfile
    restart: always
    volumes:
      - "./App/asp:/App/asp"

各コンテナ

asp-app

順番が逆になりますが、ASP.Net合ってのApache 2なのでこちらから構築していきます。
今回はASP.Net MVCの初期テンプレートのままで構築するので開発中のアプリに置き換えて見てもらえればと思います。

docker-compose.ymlではDockerfileの指定とボリュームの指定だけとなります。
ボリュームはどこでも良いのでお好きな場所へ。

・docker-compose.yml

  asp-app:
    build:
      context: ./Asp-App
      dockerfile: Dockerfile
    restart: always
    # ports:
    #   # 動作確認する時だけバインドしておくと便利
    #   - "8080:5000"
    volumes:
      # HOSTの領域とコンテナの領域をバインド
      - "./App/asp:/App/asp"

Dockerfileでは.Net 8のイメージを指定し、先程バインドしたディレクトリをワーキングディレクトリとします。
あとはdotnetコマンドでアプリケーションを実行するだけ。

Dockerfile

FROM mcr.microsoft.com/dotnet/aspnet:8.0

WORKDIR /App/asp

ENTRYPOINT ["dotnet", "sample.dll"]

直接関連はないですが、Ubuntuイメージを利用して特定のバージョンの.Netを使う場合はICU等のエラーが出るのでこちらを参考に。

apache

docker-compose.ymlではASP.Netの設定と大体同じですが、それに加えてportsによるHOSTのポートとコンテナのポートのバインドを指定します。
今回はHTTP(80)をバインドします。
実際に運用するのであれば現在はHTTPSが必須の時代なのでHTTPS(443)もバインドしてSSL証明書の用意が必要です。

・docker-compose.yml

  apache:
    build:
      context: ./Apache
      dockerfile: Dockerfile
    restart: always
    ports:
      # HOSTの80番とコンテナの80番をバインド
      - "80:80"
    volumes:
      # HOSTの領域とコンテナの領域をバインド
      - "./App/www:/var/www/html"

公式でhttpdがありますが、個人的にUbuntuのApache 2に慣れているので今回はUbuntuイメージから作成することにしました。

Dockerfile

FROM ubuntu:24.04

RUN apt-get update -y
RUN apt-get install -y apache2

COPY ./sites/000-default.conf /etc/apache2/sites-available/000-default.conf

RUN a2enmod proxy proxy_html proxy_http

ENTRYPOINT ["apachectl", "-D", "FOREGROUND"]

ASP.Netアプリケーションとのプロキシ設定を行います。

000-default.conf

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    ProxyPreserveHost on
    ProxyPass / http://asp-app:5000/
    ProxyPassReverse / http://asp-app:5000/
</VirtualHost>

ここで肝なのがプロキシで指定するURLのホスト名にはDocker Composeのコンテナ名が利用できるところです。
Docker Composeではコンテナ名がそのままホスト名になるので、コンテナ同士が同じネットワークで繋がっていればコンテナ名を指定するだけで接続が可能になります。
ちなみに5000番はASP.Netの実装やappsettings.jsonの設定次第で変わるので注意してください。
一応後で違う場合の確認方法を記載しておきます。

もし今後MySQLやPHP-FPMを入れる事になってもコンテナ名を指定するだけで接続ができるので覚えておきましょう。

起動

ここまでできたらあとは起動するだけです。

# イメージのビルド
$ sudo docker compose build
# コンテナの起動
$ sudo docker compose up -d

初回やdocker-compose.ymlの修正ならup -dだけでいいんですが、Dockerfileの修正が入るとイメージの作り直しがいるので、buildもセットで行うと良いでしょう。

大体初手でうまくは行かないのでダメなときはトラブルシューティングしていきましょう。

接続確認

構築できたら最後はHOSTのIPアドレスへ繋ぐだけです。

うまく繋がればOK。
バーチャルホストを使ったプロジェクト等でドメインがいる場合はhostsを弄る等ちょっと工夫がいります。

トラブルシューティング

Apache 2に繋がらない

接続確認でそもそも繋がらない場合はApache 2が動いてません。
この場合はコンテナのログを見てみましょう。

$ sudo docker compose logs apache

例えば000-default.confを不正な形式にしてみると

$ sudo docker compose logs apache
apache-1  | AH00526: Syntax error on line 7 of /etc/apache2/sites-enabled/000-default.conf:
apache-1  | Invalid command 'ss', perhaps misspelled or defined by a module not included in the server configuration

こんな感じにエラーのログが出ていることがあるのでエラーを解決しましょう。

Service Unavailableが出る

Apache 2には繋がるけど503エラーが出る場合はASP.Netが動いていません。

まずはASP.Netのログを見ます。

$ sudo docker compose logs asp-net
asp-app-1  | The command could not be loaded, possibly because:
asp-app-1  |   * You intended to execute a .NET application:
asp-app-1  |       The application 'sample_bad.dll' does not exist.
asp-app-1  |   * You intended to execute a .NET SDK command:
asp-app-1  |       No .NET SDKs were found.
asp-app-1  |
asp-app-1  | Download a .NET SDK:
asp-app-1  | https://aka.ms/dotnet/download
asp-app-1  |
asp-app-1  | Learn about SDK resolution:
asp-app-1  | https://aka.ms/dotnet/sdk-not-found

今回はDockerfileのエントリーポイントに誤りがあるのでそれを直せば動きます。

・Dockerfile

ENTRYPOINT ["dotnet", "sample.dll"]

あとは再ビルドして再起動しましょう。

Proxy Errorが出る

Proxy Errorが出る場合はプロキシ設定に誤りがあります。

この場合はまずはapacheコンテナに入ってログを見ましょう。

$ sudo docker compose exec apache /bin/bash
root@70f6f91f12cb:/# cd /var/log/apache2
root@70f6f91f12cb:/var/log/apache2# cat error.log

AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.18.0.3. Set the 'ServerName' directive globally to suppress this message
[Thu Jun 12 18:47:11.415362 2025] [mpm_event:notice] [pid 21:tid 125944831571840] AH00489: Apache/2.4.58 (Ubuntu) configured -- resuming normal operations
[Thu Jun 12 18:47:11.415447 2025] [core:notice] [pid 21:tid 125944831571840] AH00094: Command line: '/usr/sbin/apache2 -D FOREGROUND'
[Thu Jun 12 18:47:14.162441 2025] [proxy:error] [pid 22:tid 125944816043712] [client 192.168.0.81:55906] AH00898: DNS lookup failure for: asp-app_bad returned by /
[Thu Jun 12 18:47:25.942234 2025] [proxy:error] [pid 24:tid 125944816043712] [client 192.168.0.81:55912] AH00898: DNS lookup failure for: asp-app_bad returned by /

すると一番下に「asp-app_bad」DNSルックアップ失敗の一文があるのでホスト名が誤ってそうだとわかります。

一応ネットワークが繋がっていない可能性もあるのでcurlで正しいと思われるURLのチェックして問題ないか見ておきましょう。

root@70f6f91f12cb:/# apt update
root@70f6f91f12cb:/# apt install curl
root@70f6f91f12cb:/# curl http://asp-app:5000/
... (html)

正常にHTMLが取得できればネットワークも問題ないのでプロキシ設定を修正して再ビルドしましょう。

・000-default.conf

 ProxyPreserveHost on
    ProxyPass / http://asp-app:5000/
    ProxyPassReverse / http://asp-app:5000/

ASP.Netは起動してるのにApache 2から繋がらない

Ubuntuベースでdotnetを個別に構築している場合やASP.Netのソース次第ではありますが、localhostからの接続以外は受け付けないようになっている場合があります。

$ sudo docker compose logs asp-app
asp-app-1  | info: Microsoft.Hosting.Lifetime[14]
asp-app-1  |       Now listening on: http://localhost:5000
asp-app-1  | info: Microsoft.Hosting.Lifetime[0]
asp-app-1  |       Application started. Press Ctrl+C to shut down.
asp-app-1  | info: Microsoft.Hosting.Lifetime[0]
asp-app-1  |       Hosting environment: Production
asp-app-1  | info: Microsoft.Hosting.Lifetime[0]
asp-app-1  |       Content root path: /App/asp

このようにlistening onが「http://localhost:5000」になっていると外部からの接続は受けて受けてくれません。
この場合は同じDocker Compose内と言っても接続が受け付けてくれないので設定を変更してlocalhost以外も受け付けてくれるようにする必要があります。

・appsettings.json

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://*:5000"
      }
    }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Kestrel.Endpoints.Http.Urlをこのようにワイルドカードにすることで全HOSTからの接続を受け付けるようになります。
また、リスニングポートをここで変更することも可能です。

またはDockerfileに環境変数を指定することでも同様の対応が可能です。

・Dockerfile

...

ENV ASPNETCORE_URLS http://*:5000

...

再度ビルドして再起動するとこのように「http://[::]:5000」となるのでApache 2からも接続できるようになります。

$ sudo docker compose logs asp-app
asp-app-1  | info: Microsoft.Hosting.Lifetime[14]
asp-app-1  |       Now listening on: http://[::]:5000
asp-app-1  | info: Microsoft.Hosting.Lifetime[0]
asp-app-1  |       Application started. Press Ctrl+C to shut down.
asp-app-1  | info: Microsoft.Hosting.Lifetime[0]
asp-app-1  |       Hosting environment: Production
asp-app-1  | info: Microsoft.Hosting.Lifetime[0]
asp-app-1  |       Content root path: /App/asp

ちなみに公式の「mcr.microsoft.com/dotnet/aspnet:8.0」等のイメージを使うとデフォルトでワイルドカード相当の設定がなされているようでこの問題は基本的に起きません。