NAT の内側からリモートのサーバを使って PhpStorm+Xdebug+Docker で開発するメモ

例えば AWS の EC2 インスタンスに Docker Engine を入れて、その Docker を用いて PhpStorm+Xdebug で開発するメモ。

なお、PC 側は Windows 10 で WSL です。

Docker Remote API

EC2 インスタンス上の Docker で Remote API を有効にします。以下のいずれでもお好みで良いと思います。

  • TLS 有効で 0.0.0.0
    • -H 0.0.0.0:2376 --tlsverify --tlscacert=/etc/docker/ca.pem --tlscert=/etc/docker/cert.pem --tlskey=/etc/docker/key.pem
    • 証明書の管理がめんどくさくないなら
  • TLS 無効で 0.0.0.0 でセキュリティグループでソースアドレスで制限する
    • -H 0.0.0.0:2375
    • PC 側が固定IPで同じNATの内側に信頼できる端末しか無いなら
  • TLS 無効で 127.0.0.1
    • -H 127.0.0.1:2375
    • ssh でローカルポートを転送して Docker Remote API にアクセスする

今回は ssh でのポートフォワード前提になっているので、3番目の方法を用います。

sshd_config

ssh で -R で xdebug のポートをリモート→ローカルに転送する必要があるのですが、素のままだとリモートでのリッスンポートがループバックデバイスのみリッスンするので、コンテナ内の xdebug から転送しているポートにアクセスできません。

リモートの sshd_configGatewayPorts yes にすれば 0.0.0.0 でリッスンするので、コンテナ内からでも転送しているポートにアクセスできるようになります。

SSH でポートの転送

次のようにリモートサーバに ssh してポートを転送します。

ssh ore-no-server -D 1080 -R 9000:localhost:9000 -L 2375:localhost:2375 -N

-R 9000:localhost:9000 は Xdebug 用です。リモートサーバの 9000 ポートをローカルの 9000 ポートに転送します。

-L 2375:localhost:2375 は Docker Remote API 用です。ローカルの 2375 ポートをリモートの 2375 ポートに転送します。ローカルで Docker Desktop を実行しているなどで 2375 ポートが使えないなら -L 12375:localhost:2375 などと適当にポート番号は変えると良いです。

-D 1080 は Docker で Expose しているポートへブラウザからアクセスするための設定です。

.envrc

direnv の .envrc などで Docker のための環境変数を設定します。

export DOCKER_HOST=tcp://localhost:2375
export DOCKER_TLS_VERIFY=0

これでローカルの dockerdocker-compose が SSH で転送されたポートを経由してリモートで実行されるようになります。

PhpStorm 設定

Build, Execution, Deployment > Docker

Docker サーバを追加します。TCP sockettcp://localhost:2375 で OK です。

Path mappings はデフォルトの /c/Users -> C:\Users が無難だと思います。

Languages & Frameworks > PHP

CLI Interpreter でインタプリタを Docker Compose で上で追加したサーバを指定して追加します。その他の設定はプロジェクトの構成にあわせて指定します。

さらに AddtionalConfiguration optionsxdebug.remote_host でコンテナからみたリモートサーバの IP アドレスを指定します。コンテナに入って ip route show で表示されるデフォルトゲートウェイを指定すれば OK です(Docker Desktop なら host.docker.internal で良いですけど Linux だとそうもいかない)。

Languages & Frameworks > PHP > Test Framework

PHPUnit by Remote Interpreter で上で追加したインタプリタを選択して追加します。その他の設定はプロジェクトの構成にあわせて指定します。

ファイル同期

ローカルからリモートへファイルを同期させる必要があります。

バージョンを揃えたりがやや面倒ですが これ でも使用した unison が便利です。

docker run --rm -v /usr/local/bin:/x eugenmayer/unison:2.51.2.1 sh -c 'cp /usr/local/bin/unison* /x'

次のように同期を実行します。

unison . "ssh://ore-no-server/$PWD" -ignore 'Name .git' -ignore 'Name .idea' -auto -batch -repeat watch

ブラウザのプロキシ設定

ブラウザのプロキシ設定で SOCKS プロキシを localhost:1080 で設定します。 いちいち設定を切り替えるのは面倒だろので、簡単に切り替えられるアドオンを使うか、どこかに次のような proxy.pac を置いてブラウザに読ませると楽です。

function FindProxyForURL(url, host) {

    if (localHostOrDomainIs(host, "ore-no-server")) {
        return "SOCKS5 127.0.0.1:1080";
    }

    return "DIRECT";
}

ブラウザで http://ore-no-server:9876 のように Docker で Expose しているポートにアクセスできるようになります(セキュリティグループでポートを開ける必要はありません)。

さいごに

特に変わったことはしていないけど sshd_configGatewayPorts yes が必要というところにちょっと嵌ったのでメモ。

unison で vendor や node_modules まで同期されるのはやりすぎかもしれないので適宜設定は調整すると良いかもしれない。手元では↓みたいになってました。

unison . "ssh://root@ore-no-server/$(pwd -P)" \
  -ignore 'Name .git' \
  -ignore 'Name .idea' \
  -ignore 'Name public' \
  -ignore 'Name build' \
  -ignore 'Name storage' \
  -ignore 'Name vendor' \
  -ignore 'Name node_modules' \
  -auto -batch -repeat watch

ポートフォワードも -D 1080 ではなく Expose してるポートを個別に -L で転送しても良いかと思います。手元では↓みたいになってました。これなら http://localhost:9876 でアクセスできるので Docker Desktop と使用感が近くなります。

ssh ore-no-server -N -D 1080 \
  -R 9000:localhost:9000 \
  -L 3000:localhost:3000 \
  -L 9876:localhost:9876 \
  -L 12376:localhost:2376 \
  -L 3306:localhost:1330

修正したファイルが unison で同期されるまでに若干の待ちがあるのが欠点です。