GVKun编程网logo

记一次KUBERNETES/DOCKER网络排障(docker 网络问题)

14

本文的目的是介绍记一次KUBERNETES/DOCKER网络排障的详细情况,特别关注docker网络问题的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解记一次KUBE

本文的目的是介绍记一次KUBERNETES/DOCKER网络排障的详细情况,特别关注docker 网络问题的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解记一次KUBERNETES/DOCKER网络排障的机会,同时也不会遗漏关于docker for mac 安装 kubernetes、kubernetes dashboard、Docker Kubernetes Service 网络服务代理模式详解、Docker Kubernetes Volume 网络数据卷、docker kubernetes 网络布局的知识。

本文目录一览:

记一次KUBERNETES/DOCKER网络排障(docker 网络问题)

记一次KUBERNETES/DOCKER网络排障(docker 网络问题)

 

转载于酷壳

昨天周五晚上,临下班的时候,用户给我们报了一个比较怪异的Kubernetes集群下的网络不能正常访问的问题,让我们帮助查看一下,我们从下午5点半左右一直跟进到晚上十点左右,在远程不能访问用户机器只能远程遥控用户的情况找到了的问题。这个问题比较有意思,我个人觉得其中的调查用到的的命令以及排障的一些方法可以分享一下,所以写下了这篇文章。

问题的症状

用户直接在微信里说,他们发现在Kuberbnetes下的某个pod被重启了几百次甚至上千次,于是开启调查这个pod,发现上面的服务时而能够访问,时而不能访问,也就是有一定概率不能访问,不知道是什么原因。而且并不是所有的pod出问题,而只是特定的一两个pod出了网络访问的问题。用户说这个pod运行着Java程序,为了排除是Java的问题,用户用 docker exec -it 命令直接到容器内启了一个 Python的 SimpleHttpServer来测试发现也是一样的问题。

我们大概知道用户的集群是这样的版本,Kuberbnetes 是1.7,网络用的是flannel的gw模式,Docker版本未知,操作系统CentOS 7.4,直接在物理机上跑docker,物理的配置很高,512GB内存,若干CPU核,上面运行着几百个Docker容器。

 

问题的排查

问题初查

首先,我们排除了flannel的问题,因为整个集群的网络通信都正常,只有特定的某一两个pod有问题。而用 telnet ip port 的命令手工测试网络连接时有很大的概率出现connection refused 错误,大约 1/4的概率,而3/4的情况下是可以正常连接的。

当时,我们让用户抓个包看看,然后,用户抓到了有问题的TCP连接是收到了 SYN 后,立即返回了 RST, ACK

我问一下用户这两个IP所在的位置,知道了,10.233.14.129 是docker010.233.14.145 是容器内的IP。所以,这基本上可以排除了所有和kubernets或是flannel的问题,这就是本地的Docker上的网络的问题。

对于这样被直接 Reset 的情况,在 telnet 上会显示 connection refused 的错误信息,对于我个人的经验,这种 SYN完直接返回 RST, ACK的情况只会有三种情况:

  1.  TCP链接不能建立,不能建立连接的原因基本上是标识一条TCP链接的那五元组不能完成,绝大多数情况都是服务端没有相关的端口号。
  2. TCP链接建错误,有可能是因为修改了一些TCP参数,尤其是那些默认是关闭的参数,因为这些参数会导致TCP协议不完整。
  3. 有防火墙iptables的设置,其中有 REJECT 规则。

因为当时还在开车,在等红灯的时候,我感觉到有点像 NAT 的网络中服务端开启了tcp_tw_recycle 和 tcp_tw_reuse 的症况(详细参看《TCP的那些事(上)》),所以,让用户查看了一上TCP参数,发现用户一个TCP的参数都没有改,全是默认的,于是我们排除了TCP参数的问题。

然后,我也不觉得容器内还会设置上iptables,而且如果有那就是100%的问题,不会时好时坏。所以,我怀疑容器内的端口号没有侦听上,但是马上又好了,这可能会是应用的问题。于是我让用户那边看一下,应用的日志,并用 kublet describe看一下运行的情况,并把宿主机的 iptables 看一下。

然而,我们发现并没有任何的问题。这时,我们失去了所有的调查线索,感觉不能继续下去了……

重新梳理

