k8s技术预研8--深入掌握Kubernetes Service

本文内容已经基于k8s v1.8.8进行了验证测试。
k8s的Service定义了一个服务的访问入口地址,前端的应用通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,来自外部的访问请求被负载均衡到后端的各个容器应用上。Service与其后端Pod副本集群之间则是通过Label Selector来实现对接的。而RC的作用相当于是保证Service的服务能力和服务质量始终处于预期的标准。Service 定义可以基于 POST 方式,请求 apiserver 创建新的实例。一个 Service 在 Kubernetes 中是一个 REST 对象。本文对Service的使用进行详细说明,包括Service的负载均衡、外网访问、DNS服务的搭建、Ingress7层路由机制等。 

1、Service定义详解

1.1 yaml格式的Service定义文件的完整内容

apiVersion: v1
kind: Service
matadata:
  name: string
  namespace: string
  labels:
  - name: string
  annotations:
  - name: string
spec:
  selector: []
  type: string
  clusterIP: string
  sessionAffinity: string
  ports:
  - name: string
    protocol: string
    port: int
    targetPort: int
    nodePort: int
  status:
    loadBalancer:
      ingress:
        ip: string
        hostname: string

1.2 对Service定义文件中各属性的说明表

属性名称
取值类型
是否必选
取值说明
version
string
Required
v1
kind
string
Required
Service
metadata
object
Required
元数据
metadata.name
string
Required
Service名称
metadata.namespace
string
Required
命名空间,默认为default
metadata.labels[]
list

自定义标签属性列表
metadata.annotation[]
list

自定义注解属性列表
spec
object
Required
详细描述
spec.selector[]
list
Required
Label Selector配置,将选择具有指定Label标签的Pod作为管理范围
spec.type
string
Required
Service的类型,指定Service的访问方式,默认值为ClusterIP。取值范围如下:
ClusterIP: 虚拟服务的IP,用于k8s集群内部的pod访问,在Node上kube-proxy通过设置的Iptables规则进行转发。
NodePort:使用宿主机的端口,使用能够访问各Node的外部客户端通过Node的IP地址和端口就能访问服务。
LoadBalancer: 使用外接负载均衡器完成到服务的负载分发,需要在spec.status.loadBalancer字段指定外部负载均衡器的IP地址
,并同时定义nodePort和clusterIP,用于公有云环境。
spec.clusterIP
string

虚拟服务的IP地址,当type=clusterIP时,如果不指定,则系统进行自动分配。也可以手工指定。当type=LoadBalancer时,则需要指定。
spec.sessionAffinity
string

是否支持Session,可选值为ClientIP,表示将同一个源IP地址的客户端访问请求都转发到同一个后端Pod。默认值为空。
spec.ports[]
list

Service需要暴露的端口列表
spec.ports[].name
string

端口名称
spec.ports[].protocol
string

端口协议,支持TCP和UDP,默认值为TCP
spec.ports[].port
int

服务监听的端口号
spec.ports[].targetPort
int

需要转发到后端Pod的端口号
spec.ports[].nodePort
int

当spec.type=NodePort时,指定映射到物理机的端口号
status
object

当spec.type=LoadBalancer时,设置外部负载均衡器的地址,用于公有云环境
status.loadBalancer
object

外部负载均衡器
status.loadBalancer.ingress
object

外部负载均衡器
status.loadBalancer.ingress.ip
string

外部负载均衡器的IP地址
status.loadBalancer.ingress.hostname
string

外部负载均衡器的主机名


2、Service,RC,Pod架构层次关系


3、VIP 和 Service 代理

运行在每个Node上的kube-proxy进程其实就是一个智能的软件负载均衡器,它会负责把对Service的请求转发到后端的某个Pod实例上并在内部实现服务的负载均衡与会话保持机制。Service不是共用一个负载均衡器的IP,而是被分配了一个全局唯一的虚拟IP地址,称为Cluster IP。在Service的整个生命周期内,它的Cluster IP不会改变。 kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。在k8s v1.2版本之前默认使用userspace提供vip代理服务,从 Kubernetes v1.2 起,默认是使用 iptables 代理。

iptables 代理模式
这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会创建相关 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。 对于每个 Endpoints 对象,它也会创建 iptables 规则,这个规则会选择一个 backend Pod。默认的策略是,随机选择一个 backend。 实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 "ClientIP" (默认值为 "None")。
和 userspace 代理类似,网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。 这应该比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始选择的 Pod 没有响应,iptables 代理能够自动地重试另一个 Pod,所以它需要依赖  readiness probes



4、 发布服务 —— type类型

对一些应用希望通过外部(Kubernetes 集群外部)IP 地址暴露 Service。
Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。
Type 的取值以及行为如下:
  • ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。
  • NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。
  • LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。
  • ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。

k8s中有3种IP地址:
  • Node IP: Node节点的IP地址,这是集群中每个节点的物理网卡的IP地址;
  • Pod IP: Pod的IP地址,这是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络;
  • Cluster IP:Service 的IP地址,这也是一个虚拟的IP,但它更像是一个“伪造”的IP地址,因为它没有一个实体网络对象,所以无法响应ping命令。它只能结合Service Port组成一个具体的通信服务端口,单独的Cluster IP不具备TCP/IP通信的基础。在k8s集群之内,Node IP网、Pod IP网与Cluster IP网之间的通信采用的是k8s自己设计的一种编程实现的特殊的路由规则,不同于常见的IP路由实现。

5、 服务发现

