takaya030の備忘録

PHP、Laravel、Docker などの話がメインです

Google Cloud Monitoring を使ってみた

Google Container Engine (GKE) のモニタリングについてのメモ

Cloud Monitoring の有効化

GKE でコンテンナクラスタを作成するときに "Cloud Monitoring を有効にする" にチェックを入れます。
初回は StackDriver のユーザー登録を行った後、使用可能になります。
2016年7月23日現在、BETA期間中につき無料で使用できます。
f:id:takaya030:20160723165359p:plain

モニター画面

クラスタ起動後、約30分経過するとグラフが表示され始めます。
GKE ではエージェントの設定は特に必要なく自動で行われるようです。
VMインスタンスだけではなく、GKE で起動している POD も監視対象になってます。オートスケールを行う環境ではありがたいですね。
f:id:takaya030:20160723170150p:plain

Kubernetes で Lumen を動かす

Google Container Engine (GKE) で Kubernetes を使って Lumen を動かしたときの手順メモ。
今回は ReplicationController で pod を 3 個立ててみました。

Docker イメージの作成

1 個の pod 内で nginx と php-fpm の 2 つのコンテナを起動させます。そのための Docker イメージを作成します。

nginx の Dockerfile

FROM nginx:latest
MAINTAINER takaya030

ADD server.conf /etc/nginx/conf.d/server.conf

server.conf

同じ pod のコンテナは同じホストで起動するので php-fpm を localhost:9000 で参照する設定にしています。

server {
    listen 80 default;
    server_name _;
    root /webapp/public;
    index index.php index.html index.htm;
    charset utf-8;

    access_log off;
    error_log off;

    rewrite ^(.+)/$ $1;

    location / {
        # try_files $uri $uri/ /index.php$is_args$args;
        try_files $uri /index.php?$query_string;
    }

    location ~ ^/index.php$ {
        fastcgi_pass localhost:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include       fastcgi_params;
    }
}

nginx イメージのビルド

$ docker build -t gcr.io/my_project_id/lumen_nginx .

php-fpm + lumen の Dockerfile

k8s_lumen フォルダはこちらで作成した Lumen の実行環境になります。

FROM php:7-fpm
MAINTAINER takaya030

RUN apt-get update \
  && apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng12-dev libmcrypt-dev \
  && docker-php-ext-install pdo_mysql mysqli mbstring gd iconv mcrypt

RUN mkdir /webapp
COPY k8s_lumen /webapp

php-fpm イメージのビルド

$ docker build -t gcr.io/my_project_id/lumen_php7 .

イメージを Container Registry へ push

$ gcloud docker push gcr.io/my_project_id/lumen_nginx
$ gcloud docker push gcr.io/my_project_id/lumen_php7

ReplicationController の作成

rc-k8slumen.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: k8slumen
spec: 
  replicas: 3
  selector:
    app: weblumen
  template:
    metadata:
      name: k8slumen
      labels:
        app: weblumen
    spec:
      containers:
        - name: nginx
          image: gcr.io/my-project-id/lumen_nginx
          ports:
            - containerPort: 80
        - name: lumen
          image: gcr.io/my-project-id/lumen_php7
          ports:
            - containerPort: 9000

ReplicationController の作成

$ kubectl create -f rc-k8slumen.yaml
replicationcontroller "k8slumen" created

ReplicationController の作成確認

$ kubectl get rc
NAME       DESIRED   CURRENT   AGE
k8slumen   3         3         27s

service の作成

service-k8slumen.yaml

apiVersion: v1
kind: Service
metadata:
  name: lumentest
spec:
  ports:
    -
      port: 80
      targetPort: 80
  selector:
    app: weblumen
  type: LoadBalancer

service の作成

$ kubectl create -f service-k8slumen.yaml
service "lumentest" created

service の作成確認

$ kubectl get services
NAME         CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.3.240.1     <none>        443/TCP   7m
lumentest    10.3.255.153                 80/TCP    17s

動作確認

この状態で pod が 3 個起動しています。

