GKEチュートリアルでサイト構築・運用

概要

以前さくらVPS上で tocoちゃんバスアプリを作成しました。

さくらVPSは個人プロジェクトを幾つか載せていましたが
一部終了した為、tocoちゃんバスアプリを GCP にお引越ししました。

その時の話を GKE チュートリアルを兼ねて改めてまとめました。

何故 GCP ?

toco ちゃんバスアプリはDBも持たない軽量なサイトです。
その為、GCPの無料枠が利用できると思い、移行に至りました。

構成

GCPでは Container Cluster を利用し
この様な構成を取っております。

以下、GCP のチュートリアルに倣い構築手順まとめました。

gcloud デフォルト設定

以前無料枠を利用して構築した際の記事を参照ください。
Pod 単体の寂しい構成ではありますが汗

コンテナクラスタ作成

こちらも以前の記事同様、無料枠を利用すべく
初めに3ノードで作成し完了後、1ノードにします。

クラスターバージョンは 1.7.2 を指定しました。((2017年8月2日時点で cluster version 最新は 1.7.2))

1
$ gcloud container get-server-config

Fetching server config for us-west1-b
defaultClusterVersion: 1.6.7
defaultImageType: COS
validImageTypes:

  • CONTAINER_VM
  • COS
  • UBUNTU
    validMasterVersions:
  • 1.7.2
  • 1.6.7
    validNodeVersions:
  • 1.7.2
  • 1.7.1
  • 1.7.0
  • 1.6.7
  • 1.6.6
  • 1.6.4
  • 1.5.7
  • 1.4.9
  • コンテナクラスタ作成
1
2
3
4
5
$ gcloud container clusters create tocochan-cluster-free \
--cluster-version=1.7.2 \
--machine-type f1-micro \
--disk-size=30 \
--num-nodes=3
  • Node 数を 1 に設定
1
$ gcloud container clusters resize tocochan-cluster-free --size=1
  • 確認
1
$ gcloud container clusters describe tocochan-cluster-free | grep currentNodeCount

currentNodeCount: 1

現在のノード数が 1 であることが確認できました。
これで無料枠!

クラスタ作成後、コンテナクラスタの認証情報を取得し
kubectl でクラスタ接続し操作できる様にします。

1
$ gcloud container clusters get-credentials tocochan-cluster-free

Container Registory 登録

ローカルで起動したコンテナからイメージ作成し
GCP 上の Private な Docker リポジトリである Container Registory に登録します。

以下リポジトリを利用します。

https://github.com/kenzo0107/toda-tocochan-bus

  • Docker コンテナ起動
1
2
3
$ git clone https://github.com/kenzo0107/toda-tocochan-bus
$ cd toda-tocochan-bus
$ docker-compose up --build -d
  • 起動した Docker コンテナからイメージ作成・GCR へ push ((プロジェクトIDは 「mametsubuservice-175801」 ))
1
2
3
$ container_id=$(docker ps | grep [f]lask | awk '{print $1}')
$ docker commit $container_id gcr.io/mametsubuservice-175801/tocochan:latest
$ gcloud docker -- push gcr.io/mametsubuservice-175801/tocochan:latest

Pod 単体デプロイ as チュートリアル①

基本単体でデプロイすることは稀です。
単純に Pod 内のコンテナが異常停止した場合などを管理できない為です。
今回は無料運用の為と内容理解の為のチュートリアルとしての作業です。

  • pod.yaml
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Pod
metadata:
name: tocochan
spec:
containers:
- image: gcr.io/mametsubuservice-175801/tocochan:latest
imagePullPolicy: Always
name: tocochan

Pod 単体デプロイ実行

1
$ kubectl create -f pod.yaml

Pod 状態確認

1
$ kubectl get pods
  • アクセス設定

flask は 5000 ポートで起動します。

1
2
3
4
$ kubectl port-forward tocochan 5000

Forwarding from 127.0.0.1:5000 -> 5000
Forwarding from [::1]:5000 -> 5000
  • ブラウザから http://localhost:5000 にアクセス

トップページが取得できることが確認できます。

Pod 名指定し削除

Pod 単体デプロイが確認できましたので削除しましょう。

1
$ kubectl delete pods tocochan

ReplicaSet デプロイ as チュートリアル②

Pod 単体作成した場合、 Pod に異常停止したとしても特に何もリカバーされません。
ReplicaSet では常に正常に動作するコンテナ数を管理しており
異常停止があった場合は新たに Pod を追加します。

こちらもチュートリアルとして記載してます。こちらは終わったら削除します。

  • replicaset.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: tocochan
spec:
replicas: 1 # 常に動作するコンテナ数
template:
metadata:
labels:
name: tocochan
spec:
containers:
- image: gcr.io/mametsubuservice-175801/tocochan:latest
imagePullPolicy: Always
name: tocochan

ReplicaSet デプロイ実行

1
$ kubectl create -f replicaset.yaml

ReplicaSet 確認

1
$ kubectl get rs -l name=tocochan

NAME DESIRED CURRENT READY AGE
tocochan-4006188167 1 1 1 10m

仮に Pods を削除しようとすると?

1
$ kubectl delete pods -l name=tocochan

起動コンテナが 0 になることなく、新たに作成されていることがわかります。

