KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
04 · 让外面访问到它 — keel 龙骨
上一章结束时你把东西撤干净了。先把第 02 章那份 web.yaml 再交一次:
上一章结束时你把东西撤干净了。先把第 02 章那份 web.yaml 再交一次:
kubectl apply -f web.yaml
三章下来,它能在集群里自己活着、坏了会自己补。但你在自己电脑上打开浏览器,什么都看不到。
这一章解决这件事。
一、先试试直接用它自己的地址
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
web-565bc6668-c9g8d 1/1 Running 0 2m 10.244.0.8 keel-lab-control-plane
web-565bc6668-wkm7t 1/1 Running 0 3m 10.244.0.7 keel-lab-control-plane
IP 那一列就是容器的地址。直接 curl 它试试:
curl http://10.244.0.8
(地址换成你屏幕上的)
等它在原地转圈,最后超时。 这不是你配错了——这些地址在集群自己的内部网络里,
你电脑上的路由表不认识它们。这是它的设计,不是故障。
另一个想法是"把这两个地址填进 nginx 配置",看上去更有道理,其实才是问题所在。
回到第 03 章:你删掉过一个 Pod,回来的那个是新名字——地址也换了:
被删掉的那个 web-565bc6668-t8bp4 10.244.0.6
回来的那个 web-565bc6668-c9g8d 10.244.0.8
Pod 的地址是一次性的,随它一起生、一起灭——写不进任何配置。既然地址会变,
就给它们一个不变的入口。
二、Service:一个不变的地址 + 一份成员名单
再写一份文件,叫 web-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
只有两处是新的:selector 说哪些 Pod 收我的流量(跟 web.yaml 里的标签对上,所以选的
是那两个);ports 说对外端口 / 转发到容器里的哪个端口。交上去:
kubectl apply -f web-service.yaml
service/web created
kubectl get svc
(这一屏会多一行 kubernetes,那是集群自己用的,忽略它)
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5m11s
web ClusterIP 10.96.94.104 <none> 80/TCP 1s
CLUSTER-IP 那一列就是"不变的地址":10.96.94.104。它不随 Pod 的生灭而变。
再看一条命令,它把这个 Service 现在的成员列出来:
kubectl get endpoints web
NAME ENDPOINTS AGE
web 10.244.0.7:80,10.244.0.8:80 2s
那两个地址就是刚才 get pods -o wide 里的那两个——不是巧合:Service 做的就是"盯着符合
selector 的 Pod,把它们的地址维护成这份名单"。Pod 换了名单跟着换,而 CLUSTER-IP 不动
——这就是你要的固定入口。
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice?
这是提醒不是报错:返回类型在新版本里换了名字,结果照给。
三、真的打开它看一眼
浏览器看不到集群内部地址,但 kubectl 可以在你和集群之间架一条临时的线:
kubectl port-forward svc/web 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
读法和上一门课的 -p 一样:左边是你电脑上的 8080,右边是转发到 Service 的 80。
这一行会一直占着你的终端(它得活着,那条线才在)。
另开一个终端,敲 curl http://localhost:8080,或者浏览器打开 http://localhost:8080:
HTTP/1.1 200 OK
Server: nginx/1.27.5
Content-Type: text/html
<!DOCTYPE html>
<html>
...
这一页是从集群里那两个 Pod 之一出来的。 看完回 port-forward 那个终端按 Ctrl+C,
那条线就断了——它是临时的,真正的对外暴露是另一件事(本课不展开)。
四、它不是什么
- 不是一个新的程序。 Service 自己不跑任何东西:没有镜像、没有容器、没有进程,只有
一个固定的虚拟地址,加上一份"哪些 Pod 收我的流量"的名单。get svc里ClusterIP
那一行不是一个在运行的进程的状态。 - 不是把服务发到公网。
ClusterIP是"只在集群内部可用"——所以才需要port-forward
那根临时线。要让外面的人访问,还得有一层把外部请求转进来的东西。 - 不是给 Pod 换了个固定名字。 Pod 的地址照旧会变;Service 是站在它们前面:
变的它不给你,给你的是那个不动的。
这几句报错在哪一步
| 报错 / 输出(结尾部分) | 它在说什么 |
|---|---|
service/web created 之后 get svc 里没有 web |
多半交错了文件。先 kubectl get svc -A 找一遍 |
curl: (28) Connection timed out 打 Pod 的 IP |
正常。Pod 地址在集群内部网络里,你电脑不通 |
port-forward 报 error: lost connection to pod |
那条线断了。Ctrl+C 重敲一次,它本来就是临时的 |
unable to listen on any of the requested ports: [{8080 8080}] |
你电脑上的 8080 被占了。只改冒号左边,比如 8081:80 |
读完你能做什么
- 说出为什么"把 Pod 的地址写进配置"根本不成立;
- 交一份 Service,用
get svc拿入口、get endpoints看成员名单; - 用
port-forward在自己电脑上真的打开一个集群里的服务。
下一步:最后一章收口到一个更重要的问题:这套东西,你该不该用。