sioaji2012のブログ

普段は組み込み開発でC言語のみです。主にプログラムや勉強日記です

Redmineプラグインもくもく 山梨 #007回 プラグイン作成試し

前回、

Rubyの記述の意味がわかりませんでした

これ。

module RedmineIine
  class Hook < Redmine::Hook::ViewListener
    def view_layouts_base_content(context = {})
      controller = context[:controller]
      if controller.controller_name == 'issues' && controller.action_name == 'show'
        controller.render partial: 'redmine_iine/hooks/view_layouts_base_content'
      end
    end
  end
end

少しだけ、勉強しようと思いますが・・・。

(本来は体系的な勉強しなければいけないですが・・・すみません。。。)

モジュール - Ruby入門

Redmineプラグイン作成でHookを使ってみる - torutkの日記

上記ページからの抜粋になりますが、調べた結果を書きます。

Rubyではクラスという概念に似たものとしてモジュールという概念があります。

・モジュールはクラスと同じくメソッドを定義する事が出来ます。
・クラス変数に相当するものはモジュールにはありませんが、
 定数は定義する事が出来ます。
・クラスはクラスからオブジェクトを作成することが出来ますが、
 モジュールでは作成することは出来ません。

フックへ登録するコールバックの定義

 まず、フックのコールバックを実装するクラスを定義します。
 このクラスは、Redmine::Hook::ViewListenerクラスを継承します。
 クラス定義を記述するファイルは、プラグインのlib下に置きます

lib/redmine_iine/hook.rb

module RedmineIine ★モジュール名
  class Hook < Redmine::Hook::ViewListener ★クラスを継承
    def view_layouts_base_content(context = {}) ★クラスと同じメソッド
      controller = context[:controller]
      if controller.controller_name == 'issues' && controller.action_name == 'show'
        controller.render partial: 'redmine_iine/hooks/view_layouts_base_content' ★部分テンプレート
      end
    end
  end
end

・モジュールの利用方法としては、
 「モジュール名.メソッド名」の形式で関数のように実行するか、
 他のクラスの中にインクルードして利用することが出来ます。

Redmineプラグイン作成の情報を調べていると、
 moduleを定義している例と定義していない例を見かけますが
 (定義していないものが多い感触)、
 書籍「Redmine Plugin Extension and Development」では
 moduleを導入しているのでそれに倣っています。
 moduleにはプラグイン名をPascalケースにした文字列を指定しています。

・クラスの中身ですが、フック名と同じ名前のメソッドを定義して、
 その中で文字列(HTML)を作成しreturnするサンプルをよく見かけますが、
 書籍ではrender_onでフック名と部分テンプレートを指定している。

部分テンプレートは、ここで指定した名前の先頭にアンダースコアを付け、
 拡張子を.erbとしたファイルに記述します
 (拡張子は、.html.erbなどでも可)。
 ファイルの場所はプラグインのapp/views/redmine_iine/hooks/の下に置きました。

app/views/redmine_iine/hooks/_view_layouts_base_content.html.erb

<%= link_to 'いいね!', '#', class: 'redmine-iine', style: 'display: none;' %>

<script>
  $('.redmine-iine').on('click', function(e) {
    e.preventDefault();
    alert('いいね!');
  });

  $(function() {
    $('.redmine-iine').prependTo('#content .contextual').show();
  });
</script>

フックへ登録(init.rb)

init.rbに次を記述します。

require_dependency 'redmine_iine/hook'

libしたのlib/redmine_iine/hook.rbが呼び込まれます。

部分テンプレートを適用する条件

controller = context[:controller]
if controller.controller_name == 'issues' && controller.action_name == 'show'
  controller.render partial: 'redmine_iine/hooks/view_layouts_base_content' ★部分テンプレート
end

実装したい画面のコントローラ名:'issues'
アクション名: 'show'
これらを探すときは、
・実際にRedmine操作して、ログから追うのが早い。
・あとは、routesを読むか。
(ハンズオンより)


今回は、以上です。
わかった様な気になっているだけですが、次に進もうと思います。
只今、体調が絶不調中。。。会社も休みがちで、もくもくもあまり進められませんでした。


Redmineプラグインもくもく 山梨 #006回 プラグイン作成試し

前回、init.rb を書いただけという状態でした。 今回は、こちら。

チケット詳細ページにいいねを表示する。

既存のviewに何か要素を追加するときは、hook + jsで!

公式ページ hookリスト

www.redmine.org

既存画面に何か追加したい場合に、
上記のHooks List の中にあるView Hooksを使って実装していく。

パーシャル(Viewの断片ページ)を呼ぶ。

View Hooksでerbを挿入することになるが、
表示したいhtmlをそのまま挿入とせず、Javascriptで要素を差し込むような感じ。
Redmine本体のビューがとても変わりやすい為、JavaScriptで埋め込んだほうが後で柔軟に対応できる。

■app/views/redmine_iine/hooks/_view_layouts_base_content.html.erb
・'いいね!'のリンクにclass 'redmine-iine'を設定して、クラスで要素を指定。
・リンクが押されたらアラート表示。
・リンクは、'#content .contextual'要素の手前に追加表示する。

<%= link_to 'いいね!', '#', class: 'redmine-iine', style: 'display: none;' %>

<script>
  $('.redmine-iine').on('click', function(e) {
    e.preventDefault();
    alert('いいね!');
  });

  $(function() {
    $('.redmine-iine').prependTo('#content .contextual').show();
  });
</script>

汎用的なhookポイントを使う

RedmineのHooksでほしい位置に挿入できるようなHooksがないことがある。
(チケットの下に差し込むHookはあっても、上に差し込むのがなかったりなど)

Hookを上記ページからがんばって探すより、
汎用的に使える view_layouts_base_content で差し込んじゃう方を選ぶ感じ。

■lib/redmine_iine/hook.rb
・view_layouts_base_contentのHookを使う。
・html のbody のボトムに差し込まれる
・ここにおいて、(disply: none)、JSで置き換える。

module RedmineIine
  class Hook < Redmine::Hook::ViewListener
    def view_layouts_base_content(context = {})
      controller = context[:controller]
      if controller.controller_name == 'issues' && controller.action_name == 'show'
        controller.render partial: 'redmine_iine/hooks/view_layouts_base_content'
      end
    end
  end
end

■init.rb

require_dependency 'redmine_iine/hook'

Redmine::Plugin.register :redmine_iine do
  name 'Redmine いいね!Plugin'
  author 'sioaji'
  version '0.0.1'
end

実装したい画面のコントローラとアクション名を探すときは

実際にRedmine操作して、ログから追うのが早い。
あとは、routesを読むか。

ここまでの結果

表示した "いいね!" を押した場面。

f:id:sioaji2012:20180606211156p:plain


今日は、ここまで。
やっぱり、途中のフック指定するところが、ruby on rails な感じで
勉強しないとわからないので、お勉強しなければならない。…ですね。(^_^;)

 

Redmineプラグインもくもく会 山梨 #005回 プラグイン作成試し

これまでの開発環境を駆使するには、DockerやRailsなど詳しく勉強する必要があるかもしれませんが、一気にやっても多分忘れてしまうので(という理由で)後回しにしつつ、以前ハンズオンでちんぷんかんぷんだったものをトレースしながら、プラグイン作成にそって勉強を少しずつしていこうと思います。

