1. Qiita
  2. 投稿
  3. docker

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

  • 2
    いいね
  • 0
    コメント

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やそれ以外のいろいろなコマンドを組み合わせて、自分用のコマンド」ができることです。
ぜひ試してみてください。