基礎から理解する Docker
はじめに
業務で使用している Docker の仕組みや全体感といった基礎的な理解したかったのでまとめてみました。
内容としては、前半で理論的なことを軽くさらって、後半では公式のチュートリアルを題材として実際にローカルで Docker を動かしながら動かすといった形となっています。
key | value |
---|---|
開発元 | Docker, Inc. |
公式サイト | Empowering App Development for Developers | Docker |
GitHub | docker/docker-ce |
プログラミング言語 | Go |
対応 OS | Linux / macOS / Windows |
対象読者
- Docker を使ったことがない
- 導入してみたいが学習コストを気にして導入に漕ぎ着けていない人
- あまり理解していないがなんとなくで使ってるいる人
Docker とは何か
結論から言うと、__Docker とは「コンテナ型仮想化技術」を利用してアプリケーションを開発・運用するためのプラットフォームです。
いきなり「コンテナ型仮想化技術」という単語が出てきた。よく PC に仮想環境を構築するなど言うため仮想化という単語は聞き覚えのある方が多いのではないでしょうか。では、「コンテナ型仮想化技術」は従来の仮想化とどう違うのでしょうか。
これまでの仮想化技術、そしてコンテナへ
ここでは、PC(ホスト OS)に仮想化ソフトを使ってゲスト OS を配置する従来型の仮想化と、Docker が実現するコンテナ仮想化技術を比較することでコンテナ仮想化技術の理解していきましょう。
従来型仮想化
ホストマシン上にハイパーバイザーを通して、ゲストマシンを動かします。基本的にホストマシンのリソースをゲストマシンと共有します。VirtualBox などの仮想化ツールを使用して行うことが多く、立ち上げたゲストマシンの中で実際に必要なミドルウェアやライブラリをセットアップしていくことになります。
コンテナ型仮想化
一方でコンテナ型仮想化は、ホストマシンのカーネルを利用して、Docker Engine からコンテナを構築します。コンテナのプロセスやユーザは、ホストマシンとは隔離され独立して動作するので、あたかもホストマシン上でゲストマシンが立ち上がっているかのように振る舞います。
また OS を独自で準備する必要もなく、リソースを無駄に使うことがないので動作が軽く高速というのも特徴です。
コンテナが解決する課題
さて、従来の仮想化と比較して構成にどういう違いあるのかが分かったところで具体的にコンテナを使うことで何が嬉しいのかという点にスポットを当てていきましょう。調べると色々言われていますが中でも__「環境構築のプロセスをコード化して、共有できる。」__という点が非常に大きいのではないでしょうか。
詳しく説明すると、従来の仮想環境構築ではゲスト OS に入ってからライブラリやミドルウェアをインストールする必要があります。その作業は、コマンドを叩いて手作業で行うため、ミスやバージョンの違いなどが発生する恐れがあります。また、同じ開発チーム内のメンバー同士でもローカル環境に差異が生じてしまう可能性があります。
一方で Docker を用いたコンテナ仮想化ではコンテナの定義を Dockerfile
という 1 つのファイルで定義します。開発チームでは、そのファイルを共有するだけで手元のローカルには同じ環境を作ることができ、非常にメンテナンス性に優れています。
この話は開発だけではなく、プロダクトの提供にも影響があります。一般的にプロダクトをリリースするとなると検証環境、ステージング環境、本番環境とインフラを用意するケースが多々あります。しかし、デプロイフローをちゃんと決めていても検証環境、ステージング環境では起きないはずの障害がなぜか本番環境だけで起きてしまう、ということを経験した開発者は多いのではないでしょうか。Docker を用いることで、全ての環境で同じコンテナが動作する環境を作ることができるため、論理的にはそのような現象は起きなくなります。
実践
いよいよ実際に Dockerfile
を書いて動かしていきます。今回利用するソースコードは公式チュートリアル [1] にあるものをそのまま利用することとします。今回は基礎理解が目的なので、触りまでしかやりませんがもっと先まで学んでみたいという方は是非そちらをご活用ください。
Hello world
何はともあれ Hello world やってみます。
# ローカルのマシンに docker が入っているか確認
$ docker --version
Docker version 19.03.8, build afacb8b
# `docker run`イコマンドでメージからコンテナを起動
$ docker run hello-world
# ローカルに指定したイメージがない場合は、docker hub より自動でインストールされる
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9
Status: Downloaded newer image for hello-world:latest
# イメージが作られ Hello world と出力された
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
# `docker image` コマンドでイメージの一覧を確認
$ docker image ls
REPOSITORY TAG IMAGE ID
hello-world latest bf756fb1ae65 1 minutes ago 13.3kB
# `docker ps` コマンドでプロセスを確認
$ docker ps --all
c0d1570f2fc2 hello-world "/hello" 8 minutes ago Exited (0) 8 minutes ago
細かいところを全部理解しなくて良いので、ここでは Docker イメージを取得してコンテナを起動するというざっくりとした流れを掴むのが重要です。
Dockerfile
の作成
公式で用意されているイメージをカスタマイズしていきます。その場合、カスタマイズする内容を Dockerfile
というファイルを作成し定義します。
# 公式の Python がインストールされたベースイメージを利用
FROM python:2.7-slim
# コンテナ内の作業ディレクトリを /app に設定
WORKDIR /app
# 現在のディレクトリの内容を、コンテナ内の /app にコピー
COPY . /app
# requirements.txt で指定された必要なパッケージを全てインストール
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# ポート 80 番をコンテナの外でも利用可能に設定
EXPOSE 80
# 環境変数を定義
ENV NAME World
# コンテナ起動時に app.py を実行
CMD ["python", "app.py"]
次は、コンテナ上で動かすアプリケーションとして Python ファイルを app.py
という名前で作成し、下記のように記述します。
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
最後にコンテナ上にインストールしたいライブラリ、ミドルウェアを requirements.txt
というテキストファイルに書き出して Dockerfile から参照できるようにしておきます。
Flask
Redis
準備が整ったところでそれらを実行します。
# 現在このような状況になっている
$ ls
Dockerfile app.py requirements.txt
# 作った Dockerfile をビルドして、イメージを作成
# `--tag` オプション: イメージにタグを付与
$ docker build --tag=friendlyhello .
Sending build context to Docker daemon 5.12kB
Step 1/7 : FROM python:2.7-slim
...
略
...
Successfully built 8f74a6392754
Successfully tagged friendlyhello:latest
# ビルドが終わったら、一旦イメージの一覧を確認
$ docker image ls
REPOSITORY TAG IMAGE ID
hello-world latest bf756fb1ae65
friendlyhello latest 326387cea398
# イメージが作成されているのを確認できたらコンテナを起動
# `-d` オプション: コンテナをバックグラウンドで実行
# `-p` オプション: ポートの割り当てを指定
$ docker run -d -p 4000:80 friendlyhello
$ docker ps
コマンドでステータスが Up
になっていれば無事にコンテナが動いています。ブラウザで localhost:4000 にアクセスすると Hello world とコンテンツが表示されているのも確認できますでしょうか。
Dockerfile を書いて、自分が用意したいアプリケーションの実行環境を( Docker イメージ
)作り、コンテナ
上で動かすというところまで一通りできました。
まとめ
- Docker とは「コンテナ型仮想化技術」を提供するソフトウェアプラットフォームである
- コンテナ型仮想化技術とは、従来の仮想化技術に取って代わるデファクトスタンダードになりつつある
- Docker は環境構築のプロセスをコード化し、効率化することを実現
- またその仕組み上、軽量かつ高速なアプリケーション実行環境を提供
業務ではほとんどのプロジェクトで Docker を導入しており、今後も使う機会は増えてくるはずなので、このタイミングで概念や良さについて振り返ることができて良かったです。少しでも多くの方の参考になれば幸いです。
参考にさせていただいたサイト
Discussion