読者です 読者をやめる 読者になる 読者になる

curry化と部分適用の違いについて

今日は, rubycurryを勉強していた.

ruby 2.4.0の日本語リファレンスMethod#curryによるとこう書かれている.

self を元にカリー化した Proc を返します。カリー化した Proc はいくつかの引数をとります。十分な数の引数が与えられると、元の Proc に引数を渡して実行し、結果を返します。引数 の個数が足りないときは、部分適用したカリー化 Proc を返します

か, カリー化ってなんだ….?

wikiによるとカリー化とはこういう意味らしい.

カリー化 (currying, カリー化された=curried) とは、複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること(あるいはその関数のこと)である。

ふーん. なるほど.

ということは,

div  = lambda {|x, y| x.to_f/y.to_f}.curry # ラムダ式をカリー化
div1 = add.(1) # xを1に束縛, 
div1.(4) # yを4に束縛, このタイミングでprocが実行される

#=> 5

これで俺もカレー職人や!!!

と思っていたらこれはどうやら部分適用と呼ぶらしい.

カリー化と部分適用の違い

curry化と部分適用を混同してる人が多いと書かれたブログを見つけた. 部分適用をカリー化と呼ばないで - kmizuの日記

いまいちピンと来なかったので, 調べてみると こんな書き込みを見つけた(Shingo Omura @everpeace)

x,y,z -> V をx -> (y->(z->V)) に変換するのがカリー化。x,y,z-> Vのyに値を束縛して結果的にx,z->Vという関数になるのが部分適用。どこが同じなんだろうか。

なるほど. 階層化することが大切なんだね. でもいまいちピンと来ていない.

見落としていたけど, wikiの下の方にこんなことが書いてあった.


カリー化は部分適用と混同されやすい。

部分適用とは、

複数引数ある関数の引数の一部だけに実引数を適用する操作

のことで、div->invを導出する操作を指す。

一方、カリー化は

div->cdivを導出する操作であり、引数への値の適用までは行わない.

div = lambda {|x, y| x.to_f/y.to_f}
cdiv = lambda {|x| lambda{|y| div.(x,y)}}
inv = cdiv.(1)
p inv.(2) #=> 0.5

つまり, 値を入れた時点で「部分適用」になるんですね.

wikiによると, カリー化は以下のように表現されていた.

...Haskellでは関数は常に一つの引数のみを取り、複数の引数を取る関数とは、単にネストされた複数の一引数関数の糖衣構文(syntax suger)にすぎない

なるほど.

今読み返すと@everpeaceさんの表現がとてもわかり易い.

x,y,z -> V をx -> (y->(z->V)) に変換するのがカリー化。x,y,z-> Vのyに値を束縛して結果的にx,z->Vという関数になるのが部分適用。どこが同じなんだろうか。

調べてみて, 僕の中での「カリー化」の理解は, 以下のような感じでまとまった.

カリー化 = 複数の引数を取る関数を, 常に1つの引数を取る関数が多層化された関数に変換すること

gmailでラベル付きのメールを自動アーカイブする, の続き

1年前に書いた記事の続き. 基本的なやり方はこの記事に書いてある.

namu-r21.hatenablog.com

参照元はこちら -> うちのGMailはこうなっている(2014年完全版) - Qiita

最近, ラベル付きメールの自動アーカイブ機能がうまく動かずエラーを吐いていたので修正した.

これは本当に便利なのでオススメ.

// exclude multi-message conversations where I sent the last message?
var SINGLE_MESSAGE_ONLY = false;
// string for regular expression check
var EMAIL_REGEX = /[a-zA-Z0-9\._\-]+@[a-zA-Z0-9\.\-]+\.[a-z\.A-Z]+/g;
// look only in sent messages from last 7 days, otherwise script takes a while
var DAYS_TO_SEARCH = 7;
// set your email address.
var YOUR_EMAIL_ADDRESS = "your gmail address";

function getEmailAddress() {
  // return Session.getEffectiveUser().getEmail();
  return YOUR_EMAIL_ADDRESS;
}

