kubernetes 常见问题的探讨

关于 kubernetes 部署复杂是其特点之一,而网络问题则是最为常见的类型之一。

场景 a/b/c node 上的 pod 互通,但是对 d node 无法访问

  • 个别服务出现无法访问另一个服务的情况;
  • 测试判断无法 ping 通;
  • 080 端口无法访问;
  • 经测试其他 node 上的 pod 均无法访问该 node 上的 pod。

根据以上背景,初步断定是网络问题。

通讯链路

详细示例

  • 数据从源容器中发出,经所在主机的 docker0 网卡转发到 flannel 网卡;

  • flannel 通过 etcd 维护的路由表将数据发送给目的主机;

  • 源主机的 flanneld 服务将原数据内容 UDP 封装和根据自己的路由表传递给目的地节点的 flanneld 服务;

  • 数据到达后解包,然后直接进入目的节点的 flannel 网卡;

  • 之后转发到目的主机的 docker0 网卡;

  • 最后 docker0 路由到目标容器。

检查网卡

首先查看 a/b/c 网卡,检查 docker0 和 flannel 是否在同一网段。

检查结果正常。
检查 d 节点网卡

检查结果正常。

检查路由表

查看 a/b/c 路由表

10.100.0.0 指向 flannel
10.100.54.0 指向 docker0
192.168.1.0 指向 ens3,局域网地址

查看 d 路由表

10.100.0.0 指向 flannel
10.100.41.0 指向 docker0
192.168.1.0 指向 ens3,局域网地址

路由表无异常。
也可以使用下列命令,效果一样:route -n

防火墙

1
iptables -P FORWARD ACCEPT

ip forwad

1
2
3
[root@test_243 ~]#  sysctl -a|grep ip_forward
net.ipv4.ip_forward = 1
net.ipv4.ip_forward_use_pmtu = 0

如果没有打开转发,则使用以下方式。

临时

临时生效的配置方式,在系统重启,或对系统的网络服务进行重启后都会失效。这种方式可用于临时测试、或做实验时使用。

方案一
sysctl 命令的 -w 参数可以实时修改 Linux 的内核参数,并生效。所以使用如下命令可以开发 Linux 的路由转发功能。

1
sysctl -w net.ipv4.ip_forward=1

方案二
内核参数在 Linux 文件系统中的映射出的文件:/proc/sys/net/ipv4/ip_forward 中记录了 Linux 系统当前对路由转发功能的支持情况。文件中的值为 0, 说明禁止进行 IP 转发;如果是 1, 则说明 IP 转发功能已经打开。

1
echo 1 > /proc/sys/net/ipv4/ip_forward
永久

在 sysctl.conf 配置文件中有一项名为 net.ipv4.ip_forward 的配置项,用于配置 Linux 内核中的 net.ipv4.ip_forward 参数。其值为 0, 说明禁止进行 IP 转发;如果是 1, 则说明 IP 转发功能已经打开。

需要注意的是,修改 sysctl.conf 文件后需要执行指令 sysctl -p 后新的配置才会生效。

1
2
3
4
5
6
7
8
vim /etc/sysctl.d/99-sysctl.conf 
...
# 结尾添加:
net.ipv4.ip_forward = 1
:wq
[root@CentOS ~]# sysctl -p
net.ipv4.ip_forward = 1 //查看修改结果.

etcd

检查 etcd 数据是否正常

核对网段和 Mac 地址是否一致。 事实证明 `10.100.41.0` 的网卡和实际的网卡 Mac 地址并不一致,所以导致源主机没有找到真实的目的主机。

最后发现,原 10.100.41.0 的网卡对应的是另外一台历史机器,造成的网卡冲突导致的这一后果。

etcd 的基本使用

etcd 是一个分布式的、可靠的 k-v 存储系统,它用于存储分布式系统中的关键数据。在 kubernetes 中保存集群所有的网络配置和对象的状态信息。在集群中共有两个服务需要用到 etcd 来协同和存储配置:

  • flannel,对于其他网络差价也需要用到 etcd;
  • kubernetes 本身,包括各种对象的状态和元信息配置

获取 集群网络配置

获取子网列表

获取主机网卡信息

帮助信息

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
NAME:
etcdctl - A simple command line client for etcd.

WARNING:
Environment variable ETCDCTL_API is not set; defaults to etcdctl v2.
Set environment variable ETCDCTL_API=3 to use v3 API or ETCDCTL_API=2 to use v2 API.

USAGE:
etcdctl [global options] command [command options] [arguments...]

VERSION:
3.2.22

COMMANDS:
backup backup an etcd directory
cluster-health check the health of the etcd cluster
mk make a new key with a given value
mkdir make a new directory
rm remove a key or a directory
rmdir removes the key if it is an empty directory or a key-value pair
get retrieve the value of a key
ls retrieve a directory
set set the value of a key
setdir create a new directory or update an existing directory TTL
update update an existing key with a given value
updatedir update an existing directory
watch watch a key for changes
exec-watch watch a key for changes and exec an executable
member member add, remove and list subcommands
user user add, grant and revoke subcommands
role role add, grant and revoke subcommands
auth overall auth controls
help, h Shows a list of commands or help for one command

