Laravelでミニブログを作ろう #2

前回:https://qiita.com/customaddone/items/87041975838b9e9c9b22
GitHub:https://github.com/customaddone/larablog

# docker概要
###コンテナとは
ホストOS上に論理的な区画（コンテナ）を設定し、その中でライブラリやアプリケーションを動作させるようにする技術。
###dockerとは
コンテナ技術を使ってアプリケーションの実行環境を構築、運用するプラットフォーム。
サーバ仮想化技術のように環境立ち上げの度にOSをインストールせず、各環境はホストOSのカーネル（中核組織）を共有する。
###dockerのメリット
- Dockerfile（dockerの設定）を共有することで、どこでも誰でも同じ環境が作れる。
- 作成した環境を他人と共有しやすい。
- スクラップ＆ビルドが容易にできる。

自分のPC（ホストOS）ではこのコマンドを使えるが、他の人のPCでは違うライブラリを入れているのでコマンドを使えない。といったことがあり得る。同じdocker環境内で作業することでこうした差異を減らすことができる。
→自分一人で開発してる場合よりチームで開発する場合の方がよりメリットを感じやすい（
と思う）

#Dockerfile
Dockerfile用のディレクトリを用意します

```:ターミナル
$ mkdir docker
$ cd docker

# ディレクトリ作成
$ mkdir php nginx selenium

# phpのDockerfileとその設定ファイルを作成
$ touch php/Dockerfile
$ touch php/php.ini

# nginxのDockerfileとその設定ファイルを作成
$ touch nginx/Dockerfile
$ touch nginx/default.conf

# mysqlのDockerfileとその設定ファイルを作成
$ touch mysql/Dockerfile
$ touch mysql/my.conf

# selenium（ブラウザテスト用）のDockerfileを作成
$ touch selenium/Dockerfile
```

phpのDockerfileを書いていきます
要件定義を元に必要なライブラリをphpコンテナ(とcomposer.json)に書いていきます
開発の過程で必要だと思った都度ライブラリをインストールしていくこともできますが、汎用性が高いDockerfileを目指すのであれば、Dockerfileで全て完結させるためにも要件定義の段階で必要なライブラリを洗い出すことが必要です

前回行った要件定義より
・Scss, Vue.jsを使う → npmパッケージが必要
・Terraformを使う → pipを使いTerraformをインストールすることが必要 → 今回ECSを用いたデプロイを行い、その際phpコンテナを用いるので、phpコンテナはできるだけ軽量化した方が良い → Terraformはphpコンテナでインストールせず別にコンテナを立てる

```Dockerfile:docker/php/Dockerfile
# 基本となるイメージを引っ張ってくる
FROM php:7.3-fpm-alpine

# docker内の作業用ディレクトリを設定
ENV WORKDIR=/var/www/html
WORKDIR $WORKDIR

# php.ini（phpの設定）をdocker内にコピーする
COPY ./docker/php/php.ini /usr/local/etc/php

# composerでインストールする内容一覧をdocker内にコピーする
COPY composer.json composer.lock ${WORKDIR}/

# laravelのファイル一式をdocker内にコピーする
COPY . .

# apk alpineのパッケージマネジャー
# とりあえずgitとnpmとvimが使えればいいかな
# git openssh,openssh-keygen,vim,gitが必要
# npm nodejs, nodejs-npmが必要
# bash,curlコマンドも要
RUN set -x \
    && apk update && \
    apk add --update --no-cache --virtual=.build-dependencies \
      autoconf \
      gcc \
      g++ \
      make && \
    apk add --update --no-cache \
      curl-dev \
      icu-dev \
      libzip-dev \
      openssl \
      openssh \
      openssh-keygen \
      nodejs \
      nodejs-npm \
      bash \
      vim \
      git
RUN docker-php-ext-install \
      pdo_mysql \
      zip
# composerをインストール
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN set -x \
    && composer update \
    && composer install --no-autoloader --no-progress --no-dev

# 適当にユーザーを作っとく
ARG UID=991
ARG UNAME=www
ARG GID=991
ARG GNAME=www

# php artisan config:clear キャッシュクリアー
# ３行目以下　ユーザーに権限付与
RUN set -x \
    && composer install --no-progress --no-dev \
    && php artisan config:clear \
    && addgroup ${GNAME} -g ${GID} \
    && adduser -D -G ${GNAME} -u ${UID} ${UNAME} \
    && chown -R ${UNAME}:${GNAME} ${WORKDIR} \
    && mv /root/.composer /home/${UNAME}/ \
    && chown -R ${UNAME}:${GNAME} /home/${UNAME}

# nginxからのアクセスの窓口
EXPOSE 9000
```

composer.jsonに追加するライブラリを書き込みます


