快速掌握 Kubernetes 网络基础

《快速掌握 Kubernetes 网络基础》最先发布在 blog.hdls.me/15376903337…html

由于 Pod 的易变性(重建以后 IP 可能会改变),Service 承担了服务入口的职能,是 k8s 网络部分的核心概念,在 k8s 中,Service 主要担任了四层负载均衡的职责。本文从负载均衡、外网访问、DNS 服务的搭建及 Ingress 七层路由机制等方面,讲解 k8s 的网络相关原理。node

Service 详解

Service 是主要用来实现应用程序对外提供服务的机制。nginx

如上图所示,Service 是对 Pod 的一层抽象,主要经过 TCP/IP 机制及监听 IP 和端口号来对外提供服务。与 Pod 不一样的是,Service 一旦建立,系统会为其分发一个 ClusterIP (也能够本身指定),且在其生命周期内不会发生变化。web

Service 的建立

命令行快速建立

在建立好 RC 后,能够经过命令行 kubectl expose 来快速建立一个对应的 Service 。好比现已有一个名为 hdls 的 rc:数据库

kubectl expose rc hdls
复制代码

这种方式建立出来的 Service,其 ClusterIP 是系统自动为其分配的,而 Service 的端口号是从 Pod 中的 containerPort 复制而来。后端

经过 YAML 建立

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  ports:
  - port: 8080   # Service 的虚拟端口
    targetPort: 8000  # 指定后端 Pod 的端口号
  selector:  # Label 选择器
    app: hdls
复制代码

定义好 YAML 文件后,经过命令 kubectl create -f <service.yml> 便可建立。Service 的定义须要指定如下几个关键字段:api

  • ports
    • port: Service 的虚拟端口
    • targetPort: 后端 Pod 的端口号,若不填则默认与 Service 的端口一致
  • selector: Label 选择器,指定后端 Pod 所拥有的 Label

负载分发策略

k8s 提供了两种负载分发策略:bash

  • RoundRobin:轮询方式。即轮询将请求转发到后端的各个 Pod 上。
  • SessionAffinity:基于客户端 IP 地址进行会话保持模式。即相同 IP 的客户端发起的请求被转发到相同的 Pod 上。

在默认状况下,k8s 采用轮询模式进行路由选择,但咱们也能够经过将 service.spec.SessionAffinity 设置为 “ClusterIP” 来启用 SessionAffinity 模式。网络

一些特殊状况

Headless Serviceapp

在这种状况下,k8s 经过 Headless Service 的概念来实现,即不给 Service 设置 ClusterIP (无入口 IP),仅经过 Label Selector 将后端的 Pod 列表返回给调用的客户端。

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  ports:
  - port: 8080   
    targetPort: 8000 
  clusterIP: None
  selector: 
    app: hdls
复制代码

该 Service 没有虚拟的 ClusterIP ,对其访问能够得到全部具备 app=hdls 的 Pod 列表,客户端须要实现本身的负责均衡策略,再肯定具体访问哪个 Pod。

无 LabelSelector Service

通常来讲,应用系统须要将外部数据库做为后端服务进行链接,或另外一个集群或 namespace 中的服务做为后端服务。这些状况,能够经过创建一个无 Label Selector 的 Service 来实现:

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  ports:
  - port: 8080   
    targetPort: 8000 
复制代码

该 Service 没有标签选择器,即没法选择后端 Pod。这时系统不会自动建立 Endpoint,须要手动建立一个与该 Service 同名的 Endpoint,用于指向实际的后端访问地址。

apiVersion: v1
kind: Endpoints
metadata:
  name: hdls  # 与 Service 同名
subsets:
  - addresss:
   - IP: 1.2.3.4   # 用户指定的 IP 
   ports:
   - port: 8000
复制代码

此时,如上面的 YAML 建立出来 Endpoint,访问无 Label Selector 的 Service ,便可将请求路由到用户指定的 Endpoint 上。

多端口的 Service

在 service.spec.ports 中定义多个 port 便可,包括指定 port 的名字和协议。

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  ports:
  - name: dns
    port: 8080   
    protocol: TCP
  - name: dns-tcp
    port: 8080 
    protocol: UDP
  selector: 
    app: hdls
复制代码

外网访问

Pod 和 Service 都是 k8s 集群内部的虚拟概念,因此集群外的客户没法访问。但在某些特殊条件下,咱们须要外网能够访问 Pod 或 Service,这时咱们须要将 Pod 或 Service 的端口号映射到宿主机,这样客户就能够经过物理机访问容器应用。

外网访问 Pod

将容器应用的端口号映射到物理机上。有两种方式,以下。

设置容器级别的 hostPort

这种是将容器应用的端口号映射到物理机。设置以下:

apiVersion: v1
kind: Pod
metadata:
  name: hdls-pod
spec:
  containers:
  - name: hdls-container
   image: ***
   ports:
   - containerPort: 8000
     hostPort: 8000
复制代码

设置 Pod 级别的 hostNetwork=true

这种是将该 Pod 中全部容器端口号都直接映射到物理机上。此时须要注意的是,在容器的 ports 定义部分,若不指定 hostPort,默认 hostPort=containerPort,若设置了 hostPort,则 hostPort 必须等于 containerPort。设置以下:

apiVersion: v1
kind: Pod
metadata:
  name: hdls-pod
spec:
  hostNetwork: true
  containers:
  - name: hdls-container
   image: ***
   ports:
   - containerPort: 8000
复制代码

外网访问 Service

也有两种方式。

设置 nodePort 映射到物理机

首先须要设置 nodePort 映射到物理机,同时须要设置 Service 的类型为 NodePort:

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  type: NodePort  # 指定类型为 NodePort
  ports:
  - port: 8080 
    targetPort: 8000 
    nodePort: 8000   # 指定 nodePort
  selector: 
    app: hdls
复制代码

设置 LoadBalancer 映射到云服务商提供的 LoadBalancer 地址

这种用法仅用于在公有云服务提供商的云平台上设置 Service 的场景。须要将 service.status.loadBalancer.ingress.ip 设置为云服务商提供的负载均衡器的 IP。则对该 Service 的访问请求将会经过 LoadBalancer 转发到后端 Pod,且负载均衡的实现方式依赖于云服务商提供的 LoadBalancer 的实现机制。

DNS 搭建

为了可以实现经过服务名在集群内部进行服务的相互访问,须要建立一个虚拟的 DNS 服务来完成服务名到 ClusterIP 的解析。

k8s 提供的 DNS

k8s 提供的 DNS 服务名为 skydns,由下面四个组件组成:

  • etcd: DNS 存储;
  • kube2sky: 将 k8s Master 中的 Service 注册到 etcd ;
  • skyDNS: DNS 域名解析服务;
  • healthz: 对 skyDNS 的健康检查。

skyDNS 服务由一个 RC 和一个 Service 组成。在 RC 的配置文件中,须要定义 etcd / kube2sky / skydns / healthz 四个容器,以保证 DNS 服务正常工做。须要注意的是:

  1. kube2sky 容器须要访问 k8s Master,因此须要在配置文件中为其配置 Master 所在物理主机的 IP 地址和端口;
  2. 须要将 kube2sky 和 skydns 容器的启动参数 --domain 设置为 k8s 集群中 Service 所属域名。容器启动后 kube2sky 会经过 API Server 监控集群中全部 service 的定义,生成相应的记录并保存到 etcd ;
  3. skydns 的启动参数 -addr=<IP:Port> 表示本机 TCP 和 UDP 的 Port 端口提供服务。

在 DNS Service 的配置文件中,skyDNS 的 ClusterIP 须要咱们指定,每一个 Node 的 kubelet 都会使用这个 IP 地址,不会经过系统自动分配;另外,这个 IP 须要在 kube-apiserver 启动参数 --service-cluster-ip-range 内。

在 skydns 容器建立以前,须要先修改每一个 Node 上 kubelet 的启动参数:

  • --cluster_dns=<dns_cluster_ip> ,dns_cluster_ip 为 DNS 服务的 ClusterIP ;
  • --cluster_domain=<dns_domain> , dns_domain 为 DNS 服务中设置的域名。

DNS 工做原理

  1. 首先 kube2sky 容器应用经过调用 k8s Master 的 API 得到集群中全部 Service 信息,并持续监控新 Service 的生成,写入 etcd;
  2. 根据 kubelet 的启动参数的设置,kubelet 会在每一个新建立的 Pod 中设置 DNS 域名解析配置文件 /etc/resolv.conf 中增长一条 nameserver 配置和 search 配置,经过 nameserver 访问的实际上就是 skydns 在对应端口上提供的 DNS 解析服务;
  3. 最后,应用程序就能够像访问网站域名同样,仅经过服务的名字就能访问服务了。

Ingress

Service 工做在 TCP/IP 层,而 Ingress 将不一样的 URL 访问请求转发到后端不一样的 Service ,实现 HTTP 层的业务路由机制。而在 k8s 中,须要结合 Ingress 和 Ingress Controller ,才能造成完整的 HTTP 负载均衡。

Ingress Controller

Ingress Controller 用来实现为全部后端 Service 提供一个统一的入口,须要实现基于不一样 HTTP URL 向后转发的负载分发规则。Ingress Controller 以 Pod 的形式运行,须要实现的逻辑:

  • 监听 APIServer,获取全部 Ingress 定义;
  • 基于 Ingress 的定义,生成 Nginx 所需的配置文件 /etc/nginx/nginx.conf
  • 执行 nginx -s reload ,从新加载 nginx.conf 配置文件的内容。

定义 Ingress

k8s 中有一种单独的名为 Ingress 的资源,在其配置文件中能够设置到后端 Service 的转发规则。好比,为 hdls.me 定义一个 ingress.yml:

apiVersion: extensions/v1beta1
kind: Ingress
metadata: 
  name: hdls-ingress
spec:
  rules:
  - host: hdls.me
   http:
     paths:
     - path: /web
       backend:
         serviceName: hdls
         servicePort: 8000
复制代码

最后采用 kubectl create -f ingress.yml 建立 Ingress。能够登陆 nginx-ingress Pod 查看其自动生成的 nginx.conf 配置文件内容。

相关文章
相关标签/搜索