Docker で Express アプリをデプロイする(mkcert で https 接続) - AlmaLinux

  • 作成日:
  • 最終更新日:2025/06/25

開発環境

ホストOS Windows11
ゲストOS AlmaLinux
仮想マシンソフトウェア VirtualBox
コンテナ Apache、Node.js

ディレクトリ構成

  • exapp
    • certs
      • mylocalapp.local.pem
      • mylocalapp.local-key.pem
    • express-app
      • Express-generatorで作成したアプリ
      • Dockerfile
    • apache
      • Dockerfile
      • conf
        • httpd.conf
        • vhost-ssl.conf
        • load_ssl_modules.conf
    • docker-compose.yml

ディレクトリが作成されれば、サーバー上に「 exapp 」のディレクトリごとアップロードします。

mkcert

mkcert は、ローカル開発環境でHTTPS(SSL/TLS)通信を簡単に行うためのツールです。

localhost や開発用のドメインでは本物の証明書は発行できない為「自己署名証明書(通称:オレオレ証明書)」を使います。

mkcert を使うことで自己署名証明書を作成でき、信頼されたルート証明書ストアに、mkcert が生成したルート認証局(CA)の証明書を自動的に登録してくれます。

mkcert は、ホストOSにインストールし、自己署名証明書を作成します。

以下のコマンドを実行することで、ローカルに CA(認証局)用の秘密鍵と証明書を作成し、その CA を OS やブラウザの信頼されたルート証明機関に追加する初期設定を行います。

mkcert -install # 一度だけ実行しておけばOK

Windows 11 における mkcert のCAファイル(ルート証明書と秘密鍵)の保存場所は、通常、C:\Users\YourUsername\AppData\Local\mkcertにあります。

rootCA.pem mkcert が生成したルート認証局の証明書ファイル
rootCA-key.pem mkcert が生成したルート認証局の秘密鍵ファイル

以下のコマンドを実行することで、mkcert のCAファイルが保存されている正確なディレクトリパスが表示されます。

mkcert -CAROOT

サーバー証明書と秘密鍵を作成するには、コマンドプロンプトを管理者権限で起動し、「 exapp/certs 」に移動して、以下のコマンドを実行します。

mkcert mylocalapp.local

「 mylocalapp.local.pem (サーバー証明書) 」と「 mylocalapp.local-key.pem (秘密鍵) 」が作成されます。

認証局について

ローカル認証局の実体は、rootCA.pem(ルート証明書)と rootCA-key.pem(秘密鍵)のペアと考えてください。

ローカル認証局とは、特定の用途(特にローカル開発環境でのHTTPS化)のために、ユーザー自身が自分のPC上に作成する仮想的な認証局のことです。

ルート証明書ストアは、OSとブラウザ両方にあり、基本はOSのルート証明書ストアが中心で、多くのブラウザはそこを参照します。

Mozilla Firefox は、セキュリティ上の理由から、OSのルート証明書ストアとは独立した独自のルート証明書ストアを持っています。

ルート証明書ストアとは、オペレーティングシステム(OS)やウェブブラウザが、「この認証局が発行した証明書は信頼できる」と判断するための、信頼されたルート認証局の公開鍵証明書が格納されている場所のことです。

サーバーとホストOS間の確認プロセス(mkcertの場合)

あなたがDockerコンテナで動かしているApacheサーバーに mkcert で生成した証明書をアップロードした場合、通信の流れと確認プロセスは以下のようになります。

  • 1.ブラウザ(ホストOS上)からサーバーへHTTPSリクエスト:あなたのホストOS上で動いているブラウザが、https://mylocalapp.local/ にアクセスします。
  • 2.サーバー(Apache)が証明書を提示:Apacheサーバーは、設定された mylocalapp.local.pem(証明書)と mylocalapp.local-key.pem(秘密鍵)を使って、SSL/TLSハンドシェイクを開始し、その証明書をブラウザに提示します。
  • 3.ブラウザが証明書を検証:ブラウザは、提示された mylocalapp.local の証明書が「誰に署名されているか」を確認します。(この証明書は、mkcert がホストOS上に作成した**「ローカル認証局」によって署名されています。**)
  • 4.ブラウザが「ホストOSの信頼されたストア」に確認:ブラウザは、その署名をしたローカル認証局の証明書が、「ホストOSの信頼されたルート証明書ストア」に登録されているかどうかを確認します。
  • 5.信頼が確立される:mkcert -install によってそのローカル認証局が事前に登録されているため、ブラウザは「ああ、この認証局は信頼できる。そしてこの認証局が署名した mylocalapp.local の証明書も信頼できるな」と判断します。これにより、HTTPS接続が警告なしで確立されます。

ファイアウォール

80 と 443 ポートを開けます。

sudo firewall-cmd --permanent --add-service=http --zone=public
sudo firewall-cmd --permanent --add-service=https --zone=public
sudo firewall-cmd --reload

