如何存取 Pod----为什么会有Service ?
上几篇文章中,我们成功的
- 将 Deployment 物件部署到 k8s 丛集中
- 运行了三个 Pod
查看一下 Pod 的运行状态
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-75f4785b7-6fbd4 1/1 Running 0 1m
nginx-75f4785b7-g284n 1/1 Running 0 1m
nginx-75f4785b7-vd4tt 1/1 Running 0 1m
接下来就是讨论如何存取 Pod。由于每个 Pod 被建立的时候 k8s 会动态的分配 IP 给每个 Pod,最简单的方式当然就是直接透过 IP 存取。
但由于 Pod 有临时的特性,因此当 Pod 发生错误需要重新被产生时 IP 也会被重新分配,如果透过上述方式,存取 Pod 将会相当的不方便,因为使用者要一直更新 IP 才知道该如何正确的存取 Pod。
为什么其中一个 Pod 坏掉 k8s 会重新帮我们建立一个 Pod 呢?还记得我们说过,k8s 会尽力去满足我们想要物件的状态吗?当 Pod 发生问题无法存取的时候,k8s 会察觉到系统的变化也会发现运行三个 Pod 是使用者想要的状态,因此 k8s 就会再重新帮我们建立一个新的 Pod 以符合使用者想要的状态。
如果想查看 Pod 被分配的 IP 可以用下列指令,请记得把 Pod 名称 (nginx-75f4785b7-6fbd4) 置换成正确名字
$ kubectl get pods nginx-75f4785b7-6fbd4 -o jsonpath --template={.status.podIP}
因此 k8s 引用了一个更高阶的物件 Service 来处理连接到 Pod。
Service
Service 物件利用 Label 与 Selector 来决定如何存取 (policy) 以及可以存取哪些 Pod (logical group)。 我们在使用者跟 Pod 中间加了一个 Service 物件。透过 Selector (app==nginx) ,而使用者可以透过 Service 存取两个 Pod。当其中一个 Pod 故障需要被重新产生时,新产生的 Pod 也能够透过 Service 正确存取
Service Type
Service 物件根据使用方式有三个不同的形态
- 仅供丛集内部存取:ClusterIP
- 供丛集及外部存取:NodePort, LoadBalancer
- 对应丛集外其他资源:ExternalName
由于我们需要透过 Service 存取 Pod,因此,NodePort 形态的 Service 正符合我们的需求。
LoadBalancer 一般需搭配云端服务使用 (AWS, GCP, ...)。
ExternalName 是透过 CNAME 与外部连接
首先,建立 service.yaml
# service.yaml
---yaml
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
kind: Service:指定为 Service 物件spec.type: NodePort:指定 Service 型态。spec.selector.app: nginx:指定 Service 绑定 Label 有 app: nginx 的 Pod 请注意,如果没有指定则预设会是 ClusterIP 型态则仅供丛集内部存取,外部无法存取
接着,部署到 k8s
$ kubectl apply -f service.yaml
service "web" created
再透过指令查看 Service
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 1d
web NodePort 10.0.0.221 <none> 80:31063/TCP 6s
这里会看到两个 Service 物件,其中 web 就是我们新增加的物件。
无特别指定下,k8s 会自动配置 30000 ~ 32767 区间的 port 供 Service 使用
这里有点复杂,我们用一个图来说明 k8s 丛集中的状态
当我们部署了一个名为 web 的 Service 物件时,在 k8s 内部就会建立一个对应的 web: 10.0.0.242 (IP 会自动配置不用担心),并且将所有的 Node 的 32080 port 对应到 web 这个 Service。因此无论使用者从哪一个 Node 存取 32080 port 都会被指到 web 并透过 web 找到对应的 Pod (app: nginx)。
Pod 可能会分散在不同的 Node,如同上图中 other-svc 对应的 Pod,但是不用担心存取问题 K8s 会帮你处理。这也正是抽象化的好处,存取将透过单一入口而不需要知道各个主机的设定。
连接到 Pod
首先透过下面指令查看 minikube ip
$ minikube ip
192.168.99.100
接着打开浏览器输入http://192.168.99.100:32758
这里说明了一个很重要的概念:解耦。大家应该有发现,Servcie 与 Pod 之间透过 Label 跟 Selector 绑定,但是 Service 与 Pod 产生的顺序并没有限制。只要需要绑定的 Pod 被产生,k8s 就会自动帮我们建立关系,当然,前提是 Label 与 Selector 要正确。
Ingress解耦
我们成功地利用 Service 把存取 Pod 的工作抽象化,即把存取 Pod 的工作转换成存取 Service,我们可以透过 Node IP:Port 的方式存取 Pod,但是在 k8s 中 Node 是可以视需求随时增加或减少的,也因此 Node IP 随时有可能会有变动,因此 Ingress 物件就是再一次帮我们把存取 Service 抽象化的好东西。
底下我们来操作一下,先看个图
这里要发挥一下想像力,想像一下整个 k8s 丛集由一群 Node 组成并提供多个不同的服务 (Service),就像图中的四个不同颜色的 Service,我们希望使用者能够从一个单一的入口就能够存取 k8s 中不同的服务,而 Ingress 物件就能帮我们达到这样的目的。
如上图,我们希望在 k8s 中建立两个 Service 分别是
red-service 与green-service。
red-service绑定四个标记为 app: red-nginx 的 Pod 而 green-service 绑定两个标记为 app: green-nginx 的 Pod。使用者可以透过 http://green.myweb.com 与 http://red.myweb.com 来分别浏览 green-service 与red-service。
让我们先看看 Deployment 的内容
# ingress.deployment.yaml
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: red-nginx
spec:
replicas: 4 <=== 我们希望 red-service 有四个 Pod 运行 red-nginx
template:
metadata:
labels:
app: red-nginx
spec:
containers:
- name: nginx
image: jlptf/red <=== 预先做好的 Docker 映像档
ports:
- containerPort: 80
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: green-nginx
spec:
replicas: 2 <=== 我们希望 green-service 有两个 Pod 运行 green-nginx
template:
metadata:
labels:
app: green-nginx
spec:
containers:
- name: nginx
image: jlptf/green <=== 预先做好的 Docker 映像档
ports:
- containerPort: 80
部署到 k8s 并观察
$ kubectl apply -f ingress.deployment.yaml
deployment "red-nginx" created
deployment "green-nginx" created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
green-nginx-f5989dfd9-fcm5q 1/1 Running 0 47s
green-nginx-f5989dfd9-hzbq5 1/1 Running 0 47s
red-nginx-787f484d9c-grbsk 1/1 Running 0 34s
red-nginx-787f484d9c-h7pl6 1/1 Running 0 47s
red-nginx-787f484d9c-m897h 1/1 Running 0 29s
red-nginx-787f484d9c-n946g 1/1 Running 0 47s
很顺利,运行了六个 Pod 预计分别给 red-service 与 green-service 使用,接着看看 Service 的内容
看 prefix 应该不难分辨哪个 Pod 是给哪个 Service 使用的吧!所以说名字取的好,爸爸没烦恼!是吧~~
# ingress.service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: green-service
spec:
type: NodePort
selector:
app: green-nginx <=== 绑定标记为 green-nginx 的 Pod
ports:
- protocol: TCP
port: 80
---
apiVersion: v1
kind: Service
metadata:
name: red-service
spec:
type: NodePort
selector:
app: red-nginx <=== 绑定标记为 red-nginx 的 Pod
ports:
- protocol: TCP
port: 80
然后是今天的主角 Ingress
# ingress.yaml
---
apiVersion: extensions/v1beta1
kind: Ingress <=== 指定物件种类为 Ingress
metadata:
name: web
spec:
rules:
- host: green.myweb.com <=== 指定 host 名称为 green.myweb.com
http:
paths:
- backend:
serviceName: green-service <=== 绑定名称为 green-service 的 Service 物件
servicePort: 80
- host: red.myweb.com <=== 指定另一个 host 名称为 red.myweb.com
http:
paths:
- backend:
serviceName: red-service <=== 绑定名称为 red-service 的 Service 物件
servicePort: 80
类似 Service 绑定 Pod 的概念,Ingress 透过 serviceName 指定需要绑定的 Service。接着部署到 k8s
$ kubectl apply -f ingress.yaml
ingress "web" created
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
web green.myweb.com,red.myweb.com 192.168.99.100 80 43s
这里就会看到
green.myweb.com与red.myweb.com都被绑定到192.168.99.100的位置 如果在云端提供商上建立 Ingress,你可以在 ADDRESS 看到被分配的 IP另外,你可能需要执行 minikube addons enable ingress 启用 Ingress。感谢changec大大的提醒,更详细的内容可以参考
由于 green.myweb.com 与 red.myweb.com 这两个网址并非真正存在,因此,如果你在浏览器上打上这两个网址,并不会有任何内容。
我们要让自己的电脑能够理解这两个网址的 IP 位置,Windows 请修改\WINDOWS\system32\drivers\etc\hosts
192.168.99.100 green.myweb.com <=== 192.168.99.100 是 Ingress 分配的 IP
192.168.99.100 red.myweb.com
你可以使用以下指令将上面的内容加到\WINDOWS\system32\drivers\etc\hosts:
$ echo 192.168.99.100 green.myweb.com >> /etc/hosts
$ echo 192.168.99.100 red.myweb.com >> /etc/hosts
打开浏览器并键入网址,一切顺利的话你会看到
green.myweb.com
另外,建立 jlptf/red 与 jlptf/green 这两个映像档用到的 Dockerfile 你可以在 ingressDocker 资料夹里面找到,如果你想要自己动手建立映像档可以参考使用。
建立映像档参考指令如下:
$ docker build --build-arg HTML=green.html -t jlptf/green .
$ docker build --build-arg HTML=red.html -t jlptf/red .