<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>podman | 伪架构师</title>
    <link>/tags/podman/</link>
      <atom:link href="/tags/podman/index.xml" rel="self" type="application/rss+xml" />
    <description>podman</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Wed, 21 Feb 2024 23:37:11 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>podman</title>
      <link>/tags/podman/</link>
    </image>
    
    <item>
      <title>不用 API Server 也能运行 Pod？</title>
      <link>/post/kubelet-stand-alone/</link>
      <pubDate>Wed, 21 Feb 2024 23:37:11 +0800</pubDate>
      <guid>/post/kubelet-stand-alone/</guid>
      <description>

&lt;p&gt;遇到一个奇怪的需求：想复用 Pod 的 YAML，但是家境贫寒，不想搞个高可用 API Server；又惜字如金，不想上 Docker Compose。一顿 Google 猛如虎之后，得到了两个方案：静态 Pod 和 &lt;code&gt;podman play kube&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&#34;静态-pod&#34;&gt;静态 Pod&lt;/h2&gt;

&lt;p&gt;Kubernetes 有个功能，就是 &lt;code&gt;static pod&lt;/code&gt;，官网介绍大致如下：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;静态 Pod 由特定节点上的 kubelet 守护进程直接管理的，API 服务器并不关注静态 Pod。通常说来，Pod 是由 Deployments 之类的控制器管理的，而静态 Pod 则是在 Kubelet 的看护之下，并负责其重新启动的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;那么 Kubelet 是否可以脱离 API Server 直接运行呢？答案是肯定的，Kelsey Hightower 早在七年前就做了这样的尝试。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/kelseyhightower/standalone-kubelet-tutorial&#34; target=&#34;_blank&#34;&gt;https://github.com/kelseyhightower/standalone-kubelet-tutorial&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;想法很简单，单独运行一个 Kubelet，使用 Kubelet 拉起磁盘上的 Pod 文件。&lt;/p&gt;

&lt;h3 id=&#34;测试&#34;&gt;测试&lt;/h3&gt;

&lt;p&gt;以目前最新版本的 &lt;code&gt;1.29&lt;/code&gt; 为例，在 Ubuntu 中按照默认方式使用 &lt;code&gt;apt&lt;/code&gt; 部署 Containerd：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ apt install containerd cri-tools
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后按照官网文档安装 kubelet：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ 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 &#39;deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /&#39; | sudo tee /etc/apt/sources.list.d/kubernetes.list
$ sudo apt-get update
$ sudo apt-get install -y kubelet
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;编写如下 &lt;code&gt;kubelet.yaml&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;最后，我们启动 Kubelet：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubelet --config=kubelet.yaml
I0302 11:39:14.006446    9890 server.go:487] &amp;quot;Kubelet version&amp;quot; kubeletVersion=&amp;quot;v1.29.2&amp;quot;
I0302 11:39:14.006492    9890 server.go:489] &amp;quot;Golang settings&amp;quot; GOGC=&amp;quot;&amp;quot; GOMAXPROCS=&amp;quot;&amp;quot; GOTRACEBACK=&amp;quot;&amp;quot;
I0302 11:39:14.006622    9890 server.go:650] &amp;quot;Standalone mode, no API client&amp;quot;
I0302 11:39:14.010584    9890 server.go:538] &amp;quot;No api server defined - no events will be sent to API server&amp;quot;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
&lt;p&gt;注意，如果使用其它配置方法的容器运行时，可能需要指定不同的 &lt;code&gt;Endpoint&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里会看到，日志中直接就表明这是一个独立运行模式的 Kubelet。&lt;/p&gt;

&lt;p&gt;最后只要把一个 Pod 定义的文件拷贝到上文配置中的指定目录就能启动 Pod 了：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;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

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;使用 &lt;code&gt;crictl&lt;/code&gt; 查看运行中的 Pod：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$  sudo crictl ps
55a65b4642f47       50a1bd9b297f7       18 seconds ago      Running             apache              0                   c141f4e021cdf       apache-ubuntu
$ curl http://127.0.0.1:45678
&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;It works!&amp;lt;/h1&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pod 已经启动。&lt;/p&gt;

