クラウドエンジニアブログ

社内業務アプリ想定の WordPress を Azure App Service に構築してみた

佐藤 実

佐藤 実

こんにちは。 CI/CD を含むクラウドインフラの構築や技術支援を担当している、クラウドエンジニアの佐藤です。

有名なオープンソース CMS の一つに WordPress があります。今まで縁が無く WordPress を構築したことがありませんでした。実際に構築してみると、テーマやプラグインが豊富で色々な利用用途に簡単にカスタマイズできる所がとても素晴らしいなと感じました。

そこで今回は、アクセス元の IP アドレスを制限した社内業務アプリ想定の WordPress を Azure App Service に構築してみました。構築後のシステム構成は下記のようになります。

  • ① 名前解決 : App Service に https://intra.ceblog.net でアクセスするために名前解決します。
  • ② アクセス元 IP アドレス制限 : 許可した IP アドレスのみ App Service にアクセスできます。
  • ③ VNET統合 : App Service から MySQL と Stoarge Account にセキュアにアクセスするため VNET 経由で通信します。
  • ④ 名前解決 : App Service から MySQL にアクセスする際に MySQL の接続先を名前解決します。
  • ⑤ Private Access : App Service から MySQL へのアクセスは Private Access 経由で通信します。
  • ⑥ Service Endpoint : App Service から Storage Account へのアクセスは Service Endpoint 経由で通信します。


WordPress に必要な Azure リソースを Azure CLI で構築

VNET、Private DNS Zone、Database for MySQL、App Service、Storage Account を構築します。
DNS Zone は既存のものを利用し、最後にカスタムドメインと無料の SSL/TLS サーバー証明書を設定します。

## 構築する Azure リソースのプレフィックス名を環境変数にセットします(※英数字のみを推奨)
prefix=ceblogintra

## 東日本リージョンをロケーションに指定してリソースグループを作成します
az group create \
  --name ${prefix}-rg \
  --location japaneast

## VNET を App Service 用のサブネット付きで作成します
az network vnet create \
  --name ${prefix}-vnet \
  --resource-group ${prefix}-rg \
  --address-prefixes 10.0.0.0/24 \
  --subnet-name app-subnet \
  --subnet-prefixes 10.0.0.0/26

## MySQL 用のサブネットを作成します
az network vnet subnet create \
  --resource-group ${prefix}-rg \
  --vnet-name ${prefix}-vnet \
  --name db-subnet \
  --address-prefix 10.0.0.64/26

## NSG を作成します
az network nsg create \
  --resource-group ${prefix}-rg \
  --name ${prefix}-nsg

## App Service 用のサブネットに NSG と Storage Account への Service Endpoint を設定します
az network vnet subnet update \
  --vnet-name ${prefix}-vnet \
  --resource-group ${prefix}-rg \
  --name app-subnet \
  --network-security-group ${prefix}-nsg \
  --service-endpoints Microsoft.Storage

## MySQL 用のサブネットに NSG を設定します
az network vnet subnet update \
  --vnet-name ${prefix}-vnet \
  --resource-group ${prefix}-rg \
  --name db-subnet \
  --network-security-group ${prefix}-nsg

## MySQL 用の Pricate DNS Zone を作成します
az network private-dns zone create \
  --resource-group ${prefix}-rg \
  --name ${prefix}.private.mysql.database.azure.com

## Standard_B1ms SKU の MySQL 8.0.21 を Private Access で作成します
## MySQL Admin のユーザー名とパスワードは本記事用の設定例です
az mysql flexible-server create \
  --resource-group ${prefix}-rg \
  --name ${prefix}-mysql \
  --admin-user myadmin \
  --admin-password SuperStrongP@ss1234 \
  --database-name wpdb \
  --vnet ${prefix}-vnet \
  --subnet db-subnet \
  --private-dns-zone $(az network private-dns zone show \
  --resource-group ${prefix}-rg \
  --name ${prefix}.private.mysql.database.azure.com \
  --query id \
  --output tsv) \
  --sku-name Standard_B1ms \
  --version 8.0.21

## MySQL への接続で SSL 強制をオフにします
az mysql flexible-server parameter set \
  --resource-group ${prefix}-rg \
  --server-name ${prefix}-mysql \
  --name require_secure_transport \
  --value off

## B1 SKU の App Service for Linux を作成します
az appservice plan create \
  --name ${prefix}-plan \
  --resource-group ${prefix}-rg \
  --is-linux \
  --sku B1

## PHP 8.1 で HTTPS 接続のみを有効化した Web アプリを作成します
az webapp create \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --plan ${prefix}-plan \
  --runtime "PHP|8.1" \
  --https-only true

## Web アプリの設定で常時接続をオンにして FTPS 接続を無効化します
az webapp config set \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --always-on true \
  --ftps-state Disabled

## 接続元 IP アドレスを Web アプリの接続設定に登録します
az webapp config access-restriction add \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --priority 100 \
  --rule-name MyIP \
  --action Allow \
  --ip-address $(curl -s inet-ip.info)