这个时候,回到家,大家吃完饭,和用户通了一个电话,把所有的细节再重新梳理了一遍,这个时候,用户提供了一个比较关键的信息—— “抓包这个事,在 docker0 上可以抓到,然而到了容器内抓不到容器返回 RST, ACK ” !然而,根据我的知识,我知道在docker0 和容器内的 veth 网卡上,中间再也没有什么网络设备了(参看《Docker基础技术:LINUX NAMESPACE(下)》)!

于是这个事把我们逼到了最后一种情况 —— IP地址冲突了!

Linux下看IP地址冲突还不是一件比较简单事的,而在用户的生产环境下没有办法安装一些其它的命令,所以只能用已有的命令,这个时候,我们发现用户的机器上有 arping 于是我们用这个命令来检测有没有冲突的IP地址。使用了下面的命令:

1
2
$ arping -D -I docker0 -c 2 10.233.14.145
$ echo $?

根据文档,-D 参数是检测IP地址冲突模式,如果这个命令的退状态是 0 那么就有冲突。结果返回了 1 。而且,我们用 arping IP的时候,没有发现不同的mac地址。 这个时候,似乎问题的线索又断了

因为客户那边还在处理一些别的事情,所以,我们在时断时续的情况下工作,而还一些工作都需要用户完成,所以,进展有点缓慢,但是也给我们一些时间思考问题。

柳暗花明

现在我们知道,IP冲突的可能性是非常大的,但是我们找不出来是和谁的IP冲突了。而且,我们知道只要把这台机器重启一下,问题一定就解决掉了,但是我们觉得这并不是解决问题的方式,因为重启机器可以暂时的解决掉到这个问题,而如果我们不知道这个问题怎么发生的,那么未来这个问题还会再来。而重启线上机器这个成本太高了。

于是,我们的好奇心驱使我们继续调查。我让用户 kubectl delete 其中两个有问题的pod,因为本来就服务不断重启,所以,删掉也没有什么问题。删掉这两个pod后(一个是IP为 10.233.14.145 另一个是 10.233.14.137),我们发现,kubernetes在其它机器上重新启动了这两个服务的新的实例。然而,在问题机器上,这两个IP地址居然还可以ping得通

好了,IP地址冲突的问题可以确认了。因为10.233.14.xxx 这个网段是 docker 的,所以,这个IP地址一定是在这台机器上。所以,我们想看看所有的 network namespace 下的 veth 网卡上的IP。

在这个事上,我们费了点时间,因为对相关的命令也 很熟悉,所以花了点时间Google,以及看相关的man。

  • 首先,我们到 /var/run/netns目录下查看系统的network namespace,发现什么也没有。
  • 然后,我们到 /var/run/docker/netns 目录下查看Docker的namespace,发现有好些。
  • 于是,我们用指定位置的方式查看Docker的network namespace里的IP地址

这里要动用 nsenter 命令,这个命令可以进入到namespace里执行一些命令。比如

1
$ nsenter --net= /var/run/docker/netns/421bdb2accf1 ifconfig -a

上述的命令,到 var/run/docker/netns/421bdb2accf1 这个network namespace里执行了ifconfig -a 命令。于是我们可以用下面 命令来遍历所有的network namespace。

1
$ ls /var/run/docker/netns | xargs -I {} nsenter --net= /var/run/docker/netns/ {} ip addr

然后,我们发现了比较诡异的事情。

  • 10.233.14.145 我们查到了这个IP,说明,docker的namespace下还有这个IP。
  • 10.233.14.137,这个IP没有在docker的network namespace下查到。

有namespace leaking?于是我上网查了一下,发现了一个docker的bug – 在docker remove/stop 一个容器的时候,没有清除相应的network namespace,这个问题被报告到了Issue#31597 然后被fix在了 PR#31996,并Merge到了 Docker的 17.05版中。而用户的版本是 17.09,应该包含了这个fix。不应该是这个问题,感觉又走不下去了。

不过, 10.233.14.137 这个IP可以ping得通,说明这个IP一定被绑在某个网卡,而且被隐藏到了某个network namespace下。

到这里,要查看所有network namespace,只有最后一条路了,那就是到 /proc/ 目录下,把所有的pid下的 /proc/<pid>/ns 目录给穷举出来。好在这里有一个比较方便的命令可以干这个事 : lsns

于是我写下了如下的命令:

