どこにでもいるSEの備忘録

たぶん動くと思うからリリースしようぜ

Dockerについて勉強してみた

f:id:nogawanogawa:20180918224803p:plain:w500
 

プログラミングすることは好きでも、環境構築は嫌いという方も多いのではないでしょうか? 私は環境構築大嫌いですw

最近では"Docker"なるものがあって、何やら環境周りを簡単にできる模様です。 今どきのソフトウェア開発の現場では、もはやDockerを使えないとプログラマとしてやっていけない雰囲気がある(ほんとか??)ので、今回はDockerについて勉強してみました。

参考にしたのはこちら。

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

Dockerとは

Dockerのアーキテクチャ

以下にホストOS型仮想化とコンテナ型仮想化の比較の図を示します。

f:id:nogawanogawa:20180918230719j:plain:w600

段の数が違いますね。コンテナ型にはゲストOSがいないんです。

昔ながらの仮想化技術では、仮想化基盤の上にゲストOS用の領域を確保してその上にOSを乗せて環境を構築していました。 コンテナでは、Docker Engineが仮想化ソフトウェアとゲストOSの二役をこなすため、段の数が減っています。

物理マシンに乗っかるソフトウェアが少なければ少ないほど冗長な動作は減少し、より軽量に使用することが可能になります。

Dockerを利用する意義

Dockerを使うと何が嬉しいんでしょうか? この本ではDockerを使用するメリットとして以下の四点が挙げられています。

  • 不変な実行環境のによる冪等性の確保
  • 実行環境構築とアプリケーション構成のコード化
  • 実行環境とアプリケーションの一体化によるポータビリティ性の向上
  • システムを構成するアプリケーションやミドルウェアの構成管理の容易さ

不変な実行環境のによる冪等性(べきとうせい)の確保

近年ではImmutable Infrastructure(不変なインフラ)という考え方が提唱されており、いつ何度実行しても同じ結果が保証される冪等性を保つことが望ましいとされています。 Dockerでは、ある地点でのサーバーの状態を保存し複製可能にすることで、正しくセットアップされたサーバーを常に使用できるようになるメリットがあります。

実行環境構築とアプリケーション構成のコード化

全く同じアプリケーションでも、デプロイ先のサーバーが違えば当然挙動も違います。 こうした意図しない挙動の差異をなくすべく、近年ではInfrastructure as Code(インフラの構成管理)という考え方が提唱されています。 Dockerを使用すればインフラをDockerfileとしてコードベースで定義でき、手作業が介在する余地を減るため、サーバーの意図しない挙動を回避することに役立ちます。

実行環境とアプリケーションの一体化によるポータビリティ性の向上

また、Dockerでのデプロイの考え方も非常に重要です。 従来では、開発環境で開発したアプリケーションをどこかのサーバーにデプロイしていたため、インフラの再現とアプリケーションのデプロイは切り離されて考えられていました。 Dockerは、OSとアプリケーションを同梱した箱のようなもので、インフラとアプリケーションをセットでデプロイすることができます。 これにより、作業環境の差異が生まれづらくなり、アプリケーションのポータビリティが高くなるメリットがあります。

システムを構成するアプリケーションやミドルウェアの構成管理の容易さ

複数のアプリケーションを組み合わせた大規模なシステムを構築するためには、実行するアプリケーションだけでなくそれらで使用されるミドルウェアを適切に管理する必要があります。 Dockerでこのようなシステムを実現するためには、複数のコンテナのアプリケーション間でノードをまたいで管理するオーケストレーションと呼ばれる機能が提供されています。 これにより、運用がこれまでより手軽になります。

Containerの考え方

Docker Containerが動くまでの大まかな流れを以下に示します。 Dockerで環境を構築する際の大まかな流れは大まかにこんな感じです。

f:id:nogawanogawa:20180921203728j:plain:w600

まず、Docker Imageを手に入れます。 これは、DockerfileからImageを作成したりDocker Hubからダウンロードすることで入手可能です。

次にDocker Imageを実行することで、Docker Containerにします。

イメージとしては、Dockerfileが買い出しリスト、Docker Imageがレシピ、Docker Containerが料理といったところでしょうか。 このような流れでDocker環境を構築していきます。

使い方

※細かいコマンドやら、サンプルコードは本買って読んでください。

Dockerfile

DockerfileはDocker独自のDSLドメイン固有言語)で記述します。 このDSLでは様々なインストラクションが提供され、これらを組み合わせることでインフラ構成を定義します。

主要なインストラクションを下に示していきます。

FROM

