Istio-IngressGateway + cert-managerでGKEのHTTPS対応をさくっと実現する
この記事では、Istio-IngressGateway + cert-manager を用いた HTTPS 対応時の設定手順やハマりポイントをまとめています。
今回の検証にあたっては、下記のようなモチベーションがありました。
- Istio で遊ぶ環境が欲しかった
- GKE での HTTPS 対応の設定(クラスタ外からの接続、cert-manager を利用した証明書管理)も一通り確認したかった
- (GKE Cluster の作成の検証をしていたのでついでに)
同時期に下記記事の検証も行っていたので、リンク先から辿れる GKE Cluster 作成用の tf ファイル群を利用してベースとなる GKE Cluster の作成が可能です。
また、今回は下記の前提をもとに進めます。
- 検証時に利用するドメインは Google Domains で取得しています
- Istio-IngressGateway は
type: NodePort
に変更しています(Cloud Load Balancing(CLB)の費用節約のため)
GKEの作成
各コンポーネントを Deploy する GKE の Cluster を作成します。
下記のようなスペックでクラスタを作成しています。
- Node マシンタイプ:
e2-medium
- Node 数: 3
- Node Pool:
Preemtible VMs
- Network Tag:
istio
- 無効化したアドオン:
Istio
,HTTP load balancing
,Kubernetes Engine Monitoring
今回はGKE アドオンとして提供されている Istio は無効化した上で、手動で Istio のセットアップを行います。
各種アドオンの無効化に関しては、GCP の公式ガイドがあるのでそちらを参考にしてみて下さい。
また、Istio の Setup を想定して、下記のような変更も加えています。
(変更点の詳細は下記のリンクを参考にして下さい。)
- Firewall Rule の作成(
allow - tcp:10250,tcp:443,tcp:15017
) - Network Policy のアドオン有効化
ドメイン取得 + Cloudflare DNSの設定
HTTPS 対応の準備のために、ドメインの取得と Cloudflare DNS の設定を行います。
最初に、任意のレジストラを利用してドメインは取得します。
(Google Domains での取得方法は下記のリンクを参考にして下さい。)
次に、Cloudflare DNS 側の設定作業を行います。
アカウントを事前に作成した上で、アカウントのホーム画面から「Add a site」をクリックしてドメインを追加します。
その上で、レジストラ側で指定している DNS サーバリストを Cloudflare DNS のサーバに指定し直します。
Cloudflare Syncのセットアップ
今回の GKE クラスタは、Node として Preemtible VMs
を利用しています。
Preemtible VMs
は最長持続時間が 24 時間となっており、シャットダウン後には Node の Public IP も変わるため A レコードの更新(同期)が必要になります。
IP の同期には、kubernetes-Cloudflare-sync を利用します。
この Custom Controller によって、自身で管理しているドメイン名から GKE Nodes の外部 IP を引ける状態が維持されます。
// CloudflareのAPI keysをSecretsとして登録する
kubectl create secret generic cloudflare \
--from-literal=email=<email> \
--from-literal=api-key=<api_key>
// Service Accountの設定
kubectl create clusterrolebinding cluster-admin-binding --clusterrole cluster-admin --user <email>
kubectl apply -f ./sync-sa.yaml
// ImageのBuild + Push
docker build -t gcr.io/<gcp_project_id>/kubernetes-cloudflare-sync:latest .
docker push gcr.io/<gcp_project_id>/kubernetes-cloudflare-sync:latest
// Deploymentの作成([Configure]のsample deployment)
kubectl apply -f ./sync-deployment.yaml
Istio(IngressGateway)のセットアップ
ここまでの準備が完了したら、Istio-IngressGateway のセットアップを行います。
手順は、 getting-started
の内容と同じです。
IngressGateway の Manifest 内で type: LoadBalancer
→ type: NodePort
に変更するため、profile は demo
を指定した上で Manifest を generate します。
// demo Profile用のManifestのディレクトリを用意しておく
$ mkdir temp-profile
<dir>
|
|- istio-1.8.1
|
|- temp-profile
// Istioのdownload
$ curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.8.1 TARGET_ARCH=x86_64 sh -
$ cd istio-1.8.1
$ ./bin/istioctl manifest generate --set profile=demo >> ./../temp/istio-demo-profile.yaml
IngressGateway の Manifest 内で type: LoadBalancer
→ type: NodePort
に変更した上で修正した Manifest を Apply します。
---
apiVersion: v1
kind: Service
metadata:
annotations: null
labels:
app: istio-ingressgateway
install.operator.istio.io/owning-resource: unknown
istio: ingressgateway
istio.io/rev: default
operator.istio.io/component: IngressGateways
release: istio
name: istio-ingressgateway
namespace: istio-system
...
selector:
app: istio-ingressgateway
istio: ingressgateway
// NodePortに変更する
type: NodePort
---
$ kubectl apply -f ./../temp/istio-demo-profile.yaml
$ kubectl label namespace default istio-injection=enabled
$ kubectl get svc --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 3h38m
istio-system istio-egressgateway ClusterIP 10.1.0.190 <none> 80/TCP,443/TCP,15443/TCP 72s
istio-system istio-ingressgateway NodePort 10.1.1.172 <none> 15021:32308/TCP,80:31232/TCP,443:30791/TCP,31400:32322/TCP,15443:32480/TCP 71s
istio-system istiod ClusterIP 10.1.1.23 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 70s
kube-system calico-typha ClusterIP 10.1.3.58 <none> 5473/TCP 173m
kube-system kube-dns ClusterIP 10.1.0.10 <none> 53/UDP,53/TCP 3h38m
kube-system metrics-server ClusterIP 10.1.0.54 <none> 443/TCP 3h38m
$ kubectl describe node | grep ExternalIP
ExternalIP: <node1_ip>
ExternalIP: <node2_ip>
ExternalIP: <node3_ip>
次に、Istio-IngressGateway 作成時に割り当てられた GKE Node の port に対する通信を Allow する Firewall Rule を作成して、NodePort で外部から疎通できるようにします。
(必要に応じて、GKE Node 用の Network Tag を付与して下さい。)
// 15021:32308/TCP,80:31232/TCP,443:30791/TCP,31400:32322/TCP,15443:32480/TCP の例であれば
// <http_port_number> → 31232, <https_port_number> → 30791となる
gcloud compute firewall-rules create allow-conn-from-nodeport \
--network=default \
--project <gcp_project_id> \
--target-tags istio \
--allow tcp:<http_port_number>,<https_port_number>
HTTP 通信はこの状態でも可能になっているはずです。
httpbin などの sample deployment を Deploy した上で、疎通確認をしてみて下さい。
cert-managerのセットアップ
今回は、HTTPS に対応するために、追加で cert-manager を利用した証明書発行と Istio 側のルーティング設定を行います。
$ export CM_VERSION="v1.1.0"
$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/$CM_VERSION/cert-manager.yaml
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
...
mutatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
$ kubectl get pods --namespace cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-64887fb9d6-2hjxv 1/1 Running 0 103s
cert-manager-cainjector-99977ff45-tdw7s 1/1 Running 0 104s
cert-manager-webhook-64c5d4c9db-29b2c 1/1 Running 0 103s
cert-manager の Deploy が完了したら、下記のドキュメントに従って Cloudflare 用の DNS-01 チャレンジを行うための Issuer/ClusterIssuer を作成します。
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: istio-system
type: Opaque
stringData:
api-token: <token>
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: cloudflare-prod
namespace: istio-system
spec:
acme:
email: <email>
privateKeySecretRef:
name: cloudflare-account-key
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
key: api-token
name: cloudflare-api-token-secret
email: <email>
Issuer/ClusterIssuer を作成したら、Certificate を作成します。
Certificate 作成時の注意点としては、istio-ingressgateway の Deployment と同じ Namespace(istio-system
)で作成する必要があります。
(今回の検証では、Issuer, Certificate などの cert-manager 関連の resource は istio-system
で作成しています。)
The Certificate should be created in the same namespace as the istio-ingressgateway deployment.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: <domain>
namespace: istio-system
spec:
commonName: <domain>
dnsNames:
- <domain>
issuerRef:
kind: Issuer
name: cloudflare-prod
secretName: <secret_name>
Certificate の Manifest 内で指定した Secret Name で Secret が正常に作成できたかを確認します。
$ kubectl get secret -n istio-system
...
<secret_name> Opaque 1 21s
次に、払い出した Domain、作成した Certificate(Secret)を Manifest 内で指定した上で gateway, vservice のリソースを作成します。
ここで作成するリソースは下記のような役割を担います。
-
gateway
: リクエストを受ける istio-ingessgateway(Deployment)の設定を行うためのリソース(port, protocol, cert) -
vservice
: トラフィックのルーティング先のルールを設定するためのリソース
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
# secret name of certificate
credentialName: <secret_name>
# This should match a DNS name in the Certificate
hosts:
- <domain>
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- <domain>
gateways:
- httpbin-gateway
http:
- match:
- uri:
prefix: /status
- uri:
prefix: /delay
route:
- destination:
port:
number: 8000
# The name of a service from the service registry.
host: httpbin
これによって、クラスタ外からのリクエストを Backend の Pod にルーティングできるようになります。
作成した virtual resources を利用して、受けた HTTP(S)リクエストに対して下記のように処理を行っています。
- gateway 側で host, port, protocol などの条件を確認する
- (LB を利用していない場合は、Host Header を付与する)
- vservice 側で定義されている URL パターンのルールを見て、ルーティング先を決定する(ex.
/status
)
Istio-IngressGateway のルーティングの流れは下記の Blog を参考にしてみて下さい。
HTTPSでの接続検証
全てのセットアップが完了したら、接続の確認を行います。
リクエストのルーティング先の Deployment として、httpbin を Deploy しておきます。
$ cd istio-1.8.1
$ kubectl apply -f samples/httpbin/httpbin.yaml
ドメイン名と Istio-IngressGateway に払い出されている HTTPS 用の port を確認して、下記のように curl コマンドを実行します。
200 OK
が返ってくれば、正常に設定できていることが確認できます。
$ curl -s -I -HHost:<domain> "https://<domain>:<port_number>/status/200"
HTTP/2 200
server: istio-envoy
date: Fri, 01 Jan 2021 13:35:47 GMT
content-type: text/html; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
content-length: 0
x-envoy-upstream-service-time: 1
終わりに
Istio-IngressGateway + cert-manager を用いた HTTPS 対応時の設定手順についてまとめました。
Istio 自体の検証もこれから進めていこうと思います。
Discussion