VSCodeからDocker上のMySQLに接続する方法

今までJavaの開発はEclipseでしていましたが、VSCodeに開発環境を移しました。

Dockerを使って、なるべくローカルの環境に何も入れないようにしたかったからです。

コンテナの中で開発するとなると、Eclipseではきついので、VSCodeのDevContainersを使っています。

その環境から、VSCodeの拡張機能でDocker上のMySQLに接続しようとしたら、2時間ぐらい手こずりました。

どうせ次にやるときには忘れているので、やり方を残しておきます。

忙しい人向け

VSCodeをDevContainersでコンテナの中から使っているなら、接続先のhostにはMySQLのコンテナのIPアドレスを指定します。

IPアドレスはdocker inspectで調べられます。

ER_NOT_SUPPORTED_AUTH_MODEというエラーが出る場合は、拡張機能がMySQL8.0の既定の認証方式に対応していないのが原因です。

接続に使うユーザーの認証方式をmysql_native_passwordに変えると、つながりました。

ただしmysql_native_passwordは、MySQL8.4では既定で無効、9.0では削除されているので、この方法が使えるのは8.0までです。

前提条件

  • VSCodeのDevContainersで、Javaの開発用コンテナの中に入って作業している。
  • MySQLはDockerのコンテナで起動している。
  • VSCodeに拡張機能MySQL(formulahendry製)を入れてある。

発生環境

  • macOS(Sequoia)
  • Docker
  • VSCode+DevContainers
  • MySQL: 8.0
  • Java: 17

問題発生時のファイル構成(例)

当時のファイルは手元に残っていないので、構成の例です。

project/
├─ .devcontainer/
│  └─ devcontainer.json   # VSCodeが入るJavaの開発用コンテナ
├─ docker-compose.yml     # JavaのコンテナとMySQLのコンテナ
└─ src/

接続に使うユーザー名とパスワードは、docker-compose.ymlの環境変数などで決めているはずなので、そこで確かめます。

対処手順

手順1: MySQLのコンテナのIPアドレスを調べる

Macのターミナルで次のコマンドを実行して、MySQLが動いているコンテナのIPアドレスを調べます。

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <コンテナ名かコンテナID>

-fで、コンテナの情報のうちネットワークごとのIPアドレスだけを取り出しています(docker inspect)。

自分の環境では172.20.0.2でした。

このIPアドレスは、コンテナを作り直すと変わることがあります。

手順2: 拡張機能に接続先を追加する

VSCodeのエクスプローラーにある「MYSQL」の欄で、「+」を押します。

VSCodeのMySQLパネル(右上に+ボタン、接続先172.20.0.2)

押すと、接続先の情報を順番に聞かれるので、次のように入力します。

  • host: 手順1で調べたIPアドレス
  • user: 接続に使うユーザー
  • password: そのユーザーのパスワード
  • port: 3306(最初から入っている)
  • SSLの証明書のパス: 使わなければ空のまま。

接続できると、MySQLの中のデータベースが一覧で見られるようになります。

接続に成功したVSCodeのMySQLパネル(172.20.0.2の下にデータベース一覧)

手順3: ER_NOT_SUPPORTED_AUTH_MODEが出たら認証方式を変える

接続先を追加したあとに、ER_NOT_SUPPORTED_AUTH_MODEというエラーが出ることがあります。

自分はここで、AIに聞きながら次の手順で直しました。

MySQLのコンテナの中で、rootでログインします。

mysql -u root -p

接続に使うユーザーの認証方式を、mysql_native_passwordに変えます。

userとpasswordの部分は、自分のユーザー名とパスワードに置き換えます。

ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
FLUSH PRIVILEGES;

変わったかどうかは、mysql.userのpluginの列で確かめられます。

SELECT user, plugin FROM mysql.user WHERE user = 'user';

pluginがmysql_native_passwordになっていれば、変更できています。

これで、拡張機能から接続できるようになりました。

コンテナのIPアドレスで接続できた理由

MacのDockerDesktopでは、Macの側からコンテナごとのIPアドレスには届きません(DockerDesktopのネットワークの制限)。

コンテナは、Mac上で動く仮想マシンの中にいるためです。

それでも172.20.0.2で接続できたのは、VSCodeをDevContainersで使っていたからだと考えています。

DevContainersでは、拡張機能の多くがコンテナの中で動きます(リモート開発での拡張機能の動き方)。

MySQLの拡張機能もDockerの中から接続しに行くので、同じDockerのネットワークにいるMySQLのコンテナのIPアドレスに届きます。

逆に、DevContainersの中から見たlocalhostは、開発用コンテナ自身を指します。

MySQLは別のコンテナなので、localhostを指定してもつながりません。

なお、DevContainersを使わずにMacのVSCodeから接続するなら、MySQLのポートをportsで公開してlocalhostを指定する形になります。

こちらは今回の環境では試していません。

ER_NOT_SUPPORTED_AUTH_MODEが出た理由

MySQL8.0では、ユーザーを作ったときの既定の認証方式がcaching_sha2_passwordになりました(caching_sha2_passwordの説明)。

一方、このMySQL拡張機能は、Node.jsのmysqlパッケージを使ってMySQLに接続しています(拡張機能のpackage.json)。

このmysqlパッケージはcaching_sha2_passwordに対応しておらず、対応を求めるissueも開いたままです(mysqljs/mysqlのissue#2002)。

そのため、接続しようとすると「サーバーが求める認証方式にクライアントが対応していない」という意味のER_NOT_SUPPORTED_AUTH_MODEで止まります。

ユーザーの認証方式を古いmysql_native_passwordに戻すと、このパッケージでも接続できるようになります。

ただ、mysql_native_passwordは8.0.34で非推奨になり、8.4では既定で無効、9.0では削除されています(mysql_native_passwordの説明)。

MySQLのイメージを8.4やlatestにすると、この方法は使えなくなります。

その場合は、caching_sha2_passwordに対応した別の拡張機能やクライアントを使うことになりそうです。

まとめ

接続先にコンテナのIPアドレスを使うのも、認証方式を変えるのも、分かってしまえば難しい作業ではありませんでした。

最近はDynamoDBばかり触っていて、SQLの環境の整え方をすっかり忘れていました。

DevContainersで使うGoの開発環境の作り方は、DockerでGoの開発環境を作る記事に書いています。

参考リンク