Oracle CloudのマネージドサービスでWordPress基盤を作る

- Oracle MySQL Database Serviceの使い方メモ〜その5
- Oracle Cloud Infrastructure Container Engine for Kubernetes (OKE) + WordPress + MySQL Database Service + File Storage Service
- Kubernetes上のWordPressコンテナからMySQL Database Serviceインスタンスへ接続する

# はじめに
Oracle Cloud Infrastructure (OCI) で提供されているマネージドサービスを組み合わせてWordPress基盤を構築してみました。マネージドKubernetesサービスとしてOracle Container Engine for Kubernetes (OKE) を、マネージドRDBMSサービスとしてOracle MySQL Database Serviceを利用しています。また、WordPressの静的コンテンツの保管先はマネージドNFSサービスであるOracle Cloud Infrastructure File Storage Serviceを利用して`ReadWriteMany`なPersistentVolumeでWordPressコンテナがスケールアウト可能な構成を実現しています。


# Oracle Cloud Infrastructure上のシステム構成
最終構成は下図の様になります。
![20-12-02-23-12-43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/97232fcf-f018-380c-e5b9-b7e4ee956cb7.png)

### 検証環境情報
- Kubernetesバージョンv1.18.10 (OKE Master)
- Oracle-Linux-7.8 (OKEノードのOSイメージ)
- kubectl v1.19.3
- docker.io/library/wordpress:latest (Wordpress 5.5.3, PHP 7.4.12)
- MySQL 8.0.22-u2-cloud (Oracle MySQL Database Service)
- Oracle Linux Server release 8.2 (Bastionサーバ)

# Oracle Cloud Infrastructureリソースの構築
## OKEクラスタの作成 (VCN作成含む)
OCIダッシュボードメニューから、**ソリューションおよびプラットフォーム**>**開発者サービス**>**Kubernetesクラスタ**にアクセスします。**クラスタの作成**ボタンを押してOKEクラスタのクイック作成ウィザードを開始します。

![20-12-02-21-04-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/d9ac9250-21ef-a1d7-6aea-e4102079e359.png)


ウィザード内の入力例を下記に示します。

---
* 名前　: 任意の名前　OKE_Cluster01
* コンパートメントの選択 : 任意のコンパートメント名　 /dev
* KUBERNETESバージョン : v1.18.10
* 可視性タイプの選択 : プライベート
* シェイプ：VM.Standard.E2.1
* ノード数 : 2
* 拡張オプションの非表示
    * SSH公開キー ： 適当なキーペアの公開鍵の値を入力

---

**確認**画面から**クラスタの作成**を実行します。
クラスタの詳細画面よりクラスタのステータスがアクティブ担っていることを確認します。
![20-12-02-21-41-53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/72455ad9-69ee-ff13-faa0-5ebd787f50f3.png)


さらに左下の**リソース**メニューから**ノード・プール**を選択し一覧を表示、デフォルトで作成されている`pool1`を選択し、ノード・プール詳細画面に遷移します。ノード・プール詳細画面左下の**リソース**メニューから**ノード**を選択し一覧を表示、各ノードの**Kubernetesのノード条件**が**準備完了**となっていること、**ノード状態**が**アクティブ**となれば、OKEの利用準備完了です。
![20-12-02-23-18-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/f32a136d-5864-1944-012c-1d3406031d6b.png)


OKEクイック作成ウィザードで作成されたOCIの構成は下図となります。

![20-12-02-21-58-53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/120d6953-f55e-ae85-645c-a6ce244740b9.png)



## VCN内のネットワーク設定
OKEクイック作成ウィザードで作成されたVCN構成 (`oke-`接頭辞の付いたネットワーク・リソース群) を元に必要なネットワーク・リソースを追加構成していきます。 

### ルート表
下記のようにパブリック・サブネット用のルート表`Public`とプライベート・サブネット用のルート表`Private`をそれぞれ追加作成します。ここではOKEクイック作成ウィザードで作成されたインターネット・ゲートウェイ`oke-igw-quick-(省略)`とNATゲートウェイ`oke-ngw-quick-(省略)`を利用しています。

- **ルート表** `Public`

|宛先|ターゲット・タイプ|ターゲット|説明|
|---|---|---|---|
|0.0.0.0/0|インターネット・ゲートウェイ|oke-igw-quick-(省略)||