NAME READY STATUS RESTARTS AGE
tocochan-14s3b 1/1 Running 0 4s
tocochan-tsvfn 1/1 Terminating 0 5m

ReplicaSet 削除

1
$ kubectl delete rs tocochan

Deployment デプロイ

ReplicaSet のデプロイは k8s 上に履歴が残りません。
Deployment デプロイでは履歴が残り、現行バージョンに異常があった場合は
バージョンを簡単に戻せます。

これまでの問題を解決しているのが Deployment デプロイです。

  • deployment.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: tocochan
spec:
replicas: 1
template:
metadata:
labels:
name: tocochan
spec:
containers:
- image: gcr.io/mametsubuservice-175801/tocochan:latest
imagePullPolicy: Always
name: tocochan

Deployment デプロイ実行

--record を付けることで操作履歴を残すことができます。
履歴に残すことで問題がある場合に kubectl の操作で過去のバージョンに戻すことができます。

1
$ kubectl create -f deployment.yaml --record

Deployment 確認

1
$ kubectl get deployments -l name=tocochan

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
tocochan 1 1 1 1 10m

ReplicaSet 確認

1
$ kubectl get rs -l name=tocochan

NAME DESIRED CURRENT READY AGE
tocochan-2006588533 1 1 1 10m

Pod 確認

1
$ kubectl get pods -l name=tocochan

NAME READY STATUS RESTARTS AGE
tocochan-4006188167-3zrn9 1/1 Running 0 10m

デプロイ結果確認

1
$ kubectl rollout status deployment/tocochan

deployment “tocochan” successfully rolled out

正しく Rollout 公開されたことがわかりました。

履歴確認

1
$ kubectl rollout history deployment tocochan

deployments “tocochan”
REVISION CHANGE-CAUSE
1 kubectl create –filename=deployment.yaml –record=true

編集

1
$ kubectl edit deployment tocochan

vim が起動し deployment の編集が可能です。

1
2
- image: gcr.io/mametsubuservice-175801/tocochan:latest
+ image: gcr.io/mametsubuservice-175801/tocochan:v0.0.1

上記の様に編集し保存して終了すると

NAME READY STATUS RESTARTS AGE
tocochan-1297744065-2qb87 1/1 Terminating 0 15m
tocochan-4006188167-3zrn9 1/1 Running 0 10s

既存コンテナが停止中となコンテナが新たに立ち上がったことがわかります。

  • 履歴確認
1
$ kubectl rollout history deployment tocochan

REVISION CHANGE-CAUSE
1 kubectl create –filename=all.yaml –record=true
2 kubectl edit deployment tocochan

Rollout 履歴を確認すると 編集内容が追加されていることがわかります。

バージョンを戻す

REVISION 1 に戻します。

1
$ kubectl rollout undo deployment tocochan --to-revision=1
1
$ kubectl get pods -l name=tocochan

NAME READY STATUS RESTARTS AGE
tocochan-1297744065-2qb87 1/1 Terminating 0 6m
tocochan-4006188167-zswcj 1/1 Running 0 7s

先ほどと同様に既存コンテナが停止しコンテナが新たに起動している様子がわかります。

外部から接続できる?

ここまでの Pod の状態で以下コマンドを実行します。

1
$ kubectl get svc

NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.3.240.1 443/TCP 3h

kubernetes の cluster-ip が割り振られている以外は特に IP が割り振られておらず
外部からアクセスできない状態です。

外部からアクセス出来る様、設定する必要があります。

Service 作成

外部向けの IP を設定し、外部から Pod にアクセス出来る様にルーティングします。

  • service.yaml
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Service
metadata:
name: tocochan
spec:
type: LoadBalancer
selector:
name: tocochan
ports:
- port: 5000

Service 作成

1
$ kubectl create -f service.yaml

Service 確認

1
$ kubectl get svc

数分経過すると から IPになります。

NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.3.240.1 443/TCP 10m
tocochan 10.3.240.70 xx.xxx.xxx.xxx 5000:32429/TCP 10m

以下コマンドで Web ページにアクセス出来ることが確認できます。

1
$ curl -v http://$EXTERNAL-IP:5000

ロードバランサー作成

ロードバランサーを立てることが可能です。
80 port で受け、5000 port をバックエンドに流します。

  • ingress.yaml
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: hello-world
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: hello-world
servicePort: 5000

Ingress 作成

1
$ kubectl create -f ingress.yaml

Ingress 確認

1
$ kubectl get ingress tocochan

NAME HOSTS ADDRESS PORTS AGE
tocochan * yy.yyy.yy.yy 80 10m

以下アクセスで先ほど実施した curl -v http://$EXTERNAL-IP:5000 と同様の結果が取得できることがわかります。

1
$ curl http://$INGRESS_IP/

設定ファイルをまとめる

1
2
3
4
5
6
7
8
9
$ echo '---' > hyphen.txt; \
cat \
deployment.yaml \
hyphen.txt \
service.yaml \
hyphen.txt \
ingress.yaml \
> all.yaml; \
rm hyphen.txt
  • all.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: tocochan