agileware.connpass.com

それでは、こちらのハンズオンを思い出していきたいと思います。

プラグインを作成する

プラグインチュートリアルでは、ジェネレータでプラグインの雛形を作る方法が紹介されているのですが、ジェネレータの情報アップデートがいまいちらしく、失敗する事があるので信用していないという事でした。(ベタに作成することになります)

あと、ハンズオンはRedmine4に向けたハンズオンなので、今回の3.3.2環境ではうまくいかない可能性あり、途中でギブになる可能性あります。(^_^;)

題材は、『いいね!ボタン』です。

プラグインフォルダを作成

Dockerの開発環境用Redmineを起動します。

$ REDMINE_VERSION=3.3.2 docker-compose build --no-cache
$ docker-compose up -d 

作ったドッカー内に入ります。
■ docker exec -it 名前など /bin/bash
・-i : Interactive
・-t: terminal

$ docker exec -it redmine_sqlite3 /bin/bash

中に入れました。

root@ac584a652c94:/tmp/redmine# ls
CONTRIBUTING.md  Gemfile.lock  Rakefile  appveyor.yml  config     db   extra  lib  plugins  script  tmp
Gemfile      README.rdoc   app   bin           config.ru  doc  files  log  public   test

プラグイン用フォルダ『redmine_iine』を作成します。

cd plugins
mkdir redmine_iine

init.rbを作成

プラグインが最初に読み込むものを作成します。

ここからは、MAC側からソース修正してみます。 
Dockerfileと同じフォルダにpluginsフォルダを作ってあって、このフォルダがDockerコンテナ内のpluginsフォルダとの共有フォルダになっています。
(docker-compose.ymlで設定してる)

ctrl+p -> ctrl+q で抜けるとdockerコンテナは起動したまま、抜けられます。

ctrl+p
ctrl+q

MAC側のpluginsフォルダの中には、先程コンテナ内で作成した『redmine_iine』フォルダが作成されているのがわかると思います。

テキストエディタ等で、『init.rb』ファイルを作成します。

init.rb

Redmine::Plugin.register :redmine_iine do
  name 'Redmine いいね!Plugin'
  author 'sioaji'
  version '0.0.1'
end

ここで、通常ですと、プラグインを再読込のために、
railsサーバーをCtrl-C停止後、再起動するのですが、
現状のDocker構成でのやり方がわかりませんでした。

$ docker-compose restart
とか
$ docker stop redmine_sqlite3
$ docker start redmine_sqlite3
とかやっても
再起動されたコンテナがすぐに終了してしまう

困って、ググりました。

dev.classmethod.jp

unskilled.site

けど、良くわからず、、、一応ですが、

docker-compose.ymlを修正する事にしました。

★★★マークの行追加。

version: '3'
services:
  web:
    build:
      context: .
      args:
        REDMINE_VERSION: $REDMINE_VERSION
    image: redmine_sqlite3
    container_name: redmine_sqlite3
    command: >
      bash -c "bundle exec rake redmine:plugins:migrate &&
           bundle exec rails s -p 3000 -b '0.0.0.0'"
    environment:
      RAILS_ENV: development
    volumes:
      - ./plugins:/tmp/redmine/plugins
    ports:
      - "3000:3000"
    stdin_open: true ★★★
    tty: true ★★★

stdin_open: 標準入力
tty: 端末の付与

相当するdocker runオプション −it と同等になる様で、
こうすると、すぐにコンテナが終了しないっぽい。
(よく理解できていないです)

この構成でイメージ作り直して起動します。

$ REDMINE_VERSION=3.3.2 docker-compose build --no-cache
$ docker-compose up -d 

再起動する

docker-compose restart

で再起動した感じになる。
pluginのマイグレートとサーバーの起動するのかな。
二重起動とか心配。
(必ずうまくいくわけでは無さそう。すぐに終了しないでうまくいく場合がおおい感じ)

・・・ひとまずこのやり方で進めます。(^_^;)

ここまでの結果

f:id:sioaji2012:20180530234641p:plain


今回は、ここまで。ほとんど進まず(^_^;) 
次回は、いいね!ボタン表示とhookを少し。な感じでしょうか。

先は長いかも。。です。。。


Redmineプラグインもくもく会 山梨 #004回 開発環境

(まだ)開発環境の続きです。(遅い・・・(^_^;) )

DockerCompose

・Dockerツール群の一つであるDockerComposeを使うと、コンテナ立ち上げ設定をファイル化(yml形式)して管理することが可能になります。
・docker-compose で起動する場合は、Dockerfileを使ってイメージを作成します。

Docker Composeについてもほんの少し(汗)勉強しました。

Docker お勉強 04 Docker Compose - sioaji2012のブログ

対象の docker-compose.yml はこちら。

version: '3'
services:
  # start service for redmine with plugin
  # 1. $ docker-compose build --force-rm --no-cache
  # 2. $ docker-compose up -d
  #
  # If you want to change Redmine's version,
  #
  # 1. $ REDMINE_VERSION=master docker-compose build --force-rm --no-cache
  # 2. $ docker-compose up -d
  #
  #
  web:
    build:
      context: .
      args:
        REDMINE_VERSION: $REDMINE_VERSION
    image: redmine_sqlite3
    container_name: redmine_sqlite3
    command: >
      bash -c "bundle exec rake redmine:plugins:migrate &&
           bundle exec rails s -p 3000 -b '0.0.0.0'"
    environment:
      RAILS_ENV: development
    volumes:
      - ./plugins:/tmp/redmine/plugins
    ports:
      - "3000:3000"

勉強不足ですが、少しだけ説明書いてみます。

★今の最新は compose Ver3
version: '3'

★『WEB』というサービス名となる
services:
  web:

    ★構築時に適用するオプションを指定
    build:

      ★Dockerfile があるパス(や Git URL)を指定 → カレントDIR
      context: .

      ★構築時の引数指定
      args:
        REDMINE_VERSION: $REDMINE_VERSION

    ★ローカルのimageから検索して、なけれ DockerHubからの取得を試みる。
   (buildを指定した場合は構築する)
    image: redmine_sqlite3

    ★カスタム・コンテナ名
    container_name: redmine_sqlite3

    ★デフォルトのコマンドを上書きする
    ★DockerFileはrailsサーバー起動のみで、
     こちらはpluginマイグレードを追加したコマンドにしてる。
    ★docker run だとプラグインなしでRedmine起動。
     composeはプラグインつき起動になる。
    command: >
      bash -c "bundle exec rake redmine:plugins:migrate &&
           bundle exec rails s -p 3000 -b '0.0.0.0'"

    ★環境変数を追加する
    environment:
      RAILS_ENV: development

    ★コンテナ内にマウントするボリュームを指定する( ホスト:コンテナ )
    ★コンテナ内は、Redmineの標準pluginsフォルダ
    ★ホスト側は、カレントDirにpluginsというフォルダを作っている。
     MAC側でこの中にpluginソースを配置して開発する。
    volumes:
      - ./plugins:/tmp/redmine/plugins

    ★公開用のポートを指定する(ホスト側:コンテナ側)
    ports:
      - "3000:3000"

実行オプション docker-compose build

REDMINE_VERSION=3.3.2 docker-compose build --no-cache
docker-compose up -d

こちらも少しだけ説明してみます。