- **ルート表** `Private`

|宛先|ターゲット・タイプ|ターゲット|説明|
|---|---|---|---|
|0.0.0.0/0|NATゲートウェイ|oke-ngw-quick-(省略)||

### セキュリティ・リスト
はじめにOKEクイック作成ウィザードで作成されたパブリック・サブネット用のセキュリティ・リスト`oke-lb-seclist-(省略)`とプライベート・サブネット用のセキュリティ・リスト`oke-wkr-seclist-(省略)`のイングレス・ルールならびにエグレス・ルールを下記のように確認・追記します。

- `oke-lb-seclist-(省略)`

**イングレス・ルール**

|ステートレス|ソース・タイプ|ソースCIDR|IPプロトコル|ソース・ポート範囲|宛先ポート範囲|説明|
|---|---|---|---|---|---|---|
|はい|CIDR|0.0.0.0/0|TCP|All|All|OKE作成ウィザードで自動作成|

**エグレス・ルール**

|ステートレス|ソース・タイプ|宛先CIDR|IPプロトコル|ソース・ポート範囲|宛先ポート範囲|説明|
|---|---|---|---|---|---|---|
|はい|CIDR|0.0.0.0/0|TCP|All|All|OKE作成ウィザードで自動作成|

----

- `oke-wkr-seclist-(省略)`

**イングレス・ルール**

|ステートレス|ソース・タイプ|ソースCIDR|IPプロトコル|ソース・ポート範囲|宛先ポート範囲|説明|
|---|---|---|---|---|---|---|
|はい|CIDR|10.0.10.0/24|すべてのプロトコル|||OKE作成ウィザードで自動作成|
|いいえ|CIDR|10.0.0.0/16|TCP|All|22|OKE作成ウィザードで自動作成|
|いいえ|CIDR|10.0.10.0/24|TCP|All|2048-2050|File Storageのマウントに利用|
|いいえ|CIDR|10.0.10.0/24|TCP|All|111|File Storageのマウントに利用|
|いいえ|CIDR|10.0.10.0/24|UDP|All|2048|File Storageのマウントに利用|
|いいえ|CIDR|10.0.10.0/24|UDP|All|111|File Storageのマウントに利用|
|いいえ|CIDR|10.0.30.0/24|TCP|All|3306|MySQL Database Serviceとの接続で利用|

**エグレス・ルール**

|ステートレス|ソース・タイプ|宛先CIDR|IPプロトコル|ソース・ポート範囲|宛先ポート範囲|説明|
|---|---|---|---|---|---|---|
|はい|CIDR|10.0.10.0/24|すべてのプロトコル|||OKE作成ウィザードで自動作成|
|いいえ|CIDR|0.0.0.0/0|すべてのプロトコル|||OKE作成ウィザードで自動作成|
|いいえ|CIDR|10.0.10.0/24|TCP|All|2048-2050|File Storageのマウントに利用|
|いいえ|CIDR|10.0.10.0/24|TCP|All|111|File Storageのマウントに利用|
|いいえ|CIDR|10.0.10.0/24|UDP|All|111|File Storageのマウントに利用|

---

追加構成を行うサブネット用に下記のセキュリティ・リストを新規に作成します。

- `mds-seclist`

**イングレス・ルール**

|ステートレス|ソース・タイプ|ソースCIDR|IPプロトコル|ソース・ポート範囲|宛先ポート範囲|説明|
|---|---|---|---|---|---|---|
|いいえ|CIDR|10.0.30.0/24|TCP|All|3306|Bastion Serverからの接続で利用|
|いいえ|CIDR|10.0.30.0/24|TCP|All|33060|Bastion Serverからの接続で利用|
|いいえ|CIDR|10.0.10.0/24|TCP|All|3306|OKEとMySQL Database Serviceとの接続で利用|

**エグレス・ルール**

|ステートレス|ソース・タイプ|宛先CIDR|IPプロトコル|ソース・ポート範囲|宛先ポート範囲|説明|
|---|---|---|---|---|---|---|
|いいえ|CIDR|0.0.0.0/0|TCP|All|All||

----

- `bastion-seclist`

**イングレス・ルール**