spec:
replicas: 1
template:
metadata:
labels:
name: tocochan
spec:
containers:
- image: gcr.io/mametsubuservice-175801/tocochan:v0.0.1
imagePullPolicy: Always
name: tocochan
---
apiVersion: v1
kind: Service
metadata:
name: tocochan
spec:
type: LoadBalancer
selector:
name: tocochan
ports:
- port: 5000
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: tocochan
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: tocochan
servicePort: 5000

以降、以下コマンドで OK !

1
$ kubectl create -f all.yaml --record

ドメイン取得

toda-tocochan-bus.tkfreenom で無料ドメイン取得し
Ingress の IP を設定し公開しています。

総評

ローカルで開発して〜デプロイ、という流れが本当に簡単になりました。
コンテナの理念遂行に kubernetes は大きく寄与しているなぁと実感しました。

以上です。
参考になれば幸いです。

I used Phinx, DB migration Tool on Docker!

I used Phinx, DB migration Tool on Docker!

Overview

This is Sandbox for DB Migration Tool Phinx.

Preparation

1
2
$ git clone https://github.com/kenzo0107/phinx-mysql
$ cd phinx-mysql

Create and Run Containers of Phinx, DB (MySQL).

1
$ make build
1
2
3
4
5
6
$ docker-compose ps

Name Command State Ports
-------------------------------------------------------------------------
phinxmysql_db-migrate_1 phinx --help Exit 0
phinxmysql_db_1 docker-entrypoint.sh mysqld Up 3306/tcp

The Container db-migrate is used as for one-off container, so its state is Exit 0.

Initialize Phinx Project

Phinx creates a default file called phinx.yml.

1
$ make init

In default setting, phinx select development environment.

1. Create Table

Create phinx definition file

1
2
3
4
5
6
$ make create DB=hogehoge CLASS=CreateTableUsers
$ make create DB=mogemoge CLASS=CreateTableMembers
...
...
created db/migrations/hogehoge/20170724065658_create_table_users.php
created db/migrations/mogemoge/20170724065738_create_table_members.php

Edit phinx definition file

  • db/migrations/hogehoge/20170724065658_create_table_users.php

Writing Migrations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php

use Phinx\Migration\AbstractMigration;
use Phinx\Db\Adapter\MysqlAdapter;

class CreateTableUsers extends AbstractMigration
{
public function up()
{
// Automatically generated id is excluded, and primary key is set as user_id
$t = $this->table('users', ['id' => 'user_id']);

$t->addColumn('last_name', 'string', ['limit' => 10, 'comment' => '姓'])
->addColumn('first_name', 'string', ['limit' => 10, 'comment' => '名'])
->addColumn('last_kana_name', 'string', ['null' => true, 'limit' => 10, 'comment' => '姓(カナ)'])
->addColumn('first_kana_name', 'string', ['null' => true, 'limit' => 10, 'comment' => '名(カナ)'])
->addColumn('username', 'string', ['limit' => 20, 'comment' => 'ユーザ名'])
->addColumn('password', 'string', ['limit' => 40, 'comment' => 'パスワード'])
->addColumn('email', 'string', ['limit' => 100, 'comment' => 'Email'])
->addColumn('postcode', 'string', ['limit' => 10, 'comment' => '郵便番号'])
->addColumn('birthday', 'date', ['comment' => '誕生日'])
->addColumn('gender', 'integer', ['limit' => MysqlAdapter::INT_TINY, 'comment' => '性別(1:男 2:女 3:その他)'])
->addColumn('card_number', 'string', ['null' => true, 'limit' => 20, 'comment' =>'クレジットカードNo'])
->addColumn('description', 'text', ['null' => true, 'limit' => MysqlAdapter::TEXT_LONG, 'comment' =>'説明'])
->addColumn('created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
->addColumn('updated', 'datetime', ['null' => true])
->addIndex(['username', 'email'], ['unique' => true])
->create();
}

public function down()
{
$this->dropTable('users');
}
}
  • db/migrations/mogemoge/20170724065738_create_table_members.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

use Phinx\Migration\AbstractMigration;

class CreateTableMembers extends AbstractMigration
{
public function up()
{
$t = $this->table('members');
$t->addColumn('member_code', 'string', ['limit' => 20, 'comment' => '会員コード'])
->addColumn('created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
->addColumn('updated', 'datetime', ['null' => true])
->addIndex(['member_code'], ['unique' => true])
->create();
}

public function down()
{
$this->dropTable('members');
}
}

2. Add Column

Create phinx definition file

1
2
3
4
$ make create CLASS=AddTableUsersColumnsCity
...
...
created db/migrations/hogehoge/20170724065838_add_table_users_columns_city.php

Edit phinx definition file

  • db/migrations/hogehoge/20170724065838_add_table_users_columns_city.php

Add the column city after the column email.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

use Phinx\Migration\AbstractMigration;

class AddTableUsersColumnsCity extends AbstractMigration
{
public function up()
{
$t = $this->table('users');
$t->addColumn('city', 'string', ['limit' => 10, 'comment' => '都市', 'after' => 'postcode'])
->update();
}

public function down()
{
$t = $this->table('users');
$t->removeColumn('city')
->save();
}
}

Migration

1
$ make migrate
  • Result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
mysql> use hogehoge
mysql> show full columns from users;
+-----------------+--------------+-----------------+------+-----+-------------------+----------------+---------------------------------+---------------------------------+
| Field | Type | Collation | Null | Key | Default | Extra| Privileges | Comment |
+-----------------+--------------+-----------------+------+-----+-------------------+----------------+---------------------------------+---------------------------------+
| user_id | int(11) | NULL | NO | PRI | NULL | auto_increment| select,insert,update,references | |
| last_name | varchar(10) | utf8_general_ci | NO | | NULL || select,insert,update,references | 姓 |
| first_name | varchar(10) | utf8_general_ci | NO | | NULL || select,insert,update,references | 名 |
| last_kana_name | varchar(10) | utf8_general_ci | YES | | NULL || select,insert,update,references | 姓(カナ) |
| first_kana_name | varchar(10) | utf8_general_ci | YES | | NULL || select,insert,update,references | 名(カナ) |
| username | varchar(20) | utf8_general_ci | NO | MUL | NULL || select,insert,update,references | ユーザ名 |
| password | varchar(40) | utf8_general_ci | NO | | NULL || select,insert,update,references | パスワード |
| email | varchar(100) | utf8_general_ci | NO | | NULL || select,insert,update,references | Email |
| city | varchar(255) | utf8_general_ci | NO | | NULL || select,insert,update,references | |
| postcode | varchar(10) | utf8_general_ci | NO | | NULL || select,insert,update,references | 郵便番号 |
| birthday | date | NULL | NO | | NULL || select,insert,update,references | 誕生日 |
| gender | tinyint(4) | NULL | NO | | NULL || select,insert,update,references | 性別(1:男 2:女 3:その他) |
| card_number | varchar(20) | utf8_general_ci | YES | | NULL || select,insert,update,references | クレジットカードNo |
| description | longtext | utf8_general_ci | YES | | NULL || select,insert,update,references | 説明 |
| created | timestamp | NULL | NO | | CURRENT_TIMESTAMP || select,insert,update,references | |
| updated | datetime | NULL | YES | | NULL || select,insert,update,references | |
+-----------------+--------------+-----------------+------+-----+-------------------+----------------+---------------------------------+---------------------------------+


mysql> use mogemoge
mysql> show full columns from members;
+-------------+-------------+-----------------+------+-----+-------------------+----------------+---------------------------------+-----------------+
| Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment |
+-------------+-------------+-----------------+------+-----+-------------------+----------------+---------------------------------+-----------------+
| id | int(11) | NULL | NO | PRI | NULL | auto_increment | select,insert,update,references | |
| member_code | varchar(20) | utf8_general_ci | NO | UNI | NULL | | select,insert,update,references | 会員コード |
| created | timestamp | NULL | NO | | CURRENT_TIMESTAMP | | select,insert,update,references | |
| updated | datetime | NULL | YES | | NULL | | select,insert,update,references | |
+-------------+-------------+-----------------+------+-----+-------------------+----------------+---------------------------------+-----------------+

Rollback

1
$ make rollback

3. Create sample seeds for Multi Databases;

Create phinx definition file

1
2
3
4
5
6
$ make seed_create DB=hogehoge CLASS=UserSeeder
$ make seed_create DB=mogemoge CLASS=MembersSeeder
...
...
created ./db/seeds/hogehoge/UsersSeeder.php
created ./db/seeds/mogemoge/MembersSeeder.php

Edit phinx definition file

