六、Service与Igress

6.1 Service

6.1.1 service简介

通过以前的学习,我们已经能够通过ReplicaSet来创建一组Pod来提供具有高可用性的服务。虽然每个Pod都会分配一个单独的Pod IP,然而却存在如下两问题:

  • Pod IP仅仅是集群内可见的虚拟IP,外部无法访问。
  • Pod IP会随着Pod的销毁而消失,当ReplicaSet对Pod进行动态伸缩时,Pod IP可能随时随地都会变化,这样对于我们访问这个服务带来了难度。

因此,Kubernetes中的Service对象就是解决以上问题的实现服务发现核心关键

Service可以看作是一组提供相同服务的Pod对外的访问接口,借助Service,应用可以方便地实现服务发现和负载均衡但是在使用上有以下限制:

  • 只能提供四层负载均衡能力,而没有7层的功能,但有时我们可以能需要更多的匹配规则来转发请求,这点上4层负载均衡是不支持的。

Service同其他Kubernetes对象一样,也是通过yaml或json文件进行定义。此外,它和其他Controller对象一样,通过Label Selector来确定一个Service将要使用哪些Pod。

一个简单的Service定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
labels:
run: nginx
name: nginx-service
spec:
ports:
- port: 80
protocol: TCP
targetPort: 81
selector:
app: nginx
type: ClusterIP

下面简单分析一下上面的Service描述文件:

  • 通过spec.selector字段确定这个Service将要使用哪些Label。在本例中,这个名为nginx的Service,将会管理所有具有app: nginxLabel的Pod。
  • spec.ports.port: 80表明此Service将会监听80端口,并将所有监听到的请求转发给其管理的Pod。spec.ports.targetPort: 81表明此Service监听到的80端口的请求都会被转发给其管理的Pod的81端口,此字段可以省略,省略后其值会被设置为spec.ports.port的值。
  • type: ClusterIP表面此Service的type(ExternalName | ClusterIP | NodePort | LoadBalancer)具体下面说。

6.1.2 Service的类型

在Serive定义时我们需要指定spec.type字段,这个字段拥有四个选项:

  • ClusterIP。默认值。给这个Service分配一个Cluster IP,它是Kubernetes系统自动分配的虚拟IP,因此只能在集群内部访问。

  • NodePort。将Service通过指定的Node上的端口暴露给外部。通过此方法,访问任意一个NodeIP:nodePort都将路由到ClusterIP,从而成功获得该服务。访问流程如下:

    Client—–>NodeIP:NodePort—–>ClusterIP:ServicePort—–>PodIP:ContainerPort

  • LoadBalancer。在 NodePort 的基础上,借助 cloud provider 创建一个外部的负载均衡器,并将请求转发到 :NodePort。此模式只能在云服务器(AWS等)上使用。

  • ExternalName。把集群外部的服务引入到集群内部来,在集群内部直接访问到。将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。需要 kube-dns 版本在 1.7 以上。

ClusterIP类型

clusterIP 主要在每个node节点使用iptables或者ipvs,将请求到clusterIP对应端口的数据,转发到kube-proxy中。然后kube-proxy自己内部实现有负载均衡的方法,并可以查询到这个service下对应pod的地址和端口,进而把数据转发给对应的 pod 地址和端口。

实现上图功能主要由以下几个组件协同:

apiserver:用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中

kube-proxy:kubenetes的每个节点中都有一个叫做kube-proxy的进程,这个进程负责感知service,pod的变化,并将

变化的信息写入到本地的iptables或者ipvs规则中去

ipvs或iptables:使用nat等技术将vip的流量转发至endpoint中

一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Service
metadata:
name: mynginx-svc
labels:
name: mynginx-svc
spec:
selector:
app: mynginx-pro
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP

# 假设运行3个标签为mynginx-pro的pod,此clusterIP类型的svc创建成功后,集群中任意一个节点访问clusterIP的80端口就可以访问请求负载到这个三个pod
NodePort类型