&lt;h3 id=&#34;限制&#34;&gt;限制&lt;/h3&gt;

&lt;p&gt;因为没有 API Server 的支持，所以静态 Pod 里面是无法引用 &lt;code&gt;Configmap&lt;/code&gt;、&lt;code&gt;Secret&lt;/code&gt; 之类的外部对象的。更不要提 &lt;code&gt;Deployment&lt;/code&gt; 了。&lt;/p&gt;

&lt;h2 id=&#34;podman-play-kube&#34;&gt;Podman Play Kube&lt;/h2&gt;

&lt;p&gt;和独立模式的 Kubelet 不同，&lt;code&gt;podman play kube&lt;/code&gt; 支持的 Kubernetes 对象除了 Pod 之外，还支持：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;PVC&lt;/li&gt;
&lt;li&gt;Configmap&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;启动-pod&#34;&gt;启动 Pod&lt;/h3&gt;

&lt;p&gt;Ubuntu 下可以直接使用 &lt;code&gt;apt install podman&lt;/code&gt; 安装部署。安装结束后，可以复用刚才的 &lt;code&gt;pod.yaml&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ podman play kube pod.yaml
a container exists with the same name (&amp;quot;apache&amp;quot;) 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-&amp;gt;80/tcp  99e235dfe7a3-infra
aa4a4ba1af39  docker.io/library/httpd:latest  httpd-foreground  38 seconds ago  Up 38 seconds ago  0.0.0.0:45678-&amp;gt;80/tcp  apache_pod-apache
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;看到这里有几个发现：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;用 &lt;code&gt;podman pod ls&lt;/code&gt; 和 &lt;code&gt;podman ps&lt;/code&gt; 可以查看 Pod 和容器的情况&lt;/li&gt;
&lt;li&gt;Podman 取了个巧，使用命名的方式来区分容器和 Pod&lt;/li&gt;
&lt;li&gt;Podman 启动的 Pod 用到了 Infra 容器，所以一个 Pod 里面会有两个容器。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了让后续动作顺利，可以把容器名称修改为 &lt;code&gt;httpd&lt;/code&gt;，用于消除这种隐式变更。在应用新版本 YAML 之前，需要因为发生了改名情况，所以无法使用 &lt;code&gt;podman play kube pod.yaml --down&lt;/code&gt; 的方式停止 Pod，这里用 &lt;code&gt;podman pod kill apache_pod &amp;amp;&amp;amp; podman pod rm apache_pod&lt;/code&gt; 删除 Pod，然后重新创建修改后的 Pod：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$podman play kube pod.yaml
Pod:
...
Container:
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;甚至可以启动一个 Deployment，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用 &lt;code&gt;play kube&lt;/code&gt; 运行一下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ 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
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到，这里生成了 3 个 &lt;code&gt;nginx-pod&lt;/code&gt; 为前缀的 Pod。&lt;/p&gt;

&lt;h3 id=&#34;configmap&#34;&gt;Configmap&lt;/h3&gt;

&lt;p&gt;修改一下刚才的 &lt;code&gt;pod.yaml&lt;/code&gt;，其中加入 Configmap：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里加入了一个引用 Configmap 作为环境变量的选项，使用 &lt;code&gt;--down&lt;/code&gt; 开关停止当前 Pod 并重建后（&lt;code&gt;4.x&lt;/code&gt; 版本有了 &lt;code&gt;--replace&lt;/code&gt; 开关），验证一下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ podman exec -it [your container id] env | grep key
key1=value1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到已经成功引用了 Configmap。&lt;/p&gt;

&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;

&lt;p&gt;除了简单的运行功能之外，Podman Play 还提供了网络、命名空间等功能，甚至还有现场构建的能力，比孤零零的 &lt;code&gt;kubelet&lt;/code&gt; 强大不少，但是如果 Kubelet 加入 crictl、nerdctl 之类的东西的话，勉强也算各擅胜场。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
