有馬総一郎のブログ

(彼氏の事情)

2026年02月06日 17:14:39 JST - 7 minute read - Linux

PHP初心者がVSCodeを使ってDockerを使ってデバッグ環境を構築する その2 (Dev Containers編)

アプリインストールの続きから。

Dev Containers

VSCode、Docker Desktopがインストール後の進め方は、 Create a Dev Containerに解説がある。自分は、読まずに進めていったが、改めて読むと勉強になった。

以下、作成ファイルについて、実際の例を見せながら、どうしてそうしたかを記しておく。

./.devcontainer
├── Dockerfile
├── devcontainer.json
└── docker-compose.yml

Dockerfile

まずは、Dockerイメージの選択。最終的にphp:8.4-apacheを選択した。geminiの言われるままに選ぶと後悔することになる。当たり前だが、以下の点など注意して選択する必要がある。

  • どんな機能を必要とするか(apacheを必要とするか)
  • 動作させたいバージョン(現行バージョンか、更新予定バージョンか)
  • 構築したい環境が手間が少ないか(短期間にビルドできるかといった軽量性、逆に機能追加など可能かどうかの汎用性、応用性があるか、など)

最初、二ヶ月後にサポートの切れるphp:8.2をすすめられた。次に、8.4にしろと言うと存在しないイメージを指定されたりした。とりあえず、Microsoftが準備した microsoft/devcontainers - Docker Imageの中から、mcr.microsoft.com/devcontainers/php:8.4を選択した。

確かに Xdebugがインストール済だったりと、vscodeユーザが追加済みなど良い点もあったのだけど、構築・動作確認をすすめると各フォルダに .htaccess を配置して処理していることが分かり、apacheが必要となった。なので、途中でphp:8.4-apacheに変更することとなった。

本番サーバーのOSをそのまま選ぶのが一番良いのだろうけど、ローカルで動作させるとなるとメモリ、CPUの負荷が相当かかる。キャッシュされるとはいえ、ビルドに時間がかかると修正・検証が大変だ1

一応、出来たのがこれ。

FROM php:8.4-apache
RUN a2enmod rewrite
RUN sed -i 's/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
ENV APACHE_DOCUMENT_ROOT /var/www/html/virtual/test/public_html/testphp.coresv.com
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf
RUN export DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
    default-mysql-client \
    unzip \
    autoconf \
    g++ \
    make \
    sqlite3
RUN pecl install xdebug \
    && docker-php-ext-enable xdebug
RUN docker-php-ext-install mysqli pdo pdo_mysql
# Xdebugの設定ファイルを作成
RUN echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.discover_client_host=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.client_port=9003" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
# タイムゾーンの設定
ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN echo "date.timezone = Asia/Tokyo" > /usr/local/etc/php/conf.d/timezone.ini
# エラーレポート設定(非推奨メソッドを表示したい場合はコメントアウトしてください)
RUN echo "error_reporting = E_ALL & ~E_DEPRECATED" > /usr/local/etc/php/conf.d/error_reporting.ini
# vscodeユーザで操作する場合
ARG USERNAME=vscode
RUN useradd -m -d /home/$USERNAME -s /bin/bash $USERNAME 
RUN apt-get install -y sudo
RUN echo "$USERNAME ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME
RUN chmod 0440 /etc/sudoers.d/$USERNAME

Apache、Xdebugのためにmcr.microsoft.com/devcontainers/php:8.4と比べるとこれだけ処理が増えた…

RUN a2enmod rewriteで、apacheの mod_rewriteモジュールを有効化して、AllowOverride Allに変更しないと .htaccess によるURLの書き換えが機能しない。また、APACHE_DOCUMENT_ROOTによる書き換えにより、レンタルサーバー Coreserverによる階層の深いディレクトリ/var/www/html/virtual/test/public_html/testphp.coresv.comを公開ディクレトリ2にしている。

また、vscodeユーザ追加の手順が増えている。mcr.microsoft.com/devcontainers/php:8.4は元から存在していて、sudoもインストール、適用済。

devcontainer.json

ローカルでの拡張機能とコンテナ内での拡張機能は分けられており、Xdebugはコンテナ内だけにインストールしないとデバッグが出来ないので、コンテナ内の拡張機能として"extensions"に追記する。

{
    "name": "PHP & MariaDB",
    "dockerComposeFile": "docker-compose.yml",
    "service": "app",
    "workspaceFolder": "/var/www/html",
    "customizations": {
        "vscode": {
            "extensions": [
                "bmewburn.vscode-intelephense-client",
                "xdebug.php-debug",
                "mtxr.sqltools",
                "mtxr.sqltools-driver-mysql",
                "mtxr.sqltools-driver-sqlite"
            ]
        }
    },
    "portsAttributes": {
        "80": {
            "label": "Apache",
            "onAutoForward": "ignore" 
        },
        "9003": {
            "label": "Xdebug",
            "onAutoForward": "ignore"
        }
    },
    // rootlessコンテナの場合、remoteUserをroot(ホストユーザとなる)に変更
    "remoteUser": "root",
    "updateRemoteUserUID": false,
    // rootlessコンテナでない場合、remoteUserをvscodeに変更
    // "remoteUser": "vscode",
    // "updateRemoteUserUID": true,
    "postStartCommand": "bash .devcontainer/post-start.sh"
}

