回到基础知识。访问Kubernetes Pods
Kubernetes是一个巨大的野兽。在它开始发挥作用之前,你需要了解许多概念。在这里,学习几种访问集群外的吊舱的方法。
Kubernetes是一个巨大的野兽。在它开始发挥作用之前,你需要了解许多不同的概念。当一切都设置好后,你可能会想把一些pod暴露在集群的外部。Kubernetes提供了不同的方法来做到这一点。我将在这篇文章中描述它们。
设置
为了进行演示,我将使用kind。
kind是一个使用Docker容器 "节点 "运行本地Kubernetes集群的工具。 kind主要是为测试Kubernetes本身设计的,但也可用于本地开发或CI。
我将使用一个双节点集群。
YAML
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30800 # 1
hostPort: 30800 # 1
- role: worker # 2
- role: worker # 2
- #1:端口转发以应对Mac上的Docker VM层(见下文)
- #2:两个节点
Shell
kind create cluster -- config kind.yml
接下来,我们需要一个容器。它不应该只是运行和停止。让我们使用在写这篇文章时可用的最新的Nginx镜像。
有了实物,我们必须预装镜像,以便它们可以使用。
Shell
docker pull nginx:1.23
kind load docker-image nginx:1.23
最后,我把kubetcl 别名为k 。
Shell
alias k=kubectl
默认情况下没有外部访问
默认的情况是不提供对集群外部的访问。
Shell
k create deployment nginx --image=nginx:1.23 # 1
- #1:创建一个单一pod的部署。
让我们检查一下是否一切正常。
Shell
k get pods
纯文本
NAME READY STATUS RESTARTS AGE
nginx-6c7985744b-c7cpl 1/1 Running 0 67s
这个pod有一个IP,但是我们无法在集群外到达它。
Shell
k get pod nginx-6c7985744b-c7cpl --template '{{.status.podIP}}'
纯文本
10.244.1.2
让我们通过在pod内部运行shell来确认这个IP。
Shell
k exec -it nginx-6c7985744b-c7cpl -- /bin/bash
hostname -I
纯文本
10.244.1.2
我们无法成功地在集群外ping到这个IP:这是一个内部IP。
内部IP是不稳定的
我们创建了一个部署;因此,如果我们删除单个pod,Kubernetes会检测到它并创建一个新的,这要感谢它的自我修复能力。
Shell
k delete pod nginx-6c7985744b-c7cpl
k get pods
纯文本
NAME READY STATUS RESTARTS AGE
nginx-6c7985744b-c6f92 1/1 Running 0 71s
让我们检查一下它的新IP。
Shell
k exec -it nginx-6c7985744b-c6f92 -- /bin/bash
hostname -I
纯文本
10.244.2.2
Kubernetes创建了一个新的pod,但它的IP与被删除的pod的不同。我们不能依靠这个IP进行pod间的通信。事实上,我们不应该直接使用一个pod的IP。
为了解决这个问题,Kubernetes提供了Service 对象。服务在pod面前代表一个稳定的接口。Kubernetes管理一个服务和它的pod(s)之间的映射关系。它绑定新的pod,并解除对已删除pod的绑定。
ClusterIP:在集群内部的IP上公开Service。选择这个值使得Service只能从集群内部到达。这是默认的ServiceType。
让我们用一个服务来公开现有的部署。
Shell
k expose deployment nginx --type=ClusterIP --port=8080
k get svc
纯文本
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 9m47s
nginx ClusterIP 10.96.93.97 <none> 8080/TCP 4s
从这一点开始,就可以通过服务的ClusterIP 来访问pod。
所有的设置都是为了在集群内部访问。从外部来看,这还不可能。那么我们为什么要使用ClusteIP ?对于那些你不想暴露在外部世界的服务来说,它是非常有用的:数据库、ElasticSearch节点、Redis节点等等。
访问一个Pod
从集群外部访问一个pod是事情变得有趣的时候。
我们首先需要删除现有的部署和服务。
Shell
k delete deployment nginx
k delete svc nginx
允许外部访问的最简单的方法是将服务的类型改为NodePort 。
NodePort 添加一个访问端口到ClusterIP 。
NodePort:在每个节点的IP上以一个静态端口(NodePort)暴露服务。一个ClusterIP服务,NodePort服务将被自动创建为其路由。你可以通过请求:,从集群外部联系NodePort服务。- 发布服务(NodePort)
我想让pod返回其IP和主机名来演示它。我们必须从命令行转移到专门的Kubernetes清单文件,因为我们必须配置Nginx。其结果与命令行的状态相同,但增加了Nginx配置。
YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.23
volumeMounts: # 1
- name: conf
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
readOnly: true
volumes: # 1
- name: conf
configMap:
name: nginx-conf
items:
- key: nginx.conf
path: nginx.conf
---
apiVersion: v1 # 1
kind: ConfigMap
metadata:
name: nginx-conf
data:
nginx.conf: |
events {
worker_connections 1024;
}
http {
server {
location / {
default_type text/plain;
return 200 "host: $hostname\nIP: $server_addr\n";
}
}
}
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
type: NodePort # 2
ports:
- port: 80
nodePort: 30800
- #1:覆盖默认配置,返回主机名和IP地址。
- #2:
NodePort,将pod的端口映射到一个外部可访问的端口。
让我们应用该配置。
Shell
k apply -f deployment.yml
注意,我在Mac上运行;因此,在Docker周围有一个VM容器,就像在Windows中一样。由于这个原因,kind需要将虚拟机的端口转发到主机上。请查看kind文档,了解如何实现它。
一旦Kubernetes调度了pod,我们就可以在配置的端口上访问它。
Shell
curl localhost:30800
纯文本
host: nginx-b69d8877c-p2s79
IP: 10.244.2.2
请求的路径如下(尽管Mac/Windows上有虚拟机层):
-
curl,请求会进入任何节点。注意,在云提供商的设置上,你可以针对任何承载部署中的pod部分的Kubernetes节点。在本地设置中,我们的目标是
localhost,让虚拟机层针对一个节点。 -
节点看到端口
30800,并将请求转发给相关端口的NodePort服务。 -
该服务将请求转发给pod,将端口从
30800转移到80。
现在,让我们把部署中的pod数量增加到两个。
Shell
k scale deployment nginx --replicas=2
k get pods -o wide
Kubernetes平衡集群,使每个pod驻留在一个不同的节点上。
纯文本
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-b69d8877c-w7db4 1/1 Running 0 129m 10.244.2.2 kind-worker <none> <none>
nginx-b69d8877c-z5kqs 1/1 Running 0 38m 10.244.1.2 kind-worker2 <none> <none>
请求将被发送到哪个节点/pod?
Shell
while true; do curl localhost:30800; done
纯文本
host: nginx-b69d8877c-w7db4
IP: 10.244.2.2
host: nginx-b69d8877c-w7db4
IP: 10.244.2.2
host: nginx-b69d8877c-z5kqs
IP: 10.244.1.2
host: nginx-b69d8877c-z5kqs
IP: 10.244.1.2
host: nginx-b69d8877c-w7db4
IP: 10.244.2.2
host: nginx-b69d8877c-w7db4
IP: 10.244.2.2
该服务在所有可用的pod之间平衡请求。
负载平衡抽象允许查询任何集群节点
NodePort 允许查询任何集群节点。LoadBalancer 是集群上的一个门面,用于...负载平衡。这是一个由Kubernetes提供的抽象对象:每个云提供商根据其特殊性对其进行不同的实现,尽管其行为是相同的。
LoadBalancer。使用云提供商的负载平衡器向外部开放服务。NodePort 和 ClusterIP 服务将被自动创建,外部负载平衡器将对其进行路由。
- 发布服务(LoadBalander)
首先,我们需要一个LoadBalancer 实现。kind有开箱即用的与MetalLB的集成。
MetalLB是一个用于裸机Kubernetes集群的负载均衡器实现,使用标准路由协议。
-金属LB
转述kind关于如何安装MetalLB的优秀文档是没有用的。我们可以相应地更新清单。
YAML
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
type: LoadBalancer
ports:
- port: 80
targetPort: 30800
让我们来看看这些服务。
Shell
k get svc
纯文本
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h37m
nginx LoadBalancer 10.96.216.126 127.0.0.240 8080:31513/TCP 82m # 1
- #1:它有一个外部IP!
不幸的是,正如我上面提到的,在Mac(和Windows)上,Docker运行在一个虚拟机中。因此,我们无法从主机上访问 "外部 "IP。拥有适当的Linux系统的读者应该可以访问它。
根据云供应商的情况,LoadBalancer 可能会提供额外的专有功能。
入站,当你需要路由时
Ingress 着重于路由请求到集群中的服务。
它与LoadBalancer 共享某些方面:
然而,它不是一个Service 。
Ingress将HTTP和HTTPS路由从集群外暴露给集群内的服务。流量路由是由Ingress资源上定义的规则控制的。
安装Ingress ,在很大程度上取决于实现。唯一的共同因素是它涉及到CRD。
为了演示,我将使用Apache APISIX Ingress控制器。我不会转述Minikube上Ingress APISIX的安装说明。唯一的区别是将NodePort ,设置为一个设定值。
Shell
helm install apisix apisix/apisix \
--set gateway.type=NodePort \
--set gateway.http.nodePort=30800 \
--set ingress-controller.enabled=true \
--namespace ingress-apisix \
--set ingress-controller.config.apisix.serviceNamespace=ingress-apisix
注意,虽然文档中提到了Minikube,但它适用于任何本地集群,包括同类。
以下服务应该在ingress-apisix 名称空间中可用。
普通文本
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
apisix-admin ClusterIP 10.96.98.159 <none> 9180/TCP 22h
apisix-etcd ClusterIP 10.96.80.154 <none> 2379/TCP,2380/TCP 22h
apisix-etcd-headless ClusterIP None <none> 2379/TCP,2380/TCP 22h
apisix-gateway NodePort 10.96.233.74 <none> 80:30800/TCP 22h
apisix-ingress-controller ClusterIP 10.96.125.41 <none> 80/TCP 22h
为了演示,我们将有两个服务:每个服务将有一个pod的底层部署。请求/left ,将击中一个服务,并返回left;/right,right 。
让我们相应地更新拓扑结构。
YAML
apiVersin: apps/v1
kind: Deployment
metadata:
name: left
labels:
app: left
spec:
replicas: 1
selector:
matchLabels:
app: left
template:
metadata:
labels:
app: left
spec:
containers:
- name: nginx
image: nginx:1.23
volumeMounts:
- name: conf
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
readOnly: true
volumes:
- name: conf
configMap:
name: left-conf
items:
- key: nginx.conf
path: nginx.conf
---
apiVersion: v1
kind: Service
metadata:
name: left
spec:
selector:
app: left
ports:
- port: 80
---
apiVersion: v1
kind: ConfigMap
metadata:
name: left-conf
data:
nginx.conf: |
events {
worker_connections 1024;
}
http {
server {
location / {
default_type text/plain;
return 200 "left\n";
}
}
}
上面的片段只描述了left 的路径。它应该包含对right 路径的类似配置。
在这一点上,我们可以创建配置,将路径路由到服务。
YAML
apiVersion: apisix.apache.org/v2beta3 # 1
kind: ApisixRoute # 1
metadata:
name: apisix-route
spec:
http:
- name: left
match:
paths:
- "/left"
backends:
- serviceName: left # 2
servicePort: 80 # 2
- name: right
match:
paths:
- "/right"
backends:
- serviceName: right # 3
servicePort: 80 # 3
- 使用安装时创建的
ApisixRouteCRD。 - 将请求转发给
left服务。 - 将请求转发到
right服务。
下面是它应该有的样子。请注意,我选择只表示left 路径和一个节点,以避免图表过载。
为了检查它是否有效,让我们再来一次curl。
Shell
curl localhost:30800
纯文本
{"error_msg":"404 Route Not Found"}
这是个好迹象。APISIX有反应了。
我们现在可以尝试卷曲right 路径,以确保它将转发到相关的pod。
Shell
curl localhost:30800/right
纯文本
right
/left,它也能工作。
总结
在这篇文章中,我描述了几种访问集群外的pod的方法:NodePort 和LoadBalancer 服务以及Ingress 。对于Ingress ,你可能已经注意到,ApisixRoute 对象是一个专有的CRD。为了避免它,Kubernetes旨在提供一个抽象:CNCF正在开发一个Gateway API项目。
我将在未来的文章中描述它。