nodeport原理是在每一个集群节点的node上面开放一个端口号,将向该端口号的流量导入到kube-proxy,然后由kube-proxy通过iptables规则或者ipvs将流量转发到对应pod上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Service
metadata:
name: mynginx
namespace: default
spec:
selector:
app: mynginx-pro
app: nginx
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 32715

# 假设运行3个标签为mynginx-pro的pod,此NodePort创建成后,集群外主机访问集群中任意一个节点的32715端口,即可访问到这3个Pod的80端口(不指定nodePort这个参数将从30000-32767范围内依次随机获得端口号)
LoadBalancer类型

用于当K8s运行在一个云环境内时,若该云环境支持LBaaS,则此类型可自动触发创建一个软件负载均衡器用于对Service做负载均衡调度。

因为外部所有Client都访问一个NodeIP,该节点的压力将会很大, 而LoadBalancer则可解决这个问题。而且它还直接动态监测后端Node是否被移除或新增了,然后动态更新调度的节点数

ExternalName类型

这种类型的Service通过返回CNAME和它的值,可以将服务映射到externalName字段的内容(例如:hub.nnv5.cn)ExternalName Service是Service的特例,它没有selector,也没有定义任何端口和endpoint,相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。

以域名的方法将集群外部服务导入集群中访问
1
2
3
4
5
6
7
8
apiVersion: v1
kind: Service
metadata:
name: my-service-1
namespace: default
spec:
type: ExternalName
externalName: hub.nnv5.cn

当查询主机my-service-1.default.svc.cluster.local(svc_name.namespace.svc.cluster.local)时,集群的dns服务器返回一个值my.database.example.com的cname记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在dns层,而且不会进行代理或转发。

以ip的方式将集群外部服务导入到集群内部访问

示例:将192.168.1.21这台集群外的mysql服务导入到集群内,使其集群内的集群可以访问到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 第一步:创建一个mysql的无头service
~]$ vim mysql-svc.yaml
------------------------------------------------------
apiVersion: v1
kind: Service
metadata:
name: mysql-svc
labels:
name: mysql-svc
spec:
ports:
- name: mysql
port: 3306
targetPort: 3306
protocol: TCP
clusterIP: "None"

~]$ kubectl apply -f mysql-svc.yaml
~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql-svc ClusterIP None <none> 3306/TCP 10m


# 第二步:创建Endpoints对应上面的无头服务
~]# vim endpoints.svc.yaml
------------------------------------------------------
apiVersion: v1
kind: Endpoints
metadata:
name: mysql-svc
subsets:
- addresses:
- ip: 192.168.1.21
ports:
- port: 3306


# 第三步验证
集群内任意Pod访问mysql-svc:3306 即可访问到192.168.1.21的数据库,当mysql主机ip更改后,只需修改Endpoints的ip地址即可。

6.1.3 代理模式的分类

注:
  以上不论哪种,kube-proxy都通过watch的方式监控着kube-APIServer写入etcd中关于Pod的最新状态信息,
  它一旦检查到一个Pod资源被删除了 或 新建,它将立即将这些变化,反应再iptables 或 ipvs规则中,以便
  iptables和ipvs在调度Clinet Pod请求到Server Pod时,不会出现Server Pod不存在的情况。

  自k8s1.1以后,service默认使用ipvs规则,若ipvs没有被激活,则降级使用iptables规则. 但在1.1以前,service使用的模式默认为userspace.

6.2 Ingress和ingress controller

6.2.1 什么是ingress

从前面的学习,我们可以了解到Kubernetes暴露服务的方式目前只有三种:LoadBlancer Service、ExternalName、NodePort Service、Ingress;而我们需要将集群内服务提供外界访问就会产生以下几个问题:

Pod漂移问题