## WordPress の自己アクセス用にアウトバウンド IP アドレスを Web アプリの接続設定に登録します
az webapp config access-restriction add \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --priority 200 \
  --rule-name AppIP \
  --action Allow \
  --ip-address $(az webapp show \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --query outboundIpAddresses \
  --output tsv)

## Web アプリを App Service 用サブネットに VNET 統合します
az webapp vnet-integration add \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --vnet ${prefix}-vnet \
  --subnet app-subnet

## デフォルトアクセスを無効にしたローカル冗長ストレージの Storage Account を作成します
az storage account create \
  --name ${prefix}stor \
  --resource-group ${prefix}-rg \
  --default-action deny \
  --sku Standard_LRS

## App Service 用のサブネットからの Storage Account へのアクセスを許可します
az storage account network-rule add \
  --account-name ${prefix}stor \
  --resource-group ${prefix}-rg \
  --vnet-name ${prefix}-vnet \
  --subnet app-subnet

## 一時的に自身の接続元 IP アドレスを Storage Account に許可します
az storage account network-rule add \
  --account-name ${prefix}stor \
  --resource-group ${prefix}-rg \
  --ip-address $(curl -s inet-ip.info)

## Storage Account に Blob のパブリック読み取りアクセス可能なコンテナーを作成します
az storage container create \
  --account-name ${prefix}stor \
  --name ${prefix} \
  --public-access blob

## 一時的に許可した IP アドレスを Storage Account から削除します
az storage account network-rule remove \
  --account-name ${prefix}stor \
  --resource-group ${prefix}-rg \
  --ip-address $(curl -s inet-ip.info)

## Blob オブジェクトの誤削除対策にバージョニングを有効にします
az storage account blob-service-properties update \
  --account-name ${prefix}stor \
  --resource-group ${prefix}-rg \
  --enable-versioning true

## DNS Zone に App Service アクセス用の intra.ceblog.net を CNAME で登録します
az network dns record-set cname set-record \
  --resource-group ceblog-rg \
  --zone-name ceblog.net \
  --record-set-name intra \
  --cname ${prefix}-app.azurewebsites.net

## DNS Zone にドメイン所有者確認用の awverify.intra.ceblog.net を CNAME で登録します
az network dns record-set cname set-record \
  --resource-group ceblog-rg \
  --zone-name ceblog.net \
  --record-set-name awverify.intra \
  --cname awverify.${prefix}-app.azurewebsites.net

## Web アプリのホスト名にカスタムドメインの intra.ceblog.net を登録します
az webapp config hostname add \
  --webapp-name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --hostname intra.ceblog.net

## 無料の SSL/TLS サーバー証明書を作成します
az webapp config ssl create \
  --resource-group ${prefix}-rg \
  --name ${prefix}-app \
  --hostname intra.ceblog.net

## 無料の SSL/TLS サーバー証明書をバインドします
az webapp config ssl bind \
  --certificate-thumbprint $(az webapp config ssl show \
  --resource-group ${prefix}-rg \
  --certificate-name intra.ceblog.net \
  --query thumbprint \
  --output tsv) \
  --resource-group ${prefix}-rg \
  --name ${prefix}-app \
  --ssl-type SNI


「 ceblogintra-rg 」リソースグループに作成された Azure リソースは下記のようになりました。


https://intra.ceblog.net にアクセスします。
無料の SSL/TLS サーバー証明書を確認する事ができました。

WordPress をデプロイして初期設定

任意のバージョンの WordPress ソースをダウンロードして解凍します。

curl -O https://ja.wordpress.org/wordpress-6.1.1-ja.zip

unzip wordpress-6.1.1-ja.zip

cd wordpress


GitHub にある wp-config.php の 109 ~ 121 行目を「 wp-config-sample.php 」の 85 行目に追記します。
これは、App Service 内の WordPress に FQDN と https アクセスを認識させるために必要な設定です。

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
	$_SERVER['HTTPS'] = 'on';

$http_protocol='http://';
if (!preg_match("/^localhost(:[0-9])*/", $_SERVER['HTTP_HOST']) && !preg_match("/^127\.0\.0\.1(:[0-9])*/", $_SERVER['HTTP_HOST'])) {
	$http_protocol='https://';
}

//Relative URLs for swapping across app service deployment slots
define('WP_HOME', $http_protocol . $_SERVER['HTTP_HOST']);
define('WP_SITEURL', $http_protocol . $_SERVER['HTTP_HOST']);
define('WP_CONTENT_URL', '/wp-content');
define('DOMAIN_CURRENT_SITE', $_SERVER['HTTP_HOST']);


App Service 用にカスタマイズした WordPress 一式を ZIP にして、Web アプリにデプロイします。

## ファイル一式を一つ上のディレクトリに ZIP で固めます。
zip -r ../wordpress.zip *

## 一つ上のディレクトリに移動します
cd ..

## Web アプリにデプロイします
az webapp deployment source config-zip \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --src wordpress.zip