function archiveReadMail() {
  var query = 'in:inbox is:read has:userlabels';
  var threadAll = 0;
  var offset = 0;
  var limit = 5;
  var mailAddress = getEmailAddress();
  var AddressList = {};
  var hasMore = true;
  while (hasMore) {
    var threads = GmailApp.search(query, offset, limit);
    Logger.log(threads.length);
    for (var i = 0; i < threads.length; i++) {
      var thread = threads[i];
      thread.moveToArchive() 
    }
    hasMore = false;
  } 
}

RailsでLib以下のモジュールを読み込む方法

忘れやすいのでブログに記入. こちらのページを参考にしました.

qiita.com

config.autoload_paths += %W(#{config.root}/lib)

読んでよかった本 Best 3 2016年 一般書編

はじめに

年末となったので, 今年の振り返りをやってみよう.
今年 2016年は僕にとって, とても変化と刺激ある年だった.

そんな1年の中でも, 僕の考え方に影響を与えた本が3冊有ったので紹介していこう.

Best 3 人生がときめく片付けの魔法 / 近藤麻理恵

人生がときめく片づけの魔法

人生がときめく片づけの魔法

この本は, 掃除のHow to ばかりでなく, 物との向き合い方を教えてくれた本であった.

この本が僕に伝えてくれたことは「捨てることの大切さ」である.

この本は一貫して「好きなものに囲まれて生きていく素晴らしさ」を説いてくれる.

“好きな物に囲まれて生きていく”ということは“好きでないもの”を徹底的に生活から排除することを意味する.

http://gahag.net/img/201608/24s/gahag-0118652126-1.png

この本の大まかな要旨はこうだ

  1. 「捨てること」が最初の一歩
  2. 捨てるものの判断は「それを好きかどうか」を直感で判断していく.
  3. 手元に残った数少ない「好きなもの」に囲まれて生活をしていく.

もし, 誤って「必要だったもの」を捨ててしまった時, その時は「好きだと思うもの」を購入する.

そうやって物の循環を起こすことで, 生活に新陳代謝が起きる.

http://yajidesign.com/i/0096/tnm.png

実際に実践してみると, 所持物の約8割程度の物が廃棄された(60Lゴミ袋*15, 廃品回収2回).
そして, 意外と生活には全く困らないものであった.

好きでないものを無理に続けていくとどこかで自分が腐っていくような感覚に陥る時がある.

そんなときは思い切って捨ててみる. そして, 何かを新しいことを始める.

そのせいか, 新しい趣味や友人, 生活習慣が増えた. 大切なことに気づかせてくれた1冊であった.


Best 2 こころ / 夏目漱石

この本は青空文庫で無料で読めるので是非. http://www.aozora.gr.jp/cards/000148/files/773_14560.html

ストーリーとはほとんど関係ないある一節が僕にとってかなり印象的であった.

それは, 大学生である主人公の「僕」が大学を卒業し, 病を患う父と雑談をしている場面である.

一部を抜粋する.

私は寝ながら自分の過去を顧みた。また自分の未来を想像した。
するとその間に立って一区切りを付けているこの卒業証書なるものが、意味のあるような、また意味のないような変な紙に思われた。
(中略)
学校を卒業するのを普通の人間として当然のように考えていた私は、それを予期以上に喜んでくれる父の前に恐縮した。
「卒業ができてまあ結構だ」父はこの言葉を何遍も繰り返した。
(中略)
私はしまいに父の無知から出る田舎臭いところに不快を感じ出した。
「大学ぐらい卒業したって、それほど結構でもありません。卒業するものは毎年何百人だってあります」
私はついにこんな口の利きようをした。すると父が変な顔をした。

「何も卒業したから結構とばかりいうんじゃない。
そりゃ卒業は結構に違いないが、おれのいうのはもう少し意味があるんだ。
それがお前に解っていてくれさえすれば、……」
私は父からその後を聞こうとした。
父は話したくなさそうであったが、とうとうこういった。

「つまり、おれが結構という事になるのさ。おれはお前の知ってる通りの病気だろう。
去年の冬お前に会った時、ことによるともう三月か四月ぐらいなものだろうと思っていたのさ。
それがどういう仕合せか、今日までこうしている。起居に不自由なくこうしている。

そこへお前が卒業してくれた。だから嬉しいのさ。
せっかく丹精した息子が、自分のいなくなった後で卒業してくれるよりも、丈夫なうちに学校を出てくれる方が親の身になれば嬉しいだろうじゃないか。

大きな考えをもっているお前から見たら、高が大学を卒業したぐらいで、結構だ結構だといわれるのは余り面白くもないだろう。
しかしおれの方から見てご覧、立場が少し違っているよ。
つまり卒業はお前に取ってより、このおれに取って結構なんだ。解ったかい」
 
 私は一言もなかった。詫まる以上に恐縮して俯向いていた。
父は平気なうちに自分の死を覚悟していたものとみえる。
しかも私の卒業する前に死ぬだろうと思い定めていたとみえる。
その卒業が父の心にどのくらい響くかも考えずにいた私は全く愚かものであった。

この1節は, 修士2年となり, おそらくは自身の最後の学校生活を過ごしている自分にとってはとても感慨深いものであった.

今の自分は決して1人では生きていない, 自身は親が有り, その他の支えてくれる人がいるから成り立っている.
そしてそれらは有限である, と気づかせてくれた.

「卒業証書」はその方々の努力の証明, 普段目に見えないその存在の化身として表現されていてとても印象的であった.

http://free-illustrations-ls01.gatag.net/images/sgi01a201403101000.jpg

「親孝行ってなんだろう, って考える. それを考えること自体が親孝行なのかも知れない」と松本人志はチキンライスの歌詞に綴っている.

これから社会人になり, 自身の足で立っていく自分はどのように親にこの恩を返していこうかなぁ….と思い馳せている


Best 1 真理のことば / 佐々木閑

NHK「100分 de 名著」ブックス ブッダ 真理のことば (NHK「100分de名著」ブックス)

NHK「100分 de 名著」ブックス ブッダ 真理のことば (NHK「100分de名著」ブックス)

この本は仏教の祖ゴウダマ・シッダッタ(いわゆるブッダ)の言葉を現代語訳した本である.

なかでも, 諸行無常この言葉がとても僕に影響を与えた.

ブッダはある日「死ぬために生きている」ことに気づいてしまう.

すると, 死に達するまで何度も繰り返される生はそれ自体が”苦しみ”であることに気づいてしまう.

http://blogs.c.yimg.jp/res/blog-73-29/sirokuma6102000/folder/643879/36/53964636/img_0

そして, この苦しみから逃れる方法を探すために修行を始める.

その過程で, 世の中の全てのもの(諸行)は常に変化し安定していないこと(無常)を知る.

移りゆく世の中で物事に固執すること, 何かを求めること, これが恨みや妬みになりそれは苦しみを生む.

今の自分が明日以降も同じように続くとも限らない. この先, 迫りくる苦しみや悲しみに自身を曲げないように「自身を鍛え続ける」こと.

それが唯一苦しみから逃れるための手段である.

これが有名な諸行無常であり仏教の最も主軸となる教えである.

当たり前のことであるが, これを日常の中で意識している人は多くないと思う.

物事や世界が「常に変化し続けること」の事実と, それに対応するために物事に固執せず「変化」が自身に求められていることを知るキッカケになった.

目指す物に向かって, 無心で取り組むこと, 自身の成長に繋がりそうな局面にチャレンジすることの大切さを教えてくれた良い本であった.


まとめ

2016年に読んだ本で印象的であった本について書き綴った.

こうして, 不定期でもブログを書いたり, 与えていただいた機会に尻軽にノッて行くことで去年の今頃の自分とは違った味が持てたのではないかなぁ, と感じている.

そして, それは僕に興味を持って頂き, 機会を与えていただいた方々のおかげであったととても強く感じている.

直近では, 偶然はてなブックマークに取り上げていただいたり, ブログやGithub経由であるWebサービスの会社から面談のお話を頂いて少々お話をしてきた.

僕のように何もできない学生でも, 情報発信さえしていれば興味を持っていただけることがある. 何でも挑戦してみるもんだなぁ.

来年は, “自分”に固執せず, 好きだと思うことを思いっきりやる, そして, 自分自身の変化を楽しむことが目標である.

それでは.

vagrantを使ってubuntuでrails環境を自動構築

vagrantrails環境を作る

チーム開発でRails環境が必要になったので, vagrantで環境構築の自動化をやってみた.
今回は, vagrantとshellscriptを使って, VM作成~Railsプロジェクト作成までを自動化してみた.

必要なソフトウェアのインストール

僕が使ったのは以下の2つ

Virtualbox 8.0.28
Vagrant 1.8.6

rails環境を作る

github.com

このリポジトリgit cloneしてvagrant upするだけで, 以下の作業が行われる.

  1. VMの作成(ubuntu14.04 64bit)
  2. Railsプロジェクトの作成

構成はこんな感じ.

f:id:namu_r21:20161203212846p:plain

VirtualboxUbuntuを動かして, ruby, railsをインストールするという流れ.
vagrantを使うと, プロジェクト作成まで自動化することが出来るなんてすばらしい.

Vagrantfileの読み方

VMの構成を設定する

vm.boxでは, OSを指定する. 今回はubuntuの有力boxであるubuntu/trusty64を利用した.
vm.networkでは, VMが使用するIP, portを指定する.
VMとホストで通信するときはこのIP, PORTを利用する.
memoryやstorage, name, hostnameなどは適宜設定して欲しい.

VM上の環境を設定する

vm.provisionを使うことで, VM上の環境設定を行うことができる.
今回は, shellscriptを使って, 環境設定を行った.

config.vm.provision :shell, :path => "general_setup.sh", privileged: true
config.vm.provision :shell, :path => "rbenv.sh", privileged: false
config.vm.provision :shell, :path => "rails_practice.sh", privileged: false

vm.provision :shellとすることで, :pathに指定したスクリプトをshellscriptとして実行してくれる.
スクリプトの実行権限はprivilegedによって変更できる.
privileged: trueは, 管理者権限を表す. defaultはtrueである.

つまり, general_setup.shは管理者権限で実行され, rbenv.shは一般ユーザで実行される.

Vagrantでやることはこれだけ.
あとは, VM上で実行するコマンドをshellscriptに逐次書いていく.

shellscriptの書き方

注意したいのは, privilegedtrue/falseに関係なく, ルートディレクトリがsuのルートになることだ. script内部でpathを指定する場合や一般ユーザでインストールを行ったソフトの実行は絶対パスを指定する必要がある.
※ 僕はこれでめっちゃハマった

僕のVagrantfileでは, 3つのscriptに設定手順を書いている. Vagrantfileに記述した順番にscriptを実行していく.
つまり, general_setup -> rbenv -> rails_practiceの順に実行されていく.

shellscriptの内容は上記のgithubShuzoN/rails_practiceを参考にして欲しい.

vagrantの基本コマンド

www.vagrantup.com

cmd function
vagrant up VMの起動, VMが存在しない場合はVM作成. 初回はprovision自動実行
vagrant halt VMの停止
vagrant destroy VMの削除
vagrant ssh VMSSHで接続
vagrant status VMの状態を表示

vagrantを使ってみて

vagrantを使うことで, railsの環境設定を自動化した.

これまで環境設定にはかなりの時間を要してきた.
vagrantfileの共有だけでその問題が解決できるのはかなり便利だ.

あとは, もう少し効率的にやる方法を知りたいな...

Vagrantについて勉強した

vagrantとは

vagrantVMを作成する手順を自動化するためのツールである.
vagrantで環境設定の自動化を行ったので, vagrantについてまとめていく.

www.vagrantup.com


vagrantでできること / できないこと

できること

VMを作成する時, 通常であれば手動でVMのOSやメモリ, ストレージを指定してVMの構築を行う. VM作成にあたって, このような手順を行ったとこがある人は多いと思う. これを手動で複数の環境で行うのはかなり面倒である.
zakkiweb.net

vagrantを使うことでこの作業を自動化することが出来る.
VagrantfileにVMの設定をあらかじめ記述しておけば, vagrantにファイルを読み込ませることでVMを自動構築してくれる.

Vagrantのうまみ

vagrantの旨味は, Vagrantfileを共有するだけで複数の端末上で同じ環境を作ることが出来ることにある.
githubでVagrantfileを共有すれば, みんな同じ環境で開発ができる.

ここについては以下の記事が詳しい.
knowledge.sakura.ad.jp

vagrantとサーバ設定ツールとの組み合わせ

vagrantVMの作成だけでなく, VM上の環境設定も同時に行うことが出来る.
こういった事前環境設定をprovisioningというらしい.

ChefやPuppetといったサーバ設定ツール, shellscriptと組み合わせることで, VMの作成だけでなく, VM上の環境設定も同時に行える.

できないこと

しかし, vagrantだけでVM自体は作成できない.
VM環境はvirtualboxなど仮想環境構築ソフトが別途必要である.

vagrantの機能をまとめると...

vagrantは以下の2つを行えるツールである.

  • vagrantの機能
    • VMの構築自動化
    • VM上のプロジェクト環境構築自動化

dockerが使うUnionFileSystemを僕なりに解釈した

こちらも合わせてお読みください namu-r21.hatenablog.com

dockerのイメージとコンテナについて今一度

昨日書いた記事が運良くはてブに載り, 色々な方に見ていただけた.
namu-r21.hatenablog.com

そのおかげで, 有用なコメントを頂けた.

なんでファイルシステムの話なのにメモリの話とごちゃまぜになってるんだろ。  
コンテナを終了したらメモリ上のデータは消えるが差分ファイルシステムの writable に書かれたデータは残る。rm すると消える。

このコメントを理解するために, dockerが利用している
UnionFileSystemCopy on Writeについて調べた.

僕なりの解釈を書いていこうと思う.

dockerのファイルシステム

dockerでは, Union File Systemを導入している.
これは, コピーオンライトで動作するファイルシステムである.

dockerのファイルシステムそのものについては公式の解説が特ににわかりやすい.
docker-doc イメージ、コンテナ、ストレージ・ドライバの理解

今回は,

  1. コピーオンライト
  2. UnionFileSystem
  3. dockerにおけるUnionFileSystem
  4. 永続的なデータの保存

について掘り下げて書いていく.


コピーオンライトについて

先日のエントリdockerのファイルシステムについて知る - 1++では, 以下のように書いた.

コピーオンライトとは

"子プロセス生成時に親プロセスのメモリー空間を複製せず,書き込み処理が発生したときにはじめて複製する仕組み"である. 

このとき, 親プロセスのメモリ空間はRead onlyである.  

dockerでは, "イメージ"を親プロセス, "コンテナ"を子プロセスとして扱う. 基本的に "コンテナ単位でメモリ空間" が与えられる.
そもそもコピーオンライトとはなんなのか

コピーオンライトは, Copy on Writeと書く. 解説は, docker公式ドキュメントを引用する.
引用元 : docker-doc イメージ、コンテナ、ストレージ・ドライバの理解

コピー・オン・ライト(copy-on-write、cow)とは、共有とコピーのストラテジに似ています。

このストラテジは、システム・プロセスが自分自身でデータのコピーを持つより、同一インスタンス上にあるデータ共有を必要とします。

書き込む必要があるプロセスのみが、データのコピーにアクセスできます。
その他のプロセスは、オリジナルのデータを使い続けられます。

コピーオンライトは, 複数のプロセスが同時に動作するときに使用される, ストレージ共有の戦略である. つまり, これ自体はメモリだろうがファイルシステムだろうが関係無い.

データのコピーは非常に"重い"処理なため, その回数をできる限り減らすべく生まれたファイルシステムの最適化手法のようだ.

コピーオンライトではデータは無駄にコピーせず, 読み出しは共有領域から読み出す. 書き込みが必要な場合のみコピーして書き換える という挙動を取る.

このように, 書くとき(write)に初めてデータをコピー(copy)するから"Copy on Write"というらしい.


Union File Systemについて

UnionMountとUnion-type Filesystem(1),
まとめて束ねるUnionFSの不思議な世界を参考にした.

Unionとは, 合体という意味である.

僕の解釈ではUnion File Systemは, 複数のファイルシステムを階層的に合体させて, 1つのファイルシステムとして扱うことができるファイルシステムという理解で落ち着いた.

UnionFileSystemは, Unionマウントと呼ばれる特殊なマウント方法を取る.
その説明の前に, 通常のマウントから説明する.

その後, UnionFileSystemについて話したい.

通常のマウント

UnionFileSystemの前に, まずは通常のマウントについて考える.

f:id:namu_r21:20161027003900p:plain:w500

ここでは, /mntというディレクトリにdisc1(別名/dev/d1), disc2(別名/dev/d1)というディスクを順に1度ずつマウントする場合を考える.

2度実行した後, /mntの内容を確認すると, ユーザから見てdisc1のfileA, fileCは見当たらずdisc2のfileB, fileCのみが見える. つまり, 同時にマウントできるのは, 後にマウントした1つのディスクだけである.

Union マウント

UnionFileSystemでは, Unionマウント(以下Union)というマウントを行う.
これは, 同一のディレクトリから複数のディスクを1つのディレクトリとして透過的に扱うことができるようにするマウント方法である.

UnionMountとUnion-type Filesystem(1)を引用する.
以下のようにUnionFileSystemを表現している.

すでにディスクをマウントしているディレクトリ(マウントポイント)に別のディスクを重ねてマウントし、1つのマウントポイントから2台のディスクを同時に使用可能にするもの

f:id:namu_r21:20161027003927p:plain:w500

Unionを利用すると, 1つのディレクトリ上に3つのファイルが存在するように見える(fileCは同名なため1つとしてカウント). つまり, 実際には2つのディスクをマウントしているにもかかわらず, "見かけ上"は1つのディスクを扱っているように見える.

Unionでは, ディスクを階層的にマウントしていく. このため, 上記したように"別のディスクを重ねる"という表現になる.

実際には複数のディスクをマウントしているため, 複数のディスク上に同名のファイルがある場合問題がおきる. そのため, 常に上位レイヤのディスクを優先して読み込む.

図の場合, /dev/d2が上位, /dev/d1が下位ディレクトリである. fileCが同名ファイルであるが, この場合は, /dev/d2fileCが読み出されることとなる.

Union File System

Union File Systemは, Unionを利用したファイルシステムである.

Unionを行う場合, 下位ディスクをRead-Only, 上位ディスクをwrite/readに設定することが一般的である. 最上位のディスクをwritable状態にして, ユーザにファイルシステムを提供する.

f:id:namu_r21:20161027003958p:plain:w500

下位ディスクのデータをReadする必要がある場合はそのまま下位ディスク上のデータが読まれる.
下位ディスクのデータに対して, 書き込みを行う必要がある場合は, Copy on Writeを行う.

  1. Writableディスクに対象データをコピー
  2. Writableディスク上で対象データを編集
  3. Writableディスク上に対象データを保存

前述したように, Unionでは常に上位レイヤのディスクを優先して読み込む.
そのため, 最上位ディスクに保存することでデータを上書きしていくことができる.


dockerにおけるUnionFileSystem

冒頭でも行ったようにdockerはUnionFIleSystemを利用している.

dockerでは, イメージ用のディスクがRead-onlyなファイルシステムであり, その上にWritableなコンテナ用のディスクが重ねられる.

docker イメージとコンテナにおけるコピーオンライトの関係

図はdocker-doc イメージ、コンテナ、ストレージ・ドライバの理解より引用.

docker runでイメージからコンテナを起動した時点では, コンテナ・レイヤには何も記録されてない.

コンテナ上でファイルの"読み出し"を行う場合は, イメージ・レイヤの共通ストレージからデータ読み出しが行われる. このレイヤは, Read-onlyなため書き換えはできない.

対して, イメージ・レイヤの既存データに対して変更を加えた場合, 1度コンテナ・レイヤに対象データがコピーされそのデータを書き換えることになる.

その変更はイメージからの変更差分のみを記録する.
これがUnion File Systemの特徴である.

コンテナ内の状態変化はコンテナ・レイヤで閉じていて, 他のコンテナや大元のイメージには影響しない .

同じイメージから複数のコンテナを作った場合, コンテナごとにコンテナ・レイヤが用意される.
このため, コンテナ同士が互いに干渉せず, 複数の同環境で多様なオペレーションを行える.

僕のなかでdockerのイメージはこう解釈した.

dockerにおける"イメージ"は, 
"ベースイメージ(ファイルシステムの初期状態)に対する
ファイルの変更差分を階層的に記録した「ファイルシステムそのもの」"

コンテナは以下のような感じ.

イメージがもつファイルシステムを引き継いだ
変更部分のみを保持する固有のストレージを持つ1つの実行環境. 

dockerコンテナのライフサイクル

先日のエントリdockerのファイルシステムについて知る - 1++では, 以下のように書いた.

  dockerでは, イメージを元に"コンテナを作ったり消したりする"できることが特徴であり, 
「コンテナは使い捨て」という感覚で使われることが多い. 

コンテナはこのように"生き死に"を繰り返すので, 
「コンテナのライフサイクル」と呼ばれる.  

そして, "コンテナ上のメモリ空間はコンテナの終了とともに消える". 
つまり, コンテナ上でのファイル追加, 変更は"永続的に残らない"のである.

この部分についてもコメントでご指摘をいただいたので加筆. mapk0yさんのコメントを引用させていただきます.

コンテナを終了したらメモリ上のデータは消えるが差分ファイルシステムの writable に書かれたデータは残る。rm すると消える。

どうやら, docker rmでコンテナを削除するまでは, コンテナのwritable上にファイルが残るようだ. つまり, コンテナを削除しなければ, コンテナ上にデータは残る.

では, 永続的にデータをどうやって残すか.

コンテナのライフサイクルに関わらず永続的に残したいデータがある場合は, 以下の方法を取る.

  1. データボリュームを利用する.
  2. 現在のコンテナをイメージとしてcommitする(非推奨)
  3. データコンテナを利用する(推奨)

データ・ボリュームの利用

これは, ホストOS側にデータを記録する方法である. これをデータ・ボリュームと呼ぶ.
データ・ボリューム docker docs 公式和訳が詳しい. コンテナの起動時にホストOSのディレクトリ(データボリューム)をコンテナ起動時にマウントする. これによって, ファイルシステムの一部としてデータボリュームを利用できる.
.

現在のコンテナの状態をイメージとしてcommitする(非推奨)

dockerでは, コンテナのスナップショットをイメージとして吐き出すdocker commitというコマンドがある.

つまり, これまでコンテナ・レイヤだったストレージをイメージに含んでしまうのである.

これは元のイメージをある1つの環境に依存して汚してしまうため, お勧めできない方法である.

docker データ・コンテナ

最もお勧めできる方法は, データコンテナであるようだ. この方法では, 以下の手順をふむ.

  1. データボリュームをマウントしたデータ保存用のコンテナ(データコンテナ)を1つ作る.
  2. 他のコンテナにデータコンテナをマウントする

データコンテナのメリットは以下のようなものだ

  • データボリュームをrmするまでは, 永続的にデータが残る.
  • 複数のコンテナから同一のデータを参照できる.
  • 1つでも参照されているボリュームがあると, rmによるコンテナ削除ができない.

つまり, 1つのコンテナと存在するものの,
データコンテナとして参照され続けている間は
削除の対象にならない.

dockerを通してUnionFileSystemを勉強してみた所感

ファイルシステムがある種, "一方向のバージョン管理"のような仕組みを取られていて衝撃的であった.
きっちり記憶領域が分かれているから, "コンテナ間が依存しない"ということがわかった.

加えて, "データボリューム"周りの話についても, 以前より理解が深くなった.

dockerの永続的なデータ保持方法を知りたくて, 調べ物を始めたけれど
気づいたらファイルシステムの話をしていた.

まだまだ仮想環境やOSの話はわからないので学ぶことが多い.
今はvagrantの勉強をしているので, 次はそちらについて書く.