docker-laravel の Docker 構成をちゃんと理解する【Dockerfile 編 - PHP】

昨日は[ブッチ](https://zenn.dev/t_kitamura)さんのミライトデザインに入ってからの１年を振り返る記事でした。

https://zenn.dev/t_kitamura/articles/3b85d2c331f0ad

ブッチさんとはお互いがミライトデザインに入る前から知り合いだったので、今同じ会社で仕事をしているのが不思議な感じですね。

## この記事について

@ucan-lab さんの [docker-laravel](https://github.com/ucan-lab/docker-laravel) をちゃんと理解したいので調べるという目的の記事です。

https://qiita.com/tkek321/items/9c0c5cec1dc349c67f5f

前回の記事では飛ばしていた Dockerfile について調べていきます。

docker-compose.yml の他の記述に関しては前回の記事で解説しています。

## Dockerfile のコマンド

各 Dockerfile の詳細を見る前に docker-laravel で使われている Dockerfile のコマンドを確認します。

- [FROM](https://docs.docker.jp/engine/reference/builder.html#from)

 - ビルドステージを初期化して、ベース・イメージを指定
     - ビルドステージを初期化っていうのはおそらく、以前のビルド結果に影響を受けづに、新規でビルドを開始するみたいなイメージのはず
 - ベース・イメージは以降の命令から影響を受ける
     - ベースイメージに対してファイルをコピーしたりとか 

- [LABEL](https://docs.docker.jp/engine/reference/builder.html#label)

 - イメージにメタデータを追加する。
 - key と value の組み合わせ
 - コンテナの動作に影響はない

- [SHELL](https://docs.docker.jp/engine/reference/builder.html#shell)
 - shell で使われるデフォルトのコマンドを上書きできる

- [ENV](https://docs.docker.jp/engine/reference/builder.html#env)
 - ビルドステージ内での環境変数を指定する

- [COPY](https://docs.docker.jp/engine/reference/builder.html#copy)

 - `COPY <コピー元:ホストマシン> <コピー先:Docker内>` 
 - ホスト（自分の Mac とか） から Docker のコンテナ内にファイルやディレクトリをコピーできる

- [RUN](https://docs.docker.jp/engine/reference/builder.html#run)
 - シェルでコマンドを実行できる
 - ホストではなくコンテナ内で実行される

- [WORKDIR](https://docs.docker.jp/engine/reference/builder.html#workdir)
 - `RUN` 、 `CMD` 、 `ENTRYPOINT` 、 `COPY` 、 `ADD` 命令の処理時に（コマンドを実行する場所として）使う 作業ディレクトリworking directory を指定します。
 - ディレクトリが存在しなければ作成する

## php

https://github.com/ucan-lab/docker-laravel/blob/main/infra/docker/php/Dockerfile

```Dockerfile:Dockerfile
# ① ベースイメージを指定
FROM php:8.0-fpm-bullseye  
# ② メタデータを追加
LABEL maintainer="ucan-lab <yes@u-can.pro>" 
# ③ デフォルトのシェルをオーバーライド
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"] 

# --- ④ 環境変数を設定 ---
# timezone environment
ENV TZ=UTC \
  # locale
  LANG=en_US.UTF-8 \
  LANGUAGE=en_US:en \
  LC_ALL=en_US.UTF-8 \
  # composer environment
  COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer
# ------

# ⑤ composer をインストール
COPY --from=composer:2.1 /usr/bin/composer /usr/bin/composer 
# --- ⑥ イメージ上に任意のコマンドを実行 ---
RUN apt-get update && \
  apt-get -y install git libicu-dev libonig-dev libzip-dev unzip locales && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  locale-gen en_US.UTF-8 && \
  localedef -f UTF-8 -i en_US en_US.UTF-8 && \
  mkdir /var/run/php-fpm && \
  docker-php-ext-install intl pdo_mysql zip bcmath && \
  composer config -g process-timeout 3600 && \
  composer config -g repos.packagist composer https://packagist.org
# ------

# ⑦ zzz-www.conf をコンテナにコピー
COPY ./infra/docker/php/php-fpm.d/zzz-www.conf /usr/local/etc/php-fpm.d/zzz-www.conf
# ⑧ php.ini をコンテナにコピー
COPY ./infra/docker/php/php.ini /usr/local/etc/php/php.ini

# ⑨ 作業ディレクトリを指定
WORKDIR /work/backend
```

### ① ベースイメージを指定

`FROM php:8.0-fpm-bullseye`

- php の[イメージ](https://hub.docker.com/layers/php/library/php/8.0-fpm-bullseye/images/sha256-70ae031f0b7c9250fbfd798d6e303143d073d13c9ed847f709c6d8a8e82fbffa?context=explore)を Dockerhub から取得している

- [本家](https://qiita.com/ucan-lab/items/5fc1281cd8076c8ac9f4#from-%E5%91%BD%E4%BB%A4)に詳しい解説が乗っていました。

> PHPのイメージタグの種類として cli, apache, fpm, zts の4種類が用意されています。

- ここで apache なんかを選択すると nginx のコンテナを立てて、unix ソケットで通信して、みたいなことは必要なくる。
    - nginx のコンテナが必要なくなって、php と mysql だけで良くなる（phpのコンテナに apache がすでに入っていることになるので）

- os は bullseye を使用している
    - bullseys は [Debian](https://ja.wikipedia.org/wiki/Debian%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E5%B1%A5%E6%AD%B4) のバージョン11

### ② メタデータを追加

`LABEL maintainer="ucan-lab <yes@u-can.pro>"` 

- ucan さんがメンテナンスしてるよって書いてあるだけ、動作に影響はない


### ③ デフォルトのシェルをオーバーライド

[本家](https://qiita.com/ucan-lab/items/5fc1281cd8076c8ac9f4#shell-%E5%91%BD%E4%BB%A4)

- Linux のデフォルトのシェル（`["/bin/sh", "-c"]`）から `bash` に変更している
- `pipefail` あたりのオプションでエラーを見やすくしたりしてる

あんまりわかってないけど、シェルの設定っぽいので今回はいいかな ...

### ④ 環境変数を設定

[本家](https://qiita.com/ucan-lab/items/5fc1281cd8076c8ac9f4#env-%E5%91%BD%E4%BB%A4)


#### `TZ=UTC` 

- OS（Debian） のタイムゾーンを UTC に設定している

#### `LANG=en_US.UTF-8`, `LANGUAGE`, `LC_ALL` 

https://linuxfan.info/locale-variables

- 言語設定をしている
- 優先順とか色々あるらしい

### ⑤ composer をインストール

`COPY --from=composer:2.1 /usr/bin/composer /usr/bin/composer`

[本家](https://qiita.com/ucan-lab/items/5fc1281cd8076c8ac9f4#copy-%E5%91%BD%E4%BB%A4%E3%83%9E%E3%83%AB%E3%83%81%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%83%93%E3%83%AB%E3%83%89)

> --from オプションを付けると別のイメージのファイルを指定できます。(マルチステージビルド)
composer のインストールがとてもシンプルになっています。

https://matsuand.github.io/docs.docker.jp.onthefly/develop/develop-images/multistage-build/#use-multi-stage-builds

- dockerhub の [composer:2.1](https://hub.docker.com/layers/composer/library/composer/2.1/images/sha256-c7f7acea55794003e8b25e69ca110834912d00d5a0ec30abb45e1407412a45f8?context=explore) のイメージから `composer` をベースイメージにコピーしている


### ⑥ イメージ上に任意のコマンドを実行

```Dockerfile
RUN apt-get update && \
  apt-get -y install git libicu-dev libonig-dev libzip-dev unzip locales && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  locale-gen en_US.UTF-8 && \
  localedef -f UTF-8 -i en_US en_US.UTF-8 && \
  mkdir /var/run/php-fpm && \
  docker-php-ext-install intl pdo_mysql zip bcmath && \
  composer config -g process-timeout 3600 && \
  composer config -g repos.packagist composer https://packagist.org
```

[本家](https://qiita.com/ucan-lab/items/5fc1281cd8076c8ac9f4#run-%E5%91%BD%E4%BB%A4)

> ワンライナーで実行することによって、イメージレイヤ数を減らすことができます。

- `\` で繋いでいつのはイメージのレイヤ数を減らすため

https://www.techscore.com/blog/2018/12/10/docker-images-and-layers/

https://qiita.com/okmtz/items/f8231c83134a6363647b

- コンテナに対する変更の差分がレイヤ
- レイヤが増えると Docker image が大きくなってしまうため、できる限り減らしている


#### apt-get update

[update](https://qiita.com/white_aspara25/items/723ae4ebf0bfefe2115c#-apt-get-update)

- パッケージを管理している DB を最新にする
- ライブラリ自体の更新は行わない

https://webkaru.net/linux/apt-get-command/#:~:text=apt%2Dget%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AF%E3%80%81Debian%E7%B3%BB%E3%81%AE%E3%83%87%E3%82%A3%E3%82%B9%E3%83%88%E3%83%AA%E3%83%93%E3%83%A5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%EF%BC%88Debian%E3%82%84Ubuntu%EF%BC%89%E3%81%AE%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E7%AE%A1%E7%90%86%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%A7%E3%81%82%E3%82%8BAPT%EF%BC%88Advanced%20Package%20Tool%EF%BC%89%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%82%92%E6%93%8D%E4%BD%9C%E3%83%BB%E7%AE%A1%E7%90%86%E3%81%99%E3%82%8B%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E3%81%99%E3%80%82

> apt-getコマンドは、Debian系のディストリビューション（DebianやUbuntu）のパッケージ管理システムであるAPT（Advanced Package Tool）ライブラリを利用してパッケージを操作・管理するコマンドです。

- `FROM` で指定したベースイメージが `bullseye（Debian のバージョン 11）` を使っているので `apt-get` が使える

#### apt-get -y install git libicu-dev libonig-dev libzip-dev unzip locales

[install](https://qiita.com/white_aspara25/items/723ae4ebf0bfefe2115c#-apt-get-install-package)

- 指定されたライブラリをインストール
- `-y` 問い合わせが来たら全て yes と答える

- [git](https://git-scm.com/book/ja/v2/%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%82%8B-Git%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB#:~:text=%E3%81%BF%E3%81%BE%E3%81%97%E3%82%87%E3%81%86%EF%BC%9A-,%24%20sudo%20apt%2Dget%20install%20git%2Dall,-%E3%81%9D%E3%81%AE%E3%81%BB%E3%81%8B%E3%81%AB%E3%82%82)
    - お馴染みの git

- [libicu-dev](https://packages.debian.org/ja/sid/libicu-dev)
    - Unicode 用の国際化コンポーネントの開発用ファイル
    - なんでいるんだろ？言語設定とかで必要なんかな？
- [libonig-dev](https://packages.debian.org/bullseye/libonig-dev)
    - 正規表現のライブラリっぽい

- [libzip-dev](https://packages.debian.org/ja/sid/libzip-dev)
    - zip の作成読み取りなど

- [unzip](https://packages.debian.org/ja/bullseye/unzip)
    - zip ファイル展開のためのライブラリ

- [locales](https://packages.debian.org/ja/bullseye/locales)
    - ローカルの他言語サポートのためのライブラリ

（どのタイミングで使われるのかあんまりわかってないな。無いと Laravel 動かないのか？）

#### [apt-get clean](https://qiita.com/white_aspara25/items/723ae4ebf0bfefe2115c#-apt-get-clean)

> パッケージをインストールするために拾ってきたアーカイブファイル（ *.tar.gz とか）を削除


#### `rm -rf /var/lib/apt/lists/*`

https://karoten512.hatenablog.com/entry/2018/01/09/003330

（`ubuntu` の記事だけど、`Ubuntu` は `Debian` をベースに派生したらしいから、おそらく似たようなもん）
https://eng-entrance.com/linux-debian-ubuntu

- `/etc/apt/sources.list` （ `apt-get` で更新したファイル）に基づいて、`パッケージインデックスファイル` がダウンロードされる
- `インデックスファイル`は`/var/lib/apt/lists`に保存される
    - インデックスファイルはパッケージの情報で、インデックスファイルからインストールされているソフトウェアのバージョンを比較して、
  新しいバージョンが存在するかなどを確認しているらしい

- ここでは、`rm -rf` で `/var/lib/apt/lists` を削除してる
    - `/var/lib/apt/lists` を削除して、イメージのサイズを減らすのが[ベストプラクティス](http://docs.docker.jp/engine/articles/dockerfile_best-practice.html#:~:text=apt%20%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%82%92%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%81%AB%E3%81%97%E3%80%81%20/var/lib/apt/lits%20%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%A7%E3%80%81%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AE%E3%82%B5%E3%82%A4%E3%82%BA%E3%82%92%E6%B8%9B%E3%82%89%E3%81%97%E3%81%BE%E3%81%99%E3%80%82)らしい
    - もう使わないから消しても問題ないってことなんだろうな

#### `locale-gen en_US.UTF-8`

https://qiita.com/suin/items/856bf782d0d295352e51

> DockerでbuildしたイメージはロケールがPOSIXになっている。これに起因して、文字コード変換まわりの不具合が発生したりする。



> そしてこれがDockerfileに書く設定 

>```
RUN locale-gen en_US.UTF-8  
ENV LANG en_US.UTF-8  
ENV LANGUAGE en_US:en  
ENV LC_ALL en_US.UTF-8
```



- `④ 環境変数を設定` はこのためにやってるっぽい
- `locale-gen` コマンドが何をやってるのかがよくわからない

https://qiita.com/aosho235/items/58e2e7acd5c2ee3641ff#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AB%E4%BD%BF%E3%81%84%E3%81%9F%E3%81%84%E3%83%AD%E3%82%B1%E3%83%BC%E3%83%AB%E3%81%8C%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88


>locale-genはシェルスクリプトになっていて、内部で

>```
localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
```
に相当することをやっている。

- ちょっとよくわからないけど、一旦ここまで

#### `mkdir /var/run/php-fpm`

- `/var/run/php-fpm` ディレクトリを作成している
- docker-compose.yml で UNIX ソケットでマウントしてたディレクトリ
- デフォルトでは作成されないのかな？ 



```yaml:docker-compose.yml
volumes:
      - type: volume
        source: php-fpm-socket
        target: /var/run/php-fpm
        volume:
          nocopy: true
```

#### `docker-php-ext-install intl pdo_mysql zip bcmath`

- Laravel の動作に必要な PHP の拡張機能があるらしい

https://readouble.com/laravel/7.x/ja/installation.html#server-requirements

- このコマンドで全てインストールしてるわけじゃなさそう

##### [docker-php-ext-install](https://hub.docker.com/_/php#:~:text=php%2Dext%2Dconfigure%E3%80%81-,docker%2Dphp%2Dext%2Dinstall,-%E3%81%97%E3%80%81docker%2Dphp) 
    
- 公式が用意してくれている PHP の拡張ライブラリをインストールするためのコマンド
    - インストールしたライブラリの有効化までやってくれているっぽい

##### [intl](https://www.php.net/manual/ja/book.intl.php)

- 国際化関数

> ケール関連のさまざまな操作を行えるようにします。 フォーマット、音訳、エンコード変換、カレンダーの処理、 » UCA 準拠の照合順序 (collation)、 テキストの区切り、ロケール識別子やタイムゾーンや書記素を用いた操作などが可能です。

- 入ってないと [Laravel でエラー](https://qiita.com/SatoTakumi/items/c440eb7dbb91114d0038)になったりするらしい

##### [pdo_mysql](https://www.php.net/manual/ja/ref.pdo-mysql.php)

> PDO_MYSQL は、PHP から MySQL データベースへのアクセスを可能にするための PHP Data Objects (PDO) インターフェイス を実装したドライバです。

##### [zip](https://www.php.net/manual/ja/book.zip.php)

> この拡張モジュールにより、ZIP 圧縮されたアーカイブとその内部のファイルに対する透過的な読み書きが可能となります。

##### [bcmath](https://www.php.net/manual/ja/book.bc.php)

> 任意の精度の演算をサポートするために、PHP は 2147483647 (または 0x7FFFFFFF) までの 任意のサイズおよび精度の小数桁をサポートする BCMath を提供します。 十分なメモリがあれば、文字列表現もサポートします。

> 有効(別名 well-formed) な BCMath数 は、以下の正規表現にマッチする文字列です。 /^[+-]?[0-9]*(\.[0-9]*)?$/.


#### `composer config -g process-timeout 3600`

composer のプロセスがタイムアウトしないように上限時間を伸ばしている

https://www.ninton.co.jp/archives/6314

- [composer config](https://getcomposer.org/doc/03-cli.md#config)

>  config コマンドは、ローカルの composer.json ファイルまたはグローバルな config.json ファイルにある Composer の設定とリポジトリを編集することができます。

- [-g オプション](https://getcomposer.org/doc/03-cli.md#:~:text=Options-,%2D%2Dglobal,-(%2Dg)%3A%20Operate%20on)

> デフォルトでは `$COMPOSER_HOME/config.json` にあるグローバルな設定ファイルに対して操作します。このオプションがない場合は、ローカルの composer.json ファイルか、-file で指定したファイルに作用します。

- [process-timeout](https://getcomposer.org/doc/06-config.md#process-timeout)

> プロセス実行の秒単位のタイムアウト。デフォルトは300（5分）です。 git clonesのような期間プロセスは、Composerがそれらが消滅したと想定する前に実行できます。接続が遅い場合やベンダーが巨大な場合は、これを高くする必要があります。


#### `composer config -g repos.packagist composer https://packagist.org`

https://packagist.jp/

> PHPのライブラリリポジトリであるhttps://packagist.orgのミラーサイトです。packagist.orgの代わりにこちらを参照することで、composer updateの応答速度が速くなります。特にフランスから遠い、アジア圏では顕著な効果が得られます。

> 有効にするには以下のコマンドを打ち込んでください。

> enable
> $ composer config -g repos.packagist composer https://packagist.jp

### ⑦ `zzz-www.conf` をコンテナにコピー

`COPY ./infra/docker/php/php-fpm.d/zzz-www.conf /usr/local/etc/php-fpm.d/zzz-www.conf
`

- [php-fpm の設定項目](https://www.php.net/manual/ja/install.fpm.configuration.php)をマウント している
    - 設定ファイルが `zzz-www.conf` になってるのは公式イメージにある設定を上書きするためらしい

https://qiita.com/susuz/items/113002580945e94b1456#:~:text=zzz%2Dwww.conf%E3%81%A8%E3%81%84%E3%81%86%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8D%E3%81%AE%E7%90%86%E7%94%B1


### ⑧ `php.ini` をコンテナにコピー

- [php.ini](https://www.php.net/manual/ja/configuration.file.php)
    - php の設定をするファイル


### ⑨ 作業ディレクトリを指定

- [WORKDIR](https://docs.docker.jp/engine/reference/builder.html#workdir)
 
> WORKDIR 命令は、Dockerfile 内で以降に続く RUN 、 CMD 、 ENTRYPOINT 、 COPY 、 ADD 命令の処理時に（コマンドを実行する場所として）使う 作業ディレクトリworking directory を指定します。 WORKDIR が存在していなければ作成されます。これは、以降の Dockerfile で使われなくてもです。

- ここでは WORKDIR がなければ作成されるだけ

## `php-fpm.d/zzz-www.conf` について

```conf:zzz-www.conf
[www] ; ① プール名
listen = /var/run/php-fpm/php-fpm.sock ; ② FastCGI のリクエストを受け入れるアドレス
; --- ③ パーミッションの設定 ---
listen.owner = www-data 
listen.group = www-data
listen.mode = 0666
; ------
access.log = /dev/stdout
```

### ① プール名

`[www]`

`www` って名前をプロセスプールにつけている

https://server-recipe.com/3338/#:~:text=%E3%80%8Cpool%20tests%E3%80%8D%E3%81%A8%E3%81%84%E3%81%86%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%8C%E5%A2%97%E3%81%88%E3%81%BE%E3%81%97%E3%81%9F%E3%80%82


### ② FastCGI のリクエストを受け入れるアドレス

`listen = /var/run/php-fpm/php-fpm.sock`

[listen](https://www.php.net/manual/ja/install.fpm.configuration.php#listen)

> FastCGI リクエストを受け入れるアドレス。 'ip.add.re.ss:port', 'port', '/path/to/unix/socket' 形式の構文が使えます。 このオプションは、各プール単位で必須となります。

- PHP-fpm と Nginx を連携させるには、 TCP か UNIX ドメインソケットのどちらかで通信する

- `docker-compose.yml` で `/var/run/php-fpm` をマウントさせているのは UNIX ドメインソケットで通信するため

- ここで、 UNIX ドメインソケットのパスを指定することで、 UNIX ドメインソケットでの通信が行えている


### ③ パーミッションの設定


[listen.owner = www-data](https://www.php.net/manual/ja/install.fpm.configuration.php#listen-owner)

> unix ソケットを使う場合に、そのパーミッションを設定します。Linux では、 読み書きアクセス権限を設定しないとウェブサーバーからの接続を受け付けることができません。 多くの BSD 由来のシステムでは、パーミッションにかかわらず接続を受け付けることができます。 デフォルト値: ユーザーとグループは実行しているユーザーと同じ、モードは 0660

https://qiita.com/micron/items/56cdc72112f2f9b09fa2

- `www-data` は Ubuntu (おそらく Debian でも) 上の Web サーバー（Apache、nginxなど）がデフォルトで通常の操作に使用するユーザー

- `listen.mode = 0660` でも動くのかな？

### ログ

https://docs.docker.jp/config/container/logging/index.html#:~:text=%E5%85%AC%E5%BC%8F%20nginx%20%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AF%20/var/log/nginx/access.log%20%E3%81%8B%E3%82%89%E3%81%AE%E3%82%B7%E3%83%B3%E3%83%9C%E3%83%AA%E3%83%83%E3%82%AF%E3%83%BB%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%92%20/dev/stdout%20%E3%81%AB%E4%BD%9C%E6%88%90%E3%81%97%E3%81%BE%E3%81%99%E3%80%82


## `php.ini` について

`php.ini` は ucan さんの別記事で詳しく解説してありましたのでここでは深堀はしません。

https://qiita.com/ucan-lab/items/0d74378e1b9ba81699a9


## おわり

まだ MySql と Nginx の Dockerfile も残っているので、せっかくなのでアドカレとは別で書きたいと思います。（そのうち）


明日は [yuki](https://yukimasablog.com/) の GAE についての記事らしいです。
よろしくお願いします！