Google Cloud Platformの無料トライアルをはじめよう

はじめに

前回まではAzureを利用してKubernetesやLogicアプリなどを試してきた。今度はGoogle Cloud Platformの無料トライアルにて同じようにKubernetesを使ったりpub/subメッセージしたり、RestやEE8サービスを色々と試していこうと思う。まずは、以下のようにインターネット上からhttps://~でブラウザでアクセスできるところまでを作ろうと思う。

無料トライアルの開始

GCPの無料トライアルを利用するには、まずはgoogleアカウントが必要になります。アカウントを作成したらサインインをした上で無料トライアルの開始案内に従い2ステップで完了します。完了するには、クレジットカードの登録が必要になりますのでカードの準備が必要です。

利用規約と最新情報の通知にチェックをつけて「続行」をクリックします。

お客様様情報を入力したあとはクレジットカードを登録する必要があります。登録したら「住所は同じ」にチェックをつけて「無料トライアルを開始」をクリックします。

クレジットカードを登録したので、「無料枠を超えて勝手に引き落とされたら困るなぁ…」と思ったら、、、

こんなメッセージが出てきました。勝手に課金されることはなさそうです、安心して検証出来そうです。

GCRにイメージを保存する

まずはWebサービスのイメージをレジストリに登録します。AzureにはAzure Container Registoryというレジストリがありましたがgoogleにも同じようなネーミングのGCR(Google Container Registory)があります。ここにイメージを保存でき必要に応じてpullしたり出来ます。

Dockerfileとソースの作成

せっかくなのでHello Worldを表示するだけでなく、ホスト名も表示するアプリにします。ブラウザでアクセスした時に今どのnodeで起動しているpodにアクセスしたのかを判別できるので。これをNode.jsで作ったサンプルアプリを以下に示します。

// モジュール
const express = require('express');

// Expressアプリケーション
const app = express();

// Hello World
const HOSTNAME = process.env.HOSTNAME;
app.get('/', (req, res) => res.send('Hello World! server=' + HOSTNAME));

// Listen
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`listening on ${port}`);
});

イメージを作成するにあたって特に変わったことをやっていませんが、簡単なコメントとともにDockerfileとpackage.jsonを載せておきます。

# 公式からimage取得する
FROM node:12.10.0-alpine

# アプリに渡したい環境変数
ENV PORT 3000
ENV NODE_ENV production

# アプリケーションディレクトリを作成する
WORKDIR /usr/local/app

# app.js と package.jsonのコピー
COPY . .  

# Nodeモジュールの追加
RUN npm install

# コンテナ開放ポート
EXPOSE 3000

CMD ["npm", "start"]
{
  "name": "hello",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "~4.16.1"
  }
}

イメージの作成と保存

次はイメージを作成します。先ほど作成したソースファイルとDokerfileのあるディレクトリで次のようにdockerコマンドを入力してイメージを作成します。イメージの保存先の形式はHOSTNAME/PROJECT_ID/IMAGE:TAGです。詳しくはマニュアル参照

  • HOSTNAME:gcr.io、us.gcr.io、eu.gcr.io、asia.gcr.ioと4つのロケーションを指定できますがここではアジアを指定しています。
  • PROJECT_ID:GCPのプロジェクトIDです
  • IMAGE:保存するコンテナのイメージ名です
  • TAG:イメージを識別するために付与するタグ名です
# イメージの作成
$ docker build -f Dockerfile --tag=asia.gcr.io/liquid-almanac-279508/sample:v1.1 .
Sending build context to Docker daemon  4.608kB
Step 1/8 : FROM node:12.10.0-alpine
12.10.0-alpine: Pulling from library/node
e7c96db7181b: Pull complete
95b3c812425e: Pull complete
778b81d0468f: Pull complete
28549a15ba3e: Pull complete
Digest: sha256:744b156ec2dca0ad8291f80f9093273d45eb85378b6290b2fbbada861cc3ed01
(略)
Successfully tagged asia.gcr.io/liquid-almanac-279508/sample:v1.1

# イメージの保存
$ docker push asia.gcr.io/liquid-almanac-279508/sample:v1.1
The push refers to repository [asia.gcr.io/liquid-almanac-279508/sample]
(略)

WebコンソールからContainer Registoryを確認すると、指定したパスにイメージが作成されていることが確認できます。

GKEを作成する

次にkubernetes環境を作成します。図の赤線の部分です。ホスト名を表示するサンプルアプリにしたのでノードも3つ作ってアクセスが分散されている様子を確認するようにします。