★引数を指定 REDMINE_VERSION=3.3.2
★構築する build
 オプション:
 --force-rm  常に中間コンテナを削除
 --no-cache  構築時にイメージのキャッシュを使わない ★こちら
 --pull      常に新しいバージョンのイメージ取得を試みる
REDMINE_VERSION=3.3.2 docker-compose build --no-cache

★サービス用のコンテナの構築、作成、起動、アタッチを行う:up
 オプション:
 -d  デタッチド・モード: バックグラウンドでコンテナを実行し、新しいコンテナ名を表示
docker-compose up -d

Dockerファイルを基本に適時Compose側の設定で構築・起動していく感じでしょうか。

以上が、DockerComposeの簡単な説明になります。
(もし余裕が出来たらDockerをもっと勉強しなくては。。。その前にRuby On Railsもですが。。。)

build結果

最後の方のログ (Dockerfileのdb:migrate終わったあたりから)
コマンドのステップありますが、実行されていない状態だと思います。

== 20160529063352 AddRolesSettings: migrated (0.0005s) ========================

Removing intermediate container 244ba24cd9b2
 ---> f7771e68733e
Step 13/15 : RUN bundle exec rake generate_secret_token
 ---> Running in 81f23bf15e13
Removing intermediate container 81f23bf15e13
 ---> 4e49572fc819
Step 14/15 : CMD bundle exec rails s -p 3000 -b '0.0.0.0'
 ---> Running in 0fc6f20e4369
Removing intermediate container 0fc6f20e4369
 ---> 41e55eadecaa
Step 15/15 : EXPOSE 3000
 ---> Running in 8b167069180b
Removing intermediate container 8b167069180b
 ---> 2315b3a0cca7
Successfully built 2315b3a0cca7
Successfully tagged redmine_sqlite3:latest

起動 (docker-compose up -d)

mbp:redmine_my_plugin sioaji$ docker-compose up -d
WARNING: The REDMINE_VERSION variable is not set. Defaulting to a blank string.
Recreating redmine_sqlite3 ... done

起動したコンテナの確認(docker ps)

mbp:redmine_my_plugin sioaji$ docker ps

CONTAINER ID        IMAGE               COMMAND                  CREATED
c288f733f5dc        redmine_sqlite3     "bash -c 'bundle exe…"   About a minute ago

STATUS              PORTS                    NAMES
Up About a minute   0.0.0.0:3000->3000/tcp   redmine_sqlite3

注意する事がありました。

同じことを何回もやると、古いイメージデータが名無しで残り続ける(HDD容量減る)ので注意。  
 
今のイメージ(ID = 2315b3a0cca7)

mbp:redmine_my_plugin sioaji$ docker images

REPOSITORY        TAG        IMAGE ID         CREATED          SIZE
redmine_sqlite3   latest     2315b3a0cca7     16 minutes ago   937MB

 
2回目のイメージ(ID = 420c17967e35)
ID = 2315b3a0cca7 は名無しで残り続ける。(タグがついていない)

mbp:redmine_my_plugin sioaji$ docker images

REPOSITORY        TAG        IMAGE ID         CREATED          SIZE
redmine_sqlite3   latest     420c17967e35     4 minutes ago    937MB
<none>            <none>     2315b3a0cca7     23 minutes ago   937MB

不要なイメージを消す

docker rmi イメージID

$ docker rmi 2315b3a0cca7

もくもくで使いそうなDockerコマンド

殴り書きですが、使いそうなコマンドを書いておきます。

http://localhost:3000
------------------------------------------------------------
/* 基本 */
------------------------------------------------------------
$ docker-compose build
$ docker-compose up -d

------------------------------------------------------------
/* バージョン指定 */
------------------------------------------------------------
Dockerfile FROM ruby:2.2.2

$ REDMINE_VERSION=3.3.2 docker-compose build --no-cache
$ docker-compose up -d

------------------------------------------------------------
/* 構築済みのイメージを起動する */
------------------------------------------------------------
■ docker start コンテナ
$ docker start redmine_sqlite3

■ docker stop コンテナ

------------------------------------------------------------
/* 作ったドッカー内に入る */
------------------------------------------------------------
■ docker exec -it 名前など /bin/bash
-i : Interactive
-t: terminal 

$ docker exec -it redmine_sqlite3 /bin/bash

------------------------------------------------------------
/* コンテナを外から止めるか中から止めるか */
------------------------------------------------------------
■ 中から止める
exitでコンテナから抜けた時のコンテナ起動→接続

$ sudo docker start <コンテナID>
$ sudo docker attach <コンテナID>

ctrl+p -> ctrl+q で抜けるとdockerコンテナは起動したまま、抜けられる
$ sudo docker attach <コンテナID>
で再入場


■ 外から止める
$ sudo docker stop <コンテナID>
$ sudo docker start <コンテナID>

$ sudo docker stop redmine_sqlite3

------------------------------------------------------------
/* 作業中のコンテナをイメージとして保存する */
------------------------------------------------------------
■ docker commit 名前など 保存イメージ名

$ docker commit redmine_sqlite3 create_ticket

⬇
イメージ一覧で確認
$ docker images

------------------------------------------------------------
/* コンテナ消す */
------------------------------------------------------------
$ docker rm コンテナIDとか名前していする

- docker start: コンテナを起動する
- docker stop : コンテナを止める(コンテナプロセスにSIGTERMを送信して一定時間経過後にSIGKILLを送信、デフォルトは10秒後)
- docker kill : コンテナを止める(直ちにSIGKILLを送信)
- docker rm : 停止状態のコンテナを削除 
- docker rm -f : 起動状態のコンテナも削除 
- docker ps -aq | xargs docker rm -f : 全部のコンテナをまとめてrm
  or
- docker rm -f 'docker -aq'

------------------------------------------------------------
/* イメージ消す */
------------------------------------------------------------
$ docker rmi <IMAGE ID> 

■未使用イメージ一括削除
$ docker image prune

■タグがついていないイメージをすべて削除する
docker rmi $(docker images -f 'dangling=true' -q)

------------------------------------------------------------
/* docker run */
------------------------------------------------------------
■名前つける
$ sudo docker run -it --name infra-workshop-sioaji centos
$ sudo docker rename 変更前の名前 変更後お名前

■docker run -v のお話
-v : volume

$ sudo docker run -it --name "mamy1326" -v /home/psuser:/home/test centos
[option] image [command] を守ればオプションの順番は問われないと思いますー
イメージ名は最後

参考:Docker の Data Volume まわりを整理する
https://qiita.com/lciel/items/e21a4ede3bac7fb3ec5a

■ 起動したコンテナのIPを知らべる
# for cname in $(docker ps |grep -v NAMES|awk '{print $1}'); do echo -n "${cname} ";docker inspect -format="{{ .NetworkSettings.IPAddress }}" ${cname}; done

00c5ccb98f17 ormat=172.17.0.8
39d0cd55f2f3 ormat=172.17.0.7
bf091cc5eafa ormat=172.17.0.6
4b2898c6f5a0 ormat=172.17.0.5
08d6607d8676 ormat=172.17.0.4
cb8719479fd3 ormat=172.17.0.3
dac5fd5ff2e9 ormat=172.17.0.2



今回は以上でした。
このままで、コンテナのrailsを再起動する方法がわかりませんでした。どうやってやるのかな。
developmentなので、再起動不要らしいのですが。。。