Dockerイメージのベースとなるイメージを指定します。 FROMは1つのDockerイメージを作る際に1つしか使えません。

RUN

Dockerイメージのビルド時にDockerコンテナ内で実行するコマンドを定義します。

COPY

Dockerを動作させているホストマシン場のファイル・ディレクトリをDockerコンテナ内にコピーします。

CMD

Dockerコンテナとして実行する際に、コンテナ内で実行するプロセスを設定します。 CMDもFROM同様、1つのDockerイメージを作る際に1つしか使えません。

LABEL

イメージの作者名などに使用するようです。

ENV

Dockerコンテナ内で使用できる環境変数を指定します。

ARG

ビルド時に情報を埋め込むために使用します。イメージビルドのときだけ使用できる環境変数

Dockerfileの書き方

ものすごくシンプルに書くとこんな感じでしょうか。

まずFROM句でベースとなるDockerイメージを取得します。これは、Docker hub(Dockerイメージを提供しているサービス、Githubのようなもの)からイメージを取得します。

その後、3000番ポートを開放して、appディレクトリを作って、Dockerの中にmain.jsファイルを取り込んでいます。 起動すると、main.jsが実行されます。

Dockerイメージ

DockerfileをビルドするとDockerイメージが作成されます。

f:id:nogawanogawa:20180922210725j:plain:w500

こんなコマンドでビルドができるらしいっす。

docker image build -t イメージ名[:タグ名] Dockerfile配置のディレクトリパス

出来上がったDockerイメージを確認するときはこちらのコマンド。

docker image ls

こんな感じに出てくればOKです。

Docker コンテナ

Dockerイメージはあくまで、Dockerコンテナを作るための設計図のようなものなので、ここからコンテナを作る必要があります。

コンテナを作るとは、概念的にはread onlyなDockerイメージの上に書き込み可能なレイヤを生成するようです。

f:id:nogawanogawa:20180922210748j:plain:w500

docker container run [options] イメージ[:タグ] [コマンド] [コマンド引数]

必要なくなったコンテナはこちらのコマンドで停止できます。

docker container stop

領域がもったいないときは、こちらのコマンドでコンテナを削除します。

docker container rm

大まかな説明としては以上です ←

試しに使ってみる

こういうのは実際に使ってみないといいとこも悪いとこもわからないので、実際に作ってみます。

作りたい環境

例えばこんな感じの環境を作ってみます。

いやいや、何の環境だよ、、、ってツッコミは置いておいて、とりあえず作ってみます。

サンプルを引っ張ってくる

まずは、UbuntuのDockerイメージを取得してみます。

$ docker pull ubuntu:18.04
18.04: Pulling from library/ubuntu
Digest: sha256:de774a3145f7ca4f0bd144c7d4ffb2931e06634f11529653b23eba85aef8e378
Status: Image is up to date for ubuntu:18.04

DockerImage

DockerImageを確認してみます。

$docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 18.04 cd6d8154f1e1 2 weeks ago 84.1MB

DockerContainer

動かしてみるとこんな感じでした。

$docker container run -it ubuntu:18.04
root@647d913a5cbc:/#

オプションについて、"-i"オプションは

root権限で仮想環境のターミナルに入れました。

コンテナ内にいろいろインストール

コンテナ内でインストール作業をしてもいいんですが、dockerからexitするとすべて消えてしまうので、Dockerfileを適当に書きます。

これでなんとなく行けそうです。 Dockerを起動して確認してみます。

root@3e3dc4bace9f:/# python3 --version
Python 3.6.6
root@3e3dc4bace9f:/# node -v
v8.10.0
root@3e3dc4bace9f:/# java -version
openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-0ubuntu0.18.04.1-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
root@3e3dc4bace9f:/# mysql --version
mysql Ver 14.14 Distrib 5.7.23, for Linux (x86_64) using EditLine wrapper

おー、できてるっぽいですね。

コンテナんの停止

コンテナのターミナルに入っているときは、

exit

コンテナを停止できます。 停止の確認をしてみると、

docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

何もいません。 停止中のコンテナも含めて表示すると、コンテナIDを確認することができます。

docker ps -a

再度コンテナを起動して接続する場合は、このコンテナIDを使用して

docker container start -i <container id>

で接続できます。

感想

とりあえず、なんとかDockerが使えるようになったと思います。 実際には、Dockerイメージのデータサイズを小さくすることを考えたり、複数コンテナを起動してオーケストレーションしたりするらしいので、まだまだ奥が深いです。

オーケストレーションに関してもKubernetesとかもこの本には載っていたのですが、それはまた今度、なにかの機会に勉強して書こうかと思います。