首页 > 技术文章 > Kubernetes-service

wang-yy 2020-05-22 12:51 原文

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

推荐阅读