はじめに
GKE で WordPress を実装するために情報を探していたところ、KUSANAGI Runs on Docker というのが公開(まだベータ版みたいですが)されていましたので、この Docker イメージ(kusanagi-nginx、kusanagi-php7)をそのまま使って KUSANAGI を GKE 上に実装し、更には KUSANAGI へのアクセスに Let's Encrypt を利用して HTTPS 化したいと思います。
尚、本投稿でのコマンドの実行は、全て Google Cloud Shell 内で行っています。
実装イメージ図
クラスタ作成
GKE に リージョンクラスタ を作成します。
リージョンクラスタ はリージョン内の全ゾーン(--node-locations
オプションでゾーンを指定した場合は、指定された全ゾーン)にノードが作成されるので、可用性を高めることができます。
また KUSANAGI のパフォーマンスを出来る限り落とさないよう、永続ディスクには GCE のリージョン永続ディスク(Regional Persistent Disk)を利用します。
今回は東京リージョン(ap-northeast1)の内、2ゾーン(asia-northeast1-a、asia-northeast1-b)にまたがるクラスタを作成します。
クラスタ作成スクリプトの準備
クラスタ作成コマンドは、指定するパラメータの数が結構多いので、Shell スクリプト化します。
#!/bin/bash
CLUSTER_ID=test-cluster #クラスタ名
MACHINE_TYPE=n1-standard-1 #ノードのマシンタイプ(デフォルト:n1-standard-1)
REGION=asia-northeast1 #クラスタを作成するリージョン
NUM_NODES=1 #ゾーン毎に配置するノード数
NODE_LOCATIONS=${REGION}-a,${REGION}-b #ノードを配置するゾーン ※リージョン永続ディスクを使う場合は2ノードまで
NETWORK=default #ノードに接続されるVPC
SUBNETWORK=default #ノードに接続されるサブネット
CLUSTER_VERSION=$(gcloud beta container get-server-config --region ${REGION} --format='value(validMasterVersions[0])')
gcloud beta container clusters create ${CLUSTER_ID} \
--cluster-version=${CLUSTER_VERSION} \
--machine-type=${MACHINE_TYPE} \
--region=${REGION} \
--num-nodes=${NUM_NODES} \
--node-locations=${NODE_LOCATIONS} \
--network=${NETWORK} \
--subnetwork=${SUBNETWORK} \
--enable-autorepair
スクリプトの実行
クラスタ作成スクリプトを実行します。作成完了まで多少時間がかかります。
$ sh create-cluster.sh
Creating cluster test-cluster...done.
Created [https://container.googleapis.com/v1beta1/projects/gooday-development/zones/asia-northeast1/clusters/test-cluster].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/asia-northeast1/test-cluster?project=gooday-development
kubeconfig entry generated for test-cluster.
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
test-cluster asia-northeast1 1.10.7-gke.1 35.200.88.154 n1-standard-1 1.10.7-gke.1 2 RUNNING
Helm
GKE(K8s)で Let's Encrypt の証明書を管理(取得、自動更新)するには、K8s パッケージ(以降、Chart)の cert-manager を クラスタに用意します。
cert-manager をクラスタにデプロイするには、K8s Helm(以降、Helm)と呼ばれるパッケージマネージャを利用します。(Linux でいう yum
や apt-get
、Python でいう pip
と似た感じですかね)
Helm クライアントのインストール
Cloud Shell では Helm クライアントが用意されていないのでインストールする必要があります。
$ curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash
Tiller のインストール
Helm のサーバ部分は Tiller と呼ばれ、Helm クライアントからのリクエストの受付、公式 Chart リポジトリ(以降、Helm Repo)から Chart をクラスタにインストール(デプロイ)、インストール済 Chart のリリース監視等の役割を担います。
Tiller は自身のクラスタ内にデプロイする必要があり、Helm クライアントと Tiller 間は、クラスタ管理権限を持つ専用のサービスアカウント tiller を介して接続されます。
サービスアカウント 'tiller' の作成
サービスアカウント tiller
を、名前空間 kube-system
に作成します。
$ kubectl create serviceaccount -n kube-system tiller
# サービスアカウント確認
$ kubectl describe sa tiller -n kube-system
Name: tiller
Namespace: kube-system
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: tiller-token-XXXXX
Tokens: tiller-token-XXXXX
Events: <none>
cluster-admin ロールにバインド
サービスアカウント tiller
に クラスタ管理者ロール cluster-admin を割り当てます。
$ kubectl create clusterrolebinding tiller-binding \
--clusterrole=cluster-admin \
--serviceaccount kube-system:tiller
# ロールバインド確認
$ kubectl describe clusterrolebinding tiller-binding
Name: tiller-binding
Labels: <none>
Annotations: <none>
Role:
Kind: ClusterRole
Name: cluster-admin
Subjects:
Kind Name Namespace
---- ---- ---------
ServiceAccount tiller kube-system
Tiller のデプロイ
K8s クラスタに Tiller をデプロイします。
Tiller のデプロイには、kubectl
コマンドではなく、 helm クライアントを用います。
$ helm init --service-account tiller
# デプロイの確認
$ kubectl get pod,deploy,svc -n kube-system -l name=tiller
NAME READY STATUS RESTARTS AGE
pod/tiller-deploy-895d57dd9-gmtjs 1/1 Running 0 1d
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.extensions/tiller-deploy 1 1 1 1 1d
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/tiller-deploy ClusterIP 10.1.163.92 <none> 44134/TCP 1d
helm リポジトリのアップデート
利用可能な Chart の情報を、Helm Repo から Helm クライアントにキャッシュします。
$ helm repo update
証明書取得(DNS 認証方式)
Let's Encrypt では証明書を取得するにあたり、「自分がドメインの所有者かどうか」を認証してもらう方式として、次の3つがサポートされています。
-
HTTP-01
Let’s Encryptの認証局からワンタイムトークンを発行してもらい、Webサーバに認証用ファイルを設置する。
認証局からHTTP(80番ポート)でアクセスしてもらい、ワンタイムトークンと認証用ファイルとの妥当性を検証する。 -
TLS-SNI-01
HTTP-01と同じ方法だが、HTTPS(443ポート)を使用する。 -
DNS-01
Let’s Encryptの認証局から発行してもらったワンタイムトークンを対象ドメインのTXTレコードに登録することで検証する。
DNS による認証方式(DNS-01)だと、事前にWebサイトを準備する必要が無いので、今回は DNS プロバイダに Amazon Route53 を使い証明書を取得したいと思います。
※ Route53 の代わりに Cloud DNS を利用することもできます。
cert-manager インストール
Helm を用いて cert-manager を K8s クラスタにインストールします。
$ helm install \
--name cert-manager \
--namespace kube-system \
stable/cert-manager
# Chart 確認
$ helm list
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
cert-manager 1 Tue Sep 11 11:57:24 2018 DEPLOYED cert-manager-v0.4.1 v0.4.1 kube-system
# デプロイ確認
$ kubectl get deploy -n kube-system
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
cert-manager 1 1 1 1 2m
AWS IAM ユーザー登録
cert-manager が Route63 を操作できるよう、AWS 側にて次のポリシーをアタッチした IAM ユーザーを作成します。
作成後は、Access Key と Secret Access Key を控えておきます。
※ IAM ユーザーの作成手順は割愛します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:GetChange",
"Resource": "arn:aws:route53:::change/*"
},
{
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/*"
},
{
"Effect": "Allow",
"Action": "route53:ListHostedZonesByName",
"Resource": "*"
}
]
}
AWS Secret Access Key 登録
控えておいた Secret Access Key を Base64 エンコード (echo -n '<Secret Access Key>' | base64
)し、その値を K8s クラスタの Secret リソースに登録します。
尚、Secret Access Key の値を持った Secret リソースは、cert-manager と同一 namespace(kube-system
)に作成する必要がある点に注意してください。
Manifest ファイル作成
apiVersion: v1
kind: Secret
metadata:
name: prod-route53-credentials-secret
namespace: kube-system
type: Opaque
data:
secret-access-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Secret 作成
$ kubectl apply -f secret.yaml
# Secret 確認
$ kubectl describe secret prod-route53-credentials-secret -n kube-system
Name: prod-route53-credentials-secret
Namespace: kube-system
Labels: <none>
Annotations:
Type: Opaque
Data
====
secret-access-key: 40 bytes
Issuer/ClusterIssuer 作成
認証局(Let's Encrypt)と DNS プロバイダー(Amazon Route53)の情報を Issuer/ClusterIssuer リソースとして作成します。
尚、namespace 毎に利用可能なリソースは Issuer、K8s クラスタ全体で利用可能なリソースは ClusterIssuer で作成します。
今回は ClusterIssuer リソースとして作成します。
Manifest ファイル作成
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: myname@example.com #TODO: Your email address
privateKeySecretRef:
name: letsencrypt-prod
http01: {}
dns01:
providers:
- name: prod-dns
#couddns: #for Google Cloud DNS
# ・・・
route53:
accessKeyId: AKXXXXXXXXXXXXXXXXXX #TODO: Your aws access key
region: ap-northeast-1 #TODO: Your region of amazon route53
secretAccessKeySecretRef:
name: prod-route53-credentials-secret
key: secret-access-key
ClusterIssuer リソース作成
$ kubectl apply -f cluster-issuer.yaml
# ClusterIssuer リソース確認
$ kubectl describe clusterissuer letsencrypt-prod
Name: letsencrypt-prod
Namespace:
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"certmanager.k8s.io/v1alpha1","kind":"ClusterIssuer","metadata":{"annotations":{},"name":"letsencrypt-prod","namespace":""},"spec":{"acme...
API Version: certmanager.k8s.io/v1alpha1
Kind: ClusterIssuer
Metadata:
Cluster Name:
Creation Timestamp: 2018-09-12T02:08:04Z
Generation: 1
Resource Version: 7907617
Self Link: /apis/certmanager.k8s.io/v1alpha1/clusterissuers/letsencrypt-prod
UID: af2856dd-b630-11e8-9a5d-42010a92004d
Spec:
Acme:
Dns 01:
Providers:
Name: prod-dns
Route 53:
Access Key ID: AKXXXXXXXXXXXXXXXXXX
Hosted Zone ID:
Region: ap-northeast-1
Secret Access Key Secret Ref:
Key: secret-access-key
Name: prod-route53-credentials-secret
Email: myname@example.com
Http 01:
Private Key Secret Ref:
Key:
Name: letsencrypt-prod
Server: https://acme-v02.api.letsencrypt.org/directory
Status:
Acme:
Uri: https://acme-v02.api.letsencrypt.org/acme/acct/99999999
Conditions:
Last Transition Time: 2018-09-12T02:08:05Z
Message: The ACME account was registered with the ACME server
Reason: ACMEAccountRegistered
Status: True
Type: Ready
Events: <none>
TLS 証明書の取得
Certificate リソースにより TSL 証明書を取得します。
Manifest ファイル作成
Let's Encrypt はSAN(Subject Alternative Name、サブジェクト代替名)対応の証明書を発行することができます。例えば www.example.com
と example.com
と2つのホスト名に対応した証明書を作成することができます。
またワイルドカード(*)証明書にも対応しています。
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: cert-kusanagi
spec:
secretName: cert-kusanagi-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: 'kusanagi.example.com'
dnsNames:
- 'www.kusanagi.example.com'
- 'kusanagi.example.com'
acme:
config:
- dns01:
provider: prod-dns
domains:
- 'kusanagi.example.com'
Certificate リソース作成(証明書取得)
証明書を Manifest ファイルを元に取得します。kubectl describe
コマンドで出力された Event
に cert-manager Certificate issued successfully
が出力されたら作成完了です。
証明書に追加取得が必要な場合は、Certificate リソース を作成することで簡単に追加取得することができます。
$ kubectl apply -f certificate.yaml
# Certificate 確認
$ kubectl describe certificate cert-kusanagi
・・・
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CreateOrder 3m cert-manager Created new ACME order, attempting validation...
Normal DomainVerified 1m cert-manager Domain "kusanagi.example.com" verified with "dns-01" validation
Normal IssueCert 47s cert-manager Issuing certificate...
Normal CertObtained 45s cert-manager Obtained certificate from ACME server
Normal CertIssued 45s cert-manager Certificate issued successfully
尚、取得処理の最中に cert-manager が Route53 のゾーン情報に _acme-challenge.kusanagi.example.com
というホスト名の TXT レコードを追加し、その値(Value)に Let's Encrypt 認証トークンがセットしていることが確認できます。
TLS 証明書、秘密鍵の確認
取得した TLS 証明書(tls.crt
)と秘密鍵(tls.key
)が Secret リソースに保存されていることを確認します。
$ kubectl describe secret cert-kusanagi-tls
Name: cert-kusanagi-tls
Namespace: default
Labels: certmanager.k8s.io/certificate-name=cert-kusanagi
Annotations: certmanager.k8s.io/alt-names=kusanagi.example.com
certmanager.k8s.io/common-name=kusanagi.example.com
certmanager.k8s.io/issuer-kind=ClusterIssuer
certmanager.k8s.io/issuer-name=letsencrypt-prod
Type: kubernetes.io/tls
Data
====
tls.crt: 3846 bytes #TLS証明書
tls.key: 1679 bytes #秘密鍵
これで証明書の取得は完了です。
更に、TLS 証明書の有効期限の30日前になると、cert-manager が 認証局(Let's Encrypt)に対し自動更新してくれます。
StorageClassリソースの作成
リージョン永続ディスクの StorageClass を作成します。
StorageClass を作成することで、後述の PersistentVolumeClaim からリージョン永続ディスクをアサインすることが出来るようになります。
Manifest ファイルの準備
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: regpd
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard #pd-standard(HDD)|pd-ssd(SSD)
replication-type: regional-pd
zones: asia-northeast1-a, asia-northeast1-b
StorageClass 作成、確認
$ kubectl apply -p storage-class.yaml
storageclass.storage.k8s.io/regpd created
#StorageClass 確認
$ kubectl describe sc regpd
Name: regpd
IsDefaultClass: No
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"regpd","namespace":""},"parameters":{"replication-type":"regional-pd","type":"pd-standard","zones":"asia-northeast1-a, asia-northeast1-b"},"provisioner":"kubernetes.io/gce-pd"}
Provisioner: kubernetes.io/gce-pd
Parameters: replication-type=regional-pd,type=pd-standard,zones=asia-northeast1-a, asia-northeast1-b
AllowVolumeExpansion: <unset>
MountOptions: <none>
ReclaimPolicy: Delete
VolumeBindingMode: Immediate
Events: <none>
Cloud SQL アクセス用サービスアカウントキーの取得
Pod から Cloud SQL を利用するため、アクセス用のサービスアカウントキー を取得します。
サービスアカウント 'cloudsql' の作成
$ gcloud iam service-accounts create cloudsql --display-name="cloudsql"
サービスアカウントへ役割(Cloud SQL クライアント)付与
$ gcloud projects add-iam-policy-binding <Project ID> \
--member="serviceAccount:cloudsql@<Project ID>.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
サービスアカウントキーの取得
カレントディレクトリ内に credentials.json
というキーファイル名でサービスアカウントキーを保存します。
$ gcloud iam service-accounts keys create ./credentials.json --iam-account cloudsql@<Project ID>.iam.gserviceaccount.com
Secret に登録
サービスアカウントキーを Secret に登録します。
ここで登録した Secret service-account-token
を Pod 内の Cloud SQL Proxy コンテナが読み取り、Cloud SQL へ接続することができます。
$ kubectl create secret generic service-account-token --from-file=credentials.json=./credentials.json
$ kubectl describe secret service-account-token
Name: service-account-token
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
credentials.json: 2337 bytes
Cloud SQL
KUSANAGI のバックエンド DB を Cloud SQL に作成します。
もし既に MySQL インスタンスが存在するなら、そのインスタンスを利用することもできます。
(その場合、インスタンスの作成部分は飛ばし、データベースの作成から行ってください)
インスタンス作成スクリプト
Cloud SQL インスタンスを作成する Shell スクリプトを用意します。
#!/bin/bash
INSTANCE_ID=kusanagi #インスタンスID
REGION=asia-northeast1 #インスタンスを作成するリージョン
DATABASE_VERSION=MYSQL_5_7 #データベースバージョン
NUMBER_CPUS=1 #割当てvCPU数
MEMORY_SIZE=3840MiB #割当てメモリ容量
STORAGE_TYPE=HDD #ストレージの種類(HDD|SSD)
STORAGE_SIZE=10GB #ストレージの容量(GB単位)
gcloud beta sql instances create ${INSTANCE_ID} \
--region=${REGION} \
--database-version=${DATABASE_VERSION} \
--cpu=${NUMBER_CPUS} \
--memory=${MEMORY_SIZE} \
--storage-type=${STORAGE_TYPE} \
--storage-size=${STORAGE_SIZE} \
--storage-auto-increase
インスタンス作成
インスタンス作成スクリプトを実行します。しばらく待つとインスタンスが作成されます。
$ sh create-cloudsql.sh
Creating Cloud SQL instance...done.
Created [https://www.googleapis.com/sql/v1beta4/projects/gooday-development/instances/kusanagi].
NAME DATABASE_VERSION LOCATION TIER ADDRESS STATUS
kusanagi MYSQL_5_7 asia-northeast1-c db-custom-1-3840 35.XXX.XXX.XXX RUNNABLE
データベース作成
インスタンス内にデータベース(kusanagi
)を作成します。
$ gcloud sql databases create kusanagi --instance=kusanagi
ユーザー作成
データベース接続ユーザー(kusanagi
)を作成します。
$ gcloud sql users create kusanagi --host=% --instance=kusanagi --password=<PASSWORD>
外部IPアドレスの予約
KUSANAGI 公開用に、外部 IP アドレス(グローバル IP アドレス)を予約します。
今回は kusanagi-ip
という名前で予約しました。
$ gcloud compute addresses create kusanagi-ip --global
$ gcloud compute addresses list --filter="name=('kusanagi-ip')"
NAME REGION ADDRESS STATUS
kusanagi-ip 35.201.XXX.XXX RESERVED
Route53 に A レコードを追加
Amazon Route53 に A レコード kusanagi.example.com
(example.com
の部分は実際のドメイン名に置換してください)を追加し、名前解決ができるようにします。
その際 A レコードの値(Value)には、前述で予約した 外部 IP アドレス(35.201.XXX.XXX
)を入力してください。
※ Route53 への A レコード追加方法は割愛します。
KUSANAGI デプロイ
ここまでの手順で前準備が整いましたので、いよいよ KUSANAGI を GKE にデプロイします。
KUSANAGI イメージを GCR へアップロード
KUSANAGI の Docker イメージのうち、kusanagi-nginx
と kusanagi-php7
を Container Registry(GCR)にアップロードします。
# kusanagi-nginx イメージのダウンロード、タグ付け、GCR へアップロード
$ docker pull primestrategy/kusanagi-nginx:1.10.0-1
$ docker tag primestrategy/kusanagi-nginx:1.10.0-1 asia.gcr.io/gooday_internal_system/kusanagi-nginx:1.10.0-1
$ gcloud docker -- push asia.gcr.io/gooday_internal_system/kusanagi-nginx:1.10.0-1
# kusanagi-php7 イメージのダウンロード、タグ付け、GCR へアップロード
$ docker pull primestrategy/kusanagi-php7:7.0.6-1
$ docker tag primestrategy/kusanagi-php7:7.0.6-1 asia.gcr.io/gooday_internal_system/kusanagi-php7:7.0.6-1
$ gcloud docker -- push asia.gcr.io/gooday_internal_system/kusanagi-php7:7.0.6-1
KUSANAGI デプロイ
KUSANAGI 環境をデプロイします。
今回は1つの Manifest ファイルで次の環境を実装しています。
- PersistentVolumeClaim(
/home/kusanagi
、/etc/kusanagi.d
、/etc/nginx/conf.d
) - Deployment(
kusanagi-nginx
、kusanagi-php7
、cloudsql-proxy
) - Service(NortPort)
- Ingress
Manifest ファイルの準備
# /home/kusanagi, /etc/kusanagi.d, /etc/nginx/conf.d データ永続用PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-kusanagi-data
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: regpd
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
generation: 1
labels:
run: kusanagi
name: kusanagi
namespace: default
spec:
replicas: 1
selector:
matchLabels:
run: kusanagi
template:
metadata:
labels:
run: kusanagi
spec:
containers:
- image: asia.gcr.io/gooday_internal_system/kusanagi-nginx:1.10.0-1
imagePullPolicy: Always
name: kusanagi-nginx
env:
- name: PROFILE
value: "kusanagi_html" #KUSANAGI プロファイル名
- name: FQDN
value: "kusanagi.example.co.jp" #KUSANAGI ホスト名(FQDN)
- name: WPLANG
value: "ja" #WordPress の言語
- name: BCACHE
value: "off" #生成ページのキャッシュ(on|off)
- name: FCACHE
value: "off" #FastCGIレスポンスのキャッシュ(on|off)
ports:
- containerPort: 80
protocol: TCP
- containerPort: 443
protocol: TCP
readinessProbe: #ヘルスチェック設定(Ingressが行うヘルスチェックの設定に反映)
httpGet:
path: /readme.html
port: 80
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- mountPath: "/home/kusanagi"
name: kusanagi-data
subPath: home
- mountPath: "/etc/nginx/conf.d"
name: kusanagi-data
subPath: nginx-confd
- mountPath: "/etc/kusanagi.d"
name: kusanagi-data
subPath: kusanagid
resources:
requests:
cpu: 20m
- image: asia.gcr.io/gooday_internal_system/kusanagi-php7:7.0.6-1
imagePullPolicy: Always
name: kusanagi-php7
ports:
- containerPort: 9000
protocol: TCP
volumeMounts:
- mountPath: "/home/kusanagi"
name: kusanagi-data
subPath: home
- mountPath: "/etc/nginx/conf.d"
name: kusanagi-data
subPath: nginx-confd
- mountPath: "/etc/kusanagi.d"
name: kusanagi-data
subPath: kusanagid
resources:
requests:
cpu: 20m
- image: b.gcr.io/cloudsql-docker/gce-proxy:latest
imagePullPolicy: Always
name: cloudsql-proxy
command:
- /cloud_sql_proxy
- -dir=/cloudsql
- -instances=<Project ID>:asia-northeast1:kusanagi=tcp:3306 #Cloud SQL インスタンス接続名
- -credential_file=/credentials/credentials.json
volumeMounts:
- mountPath: /cloudsql
name: cloudsql
- mountPath: /etc/ssl/certs
name: ssl-certs
- mountPath: /credentials #サービスアカウントキーのマウント
name: service-account-token
hostAliases: #ホストエイリアスの設定
- hostnames: # Nginx がバックエンドのコンテナ kusanagi-php7 (FastCGI) に対し
- php # 'php' というホスト名で Proxy している為
ip: 127.0.0.1 # 各コンテナの /etc/hosts に追加される
volumes:
- name: kusanagi-data
persistentVolumeClaim:
claimName: "pvc-kusanagi-data"
- name: cloudsql
emptyDir:
- name: ssl-certs
hostPath:
path: /etc/ssl/certs
- name: service-account-token #サービスアカウントキーの Secret
secret:
secretName: service-account-token
---
apiVersion: v1
kind: Service
metadata:
name: kusanagi-nodeport
namespace: default
labels:
run: kusanagi-nodeport
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: kusanagi
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kusanagi-ingress
annotations:
#kubernetes.io/ingress.allow-http: "false" #HTTPSのみ許可する場合はコメントアウトを外す
kubernetes.io/ingress.global-static-ip-name: "kusanagi-ip" #外部 IP アドレス名
kubernetes.io/ingress.class: "gce"
kubernetes.io/tls-acme: "true"
spec:
tls:
- secretName: cert-kusanagi-tls #TLS 証明書、秘密鍵が保存されている Secret 名
backend:
serviceName: kusanagi-nodeport #NodePort サービス名
servicePort: 80 #NodePortの待受ポート
デプロイ
$ kubectl apply -f deploy-kusanagi.yaml
Ingress 確認
Ingress の準備が完了(ヘルスチェックの完了)するまで暫く待ちます。(結構長い時間がかかる場合があります)
Annotations:
にある ingress.kubernetes.io/backends:
のステータスが Unknown
から HEALTHY
に変われば準備完了です。
$ kubectl describe ingress kusanagi-ingress
Name: kusanagi-ingress
Namespace: default
Address: 130.211.XXX.XXX
Default backend: kusanagi-nodeport:80 (10.36.0.14:80)
TLS:
cert-kusanagi-tls terminates
Rules:
Host Path Backends
---- ---- --------
* * kusanagi-nodeport:80 (10.36.0.14:80)
Annotations:
kubernetes.io/tls-acme: true
ingress.kubernetes.io/backends: {"k8s-be-30098--49f84941322af08b":"HEALTHY"} #'Unknown' から 'HEALTHY' になるまで待つ
ingress.kubernetes.io/https-forwarding-rule: k8s-fws-default-kusanagi-ingress--49f84941322af08b
ingress.kubernetes.io/https-target-proxy: k8s-tps-default-kusanagi-ingress--49f84941322af08b
ingress.kubernetes.io/ssl-cert: k8s-ssl-d11f06e1315aac38-e5a1656857d9445c--49f84941322af08b
ingress.kubernetes.io/url-map: k8s-um-default-kusanagi-ingress--49f84941322af08b
ingress.kubernetes.io/forwarding-rule: k8s-fw-default-kusanagi-ingress--49f84941322af08b
ingress.kubernetes.io/target-proxy: k8s-tp-default-kusanagi-ingress--49f84941322af08b
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"gce","kubernetes.io/ingress.global-static-ip-name":"kusanagi-ip","kubernetes.io/tls-acme":"true"},"name":"kusanagi-ingress","namespace":"default"},"spec":{"backend":{"serviceName":"kusanagi-nodeport","servicePort":80},"tls":[{"secretName":"cert-kusanagi-tls"}]}}
kubernetes.io/ingress.class: gce
kubernetes.io/ingress.global-static-ip-name: kusanagi-ip
Events: <none>
KUSANAGI 初期設定
ブラウザから http://kusanagi.example.com
にアクセスし、次の手順で初期設定を行います。
※初期設定の際、HTTPSでアクセスすると CSS が読み込まれない為、初期設定の間は HTTP でアクセスします。
データベース名
、ユーザー名
、パスワード
、データベースのホスト名
に Cloud SQL の接続情報を入力して**送信
**をクリックします。
※注
データベースのホスト名
には localhost
がデフォルトで表示されていますが、127.0.0.1
を入力してください。
kusanagi-php7
のコンテナ内からは、名前解決ができないようです。(調査中)
各項目に任意の情報を入力して**WordPress をインストール
**をクリックします。
**※ パスワードは必ず控えておきましょう。
ログインページが表示されたら、一旦ブラウザを閉じます。
WordPress 設定ファイルの変更
このまま HTTPS でアクセスすると、CSS や JS が読み込まれない為、WordPress 設定ファイル(wp-config.php
)に細工をします。
コンテナにログイン
#Pod 名確認
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
kusanagi-XXXXXXXXXX-XXXXX 3/3 Running 0 29m
# Pod 内のコンテナ kusanagi-nginx へログイン
$ kubectl exec -it kusanagi-XXXXXXXXXX-XXXXX -c kusanagi-nginx /bin/bash
WordPress 設定ファイル編集
wp-config.php
をエディタで開きます。
# vi /home/kusanagi/kusanagi_html/DocumentRoot/wp-config.php
次の追加(ここから)
~追加(ここまで)
間の5行を、該当箇所に追加して保存します。
※実際にはマルチバイト(日本語)部分が文字化けしていますが、構わず追加しましょう。
・・・
#define('FORCE_SSL_ADMIN', true);
#define('WP_CACHE', true);
/* 追加(ここから) */
if($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
$_SERVER['HTTPS'] = 'on';
$_ENV['HTTPS'] = 'on';
}
define('FS_METHOD', 'direct');
/* 追加(ここまで) */
/* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */
・・・
HTTPS 確認
ブラウザを開き、https://kusanagi.wxample.com/wp-admin
へアクセスします。
ユーザー名
とパスワード
を入力して**ログイン
**をクリックします。
ダッシュボードが表示され、KUSANAGI を利用できる状態になります。
Tips
HSTS ※2018.09.27 追記
Cloud CloadBalancer は HTTP接続を HTTPS へリダイレクトする機能を持たないため、次のような方法でリダイレクトを実装する必要があります。
- Cloud LoadBalancer の代わりに Nginx Controller (Helm Chart) を利用する。(
ssl-redirect
パラメータ) - KUSANAGI Nginx 側でリダイレクトする。
今回は比較的手軽な後者の方法を利用することにします。
ヘルスチェック設定
Cloud LoadBalancer からのヘルスチェックに対し、HTTP ステータスコード 200 を返すよう設定します。
Pod 内の kusanagi-nginx
コンテナへログインし、次のような /etc/nginx/confd/default.conf
を作成します。
server {
listen 80 default_server;
access_log off;
error_log /var/log/nginx/error.log warn;
location / {
add_header Content-Type "text/plain";
return 200 "ok";
}
}
リダイレクト設定
HTTP アクセスされた場合、HTTPS の URL へリダイレクトさせるよう、/etc/nginx/conf.d/<KUSANAGI の PROFILE名>_http.conf
を編集します。
...
access_log ...;
error_log ...;
# TLS Redirect
if ($http_x_forwarded_proto != https) { #追加
return 301 https://$host$request_uri; #追加
} #追加
...
Nginx リロード
kill
コマンドで設定をリロードします。
# kill -HUP `cat /run/nginx.pid`