次回からは、以前ハンズオンでちんぷんかんぷんだったプラグインを写経してみようと思います。


Redmineプラグインもくもく会 山梨 #003回 開発環境

前回、plugin開発用のRedmineが起動できる様になりました。

qiita.com

こちらのページで、akikoさんが丁寧に説明されているので、説明は不要と思いますが、もくもくの覚え書きとして、僕なりの解釈で説明を書いてみようと思います。

と言いましても、結局Dockerfileを初めて読みまして、ちんぷんかんぷん。でしたので、

Dockerfile 少し勉強しました。

だいたい写経で勉強した次の日には、忘れてしまいます。(^_^;)

なので、ひとまず、もくもくで自分用に書き換えたDockerfileから説明を書いていきます。


その前に、

注意すること

がありました。皆さんはご存知と思いますが。。。

Dockerfileの中で、RUN でコマンドを実行するたびにイメージがコミットされて、 次回、Dockerfileを実行した時は、そのキャッシュが使われる。

つまり、Dockerfileを書き換えても、前のRUN行は再実行されない。


例で説明しますと、

1回目。updateとinstallは実行される。

FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl ★この行を修正する

書き換え後、

2回目。updateはキャッシュが使われ古いupdate結果が使われる。(updateは再実行されない)

FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl nginx ★この行を修正した

キャッシュを使わないでDockerfile実行する設定にすれば、良いですが時間がかかる。
こう書くのがおすすめっぽい。

FROM ubuntu:14.04
RUN apt-get update && apt-get install -y curl nginx

皆さん、Dockerをどういう用途で使っているのか気になりますが、

もくもくの説明に入りたいと思います。

.-------.
★が説明
.-------.

------------------------------------------
★ruby:2.1.10のイメージをベースにして作成
------------------------------------------
FROM ruby:2.1.10

------------------------------------------
★ラベル(説明省略)
------------------------------------------
LABEL maintainer="K.SUZUKI / (Twitter: @KuniSioaji)" \
  description="Image to run Redmine simply with sqlite to try/review plugin."

------------------------------------------
★コメント(説明省略)
------------------------------------------
#
# You can run target version of Redmine.
# If you use this Dockerfile only, try this:
#    $ docker build -t redmine_stable .
#    $ docker run -d -p 3000:3000 redmine_stable
#
# You can change Redmine version with arg
#    $ docker build --rm --build-arg REDMINE_VERSION=master -t redmine_master .
#    $ docker run -d -p 3000:3000 redmine_master
#

------------------------------------------
★構築(build)時の引数(変数)を指定しなかった時のデフォルト値
★引数でRedmineのバージョンを渡す事で、
 構築するRedmineのバージョンを指定可能にしている。
------------------------------------------
### get Redmine source
ARG REDMINE_VERSION=3.4-stable

------------------------------------------
★Dockerfileのビルドはbashではなくshで実行されるためエラーが出る。
 shをbashへのシンボリックリンクで置き換えることで対応する。
★echo :引数の表示
------------------------------------------
### Replace shell with bash so we can source files ###
RUN rm /bin/sh && ln -s /bin/bash /bin/sh && echo "REDMINE_VERSION: ${REDMINE_VERSION}"

------------------------------------------
★最低限必要なパッケージを追加
------------------------------------------
### install default sys packeges ###
RUN apt-get update && apt-get install -qq -y \
    git vim            \
    sqlite3

------------------------------------------
★Redmineのソースを /tmp/ ディレクトリにgit cloneする 
 (引数でタグバージョン指定してバージョン変更)
★コメント文の方は、安定版(ブランチ)をcloneする場合の記述
★clone先は、/tmp/redmine_coディレクトリとなる。
------------------------------------------
#RUN cd /tmp && git clone --depth 1 -b ${REDMINE_VERSION} https://github.com/redmine/redmine redmine
RUN cd /tmp && git clone --depth 1 -b ${REDMINE_VERSION} --single-branch https://github.com/redmine/redmine redmine_co

------------------------------------------
★引数の表示?
------------------------------------------
RUN echo "REDMINE_VERSION: ${REDMINE_VERSION}"

------------------------------------------
★ WORKDIR
 作業ディレクトリ(/tmp/redmine_coに、cloneしたソースがある)
 以後、このディレクトリでRUN実行されていく。
------------------------------------------
WORKDIR /tmp/redmine_co

------------------------------------------
★SQLite3用のdatabase.ymlをconfig/に作成する
------------------------------------------
RUN echo $'test:\n\
  adapter: sqlite3\n\
  database: /tmp/data_co/redmine_test.sqlite3\n\
  encoding: utf8mb4\n\
\n\
development:\n\
  adapter: sqlite3\n\
  database: /tmp/data_co/redmine_development.sqlite3\n\
  encoding: utf8mb4\n'\
>> config/database.yml

------------------------------------------
★bundle installする
------------------------------------------
RUN gem update bundler
RUN bundle install --without mysql postgresql rmagick test

------------------------------------------
★Redmine本体のマイグレーションをする
------------------------------------------
RUN bundle exec rake db:migrate
RUN bundle exec rake generate_secret_token

------------------------------------------
★CMD:ここまでに作成したイメージで実行するコマンド
    (構築(build)時には、実行しない)
 port 3000で起動する
------------------------------------------
CMD bundle exec rails s -p 3000 -b '0.0.0.0'
EXPOSE 3000

リファレンスより。

※RUN と CMD を混同しないでください。
 RUN が実際に行っているのは、コマンドの実行と結果のコミットです。
 一方の CMD は構築時には何もしませんが、イメージで実行するコマンドを指定します。


今日は、ここまで。 次回は、docker-compose.ymlと、起動などやっていく予定です。


Docker お勉強 04 Docker Compose

もくもくで使わせてもらっている docker-compose.yml が何なのかわからないので調べます。 下記ページを読みながら、覚え書きを記載していきます。

DockerCompose

・Dockerツール群の一つであるDockerComposeを使うと、コンテナ立ち上げ設定をファイル化(yml形式)して管理することが可能になります。
・docker-compose で起動する場合は、Dockerfileを使ってイメージ作成します。

.yml →YAMLファイル →ヤムルファイル

  • データ構造を記述するフォーマットの一つ
  • jsonのスーパーセット(上位互換みたいな感じ)だそう
  • json形式でもymlとして認識してくれるらしい
  • DockerComposeではこのyml形式ファイルを使って、コンテナ起動に関してを記述していきます。
    docker-compose.yml

version:   バージョン番号

Compose file versions and upgrading

現在の最新は '3'

version: '3'

services:   各コンテナに関しての記述を行っていく

services:
  web_co:
  • services構造直下に各コンテナに関しての記述を行っていく。
    次の構造(キー)としては各サービス名となる。(上記:web_co)
  • 立ち上げるコンテナごとに記述内容が異なるはずですので、区分して記述するために構造を分割します。
  • 任意の名前でOK
  • この名前がコンテナの名前になるわけではありません。

build:   構築時に適用するオプションを指定

build で指定できるのは、
・構築用コンテクストのパスを含む文字列
・context の配下にある特定の物(オブジェクト)
・dockerfile のオプション、 引数
build: ./dir

build:
  context: ./dir
  dockerfile: Dockerfile-alternate
  args:
    buildno: 1

【もくもく例】

    build:
      context: .
      args:
        REDMINE_VERSION: $REDMINE_VERSION