Kubernetes 支持2种基本的服务发现模式 —— 环境变量和 DNS。
环境变量
当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。 它同时支持 Docker links兼容 变量、简单的 {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 变量,这里 Service 的名称需大写,横线被转换成下划线。
举个例子,一个名称为 "redis-master" 的 Service 暴露了 TCP 端口 6379,同时给它分配了 Cluster IP 地址 10.0.0.11,这个 Service 生成了如下环境变量:
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
这意味着需要有顺序的要求 —— Pod 想要访问的任何 Service 必须在 Pod 自己之前被创建,否则这些环境变量就不会被赋值。DNS 并没有这个限制。
DNS
一个强烈推荐的集群插件 是 DNS 服务器。 DNS 服务器监视着创建新 Service 的 Kubernetes API,从而为每一个 Service 创建一组 DNS 记录。 如果整个集群的 DNS 一直被启用,那么所有的 Pod 应该能够自动对 Service 进行名称解析。
例如,有一个名称为 "my-service" 的 Service,它在 Kubernetes 集群中名为 "my-ns" 的 Namespace 中,为 "my-service.my-ns" 创建了一条 DNS 记录。 在名称为 "my-ns" 的 Namespace 中的 Pod 应该能够简单地通过名称查询找到 "my-service"。 在另一个 Namespace 中的 Pod 必须限定名称为 "my-service.my-ns"。 这些名称查询的结果是 Cluster IP。
Kubernetes 也支持对端口名称的 DNS SRV(Service)记录。 如果名称为 "my-service.my-ns" 的 Service 有一个名为 "http" 的 TCP 端口,可以对 "_http._tcp.my-service.my-ns" 执行 DNS SRV 查询,得到 "http" 的端口号。
Kubernetes DNS 服务器是唯一的一种能够访问 ExternalName 类型的 Service 的方式。 更多信息可以查看 DNS Pod 和 Service
Kubernetes 从 1.3 版本起, DNS 是内置的服务,通过插件管理器 集群插件 自动被启动。Kubernetes DNS 在集群中调度 DNS Pod 和 Service ,配置 kubelet 以通知个别容器使用 DNS Service 的 IP 解析 DNS 名字。

6、Service的基本用法

一般来说,对外提供服务的应用程序需要通过某种机制来实现,对于容器应用最简便的方式就是通过TCP/IP机制及监听IP和端口号来实现。
创建一个基本功能的Service
(1)例如,我们定义一个提供web服务的RC,由两个tomcat容器副本组成,每个容器通过containerPort设置提供服务号为8080:
webapp-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: webapp
spec:
  replicas: 2
  template:
    metadata:
      name: webapp
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: tomcat
        ports:
        - containerPort: 8080
创建该RC:
#kubectl create -f webapp-rc.yaml
获取Pod的IP地址:
#kubectl get pods -l app=webapp -o yaml|grep podIP
podIP:172.17.0.2
podIP:172.17.0.3
直接通过这两个Pod的IP地址和端口号访问Tomcat服务:
#curl 172.17.0.2:8080

直接通过Pod的IP地址和端口号可以访问容器内的应用服务,但是Pod的IP地址是不可靠的,例如Pod所在的Node发生故障,Pod将被k8s重新调度到另一台Node。Pod的IP地址将发生变化,更重要的是,如果容器应用本身是分布式的部署方式,通过多个实例共同提供服务,就需要在这些实例的前端设置一个负载均衡器来实现请求的分发。kubernetes中的Service就是设计出来用于解决这些问题的核心组件。

(2)为了让客户端应用能够访问到两个Tomcat Pod 实例,需要创建一个Service来提供服务
k8s提供了一种快速的方法,即通过kubectl expose命令来创建:
#kubectl expose rc webapp
查看新创建的Service可以看到系统为它分配了一个虚拟的IP地址(clusterIP),而Service所需的端口号则从Pod中的containerPort复制而来:
[root@bogon ~]# kubectl expose rc webapp
service "webapp" exposed
[root@bogon ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.10.10.1     <none>        443/TCP    19d
mysql        ClusterIP   10.10.10.200   <none>        3306/TCP   19d
webapp       ClusterIP   10.10.10.58    <none>        8080/TCP   9s

接下来,我们就可以通过Service的IP地址和Service的端口号访问该Service了:
#curl 10.10.10.58:8080
这里,对Service地址10.10.10.58:8080的访问被自动负载分发到了后端两个Pod之一。

(3)除了使用kubectl expose命令创建Service,我们也可以通过配置文件定义Service,再通过kubectl create命令进行创建。
例如前面的webapp就用,我们可以设置一个Service:
webapp-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  ports:
  - port: 8081
    targetPort: 8080
  selector:
    app: webapp
Service定义中的关键字段是ports和selector。
本例中ports定义部分指定了Service所需的虚拟端口号为8081,由于与Pod容器端口号8080不一样,所以需要在通过targetPort来指定后端Pod的端口。
selector定义部分设置的是后端Pod所拥有的label: app=webapp

(4)目前kubernetes提供了两种负载分发策略:RoundRobin和SessionAffinity
  • RoundRobin:轮询模式,即轮询将请求转发到后端的各个Pod上
  • SessionAffinity:基于客户端IP地址进行会话保持的模式,第一次客户端访问后端某个Pod,之后的请求都转发到这个Pod上
默认是RoundRobin模式。

7、多端口Service

有时候,一个容器应用提供多个端口服务,可以按下面这样定义:
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  ports:
  - name: web
    port: 8080
    targetPort: 8080
  - name: management
    port: 8005
    targetPort: 8005
  selector:
    app: webapp
另一个例子是两个端口使用了不同的4层协议,即TCP和UDP
apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "KubeDNS"
spec:
  selector:
    k8s-app: kube-dns
  clusterIP: 10.10.10.100
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP

8、Headless Service

有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service。
这个选项允许开发人员自由寻找他们自己的方式,从而降低与 Kubernetes 系统的耦合性。 应用仍然可以使用一种自注册的模式和适配器,对其它需要发现机制的系统能够很容易地基于这个 API 来构建。
对这类 Service 并不会分配 Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。仅依赖于Label Selector将后端的Pod列表返回给调用的客户端。
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
   clusterIP: None
  selector:
    app: nginx
这样,Service就不再具有一个特定的ClusterIP地址,对其进行访问将获得包含Label"app=nginx"的全部Pod列表,然后客户端程序自行决定如何处理这个Pod列表。
例如, StatefulSet就是使用Headless Service为客户端返回多个服务地址。

Lable Secector:
  • 配置 Selector:对定义了 selector 的 Headless Service,Endpoint 控制器在 API 中创建了 Endpoints 记录,并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达 Service 的后端 Pod上。
  • 不配置 Selector:对没有定义 selector 的 Headless Service,Endpoint 控制器不会创建 Endpoints 记录。 


9、外部服务Service——没有 selector 的 Service

在某些环境中,应用系统需要将一个外部数据库用为后端服务进行连接,或将另一个集群或Namespace中的服务作为服务的后端,这时可以通过创建一个无Label Selector的Service实现:
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

Servcie 抽象了该如何访问 Kubernetes Pod,但也能够抽象其它类型的 backend,例如:
  • 希望在生产环境中使用外部的数据库集群。
  • 希望服务指向另一个 Namespace 中或其它集群中的服务。
  • 正在将工作负载同时转移到 Kubernetes 集群和运行在 Kubernetes 集群之外的 backend。

由于这个 Service 没有 selector,就不会创建相关的 Endpoints 对象。可以手动将 Service 映射到指定的 Endpoints:
kind: Endpoints
apiVersion: v1
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 1.2.3.4
    ports:
      - port: 9376
注意:Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 link-local(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。
访问没有 selector 的 Service,与有 selector 的 Service 的原理相同。请求将被路由到用户定义的 Endpoint(该示例中为 1.2.3.4:9376)。

ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 相反地,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com
当查询主机 my-service.prod.svc.CLUSTER时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type。


10、集群外部访问Pod或Service的方法

由于Pod和Service是k8s集群范围内的虚拟概念,所以集群外的客户端系统无法通过Pod的IP地址或者Service的虚拟IP地址和虚拟端口号访问到它们。
为了让外部客户端可以访问这些服务,可以将Pod或Service的端口号映射到宿主机,以使得客户端应用能够通过物理机访问容器应用。

10.1 将容器应用的端口号映射到物理机

(1)通过设置容器级别的hostPort,将容器应用的端口号映射到物理机上
文件pod-hostport.yaml
apiVersion: v1
kind: Pod
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  containers:
  - name: webapp
    image: tomcat
    ports:
    - containerPort: 8080
      hostPort: 8081

通过kubectl create创建这个Pod:
#kubectl create -f pod-hostport.yaml

通过物理机的IP地址和8081端口号访问Pod内的容器服务:
#curl 10.0.2.6:8081

(2)通过设置Pod级别的hostNetwork=true,该Pod中所有容器的端口号都将被直接映射到物理机上
设置hostWork=true是需要注意,在容器的ports定义部分如果不指定hostPort,则默认hostPort等于containerPort,如果指定了hostPort,则hostPort必须等于containerPort的值。
文件pod-hostnetwork.yaml
apiVersion: v1
kind: Pod
metadata:
  name: webapp-hostnetwork
  labels:
    app: webapp-hostnetwork
spec:
  hostNetwork: true
  containers:
  - name: webapp-hostnetwork
    image: tomcat
    imagePullPolicy: Never
    ports:
    - containerPort: 8080

创建这个Pod:
#kubectl create -f pod-hostnetwork.yaml

[root@bogon ~]# kubectl get pod -o wide
NAME                   READY     STATUS    RESTARTS   AGE       IP           NODE
dapi-test-pod-volume   1/1       Running   4          12d       172.17.0.7   10.0.2.6
pod-affinity           1/1       Running   3          11d       172.17.0.4   10.0.2.6
webapp                 1/1       Running   0          6m        172.17.0.2   10.0.2.6
webapp-hostnetwork     1/1       Running   0          37s       10.0.2.10    10.0.2.10

通过物理机的IP地址和8080端口访问Pod的容器服务
#curl 10.0.2.10:8080


10.2 将Service的端口号映射到物理机

(1)通过设置nodePort映射到物理机,同时设置Service的类型为NodePort
文件webapp-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-nodeport
spec:
  type: NodePort
  ports:
  - port: 8090
    targetPort: 8080
    nodePort: 8090
  selector:
    app: webapp

创建这个Service:
[root@bogon ~]# kubectl create -f webapp-svc-nodeport.yaml
service "webapp-nodeport" created

[root@bogon ~]# kubectl get svc
NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE
kubernetes        ClusterIP   10.10.10.1     <none>        443/TCP         22d
mysql             ClusterIP   10.10.10.200   <none>        3306/TCP        22d
webapp-nodeport   NodePort    10.10.10.191   <none>        8090:8090/TCP   11s
[root@bogon ~]# kubectl get pod -o wide
NAME                   READY     STATUS    RESTARTS   AGE       IP           NODE
dapi-test-pod-volume   1/1       Running   4          12d       172.17.0.7   10.0.2.6
pod-affinity           1/1       Running   3          11d       172.17.0.4   10.0.2.6
webapp                 1/1       Running   0          15m       172.17.0.2   10.0.2.6
webapp-hostnetwork     1/1       Running   0          8m        10.0.2.10    10.0.2.10
通过物理机的IP和端口访问:
[root@bogon ~]# curl 10.0.2.6:8090
如果访问不通,查看下物理机的防火墙设置
同样,对该Service的访问也将被负载分发到后端多个Pod上

(2)通过设置LoadBalancer映射到云服务商提供的LoadBalancer地址
这种用法仅用于在公有云服务提供商的云平台上设置Service的场景。
status.loadBalancer.ingress.ip设置的146.148.47.155为云服务商提供的负载均衡器的IP地址。对该Service的访问请求将会通过LoadBalancer转发到后端的Pod上,负载分发的实现方式依赖云服务商提供的LoadBalancer的实现机制。
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: Myapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
    nodePort: 30061
  clusterIP: 10.0.171.239
  loadBalancerIP: 78.11.24.19
  type: LoadBalancer
status:
  loadBalancer:
    ingree:
    - ip: 146.148.47.155


11、DNS服务搭建指南

作为服务发现机制的基本功能,在集群内需要能够通过服务名对服务进行访问,这就需要一个集群范围的DNS服务来完成服务名到ClusterIP的解析。
kubernetes提供的虚拟DNS服务名为skydns,由四个组件组成。
1)etcd:DNS存储
2)kube2sky:将kubernetes Master中的Service(服务)注册到etcd
3)skyDNS:提供NDS域名解析服务
4)healthz:提供对skydns服务的健康检查功能

k8s DNS服务的总体架构:

11.1 skydns配置文件说明

skydns服务由一个RC和一个Service的定义组成,分别由配置文件skydns-rc.yaml和skydns-svc.yaml定义。
skydns的RC配置文件skydns-rc.yaml的内容如下,包含4个容器的定义:
apiVersion: v1
kind: ReplicationController
metadata:
  name: kube-dns-v11
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    version: v11
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    k8s-app: kube-dns
    version: v11
  template:
    metadata:
      labels:
        k8s-app: kube-dns
        version: v11
        kubernetes.io/cluster-service: "true"
    spec:
      containers:
      - name: etcd
        image: gcr.io/google_containers/etcd-amd64:2.2.1
        resources:
          limits:
            cpu: 100m
            memory: 50Mi
          requests:
            cpu: 100m
            memory: 50Mi
        command:
        - /usr/local/bin/etcd
        - -data-dir
        - /tmp/data
        - -listen-client-urls
        - http://127.0.0.1:2379,http://127.0.0.1:4001
        - -advertise-client-urls
        - http://127.0.0.1:2379,http://127.0.0.1:4001
        - -initial-cluster-token
        - skydns-etcd
        volumeMounts:
        - name: etcd-storage
          mountPath: /tmp/data
      - name: kube2sky
        image: gcr.io/google_containers/kube2sky-amd64:1.15
        resources:
          limits:
            cpu: 100m
            memory: 50Mi
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 5
        readinessProbe:
          httpGet:
            path: /readiness
            port: 8081
            scheme: HTTP
          initialDelaySeconds: 30
          timeoutSeconds: 5
        args:
        # command = "/kube2sky"
        - --kube-master-url=http://10.0.2.5:8080
        - --domain=cluster.local
      - name: skydns
        image: gcr.io/google_containers/skydns:2015-10-13-8c72f8c
        resources:
          limits:
            cpu: 100m
            memory: 50Mi
          requests:
            cpu: 100m
            memory: 50Mi
        args:
        # command = "/skydns"
        - -machines=http://127.0.0.1:4001
        - -addr=0.0.0.0:53
        - -ns-rotate=false
        - -domain=cluster.local
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
      - name: healthz
        image: gcr.io/google_containers/exechealthz:1.0
        resources:
          limits:
            cpu: 10m
            memory: 20Mi
          requests:
            cpu: 10m
            memory: 20Mi
        args:
         - -cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1 > /dev/null
         - -port=8080
        ports:
        - containerPort: 8080
          protocol: TCP
      volumes:
      - name: etcd-storage
        emptyDir: {}
      dnsPolicy: Default  #Don't use cluster DNS.


