nginx作为世界上使用最广的web服务,因其可以自由添加不同功能模块来对业务的支持,几乎所有的互联网大厂都在使用。现在有个业务场景需要nginx支持ipv6,但是nginx是用yum直接安装的,那要怎样去增添对第三方模块的支持,而且对原有yum安装的nginx平滑过渡几乎无感呢?本文以nginx增添ipv6模块为例,详细给大家介绍一下yum安装nginx后增添第三方模块的过程步骤,按步操作,其他模块大同小异。
这里Centos7下yum已经安装的nginx,先来简单介绍下方法:我们无论原始nginx怎么安装的,为了平滑无感影响最小,增添第三方模块就需要源码重新再次编译nginx,只是再次编译时要添加所需的模块参数。例如:添加支持tcp 4层转发,要添加–with-stream参数。本文以支持ipv6,添加参数:–with-ipv6为例。编译通过后,只需把编译生成的二进制文件nginx进行替换即可。
1 | nginx -V |
在官网下载合适的版本,建议选择稳定版本。根据当前版本,选择合适版本。这里选择的版本是nginx-1.20.2。
1 | wget https://nginx.org/download/nginx-1.20.2.tar.gz |
1 | mv /usr/sbin/nginx /usr/sbin/nginx.bak |
根据第1步查到已有的模块,加上本次需新增的模块参数: –with-ipv6
1 | ./configure --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-compat --with-debug --with-file-aio --with-google_perftools_module --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --with-threads --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' --with-ld-opt='-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E' --with-ipv6 |
编译过程可能报错,报错是因为缺少依赖,一般需要安装以下模块,安装完再次编译即可。
1 | yum -y install libxml2 libxml2-dev libxslt-devel |
1 | make |
注意:make完成后不要继续输入“make install”,以免现在的nginx出现问题
以上完成后,会在objs目录下生成一个nginx文件,先验证:
1 | nginx-1.20.2/objs/nginx -V |
验证生成的二进制nginx文件没有问题,进行文件替换。
1 | cp nginx-1.20.2/objs/nginx /usr/sbin/ |
1 | nginx -V |
根据以上教程的操作步骤逐步操作,添加模块就成功了。如果以后还需添加模块,和上面操作大同小异。只需添加模块参数重新编译通过后,替换编译后的二进制nginx文件即可。
]]>本文对三大主流负载均衡器LVS、Nginx、HAproxy进行对比和详解,以及适合的场景,技术选型等等
1 | LVS的是Linux Virtual Server的简写,翻译为Linux虚拟服务器,即一个虚拟的服务器集群系统, |
LVS相关的几种IP:
1 | VIP :(virtual IP) LVS服务器上接收外网数据报文的网卡IP地址 |
工作模式:
1 | LVS常用的工作模式有DR模式、TUN模式、以及NAT模式 |
1 | 直接路由: Director Route |
1 | 每个RS(Real Server)上都有两个IP:VIP和RIP,但是VIP是隐藏的,即不能提供解析等功能, |
1 | 1.各DIP(VS)必须与 RIP(RS) 在同一局域网内(即具有相同的广播域),且两个有相同的目标地址(vip); |
优点:
1 | 负载均衡器VS只负责将请求包分发给物理服务器RS,而物理服务器RS将应答包直接发给用户。所以,负载均衡器VS能处理很巨大的请求量。 |
缺点:
1 | 这种方式需要所有的DIR和RIP都在同一广播域;不支持异地容灾。 |
总结:
1 | LVS/DR是三种模式中性能最高的一种模式,比LVS-NAT模式下负载的RS serve更多,通常在100台左右,对网络环境要求更高,也是日常应用的最多的一种工作模式。 |
1 | 隧道模式: tunnel |
1 | 它的连接调度和管理与LVS/NAT中的一样,利用ip隧道技术的原理,即在原有的客户端请求包头中再加一层IP Tunnel的包头ip首部信息, |
1 | 1.RIP、VIP、DIP全是公网地址 |
优点:
1 | 1.不需要调度应答报文,负载能力强; |
缺点:
1 | 1.所有的服务器必须支持“IP Tunneling”协议,需安装内核模块,安装复杂; |
1 | NAT(Network address translation)即网络地址转换,作为一种过渡解决手段,可以用来减少对全球合法IP地址的需求。 |
1 | 当数据包到达VS时,VS做目标地址转换(DNAT),将目标IP改为RS的IP。RS接收到数据包以后,仿佛是客户端直接发给它的一样。 |
1 | 1.RS应该使用私有地址,RS的网关必须指向DIP |
优点:
1 | 集群中的物理服务器可以使用任何支持TCP/IP操作系统,物理服务器可以分配Internet的保留私有地址,只有负载均衡器需要一个合法的IP地址。 |
缺点:
1 | 扩展性有限;当服务器节点(普通PC服务器)数据增长到20个或更多时,负载均衡器将成为整个系统的瓶颈,因为所有的请求包和应答包都需要经过负载均衡器再生。 |
总结:
1 | LVS无论NAT及DR模式,均要求VS和RS在同一个网段内,NAT需要把VS当作各个RS的默认网关, |
1 | Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强。 |
1 | Nginx由内核和模块组成。Nginx本身做的工作实际很少,当它接到一个HTTP请求时,它仅仅是通过查找配置文件将此次请求映射到一个location block, |
Nginx的模块从结构上分为:
1 | 核心模块:HTTP模块、EVENT模块和MAIL模块 |
Nginx的模块从功能上分为:
1 | Core : 核心模块;构建nginx基础服务、管理其他模块。 |
1 | Nginx的核心模块:主要负责建立nginx服务模型、管理网络层和应用层协议、以及启动针对特定应用的一系列候选模块。 |
Nginx模块处理流程:
1 | 1.客户端发送HTTP请求 |
Nginx请求处理流程:
1 | Nginx在启动时会以daemon形式在后台运行,采用多进程+异步非阻塞IO事件模型来处理各种连接请求。 |
1 | 1.操作系统提供的机制(例如 epoll, kqueue 等)产生相关的事件。 |
Nginx进程模型
1 | Nginx默认采用多进程工作方式,Nginx启动后,会运行一个master进程和多个worker进程。 |
1 | Nginx能做: 正向代理 反向代理 负载均衡 HTTP服务器(包含动静分离) |
1 | 正向代理(Forward Proxy):通常都被简称为代理,就是在用户无法正常访问外部资源, |
1 | 正向代理的工作原理就像一个跳板,比如:我访问不了google.com,但是我能访问一个代理服务器A,A能访问google.com, |
1 | 正向代理是一个位于客户端和原始服务器之间的服务器。为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器), |
1 | 反向代理(Reverse Proxy):是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器, |
1 | 举个例子,比如我想访问 http://www.test.com/readme,但www.test.com上并不存在readme页面,于是他是偷偷从另外一台服务器上取回来, |
1 | 反向代理服务器对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间中的内容发送普通请求, |
总结
1 | 正向代理:针对客户端而言,代理服务器代理客户端,转发请求,并将获得的内容返回给客户端。 |
1 | 负载均衡也是Nginx常用的一个功能,负载均衡其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。 |
1.轮询(rr)
1 | 按照轮询(默认)方式进行负载,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 |
2.权重(weight)
1 | 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 |
1 | upstream westos{ |
3.ip哈希(ip_hash)
1 | 上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当我们的程序不是无状态的时候(采用了session保存数据), |
1 | ip_hash: 来自同一个IP的请求会分发到相同的后端服务器 |
1 | upstream westos{ |
第三方策略:
1.fair
1 | 按后端服务器的响应时间来分配请求,响应时间短的优先分配。 |
1 | upstream backend{ |
2.url_hash
1 | 按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 |
1 | upstream backend{ |
(4).HTTP服务器
1 | Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离, |
(1).支持高并发
1 | 官方测试Nginx能够支撑5万并发连接,实际生产环境中可以支撑2~4万并发连接数。 |
(2).内存消耗少
1 | Nginx+PHP(FastCGI)服务器,在3万并发连接下,开启10个Nginx进程消耗150MB内存,15MB*10=150MB,开启的64个PHP-CGI进程消耗1280内存,20MB*64=1280MB,加上系统自身消耗的内存,总共消耗不到2GB的内存。 |
(3).成本低廉
1 | 购买F5BIG-IP、NetScaler等硬件负载均衡交换机,需要十多万到几十万人民币,而Nginx为开源软件,采用的是2-clause BSD-like协议,可以免费试用,并且可用于商业用途。 |
(4).配置简单
1 | 网络和程序一样通俗易懂,即使,非专用系统管理员也能看懂。 |
(5).支持Rewrite重写
1 | Rewrite:重定向;能够根据域名、URL的不同,将http请求分到不同的后端服务器群组。 |
(6).内置健康检查
1 | 如果NginxProxy后端的某台Web服务器宕机了,不会影响前端的访问。 |
(7).节省带宽
1 | 支持GZIP压缩,可以添加浏览器本地缓存的Header头。 |
(8).支持热部署
1 | Nginx支持热部署,它的自动特别容易,并且,几乎可以7天*24小时不间断的运行, |
1 | HAProxy是一个使用C语言编写的自由及开放源代码软件,它提供高可用性、负载均衡,以及基于TCP(第四层)和HTTP(第七层)的应用程序代理。 |
1 | HAProxy实现了一种事件驱动, 单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制 、系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接。 |
HAProxy的负载均衡算法:
1 | 1. roundrobin:简单的轮询 |
1 | 1.免费开源,稳定性也是非常好。单HAproxy也跑得不错,稳定性可以与硬件级的F5相媲美。 |
比较LVS、Nginx、HAproxy优缺点
1 | 三大主流负载均衡器: LVS Nginx HAproxy |
优点:
1 | 1.抗负载能力强,工作在网络4层之上,仅作分发之用,没有流量的产生,这个特点也决定了它在负载均衡软件里的性能最强的,对内存和cpu资源消耗比较低。 |
缺点:
1 | 1.软件本身不支持正则表达式处理,不能做动静分离;而现在许多网站在这方面都有较强的需求,这个是Nginx/HAProxy+Keepalived的优势所在。 |
优点:
1 | 1.工作在网络的7层之上,可以针对http应用做一些分流的策略,比如针对域名、目录结构。它的正则规则比HAProxy更为强大和灵活,这也是它目前广泛流行的主要原因之一,Nginx单凭这点可利用的场合就远多于LVS了。 |
缺点:
1 | 1.Nginx仅能支持http、https和Email协议,适用范围小。 |
优点:
1 | 1.支持两种代理模式:TCP(四层)和HTTP(七层),支持虚拟主机; |
缺点:
1 | 1. 不支持POP/SMTP协议 SPDY协议; |
1 | 1.网站建设初期,可以选用Nigix/HAproxy作为反向代理负载均衡(或者流量不大都可以不选用负载均衡),因为其配置简单,性能也能满足一般的业务场景。 |
随着互联网的发展,对应用服务的要求也在发生着变化。有原来的大而臃肿的单个大型应用服务解耦拆分成由很多微服务共同调用来实现其功能。单个大型应用它自身集合了所有的功能,如有单个模块有问题就需要整个应用重新打包上线和测试,影响大耗时费力。而分布式微服务调用每个服务只负责单一功能模块,更新模块功能只需更新对应功能模块的微服务即可。但分布式微服务相互之间要怎么远程调用呢?就需要用到注册中心。目前所有互联网公司都采用分布式微服务方式,所以注册中心都是必须且必要的。
注册中心主要有三种角色:
最后,RPC Client 从本地缓存的服务节点列表中,基于负载均衡算法选择一台 RPC Sever 发起调用。
微服务网络远程调用为RPC调用,而RPC服务注册/发现过程如下:
根据注册中心原理的描述,注册中心必须实现以下功能。
CAP理论是分布式架构中重要理论
1 | 一致性(Consistency) (所有节点在同一时间具有相同的数据) |
P的理解是在整个系统中某个部分挂掉或者宕机了,并不影响整个系统的运作或者使用,是网络层面的,通常认为网络是顺畅流通的。
A可用性是系统的某个节点挂了,但并不影响系统的接受请求或者发出响应。
C一致性是客户端请求系统中的任意节点,获取的返回结果都是一致的。系统中各个节点会实时同步信息来保证,通常各种微服务和注册中心都不是单台的,通常都是有多台组成服务集群,那么各个节点的数据一致性就要考量。
但CAP 3项不可能都取,只能取其中2两项,造成侧重点不同。
如果C是第一需求的话,那么会影响A的性能,因为要数据同步,不然请求结果会有差异,但是数据同步会消耗时间,期间可用性就会降低。
如果A是第一需求,那么只要有一个服务在,就能正常接受请求,但是对与返回结果一致就不能保证,原因是,在分布式部署的时候,数据一致的过程不可能想切线路那么快。
再如果,同事满足一致性和可用性,那么分区容错就很难保证了,只能是单点,也是分布式的基本核心。好了,明白这些理论,就可以在相应的场景选取服务注册与发现了
一致性协议算法主要有Paxos、Raft、ZAB。
Paxos算法是Leslie Lamport在1990年提出的一种基于消息传递的一致性算法,非常难以理解,基于Paxos协议的数据同步与传统主备方式最大的区别在于:Paxos只需超过半数的副本在线且相互通信正常,就可以保证服务的持续可用,且数据不丢失。
Raft是斯坦福大学的Diego Ongaro、John Ousterhout两个人以易理解为目标设计的一致性算法,已经有了十几种语言的Raft算法实现框架,较为出名的有etcd,Google的Kubernetes也是用了etcd作为他的服务发现框架。
Raft是Paxos的简化版,与Paxos相比,Raft强调的是易理解、易实现,Raft和Paxos一样只要保证超过半数的节点正常就能够提供服务。这篇文章 《ETCD教程-2.Raft协议》 详细讲解了Raft原理,非常有意思,感兴趣的同学可以看看。
]]>NFS(Network File System) 即网络文件系统,它允许网络中服务器之间通过TCP/IP网络共享资源,NFS的一个最大优点是可以节省本地存储空间,一台NFS服务器和若干台客户机。
1 | # 准备两台服务器一台做客户端,一台做服务器,根据自己的电脑网段来做 |
在A服务端机器安装nfs-utils和rpcbind包
1 | yum install -y nfs-utils rpcbind |
启动rpcbind并检查是否启动了rpcbind服务并监听111端口
1 | systemctl start rpcbind |
配置NFS服务的共享文件
1 | mkdir -p /data/nfsdata |
补充知识
1 | ro:共享目录只读; |
1 | systemctl start nfs |
1 | systemctl enable rpcbind.service |
方法一:
1 | #防火请关掉很简单,直接命令关掉即可 |
方法二:
直接关闭防火墙虽然很简单,但在日常服务器上直接关闭防火墙,让其裸跑有很大安全风险。而rpc的服务接口默认是随机的。我们需要在配置中把其固定下来,这样在防火墙或安全组中只需开放对应端口即可。
1 | vim /etc/sysconfig/nfs |
在B客户端机器安装
1 | yum -y install nfs-utils |
1 | systemctl enable rpcbind.service |
1 | mount -t nfs 192.168.10.51:/data/nfsdata /nfs #挂在文件系统 |
分别在服务端或客户端创建文件,观察
根据上面教程步骤就可以简单的用单台nfs服务器来实现文件共享和挂载服务,但这只是针对紧急特殊情况。如要在生产上使用建议使用glusterfs多台服务器集群来做文件共享服务。
]]>Kubernetes Service定义了这样一种抽象:一个Pod的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。这一组Pod能够被Service访问到,通常是通过Label Selector
通俗的讲:SVC负责检测Pod的状态信息,不会因pod的改动IP地址改变(因为关注的是标签),导致Nginx负载均衡影响
Service能够提供负载均衡的能力,但是在使用上有以下限制:
Service 在 K8s 中有以下四种类型
①ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP
②NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过
访问node01的30001相当于访问定义的SVC后端的80的三个不pod同服务(RR)
client——》nginx(负载接收器,反向代理)——》node1,node2
③LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到
④ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持
SVC基础导论
总结:
在 Kubernetes 集群中,每个 Node 运行一个kube-proxy进程。kube-proxy负责为Service实现了一种VIP(虚拟 IP)的形式,而不是ExternalName的形式。在 Kubernetes v1.0 版本,代理完全在 userspace。在Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。从 Kubernetes v1.2 起,默认就是iptables 代理。在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理
代理层级:userspace——》iptables——》ipvs
在 Kubernetes 1.14 版本开始默认使用ipvs 代理
在 Kubernetes v1.0 版本,Service是 “4层”(TCP/UDP over IP)概念。在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 “7层”(HTTP)服务
为何不使用 round-robin DNS?
DNS会在很多的客户端里进行缓存,很多服务在访问DNS进行域名解析完成、得到地址后不会对DNS的解析进行清除缓存的操作,所以一旦有他的地址信息后,不管访问几次还是原来的地址信息,导致负载均衡无效。
ipvs 代理模式(标准)
这种模式,kube-proxy 会监视 Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs 规则并定期与 Kubernetes Service对象和Endpoints对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod
与 iptables 类似,ipvs 于 netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:
①rr:轮询调度
②lc:最小连接数
③dh:目标哈希
④sh:源哈希
⑤sed:最短期望延迟
⑥nq:不排队调度
<–注意;ipvs模式假定在运行 kube-proxy 之前在节点上都已经安装了IPVS内核模块。当kube-proxy以ipvs代理模式启动时,kube-proxy 将验证节点上是否安装了IEVS模块,如果末安装,则kube-proxy 将回退到iptables 代理模式–>
1 | ipvsadm -Ln |
clusterIP 主要在每个 node 节点使用 iptables,将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口
为了实现图上的功能,主要需要以下几个组件的协同工作:
api将信息写到etcd,kubeproxy监测etcd的变化,得到变化以后写入到ipvs规则
第一步 创建 svc-deployment.yaml 文件
1 | [root@k8s-master01 ~]# vim svc-deployment.yaml |
1 | kubectl apply -f svc-deployment.yaml |
1 | kubectl get pod -o wide |
这样地址访问,不太行。如果pod死亡后会出现新的pod,然后与之前的地址又不一致。因此 为了可靠的访问,需要进行第二步,SVC创建
第二步 给deploy绑定svc,即创建 Service 信息
1 | [root@k8s-master01 ~]# vim svc.yaml |
1 | kubectl apply -f svc.yaml |
这里是两个的原因是因为有一个容器还在创建,没关系
kubectl delete -f svc.yaml 也可以看得到对应的服务也被删除了。
直接访问svc的IP地址,相当于通过ipvs模块,负载均衡,实现代理到后端节点上。
直接访问svc的IP地址,可以看到轮询RR效果
它属于一种特殊的Cluster IP,
有时不需要或不想要负载均衡,以及单独的 Service IP 。遇到这种情况,可以通过指定 ClusterIP(spec.clusterIP) 的值为 “None” 来创建 Headless Service 。这类 Service 并不会分配 Cluster IP, kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由
1 | [root@k8s-master01 ~]# vim svc-none.yaml |
虽然没有svc了,但是可以通过域名的方案依然可以访问
svc创建成功会把主机名(svc名.名字空间名.集群域名)写入到coredns
1 | [root@k8s-master01 ~]# kubectl get pod -n kube-system -o wide 获取当前dns的地址信息 |
可以在当前的物理机上,暴露一个端口,让内部服务暴露到外部
客户端可以通过物理机IP+端口方式 访问到集群内部
nodePort原理在于在 node 上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy 进一步(与接口层交互)到给对应的 pod
1 | [root@k8s-master01 ~]# vim nodeport.yaml |
同时可以看出,一组pod可以对应不同的svc的。只要pod标签与svc标签一致就可以关联。多对多的关系 n:m
浏览器访问:master虚拟机IP:端口 10.0.100.10:32642
并且子节点pod也会开启这个端口
10.0.100.11:32642与10.0.100.12:32642
查询流程
1 | ipvsadm -Ln |
loadBalancer和nodePort其实是同一种方式。区别在于loadBalancer比nodePort多了一步,就是可以调用cloud provider去创建LB来向节点导流(LB收费)
别名操作,外部服务引入到集群内
这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例如:hub.atguigu.com )。ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务
1 | kind: Service |
当查询主机 my-service-1.defalut.svc.cluster.local ( SVC_NAME.NAMESPACE.svc.cluster.local ) 时,集群的DNS 服务将返回一个值 hub.atguigu.com 的 CNAME 记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发
1 | vim ex.yaml |
1 | dig -t A my-service-1.default.svc.cluster.local @10.244.0.13 |
这个IP是coredns地址,通过kubectl get pod -n kube-system -o wide
对传统的SVC来说仅支持四层
Ingress-Nginx github 地址:https://github.com/kubernetes/ingress-nginx
Ingress-Nginx 官方网站:https://kubernetes.github.io/ingress-nginx/
其实对Nginx的暴露方案是Nodepod,内部的服务暴露给外部
1 | kubectl apply -f mandatory.yaml |
进入官方下载
1 | cd /usr/local/install-k8s/plugin/ |
第一步:三个节点,一主二子都要解压导入
1 | tar -zxvf ingree.contro.tar.gz #解压 |
第二步:创建pod和svc
1 | kubectl apply -f mandatory.yaml |
deployment、Service、Ingress Yaml 文件
现在想通过Nginx的Ingress方案暴露出去,实现域名访问的这么一个结构
1 | [root@k8s-master01 ~]# vim ingress.http.yaml |
1 | [root@k8s-master01 ~]# vim ingress1.yaml |
在W10下进行测试,修改本地host解析,C:\Windows\System32\drivers\etc\hosts
10.0.100.10 www1.atguigu.com
注意访问的端口不是80,而是ingress的端口32510
1 | kubectl get svc -n ingress-nginx |
第一个deployment和第一个svc
1 | [root@k8s-master01 ~]# mkdir ingress-vh |
第二个deployment和第二个svc
1 | [root@k8s-master01 ingress-vh]# cp -a deployment.yaml deployment2.yaml |
写Ingress1、2规则
1 | [root@k8s-master01 ~]# vim ingressrule.yaml |
1 | [root@k8s-master01 ingress-vh]# kubectl get pod -n ingress-nginx |
查看Ingress暴露的端口kubectl get svc -n ingress-nginx
kubectl get ingress 查看规则
浏览器访问测试
动态图效果演示虚拟主机
创建证书,以及 cert 存储方式
1 | openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc" |
deployment、Service、Ingress Yaml 文件
1 | apiVersion: extensions/v1beta1 |
操作过程
第一步:创建证书,以及cert存储方式
1 | [root@k8s-master01 ~]# mkdir https |
第二步:创建deployment、Service文件
1 | [root@k8s-master01 https]# cp /root/ingress-vh/deployment.yaml . |
第三步:创建Ingress Yaml文件
多了个tls
1 | [root@k8s-master01 https]# vim https.ingress.yaml |
浏览器访问看效果
https://www3.atguigu.com:31401
对于nginx来说采用的apache认证的模块
1 | mkdir basic-auth |
vim ingress.yaml
1 | apiVersion: extensions/v1beta1 |
1 | [root@k8s-master01 basic-auth]# kubectl apply -f ingress.yaml |
访问的是80端口对应的32510端口
浏览器访问
实验操作
访问www4,跳到www3。 https访问
vim re.yaml
1 | apiVersion: extensions/v1beta1 |
ps:遇到粘贴错乱可以在set paste
浏览器访问:http://re.atguigu.com:32510/
跳转到 https://www3.atguigu.com:31401/hostname.html
我们在运维架构建设中,资产管理是核心环节。所有环节都需要围绕这个核心来做,包括但不限于资产监控,资产授权,资产规划和部署应用,资源费用核算,资产盘点,资产回收等等。
而jumpserver作为一款安全级别在5A的开源堡垒机,受到了所有互联网大厂的青睐和日常使用。我们这些运维、开发或架构的技术型日常工作都是在类unix环境,通常是linux环境,和服务器打交道,用命令或编写脚本来高效的维护和操作处理。所以默认堡垒机对linux的支持是必须的也是常规的,堡垒机对linux资产管理参考官网文档,这里就不再赘述了。但有时我们的业务需要在windows server上来操作,但官方文档也没有详细说明。本文重点介绍堡垒机jumpserver要如何去管理windows server。
添加windows主机那个特权用户要怎么写呢?是不是有这样的困惑,只需普通用户就可以。
操作步骤:系统用户–>普通用户–>创建RDP–>填写用户信息提交。
先添加administrator用户,该用户要和登入windows的远程用户账户一致。
操作步骤:资产列表–>创建–>填写windows server资产信息提交。
注意,这里有几点要说明下:
1,我使用的是目前最新版(v2.22.1)特权用户没有号,就是非必填项,所以可以不写,如果使用版本不一样带号为必填项,可以随便选个,虽然特权用户在windows上没用。
2,平台选windows,协议组选rdp。其他和添加linux一样。
操作步骤:资产授权–>填写授权信息(和linux一致)–>系统用户选择要选择创建windows server的普通用户(登录windows server时通过该用户远程进行操作)。
到终端列表中,选择该资产,就会用选择的windows server系统普通用户登录了。
堡垒机jumpserver添加windows server管理不需要纠结特权用户,只需建立普通系统用户即可,但对该普通用户是有要求的,该普通系统用户必须是可以远程登录windows server的用户。
]]>本文详细介绍了python项目(flask或django等)在部署到linux服务器上后,uwsgi常用配置和nginxd对应通信配置,以及supervisor常用配置详解。本篇为高级篇,至于怎么安装请参考博客中其他文档,谢谢。
作为模板uwsgi.ini,当然也可以根据uwsgi –help来查看或自定义。官方参数详解
1 | master = true |
1 | # 指定加载的WSGI文件 |
1 | # 模块名:可调用对象app |
1 | module=manager |
其中上面配置有几处,是可以选择的。
uWSGI和Nginx之间有3种通信方式,: unix socket,TCP socket和http。而Nginx的配置必须与uwsgi配置保持一致
1 | # 以下uwsgi与nginx通信手段3选一即可 |
如果你的nginx与uwsgi在同一台服务器上,优先使用本地机器的unix socket进行通信,这样速度更快。
即uwsgi配置了选项1,此时nginx的配置文件如下所示:
1 | location / { |
如果nginx与uwsgi不在同一台服务器上,可以使用选项2和3。这里使用TCP socket通信,nginx应如下配置:
1 | location / { |
同样的,如果nginx与uwsgi不在同一台服务器上,用http协议进行通信,nginx配置如下:
1 | location / { |
1 | #uwsgi --ini uwsgi.ini # 启动 |
supervisor就是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台守护进程daemon,并监控进程状态,异常退出时能自动重启。
通过这种方式安装后,会自动设置为开机启动
1 | #Ubuntu: |
也可以通过 pip install supervisor
进行安装,但是需要手动启动,然后设置为开机启动(不推荐这种安装方式)
1 | systemctl start supervisord.service #启动supervisor并加载默认配置文件 |
Supervisor 是一个 C/S 模型的程序,supervisord
是 server 端,supervisorctl
是 client 端。
下面介绍 supervisord 配置方法。supervisord 的配置文件默认位于 /etc/supervisord.conf
,内容如下(;
后面为注释):
1 | ; supervisor config file |
program 的配置文件就写在,supervisord 配置中 include
项的路径下:/etc/supervisor/conf.d/
,然后 program 的配置文件命名规则推荐:app_name.conf
1 | [program:uwsgi] |
supervisorctl 是 supervisord 的命令行客户端工具,使用的配置和 supervisord 一样,这里就不再说了。下面,主要介绍 supervisorctl 操作的常用命令:
输入命令 supervisorctl
进入 supervisorctl 的 shell 交互界面(还是纯命令行😓),就可以在下面输入命令了。:
也可以直接通过 shell 命令操作:
启动supervisor之后就可以通过ip:9001
访问supervisor的管理页面,前提是配置中supervisorctl 配置这种http的访问方式,而不是像上面用socket套接字。
Linux 的命令行里面有用来停止正在运行的进程的所有所需工具。这里将为您讲述细节。
想像一下:你打开了一个程序(可能来自于你的桌面菜单或者命令行),然后开始使用这个程序,没想到程序会锁死、停止运行、或者意外死机。你尝试再次运行该程序,但是它反馈说原来的进程没有完全关闭。
你该怎么办?你要结束进程。但该如何做?不管你信与不信,最好的解决方法大都在命令行里。值得庆幸的是, Linux 有供用户杀死错误的进程的每个必要的工具,然而,你在执行杀死进程的命令之前,你首先需要知道进程是什么。该如何处理这一类的任务。一旦你能够掌握这种工具,它实际是十分简单的……
我来概述的步骤是每个 Linux 发行版都能用的,不论是桌面版还是服务器版。我将限定只使用命令行,请打开你的终端开始输入命令吧。
杀死一个没有响应的进程的第一个步骤是定位这个进程。我用来定位进程的命令有两个:top 和 ps 命令。top 是每个系统管理员都知道的工具,用 top 命令,你能够知道到所有当前正在运行的进程有哪些。在命令行里,输入 top 命令能够就看到你正在运行的程序进程(图1)
从显示的列表中你能够看到相当重要的信息,举个例子,Chrome 浏览器反映迟钝,依据我们的 top 命令显示,我们能够辨别的有四个 Chrome 浏览器的进程在运行,进程的 pid 号分别是 3827、3919、10764 和 11679。这个信息是重要的,可以用一个特殊的方法来结束进程。
尽管 top 命令很是方便,但也不是得到你所要信息最有效的方法。 你知道你要杀死的 Chrome 进程是那个,并且你也不想看 top 命令所显示的实时信息。 鉴于此,你能够使用 ps 命令然后用 grep 命令来过滤出输出结果。这个 ps 命令能够显示出当前进程列表的快照,然后用 grep 命令输出匹配的样式。我们通过 grep 命令过滤 ps 命令的输出的理由很简单:如果你只输入 ps 命令,你将会得到当前所有进程的列表快照,而我们需要的是列出 Chrome 浏览器进程相关的。所以这个命令是这个样子:
1 | ps aux | grep chrome |
当你搜索图形化程序的信息时,这个 x 参数是很重要的。
当你输入以上命令的时候,你将会得到比图 2 更多的信息,而且它有时用起来比 top 命令更有效。
现在我们开始结束进程的任务。我们有两种可以帮我们杀死错误的进程的信息。
1 | 进程的名字 |
你用哪一个将会决定终端命令如何使用,通常有两个命令来结束进程:
1 | kill - 通过进程 ID 来结束进程 |
有两个不同的信号能够发送给这两个结束进程的命令。你发送的信号决定着你想要从结束进程命令中得到的结果。举个例子,你可以发送 HUP(挂起)信号给结束进程的命令,命令实际上将会重启这个进程。当你需要立即重启一个进程(比如就守护进程来说),这是一个明智的选择。你通过输入 kill -l 可以得到所有信号的列表,你将会发现大量的信号。
最经常使用的结束进程的信号是:
好的是,你能用信号值来代替信号名字。所以你没有必要来记住所有各种各样的信号名字。
所以,让我们现在用 kill 命令来杀死 Chrome 浏览器的进程。这个命令的结构是:
1 | kill SIGNAL PID |
这里 SIGNAL 是要发送的信号,PID 是被杀死的进程的 ID。我们已经知道,来自我们的 ps 命令显示我们想要结束的进程 ID 号是 3827、3919、10764 和 11679。所以要发送结束进程信号,我们输入以下命令:
1 | kill -9 3827 |
一旦我们输入了以上命令,Chrome 浏览器的所有进程将会成功被杀死。
我们有更简单的方法!如果我们已经知道我们想要杀死的那个进程的名字,我们能够利用 killall 命令发送同样的信号,像这样:
1 | killall -9 chrome |
附带说明的是,上边这个命令可能不能捕捉到所有正在运行的 Chrome 进程。如果,运行了上边这个命令之后,你输入 ps aux | grep chrome 命令过滤一下,看到剩下正在运行的 Chrome 进程有那些,最好的办法还是回到 kIll 命令通过进程 ID 来发送信号值 9 来结束这个进程。
正如你看到的,杀死错误的进程并没有你原本想的那样有挑战性。当我让一个顽固的进程结束的时候,我趋向于用 killall命令来作为有效的方法来终止,然而,当我让一个真正的活跃的进程结束的时候,kill命令是一个好的方法。
根据上面的kill停止,大家基本上了解了信号的概念,那么这些信号是怎么产生的呢?每个信号有有什么用呢?这里详细和大家聊聊。
linux中信号,编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。
不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。
下面我们对编号小于SIGRTMIN的信号进行讨论。
1) SIGHUP 该信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。 当登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进程组和后台有终端输出的进程就会中止。不过可以捕获这个信号,比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录,wget也能继续下载。 此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。
2) SIGINT 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出,用于通知前台进程组终止进程。
3) SIGQUIT 和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。
4) SIGILL 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。
5) SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用。
6) SIGABRT 调用abort函数生成的信号。
7) SIGBUS 非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。
8) SIGFPE 在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
9) SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
10) SIGUSR1 留给用户使用
11) SIGSEGV 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
12) SIGUSR2 留给用户使用
13) SIGPIPE 管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。
14) SIGALRM 时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
15) SIGTERM 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了,我们才会尝试SIGKILL。
17) SIGCHLD 子进程结束时, 父进程会收到这个信号。 如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)。
18) SIGCONT 让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
19) SIGSTOP 停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
20) SIGTSTP 停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号
21) SIGTTIN 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.
22) SIGTTOU 类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.
23) SIGURG 有”紧急”数据或out-of-band数据到达socket时产生.
24) SIGXCPU 超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。
25) SIGXFSZ 当进程企图扩大文件以至于超过文件大小资源限制。
26) SIGVTALRM 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
27) SIGPROF 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.
28) SIGWINCH 窗口大小改变时发出.
29) SIGIO 文件描述符准备就绪, 可以开始进行输入/输出操作.
30) SIGPWR Power failure
31) SIGSYS 非法的系统调用。
在以上列出的信号中
程序不可捕获、阻塞或忽略的信号有:SIGKILL,SIGSTOP
不能恢复至默认动作的信号有:SIGILL,SIGTRAP
默认会导致进程流产的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默认会导致进程退出的信号有:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM 默认会导致进程停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH
此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在进程挂起时是继续,否则是忽略,不能被阻塞
]]>很多朋友不清楚linux如何禁止普通用户su到root,这里需要修改两个配置文件,具体详细配置大家通过本文了解下吧
为禁止普通用户su至root,需要分别修改/etc/pam.d/su和/etc/login.defs两个配置文件。
(1)去除/etc/pam.d/su文件中如下行的注释:
1 | #auth required pam_wheel.so use_uid |
(2)在/etc/login.defs文件中加入如下配置项:
1 | SU_WHEEL_ONLY yes |
经过上述配置后,普通用户将被禁止su至root,如果希望指定普通用户su至root,可以执行如下命令将该用户添加至wheel组中:
1 | usermod -G wheel username |
1 | [root@titan ~]# id apple |
验证apple
1 | [apple@titan ~]$ su - root |
验证banana
1 | [banana@titan ~]$ su - root |
以上所述是站长给大家介绍的Linux禁止普通用户su至root的解决方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,站长会及时回复大家的。
]]>现在系统功能越来越丰富,那么响应的开发资源需要的越来越多,文件存储也越来越必要。无论是你用nfs、glusterfs等等,都需要在linux服务器中设置挂载点并执行挂载后才可使用,但如果fs文件系统有调整,那么可能就需要卸载umount,重新挂载,但是你真的可以顺顺利利的卸载吗?不见得,因为可能有应用在占用该磁盘或者系统在fstab中写入了磁盘自动挂载,本文就详细给你介绍个小技巧,帮你解决该烦恼。
但出现这种情况时,可以根据提示用lsof 或fuser来判断有哪些进程正在占用该磁盘,停掉改进程,重新挂载后再重新启动进程应用即可。
可以根据图上看到,找到了进程16011占用了该文件系统,并可以准确看到该进程是哪些应用,并占用了哪些文件等信息。
科普
1 | fuser -m -v /mnt |
可以查看到当前占用/mnt目录的进程号,然后用kill杀死它。
也可以直接杀死这个进程
1 | fuser -m -k /mnt |
如果你不是很明确是否要杀死所有霸占设备的程序,你还可以加一个 -i 参数,这样每杀死一个程序前,都会询问,加参数-i
1 | fuser -m -v -i -k /mnt |
-m : 表明指定的路径是一个挂载点显示所有使用指定文件系统的进程。后面可以跟挂载点或dev设备
-v : 给出详细的输出。可以给出了占用磁盘程序的详细信息,如进程号等。
应用程序占用可以根据场景一操作拿到进程id,你就可以对它为所欲为了。但是场景一图中有一个隐藏的信息,可能有同学已经发现
PID: kernel这个是内核占用着该磁盘,要怎么去杀掉呢。这又是怎么造成的呢?
出现这种情况是因为在linux系统fstab中添加的文件磁盘,那么在系统启动时,内核自动挂载该磁盘,所有就是内核进程。
那有办法解决吗?答案是肯定的,要不就不会有本教程了。
方法一
既然内核占有,那么先把fstab中fs挂载点删掉,并重启服务器,那么重启时内核重新加载,就不会再占用了。
但是对于生产环境,业务应用在线上跑,有没有不用重启,还能解决挂载问题的呢?
方法二
lazy umount法,使用如下命令和参数:
1 | umount -l /mnt |
–l :并不是马上umount,而是在该目录空闲后再umount。
请注意,该方法并不是完全安全的,它主要完成如下操作:
1,立即从目录结构中实现卸载,即新进程将无法通过/media/disk访问,该磁盘。
2,正在访问该文件系统的程序不受影响。即正在操作/media/disk的进程不会被打断,且仍可以读写磁盘中的所有文件。如果所有进程对/media/disk的操作都执行完,那么才真正地umount。
由此可知,lazy umount并没有真正实现umount,仅用于特殊需要的情况。
被应用程序占用,找到进程号,停掉应用解除占用就可卸载。如果是内核占用,可以重启或用lazy umount来解决。但都有优劣点,需要自行把握。
]]>今天要给大家介绍的一个类Unix下的一个网络数据采集分析工具 – Tcpdump,也就是我们常说的抓包工具。与它功能类似的工具有 wireshark。不同的是wireshark有图形化界面,而tcpdump 则只有命令行。
作为一个运维,经常和服务器打交道,但服务器追求性能很少安装图形界面,因此直接跳过wireshark,直接给大家介绍这个tcpdump神器。
这篇文章借助于很多帮助文档,终于把tcpdump的用法全部研究了个遍。毫不夸张的说,应该可以算是中文里把 tcpdump 讲得最清楚明白,并且最全的文章了。所以本文值得你收藏分享,就怕你错过了,就再也找不到像这样把 tcpdump 讲得直白而且特全的文章了。
操作系统:CentOS 7.2
tcpdump版本:v4.5.1
tcpdump核心参数图解
大家都知道,网络上的流量、数据包非常的多,因此要想抓到我们所需要的数据包,就需要我们定义一个精准的过滤器,把这些目标数据包,从巨大的数据包网络中抓取出来。
所以学习抓包工具,其实就是学习如何定义过滤器的过程。
而在 tcpdump 的世界里,过滤器的实现,都是通过一个又一个的参数组合起来,一个参数不够精准,那就再加一个,直到我们能过滤掉无用的数据包,只留下我们感兴趣的数据包。
tcpdump 的参数非常的多,初学者在没有掌握 tcpdump 时,会对这个命令的众多参数产生很多的疑惑。
就比如下面这个命令,我们要通过 host 参数指定 host ip 进行过滤
1 | $ tcpdump host 192.168.10.100 |
主程序 + 参数名+ 参数值 这样的组合才是我们正常认知里面命令行该有的样子。
可 tcpdump 却不走寻常路,我们居然还可以在 host 前再加一个限定词,来缩小过滤的范围?
1 | $ tcpdump src host 192.168.10.100 |
从字面上理解,确实很容易理解,但是这不符合编写命令行程序的正常逻辑,导致我们会有所疑虑:
除 src ,dst 还有其它可以的限定词?src,host 应该如何理解它们,叫参数名?不合适,因为 src 明显不合适。如果你在网上看到有关 tcpdump 的博客、教程,无一不是给你一个参数组合,告诉你这是实现了怎样的一个过滤器?这样的教学方式,很容易让你依赖别人的文章来使用 tcpdump,而不能将 tcpdump 这样神器消化,并达到灵活应用,灵活搭配过滤器的效果。
上面加了 src 本身就颠覆了我们的认知,你可知道在 src 之前还可以加更多的条件,比如 tcp, udp, icmp 等词,在你之前的基础上再过滤一层。如下:
1 | $ tcpdump tcp src host 192.168.10.100 |
这种参数的不确定性,让大多数人对 tcpdump 的学习始终无法得其精髓。
因此,在学习 tcpdump 之前,我觉得有必要要先让你知道:tcpdump 的参数是如何组成的?这非常重要。
为此画了一张图,方便你直观的理解 tcpdump 的各种参数:
proto、type、direction 这三类过滤器的内容比较简单,也最常用,因此我将其放在最前面,也就是 第三章:常规过滤规则一起介绍。
而 option 可选的参数非常多,有的甚至也不经常用到,因此我将其放到后面一点,也就是 第四章:可选参数解析
当你看完前面六章,你对 tcpdump 的认识会上了一个台阶,至少能够满足你 80% 的使用需求。
你一定会问了,还有 20% 呢?
其实 tcpdump 还有一些过滤关键词,它不符合以上四种过滤规则,可能需要你单独记忆。关于这部分我会在 第六章:特殊过滤规则 里进行介绍。
理解 tcpdump 的输出
2.1 输出内容结构
tcpdump 输出的内容虽然多,却很规律。
这里以我随便抓取的一个 tcp 包为例来看一下
1 | 21:26:49.013621 IP 172.20.20.1.15605 > 172.20.20.2.5920: Flags [P.], seq 49:97, ack 106048, win 4723, length 48 |
从上面的输出来看,可以总结出:
2.2 Flags 标识符
使用 tcpdump 抓包后,会遇到的 TCP 报文 Flags,有以下几种:
常规过滤规则
3.1 基于IP地址过滤:host
使用 host 就可以指定 host ip 进行过滤
1 | $ tcpdump host 192.168.10.100 |
数据包的 ip 可以再细分为源ip和目标ip两种
1 | # 根据源ip进行过滤 |
3.2 基于网段进行过滤:net
若你的ip范围是一个网段,可以直接这样指定
1 | $ tcpdump net 192.168.10.0/24 |
网段同样可以再细分为源网段和目标网段
1 | # 根据源网段进行过滤 |
3.3 基于端口进行过滤:port
使用 port 就可以指定特定端口进行过滤
1 | $ tcpdump port 8088 |
端口同样可以再细分为源端口,目标端口
1 | # 根据源端口进行过滤 |
如果你想要同时指定两个端口你可以这样写
1 | $ tcpdump port 80 or port 8088 |
但也可以简写成这样
1 | $ tcpdump port 80 or 8088 |
如果你的想抓取的不再是一两个端口,而是一个范围,一个一个指定就非常麻烦了,此时你可以这样指定一个端口段。
1 | $ tcpdump portrange 8000-8080 |
对于一些常见协议的默认端口,我们还可以直接使用协议名,而不用具体的端口号。如http=80,https = 443 等
1 | $ tcpdump tcp port http |
3.4 基于协议进行过滤:proto
常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等
若你只想查看 icmp 的包,可以直接这样写
1 | $ tcpdump icmp |
3.5 基本IP协议的版本进行过滤
当你想查看 tcp 的包,你也许会这样写
1 | $ tcpdump tcp |
这样写也没问题,就是不够精准,为什么这么说呢?
ip 根据版本的不同,可以再细分为 IPv4 和 IPv6 两种,如果你只指定了 tcp,这两种其实都会包含在内。
那有什么办法,能够将 IPv4 和 IPv6 区分开来呢?
很简单,如果是 IPv4 的 tcp 包 ,就这样写(友情提示:数字 6 表示的是 tcp 在ip报文中的编号。)
1 | $ tcpdump 'ip proto tcp' |
而如果是 IPv6 的 tcp 包 ,就这样写
1 | $ tcpdump 'ip6 proto tcp' |
关于上面这几个命令示例,有两点需要注意:
跟在 proto 和 protochain 后面的如果是 tcp, udp, icmp ,那么过滤器需要用引号包含,这是因为 tcp,udp, icmp 是 tcpdump 的关键字。跟在ip 和 ip6 关键字后面的 proto 和 protochain 是两个新面孔,看起来用法类似,它们是否等价,又有什么区别呢?关于第二点,网络上没有找到很具体的答案,我只能通过 man tcpdump 的提示, 给出自己的个人猜测,但不保证正确。
proto 后面跟的
而 protochain 后面跟的 protocol 要求就没有那么严格,它可以是任意词,只要 tcpdump 的 IP 报文头部里的 protocol 字段为
理论上来讲,下面两种写法效果是一样的
1 | $ tcpdump 'ip && tcp'$ tcpdump 'ip proto tcp' |
同样的,这两种写法也是一样的
1 | $ tcpdump 'ip6 && tcp'$ tcpdump 'ip6 proto tcp' |
可选参数解析
4.1 设置不解析域名提升速度
4.2 过滤结果输出到文件
使用 tcpdump 工具抓到包后,往往需要再借助其他的工具进行分析,比如常见的 wireshark 。
而要使用wireshark ,我们得将 tcpdump 抓到的包数据生成到文件中,最后再使用 wireshark 打开它即可。
使用 -w 参数后接一个以 .pcap 后缀命令的文件名,就可以将 tcpdump 抓到的数据保存到文件中。
1 | $ tcpdump icmp -w icmp.pcap |
4.3 从文件中读取包数据
使用 -w 是写入数据到文件,而使用 -r 是从文件中读取数据。
读取后,我们照样可以使用上述的过滤器语法进行过滤分析。
1 | $ tcpdump icmp -r all.pcap |
4.4 控制详细内容的输出
4.5 控制时间的显示
4.6 显示数据包的头部
4.7 过滤指定网卡的数据包
-i:指定要过滤的网卡接口,如果要查看所有网卡,可以 -i any
4.8 过滤特定流向的数据包
-Q:选择是入方向还是出方向的数据包,可选项有:in, out, inout,也可以使用 –direction=[direction] 这种写法4.9 其他常用的一些参数
4.10 对输出内容进行控制的参数
过滤规则组合
有编程基础的同学,对于下面三个逻辑运算符应该不陌生了吧
1 | $ tcpdump src 10.5.2.3 and dst port 3389 |
当你在使用多个过滤器进行组合时,有可能需要用到括号,而括号在 shell 中是特殊符号,因为你需要使用引号将其包含。例子如下:
1 | $ tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)' |
而在单个过滤器里,常常会判断一条件是否成立,这时候,就要使用下面两个符号
当你使用这两个符号时,tcpdump 还提供了一些关键字的接口来方便我们进行判断,比如
比如我现在要过滤来自进程名为 nc 发出的流经 en0 网卡的数据包,或者不流经 en0 的入方向数据包,可以这样子写
1 | $ tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)" |
特殊过滤规则
6.1 根据 tcpflags 进行过滤
通过上一篇文章,我们知道了 tcp 的首部有一个标志位。
TCP 报文首部
tcpdump 支持我们根据数据包的标志位进行过滤
proto [ expr:size ]
接下来,我将举几个例子,让人明白它的写法,不过在那之前,有几个点需要你明白,这在后面的例子中会用到:
1、tcpflags 可以理解为是一个别名常量,相当于 13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位,所以 tcp[tcpflags] 等价于 tcp[13] ,对应下图中的报文位置。
2、tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg 这些同样可以理解为别名常量,分别代表 1,2,4,8,16,32,64。这些数字是如何计算出来的呢?
以 tcp-syn 为例,你可以参照下面这张图,计算出来的值 是就是 2
由于数字不好记忆,所以一般使用这样的“别名常量”表示。
因此当下面这个表达式成立时,就代表这个包是一个 syn 包。
tcp[tcpflags] == tcp-syn
要抓取特定数据包,方法有很多种。
下面以最常见的 syn包为例,演示一下如何用 tcpdump 抓取到 syn 包,而其他的类型的包也是同样的道理。
据我总结,主要有三种写法:
1、第一种写法:使用数字表示偏移量
1 | $ tcpdump -i eth0 "tcp[13] & 2 != 0" |
2、第二种写法:使用别名常量表示偏移量
1 | $ tcpdump -i eth0 "tcp[tcpflags] & tcp-syn != 0" |
3、第三种写法:使用混合写法
1 | $ tcpdump -i eth0 "tcp[tcpflags] & 2 != 0"# or$ tcpdump -i eth0 "tcp[13] & tcp-syn != 0" |
如果我想同时捕获多种类型的包呢,比如 syn + ack 包
1、第一种写法
1 | $ tcpdump -i eth0 'tcp[13] == 2 or tcp[13] == 16' |
2、第二种写法
1 | $ tcpdump -i eth0 'tcp[tcpflags] == tcp-syn or tcp[tcpflags] == tcp-ack' |
3、第三种写法
1 | $ tcpdump -i eth0 "tcp[tcpflags] & (tcp-syn|tcp-ack) != 0" |
4、第四种写法:注意这里是 单个等号,而不是像上面一样两个等号,18(syn+ack) = 2(syn) + 16(ack)
1 | $ tcpdump -i eth0 'tcp[13] = 18'# or$ tcpdump -i eth0 'tcp[tcpflags] = 18' |
tcp 中有 类似 tcp-syn 的别名常量,其他协议也是有的,比如 icmp 协议,可以使用的别名常量有
icmp-echoreply, icmp-unreach, icmp-sourcequench, icmp-redirect, icmp-echo, icmp-routeradvert,icmp-routersolicit, icmp-timx-ceed, icmp-paramprob, icmp-tstamp, icmp-tstampreply,icmp-ireq, icmp-ireqreply, icmp-maskreq, icmp-maskreply
5.2 基于包大小进行过滤
若你想查看指定大小的数据包,也是可以的
1 | $ tcpdump less 32 $ tcpdump greater 64 $ tcpdump <= 128 |
5.3 根据 mac 地址进行过滤
例子如下,其中 ehost 是记录在 /etc/ethers 里的 name
1 | $ tcpdump ether host [ehost]$ tcpdump ether dst [ehost]$ tcpdump ether src [ehost] |
5.4 过滤通过指定网关的数据包
1 | $ tcpdump gateway [host] |
5.5 过滤广播/多播数据包
1 | $ tcpdump ether broadcast$ tcpdump ether multicast$ tcpdump ip broadcast$ tcpdump ip multicast$ tcpdump ip6 multicast |
如何抓取到更精准的包?
先给你抛出一个问题:如果我只想抓取 HTTP 的 POST 请求该如何写呢?
如果只学习了上面的内容,恐怕你还是无法写法满足这个抓取需求的过滤器。
在学习之前,我先给出答案,然后再剖析一下,这个过滤器是如何生效的,居然能让我们对包内的内容进行判断。
1 | $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]' |
命令里的可选参数,在前面的内容里已经详细讲过了。这里不再细讲。
本节的重点是引号里的内容,看起来很复杂的样子。
将它逐一分解,我们只要先理解了下面几种用法,就能明白
&:是位运算里的 and 操作符,比如 0011 & 0010 = 0010
>>:是位运算里的右移操作,比如 0111 >> 2 = 0011
0xf0:是 10 进制的 240 的 16 进制表示,但对于位操作来说,10进制和16进制都将毫无意义,我们需要的是二进制,将其转换成二进制后是:11110000,这个数有什么特点呢?前面个 4bit 全部是 1,后面4个bit全部是0,往后看你就知道这个特点有什么用了。
分解完后,再慢慢合并起来看
1、tcp[12:1] & 0xf0 其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 个字节是这样组成的 10110000,那么这个表达式就可以变成 10110110 && 11110000 = 10110000,得到了 10110000 后,再进入下一步。
2、tcp[12:1] & 0xf0) >> 2 :如果你不理解 tcp 报文首部里的数据偏移,请先点击这个前往我的上一篇文章,搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。
tcp[12:1] & 0xf0) >> 2 这个表达式实际是 (tcp[12:1] & 0xf0) >> 4 ) << 2 的简写形式。所以要搞懂 tcp[12:1] & 0xf0) >> 2 只要理解了(tcp[12:1] & 0xf0) >> 4 ) << 2 就行了 。
从上一步我们算出了 tcp[12:1] & 0xf0 的值其实是一个字节,也就是 8 个bit,但是你再回去看下上面的 tcp 报文首部结构图,表示数据偏移量的只有 4个bit,也就是说 上面得到的值 10110000,前面 4 位(1011)才是正确的偏移量,那么为了得到 1011,只需要将 10110000 右移4位即可,也就是 tcp[12:1] & 0xf0) >> 4,至此我们是不是已经得出了实际数据的正确位置呢,很遗憾还没有,前一篇文章里我们讲到 Data Offset 的单位是 4个字节,因为要将 1011 乘以 4才可以,除以4在位运算中相当于左移2位,也就是 <<2,与前面的 >>4 结合起来一起算的话,最终的运算可以简化为 >>2
至此,我们终于得出了实际数据开始的位置是 tcp[12:1] & 0xf0) >> 2 (单位是字节)。
找到了数据的起点后,可别忘了我们的目的是从数据中打到 HTTP 请求的方法,是 GET 呢 还是 POST ,或者是其他的?
有了上面的经验,我们自然懂得使用 tcp[((tcp[12:1] & 0xf0) >> 2):4] 从数据开始的位置再取出四个字节,然后将结果与 GET (注意 GET最后还有个空格)的 16进制写法(也就是 0x47455420)进行比对。
0x47 –> 71 –> G0x45 –> 69 –> E0x54 –> 84 –> T0x20 –> 32 –> 空格
如果相等,则该表达式为True,tcpdump 认为这就是我们所需要抓的数据包,将其输出到我们的终端屏幕上。
抓包实战应用例子
以下例子摘自:https://fuckcloudnative.io/posts/tcpdump-examples/
8.1 提取 HTTP 的 User-Agent
从 HTTP 请求头中提取 HTTP 用户代理:
1 | $ tcpdump -nn -A -s1500 -l | grep "User-Agent:" |
通过 egrep 可以同时提取用户代理和主机名(或其他头文件):
1 | $ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:' |
8.2 抓取 HTTP GET 和 POST 请求
抓取 HTTP GET 请求包:
1 | $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'# or$ tcpdump -vvAls0 | grep 'GET' |
可以抓取 HTTP POST 请求包:
1 | $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354'# or $ tcpdump -vvAls0 | grep 'POST' |
注意:该方法不能保证抓取到 HTTP POST 有效数据流量,因为一个 POST 请求会被分割为多个 TCP 数据包。
8.3 找出发包数最多的 IP
找出一段时间内发包最多的 IP,或者从一堆报文中找出发包最多的 IP,可以使用下面的命令:
1 | $ tcpdump -nnn -t -c 200 | cut -f 1,2,3,4 -d '.' | sort | uniq -c | sort -nr | head -n 20 |
8.4 抓取 DNS 请求和响应
DNS 的默认端口是 53,因此可以通过端口进行过滤
1 | $ tcpdump -i any -s0 port 53 |
8.5 切割 pcap 文件
当抓取大量数据并写入文件时,可以自动切割为多个大小相同的文件。例如,下面的命令表示每 3600 秒创建一个新文件 capture-(hour).pcap,每个文件大小不超过 200*1000000 字节:
1 | $ tcpdump -w /tmp/capture-%H.pcap -G 3600 -C 200 |
这些文件的命名为 capture-{1-24}.pcap,24 小时之后,之前的文件就会被覆盖。
8.6 提取 HTTP POST 请求中的密码
从 HTTP POST 请求中提取密码和主机名:
1 | $ tcpdump -s 0 -A -n -l | egrep -i "POST /|pwd=|passwd=|password=|Host:" |
8.7 提取 HTTP 请求的 URL
提取 HTTP 请求的主机名和路径:
1 | $ tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:" |
8.8 抓取 HTTP 有效数据包
抓取 80 端口的 HTTP 有效数据包,排除 TCP 连接建立过程的数据包(SYN / FIN / ACK):
1 | $ tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' |
8.9 结合 Wireshark 进行分析
通常 Wireshark(或 tshark)比 tcpdump 更容易分析应用层协议。一般的做法是在远程服务器上先使用 tcpdump 抓取数据并写入文件,然后再将文件拷贝到本地工作站上用 Wireshark 分析。
还有一种更高效的方法,可以通过 ssh 连接将抓取到的数据实时发送给 Wireshark 进行分析。以 MacOS 系统为例,可以通过 brew cask install wireshark 来安装,然后通过下面的命令来分析:
1 | $ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - not port 22' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - |
例如,如果想分析 DNS 协议,可以使用下面的命令:
1 | $ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - port 53' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - |
抓取到的数据:
-c 选项用来限制抓取数据的大小。如果不限制大小,就只能通过 ctrl-c 来停止抓取,这样一来不仅关闭了 tcpdump,也关闭了 wireshark。
到这里,我已经将我所知道的 tcpdump 的用法全部说了一遍,如果你有认真地看完本文,相信会有不小的收获,掌握一个上手的抓包工具,对于以后我们学习网络、分析网络协议、以及定位网络问题,会很有帮助,而 tcpdump 是我推荐的一个抓包工具。
]]>前面文章对数据库中间层进行了选型,那么要怎么安装,怎么验证,怎么优化,又有哪些坑可以避免呢?本文就详细介绍下。
1 | 1) 采用yum方式安装 |
ProxySQL有配置文件/etc/proxysql.cnf和配置数据库文件/var/lib/proxysql/proxysql.db。这里需要特别注意:如果存在如果存在”proxysql.db”文件(在/var/lib/proxysql目录下),则ProxySQL服务只有在第一次启动时才会去读取proxysql.cnf文件并解析;后面启动会就不会读取proxysql.cnf文件了!如果想要让proxysql.cnf文件里的配置在重启proxysql服务后生效(即想要让proxysql重启时读取并解析proxysql.cnf配置文件),则需要先删除/var/lib/proxysql/proxysql.db数据库文件,然后再重启proxysql服务。这样就相当于初始化启动proxysql服务了,会再次生产一个纯净的proxysql.db数据库文件(如果之前配置了proxysql相关路由规则等,则就会被抹掉)。 官方推荐用admin interface方式!(即在proxysql本机使用mysql客户端连接管理端口)
1 | [root@mysql-proxy ~]# egrep -v "^#|^$" /etc/proxysql.cnf |
proxysql的6032端口是管理入口,账号密码是admin(可以动态修改),允许客户端连接;6033端口就是客户端入口,账号密码通过管理接口去设置。在proxysql本机使用mysql客户端连接到ProxySQL的管理接口(admin interface), 该接口的默认管理员用户和密码都是admin。
mysql_ifaces
也就是说proxysql有一个admin接口专门来做配置,相当于一个mysql shell可以通过sql来让配置实时生效。
mysql_ifaces配置了允许连接proxysql的ip和port
1 | [root@mysql-proxy ~]# vim /etc/proxysql.cnf |
如果ip配置为0.0.0.0表示不限制ip,但是出于安全考虑,admin用户无论怎么设置都只能在本机登录!!!
admin_credentials
这个key保存所有可以操作proxysql的用户名和密码,格式为:user:pass;user1:pass1,这里可以修改密码或定义一个非admin的用户用于远程登录。 前提是保证想要管理proxysql的机器安装有mysql client客户端!
1 | 先在proxysql本机登录 (因为初始账号密码是admin:admin,只能在本机登录), 这里的proxysql本机地址是172.16.60.214 |
ProxySQL的库、表说明 (默认管理端口是6032,客户端服务端口是6033。默认的用户名密码都是 admin)
1 | 通过管理端口6032去连接的 (注意, 下面连接命令中后面的--prompt 'admin'字段可以不加,也是可以登录进去的) |
global_variables 有80多个变量可以设置,其中就包括监听的端口、管理账号、禁用monitor等
1 | (admin@127.0.0.1:6032) [(none)]> show tables; |
- hostgroup_id: ProxySQL通过 hostgroup (下称HG) 的形式组织后端db实例。一个 HG 代表同属于一个角色
- 该表的主键是 (hostgroup_id, hostname, port),可以看到一个 hostname:port 可以在多个hostgroup里面,如上面的 10.0.100.100:3307,这样可以避免 HG 1000 的从库全都不可用时,依然可以把读请求发到主库上。
- 一个 HG 可以有多个实例,即多个从库,可以通过 weight 分配权重
- hostgroup_id 0 是一个特殊的HG,路由查询的时候,没有匹配到规则则默认选择 HG 0
- status:
- ONLINE: 当前后端实例状态正常
- SHUNNED: 临时被剔除,可能因为后端 too many connections error,或者超过了可容忍延迟阀值 max_replication_lag
- OFFLINE_SOFT: “软离线”状态,不再接受新的连接,但已建立的连接会等待活跃事务完成。
- OFFLINE_HARD: “硬离线”状态,不再接受新的连接,已建立的连接或被强制中断。当后端实例宕机或网络不可达,会出现。
- max_connections: 允许连接到该后端mysql实例的最大连接数。不要大于MySQL设置的 max_connections,如果后端实例 hostname:port 在多个 hostgroup 里,以较大者为准,而不是各自独立允许的最大连接数。
- max_replication_lag: 允许的最大延迟,主库不受这个影响,默认0。如果 > 0, monitor 模块监控主从延迟大于阀值时,会临时把它变为 SHUNNED 。
- max_latency_ms: mysql_ping 响应时间,大于这个阀值会把它从连接池剔除(即使是ONLINE)
- comment: 备注,不建议留空。可以通过它的内容如json格式的数据,配合自己写的check脚本,完成一些自动化的工作。
表 mysql_users
1 | MySQL [(none)]> show create table mysql_users\G; |
- username, password: 连接后端db的用户密码。
这个密码你可以插入明文,也可以插入hash加密后的密文,proxysql会检查你插入的时候密码是否以 * 开头来判断,而且密文要在其它地方使用 PASSWORD()生成。但到 runtime_mysql_users 里,都统一变成了密文,所以可以明文插入,再 SAVE MYSQL USERS TO MEM,此时看到的也是HASH密文。
- active: 是否生效该用户。
- default_hostgroup: 这个用户的请求没有匹配到规则时,默认发到这个 hostgroup,默认0
- default_schema: 这个用户连接时没有指定 database name 时,默认使用的schema
注意表面上看默认为NULL,但实际上受到变量 mysql-default_schema 的影响,默认为 information_schema。关于这个参考我所提的 issue #988
- transaction_persistent: 如果设置为1,连接上ProxySQL的会话后,如果在一个hostgroup上开启了事务,那么后续的sql都继续维持在这个hostgroup上,不伦是否会匹配上其它路由规则,直到事务结束。
虽然默认是0,但我建议还是设成1,虽然一般来说由于前段应用的空值,为0出问题的情况几乎很小。作者也在考虑默认设成 1,refer this issue #793
- frontend, backend: 目前版本这两个都需要使用默认的1,将来有可能会把 Client -> ProxySQL (frontend) 与 ProxySQL -> BackendDB (backend)的认证分开。从 runtime_mysql_users 表内容看到,记录数比 mysql_users 多了一倍,就是把前端认证与后端认证独立出来的结果。
- fast_forward: 忽略查询重写/缓存层,直接把这个用户的请求透传到后端DB。相当于只用它的连接池功能,一般不用,路由规则 .* 就行了。
表 mysql_replication_hostgroups
1 | MySQL [(none)]> show create table mysql_replication_hostgroups\G; |
定义 hostgroup 的主从关系。ProxySQL monitor 模块会监控 HG 后端所有servers 的 read_only
变量,如果发现从库的 read_only 变为0、主库变为1,则认为角色互换了,自动改写 mysql_servers 表里面 hostgroup 关系,达到自动 Failover 效果。
表 mysql_query_rules
mysql_query_rules 是ProxySQL非常核心一个表,定义查询路由规则
1 | MySQL [(none)]> show create table mysql_query_rules\G; |
- rule_id: 表主键,自增。规则处理是以 rule_id 的顺序进行。
- active: 只有 active=1 的规则才会参与匹配。
- username: 如果非 NULL,只有连接用户是 username 的值才会匹配。
- schemaname: 如果非 NULL,只有查询连接使用的db是 schemaname 的值才会匹配。
注意如果是 NULL,不代表连接没有使用schema,而是不伦任何schema都进一步匹配。
- flagIN, flagOUT, apply: 用来定义路由链 chains of rules。
- 首先会检查 flagIN=0 的规则,以rule_id的顺序;如果都没匹配上,则走这个用户的 default_hostgroup。
- 当匹配一条规则后,会检查 flagOUT。
- 如果不为NULL,并且 flagIN != flagOUT ,则进入以flagIN为上一个flagOUT值的新规则链。
- 如果不为NULL,并且 flagIN = flagOUT,则应用这条规则。
- 如果为NULL,或者 apply=1,则结束,应用这条规则。
- 如果最终没有匹配到,则找到这个用户的 default_hostgroup。
- client_addr: 匹配客户端来源IP
- proxy_addr, proxy_port: 匹配本地proxysql的IP、端口。我目前没有想到它的应用场景,可能是把proxysql监听在多个接口上,分发到不同的业务?
- digest: 精确的匹配一类查询。
- match_digest: 正则匹配一类查询。query digest 是指对查询去掉具体值后进行“模糊化”后的查询,类似 pt-fingerprint / pt-query-digest 的效果。
- match_pattern: 正则匹配查询。
以上都是匹配查询的规则,1.3.5版本使用的正则引擎只有 RE2 ,1.4版本可以通过变量 mysql-query_processor_regex 设置 RE2 或者 PCRE,且1.4开始默认是PCRE。
推荐用 match_digest 。关于每条查询都会计算digest对性能的影响,计算query digest确实会有性能损失,但是这却是proxysql里面非常重要的特性,主要是两点:
- proxysql无法知道连接复用(multipexing)是否必须被自动禁用,比如连接里面有variables/tmp tables/lock table等特殊命令,是不能复用的。
- 完整的查询去匹配正则的效率,一般没有参数化后的查询匹配效率高,因为有很长的字符串内容需要处理。再者,SELECT * FROM randomtable WHERE comment LIKE ‘%INTO sbtest1 % FROM sbtest2 %’字符串里有类似这样的语句,很难排除误匹配。
- negate_match_pattern: 反向匹配,相当于对 match_digest/match_pattern 的匹配取反。
- re_modifiers: 修改正则匹配的参数,比如默认的:忽略大小写CASELESS、禁用GLOBAL.
上面都是匹配规则,下面是匹配后的行为
- replace_pattern: 查询重写,默认为空,不rewrite。
- rewrite规则要遵守 RE2::Replace 。
destination_hostgroup: 路由查询到这个 hostgroup。当然如果用户显式 start transaction 且 transaction_persistent=1,那么即使匹配到了,也依然按照事务里第一条sql的路由规则去走。
- cache_ttl: 查询结果缓存的毫秒数。
proxysql这个 Query Cache 与 MySQL 自带的query cache不是同一个。proxysql query cache也不会关心后端数据是否被修改,它所做的就是针对某些特定种类的查询结果进行缓存,比如一些历史数据的count结果。一般不设。
- timeout: 这一类查询执行的最大时间(毫秒),超时则自动kill。
这是对后端DB的保护机制,相当于阿里云RDS loose_max_statement_time 变量的功能,但是注意不同的是,阿里云这个变量的时间时不包括DML操作出现InnoDB行锁等待的时间,而ProxySQL的这个 timeout 是计算从发送sql到等待响应的时间。默认mysql-default_query_timeout给的是 10h .
- retries: 语句在执行时失败时,重试次数。默认由 mysql-query_retries_on_failure变量指定,为1 。
个人建议把它设成0,即不重试。因为执行失败,对select而言很少见,主要是dml,但自己重试对数据不放心。
- delay: 查询延迟执行,这是ProxySQL提供的限流机制,会让其它的查询优先执行。
默认值 mysql-default_query_delay,为0。我们一般不用,其实还是要配合应用端使用,比如这边延迟执行,但上层等待你返回,那前端不就堵住了,没准出现雪崩效应。
- mirror_flagOUT,mirror_hostgroup
这两个高级了,目前这部分文档不全,功能是SQL镜像。顾名思义,就是把匹配到的SQL除了发送到 destination_hostgroup,同时镜像一份到这里的hostgroup,比如我们的测试库。比如这种场景,数据库要从5.6升级到5.7,要验证现有查询语句对5.7的适用情况,就可以把生产流量镜像到5.7新库上验证。
- error_msg: 默认为NULL,如果指定了则这个查询直接被 block 掉,马上返回这个错误信息。
这个功能也很实用,比如线上突然冒出一个 “坏查询”,应用端不方便马上发版解决,我们就可以在这配置一个规则,把查询屏蔽掉,想正常的mysql报错那样抛异常。下一篇文章有演示。
- multiplex: 连接是否复用。
- log: 是否记录查询日志。可以看到log是否记录的对象是根据规则。
要开启日志记录,需要设置变量 mysql-eventslog_filename 来指定文件名,然后这个 log 标记为1。但是目前proxysql记录的日志是二进制格式,需要特定的工具才能读取: eventslog_reader_sample 。这个工具在源码目录 tools下面。
proxysql对后端server健康检查
1 | MySQL [monitor]> show variables like "mysql-monitor%"; |
两种方式,区别在于
1) 一种是在往mysql_servers表中添加server时就为其划分好hostgroup_id(例如0表示写组,1表示读组)
2) 另一种往mysql_servers表中添加server时不区分hostgroup_id(例如全部设为0),然后通过mysql_replication_hostgroups表中的值,
根据proxysql检测到的各server的read_only变量值来自动为后端server设置hostgroup_id
这里强烈推荐用第一种方式
因为第一种是完全由我们控制的;而第二种假如我们误将读server的read_only属性设置为0,则proxysql会将其重新分配到写组,这绝对是不期望的。
ProxySQL下添加与修改配置
1 | 1) 添加配置 |
针对GTID模式的主从同步,另两个从库都要设置read_only=on
接下来通过实战操作来全面了解一下 ProxySQL 的特性和使用场景。
1 | 172.16.60.211 mysql-master 安装Mysql5.7 |
1 | 在三个mysql节点机上使用yum方式安装Mysql5.7,参考:https://www.cnblogs.com/kevingrace/p/8340690.html |
在mysql-master 和 mysql-slave1、mysql-slave2节点上
1 | 1) 主数据库mysql-master (172.16.60.211)的配置操作 |
已经在上面第一步中介绍了安装方法,这里采用rpm包方式安装,安装过程省略……..
向ProxySQL中添加MySQL节点
1 | 使用insert语句添加主机到mysql_servers表中,其中:hostgroup_id 为10表示写组,为20表示读组。 |
监控后端MySQL节点
添加Mysql节点之后,还需要监控这些后端节点。对于后端是主从复制的环境来说,这是必须的,因为ProxySQL需要通过每个节点的read_only值来自动调整
它们是属于读组还是写组。
首先在后端master主数据节点上创建一个用于监控的用户名(只需在master上创建即可,因为会复制到slave上),这个用户名只需具有USAGE权限即可。如果还需
要监控复制结构中slave是否严重延迟于master(这个俗语叫做”拖后腿”,术语叫做”replication lag”),则还需具备replication client权限。
1 | 在mysql-master主数据库节点行执行: |
配置mysql_users
上面的所有配置都是关于后端MySQL节点的,现在可以配置关于SQL语句的,包括:发送SQL语句的用户、SQL语句的路由规则、SQL查询的缓存、SQL语句的重写等等。本小节是SQL请求所使用的用户配置,例如root用户。这要求我们需要先在后端MySQL节点添加好相关用户。这里以root和sqlsender两个用户名为例.
1 | 首先,在mysql-master主数据库节点上执行:(只需master执行即可,会复制给两个slave) |
读写分离:配置路由规则
ProxySQL的路由规则非常灵活,可以基于用户、基于schema以及基于每个语句实现路由规则的定制。本案例作为一个入门配置,实现一个最简单的语句级路由规则,从而实现读写分离。
必须注意: 这只是实验,实际的路由规则绝不应该仅根据所谓的读、写操作进行分离,而是从各项指标中找出压力大、执行频繁的语句单独写规则、做缓存等等。和查询规则有关的表有两个:mysql_query_rules和mysql_query_rules_fast_routing,后者是前者的扩展表,1.4.7之后才支持该快速路由表。本案例只介绍第一个表。插入两个规则,目的是将select语句分离到hostgroup_id=20的读组,但由于select语句中有一个特殊语句SELECT…FOR UPDATE它会申请写锁,所以应该路由到hostgroup_id=10的写组.
1 | [root@mysql-proxy ~]# mysql -uadmin -padmin -P6032 -h127.0.0.1 |
测试读写分离效果
1 | 由于读写操作都记录在proxysql的stats_mysql_query_digest表内。 |
如上已经配置好一主(mysql-master,在hostgroup10写组内)、两从(mysql-slave1和mysql-slave2,在hostgroup20读组内) ,并且已经在”mysql_query_rules”表中配置了路由规则,即写操作转发到hostgroup10组,读操作转发到hostgroup20组.
1 | MySQL [(none)]> select * from mysql_query_rules; |
1 | 首先打开web功能 |
查看web端口是否正常打开
1 | [root@mysql-proxy ~]# lsof -i:6080 |
访问http://172.16.60.214:6080并使用stats:stats登录即可查看一些统计信息。
1 | [root@mysql-proxy ~]# mkdir -p /opt/proxysql/log |
现在微服务几乎成为所有公司的标配,那么业务项目和数据存储的松耦合就成为基本配置,而mysql数据库在互联网公司中应用很广,几乎所有的项目都会有连它的需求。但是如果业务请求量很大,那么最先想到也是最常用的是数据库的读写分离。通常是由dba把数据库分为读写库,对数据进行更新,写入时连接读写库。查询数据时,连接读库。这样可以大大减轻写库的压力。
但是这样是由业务根据需求来区分连哪个数据库,但有些开发说我想只配置一个数据库,运维你根据请求类型来区分定义是连接只读库还是读写库。而且业务对时效性也不是很严格。那要怎么做呢?如下图:
我们就需要增加数据库的代理层,由代理层根据定义的规则来自动区分是连接读写库还是只读库。本文就是聊聊数据库的代理层–ProxySQL。
ProxySQL是灵活强大的MySQL代理层, 是一个能实实在在用在生产环境的MySQL中间件,可以实现读写分离,支持 Query 路由功能,支持动态指定某个 SQL 进行 cache,支持动态加载配置、故障切换和一些 SQL的过滤功能。还有一些同类产品比如 DBproxy、MyCAT、OneProxy 等。但经过反复对比和测试之后,还是觉得ProxySQL是一款性能不谙,靠谱稳定的MySQL 中间件产品 !
ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎。它是用C++语言开发的,虽然是一个轻量级产品,但性能很好(据测试,能处理千亿级的数据),功能也足够,能满足中间件所需的绝大多数功能。具有以下特性:
如上可知,ProxySQL集合了很多优秀特性于一身,那么它的缺点呢就是项目不够成熟,好在官方网站一直在及时更新,并且受到 Percona 官方的支持。
ProxySQL有一个完备的配置系统,配置ProxySQL是基于sql命令的方式完成的。ProxySQL支持配置修改之后的在线保存、应用,不需要重启即可生效。整个配置系统分三层设计。
- runtime:运行中使用的配置文件
- memory:提供用户动态修改配置文件
- disk:将修改的配置保存到磁盘SQLit表中(即:proxysql.db)
- config:一般不使用它(即:proxysql.cnf)
如下图所示:
ProxySQL配置系统分为三层的目的:
1) 自动更新;
2) 尽可能的不重启proxysql就可以修改配置;
3) 方便回滚错误配置;
简单说就是配置proxysql分为三个级别,RUNTIME是即时生效的,MEMORY是保存在内存中但并不立即生效的,DISK|CONFIG FILE是持久化或写在配置文件中的。
这三个级别的配置文件互不干扰,在某个层级修改了配置文件,想要加载或保存到另一个层级,需要额外的LOAD或SAVE操作:”LOAD xx_config FROM xx_level | LOAD xx_config TO xx_level | SAVE xx_config TO xx_level | SAVE xx_config FROM xx_level”,达到加载配置或者持久化配置的目的。这三层中每层的功能与含义如下:
- RUNTIME层
代表的是ProxySQL当前生效的配置,包括 global_variables, mysql_servers, mysql_users, mysql_query_rules。无法直接修改这里的配置,必须要从下一层load进来。该层级的配置时在proxysql管理库(sqlite)的main库中以runtime_开头的表,这些表的数据库无法直接修改,只能从其他层级加载;该层代表的是ProxySQL当前生效的正在使用的配置,包括global_variables, mysql_servers, mysql_users, mysql_query_rules表。无法直接修改这里的配置,必须要从下一层load进来。也就是说RUNTIME这个顶级层,是proxysql运行过程中实际使用的那一份配置,这一份配置会直接影响到生产环境的,所以要将配置加载进RUNTIME层时需要三思而行。
- MEMORY层
是平时在mysql命令行修改的 main 里头配置,可以认为是SQLite数据库在内存的镜像。该层级的配置在main库中以mysql_开头的表以及global_variables表,这些表的数据可以直接修改;用户可以通过MySQL客户端连接到此接口(admin接口),然后可以在mysql命令行查询不同的表和数据库,并修改各种配置,可以认为是SQLite数据库在内存的镜像。也就是说MEMORY这个中间层,上面接着生产环境层RUNTIME,下面接着持久化层DISK和CONFIG FILE。MEMORY层是我们修改proxysql的唯一正常入口。一般来说在修改一个配置时,首先修改Memory层,确认无误后再接入RUNTIME层,最后持久化到DISK和CONFIG FILE层。也就是说memeory层里面的配置随便改,不影响生产,也不影响磁盘中保存的数据。通过admin接口可以修改mysql_servers、mysql_users、mysql_query_rules、global_variables等表的数据。
- DISK|CONFIG FILR层
持久存储的那份配置,一般在$(DATADIR)/proxysql.db,在重启的时候会从硬盘里加载。 /etc/proxysql.cnf文件只在第一次初始化的时候用到,完了后,如果要修改监听端口,还是需要在管理命令行里修改,再 save 到硬盘。该层级的配置在磁盘上的sqlite库或配置文件里。DISK/CONFIG FILE层表示持久存储的那份配置,持久层对应的磁盘文件是$(DATADIR)/proxysql.db,在重启ProxySQL的时候,会从proxysql.db文件中加载信息。而 /etc/proxysql.cnf文件只在第一次初始化的时候使用,之后如果要修改配置,就需要在管理端口的SQL命令行里进行修改,然后再save到硬盘。 也就是说DISK和CONFIG FILE这一层是持久化层,我们做的任何配置更改,如果不持久化下来,重启后,配置都将丢失。
需要注意
1) ProxySQL每一个配置项在三层中都存在,但是这三层是互相独立的,也就是说proxysql可以同时拥有三份配置,每层都是独立的,可能三份配置都不一样,也可能三份都一样。
2) RUNTIME层代表 ProxySQL 当前生效的正在使用的配置,无法直接修改这里的配置,必须要从下一层 “load” 进来。
3) MEMORY这一层上面连接 RUNTIME 层,下面连接持久化层。在这层可以正常操作 ProxySQL 配置,随便修改,不会影响生产环境。修改一个配置一般都是先在 MEMORY 层完成,然后确认正常之后再加载到 RUNTIME 和持久化到磁盘上。
4) DISK 和 CONFIG FILE层持久化配置信息,重启后内存中的配置信息会丢失,所以需要将配置信息保留在磁盘中。重启时,可以从磁盘快速加载回来。
ProxySQL配置文件的修改流程一般是:
- 启动时:先修改必要的CONFIG FILE配置,比如管理端口,然后启动;
- 其他配置:修改MEMORY中的表,然后加载到RUNTIME并持久化。
ProxySQL具有一个复杂但易于使用的配置系统,可以满足以下需求:
- 允许轻松动态更新配置(这是为了让ProxySQL用户可以在需要零宕机时间配置的大型基础架构中使用它)。与MySQL兼容的管理界面可用于此目的。
- 允许尽可能多的配置项目动态修改,而不需要重新启动ProxySQL进程
- 可以毫不费力地回滚无效配置
- 这是通过多级配置系统实现的,其中设置从运行时移到内存,并根据需要持久保存到磁盘。
一般,修改的配置都是在memory层。可以load到runtime,使配置在不用重启proxysql的情况下也可以生效,也可以save到disk,将对配置的修改持久化!
需要修改配置时,直接操作的是 MEMORAY,以下命令可用于加载或保存 users (mysql_users): (序号对应上图“运行机制”草图)
1 | [1]: LOAD MYSQL USERS TO RUNTIME / LOAD MYSQL USERS FROM MEMORY #常用。将修改后的配置(在memory层)用到实际生产 |
个人还是比较习惯用 TO,记住往上层是 LOAD,往下层是 SAVE。以下命令加载或保存servers (mysql_servers):
1 | [1]: LOAD MYSQL SERVERS TO RUNTIME #常用,让修改的配置生效 |
后面的使用方法也基本相同,一并列出。以下命令加载或保存query rules (mysql_query_rules):
1 | [1]: load mysql query rules to run #常用 |
以下命令加载或保存 mysql variables (global_variables):
1 | [1]: load mysql variables to runtime |
以下命令加载或保存admin variables (select * from global_variables where variable_name like ‘admin-%’):
1 | [1]: load admin variables to runtime |
ProxySQL启动过程总结:
当proxysql启动时,首先读取配置文件CONFIG FILE(/etc/proxysql.cnf),然后从该配置文件中获取datadir,datadir中配置的是sqlite的数据目录。如果该目录存在,且sqlite数据文件存在,那么正常启动,将sqlite中的配置项读进内存,并且加载进RUNTIME,用于初始化proxysql的运行。如果datadir目录下没有sqlite的数据文件,proxysql就会使用config file中的配置来初始化proxysql,并且将这些配置保存至数据库。sqlite数据文件可以不存在,/etc/proxysql.cnf文件也可以为空,但/etc/proxysql.cnf配置文件必须存在,否则,proxysql无法启动。
今天接了个需求:要把一些资源文件从外网提供给客户下载。处于安全和简单快捷考虑,分享一个快速实现并安全性很强的方案:nginx配置账号密码来控制,并且密码还是加密的,再增加白名单配置。此方案简单快捷和安全。
1 | yum install httpd-tools -y |
设置用户名和密码,并把用户名、密码保存到指定文件中:
1 | [sun@bogon conf]$ sudo mkdir passwd |
注意:上面的 passwd/passwd 是生成密码文件的路径,绝对路径是/etc/nginx/passwd/passwd ,然后sun是用户名,你可以根据需要自行设置成其它用户名。运行命令后,会要求你连续输入两次密码。输入成功后,会提示已经为sun这个用户添加了密码。
查看下生成的密码文件的内容:
1 | [sun@bogon conf]$ cat passwd/passwd |
其中用户名就是sun,分号后面就是密码(已经加过密)。
找到 nginx 配置文件,因为我们要对整个站点开启验证,所以在配置文件中的第一个server修改如下:
1 | server { |
然后nginx重新加载reload:
以上都配置无误后,你重新访问你的站点,如果出现需要身份验证的弹窗就说明修改成功了。
htpasswd命令选项参数说明:
1 | -c 创建一个加密文件 |
利用htpasswd命令添加用户
1 | htpasswd -bc ./.passwd sun pass |
在当前目录下生成一个.passwd文件,用户名sandu,密码:pass,默认采用MD5加密方式
在原有密码文件中增加下一个用户
1 | htpasswd -b ./.passwd sun1 pass |
去掉c选项,即可在第一个用户之后添加第二个用户,依此类推
不更新密码文件,只显示加密后的用户名和密码
1 | htpasswd -nb sun pass |
不更新.passwd文件,只在屏幕上输出用户名和经过加密后的密码
利用htpasswd命令删除用户名和密码
1 | htpasswd -D .passwd sun |
利用 htpasswd 命令修改密码
1 | htpasswd -D .passwd sun |
nginx的使用范围和影响越来越广,很多大厂都在使用,但有些工作多年的同学可能都搞不清楚nginx中location的匹配优先级和匹配顺序是怎样的。今天又有同事不清楚,写配置时总是达不到业务需求,问到我这边帮他搞定了。那么本文就给大家详细聊聊这个问题。
nginx的安装和搭建这里就不再赘述了。无论你是直接命令包库yum或apt-get安装还是下载源码包编译安装等等,看你喜好。
nginx是通过server块中location的配置用来匹配不同url访问:
location配置匹配方式主要包括三种:精准匹配、普通匹配和正则匹配
定义
location = expression 精准匹配
location expression 普通匹配
location ^~ expression 普通匹配
location ~ regex 正则匹配(区分大小写)
location ~* regex 正则匹配(不区分大小写)
要求
精准匹配要求uri与表达式(expression)完全匹配。
普通匹配要求uri与表达式满足前缀匹配。
正则匹配要求uri与正则表达式匹配。
匹配优先级和顺序规则
精准匹配(=) > 普通匹配(^~) > 正则匹配(或*) > 普通匹配(直接目录)
1、首先精准匹配,如能匹配,则进行转发。如未能匹配成功,则进行普通匹配(^)。类型的普通匹配规则进行匹配。如有多条规则均命中,则选择最长匹配。匹配成功后,进行转发。否则,则进行正则匹配。
2、nginx将uri和所有^
3、正则匹配与顺序有关,按编写顺序进行匹配,一旦匹配成功,则转发请求并停止匹配。匹配不成功,则进行普通匹配(location expression )
4、进行普通匹配(location expression),匹配成功则转发,不成功则返回错误码。
glusterfs集群的搭建和使用这里就不再赘述了,可以看以前的教程文档。本文主要聊的是随着服务使用量的增加,那么存储集群势必要扩充空间。服务器迁移,需要先扩容后缩容等等。所以本文的主旨是聊glusterfs集群的横向优化:扩容和缩容。
集群搭建这里忽略
查看glusterfs的节点和客户端挂载情况得知,目前是三个节点的分布式卷。
1 | #查看节点数量 |
创建20个文件
查看文件的分布情况如下:
1 | # 第1台 |
现要对集群进行扩容,增加一个节点 gluster004-hf-aiui.
1 | # 添加一个节点 |
再创建30个文件,如下所示:
1 | root@wyl01:/gsclient# touch {101..130}.txt |
结论:可以看出当扩容后,原先的数据不会均衡到第四台glusterfs上,但是新增加的文件是可以的。
1 | root@wyl01:/gsclient# gluster volume rebalance gv1 start |
可以看到,数据rebalance,第 4 台上的数据明显增加了。
这里有一个需要注意的地方,当数据量太大的时候,对数据进行rebalance必须要考虑的一个问题就是性能,不能因为数据rebalance而影响我们的存储的正常使用。Glusterfs也考虑到了这个问题,在进行数据rebalance时,根据实际场景不同设计了三种不同的“级别”:
lazy:每次仅可以迁移一个文件
normal:默认设置,每次迁移2个文件或者是(CPU逻辑个数-4)/2,哪个大,选哪个
aggressive:每次迁移4个文件或者是(CPU逻辑个数-4)/2
通过以下命令进行配置:
1 | gluster volume set VOLUME-NAME cluster.rebal-throttle [lazy|normal|aggressive] |
如将volume repvol设置为lazy
1 | [root@nwyl01 ~]# gluster volume set gv1 cluster.rebal-throttle lazy |
缩容之前我们先需要将数据迁移到其他的brick上,假设我们移除gluster004-hf-aiui节点
1 | root@wyl01:/gsclient# gluster volume remove-brick gv1 gluster004-hf-aiui:/data help |
移除后,我们看数据的分布情况
1 | # 第 1 台 |
可以看到文件被迁移到其他的brick上了。
1 | root@wyl01:/gsclient# gluster volume info # 卷的基本信息 |
1 | root@wyl01:/gsclient# gluster peer probe 192.168.52.124 |
1 | root@wyl01:/gsclient# gluster peer probe 192.168.52.125 |
1 | root@wyl01:/gsclient# gluster peer status |
发现现在变成2*2了模式了。重新写入20个txt文件,扩容后这里需要注意的是必须先rebalance。然后重新写入文件才会hash到新的节点上。之前的旧数据也会被rebalance。
1 | root@wyl01:/gsclient# gluster volume rebalance gv1 start |
节点的缩容,这里是分布式复制,所以缩容也是成对节点的一起缩容,操作如下:
1 | # 开始移除节点 |
本文讨论Kafka的扩缩容以及故障后如何“补齐”分区。实质上先扩容再缩容也是迁移的操作。
Kafka 版本2.6。
扩容也就是新增节点,扩容后老的数据不会自动迁移,只有新创建的topic才可能会分配到新增的节点上面。如果我们不需要迁移旧数据,那直接把新的节点启动起来就行了,不需要做额外的操作。但有的时候,新增节点后,我们会将一些老数据迁移到新的节点上,以达到负载均衡的目的,这个时候就需要手动操作了。Kafka提供了一个脚本(在bin目录下):kafka-reassign-partitions.sh,通过这个脚本可以重新分配分区的分布。脚本的使用比较简单,提供一个JSON格式的分配方案,然后传给脚本,脚本根据我们的分配方案重新进行平衡。
举个例子,假如现在集群有181、182两个broker,上面有4个topic:test-1,test-2,test-3,test-4,这些topic都有4个分区,2个副本,如下:
1 | 两个broker |
现在扩容了,新增了两个节点:183和184。扩容后,我们想要把test-3,test-4迁移到183,184上面去。
首先我们可以准备如下JSON格式的文件(假设文件名为topics-to-move.json
):
1 | { |
里面写明想要重新分配的topic。然后执行如下命令:
1 | ➜ bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 --topics-to-move-json-file topics-to-move.json --broker-list "183,184" --generate |
可以看到上面的命令会列出当前分区的分布情况,并且会给出一个建议的新分区分配方案,都是JSON格式的,内容也很简单。然后我们将建议的分配方案保存为一个文件(假设文件名为expand-cluster-reassignment.json
),当然我们也可以手动修改这个方案,只要格式正确即可。然后执行下面命令使用新的方案进行分区重分配:
1 | ➜ bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 --reassignment-json-file expand-cluster-reassignment.json --execute |
这样就提交了重分配的任务,可以使用下面的命令查看任务的执行状态:
1 | ➜ bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 --reassignment-json-file expand-cluster-reassignment.json --verify |
完成后,我们检查一下新的test-3和test-4的分区分配情况:
1 | ➜ bin/kafka-topics.sh --describe --topic test-3 --zookeeper localhost:2181/kafka_26 |
可以看到,这两个topic的数据已经全部分配到183和184节点上了。
从上面可以看到,其实数据分配完全是由我们自己把控的,缩容也只是数据迁移而已,只需要提供正确的迁移方案即可。一般生产环境很少有缩容的,但有一个场景比较常见,就是某个节点故障了,且无法恢复。以前的文章提到过,节点故障后,这个节点上的分区就丢了,Kafka不会自动在其它可用节点上重新创建一个副本,这个时候就需要我们自己手动在其他可用节点创建副本,原理和扩容是一样的。接着上面的例子,比如现在184节点故障了,且无法恢复了,而test-3和test-4有部分分区是在该节点上面的,自然也就丢了:
1 | 节点挂了,zk中的节点已经没了 |
这个时候,我们准备把test-3原来在184上的分区分配到181上面去,把test-4在184上的分区分配到182上去,那分配方案就是下面这样的:
1 | ➜ cat expand-cluster-reassignment.json |
然后执行分配方案即可:
1 | 执行分配方案 |
页面操作不支持批量操作topic,需要逐个topic进行操作。
1,进入topic视图,点击 Generate Partition Assignments 生成分区分配。进入分区分配界面,
2,对该topic需要占用的节点进行勾选,再次点击 Generate Partition Assignments
3,分区完成 , go to topic view
4, 重新分配。 Reassign Partitions
5,go to reassign partitions 转到重新分配分区
6,验证查看
不管扩容还是缩容,或者是故障后手动补齐分区,实质都是分区重分配,使用kafka-reassign-partitions.sh
脚本即可。该脚本使用也非常简单:
--generate
生成迁移方案文件;--execute
执行新的分配方案;--verify
查看分配方案执行进度。如果对于分配方案文件格式很熟悉,可以跳过1.
]]>通过本文掌握什么是负载均衡及负载均衡的作用和意义;了解lvs负载均衡的三种模式;了解lvs-DR负载均衡部署方法;掌握nginx实现负载均衡的方法;掌握lvs+nginx负载均衡拓扑结构。
一台普通服务器的处理能力是有限的,假如能达到每秒几万个到几十万个请求,但却无法在一秒钟内处理上百万个甚至更多的请求。但若能将多台这样的服务器组成一个系统,并通过软件技术将所有请求平均分配给所有服务器,那么这个系统就完全拥有每秒钟处理几百万个甚至更多请求的能力。这就是负载均衡最初的基本设计思想。
负载均衡是由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助。通过某种负载分担技术,将外部发送来的请求按照某种策略分配到服务器集合的某一台服务器上,而接收到请求的服务器独立地回应客户的请求。负载均衡解决了大量并发访问服务问题,其目的就是用最少的投资获得接近于大型主机的性能。
DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。DNS协议运行在UDP协议之上,使用端口号53。
DNS负载均衡技术是最早的负载均衡解决方案,它是通过DNS服务中的随机名字解析来实现的,在DNS服务器中,可以为多个不同的地址配置同一个名字,而最终查询这个名字的客户机将在解析这个名字时得到其中的一个地址。因此,对于同一个名字,不同的客户机会得到不同的地址,它们也就访问不同地址上的Web服务器,从而达到负载均衡的目的。
如下图:
优点
实现简单、实施容易、成本低、适用于大多数TCP/IP应用;
缺点
1、 负载分配不均匀,DNS服务器将Http请求平均地分配到后台的Web服务器上,而不考虑每个Web服务器当前的负载情况;如果后台的Web服务器的配置和处理能力不同,最慢的Web服务器将成为系统的瓶颈,处理能力强的服务器不能充分发挥作用;
2、可靠性低,如果后台的某台Web服务器出现故障,DNS服务器仍然会把DNS请求分配到这台故障服务器上,导致不能响应客户端。
3、变更生效时间长,如果更改NDS有可能造成相当一部分客户不能享受Web服务,并且由于DNS缓存的原因,所造成的后果要持续相当长一段时间(一般DNS的刷新周期约为24小时)。
基于四层交换技术的负载均衡是通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器与请求客户端建立TCP连接,然后发送Client请求的数据。
如下图:
client发送请求至4层负载均衡器,4层负载均衡器根据负载策略把client发送的报文目标地址(原来是负载均衡设备的IP地址)修改为后端服务器(可以是web服务器、邮件服务等)IP地址,这样client就可以直接跟后端服务器建立TCP连接并发送数据。
具有代表意义的产品:LVS(开源软件),F5(硬件)
优点
性能高、支持各种网络协议
缺点
对网络依赖较大,负载智能化方面没有7层负载好(比如不支持对url个性化负载),F5硬件性能很高但成本也高需要人民币几十万,对于小公司就望而却步了。
基于七层交换技术的负载均衡也称内容交换,也就是主要通过报文中的真正有意义的应用层内容,再加上负载均衡设备设置的服务器选择方式,决定最终选择的服务器。
如下图:
七层负载均衡服务器起了一个代理服务器的作用,client要访问webserver要先与七层负载设备进行三次握手后建立TCP连接,把要访问的报文信息发送给七层负载均衡;然后七层负载均衡再根据设置的均衡规则选择特定的webserver,然后通过三次握手与此台webserver建立TCP连接,然后webserver把需要的数据发送给七层负载均衡设备,负载均衡设备再把数据发送给client。
具有代表意义的产品:nginx(软件)、apache(软件)
优点
对网络依赖少,负载智能方案多(比如可根据不同的url进行负载)
缺点
网络协议有限,nginx和apache支持http负载,性能没有4层负载高
四层负载使用lvs软件或F5硬件实现。
七层负载使用nginx实现。
如下图是lvs+nginx的拓扑结构:
在keepalived+nginx的主备容灾高可用的架构中,nginx是作为外部访问系统的唯一入口,理论上一台nginx的最大并发量可以高达50000,但是当并发量更大的时候,keepalived+nginx的高可用机制是没办法满足需求的,因为keepalived+nginx的架构中确确实实是一台nginx在工作,只有当master宕机或异常时候,备份机才会上位。那么如何解决更大的高并发问题呢,也许会问能不能搭建nginx集群,直接对外提供访问?
很显然这是欠妥当的,因为当nginx作为外部的唯一访问入口,没办法直接以集群的形式对外提供服务,没有那么多的公网ip资源可用,既太浪费也不友好。但是在内网环境下,是可以用nginx集群(nginx横向扩展服务集合)的,当然总得有一个对外入口,所以需要在nginx集群之上,在加一层负载均衡器,作为系统的唯一入口。
LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一。
运行 lPVS软件的服务器,在整个负载均衡集群中承担一调度角色 软件的服务器,(即 向真实服务器分配从客户端过来的请求。LVS中的调度方法有三种 :NAT(Network Address Translation网络地址转换)、TUN(tunnel 隧道)、DR(direct route 直接路由)
请求由LVS接受,由真实提供服务的服务器(RealServer, RS)直接返回给用户,返回的时候不经过LVS。
DR模式下需要LVS服务器和RS绑定同一个VIP, 一个请求过来时,LVS只需要将网络帧的MAC地址修改为某一台RS的MAC,该包就会被转发到相应的RS处理,注意此时的源IP和目标IP都没变,RS收到LVS转发来的包,发现MAC是自己的,发现IP也是自己的,于是这个包被合法地接受,而当RS返回响应时,只要直接向源IP(即用户的IP)返回即可,不再经过LVS。
DR模式下,lvs接收请求输入,将请求转发给RS,由RS输出响应给用户,性能非常高。
它的不足之处是要求负载均衡器与RS在一个物理段上。
NAT(Network Address Translation)是一种外网和内网地址映射的技术。NAT模式下,LVS需要作为RS的网关,当网络包到达LVS时,LVS做目标地址转换(DNAT),将目标IP改为RS的IP。RS接收到包以后,处理完,返回响应时,源IP是RS IP,目标IP是客户端的IP,这时RS的包通过网关(LVS)中转,LVS会做源地址转换(SNAT),将包的源地址改为VIP,对于客户端只知道是LVS直接返回给它的。
NAT模式请求和响应都需要经过lvs,性能没有DR模式好。
TUN模式是通过ip隧道技术减轻lvs调度服务器的压力,许多Internet服务(例如WEB服务器)的请求包很短小,而应答包通常很大,负载均衡器只负责将请求包分发给物理服务器,而物理服务器将应答包直接发给用户。所以,负载均衡器能处理很巨大的请求量。相比NAT性能要高的多,比DR模式的优点是不限制负载均衡器与RS在一个物理段上。但是它的不足需要所有的服务器(lvs、RS)支持”IP Tunneling”(IP Encapsulation)协议。
vip:192.168.101.100
lvs-director:192.168.101.8
nginx1:192.168.101.3
nginx2:192.168.101.4
在192.168.101.8上安装lvs
centos6.5自带lvs,检查linux内核是否集成lvs模块:
1 | modprobe -l | grep ipvs |
1 | yum install -y gcc gcc-c++ makepcre pcre-devel kernel-devel openssl-devel libnl-devel popt* |
将ipvsadm-1.26.tar.gz拷贝至/usr/local/下
1 | cd /usr/local |
校验是否安装成功:
在192.168.101.3和192.168.101.4上安装nginx。
nginx配置文件
创建nginx-lvs.conf,http内容如下:
1 | http { |
1 | ifconfig eth0:0 192.168.101.100 broadcast 192.168.101.100 netmask 255.255.255.255 up |
此处在eth0设备上绑定了一个虚拟设备eth0:0,同时设置了一个虚拟IP是192.168.101.100,然后指定广播地址也为192.168.101.100,需要特别注意的是,虚拟ip地址的广播地址是它本身,子网掩码是255.255.255.255。
1 | route add -host 192.168.101.100 dev eth0:0 |
1 | echo "1" >/proc/sys/net/ipv4/ip_forward |
参数值为1时启用ip转发,为0时禁止ip转发。
1 | ipvsadm --clear |
1 | ipvsadm -A -t 192.168.101.100:80 -s rr |
-s rr表示采用轮询策略。
:80表示负载转发的端口是80
1 | ipvsadm -a -t 192.168.101.100:80 -r 192.168.101.3:80 -g |
在新加虚拟IP记录中添加两条新的Real Server记录,-g表示指定LVS 的工作模式为直接路由模式。
lvs进行负载转发需要保证lvs负载的端口要和nginx服务的端口的一致,这里都为80。
1 | ipvsadm |
在lvs的DR和TUn模式下,用户的访问请求到达真实服务器后,是直接返回给用户的,而不再经过前端的Director Server,因此,就需要在每个Real server节点上增加虚拟的VIP地址,这样数据才能直接返回给用户。
1 | ifconfig lo:0 192.168.101.100 broadcast 192.168.101.100 netmask 255.255.255.255 up |
arp_announce :定义不同级别:当ARP请求通过某个端口进来是否利用这个接口来回应。
0 -利用本地的任何地址,不管配置在哪个接口上去响应ARP请求;
1 - 避免使用另外一个接口上的mac地址去响应ARP请求;
2 - 尽可能使用能够匹配到ARP请求的最佳地址。
arp_ignore:当ARP请求发过来后发现自己正是请求的地址是否响应;
0 - 利用本地的任何地址,不管配置在哪个接口上去响应ARP请求;
1 - 哪个接口上接受ARP请求,就从哪个端口上回应。
1 | echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore |
由于lvs设置为rr轮询策略,当访问虚IP http://192.168.101.100,每次刷新请求通过lvs负载到不同的服务器。
1、测试时需要在nginx的http中设置keepalive_timeout 0; 取消使用http持久连接模式,保证每次客户端发起请求都需要向服务端建立连接,这样做是为了每次刷新页面都要经过lvs负载转发。
2、lvs进行负载转发需要保证lvs负载的端口要和nginx服务的端口的一致,这里都为80。
keepalive_timeout说明:
在nginx中keepalive_timeout的默认值是75秒,默认使用http持久连接模式,可使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,可避免建立或重新建立连接。生产环境建议keepalive_timeout不要设置为0。
修改192.168.101.3和192.168.101.4下html目录中index.html的内容使之个性化。
第一次请求:http://192.168.101.100
刷新,相当于第二次请求:
依次交替测试,发现每次请求被负载到不同的nginx上。
任意停止掉一个nginx,请求http://192.168.101.100继续可以浏览,由于lvs采用轮询策略如果其中一个nginx请求不可到达则去请求另外的nginx。
为了方便配置启动lvs将上边Director Server和Real Server的配置过程封装在shell脚本中。
在/etc/init.d下创建lvsdr,内容如下:
1 |
|
1 | #修改脚本权限: |
在/etc/init.d下创建lvsdr,内容如下:
1 |
|
1 | #修改脚本权限: |
lvs采用DR模式基本上没有性能瓶颈,用户请求输入至lvs经过负载转发到后台服务上,通过后台服务输出响应给用户。nginx的负载性能远没有lvs好,lvs四层+nginx七层负载的好处是最前端是lvs接收请求进行负载转发,由多个nginx共同完成七层负载,这样nginx的负载性能就可以线性扩展。
vip:192.168.101.100
lvs-director:192.168.101.8
nginx1:192.168.101.3 安装nginx
nginx2:192.168.101.4 安装nginx
tomcat1:192.168.101.5 安装tomcat
tomcat2:192.168.101.6 安装tomcat
vip:192.168.101.100
lvs-director:192.168.101.8
参考lvs四层负载DR模式进行配置
nginx1:192.168.101.3 安装nginx
nginx2:192.168.101.4 安装nginx
参考lvs四层负载DR模式进行配置,需要修改nginx的配置文件使每个nginx对两个tomcat进行负载,如下:
1 | http { |
请求http://192.168.101.100,lvs负载到不同的nginx上,如果停止任意一台nginx或停止任意一台tomcat不影响访问。
lvs作为负载均衡器,所有请求都先到达lvs,可见lvs处于非常重要的位置,如果lvs服务器宕机后端web服务将无法提供服务,影响严重。
为了屏蔽负载均衡服务器的宕机,需要建立一个备份机。主服务器和备份机上都运行高可用(High Availability)监控程序,通过传送诸如“I am alive”这样的信息来监控对方的运行状况。当备份机不能在一定的时间内收到这样的信息时,它就接管主服务器的服务IP并继续提供负载均衡服务;当备份管理器又从主管理器收到“I am alive”这样的信息时,它就释放服务IP地址,这样的主服务器就开始再次提供负载均衡服务。
keepalived是集群管理中保证集群高可用的一个服务软件,用来防止单点故障。
Keepalived的作用是检测web服务器的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的web服务器从系统中剔除,当web服务器工作正常后Keepalived自动将web服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的web服务器。
keepalived是以VRRP协议为实现基础的,VRRP全称Virtual Router Redundancy Protocol,即虚拟路由冗余协议。
虚拟路由冗余协议,可以认为是实现路由器高可用的协议,即将N台提供相同功能的路由器组成一个路由器组,这个组里面有一个master和多个backup,master上面有一个对外提供服务的vip(该路由器所在局域网内其他机器的默认路由为该vip),master会发组播,当backup收不到VRRP包时就认为master宕掉了,这时就需要根据VRRP的优先级来选举一个backup当master。这样的话就可以保证路由器的高可用了。
keepalived主要有三个模块,分别是core、check和VRRP。core模块为keepalived的核心,负责主进程的启动、维护以及全局配置文件的加载和解析。check负责健康检查,包括常见的各种检查方式。VRRP模块是来实现VRRP协议的。
初始状态
主机宕机
主机恢复
vip:192.168.101.100
lvs-director:192.168.101.8 主lvs
lvs-director:192.168.101.9 备lvs
nginx1:192.168.101.3 安装nginx
nginx2:192.168.101.4 安装nginx
tomcat1:192.168.101.5 安装tomcat
tomcat2:192.168.101.6 安装tomcat
分别在主备lvs上安装keepalived,参考“安装手册”进行安装:
1 | yum install keepalived -y |
修改主lvs下/etc/keepalived/keepalived.conf文件
1 | ! Configuration File for keepalived |
修改备lvs下/etc/keepalived/keepalived.conf文件
配置备lvs时需要注意:需要修改state**为BACKUP , priority比MASTER低,virtual_router_id和master的值一致**
1 | ! Configuration File for keepalived |
注意:使用keepalived就不用手动配置启动lvs,在主、备lvs上启动keepalived即可。
主备lvs(192.168.101.8、192.168.101.9)都启动keepalived。
1 | service keepalived start |
192.168.101.3、192.168.101.4启动nginx和lvs的realserver配置
1 | cd /usr/local/nginx/sbin |
启动lvs的realserver配置:
1 | service lvsdr start |
注意:real server的lvs配置需要使用lvsdr脚本。
略
查看主lvs的eth0设置:
vip绑定在主lvs的eth0上。
查询lvs状态:
查看备lvs的eth0设置:
vip没有绑定在备lvs的eth0上。
访问http://192.168.101.100,可以正常负载。
将主lvs的keepalived停止或将主lvs关机(相当于模拟宕机),查看主lvs的eth0:
eth0没有绑定vip
查看备lvs的eth0:
vip已经漂移到备lvs。
访问http://192.168.101.100,可以正常负载。
将主lvs的keepalived启动。
查看主lvs的eth0:
查看备lvs的eth0:
vip漂移到主lvs。
查看备lvs的eth0:
eth0没有绑定vip
访问http://192.168.101.100,可以正常负载。
上边主备方案是当前只有一台lvs工作,这造成资源浪费,可以采用双主结构,让两台lvs当前都进行工作,采用dns轮询方式,当用户访问域名通过dns轮询每台lvs,双主结构需要两个vip,这两个vip要绑定域名。
同样,在每台lvs上安装keepalived软件,当keepalived检测到其中一个lvs宕机则将宕机的vip漂移到活动lvs上,当lvs恢复则vip又重新漂移回来。
每台lvs绑定一个vip,共两个vip,DNS设置域名对应这两个vip,通过DNS轮询每次解析到不同的vip上即解析到不同的lvs上。
其中一个主机宕机,每台lvs上安装的keepalived程序会检测到对方宕机,将宕机一方的vip漂移至活动的lvs服务器上,这样DNS轮询全部到一台lvs继续对外提供服务。
当主机恢复又回到初始状态,每个vip绑定在不同的lvs上。
前端使用1到2台lvs作为负载基本可以满足中小型网站的并发要求,当lvs的负载成为瓶颈此时就需要对lvs进行优化、扩展。
OSPF(Open Shortest Path First开放式最短路径优先)是一个内部网关协议(Interior Gateway Protocol,简称IGP),用于在单一自治系统(autonomous system,AS)内决策路由。
LVS(DR)通过ospfd,做lvs集群,实现一个VIP,多台LVS同时工作提供服务,这种方案需要依赖三层交换机设备实现。
用户请求(VIP:42.xx.xx.100)到达三层交换机之后,通过对原地址、端口和目的地址、端口的hash,将链接分配到集群中的某一台LVS上,LVS通过内网(10.101.10.x)向后端转发请求,后端再将数据返回给用户。
LVS-ospf集群模式的最大优势就在于:
1.LVS调度机自由伸缩,横向线性扩展(最大8台,受限于三层设备允许的等价路由数目maximum load-balancing);
2.LVS机器同时工作,不存在备机,提高利用率;
3.做到了真正的高可用,某台LVS机器宕机后,不会影响服务
上面讲的是一组双主结构,可以采用多组双主结构达到横向扩展lvs的目的,此方案需要每台lvs都绑定一个vip(公网ip),DNS设置域名轮询多个vip,如下图:
如果资金允许可以购买硬件设置来完成负载均衡,性能不错的有F5、Array等都可以满足超高并发的要求。
]]>Kubernetes跨主机容器之间的通信组件,目前主流的是flannel和calico,本文对两个组件进行简单介绍和对比。
由CoreOS开发的项目Flannel,可能是最直接和最受欢迎的CNI插件。它是容器编排系统中最成熟的网络结构示例之一,旨在实现更好的容器间和主机间网络。随着CNI概念的兴起,Flannel CNI插件算是早期的入门。
与其他方案相比,Flannel相对容易安装和配置。它被打包为单个二进制文件FlannelD,许多常见的Kubernetes集群部署工具和许多Kubernetes发行版都可以默认安装Flannel。Flannel可以使用Kubernetes集群的现有etcd集群来使用API存储其状态信息,因此不需要专用的数据存储。
Flannel配置第3层IPv4 Overlay网络。它会创建一个大型内部网络,跨越集群中每个节点。在此Overlay网络中,每个节点都有一个子网,用于在内部分配IP地址。在配置Pod时,每个节点上的Docker桥接口都会为每个新容器分配一个地址。同一主机中的Pod可以使用Docker桥接进行通信,而不同主机上的pod会使用flanneld将其流量封装在UDP数据包中,以便路由到适当的目标。
Flannel有几种不同类型的后端可用于封装和路由。默认和推荐的方法是使用VXLAN,因为VXLAN性能更良好并且需要的手动干预更少。
calico包括如下重要组件:Felix,etcd,BGP Client,BGP Route Reflector。下面分别说明一下这些组件。
Felix:主要负责路由配置以及ACLS规则的配置以及下发,它存在在每个node节点上。
etcd:分布式键值存储,主要负责网络元数据一致性,确保Calico网络状态的准确性,可以与kubernetes共用;
BGPClient(BIRD), 主要负责把 Felix写入 kernel的路由信息分发到当前 Calico网络,确保 workload间的通信的有效性;
BGPRoute Reflector(BIRD), 大规模部署时使用,摒弃所有节点互联的mesh模式,通过一个或者多个 BGPRoute Reflector 来完成集中式的路由分发;
如下图所示,描述了从源容器经过源宿主机,经过数据中心的路由,然后到达目的宿主机最后分配到目的容器的过程。
从上述的原理可以看出,flannel在进行路由转发的基础上进行了封包解包的操作,这样浪费了CPU的计算资源。下图是从网上找到的各个开源网络组件的性能对比。可以看出无论是带宽还是网络延迟,calico和主机的性能是差不多的。
Jumpserver堡垒机的作用和好处这里就不再赘述,本文教你快速用docker容器安装jumperserver,让你快速体验。本教程是在单机上操作,处于以后扩展的需求,强烈建议在多台服务器上搭建。
1 | cd /opt |
1 | vim config-example.txt |
1 | ./jmsctl.sh install |