【GCP】GAEからGKEへHTTPアクセスできた動作確認メモ

# はじめに

GCP (Google Cloud Platform)を使ったお仕事をさせていただいているおじさんです。
GAE (Google Application Engine)からGKE(Google Kubernetes Engine)にアクセスできたので、興奮冷めやらぬ？うちにメモを残しておきます。

# 動作確認メモ

作戦は以下の通りです。行う実装・設定は

(1) Google の解説にある [内部 TCP / UDP ロードバランサの使用]
(https://cloud.google.com/kubernetes-engine/docs/how-to/internal-load-balancing) に従って、Kubernetes Cluster に外部IPを用意する
(2) 用意した外部IPにアクセスするための「サーバレスVPCコネクタ」を用意する
(3) GAEアプリケーションが用意した「サーバレスVPCコネクタ」を使えるようにする

用意したGAEがロードバランサを参照すると、GKEのポッドが返事をするのを確認します。

## Cluster の外側にIPアドレスを設定（Load Balancer)

Kubernetes は Cluster の中でcontainer の管理を行います。その中にnode があり、ここにdeploy したり、container の数を増やしたり減らしたりします。一方で、service と呼ぶ仮想的なIPパケット通信を行うためのルータやスイッチのような役割を果たすものがあり、LoadBalancer も service の一つになります。

ここでは、Google の解説そのままですが、なぞった時のメモです。Cluster はひとつasia-northeast1 に作ってあり、そこで作業しています。

### サンプルimage をデプロイ

以下のマニフェストを```kubectl apply -f my-deployment.yaml```します。このサンプルGoogle の解説にあるものそのままです。

```yaml:my-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-app
spec:
  selector:
    matchLabels:
      app: hello
  replicas: 3
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: "gcr.io/google-samples/hello-app:2.0"
```


確かにpod ができていることを確認します。

```shell

$ kubectl get deployments
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
hello-app   3/3     3            3           94s
$ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
hello-app-7f46745f74-24ldc   1/1     Running   0          82s
hello-app-7f46745f74-2xl25   1/1     Running   0          83s
hello-app-7f46745f74-z4hzc   1/1     Running   0          82s
```

### Load Balancer の作成

つづいて、service (load balancer)を作ります。以下のマニフェストを ```kubectl apply -f my-service.yaml``` します。

```yaml:my-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: ilb-service
  annotations:
    cloud.google.com/load-balancer-type: "Internal"
  labels:
    app: hello
spec:
  type: LoadBalancer
  selector:
    app: hello
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
```

service が作られていることを確認します。EXTERNL-IPが割り当てられていることを確認します。

```shell

$ kubectl get service ilb-service
NAME          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
ilb-service   LoadBalancer   10.79.247.115   10.146.0.9    80:32456/TCP   3m14s

$ gcloud compute forwarding-rules list --filter="loadBalancingScheme=INTERNAL"
NAME                              REGION           IP_ADDRESS
IP_PROTOCOL  TARGET
a91aceace48744715a2391849b778aeb  asia-northeast1  10.146.0.9  TCP     asia-northeast1/backendServices/a91aceace48744715a2391849b778aeb

$ gcloud compute backend-services describe a91aceace48744715a2391849b778aeb --region asia-northeast1
...（略）
```

## サーバレスVPCコネクタ

ここでは、コンソール(GCPのWEBブラウザからの操作)で行いました。「VPCネットワーク」から「サーバレスVPCアクセス」を選択します。
ここでは[]()に従い、深いことは何も考えず、default を選択して、以下のように作りました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294040/1baaf295-987c-30ae-49ff-65b9d129ef49.png)

ここでネットワークというのがよくわかりませんでした。これはこのプロジェクト？で使われているdefault のネットワークのようで、以下のようなzone に対応したアドレスの総体のようでした。今回使用しているのは、asia-northeast1 なのですが、このアドレス空間(subnet)は10.146.0.0/20 で、さきほど作成したClustger の LoadBalancer は 10.146.0.9 がEXTERNAL IPとして割り振られていました。というわｋで、何となく、GCPのあぷりから 10.146.0.9 にアクセスできそうな気がしてきます。

一方で、「サーバレス」なGAEはまた別のネットワークにいるのかもしれませんが、理解できていないことを先に書いておきます。

## GAEからVPCコネクタを経由してCluster の Load Balancer にアクセスする

書くと長いですが、サンプルはごく簡単です。これもGoogle のサンプルをを少し修正しただけです。

ありものを修正して使います。コードに直書きです。同じディレクトリに３つのファイルを用意します。

```
├── app.yaml
├── main.py
└── requirements.txt
```

### VPC コネクタの利用

app.yamlに２行記述します。

```yaml:app.yaml
runtime: python37
vpc_access_connector:
  name: projects/PROJECT_ID/locations/asia-northeast1/connectors/CONNECTEOR_NAME
```

