Please enable Javascript to view the contents

Kubernetes 集群运维实践

 ·  ☕ 7 分钟

整理自「开发 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 view

切换集群

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 或服务

  1. 重启 Pod

如果 Pod 关联了副本控制器,直接删除 Pod,Kubernetes 会重新创建 Pod 。

  • 查看 Pod
1
kubectl get pod -n {NAMESPACE}
  • 删除 Pod
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 -
  1. 重启 Deployment

将服务的副本数设置为 0,然后恢复原始的副本数。

  • 查看服务
1
kubectl get deployment -n {NAMESPACE}
  • 设置副本数为 0
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} 参数可以省略。

  • 获取 Pod 名称
1
2
3
kubectl get pod -n monitor

monitor       prometheus-alertmanager-5bc4ccf9df-xmt7c         2/2     Running   6          3d23h
  • 获取 Container 名称
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 中全部容器列表。

  • 登陆 Container 终端
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 与之匹配。

  1. 在 CentOS 上搭建 NFS 服务,参考链接

  2. 安装 nfs-client-provisioner

  • helm2
1
helm install --name nfs-client --set nfs.server=x.x.x.x --set nfs.path=/data stable/nfs-client-provisioner
  • helm3
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 方案。

  1. 查看 StorageClass
1
2
3
kubectl get sc

nfs-client        cluster.local/nfs-client-nfs-client-provisioner   18s
  1. 指定 DefaultStorageClass
1
kubectl patch storageclass nfs-client -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
  1. 创建一个 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

  • 安装 cis-driver-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
  • 创建 StorageClass
 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,可以尝试去掉这个参数。

  • 创建测试 PVC
 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 节点的调度

  • 允许调度 Pod
1
kubectl uncordon {NODE_NAME}
  • 禁止调度 Pod
1
kubectl cordon {NODE_NAME}

15. Ingress 开启 HTTPS

准备好证书,domain.com.crt、domain.com.key

  1. 创建 Secret
1
kubectl create secret tls {SECRET_NAME} --key domain.com.key --cert domain.com.crt -n {NAMESPACE}
  1. 更新 Ingress 配置
1
2
3
4
5
spec:
  tls:
    - hosts:
        - domain.com
      secretName: { SECRET_NAME }

16. 强制删除 Kubernetes 资源

  • 使用 --force 删除
1
kubectl delete --force --grace-period=0 {RESOURCE_NAME}
  • 修改 finalizers

通常无法删除,是因为有些 finalizer 关联的动作未执行成功。如果一定要删除,可以试试下面的命令:

1
kubectl get namespace myns -o json | tr -d "\n" | sed "s/\"finalizers\": \[[^]]\+\]/\"finalizers\": []/"| kubectl replace /api/v1/namespaces/myns/finalize -f -
  • 在 etcd 中删除
1
yum install -y etcd
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
1
sysctl --system

另外一种可能是防火墙,没有豁免 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 等。
  • 集群直接访问到本地暴露出来的接口。
  1. kubectl 安装配置。

在本地,安装并配置 kubectl ,使其可以正常访问 Kubernetes 集群。

  1. 安装 Telepresence
1
2
brew cask install osxfuse
brew install datawire/blackbird/telepresence
  1. 本地连通远程分支
1
telepresence
  1. 本地访问远程集群服务

找到服务域名:

1
kubectl get svc
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
1
Hello, world!
  1. 远程集群访问本地服务

远程集群通过端口 8000 ,访问本地 8080 端口服务,有两种方式:

  • 新建 Deployment
1
telepresence --new-deployment new_deploy_name --expose 8080:8000
  • 替换已经存在的 Deployment
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 一条命令,就可以查看所有日志。

  • 查看所有日志
1
journalctl
  • 实时滚动显示日志
1
journalctl -f
  • 查看指定服务日志
1
journalctl -u kubelet

微信公众号
作者
微信公众号