  • ./db/seeds/hogehoge/UsersSeeder.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php

use Phinx\Seed\AbstractSeed;

class UsersSeeder extends AbstractSeed
{
public function run()
{
$t = $this->table('users');
$t->truncate();

$genders = [1,2,3];

$faker = Faker\Factory::create('ja_JP');
$d = [];
for ($i = 0; $i < 10; $i++) {
$d[] = [
'last_name' => $faker->lastName(10),
'first_name' => $faker->firstName(10),
'last_kana_name' => $faker->lastKanaName(10),
'first_kana_name' => $faker->firstKanaName(10),
'username' => $faker->userName(20),
'password' => sha1($faker->password),
'email' => $faker->email,
'postcode' => $faker->postcode,
'city' => $faker->city,
'birthday' => $faker->date($format='Y-m-d',$max='now'),
'gender' => $faker->randomElement($genders),
'card_number' => $faker->creditCardNumber,
'description' => $faker->text(200),
'created' => date('Y-m-d H:i:s'),
'updated' => date('Y-m-d H:i:s'),
];
}

$this->insert('users', $d);
}
}
  • ./db/seeds/hogehoge/MembersSeeder.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

use Phinx\Seed\AbstractSeed;

class MembersSeeder extends AbstractSeed
{
public function run()
{
$t = $this->table('members');
$t->truncate();

$faker = Faker\Factory::create('ja_JP');
$d = [];
for ($i = 0; $i < 10; $i++) {
$d[] = [
'member_code' => $faker->regexify('[0-9]{20}'),
'created' => date('Y-m-d H:i:s'),
'updated' => date('Y-m-d H:i:s'),
];
}

$this->insert('members', $d);
}
}

Run seed

1
$ make seed
  • Result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
mysql> use hogehoge;
mysql> select * from users;
+---------+-----------+------------+-----------------+-----------------+-------------+------------------------------------------+------------------------------+----------+--------------+------------+--------+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------------------+
| user_id | last_name | first_name | last_kana_name | first_kana_name | username | password | email | postcode | city | birthday | gender | card_number | description | created | updated |
+---------+-----------+------------+-----------------+-----------------+-------------+------------------------------------------+------------------------------+----------+--------------+------------+--------+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------------------+
| 1 | 佐々木 | 零 | ヤマダ | カナ | akira97 | e270038c94f231da7bca25dead3e386ba3984491 | hirokawa.rika@hotmail.co.jp | 1867251 | 佐々木市 | 1987-09-25 | 1 | 4024007116991463 | Dolor reiciendis fuga fugiat id molestiae eos. Dolores sint rem repudiandae perspiciatis. Ducimus aut mollitia aut asperiores laboriosam. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
| 2 | 宮沢 | 千代 | ノムラ | ヨウイチ | nagisa.taro | 695a90d1b84cf004357aad3eb37697b51afbf5cc | tanabe.hiroshi@kudo.org | 8639535 | 江古田市 | 1977-06-01 | 3 | 344103919563863 | Doloribus et recusandae quam accusantium pariatur nobis reiciendis quo. Eos quae et commodi quos accusamus ex. Ullam repellendus maiores vero sit sit et. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
| 3 | 斉藤 | 充 | ミヤケ | オサム | kana.suzuki | f309f34d08b4d0d686863fa38ed3d3af5e0b2104 | kana.kudo@mail.goo.ne.jp | 2763622 | 青田市 | 1997-01-30 | 1 | 4716886227252 | Veritatis voluptatem pariatur libero aut quia. Facere nemo quos enim amet ut ipsum sequi. Nobis natus et aspernatur aut. Natus pariatur deserunt voluptatum deserunt. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
| 4 | 吉田 | 太郎 | ウノ | ツバサ | naoko.uno | 45d04bda7ac79244c90a33ff68798b979138054a | taro.nagisa@hirokawa.com | 6099661 | 江古田市 | 2006-03-19 | 2 | 5372535333698250 | Nostrum velit nostrum eos magni. Reiciendis quos enim adipisci quisquam sed voluptas. Necessitatibus sint qui dolorem animi impedit consectetur commodi. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
| 5 | 野村 | 亮介 | サトウ | ミノル | rika.tanabe | dd3d50714c0775bfee453f7d9a9815ce26ba57db | wkudo@hotmail.co.jp | 6966314 | 渡辺市 | 1985-12-21 | 1 | 4929108488987091 | Id atque molestiae expedita omnis libero natus et. Repellendus ut tenetur molestias voluptas. Perspiciatis nisi et illum aut aut vel repudiandae. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
| 6 | 木村 | 裕美子 | タナカ | ヒロキ | hiroshi53 | 033bfd0493b72efd0ff60bc15c7eeb3b2e054501 | ztanabe@tanabe.biz | 3155238 | 山田市 | 1996-01-02 | 3 | 5476616628100007 | Assumenda consectetur ea sed et omnis alias fugiat quo. Porro nihil similique sint laudantium asperiores blanditiis. Error dolores vitae quia explicabo facilis deleniti distinctio. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
| 7 | 吉本 | 陽一 | キムラ | ヒデキ | akira27 | 51de6afc65f535ae58f927d698f07e60e04c7746 | rika59@suzuki.com | 6457702 | 田辺市 | 2010-04-12 | 2 | 5388155063289311 | Nesciunt qui beatae ut officia qui error autem. Temporibus alias earum ullam incidunt quo recusandae enim qui. Sed atque veritatis sed ad ullam qui. Repellendus est nostrum et pariatur. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
| 8 | 渡辺 | 翔太 | ササダ | クミコ | uno.momoko | fa2d16d5f2acffd5aeeaab6791fe64c9f70a9b2f | stanabe@uno.com | 5849600 | 伊藤市 | 2012-06-09 | 1 | 5274550197820022 | Odio quasi sunt tempora. Molestias aut qui sed quos beatae eum accusantium. Non dolores quam veniam et ab quidem nostrum repellendus. Qui ducimus et optio et. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
| 9 | 坂本 | 翔太 | ナカツガワ | ナオキ | akira.kudo | 4af41e536bf19fa3cb0527304adad0de76260e82 | suzuki.momoko@mail.goo.ne.jp | 8609563 | 宮沢市 | 2005-10-23 | 3 | 5231530310398512 | Qui id neque molestiae facere aut et consequatur. Delectus ea voluptatibus provident atque assumenda maxime eum. At quidem sint accusamus. Eaque sed voluptate quo sint non non. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
| 10 | 野村 | 翼 | ヒロカワ | ナオコ | taro.kudo | f8a63d0010c99d6403e0c1f458005b934ec03f8c | kana.tanabe@mail.goo.ne.jp | 5804069 | 桐山市 | 1988-12-25 | 2 | 5140671281503530 | Dolorem consequatur nulla alias perspiciatis ut. Tenetur modi cumque incidunt dolor. | 2017-07-25 12:22:50 | 2017-07-25 12:22:50 |
+---------+-----------+------------+-----------------+-----------------+-------------+------------------------------------------+------------------------------+----------+--------------+------------+--------+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------------------+



mysql> use mogemoge;
mysql> select * from members;

+----+----------------------+---------------------+---------------------+
| id | member_code | created | updated |
+----+----------------------+---------------------+---------------------+
| 1 | 86190539096622228312 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
| 2 | 77322186584623078448 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
| 3 | 17169562241415794809 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
| 4 | 86738824931379981947 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
| 5 | 23125815173540252188 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
| 6 | 81839177491562485300 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
| 7 | 82938165381845652192 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
| 8 | 87208503292784158954 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
| 9 | 80172779107984112104 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
| 10 | 22825755425594828330 | 2017-07-25 12:22:51 | 2017-07-25 12:22:51 |
+----+----------------------+---------------------+---------------------+

Reference

無料枠で運用! GKE + Kubernetes で Hubot 〜独自ネットワーク作成、設定ファイルから起動編〜

無料枠で運用! GKE + Kubernetes で Hubot 〜独自ネットワーク作成、設定ファイルから起動編〜

前回手元の Mac からコンテナクラスタ → Deployment → LB 作成する手順をまとめました。

但し、8080 ポートがフルオープンとなってしまい、誰でもアクセスが可能であるという、
セキュリティ的に非常によろしくない状態でした。

その為、今回は以下実施します。

