MavenのDockerでUID/GIDを変える
MavenをDocker公式イメージで実行する
MavenをDockerで実行したい場合、公式イメージを使うと便利。
docker run --rm -it maven mvn archetype:generate
プロジェクトをコンテナ内ではなくDockerホスト側に置きたいなら、ホスト側のディレクトリを適当な場所にバインドマウントして(-vでもいいけど、最近は–mount推奨とのこと)、ワーキングディレクトリをそこに移して実行する。
docker run --rm -it \
--mount type=bind,src=$PWD,dst=/myproj \
-w /myproj \
maven mvn archetype:generate
ローカルリポジトリもDockerホスト側のものを使いまわしたいなら、$HOME/.m2をコンテナの/root/.m2にバインドマウントする。
docker run --rm -it \
--mount type=bind,src=$HOME/.m2,dst=/root/.m2 \
--mount type=bind,src=$PWD,dst=/myproj \
-w /myproj \
maven mvn archetype:generate
これで一応動くんだけど、Linuxの場合はMavenが作るファイルのオーナー:グループが全部root:rootになっちゃって、ちょっと扱いづらい(macOSではこの問題は無いらしい)。
MavenのUID/GIDを変える
DockerでMavenのUID/GIDをrootじゃなくするには、公式イメージの説明にもあるとおり、下記の指定を行う。
- docker runの-uオプションでUID/GIDを指定する(なお、指定したUID/GIDがコンテナ側の/etc/passwdや/etc/groupに存在しなくても、Mavenの場合は特に問題ないようだ)
- 環境変数MAVEN_CONFIGで、コンテナ内の.m2の場所を指定する
- Javaのシステムプロパティuser.homeで、コンテナ内の.m2の親ディレクトリを指定する
コマンドラインのサンプルは下記。
docker run --rm -it \
-u $(id -u):$(id -g) \
--mount type=bind,src=$HOME/.m2,dst=/myhome/.m2 \
-e MAVEN_CONFIG=/myhome/.m2 \
--mount type=bind,src=$PWD,dst=/myproj \
-w /myproj \
maven mvn -Duser.home=/myhome archetype:generate
結論としては、これでMavenがちゃんと動くし、オーナー:グループもDockerホストの実効UID:GIDになる。
あとは、これをシェルの関数にでもしておけばいい。
function mvn() {
docker run --rm -it \
-u $(id -u):$(id -g) \
--mount type=bind,src=$HOME/.m2,dst=/myhome/.m2 \
-e MAVEN_CONFIG=/myhome/.m2 \
--mount type=bind,src=$PWD,dst=/myproj \
-w /myproj \
maven mvn -Duser.home=/myhome $@
}
そうすれば、ローカルにインストールされているのと同じような感覚でMavenを使える。
mvn clean
以下、いろいろ試したメモ
-uだけ指定すると?
まず、-uだけ指定してみた。
docker run --rm -it \
-u $(id -u):$(id -g) \
--mount type=bind,src=$HOME/.m2,dst=/root/.m2 \
--mount type=bind,src=$PWD,dst=/myproj \
-w /myproj \
maven mvn archetype:generate
この場合、実行時に下記のエラーが出る。/root/.m2はコンテナ内にもともと存在するディレクトリで、root以外には書き込み権限がないためこのエラーになる。
mkdir: cannot create directory ‘/root’: Permission denied
Can not write to /root/.m2/copy_reference_file.log. Wrong volume permissions? Carrying on ...
ただし、メッセージにCarrying on ...
とあるとおり、エラーが出たあとも処理はそのまま続き、正常に終わる。では.m2/repositoryに格納されるべきアーティファクトがどこに行ったのかというと、ワーキングディレクトリの直下に?というディレクトリ(文字化けではなくて、本当にクエスチョンマーク1文字からなるディレクトリ)ができており、その中の.m2/repositoryに格納されていた。ファイルのオーナーは、ちゃんとDockerホストの実効ユーザになっていた。
.m2を新規ディレクトリにすると?
/root/.m2に書き込み権限が無いのがダメなのであれば、ということで、コンテナには存在しない/myrepoに.m2をバインドマウントして、環境変数MAVEN_CONFIGでそのディレクトリを指定してみた。
docker run --rm -it \
-u $(id -u):$(id -g) \
--mount type=bind,src=$HOME/.m2,dst=/myrepo \
-e MAVEN_CONFIG=/myrepo \
--mount type=bind,src=$PWD,dst=/myproj \
-w /myproj \
maven mvn archetype:generate
この場合、先のcannot create directory ‘/root’
のエラーは出なくなったが、.m2はやはりワーキングディレクトリの?の中にできていた。
user.homeを指定すると?
さらに、公式説明にあるとおり、Javaのシステムプロパティuser.homeで.m2の親ディレクトリを指定してみた。これが全てうまくいったケースで、前述の結論に相当する。
docker run --rm -it \
-u $(id -u):$(id -g) \
--mount type=bind,src=$HOME/.m2,dst=/myhome/.m2 \
-e MAVEN_CONFIG=/myhome/.m2 \
--mount type=bind,src=$PWD,dst=/myproj \
-w /myproj \
maven mvn -Duser.home=/myhome archetype:generate
環境変数MAVEN_CONFIGを抜くと?
ここで指定している環境変数MAVEN_CONFIGは、コンテナのエントリポイントになっている/usr/local/bin/mvn-entrypoint.shを見る限り、/usr/share/maven/refに置かれたファイルのコピー先(およびその処理のログの格納先)として使っているだけだ。コピーの後はunset MAVEN_CONFIGで消され、それからdocker runに渡されたコマンドラインの処理(つまりmvnコマンドの実行)に移るので、結局mvnコマンドにMAVEN_CONFIGの値は伝わっていない。ということは、別に-e MAVEN_CONFIG=/myhome/.m2が無くても動くのではないかと思い、抜いてみた。
docker run --rm -it \
-u $(id -u):$(id -g) \
--mount type=bind,src=$HOME/.m2,dst=/myhome/.m2 \
--mount type=bind,src=$PWD,dst=/myproj \
-w /myproj \
maven mvn -Duser.home=/myhome archetype:generate
結果は、また下記のエラーが出るようになったが(これはまあ当然)、それ以外はすべてうまくいった。
mkdir: cannot create directory ‘/root’: Permission denied
Can not write to /root/.m2/copy_reference_file.log. Wrong volume permissions? Carrying on ...
分かったこと
ということで、分かったことは次のとおり。
- docker runの-uオプションでUID/GIDを指定すれば、Mavenはその通りに動く。指定したUID/GIDがコンテナ側の/etc/passwdや/etc/groupに存在しなくても、Mavenの場合は特に問題なし。
- 環境変数MAVEN_CONFIGは(/usr/share/maven/refに物を置かない限りは)不要だが、mvn-entrypoint.shがエラーを吐かないようにするためには一応指定が必要。
- mvnに渡すuser.homeは、mvnが書き込む.m2の位置を教えるために必要。もしこれが無くて、かつ/root/.m2に書き込み権限がなければ、ワーキングディレクトリの直下に?が作られ、その中に.m2が作られる。
※バージョンメモ
- Ubuntu Server 19.04
- Docker 18.09.5
- Apache Maven 3.6.1