### 接続テスト用flask サーバ

ずばり、HTTP GET が来たら、先ほどのCluster のEXTERNAL IPにHTTP リクエストを出して、その結果を返す、というものです。

```python:main.py

from flask import Flask
import requests

app = Flask(__name__)

@app.route('/')
def hello():
    try:
        r = requests.get('http://10.146.0.9:80/')
        print(r)
        print(r.status_code)
        print(r.headers)
        print(r.content)
        return "Hello World! response: (OK!)" + r.content.decode('utf-8')
    except Exception as e:
        return str(e)

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8080, debug=True)
```

Python3.8でrequests==2.25.1 としたらエラーが出たので、適当に変更しています。

```requirements.txt
Flask==1.1.2
requests>=2.22.0
```

これらが置いてあるディレクトリで 

```
gcloud app deploy 
```

します。


## 動作確認

### ブラウザ

いざ、Chromeに https://PROJECT_ID.an.r.appspot.com/ を入れてみると、、、(GAE のサービス名が default のままの場合）

Hello World! response: (OK!)Hello, world! Version: 2.0.0 Hostname:
hello-app-7f46745f74-24ldc

と無事に表示されました。

### GAEのログ

GAEのログでもHTTP 200を確認できます。

```
$ gcloud app logs tail -s default
...
2021-01-10 15:27:31 default[20210111t002221]  "GET / HTTP/1.1" 200
2021-01-10 15:27:31 default[20210111t002221]  <Response [200]>
2021-01-10 15:27:31 default[20210111t002221]  200
2021-01-10 15:27:31 default[20210111t002221]  {'Date': 'Sun, 10 Jan 2021 15:27:31 GMT', 'Content-Length': '66', 'Content-Type':'text/plain; charset=utf-8'}
2021-01-10 15:27:31 default[20210111t002221]  b'Hello, world!\nVersion: 2.0.0\nHostname: hello-app-7f46745f74-2xl25\n'
```

良かった。。。本当に、良かった。

# おわりに

GAEからGKEとHTTP通信できることを確認しました。

- Kubernetes Cluster にService(load balancer)を用意すること
- GAEはサーバレスVPCコネクタに接続すること

がポイントのようでした。とりあえず動きましたが、VPCやアドレス空間をまだよく理解できていません。だれがルータなのか。。


## 情報源

もとは、この記事を参考にすれば簡単にできるだろう、と思っていました。

https://zenn.dev/yka2n/articles/39729a6ab386b590f10b

しかし、実際に自分で動かそうとテストしようとしても、できません。テスト用のプログラムが良くないのか、設定が良くないのか切り分けができず、数日を使ってしまいました。結果として、設定が良くなかったです。理由は以下のように考えています。

- 自分が作ったdeployment に対して用意したservice が適応されなかった。用意したservice のマニフェストのselector などでdeployment を参照するようになっていなかったのでは。
- また、この例では「限定公開のクラスタ」ということなので、独自に作ったネットワークをりようしていましたが、自分はdefault のものを利用していたので、azia-northeast1 にある10.146.....が外部ＩＰになりましたが、サンプルでは、192.168... となっていました。これ自体は問題にはならないと思いますが。

以下も何度も？眺めました。

- GCPの解説：[内部TCP/UDPロードバランサの使用](https://cloud.google.com/kubernetes-engine/docs/how-to/internal-load-balancing) .... これは今回フォローしたものです。
- GCPの解説：[GKEのサービスでLoadBalancer について](https://cloud.google.com/kubernetes-engine/docs/concepts/service#services_of_type_loadbalancer)
- GCPの解説：[限定公開クラスタの作成](https://cloud.google.com/kubernetes-engine/docs/how-to/private-clusters)
- GCPの解説：[サービス](https://cloud.google.com/kubernetes-engine/docs/concepts/service) ... GKEの。
- Kubernetesの解説：[サービスとアプリケーションの接続](https://kubernetes.io/ja/docs/concepts/services-networking/connect-applications-service/)
  - こちらもLoad Balancer の設定についてサンプルを交えた解説になっていて良かったです。service についてDNS、HTTPSにする方法もあります。
  - Kuberentes の解説：[Creating External Load Balancer](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/)

### TODO

とりあえず動いたレベルなので、もう少ししくみをよく理解したい。。。

- Service の Load Balancer のEXTERNAL IPはどこで決められるのか。これ、CI/CDにくこむときは、毎回このservice.yaml も実行することになるのかな。
- private のネットワークにクラスタを作っている記事があった。何故だろう。
- サーバレスVPCコネクタの役割、とGAEが属するネットワークについての情報。

あと、きじによると、Cloud DNSでCluseter のExternal IPを登録すると名前でアクセスできるようになる、というがあるので、それも合わせて試してみたい。

真夜中だぞ、、、体に悪いぞ、、、寝よう。
(2021/01/11)