<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>configmap | 伪架构师</title>
    <link>/tags/configmap/</link>
      <atom:link href="/tags/configmap/index.xml" rel="self" type="application/rss+xml" />
    <description>configmap</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Thu, 16 May 2019 01:52:01 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>configmap</title>
      <link>/tags/configmap/</link>
    </image>
    
    <item>
      <title>用 Sidecar 应用 Configmap 更新</title>
      <link>/post/refresh-cm-with-signal/</link>
      <pubDate>Thu, 16 May 2019 01:52:01 +0800</pubDate>
      <guid>/post/refresh-cm-with-signal/</guid>
      <description>

&lt;p&gt;在 Kubernetes 的使用过程中，很多人会使用 Configmap 资源来进行配置文件的加载。Configmap 对象是支持热更新的，也就是说，对 Configmap 的变更，会同时反应到加载该 Configmap 的 Pod 之中。但美中不足的是，很多应用都不会检测配置文件的更新，因此就算是通过对 Configmap 的变更，完成了配置文件的修改，应用还是无法做出即时的响应的。可以在外部进行滚动更新；或者改写业务容器，监控文件变化之后重新启动业务进程。&lt;/p&gt;

&lt;p&gt;在 Kubernetes 1.10 中新增的 Pod 内共享进程命名空间的功能，给这个问题带来了一点新思路：做一个 Sidecar 用于对配置文件进行监控，发现文件变化之后，发送重新载入的信号给业务进程，要求业务进程自行刷新。这样就无需对业务容器所在镜像进行修改了。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;这种方法当然也有个局限性，需要业务进程支持这种信号。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;下面以 Apache 为例，看看这种方式的用法。&lt;/p&gt;

&lt;h2 id=&#34;创建-configmap&#34;&gt;创建 Configmap&lt;/h2&gt;

&lt;p&gt;用一个简化的 &lt;code&gt;httpd.conf&lt;/code&gt; 文件，生成 Configmap，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;kubectl create cm apache --from-file httpd.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如此就生成了一个名为 apache 的 Configmap。&lt;/p&gt;

&lt;h2 id=&#34;创建-sidecar-容器镜像&#34;&gt;创建 Sidecar 容器镜像&lt;/h2&gt;

&lt;p&gt;这个镜像要完成的任务有两个：监控文件变化，如果内容变化，则发送信号给业务进程。文件内容变化的监控，可以用哈希码或者 inotify 调用来完成，这里使用 &lt;code&gt;inotifywait&lt;/code&gt; 命令做一个死循环，发现特定事件后，则发出信号：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/sh
while :
do
  # 获取文件名称
  REAL=`readlink -f ${FILE}`
  # 监控指定事件
  inotifywait -e delete_self &amp;quot;${REAL}&amp;quot;
  # 获取特定进程名称的 PID
  PID=`pgrep ${PROCESS} | head -1`
  # 发送信号
  kill &amp;quot;-${SIGNAL}&amp;quot; &amp;quot;${PID}&amp;quot;
