Redis と PHP を使用したゲストブックの作成

このチュートリアルでは、Google Container Engine を使用して簡単な多層ウェブ アプリケーション(ゲストブック)を作成する手順を説明します。訪問者はゲストブックを使用してログにテキストを入力し、最後のいくつかのエントリを表示できます。

チュートリアルでは外部 IP 上でウェブサービスを設定し、複製された Redis ノードを基盤とする一連の複製されたサーバーへの負荷分散を行う方法を示します。例では、複製された PHP フロントエンドと、Redis マスター、およびストレージ用の Redis ワーカーを組み合わせます。

例では Container Engine のいくつもの重要な概念に焦点を当てます。具体的には、ポッドを定義する方法、一連の複製されたポッドを定義する方法、選択にラベルを使用する方法、および内部専用のサービスや外部に公開するサービスを設定する方法を示します。

事前準備

始める前に

開始方法の手順に沿って gcloud コマンドライン インターフェースのインストールや更新などを行い、Container Engine 環境を設定します。

入力の手間を省くために、使用する Cloud プロジェクトと Google Compute Engine ゾーンを gcloud config のデフォルトに設定します。

$ gcloud config set project PROJECT_ID
$ gcloud config set compute/zone us-central1-b

設定ファイルのダウンロード

Kubernetes GitHub にアクセスし、このチュートリアルで使用する次の設定ファイルをダウンロードします。

  • frontend-controller.yaml
  • frontend-service.yaml
  • redis-master-controller.yaml
  • redis-master-service.yaml
  • redis-slave-controller.yaml
  • redis-slave-service.yaml

Container Engine クラスタの作成

最初に、ゲストブックを実行する Container Engine クラスタを作成します。

guestbook という名前のクラスタを作成します。

$ gcloud container clusters create guestbook

作成に成功すると次のように表示されます。

Creating cluster guestbook...done.
Created [https://container.googleapis.com/v1/projects/container-engine-docs/zones/us-central1-b/clusters/guestbook].
kubeconfig entry generated for guestbook.
NAME       ZONE           MASTER_VERSION  MASTER_IP        MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
guestbook  us-central1-b  1.2.0           x.x.x.x          n1-standard-1  1.2.0         3          RUNNING

コマンドライン インターフェースを使用して新しいクラスタを作成すると、そのクラスタが kubectl のデフォルトに設定されます。Cloud Platform Console や REST API を使用してクラスタを作成した場合は、開始方法の手順に沿って、kubectl から認証情報を利用できるようにする必要があります。

クラスタに関する情報の取得

プロジェクト内のクラスタを一覧表示することや、特定のクラスタの詳細を表示することができます。

$ gcloud container clusters list
$ gcloud container clusters describe guestbook

クラスタでのゲストブック アプリの実行

これでクラスタが稼働するようになり、ゲストブック アプリを起動する準備が整いました。

ステップ 1: Redis マスターの起動

最初に行うのは、Redis マスター用のポッドの起動です。ポッドは 1 つだけですが、レプリケーション コントローラを使用して作成します。コントローラは正常性を監視し、必要に応じてポッドを再起動するのに役立ちます。

redis-master-controller.yaml という設定ファイルを使います。ファイルの内容を見てみましょう。

 verbatim fbdef9cbfc812bb0d07acffc1c08dd48 apiVersion: v1
kind: ReplicationController
metadata:
  name: redis-master
  # these labels can be applied automatically
  # from the labels in the pod template if not set
  labels:
    app: redis
    role: master
    tier: backend
spec:
  # this replicas value is default
  # modify it according to your case
  replicas: 1
  # selector can be applied automatically
  # from the labels in the pod template if not set
  # selector:
  #   app: guestbook
  #   role: master
  #   tier: backend
  template:
    metadata:
      labels:
        app: redis
        role: master
        tier: backend
    spec:
      containers:
      - name: master
        image: gcr.io/google_containers/redis:e2e  # or just image: redis
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
 endverbatim fbdef9cbfc812bb0d07acffc1c08dd48 

ポッドの設定の詳細については、複数コンテナポッドの作成ポッド設定ファイルのセクションをご覧ください。

ポッドを起動します。

$ kubectl create -f redis-master-controller.yaml

マスターが実行されていることを確認します。

$ kubectl get pods
NAME                 READY     REASON    RESTARTS   AGE
redis-master-5y3pb   1/1       Running   0          36s

単一の Redis マスターポッドが表示されます。ポッドがインスタンスに配置され、ステータスが Pending から Running に変わるまでに 30 秒ほどかかることがあります。

寄り道: Docker コンテナの表示

ノードで実行されているすべてのコンテナを表示するには:

  1. kubectl get を使用して NODE 列に表示されるノードの名前を調べます。

    $ kubectl get pods -o wide
    NAME                 READY     STATUS    RESTARTS   AGE       NODE
    redis-master-5y3pb   1/1       Running   0          10s       gke-guestbook-21e9ba28-node-6rux
    
  2. ノードに SSH 接続します。NODE はステップ 1 で調べた値に置き換えてください。

    $ gcloud compute ssh NODE
    
  3. sudo docker ps コマンドを使用して、実行中の Docker コンテナを一覧表示します。

    $ sudo docker ps
    CONTAINER ID        IMAGE                                                                  COMMAND                  CREATED         STATUS              PORTS           NAMES
    1b831be9597e        redis                                                                  "/entrypoint.sh redis"   7 minutes ago   Up 7 minutes                        k8s_master.13295825_redis-master-mppin_default_2fdb428e-f51a-11e5-bea9-42010af00097_7c0f468d
    78b0d26c5116        gcr.io/google_containers/pause:2.0                                     "/pause"                 7 minutes ago   Up 7 minutes                        k8s_POD.475a00de_redis-master-mppin_default_2fdb428e-f51a-11e5-bea9-42010af00097_c38d9bed
    aec913779b56        gcr.io/google_containers/fluentd-gcp:1.18                              "/bin/sh -c '/usr/sbi"   7 minutes ago   Up 7 minutes                        k8s_fluentd-cloud-logging.fe59dd68_fluentd-cloud-logging-gke-guestbook-8f351893-node-q6p5_kube-system_da7e41ef0372c29c65a24b417b5dd69f_ba2199e5
    5064e1f8575b        gcr.io/google_containers/kube-proxy:6a3c7bde4710fbeb04cee371779c437b   "/bin/sh -c 'kube-pro"   7 minutes ago   Up 7 minutes                        k8s_kube-proxy.15d8f4cd_kube-proxy-gke-guestbook-8f351893-node-q6p5_kube-system_b2aa963d8ca9e70244b9825129edc238_eb99358e
    f99f6a84f966        gcr.io/google_containers/pause:2.0                                     "/pause"                 7 minutes ago   Up 7 minutes                        k8s_POD.6059dfa2_kube-proxy-gke-guestbook-8f351893-node-q6p5_kube-system_b2aa963d8ca9e70244b9825129edc238_82f3be3f
    091ee0220ecb        gcr.io/google_containers/pause:2.0                                     "/pause"                 7 minutes ago   Up 7 minutes                        k8s_POD.6059dfa2_fluentd-cloud-logging-gke-guestbook-8f351893-node-q6p5_kube-system_da7e41ef0372c29c65a24b417b5dd69f_c34c9688
    

Redis コンテナを含む、ポッド内のコンテナが表示されます。

特定のコンテナのログを表示するには、sudo docker logs CONTAINER_ID を実行します。たとえば、上記の出力の場合は次のように入力します。

$ sudo docker logs 1b831be9597e

docker ps -a を使用すると、現在実行中のコンテナだけでなく、すべてのコンテナが一覧表示されます。ポッドが正常に起動しない場合にこのコマンドが役立ちます。

ノードへの接続を解除します。

$ exit

ステップ 2: Redis マスターのサービスの起動

サービスとは、ポッドの論理セットとポッドへのアクセスに使われるポリシーの定義を抽象化したものです。実際上は、トラフィックを 1 つまたは複数のポッドにプロキシする名前付きロードバランサです。

サービスを設定するときに、プロキシ先のポッドをポッドのラベルに基づいて指定します。ステップ 1 で作成したポッドには name=redis-master というラベルが付いています。

ここではファイル redis-master-service.yaml を使用して Redis マスター用のサービスを作成します。

 verbatim 209895d557dbb075d0c4eb5fc9ee0f31 apiVersion: v1
kind: Service
metadata:
  name: redis-master
  labels:
    app: redis
    role: master
    tier: backend
spec:
  ports:
    # the port that this service should serve on
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
    role: master
    tier: backend
 endverbatim 209895d557dbb075d0c4eb5fc9ee0f31 

サービス設定の selector フィールドによって、サービスに送信されたトラフィックを受信するポッドが決まります。そのため、設定では name=redis-master というラベルが付いたポッドをサービスの送信先に指定しています。

Redis マスターのサービスを起動します。

$ kubectl create -f redis-master-service.yaml

$ kubectl get services redis-master
NAME           CLUSTER_IP       EXTERNAL_IP   PORT(S)    AGE
redis-master   10.191.245.161   <none>        6379/TCP   1m

ステップ 3: 複製された Redis ワーカーポッドの起動

Redis マスターは単一のポッドですが、Redis 読み取りワーカーは複製されたポッドとして設定します。レプリケーション コントローラによって、指定した数のポッドレプリカがどの時点でも確実に実行されます。設定ファイルを定義して、ポッドセットをどのような状態にしたいかを指定すると、レプリケーション コントローラがその状態を実現する働きをします。ポッドの数が多すぎる場合は、レプリケーション コントローラが一部を停止させます。ポッドやノードで障害が起きて、ポッドの数が少なすぎる場合は、追加のポッドを起動します。

レプリケーション コントローラの設定にはファイル redis-slave-controller.yaml を使います。

 verbatim 39e339db2a9b5a778fe149fcf36cbea7 apiVersion: v1
kind: ReplicationController
metadata:
  name: redis-slave
  # these labels can be applied automatically
  # from the labels in the pod template if not set
  labels:
    app: redis
    role: slave
    tier: backend
spec:
  # this replicas value is default
  # modify it according to your case
  replicas: 2
  # selector can be applied automatically
  # from the labels in the pod template if not set
  # selector:
  #   app: guestbook
  #   role: slave
  #   tier: backend
  template:
    metadata:
      labels:
        app: redis
        role: slave
        tier: backend
    spec:
      containers:
      - name: slave
        image: gcr.io/google_samples/gb-redisslave:v1
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
          # If your cluster config does not include a dns service, then to
          # instead access an environment variable to find the master
          # service's host, comment out the 'value: dns' line above, and
          # uncomment the line below.
          # value: env
        ports:
        - containerPort: 6379
 endverbatim 39e339db2a9b5a778fe149fcf36cbea7 

この設定では、レプリケーション コントローラで使用するポッド テンプレートを定義し、このポッドの 2 つのレプリカを維持することを指定しています。

Redis ワーカー レプリケーション コントローラを作成するには、次のコマンドを実行します。

$ kubectl create -f redis-slave-controller.yaml

$ kubectl get rc
NAME           DESIRED   CURRENT   AGE
redis-master   1         1         5m
redis-slave    2         2         35s

稼働したら、クラスタ内のポッドを一覧表示して、マスターとワーカーが実行されていることを確認できます。

$ kubectl get pods
NAME                READY     STATUS    RESTARTS   AGE
redis-master-gti95  1/1       Running   0          5m
redis-slave-lmux8   1/1       Running   0          44s
redis-slave-suafd   1/1       Running   0          44s

ステップ 4: Redis ワーカー サービスの作成

接続を Redis 読み取りワーカーにプロキシするサービスを設定します。検出に加えて、サービスは透過的な負荷分散もクライアントに提供します。

ワーカー用のサービス仕様は redis-slave-service.yaml にあります。

 verbatim 944d277bf55dbdd2aba030c61581c2ff apiVersion: v1
kind: Service
metadata:
  name: redis-slave
  labels:
    app: redis
    role: slave
    tier: backend
spec:
  ports:
    # the port that this service should serve on
  - port: 6379
  selector:
    app: redis
    role: slave
    tier: backend
 endverbatim 944d277bf55dbdd2aba030c61581c2ff 

今回はサービスの selector が name=redis-slave になっています。これは Redis ワーカーを実行中のポッドを示しています。また、ここで示すようにサービス自体にもラベルを設定しておくと、後から kubectl get services -l "label=value" コマンドで見つけやすくなって便利です。

サービスを作成します。

$ kubectl create -f redis-slave-service.yaml

クラスタのサービスを一覧表示すると、次のように表示されます。

$ kubectl get services
NAME           CLUSTER_IP       EXTERNAL_IP      PORT(S)    AGE
kubernetes     10.3.240.1       <none>           443/TCP    18h
redis-master   10.191.245.161   <none>           6379/TCP   4m
redis-slave    10.191.252.48    <none>           6379/TCP   2m

ステップ 5: ゲストブック ウェブサーバー ポッドの作成

これでゲストブックのバックエンドが稼働するようになったので、次はフロントエンドのウェブサーバーを起動します。

このチュートリアルでは簡単な PHP フロントエンドを使用します。フロントエンドは、要求が読み取りか書き込みかに応じて Redis ワーカー サービスまたはマスター サービスと通信するように設定されます。フロントエンドは簡単な JSON インターフェースを公開し、jQuery-Ajax ベースの UX を提供します。これは Redis 読み取りワーカーと同様に、レプリケーション コントローラによってインスタンス化された複製されたサービスです。

フロントエンドはクラスタ DNS を利用してサービス名(redis-master または redis-slave)の DNS ルックアップを実行し、Redis サービスのアドレスを参照していることに注意してください。すべての Container Engine クラスタで実行される kube-dns サービスが、クラスタ内で実行中のどのコンテナ内からも常に名前でサービスを解決できるようにしています。

コントローラとそのポッド テンプレートはファイル frontend-controller.yaml に記述されています。

 verbatim c7807a05d6cba2879121086b81db8c8a apiVersion: v1
kind: ReplicationController
metadata:
  name: frontend
  # these labels can be applied automatically
  # from the labels in the pod template if not set
  # labels:
    # app: guestbook
    # tier: frontend
spec:
  # this replicas value is default
  # modify it according to your case
  replicas: 3
  # selector can be applied automatically
  # from the labels in the pod template if not set
  # selector:
  #   app: guestbook
  #   tier: frontend
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v4
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
          # If your cluster config does not include a dns service, then to
          # instead access environment variables to find service host
          # info, comment out the 'value: dns' line above, and uncomment the
          # line below.
          # value: env
        ports:
        - containerPort: 80
 endverbatim c7807a05d6cba2879121086b81db8c8a 

ここではサーバーの 3 つのレプリカを指定しています。このファイルを使用して、次のコマンドでゲストブック サーバーを起動できます。

$ kubectl create -f frontend-controller.yaml

$ kubectl get rc
NAME           DESIRED   CURRENT   AGE
frontend       3         3         15s
redis-master   1         1         4m
redis-slave    2         2         2m

サーバーが稼働したら、クラスタ内のポッドを一覧表示して、すべてのポッドが実行されていることを確認できます。

$ kubectl get pods
NAME                 READY     STATUS    RESTARTS   AGE
frontend-29mtx       1/1       Running   0          1m
frontend-7x94n       1/1       Running   0          1m
frontend-egt6b       1/1       Running   0          1m
redis-master-gti95   1/1       Running   0          16m
redis-slave-lmux8    1/1       Running   0          3m
redis-slave-suafd    1/1       Running   0          3m

Redis マスターポッドが 1 個、Redis ワーカーが 2 個、フロントエンド ポッドが 3 個あることがわかります。

PHP サービスのコードは次のようになります。

 verbatim 3bb4cb84654883dbb3cf4b62b88fdc12 <?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

require 'Predis/Autoloader.php';

Predis\Autoloader::register();

if (isset($_GET['cmd']) === true) {
  $host = 'redis-master';
  if (getenv('GET_HOSTS_FROM') == 'env') {
    $host = getenv('REDIS_MASTER_SERVICE_HOST');
  }
  header('Content-Type: application/json');
  if ($_GET['cmd'] == 'set') {
    $client = new Predis\Client([
      'scheme' => 'tcp',
      'host'   => $host,
      'port'   => 6379,
    ]);

    $client->set($_GET['key'], $_GET['value']);
    print('{"message": "Updated"}');
  } else {
    $host = 'redis-slave';
    if (getenv('GET_HOSTS_FROM') == 'env') {
      $host = getenv('REDIS_SLAVE_SERVICE_HOST');
    }
    $client = new Predis\Client([
      'scheme' => 'tcp',
      'host'   => $host,
      'port'   => 6379,
    ]);

    $value = $client->get($_GET['key']);
    print('{"data": "' . $value . '"}');
  }
} else {
  phpinfo();
} ?>
 endverbatim 3bb4cb84654883dbb3cf4b62b88fdc12 

ステップ 6: 外部 IP を使用したゲストブック ウェブサービスの作成

他のポッドと同様に、ゲストブック サーバーポッドをグループ化するサービスが必要です。ただし、今回はサービスがユーザーの要求を処理するので、外部から見えるようにする必要がある点が異なります。つまり、クライアントがクラスタの外部からサービスを要求できるようにする必要があります。そのために、サービス設定で type: LoadBalancer フィールドを設定します。

ゲストブックのサービス仕様は frontend-service.yaml にあります。

 verbatim ade932d33a74002bab0780a3d5d0983c apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # if your cluster supports it, uncomment the following to automatically create
  # an external load-balanced IP for the frontend service.
  # type: LoadBalancer
  ports:
    # the port that this service should serve on
  - port: 80
  selector:
    app: guestbook
    tier: frontend
 endverbatim ade932d33a74002bab0780a3d5d0983c 

frontend-service.yaml の行 type: LoadBalancer のコメントを解除します。

サービスを起動します。

$ kubectl create -f frontend-service.yaml

$ kubectl get services
NAME           CLUSTER_IP       EXTERNAL_IP      PORT(S)    AGE
frontend       10.191.253.158   104.197.92.229   80/TCP     1m
kubernetes     10.191.240.1     <none>           443/TCP    18h
redis-master   10.191.245.161   <none>           6379/TCP   4m
redis-slave    10.191.252.48    <none>           6379/TCP   2m

ゲストブック サービスの使用

サービスにアクセスするには、設定したサービスの外部 IP を調べます。ロードバランサの作成には数分かかることがあります。作成後に、外部 IP が kubectl get services の出力の「IP(S)」列、および kubectl describe services の出力の LoadBalancer Ingress フィールドに表示されます。

$ kubectl get services frontend
NAME       CLUSTER_IP       EXTERNAL_IP      PORT(S)   SELECTOR        AGE
frontend   10.191.253.158   104.197.92.229   80/TCP    name=frontend   1m

$ kubectl describe services frontend | grep "LoadBalancer Ingress"
LoadBalancer Ingress: 104.197.92.229

ブラウザでページを読み込みます。

これで完成です。ゲストブックのエントリを追加してみてください。

レプリケーション コントローラのサイズ変更: ウェブサーバーの数の変更

ゲストブック アプリをしばらく運用していたら、アプリが突然注目を集めるようになったとします。そこで、フロントエンドのウェブサーバーを増やすことになりました。サーバーはレプリケーション コントローラを使用するサービスとして定義されているので増やすのは簡単です。レプリケーション コントローラで次のようにポッドの数を変更します。

$ kubectl scale --replicas=5 rc frontend

これで 5 つのレプリカを実行するようにコントローラの設定が更新されます。レプリケーション コントローラはそれに合わせて実行中のポッドの数を調整します。次のように追加のポッドが実行されているのを確認できます。

$ kubectl get pods
NAME                 READY     STATUS    RESTARTS   AGE
frontend-b8zue       0/1       Running   0          5s
frontend-kn54v       1/1       Running   0          23m
frontend-lblo1       1/1       Running   0          23m
frontend-r10hx       0/1       Running   0          5s
frontend-wjtqr       1/1       Running   0          23m
redis-master-3z4f2   1/1       Running   0          30m
redis-slave-4jfa9    1/1       Running   0          26m
redis-slave-n0aqn    1/1       Running   0          26m
redis-master-gti95   1/1       Running   0          36m

サイトが元の閑散とした状態に戻ったら、同じ方法でウェブサーバー ポッドの数を減らすことができます。

クリーンアップ

フロントエンド サービスを削除して外部ロードバランサをクリーンアップします。

$ kubectl delete services frontend

クラスタの削除が完了したら、シャットダウンすることができます。

$ gcloud container clusters delete guestbook
The following clusters will be deleted.
 - [guestbook] in [us-central1-b]

 Do you want to continue (Y/n)?  y

Deleting cluster guestbook...done.
Deleted [.../projects/container-engine-docs/zones/us-central1-b/clusters/guestbook].

これにより、クラスタを実行している Google Compute Engine のインスタンスと、その上で実行されていたすべてのサービスとポッドが削除されます。

フィードバックを送信...

Container Engine ドキュメント