入門 Kubernetesを読みました。本の中で紹介されているyamlはkubernetes-up-and-running/examplesにあるみたいです。
# Docker
docker run -d --name kuard --publish 8081:8080 --memory 200m --memory-swap 1G --cpu-shares 1024 gcr.io/kuar-demo/kuard-amd64:1
docker stop kuard; docker rm kuard
# Kubernetes
kubectl version
kubectl get componentstatuses
controller-manager' クラスタ上の振る舞いを制御する
scheduler 各Podをクラスタ内のそれぞれのノードに配置する
etcd クラスタのすべてのAPIオブジェクトが保存されるストレージ
ノードの表示する
kubectl get nodes
masterノード APIサーバーやスケジューラのコンテナが動く
workerノード ユーザが作成したコンテナが動く
特定ノードの表示する
kubectl describe nodes xxx
プロキシの一覧を表示する
kubectl get daemonSets --namespace=kube-system kube-proxy
DNSの一覧を表示する
kubectl get deployments --namespace=kube-system kube-dns
DNSサーバをロードバランシングするためのKubernetes Serviceを表示する
kubectl get services --namespace=kube-system kube-dns
UIサーバの確認
kubectl get deployments --namespace=kube-system kube-dashboard
ロードバランスのためのServiceを表示
kubectl get services --namespace=kube-system kube-dashboard
UIにアクセスする
kubectl proxy
# よく使うkubectlコマンド
Namespaceはオブジェクトの集まりを入れるフォルダのようなイメージ
実行中のコンテナのログを確認する
kubectl logs podname
kubectl logs -f podname
ファイルのやりとりを行う
kubectl cp podname:/path/to/remote/file /path/to/local/file
実行中のコンテナにログインする
kubectl exec -it podname -- bash
Kubernetesではコンテナではなく、Podが最小のデプロイ単位であり、1つのPodないのコンテナはすべて同じマシン上に配置される
スケールさせる時もPod単位なので、例えばWordPressとMySQLを同じPodにすると片方だけスケールさせることができない
コンテナが違うマシンに配置されても正常に動作できるのであれば、Podを分けるのが正解な可能性が高い
Podの作成、確認、削除
kubectl run kuard --image=gcr.io/kuar-demo/kuard-amd64:1
kubectl get pods
kubectl delete deployments/kuardPodをyamlで作成する
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
name: kuard
ports:
- containerPort: 8081
name: http
protocol: TCP
kubectl apply -f kuard-pod.yaml
Podの詳しい情報の確認
kubectl describe pods kuard
詳細を確認するオプション
kubectl get pods -o wide
kubectl get pods -o json
kubectl get pods -o yaml
kubectl delete pods/kuard
kubectl delete -f kuard-pod.yaml
Podの削除コマンドを実行した時に、まずTerminatingという状態になりデフォルト30秒の猶予期間(grace period)後に削除される。Terminating状態になるとそのPodはリクエストを受け付けない。この猶予期間によって処理中の可能性のあるリクエストをPodを削除する前に終わられることができる。
ポートフォワード(コマンド実行中のみ有効)
kubectl port-forward kuard 8081:8080
ログの確認
kubectl logs kuard
kubectl logs -f kuard
kubectl logs --previous kuard
ヘルスチェック
プロセスヘルスチェック機能によってアプリケーションのメインプロセスが動いているか常に監視し、動いていない場合はKubernetesがプロセスを再起動する。プロセスが生きているものの、リクエストには応答できていない場合にはこれだけでは対応できない。
Liveness probe
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
name: kuard
livenessProbe:
httpGet:
path: /healthy
port: 8080
initialDelaySeconds: 5
timeoutSeconds: 1
periodSeconds: 10
failureThreshold: 3
ports:
- containerPort: 8081
name: http
protocol: TCP
initialDelaySecondsはPod内の全コンテナが作成されてから5秒経過するまでリクエストを送らない
timeoutSecondsの1秒以内に応答する必要がある
HTTPチェックだけでなくtcpSocketもサポートしている
exec監視も可能で、コンテナ内でスクリプトの返り値が0なら成功、0以外なら失敗のようなこともできる
https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
リソース要求 最低限必要なリソース
リソース制限 最大リソース量
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
name: kuard
resources:
requests:
cpu: "500m"
memory: "128Mi"
ports:
- containerPort: 8081
name: http
protocol: TCP
1CPUの半分および128MBをリソース要求に設定する例
CPUのリソース要求はLinuxカーネルのcpu-shares機能を使って実装されている
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
name: kuard
resources:
requests:
cpu: "500m"
memory: "128Mi"
limits:
cpu: "1000m"
memory: "256Mi"
ports:
- containerPort: 8081
name: http
protocol: TCP
リソース制限に1CPU、メモリ256MBを設定する例
データの永続化
spec.volumnsセクションもしくはvolumnMountsを使用する
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
volumes:
- name: "kuard-data"
hostPath:
path: "/var/lib/kuard"
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
name: kuard
volumeMounts:
- mountPath: "/data"
name: "kuard-data"
ports:
- containerPort: 8081
name: http
protocol: TCP
kuard-dataというボリュームを定義し、kuardコンテナの/dataにマウントする例
empDirはキャッシュに便利
NFSサーバーを利用することも可能
volumes:
- name: "kuard-data"
nfs:
server: my.nfs.server.local
path: "/exports"
# LabelとAnnotation
LabelとAnnotationの使い分けは、好みにもよる。セレクタとして使いたくなったらLabelに昇格させるのが良い
kubectl run alpaca-prod --image=gcr.io/kuar-demo/kuard-amd64:1 --replicas=2 --labels="ver=1,app=alpaca,env=prod"
kubectl run alpaca-test --image=gcr.io/kuar-demo/kuard-amd64:2 --replicas=1 --labels="ver=2,app=alpaca,env=test"
kubectl run bandicoot-prod --image=gcr.io/kuar-demo/kuard-amd64:2 --replicas=2 --labels="ver=2,app=bandicoot,env=prod"
kubectl run bandicoot-staging --image=gcr.io/kuar-demo/kuard-amd64:2 --replicas=1 --labels="ver=2,app=bandicoot,env=staging"
Labelの確認
Labelの確認
kubectl get deployments --show-labels
kubectl get deployments -L env
kubectl get pods --selector="ver=2"
kubectl get pods --selector="ver!=2"
kubectl get pods --selector="app=bandicoot,ver=2"
kubectl get pods --selector="app in (alpaca,bandicoot)"
kubectl get pods --selector="app notin (alpaca,bandicoot)"
kubectl get deployments --selector="canary"
kubectl get deployments --selector="!canary"
Labelの追加(ReplicaSetやPodは変更されないので注意)
kubectl label deployments alpaca-test "canary=true"
Deploymentの削除
kubectl delete deployments --selector="canary"
kubectl delete deployments --all
# サービスディスカバリ
kubectl run alpaca-prod --image=gcr.io/kuar-demo/kuard-amd64:1 --replicas=3 --port=8080 --labels="ver=1,app=alpaca,env=prod"
kubectl expose deployment alpaca-prod
kubectl run bandicoot-prod --image=gcr.io/kuar-demo/kuard-amd64:2 --replicas=2 --port=8080 --labels="ver=2,app=bandicoot,env=prod"
kubectl expose deployment bandicoot-prod
kubectl get services -o wide
ALPACA_POD=$(kubectl get pods -l app=alpaca -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward $ALPACA_POD 48858:8080Readiness probe
kubectl edit deployment/alpaca-prod
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
imagePullPolicy: IfNotPresent
name: alpaca-prod
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 2
initialDelaySeconds: 0
failureThreshold: 3
successThreshold: 1
ports:
- containerPort: 8080
protocol: TCP
ServiceのEndpointsに対するトラフィックの確認
kubectl get endpoints alpaca-prod --watch
Endpointsオブジェクトの確認
kubectl describe endpoints alpaca-prod
新しいServiceが作られるとkube-proxyは、iptablesのルールをホストのカーネルに設定する
# ReplicaSet
冗長性、スケール、シャーディングのために用意されているのがReplicaSet
ReplicaSetはPodのLabelを使ってクラスタの状態を監視する
不具合のあるPodのLabelだけを変更し、 ReplicaSetからPodを切り離すと、Podは起動したままになるのでデバッグがしやすくなる
ReplicaSetの最小限の定義例(kuard-rs.yaml)
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: kuard-rs
spec:
replicas: 1
template:
metadata:
labels:
app: kuard
version: "2"
spec:
containers:
- name: kuard
image: "gcr.io/kuar-demo/kuard-amd64:2"
kubectl apply -f kuard-rs.yaml
PodからReplicaSetの特定
kubectl get pods kuard-rs-4m2nj -o yaml | grep owner -A
ReplicaSetに対応するPodの集合の特定
kubectl get pods -l app=kuard,version=2
kubectl scale replicasets kuard-rs --replicas=4
宣言的スケール
yamlのreplicasの値を書き換え後にkubectl apply -f kuard-rs.yaml
水平Podオートスケーリング(HPA Horizontal Pod Autoscaling)
heapsterというPodによりメトリクスを追跡し、HPAがスケーリングの判断を行う時に使用するメトリクスを取得するAPIを提供する
クラスタ内にheapsterが存在しない場合はHPAは正常に動作しない
Podのレプリカを追加することを水平スケール、Podに必要なリソースを増やすことを垂直スケールと区別している
CPU使用率を元にしたオートスケール
kubectl autoscale rs kuard-rs --min=2 --max=5 --cpu-percent=80
CPU使用率80%を閾値にして、レプリカ数2〜5の間でスケールするオートスケーラ
確認
kubectl get hpa
ReplicaSetを削除すると管理しているPodも一緒に削除される
kubectl delete rs kuard-rs
ReplicaSetのみ削除したい場合
kubectl delete rs kuard-rs --cascade=false
kubectl delete rs kuard-rs --cascade=false
ReplicaSetと違い、ノードセレクタを使わない限り、全ノードにPodを作る
fluentd.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
labels:
app: fluentd
spec:
template:
metadata:
labels:
app: fluentd
spec:
containers:
- name: fluentd
image: fluent/fluentd:v0.14.10
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
kubectl apply -f fluentd.yaml
NodeにLabelを追加し、確認
kubectl label nodes minikube ssd=true
kubectl get nodes --show-lables
kubectl get nodes --selector ssd
nginx-fast-storage.yaml(ssd=trueというLabelがノードでのみ動くDaemonSet)
apiVersion: extensions/v1beta1
kind: "DaemonSet"
metadata:
labels:
app: nginx
ssd: "true"
name: nginx-fast-storage
spec:
template:
metadata:
labels:
app: nginx
ssd: "true"
spec:
nodeSelector:
ssd: "true"
containers:
- name: nginx
image: nginx:1.10.0
他のノードにssd=trueというLabelを追加すれば、そのノードにもデプロイされる。逆にLabelを削除するとそのPodはDaemonSetコントローラから削除される
forループを用いたPodの削除
PODS=$(kubectl get pods -o jsonpath -template='{.ites[*].metadata.name}')
for x in $PODS; do
kubectl delete pods ${x}
sleep 60
done
ローリングアップデートを使うにはspec.updateStrategy.typeフィールドにRollingUpdateを設定する。ローリングアップデート中のステータスの確認はkubectl rolloutコマンドを使用する
kubectl rollout status daemonSets nginx-fast-storage
DaemonSetの削除
kubectl delete -f fluentd.yaml
# Job
1回限りの処理を行うのに便利なのがJobオブジェクト
kubectl run -i oneshot --image=gcr.io/kuar-demo/kuard-amd64:1 --restart=OnFailure -- --keygen-enable --keygen-exit-on-complete --keygen-num-to-gen 10
-iオプションはコマンドが対話モードであることを表す
--restart=OnFailureはkubectlにJobオブジェクトを作成するよう指示する
完了後のPodも含めて表示する
kubectl get pods -a
yamlでJobを作成する例
kubectl apply -f job-oneshot.yaml
失敗するJobの例
kubectl apply -f job-oneshot-failure1.yaml
kubectl get pods -a -l job-name=oneshot
KubernetesはこのPodがCrashLoopBackOffだと認識している。クラッシュの繰り返しによってノードのリソースが使われ過ぎないように、Podの再起動まで自動で少し待つ。
restartPolicy: Neverに設定するとkubeletはJob失敗時にPodを再起動せず、代わりにPodを失敗と宣言し、代わりのPodを作成する。restartPolicy: OnFailureにしておくことを推奨。
一定数成功するまで並列実行
kubectl apply -f job-parallel.yaml
kubectl get pods -w
--watchフラグによって時間とともにPodの一覧が更新されることを確認できる
並列実行キューを実装する例
1. 1回限りのキューデーモンを管理するために、ReplicaSetを作成する
kubectl apply -f rs-queue.yaml
2. キューに接続するためにポートフォワードを設定する
QUEUE_POD=$(kubectl get pods -l app=work-queue,component=queue -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward $QUEUE_POD 8080:8080
MemQ Serverを開いてキューが動作していれば、キューのプロデューサとコンシューマがDNS経由でキューを使える状態。
3. キューのServiceを作成する
kubectl apply -f service-queue.yaml
4. サブタスクをキューに入れ、状態を確認
for in in work-item-{0..99}; do
curl -X POST localhost:8080/memq/server/queues/keygen/enqueue -d "$i"
done
curl 127.0.0.1:8080/memq/server/stats
5. コンシューマJobを作成する
kubectl apply -f job-consumers.yaml
kubectl get podsで5つのPodが並列に動作していることがわかる
# ConfigMapとSecret
ConfigMapにはワークロードに応じた設定情報を保存する
SecretにはパスワードやTLS証明書などの機密情報を保存する
ConfigMapの作成
my-config.txt
parameter1 = value1
parameter2 = value2
kubectl create configmap my-config --from-file=my-config.txt --from-literal=extra-param=extra-value --from-literal=another-param=anather-value
kubectl get configmaps my-config -o yaml
ConfigMapの使用
kuard-config.yaml
apiVersion: v1
kind: Pod
metadata:
name: kuard-config
spec:
containers:
- name: test-container
image: gcr.io/kuar-demo/kuard-amd64:1
imagePullPolicy: Always
command:
- "/kuard"
- "$(EXTRA_PARAM)"
env:
- name: ANOTHER_PARAM
valueFrom:
configMapKeyRef:
name: my-config
key: another-param
- name: EXTRA_PARAM
valueFrom:
configMapKeyRef:
name: my-config
key: extra-param
volumeMounts:
- name: config-volume
mountPath: /config
volumes:
- name: config-volume
configMap:
name: my-config
restartPolicy: Never
kubectl apply -f kuard-config.yaml
kubectl port-forward kuard-config 8080
Secretの作成
サンプルのTLSキーと証明書をダウンロードする(この例以外では使用しないこと)
curl -o kuard.crt https://storage.googleapis.com/kuar-demo/kuard.crt
curl -o kuard.key https://storage.googleapis.com/kuar-demo/kuard.key
kubectl create secret generic kuard-tls --from-file=kuard.crt --from-file=kuard.key
kubectl describe secrets kuard-tls
Secretの使用
今回の例ではSecret volumeを/tlsにマウントすると/tls/kuard.crtおよびtls/kuard.keyにアクセス可能になる
今回の例ではSecret volumeを/tlsにマウントすると/tls/kuard.crtおよびtls/kuard.keyにアクセス可能になる
kuard-secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: kuard-tls
spec:
containers:
- name: kuard-tls
image: gcr.io/kuar-demo/kuard-amd64:1
imagePullPolicy: Always
volumeMounts:
- name: tls-certs
mountPath: "/tls"
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: kuard-tls
kubectl apply -f kuard-secret.yaml
kubectl port-forward kuard-tls 8443:8443
プライベートDockerレジストリ
kubectl create secret docker-registry my-image-pull-secret \
--docker-username=xxx \
--docker-password=xxx \
--docker-email=xxx
yamlのサンプル kuard-secret-ips.yaml
ConfigMapとSecretの管理
kubectl get secrets
kubectl get configmaps
kubectl describe configmap my-config
kubectl port-forward kuard-tls 8443:8443
プライベートDockerレジストリ
kubectl create secret docker-registry my-image-pull-secret \
--docker-username=xxx \
--docker-password=xxx \
--docker-email=xxx
yamlのサンプル kuard-secret-ips.yaml
ConfigMapとSecretの管理
kubectl get secrets
kubectl get configmaps
kubectl describe configmap my-config
# Deployment
Deploymentを使うとダウンタイムやエラーを発生させずに新しいバージョンのソフトウェアをシンプルにロールアウトできる。
kubectl scale deployments nginx --replicas=2
Deploymentをスケールすると、その管理下にあるReplicaSetもスケールされる
kubectl get deployments nginx --export -o yaml > nginx-deployment.yaml
kubectl replace -f nginx-deployment.yaml --save-config
kubectl describe deployments nginx
ロールアウト中はOldReplicaSetsフィールドに値が入る
コマンドではなくyamlでスケールさせるには以下の部分を更新する
spec:
replicas: 3
ロールアウトを中断・再開する
kubectl rollout pause deployments nginx
kubectl rollout resume deployments nginx
ロールアウトの履歴を確認する
kubectl rollout history deployments nginx
kubectl rollout history deployments nginx
kubectl rollout history deployments nginx --revision=2
ロールアウトのロールバック
kubectl rollout undo deployments nginx
kubectl rollout undo deployments nginx
履歴の数を制限する方法
spec:
revisionHistoryLimit: 14
Deployment戦略
Recreate: Deploymentに基づいたPodを全て停止することでReplicaSetに新しいイメージを作成させる。シンプルで高速だが障害を起こす可能性があり、ダウンタイムも避けられない。
Rolling Update: ある一定の期間、新旧バージョンの両方がリクエストを受けトラフィックを処理する。クライアント側が新旧どちらとも連携できることが重要になってくる。
maxUnavailableパラメータはローリングアップデート中に使用不可能になってもいいPodの最大数を指定する。絶対値もしくはパーセンテージで指定する。この値はローリングアップデートを進める速度にも関わってくる。
maxSurgeパラメータはロールアウト時にどのぐらいの追加リソースを作れるかを制御する。maxUnavailableを0、maxSurgeを20%に設定すると、レプリカ数を1.2倍に増やしてからレプリカの割合を旧0.8 新0.2へ更新する。maxSurgeを100%に設定するとブルーグリーンデプロイメントを行える。
minReadySecondsを使用するとPodが起動してから指定した時間後に次のアップデートに移る
spec:
minReadySeconds: 60
タイムアウトを設定するにはprogressDeadlineSecondsを使用する
spec:
progressDeadlineSeconds: 600
progressDeadlineSeconds: 600
# ストレージソリューションとKubernetesの統合
セレクタのないService
dns-service.yaml
kind: Service
apiVersion: v1
metadata:
name: external-database
spec:
type: ExternalName
externalName: "database.company.com
ExternalNameというタイプのServiceを作成すると、外部のDNS名を示すAレコードの代わりにCNAMEレコードを作成する。クラスタ内のアプリケーションがexternal-database.default.svc.cluster.localというホスト名をDNS名前解決すると、DNSプロトコルがdatabase.company.comに名前をエイリアスする。セレクタのないService
dns-service.yaml
kind: Service
apiVersion: v1
metadata:
name: external-database
spec:
type: ExternalName
externalName: "database.company.com
IPアドレスのみ提供されている場合はサービスとエンドポイントを作成する必要がある
external-ip-service.yaml
kind: Service
apiVersion: v1
metadata:
name: external-ip-database
external-ip-endpoints.yaml
kind: Endpoints
apiVersion: v1
metadata:
name: external-ip-database
subsets:
- addresses:
- ip: 192.168.0.1
ports:
- port: 3306
Persistent Volumnの例
nfs-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: database
labels:
volume: my-volume
spec:
capacity:
storage: 1Gi
nfs:
server: 192.168.0.1
path: "/exports"
PersistentVolumeClaimオブジェクトを使ってPersistentVolumnをPodから取得する
nfs-volume-claim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: database
spec:
resources:
requests:
storage: 1Gi
selector:
matchLabels:
volume: my-volume
kubectl apply -f mongo-simple.yaml
StatefulSet作成後はDNSエントリを管理するヘッドレスなServiceも作る必要がある。
kubectl apply -f mongo-service.yaml
kubectl exec mongo-0 bash ping mongo-1.mongo
# 実用的なアプリケーションのデプロイ
Parse, Ghost, Redisのデプロイ方法のサンプル