KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
02 · 第一个应用 — keel 龙骨
上一章说"你写的是期望状态"。这一章把它写出来、交上去,然后看那句"它还没跑起来"是怎么发生的。
上一章说"你写的是期望状态"。这一章把它写出来、交上去,然后看那句"它还没跑起来"是怎么发生的。
一、写一份"想要什么"的文件
新建一个文件,叫 web.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27-alpine
yaml 只有一条规则:缩进就是层级——同一层的左边对齐,属于上一层的往右缩两格。
只能用空格,不能用 Tab。
这份文件从头到尾在说一件事,逐段读:
| 片段 | 它在说什么 |
|---|---|
apiVersion / kind / metadata.name: web |
交的是哪一种东西(这里叫 Deployment)以及它叫什么——这名字后面一直用来指代它 |
replicas: 2 |
我要 2 个副本 |
selector / template 里的 labels |
这两处必须写成一样,不一样连交都交不上去(原文见下) |
containers 里的 image |
每个副本里跑哪个镜像。就是你上一门课学会的那个 |
Deployment 本课不展开它和别的对象怎么分工,读成**"份数 + 用什么镜像"的载体**就行。
二、交上去
kubectl apply -f web.yaml
apply 读作"把我这份期望状态交上去"。你会看到:
deployment.apps/web created
created 是"收下了"。紧接着敲这一行——这是本章最重要的一步:
kubectl get pods
你会看到(注意 STATUS 那一列):
NAME READY STATUS RESTARTS AGE
web-565bc6668-t8bp4 0/1 ContainerCreating 0 1s
web-565bc6668-wkm7t 0/1 ContainerCreating 0 1s
两行都在,但没有一个在跑:0/1 是"需要的 1 个里起来了 0 个",ContainerCreating 是"正在把它造出来"。
apply 返回 created 的那一刻和这一屏,是同一秒。 这不是失败,就是它的工作方式:
你说完"我要什么",然后由它自己去达成;达成需要时间。
名字里的
web-565bc6668-是它自动拼的:前面是你的名字,中间那截是这份配置算出来的编号,
后面那截每次都是新的。第 03 章会用到这一点。
三、等到它真的在跑
再敲一次同一条命令(或者隔十几秒再看):
kubectl get pods
NAME READY STATUS RESTARTS AGE
web-565bc6668-t8bp4 1/1 Running 0 11s
web-565bc6668-wkm7t 1/1 Running 0 11s
1/1 + Running,这时候才是真的在跑。另外两条命令值得一起看:
kubectl get deploy web # 我要的那份期望状态,它认到了多少
kubectl get pods -o wide # 多两列:Pod 在哪台机器上、地址是多少
NAME READY UP-TO-DATE AVAILABLE AGE
web 2/2 2 2 55s
NAME READY STATUS RESTARTS AGE IP NODE
web-565bc6668-t8bp4 1/1 Running 0 36s 10.244.0.6 keel-lab-control-plane
web-565bc6668-wkm7t 1/1 Running 0 57s 10.244.0.7 keel-lab-control-plane
-o wide 加出来的两列后面两章都会用到:NODE 是它落在哪台机器上,IP 是它的地址。
四、看它说了什么
容器里的程序打印的东西,可以用这条命令捞出来:
kubectl logs web-565bc6668-t8bp4
(名字换成你自己 get pods 里的一个)
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
...
这就是 nginx 的启动日志。和上一门课的 docker logs 是同一份东西——
只是这个容器现在不归 Docker 直接管了。
提交失败的几句话说清楚
上面那份 YAML 少一个空格、错一个字母都交不上去,而且都会非零退出。新手最常撞上的
几句原文一次给全——它们都不是环境坏了,是文件写错了:
| 报错原文(结尾部分) | 它在说什么 |
|---|---|
error: the path "web.yaml" does not exist |
路径不对。先确认 cd 到了放文件的目录 |
error: must specify one of -f and -k |
忘了写 -f web.yaml |
error: error converting YAML to JSON: yaml: line 7: mapping values are not allowed in this context |
第 7 行缩进错了(或者混进了 Tab) |
no matches for kind "Deploymnet" in version "apps/v1" |
kind 拼错了 |
The Deployment "x" is invalid: … selector does not match template labels |
selector 那两处没写成一样 |
五、它不是什么
- 不是你启动了它。 你没有敲"把 nginx 进程跑起来"这种命令——只交了一份"我要 2 个副本、
用这个镜像"。是集群自己把进程造出来的。 第 03 章一删就露出来了。 - 不是你交完之后就完成了。
created/configured只说明文件被收下了;它跑没跑起来,
要另外敲kubectl get去看。这两件事永远是分开的。 - 不是
docker run的另一种写法。 一次只能跑一个 vs 交一次是 2 个,而且你下次还会再交
同一份文件(第 03 章会说为什么)。
读完你能做什么
- 写一份最简的 YAML 清单交上去,并等到它真的在跑;
- 看懂
get pods里0/1+ContainerCreating和1/1+Running的区别; - 说出"交上去了"和"跑起来了"为什么是两件事。
下一步:下一章去把它弄坏——你会看到一件用 Docker 时遇不到的事。