context:   Dockerfile があるパスや Git URLを指定

・Dockerfile があるディレクトリのパスや Git リポジトリの URL を指定します。
・値に相対パスを指定したら、Compose ファイルのある場所を基準とした相対パスとして解釈します。
・指定したディレクトリが構築コンテクストとなり、Docker デーモンに送信します。
・Compose は生成時の名前で構築・タグ付けし、それがイメージとなります。
build:
  context: ./dir

【もくもく例】

    build:
      context: .

args:   build のオプション(args)

・構築時に build のオプション(args)を追加します。
・配列でも辞書形式(「foo=bar」の形式)も指定できます。
・ブール演算子(true、false、yes、no)を使う場合はクォートで囲む必要があります。
 そうしないと YAML パーサは True か False か判別できません。
・構築時に引数のキーとして解釈する環境変数の値は、Compose を実行するマシン上のみです。
build:
  args:
    buildno: 1
    user: someuser

build:
  args:
    - buildno=1
    - user=someuser

【もくもく例】

    build:
      args:
        REDMINE_VERSION: $REDMINE_VERSION

image:   コンテナの作成元イメージを指定する

・コンテナを実行時に元となるイメージを指定します。
・リポジトリ名・タグあるいはイメージ ID の一部を指定できます。
image: redis
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry.com:4000/postgresql
image: a4bc65fd
  • すでに存在するimageを探してくれます。
    docker runの時と同じ様にまずローカルのimageから検索して、なければDockerHubからの取得を試みてくれます。
  • イメージが存在していなければ、Compose は pull (取得)を試みます。
    しかし build を指定している場合は除きます。その場合、指定されたタグやオプションを使って構築します。

【もくもく例】

  web_co:
    build:
      context: .
      args:
        REDMINE_VERSION: $REDMINE_VERSION
    image: redmine_co_sqlite3

command:   デフォルトのコマンドを上書きする

デフォルトのコマンドを上書きします。
command: bundle exec thin -p 3000
  • これは Dockerfile の書き方に似せることもできます。
command: [bundle, exec, thin, -p, 3000]

【もくもく例】

    command: >
      bash -c "bundle exec rake redmine:plugins:migrate &&
           bundle exec rails s -p 3000 -b '0.0.0.0'"


⬇Dockerfile側は、こう書いてある⬇

CMD bundle exec rails s -p 3000 -b '0.0.0.0'

(docker run だとプラグインなしでRedmine起動。composeはプラグインつき起動)

environment:   環境変数を追加する

・環境変数を追加します。
・配列もしくは辞書形式(dictionary)で指定できます。
・boolean 値 (true、false、yes、no のいずれか) は、
 YMLパーサによって True や False に変換されないよう、クォートで囲む必要があります。
・キーだけの環境変数は、Compose の実行時にマシン上で指定するものであり、
 シークレット(API鍵などの秘密情報)やホスト固有の値を指定するのに便利です。
environment:
  RACK_ENV: development
  SHOW: 'true'
  SESSION_SECRET:

environment:
  - RACK_ENV=development
  - SHOW=true
  - SESSION_SECRET

【もくもく例】

    environment:
      RAILS_ENV: development

volumes:   コンテナ内にマウントするボリュームを指定する

マウント・パスまたは名前を付けたボリュームは、
・オプションでホストマシン上のパス指定 →( ホスト:コンテナ )
・アクセス・モード →( ホスト:コンテナ:rw )
を指定できます。 

・ホスト上の相対パスをマウント可能です。
・相対パスは Compose 設定ファイルが使っているディレクトリを基準とします。
・相対パスは . または .. で始まります。
volumes:
  # パスを指定したら、Engine はボリュームを作成
  - /var/lib/mysql

  # 絶対パスを指定しての割り当て
  - /opt/data:/var/lib/mysql

  # ホスト上のパスを指定する時、Compose ファイルからのパスを指定
  - ./cache:/tmp/cache

  # ユーザの相対パスを使用
  - ~/configs:/etc/configs/:ro

  # 名前付きボリューム(Named volume)
  - datavolume:/var/lib/mysql

ホスト側のパスを指定せず、 volume_driver を指定したい場合があるかもしれません。

使用するボリュームドライバを指定する
volume_driver: mydriver

【もくもく例】

    volumes:
      - .:/tmp/redmine_co/plugins/redmine_issue_badge

( ホスト:コンテナ )

ports:   公開用のポートを指定する

・公開用のポートです。
・ホスト側とコンテナ側の両方のポートを指定 →( ホスト側:コンテナ側 )
・コンテナ側のポートのみも指定できます(ホスト側はランダムなポートが選ばれます)。

注釈
* ホスト側:コンテナ側 の書式でポートを割り当てる時、コンテナのポートが 60 以下でエラーが発生します。
これは YAML が xx:yy 形式の指定を、60 進数(60が基準)の数値とみなすからです。
そのため、ポートの割り当てには常に文字列として指定することを推奨します。

ports:
 - "3000"
 - "3000-3005"
 - "8000:8000"
 - "9090-9091:8080-8081"
 - "49100:22"
 - "127.0.0.1:8001:8001"
 - "127.0.0.1:5000-5010:5000-5010"

【もくもく例】

    ports:
      - "3000:3000"

実行オプション docker-compose build

【もくもく例】

 REDMINE_VERSION=master docker-compose build --force-rm --no-cache

rm:   停止済みのサービス・コンテナを削除する

使い方: rm [オプション] [サービス...]

オプション:
    -f, --force   確認なく削除する
    -v            コンテナにアタッチしているアノニマス・ボリュームも削除
    -a, --all     docker-compose run で作成した一度だけのコンテナを全て削除
                  docker-compose run
デフォルトでは、コンテナにアタッチしている匿名ボリューム(anonymous volume)を削除しません。
ボリュームを削除するには -v オプションを使います。
全てのボリュームを表示するには docker volume ls を使います。
(明示的に削除しなければ)ボリューム内にあるデータは失われません。

build:   構築する

使い方: build [オプション] [サービス...]

オプション:
--force-rm  常に中間コンテナを削除
--no-cache  構築時にイメージのキャッシュを使わない
--pull      常に新しいバージョンのイメージ取得を試みる
  • サービスは プロジェクト名_サービス として構築時にタグ付けられます。 例: composetest_db 。
  • サービスの Dockerfile や構築ディレクトリの内容に変更を加える場合は、 docker-compose build で再構築を実行します。

実行オプション docker-compose up

【もくもく例】

docker-compose up -d

up:   サービス用のコンテナの構築、作成、起動、アタッチを行う

使い方: up [オプション] [サービス...]