需要修改的几个配置参数如下:
  • kube2sky容器需要访问K8s Master,需要配置Master所在物理主机的IP地址和端口号,本例中设置参数--kube_master_url的值为http://10.0.2.5:8080 。
  • kube2sky容器和skydns容器的启动参数--domain,设置k8s集群中Service所属的域名,本例中为“cluster.local”。启动后,kube2sky会通过API Server监控集群中全部Service的定义,生成相应的记录并保存到etcd中。kube2sky为每个Service生成以下两条记录:
    • <service_name>.<namespace_name>.<domain>
    • <service_name>.<namespace_name>.svc.<domain>
  • skydns的启动参数-addr=0.0.0.0:53表示使用本机TCP和UDP的53端口提供服务。

skydns的Service配置文件skydns-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "kubeDNS"
spec:
  selector:
    k8s-app: kube-dns
  clusterIP: 10.10.10.2
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP
注意:skydns服务使用的clusterIP需要指定一个固定IP,每个Node的kubelet进程都将使用这个IP地址,不能通过kubernetes自动分配。
另外,这个IP地址需要在kube-apiserver启动参数--service-cluster-ip-range指定IP地址范围。

在创建skydns容器之前,先修改每个Node上kubelet启动参数。

故障排查:以上RC中包含4个容器,需要全部能创建成功。如果遇到报错,可以查看相应pod的详情,再使用下面方法查看容器日志,定位错误原因。
kubectl logs -f kube-dns-v11-cndzm --namespace=kube-system -c skydns


11.2 关于获取gcr.io/google_containers网站上的容器镜像的方法

本方法是参照以下文章提供的方法所实现的: https://mritd.me/2016/10/29/set-up-kubernetes-cluster-by-kubeadm/
这个从谷歌网站上获取需要的容器镜像的处理思路是同时利用了github和dockerhub提供的服务。前者提供了项目托管,后者提供了根据Dockerfile自动构建容器镜像的服务,而且是支持基于github项目的容器镜像集成构建服务。
(1)我们就可以先在自己的github项目下为需要使用的docker容器镜像定义出Dockerfile文件,文件的内容简单到只需要两行内容。
例如skydns容器镜像的Dockerfile文件内容如下所示:
FROM gcr.io/google_containers/skydns:2015-10-13-8c72f8c
MAINTAINER watermelonbig <watermelonbig@163.com>

(2)登录 https://hub.docker.com/ 
按照上面提供的参考文章的指导,为你自己构建出一份需要使用的docker容器镜像。
或者使用我已经构建好的:

(3)在k8s node本地使用下面的shell脚本完成下载容器镜像、重新打tag以及清除没用的镜像
images=(etcd-amd64:2.2.1 kube2sky-amd64:1.15 skydns:2015-10-13-8c72f8c exechealthz:1.0 )
for imageName in ${images[@]} ;
do
  docker pull watermelonbig/$imageName
  docker tag watermelonbig/$imageName gcr.io/google_containers/$imageName
  docker rmi watermelonbig/$imageName
done

查看下载到node本地的容器镜像:
[root@bogon ~]# docker images
REPOSITORY                                       TAG                  IMAGE ID            CREATED             SIZE
gcr.io/google_containers/exechealthz             1.0                  41ff19891ee8        About an hour ago   7.12MB
gcr.io/google_containers/kube2sky-amd64          1.15                 0ea1b8538ec1        About an hour ago   29.2MB
gcr.io/google_containers/skydns                  2015-10-13-8c72f8c   5f97362942c4        About an hour ago   40.6MB
gcr.io/google_containers/etcd-amd64              2.2.1                e26c5ece7b6b        About an hour ago   28.2MB


11.3 修改每台Node上的kubelet启动参数

添加上以下两个参数:
  • --cluster_dns=10.10.10.2 为DNS服务的ClusterIP地址
  • --cluster_dns=cluster.local 为DNS服务中设置的域名
然后重启kubelet服务。

11.4 创建skydns RC和Service

通过kubectl create完成skydns的RC和Service的创建:
#kubectl create -f skydns-rc.yaml
#kubectl create -f skydns-svc.yaml

查看RC、Pod和Service,确保容器成功启动:
[root@localhost ~]# kubectl get rc --namespace=kube-system
NAME           DESIRED   CURRENT   READY     AGE
kube-dns-v11   1         1         1         7m
[root@localhost ~]# kubectl get svc --namespace=kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.10.10.2   <none>        53/UDP,53/TCP   1m
[root@localhost ~]# kubectl get pods --namespace=kube-system
NAME                          READY     STATUS    RESTARTS   AGE
fluentd-cloud-logging-8rkvk   1/1       Running   5          11d
fluentd-cloud-logging-rr45f   1/1       Running   4          11d
kube-dns-v11-cndzm            4/4       Running   0          7m

至此,k8s集群内的虚拟DNS服务搭建完成,在需要访问k8s Service的应用中,仅需要配置上k8s Service的名字和服务的端口号,就能可以访问,如redis-master:6379 。
通过服务名进行配置,能够极大地简化客户端应用对后端服务变化的感知,包括服务虚拟IP地址的变化、服务后端Pod的变化等,对应用程序的微服务架构实现提供了强有力的支撑。


11.5 通过DNS查找Service

使用一个带有nslookup工具的Pod来验证DNS服务。
文件busybox.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - name: busybox
    image: busybox
    command:
      - sleep
      - "3600"

运行kubectl create -f busybox.yaml完成创建。

测试nslookup
查看下当前有哪些k8s Services:
[root@localhost ~]# kubectl get svc
NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE
kubernetes        ClusterIP   10.10.10.1     <none>        443/TCP         23d
mysql             ClusterIP   10.10.10.200   <none>        3306/TCP        23d
webapp-nodeport   NodePort    10.10.10.191   <none>        8090:8090/TCP   1d
[root@localhost ~]#

通过服务名字测试dns解析是否正确:
[root@localhost ~]# kubectl exec busybox -- nslookup mysql
Server:    10.10.10.2
Address 1: 10.10.10.2

Name:      mysql
Address 1: 10.10.10.200 bogon
[root@localhost ~]# kubectl exec busybox -- nslookup webapp-nodeport
Server:    10.10.10.2
Address 1: 10.10.10.2

Name:      webapp-nodeport
Address 1: 10.10.10.191 bogon

如果某个Service属于不同的命名空间,需要带上namespace名字:
[root@localhost ~]# kubectl get svc --namespace kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.10.10.2   <none>        53/UDP,53/TCP   16m
不指定namespace时,显示kube-dns的域名解析失败:
[root@localhost ~]# kubectl exec busybox -- nslookup kube-dns
nslookup: can't resolve 'kube-dns'
Server:    10.10.10.2
Address 1: 10.10.10.2

command terminated with exit code 1
指定了namespace后,可以正确解析出来了:
[root@localhost ~]# kubectl exec busybox -- nslookup kube-dns.kube-system
Server:    10.10.10.2
Address 1: 10.10.10.2

Name:      kube-dns.kube-system
Address 1: 10.10.10.2


11.6 DNS服务的工作原理解析

我们看看DNS服务背后的工作原理。
(1)kube2sky容器应用通过调用kubernetes Master的API获得集群中所有Service的信息,并持续监控新Service的生成,然后写入etcd中。
查看etcd中存储的Service信息:
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster
/skydns/local/cluster/svc
/skydns/local/cluster/pod

可以看到在skydns键下面,根据我们配置的域名(cluster.local)生成了local/cluster子键,接下来是pod(default和kube-system)和svc(下面也按namespace生成子键)。
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster
/skydns/local/cluster/svc
/skydns/local/cluster/pod
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/svc
/skydns/local/cluster/svc/default
/skydns/local/cluster/svc/kube-system
[root@localhost ~]#
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/pod
/skydns/local/cluster/pod/default
/skydns/local/cluster/pod/kube-system

[root@localhost ~]# kubectl get pod -o wide
NAME                   READY     STATUS    RESTARTS   AGE       IP           NODE
busybox                1/1       Running   0          26m       172.17.0.7   10.0.2.6
dapi-test-pod-volume   1/1       Running   6          13d       172.17.0.4   10.0.2.6
pod-affinity           1/1       Running   5          12d       172.17.0.3   10.0.2.6
webapp                 1/1       Running   2          1d        172.17.0.2   10.0.2.6
webapp-hostnetwork     1/1       Running   2          1d        10.0.2.10    10.0.2.10

