GAE to Kubernetesの道のり

# はじめに

こんにちわ、すえけん（[sueken5](https://twitter.com/Sueken51)）です。

今回の記事ではGCPのサービスであるGAEからGKEに移行するまでの道のりでやったことや詰まったことを書きます。

# 前提

- GAE/go 1.11
- datastoreを利用
- taskqueueを利用
- gaeのcronjobを利用
- cloudschedulerを利用

# 移行に至った理由

移行に至った理由はGAEはとても素晴らしいサービスではあったのですがgolangのバージョンが1.11で固定されており、1.12にあげるにはappengineが提供していたライブラリではなく、標準のgcpライブラリを使う必要があったためです。

全てのライブラリを標準に変えてしまうのであればGAEと並列で動かしていたGKEに乗せてしまおうという話になりました。

## datastore

https://godoc.org/google.golang.org/appengine

GAE1.11までは上記のappengine製のdatastoreライブラリを使っていましたが次のdatastore標準のライブラリに修正する必要がありました。

https://godoc.org/cloud.google.com/go/datastore

変更する点は以下の2つでした。

- 標準ライブラリを使うように修正
- [aetest](google.golang.org/appengine/aetest)を使ってテストしている部分をdatastoreエミュレータを使ってテストするように修正

datastoreの修正はただ単にコードを修正するだけだったので簡単に終わりました。

## taskqueue

https://cloud.google.com/appengine/docs/standard/python/taskqueue/

taskqueueはgae環境から利用できるキューです。これはGKEから利用できない？っぽそうだったのでPub/Subに修正しました。

https://cloud.google.com/pubsub/docs/overview?hl=ja

## GAE/cronjob & cloud scheduler

cronjobはgae環境でcron.yamlを使って設定していたスケジューラです。これはkubernetesのcronjobに全て置き換えました。

cloud schdulerで行なっていたタスクも全てkubernetesのcronjobに置き換えました。

# トラフィックを移行しよう

ここまでのミドルウェアはコードを修正したりすればよかったので難易度は高くありません。

問題はトラフィックの移行です。これがとても厄介でした。

## GKEのManagedCertがIngressのIPをDNSレコードに登録済みじゃないとうまく動かない

今回GKEに移行するに当たってSSL証明書の点で問題が起こりました。GKEのManagedCertがIngressのIPをDNSレコードに登録済みじゃないとうまく動かなかったのです。

なので今回はセルフ証明書をいい感じに使っていい感じに証明書を差し替えていく作戦を使いました。作戦の手順は次の通りです。

```
1. lets encryptを使って手元にSSL証明書を作成する
2. GAEのカスタムドメインのSSL証明書を手元で作った証明書に変更する
3. Ingressに手元で作った証明書を登録する
4. IngressのIPをDNSレコードに登録する
5. GAEのIPのDNSレコードを削除する
6. IngressにManagedCertを登録する
7. ManagedCertで作った証明書に置き換える
8. 手元で作った証明書を削除する
```

## 1. lets encryptを使って手元にSSL証明書を作成する

今回はMacを使ってこの作業を行なっています。あらかじめご了承ください。

```
brew install letsencrypt
```

まずcertbotという証明書を作成するためのcliが必要なのでこれをインストールします

```
sudo certbot certonly --manual --preferred-challenges dns -d example.com
```

次に証明書を作っていきます。

この記事では詳しくは説明しませんが今回はdns認証を使います。このコマンドを実行すると以下のようなメッセージが表示されます。

```
Please deploy a DNS TXT record under the name
_acme-challenge.example.com with the following value:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Before continuing, verify the record is deployed.
```

やることはEnterを押す前に`_acme-challenge.example.com`にTXTレコードとして`xxxxxxx`の文字列を登録しておきます。

登録が終わったら`nslookup -q=txt _acme-challenge.example.com`コマンドを実行してちゃんとTXTレコードが登録されているか確認します。

確認できたらEnterを押して認証チャレンジします。

```
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/example.com/privkey.pem
   Your cert will expire on 2020-04-27. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
```

問題なければ上記のようなメッセージが表示されて`/etc/letsencrypt/live/example.com/`に必要なファイルが提供されます。

## 2. GAEのカスタムドメインのSSL証明書を手元で作った証明書に変更する

![スクリーンショット 2020-01-28 16.23.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263369/d401ed29-dac0-e3b3-fa84-81de51fb61e9.png)

GAEにSSL証明書をアップロードします。アップロードはGAE/設定のSSL証明書タブの新しい証明書をアップロードを押します。

![スクリーンショット 2020-01-28 16.24.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263369/f9bcd3ac-dfd5-12e2-1312-438ba501456a.png)

PEMでエンコードされたX.509公開鍵証明書には

```
sudo cat /etc/letsencrypt/live/example.com/fullchain.pem
```
で表示されたテキストを入れます。

復号化されたPEMでエンコードされたRSA秘密鍵は

```
sudo openssl rsa -inform pem -in /etc/letsencrypt/live/example.com/privkey.pem -outform pem | less
```

で表示されたテキストを入れます。

あとはexample.comにこのSSL証明書をマッピングすればSSL証明書の変更が完了です。

## 3. Ingressに手元で作った証明書を登録する

次にIngressに手元で作った証明書を登録します。

まずはsecretを使って証明書をkubernetesに登録します

```
sudo kubectl create secret tls tls-example-com \
  --key /etc/letsencrypt/live/example.com/privkey.pem \
  --cert /etc/letsencrypt/live/example.com/fullchain.pem
```

次にIngressでこのsecretを利用するようにします。

```
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gateway
  annotations:
    kubernetes.io/ingress.global-static-ip-name: gateway
spec:
  tls:
    - secretName: tls-example-com # ここが大事
  backend:
    serviceName: gateway
    servicePort: 8080
```

これで１、2分経つと証明書をIngressが利用しだします。

問題ないか確認するために`/etc/hosts`に`{ingressのip} example.com`を書き込んで通信がうまくいくか確認しておきましょう。

## 4. IngressのIPをDNSレコードに登録する

これはDNSレコードをいじるだけです。

## 5. GAEのIPのDNSレコードを削除する

これもDNSレコードをいじるだけです。

## 6. IngressにManagedCertを登録する

```
apiVersion: networking.gke.io/v1beta1
kind: ManagedCertificate
metadata:
  name: example-com-cert
spec:
  domains:
    - example.com
```

```
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gateway
  annotations:
    kubernetes.io/ingress.global-static-ip-name: gateway
    networking.gke.io/managed-certificates: example.com # 追記
spec:
  tls:
    - secretName: tls-example-com # ここが大事
  backend:
    serviceName: gateway
    servicePort: 8080
```

これで20分から30分くらい待つとManagedCertがActiveになります。

## 7. ManagedCertで作った証明書に置き換える

```
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gateway
  annotations:
    kubernetes.io/ingress.global-static-ip-name: gateway
    networking.gke.io/managed-certificates: example.com # 追記
spec:
  # tls:
  #   - secretName: tls-example-com # ここをコメントアウト
  backend:
    serviceName: gateway
    servicePort: 8080
```

これで置き換え完了です。

## 8. 手元で作った証明書を削除する

最後に自分で作った証明書を削除します。

```
sudo certbot revoke --cert-name example.com
```

これで完了です。

# まとめ

GAEからGKEへの移行は意外と大変でした。ただ証明書にちょっと詳しくなれたのでよかったです。