Kubernetes 具有强大的副本控制能力,能保证在任意副本(Pod)挂掉时自动从其他机器启动一个新的,还可以动态扩容等,通俗地说,这个 Pod 可能在任何时刻出现在任何节点上,也可能在任何时刻死在任何节点上;那么自然随着 Pod 的创建和销毁,Pod IP 肯定会动态变化;那么如何把这个动态的 Pod IP 暴露出去?这里借助于 Kubernetes 的 Service 机制,Service 可以以标签的形式选定一组带有指定标签的 Pod,并监控和自动负载他们的 Pod IP,那么我们向外暴露只暴露 Service IP 就行了;这就是 NodePort 模式:即在每个节点上开起一个端口,然后转发到内部 Pod IP 上,如下图所示:
此时的访问方式:http://nodeip:nodeport/

端口管理问题

采用 NodePort 方式暴露服务面临问题是,服务一旦多起来,NodePort 在每个节点上开启的端口会及其庞大,而且难以维护;这时,我们可以能否使用一个Nginx直接对内进行转发呢?众所周知的是,Pod与Pod之间是可以互相通信的,而Pod是可以共享宿主机的网络名称空间的,也就是说当在共享网络名称空间时,Pod上所监听的就是Node上的端口。那么这又该如何实现呢?简单的实现就是使用 DaemonSet 在每个 Node 上监听 80,然后写好规则,因为 Nginx 外面绑定了宿主机 80 端口(就像 NodePort),本身又在集群内,那么向后直接转发到相应 Service IP 就行了,如下图所示:

域名分配及动态更新问题

从上面的方法,采用 Nginx-Pod 似乎已经解决了问题,但是其实这里面有一个很大缺陷:当每次有新服务加入又该如何修改 Nginx 配置呢??我们知道使用Nginx可以通过虚拟主机域名进行区分不同的服务,而每个服务通过upstream进行定义不同的负载均衡池,再加上location进行负载均衡的反向代理,在日常使用中只需要修改nginx.conf即可实现,那在K8S中又该如何实现这种方式的调度呢???

假设后端的服务初始服务只有ecshop,后面增加了bbs和member服务,那么又该如何将这2个服务加入到Nginx-Pod进行调度呢?总不能每次手动改或者Rolling Update 前端 Nginx Pod 吧!!此时 Ingress 出现了,如果不算上面的Nginx,Ingress 包含两大组件:Ingress Controller 和 Ingress。

Ingress 简单的理解就是你原来需要改 Nginx 配置,然后配置各种域名对应哪个 Service,现在把这个动作抽象出来,变成一个 Ingress 对象,你可以用 yaml 创建,每次不要去改 Nginx 了,直接改 yaml 然后创建/更新就行了;那么问题来了:”Nginx 该怎么处理?”

Ingress Controller 这东西就是解决 “Nginx 的处理方式” 的;Ingress Controoler 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,然后读取他,按照他自己模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下,工作流程如下图:

实际上Ingress也是Kubernetes API的标准资源类型之一,它其实就是一组基于DNS名称(host)或URL路径把请求转发到指定的Service资源的规则。用于将集群外部的请求流量转发到集群内部完成的服务发布。我们需要明白的是,Ingress资源自身不能进行“流量穿透”,仅仅是一组规则的集合,这些集合规则还需要其他功能的辅助,比如监听某套接字,然后根据这些规则的匹配进行路由转发,这些能够为Ingress资源监听套接字并将流量转发的组件就是Ingress Controller。

PS:Ingress 控制器不同于Deployment 控制器的是,Ingress控制器不直接运行为kube-controller-manager的一部分,它仅仅是Kubernetes集群的一个附件,类似于CoreDNS,需要在集群上单独部署。

6.2.3 ingress资源类型

Ingress的资源类型有以下4种:

  • 1、单Service资源型Ingress
  • 2、基于URL路径进行流量转发
  • 3、基于主机名称的虚拟主机
  • 4、TLS类型的Ingress资源

单Service资源型ingress

1
2
3
4
5
6
7
8
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
backend:
serviceName: my-svc
servicePort: 80

Ingress控制器会为其分配一个IP地址接入请求流量,并将其转发至后端my-svc

6.2.3 部署ingress-nginx服务

Deployment的NodePort方式部署