[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/pod/default
/skydns/local/cluster/pod/default/172-17-0-8
/skydns/local/cluster/pod/default/172-17-0-7
/skydns/local/cluster/pod/default/172-17-0-4
/skydns/local/cluster/pod/default/172-17-0-3
/skydns/local/cluster/pod/default/172-17-0-2
/skydns/local/cluster/pod/default/10-0-2-10
[root@localhost ~]#
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/pod/kube-system
/skydns/local/cluster/pod/kube-system/172-17-0-6
/skydns/local/cluster/pod/kube-system/172-17-0-2
/skydns/local/cluster/pod/kube-system/172-17-0-5

[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/svc/default
/skydns/local/cluster/svc/default/kubernetes
/skydns/local/cluster/svc/default/webapp-nodeport
/skydns/local/cluster/svc/default/mysql

从etcd数据库中查看"mysql"服务的域名解析数据:
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/svc/default/mysql
/skydns/local/cluster/svc/default/mysql/7e5750b4
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl get /skydns/local/cluster/svc/default/mysql/7e5750b4
{"host":"10.10.10.200","priority":10,"weight":10,"ttl":30,"targetstrip":0}
可以看到,mysql服务对应的完整域名为mysql.default.svc.cluster.local,并且其IP地址为10.10.10.200 。

(2)根据kubelet启动参数的设置(--cluster_dns),kubelet会在每个新创建的Pod中设置DNS域名解析配置文件/etc/resolv.conf文件,在其中增加了一条nameserver配置和一条search配置
[root@localhost ~]# kubectl exec busybox -c busybox -- cat /etc/resolv.conf
nameserver 10.10.10.2
search default.svc.cluster.local svc.cluster.local cluster.local localdomain
options ndots:5
通过名字服务器10.10.10.2访问的实际就是skydns在53端口上提供的DNS解析服务。

(3)最后应用程序就能够像访问网站域名一样,仅仅通过服务的名字就能够访问到服务了。

11.7 DNS服务的演进

在后续的版本中,skydns将被更新为kubedns(也不再使用etcd数据库),同时增加DNS专用的HPA控制器,以自动扩展DNS容器的副本数量。
DNS服务是k8s集群中服务发现的最核心组件,建议将其作为标准配置,在安装集群时部署。


12、自定义DNS和上游DNS服务器

在实际环境中,很多用户都有自己的私有域名区域,并且希望能够集成到k8s DNS的命名空间中,例如混合云用户可能希望能在集群内解析其内部的".corp"域名;用户也可能已存在一个未被k8s管理的服务发现系统(例如Consul)来完成域名解析。从k8s v1.6版本起,用户可以在k8s集群内配置私有DNS区域(通常称为存根域Stub Domain)和外部的上游域名服务了。下面讲解如何使用这一功能。

12.1 k8s默认的域名解析流程

Kubernetes 目前在Pod定义中支持两种 DNS 策略: Default和ClusteFirst。 如果 dnsPolicy 的 Flag 没有特别指明,则默认使用ClusterFirst。 如果 dnsPolicy 设置为 Default ,则名称解析配置将完全从 pod所在的节点(/etc/resolv.conf)继承而来。

k8s默认的域名解析流程如下图所示:


如果 dnsPolicy 设置为ClusterFirst ,则 DNS 查询将被发送到 kube-dns (skydns)服务。 kube-dns服务负责以集群域名为后缀(例如.cluster.local)进行服务名的解析。其他域名查询(例如 www.kubernetes.io )将被转发给节点上定义的上游域名服务器 。
在此之前,通常使用自定义解析器替换上游 DNS 来引入存根域。 然而,这会导致自定义解析器本身成为 DNS 解析的关键路径,其中可扩展性和可用性的问题成为一个高风险因素。 现在,本功能让用户无须对整个DNS路径进行改造就完成自定义DNS解析的过程。

12.2 自定义DNS的方式

从 Kubernetes 1.6 开始,集群管理员可以使用 ConfigMap 来指定自定义存根域和上游DNS Server 。
例如,下面的配置包含一个存根域和两个上游域名服务器 。对域名后缀为.acme.local的查询请求将被发送到地址为 1.2.3.4 上的 DNS 服务。同时,设置8.8.8.8和8.8.4.4为上游DNS服务器地址。 
apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-dns
  namespace: kube-system
data:
  stubDomains: |
    {"acme.local": ["1.2.3.4"]}
  upstreamNameservers: |
    ["8.8.8.8", "8.8.4.4"]

主要参数说明:
  • stubDomains (可选):JSON格式的存根域定义,以 DNS 后缀(如acme.local)为key, 值是一个JSON数组,表示一组DNS服务的IP地址。 注意,目标域名服务器也可以是 Kubernetes 服务名。 例如,可以运行自己的 dnsmasq 副本,将自定义 DNS 导出到 ClusterDNS的命名空间中。
  • upstreamNameservers :一个 DNS IP组成的 JSON 数组。 注意:如果指定了这个值,那么从节点的域名服务设置( /etc/resolv.conf )继承过来的值就会被覆盖。本字段限制最多可指定3个IP地址。

下图显示了上述配置中指定的 DNS 域名解析流程。


1)查询首先被发送到 kube-dns 中的 DNS 缓存层。
2)从缓存层,检查请求的后缀,并根据下面的情况转发到对应的 DNS 上:
  • 具有集群后缀的名字(例如 “.cluster.local”):请求被发送到 kube-dns。
  • 具有存根域后缀的名字(例如 “.acme.local”):请求被发送到配置的自定义 DNS 解析器(例如:监听在 1.2.3.4)。
  • 未能匹配上后缀的名字(例如 “widget.com”):请求被转发到上游 DNS(例如:Google 公共 DNS 服务器,8.8.8.8 和 8.8.4.4)。


域名解析的顺序: 

域名
响应查询的服务器
kubernetes.default.svc.cluster.local
kube-dns
foo.acme.local
自定义 DNS (1.2.3.4)
widget.com
上游 DNS (8.8.8.8, 8.8.4.4,其中之一)


12.3 自定义DNS的示例

1)新增加一个节点10.0.2.12,安装一个dnsmasq服务器,并创建一条主机记录供dnsmasq使用。
生成一个自定义的DNS记录文件/tmp/hosts:
# echo "192.168.10.2 server.acme.local" > /tmp/hosts

启动DNS服务:
# dnsmasq -q -d -h -R -H /tmp/hosts
参数说明:
  • -d: 以debug模式启动,在前后运行,便于观察日志
  • -q: 输出查询记录
  • -h: 不使用/etc/hosts
  • -R: 不使用/etc/resolve.conf
  • -H: 使用自定义的文件作为DNS记录

2)创建自定义DNS的Configmap
配置文件dns-configmap.yaml的内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-dns
  namespace: kube-system
data:
  stubDomains: |
    {"acme.local": ["10.0.2.12"]}
  upstreamNameservers: |
    ["8.8.8.8", "8.8.4.4"]

[root@bogon ~]# kubectl create -f dns-configmap.yaml
configmap "kube-dns" created

3)运行一个容器,进入容器内部查看自定义的DNS配置是否生效
文件dns-tester.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dns-tester
spec:
  dnsPolicy: ClusterFirst
  containers:
  - name: busybox
    image: busybox
    command:
      - sleep
      - "3600"

[root@bogon ~]# kubectl create -f dns-tester.yaml
pod "dns-tester" created

[root@bogon ~]# kubectl exec -it dns-tester  -- sh
# nslookup kubernetes.default
Server:    10.10.10.2
Address 1: 10.10.10.2 bogon

Name:      kubernetes.default
Address 1: 10.10.10.1 bogon
#

理论上,从容器中执行ping server.acme.local应该返回解析出的地址192.168.10.2 。而实际上,上面的试验失败了,自定义DNS服务器未能生效, 原因暂未找到!


13、Ingress:HTTP 7层路由机制


根据前面对Service的使用说明,我们知道Service的表现形式为IP:Port,即工作在TCP/IP层,而对于基于HTTP的服务来说,不同的URL地址经常对应到不同的后端服务或者虚拟服务器,这些应用层的转发机制仅通过kubernetes的Service机制是无法实现的。kubernetes v1.1版本中新增的Ingress资源对象将不同URL的访问请求转发到后端不同的Service,以实现HTTP层的业务路由机制。

使用 Ingress 进行负载分发时,Ingress Controller 将基于 Ingress 规则将客户端请求直接转发到 Service 对应的后端 Endpoint(即 Pod)上,这样会跳过 kube-proxy 的转发功能,kuber-proxy 不在起作用。如果 Ingress Controller 提供的是对外服务,则实际上实现的是边缘路由器的功能。

下图显示了一个典型的 HTTP 层路由的例子:


为使用Ingress,需要创建Ingress Controller(带一个默认backend服务)和Ingress策略设置来共同完成。
下面通过一个例子说明Ingress Controller和Ingress策略的配置方法和客户端如何访问Ingress提供的服务。

在定义 Ingress 策略之前,需要先部署 Ingress Controller,以实现为所有后端 Service 提供一个统一的入口。Ingress Controller 需要实现基于不同 HTTP URL 向后转发的负载分发规则,并可以灵活设置 7 层的负载分发策略。如果公有云服务商能够提供该类型的 HTTP 路由 LoadBalancer,则也可以设置其为 Ingress Controller。
在 Kubernetes 中,Ingress Controller 将以 Pod 的形式运行,监控 apiservice 的 /ingress 接口后端的 backend services,如果 service 发生变化,则 Ingress Controller 应自动更新其转发规则。
下面的例子使用 Nginx 来实现一个 Ingress Controller,需要实现的基本逻辑如下:
(1)监听 apiserver,获取全部 ingress 的定义。
(2)基于 ingress 的定义,生成 Nginx 所需的配置文件 /etc/nginx/nginx.conf。
(3)执行 nginx -s reload 命令,重新加载 nginx.conf 配置文件的内容。
基于 Go 语言的代码核心实现如下:
for {
    rateLimiter.Accept()
    ingresses, err := ingClient.List(Labels.Everything(), fields.Everything())
    if err != nil || reflect.Deepqual(ingresses.Items, known.Items) {
        contiune
    }
    if w, err :- os.Create("/etc/nginx/nginx.conf"); err != nil {
        log.Fatalf("Failed to open %v: %v", nginxConf, err)
    } else if err := tmpl.Execute(w, ingresses); err != nil {
        log.Fatalf("Failed to write template %v", err)
    }
    shellOut("nginx -s reload")
}