hostsファイルの設定(Windows側)

C:\Windows\System32\drivers\etc\hostsファイルに以下を追加します。

サーバーのIPアドレス mylocalapp.local

サーバーのIPアドレスが「 192.168.1.10 」の場合だと、以下を「 hosts ファイル 」に追加します。

192.168.1.10 mylocalapp.local

Dockerfile

apache/Dockerfile

FROM httpd:2.4

express-app/Dockerfile

# Node.js の公式イメージを使用
FROM node:20-alpine

# 作業ディレクトリを設定
WORKDIR /usr/src/app

# package.json と package-lock.json をコピーして依存関係をインストール
COPY package*.json ./
RUN npm install

# アプリケーションのコードをコピー
COPY . .

# Express アプリがリッスンするポートを公開
EXPOSE 3000

# アプリケーションを起動
CMD ["npm", "start"]

apache/conf

httpd.conf

ServerName mylocalapp.local:443
Listen 443

User daemon
Group daemon

# ここに LogLevel を追加または修正します
LogLevel debug

# Load SSL and Proxy modules
Include conf/extra/load_ssl_modules.conf

# SSLバーチャルホスト
Include conf/extra/httpd-vhosts.conf

# プロキシエラーの場合、vhost側のログ設定も確認
ErrorLog "/proc/self/fd/2"
CustomLog "/proc/self/fd/1" common

vhost-ssl.conf

<VirtualHost *:443>
    ServerName mylocalapp.local

    SSLEngine on
    SSLCertificateFile "/etc/ssl/certs/mylocalapp.local.pem"
    SSLCertificateKeyFile "/etc/ssl/certs/mylocalapp.local-key.pem"

    ProxyPreserveHost On
    ProxyRequests Off
    ProxyTimeout 300
    
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"

    ProxyPass / http://express:3000/
    ProxyPassReverse / http://express:3000/

    ErrorLog "/usr/local/apache2/logs/error.log"
    CustomLog "/usr/local/apache2/logs/access.log" combined

    <Directory />
        Require all granted
    </Directory>
</VirtualHost>

load_ssl_modules.conf

LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule headers_module modules/mod_headers.so
LoadModule authz_core_module modules/mod_authz_core.so

docker-compose.yml

services:
  apache:
    build: ./apache
    ports:
      - "443:443"
    volumes:
      - ./certs:/etc/ssl/certs:ro
      - ./apache/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro
      - ./apache/conf/vhost-ssl.conf:/usr/local/apache2/conf/extra/httpd-vhosts.conf:ro
      - ./apache/conf/load_ssl_modules.conf:/usr/local/apache2/conf/extra/load_ssl_modules.conf:ro
    depends_on:
      - express

  express:
    build: ./express-app
    expose:
      - "3000"
    environment: # このブロックを追加
      - DEBUG=myapp:* # または DEBUG=* で全てのデバッグログを有効にする

OS を再起動したときに、Docker コンテナも自動起動するには、docker-compose.yml に各サービスごとにrestart ポリシーを追加する必要があります。

Docker コンテナも自動起動

services:
  apache:
    build: ./apache
    ports:
      - "443:443"
    volumes:
      - ./certs:/etc/ssl/certs:ro
      - ./apache/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro
      - ./apache/conf/vhost-ssl.conf:/usr/local/apache2/conf/extra/httpd-vhosts.conf:ro
      - ./apache/conf/load_ssl_modules.conf:/usr/local/apache2/conf/extra/load_ssl_modules.conf:ro
    depends_on:
      - express
    restart: unless-stopped

  express:
    build: ./express-app
    expose:
      - "3000"
    environment:
      - DEBUG=myapp:*
    restart: unless-stopped

restart ポリシーの意味は、次の表を参考にしてください。

ポリシー名 動作概要
no(デフォルト) 自動再起動しない
always 常に再起動(手動停止しても再起動する)
on-failure 異常終了(ステータスコード ≠ 0)のときに再起動
unless-stopped 手動で docker stop されたのでなければ再起動(おすすめ)

ビルド

ビルドするには、以下のコマンドを実行します。

docker compose up -d --build

無事ビルドが終われば、ホストOSのブラウザを開いて、「 https://mylocalapp.local 」にアクセスします。「 Welcome to Express 」が表示されれば設定完了です。

使用するコマンド

# コンテナのビルドと起動
docker compose down -v && docker compose up -d --build

# Apache コンテナのログ
docker compose logs apache

# epress コンテナのログ
docker compose logs express

コンテナ内に curl をインストールし、コンテナ内で Express のアプリにアクセスできるか確認してみるには、以下のコマンドを実行します。

# コンテナ内に入る
docker exec -it exapp-apache-1 bash

# Debian系で試す
apt update && apt install curl -y

# コンテナ内でアクセスする
curl http://express:3000/