Docker跨主机容器间网络通信实现的工具有Pipework、Flannel、Weave、Open vSwitch(虚拟交换机)、Calico, 其中Pipework、Weave、Flannel,三者的区别是:

Weave的思路
在每个宿主机上布置一个特殊的route的容器,不同宿主机的route容器连接起来。 route拦截所有普通容器的ip请求,并通过udp包发送到其他宿主机上的普通容器。这样在跨机的多个容器端看到的就是同一个扁平网络。 weave解决了网络问题,不过部署依然是单机的。

Flannel的思路
Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。但在默认的Docker配置中,每个节点上的Docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外IP地址。并使这些容器之间能够之间通过IP地址相互找到,也就是相互ping通。Flannel设计目的就是为集群中所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得"同属一个内网"且"不重复的"IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。

Flannel实质上是一种"覆盖网络(overlay network)",即表示运行在一个网上的网(应用层网络),并不依靠ip地址来传递消息,而是采用一种映射机制,把ip地址和identifiers做映射来资源定位。也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式。

Flannel 使用etcd存储配置数据和子网分配信息。flannel 启动之后,后台进程首先检索配置和正在使用的子网列表,然后选择一个可用的子网,然后尝试去注册它。etcd也存储这个每个主机对应的ip。flannel 使用etcd的watch机制监视/coreos.com/network/subnets下面所有元素的变化信息,并且根据它来维护一个路由表。为了提高性能,flannel优化了Universal TAP/TUN设备,对TUN和UDP之间的ip分片做了代理。

Flannel工作原理
每个主机配置一个ip段和子网个数。例如,可以配置一个覆盖网络使用 10.100.0.0/16段,每个主机/24个子网。因此主机a可以接受10.100.5.0/24,主机B可以接受10.100.18.0/24的包。flannel使用etcd来维护分配的子网到实际的ip地址之间的映射。对于数据路径,flannel 使用udp来封装ip数据报,转发到远程主机。选择UDP作为转发协议是因为他能穿透防火墙。例如,AWS Classic无法转发IPoIP or GRE 网络包,是因为它的安全组仅仅支持TCP/UDP/ICMP。 Flannel工作原理流程图如下 (默认的节点间数据通信方式是UDP转发;  flannel默认使用8285端口作为UDP封装报文的端口,VxLan使用8472端口)

对上图的简单说明 (Flannel的工作原理可以解释如下):
-> 数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端。
-> Flannel通过Etcd服务维护了一张节点间的路由表,该张表里保存了各个节点主机的子网网段信息。
-> 源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一样的由docker0路由到达目标容器。

这样整个数据包的传递就完成了,这里需要解释三个问题:
1) UDP封装是怎么回事?
在UDP的数据内容部分其实是另一个ICMP(也就是ping命令)的数据包。原始数据是在起始节点的Flannel服务上进行UDP封装的,投递到目的节点后就被另一端的Flannel服务
还原成了原始的数据包,两边的Docker服务都感觉不到这个过程的存在。

2) 为什么每个节点上的Docker会使用不同的IP地址段?
这个事情看起来很诡异,但真相十分简单。其实只是单纯的因为Flannel通过Etcd分配了每个节点可用的IP地址段后,偷偷的修改了Docker的启动参数。
在运行了Flannel服务的节点上可以查看到Docker服务进程运行参数(ps aux|grep docker|grep "bip"),例如“--bip=182.48.25.1/24”这个参数,它限制了所在节
点容器获得的IP范围。这个IP范围是由Flannel自动分配的,由Flannel通过保存在Etcd服务中的记录确保它们不会重复。

3) 为什么在发送节点上的数据会从docker0路由到flannel0虚拟网卡,在目的节点会从flannel0路由到docker0虚拟网卡?
例如现在有一个数据包要从IP为172.17.18.2的容器发到IP为172.17.46.2的容器。根据数据发送节点的路由表,它只与172.17.0.0/16匹配这条记录匹配,因此数据从docker0
出来以后就被投递到了flannel0。同理在目标节点,由于投递的地址是一个容器,因此目的地址一定会落在docker0对于的172.17.46.0/24这个记录上,自然的被投递到了docker0网卡。

