GKEでの外部公開に対してCloud CDNを割り当てる

GKEでの外部公開に対してCloud CDNを割り当てる

November 28, 2020
Cloud, Kubernetes
Cloud CDN, GCP, kubernetes

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 のキャッシュヒット率も上がっています
image

一度削除する

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 をつける

image

CDN を作っていきます。

送信元を追加を選び、k8s で作った LB を選択します。 image

動作確認

こちらも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 を使用できますが、両方は使用できません。