nodePort的部署思路就是通过在每个节点上开辟nodePort的端口,将流量引入进来,而后通过iptables首先转发到ingress-controller容器中,而后由nginx根据ingress的规则进行判断,将其转发到对应的应用web容器中,这种方式部署的nginx域名后面需要加上随机的nodeport端口才能访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 下载ingress-nginx部署yaml文件
[root@k8s-master ~]$ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/deploy.yaml
[root@k8s-master ~]$ cat deploy.yaml | grep image
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.33.0
image: docker.io/jettech/kube-webhook-certgen:v1.2.0

# 手动下载ingress镜像
[root@k8s-master ~]$ docker pull docker.io/jettech/kube-webhook-certgen:v1.2.0
[root@k8s-master ~]$ docker pull quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.33.0

# 将镜像导出到复制到其他node节点
[root@k8s-master opt]$ docker save -o nginx-ingress-controller.tar.gz quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.33.0
[root@k8s-master opt]$ docker save -o kube-webhook-certgen.tar.gz jettech/kube-webhook-certgen:v1.2.0
[root@k8s-master opt]$ scp kube-webhook-certgen.tar.gz nginx-ingress-controller.tar.gz k8s-node01.nnv5.cn:/opt/
[root@k8s-master opt]$ scp kube-webhook-certgen.tar.gz nginx-ingress-controller.tar.gz k8s-node02.nnv5.cn:/opt/

# node节点导入镜像
[root@k8s-node02 opt]$ docker load -i kube-webhook-certgen.tar.gz
[root@k8s-node02 opt]$ docker load -i nginx-ingress-controller.tar.gz

# 部署ingress
[root@k8s-master ~]$ kubectl apply -f deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
configmap/ingress-nginx-controller created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
service/ingress-nginx-controller-admission created
service/ingress-nginx-controller created
deployment.apps/ingress-nginx-controller created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
serviceaccount/ingress-nginx-admission created

# 验证部署是否成功
~]$ kubectl get pods -o wide -n ingress-nginx
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-controller-58f68f5ccc-5v2qc 1/1 Running 0 10m 10.244.1.18 k8s-node01.nnv5.cn <none> <none>

# 有访问请求时访问ingress的NodePort的31101端口即访问容器的80端口 80:31101
# 有访问请求时访问ingress的NodePort的31123端口即访问容器的443端口 443:31123
~]$ kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.96.249.151 <none> 80:31101 /TCP,443:31123/TCP 84m
ingress-nginx-controller-admission ClusterIP 10.107.8.188 <none> 443/TCP 84m
DaemonSet的hostNetwork方式

1.直接将ingress-nginx部署的yaml下载下来

1
$ wget  https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/deploy.yaml

这个yaml中包含了很多资源的创建,包括namespece、configmap、role、serviceaccount等ingress-nginx需要的资源对象,配置太多就不粘帖出来了,我们重点看下deployment部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
helm.sh/chart: ingress-nginx-2.11.1
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 0.34.1
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: controller
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
revisionHistoryLimit: 10
minReadySeconds: 0
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
spec:
dnsPolicy: ClusterFirst
containers:
- name: controller
image: us.gcr.io/k8s-artifacts-prod/ingress-nginx/controller:v0.34.1@sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
args:
- /nginx-ingress-controller
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 101
allowPrivilegeEscalation: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
livenessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
ports:
- name: http
containerPort: 80
protocol: TCP
- name: https
containerPort: 443
protocol: TCP
- name: webhook
containerPort: 8443
protocol: TCP
volumeMounts:
- name: webhook-cert
mountPath: /usr/local/certificates/
readOnly: true
resources:
requests:
cpu: 100m
memory: 90Mi
serviceAccountName: ingress-nginx
terminationGracePeriodSeconds: 300
volumes:
- name: webhook-cert
secret:
secretName: ingress-nginx-admission

我们需要使用daemonset部署到特定node,需要修改部分配置:先给要部署nginx-ingress的node打上特定标签,这里测试部署再node01和node02上面

1
2
kubectl label  node k8s-node01.nnv5.cn isIngress="true"
kubectl label node k8s-node02.nnv5.cn isIngress="true"

