Dockerハンズオン資料

# 今日のゴール

- Docker についてふんわり分かる
- dockcer-compose でイメージのビルドができる
- 最初につらつら概要が書かれているのでいきなり始めたい人は[ハンズオン](#ハンズオン)から始めてください

# Dockerとは

- 軽量仮想化技術
- 標準化されたソフトウェアをコンテナとして管理できる

## コンテナと仮想マシン（VM）の比較

https://www.docker.com/resources/what-container から引用

### コンテナ

![Containers](https://www.docker.com/sites/default/files/d8/2018-11/docker-containerized-appliction-blue-border_2.png)

- ハードウェアではなくOSシステムを仮想化する
- コードと依存関係を一緒にパッケージ化するアプリ層の抽象概念
- 同じマシン上っで複数のコンテナを実行し、OSカーネルを他のコンテナと共有できる
- VMよりも占有するスペースが少ないのでより多くのアプリケーションを動かすことができる

### 仮想マシン

![Virtual Machines](https://www.docker.com/sites/default/files/d8/2018-11/container-vm-whatcontainer_2.png)

- 仮想マシン（VM）は１台のサーバーを多数のサーバーに変える物理ハードウェアの抽象概念
- 各VMにはOS、アプリケーション、バイナリ、ライブラリの完全なコピーが含まれているので、VMの起動が遅くなりがち

# Dockerを体験する

- 前提として`docker`コマンド、`docker-compose`コマンドが実行できる環境であること
    - https://docs.docker.com/ から自分のOSにあったバイナリをインストールする
        - 一番上のメニューにある Get Docker > For Desktops から選ぶ 

## ubuntuを起動する

手始めに Ubuntu の Docker イメージを起動します

```sh
$ docker run -it ubuntu /bin/bash
```

- `-t` は `tty` をアロケートする（端末名を取得する）
- `-i` は interactive（STDINを開いたままにする）

この辺はオプションを外してみるとどういう動きかわかるので、どういう動きをするかそれぞれ確かめてみよう

```sh
$ docker run -i ubuntu /bin/bash
$ docker run -t ubuntu /bin/bash
$ docker run ubuntu /bin/bash
``` 

### docker run は何をしているか

- `docker run`は`docker create`と`docker start`を同時にやっている

```sh
$ docker create -i -t ubuntu /bin/bash
1a2fa0c21916b2f323498d740c74f2a0fc6ac832268e9c906500dcc7c09612c7

$ docker start -a -i 1a2fa0c21916
root@1a2fa0c21916:/#
```

- `docker create`をすると特定のイメージ（例はubuntu）上に書き込み可能なコンテナレイヤを作成します。表示される英数字はコンテナのIDです。
- `docker start`をすると作成したコンテナを起動することができます。イメージのIDは全てを指定する必要はなく、特定できる長さで大丈夫です。

## phpを起動する

```sh
$ docker run -it php /bin/bash
```

- 起動してコンテナの中に入ったらphpのバージョンを確認してみよう
    - PHPのバージョンはそのときの最新バージョンが表示されます。

```sh
root@79b00020abf8:/# php -v
PHP 7.3.3 (cli) (built: Mar 27 2019 01:07:43) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.3, Copyright (c) 1998-2018 Zend Technologies

root@79b00020abf8:/# php -i
（phpinfo() の結果が表示されます）
```

## image はどこから持ってきているのか

- DockerのイメージはDocker Hubから引っ張ってきています
    - https://hub.docker.com/
    - 開発を始めるときはDocker Hubでイメージを探して引っ張ってくると便利です
        - nginx
        - php-apache
        - redis
        - mysql など
- Amazon ECRやGoogle GCRを指定して引っ張ってくることも可能です
    - 会社で使っている場合はこっちの方が多いですね
        - [Amazon Elastic Container Registry](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/what-is-ecr.html)
        - [Google Container Registry](https://cloud.google.com/container-registry/docs/overview?hl=ja)
        - [Azure Container Registry](https://azure.microsoft.com/ja-jp/services/container-registry/)

# Dockerをカスタマイズする

## DockerfileでDockerイメージをカスタマイズする

- Docker Hubから持ってきたイメージはだいたい最小構成なので、使う用途にあわせてカスタマイズが必要になります
- コンテナに入って必要なものをインストールすることもできますが、`Dockerfile`に記述することで、手順がコード化できるので他の人も再現可能な状態になります

```docker:Dockerfile
FROM php:7.4-cli
RUN pecl install redis-5.2.1 \
    && pecl install xdebug-2.9.4 \
    && docker-php-ext-enable redis xdebug
```
- このDockerfileをbuildして実行してみましょう

```bash
$ docker build . -t docker-handson/php:7.4-cli
$ docker run --rm -it docker-handson/php:7.4-cli php -v
```

## docker-composeで複数のコンテナで構成されるアプリを使う

- だいたいのアプリは複数のコンテナを組み合わせて使います
    - webサーバー + データベース
    - proxyサーバー + アプリサーバー + データベース + キャッシュサーバー
- `docker-compose`を使うことでコンテナ間の通信設定、コンテナのオプション設定、ビルド、コンテナの起動をまとめて行うことができるようになります

# ハンズオン

- お題: dockerでlaravelの環境をつくる
    - PHP+Apache と mysql の構成でつくって、Laravelを動かすことをできるようにします

## 準備

- 自分の環境で`docker`コマンド`docker-compose`コマンドを使えるようにします
- 作業用のフォルダを作ります
    - 必要であれば`git init`してgit管理しておくとメモを使えます

```sh
$ mkdir docker-handson;
$ cd docker-handson && mkdir docker
```

## サンプルアプリケーションを作ります

- `composer`を実行するだけのimageをpullして`laravel`のプロジェクトを作成します
- Windowsユーザーの方は`$PWD`の環境変数ががないので、`$PWD`ところは自分の作業するディレクトリ名に読み替えて指定してください

## Dockerfileを作ります

```docker:docker/Dockerfile
FROM php:7.4-apache

RUN \
    docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd --with-libdir=lib/x86_64-linux-gnu/ \
    && docker-php-ext-configure mysqli --with-mysqli=mysqlnd --with-libdir=lib/x86_64-linux-gnu/ \
    && docker-php-ext-install mysqli \
    && docker-php-ext-install pdo_mysql

RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf

RUN a2enmod rewrite

WORKDIR /var/www
```

## docker-composeファイルを作ります

```yaml:docker-compose.yaml
version: '3'

services:
  app:
    build:
      context: ./docker
    volumes:
      - ./my-laravel:/var/www/my-laravel
    ports:
      - "8080:80"
    working_dir: /var/www/my-laravel
    environment:
      APACHE_DOCUMENT_ROOT: /var/www/my-laravel/public
      TZ: "Asia/Tokyo"

  mysql:
    image: mysql:5.7
    ports:
      - "3306:3306"
    environment:
      MYSQL_USER: root
      MYSQL_DATABASE: laravel
      MYSQL_ALLOW_EMPTY_PASSWORD: 1
      TZ: "Asia/Tokyo"
```

## コンテナを起動してブラウザでアクセスしてみましょう

```sh
$ docker-compose up -d
```

起動できたら http://localhost:8080/ でアクセスします。Laravelのデフォルト画面が表示できたらOKです。

![スクリーンショット 2019-04-08 15.32.03.png](https://qiita-image-store.s3.amazonaws.com/0/16460/db5ae999-9339-1285-51ce-a9d2ae561790.png)

## Advance

ここからは応用なので、自分たちで考えてみよう！

### 1. appのコンテナからmysqlに接続してみよう

**やりたいこと**

- CLIで接続するにはどうしたらよいだろうか？
- Laravelのappから接続できるようにしてみよう
    - PHPUnitで接続するテストを書いて実行してみよう

**ヒント**

- dockerコンテナに入らないでコマンドを接続するにはどうしたらいいでしょうか？
    - `docker-compose exec` で実現できます
- このテストコードが正常に通れば接続できています

```php:src/my-laravel/tests/Unit/DbConnectTest.php
<?php

namespace Tests\Unit;

use Illuminate\Support\Facades\DB;
use Tests\TestCase;

class DbConnectiTest extends TestCase
{
    public function testConnectDatabase()
    {
        $this->assertTrue(DB::statement('SELECT NOW()'));
    }
}
```
- phpunix.xmlの設定値は適宜変更してください

### 2. 接続するコンテナを増やしてみよう

**やりたいこと**

- キャッシュ用にRedisを配置して、appコンテナから接続できるようにしよう
- port8080でアクセスするのが面倒なので、localhost/my-laravel でアクセスできるように、フロントサーバーを配置しよう

**ヒント**

- コンテナを増やすには`docker-compose.yaml`に追加します
- frontサーバーにはnginxを使ってみよう
    - nginxの設定で、`/my-laravel`にアクセスがきたら、appコンテナにリクエストを回そう

### 3. 別のアプリを増やしてみよう

**やりたいこと**

- 別のアプリをつくることになったので、コンテナ群にアプリを増やしたい
- front, db, redisは共通のコンテナを使いたい

**ヒント**

ここまできたらヒントなし！チャレンジしてみましょう。