done
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
&lt;p&gt;这里没有用监控本地文件的 &lt;code&gt;-m&lt;/code&gt; 或者 &lt;code&gt;-e modify&lt;/code&gt; 事件，而是用了 &lt;code&gt;delete_self&lt;/code&gt;，这是 Configmap 加载生成文件的差异，也可以考虑用环境变量来替换这一事件。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;然后构建镜像：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-Dockerfile&#34;&gt;FROM alpine
RUN apk add --update inotify-tools
ENV FILE=&amp;quot;/tmp&amp;quot; PROCESS=&amp;quot;httpd&amp;quot; SIGNAL=&amp;quot;USR1&amp;quot;
COPY entry.sh /usr/local/bin
CMD [&amp;quot;/usr/local/bin/entry.sh&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里假设镜像名称为 &lt;code&gt;dustise/inotify:latest&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&#34;创建实验负载&#34;&gt;创建实验负载&lt;/h2&gt;

&lt;p&gt;我们选择 Apache 作为业务应用的示范，它能够接受 &lt;code&gt;USR1&lt;/code&gt; 信号进行重新载入。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: apache
spec:
  selector:
    matchLabels:
      app: apache
  template:
    metadata:
      labels:
        app: apache
    spec:
      shareProcessNamespace: true
      containers:
      - name: apache
        image: httpd:alpine
        ports:
        - containerPort: 80
        volumeMounts:
          - name: apache
            mountPath: /usr/local/apache2/conf/
      - name: refresh
        image: dustise/inotify
        securityContext:
          capabilities:
            add:
            - SYS_PTRACE
        volumeMounts:
          - name: apache
            mountPath: /etc/httpd
        env:
          - name: FILE
            value: &amp;quot;/etc/httpd/httpd.conf&amp;quot;
          - name: PROCESS
            value: &amp;quot;httpd&amp;quot;
          - name: SIGNAL
            value: &amp;quot;USR1&amp;quot;
      volumes:
        - name: apache
          configMap:
            name: apache
---
apiVersion: v1
kind: Service
...
  type: ClusterIP
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这段代码：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在 &lt;code&gt;template.spec&lt;/code&gt; 中加入了 &lt;code&gt;shareProcessNamespace: true&lt;/code&gt;，表示启用进行命名空间共享功能；&lt;/li&gt;
&lt;li&gt;新建了一个伴行的 Sidecar 容器；&lt;/li&gt;
&lt;li&gt;Apache 和 Sidecar 共享来自同一个 Configmap 的配置文件，根据加载情况为 Sidecar 定义了环境变量。&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;接下来可以使用 &lt;code&gt;kubectl logs&lt;/code&gt; 命令来监控两个容器的日志输出：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl logs -f apache-6b8b68c857-dp6xx -c refresh
Setting up watches.
Watches established.

$ kubectl logs -f apache-6b8b68c857-dp6xx -c apache
...
[Wed May 15 18:46:47.795261 2019] [mpm_event:notice] [pid 7:tid 139810635549544] AH00489: Apache/2.4.39 (Unix) configured -- resuming normal operations
[Wed May 15 18:46:47.795330 2019] [core:notice] [pid 7:tid 139810635549544] AH00094: Command line: &#39;httpd -D FOREGROUND&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后使用 &lt;code&gt;kubectl edit cm apache&lt;/code&gt;，修改配置文件（例如删除点注释）。稍候片刻，发现两个容器的输出都发生了变化：&lt;/p&gt;

&lt;h3 id=&#34;sidecar&#34;&gt;Sidecar&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;/etc/httpd/..2019_05_15_18_43_33.773288813/httpd.conf DELETE_SELF
Setting up watches.
Watches established.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;脚本检测到了配置文件发生了删除事件，发送信号，并重新启动监控。&lt;/p&gt;

&lt;h3 id=&#34;apache&#34;&gt;Apache&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;[Wed May 15 18:46:47.775392 2019] [mpm_event:notice] [pid 7:tid 139810635549544] AH00493: SIGUSR1 received.  Doing graceful restart
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Apache 收到了 USR1 信号，进行了优雅重启。&lt;/p&gt;

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

&lt;p&gt;对于支持信号控制的第软件，例如 Nginx、Gunicorn、HA-Proxy 等都可以使用这种方式来完成配置刷新工作。能够有效的避免重启或修改业务应用的老大难问题。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Kubernetes 中的几种存储</title>
      <link>/post/storage-in-kubernetes/</link>
      <pubDate>Sat, 12 Aug 2017 00:07:43 +0800</pubDate>
      <guid>/post/storage-in-kubernetes/</guid>
      <description>

&lt;blockquote&gt;
&lt;p&gt;参考：&lt;code&gt;https://kubernetes.io/docs/concepts/storage/volumes/&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;一个运行中的容器，缺省情况下，对文件系统的写入，都是发生在其分层文件系统的可写层的，一旦容器运行结束，所有写入都会被丢弃。因此需要对持久化支持。&lt;/p&gt;

&lt;p&gt;Kubernetes 中通过 Volume 的方式提供对存储的支持。下面对一些常见的存储概念进行一点简要的说明。&lt;/p&gt;

&lt;h2 id=&#34;emptydir&#34;&gt;EmptyDir&lt;/h2&gt;

&lt;p&gt;顾名思义，&lt;code&gt;EmptyDir&lt;/code&gt;是一个空目录，他的生命周期和所属的 Pod 是完全一致的，可能读者会奇怪，那还要他做什么？&lt;code&gt;EmptyDir&lt;/code&gt;的用处是，可以在同一 Pod 内的不同容器之间共享工作过程中产生的文件。&lt;/p&gt;

&lt;p&gt;缺省情况下，EmptyDir 是使用主机磁盘进行存储的，也可以设置&lt;code&gt;emptyDir.medium&lt;/code&gt; 字段的值为&lt;code&gt;Memory&lt;/code&gt;，来提高运行速度，但是这种设置，对该卷的占用会消耗容器的内存份额。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;hostpath&#34;&gt;HostPath&lt;/h2&gt;

&lt;p&gt;这种会把宿主机上的指定卷加载到容器之中，当然，如果 Pod 发生跨主机的重建，其内容就难保证了。&lt;/p&gt;

&lt;p&gt;这种卷一般和&lt;code&gt;DaemonSet&lt;/code&gt;搭配使用，用来操作主机文件，例如进行日志采集的 FLK 中的 FluentD 就采用这种方式，加载主机的容器日志目录，达到收集本主机所有日志的目的。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;nfs-glusterfs-cephfs-aws-gce-等等&#34;&gt;NFS/GlusterFS/CephFS/AWS/GCE 等等&lt;/h2&gt;

&lt;p&gt;作为一个容器集群，支持网络存储自然是重中之重了，Kubernetes 支持为数众多的云提供商和网络存储方案。&lt;/p&gt;

&lt;p&gt;各种支持的方式不尽相同，例如 GlusterFS 需要创建 Endpoint，Ceph/NFS 之流就没这么麻烦了。&lt;/p&gt;

&lt;p&gt;各种个性配置可移步参考文档。&lt;/p&gt;

&lt;h2 id=&#34;configmap-和-secret&#34;&gt;ConfigMap 和 Secret&lt;/h2&gt;

&lt;p&gt;镜像使用的过程中，经常需要利用配置文件、启动脚本等方式来影响容器的运行方式，如果仅有少量配置，我们可以使用环境变量的方式来进行配置。然而对于一些较为复杂的配置，例如 Apache 之类，就很难用这种方式进行控制了。另外一些敏感信息暴露在 YAML 中也是不合适的。&lt;/p&gt;

&lt;p&gt;ConfigMap 和 Secret 除了使用文件方式进行应用之外，还有其他的应用方式；这里仅就文件方式做一点说明。&lt;/p&gt;

&lt;p&gt;例如下面的 ConfigMap，将一个存储在 ConfigMap 中的配置目录加载到卷中。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: [ &amp;quot;/bin/sh&amp;quot;, &amp;quot;-c&amp;quot;, &amp;quot;ls /etc/config/&amp;quot; ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        # Provide the name of the ConfigMap containing the files you want
        # to add to the container
        name: special-config
  restartPolicy: Never
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意，这里的 ConfigMap 会映射为一个目录，ConfigMap 的 Key 就是文件名，每个 Value 就是文件内容，比如下面命令用一个目录创建一个 ConfigMap：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;kubectl create configmap \
game-config \
--from-file=docs/user-guide/configmap/kubectl
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;p&gt;创建一个 Secret：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;kubectl create secret generic \
db-user-pass --from-file=./username.txt \
--from-file=./password.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;使用 Volume 加载 Secret：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1
kind: Pod
metadata:
  name: mypod
  namespace: myns
spec:
  containers:
    - name: mypod
      image: redis
      volumeMounts:
        - name: foo
          mountPath: /etc/foo
          readOnly: true
  volumes:
    - name: foo
      secret:
        secretName: mysecret
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到 Secret 和 ConfigMap 的创建和使用是很相似的。在 RBAC 中，Secret 和 ConfigMap 可以进行分别赋权，以此限定操作人员的可见、可控权限。&lt;/p&gt;

&lt;h2 id=&#34;pv-pvc&#34;&gt;PV &amp;amp; PVC&lt;/h2&gt;

&lt;p&gt;PersistentVolume 和 PersistentVolumeClaim 提供了对存储支持的抽象，也提供了基础设施和应用之间的分界，管理员创建一系列的 PV 提供存储，然后为应用提供 PVC，应用程序仅需要加载一个 PVC，就可以进行访问。&lt;/p&gt;

&lt;p&gt;而 1.5 之后又提供了 PV 的动态供应。可以不经 PV 步骤直接创建 PVC。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;参考：&lt;code&gt;http://blog.fleeto.us/translation/dynamic-provisioning-and-storage-classes-kubernetes-0&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
  </channel>
</rss>
