不用 API Server 也能运行 Pod?
遇到一个奇怪的需求:想复用 Pod 的 YAML,但是家境贫寒,不想搞个高可用 API Server;又惜字如金,不想上 Docker Compose。一顿 Google 猛如虎之后,得到了两个方案:静态 Pod 和 podman play kube
。
静态 Pod
Kubernetes 有个功能,就是 static pod
,官网介绍大致如下:
静态 Pod 由特定节点上的 kubelet 守护进程直接管理的,API 服务器并不关注静态 Pod。通常说来,Pod 是由 Deployments 之类的控制器管理的,而静态 Pod 则是在 Kubelet 的看护之下,并负责其重新启动的。
那么 Kubelet 是否可以脱离 API Server 直接运行呢?答案是肯定的,Kelsey Hightower 早在七年前就做了这样的尝试。
https://github.com/kelseyhightower/standalone-kubelet-tutorial
想法很简单,单独运行一个 Kubelet,使用 Kubelet 拉起磁盘上的 Pod 文件。
测试
以目前最新版本的 1.29
为例,在 Ubuntu 中按照默认方式使用 apt
部署 Containerd:
$ apt install containerd cri-tools
...
然后按照官网文档安装 kubelet:
$ apt-get install -y apt-transport-https ca-certificates curl gpg
$ curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
$ echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
$ sudo apt-get update
$ sudo apt-get install -y kubelet
...
编写如下 kubelet.yaml
:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
enableServer: false
staticPodPath: /home/kubelet/pods
readOnlyPort: 10250
failSwapOn: false
podCIDR: 10.241.1.0/24
authentication:
anonymous:
enabled: true
webhook:
enabled: false
authorization:
mode: AlwaysAllow
最后,我们启动 Kubelet:
$ kubelet --config=kubelet.yaml
I0302 11:39:14.006446 9890 server.go:487] "Kubelet version" kubeletVersion="v1.29.2"
I0302 11:39:14.006492 9890 server.go:489] "Golang settings" GOGC="" GOMAXPROCS="" GOTRACEBACK=""
I0302 11:39:14.006622 9890 server.go:650] "Standalone mode, no API client"
I0302 11:39:14.010584 9890 server.go:538] "No api server defined - no events will be sent to API server"
...
注意,如果使用其它配置方法的容器运行时,可能需要指定不同的
Endpoint
。
这里会看到,日志中直接就表明这是一个独立运行模式的 Kubelet。
最后只要把一个 Pod 定义的文件拷贝到上文配置中的指定目录就能启动 Pod 了:
apiVersion: v1
kind: Pod
metadata:
name: apache
spec:
containers:
- name: apache
image: httpd
ports:
- name: http
containerPort: 80
hostPort: 45678
volumeMounts:
- name: local
mountPath: /data
volumes:
- name: local
hostPath:
path: /home/volumes/data
type: Directory
使用 crictl
查看运行中的 Pod:
$ sudo crictl ps
55a65b4642f47 50a1bd9b297f7 18 seconds ago Running apache 0 c141f4e021cdf apache-ubuntu
$ curl http://127.0.0.1:45678
<html><body><h1>It works!</h1></body></html>
Pod 已经启动。
限制
因为没有 API Server 的支持,所以静态 Pod 里面是无法引用 Configmap
、Secret
之类的外部对象的。更不要提 Deployment
了。
Podman Play Kube
和独立模式的 Kubelet 不同,podman play kube
支持的 Kubernetes 对象除了 Pod 之外,还支持:
- Deployment
- PVC
- Configmap
启动 Pod
Ubuntu 下可以直接使用 apt install podman
安装部署。安装结束后,可以复用刚才的 pod.yaml
:
$ podman play kube pod.yaml
a container exists with the same name ("apache") as the pod in your YAML file; changing pod name to apache_pod
Pod:
...
Container:
...
$ podman pod ls
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
99e235dfe7a3 apache_pod Running 9 seconds ago b54991e35f58 2
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b54991e35f58 k8s.gcr.io/pause:3.5 41 seconds ago Up 38 seconds ago 0.0.0.0:45678->80/tcp 99e235dfe7a3-infra
aa4a4ba1af39 docker.io/library/httpd:latest httpd-foreground 38 seconds ago Up 38 seconds ago 0.0.0.0:45678->80/tcp apache_pod-apache
看到这里有几个发现:
- 用
podman pod ls
和podman ps
可以查看 Pod 和容器的情况 - Podman 取了个巧,使用命名的方式来区分容器和 Pod
- Podman 启动的 Pod 用到了 Infra 容器,所以一个 Pod 里面会有两个容器。
为了让后续动作顺利,可以把容器名称修改为 httpd
,用于消除这种隐式变更。在应用新版本 YAML 之前,需要因为发生了改名情况,所以无法使用 podman play kube pod.yaml --down
的方式停止 Pod,这里用 podman pod kill apache_pod && podman pod rm apache_pod
删除 Pod,然后重新创建修改后的 Pod:
$podman play kube pod.yaml
Pod:
...
Container:
...
甚至可以启动一个 Deployment,例如:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
用 play kube
运行一下:
$ podman play kube deploy.yaml
...
podman pod ls
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
0a6e4dcda93c nginx-pod-2 Running 15 seconds ago 319f12f3b6f2 2
266df25c4df1 nginx-pod-1 Running 19 seconds ago a65f6b601160 2
e6966f42c5fd nginx-pod-0 Running 22 seconds ago 953e3e830528 2
573597e627ec apache Running 9 minutes ago 3b4ff4625b46 2
可以看到,这里生成了 3 个 nginx-pod
为前缀的 Pod。
Configmap
修改一下刚才的 pod.yaml
,其中加入 Configmap:
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-sample
data:
key1: value1
---
apiVersion: v1
kind: Pod
metadata:
name: apache
spec:
containers:
- name: httpd
image: httpd
ports:
- name: http
containerPort: 80
hostPort: 45678
envFrom:
- configMapRef:
name: cm-sample
optional: false
这里加入了一个引用 Configmap 作为环境变量的选项,使用 --down
开关停止当前 Pod 并重建后(4.x
版本有了 --replace
开关),验证一下:
$ podman exec -it [your container id] env | grep key
key1=value1
可以看到已经成功引用了 Configmap。
结论
除了简单的运行功能之外,Podman Play 还提供了网络、命名空间等功能,甚至还有现场构建的能力,比孤零零的 kubelet
强大不少,但是如果 Kubelet 加入 crictl、nerdctl 之类的东西的话,勉强也算各擅胜场。