<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Shell Operator | 伪架构师</title>
    <link>/tags/shell-operator/</link>
      <atom:link href="/tags/shell-operator/index.xml" rel="self" type="application/rss+xml" />
    <description>Shell Operator</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Mon, 02 Sep 2024 22:16:09 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>Shell Operator</title>
      <link>/tags/shell-operator/</link>
    </image>
    
    <item>
      <title>无需重启，使用 Shell Operator 对 Pod 进行垂直扩缩容</title>
      <link>/post/update-pod-resource-without-restart/</link>
      <pubDate>Mon, 02 Sep 2024 22:16:09 +0800</pubDate>
      <guid>/post/update-pod-resource-without-restart/</guid>
      <description>

&lt;p&gt;通常情况下，要修改 Pod 的资源定义，是需要重启 Pod 的。在 Kubernetes 1.27 中，有一个 Alpha 状态的 &lt;code&gt;InPlacePodVerticalScaling&lt;/code&gt; 开关，开启这一特性，就能在不重启 Pod 的情况下，修改 Pod 的资源定义。&lt;/p&gt;

&lt;p&gt;要使用这个功能，需要在 &lt;code&gt;kube-apiserver&lt;/code&gt; 的 &lt;code&gt;featureGates&lt;/code&gt; 中显式地设置启用，启用这一特性之后，就可以进行测试了。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;例如 Kind 集群，需要在配置中加入：&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;featureGates:
  &amp;quot;InPlacePodVerticalScaling&amp;quot;: true
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;测试一下&#34;&gt;测试一下&lt;/h2&gt;

&lt;p&gt;假设下面的 Pod 定义：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1
kind: Pod
metadata:
  name: stress
spec:
  containers:
  - name: stress
    image: colinianking/stress-ng:latest
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: RestartContainer    
    command: [&amp;quot;sleep&amp;quot;, &amp;quot;3600&amp;quot;]
    resources:
      limits:
        cpu: 200m
        memory: 200M
      requests:
        cpu: 200m
        memory: 200M
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到，spec 中加入了 resizePolicy 字段，用来指定对 CPU 和内存的扩缩容策略。内容很直白：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU 的扩缩容策略是 &lt;code&gt;NotRequired&lt;/code&gt;，即不重启 Pod；&lt;/li&gt;
&lt;li&gt;内存的扩缩容策略是 &lt;code&gt;RestartContainer&lt;/code&gt;，即重启 Pod。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;将上述内容提交到 Kubernetes 中运行。启动之后，如果运行 &lt;code&gt;kubectl get po stress -o yaml&lt;/code&gt;，会发现状态字段中加入了如下内容：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- allocatedResources:
    cpu: 200m
    memory: 200Mi
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;说明此时分配给容器的资源。如果这时候对 CPU 进行修改，例如修改为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;resources:
  limits:
    cpu: 800m
    memory: 200Mi
  requests:
    cpu: 100m
    memory: 100Mi
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;修改后查看 Pod 列表，会发现 Pod 没有重启：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
stress   1/1     Running   0          4m14s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;重新获取 YAML，会看到状态字段的一些变化：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;resize: InProgress&lt;/code&gt;：表示正在扩缩容；&lt;/li&gt;

&lt;li&gt;&lt;p&gt;当前分配的资源也发生了变化：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- allocatedResources:
  cpu: 100m
  memory: 100Mi
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;自动纵向扩缩容&#34;&gt;自动纵向扩缩容&lt;/h2&gt;

&lt;p&gt;到目前为止，VPA 还没有支持这一特性。我们可以简地使用 Prometheus 对 Pod 资源压力进行监控，然后使用 Shell Operator 来实现自动扩缩容。总体思路就是，定期读取 Prometheus，获取指定 Pod 的 CPU 和使用情况，如果 CPU 使用率超过 80%，则将其 CPU 上限扩容一倍。&lt;/p&gt;

&lt;h3 id=&#34;prometheus-监控指标&#34;&gt;Prometheus 监控指标&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://samber.github.io/awesome-prometheus-alerts/rules#kubernetes&#34; target=&#34;_blank&#34;&gt;Awesome Prometheus alerts&lt;/a&gt; 提供了如下的告警定义，用于表达 CPU 用量和其 Limit 的关系：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  - alert: ContainerHighCpuUtilization
    expr: (sum(rate(container_cpu_usage_seconds_total{container!=&amp;quot;&amp;quot;}[5m])) by (pod, container) / sum(container_spec_cpu_quota{container!=&amp;quot;&amp;quot;}/container_spec_cpu_period{container!=&amp;quot;&amp;quot;}) by (pod, container) * 100) &amp;gt; 80
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: Container High CPU utilization (instance {{ $labels.instance }})
      description: &amp;quot;Container CPU utilization is above 80%\n  VALUE = {{ $value }}\n  LABELS = {{ $labels }}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们把它写入 Python 代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;CPU_USAGE_QUERY = &#39;&#39;&#39;(sum(rate(container_cpu_usage_seconds_total{{namespace=&amp;quot;{0}&amp;quot;, pod=&amp;quot;{1}&amp;quot;, container=&amp;quot;{2}&amp;quot;}}[5m])) by (pod, container) 