GLOBAL OPTIONS:
--debug output cURL commands which can be used to reproduce the request
--no-sync don't synchronize cluster information before sending request
--output simple, -o simple output response in the given format (simple, `extended` or `json`) (default: "simple")
--discovery-srv value, -D value domain name to query for SRV records describing cluster endpoints
--insecure-discovery accept insecure SRV records describing cluster endpoints
--peers value, -C value DEPRECATED - "--endpoints" should be used instead
--endpoint value DEPRECATED - "--endpoints" should be used instead
--endpoints value a comma-delimited list of machine addresses in the cluster (default: "http://127.0.0.1:2379,http://127.0.0.1:4001")
--cert-file value identify HTTPS client using this SSL certificate file
--key-file value identify HTTPS client using this SSL key file
--ca-file value verify certificates of HTTPS-enabled servers using this CA bundle
--username value, -u value provide username[:password] and prompt if password is not supplied.
--timeout value connection timeout per request (default: 2s)
--total-timeout value timeout for the command execution (except watch) (default: 5s)
--help, -h show help
--version, -v print the version
常见操作
  • set 指定某个键的值
1
2
3
4
[root@xxx ~]# etcdctl set t1 "v1"
v1
[root@xxx ~]# etcdctl ls /
/t1
  • get 获取指定键的值
1
2
[root@xxx ~]# etcdctl get t1
v1
  • rm/rmdir 删除键
1
2
3
4
[root@scm ~]# etcdctl rm t1
PrevNode.Value: v1
[root@scm ~]# etcdctl get t1
Error: 100: Key not found (/t1) [127844346]

网卡地址不在一个网段

在 kubernetes 集群中某些服务启动顺序是特定的,必须是先启动 flannel 然后再启动 docker。

flannel 服务启动时主要做了以下几步的工作:

  • 从 etcd 中获取 network 的配置信息;
  • 划分 subnet,并在 etcd 中进行注册;
  • 将子网信息记录到 /run/flannel/subnet.env 中。
1
2
3
4
5
[root@xxx ~]# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.100.0.0/16
FLANNEL_SUBNET=10.100.28.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false

之后将会有一个脚本将 subnet.env 转写成一个 docker 的环境变量文件 /run/flannel/docker

1
2
3
4
5
[root@xxx ~]# cat /run/flannel/docker
DOCKER_OPT_BIP="--bip=10.100.28.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1450"
DOCKER_NETWORK_OPTIONS=" --bip=10.100.28.1/24 --ip-masq=true --mtu=1450"

然后是 Docker 服务

Docker 服务会根据 flannel 拿到的网段然后把 pod 启动在这些网段,这样 kubernetes 在寻址 pod 的时候就会找到相应的宿主机,进行通讯。如果 Docker 服务和 Flannel 服务没有这种关联关系的化,很可能 Docker 先用原来的 ip 段启动,而这个段并没有写到 etcd 中,导致寻址失败。

kubernetes 的高可用

首先简单介绍一下常见的负载均衡类型,主要分为两类:四层和七层。
四层负载均衡是指负载均衡器用 IP+Port 的方式接收请求,再直接转发到后端对应的服务器上。工作在传输层。客户端和服务器之间建立一次 TCP 连接,而负载均衡设备至少起类似路由器的转发动作。常见的软件比如 LVS。
七层负载均衡是根据虚拟的 URL 或者主机名来接收请求,经过处理后再转向响应的后端服务器上。工作在应用层,且建立两次连接。

  • 客户端到负载均衡器,负载均衡根据消息中的内容来做出负载均衡的决定;
  • 然后建立负载均衡器到后端服务器的连接;

负载均衡器需要先代理最终的服务器和客户端建立 TCP 连接后,才可能接收到客户端发送的真正应用层内容的保温,再根据报文中的特定字段,再加上负载均衡设备的服务器选择方式,决定最终选的服务器。常见的软件比如 NGINX。

kubernetes 部署架构

kubernetes 集群的高可用实际是各个核心组件的高可用。

  • apiserver 通过 keepalived+haproxy 实现高可用,当某个节点故障时触发 keepalived vip 转移,haproxy 负责将流量负载到 apiserver 节点;
  • controller-manager k8s 内部通过选举方式产生领导者 (由 –leader-elect 选型控制,默认为 true),同一时刻集群内只有一个 controller-manager 组件运行,其余处于 backup 状态;
  • scheduler k8s 内部通过选举方式产生领导者 (由 –leader-elect 选型控制,默认为 true),同一时刻集群内只有一个 scheduler 组件运行,其余处于 backup 状态;
  • etcd 通过运行 kubeadm 方式自动创建集群来实现高可用,部署的节点数为奇数,3 节点方式最多容忍一台机器宕机。

多节点的核心点就是需要指向一个核心的地址或者 VIP。node 请求 apiserver 时不时直接指向 master 服务器,而是通过一个虚拟地址来分发到某个 master。

常规高可用

方案一、

  • haproxy,提供高可用性,负载均衡,基于 TCP 和 HTTP 的代理,支持数以万记的并发连接
  • keepalived,是以 VRRP (虚拟路由冗余协议) 协议为基础,包括一个 master 和多个 backup。 master 劫持 vip 对外提供服务。master 发送组播,backup 节点收不到 vrrp 包时认为 master 宕机,此时选出剩余优先级最高的节点作为新的 master, 劫持 vip。keepalived 是保证高可用的重要组件。

haproxy 提供代理后端 apiserver 的功能,keepalived 提供健康检查和切换 IP 的用途。

方案二、

  • nginx
  • keepalived

该方案和方案一类似,也是使用代理功能,只不过使用的是 NGINX 提供的四层转发。

阿里云环境的负载均衡

阿里云云服务器不支持再单独购买 IP,无法安装配置 keepalived,进行负载均衡。如果需要配置负载均衡,可以直接购买负载均衡,进行负载均衡配置。这样就可以省略代理和 keepalived 的配置,只需要使用 slb 即可,注意后端服务指向的是 master 的 apiserver 的地址和端口。