kubernetesにて複数ドメインかつhttps接続を用いるWebサービスのクラスタを構築したのでその手順をメモしておきます。なお、環境はGoogle Kubernetes Engine、作業はCloud Shell上にて行っています。
目次
達成出来ること
- 一つのIPアドレスで複数のドメインを管理し、かつ各々がhttps接続可能な状態にする
- TLS証明書の自動取得、及び自動更新を行う
Kubernetes クラスタの作成
GCPのWebUIよりKubernetes クラスタの作成を行います。
「+ クラスタを作成」→「標準クラスタ」を選択
任意のクラスタ名、リージョン or ゾーンを設定します。
マスターのバージョンはデフォルトで「1.9.7-gke.7」が選択されていますが、こちらを最新のバージョンに変更します。
※執筆時最新バージョン「1.11.2-gke15」
アプリケーションの動作に必要な処理能力を持つノードプールを作成します。
ここではクラスタのワーカーノードの個数を指定します。
Google Kubernetes Engine ではマスターノードの存在は無視(隠蔽)されるため、純粋に必要なワーカーノードのみを準備すればOKです。
画像の例ではゾーン毎に1ノード、計3個のワーカーノードを指定しています。
設定が完了したら、画面下部の「作成」を押下して完了です。
クラスタの作成には数分かかるため、完了するまで待機します。
Cloud Shell からクラスタに接続
Cloud Shell を起動し、作成したクラスタへ接続します。
まず、作成したクラスタの右にある「接続」を押下します。
Cloud Shell が起動するのと同時に、クラスタへの接続コマンドが入力された状態になるので、そのままエンターを押せば接続が完了します。
試しに下記コマンドを入力して、正常にクラスタへ接続出来ているか確認します。
kubectl get nodes
作成したノードが全て取得出来れば正常です。
クラスタにHelm をインストール
HelmはKubernetes のパッケージマネージャーです。
※後ほどこれを用いてクラスタにcert-manager をインストールします。
こちらを参考にHelm をインストールします。
Helm のインストール手順
- 公式Github のリリースページより最新のバイナリを取得します。
- パッケージを解凍します。
tar -zxvf helm-v2.11.0-linux-amd64.tar.gz
- バイナリをパスが通った場所へ移動します。
sudo mv linux-amd64/helm /usr/local/bin/helm
※参考link
Helm はKubernetesクラスタ上にTiller
と呼ばれるサービス群を必要とするため、そのインストールを行います。
ネームスペースkube-system
にtiller
という名前のサービスアカウントを作成
kubectl create serviceaccount -n kube-system tiller
作成したサービスアカウント(tiller) にcluster-admin
の権限を与える
kubectl create clusterrolebinding tiller-binding \
--clusterrole=cluster-admin \
--serviceaccount kube-system:tiller
Tiller をクラスタへインストール
helm init --service-account tiller
Tiller のPod 起動後、リポジトリをアップデート
helm repo update
クラスタにcert-manager をインストール
前項でインストールしたHelm を利用してcert-manager をインストールします。
helm install --name cert-manager --version v0.5.0 \
--namespace kube-system stable/cert-manager
Let‘s Encrypt のセットアップ
環境変数にEmail をセットします。
export EMAIL=test@example.com
※証明書の有効期限通知等を受け取れるため、実在するメールアドレスが望ましいです。
ClusterIssuer を作成します。
curl -sSL https://rawgit.com/ahmetb/gke-letsencrypt/master/yaml/letsencrypt-issuer.yaml | \
sed -e "s/email: ''/email: $EMAIL/g" | \
kubectl apply -f-
アプリケーションのDeployment、Service を作成
今回はサンプルとして2つのDeployment、Service を準備しました。
# deployment-service.yaml
# Site 1
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: site1-deployment
spec:
replicas: 3
selector:
matchLabels:
run: site1-replica
template:
metadata:
labels:
run: site1-replica
spec:
restartPolicy: Always
containers:
- image: httpd
name: site1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: site1-service
spec:
type: NodePort
ports:
- port: 8001
targetPort: 80
protocol: TCP
selector:
run: site1-replica
---
# Site2
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: site2-deployment
spec:
replicas: 3
selector:
matchLabels:
run: site2-replica
template:
metadata:
labels:
run: site2-replica
spec:
restartPolicy: Always
containers:
- image: httpd
name: site2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: site2-service
spec:
type: NodePort
ports:
- port: 8002
targetPort: 80
protocol: TCP
selector:
run: site2-replica
作成します。
kubectl apply -f deployment-service.yaml
固定グローバルIPを取得し、Ingress を作成
固定IPを取得します。
gcloud compute addresses create example-ingress-ip --global
取得した固定IPは次のコマンドで確認出来ます。
gcloud compute addresses list --global
TLS通信を行わない形でIngress を作成します。
# ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
kubernetes.io/ingress.global-static-ip-name: example-ingress-ip
spec:
rules:
- host: site1.example.com
http:
paths:
- path: /*
backend:
serviceName: site1-service
servicePort: 8001
- host: site2.example.com
http:
paths:
- path: /*
backend:
serviceName: site2-service
servicePort: 8002
※参考
作成します。
kubectl apply -f ingress.yaml
証明書の取得
証明書取得のためのマニフェストファイルを作成します。
今回の例ではsite1.example.com
、site2.example.com
の2つのドメインの証明書を取得したいため、2つのマニフェストを記載しています。
# certificate.yaml
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: cert-site1
spec:
secretName: cert-site1
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: site1.example.com
dnsNames:
- site1.example.com
acme:
config:
- http01:
ingress: example-ingress
domains:
- site1.example.com
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: cert-site2
spec:
secretName: cert-site2
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: site2.example.com
dnsNames:
- site2.example.com
acme:
config:
- http01:
ingress: example-ingress
domains:
- site2.example.com
マニフェストファイルを適用します。
kubectl apply -f certificate.yaml
この時マニフェストは、Ingress の設定にLet’s Encrypt 認証用の一時的なルーティングを動的に追加し、認証を通過させます。
※詳細なフローはこちらに掲載されています。
証明書の取得には数分から数十分かかるため、完了するまで待機します。
証明書の取得が完了すると、指定したsecretName
にてSecret
が作成されます。
※今回はcert-site1
及びcert-site2
証明書の取得完了後、次のコマンドにて作成されたSecret を確認することが出来ます。
kubectl get secret
このマニフェストの設定によって、証明書の自動更新も行われるようになります。
取得した証明書をIngress に設定
先程作成したIngress に対して作成した証明書を設定します。
ingress.yaml
を次のように拡張します。
# ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
kubernetes.io/ingress.global-static-ip-name: example-ingress-ip
spec:
rules:
- host: site1.example.com
http:
paths:
- path: /*
backend:
serviceName: site1-service
servicePort: 8001
- host: site2.example.com
http:
paths:
- path: /*
backend:
serviceName: site2-service
servicePort: 8002
# GLBC supports SNI, after ver 1.1. Recommend to select GKE master ver 1.11.2-gke.9 and later.
tls:
- secretName: cert-site1
hosts:
- site1.example.com
- secretName: cert-site2
hosts:
- site2.example.com
適用します。
kubectl apply -f ingress.yaml
Ingress の設定が適用されるまで数分から数十分かかるため待機します。
ドメインの向き先を取得した固定IPに設定
最後にsite1.example.com
、site2.example.com
の向き先を取得した固定グローバルIPに設定すれば完了です。
この段階でhttps://site1.example.com
、https://site2.example.com
への接続が可能となります。
おわりに
Kubernetes を用いることで得られるメリットは様々ありますが、その中の一つとしてスケーラビリティがあります。Helm、cert-manager を用いることで、高速かつ容易にTLS対応も行えるため、マイクロサービス時代のディファクトスタンダードとして、今後の強力な選択肢となりそうです。