|ステートレス|ソース・タイプ|ソースCIDR|IPプロトコル|ソース・ポート範囲|宛先ポート範囲|説明|
|---|---|---|---|---|---|---|
|いいえ|CIDR|0.0.0.0/24|TCP|All|22|ClientとのSSH接続で利用|

**エグレス・ルール**

|ステートレス|ソース・タイプ|宛先CIDR|IPプロトコル|ソース・ポート範囲|宛先ポート範囲|説明|
|---|---|---|---|---|---|---|
|いいえ|CIDR|0.0.0.0/0|TCP|All|All||

----

### サブネットの作成
下記のようにパブリック・サブネット`Bastion`とプライベート・サブネット`MDS`を作成します。

**パブリック・サブネット** `Bastion`
* 名前 : 任意のサブネット名　`Bastion`
* コンパートメント : 任意のコンパートメント名　 /dev
* サブネット・タイプ : リージョナル
* CIDRブロック : 10.0.0.0/24
* ルート表 : `Public`
* サブネット・アクセス : パブリック・サブネット
* DNS解決：(任意) 選択
* DNSラベル：(任意) `bastion`
* DHCPオプション : Default DHCP Option
* セキュリティ・リスト：`bastion-seclist`

**プライベート・サブネット** `MDS`
* 名前 : 任意のサブネット名　`MDS`
* コンパートメント : 任意のコンパートメント名　 /dev
* サブネット・タイプ : リージョナル
* CIDRブロック : 10.0.0.0/24
* ルート表 : `Private`
* サブネット・アクセス : プライベート・サブネット
* DNS解決：(任意) 選択
* DNSラベル：(任意) `mds`
* DHCPオプション : Default DHCP Option
* セキュリティ・リスト：`mds-seclist`

この段階のOCIの構成は下図となります。
![20-12-02-23-01-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/29880898-a054-790c-6e42-4600c24020e7.png)


## Oracle MySQL Database Serviceインスタンスの作成
OCIダッシュボードメニューから、**データベース**>**MySQL**にアクセスします。**MySQL Databaseシステムの作成**ボタンを押してインスタンスの作成ウィザードを開始します。ウィザード内の入力例を下記に示します。

---

* DBシステムの基本情報の指定
    * コンパートメントの選択 : 任意のコンパートメント名　 `/dev`
    * DBシステムの名前　: 任意名前　`wpmds`
    * 説明 : コメント記入
    * 可用性ドメインの選択　：デフォルト　`Fubk:AP-TOKYO-1-AD-1`
    * フォルト・ドメインの選択　：デフォルト `FAULT-DOMAIN-1`
    * シェイプの選択：デフォルト　 デフォルト `MySQL.VM.Standard.E3.1.8GB`
    * データ・ストレージ・サイズ(GB) : `50`
    * メンテナンス・ウィンドウの開始時間 :  `Sunday 18:00` (UTCで入力)

* 管理者資格証明の作成
    * ユーザー名  MySQL管理ユーザー名
    * パスワード　MySQL管理者パスワード
    > 管理者パスワードは8文字から32文字までの長さで、大文字、小文字、数字および特殊文字をそれぞれ1つ以上含める必要があります
* ネットワーク情報の指定
    * 仮想クラウド・ネットワーク : `oke-vcn-quick-(省略)`
    * サブネット : `MDS`
    * ホスト名: 任意のホスト名　`wpmds`
    * MYSQLポート : `3306` 
    * MYSQL Xプロトコル・ポート:　`33060` 

* DBシステム・バックアップの基本情報の指定：
    * 自動バックアップの有効化 : 選択
    * バックアップ保有期間 (日数) : `7`
    * バックアップ・ウィンドウの開始時間  デフォルト・バックアップ・ウィンドウ

---

## Bastionサーバの作成

OCIダッシュボードメニューから、**コア・インフラストラクチャ**>**コンピュート**>**インスタンス**にアクセスします。**インスタンスの作成**ボタンを押してComputeインスタンスの作成ウィザードを開始します。
ウィザード内の入力例を下記に示します。

---

* 名前　: 任意名前　`bastion`
* コンパートメントの選択 : 任意のコンパートメント名　 `/dev`
* 配置とハードウェアの構成
    * 可用性ドメインの選択　：デフォルト　`Fubk:AP-TOKYO-1-AD-1`
    * フォルト・ドメインの選択　：デフォルト　 `FAULT-DOMAIN-1`
    * イメージ：`Oracle Linux 8`
    * シェイプの選択：デフォルト　 `VM.Standard.E2.1`
