Service的概念
service定义了这样一种抽象: 一个Pod的逻辑分组,一种可以访问它们的策略----通常称为微服务, 这一组Pod能够被Service访问到,通常是通过 Label Selector
Service能够提供负载均衡的能力,只提供4层负载均衡能力,而没有7层功能.
Service的类型
Service在K8s中有以下四种类型:
- Clusterlp:默认类型, 自动分配一个仅Cluster内部可以访问的虚拟IP
- NodePort:在ClusterlP基础上为Service在每台机器上绑定一个端口, 这样就可以通过:NodePort来访问该服务(比较常见),需要在外部再做个负载均衡,到每个node
- LoadBalancer:在NodePort的基础上, 借助cloudprovider创建一个外部负载均衡器, 并将请求转发 到:NodePort,和NodePort实现原理一下,需要在外部再做个负载均衡到每个node,但这个负载均衡是由云供应商来提供
- ExternalName:把集群外部的服务引入到集群内部来, 在集群内部直接使用。没有任何类型代理被创建, 这只有kubernetes 1.7或更高版本的kube-dns才支持. (类似配置中心的功能,把需要访问外部某个集群的地址和端口写在这里,内部pod只要访问这里即可拿到地址,如果外部的地址更新了,不需要变更pod的配置,只要修改这个service的配置即可)
VIP和Service代理
在Kubernetes集群中, 每个Node运行一个 kube-proxy进程。 kube-proxy负责为Service实现了一种 VIP(虚拟IP) 的形式, 而不是ExternalName 的形式。在Kubernetes v1.0版本, 代理完全在userspace。在 Kubernetes v 1.1版本, 新增了iptables代理, 但并不是默认的运行模式。 从Kubernetes v1.2起, 默认就是 iptables代理。在Kubernetes v1.8.0-beta.0中, 添加了ipvs代理
在Kubernetes v1.14版本开始默认使用ipvs代理
在Kubernetes v1.0版本,Service是“4层”(TCP/UDP over IP) 概念。
在Kubernetes v1.1版本, 新增了 Ingress API(beta版) , 用来表示“7层”(HTTP) 服务
代理模式的分类
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:不排队调度
注: ipvs模式假定在运行 kube-proxy 之前在节点上都已经安装了IPVS内核模块.当kube-proxy以ipvs代理模式启动时,kube-proxy将验证节点上是否安装了ipvs模块,如果未安装,则kube-proxy将回退到iptables代理模式
Ingress代理模式(7层)
ingress-Nginx github: https://github.com/kubenetes/ingress-nginx
ingress-Nginx 官网: https://kubernetes.github.io/ingress-nginx
Ingress配置
# 官网 - deployment - install页面能看到类似这样一行命令
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.4.0/deploy/static/provider/cloud/deploy.yaml # 云部署,阿里云用的也是AWS
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.4.0/deploy/static/provider/baremetal/deploy.yaml # 裸机
# 如果您运行的是旧版本的Kubernetes(1.18或更早版本),请阅读本段了解具体说明。由于api被弃用,默认清单可能无法在集群上运行。
# 使用对应k8s 1.15版本的ingress-nginx的清单文件
https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.25.0/deploy/static/mandatory.yaml
https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.25.0/deploy/baremetal/service-nodeport.yaml
# mandatory.yaml
# spec:
# # wait up to five minutes for the drain of connections
# terminationGracePeriodSeconds: 300
# hostNetwork: true # 这句一定要加上,直接从官网下载的没有这句,否则liveness检测会不通过
# serviceAccountName: nginx-ingress-serviceaccount
# service-nodeport.yaml
# 官方下载的service-nodeport.yaml也要在spec下面加上,选择器,否则不会绑定转发规则
# selector:
# app.kubernetes.io/name: ingress-nginx
# app.kubernetes.io/part-of: ingress-nginx
kubectl apply -f mandatory.yaml
kubectl apply -f service-nodeport.yaml
ingress HTTP代理访问
# deployment
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-dm
spec:
replicas: 2
template:
metadata:
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
# Service
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
name: nginx
---
# ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
spec:
rules:
- host: my.example.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
kubectl get svc -A
拿到ingress-nginx的暴露的端口
DNS解析域名
my.example.com:端口 访问了
Ingress HTTPS代理访问
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
** Ingress Yaml配置文件 **
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
spec:
tls:
- hosts:
- foo.bar.com
secretName: tls-secret
rules:
- host:foo.bar.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
Nginx进行BasicAuth
yum -y install httpd
htpasswd -c auth foo # auth是文件名 foo是用户名,还需要录入用户密码
kubectl create secret generic basic-auth --from-file=auth
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-with-auth
annotations:
nginx.ingress.kubernetes.io/auth-type: basi
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm 1: 'Authentication Required-foo'
spec:
rules:
- host: foo2.bar.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
Nginx进行重写
名称 | 描述 | 值 |
---|---|---|
nginx.ingress.kubernetes.io/rewrte-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上定义的路径是否使用正则表达式 | 布尔 |
apiVersion: extensions/v1beta1
kind:Ingress
metadata:
name: nginx-test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: http://foo.bar.com:31795/hostname.html
spec:
rules:
- host: foo10.bar.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
Service配置
ClusterIP配置
clusterIP主要在每个node节点使用iptables | ipvs, 将发向clusterIP对应端口的数据, 转发到kube-proxy中。然后kube-proxy自己内部实现有负载均衡的方法, 并可以查询到这个service下对应pod的地址和端口, 进而把数据转发给对应的pod的地址和端口
为了实现图上的功能,主要需要以下几个组件的协同工作:
- apiserver用户通过kubectl命令向apiserver发送创建service的命令, apiserver接收到请求后将数据存储到etcd中
- kube-proxy kubernetes的每个节点中都有一个叫做kube-porxy的进程, 这个进程负责感知service, pod 的变化, 并将变化的信息写入本地的iptables规则中
- iptables使用NAT等技术将virtualIP的流量转至endpoint中
Deployment配置文件
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: myapp:v2
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
Service配置文件
apiVersion: v1
kind: Service
metadata:
name: mvapp
namespace: default
spec:
type: ClusterIP
selector:
app: myapp
release: stabel
ports:
- name: http
port: 80
targetPort: 80
Headless Service(无头服务)
有时不需要或不想要负载均衡, 以及单独的Service IP。遇到这种情况, 可以通过指定ClusterIP(spec.clusterIP) 的值为“None"来创建Headless Service。这类Service并不会分配Cluster IP, kube- proxy不会处理它们, 而且平台也不会为它们进行负载均衡和路由
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
namespace: default
spec:
selector:
app: myapp
clusterIP: "None"
ports:
- port: 80
targetPort: 80
dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10
# myapp-headless svc名称
# default 名称空间
# svc.cluster.local 默认配置的名称
# @10.96.0.10 coreDNS的IP, 从kubectl get pod -n kube-system -o wide中查询
# dig命令在 bind-utils 包中
# local后面的.和@之间有个空格
NodePort配置
nodePort的原理在于在node上开了一个端口, 将向该端口的流量导入到kube-proxy, 然后由kube-proxy进一步到给对应的pod
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: NodePort
selector:
app: myapp
release: stabel
ports:
- name: http
port: 80
targetPort: 80
查询流程
iptables -t nat -nvL
LoadBalancer配置
loadBalancer和nodePort其实是同一种方式, 区别在于LoadBalancer比nodePort多了一步,就是可以调用cloud provider 去创建LB来向节点导流
ExternalName配置
这种类型的Service通过返回CNAME和它的值, 可以将服务映射到externalName字段的内容(例如: my.database.example.com) 。ExternalName Service是Service的特例, 它没有selector, 也没有定义任何的端口和 Endpoint。相反的, 对于运行在集群外部的服务, 它通过返回该外部服务的别名这种方式来提供服务
kind: Service
apiVersion: v1
metadata:
name: my-service-1
namespace: default
spec:
type: ExternalName
externalName: my.database.example.com
当查询主机my-service.defalut.svc.cluster.local(SVC_NAME.NAMESPACE.svc.cluster.local) 时, 集群的DNS服务将返回一个值my.database.example.com的CNAME记录。访问这个服务的工作方式和其他的相同, 唯一不同的是重定向发生在DNS层, 而且不会进行代理或转发(即将无头服务的service解析转发到my.database.example.com, 相当于DNS的别名服务)