1
$ lsns -t net | awk ‘{print $4}'' | xargs -t -I {} nsenter -t {}&nbsp;-n ip addr | grep -C 4 "10.233.14.137"

解释一下。

  • lsns -t net 列出所有开了network namespace的进程,其第4列是进程PID
  • 把所有开过network namespace的进程PID拿出来,转给 xargs 命令
  •  xargs 命令把这些PID 依次传给 nsenter 命令,
    • xargs -t 的意思是会把相关的执行命令打出来,这样我知道是那个PID。
    • xargs -I {}  是声明一个占位符来替换相关的PID

最后,我们发现,虽然在 /var/run/docker/netns 下没有找到 10.233.14.137 ,但是在lsns 中找到了三个进程,他们都用了10.233.14.137 这个IP(冲突了这么多),而且他们的MAC地址全是一样的!(怪不得arping找不到)。通过ps 命令,可以查到这三个进程,有两个是java的,还有一个是/pause (这个应该是kubernetes的沙盒)。

我们继续乘胜追击,穷追猛打,用pstree命令把整个进程树打出来。发现上述的三个进程的父进程都在多个同样叫 docker-contiane 的进程下!

这明显还是docker的,但是在docker ps 中却找不道相应的容器,什么鬼!快崩溃了……

继续看进程树,发现,这些 docker-contiane 的进程的父进程不在 dockerd 下面,而是在 systemd 这个超级父进程PID 1下,我靠!进而发现了一堆这样的野进程(这种野进程或是僵尸进程对系统是有害的,至少也是会让系统进入亚健康的状态,因为他们还在占着资源)。

docker-contiane 应该是 dockerd 的子进程,被挂到了 pid 1 只有一个原因,那就是父进程“飞”掉了,只能找 pid 1 当养父。这说明,这台机器上出现了比较严重的 dockerd 进程退出的问题,而且是非常规的,因为 systemd 之所以要成为 pid 1,其就是要监管所有进程的子子孙孙,居然也没有管理好,说明是个非常规的问题。(注,关于 systemd,请参看《Linux PID 1 和 Systemd 》,关于父子进程的事,请参看《Unix高级环境编程》一书)

接下来就要看看 systemd 为 dockerd 记录的日志了…… (然而日志只有3天的了,这3天dockerd没有任何异常)

总结

通过这个调查,可以总结一下,

1) 对于问题调查,需要比较扎实的基础知识,知道问题的成因和范围。

2)如果走不下去了,要重新梳理一下,回头仔细看一下一些蛛丝马迹,认真推敲每一个细节。

3) 各种诊断工具要比较熟悉,这会让你事半功倍。

4)系统维护和做清洁比较类似,需要经常看看系统中是否有一些僵尸进程或是一些垃圾东西,这些东西要及时清理掉。

最后,多说一下,很多人都说,Docker适合放在物理机内运行,这并不完全对,因为他们只考虑到了性能成本,没有考虑到运维成本,在这样512GB中启动几百个容器的玩法,其实并不好,因为这本质上是个大单体,因为你一理要重启某些关键进程或是机器,你的影响面是巨大的

 

———————— 更新 2018/12/10 —————————

问题原因

这两天在自己的环境下测试了一下,发现,只要是通过 systemctl start/stop docker 这样的命令来启停 Docker, 是可以把所有的进程和资源全部干掉的。这个是没有什么问题的。我唯一能重现用户问题的的操作就是直接 kill -9 <dockerd pid> 但是这个事用户应该不会干。而 Docker 如果有 crash 事件时,Systemd 是可以通过 journalctl -u docker 这样的命令查看相关的系统日志的。

于是,我找用户了解一下他们在Docker在启停时的问题,用户说,他们的执行 systemctl stop docker 这个命令的时候,发现这个命令不响应了,有可能就直接按了 Ctrl +C

这个应该就是导致大量的 docker-containe 进程挂到 PID 1 下的原因了。前面说过,用户的一台物理机上运行着上百个容器,所以,那个进程树也是非常庞大的,我想,停服的时候,系统一定是要遍历所有的docker子进程来一个一个发退出信号的,这个过程可能会非常的长。导致操作员以为命令假死,而直接按了 Ctrl + C ,最后导致很多容器进程并没有终止……

 

其它事宜

