【初心者向け】手を動かして覚えるDocker入門

# はじめに
本記は「**Dockerをこれから触ろう/学ぼうとしている人向け**」の記事になります！

- Dockerの歴史
- 実際にDockerを動かすハンズオン

が記載されています！

# Dockerとは
## そもそもDockerて何もの？
一言でいうと、**「軽量な仮想環境」を構築するツールです。**

今までエンジニアが開発を行う際、「**Hyper-V**」や「**Vagrant**」「**VirtualBox**」など、PC上に仮想のPCを作成する仮想化ツール( **ハイパーバイザー型**)を利用してきました。
便利な上記のツールですが、それらは皆大きな課題を抱えていました。
***「環境構築に時間がかかる」「起動にも時間がかかる」「ファイルサイズが大きい」***

<img width="653" alt="スクリーンショット 2022-04-30 19.02.37.png (766.3 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/04/30/122490/441fea60-1ef2-46df-9d8b-46c3e1e7b4ac.png">



そんな課題を解決してくれるのが、**Docker**というコンテナ型の仮想化技術です。
従来のハイパーバーザー型の仮想化技術とは違い、アプリケーションの実行をホストOSが行っているため、「**GuestOS**」を起動・利用する必要がなくなりました。
またDockerはコンテナ型の仮想化技術 を用いていることから、実行されるアプリの単位を「**コンテナ**」と呼びます。
(**※コンテナ型の仮想化技術を利用する場合、ホストOSはLinuxである必要があります。**)