Storage Account へのリバースプロキシを Web アプリ内の Nginx に設定します。
※ Azure Apps on Azure Blog の Configure Nginx for PHP 8 Linux Azure App Service を参考に App Service の Nginx 設定をカスタマイズします。

## Web アプリに SSH トンネルを作成します
## Opening tunnel on port: 41219 のように任意のポート番号が表示されます
az webapp create-remote-connection \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg &
  
## 任意のポート番号を指定して SSH 接続します
ssh root@127.0.0.1 -p 41219

## Nginx の設定ファイルをコピーします
cp /etc/nginx/sites-enabled/default /home/default

## Nginx の設定ファイルを編集します
vi default

## default ファイルの 48 行目に下記 3 行を追加
    location /blob/ {
        proxy_pass https://ceblogintrastor.blob.core.windows.net/;
    }

## SSH 接続から抜けます
exit

## SSH トンネルをフォアグランドに戻してから [ Ctrl + C ] で終了します
fg

## Web アプリの設定にスタートアップスクリプトを入れます
az webapp config set \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --startup-file "cp /home/default /etc/nginx/sites-enabled/default; service nginx restart"


https://intra.ceblog.net にアクセスします。
WordPress のセットアップが面が表示ました。
「さあ、始めましょう!」をクリックします。


MySQL の情報を入力します。
「送信」をクリックします。


無事 MySQL と接続する事ができました。 「インストール実行」をクリックします。


「サイトのタイトル」「ユーザー名」「メールアドレス」を入力します。
「 WordPress をインストール」をクリックします。


WordPress のインストールが成功しました。
「ログイン」をクリックします。


ブラウザに自動保存されたユーザー名とパスワードが入力済みの状態です。
「ログイン」をクリックします。


WordPress にログインする事ができました。


ちなみに、App Service で許可していない接続元の IP アドレスから https://intra.ceblog.net にアクセスすると、下記のように表示されます。

メディアライブラリ用のプラグイン導入

WordPress のメディアライブラリに登録するファイルを Storage Account に保存するプラグインを導入します。
このプラグインの設定で、App Service 内にはファイルを保存せず、Storage Account に保存する事ができるようになります。


プラグインの新規追加で「 Azure 」をキーワードに入力します。
Windows Azure Storage for WordPress の「今すぐインストール」をクリックします。


「有効化」をクリックします。


設定の「 Microsoft Azure 」をクリックします。


ブラウザの別タブで「 ceblogintrastor 」ストレージアカウントの「アクセスキー」を開きます。
「ストレージアカウント名」と「 key1 のキー」のコピーを使用して WordPress のプラグイン設定を行います。


「 Store Account Name 」と「 Store Account Key 」に上でコピーした値をペーストします。


一番下までスクロールして「 Save Changes 」をクリックします。


「設定を保存しました。」とメッセージが表示されます。
Default Storage Container には、自動的に「 ceblogintra 」コンテナーが選択された状態です。
CNAME は Nginx 設定した「 https://intra.ceblog.net/blob/ 」を入力します。
Use for default upload にチェックして、メディアライブラリに登録するファイルをすべてストレージアカウントにアップロードするようにします。


一番下までスクロールして「 Save Changes 」をクリックします。


メディアの新規追加から「ファイルを選択」をクリックし、ローカルにある画像を選択します。
この例では「 wordpress.png 」というファイルをアップロードしました。


投稿の投稿一覧から Hello world! ブログ記事の「編集」をクリックします。


プラスアイコンから「画像」をクリックします。


「メディアライブラリ」をクリックします。


先ほどメディアライブラリにアップロードした画像をクリックします。
「選択」をクリックします。


「更新」をクリックします。


URL 欄から「投稿を表示」の URL をクリックして、ブラウザの別ウィンドウでブログ記事を表示します。


投稿したブログ記事にアップロードした画像が正常に表示されました。

まとめ

アクセス元の IP アドレスを制限した、社内業務アプリ想定の WordPress を Azure App Service に構築しました。また、Azure App Service 内のディスク容量を節約するため、Storage Account にメディアライブラリのファイルを保存するプラグインを導入しました。そして、Storage Account へのアクセスは Azure App Service 経由しかアクセスできないよう制限して、Nginx にリバースプロキシを設定しました。

社内業務アプリ想定という前提で、不特定多数の大量アクセスが想定されないため、WAF や CDN は今回のシステム構成には含まれていません。しかしながら、Azure サービス自体が備えている冗長化によって可用性が担保されるため、社内業務アプリを安心して運用できる構成だと思います。

また、社内業務アプリであってもカスタムドメインでアクセスできるようにしておけば、例えば Azure App Service のフロントに Azure Front Door を追加するシステム構成の変更や、Azure App Service から他社のサービスに移行するなどの変更に、柔軟に対応しやすいシステムにする事ができます。

このブログ記事が誰かの何かの参考になればうれしいです。 最後まで読んで頂き、ありがとうございます。

お問い合わせ

製品・サービスに関するお問い合わせはお気軽にご相談ください。

ピックアップ

セミナー情報
クラウドエンジニアブログ
clouXion
メールマガジン登録