service
什么是service
Pod存在生命周期,有销毁,有重建,
无法提供一个固定的访问接
口给客户端。并且为了同类的Pod都能够实现工作负载的价值,由此Service资源出现了,可以为一类Pod资源对象提供一个固定的访问接口
和负载均衡
,类似于阿里云的负载均衡或者是LVS的功能。
但是要知道的是,Service和Pod对象的IP地址,一个是虚拟地址,一个是Pod IP地址,都仅
仅在集群内部可以进行访问
,无法接入集群外部流量。而为了解决该类问题的办法可以是在单一的节点上做端口暴露
(hostPort)以及让Pod资源共享工作节点的网络名称空间(hostNetwork)以外,还可以使用NodePort
或者是LoadBalancer
类型的Service资源,或者是有7层负载均衡能力的Ingress
资源。
Service的作用:
暴露流量
: 让用户可以通过ServiceIP+ServicePort访问对应后端的Pod应用;负载均衡
: 提供基于4层的TCP/IP负载均衡,并不提供HTTP/HTTPS等负载均衡;服务发现
: 当发现新增Pod则自动加入至Service的后端,如发现Pod异常则自动从Service后端删除Pod的IP
暴露流量
-
ClusterIP: 这是默认的Service类型。它会创建一个虚拟IP地址,供集群内部的Pod访问。这种类型的Service只能在
集群内部访问
,无法从集群外部访问。 -
NodePort: 这种Service类型除了分配一个集群IP外,还会在每个Node的某个端口(非著名端口)上暴露服务。
集群外部可以通过访问任意Node的该端口来访问
服务。 -
LoadBalancer: 这是公有云环境下常用的Service类型。它不仅会分配集群IP,还会自动在公有云上
创建一个负载均衡器
,暴露一个外部可访问的IP
地址。 -
ExternalName: 这种Service类型不会分配集群IP,而是
将服务映射到一个外部的DNS名称
。当集群内部访问这种Service时,会自动解析到外部服务的地址
。 -
Headless Service: 这种Service不会分配集群IP,而是直接
将后端的Pod IP暴露给客户端
。适用于不需要负载均衡的场景,比如DNS服务
等。
负载均衡
-
Round-Robin 负载均衡:
- 默认情况下,Service会使用Round-Robin的方式在后端的Pod之间分配流量。
- 这种方式简单快捷,能够
将流量平均分配
到各个Pod上。
-
会话亲和性:
- Service支持基于客户端IP的会话亲和性。
- 对于同一个客户端IP,后续的请求会被转发到之前处理该客户端请求的Pod上。
- 适用于需要
保持会话状态
的应用程序。
-
健康检查与自动故障转移:
- Service会定期检查后端Pod的健康状态。
- 当某个Pod发生故障时,Service会自动将其从负载均衡池中剔除,将流量转发到健康的Pod上。
- 这可以
提高应用程序的可用性和容错性
。
服务发现
-
说明
- Kubernetes 服务发现通过创建和管理服务(
Service
)资源来实现。服务资源定义了如何访问一组 Pod,这些 Pod 通常执行相同的任务
。 - 通过
标签选择器
筛选同一名称空间下的 Pod 资源 的 标签,完成 Pod 筛选。- 例如,定义一个服务时,可以指定一个标签选择器,服务会自动发现并包含所有匹配该标签的 Pod。
- Kubernetes
内部 DNS
服务器会自动为每个服务创建一个DNS 条目
,使得其他 Pod 可以通过服务名称进行访问
。
- Kubernetes 服务发现通过创建和管理服务(
-
本质
- 当一个
Service
资源创建后,Kubernetes 会自动创建
一个与该 Service 同名
的Endpoint
或EndpointSlice
资源。 Endpoint
和EndpointSlice
资源包含了服务所对应的Pod 的 IP 地址和端口信息
。
- 当一个
sequenceDiagram participant User participant API_Server participant Service participant Controller participant Endpoint User ->> API_Server: 创建 Service 资源 API_Server ->> Service: 创建 Service 对象 API_Server ->> Controller: 通知创建 Service Controller ->> API_Server: 创建与 Service 同名的 Endpoint 或 EndpointSlice API_Server ->> Endpoint: 包含 Pod 的 IP 地址和端口信息 loop 监视 Pod 的变化 Controller ->> API_Server: 监视 Pod 的变化 API_Server ->> Controller: Pod 状态变化通知 Controller ->> Endpoint: 更新 Endpoint 或 EndpointSlice 资源 end
标签选择器
标签:附加在资源对象上的
键值型元数据
(pod.metadata.labels
)
!!! tip 标签格式
- 键标识:由“键前缀(可选)”和“键名”组成,格式为
[key_prefix]/key_name
键前缀 key_prefix
必须使用DNS域名格式键名 key_name
的命名格式:支持字母
、数字
、连接号
、下划线
和点号
,且只能以字母或数字开头
;最长63个字符;
- “kubectl label”命令可管理对象的标签
!!!
!!! tip 标签选择器 -- 支持类型
- 基于
等值关系
=
、==
、!=
- 基于
集合关系
-
in
: 表示key
在value
集合里即命中- KEY in (VALUE1, VALUE2, …)
spec: selector: matchExpressions: - key: app operator: In values: - web - db
- KEY in (VALUE1, VALUE2, …)
-
notin
: 表示key
不在value
集合里即命中- KEY notin (VALUE1, VALUE2, …)
spec: selector: matchExpressions: - key: app operator: NotIn values: - web - db
- KEY notin (VALUE1, VALUE2, …)
-
exists
: 筛选存在某个标签
的资源,而不关心标签的具体值- KEY1
spec: selector: matchExpressions: - key: app operator: Exists
- KEY1
-
!!!
服务注册
服务注册
是服务发现
过程中的一个关键步骤。
服务注册
可以被视为服务发现
实现的一个方式或步骤。
-
本质
- 控制器会不断
监视 Pod 的变化
,确保 Endpoint 或 EndpointSlice 资源始终准确反映当前状态。
- 控制器会不断
-
工作方式
-
注册服务:
- 当用户创建一个 Service 资源时,Kubernetes 自动注册该服务,即创建一个与服务同名的 Endpoint 或 EndpointSlice 资源。
- 这些资源包含了所有匹配服务标签选择器的 Pod 的 IP 地址和端口信息。
-
更新服务信息:
- 控制器持续监视 Pod 的变化(如新增、删除、更新等),并相应地更新 Endpoint 或 EndpointSlice 资源。
- 确保服务始终反映当前的实际 Pod 状态。
-
服务发现机制:
- Kubernetes 内部的 DNS 服务器会为每个服务创建一个 DNS 条目,这使得其他 Pod 可以通过服务名称进行访问。
- 当一个 Pod 需要访问某个服务时,DNS 解析服务名称并返回与该服务关联的 Pod 的 IP 地址,从而实现服务发现。
-
资源规范
apiVersion: v1
kind: Service
metadata:
name: …
namespace: …
spec:
# Service类型,默认为ClusterIP
type <string>
# 等值类型的标签选择器,内含“与”逻辑
selector <map[string]string>
# Service的端口对象列表
ports:
# 端口名称
- name <string>
# 协议,目前仅支持TCP、UDP和SCTP,默认为TCP
protocol <string>
# Service的端口号
port <integer>
# 后端目标进程的端口号或名称,名称需由Pod规范定义
targetPort <string>
# 节点端口号,仅适用于NodePort和LoadBalancer类型
nodePort <integer>
# Service的集群IP,建议由系统自动分配
clusterIP <string>
# 外部流量策略处理方式
# Local表示由当前节点处理,Cluster表示向集群范围调度
externalTrafficPolicy <string>
# 外部负载均衡器使用的IP地址,仅适用于LoadBlancer
loadBalancerIP <string>
# 外部服务名称,该名称将作为Service的DNS CNAME值
externalName <string>
层次说明
负载均衡器入口:ClusterIP及相关的Service Port、NodePort
标签选择器:用于筛选Pod,并基于筛选出的Pod的IP生成后端端点列表
service 工作
工作逻辑
- 创建service任务下发
- service通过
label selector
获取匹配的pod - 将匹配的pod汇总到
endpoint
对象中(pod IP,端口信息)进行维护
具体实现
1.创建Service资源后,会分配一个随机的ServiceIP
,返回给用户,然后写入etcd
;
2.endpoints-controller
负责生成和维护所有endpoints
,它会监听Service和Pod的状态
,当Pod处于running且准备就绪时
,endpoints-controller
会将Pod IP更新对应Service的endpoints对象
中,然后写入Etcd
;
3.kube-proxy
通过API-Server
监听Service
,Endpoints
的资源变动,一旦Service或Endpoints
资源发生变化,kube-proxy
会将最新的信息转换为对应的iptables/ipvs
访问规则,而后在本地主机上执行;
4.当客户端访问Service时,会经由iptables/ipvs
规则,路由到对应节点;
service 创建 通过 label selector 选定 pod
endpoint 隐含创建 监听 service 和 pod状态, 记录IP端口信息
kube-proxy 监听 service和endpoint 状态,创建对应的 iptables/ipvs 路由规则client 访问 service 查询endpoint,通过 iptables/ipvs 规则最总到达pod, 资源创建信息均写入etcd
kube-proxy模型
kube-proxy 负责为 Service 实现了一种
VIP
(虚拟 IP)的形式,而不是ExternalName
的形式。
Kubernetes v1.0 版本,代理完全在userspace
Kubernetes v1.1 版本,新增了iptables
代理1.2
中设定为默认模式
Kubernetes v1.8.0-beta.0中,添加了ipvs
代理1.11
设为为默认模式
iptables/ipvs 被定位为 (TCP/UDP over IP)4层服务
Kubernetes v1.1 版本,新增了 Ingress API(beta 版),引入7层 (HTTP)服务
kube-proxy 这个组件始终监视着apiserver中有关Service的变动信息,获取任何一个与Service资源相关的变动状态,通过watch监视,一旦有Service资源相关的变动和创建,kube-proxy都要
转换为当前节点上的能够实现资源调度规则
。
userspace mode
当客户端Pod请求内核空间的
Service iptables
后,把请求转到给用户空间监听的kube-proxy
的端口,由kube-proxy
来处理后,再由kube-proxy
将请求转给内核空间的Service ip
,再由Service iptalbes
根据请求转给各节点中的的Service pod
。
问题:
由客户端请求先进入内核空间的,又进去用户空间访问kube-proxy,由kube-proxy封装完成后再进去内核空间的iptables,再根据iptables的规则分发给各节点的用户空间的pod。这样流量从用户空间进出内核带来的性能损耗是不可接受的。在Kubernetes 1.1版本之前,userspace是默认的代理模型。
iptables mode
kube-proxy为service后端的所有pod创建对应的iptables规则,当用户向serviceIP发送请求.
1.首先iptables会拦截用户请求
2.然后直接将请求调度到后端的Pod
优化:
减少再调度过程中,内核空间和用户空间的多次切换。
问题:
一个Service会创建出大量的规则
,且不支持更高级的调度算法
,当Pod不可用也无法重试。
ipvs mode
客户端IP请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。kube-proxy会监视Kubernetes Service对象和Endpoints,调用
netlink接口以相应地创建ipvs规则
并定期与Kubernetes Service对象和Endpoints对象同步ipvs规则
,以确保ipvs状态与期望一致。ipvs为负载均衡算法提供了更多选项
- rr:轮询调度
- lc:最小连接数
- dh:目标哈希
- sh:源哈希
- sed:最短期望延迟
- nq:不排队调度
优化:
提供了丰富的负载均衡调度算法,对service和endpoint的监视有助于同步规则减少故障。
!!! warning 降级注意
ipvs模式假定在运行kube-proxy
之前在节点上都已经安装了IPVS内核模块
。当kube-proxy以ipvs代理模式启动时
,kube-proxy将验证节点上是否安装了IPVS模块,如果未安装则kube-proxy将回退到iptables代理模式
。
!!!
ClusterIP
通过
集群的内部IP暴露服务
,选择ServiceIP只能在集群内部访问。这也是默认的ServiceType.
ClusterIP
: This is thedefault ServiceType
. This makes the Service only reachable from within the cluster and allows applications within the cluster to communicate with each other. There isno external access
.
- 接入方式
支持Service_IP:Service_Port接入;
serviceIP
即clusterIP
!!! info 实例信息
-
命令执行
kubectl create service clusterip NAME [--tcp=<port>:<targetPort>] [--dry-run=server|client|none] [options]
-
案例
创建一个名为
demoapp
的ClusterIP
类型的service
标签过滤器选中app=demoapp
的 pod
pod由80(targetPort)/tcp
端口提供服务
clusterIP
也从80(port)
端口提供服务
kind: Service
apiVersion: v1
metadata:
name: demoapp
spec:
# 类型标识,默认即为ClusterIP;
type: ClusterIP
selector:
app: demoapp
# 端口名称标识
ports:
- name: http
# 协议,支持TCP、UDP和SCTP
protocol: TCP
# Service的端口号
port: 80
# 目标端口号,即后端端点提供服务的监听端口号
targetPort: 80
!!!
NodePort
NodePort类型是对ClusterIP类型Service资源的扩展。它通过每个节点上的IP和端口接入集群外部流量,并分发给后端的Pod处理和响应。因此通过
<节点IP>:<节点端口>
,可以从集群外部访问服务。
NodePort
: This allows the external traffic to access the Service byopening a specific port
on all the nodes. Any traffic that is sent to this Port is then forwarded to the Service.
- 接入方式
支持Node_IP:Node_Port接入,同时支持ClusterIP;
!!! info 实例信息
-
命令执行
kubectl create service nodeport NAME [--tcp=port:targetPort] [--dry-run=server|client|none] [options]
-
案例
创建一个
demoapp
的nodePort
类型的service
通过app:demoapp
进行标签选择
在node
上使用30080
端口提供服务映射到cluster
的80端口,到pod
上的80
端口
对于nodePort
仅能使用30000
以上的端口
终端用户通过访问node
的30080端口进行请求
kind: Service
apiVersion: v1
metadata:
name: demoapp
spec:
# 必须明确给出Service类型
type: NodePort
selector:
app: demoapp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
# 可选,为避免冲突,建议由系统动态分配
nodePort: 30080
!!!
LoadBalancer
依赖云厂商,需要通过
云厂商调用API接口
创建软件负载均衡将服务暴露到集群外部。当创建LoadBalance
类型的Service对象
时,它会在集群上自动创建一个NodePort
类型的Service
。集群外部的请求流量会先路由至该负载均衡,并由该负载均衡调度至各个节点的NodePort
。
LoadBalancer
: This ServiceType exposes the Services externally using thecloud provider’s load balancer
. Traffic from the external load balancer is directed at the backend Pods. The cloud provider decides how it is load-balanced.
NodePort存在着几个方面的问题
非知名端口、私网IP地址、节点故障转移、节点间负载均衡、识别能适配到某Service的Local流量策略的节点等
外置的Cloud Load Balancer可以解决以上诸问题
- 接入方式
支持通过外部的LoadBalancer的LB_IP:LB_Port接入,同时支持NodePort和ClusterIP;
LoadBalancer
需与相关的NodePort的Service生命周期联动LoadBalancer
应该是由软件定义- Kubernetes需要同
LoadBalancer
所属的管理API联动
!!! info 实例信息
-
命令执行
kubectl create service loadbalancer NAME [--tcp=port:targetPort] [--dry-run=server|client|none] [options]
-
案例
创建一个名为
my-web-service
的loadbalancer
对外服务的端口为80,pod
的端口为8080
pod需要具备的标签为app:web-app
apiVersion: v1
kind: Service
metadata:
name: my-web-service
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: web-app
!!!
ExternalName
ExternalName: 此类型不是用来定义如何访问集群内服务的,而是把集群外部的某些服务以DNS CNAME方式映射到集群内,从而
让集群内的Pod资源能够访问外部服务的一种实现方式
ExternalName
: This type of Service maps a Service to a DNS name by using the contents of the externalName field by returning aCNAME record
with its value. No proxying of any kind is set up.
- 说明
- 负责将集群外部的服务引入到集群中
- 需要借助于ClusterDNS上的CNAME资源记录完成
- 特殊类型,无需ClusterIP和NodePort
- 无须定义标签选择器发现Pod对象
!!! info 实例信息
-
命令执行
kubectl create service externalname NAME --external-name external.name [--dry-run=server|client|none] [options]
-
场景
- 应用程序需要访问集群外部的服务,但又不想直接对外部 URL 进行硬编码。
- 可以使用 Service 提供一个稳定的内部域名,来访问外部服务。
- 当外部服务的 URL 变更时,只需要更新 externalName 字段即可,应用程序无需修改。
-
案例
创建名为
my-service
的externalName
service
当 Kubernetes 集群内部的应用访问my-service
这个 Service 时,它实际上会被解析到docs.alfie.com
这个外部 DNS 名称,从而访问到集群外部的服务。
例如: kubectl exec -it my-app -- /bin/bash
curl http://my-service --> 访问的为 docs.alfie.com
或通dig 查看dig myservice.default.svc.cluster.local @10.96.0.10 +short docs.alfie.com
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: docs.alfie.com
!!!
访问权限
创建token认证用户
-
创建
令牌认证文件
!!! abstract 认证文件
文件格式为CSV,每行定义一个用户:格式 :
token,user,uid,"group1,group2,group3"
<用户组为可选字段>token 生成方式参考:
echo "$(openssl rand -hex 3).$(openssl rand -hex 8)"cat /etc/kubernetes/pki/token.csv fab5ec.0c682bef86c83ad9,alfie,100 8f8340.89a0c3cd39acbd5d,leslie,101 7a27bf.cdd7e425bac85e03,tom,102
!!!
-
apiserver
开启token
认证# 编辑apiserver配置 (应先进行备份) vim /etc/kubernetes/manifests/kube-apiserver.yaml # 添加配置 spec: containers: - command: - --token-auth-file=/etc/kubernetes/pki/token.csv ...
-
client
使用toekn
认证curl -k -H "Authorization: Bearer fab5ec.0c682bef86c83ad9" -k \ https://API_SERVER:6443/api/v1/namespaces/default/pods/ { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "pods is forbidden: User \"alfie\" cannot list resource \"pods\" in API group \"\" in the namespace \"default\"", "reason": "Forbidden", "details": { "kind": "pods" }, "code": 403 }
创建身份凭据
-
定义Cluster
提供包括集群名称、API Server URL和信任的CA的证书相关的配置;clusters配置段中的各列表项名称需要惟一
# 设置名称为 kube-demo 的 cluster 输出配置文件到 `$HOME/.kube/kubeusers.conf` kubectl config set-cluster kube-demo \ --embed-certs=true \ --kubeconfig=$HOME/.kube/kubeusers.conf \ --server="https://API_SERVER:6443" \ --certificate-authority=/etc/kubernetes/pki/ca.crt
-
定义User
添加身份凭据,使用静态令牌文件认证的客户端提供令牌令牌即可
LESLIE_TOKEN='8f8340.89a0c3cd39acbd5d' kubectl config set-credentials leslie \ --token="$LESLIE_TOKEN" \ --kubeconfig=$HOME/.kube/kubeusers.conf
-
定义Context
为用户
leslie
的身份凭据与kube-demo
集群建立映射关系名称leslie@kube-demo
kubectl config set-context leslie@kube-demo \ --user=leslie \ --cluster=kube-demo \ --kubeconfig=$HOME/.kube/kubeusers.conf
-
设定Current-Context
kubectl config use-context leslie@kube-demo \ --kubeconfig=$HOME/.kube/kubeusers.conf
-
查询
current-context
kubectl config current-context --kubeconfig ~/.kube/kubeusers.conf
-
使用凭据访问
kubectl get pod \ --context leslie@kube-demo \ --kubeconfig $HOME/.kube/kubeusers.conf
token用户对namespace
内pods
具有查看权限
-
创建role
kubectl create role default-ns-pod-view \ --verb list,watch --resource=pods \ --namespace=default --dry-run=client -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: creationTimestamp: null name: default-ns-pod-view namespace: default rules: - apiGroups: - "" resources: - pods verbs: - list - watch
-
创建rolebinding
kubectl create rolebinding leslie@default-ns-pod-view \ --role default-ns-pod-view \ --user leslie \ --namespace default \ --dry-run=client -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: creationTimestamp: null name: leslie@default-ns-pod-view namespace: default roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: default-ns-pod-view subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: leslie
-
验证权限
kubectl get pod --kubeconfig $HOME/.kube/kubeusers.conf
kubectl get pod -A --kubeconfig $HOME/.kube/kubeusers.conf
ingress
初始化配置
demoapp-v10
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demoapp
name: demoapp-v10
spec:
replicas: 1
selector:
matchLabels:
app: demoapp
version: v1.0
strategy: {}
template:
metadata:
labels:
app: demoapp
version: v1.0
spec:
containers:
- image: ikubernetes/demoapp:v1.0
name: demoapp
resources: {}
---
apiVersion: v1
kind: Service
metadata:
labels:
app: demoapp
name: demoapp-v10
spec:
ports:
- name: http-80
port: 80
protocol: TCP
targetPort: 80
selector:
app: demoapp
version: v1.0
type: ClusterIP
demoapp-v11
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demoapp
name: demoapp-v11
spec:
replicas: 1
selector:
matchLabels:
app: demoapp
version: v1.1
strategy: {}
template:
metadata:
labels:
app: demoapp
version: v1.1
spec:
containers:
- image: ikubernetes/demoapp:v1.1
name: demoapp
resources: {}
---
apiVersion: v1
kind: Service
metadata:
labels:
app: demoapp
name: demoapp-v11
spec:
ports:
- name: http-80
port: 80
protocol: TCP
targetPort: 80
selector:
app: demoapp
version: v1.1
type: ClusterIP
基于URL路由
在同一个
FQDN
下通过不同的URI
完成不同应用间的流量分发
不需要
为每个应用配置专用的域名- 基于
单个虚拟主机
接收多个应用的流量 - 常用于将流量分发至同
一个应用下的多个不同子应用
,同一个应用内的流量由调度算法分发至该应用的各后端端点
案例
# 对于发往demoapp.alfie.com的请求,将“/v10”代理至service/demoapp10,将“/v11”代理至service/demoapp11
# 此处的重写是将所有的请求都重写到 svc 的 `/` 路径下
#> 此处重写功能是 http://example.com/foo/bar --> http://example.com/
kubectl create ingress demo \
--rule="demoapp.alfie.com/v10=demoapp-v10:80" \
--rule="demoapp.alfie.com/v11=demoapp-v11:80" \
--class=nginx --annotation nginx.ingress.kubernetes.io/rewrite-target="/" \
--dry-run=client -o yaml
# 查看创建的 demo ingress
kubectl describe ingress demo
Name: demo
Labels: <none>
Namespace: default
Address:
Ingress Class: nginx
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
demoapp.alfie.com
/v10 demoapp-v10:80 (10.244.1.35:80)
/v11 demoapp-v11:80 (10.244.2.16:80)
Annotations: nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 5s nginx-ingress-controller Scheduled for sync
# 修改本地hosts文件
tail -n 1 /etc/hosts
192.168.100.50 demoapp.alfie.com
# 访问测试
curl demoapp.alfie.com/v11
iKubernetes demoapp v1.1 !! ClientIP: 10.244.3.23, ServerName: demoapp-v11-5f64448cd5-8q72l, ServerIP: 10.244.2.16!
curl demoapp.alfie.com/v10
iKubernetes demoapp v1.0 !! ClientIP: 10.244.3.23, ServerName: demoapp-v10-7f44bbc8c5-rk826, ServerIP: 10.244.1.35!
基于主机名路由
每个应用使用一个
专有的主机名
,并基于这些名称完成不同应用间的流量转发
- 每个FQDN对应于
Ingress Controller
上的一个虚拟主机的定义 - 同一组内的应用的流量,由
Ingress Controller
根据调度算法完成请求调度
案例
# 对demoapp10.alfie.com的请求代理至service/demoapp10,对demoapp11.alfie.com请求代理至 service/demoapp11
# 此处的重写是将所有的请求都重写到 svc 的 `/` 路径下
#> 此处重写功能是 http://example.com/foo/bar --> http://example.com/
kubectl create ingress name-demo \
--rule="demoappv10.alfie.com/*=demoapp-v10:80" \
--rule="demoappv11.alfie.com/*=demoapp-v11:80" \
--class=nginx --dry-run=client -o yaml
# 查看ingress
k get ingress name-demo
NAME CLASS HOSTS ADDRESS PORTS AGE
name-demo nginx demoappv10.alfie.com,demoappv11.alfie.com 192.168.100.50 80 40s
# describe 查看 ingress
k describe ingress name-demo
Name: name-demo
Labels: <none>
Namespace: default
Address: 192.168.100.50
Ingress Class: nginx
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
demoappv10.alfie.com
/ demoapp-v10:80 (10.244.1.35:80)
demoappv11.alfie.com
/ demoapp-v11:80 (10.244.2.16:80)
Annotations: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 50s (x2 over 61s) nginx-ingress-controller Scheduled for sync
# 对hosts文件进行修改
tail -n 2 /etc/hosts
192.168.100.50 demoappv10.alfie.com
192.168.100.50 demoappv11.alfie.com
# 测试验证
curl demoappv10.alfie.com
iKubernetes demoapp v1.0 !! ClientIP: 10.244.3.23, ServerName: demoapp-v10-7f44bbc8c5-rk826, ServerIP: 10.244.1.35!
curl demoappv11.alfie.com
iKubernetes demoapp v1.1 !! ClientIP: 10.244.3.23, ServerName: demoapp-v11-5f64448cd5-8q72l, ServerIP: 10.244.2.16!
基于TLS路由
Ingress也可以提供TLS支持,但仅限于
443/TCP
端口
- 若TLS配置
部分指定了不同的主机
,则它们会根据通过SNI TLS
扩展指定的主机名- 前提:Ingress控制器
支持SNI在同一端口上复用
- 前提:Ingress控制器
TLS Secret
必须包含名为tls.crt
和 的密钥tls.key
,它们分别含有TLS的证书和私钥
案例
# 生成密钥
(umask 077; openssl genrsa -out alfie.key 2048)
# 自签名证书
# 注意此处证书CN 名称需要和 ingress使用访问的域名保持一致(demo.alfie.com)
openssl req -new -x509 \
-key alfie.key -out alfie.crt \
-subj /C=CN/ST=Guangzhou/L=Guangzhou/O=DevOps/CN=demo.alfie.com
# 创建 tls 类型的 secret 资源
kubectl create secret tls tls-alfie \
--cert=./alfie.crt --key=./alfie.key
# 创建ingress
# 启用tls后,该域名下的所有URI默认为强制将http请求跳转至https
# 关闭该功能:--annotation nginx.ingress.kubernetes.io/ssl-redirect=false
kubectl create ingress tls-demo \
--rule='demo.alfie.com/*=demoapp-v10:80,tls=tls-alfie' \
--class=nginx --dry-run=client -o yaml
# 查看ingress
kubectl get ingress tls-demo
NAME CLASS HOSTS ADDRESS PORTS AGE
tls-demo nginx demo.alfie.com 192.168.100.50 80, 443 4m59s
# describe 查看 ingress
kubectl describe ingress tls-demo
Name: tls-demo
Labels: <none>
Namespace: default
Address: 192.168.100.50
Ingress Class: nginx
Default backend: <default>
TLS:
tls-alfie terminates demo.alfie.com
Rules:
Host Path Backends
---- ---- --------
demo.alfie.com
/ demoapp-v10:80 (10.244.1.35:80)
Annotations: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 5m20s (x2 over 5m22s) nginx-ingress-controller Scheduled for sync
# 对hosts文件进行修改
tail -n 1 /etc/hosts
192.168.100.50 demo.alfie.com
# 访问测试
## 跳过TLS验证
curl -k https://demo.alfie.com
iKubernetes demoapp v1.0 !! ClientIP: 10.244.3.23, ServerName: demoapp-v10-7f44bbc8c5-rk826, ServerIP: 10.244.1.35!
## 使用证书验证
curl --cacert alfie.crt https://demo.alfie.com
iKubernetes demoapp v1.0 !! ClientIP: 10.244.3.23, ServerName: demoapp-v10-7f44bbc8c5-rk826, ServerIP: 10.244.1.35!
## 信任证书访问
cp alfie.crt /usr/local/share/ca-certificates/
update-ca-certificates --verbose --fresh
curl https://demo.alfie.com
iKubernetes demoapp v1.0 !! ClientIP: 10.244.3.23, ServerName: demoapp-v10-7f44bbc8c5-rk826, ServerIP: 10.244.1.35!
rewrite
对于七层路由规则,使用
host-path
路由时,path
作为一个抽象层无法也不需要与真实应用的URL
一一对应
因此就有机会需要将用户请求的URL
进行重写,例如清除path
,传递path
后续参数
案例
# 传递后续参数给service
# 此处我们对 `demoapp.alfie.com` 末尾的参数进行捕获,将/结尾后的内容作为也进行捕获
# 通过`annotation`书写rewrite规则,通过标识符引用将$2 (即/后的内容) 传递给 svc,让其继续进行解析
kubectl create ingress rewrite-demo \
--rule='demoapp.alfie.com/v10(/|$)(.*)=demoapp-v10:80' \
--rule='demoapp.alfie.com/v11(/|$)(.*)=demoapp-v11:80' \
--class=nginx --annotation nginx.ingress.kubernetes.io/rewrite-target='/$2'
# descripbe 查看 ingress
Name: rewrite-demo
Labels: <none>
Namespace: default
Address: 192.168.100.50
Ingress Class: nginx
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
demoapp.alfie.com
/v10(/|$)(.*) demoapp-v10:80 (10.244.1.35:80)
/v11(/|$)(.*) demoapp-v11:80 (10.244.2.16:80)
Annotations: nginx.ingress.kubernetes.io/rewrite-target: /$2
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 5s (x2 over 36s) nginx-ingress-controller Scheduled for sync
# 访问测试
curl demoapp.alfie.com/v10
iKubernetes demoapp v1.0 !! ClientIP: 10.244.3.23, ServerName: demoapp-v10-7f44bbc8c5-rk826, ServerIP: 10.244.1.35!
# 测试第二参数
curl demoapp.alfie.com/v10/hostname
ServerName: demoapp-v10-7f44bbc8c5-rk826