複数言語環境を 1 つの Docker イメージにまとめる
一般的にはサービスごとに Docker コンテナを起ち上げていくことが推奨されていますが、 使いたい言語環境全部入りのコンテナがあると便利な場合もあるので作ってみる
参考
まとめかた
言語のバージョンを管理しやすくするためにも、マルチステージビルドを利用して各言語の公式コンテナからバイナリなど必要なものだけを抽出する
では、抽出すべきデータはどうやって見つける…?
基本的には言語公式コンテナを実行してwhich
やらls -l
などで辿っていく
(または言語公式の Dockerfile を眺めてもいい?)
Node.js (Alpine) の例を挙げてみると…
コンテナに入る
docker run --rm -it node:15.0.1-alpine ash
node
やyarn
などのコマンドの所在を探るwhich node
# output /usr/local/bin/node
/usr/local/bin
下にあることがわかる
/usr/local/bin
下を探るls -l /usr/local/bin
# output total 75184 -rwxrwxr-x 1 root root 116 Oct 23 17:20 docker-entrypoint.sh -rwxr-xr-x 1 root root 76977616 Oct 21 21:17 node lrwxrwxrwx 1 root root 19 Oct 23 17:20 nodejs -> /usr/local/bin/node lrwxrwxrwx 1 root root 38 Oct 23 17:20 npm -> ../lib/node_modules/npm/bin/npm-cli.js lrwxrwxrwx 1 root root 38 Oct 23 17:20 npx -> ../lib/node_modules/npm/bin/npx-cli.js lrwxrwxrwx 1 root root 26 Oct 23 17:20 yarn -> /opt/yarn-v1.22.5/bin/yarn lrwxrwxrwx 1 root root 29 Oct 23 17:20 yarnpkg -> /opt/yarn-v1.22.5/bin/yarnpkg
Command Link Link Type node
/usr/local/bin/node
ハードリンク nodejs
/usr/local/bin/node
シンボリックリンク npm
/usr/local/lib/node_modules/npm/bin/npm-cli.js
シンボリックリンク npx
/usr/local/lib/node_modules/npm/bin/npx-cli.js
シンボリックリンク yarn
/opt/yarn-v1.22.5/bin/yarn
シンボリックリンク yarnpkg
/opt/yarn-v1.22.5/bin/yarnpkg
シンボリックリンク
- 抽出すべきデータは…
/usr/local/bin
/usr/local/lib/node_modules/npm
/opt/yarn-v1.22.5
言語別抽出方法
試してみた言語の抽出を書き出してみる(間違いがあるかもしれません)
Alpine 想定で書きますが、Debian でも同じ抽出の仕方で動作しました
Node.js
FROM node:15.0.1-alpine as node
FROM alpine
COPY --from=node /usr/local/bin /usr/local/bin
COPY --from=node /usr/local/lib/node_modules/npm /usr/local/lib/node_modules/npm
COPY --from=node /opt/yarn* /opt/yarn
RUN ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn && \
ln -fs /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg
- Yarn のディレクトリ名にバージョン番号が含まれてしまっている(
/opt/yarn-vx.x.x
みたいな感じ)ので、COPY
するときにopt/yarn
に名前を変えている opt/yarn
のリネームの影響で Yarn のリンクが効かなくなっているのでシンボリックリンクを上書きしている
Python
FROM python:3.9.0-alpine as python
FROM alpine
# pip deps
RUN apk add --no-cache expat
COPY --from=python /usr/local/bin /usr/local/bin
COPY --from=python /usr/local/lib /usr/local/lib
COPY --from=python /usr/local/include /usr/local/include
- pip が
libexpat.so.1
に依存しているのでexpat
をインストールしておく- pip インストールするパッケージによってはもっと依存があるので必要があれば追加インストールする
Go
FROM golang:1.15.3-alpine as golang
FROM alpine
COPY --from=golang /usr/local/go /usr/local/go
ENV PATH $PATH:/usr/local/go/bin/
/usr/local/go/bin/
を環境変数を追加する必要あり
Rust
FROM rust:1.47.0-alpine as rust
FROM alpine
# Rust deps
RUN apk add --no-cache libc-dev gcc
COPY --from=rust /usr/local/cargo /usr/local/cargo
COPY --from=rust /usr/local/rustup /usr/local/rustup
ENV PATH $PATH:/usr/local/cargo/bin/
ENV RUSTUP_HOME /usr/local/rustup
ENV USER root
/usr/local/cargo/bin/
を環境変数を追加する必要ありCargo 動作のために少なくとも環境変数
RUSTUP_HOME
を設定する必要ありRust は環境変数
USER
がセットされていないと動作しないので設定すること一般ユーザをセットするには…
# ... RUN adduser --disabled-password username ENV USER username
V
FROM vlang:0.1.29-alpine as vlang
FROM alpine
COPY --from=vlang /opt/vlang /opt/vlang
RUN ln -s /opt/vlang/v /usr/local/bin/v
Docker Hub に V 言語のイメージはないので公式 Github レポジトリの手順で事前に手動で生成する
git clone https://github.com/vlang/v cd v docker build -t vlang:x.x.x-alpine --file=Dockerfile.alpine .
- バージョンタグつけておくとよい
V 言語は
root
権限でしか使いこなせないようになっているので、一般ユーザで使いたい場合はchown
で/opt/vlang
の所有者を変えておくとよい# ... RUN chown -R user:user /opt/vlang && \ ln -s /opt/vlang/v /usr/local/bin/v
All in One
ということで Node.js, Python, Go, Rust, V 全部入りのコンテナは…
FROM node:15.0.1-alpine as node
FROM python:3.9.0-alpine as python
FROM golang:1.15.3-alpine as golang
FROM rust:1.47.0-alpine as rust
FROM vlang:0.1.29-alpine as vlang
FROM alpine:3.12.0
RUN adduser --disabled-password user
ENV USER user
# clang, c++, and deps
RUN apk add --no-cache libc-dev gcc g++ make cmake tzdata
# nodejs
COPY --from=node /usr/local/bin /usr/local/bin
COPY --from=node /usr/local/lib/node_modules/npm /usr/local/lib/node_modules/npm
COPY --from=node /opt/yarn* /opt/yarn
RUN ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn && \
ln -fs /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg
# python
COPY --from=python /usr/local/bin /usr/local/bin
COPY --from=python /usr/local/lib /usr/local/lib
COPY --from=python /usr/local/include /usr/local/include
RUN pip install pipenv
# golang
COPY --from=golang /usr/local/go /usr/local/go
ENV PATH $PATH:/usr/local/go/bin/
# rust
COPY --from=rust /usr/local/cargo /usr/local/cargo
COPY --from=rust /usr/local/rustup /usr/local/rustup
ENV PATH $PATH:/usr/local/cargo/bin/
ENV RUSTUP_HOME /usr/local/rustup
# vlang
COPY --from=vlang /opt/vlang /opt/vlang
RUN chown -R user:user /opt/vlang && \
ln -s /opt/vlang/v /usr/local/bin/v
USER user
WORKDIR /home/user