```json:composer.json
{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The Laravel Framework.",
    "keywords": [
        "framework",
        "laravel"
    ],
    "license": "MIT",
    "require": {
        "php": "^7.2",
        "fideloper/proxy": "^4.0",
        "guzzlehttp/guzzle": "^6.5", # 追加　外部API使用用
        "laravel/framework": "^6.2",
        "laravel/tinker": "^2.0"
    },
    "require-dev": {
        "barryvdh/laravel-ide-helper": "^2.6",
        "facade/ignition": "^1.4",
        "fzaninotto/faker": "^1.4",
        "laravel/dusk": "^5.8", # 追加　ブラウザテスト用
        "mockery/mockery": "^1.0",
        "nunomaduro/collision": "^3.0",
        "nunomaduro/larastan": "^0.5.1", # 追加　静的解析ツール用
        "phpstan/phpstan": "^0.12.9", # 追加　静的解析ツール用
        "phpunit/phpunit": "^8.0"
    },
    "config": {
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "sort-packages": true
    },
    "extra": {
        "laravel": {
            "dont-discover": []
        }
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        },
        "classmap": [
            "database/seeds",
            "database/factories"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi"
        ],
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate --ansi"
        ]
    }
}
```

お好みのライブラリをcomposer.jsonに追加してコンテナ立ち上げ時に
composer update, composer installします

```Dockerfile:docker/php/php.ini
default_charset = "UTF-8"
max_execution_time = 30
memory_limit = 128M
file_uploads = On
upload_max_filesize = 5M
post_max_size = 5M
display_errors = On
```

nginx,mysql,seleniumのDockerfileを書いていきます
特に気をつけるところはありません
後は流れです

```Dockerfile:docker/nginx/Dockerfile
FROM nginx:1.15-alpine

COPY public /var/www/html/public
COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf.template

ENV PHP_HOST=127.0.0.1

EXPOSE 80

CMD /bin/sh -c 'sed "s/\${PHP_HOST}/${PHP_HOST}/" /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g "daemon off;"'
```

```Dockerfile:docker/nginx/default.conf
server {
    listen 80;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    root /var/www/html/public/;
    charset     utf-8;

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    location / {
        try_files $uri /index.php?$query_string;
    }

    location ~ [^/]\.php(/|$) {
        fastcgi_pass            app:9000;
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        fastcgi_index           index.php;
        fastcgi_param           SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param           PATH_INFO        $fastcgi_path_info;
        fastcgi_param           REQUEST_FILENAME $request_filename;
        include                 fastcgi_params;
    }
}
```

```Dockerfile:docker/mysql/Dockerfile
FROM mysql:5.7

EXPOSE 3306

COPY ./docker/mysql/my.cnf /etc/mysql/conf.d/my.cnf

CMD ["mysqld"]
```

```Dockerfile:docker/mysql/my.cnf
# このUTF-8の設定をしないと文字化けします
[mysqld] 
character-set-server=utf8
collation-server=utf8_general_ci

[client]
default-character-set=utf8
```

mysqlコンテナを立ち上げるとmysqlディレクトリができますが、gitignoreに記入してgitの管理外になるようにしましょう

```:.gitignore
/node_modules
/public/hot
/public/storage
/storage/*.key
/vendor
/mysql # 追加
.env
...
```

```Dockerfile:docker/selenium/Dockerfile
FROM selenium/standalone-chrome

LABEL maintainer="Edmund Luong <edmundvmluong@gmail.com>"

EXPOSE 4444
```

最後にdocker-compose.yamlを作成し、中身を書いていきます

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

services:
  nginx:
    build:
      context: .
      dockerfile: docker/nginx/Dockerfile
    volumes:
      - ./public:/var/www/html/public:ro
    ports:
      - 8888:80
    environment:
      PHP_HOST: app
    depends_on: # phpコンテナとアクセスするため必要
      - app
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    depends_on: # mysqlコンテナと連携するため必要
      - mysql
    env_file:
      - .env
    volumes:
      - .:/var/www/html:cached
  mysql:
    build:
      context: .
      dockerfile: docker/mysql/Dockerfile
    volumes:
      - ./mysql:/var/lib/mysql:delegated
      - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
    image: mysql
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
    environment:
      MYSQL_DATABASE: larablog
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: secret
    ports:
      - "3306:3306"
  selenium:
    build:
      context: .
      dockerfile: docker/selenium/Dockerfile
    ports:
      - '4444:4444'
```

あとはターミナルでdocker-compose up -dしてやると各コンテナが立ち上がります
docker-compose exec app shでappコンテナの中に入って作業ができます
gitなりnpmなりも使えるようになっています

次回はTerraformのdockerfileを書いていきます