Check! Azure Web Apps + MySQL をARMテンプレートでデプロイ（シブヤクハックでつくった環境解説 (428チーム)）

こんにちは、 @dz_ こと大平かづみです。

Prologue - はじめに
====
先日開催された [シブヤクハック](https://www.shibuyaku-hack.tech/) （ハッカソン）では、大学生のメンバーと一緒に挑戦しました！

メンバーが少なく、それぞれの作業がクリティカルパスという状況。その中でわたしが担当したインフラ構築についてご紹介します。

なお、ここでは全体の説明が長くなってしまったので、デプロイ部分だけ知りたい方は「[ARM テンプレートで各種リソースを構築する](#arm-テンプレートで各種リソースを構築する)」「[デプロイ後の作業](#デプロイ後の作業)」の項をご参照ください。

テーマ
====

「価値観の違いで埋もれている魅力的なスポットを見つける！」

私たちのチームでは、国外からいらした方々にとって魅力的な渋谷区のスポットをを、探しやすく、活性化させるためには？をテーマにしました。

- [428 | Hacklog By Mashup Awards](https://hacklog.jp/works/512000)

様々なアプローチのアイディアが集まり、ハッカソンの段階では下記に注目し、それらを検索できるアプリからはじめることにしました。よく検索・利用される条件データをバックエンドで蓄積し、区役所側でそのデータを可視化できることにより、地域活性に活かせるというシナリオです。

- 支払い方法の違い（クレジットカード、アプリ決済文化）
- 食の制限（ビーガン、グルテンフリー、ハラールなど）

先にいいますと、残念ながら、アプリ完成までは至らなかったのです。ですが、担当部分のバックエンドのつなげこみはできたので、主にその部分をここでご紹介します。

開発のまとめ
====

システム要件
----

ひとりはモバイルアプリ開発、ひとりはサーバーサイドをPHPで開発ができるということで、それらをつなげる全体設計とインフラ構築をわたしが担当しました。

| 用途 | 構成 |
| --- | --- |
| Web サーバー | CakePHP 3.5, PHP 7. x|
| データベース | MySQL |
| ソース管理 |  git |

- サーバーサイド担当者の希望により CakePHP で開発。
- データベースは、Power BI からもつなげたく、SQL Server と併せて検討の末、Power BI for Desktop でコネクタをインストールすればつなげるとわかったので、CakePHP と相性の良い MySQL を採用。

システム構成
----
これを、Azure で手っ取り早く構築するべく、以下の構成にしました。

![20180407_shibuyaku-hack_001.png](https://qiita-image-store.s3.amazonaws.com/0/3812/7ea755a7-9779-24d9-7289-c5aed8c8edb8.png)

この環境 Azure Web App + MySQL をほぼ一発で構築する（再現版）
====

当日は手探りで Azureポータルからポチポチ作ってた上記の構成を、せっかくなので ARM (Azure Resouce Manager) テンプレートに落とし込んで公開します。

インフラの構成
----

| 役割 | 構成 | 説明 |
| --- | --- | --- |
| Web サーバー |  Azure Web App | Windows （IIS）、PHP 7.1 |
| データベース | Azure Database for MySQL |  MySQL 5.7、Azure内からの接続のみ許可 |
| ソース管理 |  GitHub | Web App に CI を設定して反映 |

- 当初、`Web App on Linux` で試みたが、 `ext-intl` を読み込めないようで断念。（ `phpinfo` をみるかぎりでは Itnl は入ってるのに。）
- Web App の Windows （IIS）では、PHP 7.2 が最新だが、 `pdo_mysql` が含まれていなかったので、 7.1 を採用。

ソースコードを Web App に最適化する（対応済み）
----

PHPの開発は、メンバーのローカル（Mac）で行われていたので、 Web App (IIS) で動作するように、ソースコードをフォークして一部手を加えました。以下は手を加えた差分です。（一部、検証コードもあります。）

- [Comparing master...dev · dzeyelid/428](https://github.com/dzeyelid/428/compare/master...dzeyelid:dev)

### 最適化の内容

- データベース接続情報（ `host`, `username`, `password`, `database` ) を環境変数から取得するよう修正。
    - 開発リポジトリにマージする際は、 `.env` などで同環境変数指定することにより対応可能の見込み。
- デフォルトの Apache 用 `.htaccess` を、 IIS のルーティングファイル `web.config` に置換え

ARM テンプレートで各種リソースを構築する
----

ということで、Azure Web App と Azure Database for MySQL をデプロイする ARMテンプレートを作りました。下記リポジトリに、にハッカソン当日の環境をデプロイする用のパラメータとともに置きましたのでご参考になればさいわいです。Azure CLI などでデプロイ可能です。

- [dzeyelid/shibuyaku-hack-428-infra](https://github.com/dzeyelid/shibuyaku-hack-428-infra)

参考までに、ARMテンプレートを掲載します。

```json:azuredeploy.json
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters":{
        "hostingPlanSkuName": {
            "type": "string",
            "defaultValue": "B1",
            "allowedValues": ["FREE", "SHARED", "D1", "B1", "B2", "B3",
                "S1", "S2", "S3", "F1", "P1", "P1V2", "P2", "P2V2", "P3", "P3V2"]
        },
        "databaseForMySqlAdminName": {
            "type": "string"
        },
        "databaseForMySqlAdminPassword": {
            "type": "securestring"
        },
        "databaseForMySqlDatabaseName": {
            "type": "string"
        },
        "databaseForMySqlTier": {
            "type": "string",
            "defaultValue": "Basic",
            "allowedValues": ["Basic", "GeneralPurpose", "MemoryOptimized"]
        },
        "databaseForMySqlFamily": {
            "type": "string",
            "defaultValue": "Gen4",
            "allowedValues": ["Gen4", "Gen5"]
        },
        "databaseForMySqlCores": {
            "type": "int",
            "defaultValue": 1,
            "allowedValues": [1, 2, 4, 8, 16, 32]
        },
        "databaseForMySqlVersion": {
            "type": "string",
            "defaultValue": "5.7",
            "allowedValues": ["5.7", "5.6"]
        },
        "phpVersion": {
            "type": "string",
            "defaultValue": "7.1",
            "allowedValues": ["5.6", "7.0", "7.1", "7.2"]
        },
        "ciRepositoryUrl": {
            "type": "string",
            "defaultValue": "git@github.com:dzeyelid/428.git"
        },
        "ciRepositoryBranch": {
            "type": "string",
            "defaultValue": "dev"
        }
    },
    "variables": {
        "location": "[resourceGroup().location]",
        "webAppName": "[concat(resourceGroup().name, 'web')]",
        "hostingPlanName": "[concat(resourceGroup().name, 'appplan')]",
        "databaseForMySqlName": "[concat(resourceGroup().name, 'mysql')]",
        "databaseForMySqlSku": "[concat(variables('tierSymbol')[parameters('databaseForMySqlTier')], '_', parameters('databaseForMySqlFamily'), '_', parameters('databaseForMySqlCores'))]",
        "tierSymbol": {
            "Basic": "B",
            "GeneralPurpose": "GP",
            "MemoryOptimized": "MO"
        }
    },
    "resources": [
        {
            "name": "[variables('hostingPlanName')]",
            "type": "Microsoft.Web/serverfarms",
            "apiVersion": "2016-09-01",
            "location": "[variables('location')]",
            "kind": "app",
            "sku": {
                "name": "[parameters('hostingPlanSkuName')]"
            }
        },
        {
            "name": "[variables('webAppName')]",
            "type": "Microsoft.Web/sites",
            "apiVersion": "2016-08-01",
            "location": "[variables('location')]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]"
            ],
            "kind": "app",
            "properties": {
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
                "siteConfig": {
                    "phpVersion": "[parameters('phpVersion')]",
                    "connectionStrings": [
                        {
                            "name": "host",
                            "ConnectionString": "[concat(variables('databaseForMySqlName'), '.mysql.database.azure.com')]",
                            "type": "MySql"
                        },
                        {
                            "name": "username",
                            "ConnectionString": "[concat(parameters('databaseForMySqlAdminName'), '@', variables('databaseForMySqlName'))]",
                            "type": "MySql"
                        },
                        {
                            "name": "password",
                            "ConnectionString": "[parameters('databaseForMySqlAdminPassword')]",
                            "type": "MySql"
                        },
                        {
                            "name": "database",
                            "ConnectionString": "[parameters('databaseForMySqlDatabaseName')]",
                            "type": "MySql"
                        }
                    ]
                }
            },
            "resources": [
                {
                    "apiVersion": "2015-08-01",
                    "name": "web",
                    "type": "sourcecontrols",
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites', variables('webAppName'))]"
                    ],
                    "properties": {
                        "RepoUrl": "[parameters('ciRepositoryUrl')]",
                        "branch": "[parameters('ciRepositoryBranch')]",
                        "IsManualIntegration": false
                    }
                }
            ]
        },
        {
            "name": "[variables('databaseForMySqlName')]",
            "type": "Microsoft.DBforMySQL/servers",
            "apiVersion": "2017-12-01",
            "location": "[variables('location')]",
            "sku": {
                "name": "[variables('databaseForMySqlSku')]"
            },
            "properties": {
                "version": "[parameters('databaseForMySqlVersion')]",
                "administratorLogin": "[parameters('databaseForMySqlAdminName')]",
                "administratorLoginPassword": "[parameters('databaseForMySqlAdminPassword')]",
                "sslEnforcement": "Disabled",
                "createMode": "default"
            },
            "resources": [
                {
                    "name": "[parameters('databaseForMySqlDatabaseName')]",
                    "type": "databases",
                    "apiVersion": "2017-12-01",
                    "dependsOn": [
                        "[resourceId('Microsoft.DBforMySQL/servers', variables('databaseForMySqlName'))]"
                    ],
                    "properties": {
                        "charset": "utf8",
                        "collation": "utf8_general_ci"
                    }
                }
            ]
        }
    ]
}
```

なお、ハッカソンでスピード勝負のため、MySQL のSSL接続要求は解除しています。


デプロイ後の作業
----

さて、残念ながら Azure  Database for MySQL がGAしたばかりのためか、下記の設定値は ARM テンプレートから設定できなかったので、デプロイ後に手動で設定します。

### Azure Database for MySQL で、Azure サービスからの接続を許可する

Azure ポータルで、 Azure Database for Azure のプレートを開き、下記の設定を保存します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/3812/dc2d71ca-0519-3682-1bba-2bbae7e0f41d.png)

### データベースを初期化する

上記の接続を許可すると、Web App から MySQL に接続できるようになるので、データベースのマイグレーションを行います。

Azure ポータルで、Azure Web App のプレートを開き、「コンソール」を開いて、CakePHP のマイグレーションを行います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/3812/edd81961-874e-b978-a096-ce110ab99691.png)

CakePHP のマイグレーションについては、ドキュメントをご参照ください。

- [Migrations - CakePHP 3.5](https://book.cakephp.org/3.0/en/migrations.html)

```powershell:CakePHPマイグレーション
bin\cake migrations migrate
```

### 動作確認！

ここまでできましたら、 `http://<resource group name>web.azurewebsites.net/users` にアクセスし、エラーが出なければデプロイ完了です！

いえーい！(/・ω・)/

可視化環境について
----

さいごに、可視化については、Power Bi Desktop から上記の Azure Database for MySQL に対して接続する動作確認のみ行いました。

| 役割 | 構成 | 説明 |
| --- | --- | --- |
| 可視化 | Power BI  Desktop | 職員のPC上の利用を想定、MySQL コネクタ利用 |

-  Power BI は、ハッカソンの都合上、アカウント登録なしに使えるデスクトップ版を利用。（オンライン版は組織アカウントが必要なので割愛）
- また、Power BI Desktop のデータソースで MySQL を利用するには、[Power BI データ ソースの前提条件 - Power BI | Microsoft Docs](https://docs.microsoft.com/ja-jp/power-bi/desktop-data-source-prerequisites) の MySQL コネクタをインストールしたうえで利用。

実際には、オンライン版を利用したほうが、インストール不要で、データベースとの接続もセキュアにできるかと思います。ただ、こちらは現状 MySQL とは接続できないみたいなので、データベースを変えるか、Microsoftさんにリクエストするかが必要そうです（笑） :eyes: 

ご報告は以上です！

Epilogur - おわりに
====

いざ、ARMテンプレートでやろうとすると、当日手作業でやった部分を思い出しつつ、あの時なんでこの選択したんだっけと改めて検証したりと、この記事を書き上げるまでに時間がかかってしまいました。

そのおかげで、Azure Web App と Azure Database for MySQL の見識が広がったので、私としては良い学びとなりました！

ふりかえり大事！

ということで、メンバーも、スタッフの皆々様方も、お疲れさまでした！

よい機会を設けてくださり、ありがとうございました！！ :clap: