Kubernetes お試し中です。Rancher で構築するべきか悩み中。 今回は YAML を書いて Deployment, Service を作成して Kubernetes 上に netbox を構築してみます。(netbox は Django + PostgreSQL のデータセンターファシリティ管理ツールです、Wordpress のセットアップは飽きた) パスワードなどは Kubernetes Secrets を使います。 minikube の起動は
$ minikube start
たったこれだけ。詳細は以前の投稿で。
構成図
こんな構成にしてみます
Version
minikube の version は 0.16.0
$ minikube version
minikube version: v0.16.0
Kubernetes の version は
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.1", GitCommit:"82450d03cb057bab0950214ef122b67c83fb11df", GitTreeState:"clean", BuildDate:"2016-12-14T00:57:05Z", GoVersion:"go1.7.4", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.2", GitCommit:"08e099554f3c31f6e6f07b448ab3ed78d0520507", GitTreeState:"clean", BuildDate:"1970-01-01T00:00:00Z", GoVersion:"go1.7.1", Compiler:"gc", Platform:"linux/amd64"}
kubectl 更新しなきゃ
Secret を作成
https://kubernetes.io/docs/user-guide/secrets/ 実際に試した時にはまずは Secrets なしでやったので順序が違いますが、Secrets を使うとなったらまずはこれを作るところから。
秘密なのでコマンドラインの履歴に残らないように --from-file
を使いました。
YAML や JSON からでも作れますが、値を Base64 に encode する手間が必要です。直接文字列を渡すには --from-literal=username=testuser
という指定を使います。
$ kubectl create secret generic netbox-secret \
--from-file=secret_key=secrets/secret_key.txt \
--from-file=email_address=secrets/email_address.txt \
--from-file=email_password=secrets/email_password.txt \
--from-file=netbox_password=secrets/netbox_password.txt \
--from-file=db_password=secrets/db_password.txt \
--from-file=superuser_password=secrets/superuser_password.txt
secret "netbox-secret" created
ファイルで渡す場合は末尾の改行も含まれてしまうため、改行が入らないようの echo -n
などで作ります。
あれ? echo コマンドが履歴に・・・
$ echo -n 'string' > secret.txt
netbox-secret という名前の secret ができているので確認してみます。
$ kubectl get secrets netbox-secret
NAME TYPE DATA AGE
netbox-secret Opaque 6 52s
$ kubectl describe secrets netbox-secret
Name: netbox-secret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
superuser_password: 5 bytes
db_password: 16 bytes
email_address: 27 bytes
email_password: 16 bytes
netbox_password: 5 bytes
secret_key: 49 bytes
ひとつの secret に複数の key / value のセットを登録されていることが確認できます。
これを volume としてマウントてファイルのようにしてアクセスしたり、環境変数として渡すことができます。今回は環境変数として設定します。 Docker 1.13 の secret では一度設定した値を更新できませんでしたが、Kubernetes の場合には更新が可能で、ファイルとしてアクセスする場合には定期的に更新がチェックされ、反映されます。 マウント時にパーミッションも指定できるようなのでコンテナ側から更新できるのかな?試してないけど。
ファイルから登録してファイルとして見せられるので TLS の証明書と秘密鍵とか SSH の private key などを渡すのにも便利に使えそうです。
ドキュメントの Risks に書かれていることは理解しておいたほうが良いです。Kubernetes で任意のコンテナを起動できる人には Secrets は丸見えだとか、etcd の中には平文で入っているので etcd のファイルにアクセスできてしまうとダメだとか書かれています。
さらに、Kubernetes の Dashboard で値が見れちゃう、ええぇぇっ! ということは
$ kubectl get secret netbox-secret -o yaml
とかで YAML や JSON には Base64 の文字列が含まれてるから簡単に値が読めちゃう。API にアクセス出来ちゃう人には丸見えってことだな。
PostgreSQL Deployment の YAML ファイル作成
近頃の Kubernetes では Pod を直接作るのではなく、Deployment というものを使うみたいですね。 Deployment は template として設定した Pod を何個起動させるかを定義する感じです。
マスタの DB は1個で良いので replicas: 1 です。
Pod のコンテナもここでは postgres の1個だけです。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: netbox-db-deployment
spec:
replicas: 1
template:
metadata:
labels:
app: netbox-db
spec:
containers:
- name: netbox-db
image: postgres:9.6.2
env:
- name: POSTGRES_USER
value: netbox
- name: POSTGRES_PASSWORD
# netbox-secret という secret volume から key で指定した値を環境変数に設定
valueFrom:
secretKeyRef:
name: netbox-secret
key: db_password
- name: POSTGRES_DB
value: netbox
# 5432/tcp を公開
ports:
- containerPort: 5432
volumeMounts:
# /var/lib/postgresql にデータ用 volume をマウント
- mountPath: /var/lib/postgresql
name: netbox-db-data
restartPolicy: Always
volumes:
# データファイル用の volume を定義
- name: netbox-db-data
emptyDir: {}
# netbox-secret という名前の secret を netbox-secret という volume 名で定義
- name: netbox-secret
secret:
secretName: netbox-secret
アプリの Deployment の YAML ファイル作成
こちらの Pod には Reveerse Proxy としての nginx と Django で書かれた netbox アプリの2つのコンテナを入れてあります。
Django 側で持っている制定ファイルを nginx が直接返したいために volume を共有してあります。
また、冗長化、負荷分散として replicas: 2 として2セット立ち上げるようにしてあります。 replicas は起動後にも増減が可能です。
netbox は docker-entrypoint.sh で起動されるようになっています。
netbox の repository には docker-compose.yml が置いてあるのでこれを参考に YAML ファイルを作りました。 Docker イメージは公開されていないようなので作りました。yteraoka/netbox, yteraoka/netbox-nginx
環境変数は文字列として定義しないとダメというのになかなか気付けずにしばらくハマってしまいました。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: netbox-app-deployment
spec:
replicas: 2
template:
metadata:
labels:
app: netbox-app
spec:
containers:
# nginx コンテナ
- name: netbox-nginx
image: yteraoka/netbox-nginx:1.11.10
command:
- nginx
# 80/tcp を公開する
ports:
- containerPort: 80
# アプリ側と共有する volume を /opt/netbox/netbox/static にマウントする
# /static/ はここのファイルを返すように nginx.conf で指定してある
volumeMounts:
- mountPath: /opt/netbox/netbox/static
name: netbox-static-files
# netbox アプリコンテナ
- name: netbox-app
image: yteraoka/netbox:1.8.3
# 環境変数指定
env:
- name: SUPERUSER_NAME
value: admin
- name: SUPERUSER_EMAIL
# netbox-secret volume から key で指定した secret の値を環境変数として指定
valueFrom:
secretKeyRef:
name: netbox-secret
key: email_address
- name: SUPERUSER_PASSWORD
valueFrom:
secretKeyRef:
name: netbox-secret
key: superuser_password
- name: ALLOWED_HOSTS
value: '*'
- name: DB_NAME
value: netbox
- name: DB_USER
value: netbox
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: netbox-secret
key: db_password
- name: DB_HOST
value: netbox-db
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: netbox-secret
key: secret_key
- name: EMAIL_SERVER
value: smtp.gmail.com
- name: EMAIL_PORT
# ハマりポイント!環境変数は文字列として定義する必要があるので数値はクオートが必要
value: "587"
- name: EMAIL_USERNAME
valueFrom:
secretKeyRef:
name: netbox-secret
key: email_address
- name: EMAIL_PASSWORD
valueFrom:
secretKeyRef:
name: netbox-secret
key: email_password
- name: EMAIL_TIMEOUT
value: "10"
- name: EMAIL_FROM
valueFrom:
secretKeyRef:
name: netbox-secret
key: email_address
- name: NETBOX_USERNAME
value: guest
- name: NETBOX_PASSWORD
valueFrom:
secretKeyRef:
name: netbox-secret
key: netbox_password
volumeMounts:
- mountPath: /opt/netbox/netbox/static
name: netbox-static-files
restartPolicy: Always
volumes:
- name: netbox-static-files
emptyDir: {}
# netbox-secret という名前の secret を netbox-secret という volume 名で定義
- name: netbox-secret
secret:
secretName: netbox-secret
作った YAML ですぐに Deployment を作成しても良いのですが、後に回して次の Service の定義を作成します
Service の作成
Deployment だけではコンテナは起動するものの外部からのアクセスはもちろん Pod 間の通信すらできません。 これを可能にするために受け口である Service を作成する必要があります。
PostgreSQL の Service
kind: Service
apiVersion: v1
metadata:
name: netbox-db
spec:
selector:
# 紐付ける pod (deployment) を label で指定
app: netbox-db
ports:
- protocol: TCP
port: 5432
targetPort: 5432
アプリ側の Service
kind: Service
apiVersion: v1
metadata:
name: netbox-app
spec:
selector:
# 紐付ける pod (deployment) を label で指定
app: netbox-app
ports:
- protocol: "TCP"
port: 80
# minikube なので node の port にマッピングします
# minikube node の ephemeral port が割り当てられます
type: NodePort
Deployment や Service の YAML は ---
行を挟むことで複数をひとつのファイルにまとめることができるのでDBとアプリそれぞれを1つのファイルにして netbox-db-deployment.yaml netbox-app-deployment.yaml というファイルにしました。
Service, Deployment の作成
Secret を作成し、Service と Deployment の定義ができたのでいよいよデプロイしてみます。
DB の起動
$ kubectl create -f netbox-db-deployment.yaml --record
service "netbox-db" created
deployment "netbox-db-deployment" created
アプリの起動
$ kubectl create -f netbox-app-deployment.yaml --record
service "netbox-app" created
deployment "netbox-app-deployment" created
describe
コマンドで確認してみます
DB の Deployment
$ kubectl describe deployment netbox-db
Name: netbox-db-deployment
Namespace: default
CreationTimestamp: Wed, 22 Feb 2017 23:00:03 +0900
Labels: app=netbox-db
Selector: app=netbox-db
Replicas: 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: NewReplicaSet: netbox-db-deployment-3568492614 (1/1 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set netbox-db-deployment-3568492614 to 1
DB の Service
$ kubectl describe service netbox-db
Name: netbox-db
Namespace: default
Labels: Selector: app=netbox-db
Type: ClusterIP
IP: 10.0.0.94
Port: 5432/TCP
Endpoints: 172.17.0.4:5432
Session Affinity: None
No events.
アプリの Deployment
$ kubectl describe deployment netbox-app
Name: netbox-app-deployment
Namespace: default
CreationTimestamp: Wed, 22 Feb 2017 23:00:16 +0900
Labels: app=netbox-app
Selector: app=netbox-app
Replicas: 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: NewReplicaSet: netbox-app-deployment-205279487 (2/2 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
59s 59s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set netbox-app-deployment-205279487 to 2
アプリの Service Endpoints に2つの登録があるので2つのサーバーにアクセスが割り振られそうです。実際に Pod のログを確認すると2つに割り振られていました。 NodePort に 31631 とありますので http://192.168.99.100:31631/ で netbox にアクセスができます。 IPアドレスは minikube ip コマンドで確認できます。
$ kubectl describe service netbox-app
Name: netbox-app
Namespace: default
Labels: Selector: app=netbox-app
Type: NodePort
IP: 10.0.0.214
Port: 80/TCP
NodePort: 31631/TCP
Endpoints: 172.17.0.5:80,172.17.0.6:80
Session Affinity: None
No events.
前に教えてもらった minikube service list
の方が便利なのでした
$ minikube service list
|-------------|----------------------|-----------------------------|
| NAMESPACE | NAME | URL |
|-------------|----------------------|-----------------------------|
| default | kubernetes | No node port |
| default | netbox-app | http://192.168.99.100:31631 |
| default | netbox-db | No node port |
| kube-system | kube-dns | No node port |
| kube-system | kubernetes-dashboard | http://192.168.99.100:30000 |
|-------------|----------------------|-----------------------------|
Pod の状況
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
netbox-app-deployment-205279487-1lx6p 2/2 Running 0 1h
netbox-app-deployment-205279487-r2ksc 2/2 Running 0 1h
netbox-db-deployment-3568492614-qwblg 1/1 Running 0 1h
netbox にアクセスしてログインできました!!
あとは rolling update や healthcheck、実際の network 環境での Service 設定、ログ収集の仕組みとかいろいろあるなあ
お掃除
作ったものの削除です。minikube をまるっと消すなら minikube delete
で全部消えます。
Service の削除
$ kubectl get services
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.0.0.1 443/TCP 12m
netbox-app 10.0.0.117 80:32678/TCP 6m
netbox-db 10.0.0.140 5432/TCP 6m
$ kubectl delete service netbox-app
service "netbox-app" deleted
$ kubectl delete service netbox-db
service "netbox-db" deleted
Deployment の削除
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
netbox-app-deployment 2 2 2 0 6m
netbox-db-deployment 1 1 1 1 6m
$ kubectl delete deployment netbox-app-deployment
deployment "netbox-app-deployment" deleted
$ kubectl delete deployment netbox-db-deployment
deployment "netbox-db-deployment" deleted
これで Pod も削除されますが、完全に消えるまでにはちょっと時間がかかります。
Secret の削除
$ kubectl get secrets
NAME TYPE DATA AGE
default-token-8674j kubernetes.io/service-account-token 3 52m
netbox-secret Opaque 6 41m
$ kubectl delete secrets netbox-secret
secret "netbox-secret" deleted