どぅー!Hack

徒然なるままに、しぇありんぐ

Dockerの始め方

OSXでの導入方法と簡単な操作方法をまとめます。 Windowsは一応Docker for Windowsってのがあるけど、挙動微妙らしいしpro以上じゃないとHyper-Vが使えないから動かせないらしい。
Windows10には、Windows Subsystem for Linux(WSL)というカーネルレベルでbashが動く素晴らしい機能がありますが、こちらはデーモンが動かせないのでDocker用途にはつかえない。。。
次のクリエイターズアップデートでデーモンが動かせるようになるみたいなので期待

Background Task Support in WSL – Windows Command Line Tools For Developers

Linuxの場合は、apt等でインストールできます。

インストール

まずはdocker docsにアクセス
すると以下のような画面が出現するので、Get Dockerをクリック

開いたページのSupportede platformsの中にDocker for Mac(macOS)があるのでクリック

するとStable channelかEdge channelかを聞かれるのでStable channel(安定版)を選択

モーダルがでるのでそのままダウンロードしてください

ダウンロードしたdmgファイルを開くと下記のようなウィンドウが表示されると思うので絵の通りDocker.appをApplicationsにドラッグアンドドロップすればインストール完了です。

GUI管理ツールKitematicのインストール

Dockerには公式でイメージやコンテナをGUIで管理することのできるKitematicというアプリケーションが用意されています。イメージからコンテナを作ったり現在動いているコンテナにattachしたりexecしたりをGUIでできるのでDocker初心者にはおすすめです。
必須では無いですが導入方法をまとめます。

まず先程インストールしたDockerをアプリケーション一覧から起動

macの上部のメニューバーにDockerのクジラアイコンが出現していると思います。 そのアイコンをクリック

クリックするとメニューが開くのでKitematicを選択

するとインストールされていないといわれるので指示に従いインストール

下記のような画面が表示されればインストール完了です。

Dockerについて

Dockerを使う上で重要なのVM(仮想マシン)との違いを理解することだと思います。
VMと同じ感覚で扱ってしまい、意図した挙動にならないなんてことになりかねませんね。
そもそもDockerというアプリケーションはLinuxコンテナのテクノロジーを利用して、開発、デプロイ共有を実現しているアプリケーションです。要はLinuxコンテナをラップして開発環境に最適化して使いやすくしたアプリケーションってことです。なのでDockerを使いこなすには言うまでもなくコンテナを理解しなければなりません。

コンテナとは

コンテナというのはLinuxカーネルの機能を利用し、実行に必要なファイルをパッケージし分離するためのものです。これによって複数環境での移行が簡単にできたり、環境を汚さずに開発ができるというわけです。下記の図のイメージのように一つのカーネルの上でコンテナごとに環境を分けて実行することができます。

コンテナのイメージは本物のコンテナのように荷物(ミドルウェア)を詰め込むイメージでいいと思います。

コンテナとVM

VM(仮想マシン)

OSをまるごとエミュレートすることで一台のマシンの上に複数のOS,アプリケーションを実行することができる。ホスト型とハイパーバイザー型という構築方法があり右の図はハイパーバイザー型のイメージです。どちらもVMを載せるレベルが異なるだけです。ホスト型はホストOSの上に構築するため、ホストOSの制約を受けたりオーバーヘッドが発生する。ハイパーバイザー型はHyper-Vなどでハードウェアレベルで構築するためホスト型と比較してパフォーマンスが高いのが特徴である。

コンテナ

コンテナは上記で説明したようにホストOSのLinuxカーネルの機能を使って実行するのでVMのようにOSのイメージを必要としません。なので基本的にイメージファイルはVMのイメージと比べ遥かに小さいです。これによってイメージの共有が簡単にできるようになってます。 VMではOSの起動が完了しなければアプリケーションを実行できないに対して、コンテナは作成し即座に実行することが可能です。

Dockerイメージのレイヤー構造

個人的にはここがDockerを扱っていく上で最も意識しなければいけない点だと思います。
AUFSというファイルシステムによってDockerイメージはレイヤー化されています。
そしてイメージはread-onlyで、イメージから生成したコンテナのみwritableになっています。
イメージのwritableな部分がコンテナになっているのでgitのリポジトリワークスペースの関係と似ています。イメージにcommitした変更はread-onlyになってしまうので変更することができません。なので不要になったアプリケーション等はcommitする前に消しておかないと余分な変更としてずっと残ります。これによってイメージのサイズが大きくなってしまうので注意が必要です。

そして最も気をつけなければいけないのはこのレイヤーが127階層までしか作れないことです。なのであまり細々とした変更でレイヤーを作成するのはおすすめできません。オフィシャルなDockerfileなどを見ればわかりますが1つのRUNに複数のコマンドを詰め込んでいます。

nodeのDockerfile →https://github.com/nodejs/docker-node/blob/master/Dockerfile.template

exportしてからimportし直せばまたcommitできるようになりますが、今までの変更履歴が消えてしまうので出来るだけまとまったcommitをおすすめします。

Dockerfile

Dockerfileとは、イメージを生成するための命令を記述したものです。これを記述してdocker buildを実行することでDockerイメージを生成することができます。

ここではそのDockerfileの記述方法を説明したいと思います。

命令たち

以下の命令は、Dockerfileを書くにあたって最低限知っておきたい命令です。
他にも使える命令があるので公式のドキュメントなんかを参考にするといいでしょう。

公式ドキュメントのDocker docsが有志によって翻訳されています。

Docker ドキュメント日本語化プロジェクト — Docker-docs-ja 17.06.Beta ドキュメント

  • FROM ベースとなるイメージをリポジトリから取得する命令です。 必須項目であり1番初めに記述する必要があります。

FROM node:latest

:の後ろにタグを指定できます。省略した場合はlatestになります。
一つのDockerfileに対して複数回記述できるようですが、その場合は複数のイメージが生成されるようです。

  • RUN 指定されたコマンドを実行し、結果をコミットする命令です。
    このコマンドは記述の仕方が2通りあります。

    • シェル形式 RUN <コマンド> 指定されたコマンドが/bin/sh -cで実行される。
    • exec形式 RUN ["実行バイナリ", "パラメータ1", "パラメータ2"] 正直使ったこと無いです。これ書いてて初めて知りました。 Docker docs翻訳版によると

      exec 形式はシェルの文字列を変更できないようにします。また、 指定されたシェル実行環境がベース・イメージに含まれなくても RUN コマンドを使えます。

      らしいです。いままでイメージによってはbashが入っていなかったりしていたのでexec方式で書けば実行できるみたいですね。勉強になりました今度使ってみます。

  • WORKDIR 作業ディレクトリ指定する命令です。cdコマンドイメージしてもらうとわかりやすかもしれません。 複数回指定可能で、前回指定したディレクトからの相対パスでの指定も可能です。

サンプル

Dockerfileで使える命令いくつか紹介しましたが、最悪FROMとRUNさえ覚えておけばイメージは作れます。細かいところはコンテナを生成する際に指定してあげた方が良かったりもします。
てなわけでここでは例をいくつか紹介したいと思います。

RedisのDockerfile

FROM        ubuntu:14.04
RUN         apt-get update && apt-get install -y redis-server
EXPOSE      6379
ENTRYPOINT  ["/usr/bin/redis-server"]

MongoDBのDockerfile

FROM       ubuntu:latest
MAINTAINER M.Y. Name <myname@addr.ess>
# インストール:
# MongoDB 公開 GPG 鍵を取り込み、MongoDB リストファイルを作成
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
RUN echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-3.0.list
# apt-get ソースを更新し、MongoDB をインストール
RUN apt-get update && apt-get install -y mongodb-org
RUN apt-get update && apt-get install -y mongodb-org=3.0.1 mongodb-org-server=3.0.1 mongodb-org-shell=3.0.1 mongodb-org-mongos=3.0.1 mongodb-org-tools=3.0.1
# MongoDB データ・ディレクトリの作成
RUN mkdir -p /data/db
# コンテナのポート 27017 をホスト側に露出(EXPOSE)
EXPOSE 27017

# usr/bin/mongod を Docker 化アプリケーションのエントリ・ポイントに設定
ENTRYPOINT ["/usr/bin/mongod"]

PostgreSQLのDockerfile

FROM ubuntu
MAINTAINER SvenDowideit@docker.com
RUN apt-key adv --keyserver hkp:
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list
RUN apt-get update && apt-get install -y python-software-properties software-properties-common postgresql-9.3 postgresql-client-9.3 postgresql-contrib-9.3
USER postgres
RUN    /etc/init.d/postgresql start &&\
    psql --command "CREATE USER docker WITH SUPERUSER PASSWORD 'docker';" &&\
    createdb -O docker docker
RUN echo "host all  all    0.0.0.0/0  md5" >> /etc/postgresql/9.3/main/pg_hba.conf
RUN echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf
EXPOSE 5432
VOLUME  ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"]
CMD ["/usr/lib/postgresql/9.3/bin/postgres", "-D", "/var/lib/postgresql/9.3/main", "-c", "config_file=/etc/postgresql/9.3/main/postgresql.conf"]

これらは公式ドキュメントの翻訳版であるDocker-docs-jaのアプリケーションのDocker化から引用しました。どうでしょう、RedisのDockerfileなんかを見るとそんなに難しいとは思わないんじゃないでしょうか?Redisの様にほとんどインストールしか行っていないようなものからpostgreSQLのようにコンフィグファイルの書き換えを行ったりするものもあります。うまく自分の作りたい環境に合わせて命令を駆使して作成すると良いでしょう。

この例ではRUN命令を複数回にわけて記述されていますが、Dockerイメージのレイヤー構造でも説明した様にRUN命令は実行ごとにcommitされるのでレイヤーがその分増えてしまします。なので出来るだけ一つのRUN命令に詰め込んで記述することをおすすめします。

イメージの作成

Dockerfileが書けたなら以下のコマンドでイメージを生成することができます。

docker build [Dockerfileのあるディレクトリパス]

-t [名前]で生成するイメージに名前をつけることができます。 docker imagesでイメージの一覧を表示することができます。docker buildがうまくできていれば一覧の中に存在するはずです。

コンテナの生成

イメージができたらコンテナを生成します。基本的にコンテナはdocker runのコマンドで生成されることが多いです。

基本的な形は以下のとおりです。

docker run [オプション] イメージ[:タグ|@ダイジェスト値] [コマンド] [引数...]

docker runコマンドはコンテナを生成するために頻繁に使われるため、勘違いしやすいと思いますがコンテナの生成だけを行うコマンドはdocker createです。docker runはコンテナを生成した後、指定したコマンドを生成したコンテナ内で実行するコマンドです。それに加えてこのコマンドは他のコマンドよりオプションが多く存在するのでコンテナの細かい設定を行うことができます。
本当に多くのオプションが存在するのでここではよく使うものだけ紹介します。

  • -i, --interactive コンテナへの標準入力が可能になる。シェル等で入力出来るようになる。入力できるようになるだけなので仮想コンソールが割り当てられていないとコンテナへのコマンド実行等はできないため後述の-tコマンドと一緒に使われる事が多い。

  • -t, --tty 仮想コンソールを割り当てる。-iコマンドと一緒に使われる。

  • -d,--detach バックグラウンドで実行する。開発というよりサービスを稼働させるときなど指定することが多い。 --rmコマンド指定できなくなる。

  • -p,--publish コンテナ内のポートをホスト上のポートで公開する。
    -p [ホスト上のポート]:[コンテナ内のポート]と指定することで公開することができます。

  • -v,--volumes ホスト上のディレクトリをマウントすることができる。
    -v [ホスト上のディレクトリ]:[コンテナ内のディレクトリ]でマウントすることができる。
    この時コンテナ内のディレクトリの指定は常に絶対パスでなければいけません。
    Dockerfileの命令にVOLUMEという同じような命令がありますが、そちらではイメージのポータビリティを保持するためホスト上のディレクトリを指定することができません。

コンテナの操作

作成したコンテナに対する操作を行なうコマンドをいくつか紹介します。

  • docker ps 現在実行中のコンテナを一覧で表示します。-aをつけることで起動していないコンテナも表示することができます。コンテナに対するコマンドはコンテナIDで特定のコンテナを指定するので確認するために頻繁に使います。psコマンドのコンテナ版だと思うと覚えやすいですね。
    ちなみにコマンドでコンテナを指定するためのコンテナIDは、被らなければ頭から3文字くらいでも指定できます便利です。

  • docker attach [コンテナID] 実行中のコンテナに接続することができます。

  • docker exec [コンテナID] [コマンド] 実行中のコンテナで指定したコマンドを実行することができます。

  • docker start [コンテナID] 停止中のコンテナを起動することができます。

  • docker stop [コンテナID] 起動中のコンテナを停止します。

  • docker rm [コンテナID] コンテナを削除します。

イメージの共有方法

ここまででイメージを作ってコンテナを生成しました。これで開発環境としては普通に使用できると思います。それだけでも早く軽量な開発環境が手に入り喜ばしいことですがDockerの良さはそれだけではありません。生成したイメージに変更を加え、それを開発チームで簡単に共有することもできます。ここでは、その方法を紹介します。

DockerHubにログイン

イメージを共有するにはDockerレジストリ(保存場所)が必要です。
Dockerレジストリは公式が配布しているイメージを使用することで独自のサーバ上でも運用可能です。
以下のコマンドでレジストリにログインする事ができます。

docker login [サーバー]

[サーバー]のところに何も入力しないと自動的にDocker Hubへのログインになります。

リポジトリを作成

レジストリ上のリポジトリにpushします。pushする前に事前にレジストリ上にリポジトリを作成しておく必要があります。Dcoker HubではRepositoriesの画面のCreate Repositoryから作成できます。

遷移した画面の項目を適当に埋めたらCreateをクリックすると作成されます。

ローカルリポジトリにcommit

変更したコンテナをリポジトリ(イメージ)にコミットする必要があります。コミットしたいコンテナを指定し以下のコマンドを実行してください。

docker commit [コンテナID] [リポジトリ]

[リポジトリ]には上記で作成したレジストリリポジトリ名を指定してください。形式はユーザー名/イメージ名のような形になると思います。
これでイメージに対してコンテナ内の変更をcommitすることができます。この時マウントしているディレクトリ内の変更はcommitされません。

レジストリリポジトリにpush

ログインしたレポジトリへのpushは以下のコマンドです。

docker push [リポジトリ]

これであなたのイメージを他人に共有する準備が整いました。

レポジトリのpull

上記の手順を終えレジストリリポジトリにpushできたなら共有したい人に以下のコマンドを教えてあげましょう。

docker pull [リポジトリ]

これでレジストリリポジトリのイメージが共有できたと思います。