有同学问,为什么我在这个文章里写的是 docker-containe 而不是 containd 进程?这是因为,前着是 yum install  安装CentOS系统 rpm 包而来的,后者是安装 Docker 发布的安装 rpm 包而来的。

下面是这两种不同安装包的进程树的差别(其中 sleep 是我用 buybox 镜像启动的)

CENTOS 系统安装包
1
2
3
4
5
6
systemd───dockerd─┬─docker-containe─┬─3*[docker-containe─┬─ sleep ]
                   │                 │                    └─9*[{docker-containe}]]
                   │                 ├─docker-containe─┬─ sleep
                   │                 │                 └─10*[{docker-containe}]
                   │                 └─14*[{docker-containe}]
                   └─17*[{dockerd}]
DOCKER 官方安装包
1
2
3
4
5
6
systemd───dockerd─┬─containerd─┬─3*[containerd-shim─┬─ sleep ]
                   │            │                 └─9*[{containerd-shim}]
                   │            ├─2*[containerd-shim─┬─ sleep ]
                   │            │                    └─9*[{containerd-shim}]]
                   │            └─11*[{containerd}]
                   └─10*[{dockerd}]

顺便说一下,自从 Docker 1.11版以后,Docker进程组模型就改成上面这个样子了.

  • dockerd 是 Docker Engine守护进程,直接面向操作用户。dockerd 启动时会启动containerd 子进程,他们之前通过RPC进行通信。
  • containerd 是dockerdrunc之间的一个中间交流组件。他与 dockerd 的解耦是为了让Docker变得更为的中立,而支持OCI 的标准 。
  • containerd-shim  是用来真正运行的容器的,每启动一个容器都会起一个新的shim进程, 它主要通过指定的三个参数:容器id,boundle目录(containerd的对应某个容器生成的目录,一般位于:/var/run/docker/libcontainerd/containerID), 和运行命令(默认为 runc)来创建一个容器。
  • docker-proxy 你有可能还会在新版本的Docker中见到这个进程,这个进程是用户级的代理路由。只要你用 ps -elf 这样的命令把其命令行打出来,你就可以看到其就是做端口映射的。如果你不想要这个代理的话,你可以在 dockerd 启动命令行参数上加上:  --userland-proxy=false 这个参数。

更多的细节,大家可以自行Google。这里推荐两篇文章:

  • Docker, Containerd & Standalone Runtimes — Here’s What You Should Know
  • Docker components explained

docker for mac 安装 kubernetes、kubernetes dashboard

docker for mac 安装 kubernetes、kubernetes dashboard

  • 安装参考地址(按照此文档,安装成功):https://yq.aliyun.com/articles/508460

  • 官方说明:https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/

  • 常用命令 kubectl 命令:

kubectl get namespaces
kubectl get pods --namespace kube-system
kubectl get deployments --namespace kube-system
kubectl get services --namespace kube-system
kubectl -n kube-system edit service kubernetes-dashboard
kubectl get pods
kubectl get deployments
kubectl get services
kubectl config view

获取令牌,然后登陆 kubernetes dashboard

➜  ~ kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-6ljm8   kubernetes.io/service-account-token   3         6h
➜  ~ kubectl describe secrets default-token-6ljm8
Name:         default-token-6ljm8
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name=default
              kubernetes.io/service-account.uid=77d014c2-0804-11e9-acd8-025000000001

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tNmxqbTgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6Ijc3ZDAxNGMyLTA4MDQtMTFlOS1hY2Q4LTAyNTAwMDAwMDAwMSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.G9TGa4KGj5B-cMv-0-vuangR2_tFiQ1nJMgtsEPs1BEwPAyjmaC-BL5y0Ux9HyC1mlt0DklO-8_o41i4OD_w0wLymxi8zZQxgY7Tlu3_oE5OKnK58xWN-mMTKKnvfDpZrIBbkWQ5EB49LC7QiTBKGAoixGyOBvU1fmD2AzpdO3sWvNsaOWbMLFcwzHA-M2V-CKU3I07Hxs6uIi9juk4IqkTryfvCDUafTrubpkVktwQr7UwzvmKfbPoWLyn1tbCDhR3Il64daoTE9nlmqWwYZZFmfaZjWWWYfi3QPXuNUNpXRVVd_6gcjUzebR1o-22KoOUbobQ94K-1bYJOQSZNnA