pipework的思路
pipework是一个单机的工具,组合了brctl等工具,可以认为pipework解决的是宿主机上的设置容器的虚拟网卡、网桥、ip等,可以配合其他网络使用。

如果容器数量不多,想简单的组一个大的3层网络,可以考虑weave
如果容器数量很多,而且你们的环境复杂,需要多个子网,可以考虑open vswitch或者fannel
weave的总体网络性能表现欠佳, flannel VXLAN 能满足要求,一般推荐用flannel

Flannel环境部署记录

1)机器环境(centos7系统)

182.48.115.233     部署etcd,flannel,docker      主机名:node-1   主控端(通过etcd)
182.48.115.235 部署flannel,docker 主机名:node-2 被控端

2)node-1(182.48.115.233)机器操作

设置主机名及绑定hosts
[root@node-1 ~]# hostnamectl --static set-hostname node-1
[root@node-1 ~]# vim /etc/hosts
182.48.115.233 node-1
182.48.115.233 etcd
182.48.115.235 node-2 关闭防火墙,如果开启防火墙,则最好打开2379和4001端口
[root@node-1 ~]# systemctl disable firewalld.service
[root@node-1 ~]# systemctl stop firewalld.service 先安装docker环境
[root@node-1 ~]# yum install -y docker 安装etcd
k8s运行依赖etcd,需要先部署etcd,下面采用yum方式安装:
[root@node-1 ~]# yum install etcd -y yum安装的etcd默认配置文件在/etc/etcd/etcd.conf,编辑配置文件:
[root@node-1 ~]# cp /etc/etcd/etcd.conf /etc/etcd/etcd.conf.bak
[root@node-1 ~]# cat /etc/etcd/etcd.conf
#[member]
ETCD_NAME=master #节点名称
ETCD_DATA_DIR="/var/lib/etcd/default.etcd" #数据存放位置
#ETCD_WAL_DIR=""
#ETCD_SNAPSHOT_COUNT="10000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
#ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001" #监听客户端地址
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
#ETCD_CORS=""
#
#[cluster]
#ETCD_INITIAL_ADVERTISE_PEER_URLS="http://localhost:2380"
# if you use different ETCD_NAME (e.g. test), set ETCD_INITIAL_CLUSTER value for this name, i.e. "test=http://..."
#ETCD_INITIAL_CLUSTER="default=http://localhost:2380"
#ETCD_INITIAL_CLUSTER_STATE="new"
#ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_ADVERTISE_CLIENT_URLS="http://etcd:2379,http://etcd:4001" #通知客户端地址
#ETCD_DISCOVERY=""
#ETCD_DISCOVERY_SRV=""
#ETCD_DISCOVERY_FALLBACK="proxy"
#ETCD_DISCOVERY_PROXY="" 启动etcd并验证状态
[root@node-1 ~]# systemctl start etcd [root@node-1 ~]# ps -ef|grep etcd
etcd 28145 1 1 14:38 ? 00:00:00 /usr/bin/etcd --name=master --data-dir=/var/lib/etcd/default.etcd --listen-client-urls=http://0.0.0.0:2379,http://0.0.0.0:4001
root 28185 24819 0 14:38 pts/1 00:00:00 grep --color=auto etcd
[root@node-1 ~]# lsof -i:2379
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
etcd 28145 etcd 6u IPv6 1283822 0t0 TCP *:2379 (LISTEN)
etcd 28145 etcd 18u IPv6 1284133 0t0 TCP localhost:53203->localhost:2379 (ESTABLISHED)
........ [root@node-1 ~]# etcdctl set testdir/testkey0 0
0
[root@node-1 ~]# etcdctl get testdir/testkey0
0
[root@node-1 ~]# etcdctl -C http://etcd:4001 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://etcd:2379
cluster is healthy
[root@node-1 ~]# etcdctl -C http://etcd:2379 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://etcd:2379
cluster is healthy 安装覆盖网络Flannel
[root@node-1 ~]# yum install flannel 配置Flannel
[root@node-1 ~]# cp /etc/sysconfig/flanneld /etc/sysconfig/flanneld.bak
[root@node-1 ~]# vim /etc/sysconfig/flanneld
# Flanneld configuration options # etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="http://etcd:2379" # etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX="/atomic.io/network" # Any additional options that you want to pass
#FLANNEL_OPTIONS="" 配置etcd中关于flannel的key(这个只在安装了etcd的机器上操作)
Flannel使用Etcd进行配置,来保证多个Flannel实例之间的配置一致性,所以需要在etcd上进行如下配置('/atomic.io/network/config'这个key与上文/etc/sysconfig/flannel中的配置项FLANNEL_ETCD_PREFIX是相对应的,错误的话启动就会出错):
[root@node-1 ~]# etcdctl mk /atomic.io/network/config '{ "Network": "182.48.0.0/16" }'
{ "Network": "182.48.0.0/16" } 温馨提示:上面flannel设置的ip网段可以任意设定,随便设定一个网段都可以。容器的ip就是根据这个网段进行自动分配的,ip分配后,容器一般是可以对外联网的(网桥模式,只要宿主机能上网就可以) 启动Flannel
[root@node-1 ~]# systemctl enable flanneld.service
[root@node-1 ~]# systemctl start flanneld.service
[root@node-1 ~]# ps -ef|grep flannel
root 9305 9085 0 09:12 pts/2 00:00:00 grep --color=auto flannel
root 28876 1 0 May15 ? 00:00:07 /usr/bin/flanneld -etcd-endpoints=http://etcd:2379 -etcd-prefix=/atomic.io/network 启动Flannel后,一定要记得重启docker,这样Flannel配置分配的ip才能生效,即docker0虚拟网卡的ip会变成上面flannel设定的ip段
[root@node-1 ~]# systemctl restart docker

