2021/09/30
Kubernetesでの運用のTipsまとめ
弊社はサーバーアプリケーションのほぼ全てをkubernetesを使って運用しています。
主にGKE(GCPのk8sサービス)とEKS(AWSのk8sサービス)を使っていますが、オフィス内では自前で立てたUbuntuサーバーを活用しています。
数十のプロジェクトをk8sで運用しており、以下の内容を記事として残しておきます。
- 普段からよくやるやり方
- 運用上たまにある「あれってどうやったっけ?」的なもの
あと、これは私のポリシーなのですが、kubectl
をラップしたようなツールはなるべく使わないようにしています。(例えばk9s
)
helm chart
などもツールはそのまま使わずに、helm chart
が出力するk8sの設定ファイルを実際に自分でyamlで書いて使っています。
k8s configの切り替え
プロジェクトの切り替えにかなり便利で、もうこれなしでは運用が成り立たないです。
プロジェクトのフォルダに入ったら、そのプロジェクトが使っているk8s apiサーバーの設定を切り替える方法です。
direnvを使って、KUBECONFIG
環境変数を切り替えて利用します。
これをすると、プロジェクトのフォルダにターミナルで入ってdirenv edit .
でENV
を切り替えると、k8s api serverの向き先がかわります。
以下設定例です。
.envrc
ENV=prd # dev/stg/prd などの環境を分ける際の識別子
export KUBECONFIG=$(pwd)/k8s/${ENV}/.kube/config
configが切り替わっているか確認
EKSを使っているとaws
としか表示されないのですが、GCPなどではどのクラスターと繋がっているかわかるので便利です。
% kubectl config current-context
podsの更新を監視する
kubectl apply
などを使って、デプロイした時の監視によく利用しています。podsのstatusの変更をwatchしてくれます。(-w
オプションを付ければ良いだけ)
% kubectl get pods -n your-namespace -w
特定のpodにログインする
まずは、kubectl get pods
でpod名をみます。pod名がわかったら、以下のようにしてログインします。
% kubectl exec -it your-pod-name -n your-namespace -- bash
# bashがcontaner内にない場合(だいたいインストールしていますが、たまにない場合は/bin/shを指定します)
% kubectl exec -it your-pod-name -n your-namespace -- /bin/sh
毎回、pod名を見るのがめんどくさい場合(deploymentなどではpod名が頻繁に変更になるので)は以下のようにしています。
grep
に引っかかった最初のpodにログインできます。
kubectl exec -it $(kubectl get pods -n your-namespace -o name | grep filter) -n your-namespace -- bash
サイドカーのpodを動かしている場合は -c
オプションをつけてコンテナ名を指定する必要があります。
% kubectl exec -it your-pod-name -n your-namespace -n your-contaner-name -- bash
特定のpodのログを監視する
以下のようにすると、ログを監視することができます。--tail
オプションで最後の50行を最初に表示して、-f
オプションをつけて監視します。
% kubectl logs your-pod-name -n your-namespace --tail=50 -f
ローカルのportにバインドする(port-forward)
例えば、your-service-name
というserviceをローカル上で使いたい場合は以下のようにします。
% kubectl port-forward svc/your-service-name -n your-namespace 8080:80
例えば、serviceではなく、podに対してもできます。
複数ポートの紐付けもできます。
% kubectl port-forward your-pod-name -n your-namespace 8443:443 8080:80
特定のpodを検索して全て削除
EvictedしてたりShutdownしてしまったpodを一括で削除します。
何らかの原因で、nodeが再起動したり、スケールアウトしたりする際に起きたりします。
以下はyour-namespace1
、your-namespace2
、your-namespace3
のShutdown
しているpodを全て削除しています。
※ Shutdown
という名前が含まれているpodも削除してしまうかもしれませんので注意してください。
namespaces=(your-namespace1 your-namespace2 your-namespace3)
for namespace in ${namespaces[@]}; do
echo "Process ${namespace}"
shutdown_pods=`kubectl get pods -n ${namespace} | grep Shutdown | awk '{print $1}'`
for pod in ${=shutdown_pods}; do
echo "Delete ${pod}..."
kubectl delete pods -n ${namespace} --force --grace-period=0 ${pod}
done
done
podを立ててPVCの中身を見る
PVCのdiskの中身を見たいときに使います。nginx
が動くpodにdiskをくっつけて起動します。
podが起動したら、bashでログインして、/root/data
の中身を見て、必要があれば操作します。
apiVersion: v1
kind: Pod
metadata:
name: check-data
namespace: your-namespace
spec:
containers:
- name: check-data
image: nginx
volumeMounts:
- name: disk-data
mountPath: /root/data
volumes:
- name: disk-data
persistentVolumeClaim:
claimName: your-pvc-name
PVCのdiskを拡張した時
例えば、容量が少なくなってしまった(または足りなくなってエラーになってしまった)みたいな時によくやります。
以下はGKE/GCPなどでやる例です。
- podを停止して、diskを解放します。
- diskの容量を拡張します。(拡張しても、パーティションが拡張されていないので注意です。)
- diskをVMインスタンスにくっつけて作成・起動してsshします。
resize2fs
コマンドを使って、拡張します。
$ ls /dev/disk/by-id/
$ sudo mkdir -p /mnt/disks/extend
$ sudo mount -o defaults /dev/disk/by-id/google-your-disk-name /mnt/disks/extend
$ sudo df -h
$ sudo resize2fs /dev/sdb
$ sudo df -h
この手順で、拡張したdiskを再びPVCとして利用します。
SecretをBase64ではない生の文字列で定義
結構便利です。stringData
を使うことで、base64エンコーディングしないでyamlファイルに定義できます。
apiVersion: v1
kind: Secret
metadata:
name: your-secret-name
namespace: your-namespace
type: Opaque
stringData:
example.json: |-
{
"password": "123456"
}
特定のnode poolにしかpodがデプロイされないようにする
affinity.requiredDuringSchedulingIgnoredDuringExecution
を使います。
以下はGKEでの例です。
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "cloud.google.com/gke-nodepool"
operator: In
values:
- your-node-pool-name
アプリケーションログの出し方
warning以下は標準出力、errorは標準エラーに出力します。
(必要に応じて出力されたものをfluentdとかを使って転送します。)
pod内でDBやQueueなどのミドルウェアの監視
readinessProbe
で呼ぶapiのpath(以下では/healthz)で、アプリケーションサーバーが依存するDB/Queue/Redisなどが正常に動いているかチェックします。
問題があれば、SentryやAirbrakeなどに飛ばして通知します。
またこれらがNGになると自動的に、アプリケーションサーバーのpodも利用不可になるので、データの整合性の破壊を緩和することができます。
- DBの場合は
select 1
- Redisの場合は
ping
- Queueの場合はコネクションを張ってみてclose
設定例
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 10
periodSeconds: 30
cluster内の独自ドメイン対応(kube-dns編)
いわゆる /etc/resolver/your-domain.com
の対応です。
主にGKEではこの方式が利用できて便利です。
% kubectl edit configmap kube-dns -n kube-system
apiVersion: v1
# 以下の行を追加します。
data:
stubDomains: |
{
"your-domain.com": [
"XXX.XXX.XXX.XXX"
]
}
kind: ConfigMap
metadata:
labels:
addonmanager.kubernetes.io/mode: EnsureExists
name: kube-dns
namespace: kube-system
cluster内の独自ドメイン対応(coredns編)
主にEKSではこの方式が利用できて便利です。
% kubectl -n kube-system edit configmap coredns
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
your-domain.com:53 {
errors
cache 30
forward . XXX.XXX.XXX.XXX
reload
}
以上です。
またいろいろ追加あれば随時更新しようと思います。
関連する記事
[小〜中規模向け]GKEにTiDBをデプロイする
MySQL互換のNewSQLであるTiDBをGKEにデプロイしてみました。
NATS JetStream Controllerを使ってNATSをGKEにデプロイする
helm chartのnackを使って、NATS JetStreamサーバーをデプロイして、Stream/Consumerをk8sリソースとして管理する
GKEにDragonflydbをデプロイする
redis互換のdragonflydbをGKEにデプロイしました
[GKE]Kafka Strimziをアップグレードする
GKEにデプロイしているKafka Strimzi 0.26.0を0.30.0にアップグレードする