将 token 部分复制到登录页的 token 输入框,登陆即可。 出处:http://www.cnblogs.com/along21/p/9811860.html#auto_id_11

Docker Kubernetes Service 网络服务代理模式详解

Docker Kubernetes Service 网络服务代理模式详解

Docker Kubernetes  Service 网络服务代理模式详解

Service

  • service 是实现 kubernetes 网络通信的一个服务
  • 主要功能:负载均衡、网络规则分布到具体 pod

注:kubernetes deployment 服务分配服务器负载均衡 VIP 只能 NODE 节点单独访问,这里需要外网用户可以放问到容器内,这里就需要用到 service。


网络代理模式

  • kube-proxy v1.0 中只支持 userspace 模式,在 v1.1 中,添加了 iptables 代理,在 v1.2 开始 iptables 是默认的。
  • service 为 pod 提供了一个统一的访问入口已 userspace 模式 iptables 模式,进行代理转发。
  • userspace 与 iptables 主要是实现对 IP 的转发。

userspace 模式
1、NODE 节点的客户端要访问 service 的代理 IP + 端口。
2、IP 与端口是由 kube-proxy 维护的,它创建了 IPtables 一个规则。
3、请求经过 IPtables 后,再转发给 kube-proxy 端口。
4、kube-proxy 关联后端的 backend pod,将请求根据标签代理到指定的 Backenpod。

注:Backend Pod:访问节点 IP 的指定端口,转发到后端的 pod。kubectl get ep 可查看 Backend Pod IP。
注:效率比较低,需要经过一次转发。
注:kube-proxy 是使用的用户空间处理。

 

iptables 模式
1、NODE 节点客户端直接访问 serviceIP,Linux 根据 Iptables 协议栈规则策略匹配。
2、serviceIP 根据标签直接访问 Backendpod。

注:直接使用 iptables 使用内核空间处理 速度快。


服务代理类型

ClusterIP

  分配一个内部集群 IP 地址,只能在集群内部访问(同 Namespace 内的 Pod),默认 ServiceType。

NodePort

  分配一个内部集群 IP 地址,并在每个节点上启用一个端口来暴露服务,可以在集群外部访问。

  访问地址:<NodeIP>:<NodePort>

LoadBalancer

  分配一个内部集群 IP 地址,并在每个节点上启用一个端口来暴露服务。

  除此之外,Kubernetes 会请求底层云平台上的负载均衡器,将每个 Node([NodeIP]:[NodePort])作为后端添加进去。

ExternalName

  通过 CNAME 将 Service 与 externalName 的值映射。要求 kube-dns 的版本为 v1.7+。

 

拓扑图
nodeport:前端对用户代理使用 nginx/LVS/HAProxy 来做为负载均衡使用,代理每个 NODE 节点 IP 端口去访问 pod。

 

LoadBalancer:主要通过云平台提供封装好的接口来做为负载均衡器进行代理转发。

 

Docker Kubernetes Volume 网络数据卷

Docker Kubernetes Volume 网络数据卷

Docker Kubernetes Volume 网络数据卷

由于支持网络数据卷众多 今天只拿 nfs 作为案例。

支持网络数据卷

  • nfs
  • iscsi
  • glusterfs
  • awsElasticBlockStore
  • cephfs
  • azureFileVolume
  • azureDiskVolume
  • vsphereVolume
  • .....

环境:

  • 系统:Centos 7.4 x64
  • Docker 版本:18.09.0
  • Kubernetes 版本:v1.8
  • 管理节点:192.168.1.79
  • 工作节点:192.168.1.78
  • 工作节点:192.168.1.77

一、搭建 NFS 服务与客户端

1、管理节点:安装 nfs 服务端、配置 nfs 主配置文件、添加权限、启动

yum install nfs-utils -y
vim /etc/exports
# 添加目录给相应网段访问并添加读写权限 /data 192.168.1.0/24(insecure,rw,async,no_root_squash)
# 创建共享目录,添加权限
mkdir -p /data
chmod 777 /data
# 开启rpc服务
systemctl start rpcbind
# 启动服务并设置开机自启 systemctl start nfs

2、工作节点:安装 nfs 客户端、启动服务

yum install nfs-utils -y
# 开启rpc服务
systemctl start rpcbind
# 启动服务并设置开机自启
systemctl start nfs