3)node-2(182.48.115.235)机器操作

设置主机名及绑定hosts
[root@node-2 ~]# hostnamectl --static set-hostname node-2
[root@node-2 ~]# vim /etc/hosts
182.48.115.233 node-1
182.48.115.233 etcd
182.48.115.235 node-2 关闭防火墙,如果开启防火墙,则最好打开2379和4001端口
[root@node-2 ~]# systemctl disable firewalld.service
[root@node-2 ~]# systemctl stop firewalld.service 先安装docker环境
[root@node-2 ~]# yum install -y docker 安装覆盖网络Flannel
[root@node-2 ~]# yum install flannel 配置Flannel
[root@node-2 ~]# cp /etc/sysconfig/flanneld /etc/sysconfig/flanneld.bak
[root@node-2 ~]# vim /etc/sysconfig/flanneld
# Flanneld configuration options # etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="http://etcd:2379" # etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX="/atomic.io/network" # Any additional options that you want to pass
#FLANNEL_OPTIONS="" 启动Flannel
[root@node-2 ~]# systemctl enable flanneld.service
[root@node-2 ~]# systemctl start flanneld.service
[root@node-2 ~]# ps -ef|grep flannel
root 3841 9649 0 09:11 pts/0 00:00:00 grep --color=auto flannel
root 28995 1 0 May15 ? 00:00:07 /usr/bin/flanneld -etcd-endpoints=http://etcd:2379 -etcd-prefix=/atomic.io/network 启动Flannel后,一定要记得重启docker,这样Flannel配置分配的ip才能生效,即docker0虚拟网卡的ip会变成上面flannel设定的ip段
[root@node-2 ~]# systemctl restart docker

4)创建容器,验证跨主机容器之间的网络联通性

首先在node-1(182.48.115.233)上容器容器,如下,登陆容器发现已经按照上面flannel配置的分配了一个ip段(每个宿主机都会分配一个182.48.0.0/16的网段)

