GKEでの外部公開に対してCloud CDNを割り当てる
November 28, 2020
Google Kubernetes Engine で外部公開した Pod に対して CDN を割り当てるのを調べました。
Load Balancer まで作成後に手動で Cloud CDN を割り当てるのかと思ってたのですが、Backend Config というのもありそうだったのでどちらも試してみました。
事前に GKE クラスタの構築
一般公開クラスタで作成しておきます。作成後に接続コマンドを表示して、Cloud Shell から kubectl を打てるようにします。
以後は Cloud Shell で操作していきます。
Backend Config での CDN 作成
ドキュメントを参考にサンプルのアプリケーション Deployment を作成、外部 Ingress, CDN で公開してみます
Namespace の作成
今回の確認用に namsspace を切ります。
$ kubectl create namespace cdn-how-to
Deployment を作成
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: cdn-how-to
name: my-deployment
spec:
selector:
matchLabels:
purpose: demonstrate-cdn
replicas: 2
template:
metadata:
labels:
purpose: demonstrate-cdn
spec:
containers:
- name: echo-amd64
image: gcr.io/google-samples/hello-app-cdn:1.0
これをdeploy.yml
というファイル名で保存してデプロイします
$ kubectl apply -f deploy.yml
$ kubectl get po -n cdn-how-to
NAME READY STATUS RESTARTS AGE
my-deployment-7fbf967d78-8s2r6 1/1 Running 0 97s
my-deployment-7fbf967d78-dsrdj 1/1 Running 0 97s
Service を作成
apiVersion: v1
kind: Service
metadata:
namespace: cdn-how-to
name: my-service
labels:
purpose: demonstrate-cdn
# ここで、Backend Configを使うように指定している.
annotations:
cloud.google.com/backend-config: '{"ports": {"80":"my-backendconfig"}}'
spec:
type: NodePort
selector:
purpose: demonstrate-cdn
ports:
- port: 80
protocol: TCP
targetPort: 8080
これをservice.yml
というファイル名で保存してデプロイします
$ kubectl apply -f service.yml
$ kubectl get svc -n cdn-how-to
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service NodePort 10.0.22.158 <none> 80:30605/TCP 43s
グローバル IP を取得しておく
次の Ingress で作成される LB に割り当てる静的 IP を作成しておきます。
$ gcloud compute addresses create cdn-how-to-address --global
Ingress を作成
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
namespace: cdn-how-to
name: my-ingress
annotations:
kubernetes.io/ingress.global-static-ip-name: "cdn-how-to-address"
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: my-service
servicePort: 80
これをingress.yml
というファイル名で保存してデプロイします
$ kubectl apply -f ingress.yml
$ kubectl get ingress -n cdn-how-to
NAME CLASS HOSTS ADDRESS PORTS AGE
my-ingress <none> * 34.120.43.135 80 4m35s
動作確認
実際に CDN が動作するまでは、10 分ほど待つ必要があります。
作成しておいた静的 IP にアクセスしてみます。
$ curl -v http://34.120.43.135/?cache=true
* Expire in 0 ms for 6 (transfer 0x558977458fb0)
* Trying 34.120.43.135...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x558977458fb0)
* Connected to 34.120.43.135 (34.120.43.135) port 80 (#0)
> GET /?cache=true HTTP/1.1
> Host: 34.120.43.135
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sat, 28 Nov 2020 13:59:46 GMT
< Content-Length: 70
< Content-Type: text/plain; charset=utf-8
< Via: 1.1 google
< Age: 851
< Cache-Control: max-age=86400,public
<
Hello, world!
Version: 1.0.0
Hostname: my-deployment-7fbf967d78-4srsl
レスポンスヘッダーにAge: xxx
が付いていれば、CDN が使われています。
実際に CDN のキャッシュヒット率も上がっています
一度削除する
kubectl delete -f ingress.yml
kubectl delete -f service.yml
kubectl delete -f backend.yml
CDN が消えるまで少し時間かかります。エラーが返るまで待ちましょう
$ curl -v http://34.120.43.135/?cache=true
* Expire in 0 ms for 6 (transfer 0x561b7d63ffb0)
* Trying 34.120.43.135...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x561b7d63ffb0)
* Connected to 34.120.43.135 (34.120.43.135) port 80 (#0)
> GET /?cache=true HTTP/1.1
> Host: 34.120.43.135
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Content-Type: text/html; charset=UTF-8
< Referrer-Policy: no-referrer
< Content-Length: 1561
< Date: Sat, 28 Nov 2020 14:36:29 GMT
<
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 404 (Not Found)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:20
5px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.
png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_colo
r_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
</style>
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>404.</b> <ins>That’s an error.</ins>
<p>The requested URL <code>/</code> was not found on this server. <ins>That’s all we know.</ins>
* Connection #0 to host 34.120.43.135 left intact
Backend Config を利用せずに手動で CDN を割り当てる
service.yml を変更する
Backend Config を利用しないように変更します。
apiVersion: v1
kind: Service
metadata:
namespace: cdn-how-to
name: my-service
labels:
purpose: demonstrate-cdn
# ここで、Backend Configを使うように指定している.
- annotations:
- cloud.google.com/backend-config: '{"ports": {"80":"my-backendconfig"}}'
spec:
type: NodePort
selector:
purpose: demonstrate-cdn
ports:
- port: 80
protocol: TCP
targetPort: 8080
保存してデプロイします
$ kubectl apply -f service.yml
$ kubectl get svc -n cdn-how-to
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service NodePort 10.0.22.158 <none> 80:30605/TCP 43s
Ingress もデプロイしておきます
$ kubectl apply -f ingress.yml
$ kubectl get ingress -n cdn-how-to
NAME CLASS HOSTS ADDRESS PORTS AGE
my-ingress <none> * 34.120.43.135 80 1m35s
今度は CDN ないので、レスポンスヘッダーにAge: xxx
がついてないです。
$ curl -v http://34.120.43.135/?cache=true
* Expire in 0 ms for 6 (transfer 0x55b0f2ff9fb0)
* Trying 34.120.43.135...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x55b0f2ff9fb0)
* Connected to 34.120.43.135 (34.120.43.135) port 80 (#0)
> GET /?cache=true HTTP/1.1
> Host: 34.120.43.135
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: max-age=86400,public
< Date: Sat, 28 Nov 2020 14:44:38 GMT
< Content-Length: 70
< Content-Type: text/plain; charset=utf-8
< Via: 1.1 google
<
Hello, world!
Version: 1.0.0
Hostname: my-deployment-7fbf967d78-w4tcz
* Connection #0 to host 34.120.43.135 left intact
手動で CDN をつける
CDN を作っていきます。
送信元を追加を選び、k8s で作った LB を選択します。
動作確認
こちらもAge: xxx
がついた
$ curl -v http://34.120.43.135/?cache=true
* Expire in 0 ms for 6 (transfer 0x55dbb6953fb0)
* Trying 34.120.43.135...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x55dbb6953fb0)
* Connected to 34.120.43.135 (34.120.43.135) port 80 (#0)
> GET /?cache=true HTTP/1.1
> Host: 34.120.43.135
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sat, 28 Nov 2020 14:51:51 GMT
< Content-Length: 70
< Content-Type: text/plain; charset=utf-8
< Via: 1.1 google
< Age: 37
< Cache-Control: max-age=86400,public
<
Hello, world!
Version: 1.0.0
Hostname: my-deployment-7fbf967d78-w4tcz
* Connection #0 to host 34.120.43.135 left intact
まとめ
とりあえず動くところまで、細かい設定とかする場合は手動で作成の方がいいのか?
Backend Config で設定できる項目は下記とのこと
https://cloud.google.com/kubernetes-engine/docs/how-to/ingress-features?hl=ja#cloud_cdn
apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
name: my-backendconfig
spec:
cdn:
enabled: cdnEnabled
cachePolicy:
includeHost: includeHost
includeProtocol: includeProtocol
includeQueryString: includeQueryString
queryStringBlacklist: queryStringBlacklist
queryStringWhitelist: queryStringWhitelist
cdnEnabled: true に設定すると、この Ingress バックエンドの Cloud CDN が有効になります。 includeHost: true に設定すると、異なるホストへのリクエストは個別にキャッシュされます。 includeProtocol: true に設定すると、HTTP リクエストと HTTPS リクエストは個別にキャッシュされます。 includeQueryString: true に設定すると、queryStringBlacklist または queryStringWhitelist に従ってクエリ文字列パラメータがキャッシュキーに含まれます。どちらも設定されていない場合は、クエリ文字列全体が含まれます。false に設定すると、クエリ文字列全体がキャッシュキーから除外されます。 queryStringBlacklist: キャッシュキーから除外するクエリ文字列パラメータの名前を含む文字列配列を指定します。これ以外のパラメータはすべて含まれます。queryStringBlacklist または queryStringWhitelist を指定できますが、両方は指定できません。 queryStringWhitelist: キャッシュキーに含めるクエリ文字列パラメータの名前を含む文字列配列を指定します。これ以外のパラメータはすべて除外されます。queryStringBlacklist または queryStringWhitelist を使用できますが、両方は使用できません。