* ネットワーキングの構成
    * 仮想クラウド・ネットワーク: `oke-vcn-quick-(省略)`
    * サブネット: `Bastion`
    * ネットワーク・セキュリティ・グループを使用してトラフィックを制御: いいえ
    * パブリックIPv4アドレスの割当て: はい
* SSHキーの追加：公開キー・ファイルの選択
* ブート・ボリュームの構成　：デフォルト設定

---


## File Storage Serviceの構成

OCIダッシュボードメニューから、**コア・インフラストラクチャ**>**ファイル・ストレージ**>**ファイル・システム**にアクセスします。**ファイル・システムの作成**ボタンを押してファイル・システムとエクスポート、マウント・ターゲットを作成します。
![20-12-03-21-19-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/ee276e62-8ab0-e2c9-922a-a9a19f7750b4.png)


ウィザード内の`詳細の編集`を選択して各項目を編集します。入力例を下記に示します。

---

* ファイル・システム情報
    * 名前　: 任意名前　`filesystem-oke`
    * 可用性ドメインの選択　：デフォルト　`Fubk:AP-TOKYO-1-AD-1`
    * 暗号化 : `Oracle管理キーを使用した暗号化`
* エクスポート情報
    * エクスポート・パス　: 任意名前　`/filesystem-oke`
    * セキュア・エクスポート・オプションの使用 : いいえ
* マウント・ターゲット情報
    * `新規マウント・ターゲットの作成`
    * 新規マウント・ターゲット名　: 任意名前　`mounttarget-fs-oke`
    * 仮想クラウド・ネットワーク: `oke-vcn-quick-(省略)`
    * サブネット: `oke-subnet-quick-OKE-(省略)`
    *  ネットワーク・セキュリティ・グループを使用してトラフィックを制御: いいえ

---

ファイル・システムを作成後、下記の**マウント・ターゲットの詳細**画面より、マウント・ターゲットのOCID (例: `ocid1.mounttarget.oc1.ap_tokyo_1.aa(省略)`)、エクスポート・パス (例: `/filesystem-oke`) とマウント・ターゲットのIPアドレス (例: `10.0.10.5`) を確認しておきます

![20-12-03-21-34-55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/3628bfef-14ad-94a4-b75f-1b2a2f66d7ec.png)


# Oracle MySQL Database Serviceインスタンスの構成
## Bastionサーバの構成
ここまでの作業で下記のようなOCIの構成ができていると思います。

![20-12-03-21-55-33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/6cf94399-7386-df04-96b2-f542770edc93.png)


BastionサーバにSSHクライアントからアクセスし、MySQL Database Serviceインスタンスのデータベース・スキーマ作成を行います。macOSの場合はターミナルから下記コマンドを実行して接続します。

```shell-session:
ssh -i [秘密鍵のパス] opc@[BastionサーバのパブリックIPアドレス]
```

次に下記コマンドでBastionサーバ上にMySQL Shellをインストールします。

```shell-session:
sudo yum install https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
sudo yum install mysql-shell
```

Oracle MySQL Database Serviceインスタンスを作成した際に設定した管理者ユーザー名と、サービス・コンソール画面から確認できるエンドポイントのプライベートIPアドレスを指定してMySQL Shellを起動します。起動時に入力が求められるパスワードはOracle MySQL Database Serviceインスタンス作成ウィザード内で設定したパスワードです。

```shell-session:
$ mysqlsh [管理者ユーザー名]@[プライベートIPアドレス]
Please provide the password for '[管理者ユーザー名]@[プライベートIPアドレス]': **************
MySQL Shell 8.0.22
```

## WordPress用のデータベース・スキーマの作成
BastionサーバにアクセスしMySQL Shellなどクライアント・ツールを利用してMySQL Database Serviceインスタンス作成時に設定した管理者ユーザー・管理者パスワードでアクセスします。下記にはMySQL Shellを利用した例を記しますが、SQLモードに切り替え後、WordPressが利用するデータベース・スキーマ`wpdb`を作成しています。

