Help us understand the problem. What is going on with this article?

自作コマンド配布ツールとしてDockerを使う

More than 3 years have passed since last update.

コマンド配布ツールとしてDockerは使えるか?

まずは論よりなんとやら。Docker導入済みの方は、下記のコマンドを実行してみてください。

Terminal
$ docker run -i --rm greymd/cureutils cure precures

すると、下記のようにDockerhubからDockerのイメージの取得が始まり、そのイメージに含まれるcureコマンドが実行されます。このコマンドは、以前私が自作したコマンドで、プリキュア1全員2の名前を表示できます。

Terminal
Unable to find image 'greymd/cureutils:latest' locally # Dockerhubからイメージの取得が始まる
latest: Pulling from greymd/cureutils

efd26ecc9548: Pull complete
a3ed95caeb02: Pull complete
d1784d73276e: Pull complete
72e581645fc3: Pull complete
9709ddcc4d24: Pull complete
7013ecfd652c: Pull complete
66373b9c2e12: Pull complete
2a6578ab609c: Pull complete
4356bd31ea9f: Pull complete
34ea4e1d4a33: Pull complete
Digest: sha256:8ae33b900e562e9cbafb8bd6dda45b658521b9e81d999bd5bfd34e40c5a61015
Status: Downloaded newer image for greymd/cureutils:latest
キュアブラック # コマンドが実行される
キュアホワイト
シャイニールミナス
キュアブルーム
キュアイーグレット
キュアドリーム
キュアルージュ
キュアレモネード
# (中略)
Terminal
# 一旦イメージを取得してしまえば、以降はスムーズにできる
$ docker run -i --rm greymd/cureutils cure precures
キュアブラック
キュアホワイト
シャイニールミナス
キュアブルーム
# (中略)

このcureコマンドを使うためには、本来はRuby2.0.0以上を用意してgemでインストールする必要があります。しかし上記の方法で、それらが入っていない環境でもDockerを使ってコマンドを実行することができました。

とはいえ、わざわざcureコマンドを使うのに、毎回docker...ほにゃららなどと打っていられないと思います。そういう時は、下記のような関数を.bashrcに追加します。

.bashrcの一部
cure () {
    [ -t 0 ] && T="t" || T=""
    docker run -i$T --rm greymd/cureutils cure "$@"
}

すると、まるでホスト上にcureコマンドがインストールされたかのように、cureコマンドを使うことができます。標準入力をフィルターできますし、オプションやコマンド引数も動作します。実際にホストに直接cureコマンドはインストールしていないにもかかわらず、です。

Terminal
$ cure echo -a -p cure_heart -q
プリキュアラブリンク!
L! O! V! E!
みなぎる愛! キュアハート!
響け愛の鼓動!ドキドキプリキュア!
愛を無くした悲しいジコチューさん、
このキュアハートがあなたのドキドキ取り戻してみせる!
あなたに届け!マイスイートハート!
Terminal
$ echo キュア{ハート,ダイヤ,エース,スペード} | xargs -n 1 | cure grep
キュアハート
キュアエース

このような方法で、ホスト側からの標準入力を、コンテナ内のコマンドでフィルタリングして、ホスト側の標準出力に結果として出力できます。この方法を知った時「自作コマンド配布のためにDockerのイメージ使うのってどうなんだろう?流行る可能性はあるのだろうか?」と思ったので、以下、作り方をざっくりとまとめ、軽く考察します。

試した環境

Mac OSX Yosemite 10.10.5の端末上で実施。
zshを使って動作確認してますが、多分bashでも動きます。

Terminal
$ zsh --version
zsh 5.0.6 (x86_64-apple-darwin13.3.0)

DockerはMac OSXのネイティブ版Dockerを使います。

Terminal
$ docker --version
Docker version 1.12.5, build 7392c3b

作り方

動かしたいコマンドが動くDockerのイメージを作る

例えば私の場合、cureコマンドを動かすために下記のようなDockerfileを作りました。依存するDockerイメージとしてruby:2.3.0を指定してあります(OS自体はDebianです)。やっていることは、リポジトリからソースをクローンして、rakeでビルドし、bundleでインストールしています。この一連の作業で、cureコマンドがPATHが通っている/usr/local/bundle/bin/cureに置かれました。
https://github.com/greymd/dockerfiles/blob/master/cureutils/debian/Dockerfile

