Kubernetes之Service和Ingress
1. Service资源及其实现模型
1. Service资源概述
Service资源基于标签选择器将一组Pod定义成一个逻辑组合,并通过自己的IP地址和端口调度代理请求至组内的Pod对象之上,它向客户端隐藏了真实的处理用户请求的Pod资源,使得客户端的请求看上去就像是由Service直接处理并进行响应的一样
Service对象的IP地址也称为Cluster IP,是一种虚拟IP地址,在Service对象创建后保持不变,并且能够被同一集群中的Pod资源访问。Service端口用于接收客户端请求并将其转发至其后端的Pod中应用的相应端口之上,这种代理机制称为端口代理

2. 虚拟IP和服务代理
一个Service对象就是工作节点上的一些iptables或ipvs规则,用于将到达Service对象IP地址的流量调度转发到相应的Endpoints对象指向的IP地址和端口之上
kube-proxy将请求代理至相应断电的方式有三种
1. userspace代理模型
在kubernetes1.1版本之前,userspace是默认的代理模型
在这种模型中,kube-proxy负责跟踪API Server上Service和Endpoints对象的变动,并据此调整Service资源的定义。
对于每个 Service 对象,它会随机打开一个本地端口(运行于用户 空 间的 kube-proxy 进程负责监昕),任何到达此代理端口的连接请求都将被代理至当前 Service 资源后端的各 Pod 对象上,至于会挑 中哪个 Pod 对象则取决于当前 Service 资源的调度方式,默认的调度算法是轮询( round-robin ) , 另外,此类的 Servic巳对象还会创建 iptables 规则以捕获任何到达 ClusterIP 和端口的流量 。
2. iptables代理模型
在kubernetes1.2版本后开始,成为默认的类型
对于每个 Service ,它都会创建 iptables 规则直接捕获到达 ClusterIP 和 Port 的流量 ,并将其重定向至当前 Service 的后端, 对于每个 Endpoints 对象, Service 资源会为其创建iptables 规则并关联至挑选的后端 Pod 资源,默认算法是随机调度( random ) 。
iptables模型无须将流量在用户空间和内核空间来回切换,因而更加高效和可靠。
3. ipvs代理模型
在kubernetes1.11版本后开始,成为默认的类型
此种模型中, kube-proxy 跟踪 APIServer 上 Servic巳和 Endpoints 对象的变动,据此来调用 netlink 接口创建 ipvs 规则,并确保与 API Se凹er 中的变动保持同步, 它与 iptables 规则的不同之处仅在于其请求流量的调度功能由ipvs 实现 ,余下的其他功能仍由 iptables完成 。
2. Service资源的基础应用
1. Service存在的意义
1. 防止Pod失联(服务发现)
2. 定义一组Pod的访问策略(负载均衡)
3. 支持ClusterIP,NodePort以及LoadBalancer三种类型
2. 创建Service资源
1. 使用命令
kubectl expose
2. 使用yaml文件
定义service资源对象,spec两个常用的内嵌字段:selector和ports
kind: Service
apiVersion: vl
metadata:
name: myapp-svc
spec :
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
Service 资源 myapp-svc 通过标签选择器关联至标签为“ app=myapp ”的各 Pod 对象,它会自动创建名为 myapp - svc 的 Endpoints 资源对象,并自动配置一个 ClusterIP , 暴露的端口由 port 字段进行指定,后端各 Pod 对象的端口则由 targetPort 给出,也可以使用同 port 字段的默认值。
3. 查看service资源对象信息
kubectl get svc myapp-svc
kubectl get endpoints myapp-svc
4. Service会话粘性
Service 资源还支持 Session affinity (粘性会话或会话粘性)机制,它能够将来自同 一个 客户端的请求始终转发至同一个后端的 Pod 对象,这意味着它会影响调度算法的流量分发 功用,进而降低其负载均衡的效果 。 因此,当客户端访问 Pod 中的应用程序时,如果有基 于客户端身份保存某些私有信息,并基于这些私有信息追踪用户的活动等一类的需求时,那 么应该启用 session affinity 机制 。 Session affinity 的效果仅会在一定时间期限内 生效,默认值为 10 800 秒,超出此时长 之后,客户端的再次访问会被调度算法重新调度 。 另外 , Service 资源的 S巳ssion affinity 机 制仅能基于客户端 IP 地址识别客户端身份,它会把经 由同一个 NAT 服务器进行源地址转换 的所有客户端识别为同一个客户端,调度粒度粗糙且效果不佳,因此,实践中并不推荐使用 此种方法实现粘性会话 。 此节仅用于为读者介绍其功能及实现 。
3. 服务发现
pod客户端中的应用如何得知某个特定Service资源的IP和端口?
通过服务发现。
1. 服务发现类型
客户端发现
由客户端到服务注册中心发现其依赖到的服务的相关信息,因此,它需要内置特定的服务发现程序和发现逻辑
服务端发现
这种方式需要额外用到一个称为中央路由器或服务均衡器的组件;服务消费者将请求发往中央路由器或者负载均衡器,由它们负责查询服务注册中心获取服务提供者的位置信息,并将服务消费者的请求转发给服务提供者 。
常用的服务注册中心
zookeeper,etcd分布式键值存储系统,它们只提供数据存储功能,更注重数据的一致性
Eureka一可用性目的为先,可以在多种故障期间保持服务发现和服务注册的功能可用
Consul除了服务发现的基础功能还有多数据中心的部署能力
k8s使用的服务发现系统
k8s1.3版本,kubeDNS
k8s1.11版本,CoreDNS
2. 服务发现方式
1. 环境变量
1. kubernetes service环境变量
在同一名称空间中创建的Pod对象都会自动拥有下面的变量
{SVCNAME}_SERVICE_HOST
{SVCNAME}_SERVICE_PORT
2. Docker Link形式的环境变量
[java@jar-wms-75dc8bbc7f-skz9c ansible]$ printenv | grep JAR JAR_WMS_PORT=tcp://10.1.45.135:80 JAR_WMS_PORT_80_TCP_PORT=80 JAR_WMS_PORT_80_TCP_ADDR=10.1.45.135 JAR_WMS_PORT_80_TCP=tcp://10.1.45.135:80 JAR_WMS_PORT_80_TCP_PROTO=tcp
2. DNS记录
1. 拥有ClusterIP的service资源,需要具有以下的资源记录
A记录
SRV记录
PTR记录
2. Headless类型的service资源,需要具有以下的资源记录
A记录
SRV记录
PTR记录
3. ExternalName类型的service资源,需要具有以下的资源记录
CNAME记录
3. resolv.conf文件内容
nameserver 10.1.0.10 这是kube-dns的IP地址
search default.svc.cluster.local svc.cluster.local cluster.local
{NAMESPACE}.svc.{CLUSTER_DOMAIN}:如 default.svc.cluster.local
SVC. {CLUSTER DOMAIN }:如 svc.cluster.local
{CLUSTER_DOMAIN}:如 cluster.local
{WORK_NODE_DOMAIN}:如 ilinux.io
4. 服务暴露
service的IP仅在集群内可达,当一些服务需要暴露到外部网络中,就需要在集群的边缘为其添加一层转发机制,以实现将外部请求流量接入到集群的service资源之上,这种操作称为发布服务到外部网络中。
1. service类型
1. ClusterIP
此地址仅在集群内部可达,而无法被集群外部的客户端访问,为默认的Service类型
2. NodePort
在工作节点的IP地址上选择一个端口用于将集群外部的用户请求转发至目标Service的ClusterIP的Port,因此这种类型既可以像ClusterIP一样受到集群内客户端Pod的访问,也会接受集群外部客户端通过<NodeIP>:<NodePort>进行的请求。
默认端口范围:30000~32767
kind : Service
apiVersion : vl
metadata:
name : myapp-svc-nodeport
spec .
type: NodePort
selector :
app: myapp
ports :
- protocol : TCP
port : 80
targetPort : 80
nodePort: 32223
3. LoadBalancer
这种类型构建在NodePort类型之上,其通过cloud provider提供的负载均衡器将服务暴露到集群外部。
例如 Amazon 云计算环境中的 ELB 实例即为此类的负载均衡设备。 此类型的优势在于,它能够把来自 于集群外部客户端的请求调度至所有节点(或部分节点)的 NodePort 之上,而不是依赖于客户端 自行决定连接至哪个节点,从而避免了因客户端指定的节点故障而导致的服务不可用 。