まずCloud Shellを起動します。最初にアクセスした時は初期化が必要になるので次のようにコマンドを入力します。東京の場合のリージョンはasia-northeast1になります。リージョンとゾーンの詳細はこちら

$ gcloud init
Welcome! This command will take you through the configuration of gcloud.
(略)
 [32] asia-northeast1-b
 [33] asia-northeast1-c
 [34] asia-northeast1-a
(略)
Did not print [24] options.
Too many options [74]. Enter "list" at prompt to print choices fully.
Please enter numeric choice or text value (must exactly match list
item):  34

以下のコマンドでクラスタを作成します。オプションでゾーンと1ゾーンあたりのノード数を指定します。

$ gcloud container clusters create gke --zone=asia-northeast1-a --num-nodes=3

しばらくすると、クラスタが出来上がります。Webコンソール上で入力しても作成出来ます。出来上がると以下のように緑のチェックと共にサイズやロケーションなどのクラスタ情報が表示されます。

Cloud shellからkubectlコマンドを入力すればクラスタのノード情報を表示でき3つのノードが起動していることが確認出来ます。

$ kubectl get nodes
NAME                                 STATUS   ROLES    AGE   VERSION
gke-gke-default-pool-9bd3c2e2-9inn   Ready    <none>   17m   v1.15.11-gke.15
gke-gke-default-pool-9bd3c2e2-mtlr   Ready    <none>   14m   v1.15.11-gke.15
gke-gke-default-pool-9bd3c2e2-umkn   Ready    <none>   12m   v1.15.11-gke.15

Webサービスを起動する

次にWebサービスを立ち上げます。図の赤線の部分です。先程コンテナレジストリに作成したイメージをもとに3つのpodを起動し、それを内部ロードバランサーで負荷分散させます。負荷分散は次で作成するIngressからのトラフィックを受信する想定です。

Node.jsを起動

それではコンテナレジストリからイメージをpullしてレプリカを3つ作るマニフェストを作成します。ポイントとしてはimageにて先程作成したイメージを指定しているところです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deployment
spec: # ======================== Deploymentのスペック =========================
  replicas: 3 # レプリカ数
  selector:
    matchLabels: # 検索条件
      app: web-nodejs
  template: # ================== テンプレート =================================
    metadata:
      labels:
        app: web-nodejs
        env: production
    spec:
      containers:
      - image: asia.gcr.io/liquid-almanac-279508/sample:v1.1 # コンテナイメージの場所
        name: nodejs-container   # コンテナ名
        ports:
        - containerPort: 3000   # ポート番号

作成したマニフェストを適用してWebサービスを起動します。

# マニフェストを適用します
$ kubectl apply -f web-deployment.yaml
deployment.apps/web-deployment configured

# ポッドが3つ起動していることを確認
$ kubectl get pod
NAME                              READY   STATUS    RESTARTS   AGE
web-deployment-579897794b-bt55m   1/1     Running   0          40m
web-deployment-579897794b-d9dkr   1/1     Running   0          19s
web-deployment-579897794b-jpp6f   1/1     Running   0          19s

# 各ノードにpodが起動していることを確認
$ kubectl get pod -o wide
NAME                              READY   STATUS    RESTARTS   AGE   IP          NODE                                 NOMINATED NODE   READINESS GATES
web-deployment-579897794b-bt55m   1/1     Running   0          41m   10.28.4.4   gke-gke-default-pool-9bd3c2e2-mtlr   <none>           <none>
web-deployment-579897794b-d9dkr   1/1     Running   0          66s   10.28.5.7   gke-gke-default-pool-9bd3c2e2-umkn   <none>           <none>
web-deployment-579897794b-jpp6f   1/1     Running   0          66s   10.28.3.7   gke-gke-default-pool-9bd3c2e2-9inn   <none>           <none>

内部ロードバランサーを起動

Webサービスの上位におく負荷分散のマニフェストを作成します。GKEで内部でバランスさせる場合はアノテーションでcloud.google.com/load-balancer-typeにInternalを指定します。詳しくはマニュアルを参照。

apiVersion: v1
kind: Service
metadata:
  name: internal-lb
  annotations:
    cloud.google.com/load-balancer-type: "Internal"
spec: # ======================== スペック =================================
  type: LoadBalancer
  ports:
    - port: 80         # 自身のポート番号
      targetPort: 3000 # 振り先のポート番号
      protocol: TCP
  selector: # =========== 接続先(POD)の条件 =============================
    app: web-nodejs