[root@node-1 ~]# docker run -ti -d --name=node-1.test docker.io/nginx /bin/bash
5e403bf93857fa28b42c9e2abaa5781be4e2bc118ba0c25cb6355b9793dd107e
[root@node-1 ~]# docker exec -ti node-1.test /bin/bash
root@5e403bf93857:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2953: eth0@if2954: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
link/ether 02:42:b6:30:19:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 182.48.25.4/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:b6ff:fe30:1904/64 scope link
valid_lft forever preferred_lft forever 接着在node-2(182.48.115.233)上容器容器
[root@node-2 ~]# docker exec -ti node-2.test /bin/bash
root@052a6a2a4a19:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
link/ether 02:42:b6:30:43:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 182.48.67.3/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:b6ff:fe30:4303/64 scope link
valid_lft forever preferred_lft forever root@052a6a2a4a19:/# ping 182.48.25.4
PING 182.48.25.4 (182.48.25.4): 56 data bytes
64 bytes from 182.48.25.4: icmp_seq=0 ttl=60 time=2.463 ms
64 bytes from 182.48.25.4: icmp_seq=1 ttl=60 time=1.211 ms
....... root@052a6a2a4a19:/# ping www.baidu.com
PING www.a.shifen.com (14.215.177.37): 56 data bytes
64 bytes from 14.215.177.37: icmp_seq=0 ttl=51 time=39.404 ms
64 bytes from 14.215.177.37: icmp_seq=1 ttl=51 time=39.437 ms
....... 发现,在两个宿主机的容器内,互相ping对方容器的ip,是可以ping通的!也可以直接连接外网(桥接模式) 查看两台宿主机的网卡信息,发现docker0虚拟网卡的ip(相当于容器的网关)也已经变成了flannel配置的ip段,并且多了flannel0的虚拟网卡信息
[root@node-1 ~]# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1472
inet 182.48.25.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::42:31ff:fe0f:cf0f prefixlen 64 scopeid 0x20<link>
ether 02:42:31:0f:cf:0f txqueuelen 0 (Ethernet)
RX packets 48 bytes 2952 (2.8 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 31 bytes 2286 (2.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 182.48.115.233 netmask 255.255.255.224 broadcast 182.48.115.255
inet6 fe80::5054:ff:fe34:782 prefixlen 64 scopeid 0x20<link>
ether 52:54:00:34:07:82 txqueuelen 1000 (Ethernet)
RX packets 10759798 bytes 2286314897 (2.1 GiB)
RX errors 0 dropped 40 overruns 0 frame 0
TX packets 21978639 bytes 1889026515 (1.7 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1472
inet 182.48.25.0 netmask 255.255.0.0 destination 182.48.25.0
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC)
RX packets 12 bytes 1008 (1008.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 12 bytes 1008 (1008.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 通过下面命令,可以查看到本机的容器的ip所在的范围
[root@node-1 ~]# ps aux|grep docker|grep "bip"
root 2080 0.0 1.4 796864 28168 ? Ssl May15 0:18 /usr/bin/dockerd-current --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --exec-opt native.cgroupdriver=systemd --userland-proxy-path=/usr/libexec/docker/docker-proxy-current --insecure-registry registry:5000 --bip=182.48.25.1/24 --ip-masq=true --mtu=1472 这里面的“--bip=182.48.25.1/24”这个参数,它限制了所在节点容器获得的IP范围。
这个IP范围是由Flannel自动分配的,由Flannel通过保存在Etcd服务中的记录确保它们不会重复。

==========================================================================
温馨提示:
如上面操作后,发现各容器内分配的ip之间相互ping不通,基本就是由于防火墙问题引起的!
可是明明已经在前面部署的时候,通过"systemctl stop firewalld.service"关闭了防火墙,为什么还有防火墙问题??
这是因为linux还有底层的iptables,所以解决办法是在各节点上执行下面操作:

[root@node-1 ~]# iptables -P INPUT ACCEPT
[root@node-1 ~]# iptables -P FORWARD ACCEPT
[root@node-1 ~]# iptables -F
[root@node-1 ~]# iptables -L -n 执行上面操作后,基本各容器间就能相互ping通了。

docker通过Flannel可以实现各容器间的相互通信,即宿主机和容器,容器和容器之间都能相互通信。

Docker网络解决方案 - Flannel部署记录的更多相关文章

  1. Docker网络解决方案 - Calico部署记录

    简单来说,实现docker跨主机容器间通信,常用的第三方网络方案是Flannel,Weave,Calico:Flannel会为每个host分配一个subnet,容器从这个subnet中分配ip,这些i ...

  2. Docker网络解决方案 - Weave部署记录

    前面说到了Flannel的部署,今天这里说下Docker跨主机容器间网络通信的另一个工具Weave的使用.当容器分布在多个不同的主机上时,这些容器之间的相互通信变得复杂起来.容器在不同主机之间都使用的 ...

  3. [经验分享] Docker网络解决方案-Weave部署记录

    前面说到了Flannel的部署,今天这里说下Docker跨主机容器间网络通信的另一个工具Weave的使用.当容器分布在多个不同的主机上时,这些容器之间的相互通信变得复杂起来.容器在不同主机之间都使用的 ...

  4. Docker网络解决方案-Flannel(转)

    转自https://www.cnblogs.com/kevingrace/p/6859114.html Docker跨主机容器间网络通信实现的工具有Pipework.Flannel.Weave.Ope ...

  5. Linux下smokeping网络监控环境部署记录

    smokeping是一款监控网络状态和稳定性的开源软件(它是rrdtool的作者开发的),通过它可以监控到公司IDC的网络状况,如延时,丢包率,是否BGP多线等:smokeping会向目标设备和系统发 ...

  6. Docker管理工具 - Swarm部署记录

    之前介绍了Docker集群管理工具-Kubernetes部署记录,下面介绍另一个管理工具Swarm的用法,Swarm是Docker原生的集群管理软件,与Kubernetes比起来比较简单. Swarm ...

  7. Mesos+Zookeeper+Marathon的Docker管理平台部署记录(1)

    随着"互联网+"时代的业务增长.变化速度及大规模计算的需求,廉价的.高可扩展的分布式x86集群已成为标准解决方案,如Google已经在几千万台服务器上部署分布式系统.Docker及 ...

  8. DOCKER学习_005:Flannel网络配置

    一 简介 Flannel是一种基于overlay网络的跨主机容器网络解决方案,也就是将TCP数据包封装在另一种网络包里面进行路由转发和通信, Flannel是CoreOS开发,专门用于docker多机 ...

  9. Docker 网络 Flannel

    flannel 安装 sudo yum install kernel-headers golang gccyum install flannel flannel 配置 在etcd中设置变量 etcdc ...

随机推荐

  1. The Tomcat connector configured to listen on port 8080 failed to start. The port may already be in use or the connector may be misconfigured

    springboot 8080端口被占用报错:The Tomcat connector configured to listen on port 8080 failed to start. The p ...

  2. Chrome 百度搜索热点过滤插件 - 开源软件

    学习时,为了搜集最全的中文资料,有时候不得不使用Baidu搜索引擎.在你还是个小菜鸡的时候你可能会花费大量时间在百度上! 但是,时间久了你会发现,你总会被网络上一些奇奇怪怪或者有趣的事情吸引过去而逐渐 ...

  3. 10 种机器学习算法的要点(附 Python 和 R 代码)

    本文由 伯乐在线 - Agatha 翻译,唐尤华 校稿.未经许可,禁止转载!英文出处:SUNIL RAY.欢迎加入翻译组. 前言 谷歌董事长施密特曾说过:虽然谷歌的无人驾驶汽车和机器人受到了许多媒体关 ...

  4. PLSQL操作Oracle创建用户和表

    1.打开PLSQL,填写用户名和密码(初始有两个用户sys和system,密码是自己安装oracle数据库时定的),Database选择ORCL(默认数据库,oracle中创建的用户就像是mysql中 ...

  5. input 属性radio中设置checked 不生效

    同一个页面中有许多地方都用到了单选按钮并设置了默认选中 , 结果在运行的时候发现单选按钮没有被默认选中 由于是复制然后修改个别属性 ,然后直接使用的 , 所以name值忘记修改了 , 单选框是根据na ...

  6. webpack打包去掉console.log打印与debugger调试

    如图,找到build/webpack.prod.conf.js 在 UglifyJsPlugin 插件下添加下列代码 drop_debugger: true, drop_console: true

  7. (15)Python时间

  8. 转://tcpdump抓包实例

    基本语法 ========过滤主机--------- 抓取所有经过 eth1,目的或源地址是 192.168.1.1 的网络数据# tcpdump -i eth1 host 192.168.1.1- ...

  9. LOOPS HDU - 3853 (概率dp):(希望通过该文章梳理自己的式子推导)

    题意:就是让你从(1,1)走到(r, c)而且每走一格要花2的能量,有三种走法:1,停住.2,向下走一格.3,向右走一格.问在一个网格中所花的期望值. 首先:先把推导动态规划的基本步骤给出来. · 1 ...

  10. http://blog.csdn.net/pipisorry/article/details/51471222

    这个博主很有意思 机器学习之用Python从零实现贝叶斯分类器 参数估计:贝叶斯思想和贝叶斯参数估计