一、日志收集案例-容器内置日志收集
容器内置日志收集架构
应用程序以tomcat为例,实现方法如下:
1.1 构建镜像
1.1.1 准备基础tomcat镜像
tomcat基础镜像构建参考: 5.1.2 tomcat基础镜像制作[1]
生成镜像为:tomcat-base:v8.5.43
1.1.2 构建业务镜像
1.1.2.1 准备文件
[root@k8s-deploy ~]#ll
-rw-r--r-- 1 root root 536 Feb 8 13:53 Dockerfile
-rw-r--r-- 1 root root 23611 Feb 8 13:53 catalina.sh
-rw-r--r-- 1 root root 32600353 Feb 8 13:53 filebeat-7.12.1-x86_64.rpm
-rw-r--r-- 1 root root 690 Feb 8 13:53 filebeat.yml
-rw-r--r-- 1 root root 31 Feb 14 01:21 index.html
-rw-r--r-- 1 root root 372 Feb 8 13:53 run_tomcat.sh
-rw-r--r-- 1 root root 6462 Feb 8 13:53 server.xml
- Dockerfile
FROM harbor.chu.net/baseimages/tomcat-base:v8.5.43
# 因基础镜像里已包含filebeat安装包,无需再次添加安装包
# ADD filebeat-7.12.1-x86_64.rpm /tmp/
RUN cd /tmp && yum localinstall -y filebeat-7.12.1-x86_64.rpm
ADD filebeat.yml /etc/filebeat/filebeat.yml
ADD catalina.sh /apps/tomcat/bin/catalina.sh
ADD server.xml /apps/tomcat/conf/server.xml
ADD index.html /data/tomcat/webapps/myapp/index.html
ADD run_tomcat.sh /apps/tomcat/bin/run_tomcat.sh
RUN chown -R tomcat:tomcat /data/ /apps/ && chmod a+x /apps/tomcat/bin/*
EXPOSE 8080 8443
CMD ["/apps/tomcat/bin/run_tomcat.sh"]
- filebeat安装包
官网下载地址:https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.12.1-x86_64.rpm
- filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /apps/tomcat/logs/catalina.out
fields:
type: filebeat-tomcat-catalina
- type: log
enabled: true
paths:
- /apps/tomcat/logs/localhost_access_log.*.txt
fields:
type: filebeat-tomcat-accesslog
filebeat.config.modules:
path: ${path.config}/modules.d/*.yml
reload.enabled: false
setup.template.settings:
index.number_of_shards: 1
setup.kibana:
output.kafka:
hosts: ["10.0.0.56:9092","10.0.0.57:9092","10.0.0.58:9092"] # kafka集群
required_acks: 1
topic: "filebeat-web-app1"
compression: gzip
max_message_bytes: 1000000
- catalina.sh
根据需要自行设置tomcat启动脚本
- index.html
<h1>tomcat app1 test page</h1>
- server.xml
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="/data/tomcat/webapps" unpackWARs="false" autoDeploy="false">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
- run_tomcat.sh容器运行脚本
#!/bin/bash
/usr/share/filebeat/bin/filebeat -e -c /etc/filebeat/filebeat.yml -path.home /usr/share/filebeat -path.config /etc/filebeat -path.data /var/lib/filebeat -path.logs /var/log/filebeat &
su - tomcat -c "/apps/tomcat/bin/catalina.sh start"
tail -f /etc/hosts
1.1.2.2 构建镜像
docker build -t harbor.chu.net/web/tomcat-app1:v1-filebeat .
# 上传本地harbor仓库
docker push harbor.chu.net/web/tomcat-app1:v1-filebeat
1.2 运行web服务
1.2.1 RBAC鉴权
授予web命名空间内default服务账户具有对集群内namespace、pod、node等资源进行get、watch、list的权限。
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: filebeat-serviceaccount-clusterrole
labels:
k8s-app: filebeat-serviceaccount-clusterrole
rules:
- apiGroups: [""] # "" indicates the core API group
resources:
- namespaces
- pods
- nodes
verbs:
- get
- watch
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: filebeat-serviceaccount-clusterrolebinding
subjects:
- kind: ServiceAccount
name: default
namespace: web
roleRef:
kind: ClusterRole
name: filebeat-serviceaccount-clusterrole
apiGroup: rbac.authorization.k8s.io
查看
[root@k8s-deploy ~]#kubectl get clusterroles filebeat-serviceaccount-clusterrole
NAME CREATED AT
filebeat-serviceaccount-clusterrole 2023-02-14T05:23:28Z
[root@k8s-deploy ~]#kubectl get clusterrolebinding filebeat-serviceaccount-clusterrolebinding
NAME ROLE AGE
filebeat-serviceaccount-clusterrolebinding ClusterRole/filebeat-serviceaccount-clusterrole 3m24s
1.2.2 部署服务
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
app: web-tomcat-app1-filebeat-deployment-label
name: web-tomcat-app1-filebeat-deployment
namespace: web
spec:
replicas: 3
selector:
matchLabels:
app: web-tomcat-app1-filebeat-selector
template:
metadata:
labels:
app: web-tomcat-app1-filebeat-selector
spec:
containers:
- name: web-tomcat-app1-filebeat-container
image: harbor.chu.net/web/tomcat-app1:v1-filebeat
#imagePullPolicy: IfNotPresent
imagePullPolicy: Always
ports:
- containerPort: 8080
protocol: TCP
name: http
env:
- name: "password"
value: "123456"
- name: "age"
value: "18"
resources:
limits:
cpu: 1
memory: "512Mi"
requests:
cpu: 500m
memory: "512Mi"
查看
[root@k8s-deploy ~]#kubectl get pod -n web
NAME READY STATUS RESTARTS AGE
web-tomcat-app1-filebeat-deployment-84cb6757d7-2wck7 1/1 Running 0 8s
web-tomcat-app1-filebeat-deployment-84cb6757d7-cb2mn 1/1 Running 0 8s
web-tomcat-app1-filebeat-deployment-84cb6757d7-l42r7 1/1 Running 0 8s
1.2.3 创建service
---
kind: Service
apiVersion: v1
metadata:
labels:
app: web-tomcat-app1-filebeat-service-label
name: web-tomcat-app1-filebeat-service
namespace: web
spec:
type: NodePort
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
nodePort: 30092
selector:
app: web-tomcat-app1-filebeat-selector
查看
[root@k8s-deploy ~#kubectl get svc -n web
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web-tomcat-app1-filebeat-service NodePort 10.100.159.7 <none> 80:30092/TCP 5s
1.2.4 浏览器访问
1.3 验证kafka日志数据
1.4 配置logstash服务器
/etc/logstash/conf.d/logstash-filebeat-process-kafka-to-es.conf
input {
kafka {
bootstrap_servers => "10.0.0.56:9092,10.0.0.57:9092,10.0.0.58:9092"
topics => ["filebeat-web-app1"]
codec => "json"
}
}
output {
if [fields][type] == "filebeat-tomcat-catalina" {
elasticsearch {
hosts => ["10.0.0.51:9200","10.0.0.52:9200","10.0.0.53:9200"]
index => "filebeat-tomcat-catalina-%{+YYYY.MM.dd}"
}}
if [fields][type] == "filebeat-tomcat-accesslog" {
elasticsearch {
hosts => ["10.0.0.51:9200","10.0.0.52:9200","10.0.0.53:9200"]
index => "filebeat-tomcat-accesslog-%{+YYYY.MM.dd}"
}}
}
重启服务
systemctl restart logstash.service
1.5 验证Elasticsearch数据
1.6 Kibana展示日志数据
- 创建索引
- 展示日志
Catalina日志
access日志
二、overlay与underlay通信总结
原文:https://info.support.huawei.com/info-finder/encyclopedia/zh/Overlay%E7%BD%91%E7%BB%9C.html
Overlay网络是通过网络虚拟化技术,在同一张Underlay网络上构建出的一张或者多张虚拟的逻辑网络。不同的Overlay网络虽然共享Underlay网络中的设备和线路,但是Overlay网络中的业务与Underlay网络中的物理组网和互联技术相互解耦。Overlay网络的多实例化,既可以服务于同一租户的不同业务(如多个部门),也可以服务于不同租户,是SD-WAN以及数据中心等解决方案使用的核心组网技术。
Overlay网络和Underlay网络是一组相对概念,Overlay网络是建立在Underlay网络上的逻辑网络。而为什么需要建立Overlay网络,就要从底层的Underlay网络的概念以及局限讲起。
2.1 Underlay网络
Underlay网络正如其名,是Overlay网络的底层物理基础。
如下图所示,Underlay网络可以是由多个类型设备互联而成的物理网络,负责网络之间的数据包传输。
典型的Underlay网络
在Underlay网络中,互联的设备可以是各类型交换机、路由器、负载均衡设备、防火墙等,但网络的各个设备之间必须通过路由协议来确保之间IP的连通性。
Underlay网络可以是二层也可以是三层网络。其中二层网络通常应用于以太网,通过VLAN进行划分。三层网络的典型应用就是互联网,其在同一个自治域使用OSPF、IS-IS等协议进行路由控制,在各个自治域之间则采用BGP等协议进行路由传递与互联。随着技术的进步,也出现了使用MPLS这种介于二三层的WAN技术搭建的Underlay网络。
然而传统的网络设备对数据包的转发都基于硬件,其构建而成的Underlay网络也产生了如下的问题:
- 由于硬件根据目的IP地址进行数据包的转发,所以传输的路径依赖十分严重。
- 新增或变更业务需要对现有底层网络连接进行修改,重新配置耗时严重。
- 互联网不能保证私密通信的安全要求。
- 网络切片和网络分段实现复杂,无法做到网络资源的按需分配。
- 多路径转发繁琐,无法融合多个底层网络来实现负载均衡。
2.2 Overlay网络
为了摆脱Underlay网络的种种限制,现在多采用网络虚拟化技术在Underlay网络之上创建虚拟的Overlay网络。
Overlay网络拓扑
在Overlay网络中,设备之间可以通过逻辑链路,按照需求完成互联形成Overlay拓扑。
相互连接的Overlay设备之间建立隧道,数据包准备传输出去时,设备为数据包添加新的IP头部和隧道头部,并且被屏蔽掉内层的IP头部,数据包根据新的IP头部进行转发。当数据包传递到另一个设备后,外部的IP报头和隧道头将被丢弃,得到原始的数据包,在这个过程中Overlay网络并不感知Underlay网络。
Overlay网络有着各种网络协议和标准,包括VXLAN、NVGRE、SST、GRE、NVO3、EVPN等。
随着SDN技术的引入,加入了控制器的Overlay网络,有着如下的优点:
- 流量传输不依赖特定线路。Overlay网络使用隧道技术,可以灵活选择不同的底层链路,使用多种方式保证流量的稳定传输。
- Overlay网络可以按照需求建立不同的虚拟拓扑组网,无需对底层网络作出修改。
- 通过加密手段可以解决保护私密流量在互联网上的通信。
- 支持网络切片与网络分段。将不同的业务分割开来,可以实现网络资源的最优分配。
- 支持多路径转发。在Overlay网络中,流量从源传输到目的可通过多条路径,从而实现负载分担,最大化利用线路的带宽。
2.3 Overlay网络示例
Overlay网络在SD-WAN、数据中心两大解决方案中被广泛应用,由于其底层Underlay网络的架构也不尽相同,使得Overlay网络的拓扑存在不同的形式。
2.3.1 数据中心的Overlay网络
随着数据中心架构演进,现在数据中心多采用Spine-Leaf架构构建Underlay网络,通过VXLAN技术构建互联的Overlay网络,业务报文运行在VXLAN Overlay网络上,与物理承载网络解耦。
数据中心的Overlay网络
- Leaf与Spine全连接,等价多路径提高了网络的可用性。
- Leaf节点作为网络功能接入节点,提供Underlay网络中各种网络设备接入VXLAN网络功能,同时也作为Overlay网络的边缘设备承担VTEP(VXLAN Tunnel EndPoint)的角色。
- Spine节点即骨干节点,是数据中心网络的核心节点,提供高速IP转发功能,通过高速接口连接各个功能Leaf节点。
2.3.2 SD-WAN中的Overlay网络
SD-WAN的Underlay网络基于广域网,通过混合链路的方式达成总部站点、分支站点、云网站点之间的互联。通过搭建Overlay网络的逻辑拓扑,完成不同场景下的互联需求。
SD-WAN的Overlay网络(以Hub-Spoke为例)
SD-WAN的网络主要由CPE设备构成,其中CPE又分为Edge和GW两种类型。
- Edge:是SD-WAN站点的出口设备。
- GW:是联接SD-WAN站点和其他网络(如传统VPN)的网关设备。
根据企业网络规模、中心站点数量、站点间互访需求可以搭建出多个不同类型的Overlay网络。
- Hub-spoke:适用于企业拥有1~2个数据中心,业务主要在总部和数据中心,分支通过WAN集中访问部署在总部或者数据中心的业务。分支之间无或者有少量的互访需求,分支之间通过总部或者数据中心绕行。
- Full-mesh:适用于站点规模不多的小企业,或者在分支之间需要进行协同工作的大企业中部署。大企业的协同业务,如VoIP和视频会议等高价值的应用,对于网络丢包、时延和抖动等网络性能具有很高的要求,因此这类业务更适用于分支站点之间直接进行互访。
- 分层组网:适应于网络站点规模庞大或者站点分散分布在多个国家或地区的大型跨国企业和大企业,网络结构清晰,网络可扩展性好。
- 多Hub组网:适用于有多个数据中心,每个数据中心均部署业务服务器为分支提供业务服务的企业。
- POP组网:当运营商/MSP面向企业提供SD-WAN网络接入服务时,企业一时间不能将全部站点改造为SD-WAN站点,网络中同时存在传统分支站点和SD-WAN站点这两类站点,且这些站点间有流量互通的诉求。一套IWG(Interworking Gateway,互通网关)组网能同时为多个企业租户提供SD-WAN站点和已有的传统MPLS VPN网络的站点连通服务。
2.4 网络比较
Overlay网络和Underlay网络的区别如下所示
对比项 | Underlay网络 | Overlay网络 |
---|---|---|
数据传输 | 通过网络设备例如路由器、交换机进行传输 | 沿着节点间的虚拟链路进行传输 |
包封装和开销 | 发生在网络的二层和三层 | 需要跨源和目的封装数据包,产生额外的开销 |
报文控制 | 面向硬件 | 面向软件 |
部署时间 | 上线新服务涉及大量配置,耗时多 | 只需更改虚拟网络中的拓扑结构,可快速部署 |
多路径转发 | 因可扩展低,需要使用多路径转发,会产生更多的开销和网络复杂度 | 支持虚拟网络内的多路径转发 |
扩展性 | 底层网络一旦搭建好,新增设备较为困难,可扩展性差 | 扩展性强,如VLAN最多支持4096个标识符,VXLAN提供多达1600万个标识符 |
协议 | 以太网交换、VLAN、路由协议(OSPF、IS-IS、BGP等) | VXLAN、NVGRE、SST、GRE、NVO3、EVPN |
多租户管理 | 需要使用基于NAT或者VRF的隔离,在大型网络中是巨大挑战 | 能够管理多个租户之间的重叠IP地址 |
三、网络组件flannel总结
https://github.com/flannel-io/flannel
官方文档:https://docs.openshift.com/container-platform/3.4/architecture/additional_concepts/flannel.html
参考:https://blog.yingchi.io/posts/2020/8/k8s-flannel.html
参考:https://segmentfault.com/a/1190000022365692?utm_source=sf-similar-article
3.1 CNI
在理解 CNI 机制以及 Flannel 等具体实现方案之前,首先要理解问题的背景,这里从 kubernetes 网络模型开始回顾。
从底层网络来看,kubernetes 的网络通信可以分为三层去看待:
- Pod 内部容器通信;
- 同主机 Pod 间容器通信;
- 跨主机 Pod 间容器通信;
对于前两点,其网络通信原理其实不难理解。
- 对于 Pod 内部容器通信,由于 Pod 内部的容器处于同一个 Network Namespace 下(通过 Pause 容器实现),即共享同一网卡,因此可以直接通信。
- 对于同主机 Pod 间容器通信,Docker 会在每个主机上创建一个 Docker0 网桥,主机上面所有 Pod 内的容器全部接到网桥上,因此可以互通。
而对于第三点,跨主机 Pod 间容器通信,Docker 并没有给出很好的解决方案,而对于 Kubernetes 而言,跨主机 Pod 间容器通信是非常重要的一项工作,但是有意思的是,Kubernetes 并没有自己去解决这个问题,而是专注于容器编排问题,对于跨主机的容器通信则是交给了第三方实现,这就是 CNI 机制。
CNI,它的全称是 Container Network Interface,即容器网络的 API 接口。kubernetes 网络的发展方向是希望通过插件的方式来集成不同的网络方案, CNI 就是这一努力的结果。CNI 只专注解决容器网络连接和容器销毁时的资源释放,提供一套框架,所以 CNI 可以支持大量不同的网络模式,并且容易实现。平时比较常用的 CNI 实现有 Flannel、Calico、Weave 等。
CNI 插件通常有三种实现模式:
- Overlay:靠隧道打通,不依赖底层网络;
- 路由:靠路由打通,部分依赖底层网络;
- Underlay:靠底层网络打通,强依赖底层网络;
在选择 CNI 插件时是要根据自己实际的需求进行考量,比如考虑 NetworkPolicy 是否要支持 Pod 网络间的访问策略,可以考虑 Calico、Weave;Pod 的创建速度,Overlay 或路由模式的 CNI 插件在创建 Pod 时比较快,Underlay 较慢;网络性能,Overlay 性能相对较差,Underlay 及路由模式相对较快。
3.2 Flannel的工作原理
Flannel 实质上就是一种 Overlay 网络,也就是将 TCP 数据包装在另一种网络包里面进行路由转发和通信,目前已经支持 UDP、VxLAN、AWS VPC 和 GCE 路由等数据转发方式。
Flannel会在每一个宿主机上运行名为 flanneld 代理,其负责为宿主机预先分配一个子网,并为 Pod 分配IP地址。Flannel 使用Kubernetes 或 etcd 来存储网络配置、分配的子网和主机公共IP等信息。数据包则通过 VXLAN、UDP 或 host-gw 这些类型的后端机制进行转发。
Flannel 规定宿主机下各个Pod属于同一个子网,不同宿主机下的Pod属于不同的子网。
3.3 Flannel 工作模式
支持3种实现:UDP、VxLAN、host-gw
- UDP 模式:使用设备 flannel.0 进行封包解包,不是内核原生支持,频繁地内核态用户态切换,性能非常差;
- VxLAN 模式:使用 flannel.1 进行封包解包,内核原生支持,性能较强;
- host-gw 模式:无需 flannel.1 这样的中间设备,直接宿主机当作子网的下一跳地址,性能最强;
host-gw的性能损失大约在10%左右,而其他所有基于VxLAN“隧道”机制的网络方案,性能损失在20%~30%左右。
3.3.1 UDP 模式
官方已经不推荐使用 UDP 模式,性能相对较差。
UDP 模式的核心就是通过 TUN 设备 flannel0 实现。TUN设备是工作在三层的虚拟网络设备,功能是:在操作系统内核和用户应用程序之间传递IP包。 相比两台宿主机直接通信,多出了 flanneld 的处理过程,这个过程,使用了 flannel0 这个TUN设备,仅在发出 IP包的过程中经过多次用户态到内核态的数据拷贝(linux的上下文切换代价比较大),所以性能非常差,原理如下:
以flannel0为例,操作系统将一个IP包发给flannel0,flannel0把IP包发给创建这个设备的应用程序:flannel进程(内核态->用户态) 相反,flannel进程向flannel0发送一个IP包,IP包会出现在宿主机的网络栈中,然后根据宿主机的路由表进行下一步处理(用户态->内核态) 当IP包从容器经过docker0出现在宿主机,又根据路由表进入flannel0设备后,宿主机上的flanneld进程就会收到这个IP包
flannel管理的容器网络里,一台宿主机上的所有容器,都属于该宿主机被分配的“子网”,子网与宿主机的对应关系,存在Etcd中(例如Node1的子网是100.96.1.0/24,container-1的IP地址是100.96.1.2)
当flanneld进程处理flannel0传入的IP包时,就可以根据目的IP地址(如100.96.2.3),匹配到对应的子网(比如100.96.2.0/24),从Etcd中找到这个子网对应的宿主机的IP地址(10.168.0.3)
然后 flanneld 在收到container-1给container-2的包后,把这个包直接封装在UDP包里,发送给Node2(UDP包的源地址,就是Node1,目的地址是Node2)
每台宿主机的flanneld都监听着8285端口,所以flanneld只要把UDP发给Node2的8285端口就行了。然后Node2的flanneld再把IP包发送给它所管理的TUN设备flannel0,flannel0设备再发给docker0
3.3.2 VxLAN模式
VxLAN,即Virtual Extensible LAN(虚拟可扩展局域网),是Linux本身支持的一网种网络虚拟化技术。VxLAN可以完全在内核态实现封装和解封装工作,从而通过“隧道”机制,构建出 Overlay 网络(Overlay Network)
VxLAN的设计思想是: 在现有的三层网络之上,“覆盖”一层虚拟的、由内核VxLAN模块负责维护的二层网络,使得连接在这个VxLAN二层网络上的“主机”(虚拟机或容器都可以),可以像在同一个局域网(LAN)里那样自由通信。 为了能够在二层网络上打通“隧道”,VxLAN会在宿主机上设置一个特殊的网络设备作为“隧道”的两端,叫VTEP:VxLAN Tunnel End Point(虚拟隧道端点) 原理如下:
-
flannel.1设备,就是VxLAN的VTEP(既有IP地址,也有MAC地址),与UDP模式类似,当container-1发出请求后,请求container-2地址10.1.16.3的IP包,会先出现在docker网桥,再路由到本机的flannel.1设备进行处理(进站),为了能够将“原始IP包”封装并发送到正常的主机,VxLAN需要找到隧道的出口:宿主机的VTEP设备,这个设备信息,由宿主机的flanneld进程维护
-
VTEP设备之间通过二层数据帧进行通信 源VTEP设备收到原始IP包后,在上面加上一个目的MAC地址,封装成一个内部数据帧,发送给目的VTEP设备(获取 MAC地址需要通过三层IP地址查询,这是ARP表的功能)
-
封装过程只是加了一个二层头,不会改变“原始IP包”的内容 这些VTEP设备的MAC地址,对宿主机网络来说没什么实际意义,称为内部数据帧,并不能在宿主机的二层网络传输,Linux内核还需要把它进一步封装成为宿主机的一个普通的数据帧,好让它带着“内部数据帧”通过宿主机的eth0进行传输
-
Linux会在内部数据帧前面,加上一个VxLAN头,VxLAN头里有一个重要的标志叫VNI,它是VTEP识别某个数据帧是不是应该归自己处理的重要标识。 在Flannel中,VNI的默认值是1,这也是为什么宿主机的VTEP设备都叫flannel.1的原因
-
一个flannel.1设备只知道另一端flannel.1设备的MAC地址,却不知道对应的宿主机地址是什么。 在linux内核里面,网络设备进行转发的依据,来自FDB的转发数据库,这个flannel.1网桥对应的FDB信息,是由flanneld进程维护的
-
linux内核再在IP包前面加上二层数据桢头,把Node2的MAC地址填进去。这个MAC地址本身,是Node1的ARP表要学习的,需 Flannel维护,这时候Linux封装的“外部数据桢”的格式如下
- 然后Node1的flannel.1设备就可以把这个数据桢从eth0发出去,再经过宿主机网络来到Node2的eth0 Node2的内核网络栈会发现这个数据桢有VxLAN Header,并且VNI为1,Linux内核会对它进行拆包,拿到内部数据桢,根据VNI的值,所它交给Node2的flannel.1设备
3.3.3 host-gw模式
Flannel 第三种协议叫 host-gw (host gateway),这是一种纯三层网络的方案,性能最高,即 Node 节点把自己的网络接口当做 pod 的网关使用,从而使不同节点上的 node 进行通信,这个性能比 VxLAN 高,因为它没有额外开销。不过他有个缺点, 就是各 node 节点必须在同一个网段中 。
howt-gw 模式的工作原理,就是将每个Flannel子网的下一跳,设置成了该子网对应的宿主机的 IP 地址,也就是说,宿主机(host)充当了这条容器通信路径的“网关”(Gateway),这正是 host-gw 的含义。
所有的子网和主机的信息,都保存在 Etcd 中,flanneld 只需要 watch 这些数据的变化 ,实时更新路由表就行了。 核心是IP包在封装成帧的时候,使用路由表的“下一跳”设置上的MAC地址,这样可以经过二层网络到达目的宿主机。
另外,如果两个 pod 所在节点在同一个网段中 ,可以让 VxLAN 也支持 host-gw 的功能, 即直接通过物理网卡的网关路由转发,而不用隧道 flannel 叠加,从而提高了 VxLAN 的性能,这种 flannel 的功能叫 directrouting。
3.4 Flannel 通信过程描述
3.4.1 UDP 模式
UDP模式跨主机容器间通信过程如下图所示:
上图是 Flannel 在 UDP 模式下一个数据包经过封包、传输以及拆包的示意图,从这个图中可以看出两台机器的 docker0 分别处于不同的段:10.1.20.1/24 和 10.1.15.1/24 ,如果从 Web App Frontend1 pod(10.1.15.2)去连接另一台主机上的 Backend Service2 pod(10.1.20.3),网络包从宿主机 192.168.0.100 发往 192.168.0.200,内层容器的数据包被封装到宿主机的 UDP 里面,并且在外层包装了宿主机的 IP 和 mac 地址。这就是一个经典的 overlay 网络,因为容器的 IP 是一个内部 IP,无法从跨宿主机通信,所以容器的网络互通,需要承载到宿主机的网络之上。
3.4.2 VxLAN 模式
VxLAN 模式在源容器宿主机中的数据传递过程如下:
1)源容器向目标容器发送数据,数据首先发送给 docker0 网桥
在源容器内容查看路由信息:
$ kubectl exec -it -p {Podid} -c {ContainerId} -- ip route
2)docker0 网桥接受到数据后,将其转交给flannel.1虚拟网卡处理
docker0 收到数据包后,docker0 的内核栈处理程序会读取这个数据包的目标地址,根据目标地址将数据包发送给下一个路由节点: 查看源容器所在Node的路由信息:
$ ip route
3)flannel.1 接受到数据后,对数据进行封装,并发给宿主机的eth0
flannel.1收到数据后,flannelid会将数据包封装成二层以太包。 Ethernet Header的信息:
- From: 源容器flannel.1虚拟网卡的MAC地址
- To: 目录容器flannel.1虚拟网卡的MAC地址
4)对在flannel路由节点封装后的数据,进行再封装后,转发给目标容器Node的eth0;
由于目前的数据包只是vxlan tunnel上的数据包,因此还不能在物理网络上进行传输。因此,需要将上述数据包再次进行封装,才能源容器节点传输到目标容器节点,这项工作在由linux内核来完成。 Ethernet Header的信息:
- From: 源容器Node节点网卡的MAC地址
- To: 目录容器Node节点网卡的MAC地址
IP Header的信息:
- From: 源容器Node节点网卡的IP地址
- To: 目录容器Node节点网卡的IP地址
通过此次封装,就可以通过物理网络发送数据包。
在目标容器宿主机中的数据传递过程:
5)目标容器宿主机的eth0接收到数据后,对数据包进行拆封,并转发给flannel.1虚拟网卡;
6)flannel.1 虚拟网卡接受到数据,将数据发送给docker0网桥;
7)最后,数据到达目标容器,完成容器之间的数据通信。
四、网络组件caclico总结
官网:https://docs.tigera.io/calico/3.25/about
参考:https://juejin.cn/post/6896022422865215495
4.1 架构
4.2 Calico主要组件
Calico和hots-gateway模式很类似,都是通过在宿主机增加路由规则实现容器组网,但是Calico并不像Flannel那样通过Etcd和宿主的Flanned维护路由信息,而是使用BGP协议(边界网关协议)在集群内分发路由信息。
Calico主要组件如下:
- Felix
运行在每一台 Host 的 agent 进程,主要负责网络接口管理和监听、路由、ARP 管理、ACL 管理和同步、状态上报等。
- etcd
分布式键值存储,主要负责网络元数据一致性,确保Calico网络状态的准确性,可以与kubernetes共用;
- BGP Client(BIRD)
Calico 为每一台 Host 部署一个 BGP Client,使用 BIRD 实现,BIRD 是一个单独的持续发展的项目,实现了众多动态路由协议比如 BGP、OSPF、RIP 等。在 Calico 的角色是监听 Host 上由 Felix 注入的路由信息,然后通过 BGP 协议广播告诉剩余 Host 节点,从而实现网络互通。
- BGP Route Reflector
在大型网络规模中,如果仅仅使用 BGP client 形成 mesh 全网互联的方案就会导致规模限制,因为所有节点之间俩俩互联,需要 N^2 个连接,为了解决这个规模问题,可以采用 BGP 的 Router Reflector 的方法,使所有 BGP Client 仅与特定 RR 节点互联并做路由同步,从而大大减少连接数。
Felix
Felix会监听ECTD中心的存储,从它获取事件,比如说用户在这台机器上加了一个IP,或者是创建了一个容器等。用户创建pod后,Felix负责将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。同样如果用户制定了隔离策略,Felix同样会将该策略创建到ACL中,以实现隔离。
BIRD
BIRD是一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,你们路由的时候得到这里来。
BGP Route Reflector
在默认配置下每台宿主机的BGPClient需要和集群所有的BGPClient建立连接,进行路由信息交换,随着集群规模的扩大,集群的网络将会面临巨大的压力并且宿主机的路由表也会变的过大。所以在大规模的集群中,通常使用BGP Route Reflector充当BGP客户端连接的中心点,从而避免与互联网中的每个BGP客户端进行通信。Calico使用BGP Route Reflector是为了减少给定一个BGP客户端与集群其他BGP客户端的连接。用户也可以同时部署多个BGP Route Reflector服务实现高可用。Route Reflector仅仅是协助管理BGP网络,并没有工作负载的数据包经过它们。
4.3 Calico工作原理
工作流程
以容器1访问容器2为例,先进入容器1查看它的路由表和网络配置。
root@nginx-deployment-6b474476c4-p82xb:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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
3: eth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether f6:64:05:f3:4b:be brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.123.5/32 brd 172.16.123.5 scope global eth0
valid_lft forever preferred_lft forever
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
可以看到容器1的IP地址为172.16.123.5/32,而MAC地址为ff:ff:ff:ff:ff:ff
,这明显是一个固定的MAC地址,因为Calico不关心二层的MAC地址,只关心三层的IP地址。
当容器1执行ping 172.16.215.3/32时,因为两者不在同一网络内,所以容器1会查看自己的路由表。
root@nginx-deployment-6b474476c4-p82xb:/# ip route
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link
容器的的所有报文都会通过eth0发送到下一跳169.254.1.1,容器需要知道下一跳的MAC地址,那ARP请求发送到哪里那。eth0是veth pair 的一端,那它的另一端在哪那。我们可以通过ethtool -S eth0
列出对端网卡的index。
root@nginx-deployment-6b474476c4-p82xb:/# ethtool -S eth0
NIC statistics:
peer_ifindex: 4
rx_queue_0_xdp_packets: 0
rx_queue_0_xdp_bytes: 0
rx_queue_0_xdp_drops: 0
主机上index为4的网卡
root@work2:/home/work2/Desktop# ip addr
4: cali85354e41ec1@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::ecee:eeff:feee:eeee/64 scope link
valid_lft forever preferred_lft forever
这块网卡并没有IP第地址,它接收到查询169.254.1.1的ARP报文会进行"代答",即将自己的MAC地址告诉容器1,后续报文则会转发给主机处理。
root@work2:/home/work2/Desktop# cat /proc/sys/net/ipv4/conf/cali85354e41ec1/proxy_arp
1
主机上的calixx网卡接收到报文后,再根据路由表转发
root@work2:/home/work2/Desktop# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.2.2 0.0.0.0 UG 100 0 0 ens33
169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 ens33
172.16.123.0 0.0.0.0 255.255.255.192 U 0 0 0 *
172.16.123.5 0.0.0.0 255.255.255.255 UH 0 0 0 cali85354e41ec1
172.16.123.6 0.0.0.0 255.255.255.255 UH 0 0 0 cali35e3a5cb80b
172.16.215.0 192.168.2.132 255.255.255.192 UG 0 0 0 tunl0
172.16.243.192 192.168.2.131 255.255.255.192 UG 0 0 0 tunl0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.2.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
接下来数据包会通过tun0设备发送到下一跳,也就是容器2的宿主机192.168.2.132/32。当数据包到达宿主机时,会先经过iptables规则,如果被iptables拦截那么数据包将会被丢弃。然后通过路由表转发,接着从cali34af9914629
网卡,发送到容器2。
# 容器2宿主机路由表
root@work1:/home/work1/Desktop# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.2.2 0.0.0.0 UG 100 0 0 ens33
169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 ens33
172.16.123.0 192.168.2.133 255.255.255.192 UG 0 0 0 tunl0
172.16.215.0 0.0.0.0 255.255.255.192 U 0 0 0 *
172.16.215.3 0.0.0.0 255.255.255.255 UH 0 0 0 cali34af9914629
172.16.243.192 192.168.2.131 255.255.255.192 UG 0 0 0 tunl0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.2.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
4.4 Calico工作模式
4.4.1 IPIP模式
通过上面的分析可以看出calico和host一样要求二层网络是可达的,但是在实际的部署中集群的网络通常划分在不同的网段中,calico如何处理跨网段通信那。在这种情况下就需要为Calico打开IPIP模式。
在IPIP模式下,calico会使用tunl0
设备,tunl0
是一个ip隧道设备,通过上面的路由表发现,IP包会被tunl0
设备接管。tunl0
设备会将IP包封装为宿主机IP包。经过封装后的IP包,如下图所示
这样新的IP包就可以通过宿主机路由器发送了,如下图所示
Cailco在IPIP模式下会进行封包,也会对性能造成一定的影响,但是相对Flannel的VXLAN模式来说要好一些。
4.4.2 BGP模式
边界网关协议(BorderGateway Protocol, BGP)是互联网上一个核心的去中心化的自治路由协议。它通过维护IP路由表或‘前缀’表来实现自治系统(AS)之间的可达性,属于矢量路由协议。BGP不使用传统的内部网关协议(IGP)的指标,而是基于路径、网络策略或规则集来决定路由。因此,它更适合被称为矢量性协议,而不是路由协议,通俗的说就是将接入到机房的多条线路(如电信、联通、移动等)融合为一体,实现多线单IP;
BGP 机房的优点:服务器只需要设置一个IP地址,最佳访问路由是由网络上的骨干路由器根据路由跳数与其它技术指标来确定的,不会占用服务器的任何系统;
官方提供的calico.yaml模板里,默认打开了ip-ip功能,该功能会在node上创建一个设备tunl0
,容器的网络数据会经过该设备被封装一个ip头再转发。这里,calico.yaml中通过修改calico-node的环境变量:CALICO_IPV4POOL_IPIP
来实现ipip功能的开关:默认是Always,表示开启;Off表示关闭ipip。
4.5 Calico优势与不足
calico是一个虚拟网络解决方案,完全利用路由规则实现动态组网,通过BGP协议通告路由。
calico的好处有:
- endpoints组成的网络是单纯的三层网络,报文的流向完全通过路由规则控制,没有overlay等额外开销。
- endpoint可以漂移,并且实现了acl。
calico的缺点有:
- 路由的数目与容器数目相同,非常容易超过路由器、三层交换、甚至node的处理能力,从而限制了整个网络的扩张
- 每个node上会设置大量(海量)的iptables规则、路由,运维、排障难度大
- calico的原理决定了它不可能支持VPC,容器只能从calico设置的网段中获取ip
- calico目前的实现没有流量控制的功能,会出现少数容器抢占node多数带宽的情况
- calico的网络规模受到BGP网络规模的限制
说明:
endpoint: 接入到calico网络中的网卡称为endpoint
AS: 网络自治系统,通过BGP协议与其它AS网络交换路由信息
ibgp: AS内部的BGP Speaker,与同一个AS内部的ibgp、ebgp交换路由信息。
ebgp: AS边界的BGP Speaker,与同一个AS内部的ibgp、其它AS的ebgp交换路由信息。
workloadEndpoint: 虚拟机、容器使用的endpoint
hostEndpoints: 物理机(node)的地址
五、NetworkPolicy Ingress及Egress简介及案例
官网:https://kubernetes.io/zh-cn/docs/concepts/services-networking/network-policies/
参考文档:https://cloud.tencent.com/developer/article/1782925
https://cloud.tencent.com/developer/article/1782926
5.1 Network介绍
NetworkPolicy(网络策略)说明一组 Pod
之间是如何被允许互相通信,以及如何与其它网络 Endpoint 进行通信。 NetworkPolicy
资源使用标签来选择 Pod
,并定义了一些规则,这些规则指明允许什么流量进入到选中的 Pod
上。
Network Policy 的作用对象是 Pod,也可以应用到 Namespace 和集群的 Ingress、Egress 流量。Network Policy 是作用在 L3/4 层的,即限制的是对 IP 地址和端口的访问,如果需要对应用层做访问限制需要使用如 Istio 这类 Service Mesh。
Pod之间能否通信可通过如下三种组合进行确认:
- 其他被允许的Pods(例如:Pod 无法限制对自身的访问)
- 被允许访问的namespace
- IP CIDR(例如:与 Pod 运行所在节点的通信总是被允许的)
Pod 隔离的两种类型:
Pod 有两种隔离: 出口隔离和入口隔离。
默认情况下,一个 Pod 的出口是非隔离的,即所有外向连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes
中包含 “Egress”,则该 Pod 是出口隔离的。当一个 Pod 的出口被隔离时, 唯一允许的来自 Pod 的连接是适用于出口的 Pod 的某个 NetworkPolicy 的 egress
列表所允许的连接。 这些 egress
列表的效果是相加的。
默认情况下,一个 Pod 对入口是非隔离的,即所有入站连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes
中包含 “Ingress”,则该 Pod 被隔离入口。当一个 Pod 的入口被隔离时,唯一允许进入该 Pod 的连接是适用于入口的 Pod 的某个 NetworkPolicy 的 ingress
列表所允许的连接。这些 ingress
列表的效果是相加的。
网络策略是相加的,所以不会产生冲突。如果策略适用于 Pod 某一特定方向的流量, Pod 在对应方向所允许的连接是适用的网络策略所允许的集合。 因此,评估的顺序不影响策略的结果。
要允许从源 Pod 到目的 Pod 的连接,源 Pod 的出口策略和目的 Pod 的入口策略都需要允许连接。 如果任何一方不允许连接,建立连接将会失败。
5.2 NetworkPolicy 配置说明
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes: # 策略类型
- Ingress # 入站
- Egress # 出站
ingress: # 定义入栈内容
- from: # ingres from策略,以下策略是累加的,需同时满足才可访问
- ipBlock: # ip策略
cidr: 172.17.0.0/16
except: # 排除地址
- 172.17.1.0/24
- namespaceSelector: # namespace策略
matchLabels:
project: myproject
- podSelector: # pod策略
matchLabels:
role: frontend
ports: # 端口策略
- protocol: TCP # 端口类型
port: 6379 # 端口号
egress: # 定义出站内容
- to: # egress to策略
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
必需字段:与所有其他的 Kubernetes 配置一样,NetworkPolicy 需要 apiVersion
、 kind
和 metadata
字段。
spec:NetworkPolicy 规约 中包含了在一个namespace
中定义特定网络策略所需的所有信息。
podSelector:每个 NetworkPolicy 都包括一个 podSelector
, 它对该策略所适用的一组 Pod 进行选择。示例中的策略选择带有 role=db
标签的 Pod。 空的 podSelector
选择名字空间下的所有 Pod。
policyTypes:每个 NetworkPolicy 都包含一个 policyTypes
列表,其中包含 Ingress
或 Egress
或两者兼具。policyTypes
字段表示给定的策略是应用于进入所选 Pod 的入站流量还是来自所选 Pod 的出站流量,或两者兼有。 如果 NetworkPolicy 未指定 policyTypes
则默认情况下始终设置 Ingress
; 如果 NetworkPolicy 有任何出口规则的话则设置 Egress
。
ingress:每个 NetworkPolicy 可包含一个 ingress
规则的白名单列表。 每个规则都允许同时匹配 from
和 ports
部分的流量。示例策略中包含一条简单的规则: 它匹配某个特定端口,来自三个来源中的一个,第一个通过 ipBlock
指定,第二个通过 namespaceSelector
指定,第三个通过 podSelector
指定。
egress:每个 NetworkPolicy 可包含一个 egress
规则的白名单列表。 每个规则都允许匹配 to
和 port
部分的流量。该示例策略包含一条规则, 该规则将指定端口上的流量匹配到 10.0.0.0/24
中的任何目的地。
所以,该网络策略示例:
-
隔离
default
名字空间下role=db
的 Pod (如果它们不是已经被隔离的话)。 -
(Ingress 规则)允许以下 Pod 连接到
default
名字空间下的带有role=db
标签的所有 Pod 的 6379 TCP 端口:-
default
名字空间下带有role=frontend
标签的所有 Pod - 带有
project=myproject
标签的所有名字空间中的 Pod - IP 地址范围为 172.17.0.0–172.17.0.255 和 172.17.2.0–172.17.255.255 (即,除了 172.17.1.0/24 之外的所有 172.17.0.0/16)
-
-
(Egress 规则)允许
default
名字空间中任何带有标签role=db
的 Pod 到 CIDR 10.0.0.0/24 下 5978 TCP 端口的连接。
5.3 选择器 to
和 from
可以在 ingress
的 from
部分或 egress
的 to
部分中指定四种选择器:
podSelector:此选择器将在与 NetworkPolicy 相同的名字空间中选择特定的 Pod,应将其允许作为入站流量来源或出站流量目的地。
namespaceSelector:此选择器将选择特定的名字空间,应将所有 Pod 用作其入站流量来源或出站流量目的地。
namespaceSelector 和 podSelector:一个指定 namespaceSelector
和 podSelector
的 to
/from
条目选择特定名字空间中的特定 Pod。 注意使用正确的 YAML 语法;下面的策略:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
role: client
...
此策略在 from
数组中仅包含一个元素,只允许来自标有 role=client
的 Pod 且该 Pod 所在的namespace
中标有 user=alice
的连接。但是这项策略:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
- podSelector:
matchLabels:
role: client
...
它在 from
数组中包含两个元素,允许来自本地namespace
中标有 role=client
的 Pod 的连接,或来自任何名字空间中标有 user=alice
的任何 Pod 的连接。
如有疑问,请使用 kubectl describe
查看 Kubernetes 如何解释该策略。
ipBlock:此选择器将选择特定的 IP CIDR 范围以用作入站流量来源或出站流量目的地。 这些应该是集群外部 IP,因为 Pod IP 存在时间短暂的且随机产生。
集群的入站和出站机制通常需要重写数据包的源 IP 或目标 IP。 在发生这种情况时,不确定在 NetworkPolicy 处理之前还是之后发生, 并且对于网络插件、云提供商、Service
实现等的不同组合,其行为可能会有所不同。
对入站流量而言,这意味着在某些情况下,你可以根据实际的原始源 IP 过滤传入的数据包, 而在其他情况下,NetworkPolicy 所作用的 源IP
则可能是 LoadBalancer
或 Pod 的节点等。
对于出站流量而言,这意味着从 Pod 到被重写为集群外部 IP 的 Service
IP 的连接可能会或可能不会受到基于 ipBlock
的策略的约束。
5.4 默认策略
默认情况下,如果命名空间中不存在任何策略,则所有进出该命名空间中 Pod 的流量都被允许。 以下示例可以更改该命名空间中的默认行为。
5.4.1 默认拒绝所有入站流量
可以通过创建选择所有容器但不允许任何进入这些容器的入站流量的 NetworkPolicy 来为命名空间创建 “default” 隔离策略。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
这确保即使没有被任何其他 NetworkPolicy 选择的 Pod 仍将被隔离以进行入口。 此策略不影响任何 Pod 的出口隔离。
5.4.2 允许所有入站流量
如果想允许一个名字空间中所有 Pod 的所有入站连接,可以创建一个明确允许的策略。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
有了这个策略,任何额外的策略都不会导致到这些 Pod 的任何入站连接被拒绝。 此策略对任何 Pod 的出口隔离没有影响。
5.4.3 默认拒绝所有出站流量
可以通过创建选择所有容器但不允许来自这些容器的任何出站流量的 NetworkPolicy 来为名字空间创建 “default” 隔离策略。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许流出流量。 此策略不会更改任何 Pod 的入站流量隔离行为。
5.4.4 允许所有出站流量
如果要允许来自命名空间中所有 Pod 的所有连接, 则可以创建一个明确允许来自该名字空间中 Pod 的所有出站连接的策略。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
有了这个策略,任何额外的策略都不会导致来自这些 Pod 的任何出站连接被拒绝。 此策略对进入任何 Pod 的隔离没有影响。
5.4.5 默认拒绝所有入站和所有出站流量
可以为命名空间创建“默认”策略,以通过在该名字空间中创建以下 NetworkPolicy 来阻止所有入站和出站流量。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许入站或出站流量。
5.5 Ingress
示例
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: tomcat-access--networkpolicy
namespace: python
spec:
policyTypes: # 策略类型
- Ingress # 入口规则
podSelector:
matchLabels:
app: python-tomcat-app1-selector # 对匹配到的目的Pod应用以下规则
ingress: # 定义入口规则内容
- from: # from规则
- podSelector: # pod选择算符
matchLabels:
#app: python-nginx-selector # 如果存在多个matchLabel条件,如果存在多个matchLabel条件,是and的关系,即要同时满足条件A、条件B、条件X
project: "python"
ports: # 入口规则,如果指定目标端口就是匹配全部端口和协议,协议TCP, UDP, or SCTP
- protocol: TCP
port: 8080 # 允许通过TCP协议访问目标pod的8080端口,但是其它没有允许的端口将全部禁止访问
#port: 80
在python命名空间中,具有app=python-tomcat-app1-selector
标签的pod只允许被来自具有projcet=python
标签的pod,且该pod的tcp8080端口访问。
5.6 Egress
示例
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-access-networkpolicy
namespace: python
spec:
policyTypes:
- Egress
podSelector: #目标pod选择器
matchLabels: #基于label匹配目标pod
app: python-tomcat-app1-selector #匹配python namespace中app的值为python-tomcat-app1-selector的pod,然后基于egress中的指定网络策略进行出口方向的网络限制
egress:
- to:
- ipBlock:
cidr: 10.200.0.0/16 #允许匹配到的pod出口访问的目的CIDR地址范围
- podSelector: #匹配pod,matchLabels: {}为不限制源pod即允许所有pod,写法等同于resources(不加就是不限制)
matchLabels:
app: python-tomcat-app1-selector
- namespaceSelector:
matchLabels:
nsname: linux #指定允许访问的目的namespace
ports:
- protocol: TCP
port: 80 #允许匹配到的pod访问目的端口为80的访问
- protocol: TCP
port: 53 #允许匹配到的pod访问目的端口为53 即DNS的解析
- protocol: UDP
port: 53 #允许匹配到的pod访问目的端口为53 即DNS的解析
六、ingress使用总结
https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/
6.1 Ingress简介
Ingress是kubernetes内置的一个资源对象,是kubernetes集群外部访问集群的一个入口,将外部的请求转发到不同的service上,其实就是相当nginx和haproxy等负载均衡代理服务器。Ingress将集群外部的请求资源转发到集群内部的service,service再将请求转发至pod。
下图是一个将所有流量都发送到同一 Service 的简单 Ingress 示例:
Ingress 可为 Service 提供外部可访问的 URL、负载均衡流量、 SSL/TLS终止,以及基于名称的虚拟主机。 Ingress 控制器 通常负责通过负载均衡器来实现 Ingress,尽管它也可以配置边缘路由器或其他前端来帮助处理流量。
Ingress 不会公开任意端口或协议。 将 HTTP 和 HTTPS 以外的服务公开到 Internet 时,通常使用 Service.Type=NodePort
或Service.Type=LoadBalancer
类型的 Service
6.2 部署ingress-nginx控制器
为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。与作为 kube-controller-manager
可执行文件的一部分运行的其他类型的控制器不同, Ingress 控制器不是随集群自动启动的。
Kubernetes 目前支持和维护 AWS、 GCE 和 Nginx Ingress 控制器。通常使用nginx-ingress
作为控制器,它使用NGINX
服务器作为反向代理来把流量路由给后面的Service
。
https://github.com/kubernetes/ingress-nginx/blob/main/README.md#readme
https://kubernetes.github.io/ingress-nginx/deploy/
- 下载yaml文件
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml
- 下载镜像,并上传至本地harbor仓库
# 查看镜像
[root@k8s-deploy ~]#grep 'image:' deploy.yaml
image: registry.k8s.io/ingress-nginx/controller:v1.5.1@sha256:4ba73c697770664c1e00e9f968de14e08f606ff961c76e5d7033a4a9c593c629
image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20220916-gd32f8c343@sha256:39c5b2e3310dc4264d638ad28d9d1d96c4cbb2b2dcfb52368fe4e3c63f61e10f
image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20220916-gd32f8c343@sha256:39c5b2e3310dc4264d638ad28d9d1d96c4cbb2b2dcfb52368fe4e3c63f61e10f
# 因谷歌镜像无法直接下载,下载个人镜像
docker pull anjia0532/google-containers.ingress-nginx.controller:v1.5.1
docker pull anjia0532/google-containers.ingress-nginx.kube-webhook-certgen:v20220916-gd32f8c343
# 打tag
docker tag anjia0532/google-containers.ingress-nginx.controller:v1.5.1 harbor.chu.net/baseimages/controller:v1.5.1
docker tag anjia0532/google-containers.ingress-nginx.kube-webhook-certgen:v20220916-gd32f8c343 harbor.chu.net/baseimages/kube-webhook-certgen:v20220916-gd32f8c343
# 上传至harbor仓库
docker push harbor.chu.net/baseimages/controller:v1.5.1
docker push harbor.chu.net/baseimages/kube-webhook-certgen:v20220916-gd32f8c343
# 修改yaml文件中镜像地址
[root@k8s-deploy ~]#grep 'image:' deploy.yaml
image: harbor.chu.net/baseimages/controller:v1.5.1
image: harbor.chu.net/baseimages/kube-webhook-certgen:v20220916-gd32f8c343
image: harbor.chu.net/baseimages/kube-webhook-certgen:v20220916-gd32f8c343
# 将ingress-nginx-controller service中type由LoadBalancer修改为NodePort,第381行
type: NodePort
- 创建ingress控制器
[root@k8s-deploy ~]#kubectl apply -f deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
- 验证
[root@k8s-deploy ~]#kubectl get pod -n ingress-nginx
NAMESPACE NAME READY STATUS RESTARTS AGE
ingress-nginx ingress-nginx-admission-create-dd96m 0/1 Completed 0 2m41s
ingress-nginx ingress-nginx-admission-patch-hlp4c 0/1 Completed 2 2m41s
ingress-nginx ingress-nginx-controller-5ccc5d6c7c-v42zl 1/1 Running 0 2m41s
# 查看ingress-nginx控制器service监听端口
[root@k8s-deploy ~]#kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.100.170.188 <none> 80:33593/TCP,443:33157/TCP 4m42s
ingress-nginx-controller-admission ClusterIP 10.100.9.92 <none> 443/TCP 4m42s
ingress-nginx控制器service监听宿主机端口为33593(http),33157(https)
说明:ingress-nginx
监听端口可在部署时设置,即修改deploy.yaml文件中ingress-nginx-controller
service配置
- 配置负载均衡
[root@k8s-ha1 ~]#vim /etc/haproxy/haproxy.cfg
...
listen ingress_nginx_http_80
bind 10.0.0.35:80
mode tcp
server 10.0.0.11 10.0.0.11:33593 check inter 3s fall 3 rise 5
server 10.0.0.12 10.0.0.12:33593 check inter 3s fall 3 rise 5
server 10.0.0.13 10.0.0.13:33593 check inter 3s fall 3 rise 5
server 10.0.0.41 10.0.0.41:33593 check inter 3s fall 3 rise 5
server 10.0.0.42 10.0.0.42:33593 check inter 3s fall 3 rise 5
server 10.0.0.43 10.0.0.43:33593 check inter 3s fall 3 rise 5
listen ingress_nginx_http_443
bind 10.0.0.35:443
mode tcp
server 10.0.0.11 10.0.0.11:33157 check inter 3s fall 3 rise 5
server 10.0.0.12 10.0.0.12:33157 check inter 3s fall 3 rise 5
server 10.0.0.13 10.0.0.13:33157 check inter 3s fall 3 rise 5
server 10.0.0.41 10.0.0.41:33157 check inter 3s fall 3 rise 5
server 10.0.0.42 10.0.0.42:33157 check inter 3s fall 3 rise 5
server 10.0.0.43 10.0.0.43:33157 check inter 3s fall 3 rise 5
查看端口
[root@k8s-ha1 ~]#ss -ntl|grep 10.0.0.35
LISTEN 0 4096 10.0.0.35:443 0.0.0.0:*
LISTEN 0 4096 10.0.0.35:80 0.0.0.0:*
6.3 Ingress 资源
6.3.1 Ingress 规则
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations: # 注解,表示把ingress的资源通知给哪个类别的ingress controller
nginx.ingress.kubernetes.io/rewrite-target: / # 重写目标注解
spec:
rules: # 里面主要字段就只有host和http
- host: www.test.com # 用于指定虚拟主机的名称,如果不写将匹配任意名称
http: # http用来定义指向后端的http选择器列表,其值为一个对象,里面只有一个paths
paths: # paths字段用于把请求映射到后端的某个路径,其值为一个对象列表
- path: /testpath # 定义path来指定映射后端的路径
pathType: Prefix # 路径类型
backend: # backend用于指定后端pod的service,其值为一个对象
service: # 定义后端service
name: test # service名称
port: # 端口
number: 80 # 端口号80
Ingress 需要指定 apiVersion
、kind
、 metadata
和 spec
字段。 Ingress 对象的命名必须是合法的 DNS 子域名名称。
Ingress 经常使用注解(annotations)来配置一些选项,具体取决于 Ingress 控制器,例如重写目标注解。 不同的 Ingress 控制器支持不同的注解。
Ingress 规约 提供了配置负载均衡器或者代理服务器所需的所有信息。 最重要的是,其中包含与所有传入请求匹配的规则列表。 Ingress 资源仅支持用于转发 HTTP(S) 流量的规则。
每个 HTTP 规则都包含以下信息:
- 可选
host
。在此示例中,未指定host
,该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了host
(例如foo.bar.com
),则rules
适用于该host
。 - 路径列表(例如
/testpath
),每个路径都有一个由service.name
和service.port.name
或service.port.number
定义的关联后端。 在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。 -
backend
(后端)是 Service 文档中所述的服务和端口名称的组合, 或者是通过 CRD 方式来实现的自定义资源后端。 与规则的host
和path
匹配的对 Ingress 的 HTTP(和 HTTPS )请求将发送到列出的backend
。
通常在 Ingress 控制器中会配置 defaultBackend
(默认后端),以服务于无法与规约中 path
匹配的所有请求。
6.3.2 默认后端
没有设置规则的 Ingress 将所有流量发送到同一个默认后端,而 .spec.defaultBackend
则是在这种情况下处理请求的那个默认后端。 defaultBackend
通常是 Ingress 控制器的配置选项, 而非在 Ingress 资源中指定。 如果未设置任何的 .spec.rules
,那么必须指定 .spec.defaultBackend
。 如果未设置 defaultBackend
,那么如何处理所有与规则不匹配的流量将交由 Ingress 控制器决定。
如果没有 hosts
或 paths
与 Ingress 对象中的 HTTP 请求匹配,则流量将被路由到默认后端。
6.3.3 资源后端
Resource
后端是一个引用,指向同一命名空间中的另一个 Kubernetes 资源,将其作为 Ingress 对象。 Resource
后端与 Service
后端是互斥的,在二者均被设置时会无法通过合法性检查。 Resource
后端的一种常见用法是将所有入站数据导向带有静态资产的对象存储后端。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-resource-backend
spec:
defaultBackend:
resource:
apiGroup: k8s.example.com
kind: StorageBucket
name: static-assets
rules:
- http:
paths:
- path: /icons
pathType: ImplementationSpecific
backend:
resource:
apiGroup: k8s.example.com
kind: StorageBucket
name: icon-assets
6.3.4 PathType
Ingress 中的每个路径都需要有对应的路径类型(PathType
)。未明确设置 pathType
的路径无法通过合法性检查。当前支持的路径类型有三种:
-
ImplementationSpecific
:对于该路径类型,匹配方法取决于 IngressClass。 具体实现可以将其作为单独的pathType
处理或者与Prefix
或Exact
类型作相同处理。 -
Exact
:精确匹配 URL 路径,且区分大小写。 -
Prefix
:基于以/
分隔的 URL 路径前缀匹配。匹配区分大小写,并且对路径中的元素逐个完成。 路径元素指的是由/
分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。
说明: 如果路径的最后一个元素是请求路径中最后一个元素的子字符串,则不会匹配 (例如:/foo/bar
匹配 /foo/bar/baz
, 但不匹配 /foo/barbaz
)。
6.3.5 匹配规则
类型 | 路径 | 请求路径 | 匹配与否 |
---|---|---|---|
Prefix | / |
(所有路径) | 是 |
Exact | /foo |
/foo |
是 |
Exact | /foo |
/bar |
否 |
Exact | /foo |
/foo/ |
否 |
Exact | /foo/ |
/foo |
否 |
Prefix | /foo |
/foo , /foo/ |
是 |
Prefix | /foo/ |
/foo , /foo/ |
是 |
Prefix | /aaa/bb |
/aaa/bbb |
否 |
Prefix | /aaa/bbb |
/aaa/bbb |
是 |
Prefix | /aaa/bbb/ |
/aaa/bbb |
是,忽略尾部斜线 |
Prefix | /aaa/bbb |
/aaa/bbb/ |
是,匹配尾部斜线 |
Prefix | /aaa/bbb |
/aaa/bbb/ccc |
是,匹配子路径 |
Prefix | /aaa/bbb |
/aaa/bbbxyz |
否,字符串前缀不匹配 |
Prefix | / , /aaa |
/aaa/ccc |
是,匹配 /aaa 前缀 |
Prefix | / , /aaa , /aaa/bbb |
/aaa/bbb |
是,匹配 /aaa/bbb 前缀 |
Prefix | / , /aaa , /aaa/bbb |
/ccc |
是,匹配 / 前缀 |
Prefix | /aaa |
/ccc |
否,使用默认后端 |
混合 | /foo (Prefix) , /foo (Exact) |
/foo |
是,优选 Exact 类型 |
在某些情况下,Ingress 中的多条路径会匹配同一个请求。 这种情况下最长的匹配路径优先。 如果仍然有两条同等的匹配路径,则精确路径类型优先于前缀路径类型。
6.3.6 主机名通配符
主机名可以是精确匹配(例如 foo.bar.com
)或者使用通配符来匹配 (例如 *.foo.com
)。 精确匹配要求 HTTP host
头部字段与 host
字段值完全匹配。 通配符匹配则要求 HTTP host
头部字段与通配符规则中的后缀部分相同。
主机 | host 头部 | 匹配与否 |
---|---|---|
*.foo.com |
bar.foo.com |
基于相同的后缀匹配 |
*.foo.com |
baz.bar.foo.com |
不匹配,通配符仅覆盖了一个 DNS 标签 |
*.foo.com |
foo.com |
不匹配,通配符仅覆盖了一个 DNS 标签 |
6.4 准备环境
6.4.1 下载镜像,并上传至harbor仓库
docker pull nginx:mainline-alpine-slim
docker tag nginx:mainline-alpine-slim harbor.chu.net/baseimages/nginx:mainline-alpine-slim
docker push harbor.chu.net/baseimages/nginx:mainline-alpine-slim
6.4.2 编写yaml
kind: Pod
apiVersion: v1
metadata:
name: foo-app
namespace: web
labels:
app: foo
spec:
containers:
- name: foo-app
image: harbor.chu.net/baseimages/nginx:mainline-alpine-slim
ports:
- containerPort: 80
---
kind: Service
apiVersion: v1
metadata:
name: foo-service
namespace: web
spec:
selector:
app: foo
ports:
- port: 8080
targetPort: 80
---
kind: Pod
apiVersion: v1
metadata:
name: bar-app
namespace: web
labels:
app: bar
spec:
containers:
- name: bar-app
image: harbor.chu.net/baseimages/nginx:mainline-alpine-slim
ports:
- containerPort: 80
---
kind: Service
apiVersion: v1
metadata:
name: bar-service
namespace: web
spec:
selector:
app: bar
ports:
- port: 8080
targetPort: 80
---
6.4.3 创建服务
kubectl apply -f test.yaml
[root@k8s-master1 ~]#kubectl get pod -n web
NAME READY STATUS RESTARTS AGE
bar-app 1/1 Running 0 17s
foo-app 1/1 Running 0 17s
[root@k8s-master1 ~]#kubectl get svc -n web
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
bar-service ClusterIP 10.100.69.63 <none> 8080/TCP 38s
foo-service ClusterIP 10.100.65.203 <none> 8080/TCP 38s
6.4.4 准备页面
[root@k8s-master1 ~]#kubectl exec -it foo-app -n web sh
/ # echo 'foo-app' > /usr/share/nginx/html/index.html
[root@k8s-master1 ~]#kubectl exec -it bar-app -n web sh
/ # echo 'bar-app' > /usr/share/nginx/html/index.html
6.4.5 验证service
# 进入容器,访问测试
[root@k8s-master1 ~]#kubectl exec -it net-test1 -n myserver sh
/ # curl bar-service.web.svc:8080
bar-app
/ # curl foo-service.web.svc:8080
foo-app
6.5 实现虚拟主机
基于客户端请求的host域名进行转发
6.5.1 单虚拟主机
flowchart TB a[客户端] b[Ingress:10.0.0.35] c1[Service foo-serivce:8080] d1[pod] d2[pod] a-.Ingress负载均衡器.->b subgraph b--host:www.myapp.com--->c1 c1-->d1 & d2 end
编写yaml文件
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-web
namespace: web
annotations:
kubernetes.io/ingress.class: "nginx" #指定Ingress Controller的类型
nginx.ingress.kubernetes.io/use-regex: "true" #指定后面rules定义的path可以使用正则表达式
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600" #连接超时时间,默认为5s
nginx.ingress.kubernetes.io/proxy-send-timeout: "600" #后端服务器回转数据超时时间,默认为60s
nginx.ingress.kubernetes.io/proxy-read-timeout: "600" #后端服务器响应超时时间,默认为60s
nginx.ingress.kubernetes.io/proxy-body-size: "50m" #客户端上传文件,最大大小,默认为20m
#nginx.ingress.kubernetes.io/rewrite-target: / #URL重写
nginx.ingress.kubernetes.io/app-root: /index.html
spec:
rules:
- host: www.myapp.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: foo-service
port:
number: 8080
查看
[root@k8s-master1 ~]#kubectl get ingress -n web
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-web <none> www.myapp.com 10.100.170.188 80 4m
验证
# 配置hosts
echo "10.0.0.35 www.myapp.com" >> /etc/hosts
# 域名访问测试,ip访问无效
[root@k8s-node1 ~]#curl -kL www.myapp.com
foo-app
6.5.2 多虚拟主机
flowchart TB a[客户端] b[Ingress:10.0.0.35] c1[Service foo-serivce:8080] c2[Service bar-serivce:8080] d1[pod] d2[pod] d3[pod] d4[pod] a-.Ingress负载均衡器.->b subgraph b--host:www.foo.com--->c1 b--host:www.bar.com--->c2 c1-->d1 & d2 c2-->d3 & d4 end
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-web
namespace: web
annotations:
kubernetes.io/ingress.class: "nginx" ##指定Ingress Controller的类型
nginx.ingress.kubernetes.io/use-regex: "true" ##指定后面rules定义的path可以使用正则表达式
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600" ##连接超时时间,默认为5s
nginx.ingress.kubernetes.io/proxy-send-timeout: "600" ##后端服务器回转数据超时时间,默认为60s
nginx.ingress.kubernetes.io/proxy-read-timeout: "600" ##后端服务器响应超时时间,默认为60s
nginx.ingress.kubernetes.io/proxy-body-size: "10m" ##客户端上传文件,最大大小,默认为20m
#nginx.ingress.kubernetes.io/rewrite-target: / ##URL重写
nginx.ingress.kubernetes.io/app-root: /index.html
spec:
rules:
- host: www.foo.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: foo-service
port:
number: 8080
- host: www.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: bar-service
port:
number: 8080
查看状态
[root@k8s-deploy ~]#kubectl describe ingress nginx-web -n web
Name: nginx-web
Labels: <none>
Namespace: web
Address: 10.100.170.188
Ingress Class: <none>
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
www.foo.com
/ foo-service:8080 (10.200.36.106:80)
www.bar.com
/ bar-service:8080 (10.200.169.155:80)
Annotations: kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/app-root: /index.html
nginx.ingress.kubernetes.io/proxy-body-size: 10m
nginx.ingress.kubernetes.io/proxy-connect-timeout: 600
nginx.ingress.kubernetes.io/proxy-read-timeout: 600
nginx.ingress.kubernetes.io/proxy-send-timeout: 600
nginx.ingress.kubernetes.io/use-regex: true
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 2s (x2 over 53s) nginx-ingress-controller Scheduled for sync
浏览器验证
# 配置hosts域名解析
10.0.0.35 www.foo.com www.bar.com
访问www.foo.com
访问www.bar.com
6.5.3 其他
如果创建的 Ingress 资源没有在 rules
中定义的任何 hosts
,则可以匹配指向 Ingress 控制器 IP 地址的任何网络流量,而无需基于名称的虚拟主机。
例如,以下 Ingress 会将请求 first.bar.com
的流量路由到 service1
,将请求 second.bar.com
的流量路由到 service2
,而所有其他流量都会被路由到 service3
。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-virtual-host-ingress-no-third-host
spec:
rules:
- host: first.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
- host: second.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service2
port:
number: 80
- http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service3
port:
number: 80
6.6 实现URL转发
根据客户端请求的同一host不同的URL进行转发
flowchart TB a[客户端] b[Ingress:10.0.0.35] c1[Service foo-serivce:8080] c2[Service bar-serivce:8080] d1[pod] d2[pod] d3[pod] d4[pod] a-.Ingress负载均衡器.->b subgraph b--/foo--->c1 b--/bar--->c2 c1-->d1 & d2 c2-->d3 & d4 end
编写yaml文件
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-web
namespace: web
annotations:
kubernetes.io/ingress.class: "nginx" ##指定Ingress Controller的类型
nginx.ingress.kubernetes.io/use-regex: "true" ##指定后面rules定义的path可以使用正则表达式
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600" ##连接超时时间,默认为5s
nginx.ingress.kubernetes.io/proxy-send-timeout: "600" ##后端服务器回转数据超时时间,默认为60s
nginx.ingress.kubernetes.io/proxy-read-timeout: "600" ##后端服务器响应超时时间,默认为60s
nginx.ingress.kubernetes.io/proxy-body-size: "10m" ##客户端上传文件,最大大小,默认为20m
nginx.ingress.kubernetes.io/rewrite-target: / ##URL重写
nginx.ingress.kubernetes.io/app-root: /index.html
spec:
rules:
- host: www.myapp.com
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: foo-service
port:
number: 8080
- pathType: Prefix
path: "/bar"
backend:
service:
name: bar-service
port:
number: 8080
查看状态
[root@k8s-deploy 4.Ingress-cases]#kubectl describe ingress nginx-web -n web
Name: nginx-web
Labels: <none>
Namespace: web
Address: 10.100.170.188
Ingress Class: <none>
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
www.myapp.com
/foo foo-service:8080 (10.200.36.106:80)
/bar bar-service:8080 (10.200.169.155:80)
Annotations: kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/app-root: /index.html
nginx.ingress.kubernetes.io/proxy-body-size: 10m
nginx.ingress.kubernetes.io/proxy-connect-timeout: 600
nginx.ingress.kubernetes.io/proxy-read-timeout: 600
nginx.ingress.kubernetes.io/proxy-send-timeout: 600
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/use-regex: true
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 46s (x8 over 58m) nginx-ingress-controller Scheduled for sync
浏览器验证
访问www.myapp.com/foo
访问www.myapp.com/bar
注意: .metadata.annotations 字段中添加URL重写属性nginx.ingress.kubernetes.io/rewrite-target: /
,www.myapp.com/foo/index.html
路径将转发至后端foo-service/index.html
,若不增加URL重写属性,将转发至后端foo-service/foo/index.html
路径。
6.7 实现域名HTTPS
配置多域名HTTPS
6.7.1 创建secret
mkdir -p /data/certs
cd /data/certs
# 创建ca
openssl req -x509 -sha256 -newkey rsa:4096 -keyout ca.key -out ca.crt -days 3560 -nodes -subj '/CN=www.ca.com'
# 创建www.foo.com域名证书,并用CA进行签名
openssl req -new -newkey rsa:4096 -keyout server_foo.key -out server_foo.csr -nodes -subj '/CN=www.foo.com'
openssl x509 -req -sha256 -days 3650 -in server_foo.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server_foo.crt
# 创建secret
kubectl create secret tls tls-secret-foo --cert=./server_foo.crt --key=./server_foo.key -n web
# 创建www.bar.com secret
openssl req -new -newkey rsa:4096 -keyout server_bar.key -out server_bar.csr -nodes -subj '/CN=www.bar.com'
openssl x509 -req -sha256 -days 3650 -in server_bar.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server_bar.crt
kubectl create secret tls tls-secret-bar --cert=./server_bar.crt --key=./server_bar.key -n web
验证
[root@k8s-deploy ~]#kubectl describe secret -n web
Name: tls-secret-bar
Namespace: web
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1663 bytes
tls.key: 3268 bytes
Name: tls-secret-foo
Namespace: web
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1663 bytes
tls.key: 3272 bytes
6.7.2 编写yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-https-web
namespace: web
annotations:
kubernetes.io/ingress.class: "nginx" ##指定Ingress Controller的类型
nginx.ingress.kubernetes.io/ssl-redirect: 'true'
spec:
tls:
- hosts:
- www.foo.com
secretName: tls-secret-foo
- hosts:
- www.bar.com
secretName: tls-secret-bar
rules:
- host: www.foo.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: foo-service
port:
number: 8080
- host: www.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: bar-service
port:
number: 8080
查看状态
[root@k8s-deploy ~]#kubectl describe ingress nginx-https-web -n web
Name: nginx-https-web
Labels: <none>
Namespace: web
Address: 10.100.170.188
Ingress Class: <none>
Default backend: <default>
TLS:
tls-secret-foo terminates www.foo.com
tls-secret-bar terminates www.bar.com
Rules:
Host Path Backends
---- ---- --------
www.foo.com
/ foo-service:8080 (10.200.36.106:80)
www.bar.com
/ bar-service:8080 (10.200.169.155:80)
Annotations: kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: true
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 68s (x2 over 113s) nginx-ingress-controller Scheduled for sync
6.7.3 浏览器验证
验证www.foo.com
验证www.bar.com
注意:默认规则上无法使用 TLS,需要为所有可能的子域名发放证书。 因此,tls
字段中的 hosts
的取值需要与 rules
字段中的 host
完全匹配。
6.8 更新HTTPS证书
6.8.1 创建新证书
cd /data/certs
# 若没有CA证书,则执行新建证书命令
# openssl req -x509 -sha256 -newkey rsa:4096 -keyout ca.key -out ca.crt -days 3560 -nodes -subj '/CN=www.ca.com'
# 创建新www.foo.com域名证书
openssl req -new -newkey rsa:4096 -keyout server_foo.key -out server_foo.csr -nodes -subj '/CN=www.foo.com'
openssl x509 -req -sha256 -days 3650 -in server_foo.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server_foo.crt
#创建新www.bar.com域名证书
openssl req -new -newkey rsa:4096 -keyout server_bar.key -out server_bar.csr -nodes -subj '/CN=www.bar.com'
openssl x509 -req -sha256 -days 3650 -in server_bar.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server_bar.crt
6.8.2 创建新secret
kubectl create secret tls tls-secret-foo-update --cert=./server_foo.crt --key=./server_foo.key -n web
kubectl create secret tls tls-secret-bar-update --cert=./server_bar.crt --key=./server_bar.key -n web
# 查看新secret中tls.crt、tls.key值
[root@k8s-deploy certs]#kubectl get secret tls-secret-foo-update -n web -oyaml
apiVersion: v1
data:
tls.crt: LS0tLS1CRUdJ.....ZJQ0FURS0tLS0tCg==
tls.key: LS0tLS1CRUdJTiBQUotL......lWQVRFIEtFWS0tLS0tCg==
kind: Secret
metadata:
creationTimestamp: "2023-02-18T17:08:31Z"
name: tls-secret-foo-update
namespace: web
resourceVersion: "2879019"
uid: 0858588b-fd34-4205-916d-606fc718d780
type: kubernetes.io/tls
6.8.3 更新原secret
更新www.foo.com
secret
# 获取tls.crt、tls.key的值
[root@k8s-deploy ~]#kubectl get secret tls-secret-foo-update -n web -oyaml|grep tls.crt|awk '{print $2}'
LS0tLS1CRUdJTiBDRVJUSUZJQ0FU......HQTFVRUF3d0tkM2QzTG1OaExtTnYKYlRBZ
[root@k8s-deploy ~]#kubectl get secret tls-secret-foo-update -n web -oyaml|grep tls.key|awk '{print $2}'
LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRUUl......OXcVBQW9JQ0FRQzY3SjQ5eWk4bVQzM0oKVjU0e
# 编辑原secret,进行tls.crt、tls.key的值替换
kubectl edit secret tls-secret-foo -n web
# 操作与vi命令相同,替换tls.crt、tls.key的值后保存退出,证书自动更新
更新www.bar.com
secret
# 更新操作与前面相同
kubectl get secret tls-secret-bar-update -n web -oyaml|grep tls.crt|awk '{print $2}'
kubectl get secret tls-secret-bar-update -n web -oyaml|grep tls.key|awk '{print $2}'
kubectl edit secret tls-secret-bar -n web
6.8.4 验证
验证www.foo.com
验证www.bar.com
查看发现证书时间都已经更新
5.1.2 tomcat基础镜像制作
- 编写Dockerfile
#Tomcat 8.5.43基础镜像 FROM harbor.chu.net/baseimages/jdk-base:v8.212 RUN mkdir -p /apps /data/tomcat/webapps /data/tomcat/logs ADD apache-tomcat-8.5.43.tar.gz /apps RUN useradd tomcat -u 2050 && ln -sv /apps/apache-tomcat-8.5.43 /apps/tomcat && chown -R tomcat:tomcat /apps /data
- 构建镜像,并上传至harbor仓库
↩︎nerdctl build -t harbor.chu.net/baseimages/tomcat-base:v8.5.43 . nerdctl push harbor.chu.net/baseimages/tomcat-base:v8.5.43