オプション:
    -d                         デタッチド・モード: バックグラウンドでコンテナを実行し、新しいコンテナ名を表示
                               --abort-on-container-exit と同時に使えない
    --no-color                 白黒で画面に表示
    --no-deps                  リンクしたサービスを表示しない
    --force-recreate           設定やイメージに変更がなくても、コンテナを再作成する
                               --no-recreate と同時に使えません
    --no-recreate              コンテナが既に存在していれば、再作成しない
                               --force-recreate と同時に使えない
    --no-build                 イメージが見つからなくても構築しない
    --build                    コンテナを開始前にイメージを構築する
    --abort-on-container-exit  コンテナが1つでも停止したら全てのコンテナを停止
                               -d と同時に使えない
    -t, --timeout TIMEOUT      アタッチしている、あるいは既に実行中のコンテナを
                               停止する時のタイムアウト秒数を指定 (デフォルト:10 )
    --remove-orphans           Compose ファイルで定義されていないサービス用のコンテナを削除
  • 既に実行している場合は、このコマンドによってリンクされているサービスも起動します。
  • docker-compose up コマンドは各コンテナの出力を統合します。
  • コマンドを終了(exit)すると、全てのコンテナを停止します。
  • docker-compose up -d で実行すると、コンテナをバックグラウンドで起動し、実行し続けます。
  • もしサービス用のコンテナが存在している場合、かつ、コンテナを作成後にサービスの設定やイメージを変更している場合は、 docker-compose up -d を実行すると、 設定を反映するためにコンテナを停止・再作成します(マウントしているボリュームは、そのまま保持します)。
  • Compose が設定を反映させないようにするには、 --no-recreate フラグを使います。
  • Compose で処理時、強制的に全てのコンテナを停止・再作成するには --force-recreate フラグを使います。

ひとまず、ここまで。

【もくもくのdocker-compose.yml】

version: '3'
services:
  # start service for redmine with plugin
  # 1. $ docker-compose build --force-rm --no-cache
  # 2. $ docker-compose up -d
  #
  # If you want to change Redmine's version,
  #
  # 1. $ REDMINE_VERSION=master docker-compose build --force-rm --no-cache
  # 2. $ docker-compose up -d
  #
  #
  web_co:
    build:
      context: .
      args:
        REDMINE_VERSION: $REDMINE_VERSION
    image: redmine_co_sqlite3
    container_name: redmine_co_sqlite3
    command: >
      bash -c "bundle exec rake redmine:plugins:migrate &&
           bundle exec rails s -p 3000 -b '0.0.0.0'"
    environment:
      RAILS_ENV: development
    volumes:
      - .:/tmp/redmine_co/plugins/redmine_issue_badge
    ports:
      - "3000:3000"

Docker お勉強 03 Dockerfile

もくもくで使わせていただいているDockerfileを理解するためにお勉強です。

以下のページを参考に勉強して、もくもくで使ったDockerfileを例にあげて、覚え書きをこちらに記載します。

FROM: ベースイメージの指定

【解説】 Dockerfile リファレンスの FROM 命令 → 本家

可能であれば、自分のイメージの元として現在の公式レポジトリを使います。
私たちは Debian イメージ を推奨します。
これは、非常にしっかりと管理されており、ディストリビューションの中でも最小(現在は 100 MB 以下)になるよう維持されているからです。
FROM <イメージ>
FROM <イメージ>:<タグ>
FROM <イメージ>@<digest>
  • 以降の命令で使う ベース・イメージ を指定する。
  • コメント以外では FROM を一番始めに書く必要がある。
  • 単一の Dockerfile から複数のイメージを作成するため、複数の FROM を指定できる。
    各 FROM 命令ごとに自動的にコミットし、最新のイメージ ID が出力される。
  • タグ や digest 値はオプション。
    省略した場合、ビルダーはデフォルトの latest (最新)とみなす。
    ビルダーは一致する tag 値が無ければエラーを返す。
  • digestは、イメージのユニークな識別子ID (docker images --digestsで表示確認出来る)
    メージ生成後に変更が加えられなければ、digest 値は変更されていないと考えられる。
    v2 以降の形式を使うイメージで使える。

【もくもく例】

FROM ruby:2.1.10

LABEL: コンテナとイメージに付与できるラベル

【解説】Dockerfile リファレンスの LABEL 命令 → 本家

LABEL 命令はイメージにメタデータを追加します。 
LABEL はキーとバリューのペアです。 
LABEL の値に空白スペースを含む場合はクォートを使いますし、コマンドラインの分割にバックスラッシュを使います。

MAINTAINER 命令は現在非推奨

LABEL <key>=<value> <key>=<value> <key>=<value> ...

【例】

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
  • イメージは複数のラベルを持てます。
    可能であれば1つの LABEL にすることをお勧めします。
    複数のラベルを指定したら、各 LABEL 命令は新しいレイヤを準備しますが、多くのラベルを使えば、それだけレイヤを使います。
    次の例は1つのイメージ・レイヤを使うものです。
LABEL multi.label1="value1" multi.label2="value2" other="value3"

上記の例は、次のようにも書き換えられます。

LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"
  • ラベルには、FROM イメージが使う LABEL も含まれています。
    ラベルのキーが既に存在している時、Docker は特定のキーを持つラベルの値を上書きします。
  • イメージが使っているラベルを確認するには、 docker inspect コマンドを使います。

【もくもく例】

LABEL maintainer="K.SUZUKI / (Twitter: @KuniSioaji)" \
  description="Image to run Redmine simply with sqlite to try/review plugin."

ARG: docker build コマンドで使う変数

【解説】Dockerfile リファレンスの ARG 命令 → 本家

ARG 命令は、構築時に作業者が docker build コマンドで使う変数
ARG <名前>[=<デフォルト値>]
  • --build-arg <変数名>=<値> フラグを定義するものです。
    ユーザが構築時に引数を指定しても Dockerfile で定義されていなければ、構築時に次のようなエラーが出ます。
    One or more build-args were not consumed, failing build.
  • 複数の ARG を指定可能です。
  • ARG 命令のデフォルト値を指定できます。
    構築時に値の指定が無ければ、このデフォルト値を使います。
ARG user1=someuser
ARG buildno=1
  • ARG 変数は Dockerfile で記述した行以降で効果があります。ただし、コマンドライン上で引数の指定が無い場合です。

【例】

FROM busybox
USER ${user:-some_user}
ARG user
USER $user
...
$ docker build --build-arg user=what_user Dockerfile

FROM busybox
USER ${user:-some_user}
          → ここまでは、usr=some_user
ARG user
USER $user    → ここからは、usr=what_user (コマンドラインで指定したもの)
...
  • 構築時の変数として、GitHub の鍵やユーザの証明書などの秘密情報を含むのは、推奨される使い方ではありません。

  • ARG や ENV 命令を RUN 命令のための環境変数にも利用できます。
    ENV 命令を使った環境変数の定義は、常に同じ名前の ARG 命令を上書きします。

【例】

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER

or

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER
$ docker build --build-arg CONT_IMG_VER=v2.0.1 Dockerfile

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER →引数を設定した場合、v2.0.1。指定しない場合、v1.0.0
  • 以下のものは、ARG 命令を記載しなくても、ARG 変数で指定出来ます。
    • HTTP_PROXY
    • http_proxy
    • HTTPS_PROXY
    • https_proxy
    • FTP_PROXY
    • ftp_proxy
    • NO_PROXY
    • no_proxy

※これらを使うには、コマンドラインで --build-arg <変数名>=<値> フラグを単に渡すだけです。

構築キャッシュの影響

キャッシュについてはこちらのページをみて少し勉強しました。
Dockerfileを書く時の注意とかコツとかハックとか

ENV 変数は、値が残り続ける。
ARG 変数は、値が残り続けない。
ARG 変数は、構築キャッシュで残り続ける方法として扱える。

ARG 変数の値が以前の値と違う時は、「キャッシュ・ミス」が発生する。
ARG 変数の値を定義していなくても発生します。
特に、すべての RUN 命令は ARG 変数を(環境変数から)暗黙的に使おうとするため、結果としてキャッシュ・ミスを引き起こす。