> Q：**なぜ「GuestOS」利用をする必要がないのか？**
> A：Linuxのカーネルを用いて、 コンテナ単位でアプリ(プロセス)を実行するためGuestOSが不要になります。
> 参考：[Linuxのカーネルとディストリビューションについて](https://lpi.or.jp/lpic_all/linux/intro/intro02.shtml)

## コンテナ型仮想化技術の歴史について
これだけ便利に見えるDocker(コンテナ型の仮想化)ですが、人気が出てきたのは割と最近な気がします。
ですがコンテナ技術自体は1980年頃から存在しているのです。

<img width="644" alt="スクリーンショット 2022-04-30 19.40.36.png (2.3 MB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/04/30/122490/81a84036-8bb5-492e-a8a2-2be4dbe06c14.png">


### コンテナ技術の原点「chroot」
1979年にコンテナ技術の原点である「**chroot**」が誕生しました。
名前の通り「**root(/)ディレクトリを変える(change)**」技術になります。

chrootでルートで区切ったディレクトリごとにユーザー権限の付与を行うことで、複数人がアクセスできる擬似的な仮想環境を作成することができます。
現在のコンテナ技術のように「**1つのLinuxカーネル**」を使って、複数の仮想環境を作成していたためコンテナ技術の原点と言われています。

またchrootによって区切られたディレクトリは外に出られないことから「**chroot監獄**」と呼ばれていたそうです。

<img width="601" alt="スクリーンショット 2022-05-01 19.59.21.png (637.1 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/05/01/122490/6c7ed5c8-a00d-40e4-8895-5c8ce2dcba3b.png">


ただこの頃のchrootはセキュリティ上に問題があったため、後続の「**FreeBSD Jail**」からレンタルサーバーやホスティングの共用サーバーとして利用され始めたそうです。

> chrootは二段階chrootで簡単に突破出来てしまうのでセキリュティの観点からは意味がないという話があったそうです。
> https://www.gcd.org/blog/2007/09/132/

### コンテナ技術のコアパーツ「Cgroup, Namespace」
先程のchroot等のおかげでコンテナ型の仮想化環境を作成することはできました。
ですがこれまでの仮想化技術はいくつかの課題が合ったのですが、その中でも「**ホストとのプロセス分離が完全にできない**」「**ハードウェアリソース割当ができない**」等の課題がありました。
それを解決する技術である**cgroup(Google社)** と **namespace(RedHat社)** が公開されました。

####  Namespace
**Namespace**はシステムの**ネットワークやプロセス等を分離する**技術になります。
これが利用できるようになったことにより、コンテナ型の仮想化技術が完全に隔離された仮想環境として利用できるようになりました。

<img width="554" alt="スクリーンショット 2022-05-01 21.27.34.png (793.8 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/05/01/122490/9ad84fd4-1878-4502-8fbe-694dfec2b6fe.png">

> Q：プロセスとは？
> A：CPU上で実行されるのプログラムやアプリの単位ことです。
> https://milestone-of-se.nesuke.com/sv-basic/architecture/cpu/

<img width="554" alt="スクリーンショット 2022-05-20 11.22.13.png (251.6 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/05/20/122490/875988ee-47b3-4268-96f8-eb928df21628.png">


#### cgroup
**cgroup**はハードウェアの**物理的なリソース**を分離する技術になります。
ハイパーバイザー型の仮想化技術を使ったことがある人は身に覚えがあると思いますが、仮想環境を作成する際にメモリやCPUの割当をやったことがあるのではないでしょうか？
メモリやCPUを仮想環境単位で分離・制限させる仕組みを実現するためにcgroupが利用されています。

[**Hyper-V のリソース割当画面**]
<img width="600" alt="スクリーンショット 2022-05-16 15.33.30.png (104.3 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/05/16/122490/2acb38bc-7920-4181-af2f-50b34ccee033.png">


### そして現代へ「LXC, Docker」
これら仮想化のための技術が出てきた後、我々が今も利用しているようなコンテナ技術が世の中に出周ってきました。
その中でも特に有名なのが「**LXC(Linux Container)**」「**Docker**」になります。

#### LXC
2008年頃に「**LXC(IBM社)**」という**namespace**や**cgroup**を組み込んだ、最初のLinuxのコンテナエンジンが発表されました。
これは**chroot**では実現できなかった、プロセスや物理リソースの分離ができるコンテナ型仮想化技術の原型になります。
現在でもLXCは「**軽量なLinux仮想環境と構築するツール**」として様々なところで利用されています。

#### Docker
お待たせしました。2013年、ついに**Docker(dotCloud社：現Docker社)**が発表されました。
DockerはLXCとは異なり、**アプリケーションのデプロイ**を目的として作られた技術のため「**1 コンテナ1プロセス**」という仕組みが取られています。
「サードパーティの充実さ」や「導入敷居の低さ」も相まって、仮想化技術の覇権を握ることになりました。

またDockerのリリース当初はLXCの技術をベースに作られていたそうですが、時代が進むにつれてLXCとの依存関係を無くすため、**libcontainer**を利用するようになりました。

<img width="644" alt="スクリーンショット 2022-04-30 19.40.36.png (2.3 MB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/04/30/122490/81a84036-8bb5-492e-a8a2-2be4dbe06c14.png">

参考記事：
- [【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その1:コンテナってなに？ ](https://tech-lab.sios.jp/archives/18811)
- [コンテナってなんだろう― 「コンテナ」の概要を知る - Think IT](https://thinkit.co.jp/article/17301)
- [【chroot】 なぜFTPでレンタルサーバーにアクセスしても、他のアカウントのファイルが見れないか？](https://go-journey.club/archives/552)


# Dockerを使ってみよう
前章でコンテナ型の仮想化技術について理解が深まったと思います。
本章から実際に手を動かしながらDockerの使い方を覚えていきます。

## Dockerで利用される「イメージ」「コンテナ」について
先程、DockerはホストOSを利用して仮想化したアプリを起動すると説明しました。
その*仮想化した*アプリ*をどのように作成するのかを解説します。

Dockerを利用する私達は、２つのDocker用語を覚える必要あります。
- **イメージ**：コンテナを実行するためのテンプレートが記載されている
- **コンテナ**：イメージを用いて、仮想環境上でアプリを起動させる

### イメージとは
Dockerの「**イメージ**」とは、コンテナを実行するためのテンプレートになります。
基本的には[DockerHub](https://www.docker.com/products/docker-hub/)から取得したり、自身でカスタマイズしたイメージを利用します。
(CDのようなイメージ)
<img width="633" alt="スクリーンショット 2022-03-29 12.51.11.png (173.7 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/03/29/122490/53bbae95-7aef-446b-bd1f-0090597d5c95.png">


### コンテナとは
先程説明したイメージを実行する環境になります。
イメージがなければコンテナの起動はできません。
(CD Playerのようなイメージ)
<img width="684" alt="スクリーンショット 2022-03-29 12.57.40.png (231.3 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/03/29/122490/71efa4ab-d6e4-4474-94bc-66535802bc36.png">



 ## Docker環境を作成してみよう
Ubuntu環境をDockerHubからインストールし、nginxを起動させてみましょう。

まずはじめにDockerHub から、[Ubuntu](https://hub.docker.com/_/ubuntu)のイメージをインストールします。
```sh
docker pull ubuntu:latest
```

インストールした`Ubuntu`イメージをコンテナで実行します。
```sh
# open browser 「localhost:8080」
docker run -it -d -p 8080:80 --name ubuntu ubuntu:latest
```

参考記事：
- [「docker run -it」にするとコンテナがすぐに終了しない理由](https://architecting.hateblo.jp/entry/2020/08/13/153842)

nginxのインストール・起動を行っていないため、Ubuntuにnginxを入れて実行させましょう。
```sh
# Ubuntu環境に侵入
docker exec -it ubuntu bash

# nginxのインストール
apt update && \
apt install -y -q curl gnupg2 && \
curl http://nginx.org/keys/nginx_signing.key | apt-key add - && \
apt install -y -q nginx

# nginxの起動
nginx
# reload browser 「localhost:8080」
```

起動させたコンテナを終了させる場合は、以下を実行
```sh
# コンテナの停止
docker stop [コンテナID]
# コンテナの削除
docker rm [コンテナID]
```

これまでの仮想化技術だったら数十分〜数時間かかっていた準備〜起動も、Dockerだと数秒〜数分で起動できる！（嬉しいね！）

参考：
- [Docker コマンド — Docker-docs-ja 19.03 ドキュメント](http://docs.docker.jp/v19.03/engine/reference/commandline/index.html)
- [【Ubuntu + Nginx】Docker イメージの作成手順](https://tamnology.com/docker-image/)

# Dockerfileを使おう
Dockerについて概要を理解できたでしょうか？
Dockerを便利に使うための機能として、「**Dockerfile**」というものが存在します。
その「**Dockerfile**」というものについて解説します。

## DockerFileって何？
先程Dockerを使い、Ubuntuコンテナ内からNginxを起動させましたが
***毎回Ubuntuに入ってNginxを起動させるのは煩わしい***

ですので先程出てきました「**イメージを自分で作る**」という手段で、***Ubuntu + nginxが起動する環境***を作ります。
その際に使われるのが、「Dockerfile」になります。

<img width="849" alt="スクリーンショット 2022-03-29 13.35.54.png (250.2 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/03/29/122490/4f239c33-0295-4336-aa47-11b9a19a9f40.png">


 ##  DockerFileからオリジナルイメージを作成しよう
今回作成するDockerfileの完成形はこちらになります。
お好きなディレクトリに`Dockerfile`(拡張子なし)というファイルを作成して以下を記載します。

```Dockerfile
# 利用する元のイメージを指定
FROM ubuntu:latest

# 必要なパッケージをインストール
RUN apt update && \
    apt install -y -q curl gnupg2 && \
    curl http://nginx.org/keys/nginx_signing.key | apt-key add - && \
    apt install -y -q nginx

# nginx実行
CMD ["nginx", "-g", "daemon off;"]
```

以下のコマンドを実行することで、対象のDockerfileからイメージの作成を行います。
```sh
# 「-t」は作成するイメージに付ける名前。今回は「ubuntu-nginx」と名付けます。
docker build -t ubuntu-nginx:latest .
```

イメージの作成ができたので、以下のコマンドからコンテナを実行すると***ubuntu + nginx***の環境が立ち上がります。
```sh
docker run --rm -p 8080:80 ubuntu-nginx:latest
```

<img width="844" alt="スクリーンショット 2022-03-29 13.40.15.png (283.6 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/03/29/122490/a575fd4a-564f-48d3-a772-026b592957f0.png">


イメージ作成が完了したので、これ以降はコンテナを起動させるだけで***ubuntu + nginx***の環境が使えます！

# Docker Composeを使おう
前項の内容でDockerfileを使い、自身のオリジナルイメージを構築する事ができました。
最後に、もっとDockerを便利に使うための機能として、「**Docker Compose**」というものを紹介したいと思います。

## Docker Composeって何？
一言で言いますと、「**Docker Compose**」はコンテナのマネジメントツールです。

前項で**Dockerfile**を使い、オリジナルの仮想環境(イメージ)を作成し、コンテナでイメージを実行しました。
しかし実際に開発を行う場合は、1 つのイメージ(コンテナ)だけで事足りるケースというのは少ないです。
- フロントエンドサーバー
- バックエンドサーバー
- DBサーバー
- セッションサーバー

など様々なアプリケーションの実行環境用意する必要があります。
ですが、その度にDockerのコンテナを1つずつ実行するのは大変億劫です。

そんな時「**Docker Compose**」を使うことで、複数のコンテナ環境をまとめて実行・管理する事ができます。

<img width="864" alt="スクリーンショット 2022-03-29 14.54.11.png (375.9 kB)" src="https://img.esa.io/uploads/production/attachments/10655/2022/03/29/122490/8b38e366-e577-47b2-87a3-4559690d6d51.png">


 ## Docker Composeを使って、複数のコンテナを動かしてみよう
今回作成するDocker Composeの完成形はこちらになります。
`docker-compose.yml`というファイルを作成して以下を記載します。

先ほど作成した`Ubuntu+Nginx`のイメージの他に、`PHPAdmin`と`MySQL`のイメージを実行したいと思います。

```yml
version: '3'
services:
  #　先程作成したイメージを指定
  app:
    # Dockerfileを指定することも可能
    # https://qiita.com/kai_kou/items/eaafa3cb15e1496f50ec
    image: ubuntu-nginx
    ports:
      - 8080:80

  # 利用するイメージを追加
  mysql:
    image: mysql:8.0-oracle
    environment:
      - MYSQL_ROOT_PASSWORD=password
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=mysql
      - PMA_USER=root
      - PMA_PASSWORD=password
    ports:
      - 8081:80
```

以下のコマンドを実行することで、対象の`docker-compose.yml`に書かれたイメージからコンテナの作成を行います。
```sh
docker-compose up
```

停止するときは以下のコマンド
```sh
docker-compose down
```

**Dockerfile**と**Docker Compose**を使うことで、1コマンドで様々な環境を作れるようになりました。
また**Dockerfile**と**Docker Compose**はテキストファイルなので、Git管理できるのも嬉しいですね！

# Dockerまとめ

## Docker
- Linux上で利用することのできる、「**軽量な仮想環境**」を作れるツール
- 「**イメージ**」から「**コンテナ**」を作成し、プロセスの実行を行う
- 「[DockerHub]([DockerHub](https://www.docker.com/products/docker-hub/))」というところにある、様々なイメージを利用することができる

## Dockerfile
- DockerHubにあるイメージを自分好みにカスタマイズして、新しいイメージを作成する機能
- 「**Dockerfile**」というファイルに、自分のカスタマイズ環境を記載する

## Docker Compose
- コンテナのマネジメントを行う機能
- 「**docker-compose.yml**」というファイルに、管理したい「イメージ」「コンテナ」の指定・設定を行う
- 利用できるイメージは「DockerHubにあるイメージ」、「ローカル環境で作成したイメージ」、「Dockerfile」のいずれか


# 最後に
弊社でQiitaEngineerFesta2023の記事投稿キャンペーンを実施してます。
参加＆投稿お待ちしております！

https://qiita.com/official-events/0cc35c4799f72609b7ec