  • 独自ネットワーク(ファイアウォール)作成
  • 独自ネットワーク上にクラスタ作成
  • 設定ファイルでコンテナ起動・更新

前回の独自ネットワーク設定していないクラスタは削除して問題ないです。お任せします m(_ _)m

前回同様の Git Repository 用意

1
2
$ git clone https://github.com/kenzo0107/hubot-slack-on-docker
$ cd hubot-slack-on-docker

Network 作成

  • hubot-network というネットワークを作成します。
1
macOS%$ gcloud compute networks create hubot-network

ファイアウォール作成

  • 作成したネットワークに特定 IP からのみ 8080 ポートアクセス許可
1
macOS%$ gcloud compute firewall-rules create hubot-rule --network hubot-network --allow tcp:8080 --source-ranges xxx.xxx.xxx.xxx,yyy.yyy.yyy.yyy.yyy

Container Clusters 作成

  • 作成したネットワーク指定しクラスタ作成
1
2
3
4
5
6
macOS%$ gcloud container clusters create hubot-cluster-free \
--machine-type f1-micro \
--disk-size=30 \
--num-nodes=3 \
--network=hubot-network \
--cluster-ipv4-cidr=10.0.0.0/14
  • cluster-ipv4-cidr オプション必須!
    指定しクラスタ内の Pod の IP アドレスの範囲指定しています。
    ※サブネットマスク(10.0.0.0/14 の “/14” 部分)指定は 9〜19 で指定する必要があります。

例) –cluster-ipv4-cidr=10.0.0.0/8 指定した場合のエラー

1
ERROR: (gcloud.container.clusters.create) ResponseError: code=400, message=cluster.cluster_ipv4_cidr CIDR block size must be no bigger than /9 and no smaller than /19, found /8.

ノード数を 1 に変更

1
macOS%$ gcloud container clusters resize hubot-cluster-free --size=1

Deployment 作成

1
macOS%$ kubectl create -f gke-deployment.yml

Deployment, Replicaset, Pod 一覧表示

  • ラベル付けした app: hubot を条件指定
1
macOS%$ kubectl get deployments,replicasets,pods --selector app=hubot

フォーマットを yaml 形式で出力

1
macOS%$ kubectl get deployment deployment-hubot -o yaml

サービス公開する為、LoadBalancer 付加

1
macOS%$ kubectl create -f gke-lb.yml

サービス一覧表示

1
2
3
4
macOS%$ kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.3.240.1 <none> 443/TCP 20m
loadbalancer 10.3.241.129 zz.zzz.zzz.zzz 8080:31628/TCP 4m

※EXTERNAL-IP : zz.zzz.zzz.zzz はグローバル IP

いざ、テスト !

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
macOS%$ curl \
-X POST \
-H "Content-Type: application/json" \
-d \
'{
"webhookEvent":"jira:issue_updated",
"comment":{
"author":{
"name":"himuko"
},
"body":"[~kenzo.tanaka] 東京03 秋山 ケンコバ 劇団ひとり"
},
"issue": {
"key":"key",
"fields":{
"summary":"summary"
}
}
}' \
http://zz.zzz.zzz.zzz:8080/hubot/jira-comment-dm
  • できた!
    [f:id:kenzo0107:20170516220548p:plain]

更新(ローリングアップデート)

ReplicationController を利用することで無停止で更新します。

実際に以下の様にして更新しているのが確認できます。

  • 既存の Running 中のコンテナの個数分、更新したイメージからビルドしたコンテナを起動
  • 更新版コンテナが Running 状態になったら既存コンテナを削除
1
2
3
4
5
6
7
8
9
10
11
12
// ローカルで更新した Docker Container を コミット
macOS%$ docker commit 12f77feb09b4 gcr.io/hubot-167007/hubot:latest
// Google Container Registory にプッシュ
macOS%$ gcloud docker -- push gcr.io/hubot-167007/hubot:latest

// Pod 表示
macOS%$ kubectl get pods
NAME READY STATUS RESTARTS AGE
deployment-hubot-cfe7528ee0b5059b14a30b942597e5ef-z8nws 1/1 Running 1 1d