"portsAttributes"は、Docker側で8000:80とポートフォワード設定済みなので、VSCodeがさらに自動でポート転送を行わないように抑制する3。また、デバッグ起動時に9003のポートでブラウザを立ち上げて繋げようとするのが鬱陶しいので記述している。

"remoteUser""updateRemoteUserUID"これらの記述ないときは、コンテナ内でファイルを編集すると所有権がなかったり、rootで書き込まれてホスト側が後で編集できなくなったりした。

以下のようにする。

  • Mac/Win/Chromebook (Rootful): "remoteUser": "vscode", "updateRemoteUserUID": true
  • Linux (Rootless): "remoteUser": "root", "updateRemoteUserUID": false

rootlessモードだとrootとしても、ホストのユーザとして扱われる。コンテナ内ではroot、ホストでは自ユーザとなる。updateRemoteUserUIDは、ユーザをコンテナとホストに一致させるか、ということ。rootをホストでも一致させようとすると権限がおかしくなる。実際、trueにすると、所有ユーザ、グループが?とLinuxシステムが認識できない番号になってしまった。

逆にrootfulモードの場合、rootで書き込むとホストでもrootとなってしまうので、既にコンテナに存在するユーザなり、追加したユーザを指定する。そして"updateRemoteUserUID": trueとしてvscode=自ユーザとして一致するようにする。ちょっとややこしくて、設定をミスると簡単に所有者がおかしくなる。

また、通常はユーザ、グループが適切な所有者のままファイル配置(コミット)されているだろうが、例えば、phpでsqliteDB登録・更新処理をしているところがあると、 db ファイルの所有者がwww-dataのままでないと問題が起こる4

コンテナビルド、起動後に何か処理させたい場合は"postStartCommand"に記述する。これはremoteUserでの処理となる。

docker-compose.yml

compose.ymlは以下のようになる。

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      # 親フォルダ(..)をコンテナ内の /var/www/html に同期する
      - ../:/var/www/html:cached
    ports:
      - "8000:80"
    extra_hosts:
          - "host.docker.internal:host-gateway"

  db:
    image: mariadb:10
    restart: always
    environment:
      # パスワード等の設定(必要に応じて変更してください)
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: database
      MYSQL_USER: user
      MYSQL_PASSWORD: password #含めないほうがセキュリティとしては安全
    volumes:
      # DBデータを永続化(コンテナを消してもデータは残る)
      - db-data:/var/lib/mysql

volumes:
  db-data:

php:8.4からphp:8.4-apacheに変更したので、以下の修正が必要だった。

-    command: sleep infinity
+    ports:
+      - "8000:80"

素のphpイメージでは、php実行後も終了させないためにsleep infinityが必要だったが、apacheとして起動するのに不要となった。また、素のphpイメージでは、 devcontainer.jsonforwardPorts: [8000]という記述でVSCode(Dev Containers)がトンネルを開けさせたが、Dockerに開けさせるようにした。

  • Dockerfile
  • devcontainer.json
  • docker-compose.yml

修正を入れ、適用させるにはコンテナのリビルド(Dev Container: Rebuild Container)してコンテナを開きなおす必要がある。ケースによってはキャッシュなしのリビルドをした方が良い5

と、また長くなったので デバッグ編へ続く


  1. 事実、 AeroBook Plusという i5-6287U10年前のCPUを積んだパソコンではqemu: process terminated unexpectedly: signal: aborted (core dumped)というエラーが出てビルドに失敗したりした。 ↩︎

  2. 敢えてそのままのディクレクトリ構成のままにしてるが、開発効率を考えるとvirtual/test/public_html/testphp.coresv.compulbicの一階層にしてしまった方が良いと思うし。実際そうしている。 ↩︎

  3. :38977など勝手に他のポートを開けられたりする。 ↩︎

  4. そもそも処理がエラーとなる。そして、所有者などの変更してしまうと、変更破棄で戻せない。sudo git checkout -q -- sqlite.dbsudoを付けるか、所有者を自ユーザにしてしまえば戻せる。 ↩︎

  5. キャッシュなしビルドでの直らない場合、現在動いているコンテナなどを停止・削除するdocker compose -f .devcontainer/docker-compose.yml down --rmi local --volumesを行い、ビルドキャッシュの削除docker builder prune -fが必要になるかも知れない。 ↩︎