/ sum(container_spec_cpu_quota{{namespace=&amp;quot;{0}&amp;quot;, pod=&amp;quot;{1}&amp;quot;,container=&amp;quot;{2}&amp;quot;}}
/container_spec_cpu_period{{namespace=&amp;quot;{0}&amp;quot;, pod=&amp;quot;{1}&amp;quot;, container=&amp;quot;{2}&amp;quot;}}) by (pod, container) * 100)&#39;&#39;&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;定期运行&#34;&gt;定期运行&lt;/h3&gt;

&lt;p&gt;要设置 Shell Operator 的定期运行，需要使用 Schedule 类型的配置，下面的 Configmap 设置每两分钟运行一次：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1
data:
  config.yaml: |+
    configVersion: v1
    schedule:
    - crontab: &amp;quot;*/2 * * * *&amp;quot;
      allowFailure: true
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: so-config
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们会将这个 Configmap 加载到 Pod 定义中，&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;...
volumeMounts:
- mountPath: /conf/
  name: operator-config
...
volumes:
- configMap:
  name: so-config
name: operator-config
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在 Hook 代码执行参数中带有 &lt;code&gt;--config&lt;/code&gt; 参数时，读取该配置进行返回：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;if len(sys.argv) &amp;gt; 1 and sys.argv[1] == &amp;quot;--config&amp;quot;:
    with open(&amp;quot;/conf/config.yaml&amp;quot;, &amp;quot;r&amp;quot;) as f:
        print(f.read())
    exit(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;rbac&#34;&gt;RBAC&lt;/h3&gt;

&lt;p&gt;Shell Operator 需要对 Pod 资源进行扩容，所以需要如下授权：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- apiGroups: [&amp;quot;&amp;quot;]
  resources: [&amp;quot;pods&amp;quot;]
  verbs: [&amp;quot;get&amp;quot;, &amp;quot;list&amp;quot;, &amp;quot;watch&amp;quot;, &amp;quot;patch&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;构建-docker-镜像&#34;&gt;构建 Docker 镜像&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM flant/shell-operator
RUN apk update &amp;amp;&amp;amp; \
    apk add --no-cache py3-requests
COPY main.py /hooks
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;Workload 中，我们设置的资源是 100M 内存+100m CPU 的配置，使用 &lt;code&gt;kubectl exec -it stress -- sh&lt;/code&gt; 进入 Pod 之后，执行 &lt;code&gt;stress-ng --cpu 1 --fork 2&lt;/code&gt; 制造一点压力，触发 Shell Operator 中的脚本对 Pod 进行纵向扩容，在 Prometheus 会看到如下曲线：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/prom.png&#34; alt=&#34;images/prom.png&#34; /&gt;&lt;/p&gt;

&lt;p&gt;随着每次运行和扩容，CPU 水位不断下降，直到稳定。打开 Pod 定义，会看到扩容的痕迹：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  containerStatuses:
  - allocatedResources:
      cpu: 6400m
      memory: 100M
    containerID: containerd://10c55739a6a63f3464184f5384a2f2b091a235b7b6689bcdb58526e3eb8bdb19
    image: docker.io/colinianking/stress-ng:latest
    imageID: docker.io/colinianking/stress-ng@sha256:1b10c09968ea3460196596398f7811c7a604489a8311b3dbf477f552ac5ea972
    lastState:
      terminated:
        containerID: containerd://e3f1fe628086e291830b47247c88403d3ce0f4fd5db38b18afcca444659011d3
        exitCode: 0
        finishedAt: &amp;quot;2024-09-08T08:07:49Z&amp;quot;
        reason: Completed
        startedAt: &amp;quot;2024-09-08T07:07:49Z&amp;quot;
    name: stress
    ready: true
    resources:
      limits:
        cpu: 6400m
        memory: 100M
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;全部代码被上传到了 &lt;code&gt;https://github.com/fleeto/vscale-by-shelloperator&lt;/code&gt;。内容当然还是非常简陋，例如缺乏缩容手段、没有对上限进行限制，防抖动措施也是缺乏的。另外该特性还处在 Alpha 阶段，因此不推荐在生产环境中使用。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
