Kubernetes 监控
环境信息
- Centos 7
- Kubernetes 1.24
Kubernetes Metrics Server
Kubernetes Metrics Server 从 kubelet
收集资源使用指标(情况)并通过 Metrics API 将其暴露给 Kubernetes API Server,以供 HPA(Horizontal Pod Autoscaler) 和 VPA(Vertical Pod Autoscaler) 使用。kubectl top
也使用 Metrics API。[1]
安装 Kubernetes Metrics Server
Kubernetes Metrics Server 安装之前必须要开启 kube api-server 的聚合层功能以及认证鉴权功能 [3]
检查 Kubernetes API Server 是否启用了聚合层功能及认证鉴权功能
$ ps -ef | grep apiserver | grep -E "authorization-mode|enable-aggregator-routing" |
输出中包含了 --authorization-mode=Node,RBAC
和 --enable-aggregator-routing=true
,表示已开启对应功能。如若未开启,修改 api-server
的 manifest 文件 /etc/kubernetes/manifests/kube-apiserver.yaml
,在 kube-apiserver
的启动参数中添加 --enable-aggregator-routing=true
和 --authorization-mode=Node,RBAC
以下命令使用 yaml 文件安装最新版本 Metrics Server [2]
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml |
Kubernetes Metrics Server 部署配置常见错误
Failed to scrape node
参考部署步骤 部署后,Metrics Server 的 Pod 一直处于未就绪状态
$ kubectl get pods -A | grep metrics |
检查 Pod 日志
$ kubectl logs -n kube-system metrics-server-5cdf47479d-rwtd6 |
根据日志提示,由于 metrics-server 未获得TLS Bootstrap 签发证书的导致访问各节点资源时报错。
- 参考解决办法
- 添加选项
--kubelet-insecure-tls
不验证 Kubelet 的 CA 证书spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls
no such host
参考部署步骤 部署后,Metrics Server 的 Pod 一直处于未就绪状态,检查 Pod 日志
$ kubectl logs -n kube-system metrics-server-5cdf47479d-rwtd6 |
根据日志显示,API Server 的主机名解析存在问题。这是因为节点主机名在集群的 DNS 中无法解析导致,可以通过在 Metrics Server 的 Pod 中手动添加解析解决
metadata: |
Metrics Server 无法获取所在节点的主机的监控信息
Metrics Server 部署成功后,无法获取所在节点的主机的监控信息,获取其他主机的监控信息正常,具体信息如下
$ kubectl get nodes -o wide |
查看 Metrics Server Pod 日志,显示
$ kubectl logs -n kube-system metrics-server-c48655c66-jxwpt |
根据日志显示,是因为无法连接节点的 kubelet (端口 10250) 导致。为了定位问题,修改 Metrics Server 的部署 Yaml 文件,在其中加入一个容器,来方便使用工具测试问题。参考以下内容,在 Pod 中加入容器 centos:centos7.9.2009
apiVersion: apps/v1 |
重新部署后,登陆容器 centos7
,安装所需工具进行测试。根据日志信息,首先测试 Pod 是否能连接到节点的 kubelet
$ curl -v 172.31.16.124:10250 |
根据以上测试,Metrics Server 的 Pod 无法连接所在节点 k8s-worker1(172.31.16.124
)的 kubelet (172.31.16.124:10250
),可以正常连接其他节点的 kubelet。由此可以确定问题原因。
考虑到 Metrics Server 的 Pod 只是访问不到宿主节点所在的 kubelet,可以访问其他节点的 kubelet,梳理其中的网络连同流程发现,在访问其他节点的 kubelet 时,Metrics Server Pod 的报文在流出宿主节点前,会被 SNAT 为宿主节点的出口 IP,报文源 IP 为 宿主节点的 IP。而访问宿主节点的 kubelet 的报文,其源 IP 为 Metrics Server Pod 的 IP,目的 IP 为宿主节点的 IP。怀疑可能因为集群节点上的 iptables 允许集群节点的 IP 访问 kubelet,而 Pod 的 IP 未被允许访问 kubelet。为验证此猜想,在节点 k8s-worker1
的 iptables 添加允许 Metrics Server Pod 的 IP 访问的规则进行测试
iptables -I INPUT 7 -s 10.244.4.138 -j ACCEPT |
再次测试和 kubelet 的连通性,发现可以正常连通,再次检查 kubectl top node
,可以查到所有节点的监控数据
$ curl -v 172.31.16.124:10250 |
脚注
Kubernetes Pod 调度
环境信息
- Centos 7
- Kubernetes 1.24
污点和容忍度
默认情况下,集群中的 master 节点被设置成了污点,以确保只有控制平面的 Pod 才能调度部署到主节点上。
显示节点的污点信息
$ kubectl describe node k8s-master1 |
Master 节点包含 2 个污点
node-role.kubernetes.io/control-plane:NoSchedule
node-role.kubernetes.io/master:NoSchedule
这 2 个污点将阻止 Pod 调度到这个节点上面,除非有 Pod 能够容忍这个污点(Taints),通常能容忍这个污点的都是控制平面的 Pod。
查看 Pod 的容忍度
$ kubectl describe pod -n kube-system kube-proxy-2cslw |
aliyun 相关配置
cmd 工具
ossutil 工具
列出 oss 中的所有对象
命令格式 [1]
./ossutil64 ls oss://bucketname[/prefix] [-s] [-d] [--limited-num] [--marker] [--include] [--exclude] [--version-id-marker] [--all-versions] |
查看桶中资源总数及总的存储大小
$ ossutil64 du oss://test-bucket |
CDN 加速域名配置缓存共享
CDN 加速域名配置缓存共享验证方法
比如 A 域名使用 B 域名的共享缓存,测试的时候先访问一条 B 域名中的新资源,使用 B 域名查看对应的缓存状态响应头,第一次访问应该是 X-Cache: MISS
,重新访问一次,应该变成 X-Cache: HIT
。接下来使用 A 域名访问,如果首次访问结果即为 HIT
,说明共享缓存配置成功
$ curl -v -I B/test.png |
Kubernetes Pod
Pod 是由一个或多个容器构成的集合,被 Kubernetes 作为一个整体进行部署和调度,是 Kubernetes 调度的最小单元。同一个 Pod 内的容器共享 network namespace、UTS nanespace
环境信息
- Centos7
- Kubernetes v1.24.7
Pod 创建过程
当用户在 Kubernetes 中创建了一个 Pod 后,CRI 和 CNI 协同创建 Pod 所属容器,并为 Pod 中的容器初始化网络协议栈的具体过程如下
用户在 Kubernetes 中创建了一个 Pod 后,Kubelet 接收到创建新 Pod 的任务,首先调用 CRI 创建 Pod 内的容器
Pod 中第一个被创建的容器是
pause
容器。pause
容器中运行着一个功能非常简单的 C 程序,具体逻辑是把自己永远阻塞,没有实际的业务逻辑,主要功能是用来占用一个 network namespace。创建
pause
容器,使用 docker none 网络模式,创建出来的容器除了 lo 回环网卡外没有其他网络设备。Pod 内的其他用户容器通过加入
pause
容器已占用的 network namespace 的方式共享同一个 network namespace。对应于 docker 的 Container 模式其他用户容器都使用
pause
容器的主机名,但并不使用同一个 UTS namespace。CNI 负责 Pod 中容器的网络初始化工作。主要为 Pod 内的
pause
容器添加eth0
网卡、分配 IP、配置网关等。
pause 容器
pause
容器是 Pod 中被创建的第一个容器,Pod 中的其他容器通过使用 pause
容器的 network namespace 共享网络协议栈和主机名。它是 Pod 中其他容器的父容器。
1.8 以后版本默认不启用 PID namespace 共享。每个容器拥有独立的 PID namespace。
以下步骤通过 docker 演示 Pod 容器创建的整个过程。首先,创建 pause
容器
docker run --name=test-pause \ |
然后在 Pod 中运行其他容器,本示例启动 2 个应用容器,一个 nginx 容器,里面启动了 nginx 服务,一个自定义的容器,里面包含常用工具
$ docker run --name=pod_nginx_test \ |
进入 pod_centos_test
容器,查看网络、端口、主机名信息
$ hostname |
- Pod 中的容器主机名相同,都使用
pause
容器的主机名,但实际并未共享 UTS namespace。可以通过找到对应 docker 容器的进程 PID,找到对应进程的 namespace 信息对比确认
$ ps -elf | grep ping
4 S root 35333 35311 0 80 0 - 6218 skb_wa 01:01 ? 00:00:00 ping 127.1
$ ls -l /proc/35333/ns/
total 0
lrwxrwxrwx 1 root root 0 Apr 8 01:13 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Apr 8 01:02 ipc -> ipc:[4026532664]
lrwxrwxrwx 1 root root 0 Apr 8 01:02 mnt -> mnt:[4026532662]
lrwxrwxrwx 1 root root 0 Apr 8 01:02 net -> net:[4026532588]
lrwxrwxrwx 1 root root 0 Apr 8 01:02 pid -> pid:[4026532665]
lrwxrwxrwx 1 root root 0 Apr 8 01:13 pid_for_children -> pid:[4026532665]
lrwxrwxrwx 1 root root 0 Apr 8 01:13 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Apr 8 01:02 uts -> uts:[4026532663]
$ ps -elf | grep nginx
4 S root 34909 34887 0 80 0 - 2233 sigsus 00:59 ? 00:00:00 nginx: master process nginx -g daemon off;
$ ls -l /proc/34909/ns/
total 0
lrwxrwxrwx 1 root root 0 Apr 8 01:14 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Apr 8 01:00 ipc -> ipc:[4026532660]
lrwxrwxrwx 1 root root 0 Apr 8 01:00 mnt -> mnt:[4026532658]
lrwxrwxrwx 1 root root 0 Apr 8 01:00 net -> net:[4026532588]
lrwxrwxrwx 1 root root 0 Apr 8 01:00 pid -> pid:[4026532661]
lrwxrwxrwx 1 root root 0 Apr 8 01:14 pid_for_children -> pid:[4026532661]
lrwxrwxrwx 1 root root 0 Apr 8 01:14 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Apr 8 01:00 uts -> uts:[4026532659]
$ ps -elf | grep pause
4 S root 34239 34221 0 80 0 - 241 ia32_s 00:51 ? 00:00:00 /pause
$ ls -l /proc/34239/ns/
total 0
lrwxrwxrwx 1 root root 0 Apr 8 01:15 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Apr 8 00:51 ipc -> ipc:[4026532585]
lrwxrwxrwx 1 root root 0 Apr 8 00:51 mnt -> mnt:[4026532583]
lrwxrwxrwx 1 root root 0 Apr 8 00:51 net -> net:[4026532588]
lrwxrwxrwx 1 root root 0 Apr 8 00:51 pid -> pid:[4026532586]
lrwxrwxrwx 1 root root 0 Apr 8 01:15 pid_for_children -> pid:[4026532586]
lrwxrwxrwx 1 root root 0 Apr 8 01:15 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Apr 8 00:51 uts -> uts:[4026532584]以上观察可以看到,Pod 中的容器共享了 network namespace,未共享 UTS namespace。Kubernetes 中 Pod 中的容器同理。
- Pod 中容器的 PID namespace 进行了隔离,各个容器的 PID 进行了隔离,PID namespace 不同。
- Pod 中的容器共享了 network namespace,具有相同的网络信息(网卡、IP、端口资源)
- Pod 中的容器直接可以通过
localhost
互相访问
Kubernetes 权限控制
环境信息
- Centos 7
- Kubernetes 1.24
ServiceAccount
ServiceAccount 就像 Pod、Secret、ConfigMap 等一样,都是资源,属于 namespace 级别,作用在单独的命名空间。默认情况,每个 namespace 都有一个默认的 ServiceAccount。
$ kubectl get serviceaccount |
namespace 中的每个 Pod 都和一个 ServiceAccount 关联,它代表了运行在 Pod 中的应用程序的身份证明。Pod 中的每个容器都会挂载此 ServiceAccount token
在 Pod 的 manifest 定义文件中,可以通过指定账户名称的方式将一个 ServiceAccount 关联到 Pod。如果不显示指定 ServiceAccount 的账户名称,Pod 会使用 namespace 中默认的 ServiceAccount。可以将不同的 ServiceAccount 关联给不同的 Pod 来控制每个 Pod 可以访问的资源。
当 API 服务接收到一个带有认证 token 的请求时, API 会用这个 token 来验证发送请求的客户端所关联的 ServiceAccount 是否允许执行请求的操作。
查看 ServiceAccount
以 rancher 相关的 ServiceAccount 为例,查看 ServiceAccount 信息
$ kubectl get serviceaccount -n cattle-system |
以下输出名为 rancher
的 ServiceAccount 信息
$ kubectl describe sa rancher -n cattle-system |
ServiceAccount 主要包含了密钥(token)信息,客户端 (Pod)请求 API Service 时使用的 token 文件(如 /var/run/secrets/kubernetes.io/serviceaccount/token
)持有 ServiceAccount 的 token 。
$ kubectl describe secret rancher-token -n cattle-system |
在新建了 ServiceAccount 之后,若要将它赋值给 Pod,通过在 Pod 定义中的 spec.serviceAccountName
字段上配置 ServiceAccount 名称来分配。Pod 的 ServiceAccount 必须在 Pod 创建时进行配置,后续不能被修改
RBAC
从 Kubernetes 1.8.0 开始,RBAC(基于角色的权限控制)授权插件升级为 GA(通用可用性),并在大多数集群上默认开启(比如通过 kubeadm
部署的集群)。RBAC 会阻止未授权的用户查看和修改集群状态,默认的 ServiceAccount 不允许查看集群状态。
RBAC 授权规则是通过四种资源对象来进行配置的,他们可以分为 2 个组 [1]
Role
、ClusterRole
- 一组代表相关权限的规则,他们指定了在资源上可以执行哪些操作(动词)RoleBinding
、ClusterRoleBinding
- 将角色中定义的权限赋予一个或者一组用户。它包含若干主体(用户、组或者 ServiceAccount)的列表和对这些主体所获得的角色的引用。
Role
和RoleBinding
属于 namespace 范围的资源,必须在 namespace 中配置
ClusterRole
和ClusterRoleBinding
是集群作用域的资源一个 RoleBinding 可以引用某 ClusterRole 并将该 ClusterRole 绑定到 RoleBinding 所在的名字空间。但是,RoleBinding 不能授予主体集群级别的资源的访问权限,即使它引用了一个 ClusterRoleBinding
Godaddy api 使用
根据参考文档说明,获取到 key
和 secret
Python3 sdk 可以使用 godaddypy
环境信息
- Python 3.11.2
- godaddypy
godaddypy
主要提供了 2 个类,分别为
account
- 使用key
和secret
为client
生成鉴权头部client
- 连接 Godaddy API 并执行相应的请求
具体使用方法可以查看帮助信息,或者查看 Godaddy API 参考文档
>>> help(godaddypy) |
godaddypy 常见用法示例
获取账号中的所有域名
>>> import godaddypy |
根据 Godaddy API 文档说明,获取域名,每次默认获取 500 个,最多可以一次性获取 1000 个。
# 获取域名,默认 100 个。如果账户内的域名数量少于 100,则一次性可以获取完毕,多于 100,获取前 100 个 |
Kubernetes Volume 使用方法
常用 Volume 类型
以下是几种可用卷类型:
emptyDir
- 用于存储临时数据的简单空目录,生命周期和 Pod 一致。hostPath
- 用于将宿主机中的文件系统挂载到 Pod 中,生命周期不与 Pod 绑定。gitRepo
- 通过拉取 Git 仓库的内容来初始化的卷。已弃用nfs
- 挂载到 Pod 中的 NFS 共享文件系统。configMap
、secret
- 用于将 Kubernetes 中的部分资源和集群信息公开给 Pod 的特殊类型的卷persistentVolumeClaim
- 简称 PVC,使用预置和动态配置的持久卷。downwardAPI
- 在不使用 Kubernetes 客户端或 API 服务器的情况下获得自己或集群的信息 [5]
emptyDir 卷
emptyDir
表示与 Pod 生命周期相同的临时目录。[4]
emptyDir 配置示例
apiVersion: v1 |
emptyDir.medium
- 表示此目录应使用哪种类别的存储介质,默认为""
,表示使用节点的默认介质(一般是节点的本地磁盘)。可选值必须为""
或者Memory
,Memory
表示使用tmfs
(存在内存而非硬盘)
PersistentVolume 和 PersistentVolumeClaim
PersistentVolume (PV)
(持久化卷),是对底层的共享存储的一种抽象,PV 由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph
、GlusterFS
、NFS
等,都是通过插件机制完成与共享存储的对接。[1]
PersistentVolumeClaim(PVC)
是由用户发起的对存储的请求。 它类似于 Pod。 Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU和内存)。PVC 可以请求特定的存储大小和访问模式(例如,可以一次读/写或多次只读)匹配的 PV。
PVC
和 PV
中的 spec
关键字段要匹配,比如存储(storage)大小、读写模式,才能申请到对应的 PV 中的资源。PV 和 PVC 处于一一对应的关系。 [2]
PV
可以设置三种回收策略:
- 保留(Retain) - 保留产生的数据,PV 不进行处理
- 回收(Recycle) - 将执行清除操作,之后可以被新的pvc使用,需要插件支持。NFS 支持全部 3 种。
- 删除(Delete) - 删除pv和外部关联的存储资源,需要插件支持。
PV卷阶段状态
Available
– 资源尚未被 claim 使用Bound
– 卷已经被绑定到 claim 了Released
– claim 被删除,卷处于释放状态,但未被集群回收。Failed
– 卷自动回收失败
kubernetes Service 详解
Kubernetes 的 Service 代表的是 Kubernetes 后端服务的入口,它主要包含服务的访问 IP(虚拟 IP)和端口,工作在 L4.
Service 只存储服务的入口信息(IP:PORT),不存储后端 Pod 信息,Service 是通过 Label Selector
选择与之匹配的后端 Pod。当被 Service 选择的后端 Pod 运行且能正常向外提供服务时,Kubernetes 的 Endpoint Controller
会生成一个新的 Endpoint 对象,指向此 Pod 的 IP:PORT。Service 接收到访问请求,会将请求转发到对应的 Endpoint。同时,Service 的访问 IP 和 Endpoint/Pod 的 IP 都会在 Kubernetes 的 DNS 服务里面进行注册以记录域名和 IP 的对应关系,因此用户可以在集群中通过域名的方式访问 Service 和 Pod。
用户创建 Service 后,Kubernetes 会从集群的可用服务 IP 池中为 Service 分配一个稳定的集群内访问 IP,称为 Cluster IP。Kubernetes 还会通过注册 DNS 条目为 Cluster IP 分配 域名(主机名)。Cluster IP 和 域名 在集群内是独一无二的,并且在服务的整个生命周期中保持不变,直到将 Service 从集群中删除,Kubernetes 才会释放 Cluster IP 和 域名 。通过此方法,Service 作为代理,向客户端提供了稳定不变的访问后端服务的入口。
Service 除了作为 代理 功能,同时也提供了 负载均衡 和 高可用。当后端的 Pod 有多个时,默认会通过 轮询 将请求流量均匀分布到多个 Pod 上,当某个 Pod 不可用是,Service 不会将请求调度到问题节点。
Kubernetes 使用节点上运行的 kube-proxy
组件管理各 Service 和后端 Pod 的连接。kube-proxy
是一个基于出战流量的 负载均衡控制器,它监控 Kubernetes API Service 并持续将 Service IP (ClusterIP)映射到运行状况良好的后端 Pod,具体实现是通过主机上的 iptables/IPVS
的规则。访问 Service 的 IP 会被这些(路由)规则直接 DNAT 到后端 Pod 的 IP。
环境信息
- Centos 7 3.10.0-1160
- Kubernetes v1.24.7
kube-proxy iptables 模式下的规则解析
kube-proxy 默认使用 iptables
模式实现 Service 的代理转发和负载均衡。检查 kube-proxy
使用的代理模式
本示例使用相关信息如下
$ kubectl get services |
针对 api-service
,后端的 Pod 就绪后,生成了 ENDPOINTS,kube-proxy 会为该服务创建以下 iptables 规则
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES |
具体工作流程如下:
- 所有进入
PREROUTING
链的流量会被跳转到KUBE-SERVICES
自定义链 - 本示例中请求
api-service
的访问数据包(目的地址为10.106.126.96:10302
)在PREROUTING
链中被-d 10.106.126.96/32 -p tcp -m comment --comment "default/api-service:api-pord cluster IP" -m tcp --dport 10302
匹配到,根据规则跳转到自定义链KUBE-SVC-DVTQLPR6DVOLBZS4
- 在自定义链
KUBE-SVC-DVTQLPR6DVOLBZS4
中,实现了负载均衡,一半的流量会被跳转到链KUBE-SEP-JXBL7O23XHVFQV2I
,另一半的流量会被跳转到KUBE-SEP-BBPDSOPRICLINLPY
- 在自定义链
KUBE-SEP-JXBL7O23XHVFQV2I
和KUBE-SEP-BBPDSOPRICLINLPY
中,流量分别被 DNAT 分配到了后端的 Pod(就绪的 Endpoints)。 - Pod 处理完数据请求,向客户端返回请求结果时,在
POSTROUTING
链上要对数据包做 SNAT 处理,以确保客户端接收到的数据包的源地址是其发送请求时的目标地址。
通过以上 kube-proxy 管理的规则,Service 实现了对后端 Pod 的服务代理及负载均衡功能。
综上所述,iptables 模式最主要的链是 KUBE-SERVICES
、KUBE-SVC-*
、KUBE-SEP-*
KUBE-SERVICES
- 访问集群服务的数据包入口,它会根据匹配到的目标 IP:PORT 将数据包分发到相应的链KUBE-SVC-*
KUBE-SVC-*
- 相当于一个负载均衡器,它会将数据包平均分配到KUBE-SEP-*
链。每个KUBE-SVC-*
链后面的KUBE-SEP-*
链的数量和 Service 后端就绪的 Pod 数量一致。KUBE-SEP-*
- 通过 DNAT 将数据包的目的地址(IP:PORT)修改为后端就绪的 Pod 的 IP:PORT,从而将流量转发到相应的 Pod
kube-proxy
的 iptables 模式因为使用 DNAT 转发数据包,存在一定的性能损耗,另外,当集群中的 Service 数量上万时,节点上的 iptables rules 会非常庞大,对管理是个不小的负担,性能也会大打折扣。
kubernetes dns
环境信息
- Centos 7 5.4.225-1
- Kubernetes v1.24.7
Kubernetes DNS 服务的功能,是用来解析 Kubernetes 集群内的 Pod 和 Service 的域名,一般只供集群内部使用,不给外部使用。
默认情况下,Kubernetes DNS 应用部署后,会对外暴露一个 Service,集群内的容器通过访问该 Service 获得域名解析服务,这个 Service 的 ClusterIP 一般情况下都是固定的。
$ kubectl get services -n kube-system -o wide |
当 Kubernetes DNS 服务获得 ClusterIP 后,系统会给 Kubelet 配置启动参数指定 DNS Service 的 ClusterIP,DNS Service 的 IP 会在容器启动时传入,并写入容器系统的 DNS 配置中(一般为 /etc/resolv.conf
文件)
根据 kubelet
服务的启动命令,配置参数可以写在以下相关配置文件中
$ systemctl status kubelet -l |
DNS 的相关配置在文件 /var/lib/kubelet/config.yaml
中,主要选项为 clusterDNS
apiVersion: kubelet.config.k8s.io/v1beta1 |
Centos VNC server 配置
环境信息
- Centos 7
VNC
是一个图形桌面共享系统,可以通过远程连接的方式连接到服务器的图形系统以控制操作系统VNC
包括以下四个部分 : vncserver
,vncviewer
,vncpassword
及 vncconnect
。VNC client
端通过 VNC
协议远程连接到 vnc server
端,进行桌面共享及交互;
安装 tigervnc
安装之前首先要确保系统已安装图形系统,本文以 GNOME Desktop
为图形桌面系统,执行以下命令检查是否已安装 GNOME Desktop
,如果未安装,使用命令 yum groupinstall "GNOME Desktop"
安装
$ yum grouplist |
桌面系统安装后,使用以下命令,安装 tigervnc
yum install -y tigervnc-server |
可以参考 /lib/systemd/system/vncserver@.service
中的指示,生成服务配置文件:
cp /lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@:1.service |
修改以下行,将其中的 <USER>
改为要远程登录的用户名,例如 root
ExecStart=/usr/bin/vncserver_wrapper root %i |
如果有多个用户需要远程连接,可以重复以上步骤,建立多个配置文件,如 /etc/systemd/system/vncserver@:2.service
,各个服务之间互不影响,关闭一个服务,不影响其他的 vnc
服务 (如关闭: vncserver@:2.service
,vncserver@:1.service
的 vnc
远程依旧可以使用)
修改服务文件后,使用以下命令重新加载服务启动脚本:
systemctl daemon-reload |
启动服务:
systemctl start vncserver@:1.service |
服务启动后,会监听 5901 端口,如果有第二个服务,会监听在 5902 端口,以此类推,防火墙需要放通此端口
切换到需要 vnc 远程登录的用户,使用以下命令配置 vnc
远程登录用户的密码(只是 vnc 登录时使用的密码,非系统用户密码)
[root@localhost ~]# su - USER |
设置 vncpasswd
密码后,在账号的家目录里就会自动建立 .vnc 文件,其中包括对应用户的 vnc
相关的日志
配置完成后,下载客户端 进行远程登录
常见错误
getpassword error: Inappropriate ioctl for device
vncserver@:1.service
服务启动失败,端口未监听
$ netstat -anutp | grep LIST |
解决方法 配置登陆用的 VNC 密码,重启服务
su - root |
Vmware workstation 使用
环境信息
- Vmware workstation 17
激活密钥: NZ4RR-FTK5H-H81C1-Q30QH-1V2LA
Centos7 server 中安装使用 Vmware workstation
Centos 7 server版本(无图形界面)安装使用 Vmware workstation 参考链接
常见错误
VMware Workstation and Hyper-V are not compatible. Remove the Hyper-V role from the system before running VMware Workstation.
aws workspace 中不兼容 Vmware workstation
Failed to start SYSV: This service starts and stops VMware services.
参考 Centos7 server 中安装使用 Vmware workstation ,安装后,vmware 无法启动
$ /etc/rc.d/init.d/vmware start |
执行以下命令 [1]
$ vmware-modconfig --console --install-status |
查看内核版本
$ uname -r |
脚注
Kubernetes 网络
环境信息
- Centos7 3.10.0-1160
- Docker Engine - Community 23.0.3
- kubernetes 1.21.2-0
- kubernetes-cni-0.8.7-0
Kubernetes 对任何网络实现都规定了以下要求: [1]
所有 Pod 都可以在不使用网络地址转换 (NAT) 的情况下与所有其他 Pod 通信。
容器之间直接通信,不需要额外的 NAT,不存在源地址伪装的情况
所有节点都可以在没有 NAT 的情况下与所有 Pod 通信。
Node 与容器直接通信,不需要额外的 NAT
Pod 认为自己的 IP 与其他人认为的 IP 相同。
CNI
CNI 是 Kubernetes 容器网络的标准,CNI 是 Kubernetes 和底层网络插件之间的一个抽象层,为 Kubernetes 屏蔽了底层网络实现的负责度,同时解耦了 Kubernetes 和具体的网络插件实现。
安装 CNI
$ yum install kubernetes-cni |
Kubernetes 要使用 CNI,需要在 kubelet 启动时配置启动参数 --network-plugin=cni
(默认配置,可使用 systemctl status kubelet -l
查看启动参数)。
kubelet 从 --cni-config-dir
(默认为 /etc/cni/net.d/
)中读取网络插件的配置文件,并使用该文件中的 CNI 配置来配置每个 Pod 网络。如果该目录 (/etc/cni/net.d/
)中有多个配置文件,则使用文件名字典序列中的第一个文件。
CNI 插件的二进制文件放置的目录是通过 kubelet 的 --cni-bin-dir
参数指定,默认为 /opt/cni/bin/
linux rabbitmq
环境信息
- Centos7 3.10.0-1160.45.1.el7
- RabbitMQ 3.9.10
常见错误
RabbitMQ 启动失败
使用命令启动,报错
$ /sbin/rabbitmq-server -v |
关键错误信息 Protocol 'inet_tcp': register/listen error: ehostunreach
,根据提示,可能是某个地址不可达,rabbitmq 启动时需要连接 epmd ,默认端口为 4369,在本地测试连接此端口,发现不通 [1]
$ curl -v 127.0.0.1:4369 |
根据输出的错误可知,是因为 127.0.0.1
无法连接,检查 iptables 防火墙策略,发现未允许回环网卡访问,在 iptables 中添加以下规则允许回环网卡访问
*filter |
允许回环网卡访问后,重新测试连接 epmd ,可以正常连接,重新启动 rabbitmq-server 正常。
$ curl -v 127.0.0.1:4369 |
脚注
linux macvlan 网卡虚拟化技术
Macvlan
Macvlan 本身是 linxu kernel 模块
Macvlan 接口是物理以太网接口的虚拟子接口,Macvlan 允许用户在一个物理网络接口上面配置多个虚拟的网络接口,每个 Macvlan 接口都有自己的区别与父接口的 MAC 地址,并且可以像普通的物理网络接口一样分配 IP 地址。使用 Macvlan 技术实现的效果是一块物理网卡可以绑定多个 IP 地址,并且每个 IP 地址有自己独立的 MAC 地址。
Macvlan 虚拟出来的虚拟网卡,在逻辑上和物理网卡是对等的。使用 Macvlan 的虚拟网卡要和父接口在同一个网段。
Macvlan 的最大优点是性能极好,相比其他方式,macvlan 不需要创建 Linux bridge,而是直接通过interface 连接到物理网络。
为保证父接口能接收多个不同 MAC 地址的网络包,需要开启网卡的 混杂模式
docker 中使用 Macvlan 虚拟网卡
本示例演示 docker 环境中使用 macvlan。首先创建使用 macvlan 驱动的 network,Docker 中 macvlan 只支持 bridge 模式 [1]
$ docker network create -d macvlan --subnet=192.168.142.0/24 \ |
在 docker 环境中,创建网络时,会自动将宿主机网卡设置为混杂模式,此时查看网卡信息,未显示混杂模式,但是查看
dmesg
日志,会看到网卡进入了混杂模式
$ dmesg
[93203.228311] device ens33 entered promiscuous mode
运行容器并连接到新建的 macvlan 网络 macvlan1
docker run -itd --name test01 \ |
使用命令 docker exec -it test01 bash
进入容器查看容器的 IP 地址信息,可以看到容器中的网卡类型为 macvlan
,模式为 bridge
,网关为 docker 网络 macvlan1
中配置的网关。
$ ip -d add |
测试容器可以和宿主机网络一样访问外网。
此时检查宿主机网卡信息,系统上只有 lo
,ens33
,docker0
,未出现其他网卡。
$ ip -d link |
在另一个 docker 节点上同样配置 docker 网络和容器
$ docker network create -d macvlan --subnet=192.168.142.0/24 \ |
进入容器 test01
,访问到另一个节点上的容器的连通性
$ ping 192.168.142.12 |
进入容器 test01
,测试和宿主机 ip 的连通性,结果发现不通,原因为:在 macvlan 虚拟网络中,父接口(物理网卡)相当于一个交换机,对于其子 macvlan 网卡的数据包,只进行转发而不处理,于是造成了使用本机 macvlan 网卡的虚拟 IP 无法和本机物理网卡的 IP 通信。
$ ping 192.168.142.10 |
linux network namespace 使用说明
环境信息
- Centos 7 5.4.239-1
Linux 的 namespace 的作用是 隔离内核资源
,目前主要实现了以下 namespace
mount namespace
- 文件系统挂载点UTS namespace
- 主机名IPC namespace
- POSIX 进程间通信消息队列PID namespace
- 进程 pid 数字空间network namespace
- networkuser namespace
- user ID 数字空间
其中,除了 network namespace
,其他 namespace 的操作需要使用 C 语言调用系统 API 实现。network namespace
的增删改查功能已经集成到了 Linux 的 ip
工具集的 netns
子命令中
Linux 里面的 namespace 给处在其中的进程造成 2 个错觉:
- 它是系统里面唯一的进程
- 它独享系统的所有资源
默认情况下,Linux 里面的所有进程处在和宿主机相同的 namespace ,即初始 namespace 里,默认享有全局系统资源。
network namespace 常用操作
network namespace 的增删改查功能已经集成到了 Linux 的 ip
工具集的 netns
子命令中,因此在 Linux 系统中,对 network namespace 的操作主要使用 ip netns
命令
$ ip netns help |
创建并查看 network namespace
使用以下命令创建名为 netns1
的 network namespace
ip netns add netns1 |
以下命令查看系统中的 network namespace
$ ip netns list |
新的 network namespace
创建后,系统会在 /var/run/netns/
下面生成一个同名的挂载点
$ ls -l /var/run/netns/ |
此挂载点的主要作用一方面是方便对 namespace 的管理,一方面是使 namespace 即使没有进程运行也能继续存在。
新的 network namespace
创建后,可以使用 ip netns exec
命令进入 namespace,做网络配置或者查询的工作。
ip netns exec
命令只能根据 network namespace 的名称进入 namespace
以下命令查询 netns1
的 network namespace
的 IP 地址信息
$ ip netns exec netns1 ip add |
默认的 network namespace
除了附带一个 lo
网卡外,没有任何其他网络设备,并且此 lo
接口还处于 DOWN
的状态,因此此回环网卡也是不可访问的。
$ ip netns exec netns1 ping 127.0.0.1 |
在此示例中,如果想启用本地回环地址,首先需要进入 namespace,将本地回环网卡的状态修改为 UP
$ ip netns exec netns1 ip link set dev lo up |
此时,namespace 中的 lo
网卡可以正常使用,但是因为 namespace 中没有其他网络设备,此 network namespace
无法和其他网络通信,要和其他网络通信,需要用到其他的网络技术,例如 veth pair
删除 network namespace
要删除 network namespace
,可以使用以下命令
ip netns delete netns1 |
上面这条命令并没有实际删除
netns1
这个network namespace
,它只是移除了这个 namespace 对应的挂载点(/var/run/netns/netns1
),只要里面的进程还运行着,network namespace
就会一直存在
veth pair
veth 是虚拟以太网(Virtual Ethernet)的缩写。veth 设备总是成对出现的,因此我们称之为 veth pair
,veth pair
的一端发送的数据会在另外一端接收。根据这一特性,veth pair
常被用于跨 network namespace
的通信,即分别将 veth pair
的 2 端放在不同的 network namespace
。
linux tun/tap 设备的工作原理
从 Linux 文件系统的角度看,tun/tap 设备是用户可以使用文件句柄操作的字符设备
从 Linux 网络虚拟化的角度看,tun/tap 设备是虚拟网卡,一端连接内核网络协议栈,一端连接用户态的程序。
tun/tap 设备主要的作用是可以将 TCP/IP 协议栈处理好的数据包发送给任何一个使用 tun/tap 设备驱动的程序,由用户态程序重新处理数据包后重新发送到 TCP/IP 协议栈。
tun/tap 设备的工作原理完全相同,主要区别在于:
- tun 设备的 /dev/tunX 文件收发的是 IP 包,因此只能工作在网络层(L3),无法与物理网卡做桥接,可以通过三层交换(如 ip_forward) 与物理网卡交互
- tap 设备的 /dev/tapX 文件收发的是数据链路层报文,可以与物理网卡做桥接。
tun/tap 设备的工作原理
上图展示了物理设备上的数据是如何通过 Linux 内核网络协议栈发送到用户态程序的。
物理网卡的数据送达网络协议栈,进程通过 Socket 创建特殊套接字,从网络协议栈接收数据。
从网络协议栈的角度看,tun/tap 设备这类虚拟网卡与物理网卡并无区别。对 tun/tap 设备而言,他与物理网卡的不同表现在它的数据源不是物理链路,而是来自用户态。
普通的物理网卡通过网线收发数据包,而 tun/tap 设备通过一个设备文件 (/dev/tunX
,/dev/tapX
)收发数据包,所有对这个文件的写操作会通过 tun/tap 设备转换成一个网络数据包传送给内核的网络协议栈。当内核网络协议栈发送一个包给 tun/tap 设备时,用户态的进程通过读取设备文件,就可以拿到报的内容。用户态的程序也可以通过写入这个设备文件向 tun/tap 设备发送数据。
VPN 原理简述
如上图所示,整个数据包的流程包括
- App1 通过 Socket API 发送了一个数据包,假设这个数据包的目的 IP 地址是 192.168.1.3,和 tun0 位于同一个网段
- 数据包到达网络协议栈后,协议栈根据数据包的目的 IP 地址进行路由,匹配到数据包应该发送给 tun0 网卡,于是将数据包发送给 tun0 网卡
- tun0 网卡收到数据包,将数据包写入 /dev/tun0,/dev/tun0 由 App2 打开,于是 App2 获得了数据包
- App2 获得数据包后,通过报文封装,将原来的目的 IP 为 192.168.1.3 的报文封装在源 IP 为 eth0 的 IP,目的 IP 为 VPN 对端 IP 地址的报文中,构造出新的报文,并通过 Socket API 发送给内核网络协议栈
- 内核网络协议栈根据路由,发现数据包应该由 eth0 发送出去,于是将数据包发给 eth0,最终通过 eth0 将数据包发送到 VPN 的对端。
综上所述,发到 192.168.1.0/24 的数据包,首先通过监听在 tun0 设备上的 App2 (VPN 客户端)进行封包,再利用物理网卡 eth0 发送到远端网络的物理网卡上,从而实现 VPN。
VPN 网络报文真正从物理网卡出去需要经过 2 次内核网络协议栈,因此会有一定的性能损耗。
Linux 常用内核参数说明
sysctl
命令被用于在内核运行时动态地修改内核的运行参数,可用的内核参数在目录 /proc/sys
中。
sysctl
命令对内核参数的修改仅在当前生效,重启系统后参数丢失。如果希望参数永久生效可以修改配置文件 /etc/sysctl.conf
。
常用内核参数说明
内核参数 | 取值范围 | 含义 | 使用说明 |
---|---|---|---|
kernel.hung_task_timeout_secs = 120 |
int | 设置系统检测到进程阻塞(如 `D` 状态)后,等待进程结束的时常。 如果进程未在规定时间内结束,系统认为该进程无响应,则自动杀死以避免系统奔溃 |
mem
内核参数 | 取值范围 | 含义 | 使用说明 |
---|---|---|---|
vm.min_free_kbytes = 2097152 |
单位 KB |
设置系统最小剩余内存,以避免缓存不释放导致的死机 | |
vm.oom-kill = 0 |
0,1 默认值为 1,开启 |
是否启用 OOM-killer。 特定情况下,可能不希望核心执行 OOM killer 的工作,关闭 OOM killer。 例如,排错时 echo 0 > /proc/sys/vm/oom-kill 临时关闭 [1] |
|
vm.overcommit_memory = 0 |
0,1,2 | 内存分配策略/proc/sys/vm/overcommit_memory |
vm.overcommit_memory 详细说明 |
vm.overcommit_ratio = 50 |
int default = 50 |
vm.overcommit_memory = 2 时才生效,配置允许 overcommit 的百分比 |
vm.overcommit_memory 详细说明 |
vm.panic_on_oom = 0 |
0,1 默认为 0 ,开启 |
表示当内存耗尽时,内核会触发 OOM killer 杀掉最耗内存的进程 | |
vm.oom_kill_allocating_task = 0 |
0,1 默认为 0 ,不启用 |
OOM-Killer 时,是否选择当前正在申请内存的进程进行 kill |
Linux 系统崩溃问题分析
环境信息
- Centos7 5.4.221-1.el7
- Docker Engine - Community 20.10.9
本文档中的日志分析主要依赖于 journald
服务记录的日志,因此首先需要对 `journald` 服务记录的日志进行持久化配置
场景分析
k8s worker 节点经常死机(奔溃)
环境信息
- Centos7 5.4.221-1.el7
- Docker Engine - Community 20.10.9
- Kubernetes v1.24.7
k8s 节点经常出现无响应(死机),重启才能恢复正常。重启恢复后,检查系统 messages
日志。
Feb 10 12:21:40 k8s-work1 kernel: INFO: task dockerd:1443 blocked for more than 368 seconds. |
从日志中可看出,dockerd
进程处于 `D` 状态,说明 dockerd
在等待 IO 操作,根据进程调用的栈信息,显示存在对 overlay
文件系统的同步操作,初步猜测,可能是因为 overlay
文件系统中的某些操作未能及时完成,导致了 dockerd
进程的阻塞。
继续检查日志,看到系统连接 NFS 服务超时,怀疑可能是因为 NFS 异常导致。
Feb 10 12:21:40 k8s-work1 kernel: nfs: server 172.31.88.9 not responding, timed out |
linux 网络接口的混杂模式
Linux 网卡的混杂模式(Promiscuous mode),简称 Promisc mode,俗称 监听模式
。在非混杂模式下,网卡只会接受目的 MAC 地址是它自己的单播帧,以及多播帧;在混杂模式下,网卡会接受经过它的所有帧。
查看网卡是否处于 Promiscuous mode
,可以使用 ifconfig
或者 netstat -i
命令
ifconfig ens33 |
当输出包含 PROMISC
时,表明该网络接口处于 Promiscuous mode
,否则表明未处于 Promiscuous mode
。要开启网卡的 Promiscuous mode
,可以使用以下命令
$ ifconfig ens33 promisc |
以下命令使网卡退出 Promiscuous mode
ifconfig ens33 -promisc |
将网络设备加入 Linux bridge 后,网络设备会自动进入混杂模式,此种情况使用 ifconfig
或者 netstat -i
命令查看网卡,未显示 PROMISC
,但是查看内核日志,显示网卡已进入混杂模式,并且无法退出,直到将 veth 从Linux bridge 中移除。网络设备移除网桥后,会自动退出混杂模式。
$ ip link add veth0 type veth peer name veth1 |