二、共享 NFS 网络数据卷

1、管理节点:创建 yaml 文件

vim nginx-nfs.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment-nfs
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.10
        volumeMounts:
        - name: wwwroot
          mountPath: /var/www/html
        ports:
        - containerPort: 80
      volumes:
      - name: 
        nfs:
          server: 192.168.1.79
          path: /data
# 指定api版本
apiVersion: extensions/v1beta1
# 指定需要创建的资源对象
kind: Deployment
# 源数据、可以写name,命名空间,对象标签
metadata:
# 指定对象名称
  name: nginx-deployment2-nfs
# 描述资源相关信息
spec:
# 指定pod 副本数,默认1
  replicas: 3
# 描述资源具体信息
  template:
# 匹配标签字段
    metadata:
# 指定pod标签value:key
      labels:
# 标签名
        app: nginx
# 管理容器
    spec:
# 指定容器信息
      containers:
# 指定容器名称
      - name: nginx
# 指定镜像名称
        image: nginx:1.10
# 网络数据卷管理
        volumeMounts:
# 数据卷名称
        - name: wwwroot
# 容器数据卷挂载路径
          mountPath: /var/www/html
# 端口管理
        ports:
# 暴露端口
        - containerPort: 80
# 网络共享数据卷管理
      volumes:
# 数据卷名称两边需要相同
      - name: wwwroot
# 数据卷类型为nfs
        nfs:
# NFS服务器地址
          server: 192.168.1.79
# 服务端共享路径
          path: /data
文件注解

2、管理节点:创建 Deployment

kubectl create -f nginx-nfs.yaml
命令:kubectl get pods -o wide

NAME                                    READY     STATUS    RESTARTS   AGE       IP            NODE
nginx-deployment-nfs-5fbcddddb6-7btt4   1/1       Running   0          55s       172.17.2.11   192.168.1.78
nginx-deployment-nfs-5fbcddddb6-sf6bz   1/1       Running   0          55s       172.17.2.10   192.168.1.78
nginx-deployment-nfs-5fbcddddb6-ws8wk   1/1       Running   0          55s       172.17.1.9    192.168.1.77
查看创建情况
命令:kubectl describe nginx-deployment-nfs-5fbcddddb6-sf6bz

    Mounts:
      /var/www/html from wwwroot (rw)
Conditions:
  Type           Status
  Initialized    True 
  Ready          True 
  PodScheduled   True 
Volumes:
  wwwroot:
    Type:        NFS (an NFS mount that lasts the lifetime of a pod)
    Server:      192.168.1.79
    Path:        /data
    ReadOnly:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     <none>
reated container
查看详细信息

3、测试

# 1、宿主端nfs共享文件内创建文件
命令:touch /data/123

# 2、进入容器内查看文件是否共享
命令:kubectl exec nginx-deployment-nfs-5fbcddddb6-sf6bz -it bash

root@nginx-deployment-nfs-5fbcddddb6-sf6bz:/# ls /var/www/html/
123

 

docker kubernetes 网络布局

docker kubernetes 网络布局

本文主要是针对docker 和 kubernetes 的网络的一个简单学习,让大家对docker 和 kubernetes 有一个基本的认知,方便后面深入的学习。

前端为什么要学习Docker 和 kubernetes

Docker的背景:

  • 2013年Docker项目发布,迅速风靡全球。
  • 2015~2017年,容器编排项目k8s,docker swarm,mesos展开激烈竞争。
  • 2017年10月docker企业版内置k8s项目。标志k8s最终胜出。
  • k8s作为云计算的操作系统,直接影响了服务部署运维,因此普通开发者需要理解容器相关的各种概念,掌握k8s的使用技能。

环境隔离
docker 实现了资源隔离,一台机器运行多个容器互无影响。

更高效的资源利用
docker 容器的运行不需要额外的虚拟化管理程序的支持,它是内核级的虚拟化,可以实现更高的性能,同时对资源的额外需求很低。

更快速的交付部署
使用 docker,开发人员可以利用镜像快速构建一套标准的研发环境,开发完成后,测试和运维人员可以直接通过使用相同的环境来部署代码。

更易迁移扩展
docker 容器几乎可以在任意的平台上运行,包括虚拟机、公有云、私有云、个人电脑、服务器等,这种兼容性让用户可以在不同平台之间轻松的迁移应用。