```Bash: 
$ mysqlsh mdsadmin@10.0.0.3
MySQL Shell 8.0.22

Copyright (c) 2016, 2020, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.

Type '\help' or '\?' for help; '\quit' to exit.
Creating a session to 'mdsadmin@10.0.0.3'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 24 (X protocol)
Server version: 8.0.22-u2-cloud MySQL Enterprise - Cloud
No default schema selected; type \use <schema> to set one.

 MySQL  10.0.0.3:33060+ ssl  JS > \sql
Switching to SQL mode... Commands end with ;

 MySQL  10.0.10.3:33060+ ssl  SQL > CREATE DATABASE wpdb;
Query OK, 1 row affected (0.0042 sec)
```

## WordPress用のデータベース・ユーザーの作成と権限の付与
WordPress用のデータベース・ユーザー`wpuser`の作成と`wpdb`スキーマに対する権限の付与を行います。WordPressコンテナが稼働するOKEクラスタが稼働するサブネット10.0.10.0/24からリモート・アクセスを許可するようにワイルドカードを利用してhost部分を`'10.0.10.%'`としています。

```Bash: 
 MySQL  10.0.0.3:33060+ ssl  SQL > CREATE USER 'wpuser'@'10.0.10.%' IDENTIFIED BY 'PASSWORD';
Query OK, 0 rows affected (0.0068 sec)

 MySQL  10.0.0.3:33060+ ssl  SQL > GRANT ALL ON wpdb.* TO 'wpuser'@'10.0.10.%' WITH GRANT OPTION;
Query OK, 0 rows affected (0.0029 sec)
```

# OKE上へのWordPressコンテナのデプロイ
## kubectlの準備

下記ドキュメントを参考にクライアント環境にOCI CLIとkubestlをインストールしてください。