$ kubectl get pods -l app=weblumen -o wide
NAME             READY     STATUS    RESTARTS   AGE       NODE
k8slumen-5r9jc   2/2       Running   0          1m        gke-cluster-1-default-pool-a58af72c-3ais
k8slumen-itl2h   2/2       Running   0          1m        gke-cluster-1-default-pool-a58af72c-yx7i
k8slumen-xfed2   2/2       Running   0          1m        gke-cluster-1-default-pool-a58af72c-4rtd

service を作成してから 1 分程経過するとグローバルIP (EXTERNAL-IP) が割り当てられるので、 http://EXTERNAL-IP にブラウザでアクセスして以下のように表示されれば成功です。
f:id:takaya030:20160702234718p:plain
ロードバランスされているので、ブラウザをリロードした際、前回と別の pod にリクエストしたときは以下のように Server IP が変化します。
f:id:takaya030:20160702234754p:plain
f:id:takaya030:20160702234807p:plain

フェイルオーバー(failover)の検証

試しに pod を一つ削除してみます。

$ kubectl delete pod k8slumen-5r9jc
pod "k8slumen-5r9jc" deleted

kubernetes が pod の減少を検知して自動で pod を立ち上げます。
pod のリストを見ると新しい pod が追加されているのが分かります。

$ kubectl get pods -l app=weblumen -o wide
NAME             READY     STATUS    RESTARTS   AGE       NODE
k8slumen-itl2h   2/2       Running   0          31m       gke-cluster-1-default-pool-a58af72c-yx7i
k8slumen-w468c   2/2       Running   0          12s       gke-cluster-1-default-pool-a58af72c-3ais
k8slumen-xfed2   2/2       Running   0          31m       gke-cluster-1-default-pool-a58af72c-4rtd

後始末

service、ReplicationController の削除
ReplicationController を削除すれば pod も自動で削除されます

$ kubectl delete -f service-k8slumen.yaml
service "lumentest" deleted

$ kubectl delete -f rc-k8slumen.yaml
replicationcontroller "k8slumen" deleted

Lumen の Welcome Page を作る

nginx + php-fpm で Lumen を動かす記事の続編です。

Lumen 5.2 で Welcome Page が無くなっていた

最新版の Lumen をインストールして実行してみたらバージョン番号のテキストが表示されました。あの白い画面の Welcome Page が無いとチョット寂しいので Lumen 5.2 で作ってみました。
f:id:takaya030:20160505005324p:plain

ソースコード

app/Http/Controllers/WelcomeController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class WelcomeController extends Controller
{
    public function index( Request $request )
    {
	$client_ip = $request->ip();
	$server = $request->server();		// $_SERVER

        return view('welcome', ['server' => $server, 'client_ip' => $client_ip]);
    }
}

app/Http/routes.php

<?php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It is a breeze. Simply tell Lumen the URIs it should respond to
| and give it the Closure to call when that URI is requested.
|
*/

$app->get('/', [
	'uses' => 'WelcomeController@index',
]);

resources/views/welcome.blade.php

<!DOCTYPE html>
<html>
<head>
	<title>Lumen</title>

	<link href='//fonts.googleapis.com/css?family=Lato:100' rel='stylesheet' type='text/css'>

	<style>
		body {
			margin: 0;
			padding: 0;
			width: 100%;
			height: 100%;
			color: #B0BEC5;
			display: table;
			font-weight: 100;
			font-family: 'Lato';
		}

		.container {
			text-align: center;
			display: table-cell;
			vertical-align: middle;
		}

		.content {
			text-align: center;
			display: inline-block;
		}

		.title {
			font-size: 96px;
			margin-bottom: 40px;
		}

		.quote {
			font-size: 24px;
		}

		.info {
			font-size: 48px;
		}
	</style>
</head>
<body>
	<div class="container">
		<div class="content">
			<div class="title">Lumen.</div>
			<div class="info">Hostname: {{ gethostname() }}</div>
			<div class="info">Server IP: {{ $server['SERVER_ADDR'] }}</div>
			<div class="info">Client IP: {{ $client_ip }}</div>
@if ( array_key_exists('X-Forwarded-For', $server) )
			<div class="info">X-Forwarded-For: {{ $server['X-Forwarded-For'] }}</div>