kind : Service
apiVersion : vl
metadata:
name : myapp-svc-nodeport
spec .
type: LoadBalancer
selector :
app: myapp
ports :
- protocol : TCP
port : 80
targetPort : 80
nodePort: 32223
4. ExternalName
此种类型并非定义由 Kubemetes 集群提供的服务,而是把集群外部的某服务以 DNSCNAME记录的方式映射到集群内,从而让集群内的 Pod 资源能够访问外部的 Service 的一种实现方式
apiVersion : vl
metadata :
name : external-redis-svc
namespace : default
spec:
type : ExternalName
externalName : redis.ilinux.io
ports :
- protocol : TCP
port : 6379
targetPort : 6379
nodePort : 0
selector : {}
5. Headless类型的Service资源
客户端要求直接访问Service资源后端的所有pod资源,这时就应该向客户端暴露每个pod资源的IP地址,而不再是中间层Service对象的ClusterIP,这种类型的Service资源便称为Headless Service
1. 创建Headless Service资源
kind : Service
apiVersion : v1
metadata :
name : myapp-headless-svc
spec:
clusterIP: None
selector :
app : myapp
ports :
- port : 80
targetPort : 80
name : httpport
2. 查看Headless资源的endpoints记录
kubectl describe svc myapp-headless-svc
3. pod资源发现
Headless Service通过标签选择器关联到所有pod资源的IP地址
客户端向Headless Service对象发起的请求将直接接入到pod资源中的应用之上,而不再由Service资源进行代理转发,它每次接入的pod资源则由DNS服务器接收到查询请求时以轮询的方式返回的IP地址
6. Ingress资源
1. Ingress为解决NodePort的痛点
痛点
1. 端口冲突
2. NodePort只支持四层
pod与ingress的关联
1. 通过service关联pod
2. 基于域名访问
3. 通过ingress controller实现pod的负载均衡
支持TCP/UDP四层以及HTTP七层
2. Ingress和Ingress Controller
一组基于DNS名称或URL路径将请求转发至指定的Service资源的规则,用于将集群外部的请求流量转发至集群内部完成服务发布,这个就叫做Ingress
能够为Ingress资源监听套接字并转发流量的组件称为Ingress控制器,这个控制器不是kube-controller-manager的一部分,它是一个附件,需要在集群上单独部署
3. 创建Ingress资源
apiVersion : extensions/v1beta1
kind : Ingress
metadata :
name : my-ingress
annotations :
kubernetes.io/ingress.class : "nginx"
spec:
rules :
- host : www.ilinux.io
http :
paths :
- backend :
serviceName : myapp-svc
servicePort: 80
4. Ingress资源类型
1. 单Service资源型Ingress
apiVersion : extensions / v1beta1
kind : Ingress
metadata :
name: my-ingress
spec :
backend:
serviceNarne: my-svc
servicePort : 80
2. 基于URL路径进行流量分发
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/cors-allow-credentials: 'true'
nginx.ingress.kubernetes.io/cors-allow-headers: >-
DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization
nginx.ingress.kubernetes.io/cors-allow-methods: 'PUT, GET, POST, OPTIONS'
nginx.ingress.kubernetes.io/cors-allow-origin: '*'
nginx.ingress.kubernetes.io/enable-cors: 'true'
nginx.ingress.kubernetes.io/service-weight: ''
creationTimestamp: '2019-06-27T12:36:08Z'
generation: 1
name: http-back
spec:
rules:
- host: back.dev.zp.com
http: &http_rules
paths:
- path: /adapter
backend:
serviceName: tomcat-adapter
servicePort: 80
- path: /backend
backend:
serviceName: tomcat-backend
servicePort: 80
3. 基于主机名称的虚拟主机
apiVersion : extensions/v1beta1
kind : Ingress
metadata :
name: test
spec :
rules :
- host: api.ik8s.io
http :
paths :
- backend :
serviceName: api
servicePort : 80
- host : wap.ik8s.io
http :
paths :
- backend :
serviceName : wap
servicePort : 80
4. TLS类型的Ingress资源
apiVersion : extensions/v1beta1
kind : Ingress
metadata:
name : no-rules map
spec :
tls :
- secretName: ikubernetesSecret
backend :
serviceName : homesite
servicePort : 80
5. 部署Ingress Controller
apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
data:
# 获取真实IP地址
compute-full-forwarded-for: "true"
forwarded-for-header: "X-Forwarded-For"
use-forwarded-headers: "true"
# nginx-ingress前端还有代理,后端获取真实IP,前端代理务必配置,配置方法如下URL
# https://a5pop.com/container/2020-06-04-nginx-ingress-real-ip.html
# 如果ingress的前端代理不设置,后端会无法访问,报错broken header
# proxy-real-ip-cidr: 10.127.0.10
use-proxy-protocol: "true"
# 客户端请求头的缓冲区大小
client-header-buffer-size: "512k"
# 设置用于读取大型客户端请求标头的最大值number和size缓冲区
large-client-header-buffers: "4 512k"
# 读取客户端请求body的缓冲区大小
client-body-buffer-size: "128k"
# 代理缓冲区大小
proxy-buffer-size: "256k"
# 代理body大小
proxy-body-size: "50m"
# 服务器名称哈希大小
server-name-hash-bucket-size: "128"
# map哈希大小
map-hash-bucket-size: "128"
# SSL加密套件
ssl-ciphers: "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
# ssl 协议
#ssl-protocols: "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 SSLv2 SSLv3"
ssl-protocols: "TLSv1.2 TLSv1.3"
# http重定向https
#ssl-redirect: 'true'
# 日志格式
log-format-upstream: '[$the_real_ip] - $remote_user [$time_local] "$request" $status $body_bytes_sent $request_time "$http_referer" $host DIRECT/$upstream_addr $upstream_http_content_type "$http_user_agent" "$http_x_forwarded_for" $request_length [$proxy_upstream_name] $upstream_response_length $upstream_response_time $upstream_status $req_id'
---
kind: ConfigMap
apiVersion: v1
metadata:
name: tcp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: udp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: nginx-ingress-clusterrole
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- nodes
- pods
- secrets
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- apiGroups:
- "extensions"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- "extensions"
resources:
- ingresses/status
verbs:
- update
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: nginx-ingress-role
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- pods
- secrets
- namespaces
verbs:
- get
- apiGroups:
- ""
resources:
- configmaps
resourceNames:
# Defaults to "<election-id>-<ingress-class>"
# Here: "<ingress-controller-leader>-<nginx>"
# This has to be adapted if you change either parameter
# when launching the nginx-ingress-controller.
- "ingress-controller-leader-nginx"
verbs:
- get
- update
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: nginx-ingress-role-nisa-binding
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: nginx-ingress-role
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: nginx-ingress-clusterrole-nisa-binding
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-clusterrole
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
apiVersion: apps/v1
#kind: Deployment
kind: DaemonSet
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
# replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
spec:
serviceAccountName: nginx-ingress-serviceaccount
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.24.1
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: 1000m
memory: 1000Mi
requests:
cpu: 30m
memory: 50Mi
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --publish-service=$(POD_NAMESPACE)/ingress-nginx
- --annotations-prefix=nginx.ingress.kubernetes.io
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# www-data -> 33
runAsUser: 33
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
#hostPort: 80
- name: https
containerPort: 443
#hostPort: 443
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
kubectl apply -f nginx-ingress.yaml
kubectl get pods -n ingress-nginx -o wide 查看ingress控制器所在的节点服务器
6. 使用ingress暴露https应用
1. 拿到域名的私钥和公钥 如果有购买,供应商会给你证书和私钥;如果没有就自己做一个自签名证书
2. kubectl create secret tls wzlinux-secret --cert=wzlinux.crt --key=wzlinux.key
3. 查看创建好的secret
kubectl describe secret wzlinux-secret
4. 创建ingress资源
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: wzlinux-ingress
spec:
tls:
- hosts:
- www.wzlinux.com
secretName: wzlinux-secret
rules:
- host: www.wzlinux.com
http:
paths:
- path: /
backend:
serviceName: wzlinux-svc
servicePort: 8080
5. 通过浏览器访问https://www.wzlinux.com
6. 查看ingress-controller的内容
kubectl get pods -n ingress-nginx -o wide 获取ingress-controller的名称
kubectl exec -it nginx-ingress-controller-2xdz8 bash -n ingress-nginx 登录到指定的ingress-controller中
可以看到server模块中的test.zhen.net和test2.zhen.net的内容

浙公网安备 33010602011771号