【例】

--build-arg CONT_IMG_VER=<値> をコマンドライン上で指定

FROM ubuntu
ARG CONT_IMG_VER 
RUN echo $CONT_IMG_VER 
FROM ubuntu
ARG CONT_IMG_VER
RUN echo hello        → キャッシュ・ミス発生

上記の変更は、3行目でキャッシュミス発生。

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER $CONT_IMG_VER → キャッシュ・ミス発生
RUN echo $CONT_IMG_VER

上記は、ENV コマンドはイメージの中で処理される。キャッシュイメージと値が変わるのでミス発生。

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER hello → キャッシュ・ミス発生しない
RUN echo $CONT_IMG_VER

上記は、ENV コマンドはイメージの中で処理される。キャッシュイメージは、helloの固定値でミス発生しない。

ENVとの使い分け

Dockerfile ARG入門より

  • ENVでは、コンテナ内で環境変数として変数が定義されます。
    ENVを使用する場合は、CMDやENTRYPOINTによって実行されたコマンドに適している。
  • ARG は、 Dockerfileのbuild時に一時的に変数が定義され、コマンドの実行時に展開されます。
    ARGは、変数を展開した状態でコンテナ内にファイルを配置したい場合に有効です。
  • ARGは変数を展開した状態でコンテナに状態を設定するという特性から、コンテナが管理するファイルには有効ですが、 VOLUMEによって管理されているディレクトリに配置されているファイルに対してはENVを使ったほうが便利な場面が多いです。

【もくもく例】

### get Redmine source
ARG REDMINE_VERSION=3.4-stable

RUN: コマンドの実行

【解説】Dockerfile リファレンスの RUN 命令 → 本家

RUN 命令は既存イメージ上の新しいレイヤで、あらゆるコマンドを実行し、その結果をコミットする命令です。
コミットの結果得られたイメージは、 Dockerfile の次のステップで使われます。
ソース・コントロールのように、イメージの履歴上のあらゆる場所からコンテナを作成可能です。

RUN には2つの形式があります。

RUN <コマンド> 
 ・シェル形式、コマンドを実行する。
 ・Linux 上のデフォルトは /bin/sh -c であり、Windows 上 cmd /S /C。

RUN ["実行バイナリ", "パラメータ1", "パラメータ2"] 
 ・exec 形式
  • exec 形式はシェルの文字列を変更できないようにします。
    また、 指定されたシェル実行環境がベース・イメージに含まれなくても RUN コマンドを使えます。
  • デフォルトの shell のシェルを変更するには SHELL コマンドで変更できます。
  • シェル 形式では、RUN 命令を \ (バックスラッシュ)を使い、次の行と連結します。
  • RUN 命令によるキャッシュは自動的に無効化できません。
    RUN apt-get dist-upgrade -y のような命令のキャッシュがあれば、次の構築時に再利用されます。
    RUN 命令でキャッシュを使いたくない場合は、 --no-cache フラグを使います。
    例: docker build --no-cache .

一般的な利用例は apt-get アプリケーションです。
RUN apt-get コマンドは、パッケージをインストールするので、気をつけるべきいくつかの了解事項があります。

RUN apt-get update や dist-upgrade を避けるべきでしょう。
ベース・イメージに含まれる「必須」パッケージの多くが、権限を持たないコンテナの内部で更新されないためです。 もし、ベース・イメージに含まれるパッケージが時代遅れになっていれば、イメージのメンテナに連絡すべきでしょう。
たとえば、 foo という特定のパッケージを知っていて、それを更新する必要があるのであれば、自動的に更新するために apt-get install -y foo を使います。

RUN apt-get updaate と apt-get install は常に同じ RUN 命令文で連結します。以下は実行例です。

RUN apt-get update && apt-get install -y \
    package-bar \
    package-baz \
    package-foo

RUN 命令文で apt-get update だけを使うとキャッシュ問題を引き起こし、その後の apt-get install 命令が失敗します。
例えば、次のように Dockerfile を記述したとします。

FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl

イメージを構築すると、全てのレイヤは Docker にキャッシュされます。
次に、別のパッケージを追加する apt-get install を編集したとします。

FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl nginx

Docker は冒頭からファイルを読み込み、命令の変更を認識すると、前のステップで作成したキャッシュを再利用します。しかし、 apt-get update は 決して 実行されず、キャッシュされたバージョンを使います。これは apt-get update が実行されていないためです。そのため、古いバージョンの curl と nginx パッケージを取得する恐れがあります。

そこで Dockerfile でのインストールには RUN apt-get update && apt-get install -y を使うことで、最新バージョンのパッケージを、追加の記述や手動作業なく利用できます。

RUN apt-get update && apt-get install -y \
    package-bar \
    package-baz \
    package-foo=1.3.*

バージョンを指定すると、何がキャッシュされているか気にせずに、特定バージョンを取得した上での構築を強制します。このテクニックは、必要なパッケージの予期しない変更によって引き起こされる失敗を減らします。

以下は 推奨する apt-get の使い方の全てを示すものです。

RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    lxc=1.0* \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
 && apt-get clean \
 && rm -rf /var/lib/apt/lists/*
  • s3cmd 命令行は、バージョン 1.1.* を指定します。
    従来のイメージが古いバージョンを使っていたとしても、新しいイメージは apt-get update でキャッシュを使わないので、確実に新しいバージョンをインストールします。
  • 各行はパッケージのリストであり、パッケージの重複という間違いをさせないためです。
  • 付け加えると、apt キャッシュをクリーンにし、 /var/lib/apt/lits を削除することで、イメージのサイズを減らします。
  • RUN 命令は apt-get update から開始されるので、 apt-get install でインストールされるパッケージは、常に新鮮なものです。

【もくもく例】

1)
FROM ruby:2.1.10
LABEL maintainer="K.SUZUKI / (Twitter: @KuniSioaji)" \
  description="Image to run Redmine simply with sqlite to try/review plugin."

#
# You can run target version of Redmine.
# If you use this Dockerfile only, try this:
#    $ docker build -t redmine_stable .
#    $ docker run -d -p 3000:3000 redmine_stable
#
# You can change Redmine version with arg
#    $ docker build --rm --build-arg REDMINE_VERSION=master -t redmine_master .
#    $ docker run -d -p 3000:3000 redmine_master
#

2)
### get Redmine source
ARG REDMINE_VERSION=3.4-stable
3)
### Replace shell with bash so we can source files ###
RUN rm /bin/sh && ln -s /bin/bash /bin/sh && echo "REDMINE_VERSION: ${REDMINE_VERSION}"

4)
### install default sys packeges ###
RUN apt-get update && apt-get install -qq -y \
    git vim            \
    sqlite3
5)
RUN cd /tmp && git clone --depth 1 -b ${REDMINE_VERSION} --single-branch https://github.com/redmine/redmine redmine_co
RUN echo "REDMINE_VERSION: ${REDMINE_VERSION}"
WORKDIR /tmp/redmine_co
6)
RUN echo $'test:\n\
  adapter: sqlite3\n\
  database: /tmp/data_co/redmine_test.sqlite3\n\
  encoding: utf8mb4\n\
\n\
development:\n\
  adapter: sqlite3\n\
  database: /tmp/data_co/redmine_development.sqlite3\n\
  encoding: utf8mb4\n'\
>> config/database.yml
7)
RUN gem update bundler
RUN bundle install --without mysql postgresql rmagick test
RUN bundle exec rake db:migrate
RUN bundle exec rake generate_secret_token
CMD bundle exec rails s -p 3000 -b '0.0.0.0'
EXPOSE 3000
  1. ベースのイメージは、ruby2.1.10にしています。
  2. ARG REDMINE_VERSION というところで、デフォルトでRedmine最新バージョンの安定版を指定
  3. Dockerfileのビルドはbashではなくshで実行されるためエラーが出る。
    shをbashへのシンボリックリンクで置き換えることで対応する。
  4. 最低限必要なパッケージを追加
  5. Redmineのソースを /tmp/ ディレクトリにgit cloneする (引数でタグバージョン指定してバージョン変更)
  6. SQLite3用のdatabase.ymlを作成する
  7. bundle installする
    Redmine本体のマイグレーションをする
    port 3000で起動する

WORKDIR: 作業ディレクト

【解説】Dockerfile リファレンスの WORKDIR 命令 → 本家

WORKDIR 命令セットは Dockerfile で RUN 、 CMD 、 ENTRYPOINT 、 COPY 、 ADD 命令実行時の作業ディレクトリ(working directory)を指定します。
もし WORKDIR が存在しなければ、 Dockerfile 命令内で使用しなくてもディレクトリを作成します。

明確さと信頼性のため、常に WORKDIR からの絶対パスを使うべきです。
また、 WORKDIR を RUN cd ... && 何らかの処理 のように増殖する命令の代わり使うことで、より読みやすく、トラブルシュートしやすく、維持しやすくします。
WORKDIR /path/to/workdir
  • 1つの Dockerfile で複数回の利用が可能です。
    パスを指定したら、 WORKDIR 命令は直前に指定した相対パスに切り替えます。

【例】

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

この Dockerfile を使えば、最後の pwd コマンドの出力は /a/b/c になります。

WORKDIR 命令は ENV 命令を使った環境変数も展開できます。
環境変数を使うには Dockerfile で明確に定義する必要があります。

【例】

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

この Dockerfile を使えば、最後の pwd コマンドの出力は /path/$DIRNAME になります。

【もくもく例】

RUN cd /tmp && git clone --depth 1 -b ${REDMINE_VERSION} --single-branch https://github.com/redmine/redmine redmine_co
RUN echo "REDMINE_VERSION: ${REDMINE_VERSION}"
WORKDIR /tmp/redmine_co

CMD: docker runの際に起動するコマンド

【解説】Dockerfile リファレンスの CMD 命令 → 本家

Dockerfile で CMD 命令を一度だけ指定できます。
複数の CMD がある場合、最も後ろの CMD のみ有効です。

CMD の主な目的は、 コンテナ実行時のデフォルトを提供します 。 
デフォルトには、実行可能なコマンドが含まれているか、あるいは省略されるかもしれません。
省略時は ENTRYPOINT 命令で同様に指定する必要があります。

RUN と CMD を混同しないでください。 
RUN が実際に行っているのは、コマンドの実行と結果のコミットです。
一方の CMD は構築時には何もしませんが、イメージで実行するコマンドを指定します。

CMD には3つの形式があります。

■exec 形式、推奨する形式
CMD ["実行バイナリ", "パラメータ1", "パラメータ2"] 

■ENTRYPOINT のデフォルト・パラメータ
CMD ["パラメータ1", "パラメータ2"] 

■シェル形式
CMD <コマンド>
  • シェルあるいは exec 形式を使う時、 CMD 命令はイメージで実行するコマンドを指定します。
  • CMD を シェル 形式で使えば、 <コマンド> は /bin/sh -c で実行されます。

【例】

FROM ubuntu
CMD echo "This is a test." | wc -
  • <コマンド>をシェルを使わずに実行 したい場合、コマンドを JSON 配列で記述し、実行可能なフルパスで指定する必要があります。
    配列の形式が CMD では望ましい形式です 。
    あらゆる追加パラメータは個々の配列の文字列として指定する必要があります。

【例】

FROM ubuntu
CMD ["/usr/bin/wc","--help"]
  • もしコンテナで毎回同じものを実行するのであれば、 CMD と ENTRYPOINT の使用を検討ください。
    詳細は ENTRYPOINT をご覧ください。
  • ユーザが docker run で引数を指定した時、これらは CMD で指定したデフォルトを上書きします。

【補足】

CMD 命令は、イメージに含まれるソフトウェアの実行と、その引数のために使うべきです。
CMD は常に CMD [“executable”, “param1”, “param2”…] のような形式で使うべきです。

そのため、イメージがサービス向け(Apache、Rails 等)であれば、 CMD ["apache2","-DFOREGROUND"] のようにすべきでしょう。
実際に、あらゆるサービスのベースとなるイメージで、この命令形式が推奨されます。

大部分の他のケースでは、 
CMD はインタラクティブなシェル(bash、python、perl 等)で使われます。

たとえば、 CMD ["perl", "-de0"] 、 CMD ["python"] 、 CMD [“php”, “-a”] です。
この利用形式が意味するのは、 docker run -it python のように実行すると、それを使いやすいシェル上に落とし込み、すぐに使えるようにします。 
あなたとあなたの想定ユーザが ENTRYPOINT の動作になれていない限り、CMD を ENTRYPOINT と一緒に CMD [“パラメータ”, “パラメータ”] の形式で使うべきではないでしょう。

【もくもく例】

RUN gem update bundler
RUN bundle install --without mysql postgresql rmagick test
RUN bundle exec rake db:migrate
RUN bundle exec rake generate_secret_token
CMD bundle exec rails s -p 3000 -b '0.0.0.0'
EXPOSE 3000

EXPOSE: コンテナ内のプロセスがListenするポート

【解説】 Dockerfile リファレンスの EXPOSE 命令 → 本家

EXPOSE 命令は、コンテナが接続用にリッスンするポートを指定します。
そのため、アプリケーションが一般的に使う、あるいは、伝統的なポートを使うべきです。
例えば、Apache ウェブ・サーバのイメージは、 EXPOSE 80 を使い、
MongoDB を含むイメージは EXPOSE 27017 を使うでしょう。

外部からアクセスするためには、ユーザが docker run 実行時にフラグを指定し、
特定のポートを任意のポートに割り当てられます。
コンテナのリンク機能を使うと、Docker はコンテナがソースをたどれるよう、環境変数を提供します(例: MYSQL_PORT_3306_TCP )。
EXPOSE <port> [<port>...]
  • EXPOSE 命令は、特定のネットワーク・ポートをコンテナが実行時にリッスンすることを Docker に伝えます。
  • EXPOSE があっても、これだけではホストからコンテナにアクセスできるようにしません。
    アクセスするには、 -p フラグを使ってポートの公開範囲を指定するか、 -P フラグで全ての露出ポートを公開する必要があります。
    外部への公開時は他のポート番号も利用可能です。
  • ホストシステム上でポート転送を使うには、 -P フラグを使う をご覧ください。
  • Docker のネットワーク機能は、ネットワーク内でポートを公開しないネットワークを作成可能です。
    詳細な情報は 機能概要 をご覧ください。

Dockerfile コマンドについては、一旦ここまで。