本例使用 Google 提供的 nginx-ingress-controller 镜像来创建 Ingress Controller。该 Ingress Controller 以 daemonset 的形式进行创建,在每个Node上都将启动一个Nginx服务。
这里为Nginx容器设置了hostPort,将容器应用监听的80和443端口号映射到物理机上,使得客户应用可以通过URL地址"http://物理机IP:80"或"https://物理机IP:443"来访问该Ingress Controller。这使得Nginx类似于通过NodePort映射到物理机的Service,成为代替kube-proxy的HTTP层的Load Balancer。

默认情况下,Kubernetes服务的Pod无法从外部网络访问,但只能由Kubernetes集群内的其他Pod访问。 Kubernetes具有用于HTTP负载平衡的内置配置,称为Ingress,该配置定义了与Kubernetes服务的外部连接的规则。 需要提供对其Kubernetes服务的外部访问的用户创建一个定义规则的Ingress资源,其中包括URI路径,支持服务名称和其他信息。 然后,Ingress控制器可以自动编写前端负载平衡器以启用Ingress配置。 NGINX Ingress控制器使Kubernetes能够配置NGINX和NGINX Plus来负载均衡Kubernetes服务。

13.1 创建一个专用的Namespace、Service Account和Default Secret

文件ns-and-sa.yaml,创建命名空间和服务账号
apiVersion: v1
kind: Namespace
metadata:
  name: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-ingress
  namespace: kube-system

[root@bogon ~]# kubectl create -f ns-and-sa.yaml
namespace "kube-system" created
serviceaccount "nginx-ingress" created
注:以上配置需要依赖于ServiceAccount认证,请确认你的kube-apiserver服务已经启用了对ServiceAccount认证的支持。

在NGINX中为默认服务器创建一个带有TLS证书和密钥的Secret.
文件default-server-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: default-server-secret
  namespace: kube-system
type: Opaque
data:
  tls.crt: MIICoTCCAYkCCQCTK3tHyCzQOTANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAVib2dvbjAeFw0xODAyMjgwMDU4NTlaFw0zMTExMDcwMDU4NTlaMBUxEzARBgNVBAMMCmt1YmUtYWRtaW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCauNxlQU6xaDhjH77x6zwQeF77acYW9EkBfbEd8MM1ZcWm1agplI1M90uWJLwcS72qFeQ+zCxzBv6aQ5/LEziJLX3ETacSvNa+WoWKjhaJh7DuWybRHHC4731jwuYAbrjGrljSxmrBRWlUygTYobjp19CBEBU5PbsbzO5GMYFWRwPZXGyPR3XN0AjRFqoxj4wsuN/4UvMsPn7CVWJqv4is2hpRcoBZMTkBIaWal/TQkNRCg6PTufSfjOLa2U9P4UDGtcl/FHOotGRwP0Ouf34NKxtlCweeSr2dr+L92eymTde5EyTyiF6qexbTOouAxtzkQNgraRacm1vMmJNeLv/PAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABdhlVenHx1ifsUV7TAAoh9dVYmTS+RLrlcEKnEMZOgm9qFYIZiG/Imo4hcy2sVsbRtl6AsZuFCpPtVo7CFW+9FGCrJfrxmBUwz5lrTvsHmvqMakT8lUv14fBgBkeNDycqNZZODwASn9Ycd//i1XlwGdKPZ52X+ixr184Clg6MUtYhkDQ7NwNEetwe7ajWFyMwrSNm/weFG4jPvX4e3ZwdRKhj2rv4DnuUIwRXAoY9b2mvrWRlx7mzzIvg3eu7WwBDCst1LNjsIVanI2tHLtCcuxqTCpitPRgRa+9/o8AarO6MOLN0aJucQ6THTUv0pcCwZ4RQXUgnaCBsxWQL862CY=
  tls.key: MIIEpAIBAAKCAQEAmrjcZUFOsWg4Yx++8es8EHhe+2nGFvRJAX2xHfDDNWXFptWoKZSNTPdLliS8HEu9qhXkPswscwb+mkOfyxM4iS19xE2nErzWvlqFio4WiYew7lsm0RxwuO99Y8LmAG64xq5Y0sZqwUVpVMoE2KG46dfQgRAVOT27G8zuRjGBVkcD2Vxsj0d1zdAI0RaqMY+MLLjf+FLzLD5+wlViar+IrNoaUXKAWTE5ASGlmpf00JDUQoOj07n0n4zi2tlPT+FAxrXJfxRzqLRkcD9Drn9+DSsbZQsHnkq9na/i/dnspk3XuRMk8oheqnsW0zqLgMbc5EDYK2kWnJtbzJiTXi7/zwIDAQABAoIBABfjDnO7f3C4TGxRTGOBE8bfLprWG7UThenrA9tBfoHR8o/tUIcK3j3RuCO4DCq6LtABjTl8wCgKHmimpBbwIrxa/52891xXNzgwNYnMogIdpt1FyVpjb6u9nbg3MNdEQNa7uA7ifzzTgI2Mmu7vGONRZGlomD48H5UNMIQ5xGQTWTeXbdCNM3bdLMLRjJ3Fd4V5BlzOFYSXBcZV2aOIfXizJaamvdHdEXDQUC9/g5RcOncqzzy6uGR6PRJMwBWyV3AILzGvp/JJoj+68hppNNjsJhfs1ic5VaZ55gkPZnKRK8wJZUl8brbFNCbt49uT9tAYPN/VLFppGJsTVGn9/aECgYEAyyMy56S2Dp9zlsSBHqMetEXTuBW01Vl7+rcFM7yBlwWq+I0MCoOJAMWbitLoBBxfHwERLXJRoEPmA+fuITG8a1qr1rjPwXuC6FST81HjlX0aD/DvEySzHr/F/jHtwnMthXsx0c3Zx5E68KkMkOTgm3qkVsONWJfqO4m6yTdG1+kCgYEAwvxIDDqYlpgn0/dFwjmG7JMiL+KBULZ1iiH+J91mjXrXH+ZlO+EFMdwK3H4eRyME9EWDgV98VwJ5PQ6Acu0mRcBAaqfvimdypdIp4D32DQtPXJ5jwiyWb0EbUd9EYM0R4+WZO5c0E1lQ4BOPHdeQky9xKjcTc4qZ36MQZ8wQfvcCgYEAtcszdGFQ2PdUP7puf5emE9ll55ntcv4ZWT/PtQRfyWM2Jig8fXBH/NvcHrovD/bAHgQbdluXt2DsidRXpXdYU48auBg/Pb2mYvvGSHyhuxfxahDKNIykjME9lQbj7CCdvZaJ+GRMITDU2anAaC+c3yg7yLZLWdzisBPLiOYiTXkCgYEAtTpo+LzJsEbx1cihlNmFN4O1pc4gJVXBP8dCg+j7dYp0QpkRBKur+WCs062BFNtOsn+dr/SBDBond4FwD+GtzezXsoouUXS/EbKZ62uLsyoM9GffDs4EcJmR+/HJizNvamOvnbx20XkVe3+SaMk//h19UCBqutxbEopsKB16Q5cCgYA4vQj2OOjkRAux/lHhkamjVFK1LVQn3br4jvhStVfWvxPReDTxMQHS9KmPdZF8A1sFAPkDQt95z/kJy0Tvg8P9ZRXe2wuGo6lSTT9Hj8XiYMwaLIAhChxagBr7VJ4dvf65/j/QSjH4YzHztPvzLy9T0xeBP3jnY3t4/gCtOCmNgA==

[root@bogon ~]# kubectl create -f default-server-secret.yaml
secret "default-server-secret" created
注:这里建议自行创建一个自签的证书和密钥。

可选的。 还可以创建一个用于定制NGINX配置的configmap(在 这里 阅读更多关于定制的信息)

13.2 配置RBAC

基于角色的访问控制(“RBAC”)使用“rbac.authorization.k8s.io”API 组来实现授权控制,允许管理员通过Kubernetes API动态配置策略。
截至1.6 RBAC模式是beta版。
启用RBAC:apiserver --authorization-mode=RBAC。
如果群集中启用了RBAC,请创建群集角色并将其绑定到在步骤1中创建的服务帐户:
文件rbac.yaml:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: nginx-ingress
rules:
- apiGroups:
  - ""
  resources:
  - services
  - endpoints
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - configmaps
  - secrets
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - list
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
- apiGroups:
  - extensions
  resources:
  - ingresses
  verbs:
  - list
  - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: nginx-ingress
subjects:
- kind: ServiceAccount
  name: nginx-ingress
  namespace: nginx-ingress
roleRef:
  kind: ClusterRole
  name: nginx-ingress
  apiGroup: rbac.authorization.k8s.io

$ kubectl apply -f rbac.yaml

13.3 部署Ingress Controller

有两种可先的部署方法,各不有同的用途:
  • deployment, 如果计划要动态更改Ingress控制器副本的数量,请使用deployment。
  • DaemonSet, 使用DaemonSet在每个节点或节点的子集上部署Ingress控制器。
使用deployment方式:
文件nginx-ingress.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-ingress
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-ingress
  template:
    metadata:
      labels:
        app: nginx-ingress
    spec:
      serviceAccountName: nginx-ingress
      containers:
      - image: nginxdemos/nginx-ingress:1.1.1
        name: nginx-ingress
        ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        args:
          - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
          - -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
         #- -v=3 # Enables extensive logging. Useful for trooublshooting.

$ kubectl apply -f nginx-ingress.yaml

Kubernetes将创建一个Ingress控制器Pod。

使用DaemonSet方式:
文件nginx-ingress.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: nginx-ingress
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: nginx-ingress
  template:
    metadata:
      labels:
        app: nginx-ingress
    spec:
      serviceAccountName: nginx-ingress
      containers:
      - image: nginxdemos/nginx-ingress:1.1.1
        name: nginx-ingress
        ports:
        - name: http
          containerPort: 80
          hostPort: 80
        - name: https
          containerPort: 443
          hostPort: 443
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        args:
          - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
          - -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
         #- -v=3 # Enables extensive logging. Useful for trooublshooting.
创建和查看Ingress控制器:
[root@bogon ~]# kubectl apply -f nginx-ingress.yaml
daemonset "nginx-ingress" created
[root@bogon ~]# kubectl get ds --namespace=kube-system
NAME            DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
nginx-ingress   2         2         2         2            2           <none>          4s
[root@bogon ~]# kubectl get pods --namespace=kube-system
NAME                  READY     STATUS    RESTARTS   AGE
nginx-ingress-2w89p   1/1       Running   0          19s
nginx-ingress-8d9js   1/1       Running   0          19s
[root@bogon ~]# kubectl logs nginx-ingress-2w89p --namespace=kube-system
I0325 12:05:27.883237       1 main.go:65] Starting NGINX Ingress controller Version=1.1.1 GitCommit=8fc772d
2018/03/25 12:05:27 [notice] 18#18: signal process started
I0325 12:05:27.928047       1 event.go:218] Event(v1.ObjectReference{Kind:"Secret", Namespace:"kube-system", Name:"default-server-secret", UID:"0cf6ec9b-3024-11e8-9fed-080027cf1a4c", APIVersion:"v1", ResourceVersion:"2151163", FieldPath:""}): type: 'Normal' reason: 'Updated' the default server Secret nginx-ingress/default-server-secret was updated

Kubernetes将在集群的每个节点上创建一个Ingress控制器Pod。 阅读 本文 以了解如何在节点的子集上运行Ingress控制器,而不是在集群的每个节点上运行。

13.4 访问Ingress控制器

如果是创建的DaemonSet,则Ingress控制器容器的端口80和443将映射到容器正在运行的节点的相同端口。 要访问Ingress控制器,请使用Ingress控制器正在运行的群集中任何节点的IP地址。
[root@bogon ~]# kubectl get nodes -o wide
NAME        STATUS    ROLES     AGE       VERSION   EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION               CONTAINER-RUNTIME
10.0.2.10   Ready     <none>    17d       v1.8.8    <none>        CentOS Linux 7 (Core)   3.10.0-693.17.1.el7.x86_64   docker://17.12.0-ce
10.0.2.6    Ready     <none>    28d       v1.8.8    <none>        CentOS Linux 7 (Core)   3.10.0-693.17.1.el7.x86_64   docker://17.12.0-ce
[root@bogon ~]# curl 10.0.2.6
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.13.8</center>
</body>
</html>
[root@bogon ~]# curl 10.0.2.10
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.13.8</center>
</body>
</html>
进入一个nginx ingress controller pod中查看:
# kubectl exec -it  nginx-ingress-2w89p  --namespace=kube-system -c nginx-ingress -- /bin/bash

如果是创建的deployment,则有两种方法可以访问到Ingress controller pods。
1)使用NodePort类型的Service
文件nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-ingress
  namespace: kube-system
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    targetPort: 443
    protocol: TCP
    name: https
  selector:
    app: nginx-ingress

$ kubectl create -f nodeport.yaml

2)使用LoadBalancer类型的Service
使用LoadBalancer类型创建一个服务。 Kubernetes将分配和配置一个云负载均衡器,以负载平衡Ingress控制器Pods。

13.5 删除Ingress控制器

删除 kube-system 命名空间可以卸载Ingress控制器以及创建的所有辅助资源:
$ kubectl delete namespace kube-system


13.6 为Ingress Controller配置一个默认的 backend

这个 backend 服务用任何应用实现都可以,只要满足默认对 /路径 的访问返回 404 应答,并且提供 /healthz 路径以使 kubelet 完成对它的健康检查。另外,由于 Nginx 通过 default-backend-service 的服务名称(Service Name)去访问它,所以需要 DNS 服务正确运行。
文件:default-backend.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: default-http-backend
  labels:
    k8s-app: default-http-backend
  namespace: kube-system
spec:
  replicas: 1
  template:
    metadata:
      labels:
        k8s-app: default-http-backend
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: default-http-backend
        # Any image is permissable as long as:
        # 1. It serves a 404 page at /
        # 2. It serves 200 on a /healthz endpoint
        image: gcr.io/google_containers/defaultbackend:1.4
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 30
          timeoutSeconds: 5
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 10m
            memory: 20Mi
          requests:
            cpu: 10m
            memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
  name: default-http-backend
  namespace: kube-system
  labels:
    k8s-app: default-http-backend
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    k8s-app: default-http-backend

# kubectl create -f default-backend.yaml

[root@bogon ~]# kubectl get svc --namespace=kube-system
NAME                   TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE
default-http-backend   ClusterIP   10.10.10.22   <none>        80/TCP          18h
kube-dns               ClusterIP   10.10.10.2    <none>        53/UDP,53/TCP   5d

[root@bogon ~]# kubectl get deploy --namespace=kube-system
NAME                   DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
default-http-backend   1         1         1            1           18h
[root@bogon ~]# kubectl get pod --namespace=kube-system
NAME                                   READY     STATUS    RESTARTS   AGE
default-http-backend-8788bb498-z9cxl   1/1       Running   1          13h
kube-dns-v11-cndzm                     4/4       Running   20         5d
nginx-ingress-k62b5                    1/1       Running   0          8m
nginx-ingress-l4rhx                    1/1       Running   0          8m


13.7 定义Ingress策略

为mywebsite.com定义Ingress,设置到后端Service的转发规则。
文件my-ingress.yaml:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: mywebsite-ingress
spec:
  rules:
  - host: mywebsite.com
    http:
      paths:
      - path: /healthz
        backend:
          serviceName: default-http-backend
          servicePort: 80
这个Ingress的定义说明对目标http://mywebsite.com/healthz的访问将被转发到kubernetes的一个Service上 default-http-backend:80/healthz上。
创建该Ingress
[root@bogon ~]# kubectl create -f my-ingress.yaml
ingress "mywebsite-ingress" created

[root@bogon ~]# kubectl get ingress
NAME                HOSTS           ADDRESS   PORTS     AGE
mywebsite-ingress    mywebsite.com             80        33s

创建后登陆nginx-ingress Pod,查看自动生成的nginx负载均衡配置内容:
[root@bogon ~]# kubectl exec -it  nginx-ingress-k62b5  --namespace=kube-system -c nginx-ingress -- /bin/bash
root@nginx-ingress-k62b5:/# cd etc
root@nginx-ingress-k62b5:/etc# cd nginx
root@nginx-ingress-k62b5:/etc/nginx# ls
conf.d        koi-utf  mime.types  nginx.conf   secrets    win-utf
fastcgi_params    koi-win  modules     scgi_params  uwsgi_params
root@nginx-ingress-k62b5:/etc/nginx# cd conf.d
root@nginx-ingress-k62b5:/etc/nginx/conf.d# ls
kube-system-mywebsite-ingress.conf
onf @nginx-ingress-k62b5:/etc/nginx/conf.d# more kube-system-mywebsite-ingress.co

    server 172.17.0.5:8080;    
}
server {    
    listen 80;  
    server_tokens on;
    server_name mywebsite.com;
    
    location /healthz {
        proxy_http_version 1.1;  
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        client_max_body_size 1m;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_buffering on;        
        proxy_pass http://kube-system-mywebsite-ingress-mywebsite.com-default-http-backend;        
    }
}

访问http://mywebsite.com/web
我们可以通过物理机对其进行访问。通过curl --resolve进行指定
[root@bogon ~]# curl -I --resolve mywebsite.com:80:10.0.2.6 mywebsite.com/healthz
HTTP/1.1 200 OK
Server: nginx/1.13.8
Date: Sun, 25 Mar 2018 14:09:48 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive

13.8 Ingress的策略配置技巧

为展开下面的测试,我们需要准备一些测试环境。
文件:webapp-rc.yaml,提供测试用的backend pods
apiVersion: v1
kind: ReplicationController
metadata:
  name: webapp
  namespace: kube-system
spec:
  replicas: 2
  template:
    metadata:
      name: webapp
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: tomcat
        ports:
        - containerPort: 8080
生成一个Service服务,服务端口将默认与容器端口一致:
# kubectl expose rc webapp --namespace=kube-system

[root@bogon ~]# kubectl get svc --namespace=kube-system
NAME                   TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE
default-http-backend   ClusterIP   10.10.10.22    <none>        80/TCP          1d
kube-dns               ClusterIP   10.10.10.2     <none>        53/UDP,53/TCP   6d
webapp                 ClusterIP   10.10.10.206   <none>        8080/TCP        2h

[root@bogon ~]# kubectl get pod --namespace=kube-system
NAME                                   READY     STATUS    RESTARTS   AGE
default-http-backend-8788bb498-z9cxl   1/1       Running   3          1d
kube-dns-v11-cndzm                     4/4       Running   28         6d
nginx-ingress-k62b5                    1/1       Running   2          1d
nginx-ingress-l4rhx                    1/1       Running   4          1d
webapp-ms8z2                           1/1       Running   0          2h
webapp-t6jlm                           1/1       Running   0          2h

1)转发到单个后端服务上
客户端到Ingress Controller的访问请求都将被转发到后端的唯一Service上。这种情况下Ingress无须定义任何rule。
例如,通过以下设置,对Ingress Controller的访问请求都将被转发到"myweb:8080"这个服务上。
文件my-web-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
spec:
  backend:
    serviceName: webapp
    servicePort: 8080

2)同一域名下,不同的URL路径被转发到不同的服务上
例如/examples表示访问examples web页面,/healthz表示访问健康检查接口。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name:  mywebsite-ingress
  namespace: kube-system
spec:
  rules:
  - host: mywebsite.com
    http:
      paths:
      - path: /healthz
        backend:
          serviceName: default-http-backend
          servicePort: 80
      - path: /examples
        backend:
          serviceName: webapp
          servicePort: 8080
创建并查看结果:
[root@bogon ~]# kubectl get ingress --namespace=kube-system
NAME                HOSTS           ADDRESS   PORTS     AGE
mywebsite-ingress    mywebsite.com             80        4m
查看此时的pods信息:
[root@bogon ~]# kubectl get pod --namespace=kube-system -o wide
NAME                                   READY     STATUS    RESTARTS   AGE       IP           NODE
default-http-backend-8788bb498-z9cxl   1/1       Running   3          1d        172.17.0.4   10.0.2.6
kube-dns-v11-cndzm                     4/4       Running   28         6d        172.17.0.6   10.0.2.6
nginx-ingress-k62b5                    1/1       Running   2          1d        172.17.0.7   10.0.2.6
nginx-ingress-l4rhx                    1/1       Running   4          1d        172.17.0.2   10.0.2.10
webapp-ms8z2                           1/1       Running   0          2h        172.17.0.5   10.0.2.6
webapp-t6jlm                           1/1       Running   0          2h        172.17.0.3   10.0.2.10
我们选择使用节点10.0.2.6做些测试验证:
[root@bogon ~]# curl -I  --resolve mywebsite.com:80:10.0.2.6 mywebsite.com/healthz
HTTP/1.1 200 OK
Server: nginx/1.13.8
Date: Mon, 26 Mar 2018 18:16:56 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive

[root@bogon ~]# curl -I  --resolve mywebsite.com:80:10.0.2.6 mywebsite.com/examples/
HTTP/1.1 200
Server: nginx/1.13.8
Date: Mon, 26 Mar 2018 18:17:00 GMT
Content-Type: text/html
Content-Length: 1126
Connection: keep-alive
Accept-Ranges: bytes
ETag: W/"1126-1520255626000"
Last-Modified: Mon, 05 Mar 2018 13:13:46 GMT
查看节点10.0.2.6上面的Ingress Controller的日志信息:
[root@bogon ~]# kubectl logs nginx-ingress-k62b5  --namespace=kube-system
10.0.2.5 - - [26/Mar/2018:18:16:56 +0000] "HEAD /healthz HTTP/1.1" 200 0 "-" "curl/7.29.0" "-"
10.0.2.5 - - [26/Mar/2018:18:17:00 +0000] "HEAD /examples/ HTTP/1.1" 200 0 "-" "curl/7.29.0" "-"

查看下节点10.0.2.6上面的Ingress Controller的nginx负载均衡配置信息:
[root@bogon ~]# kubectl exec -it  nginx-ingress-k62b5  --namespace=kube-system -c nginx-ingress -- /bin/bash
root@nginx-ingress-k62b5:/# cd /etc/nginx/conf.d
root@nginx-ingress-k62b5:/etc/nginx/conf.d# ls
kube-system-mywebsite-ingress.conf
root@nginx-ingress-k62b5:/etc/nginx/conf.d# more kube-system-mywebsite-ingress.conf
    server 172.17.0.4:8080;    
}
    server 172.17.0.3:8080;
    server 172.17.0.5:8080;    
}
server {    
    listen 80;
    server_tokens on;
    server_name mywebsite.com;
    location /healthz {
        proxy_http_version 1.1;    
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        client_max_body_size 1m;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering on;        
    }
    location /examples {
        proxy_http_version 1.1;
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        client_max_body_size 1m;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering on;        
        proxy_pass http://kube-system-mywebsite-ingress-mywebsite.com-webapp;        
    }
}
root@nginx-ingress-k62b5:/etc/nginx/conf.d#

3)不同的域名(虚拟主机名)被转发到不同的服务上
如下所示,对"a.ourdomain.com"的访问请求将被转发到"webapp:8080"服务上,对"b.ourdomain.com"的访问请求将被转发到"default-http-backend:80"的服务上。
文件my-ingress-vhost.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  namespace: kube-system
spec:
  rules:
  - host: a.ourdomain.com
    http:
      paths:
      - path: /examples
        backend:
          serviceName: webapp
          servicePort: 8080
  - host: b.ourdomain.com
    http:
      paths:
      - path: /healthz
        backend:
          serviceName: default-http-backend
          servicePort: 80

创建:
[root@bogon ~]# kubectl create -f my-ingress-vhost.yaml
ingress "ourdomain-ingress" created
[root@bogon ~]# kubectl get ingress --namespace=kube-system
NAME                HOSTS                             ADDRESS   PORTS     AGE
ourdomain-ingress    a.ourdomain.com, b.ourdomain.com             80        13s
[root@bogon ~]# kubectl get ingress --namespace=kube-system -o wide
NAME                HOSTS                             ADDRESS   PORTS     AGE
ourdomain-ingress    a.ourdomain.com, b.ourdomain.com             80        17s
测试:
[root@bogon ~]# curl -I  --resolve a.ourdomain.com:80:10.0.2.6 a.ourdomain.com/examples/
HTTP/1.1 200
Server: nginx/1.13.8
Date: Tue, 27 Mar 2018 00:48:52 GMT
Content-Type: text/html
Content-Length: 1126
Connection: keep-alive
Accept-Ranges: bytes
ETag: W/"1126-1520255626000"
Last-Modified: Mon, 05 Mar 2018 13:13:46 GMT

[root@bogon ~]# curl -I  --resolve b.ourdomain.com:80:10.0.2.6 b.ourdomain.com/healthz
HTTP/1.1 200 OK
Server: nginx/1.13.8
Date: Tue, 27 Mar 2018 00:48:59 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive
进入nginx ingress controller pod容器中查看nginx虚拟主机的配置:
[root@bogon ~]# kubectl exec -it  nginx-ingress-k62b5  --namespace=kube-system -c nginx-ingress -- /bin/bash
root@nginx-ingress-k62b5:/# cd /etc/nginx/conf.d
root@nginx-ingress-k62b5:/etc/nginx/conf.d# ls
kube-system-ourdomain-ingress.conf
root@nginx-ingress-k62b5:/etc/nginx/conf.d# more kube-system-ourdomain-ingress.conf
    server 172.17.0.3:8080;
    server 172.17.0.5:8080;    
}
    server 172.17.0.4:8080;    
}
server {    
    listen 80;
    server_tokens on;
    server_name a.ourdomain.com;
    location /examples {
        proxy_http_version 1.1;
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        client_max_body_size 1m;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering on;        
        proxy_pass http://kube-system-ourdomain-ingress-a.ourdomain.com-webapp;        
    }
}
server {    
    listen 80;
    server_tokens on;
    server_name b.ourdomain.com
    location /healthz {
        proxy_http_version 1.1;
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        client_max_body_size 1m;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering on;        
    }
}
root@nginx-ingress-k62b5:/etc/nginx/conf.d#

4)不使用域名的转发规则
用于一个网站不使用域名直接提供服务的场景。
下面的配置将<ingress-controller-ip>/examples/的访问请求转发到"webapp:8080/examples/"服务上。
文件:my-nonedomain-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: none-domain-ingress
  namespace: kube-system
spec:
  rules:
  - http:
      paths:
      - path: /examples
        backend:
          serviceName: webapp
          servicePort: 8080
注意:使用无域名的Ingress转发,将默认为禁用非安全HTTP,强制启用HTTPS,此时全部的HTTP访问请求将直接返回301错误。

[root@bogon ~]# kubectl get ingress --namespace=kube-system
NAME                  HOSTS                             ADDRESS   PORTS     AGE
none-domain-ingress   *                                           80        2m
test-ingress           a.ourdomain.com, b.ourdomain.com             80        8m

curl http://10.0.2.6/examples/
curl -k https://10.0.2.6/examples/

可以在Ingress的定义中设置一个annotation "ingress.kubernetes.io/ssl-redirect=false"来关闭强制启用HTTPS的设置。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: none-domain-ingress
  annotations:
    ingress.kubernetes.io/ssl-redirect: "false"
  namespace: kube-system
spec:
  rules:
  - http:
      paths:
      - path: /examples
        backend:
          serviceName: webapp
          servicePort: 8080

curl http://10.0.2.6/examples/

问题:在实验中发现按上面的不使用域名的转发规则去配置Ingress,虽然可以正常生成对应的Ingress资源对象,但是进入ingress controller中却发现始终无法生成对应的nginx配置文件信息。网上的相关资料很少,具体原因推测不排除是与软件版本迭代、功能更新有关。 因此,不使用域名的转发规则未得到成功的验证!

13.9 Ingress的TLS安全设置

为了使得Ingress提供HTTPS的安全访问,可以为Ingress中的域名进行TLS安全证书的设置。
设置的步骤如下:
1)创建自签名的密钥和SSL证书文件。
2)将证书保存到Kubernetes中的一个Secret资源对象上。
3)将该Secret对象设置到Ingress中。
根据提供服务的网站是一个还是多个,可以使用不同的操作完成前两步SSL证书和Secret对象的创建,在只有一个域名的情况下设置相对简单。
第3步对于这两种场景来说是相同的。

只有一个域名时的配置方法
对于只有一个域名的场景来说,可以通过OpenSSL工具直接生成密钥和证书文件,将命令行参数-subj中的/CN设置为网站的域名:

[root@bogon ~]# openssl req -x509 -nodes -days 5000 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN= mywebsite.com"
Generating a 2048 bit RSA private key
.................................................+++
.+++
writing new private key to 'tls.key'
-----
[root@bogon ~]#
上述命令将在一个命令中生成自签名的tls.crt证书和tls.key密钥两个文件。