// push したImageを元にローリングアップデート
macOS%$ kubectl rolling-update deployment-hubot-cfe7528ee0b5059b14a30b942597e5ef-z8nws --image=gcr.io/hubot-167007/hubot:latest

後片付け

  • Deployment 削除
1
macOS%$ kubectl delete -f gke-deployment.yml
  • LoadBalancer 削除
1
macOS%$ kubectl delete -f gke-lb.yml

総評

ネットワークのファイアウォール設定してコンテナ起動したが動かなかった所、かなり詰まりました (; _)
Stackoverflow にたまたま同様のイシューをあげている方がおり参考にさせて頂きました。
助かった汗

これから Nginx + Rails 等、よくありそうなケースで GKE + Kubernetes を試して運用してみたいと思います。
まとまったらまた追記します!

参考

Unable to launch a GKE (Google Container Engine) cluster with a custom network

無料枠で運用! GKE + Kubernetes で Hubot 〜CLIから実行編〜

無料枠で運用! GKE + Kubernetes で Hubot 〜CLIから実行編〜

概要

  • 無料枠を使って Slack 連携する Hubot を GKE で構築します。
  • おまけで JIRA 連携も

Google Cloud SDK のインストール方法と初期化

Mac OS X 用クイックスタート を参照して SDK をダウンロードします。

  • Mac OS X (x86_64), (x86) かは以下コマンドで確認
1
2
3
macOS%$ uname -m

x86_64

kubectl のインストール

1
macOS%$ gcloud components update kubectl

gcloud デフォルト設定

以下は作成したプロジェクト、リージョン、ゾーンを設定してます。
今後 gcloud コマンド実行時に region 指定等しなくて良くなります。

  • 作成したプロジェクト ID : hubot-167007
  • us-west 利用で無料枠を使う為に US リージョンに設定してます。
1
2
3
4
macOS%$ gcloud auth login
macOS%$ gcloud config set project hubot-167007
macOS%$ gcloud config set compute/region us-west1
macOS%$ gcloud config set compute/zone us-west1-b

Google Cloud Platform の無料階層 参照してください。

クラスタ作成

  • 無料枠を利用するべく f1-micro で 30GB 設定
  • でも作成時は 3 ノード必須
  • 作成完了後、リサイズで 1 ノードに
1
2
3
4
5
6
7
8
9
10
macOS%$ gcloud container clusters create hubot-cluster-free \
--machine-type f1-micro \
--disk-size=30 \
--num-nodes=3

