整理自「开发 Tips」系列,汇总 Kubernetes 集群日常运维中的常见问题与解决方法。
1. Kubectl 配置多个集群
在进行 Kubernetes 相关开发时,通常会涉及多个集群的管理。Kubectl 提供了多集群上下文管理的功能。
通常 Kubectl 的配置信息在 $HOME/.kube/config 或 /etc/kubernetes/admin.conf 。登陆机器,查看集群的配置信息,按照下面的格式进行编辑。
kubeconfig 配置格式
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
| apiVersion: v1
kind: Config
preferences: {}
clusters:
- cluster:
certificate-authority-data: xxx
server: xxx
name: { cluster-name1 }
- cluster:
certificate-authority-data: xxx
server: xxx
name: { cluster-name2 }
users:
- name: { user-name1 }
user: xxx
- name: { user-name2 }
user: xxx
contexts:
- context:
cluster: { cluster-name1 }
user: { user-name1 }
name: { context-name1 }
- context:
cluster: { cluster-name2 }
user: { user-name2 }
name: { context-name2 }
current-context: { context-name1 }
|
查看集群
1
| kubectl config get-contexts
|
查看 config 信息
切换集群
1
| kubectl config use-context {context-name}
|
2. helm 提示 cannot get resource “namespaces”
使用 helm 安装应用:
1
2
| helm install --name prometheus-operator --namespace=monitoring stable/prometheus-operator
Error: namespaces "monitoring" is forbidden: User "system:serviceaccount:kube-system:default" cannot get resource "namespaces" in API group "" in the namespace "monitoring"
|
解决办法,添加服务账号:
1
2
3
| kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
|
3. Kubernetes 中给 Node 增加 Role: worker
1
2
3
4
| kubectl get nodes
NAME STATUS ROLES AGE VERSION
i-6fns0nua Ready master 6d3h v1.15.2
i-m69skuyd Ready <none> 6d2h v1.15.2
|
1
2
| kubectl label node i-m69skuyd node-role.kubernetes.io/worker=
node/i-m69skuyd labeled
|
1
2
3
4
| kubectl get node
NAME STATUS ROLES AGE VERSION
i-6fns0nua Ready master 6d3h v1.15.2
i-m69skuyd Ready worker 6d2h v1.15.2
|
4. 删除 Kubernetes 的一个节点
查看当前节点:
1
2
3
4
| kubectl get node
NAME STATUS ROLES AGE VERSION
i-6fns0nua Ready master 6d3h v1.15.2
i-m69skuyd Ready worker 6d2h v1.15.2
|
迁移 Pod ,禁止调度待删除节点:
1
| kubectl drain i-m69skuyd --delete-local-data --force --ignore-daemonsets
|
删除节点:
1
| kubectl delete node i-m69skuyd
|
5. Kubernetes 上用 Helm 安装 Prometheus
首先需要创建 PV,可以参考第一章节。
然后,执行命令:
1
2
3
4
5
| helm install --namespace monitor --name prometheus stable/prometheus \
--set alertmanager.persistentVolume.storageClass="local" \
--set server.persistentVolume.storageClass="local"
helm install --namespace monitor --name grafana stable/grafana \
--set persistence.storageClassName="local"
|
其他操作可以参考,在 Minikube 集群上安装 Prometheus。
6. 如何重启 Kubernetes 中的 Pod 或服务
- 重启 Pod
如果 Pod 关联了副本控制器,直接删除 Pod,Kubernetes 会重新创建 Pod 。
1
| kubectl get pod -n {NAMESPACE}
|
1
| kubectl delete pod {POD_NAME} -n {NAMESPACE}
|
另外一种是,使用 replace 。
1
| kubectl replace --force -f pod.yaml
|
如果没有 pod.yaml 文件,可以直接使用下面的命令:
1
| kubectl get pod {POD_NAME} -n {NAMESPACE} -o yaml | kubectl replace --force -f -
|
- 重启 Deployment
将服务的副本数设置为 0,然后恢复原始的副本数。
1
| kubectl get deployment -n {NAMESPACE}
|
1
| kubectl scale deployment {DEPLOYMENT_NAME} --replicas=0 -n {NAMESPACE}
|
1
| kubectl scale deployment {DEPLOYMENT_NAME} --replicas={REPLICAS_NUM} -n {NAMESPACE}
|
7. 通过 NodePort 的方式,暴露正在运行的服务
在服务没有启动之前,通过修改 yaml 配置,可以暴露服务。当服务已经运行起来之后,可以通过 patch 的方式暴露服务。
1
| kubectl get service -n {NAMESPACE}
|
1
| kubectl patch service {SERVICE_NAME} -p '{"spec":{"type":"NodePort"}}' -n {NAMESPACE}
|
8. NodePort 服务仅指定 Node 可以访问
通过 NodePort 暴露的服务,在集群外可以使用 Kubernetes 任意 Node IP 加端口的形式访问。kube-proxy 会将访问流量以轮询的方式转发给 service 中的每个 Pod。
但是,发现并不是每一个 Node IP 加端口都可以访问,仅运行 Pod 的 Node 可以。
原因是,任意 Node IP 加端口访问,是通过主机间通信实现的。但是 docker 1.13 版本之后对 iptables 规则进行了改动,默认禁用了 FORWARD 。
查看 iptables 规则:
1
2
3
4
5
| iptables -L -n
...
Chain FORWARD (policy DROP)
...
|
打开全局 FORWARD :
1
| iptables -P FORWARD ACCEPT
|
9. 登陆 Kubernetes 中的容器终端
1
| kubectl exec -it {POD_NAME} -c {CONTAINER_NAME} -n {NAMESPACE_NAME} sh
|
如果 Pod 中只有一个 Container,则 -c {CONTAINER_NAME} 参数可以省略。
1
2
3
| kubectl get pod -n monitor
monitor prometheus-alertmanager-5bc4ccf9df-xmt7c 2/2 Running 6 3d23h
|
1
2
3
4
| kubectl log prometheus-alertmanager-5bc4ccf9df-xmt7c -n monitor
log is DEPRECATED and will be removed in a future version. Use logs instead.
Error from server (BadRequest): a container name must be specified for pod prometheus-alertmanager-5bc4ccf9df-xmt7c, choose one of: [prometheus-alertmanager prometheus-alertmanager-configmap-reload]
|
提示信息中,[prometheus-alertmanager prometheus-alertmanager-configmap-reload] 就是 Pod 中全部容器列表。
kubectl exec -it prometheus-alertmanager-5bc4ccf9df-xmt7c -c prometheus-alertmanager -n monitor sh
10. 批量删除 PVC
1
2
| kubectl get pvc -A | awk '{print $2}' |grep {KEYWORD} |xargs kubectl delete pvc -n
{NAME_SPACE}
|
11. 使用 StorageClass 提供 PV 动态存储
StorageClass 动态存储的特点是,管理员只需要创建存储服务、服务相关的 provisioner,而不用指定 PV 的大小。当用户使用存储时,只需要创建 PVC,Provisioner 会自动创建 PV 与之匹配。
在 CentOS 上搭建 NFS 服务,参考链接 。
安装 nfs-client-provisioner
1
| helm install --name nfs-client --set nfs.server=x.x.x.x --set nfs.path=/data stable/nfs-client-provisioner
|
1
2
| helm repo add stable https://charts.helm.sh/stable
helm install nfs-client stable/nfs-client-provisioner --set nfs.server=x.x.x.x --set nfs.path=/data
|
但 nfs-client-provisioner 已经停止维护,同时不支持高版本的 Kubernetes,推荐使用 csi-nfs 方案。
- 查看 StorageClass
1
2
3
| kubectl get sc
nfs-client cluster.local/nfs-client-nfs-client-provisioner 18s
|
- 指定 DefaultStorageClass
1
| kubectl patch storageclass nfs-client -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
|
- 创建一个 PVC 测试
新建文件 pvc.yaml
1
2
3
4
5
6
7
8
9
10
| apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 30Gi
|
执行命令:
1
| kubectl create -f pvc.yaml
|
查看 PVC:
1
2
3
4
| kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc1 Bound pvc-24e4dfc1-cb8b-444b-8e3c-36ec8350df3c 30Gi RWX nfs-client 2m
|
可以看到 pvc1 状态已经是 Bound,同时在 nfs 共享目录下发现,新文件夹 default-pvc1-pvc-24e4dfc1-cb8b-444b-8e3c-36ec8350df3c 。
12. 部署 csi-nfs
1
2
| helm repo add csi-driver-nfs https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts
helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system --version v4.9.0
|
1
2
3
4
5
6
7
8
9
10
11
12
| apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-csi-node1
provisioner: nfs.csi.k8s.io
parameters:
server: x.x.x.x
share: /data/nfs
reclaimPolicy: Delete
volumeBindingMode: Immediate
mountOptions:
- nfsvers=4.1
|
有些公有云的 NFS 可能不支持 nfsvers=4.1,可以尝试去掉这个参数。
1
2
3
4
5
6
7
8
9
10
11
| apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-csi-node1-test
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 30Gi
storageClassName: nfs-csi-node1
|
13. Kubernetes 集群添加新的 Node 节点
在执行 kubeadm init 时,Console 会打印添加 Node 的命令。Token 默认的有效期为 24h 。当超过有效期时,需要重新创建 Token ,执行命令:
1
2
| kubeadm token create --print-join-command
kubeadm join 192.168.10.2:6443 --token ocyzce.3hv8y7w60lrvulir --discovery-token-ca-cert-hash sha256:7a86632f54de1004bb3f38124b663f837399d6ba9aa803d58c6707a76c02a6cb
|
使用 Console 输出的命令,即可将 Node 节点添加到集群。
14. 控制 Node 节点的调度
1
| kubectl uncordon {NODE_NAME}
|
1
| kubectl cordon {NODE_NAME}
|
15. Ingress 开启 HTTPS
准备好证书,domain.com.crt、domain.com.key
- 创建 Secret
1
| kubectl create secret tls {SECRET_NAME} --key domain.com.key --cert domain.com.crt -n {NAMESPACE}
|
- 更新 Ingress 配置
1
2
3
4
5
| spec:
tls:
- hosts:
- domain.com
secretName: { SECRET_NAME }
|
16. 强制删除 Kubernetes 资源
1
| kubectl delete --force --grace-period=0 {RESOURCE_NAME}
|
通常无法删除,是因为有些 finalizer 关联的动作未执行成功。如果一定要删除,可以试试下面的命令:
1
| kubectl get namespace myns -o json | tr -d "\n" | sed "s/\"finalizers\": \[[^]]\+\]/\"finalizers\": []/"| kubectl replace /api/v1/namespaces/myns/finalize -f -
|
1
| ETCDCTL_API=3 etcdctl --endpoints=https://[127.0.0.1]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key del --prefix=/registry/namespaces/{NAMESPACENAME}
|
17. Kubernetes 服务仅在负载节点可用
正常情况下 NodePort 类型的 Service ,任意 Node 节点 IP + 端口,都可以访问。但是,也有可能仅负载的 Node 节点 IP + 端口可以访问。
首先,可以尝试配置转发相关参数:
1
2
3
4
5
6
| cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward=1
vm.swappiness=0
EOF
|
另外一种可能是防火墙,没有豁免 IPENCAP 协议:
Calico 网络插件,有两种模式 BGP 、IPIP。使用 IPIP 模式时,需要在防火墙中,开启 IPENCAP 协议。
18. kubectl 获取全部使用的镜像
1
| kubectl get pod --all-namespaces -o=jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{range .spec.initContainers[*]}{.image}{"\n"}{end}{end}' | sort -u
|
19. 使用 Telepresence 远程调用 Kubernetes
Telepresence 是 CNCF 基金会下的一个项目。它的工作原理是在本地和 Kubernetes 集群之间,搭建一个透明的双向代理。
使用 Telepresence 可以实现的功能:
- 本地的服务就可以完整的访问到远程集群中的其他服务。
- 本地的服务直接访问到 Kubernetes 里的各种资源,包括环境变量、Secrets、ConfigMap 等。
- 集群直接访问到本地暴露出来的接口。
- kubectl 安装配置。
在本地,安装并配置 kubectl ,使其可以正常访问 Kubernetes 集群。
- 安装 Telepresence
1
2
| brew cask install osxfuse
brew install datawire/blackbird/telepresence
|
- 本地连通远程分支
- 本地访问远程集群服务
找到服务域名:
1
2
3
| NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 2h
myservice ClusterIP 10.233.4.163 <none> 8000/TCP 2m
|
打开本地任意 Console ,即可直接访问集群内部服务:
1
| curl http://myservice:8000
|
- 远程集群访问本地服务
远程集群通过端口 8000 ,访问本地 8080 端口服务,有两种方式:
1
| telepresence --new-deployment new_deploy_name --expose 8080:8000
|
1
| telepresence --swap-deployment existed_deploy_name --expose 8080:8000
|
20. 重启 Kubernetes 中的 Job 任务
1
| kubectl -n {NAMESPACE} get job {JOB_NAME} -o json | jq 'del(.spec.selector)' | jq 'del(.spec.template.metadata.labels)' | kubectl replace --force -f -
|
如果提示没有找到 jq 命令,需要先按照 jq ,yum install -y jq。
21. macOS 快速切换不同 Kubernetes 环境
涉及 Kubernetes 相关开发时,经常需要在多个集群之间切换。配置多集群 context 是一个选择,但是如果集群在不断重置,可以试下如下方法:
在 ~/.profile 文件中定义一系列相关 function,切换时只需要执行 on_cluster_name 即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Switch Kubernetes Cluster
function switch_kubeconfig(){
sudo sed -i "" "/$2/d" /etc/hosts
sudo echo "$1 $2" >> /etc/hosts
if test -f ~/.ssh/known_hosts; then
sed -i "" '/kubernetes.default/d' ~/.ssh/known_hosts
fi
sshpass -p "your_password" ssh -o StrictHostKeyChecking=no root@$1 "cat /etc/kubernetes/admin.conf" > ~/.kube/config
}
function on_dev1(){
switch_kubeconfig 10.10.10.11 kubernetes.default
}
|
22. 给 Pod 附加容器,调试 Kubernetes 运行环境
在 Service Mesh 中,为了减少对原有系统的干扰,推荐采用 Sidecar 模式进行设计和实践。Sidecar 设计模式可以给应用程序添加新的功能,而无需额外第三方组件的配置和代码。
借助 Sidecar ,可以对运行中的容器或未运行的容器环境进行处理,解决一些运维问题。
第一步,添加 Sidecar 容器,共享存储、网络等
1
2
3
4
5
6
7
8
9
10
11
12
13
| Containers:
- name: sidecar
image: busybox:1.28.4
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- name: db-persistent-storage
mountPath: /var/lib/mysql
|
第二步,进入 Sidecar 容器解决运维故障
1
| kubectl exec -it {POD_NAME} -c sidecar sh
|
参考,Istio Sidecar 注入:例外和除错。
23. 使用 Journalctl 查看日志
Systemd 是 Linux 系统工具,用来启动守护进程。journald 收集由内核、initrd 以及服务等产生的日志信息。
只需要 journalctl 一条命令,就可以查看所有日志。