根据tls.key和tls.crt创建secret资源对象,有以下两种方法。
方法一:通过kubectl create secret tls命令直接通过tls.key和tls.crt文件创建secret对象。
[root@bogon ~]# kubectl create secret tls mywebsite-ingress-secret --key tls.key --cert tls.crt  --namespace kube-system
secret "mywebsite-ingress-secret" created

方法二:编辑mywebsite-ingress-secret.yaml文件,将tls.key和tls.crt文件的内容复制进去,使用kubectl create命令进行创建。
文件mywebsite-ingress-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mywebsite-ingress-secret
  namespace: kube-system
type: kubernetes.io/tls
data:
  tls.crt:
MIIDAzCCAeugAwIBAgIJAO6WRm6qQzW/MA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV BAMMDW15d2Vic2l0ZS5jb20wHhcNMTgwMzI3MTIzNjQ2WhcNMzExMjA0MTIzNjQ2 WjAYMRYwFAYDVQQDDA1teXdlYnNpdGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC AQ8AMIIBCgKCAQEAwNUw4kvWdA9GK2CCuEYAShJhfh3etUv8PjSXMqb6E5TKipwE I7ui0dh1T6GOm4ZH81myDyy1hmcpv1z1Pcx2D+EYada3c9Ea2YsFj5wdknsTP1Bl O0env2pH0jCm2o+AR91gABVolw3u5gMvRuS7xJQQVzebStu0jK/20DtWbhVsuKvp /Bfdi+idKqaQXdleFgDeaLjUOPEAVCU8DOAvnmfQ639d05UMmqujMd7tisDA4p4D 3e6jBQ2YOIq8DH0dxKtNRS70LudHBKa3xxofD/IVNnwaBdMi4t5txdTuvrS0Q5gW CMDgF4Ow0uJFKqcP3y83u3nxxCYk5RwRlY4myQIDAQABo1AwTjAdBgNVHQ4EFgQU 3NpXIpRH/HUun+/DzOAGrXnR9uwwHwYDVR0jBBgwFoAU3NpXIpRH/HUun+/DzOAG rXnR9uwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfyKX4RVf190Y wy29Wreq+gzpo5yPZzMNf0EF7yw64EJq4Gf8Tue0P+HO2xVKK8+RsoG3QsZh2/mL X9Q4g/kaPBK/i4PFOewOWglHvG1j8rW3zOQ2DlTa1fDhGgAnz39c/9L7AMy55xkT 6fPLRGELBD/K6KyvzLIgpnHN5gRHqJVgsa+RXzk1pMctpgIdllxST1Wm4qGqWwyM qxqwoypmLcHFjEXmyezzXQ42S2FMi5KbwAY6JvbrC9ovIpxOGExoiIZPn/Ll7O66 J1CDfLCVEgKFGwAI0csi8inFMpjlpLaBBs0O3thBRole08+bs6BAnWQBoQ7eUG98 xnGjHStOdg==
  tls.key:
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDA1TDiS9Z0D0Yr YIK4RgBKEmF+Hd61S/w+NJcypvoTlMqKnAQju6LR2HVPoY6bhkfzWbIPLLWGZym/ XPU9zHYP4Rhp1rdz0RrZiwWPnB2SexM/UGU7R6e/akfSMKbaj4BH3WAAFWiXDe7m Ay9G5LvElBBXN5tK27SMr/bQO1ZuFWy4q+n8F92L6J0qppBd2V4WAN5ouNQ48QBU JTwM4C+eZ9Drf13TlQyaq6Mx3u2KwMDingPd7qMFDZg4irwMfR3Eq01FLvQu50cE prfHGh8P8hU2fBoF0yLi3m3F1O6+tLRDmBYIwOAXg7DS4kUqpw/fLze7efHEJiTl HBGVjibJAgMBAAECggEAXQi+i/+w2M/ht93ZU5SaYSJbHSDGKUwex9hRtmNvNfFM gvFg4eiOVpYLiz7T8d8oaZU13jJg5hZxwtzTiVxS/bhJMg8g9WTGgnybUz0r21c7 4wkjviUFHsnlGtjUN5CSgJmizoeFrZNQVnLk5VxH50DUXW/7oXM8ub/P4YkvWu5t JVquRmxlgzfsb+A2OAefDg+6hVRfrRT5yuAynTMTzocxDWIKX5qT2kWR2w3oOw1P xB+XIPeJYiWbM2Js49X9vnIoV5Ep4JsdfP7ViK7zD1qIk4K47L/LlfriKlW4o0iF 7eTxqV4s1VsBzq6mU24waucUk2bTh0U1tVpMyA0DcQKBgQD8RU7roMhZKEPibv9F tsU6AmGXbdOzNchRzKAJUc7+MtYx79LfgE7Z6menaOAvQopjYf3iha5fuK+0qpD/ tPvSTDlEZmSRbUjBzknVJRT3LLcT5BdRuOCKa+9oRDOvRF5KeVJp4UPcFcuX0HYC pKeffW6Bp6GCC90QPw2oPoJkdQKBgQDDrvIwW/PwdouHM3mUHg55BtC/6EpVeSqu vbe5TDjM8nRv2WzfulNXxXE7t6Np4rQUWRRu1sSetY4j/nABc1doTmr33KpL0CDG QgbyIiAilP+P3d4vM7SQ/D7DAkZCx/sPU2CpT1UAamxMQx4UusQV5VCxlpU0WSOD d7RQTZlehQKBgGm5ekvSBGUpNNoO/rju0lVvbgsSoih3H874XRHK0V9DWvVutweo NGOrk2lb0Ki90FW6Wba3o87GDg0/dqlT5j3KaC28Y+V20Yk3tUW41sKIYOnbK9K6 EpCwj5M5EhggJQw9MEbje5cfI0YqvWp6Ky3yWX/JCyz/X9RROJ4MJIWJAoGAN/If mgXgI2KxF5F29Ar31lBFhGh0QCWpN1ZUXazWHHpBIe1GvFw4EcMIUat6E/7Vv2+M cPgENN/xaIcJHNEOL2skGANFyPETPbuI4tcw2tJAkrA7IsGkoUMM6pIO5m7ob4RG AYIF6l2C4vbZddHD2AK85FkKBXJqvd6OddsJu0UCgYEA34vgqzIC2EyvqdmGO3fP piEyATRVJhBtMUY/RL/NsXA+bweZ7r+77SpOHXn3cYrF+HRRGjQTJY4ndNrKrpEr lNoCEs5TQUMH1YxvDmHcSUOMcQuQa03MUVvSCu9vR2JmM7JAx89H0ccm45UcuSAC lttBYol4/wRnXBm5hZ4Q9bk=


# kubectl create -f mywebsite-ingress-secret.yaml
secret "mywebsite-ingress-secret" created


有多个域名时的配置方法
如果提供服务的网站不止一个域名,例如前面的Ingress策略配置方式三,则SSL证书需要使用额外的一个x509 v3配置文件辅助完成,在[alt_names]段中完成多个DNS域名的设置。
编写openssl.cnf配置文件:
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = mywebsite.com
DNS.2 = mywebsite2.com

1)首先生成自签名的CA证书
# openssl genrsa -out ca.key 2048

# openssl req -x509 -new -nodes -key ca.key -days 5000 -out ca.crt -subj "/CN= mywebsite.com"

2)基于openssl.cnf和ca证书生成ingress SSL证书
# openssl genrsa -out ingress.key 2048

# openssl req -new -key ingress.key -out ingress.csr -subj "/CN= mywebsite.com" -config openssl.cnf

# openssl x509 -req -in ingress.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ingress.crt -days 5000 -extensions v3_req -extfile openssl.cnf

3)根据ingress.key和ingress.crt文件创建secret资源对象
# kubectl create secret tls mywebsite-ingress-secret --key ingress.key --cert ingress.crt
至此,就成功完成了多个域名条件下的Ingress TLS证书与Secret对象的创建了。


最后,创建Ingress对象并在tls段中引用刚刚创建好的Secret对象
文件mywebsite-ingress-tls.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: mywebsite-ingress-tls
  namespace: kube-system
spec:
  tls:
  - hosts:
    - mywebsite.com
    secretName:  mywebsite-ingress-secret
  rules:
  - host: mywebsite.com
    http:
      paths:
      - path: /healthz
        backend:
          serviceName: default-http-backend
          servicePort: 80
      - path: /examples
        backend:
          serviceName: webapp
          servicePort: 8080

之后,就可以通过HTTPS来访问mywebsite.com了。
[root@bogon ~]# curl -H 'Host: mywebsite.com' -k https://10.0.2.6/healthz
ok
[root@bogon ~]#

[root@bogon ~]# curl -H 'Host: mywebsite.com' -k https://10.0.2.6/examples/
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at


  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!DOCTYPE HTML><html lang="en"><head>
<meta charset="UTF-8">
<title>Apache Tomcat Examples</title>
</head>
<body>
<p>
<h3>Apache Tomcat Examples</H3>
<p></p>
<ul>
<li><a href="servlets">Servlets examples</a></li>
<li><a href="jsp">JSP Examples</a></li>
<li><a href="websocket/index.xhtml">WebSocket Examples</a></li>
</ul>
</body></html>
[root@bogon ~]#


13.10 Ingress的发展路线

当前的Ingress还是Beta版本,在Kubernetes的后续版本中将增加以下功能。
  • 支持更多TLS选项,例如SNI、重加密等。
  • 支持L4和L7负载均衡策略(目前只支持HTTP层的规则)。
  • 支持更多类型的Ingress Controller,除了Nginx,还有HAProxy、Linkerd、traefik、AWS Application Load Balancer Ingress Controller等已经实现了的Ingress Controller,详情可参考https://github.com/kubernetes/ingress-nginx/blob/master/docs/catalog.md的说明。


参考资料:
《Kubernetes权威指南——从Docker到Kubernetes实践全接触》第2章
https://github.com/kubernetes/ingress-nginx


  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值