然后修改上面deployment部署配置为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
helm.sh/chart: ingress-nginx-2.11.1
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 0.34.1
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: controller
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
# 删除replicas
# replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
revisionHistoryLimit: 10
minReadySeconds: 0
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
spec:
dnsPolicy: ClusterFirst
# 选择对应标签的node
nodeSelector:
isIngress: "true"
# 使用hostNetwork暴露服务
hostNetwork: true
containers:
- name: controller
image: us.gcr.io/k8s-artifacts-prod/ingress-nginx/controller:v0.34.1@sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
args:
- /nginx-ingress-controller
- --election-id=ingress-controller-leader
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=ingress-nginx/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 101
allowPrivilegeEscalation: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
livenessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
ports:
- name: http
containerPort: 80
protocol: TCP
- name: https
containerPort: 443
protocol: TCP
- name: webhook
containerPort: 8443
protocol: TCP
volumeMounts:
- name: webhook-cert
mountPath: /usr/local/certificates/
readOnly: true
resources:
requests:
cpu: 1m
memory: 90Mi
serviceAccountName: ingress-nginx
terminationGracePeriodSeconds: 300
volumes:
- name: webhook-cert
secret:
secretName: ingress-nginx-admission

然后应用并检查服务,由于配置的hostnetwork,nginx会在特定node本地主机上监听80/443/8181端口。其中8181是nginx-controller默认配置的一个default backend。这样,只要访问node主机有公网IP,就可以直接映射域名来对外暴露服务,如果要实现高可用的话,可以在多个node上部署,前面加一个负载均衡器。

例如你创建一个 www.nnv5.cn 这样域名的ingress

deployment方式部署的你需要获取ingress的svc的80端口映射的nodeport这样访问:http://www.nnv5.cn:32134

daemonset方式部署的你只需要将域名解析到特定节点上,直接访问域名就能到:http://www.nnv5.cn

6.2.4 如何创建ingress资源

Ingress资源时基于HTTP虚拟主机或URL的转发规则,需要强调的是,这是一条转发规则。它在资源配置清单中的spec字段中嵌套了rules、backend和tls等字段进行定义。如下示例中定义了一个Ingress资源,其包含了一个转发规则:将发往myapp.magedu.com的请求,代理给一个名字为myapp的Service资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: extensions/v1beta1		
kind: Ingress
metadata:
name: ingress-myapp
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: myapp.magedu.com
http:
paths:
- path: /
backend:
serviceName: myapp
servicePort: 80

Ingress 中的spec字段是Ingress资源的核心组成部分,主要包含以下3个字段:

  • rules:用于定义当前Ingress资源的转发规则列表;由rules定义规则,或没有匹配到规则时,所有的流量会转发到由backend定义的默认后端。

  • backend:默认的后端用于服务那些没有匹配到任何规则的请求;定义Ingress资源时,必须要定义backend或rules两者之一,该字段用于让负载均衡器指定一个全局默认的后端。

  • tls:TLS配置,目前仅支持通过默认端口443提供服务,如果要配置指定的列表成员指向不同的主机,则需要通过SNI TLS扩展机制来支持该功能。 backend对象的定义由2个必要的字段组成:serviceName和servicePort,分别用于指定流量转发的后端目标Service资源名称和端口。 rules对象由一系列的配置的Ingress资源的host规则组成,这些host规则用于将一个主机上的某个URL映射到相关后端Service对象,其定义格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-myapp
namespace: default
spec:
rules:
- hosts: <string>
http:
paths:
- path: <string>
backend:
serviceName: <string>
servicePort: <string>

需要注意的是,.spec.rules.host属性值,目前暂不支持使用IP地址定义,也不支持IP:Port的格式,该字段留空,代表着通配所有主机名。
tls对象由2个内嵌的字段组成,仅在定义TLS主机的转发规则上使用。

  • hosts: 包含 于 使用 的 TLS 证书 之内 的 主机 名称 字符串 列表, 因此, 此处 使用 的 主机 名 必须 匹配 tlsSecret 中的 名称。
  • secretName: 用于 引用 SSL 会话 的 secret 对象 名称, 在 基于 SNI 实现 多 主机 路 由 的 场景 中, 此 字段 为 可选