* [Oracle Cloud Infrastructureドキュメント  CLIのインストール](https://docs.cloud.oracle.com/ja-jp/iaas/Content/API/SDKDocs/cliinstall.htm)

* [Oracle Cloud Infrastructureドキュメント  Kubectlを使用したクラスタへのアクセス](https://docs.cloud.oracle.com/ja-jp/iaas/Content/ContEng/Tasks/contengaccessingclusterkubectl.htm)

* [Kubernetesドキュメント  kubectlのインストールおよびセットアップ](https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/)

OCIダッシュボードメニューから、**ソリューションおよびプラットフォーム**>**開発者サービス**>**Kubernetesクラスタ**のクラスタの詳細コンソール画面から**クラスタへのアクセス**ボタンを押して**ローカル・アクセス**を選択します。表示されたコマンドを実行し、作成したOKEクラスタのkubeconfigを登録し、kubectlの構成を完了します。

![20-12-03-22-32-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/109c6a83-cb73-b04a-69e8-2dd4097ba30d.png)


kubectlの構成完了後、下記コマンドを実行してOKEクラスタが使用可能であることを確認します。

```Bash: 
$ kubectl version

Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.3", GitCommit:"1e11e4a2108024935ecfcb2912226cedeafd99df", GitTreeState:"clean", BuildDate:"2020-10-14T12:50:19Z", GoVersion:"go1.15.2", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.10", GitCommit:"79f4a3a6e44e0717ac55cfc401801555ee07a37f", GitTreeState:"clean", BuildDate:"2020-10-21T19:09:25Z", GoVersion:"go1.13.15 BoringCrypto", Compiler:"gc", Platform:"linux/amd64"}

```

## PersistentVolumeとPersistentVolumeClaimの作成
ここまでの手順でKubernetes (OKE)を利用するための事前準備が完了しました。OKE上に展開したコンテナから他のOCIリソースへアクセスするために必要なリソースを作成していきます。はじめにOCI File Storageを利用してWordPressの静的コンテンツを格納する`ReadWriteMany`なPersistentVolumeとコンテナからのアクセスで利用するPersistentVolumeClaimを作成します。OKE上のコンテナからOCI File Storageへのアクセス方法はこちらの[ブログ記事](https://blogs.oracle.com/cloud-infrastructure/using-file-storage-service-with-container-engine-for-kubernetes)を参考にしました。
ここで、事前に作成したファイル・システムのマウント・ターゲットのOCIDを利用してStorageClassを作成します。このとき利用したManifestファイル`storageclass.yaml`を下記に記します。

```YAML:storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: oci-fss
provisioner: oracle.com/oci-fss
parameters:
  mntTargetId: ocid1.mounttarget.oc1.ap_tokyo_1.aaaaaa4np2sq62aqnzzhillqojxwiotboawxi33lpfxs2mjnmfsc2mia
```

下記コマンドでOKE上にStorageClassを作成し、StorageClass`oci-fss`が作成されていることを確認します。

```Bash:
$ kubectl apply -f storageclass.yaml
storageclass.storage.k8s.io/oci-fss created

$ kubectl get storageclass -o wide
NAME            PROVISIONER                       RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
oci (default)   oracle.com/oci                    Delete          Immediate              false                  25h
oci-bv          blockvolume.csi.oraclecloud.com   Delete          WaitForFirstConsumer   false                  25h
oci-fss         oracle.com/oci-fss                Delete          Immediate              false                  2m37s
```

次にPersistentVolume `wspv`を作成します。`storageClassName`に先ほど作成した`oci-fss`を指定し、`accessModes`に`ReadWriteMany`を指定、`server: `に作成したファイルシステムの**マウント・ターゲットのIPアドレス**を、`path: `に**エクスポート・パス**をそれぞれ指定します。

```YAML:wspv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
 name: wspv
spec:
 storageClassName: oci-fss
 capacity:
  storage: 5Gi
 accessModes:
  - ReadWriteMany
 mountOptions:
  - nosuid
 nfs:
  server: 10.0.10.5
  path: "/filesystem-oke"
  readOnly: false
```

下記コマンドでOKE上にPersistentVolumeを作成し、PersistentVolume`wspv`が作成されていることを確認します。

```Bash:
$ kubectl apply -f wspv.yaml
persistentvolume/wspv created

$ kubectl get pv -o wide
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE   VOLUMEMODE
wspv   5Gi        RWX            Retain           Available           oci-fss                 15s   Filesystem
```

OKE上のコンテナからPersistentVolume`wspv`へアクセスするためのPersistentVolumeClaim `wspvc`を作成します。下記のように`storageClassName`に`oci-fss`を指定し、`volumeName`に`wspv`を指定しています。

```YAML:wspvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: wspvc
spec:
  storageClassName: oci-fss
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  volumeName: wspv
```

下記コマンドでOKE上にPersistentVolumeClaimを作成し、PersistentVolumeClaim `wspvc`が作成されていることを確認します。

```Bash:
$ kubectl apply -f wspvc.yaml
persistentvolumeclaim/wspvc created

$ kubectl get pvc -o wide
NAME    STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE   VOLUMEMODE
wspvc   Bound    wspv     5Gi        RWX            oci-fss        11s   Filesystem
```

## Oracle MySQL Database Serviceへ接続するためのServiceとEndpointの作成

OKE上のコンテナからOKE外部のOracle MySQL Database ServiceインスタンスへアクセスするためのServiceとEndpointを作成します。下記にそれぞれのManifestファイルの記述内容を記します。

```YAML:externalservice.yaml
kind: Service
apiVersion: v1
metadata:
  name: mds-service
spec:
  ports:
  - protocol: TCP
    port: 3306
```

```YAML:endpoints.yaml
apiVersion: v1
kind: Endpoints
metadata:
  name: mds-service
subsets:
  - addresses:
      - ip: 10.0.0.3
    ports:
      - port: 3306
```

ここで`ip`に指定している`10.0.0.3`はMySQL Database Serviceインスタンスのエンドポイントのプライベート・IPアドレスです。下記コマンドを実行してService `mds-service`とEndpoint `mds-service`が作成されていることを確認します。

```Bash: 
$ kubectl apply -f externalservice.yaml
service/mds-service created

$ kubectl apply -f endpoints.yaml
endpoints/mds-service created

$ kubectl get service,endpoints
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/kubernetes    ClusterIP   10.96.0.1       <none>        443/TCP    26h
service/mds-service   ClusterIP   10.96.114.184   <none>        3306/TCP   30s

NAME                       ENDPOINTS            AGE
endpoints/kubernetes       192.29.37.142:6443   26h
endpoints/mds-service      10.0.0.3:3306        18s
endpoints/oracle.com-oci   <none>               26h
```

## Oracle MySQL Database Serviceへの接続情報を管理するためのSecretの作成

Oracle MySQL Database Serviceインスタンスに事前に作成した`wpuser`とそのパスワードの文字列をBase64エンコードします。

```Bash: 
$ echo -n 'wpuser' | base64
d3B1c2Vy

$ echo -n 'PASSWORD' | base64
UEFTU1dPUkQ=
```

これらの値をusernameとpasswordに指定したManifestファイルを作成します。

```YAML:mdssecret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mdssecret
type: Opaque
data:
  username: d3B1c2Vy
  password: UEFTU1dPUkQ=
```

下記コマンドを実行してSecret `mdssecret`が作成されていることを確認します。

```Bash: 
$ kubectl apply -f mdssecret.yaml
secret/mdssecret created

$ kubectl get secret
NAME                  TYPE                                  DATA   AGE
default-token-2kqzs   kubernetes.io/service-account-token   3      26h
mdssecret             Opaque                                2      17s
```

## WordPressコンテナを構成するDeploymentの作成
ここまでで作成したPersistentVolumeClaim `wspvc`、Endpoint `mds-service`、Secret `mdssecret`を参照してOKE上にWordPressコンテナを構築するDeploymentのManifestファイルを作成します。

```YAML:wp_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: wordpress
 labels:
    app: wordpress
spec:
 replicas: 2
 selector:
    matchLabels:
      app: wordpress
 strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
 template:
   metadata:
     labels:
       app: wordpress 
   spec:
     containers:
       - name: wordpress
         image: docker.io/library/wordpress:latest
         imagePullPolicy: Always
         env:
           - name: PATH
             value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
           - name: WORDPRESS_DB_PASSWORD
             valueFrom:
               secretKeyRef:
                 name: "mdssecret"
                 key: password
           - name: WORDPRESS_DB_USER
             valueFrom:
               secretKeyRef:
                 name: "mdssecret"
                 key: username
           - name: WORDPRESS_DB_NAME
             value: wpdb
           - name: WORDPRESS_DB_HOST
             value: mds-service:3306
         ports:
           - containerPort: 80
             name: wordpress
             protocol: TCP
           - containerPort: 3306
             name: mysql
             protocol: TCP
         volumeMounts:
           - mountPath: /var/www/html
             name: wspd
     volumes:
        - name: wspd
          persistentVolumeClaim:
            claimName: wspvc
```

下記コマンドを実行してDeployment `wordpress`が作成されていることを確認します。

```Bash:
$ kubectl apply -f wp_deployment.yaml
deployment.apps/wordpress created

$ kubectl get deployment -o wide

NAME        READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES                               SELECTOR
wordpress   2/2     2            2           92s   wordpress    docker.io/library/wordpress:latest   app=wordpress
```

## Service LoadBalancerの作成
最後にInternetからOKE上で稼働するWordPressコンテナへアクセスするエンドポイントとなるService LoadBalancerを作成します。Deploymentで定義されたapp=wordpressラベルのついた各Pod上のTCPの80番ポートを利用するコンテナをターゲットとして作成します。下記にManifestファイルを記します。

```YAML:wplb.yaml
apiVersion: v1
kind: Service
metadata:
 name: wslb
 labels:
   app: wslb
spec:
 type: LoadBalancer
 ports:
   - name: http
     port: 80
     targetPort: 80
 selector:
   app: wordpress
```

下記コマンドを実行してService `wslb`が作成されていることを確認します。

```Bash:
$  kubectl apply -f wslb.yaml
service/wslb created

$ kubectl get service -o wide

NAME          TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE   SELECTOR
kubernetes    ClusterIP      10.96.0.1       <none>         443/TCP        27h   <none>
mds-service   ClusterIP      10.96.114.184   <none>         3306/TCP       49m   <none>
wslb          LoadBalancer   10.96.121.80    XXX.XXX.XX.X   80:32015/TCP   41s   app=wordpress
```

ここで`EXTERNAL-IP`として表示されるパブリック・IPアドレスがWebブラウザがらアクセスする際に参照する外部IPとなります。また、OCIのダッシュボードから同じパブリック・IPアドレスを有したOCI Load Balancerが自動で作成されていることを確認できます。

![20-12-04-00-42-53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/ec266301-8620-ac73-ab2a-ebc9db0350cc.png)



# 最終構成の確認
## OCI上で稼働するインフラ構成
OKE上のリソースを展開することでOCI File StorageへのアクセスやOCI Load Balancerが自動構築され、OCI上のインフラ構成が完成します。最終構成は下記のようになっていると思います。
![20-12-04-00-50-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/62c3690f-9878-3af2-87ef-77ad66c590a2.png)


## OKE上のリソース一覧
OKE上に展開されたリソースの一覧は下記のようになっていると思います。

```Bash:
$  kubectl apply -f wslb.yaml
service/wslb created

$ kubectl get all -o wide

NAME                             READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
pod/wordpress-645f975dc5-gxlfz   1/1     Running   0          24m   10.244.0.4     10.0.10.2   <none>           <none>
pod/wordpress-645f975dc5-ndfj5   1/1     Running   2          24m   10.244.0.131   10.0.10.3   <none>           <none>

NAME                  TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE     SELECTOR
service/kubernetes    ClusterIP      10.96.0.1       <none>         443/TCP        27h     <none>
service/mds-service   ClusterIP      10.96.114.184   <none>         3306/TCP       57m     <none>
service/wslb          LoadBalancer   10.96.121.80    XXX.XXX.XX.X   80:32015/TCP   8m24s   app=wordpress

NAME                        READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES                               SELECTOR
deployment.apps/wordpress   2/2     2            2           24m   wordpress    docker.io/library/wordpress:latest   app=wordpress

NAME                                   DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES                               SELECTOR
replicaset.apps/wordpress-645f975dc5   2         2         2       24m   wordpress    docker.io/library/wordpress:latest   app=wordpress,pod-template-hash=645f975dc5
```

## WordPressの動作確認
WebブラウザからLoadBalancerにアサインされているEXTERNAL-IP`XXX.XXX.XX.X`を参照するようにWordPressの管理画面に下記のURLでアクセスし、WordPressが正常に稼働していることを確認します。

http://XXX.XXX.XX.X/wp-admin/install.php

![20-12-04-00-53-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/58e4663a-d049-6adf-c519-dbe2d187c598.png)


WordPressの初期設定後、カスタムテーマの導入を行い、簡単なサンプルページを作成しました。その後、下記コマンドを実行してDeploymentを再作成し、データが永続化していることを確認します。

```Bash:
$ kubectl delete deployment wordpress
deployment.apps "wordpress" deleted

$ kubectl apply -f wp_deployment.yaml
deployment.apps/wordpress created

$ kubectl get deployment -o wide
NAME        READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES                               SELECTOR
wordpress   2/2     2            2           21s   wordpress    docker.io/library/wordpress:latest   app=wordpress
```

`http://XXX.XXX.XX.X`にアクセスして導入したカスタムテーマや投稿したデータが永続化していることを確認できました。

![20-12-04-01-05-29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/831381/2f6298de-da61-4d6a-ad61-133358242866.png)


# まとめ
お疲れさまでした。Oracle Cloud Infrastructure (OCI) で提供されているマネージドサービスを組み合わせてWordPress基盤を構築することができました。Oracle MySQL Database ServiceとFile Storage Serviceを利用することで永続化が必要なデータ層をOKE外部に分離することができ、OKE上のWordPressコンテナは柔軟にスケールアウトが可能なアーキテクチャを実現しています。また、今後のOracle MySQL Database Serviceの機能拡張に合わせてより高度なシステム要件にも対応できるようになるので今後の機能拡張が楽しみです。





# 関連情報
[Kubernetesドキュメント](https://kubernetes.io/ja/docs/home/)

[Example: Deploying WordPress and MySQL with Persistent Volumes](https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/)

[Oracle Cloud Infrastructureドキュメント Container Engine for Kubernetesの概要](https://docs.cloud.oracle.com/ja-jp/iaas/Content/ContEng/Concepts/contengoverview.htm)

[Oracle Cloud Infrastructureドキュメント ファイル・ストレージの概要](https://docs.cloud.oracle.com/ja-jp/iaas/Content/File/Concepts/filestorageoverview.htm)

[Oracle Cloud Infrastructure Documentation MySQL Database](https://docs.cloud.oracle.com/en-us/iaas/mysql-database/index.html)