また、今回の場合cureコマンドは日本語を出力します。なので、イメージの文字エンコーディングの諸々の設定を全てja_JP.UTF-8にしてあります。

Dockerfile
FROM ruby:2.3.0
RUN apt update && \
    apt install -y locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.utf8 && \
    cd ~ && \
    git clone https://github.com/greymd/cureutils.git && \
    cd cureutils && \
    gem install bundler rake && \
    bundle install && \
    rake build && \
    find ./pkg/ -type f | head -n 1 | xargs gem install && \
    cd .. && \
    rm -rf cureutils
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8

Dockerfileがあるディレクトリでビルドします。

Terminal
# docker build -t <Dockerhubのユーザ名>/<イメージ名> .
$ docker build -t greymd/cureutils . 

DockerhubにPushする

Dockerのイメージを登録できるサービスであるDockerhubにイメージを登録します。事前にDockerhubのアカウントを作成するなどの作業が必要です。こちらの記事によくまとまっていたので、説明は割愛します。
http://qiita.com/moru3/items/32931813db81d891effb

Terminal
$ docker login
$ docker push greymd/cureutils

こうして、docker pullでイメージが取得できれば成功です。

Terminal
$ docker pull greymd/cureutils

.bashrcに追加

再掲になります。

.bashrc
cure () {
    [ -t 0 ] && T="t" || T=""
    # docker run -i$T --rm <Dockerのイメージ名> <コマンド名> "$@"
    docker run -i$T --rm greymd/cureutils cure "$@"
}

[ -t 0 ] && T="t" || T=""という箇所で、標準入力が無ければ Tという変数に"t"という文字列を入れ、標準入力があれば空文字を入れます。これでdocker runコマンドの-tオプションの有無を制御しています。

-tオプションがあると、標準入力がコンテナ内のコマンドまで渡らないので、フィルタのような動きができません。そこで標準入力がある場合は-tを無効にしています。逆に、常に-tオプションを無効にすると、スリープをかけながら標準出力するコマンド3が正しく動かないため、基本的には有効にしています。

配布する時

人様に自作コマンドを使ってもらいたいときは、
1. Docker入れて!
2. .bashrcmy_command () ... って関数入れて!

ってやればできる。(2はzplugのほうが良いかもしれない。)

考察

この方法の利点

  • 環境の影響を気にせずコマンドを配布できる。
    • コマンドの動作が標準入出力のみで決定するようであれば、環境の差分はないハズ?
  • 導入が面倒なコマンドを配布するのに向いている
    • パッケージ管理システムで配布されておらず、色々入れてビルドが必要なものとか。
  • 異なるバージョンのインタプリタが必要なコマンドを共存するのに役立つ。
    • 例: コマンドAはRuby2.0でしか動かない。コマンドBはRuby2.1でしか動かない。じゃあ片方Dockerにして、ホストのPATHrubyはRuby2.1にしよう、みたいな。

この方法の欠点

  • 標準入出力以外の要素が介在すると、コマンドが正しく動くとは限らない。(例: ディレクトリやファイル、GUI、ネットワークなど)
  • ホストとコンテナ間の情報のやり取りのオーバーヘッドがあるのか、比較的動作が遅い。コマンドに巨大なデータを投げるのには向かない。(例: grep, awkなど)

所感

標準入出力以外のホストのリソースを簡単に使えないため、総合的には微妙な気がしました。ただ、コマンドをこんな感じでDockerのイメージと抱き合わせで配布しやすくするための、パッケージ管理ツール的なものがあれば、需要はありそうだと思いました。

おまけ(活用例)

Mac OSXだとgrepコマンドがデフォルトではBSD版ですが、この手法を使うことでGNU版のコマンドが使えます(coreutils使えって話ですが)。

Terminal
# BSD版にはない-Pオプションが動く
$ seq 10 20 | docker run -i ubuntu:16.04 grep -P '.0'
10
20

  1. 日本が世界に誇る国民的アニメ。2017年1月現在「魔法つかいプリキュア」絶賛放映中! 

  2. 2017年1月時点で、TVシリーズのメインヒロイン44人。厳密には「伝説の戦士プリキュア」ではない「キュア」のつかない戦士に変身する女の子も含みます。残念ながら満と薫、レジーナなどは含まないので悪しからず。 

  3. cure echoコマンドのことです。 

greymd
ハイパーシェル芸キュアエンジニア
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした