前回の予告通りDockerです!!
最近よく聞くし、使ってみたいけど何からやったら良いか分からないという方のために、新卒未経験エンジニアの僕がDockerを使ってみました!
て記事を書いたら安心して導入出来るんじゃないかな、と思います。
今回はDockerのチュートリアルを試してました。
Dockerって何?
Dockerの公式イメージキャラクターはこちらのクジラくんです。
上の四角いブロックみたいなの何?、て思うかもしれません。
そもそもDockerという言葉の意味をみなさまご存知でしょうか?
Dockerとは、「港湾労働者」という意味があります。
造船所に行くと「1番ドック」のように、船を泊めておく駐車場的なスペースを「Dock」と呼んでいます。(某有名海賊漫画で学びました。)
そのDockで働く人たちなので、Dockerです。
貿易に携わるDockerたちの仕事道具がコンテナ(Container)です。
コンテナには細かい規格や積み方のルールがあり、また、船による効率的な海上輸送を実現する重要な手段になっています。
「Docker」はまさにこのイメージの通りで、コンテナの中に自分の使いたいOSやミドルウェア、ファイルシステムを自由に入れることが出来ます。
いやー、なんとも秀逸な名前だなぁ、と思います。
Dockerの仕組み
Dockerは「コンテナ型の仮想環境」を構築するツールです。
仮想環境には
・ホスト型
・ハイパーバイザー型
・コンテナ型
があります。
ホスト型、ハイパーバイザー型とコンテナ型とでは大きな違いがあります。
ホスト型、ハイパーバイザー型は仮想マシンレベルの仮想化を行い、コンテナ型はOSレベルの仮想化を行っています。
ホスト型・ハイパーバイザー型は、PCの中に仮想的にもう1台PCを立ち上げるようなイメージです。
そしてコンテナ型はホストOSがLinuxであれば、わざわざもう1台のPCを立ち上げるようなめんどくさいことはしなくて良いのです。
こうすることで、コンテナ型の大きなメリットが見えてきます。
・仮想マシンよりも起動が早いこと
→サクサク動く!
・リソースの消費が少ないこと
→サクサク動く!!
・起動する環境に依存せずに動かせる
→サクサク開発!!!
案件ごとに異なる環境を構築できるため、特定のPC依存を回避出来ますし、ミドルウェア導入や新インフラ環境のテストが各自のPCで可能になります。
なんとまぁ素晴らしいことでしょう!!
僕自身説明下手なのとまだまだ理解が浅いので詳しく説明出来ないのですが、とりあえず多くのサイトで語られているのは上記のようなメリットです。
コンテナに限ったイメージでいえば、巨大なコンテナ船の中にたくさんのプールがあり、そのプールにはそれぞれ別のコンテナ船が浮かんでいるというような感じですかね。
イメージとコンテナ
ではコンテナの中にどんなものを入れていくか、またどのように入れるか気になるところだと思います。
コンテナは「イメージ(image)」という、コンテナの設計書のようなものを元に作られます。
恒例の、僕のイメージをお伝えします!
旅行にいく前の夜を想像してみてください。
忘れ物をしたくない方は持っていくものをリスト化してそれを元にカバンに物を詰めていきます。
そのリスト化された項目が書かれた紙がイメージ、そのリストを元に作られた旅行カバンがコンテナです。
ちなみに僕は用意は当日の朝にする派です笑
AWsを使ったことある方は、AMIを想像してもらえればと思います。
イメージを実体化させたものがコンテナで、また、同じイメージから複数のコンテナを作成することが可能です。
何はともあれ事前準備
まずは Dockerをインストールしましょう!
公式サイトからインストール出来ます。
インストール出来て、うまく起動出来ればmacのメニューバーのところにクジラのロゴが現れます。
いざ実践!
参考にさせて頂いたのはこちらのサイトです。
公式チュートリアルで始めるDocker
Docker起動確認
まず、きちんとDockerが起動出来ているか確認します。
$ docker version
Client:
Version: 17.09.0-ce
API version: 1.32
Go version: go1.8.3
Git commit: afdb6d4
Built: Tue Sep 26 22:40:09 2017
OS/Arch: darwin/amd64
Server:
Version: 17.09.0-ce
API version: 1.32 (minimum version 1.12)
Go version: go1.8.3
Git commit: afdb6d4
Built: Tue Sep 26 22:45:38 2017
OS/Arch: linux/amd64
Experimental: true
Dockerのバージョンが表示されたら起動確認OKです。
続いて以下のコマンドを打ってみてください。
コンテナ作成
$ docker run -it ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
ae79f2514705: Pull complete
5ad56d5fc149: Pull complete
170e558760e8: Pull complete
395460e233f5: Pull complete
6f01dc62e444: Pull complete
Digest: sha256:506e2d5852de1d7c90d538c5332bd3cc33b9cbd26f6ca653875899c505c82687
Status: Downloaded newer image for ubuntu:latest
root@38f5848d06eb:/#
「docker run」というコマンドはコンテナを起動する際に打つコマンドです。
その元となるイメージがここでは「ubuntu」を指定しているのですが、そんなものはないので、ネット上からpullしてきています。
オプションの「-it」は「-i」と「-t」を両方行う、という意味でそれぞれ
-i:interactiveの意味で、コンテナのシェルとinteractiveにコマンドをやり取りすると言う意味
-t:仮想端末を割り当て
です。
ubuntuの後についている「bash」はコンテナを起動して最初に動かすコマンドです。
最後にrootユーザーになっているのは無事ubuntuを起動出来た証拠です。
Commit!!
元のイメージをカスタマイズすることも出来ます。
試しにcurlコマンドを打ってみます。
root@38f5848d06eb:/# curl 127.0.0.1
bash: curl: command not found
そんなコマンド知らねぇ、と怒られます。
DockerのImageは最小構成のミドルウェアしか入ってないため、必要なものを自分で追加する必要があります。
root@38f5848d06eb:/# apt-get install -y curl
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package curl
エラーになったのはapt-getのパッケージの取得を全く行っていないためだとか。
これを解決するにはupdateコマンドを叩いて必要なパッケージを取得してあげる必要があるようです。
root@38f5848d06eb:/# apt-get -qq update
root@38f5848d06eb:/# apt-get install -y curl
・・・
・・・
root@38f5848d06eb:/# curl 127.0.0.1
curl: (7) Failed to connect to 127.0.0.1 port 80: Connection refused
Failedと出てはいますが、curlコマンドは通っているので無事にミドルウェアを追加出来たことを確認出来ました。
この変更をcommitしてあげる必要があります。
ここら辺はgitと同じ要領で行うことが出来ます。
psコマンドを使うことで先ほどのubuntuイメージを元に作成したコンテナのIDを確認出来ます。
一旦、exitコマンドを打ち、コンテナからexitしてからpsコマンドを打ちます。
root@38f5848d06eb:/# exit
exit
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38f5848d06eb ubuntu "bash" 16 minutes ago Exited (7) 11 seconds ago friendly_keller
コンテナIDが38f5848d06eb だということが分かったので、そのIDを指定してcommitします。
$ docker commit 38f5848d06eb kosuke/curl:1.0
sha256:43f98db0a2f026ed6c06c8f7c5f424a375dada8e0953c2644233b6b5deedfb7a
この時、最後の「kosuke/curl:1.0」は何でも良いのですが、「:」以下はTAGとして認識されます。
おそらくバージョン管理に利用したりするんだと思います。
なので、
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
kosuke/curl 1.0 43f98db0a2f0 11 days ago 177MB
と表示されます。
これでubuntuにcurlがインストールされたイメージが作成されました!!
イメージからコンテナ起動
では作成したイメージを元にコンテナを起動してみましょう!
$ docker run -it kosuke/curl:1.0 bash
root@f9b24c878481:/# curl 127.0.0.1
curl: (7) Failed to connect to 127.0.0.1 port 80: Connection refused
今度は何もしなくてもcurlコマンドが通ったかと思います。
これはcurlが入ってますよ、という設計書(イメージ)を元に作成したコンテナだからです。
Dockerfileを作ってみよう!
Dockerイメージ作成時のコンフィグファイルです。
つまり、手順書のようなものでこいつを共有することでどんな複数に人が同じ環境を作り出せるようになるのです。
大事なやつです。
$ mkdir DockerTest
$ cd DockerTest
$ vim Dockerfile
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
FROMでベースImageを指定し、RUNで自動実行するスクリプトを指定します。
Dockerfile
作成したDockerfileを指定してイメージを作成してみます。
$ docker build -t kosuke/testimage:1.0
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu
---> 747cb2d60bbe
Step 2/4 : RUN apt-get update
---> Using cache
---> 8a13d832a50e
Step 3/4 : RUN apt-get install -y curl
---> Using cache
---> 95ff1ddebfac
Step 4/4 : RUN apt-get install -y vim
---> Using cache
---> fe1f32a75803
Successfully built fe1f32a75803
Successfully tagged kosuke/testimage:1.0
-tオプション以降は例によって何でも良いです。
作成したイメージを確認します。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
kosuke/testimage 1.0 fe1f32a75803 11 days ago 230MB
はい!
無事作成出来ました!
コマンド実行してみる
Dockerfile内でCMDキーワードを使うことでdocker runした際に、任意のコマンドを実行出来ます。
今回はpingコマンドを実行させてみることにしましょう!
$ vim Dockerfile
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y curl vim
CMD ping 127.0.0.1 -c 30
ちなみにRUNのコマンドは「&&」でつなげてまとめることが出来ます。
Dockerfileを編集出来たらbuildしていきます。
$ docker build -t kosuke/testimage:1.1 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu
---> 747cb2d60bbe
Step 2/4 : RUN apt-get update && apt-get install -y curl vim iputils-ping
---> Using cache
---> ac0b52a45fdf
Step 3/4 : CMD ping 127.0.0.1 -c 30
---> Using cache
---> 6bd251e733b3
Step 4/4 : ENTRYPOINT ping
---> Running in 9426e3bb8666
---> a6c3e8e65687
Removing intermediate container 9426e3bb8666
Successfully built a6c3e8e65687
Successfully tagged kosuke/testimage:1.1
ちなみに、Dockerfileの記述で過去に同じ指示をしている場合、Imageがキャッシュ化されていますので、Buildは高速に終わります。
イメージを作成出来たら早速コンテナを起動させて、pingコマンドが実行されるか確認してみましょう。
$ docker run -t kosuke/testimage:1.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.058 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.067 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.071 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.068 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.068 ms
はい!何とか無事pingコマンドが実行されました!!
また、Dockerfileの中身を以下のように変えても同じような結果が得られます。
$ vim Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y curl vim
ENTRYPOINT ["ping"]
$ docker run -t kosuke/testimage:1.2 .
$ docker run -t kosuke/testimage:1.2 127.0.0.1 -c 5
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.067 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.056 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.066 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.067 ms
エントリーポイントを用いることで、Dockerを起動時にあらかじめ指定したコマンドを実行することが出来ます。
ここで一つの疑問が。
CMDとエントリーポイントは何が違うのか・・・
長くなるので割愛しますが簡潔にまとめますと
CMD:docker run時の引数でCMD commandのcommand部分を全て上書きして実行
エントリーポイント:docker run時の引数がENTRYPOINT ["command"] のcommand の引数として実行
という違いがあります。
詳しくは以下の記事で紹介されていますので興味があればご参考にしてください。
[docker] CMD とENTRYPOINT の違いを試してみた
コンテナの起動とストップは以下のコマンドで行います。
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
352373b0b5aa kosuke/testimage:1.2 "/bin/sh -c 'ping ..." 12 minutes ago Exited (0) 11 minutes ago priceless_shirley
4038641f5e36 kosuke/testimage:1.1 "/bin/sh -c 'ping ..." 23 minutes ago Exited (127) 23 minutes ago ecstatic_perlman
3ab31c3cc95f kosuke/testimage:1.0 "/bin/sh -c 'ping ..." 27 minutes ago Exited (127) 27 minutes ago sad_darwin
$ docker stop 352373b0b5aa
352373b0b5aa
$ docker start 352373b0b5aa
352373b0b5aa
最後にコンテナの削除とイメージの削除を行ってみます!
・コンテナの削除
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
352373b0b5aa kosuke/testimage:1.2 "/bin/sh -c 'ping ..." 12 minutes ago Exited (0) 11 minutes ago priceless_shirley
4038641f5e36 kosuke/testimage:1.1 "/bin/sh -c 'ping ..." 23 minutes ago Exited (127) 23 minutes ago ecstatic_perlman
3ab31c3cc95f kosuke/testimage:1.0 "/bin/sh -c 'ping ..." 27 minutes ago Exited (127) 27 minutes ago
$ docker stop 352373b0b5aa
352373b0b5aa
$ docker rm 352373b0b5aa
352373b0b5aa
起動中のコンテナを削除をする時は一旦stopさせてから削除するかオプションで-fをつけて削除します。
・イメージの削除
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
kosuke/testimage 1.2 6bd251e733b3 2 weeks ago 230MB
kosuke/testimage 1.1 a62c9ca758f3 2 weeks ago 230MB
kosuke/testimage 1.0 a62c9ca758f3 2 weeks ago 230MB
$ docker rmi 6bd251e733b3
Untagged: kosuke/testimage:1.2
Deleted: sha256:0c19b849e34f7711501cfacd34dd63dcb523cb78ffb4068da1ba61dc92ebfd18
コンテナが起動中の場合、イメージの削除が出来ないのでその場合はコンテナを停止させてから、イメージの削除を行います。
まとめ
イメージの作成からコンテナの起動、コンテナの削除とイメージの削除までを一連の流れで確認してみました。
実際に手を動かしてやってみると割と分かり易く、イメージも湧きやすかったです。
最初の「Dockerって何?」のところのイメージがあったから分かりやすかったからかもしれません!
やはり、身近な例に例えてイメージをするのが良いのかもしれません。
現在、Dockerで開発環境の構築に勤しんでいるので出来次第、また記事にして投稿しようと思っているのでお楽しみに!!