マニフェストを適用して負荷分散サービスを起動します。外側のIPアドレスが払い出されるまで数十秒待ちます。

# マニフェストを適用します
$ kubectl apply -f internal-lb-service.yaml
service/internal-lb created

# サービスの起動を確認(IP払出し中)
$ kubectl get svc
NAME          TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
internal-lb   LoadBalancer   10.31.246.69   <pending>     80:32562/TCP   8s

# サービスの起動を確認(IP付与済み)
$ kubectl get svc
NAME          TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
internal-lb   LoadBalancer   10.31.246.69   10.146.0.6   80:32562/TCP   100s

負荷分散の外側のIPアドレスをcurlで叩いて、トラフィックがバランスされていることを確認します。

# curlが使えるpodを一時的に立ち上げます
$ kubectl run -it curl --image=alpine --restart=Never --rm ash
If you don't see a command prompt, try pressing enter.
/ # apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/community/x86_64/APKINDEX.tar.gz
v3.12.0-45-g0e4d4e3558 [http://dl-cdn.alpinelinux.org/alpine/v3.12/main]
v3.12.0-52-ge766baa626 [http://dl-cdn.alpinelinux.org/alpine/v3.12/community]
OK: 12732 distinct packages available
/ # apk add curl
(1/4) Installing ca-certificates (20191127-r3)
(2/4) Installing nghttp2-libs (1.41.0-r0)
(3/4) Installing libcurl (7.69.1-r0)
(4/4) Installing curl (7.69.1-r0)
Executing busybox-1.31.1-r16.trigger
Executing ca-certificates-20191127-r3.trigger
OK: 7 MiB in 18 packages

# 何度が負荷分散の外側のIPアドレス10.146.0.6にアクセスしてみると、、、
/ # curl http://10.146.0.6
Hello World! server=web-deployment-579897794b-bt55m/ #
/ # curl http://10.146.0.6
Hello World! server=web-deployment-579897794b-d9dkr/ #
/ # curl http://10.146.0.6
Hello World! server=web-deployment-579897794b-jpp6f/ #
/ # curl http://10.146.0.6
Hello World! server=web-deployment-579897794b-jpp6f/ #

# と、3つのpodにアクセスしていることが確認できる

何度か叩くと、異なるpodにアクセスしていることが確認できるはずだ。次はIngressだ。

Ingressのインストール

前回Azureの時と同様に、Helmと呼ばれるkubernetesのパッケージマネージャを利用してIngressをインストールする。

Azureの時はhelmのバージョンが3であったがGCPはバージョン2のようである。

$ helm version
Client: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}

バージョン3の場合はtillerサービスアカウントの追加などの作業が不要であったが、バージョン2では以下のように必要となる。

# tillerサービスアカウントの作成
$ kubectl create serviceaccount tiller --namespace kube-system

# アクセス権を付与
$ kubectl create clusterrolebinding tiller-binding --clusterrole=cluster-admin --serviceaccount=kube-system:tiller

# Tillerをインストール
$ helm init --upgrade --service-account tiller

次にhelmを使ってingressをインストールするのだが、その前にIngressに付与されるIPを予約する。(この後、https化する際にDNSのAレコードを固定化したいので)

静的予約したIPアドレスを使ってIngressをインストールします。

$ helm install stable/nginx-ingress --name nginx-ingress --namespace kube-system --set rbac.create=true --set controller.publishService.enabled=true --set controller.service.loadBalancerIP="<予約したアドレス(34.84.203.181)>"

Ingressのインストールを確認します。

# ingressがデプロイされていることを確認
$ helm list
NAME            REVISION        UPDATED                         STATUS          CHART                   APP VERSION     NAMESPACE
nginx-ingress   1               Mon Jun  8 19:01:17 2020        DEPLOYED        nginx-ingress-1.39.1    0.32.0          kube-system

# 外部アドレスが予約したIPアドレスになっていることを確認
$ kubectl get svc --namespace kube-system
NAME                            TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
default-http-backend            NodePort       10.31.254.4     <none>          80:31875/TCP                 2d5h
nginx-ingress-controller        LoadBalancer   10.31.242.183   34.84.203.181   80:31670/TCP,443:31442/TCP   27h
nginx-ingress-default-backend   ClusterIP      10.31.242.121   <none>          80/TCP                       27h
tiller-deploy                   ClusterIP      10.31.242.87    <none>          44134/TCP                    47h

Ingressのマニフェスト(https化は今はまだしない)を作成します。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress
  annotations:
    kubernetes.io/ingress.class: nginx
spec: # ======================== スペック =================================
  rules:
  - host: gcp.kimkaz.info
    http:
      paths:
      - backend:                    # パス指定なし(すべて)
          serviceName: internal-lb  # 振り先
          servicePort: 80

マニフェストを適用して、インターネットからhttp://gcp.kimkaz.infoでアクセスできることを確認する。

HTTPS化(SSL証明書の自動更新)

最後にhttpsでアクセスできるようにする。Kubernetesにはcert-managerと呼ばれるSSL証明書の管理パッケージがあるのでこれを利用する。

Cert-Managerのインストール

kubernetesのマニュアルのとおりマニフェストを適用していくのだが、kubernetesのバージョンが1.15より低かったので先にあげておく。(最初にクラスタいれたときに最新化していなかった)

GCPのコンソールから「Kubernetes Engine」「クラスタ」を選択して「アップグレード」をクリックする。すると、数分でバージョンが上がる。

注意書きにあるように、GKEの場合はマニフェスト適用に先立って以下を実行しておく。これを実行していないとアクセス権の関連で失敗することがあるらしい。

$ kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

cert-managerのマニフェストを適用する。

$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.15.1/cert-manager.yaml
customresourcedefinition.apiextensions.k8s.io/issuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/orders.acme.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
(略)
mutatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created

cert-manager関連のpodが3つ上がっていることを確認する。

$ kubectl get pod --namespace cert-manager
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-57f89dbdf6-gs88q              1/1     Running   0          2m15s
cert-manager-cainjector-6c78fb8b77-hd5hx   1/1     Running   0          2m15s
cert-manager-webhook-5567d8d596-ttrlz      1/1     Running   0          2m15s

ClusterIssuer、Certificateの適用

証明書の発行にあたり身元を示す発行者、ClusterIssuerリソースを作成します。メールアドレスを自身のアドレスに変えてマニフェストを適用します。

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <メールアドレス>
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
        ingress:
          class: nginx
$ kubectl apply -f cluster-issuer.yaml

続いて、証明書リソースを作成して適用します。

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: gcp.kimkaz.info-tls
  namespace: default
spec:
  secretName: gcp.kimkaz.info-tls # 証明書のSecret名
  commonName: gcp.kimkaz.info # TLS化対象のドメイン
  dnsNames:
  - gcp.kimkaz.info
  issuerRef:
    name: letsencrypt
    kind: ClusterIssuer # defaultはIssuer
$ kubectl apply -f certificate.yaml

IngressでTLS終端

ドメインgcp.kimkaz.infoに先ほど指定したSSL証明書を適用するようIngressリソースを修正して適用します。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
spec: # ======================== スペック =================================
  tls:
  - hosts:
    - gcp.kimkaz.info
    secretName: gcp.kimkaz.info-tls
  rules:
  - host: gcp.kimkaz.info
    http:
      paths:
      - backend:                    # パス指定なし(すべて)
          serviceName: internal-lb  # 振り先
          servicePort: 80
$ kubectl apply -f ingress.yaml

しばらくすると証明書が発行されSSLで通信ができるようになります。

# READYステイタスがtrueになっていることを確認
$ kubectl get certificate
NAME                  READY   SECRET                AGE
gcp.kimkaz.info-tls   True    gcp.kimkaz.info-tls   54m

$ kubectl describe certificate
Name:         gcp.kimkaz.info-tls
Namespace:    default
Labels:       <none>
Annotations:  API Version:  cert-manager.io/v1alpha3
Kind:         Certificate
(略)
Status:
  Conditions:
    Last Transition Time:  2020-06-08T13:00:03Z
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2020-09-06T12:00:01Z
Events:
  Type    Reason        Age    From          Message
  ----    ------        ----   ----          -------
  Normal  GeneratedKey  53m    cert-manager  Generated a new private key
  Normal  Requested     53m    cert-manager  Created new CertificateRequest resource "gcp.kimkaz.info-4292219798"
  Normal  Issued        3m55s  cert-manager  Certificate issued successfully

ブラウザからもhttpsでサンプル画面が表示されることを確認。

一度Azureのkubernetes環境を構築しているので、GCPでも同様の要領で作成することができた。あえて違い、手こずった箇所をあげるとすると、Ingressのインストールのところ(tillerサービスアカウントを追加)とCert-Manager(helmでなくマニフェストを適用)のところ。

今後はビルドの自動化、CI/CDサービスあたりを調べて試していきたい。

タイトルとURLをコピーしました