Creating cluster hubot-cluster-free...done.
Created [https://container.googleapis.com/v1/projects/hubot-167007/zones/us-west1-b/clusters/hubot-cluster-free].
kubeconfig entry generated for hubot-cluster-free.
NAME ZONE MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
hubot-cluster-free us-west1-b 1.5.7 35.xxx.xxx.xxx f1-micro 1.5.7 3 RUNNING
  • コンソールを見ると作成中であることが確認できます。

Imgur

  • 以下コマンドで確認可
1
2
3
4
5
macOS% $ kubectl get nodes
NAME STATUS AGE VERSION
gke-hubot-cluster-free-default-pool-a3b110d2-9k6s Ready 59s v1.5.7
gke-hubot-cluster-free-default-pool-a3b110d2-lqxg Ready 1m v1.5.7
gke-hubot-cluster-free-default-pool-a3b110d2-xqs8 Ready 1m v1.5.7
  • 1 ノードにリサイズ
1
2
3
4
5
6
7
8
macOS%$ gcloud container clusters resize hubot-cluster-free --size=1

Pool [default-pool] for [hubot-cluster-free] will be resized to 1.

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

Resizing hubot-cluster-free...done.
Updated [https://container.googleapis.com/v1/projects/hubot-167007/zones/us-west1-b/clusters/hubot-cluster-free].

Imgur

リサイズできるなら初めから 1 ノードで作らせて欲しい (>_<)

コンソール上だとやっぱりダメ (T_T)

Imgur

認証情報 取得

  • コンテナクラスタの認証情報を取得し、kubectl を利用してコンテナ クラスタ上にコンテナを作成できるようになります。
1
2
3
4
macOS%$ gcloud container clusters get-credentials hubot-cluster-free

Fetching cluster endpoint and auth data.
kubeconfig entry generated for hubot-cluster-free.
  • コンテナクラスタ情報表示
1
macOS%$ gcloud container clusters describe hubot-cluster-free

ローカルの Docker 起動

[https://github.com/kenzo0107/hubot-slack-on-docker:embed:cite]

1
2
3
macOS%$ git clone https://github.com/kenzo0107/hubot-slack-on-docker
macOS%$ cd hubot-slack-on-docker
macOS%$ docker-compose up -d
1
2
3
4
macOS%$ docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
12f77feb09b4 hubotslackondocker_hubot "/bin/sh -c 'bash ..." 24 minutes ago Up 24 minutes 6379/tcp, 0.0.0.0:8080->8080/tcp hubotslackondocker_hubot_1

Hubot 動作確認

Slack 上に Hubot が登場していて hello と呼びかけると Hi と返してくれたら成功です。

CONTAINER ID から イメージを commit

1
2
3
4
5
macOS%$ docker commit 12f77feb09b4 gcr.io/hubot-167007/hubot:latest

macOS%$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
gcr.io/hubot-167007/hubot latest 2f7336b3a3ce 3 seconds ago 484 MB

gke registory に push

参考: Container Registry への push

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
macOS%$ gcloud docker -- push gcr.io/hubot-167007/hubot:latest

The push refers to a repository [gcr.io/hubot-167007/hubot]
0569b419082b: Pushed
a7637cfcdfba: Pushed
9f0bdbb7b1fa: Pushed
f1d85eafc75a: Pushed
c2c2b58591f2: Pushed
51c94eacef50: Pushed
69e7fcf7ba41: Pushed
293d09ca6a9d: Pushed
247e72dfcaf5: Pushed
8c2bc9bf1f19: Pushed
40907ce0d959: Pushed
bfba578a7fbe: Pushed
561cbcaac156: Pushed
293a1e72e88b: Pushed
ae09eb3da3dc: Pushed
c06c14d7f919: Pushed
e14577d2cac5: Layer already exists
e8829d5bbd2c: Layer already exists
674ce3c5d814: Layer already exists
308b39a73046: Layer already exists
638903ee8579: Layer already exists

latest: digest: sha256:0c3b29d18b64c1f8ecc1a1bf67462c84d5915a4a708fe87df714d09198eb5fa1 size: 4704
  • latest が被ると過去のイメージのタグが奪われます。容量の無駄になるので削除しましょう。

Imgur

Deployments 作成

1
2
3
4
5
6
7
8
9
10
macOS%$ kubectl run pod-hubot \
--image=gcr.io/hubot-167007/hubot:latest \
--env="HUBOT_SLACK_TOKEN=xoxb-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx" \
--env="HUBOT_SLACK_TEAM=xxxxxx.slack.com" \
--env="HUBOT_SLACK_BOTNAME=hubot" \
--env="HUBOT_JIRA_URL=https://<jira_server_domain_or_ip>" \
--port=8080 \
--restart='Always'

deployment "pod-hubot" created
  • deployments 状態確認
1
2
3
macOS%$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
pod-hubot 1 1 1 0 10s
  • Pod 状態確認
1
2
3
4
macOS%$ kubectl get pods

NAME READY STATUS RESTARTS AGE
pod-hubot-1713414922-b2dkq 0/1 ImagePullBackOff 0 23s
  • Pod にログイン
1
$ kubectl exec -it pod-hubot-1713414922-b2dkq /bin/bash
  • service の状態確認
1
2
3
4
5
macOS%$ kubectl get service


NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.23.240.1 <none> 443/TCP 22m

EXTERNAL-IP: <none> … 外部へ開いている IP がない。という状態
Private IP は付与されたが Public IP がない、外部のネットワークからアクセスできない状態です。

コンテナ公開

  • Service にロードバランサ付与し公開

※ ロードバランサを追加すると課金の桁が跳ね上がります。。
(2000 円/月くらい。念の為、設定した予算アラートでわかりました。)

1
2
macOS%$ kubectl expose deployment pod-hubot --type="LoadBalancer"
service "pod-hubot" exposed
  • Service 確認

EXTERNAL-IP: <pending> となっており、作成途中であることがわかります。

1
2
3
4
5
macOS%$ kubectl get service

NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.23.240.1 <none> 443/TCP 25m
pod-hubot 10.23.244.214 <pending> 8080:30453/TCP 8s
  • 再度 Service 確認

無事付与されているのがわかりました。

1
2
3
4
macOS%$ kubectl get service
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.23.240.1 <none> 443/TCP 27m
pod-hubot 10.23.244.214 104.xxx.x.xxx 8080:30453/TCP 1m

テスト

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
macOS%$ curl \
-X POST \
-H "Content-Type: application/json" \
-d \
'{
"webhookEvent":"jira:issue_updated",
"comment":{
"author":{
"name":"himuko"
},
"body":"[~kenzo.tanaka] 東京03 秋山 ケンコバ 劇団ひとり"
},
"issue": {
"key":"key",
"fields":{
"summary":"summary"
}
}
}' \
http://104.xxx.x.xxx:8080/hubot/jira-comment-dm

Imgur

後始末

掃除しときたい場合に以下実行してください。

  • service 削除
1
2
macOS%$ kubectl delete service pod-hubot
service "pod-hubot" deleted
  • pod 削除
1
2
macOS%$ kubectl delete pod pod-hubot-729436916-htw3r
service "pod-hubot" deleted
  • deployments 削除
1
macOS%$ kubectl delete deployments pod-hubot
  • container clusters 削除
    container cluster を削除すれば紐付く deployments, service, pod も削除されます。
1
macOS%$ gcloud container clusters delete hubot-cluster-free

以上です。

総評

GKE は概念が多く、一概に deployment, pod, service, kubernetes 等覚えることが多いですが
動かしつつ学ぶのは楽しいです。

ほぼ手元の Mac で設定できました!
手元で済むから macOS%$ は不要だった。。

今回作成した service だと外部に 8080 ポート全開です。

次回はアクセス元を制限したポートアクセスやコンテナのアップデートについてまとめます。

[http://kenzo0107.hatenablog.com/entry/2017/05/16/222815:embed:cite]