【GCP】GCE/GKEを利用したWebサーバーを公開するまで
概要
GCP 上で Web サーバーを公開するまでにやったことを忘れないようにメモしておく。
Web サーバーは、
- アプリケーション(Node.js)
- DB (Postgresql)
で構築される。
やることまとめ
- アプリケーションをローカルで開発する
- アプリケーションをコンテナ化しておく
- ローカルで DB を立てる(on docker)
- ローカルで DB + アプリケーションを dockerize し動作を確認する(docker-compose)
- GCE で DB を構築する
- GKE でアプリケーションをデプロイする
- アプリケーションから DB を接続するためにファイアウォールを設定する
- GKE のサービスにドメインを設定する
詳細
1. アプリケーションをローカルで開発する
いつも通り node.js のアプリケーションを開発する。
npm install npm run build npm run start
とさえすれば動くようにしておく。
今回は graphql を利用したので、 tsc で dist ファイルに build する際に、
tsc && cp src/schema.graphql dist
として、schema.graphql もコピーした。
そうじゃないと以下のような実行時エラーが出る。
Error: Unable to find any GraphQL type definitions for the following pointers: - /Users/xxx/sample-server/dist/schema.graphql at prepareResult (/Users/xxx/sample-server/node_modules/@graphql-tools/load/index.cjs.js:591:15) at loadTypedefsSync (/Users/...
2. アプリケーションをコンテナ化しておく
のちに GKE でコンテナとして動かしたいので、 Dockerfile を用意しておく。
FROM node:14 WORKDIR /usr/src/app COPY package*.json ./ RUN npm install COPY . . RUN npx prisma generate RUN npm run build EXPOSE 4000 CMD [ "npm", "run", "start" ]
今回アプリケーションに prisma を利用したため、
prisma generate
を行って node_modules 配下に types を配置しないと build が通らない。
こんな感じのエラーが出る。
> [6/6] RUN npm run build: #10 4.414 #10 4.414 > sample-server@1.0.0 build /usr/src/app #10 4.414 > tsc && cp src/schema.graphql dist/src #10 4.414 #10 21.51 src/resolvers/Query/index.ts(13,25): error TS7006: Parameter 'item' implicitly has an 'any' type.
2. ローカルで DB を立てる(on docker)
あとでアプリケーションをのせる前提で、postgres だけ立てておく。
version: "3" services: postgres: image: postgres:latest environment: POSTGRES_USER: dev POSTGRES_PASSWORD: xxx POSTGRES_DB: xxx volumes: - ./pgsql-tmp:/var/lib/postgresql/data ports: - 15432:5432
localhost:15432
で接続を確認しておく。
3. ローカルで DB + アプリケーションを dockerize し動作を確認する(docker-compose)
docker-compose.yaml にアプリケーション (app) を追加する。
app: build: context: . dockerfile: ./Dockerfile ports: - 4000:4000 environment: DATABASE_URL: "postgresql://dev:xxx@postgres:5432/sample?schema=public" depends_on: - postgres
docker-compose up -d --build
とし、localhost:4000/graphql
で
Playground が見られることを確認しておく。
4. GCE で DB を構築する
GCP には新たな Project を作っておく。 この Project で GCE / GKE を使う。
Cloud SQL は有料しかないので、無料枠で楽しむために DB は GCE で立てる。
以下の記事を参考にすれば GCE 上に簡単に Postgresql を立てることができる。
Set up PostgreSQL on Compute Engine | Google Cloud Platform Community
5. GKE でアプリケーションをデプロイする
5.1 コンテナを GCR にあげる
まずアプリケーションの Docker コンテナを build し、 GCR (コンテナレジストリ)にアップロードする必要がある。
docker build -f Dockerfile -t gcr.io/${projectID}/sample-server:v1 . docker push gcr.io/${projectID}/sample-server:v1
うまくいけば、 GCP の自分のプロジェクトの Container Registry であげた image を確認できる。
5.2 secret で環境変数を用意
ここから GKE になる。 あらかじめ Kubernetes クラスタ を新規で作成しておく。 ゾーンを GCE と揃えておかないと DB にアクセスできないので注意する。
kubectl を対象のクラスタ(sample-cluster)で使えるようにするため、以下コマンドを実行する。
gcloud container clusters get-credentials sample-cluster --zone us-central1
prisma は環境変数で DATABASE_URL を指定するようになっている。
ローカルで開発する際は、 .env
で管理するようになっている環境変数を、
k8s の service 実行時に指定する必要がある。
ただし DB のパスワードも含まれるため、これを k8s の secret で管理するようにする。
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: database_url: XXX
base64 で指定するため、
echo "postgresql://postgres:xxx@10.128.0.1:5432/sample?schema=public" | base64
のようにして得られた base64 の値を指定する。 IP は GCE の VM の内部 IP を指定する。
secret を k8s に apply する。
kubectl apply -f k8s/secret.yaml
5.3 deployment/service をデプロイ
deployment と service を示す yaml を書いて、 apply する。
kubectl apply -f k8s/deployment.yaml kubectl apply -f k8s/service.yaml
作成したファイルは以下。
apiVersion: apps/v1 kind: Deployment metadata: name: sample-server labels: app: sample-server spec: replicas: 1 selector: matchLabels: app: sample-server template: metadata: labels: app: sample-server spec: containers: - name: sample-server image: gcr.io/sample/sample-server:v1 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: mysecret key: database_url ports: - containerPort: 4000 resources: requests: cpu: 200m
環境変数は上で作った mysecret から取得する。
kind: Service apiVersion: v1 metadata: name: sample-server-service spec: type: LoadBalancer selector: app: sample-server ports: - protocol: TCP port: 80 targetPort: 4000
kubectl get service
で得られる EXTERNAL-IP の外部 IP を使い、
XX.XX.XX.XX/graphql
などで、Playground が見られることを確認する。
$ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE sample-server-service LoadBalancer 10.128.0.1 XX.XX.XX.XX 80:30174/TCP 56m kubernetes ClusterIP 10.128.0.1 <none> 443/TCP 60m
おそらくこの時点では実際にクエリをリクエストしても DB にアクセスできない旨のエラーが表示される。
6. アプリケーションから DB を接続するためにファイアウォールを設定する
GKE のアプリケーションから GCE の DB にアクセスを許可するために少し設定が必要になる。
GCE に ssh で入り、 pg_hba.conf
に Pod のアドレス範囲を許可する様に指定しておく。
Kubernetes クラスタ の詳細から、 ポッドのアドレス範囲 10.0.0.0/17
はわかる。
host all all [POD_ADDRESS] md5
GKE のクラスタを作成すると、 自動的にファイアウォールが作成されていた。
gke-sample-cluster-xxxxxxx-all
これをみると、ソースフィルタ の IP 範囲がクラスタの Pod のアドレス範囲になっているためこれを適用するようにする。
ターゲットタグの gke-sample-cluster-xxxxxxx-node
のようなタグをコピーし、
GCE の VM インスタンスのネットワーク タグに貼り付ける。
これによりファイアウォール側のコンソールに、インスタンスとして Postgres の VM が表示され、実際に IP ベースで Graphql にアクセス可能になっている。
7. GKE のサービスにドメインを設定する
静的 IP を取得する。
gcloud compute addresses create sample-server-ip --region us-central1
以下のコマンドで取得した IP を確認できる。
gcloud compute addresses describe sample-server-ip --region us-central1
service.yaml に上記で確認した IP を以下のように追加する。
kind: Service ... targetPort: 4000 + loadBalancerIP: "XX.XX.XX.XX"
apply する。
kubectl apply -f k8s/service.yaml
確認する。
kubectl get service # EXTERNAL-IP を確認できる curl http://XX.XX.XX.XX/
これにより固定化された IP で Web サービスにアクセスできる。
今回は、ドメインはもともと取得していたもののサブドメインに追加する。 Vercel で DNS の設定をしていたため、 Vercel の管理画面からサブドメインを IP 設定する A レコードを追加する。
api A XX.XX.XX.XX 60
これで http://api.sample.com/ のような形でアクセスできるようになる。
まとめ
とりあえず GKE / GCE で Web サーバーを動かすところまでやってみた。 ファイアウォール周り、cloud build 、terraform 、 https 化などもやってみたいがまた今度。