@elseif ( array_key_exists('HTTP_X_FORWARDED_FOR', $server) )
			<div class="info">HTTP_X_FORWARDED_FOR: {{ $server['HTTP_X_FORWARDED_FOR'] }}</div>
@elseif ( array_key_exists('REMOTE_ADDR', $server) )
			<div class="info">REMOTE_ADDR: {{ $server['REMOTE_ADDR'] }}</div>
@endif
		</div>
	</div>
</body>
</html>

動作確認のための Docker 関連ファイル

ディレクトリ構成

lumen
│  docker-compose.yml
│  
├─nginx
│    Dockerfile
│    server.conf
│
└─php7
      Dockerfile
      k8s_lumen/

lumen/php7/k8s_lumen は下記コマンドでインストールされた Lumen の実行環境に上記ソースコードを適用したものになります。

$ composer create-project --prefer-dist laravel/lumen k8s_lumen

lumen/docker-compose.yml

nginx: 
  build: ./nginx
  ports: 
    - "80:80"
  links: 
    - php7

php7: 
  build: ./php7

lumen/nginx/Dockerfile

FROM nginx:latest
MAINTAINER takaya030

ADD server.conf /etc/nginx/conf.d/server.conf

lumen/nginx/server.conf

server {
    listen 80 default;
    server_name _;
    root /webapp/public;
    index index.php index.html index.htm;
    charset utf-8;

    access_log off;
    error_log off;

    rewrite ^(.+)/$ $1;

    location / {
        # try_files $uri $uri/ /index.php$is_args$args;
        try_files $uri /index.php?$query_string;
    }

    location ~ ^/index.php$ {
        fastcgi_pass lumen_php7_1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include       fastcgi_params;
    }
}

lumen/php7/Dockerfile

FROM php:7-fpm
MAINTAINER takaya030

RUN apt-get update \
  && apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng12-dev libmcrypt-dev \
  && docker-php-ext-install pdo_mysql mysqli mbstring gd iconv mcrypt

RUN mkdir /webapp
COPY k8s_lumen /webapp

イメージのリビルド

$ cd lumen
$ docker-compose build

コンテナの起動

$ cd lumen
$ docker-compose up -d

動作確認

web ブラウザで http://192.168.99.100/ にアクセスして下の画像のように表示されるか確認。
f:id:takaya030:20160627001224p:plain

参考サイト

takaya030.hatenablog.com

Lumen の Controller の namespace について

Lumen 5.2 でコントローラのルートの namespace が App\Http\Controllers に変更されていたのでメモ

Lumen 5.1 で動いていたコードが 5.2 でエラーになった件

app/Http/routes.php

<?php

$app->get('/', [
	'uses' => 'App\Http\Controllers\WelcomeController@index',
]);

上のコードを Lumen の最新版(5.2)で実行したとき発生したエラー

[2016-06-25 04:01:55] lumen.ERROR: ReflectionException: Class App\Http\Controllers\App\Http\Controllers\WelcomeController does not exist in /webapp/vendor/illuminate/container/Container.php:734

Lumen 5.1 では Controller のルートは namespace 無しだったが、5.2 では 'App\Http\Controllers' になったらしい。
以下のように変更したら無事動きました。

<?php

$app->get('/', [
	'uses' => 'WelcomeController@index',
]);

ちなみに Controller のデフォルト namespace は bootstrap/app.php で定義されている。
Lumen 5.1 の bootstrap/app.php 84行目

<?php

/*
|--------------------------------------------------------------------------
| Load The Application Routes
|--------------------------------------------------------------------------
|
| Next we will include the routes file so that they can all be added to
| the application. This will provide all of the URLs the application
| can respond to, as well as the controllers that may handle them.
|
*/

require __DIR__.'/../app/Http/routes.php';

Lumen 5.2 の bootstrap/app.php 85行目

<?php

/*
|--------------------------------------------------------------------------
| Load The Application Routes
|--------------------------------------------------------------------------
|
| Next we will include the routes file so that they can all be added to
| the application. This will provide all of the URLs the application
| can respond to, as well as the controllers that may handle them.
|
*/

