VS Code Remote Containers用PHP開発環境を構築する
みなさん、VS Code Remote Containersは使っていますか?
プロジェクト毎に開発環境を構築・共有できて、とても便利ですよね。
コンテナの起動・停止がVS Codeと連動するのでとても良い開発体験かと思います。
また、GitHub Codespacesというクラウドの開発環境でも同じ設定で動かすことができるので、さまざまなシーンでの活用が期待できます。
今回、PHPの開発環境を構築してみたので共有したいと思います。
設定
ディレクトリ構成
.devcontainer
配下に設定をまとめてあります。
.
└── .devcontainer
├── .env
├── devcontainer.json
├── docker
│ ├── mysql
│ │ └── initdb.d
│ │ └── create_database.sh
│ ├── nginx
│ │ ├── Dockerfile
│ │ └── config
│ │ └── default.conf
│ ├── php
│ │ ├── Dockerfile
│ │ └── config
│ │ ├── php.ini
│ │ └── xdebug.ini
│ ├── postgres
│ │ └── initdb.d
│ │ └── create_database.sh
│ └── workspace
│ ├── Dockerfile
│ └── config
│ └── php.ini
└── docker-compose.yml
devcontainer.json
VS Codeのコンテナ開発の設定を行います。
{
"name": "PHP Development",
"dockerComposeFile": [
"docker-compose.yml"
],
"service": "workspace",
"workspaceFolder": "/var/www",
"remoteUser": "vscode",
"settings": {
"php.validate.enable": false,
"php.suggest.basic": false,
"[php]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "bmewburn.vscode-intelephense-client"
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/*.code-search": true,
"**/vendor/*/**": true
}
},
"extensions": [
"mikestead.dotenv",
"EditorConfig.EditorConfig",
"mhutchie.git-graph",
"eamodio.gitlens",
"xdebug.php-debug",
"neilbrayfield.php-docblocker",
"bmewburn.vscode-intelephense-client",
"recca0120.vscode-phpunit"
]
}
service
に、接続するサービス名を指定します。
settings
に、接続コンテナの VS Code の設定を行います。
extensions
に、接続コンテナにインストールする拡張機能を指定します。
docker-compose.yml
services:
workspace:
build:
context: ./docker/workspace
args:
USERNAME: ${USERNAME-vscode}
USER_UID: ${USER_UID-1000}
USER_GID: ${USER_GID-1000}
TIME_ZONE: ${TIME_ZONE-UTC}
LOCALE: ${LOCALE-C}
tty: true
volumes:
- ../:/var/www
- ./docker/workspace/config/php.ini:/usr/local/etc/php/conf.d/99-php.ini
working_dir: /var/www
nginx:
build: ./docker/nginx
ports:
- "${IP_ADDRESS_SETTING}80:80"
- "${IP_ADDRESS_SETTING}443:443"
volumes:
- ./docker/nginx/config:/etc/nginx/conf.d
- ../.docker/nginx/log:/var/log/nginx
- ../:/var/www
environment:
TZ: ${TIME_ZONE-UTC}
php:
build:
context: ./docker/php
args:
USER_UID: ${USER_UID-1000}
USER_GID: ${USER_GID-1000}
volumes:
- ./docker/php/config/php.ini:/usr/local/etc/php/php.ini
- ./docker/php/config/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
- ../:/var/www
working_dir: /var/www
mysql:
image: mysql/mysql-server:8.0
ports:
- "${IP_ADDRESS_SETTING}3306:3306"
volumes:
- mysql:/var/lib/mysql
- ./docker/mysql/initdb.d:/docker-entrypoint-initdb.d
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD-docker}
MYSQL_USER: ${DB_USERNAME-docker}
MYSQL_PASSWORD: ${DB_PASSWORD-docker}
MYSQL_DATABASE: ${DB_DATABASE-docker}
TZ: ${TIME_ZONE-UTC}
postgres:
image: postgres:15.0-bullseye
ports:
- "${IP_ADDRESS_SETTING}5432:5432"
volumes:
- postgres:/var/lib/postgresql/data
- ./docker/postgres/initdb.d:/docker-entrypoint-initdb.d
environment:
POSTGRES_USER: ${DB_USERNAME-docker}
POSTGRES_PASSWORD: ${DB_PASSWORD-docker}
POSTGRES_DB: ${DB_DATABASE-docker}
TZ: ${TIME_ZONE-UTC}
redis:
image: redis:7.0
ports:
- "${IP_ADDRESS_SETTING}6379:6379"
volumes:
- redis:/data
redisinsight:
image: redislabs/redisinsight:latest
ports:
- "${IP_ADDRESS_SETTING}8001:8001"
mailhog:
image: mailhog/mailhog
ports:
- ${IP_ADDRESS_SETTING}1025:1025
- ${IP_ADDRESS_SETTING}8025:8025
selenium:
image: 'selenium/standalone-chrome'
volumes:
- '/dev/shm:/dev/shm'
volumes:
mysql:
driver: local
postgres:
driver: local
redis:
driver: local
設定しているコンテナの種類です。
- PHP(CLI)
- nginx
- PHP(FPM)
- MySQL
- PostgreSQL
- Redis
- Redisinsight
- Mailhog
- Selenium
workspace
がPHP(CLI)になり、このコンテナ内で作業を行います。
純粋にPHPだけの開発であれば、workspace
のみあれば大丈夫です。
.env
.env
にはdocker-compose.yml
の環境変数を設定します。
TIME_ZONE=Asia/Tokyo
LOCALE=ja_JP.UTF-8
DB_DATABASE=docker
DB_USERNAME=docker
DB_PASSWORD=docker
# Local Loopback Address(127.0.0.0/8):
IP_ADDRESS_SETTING=127.127.127.127:
IP_ADDRESS_SETTING
Dockerではポート毎に公開するIPアドレスを設定できます。
IPアドレスを設定することにより、複数のプロジェクトでのポートの重複を気にしなくて済むようになります。
また、設定したIPアドレスでhosts
を設定すると、より便利に開発できます。
127.127.127.127 php-develop.test
IP_ADDRESS_SETTING
に値を設定しない場合は、localhost
での接続になります。
IPアドレスはローカルループバックアドレスを使います。
他のIPアドレスはホストOSから到達できないと思いますが、ローカルループバックアドレスであれば自分自身(Dockerホスト)をさすので接続可能です。
docker
docker
ディレクトリにDockerfile
や各種コンテナアプリケーションの設定ファイルを設置しています。
workspace
FROM php:8.1-cli-bullseye
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=${USER_UID}
ARG LOCALE=en_US.UTF-8
ARG TIME_ZONE=UTC
ENV PKG="bash-completion curl dnsutils git imagemagick jq locales mariadb-client postgresql-client rsync sqlite3 tree unzip vim wget zip"
ENV PKG_LIB="libc-client-dev libfreetype6-dev libjpeg62-turbo-dev libkrb5-dev libmagickwand-dev libonig-dev libpng-dev libpq-dev libsqlite3-dev libxslt-dev libzip-dev"
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV DEBIAN_FRONTEND noninteractive
ENV LANG=${LOCALE}
ENV TZ=${TIME_ZONE}
COPY /usr/bin/composer /usr/bin/composer
RUN apt-get update \
&& apt-get install -y $PKG $PKG_LIB \
&& pecl install imagick redis-5.3.4 xdebug-3.1.1 \
&& docker-php-ext-configure imap --with-kerberos --with-imap-ssl \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-enable imagick redis xdebug \
&& docker-php-ext-install bcmath exif gd imap intl pdo_mysql pdo_pgsql pdo_sqlite xml zip \
#
# nodejs
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs \
&& npm install -g yarn \
#
# locale
&& sed -i -E "s/# (${LOCALE})/\1/" /etc/locale.gen \
&& locale-gen ${LOCALE} \
&& dpkg-reconfigure locales \
&& update-locale LANG=${LOCALE} \
#
# timezone
&& ln -snf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime && echo ${TIME_ZONE} > /etc/timezone \
#
# user
&& groupadd --gid ${USER_GID} ${USERNAME} \
&& useradd -s /bin/bash --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} \
&& apt-get install -y sudo \
&& echo ${USERNAME} ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/${USERNAME} \
&& chmod 0440 /etc/sudoers.d/${USERNAME}
作業を行うコンテナです。
PHP(CLI)のphp:8.1-cli-bullseye
をベースに設定しています。
COPY --from
で別のイメージからコピーしています。
作業はroot
で行わず、別ユーザで動かすのでユーザの設定を行っています。
[xdebug]
xdebug.mode=debug,develop,coverage
xdebug.start_with_request=yes
xdebug.client_host=localhost
xdebug.log_level=0
PHP(CLI)の設定を行います。
php
FROM php:8.1-fpm-bullseye
ARG USER_UID=1000
ARG USER_GID=${USER_UID}
RUN apt-get update \
&& apt-get install -y libc-client-dev libfreetype6-dev libjpeg62-turbo-dev libkrb5-dev libmagickwand-dev libonig-dev libpng-dev libpq-dev libsqlite3-dev libxslt-dev libzip-dev sqlite3 zip \
&& pecl install imagick redis-5.3.4 xdebug-3.1.1 \
&& docker-php-ext-configure imap --with-kerberos --with-imap-ssl \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-enable imagick redis xdebug \
&& docker-php-ext-install bcmath exif gd imap intl pdo_mysql pdo_pgsql pdo_sqlite xml zip \
#
# user
&& groupmod -o -g ${USER_GID} www-data \
&& usermod -o -u ${USER_UID} -g www-data www-data
PHP(FPM)のphp:8.1-fpm-bullseye
をベースに設定しています。
PHP(CLI)とPHP(FPM)のユーザが異なるとパーミッションの設定が面倒なので、UID/GIDを合わせます。
post_max_size = 520M
upload_max_filesize = 512M
[xdebug]
xdebug.mode=debug,develop,coverage
xdebug.start_with_request=yes
xdebug.client_host=workspace
xdebug.log_level=0
PHP(FPM)の設定を行います。
nginx
FROM nginx:1.23.2
RUN apt-get update && apt-get -y install openssl \
&& openssl req -newkey rsa:2048 -x509 -nodes -set_serial 1 -days 3650 \
-subj "/C=JP/ST=Tokyo/L=Chiyoda-ku" \
-keyout "/etc/ssl/private/server.key" -out "/etc/ssl/private/server.crt" \
&& chmod 400 /etc/ssl/private/server.*
https
で接続できるようにしたいので、自己SSL証明書を作成します。
server {
listen 80;
listen 443 ssl;
client_max_body_size 520M;
root /var/www/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
include fastcgi_params;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 600;
}
location ~ /\.(?!well-known).* {
deny all;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
location ~ \.(css|gif|jpeg|jpg|js|png|svg) {
access_log off;
log_not_found off;
}
ssl_certificate /etc/ssl/private/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
}
mysql
#!/bin/sh
DATABASE="${MYSQL_DATABASE}_test"
CMD_MYSQL="mysql -u root -p${MYSQL_ROOT_PASSWORD}"
echo " Creating database '$DATABASE'"
$CMD_MYSQL -e "CREATE DATABASE ${DATABASE};"
$CMD_MYSQL -e "GRANT ALL PRIVILEGES ON ${DATABASE}.* to '${MYSQL_USER}'@'%';"
テスト用データベースを作成しています。
postgres
#!/bin/bash
set -eu
DATABASE="${POSTGRES_DB}_test"
echo " Creating database '$DATABASE'"
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
CREATE DATABASE $DATABASE;
GRANT ALL PRIVILEGES ON DATABASE $DATABASE TO $POSTGRES_USER;
EOSQL
テスト用データベースを作成しています。
使用例
プロジェクトのディレクトリを作成し、VS Codeを起動します。
mkdir php-develop
cd php-develop
code .
プロジェクトのディレクトリに.devcontainer
を配置します。
VS Codeのコマンドパレット(F1)から [Remote-Containers: Reopen in Container] を実行します。
コンテナの開発環境の構築が始まります。
構築完了後、コンテナ内での開発が可能になります。
Xdebug
VS CodeにXdebugを受け入れる設定を行います。
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
}
]
}
Xdebugはバージョン3系をインストールしています。
デフォルトのport
は9003
になります。
Xdebugは、Xdebug側からIDE(VS Code)に接続するのでホスト名を正しく設定する必要があります。
-
PHP(CLI)
xdebug.client_host=localhost
PHP(CLI)とVS Codeは同じコンテナになるのでlocalhost
になります。 -
PHP(FPM)
xdebug.client_host=workspace
VS Codeはworkspace
で動いているので、workspace
を設定します。
Laravel
インストール
composer
でインストールします。
composer create-project
は空のディレクトリでないとインストールできないので、いったんtmp
に退避させてから移動します。
composer create-project --prefer-dist "laravel/laravel:9.*" /tmp/laravel
mv -n /tmp/laravel/* /tmp/laravel/.[^\.]* .
正しくインストールされていればページが表示されると思います。
設定
'timezone' => env('TZ', 'UTC'),
コンテナの環境変数が設定されている場合、このような設定も可能です。
MySQL
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=docker
DB_USERNAME=docker
DB_PASSWORD=docker
PostgreSQL
DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=docker
DB_USERNAME=docker
DB_PASSWORD=docker
Redis
REDIS_HOST=redis
REDIS_PORT=6379
Redisinsight
Mailhog
MAIL_DRIVER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
Laravel Dusk
Laravelを使ったブラウザテストです。
Seleniumを使った設定を行います。
パッケージをインストール
composer require --dev laravel/dusk
Laraveのインストール設定
php artisan dusk:install
WEBサーバ(nginx)のURL
APP_URL=http://nginx
WebDriverのURL
DUSK_DRIVER_URL=http://selenium:4444/wd/hub
テスト実行
php artisan dusk
その他
maxOSの場合、Dockerの動作が遅いという記事をよく見ます。
色々対策があるようなのでこちらを参考にしてみてください。
また、WindowsのWSL2も同様の問題があるのですが、こちらはWSL2側をマウントさせれば問題ありません。
/mnt/c
などWindows側をマウントさせると、すごく遅いです。
リポジトリ
参考
Discussion