更简单的更新管理
使用 Dockerfile,只需要很少的配置修改,就可以替代以往大量的更新工作。并且所有修改都是以增量的方式进行分发和更新,从而实现自动化和高效的容器管理。

介绍一下Linux network namespace

network namespace 是实现网络虚拟化的重要功能。

  1. linux 内核提供的功能
  2. 它能创建多个隔离的网络空间,这些网络空间有独自的网络栈信息(独立的网卡、路由表、ARP 表、iptables 等和网络相关的资源)。

这篇文章借助 ip 命令来完成各种操作。ip 命令来自于 iproute2 安装包,一般系统会默认安装

Network Namespace隔离网络

  • 容器与容器之间,如果网络不隔离的话,比如我们起了一个nginx,占了80端口,就不能再启动监听80端口的服务,如果两个容器客户租用同一台宿主机,显然不隔离网络的容器不能满足需求。
  • Network Namespace提供了网络资源的隔离功能,包括网络设备,IPv4和IPv6协议栈,IP路由表,防火墙,套接字等。一个物理网络设备最多存在于一个Network Namespace中,不同的Network Namespace的进程如果需要网络通信,可以创建veth pair(虚拟网络设备对:有两端,类似管道,如果数据从一端传入,另一端也能收到,反之亦然),veth pair一端放在Network Namespace A中,另一端放在Network Namespace B中,这样A和B就可以网络通信了。
  • 下面实地操作下,使用ip netns命令,在宿主机上添加两个Network Namespace。
root@10-25-125-129:~# ip netns add net1
root@10-25-125-129:~# ip netns ls
net1
net0
  • 创建veth pair,然后执行ip link可以看到veth0和veth1两个虚拟网卡。
root@10-25-125-129:~# ip link add type veth
root@10-25-125-129:~# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
...
4: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 62:47:4c:5a:bd:da brd ff:ff:ff:ff:ff:ff
5: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether de:ad:ee:97:79:39 brd ff:ff:ff:ff:ff:ff
  • 接下来把两个虚拟网卡分别放到两个namespace里面,可以用命令’ip link set DEV netns NAME‘来实现。然后分别在两个namespace里面查看网络设备。可以看到网卡设备已绑定。
root@10-25-125-129:~# ip link set veth0 netns net0
root@10-25-125-129:~# ip link set veth1 netns net1
root@10-25-125-129:~# ip netns exec net0 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: veth0@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 62:47:4c:5a:bd:da brd ff:ff:ff:ff:ff:ff link-netnsid 1
root@10-25-125-129:~# ip netns exec net1 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether de:ad:ee:97:79:39 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  • 最后,我们先启用这对网卡,给这对网卡配置上IP地址。
root@10-25-125-129:~# ip netns exec net0 ip link set veth0 up
root@10-25-125-129:~# ip netns exec net0 ip addr add 10.0.1.1/24 dev veth0
root@10-25-125-129:~# ip netns exec net1 ip link set veth1 up
root@10-25-125-129:~# ip netns exec net1 ip addr add 10.0.1.2/24 dev veth1
  • 配置完以后,我们在namespace net0里面ping net1的IP地址。ping通后,说明这两个namespace已经网络连通了。
root@10-25-125-129:~# ip netns exec net0 ping 10.0.1.2
PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.
64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from 10.0.1.2: icmp_seq=2 ttl=64 time=0.035 ms
64 bytes from 10.0.1.2: icmp_seq=3 ttl=64 time=0.032 ms
64 bytes from 10.0.1.2: icmp_seq=4 ttl=64 time=0.038 ms
^C
--- 10.0.1.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3062ms
rtt min/avg/max/mdev = 0.032/0.037/0.045/0.007 ms
  • Docker的容器之间通信原理基本类似,我们实现了两个namespace通信,但是如果多个namespace通信如何实现呢?直觉告诉我们需要一个虚拟交换机,在docker里面就是docker0网桥。我们创建一个veth pair,一端绑在容器的network namespace,另一端绑在宿主机的docker0网桥上。网络拓扑图如下所示。(图片来自《容器与容器云(第二版)》)。这样每个容器都有自己的网络设备,对开发者来说,每个容器都有自己的IP地址。

Docker网络模式