$app->group(['namespace' => 'App\Http\Controllers'], function ($app) {
    require __DIR__.'/../app/Http/routes.php';
});

検証した Lumen のバージョン

Lumen 5.1

$ php artisan --version
Laravel Framework version Lumen (5.1.6) (Laravel Components 5.1.*)

Lumen 5.2

$ php artisan --version
Laravel Framework version Lumen (5.2.7) (Laravel Components 5.2.*)

Kubernetes で nginx + php-fpm を動かす

Google Container Engine (GKE) で Kubernetes を使って nginx + php-fpm を動かしたときの手順メモ

Docker イメージの作成

今回は 1 個の pod 内で nginx と php-fpm の 2 つのコンテナを起動させます。そのための Docker イメージを作成します。

nginx の Dockerfile

FROM nginx:latest
MAINTAINER takaya030

ADD server.conf /etc/nginx/conf.d/server.conf

server.conf

同じ pod のコンテナは同一ホストで起動するので php-fpm を localhost:9000 で参照する設定にしています。

server {
    listen 80 default;
    server_name _;
    root /var/www/html;
    index index.php index.html index.htm;
    charset utf-8;

    access_log off;
    error_log off;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ ^/index.php$ {
        fastcgi_pass localhost:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include       fastcgi_params;
    }
}

nginx イメージのビルド

$ docker build -t gcr.io/my_project_id/k8sphp_nginx .

php-fpm の Dockerfile

FROM php:7-fpm
MAINTAINER takaya030

RUN apt-get update \
  && apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng12-dev libmcrypt-dev \
  && docker-php-ext-install pdo_mysql mysqli mbstring gd iconv mcrypt

COPY index.php /var/www/html/

index.php

<?php
	phpinfo();

php-fpm イメージのビルド

$ docker build -t gcr.io/my_project_id/k8sphp_php7 .

イメージを Container Registry へ push

$ gcloud docker push gcr.io/my_project_id/k8sphp_nginx
$ gcloud docker push gcr.io/my_project_id/k8sphp_php7

pod の作成

pod-k8sphp.yaml

apiVersion: v1
kind: Pod
metadata:
  name: k8sphp
  labels:
    app: webphp
spec:
  containers:
    - name: nginx
      image: gcr.io/my-project-id/k8sphp_nginx
      ports:
        - containerPort: 80
    - name: php-fpm
      image: gcr.io/my-project-id/k8sphp_php7
      ports:
        - containerPort: 9000

pod の作成

$ kubectl create -f pod-k8sphp.yaml
pod "k8sphp" created

pod の作成確認

$ kubectl get pods
NAME      READY     STATUS    RESTARTS   AGE
k8sphp    2/2       Running   0          34s

service の作成

service-k8sphp.yaml

apiVersion: v1
kind: Service
metadata:
  name: phptest
spec:
  ports:
    -
      port: 80
      targetPort: 80
  selector:
    app: webphp
  type: LoadBalancer

service の作成

$ kubectl create -f service-k8sphp.yaml
service "phptest" created

service の作成確認

$ kubectl get services
NAME         CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.3.240.1    <none>        443/TCP   45m
phptest      10.3.242.13                 80/TCP    25s

動作確認

service を作成してから 1 分程経過するとグローバルIP (EXTERNAL-IP) が割り当てられるので、 http://EXTERNAL-IP にブラウザでアクセスして以下のように表示されれば成功です。
f:id:takaya030:20160618004808p:plain

後始末

service、pod の削除

$ kubectl delete -f service-k8sphp.yaml
service "phptest" deleted

$ kubectl delete -f pod-k8sphp.yaml
pod "k8sphp" deleted

Google Container Engine (GKE) に kubectl で Docker コンテナを起動する

GKE に作成したクラスタに kubectl を使って Docker コンテナを単体起動したときの手順メモ

Docker イメージを Container Registry へ push

今回は nginx の Docker Hub 公式イメージをそのまま使います。

$ docker pull nginx
$ docker tag nginx gcr.io/my_project_id/nginx
$ gcloud docker push gcr.io/my_project_id/nginx

my_project_id にハイフンが含まれている場合、アンダースコアに置き換えて下さい。
push されたイメージは GKE のコンソールで確認できます。
f:id:takaya030:20160604215241p:plain

コンテナクラスタの作成

GKE のコンソールからコンテナクラスタを作成します。
f:id:takaya030:20160604215333p:plain
今回はこのような内容で作成しました。Cluster size がノード数になります。
f:id:takaya030:20160604215344p:plain
作成ボタンを押してから数分後にクラスタが作成されます。
f:id:takaya030:20160604215409p:plain

Container Registry のイメージを使ってコンテナを起動

$ kubectl run hello-nginx --image=gcr.io/my-project-id/nginx:laest --port=80

コンテナのエクスポート

外部からアクセス可能にするため下記コマンドを実行

$ kubectl expose deployment hello-nginx --type="LoadBalancer"

数分待つとコンテナにグローバル IP が割り当てられます。

コンテナのグローバル IP の確認

$ kubectl get service hello-nginx
NAME          CLUSTER-IP     EXTERNAL-IP      PORT(S)   AGE
hello-nginx   10.3.243.148   130.211.xxx.xx   80/TCP    7m

EXTERNAL-IP がコンテナのグローバル IP になります。

動作確認

web ブラウザで http://EXTERNAL-IP を開いて以下の通りに表示されば成功です。
f:id:takaya030:20160604215454p:plain

コンテナの停止

$ kubectl delete service hello-nginx

コンテナクラスタをそのまま放置すると課金されるので、コンソールで削除します。

トラブルシューティング

kubectl run のとき "The connection to the server localhost:8080 was refused" のエラーになる

gcloud の config にクラスタの情報が登録されていないのが原因です。
以下の操作で登録します。

$ gcloud config set project my-project-id
$ gcloud config set compute/zone us-central1-b
$ gcloud config set container/cluster cluster-1
$ gcloud container clusters get-credentials cluster-1
Fetching cluster endpoint and auth data.
kubeconfig entry generated for cluster-1.
$ gcloud config list
Your active configuration is: [default]

[compute]
region = us-central1
zone = us-central1-b
[container]
cluster = cluster-1
...

kubectl version で Server Version が表示されていれば、サーバーと通信できています。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"2", GitVersion:"v1.2.4", GitCommit:"3eed1e3be6848b877ff80a93da3785d9034d0a4f", GitTreeState:"clean"}
Server Version: version.Info{Major:"1", Minor:"2", GitVersion:"v1.2.4", GitCommit:"3eed1e3be6848b877ff80a93da3785d9034d0a4f", GitTreeState:"clean"}

Docker コンテナに Google Cloud SDK (gcloud) と Kubernetes (kubectl) をインストール

WindowsGoogle Cloud SDK では Kubernetes (kubectl) が動かないため、Docker コンテナ にインストールしたときの手順メモ

Dockerfile

gcloud を動作させるために Python 2.7 が必要なため、Docker Hub の公式イメージを流用して作成

# gcloud shell
#

FROM python:2.7
MAINTAINER takaya030

RUN apt-get update && apt-get install -y openssh-server sudo
RUN mkdir /var/run/sshd
RUN echo 'root:root' | chpasswd
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile

# create login user
RUN useradd -d /home/gcp -m -s /bin/bash gcp
RUN echo gcp:****gcp | chpasswd
RUN echo 'gcp ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

イメージのビルド

$ docker build -t takaya030/gcloud .

コンテナの起動

$ docker run -d -it -p 2222:22 --hostname gcsh --name gcp_gcsh_1 takaya030/gcloud

コンテナへログイン

$ ssh -p 2222 gcp@192.168.99.100

※パスワードは '****gcp'

Google Cloud SDK のインストール

$ curl https://sdk.cloud.google.com | bash

今回は /home/gcp/google-cloud-sdk/ にインストールした。
インストール後、直ちにコマンドのパスを有効にしたい場合は以下のコマンドを入力

$ source .bashrc

GCP のユーザー情報の設定

$ gcloud init

kubectl のインストール

$ gcloud components update kubectl
$ which kubectl
/home/gcp/google-cloud-sdk/bin/kubectl