はじめに
- Mac で Minikube を使って Kubernetes を動かしてみる
- Kubernetes の用語も勉強中なのでまとめる
- 状態を持たないウェブサーバーを複数台立ち上げる設定と、状態を保つ Zookeeper クラスタを複数台立ち上げる設定について内容を理解する
Minikube のインストール
- https://github.com/kubernetes/minikube の通りにインストール
- VirtualBox のインストール
- minikube のインストール
brew cask install minikube
minikube の実行
# 起動
$ minikube start
# ステータス確認
$ minikube status
# ブラウザでステータスを確認する
$ minikube dashboard
# コンテナを起動する
$ kubectl run hello-minikube --image=gcr.io/google_containers/echoserver:1.4 --port=8080
# サービスの公開
$ kubectl expose deployment hello-minikube --type=NodePort
# サービスのURL取得
$ minikube service hello-minikube --url
# サービスの削除
$ kubectl delete service hello-minikube
# deployment の削除
$ kubectl delete deployment hello-minikube
# 停止
$ minikube stop
用語
Cluster
- Node の集合
Node
- Pod が動作する物理もしくは仮想マシン
- 1 Node で複数の Pod が動作する
Pod
- docker コンテナの集合. docker コンテナは Pod 単位で Node に配備される
- 依存関係のあるコンテナ同士を同一の Pod に所属させて Pod 単位で取り扱うことで管理が容易になる
ReplicaSet
- ReplicaSet は Pod の定義や複製数を持ち、指定された数の Pod が常に起動されていることを保証する
Deployment
- ReplicaSet の作成・管理をする
- ReplicaSet のように Pod の定義や複製数を持ち、ReplicaSet を作成することで Pod を起動できる
- 定義を更新して新しい ReplicaSet を作成し、古い ReplicaSet から 1 台づつ更新することができる(ローリングアップデート)
- ReplicaSet の履歴を持ち、古い ReplicaSet に巻き戻すことができる(ロールバック)
Service
- クラスタ外部向けのエンドポイントとしてクラスタ内部とポートフォワーディングしたり負荷分散したりする
ConfigMap
- Key-Value でデータを定義して Pod に環境変数やファイルとしてデータを提供する
- 環境変数の場合は Key が変数名で Value が値
- ファイルの場合は Key がファイル名で Value がファイルの内容、Pod の定義でマウント先の Path を指定する
StatefulSet
- Pod の定義と複製数を持ち、定義された数だけ Pod を起動する
- 複製数が N なら Pod の名前は {Name}-0 から {Name}-N となり Pod が死んで再作成されても同じ名前になる
- 永続ストレージの定義を持ち Pod 作成時に自動でストレージも作成される
- ストレージの削除は自動では行われない
- 複製数が N なら 0 番目の Pod から N 番目の Pod まで順番に起動される
- 複製数が N なら N 番目の Pod から 0 番目の Pod まで順番に停止される
ローカルのイメージでコンテナを起動する
# minikube の docker マシンに接続する
$ eval $(minikube docker-env)
$ docker ps
# ローカルにイメージを作成する
$ echo "FROM nginx" > Dockerfile
$ docker build -t example/nginx:v1.0 .
# ローカルのイメージを使ってコンテナを起動する
$ cat <<EOL > example.yaml
apiVersion: v1
kind: Pod
metadata:
name: example
spec:
containers:
- name: nginx
image: "example/nginx:v1.0"
imagePullPolicy: IfNotPresent
EOL
$ kubectl create -f example.yaml
# 動作確認をする
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
example 1/1 Running 0 16s
$ kubectl exec -it example /bin/bash
kubectl のコンテキストを GKE と切り替える
# kubectl の実行コンテキストの確認
$ kubectl config current-context
# kubectl を GKE のコンテキストに切り替え
$ gcloud container clusters get-credentials ${CLUSTER_NAME}
# kubectl を minikube のコンテキストに切り替え
kubectl config use-context minikube
Pod の情報を参照して環境変数にセットする
-
取得例
-
環境変数にセットしている例
-
セットした環境変数を参照する例
状態を持たない単純なサーバーを Kubernetes で複数台立ち上げる
- ウェブサーバーのような状態を持たないサーバーを複数台立ち上げる場合は Deployment を使って立ち上げることで常に設定した台数起動した状態になってくれて便利
-
kubectl apply -f deployment.yml
として起動する
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2 # Pod を常に 2 つ起動させる
template: # 起動する Pod の設定テンプレート
metadata:
# kubectl コマンドや Service などから Pod を参照する際にこのラベルを使って絞り込むことができる
labels:
app: nginx
spec:
containers:
- name: nginx # 今回は 1 Pod に nginx のコンテナが一つだけ入っているものを立ち上げる
image: nginx:1.7.9
ports:
- containerPort: 80
- さらに詳しくは公式ドキュメントを参考に
複数台でクラスタを作って動作するミドルウェアを Kubernetes で立ち上げる
- 複数台で相互に通信しあって動作するミドルウェアである Zookeeper のクラスタ群を立ち上げる設定について https://github.com/Yolean/kubernetes-kafka/tree/master/zookeeper を見ながら確認する
Namespace の設定
- 00namespace.yml
- Pod, Service, StatefulSet, ConfigMap などに名前をつけて管理しやすくする
apiVersion: v1
kind: Namespace
metadata:
name: kafka
ConfigMap の設定
- 10zookeeper-config.yml
- 設定ファイルを ConfigMap を使って定義しておく
- StatefulSet の Pod のボリューム定義(spec.template.spec.volumes)でボリュームとして読み込まれ、テンプレート定義(spec.template.spec.containers.[n].volumeMounts)でボリューム名とマウント先パスを指定してマウントされる
kind: ConfigMap
metadata:
name: zookeeper-config
namespace: kafka
apiVersion: v1
data:
init.sh: |-
#!/bin/bash
set -x
[ -z "$ID_OFFSET" ] && ID_OFFSET=1
export ZOOKEEPER_SERVER_ID=$((${HOSTNAME##*-} + $ID_OFFSET))
echo "${ZOOKEEPER_SERVER_ID:-1}" | tee /var/lib/zookeeper/data/myid
sed -i "s/server\.$ZOOKEEPER_SERVER_ID\=[a-z0-9.-]*/server.$ZOOKEEPER_SERVER_ID=0.0.0.0/" /etc/kafka/zookeeper.properties
zookeeper.properties: |-
tickTime=2000
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
clientPort=2181
initLimit=5
syncLimit=2
server.1=pzoo-0.pzoo:2888:3888:participant
server.2=pzoo-1.pzoo:2888:3888:participant
server.3=pzoo-2.pzoo:2888:3888:participant
server.4=zoo-0.zoo:2888:3888:participant
server.5=zoo-1.zoo:2888:3888:participant
log4j.properties: |-
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d] %p %m (%c)%n
# Suppress connection log messages, three lines per livenessProbe execution
log4j.logger.org.apache.zookeeper.server.NIOServerCnxnFactory=WARN
log4j.logger.org.apache.zookeeper.server.NIOServerCnxn=WARN
Service の設定
Headless Service の設定
- 20pzoo-service.yml
- Headless Service とはクラスタ内のコンテナ同士が通信し合うためのインターフェイス
- 外部向けの IP アドレスを持たないため headless と呼ばれる
apiVersion: v1
kind: Service
metadata:
# サービス名の定義
# StatefulSet の Pod の割り当てサービス設定(spec.serviceName)でこの名前を指定する
# これにより `POD_NAME.SERVIE_NAME.NAMESPACE.svc.cluster.local` で名前解決ができるようになるので、この名前で設定などを行う
name: pzoo
namespace: kafka
spec:
# 公開ポートの定義
ports:
- port: 2888
name: peer
- port: 3888
name: leader-election
clusterIP: None
# 適用される Pod の絞り込み条件の定義
selector:
app: zookeeper
storage: persistent
- 21zoo-service.yml の方は永続化ストレージを持たない Pod 用のサービス設定
Client Service の設定
- 30service.yml
- 外部に公開するサービスの定義を行う
# the headless service is for PetSet DNS, this one is for clients
apiVersion: v1
kind: Service
metadata:
name: zookeeper
namespace: kafka
spec:
ports:
- port: 2181
name: client
selector:
app: zookeeper
StatefulSet の設定
- 50pzoo.yml
- Pod のテンプレート定義を持ち、状態持った Pod の集合を生成する
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: pzoo
namespace: kafka
spec:
# pzoo サービスを使って名前解決を行えるように
serviceName: "pzoo"
# 3 台立ち上げる
replicas: 3
template:
metadata:
labels:
app: zookeeper
storage: persistent
annotations:
spec:
# グレースフルシャットダウンの間隔
terminationGracePeriodSeconds: 10
# Pod の作成時に 1 度だけ立ち上げられるコンテナのテンプレート定義
# ここでは ConfigMap に定義したスクリプトを使って設定ファイルの初期化処理を行なっている
# 実際に Zookeeper が起動するコンテナとは違うコンテナとして実行されるが、両コンテナでマウントされている設定ファイルのボリュームに対して変更を行うので変更が引き継がれる
initContainers:
- name: init-config
# イメージの実体は https://github.com/solsson/dockerfiles/tree/master/initutils にある(Docker Hub から参照)
image: solsson/kafka:0.11.0.0@sha256:b27560de08d30ebf96d12e74f80afcaca503ad4ca3103e63b1fd43a2e4c976ce
command: ['/bin/bash', '/etc/kafka/init.sh']
volumeMounts:
# 設定ファイルのマウント
- name: config
mountPath: /etc/kafka
- name: data
mountPath: /var/lib/zookeeper/data
# 実際に Zookeeper が起動するコンテナの設定
containers:
- name: zookeeper
# イメージの実体は https://github.com/solsson/dockerfiles/tree/master/kafka にある(Docker Hub から参照)
image: solsson/kafka:0.11.0.0@sha256:b27560de08d30ebf96d12e74f80afcaca503ad4ca3103e63b1fd43a2e4c976ce
env:
- name: KAFKA_LOG4J_OPTS
value: -Dlog4j.configuration=file:/etc/kafka/log4j.properties
command:
- ./bin/zookeeper-server-start.sh
- /etc/kafka/zookeeper.properties
ports:
- containerPort: 2181
name: client
- containerPort: 2888
name: peer
- containerPort: 3888
name: leader-election
resources:
requests:
cpu: 10m
memory: 100Mi
# コンテナのヘルスチェックコマンドの定義
readinessProbe:
exec:
command:
- /bin/sh
- -c
- '[ "imok" = "$(echo ruok | nc -w 1 127.0.0.1 2181)" ]'
volumeMounts:
# 設定ファイルのマウント
- name: config
mountPath: /etc/kafka
# 永続化ストレージのマウント
- name: data
mountPath: /var/lib/zookeeper/data
volumes:
- name: config
configMap:
name: zookeeper-config
# 永続化ストレージの作成要求
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
- 51zoo.yml の方は永続化ストレージを持たない Pod 用のサービス設定
クラスタを立ち上げる
-
kubectl apply -f zookeeper/
として立ち上げる