6.2.5 创建http类型ingress

创建一个deployment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-v1
labels:
deployment: nginx-v1
spec:
replicas: 3
selector:
matchLabels:
app: nginxv1
template:
metadata:
name: nginxv1
labels:
app: nginxv1
spec:
restartPolicy: Always
containers:
- name: nginx-01
image: hub.nnv5.cn/test/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
workingDir: /opt
---
apiVersion: v1
kind: Service
metadata:
name: nginxv1-svc
labels:
svc: nginxv1-svc
spec:
selector:
app: nginxv1
ports:
- name: web-80
port: 80
targetPort: 80
protocol: TCP
创建ingress关联svc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginxv1-ingress
labels:
ingress: nginxv1-ingress
spec:
rules:
- host: nginxv1.nnv5.cn
http:
paths:
- backend:
serviceName: nginxv1-svc
servicePort: 80
path: /
验证访问
1
2
3
4
5
6
7
8
# 查看ingress的nodeport端口号
]$ kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default nginxv1-svc ClusterIP 10.101.16.230 <none> 80/TCP 74m
ingress-nginx ingress-nginx-controller NodePort 10.96.249.151 <none> 80:31101/TCP,443:31123/TCP 105m

# client添加nginxv1.nnv5.cn的hosts解析记录
# 浏览器访问 `http://nginxv1.nnv5.cn:31101` 验证

6.2.6 创建https类型ingress

准备证书
1
2
3
4
5
6
[root@k8s-master ingress]# openssl genrsa -out tls.key 2048 
Generating RSA private key, 2048 bit long modulus
................................................................................................+++
.............................................................................................+++
e is 65537 (0x10001)
[root@k8s-master ingress]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=nginxv3.nnv5.cn
生成secret
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@k8s-master ingress]# kubectl create secret tls nginxv3-ingress-secret --cert=tls.crt --key=tls.key
secret/nginxv3-ingress-secret created
[root@k8s-master ingress]# kubectl get secret
NAME TYPE DATA AGE
default-token-zl497 kubernetes.io/service-account-token 3 25h
nginxv3-ingress-secret kubernetes.io/tls 2 3s
[root@k8s-master ingress]# kubectl describe secret nginxv3-ingress-secret
Name: nginxv3-ingress-secret
Namespace: default
Labels: <none>
Annotations: <none>

Type: kubernetes.io/tls

Data
====
tls.crt: 1289 bytes
tls.key: 1675 bytes
创建一个deployment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-v3
labels:
deployment: nginx-v3
spec:
replicas: 3
selector:
matchLabels:
app: nginxv3
template:
metadata:
name: nginxv3
labels:
app: nginxv3
spec:
restartPolicy: Always
containers:
- name: nginx-01
image: hub.nnv5.cn/test/myapp:v3
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
workingDir: /opt
---
apiVersion: v1
kind: Service
metadata:
name: nginxv3-svc
labels:
svc: nginxv3-svc
spec:
selector:
app: nginxv3
ports:
- name: web-80
port: 80
targetPort: 80
protocol: TCP
创建一个ingress
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginxv3-ingress
labels:
ingress: nginxv3-ingress
spec:
tls:
- hosts:
- nginxv3.nnv5.cn
secretName: nginxv3-ingress-secret
rules:
- host: nginxv3.nnv5.cn
http:
paths:
- backend:
serviceName: nginxv3-svc
servicePort: 80
path: /
验证访问
1
2
3
4
5
6
7
8
# 查看ingress的nodeport https端口号
]$ kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default nginxv1-svc ClusterIP 10.101.16.230 <none> 80/TCP 74m
ingress-nginx ingress-nginx-controller NodePort 10.96.249.151 <none> 80:31101/TCP,443:31123/TCP 105m

# client添加nginxv3.nnv5.cn的hosts解析记录
# 浏览器访问 `https://nginxv3.nnv5.cn:31123` 验证