《kubernetes in Action》(中文版)读书笔记 1
1. 集群内如果使用服务
K8s 可以通过创建 service 的方式,提供固定的 IP 和端口,当服务不受经常变动的 pod 的影响。
k8s 可以根据 service 的 manifest 来创建新的服务,可以通过以下命令来访问service,
1
kubectl exec [podName] --curl -a [url]
收到请求的 service 会从下辖的 pod 中用负载均衡的方式随机调一个pod 对请求进行响应。
- 配置
sessionAffinity为ClientIp可以让特定客户端的请求每次都指向同一个 pod。 - service 是在 TCP 和 UDP 层进行工作的,所以不涉及 HTTP 协议中的 cookie 概念。
- service mainifest:指明 name:http;port:80;targetPort:8080(Pod 上的端口)。
- 也可以是 targetPort:someAliaName,然后再 pod 的 manifest 中指明 name:someAliaName,containerPort:8080.实现了解耦。
- 服务发现:
- 方法一:环境变量。先有 service后有 pod 时,pod 可以取到环境变量中的 serviceIp和 port 等信息。但若先有 pod 后有 service,信息就很难取到。
- 方法二:DNS 服务。k8s 中设置一个 DNS 服务的 pod,如此一来,—curl 时不再使用ip 地址的 url,而是使用 FQDN(全限定域名),请求先查 DNS 服务,然后就能找到服务了,再然后就找到真正工作的 pod 了。
- 配置
- 但 service 也只能指向此集群内部的若干 pod,无法对外暴露,无法指向外部 IP 和端口。
- 所以可能需要手动创建 Endpoint 来完成。
- 在 create service 时,如果制定了 pod 选择器,那么 service 被创建,endpoint 也会自动创建,此时的 endpoint 携带着 pod 选择器选中的 pod 的信息(都属于内部 pod)。
- 手动创建时,需要 create 一个没有 pod 选择器的 service,那么将不会有 Endpoint 被自动创建,所以可以手动create一个,并将外部的 ip 及端口信息写入 endpoint 的 manifest中,这样一来,当请求 service 服务时,就可以关联到外部的 ip 地址及端口上了。
- 除了手动创建 Endpoint 外,还可以用创建别名的方式来做到。
- 对 service manifest 中的 type 字段设置为
ExternalName,随后在 externalName自定义一个别名。 - 这样就可以通过此别名请求服务,即通过 CNAME 的方式定位到你想去的地方(需要 FQDN,而不是集群 IP),隐藏了实际的服务名称和服务的 pod 信息,可以完全绕过服务代理。
- 对 service manifest 中的 type 字段设置为
- 所以可能需要手动创建 Endpoint 来完成。
2. 服务对外暴露
service 的类型设置为 NodePort、LoadBalance,或者创建 Ingress 资源等三种机制。
这篇文章讲得不错:NodePort,LoadBalancer还是Ingress?我该如何选择
2.1. NodePort
创建一个 NodePort 服务(type 为 NodePort),k8s 在每一个 node 上都可以保留一个 port(所有 node 上的此 port 都相同),传入此 port 的流量转发给服务部分(type 为 clusterIP)的 pod。
- NodePort 服务可以跟服务部分 pod 互联。而且,node 之间,可以通过此 port 做到彼此互联。
- 在 NodePort 的 manifest 中定义好了service port(service port 是跟背后的 pod 的 targetPort是一对多映射的),也就明确了能够提供的哪种服务。
- 开放的 port 可以自定义,也可以不设置,交由 k8s 来决定。
通过地址
[any node's ip]:开放port就可以访问服务。这种方式有一些不足:
- 一个端口只能供一个服务使用;
- 只能使用30000–32767的端口;
- 如果节点 / 虚拟机的IP地址发生变化,需要进行处理。
- 一个端口只能供一个服务使用;
2.2 loadBalance
loadBalance 是 NodePort 的扩展,在部分不支持 loadBalance 的环境中,k8s 应该采取 NodePort 的方式。
type 为 LoadBalancer。
这是默认方式。指定端口的所有流量都会转发到服务中,没有过滤,也没有路由。这意味着你几乎可以发送任意类型的流量到服务中,比如HTTP、TCP、UDP、Websockets、gRPC等等。
这里最大的不足是,使用LoadBalancer发布的每个服务都会有一个独有的公有IP地址,你需要支付每个服务的LoadBalancer 费用,这是一笔不小的开支。
可设置 spec 中
externalTrafficPolicy为Local,来让真正运行的 pod,就是接收连接的 node 上的 pod。可减少网络中不必要的跳数。举例:node1、node2、node3 上都有相同的 pod 提供对外服务。此时 node1被外部连接上了,通过 loadBalancer 后,外部通信需要重定向到执行的 pod 上,如果将
externalTrafficPolicy设置为Local了,那么将优先选择本地运行的 pod 来执行服务。- 如果没有本地 pod 存在,则连接将被挂起(此情况应当被避免,所以负载均衡器要确保把连接转发到至少具有一个 pod 的 node 上)。
- 采用此策略可能导致负载分布的不均衡。LoadBalancer 在 node 间是均匀分布连接的,但nodes 之间的 pod 数量可能各有不同。
因为发生了源网络地址转换(SNAT),所以最初连接时的客户端 IP 在数据包转发时发生更改,pod 将不会看到实际的客户端 IP。
2.3 Ingress
Ingress 是在多个服务前充当入口的角色,通过一个 Ingress 就可以暴露多个服务。
Ingress 只需要一个公网 IP 就能为许多服务提供访问。进入的 HTTP 请求,会根据请求的主机名和路径决定请求转发到的服务。
想使用 Ingress的前提是需要一个 Ingress 控制器,有了控制器才可以创建 Ingress 资源,创建步骤:
Ingress manifest 如下,说明是将 host 的所有 HTTP 请求,全都转发给80 端口上的 kubia-nodeport 服务。
1
2
3
4
5
6
7
8
9
10
11# 省略了 apiVersion、kind、metadata 等 info
...
spec:
rules:
- host: kubia.example.com
http:
paths:
-path: /
backend:
serviceName: kubia-nodeport
servicePort: 80- rules 和 paths 都是数组,说明可以将多个主机和路径映射到多个服务
创建后使用命令:
kubectl get ingresses,查看 Ingress 的 IP 地址。
需要在 Ingress 中配置 Host 指向 Ingress 的 IP 地址。
之后就可以
curl http://kubia.example.com访问服务了。- 原理:Ingress 通过此 host 知道 client 想连接的是哪个服务,然后通过服务关联的 endpoint查看 podIp,并将 client 的请求转发给其中的一个 pod。
Ingress 甚至可以创建TLS认证,支持 HTTPS 服务。
Ingress 控制器负责处理与 TLS 相关的内容,而后面的服务以及 Pod 都只需处理 Http 的内容即可。
需要将证书和私钥附加到 Ingress 上,做法:这两个东西存储在 Secret 的资源中,然后在 Ingress manifest 中引用它。
1
2
3
4
5
6
7
spec:
tls:
- hosts:
- kubia.example.com
secretName: tls-secret # 先创建证书和私钥,然后创建一个 Secret,可以取名为 tls-secret
Ingress目前只支持 L7 层的负载均衡。
3. Volume 数据卷
- 容器是无状态的,容器内会挂载一个远程数据卷,使得在容器里创建的文件,实质上保存在远程的存储服务器上,或者以分布式的方式保存在多个节点上,与当前的宿主机没有任何绑定关系。
- 因此无论在哪个宿主机上启动新的容器,都可以请求挂载指定的持久化 Volume,从而访问到数据卷里保存的内容。
- 以上过程可以借助 k8s 的存储插件来完成,比如 Ceph、Rook等。Rook 甚至具备了迁移、容灾备份、监控等大型企业级功能。