Docker网络模式配置说明
host模式–net=host容器和宿主机共享Network namespace
container模式–net=container:NAME_or_ID容器和另外一个容器共享Network namespace。 kubernetes中的pod就是多个容器共享一个Network namespace。
none模式–net=none容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等。
bridge模式–net=bridge(默认为该模式)

k8s简介

架构体系

. API Server(kube-apiserver)
API Server是k8s的前端接口,各种客户端工具以及k8s其他组件可以通过它管理集群的各种资源。

2.Scheduler(kube-scheduler)
scheduer负责决定将pod放在哪个node上运行。另外scheduler在调度时会充分考虑集群的架构,当前各个节点的负载,以及应用对高可用、性能、数据亲和性的需求。

3.Controller Manager(kube-controller-manager)
负责管理集群的各种资源,保证资源处于预期的状态

4.etcd
负责保存k8s集群的配置信息和各种资源的状态信息,当数据发生变化时,etcd会快速的通知k8s相关组件。

5.kubelet~~~~
是node的agent,当scheduler去确定在某个node上运行pod后,会将pod的具体配置信息发送给该节点的kubelet,kubelet会根据遮羞信息创建和运行容器,并向master报告运行状态。

6.kube-proxy
每个node都会运行kube-proxy服务,外界通过service访问pod,kube-proxy负责将降访问service的TCP/UDP数据流转发到后端的容器。如果有多个副本,kube-proxy会实现负载均衡。

K8S网络模型

根据以上的一些要求,需要解决的问题

  • 容器之间的网络
  • Pod与Pod之间的网络
  • Pod与Service之间的网络?
1.容器和容器之间的网络

pod有多个容器,它们之间怎么通信?

pod中每个docker容器和pod在一个网络命名空间内,所以ip和端口等等网络配置,都和pod一样,主要通过一种机制就是,docker的一种网络模式,container,新创建的Docker容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等

2.pod与pod之间的网络

pod与pod之间的网络:首先pod自身拥有一个IP地址,不同pod之间直接使用IP地址进行通信即可

3.同一台node节点上pod和pod通信

疑问:那么不同pod之间,也就是不同网络命名空间之间如何进行通信(现在还是,同一台node节点上)

解决

简单说veth对就是一个成对的端口,所有从这对端口一端进入的数据包,都将从另一端出来。

为了让多个Pod的网络命名空间链接起来,我们可以让veth对的一端链接到root网络命名空间(宿主机的),另一端链接到Pod的网络命名空间。

嗯,那么继续。

还需要用到一个Linux以太网桥,它是一个虚拟的二层网络设备,目的就是把多个以太网段连接起来,它维护一个转发表,通过查看每个设备mac地址决定转发,还是丢弃数据

  • pod1-->pod2(同一台node上),pod1通过自身eth0网卡发送数据,eth0连接着veth0,网桥把veth0和veth1组成了一个以太网,然后数据到达veth0之后,网桥通过转发表,发送给veth1,veth1直接把数据传给pod2的eth0。
4.不同node节点上pod和pod通信

CIDR的介绍:

CIDR(Classless Inter-Domain Routing,无类域间路由选择)它消除了传统的A类、B类和C类地址以及划分子网的概念,因而可以更加有效地分配IPv4的地址空间。它可以将好几个IP网络结合在一起,使用一种无类别的域际路由选择算法,使它们合并成一条路由从而较少路由表中的路由条目减轻Internet路由器的负担。

192.168.19.0 / 255.255.255.0
.。。。。
192.168.50.0 / 255.255.255.0
这几个地址展开来是:
11000000 10101000 00010011 00000000
11000000 10101000 00110010 00000000
他们都有相同的前18位,所以汇总地址就是192.168.0.0/18

k8s集群中,每个node节点都会被分配一个CIDR块,(把网络前缀都相同的连续地址组成的地址组称为CIDR地址块)用来给node上的pod分配IP地址,另外还需要把pod的ip和所在nodeip进行关联

关于记一次KUBERNETES/DOCKER网络排障docker 网络问题的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于docker for mac 安装 kubernetes、kubernetes dashboard、Docker Kubernetes Service 网络服务代理模式详解、Docker Kubernetes Volume 网络数据卷、docker kubernetes 网络布局的相关信息,请在本站寻找。

本文标签: