Service四层
Service 的概念
Kubernetes `Service` 定义了这样一种抽象:一个 `Pod` 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 这一组 `Pod` 能够被 `Service` 访问到,通常是通过 `Label Selector`
Service能够提供负载均衡的能力,但是在使用上有以下限制:
只提供 4 层负载均衡能力,而没有 7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的*
Service 的类型
Service 在 K8s 中有以下四种类型
1.ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP**
2.NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 <NodeIP>: NodePort 来访问该服务**
3.LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到<NodeIP>: NodePort
4.ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才持**
1.nginx通过svc(clusteip类型:集群内部服务间的访问)关联php-fpm;
2.svc(NodePort类型:把集群内部的服务暴露给外部用户)在当前的物理机上开启一个大于30000的端口,svc一端连接端口一端连接nginx,如有多个集群,同样每个集群都会开启一个端口
3.在云环境中,通过LAAS创建一个LB(LoadBalancer负载调度器)调取节点端口;
4.在内部创建一个别名(ExternalName类型),被calldns自动组装成一个域名,php-fpm将地址信息引入到创建的别名上,若外部ip地址改变只需修改别名内信息即可,不需要修改php的pod。
VIP 和 Service 代理
在 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 代理
在 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缓存
service代理模式的分类
Ⅰ、userspace 代理模式
Ⅱ、iptables 代理模式
Ⅲ、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: 不排队 调度
ClusterIP
clusterIP 主要在每个 node 节点使用 iptables,将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口
clusterIP 主要在每个 node 节点使用 ipvs,将发向集群节点的流量,通过负载均衡的方式发送到pod的每一个节点上。
为了实现图上的功能,主要需要以下几个组件的协同工作:
apiserver 用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到 etcd 中
kube-proxy kubernetes的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables规则中
iptables 使用NAT等技术将virtualIP的流量转至endpoint中
apiserver 用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到 etcd 中
kube-proxy kubernetes的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的ipvs规则中
ipvs 使用NAT等技术将virtualIP的流量转至endpoint中
创建 myapp-deploy.yaml 文件
# 创建deployment控制器的pod
$ vim myapp-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
release: stabel
template:
metadata:
labels:
app: myapp
release: stabel
env: test
spec:
containers:
- name: myapp
image: hub.wyy.com/myapp:v2
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
$ kubectl apply -f myapp-deploy.yaml # 创建
$ kubectl get pod # 查看状态
# 创建clusterip类型的svc
$ kubectl create service clusterip myapp --tcp=80:80 --dry-run -o yaml
创建 Service 信息
[root@master manifests]# vim myapp-service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: ClusterIP
selector:
app: myapp
release: stabel
ports:
- name: http
port: 80
targetPort: 80
$ kubectl apply -f svc myapp-service.yaml
$ kubectl get svc
# /usr/local/kubernetes/install/kubeadm-config.yaml 可查看pod网络 service网络
$ kubectl get pod
$ while 2>1;
$ do
$ curl 10.104.137.133/hostname.html
$ done
$ ipvsadm -Ln
# 有出站入站数据。证明是nat模式
Headless Service
有时不需要或不想要负载均衡,以及单独的 Service IP 。遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP) 的值为 “None” 来创建 Headless Service 。这类 Service 并不会分配 Cluster IP, kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由
$ yum -y install bind-utils
$ kubectl get pod -n kube-system -o wide # 确定DNS服务器的地址
$ dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10 # 查看正常的svc地址
# 无头服务
[root@k8s-master mainfests]# vim myapp-svc-headless.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
namespace: default
spec:
selector:
app: myapp
clusterIP: "None"
ports:
- port: 80
targetPort: 80
$ kubectl apply -f myapp-svc-headless.yaml
$ kubectl get svc
$ dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10
# -t tcp协议发起解析 A记录解析 要解析的A记录@ 交给哪个DNS服务器处理
NodePort
nodePort 的原理在于在 node 上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy 进一步到给对应的 pod
[root@master manifests]# vim myapp-service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: NodePort
selector:
app: myapp
release: stabel
ports:
- name: http
# nodePort 32111 可以添加定义端口
port: 80
targetPort: 80
$ kubectl apply -f myapp-service.yaml
$ kubectl get pod
$ kubectl get svc
$ kubectl get svc myapp -o yaml
$ 192.168.66.11:32111 # 11/12/13都可访问
查询流程
iptables -t nat -nvL
KUBE-NODEPORTS
LoadBalancer
loadBalancer 和 nodePort 其实是同一种方式。区别在于 loadBalancer 比 nodePort 多了一步,就是可以调用cloud provider 去创建 LB 来向节点导流
ExternalName
这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例如:hub.hongfu.com )。ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务
kind: Service
apiVersion: v1
metadata:
name: my-service-1
namespace: default
spec:
type: ExternalName
externalName: hub.hongfu.com
当查询主机 my-service.defalut.svc.cluster.local ( SVC_NAME.NAMESPACE.svc.cluster.local )时,集群的 DNS 服务将返回一个值 my.database.example.com 的 CNAME 记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发
ingress七层接口
资料信息
Ingress-Nginx github 地址:https://github.com/kubernetes/ingress-nginx
Ingress-Nginx 官方网站:https://kubernetes.github.io/ingress-nginx/
部署 Ingress-Nginx
# 上传下载的Ingress-Nginx软件包,并解压
$ tar -zxvf Ingress-Nginx.tar.gz
$ ls # 镜像文件、通过deployment方案部署、通过nodeport方案将集群暴露给web服务
$ kocker load -i ingress.tart # 导入镜像文件
# 将镜像拷贝至所有节点,并导入
$ scp ingress.tar root2@n1:/root/
# deployment方案部署
$ kubectl apply -f mandatory.yaml
$ kubectl get pod -n ingress-nginx
# nodeport方案将集群暴露给web服务
$ kubectl apply -f service-nodeport.yaml
$ kubectl get svc -n ingress-nginx
Ingress HTTP 代理访问
deployment、Service、Ingress Yaml 文件
# 创建http协议的虚拟主机
$ vim www1.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-dm
spec:
replicas: 2
template:
metadata:
labels:
name: nginx
spec:
containers:
- name: nginx
image: hub.wyy.com/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
name: nginx
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
spec:
rules:
- host: www1.wyy.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
$ kubectl apply -f www1
# 真实机添加hosts解析
192.168.66.11 www1.wyy.com
# 浏览器测试、
www1.wyy.com:32111
Ingress HTTPS 代理访问
创建证书,以及 cert 存储方式
# 创建证书
$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
$ kubectl create secret tls tls-secret --key tls.key --cert tls.crt
$ kubectl get secret # 查看secret tls-decret -o yaml
deployment、Service、Ingress Yaml 文件
# 创建Ingress
$ vim ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
spec:
tls:
- hosts:
- ssl.wyy.com
secretName: tls-secret
rules:
- host: ssl.wyy.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
$ kubectl apply -f ingress.yaml
# 添加真实机hosts文件解析
192.168.66.11 ssl.wyy.com
$ kubectl get svc -n ingress-nginx
# 浏览器访问
ssl.wyy.com:31395
Nginx 进行 BasicAuth
yum -y install httpd
htpasswd -c auth foo
kubectl create secret generic basic-auth --from-file=auth
$ vim 1.yam;
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-with-auth
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:
rules:
- host: basic.wyy.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
$ kubectl apply -y 1.yaml
# 真实机添加地址解析
192.168.66.11 basic.wyy.com
$ kubectl get svc -n ingress-nginx
# 浏览器测试
basic.wyy.com:31359
Nginx 进行重写
名称 | 描述 | 值 |
---|---|---|
nginx.ingress.kubernetes.io/rewrite-target | 必须重定向流量的目标URI | 串 |
nginx.ingress.kubernetes.io/ssl-redirect | 指示位置部分是否仅可访问SSL(当Ingress包含证书时默认为True) | 布尔 |
nginx.ingress.kubernetes.io/force-ssl-redirect | 即使Ingress未启用TLS,也强制重定向到HTTPS | 布尔 |
nginx.ingress.kubernetes.io/app-root | 定义Controller必须重定向的应用程序根,如果它在'/'上下文中 | 串 |
nginx.ingress.kubernetes.io/use-regex | 指示Ingress上定义的路径是否使用正则表达式 | 布尔 |
$ vim rew.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: https://ssl.wyy.com:31795/hostname.html
spec:
rules:
- host: rew.wyy.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
$ kubectl apply -f rew.yaml
$
# 真实机添加地址解析
192.168.66.11 rew.wyy.com
$ kubectl get svc -n ingress-nginx
# 浏览器测试
rew.wyy.com:31111