Ruby
rake
Docker
docker-compose
Docker2Day 18

Dockerのコマンド簡略化とコンテナの管理について

More than 1 year has passed since last update.

Docker2 Advent Calendar 2016 18日目です。

今回僕が書こうと思っているのは、Dockerのコンテナを走らせる時のタグやバージョン、コマンドのオプションを意識せずに実行することを書こうと思っております。

Dockerのコマンドのオプションは覚えることも大事だと思うので、オプションを詳しく知りたい方は下記の記事をご覧ください。

docker コマンド チートシート

また、「そんなことをするんだったら、docker-composeでいいじゃん!」となると思うのですが、今回はdocker-composeについての紹介はしません。

今回は、docker-composeを使わず、MakeとRakeを使って実行するようにします。Makeを使う理由はUnix、Linuxですとデフォルトで使用できるので、実行しやすいからです。Rakeは、RubyのDSLで書くことができ、Makeのように実行できるので、普段Rubyを使ってるので採用しました。

docker-composeってなんだ?という方は、下記の記事をご覧ください。

docker-compose コマンドまとめ

Docker Compose - docker-compose.yml リファレンス

今回の記事の対象読者は、「Docker使い始めたばかりの人」「Dockerを知ってるけど、まだ触ったことがない人」想定しています。

今回はRailsのアプリケーションを管理しています。以下がRailsの環境をビルドするDockerfileです。Gemfile(及びGemfile.lock)は自らで用意してください。


Dockerfile

FROM debian:jessie

ENV LANG C.UTF-8
RUN apt-get update -y && apt-get dist-upgrade -y
RUN apt-get install -y \
sudo build-essential wget curl git \
libssl-dev \
mysql-client \
libcurl4-openssl-dev \
libssl-dev \
libmysqld-dev \
libyaml-dev

# Set environment
ENV app rails_app
ENV deploy /var/www/$app
ENV user rails_app_user
ENV RUBY_VERSION 2.4.0-rc1
ENV RUBY_FILE ruby-$RUBY_VERSION

# Build Ruby
RUN wget http://ftp.ruby-lang.org/pub/ruby/2.4/$RUBY_FILE.tar.gz; \
tar -zxvf $RUBY_FILE.tar.gz; \
cd $RUBY_FILE; \
./configure && make && make install; \
cd .. ; \
rm -rf $RUBY_FILE $RUBY_FILE.tar.gz;

# Set User
RUN useradd -d /home/$user -m -s /bin/bash $user
RUN echo "$user:$user" | chpasswd
RUN echo "$user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/$user
USER $user
ENV HOME /home/$user

# Working Directory
RUN sudo mkdir -p $deploy
RUN sudo chown -R $user:$user $deploy
WORKDIR $deploy

# Ruby on Rails
RUN sudo gem install bundler
ADD Gemfile $deploy/
ADD Gemfile.lock $deploy/
RUN sudo chown $user:$user Gemfile
RUN sudo chown $user:$user Gemfile.lock
RUN bundle install



Makefile


Makefile


app = rails_app
tag := $(shell git rev-parse --abbrev-ref HEAD | sed -e 's/\//_/')
root := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
args := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))

$(eval $(args):;@:)

build:
docker build -t "$(app):$(tag)" .

restart: stop start

stop:
docker rm -f $(app)

start:
docker run -itd \
-p 3000:3000 \
-v $(root):/var/www/$(app) \
--name $(app) \
$(app):$(tag) \
bash -c rm -f tmp/pids/server.pid; \
bundle exec rails s -b 0.0.0.0

login:
docker exec -it $(app) /bin/bash

logs:
docker logs $(app)

tail:
docker logs -f $(app)

test:
docker run -itd --rm \
-v $(root):/var/www/$(app) \
--name $(app)-test \
$(app):$(tag) \
bundle exec rspec $(args)


基本的には、イメージのビルド、コンテナを起動する、コンテナを削除する、コンテナのログを見る、testを実行するコマンドが書かれています。

これだけ書けば、長ったらしいdocker run ......make startで実行することができます。

またdocker-composeだと自動的にタグ名を「application_hoge」みたいな名前でイメージを作成してくれますが、ここで記述されているのは、アプリケーション名とgitのブランチ名になっています。

ブランチによってビルドするイメージを変えることによって、imageもバージョン管理するみたいなことができます。例えば、同じdocker-composeを使用すると、自動でタグ名がつけられるので、imageを上書きしてそのimageをもとにコンテナを立ち上げるので、前のバージョンのimageを残すことができなくなってしまいます(docker hubにコミットする方法もありますが)。

なので、前のバージョンのimageを使いたくなったとなれば、そのビルドした時のブランチ名に切り替えて実行すれば、そのまま使用することができるようになります。


Rakefile


docker_commands.rake

app = 'latest_rails'

tag = system("git symbolic-ref --short HEAD")

namespace :docker do
desc "build docker image"
task :build do
system("docker build -t '#{app}:#{tag}' . ")
end

desc "stop docker container"
task :stop do
system("docker rm -f #{app}")
end

desc "run docker container"
task :start do
system("docker run -itd \
-p 3000:3000 \
-v .:/var/www/
#{app} \
--name
#{app} \
#{app}:#{tag} \
bash -c rm -f tmp/pids/server.pid; \
bundle exec rails s -b 0.0.0.0"
)
end

desc "login docker container"
task :login do
system("docker exec -it #{app} /bin/bash")
end

desc "logger docker container"
task :logs do
system("docker logs #{app}")
end

desc "tail docker container"
task :tail do
system("docker logs -f #{app}")
end

desc "test docker container"
task :docker_test, :word do |task, args|
system("docker run -itd --rm \
-v .:/var/www/
#{app} \
--name
#{app}-test \
#{app}:#{tag} \
bundle exec rspec
#{args[:name]}")
end
end


Railsアプリケーションの中で、Rakeコマンドを実行しようとすると、Railsで使うGemが全てrequireされてしまいます。なので、Railsが動くコンテナを作るのにRailsが動く環境にしなければいけなくなってしまうのに注意が必要です。

ただ、rakeだけをrequireすればいいので、Rakefileを多少編集すれば問題ないでしょう。


Rakefile

-require_relative 'config/application'

-Rails.application.load_tasks

+# require_relative 'config/application'
+# Rails.application.load_tasks
+
+require 'rake'
+load 'lib/tasks/docker_commands.rake'

これで、rake docker:buildなどとコマンドを打てば、imageを作成してくれるようになります。(ただし、RubyとRakeがある必要があります。)

またそのまま開発を進めて、Rakeコマンドを実行する場合、コメントアウトは戻してください。何もできなくなります。


まとめ

といった感じで、DockerコマンドをMakeとRakeで書いてみました。Rakeの方は無理やりですが、Makeは実際に開発しているときには用いています。

またMakeでやる良さは、「dockerやそれ以外のいろいろなコマンドを組み合わせて、自分用のコマンド」ができることです。

ぜひ試してみてください。