まとめ
現状だとメリットと学習コストが釣り合ってないから、まだ使わないかも!
今後書きやすくなったり、並列ビルドの機運が出てきた時に要検討といったところでした。
インストール
linuxであればバイナリがあります。macの場合はビルドしないといけないみたいなのでcargoを使います
🎐 ❯ cargo install modus
Modusfileを書く
書くだけであればほぼDockerfileなので例を見れば理解できると思います。
FROM ubuntu
ENV DEBIAN_FRONTEND noninteractive
WORKDIR /var/www/html
RUN apt update && \
apt install -y git zip unzip libicu-dev libzip-dev
↓
app :- (
from("ubuntu"),
run("apt update && \
apt install -y git zip unzip libicu-dev libzip-dev")
)
::set_workdir("/var/www/html")
::set_env("DEBIAN_FRONTEND", "nontnteractive").
app :-
ここはappという名前の新しいruleを定義しています。 modus build . 'app'
というようにビルド対象を指定するのに使います。
他の書き方はドキュメントや、自分など他の人が書いたModusfileを参考にしてください。
実行計画を見る、文法チェックする
文法が正しいかどうかや、実際に実行するとどんなイメージが出来上がるかを確認できます。
# まずghcr.ioにログインしてください(自分の環境では必要でした)
🎐 ❯ docker login ghcr.io -u {username}
# password: {PAT}
🎐 ❯ modus check . # -> OKなら何も出ない
🎐 ❯ modus proof . 'app'
1 proof(s) found for query app
└─ app
├─ (
│ ├─ from("ubuntu")
│ └─ run("apt update && apt install -y git zip unzip libicu-dev libzip-dev")
├─ )::set_workdir("/var/www/html")
└─ ::set_env("DEBIAN_FRONTEND", "nontnteractive")
ビルドする
modusでビルドしたイメージにはタグがついてないので、つける方法もドキュメントにあります。
実行結果はjsonの形式で出せるので、それをなんやかんやしてつけます。
🎐 ❯ modus build --json . 'app'
[
{
"predicate": "app",
"args": [],
"digest": "sha256:08495686536f9e93b6a71bbe44e6633ff772a5777bc3ba328ecd4295bce14185"
}
]
# argsがない場合
🎐 ❯ modus build --json . 'app' | jq '.[] | [.digest, .predicate] | join(" ")' | xargs -I % sh -c 'docker tag %'
# args がある場合
🎐 ❯ modus build --json . 'app(x)' | jq '.[] | [.digest, .predicate + ":" + (.args | join("-"))] | join(" ")' | xargs -I % sh -c 'docker tag %'
実行する
ここまできたらただのdocker imageなので普通に使えます
Modusっぽい書き方1 - ruleの引数
いくつかの環境で同じdockerfileを使う場合、どこか1つのコマンドだけ分けたいって場面ありますよね。
例えば本番環境とローカル環境であれば、本番に必要ないライブラリはインストールしないように書きたいです。modusだとこんな感じに書き分けることができます。
setup_composer("local") :- run("composer install --no-scripts").
setup_composer("prod") :- run("composer install --no-scripts --prefer-dist --no-dev").
app(profile) :- (
from("php"),
copy("composer.json", "."),
copy("composer.lock", "."),
setup_composer(profile),
copy(".", ".")
).
実行時の指定でどちらかの環境(profile)を選ぶこともできますし、まとめて指定することもできます。
# 指定する
🎐 ❯ modus proof . 'app("local")'
# 全て
🎐 ❯ modus proof . 'app(X)' # app(profile)でも可
# 結果
2 proof(s) found for query app(x)
└─ app("prod")
├─ from("php")
├─ copy("composer.json", ".")
├─ copy("composer.lock", ".")
├─ setup_composer("prod")
│ └─ run("composer install --no-scripts --prefer-dist --no-dev")
└─ copy(".", ".")
└─ app("local")
├─ from("php")
├─ copy("composer.json", ".")
├─ copy("composer.lock", ".")
├─ setup_composer("local")
│ └─ run("composer install --no-scripts")
└─ copy(".", ".")
Modusっぽい書き方2 - 条件分岐をする
phpであればよくある(?)、「ローカル環境だけxdebugを入れたい!」といった要望もかけます。(が、仕様の制約でちょっと汚くなります)
setup_composer("local") :- run("composer install --no-scripts").
setup_composer("prod") :- run("composer install --no-scripts --prefer-dist --no-dev").
setup_xdebug :- (
run("pecl install xdebug && \
docker-php-ext-enable xdebug"),
copy("./xdebug.ini", "/usr/local/etc/php/conf.d/xdebug.ini")
).
app(profile) :- (
from("php"),
(
(
profile != "prod",
setup_xdebug
)
;
(
run("echo")
)
),
copy("composer.json", "."),
copy("composer.lock", "."),
setup_composer(profile),
copy(".", ".")
).
🎐 ❯ modus proof . 'app(x)'
2 proof(s) found for query app(x)
└─ app("local")
├─ from("php")
├─ setup_xdebug
│ ├─ run("pecl install xdebug && docker-php-ext-enable xdebug")
│ └─ copy("./docker/8.1/php_xdebug.ini", "/usr/local/etc/php/conf.d/php_xdebug.ini")
├─ copy("composer.json", ".")
├─ copy("composer.lock", ".")
├─ setup_composer("local")
│ └─ run("composer install --no-scripts")
└─ copy(".", ".")
└─ app("prod")
├─ from("php")
├─ run("echo")
├─ copy("composer.json", ".")
├─ copy("composer.lock", ".")
├─ setup_composer("prod")
│ └─ run("composer install --no-scripts --prefer-dist --no-dev")
└─ copy(".", ".")
どうやら比較する対象が同じ属性?じゃないといけないみたいです。
今回の例だと setup_xdebug
がlayer predicateだから、それと反対にもlayer predicateが必要といったところです。