k8s (六) ConfigMap 和 Secret:配置应用程序

一、向容器传递命令行参数

1.1. 在 Docker 中定义命令与参数

容器中运行的完整指令由两部分组成:命令与参数。

ENTRYPOINT 与 CMD:

  • ENTRYPOINT:定义容器启动时被调用的可执行程序。
  • CMD:指定传递给 ENTRYPOINT 的参数。

尽管可以直接使用 CMD 指令指定镜像运行时想要执行的命令,正确的做法依旧是借助 ENTRYPOINT 指令,仅仅用 CMD 指定所需的默认参数。这样,镜像可以直接运行,无须添加任何参数:

docker run <image>

或者是添加一些参数,覆盖 Dockerfile 中任何由 CMD 指定的默认参数值:

docker run <image> <arguments>

shell 与 exec 形式的区别:

  • shell 形式:如 ENTRYPOINT node app.js
  • exec 形式:如 ENTRYPOINT [“node”, “app.js”]

两者的区别在于指定的命令是否是在 shell 中被调用。exec 形式的 ENTRYPOINT 指令是直接运行 node 进程;shell 形式的指令主进程(PID 1)是 shell 进程而非 node 进程,node 进程于 shell 中启动。shell 进程往往是多余的,因此通常可以直接采用 exec 形式的 ENTRYPOINT 指令。

实例:

luksa/fortune:args 镜像中的运行脚本:

#!/bin/bash
trap "exit" SIGINT

INTERVAL=$1
echo Configured to generate new fortune every $INTERVAL seconds

mkdir -p /var/htdocs

while :
do
  echo $(date) Writing fortune to /var/htdocs/index.html
  /usr/games/fortune > /var/htdocs/index.html
  sleep $INTERVAL
done

luksa/fortune:args 的 Dockerfile 内容:

FROM ubuntu:latest

RUN apt-get update ; apt-get -y install fortune
ADD fortuneloop.sh /bin/fortuneloop.sh

ENTRYPOINT ["/bin/fortuneloop.sh"]
CMD ["10"]

测试:

docker run -it luksa/fortune:args # 默认的 10 秒钟
docker run -it luksa/fortune:args 15 # 传参修改为 15 秒钟

1.2. 在 Kubernetes 中覆盖命令和参数

在 Kubernetes 中定义容器时,镜像的 ENTRYPOINT 和 CMD 均可以被覆盖,仅需要在容器定义中设置 command 和 args 的值:

kind: Pod
spec:
  containers:
  - image: some/image
    command: ["/bin/command"] # 对应 ENTRYPOINT
    args: ["arg1", "arg2", "arg3"] # 对应 CMD,多参数值可以使用 "-" 数组的形式(字符串值无须用引号标记,数值需要)

注意:command 和 args 字段在 pod 创建后无法被修改。绝大多数情况下,只需要设置自定义参数。命令一般很少被覆盖,除非针对一些未定义 ENTRYPOINT 的通用镜像。

二、为容器设置环境变量

在容器定义中指定环境变量:

apiVersion: v1
kind: Pod
metadata:
  name: fortune-env
spec:
  containers:
  - image: luksa/fortune:env
    env:
    - name: INTERVAL
      value: "30"

在环境变量值中引用其他环境变量:

    env:
    - name: FIRST_VAR
      value: "foo"
    - name: SECOND_VAR
      value: "$(FIRST_VAR)bar"

三、利用 ConfigMap 解耦配置

Kubernetes 允许将配置选项分离到单独的资源对象 ConfigMap 中,本质上就是一个键/值对映射,值可以是短字面量,也可以是完整的配置文件。应用无须直接读取 ConfigMap,甚至根本不需要知道其是否存在。映射的内容通过环境变量或者卷文件的形式传递给容器,而并非直接传递给容器。

3.1. 创建 ConfigMap

3.1.1. 使用指令创建 ConfigMap

kubectl create configmap fortune-config --from-literal=sleep-interval=25

创建了一个叫 fortune-config 的 ConfigMap,只包含一个条目。也可以添加多个条目:

kubectl create configmap myconfigmap --from-literal=foo=bar --from-literal=bar=baz --from-literal=one=two

注意:ConfigMap 中的键名必须是一个合法的 DNS 子域,仅包含数字字母、破折号、下划线以及圆点。

查看创建的 ConfigMap 的 YAML 格式的定义描述:

kubectl get configmap fortune-config -o yaml

3.1.2. 从文件内容创建 ConfigMap 目录

kubectl create configmap my-config --from-file=config-file.conf

默认使用文件名作为键名,也可以手动指定键名:

kubectl create configmap my-config --from-file=customkey=config file.conf

多次使用 —from-file 参数可增加多个文件条目。

3.1.3. 从文件夹创建 ConfigMap

kubectl create configmap my -config --from-file=/path/to/dir

为文件夹中的每个文件单独创建条目,仅限于那些文件名可作为合法 ConfigMap 键名的文件

3.1.4. 合并不同选项

kubectl create configmap my -config
  --from-file=foo.json # 单独的文件
  --from-file=bar=foobar.conf # 自定义键名条目下的文件
  --from-file=config-opts/ # 完整的文件夹
  --from-file=some=thing # 字面量

3.2. 给容器传递 ConfigMap 条目作为环境变量

apiVersion: v1
kind: Pod
metadata:
  name: fortune-env-from-configmap
spec:
  containers:
  - image: luksa/fortune:env
    env:
    - name: INTERVAL # 环境变量名为 INTERVAL
      valueFrom: 
        configMapKeyRef: # ConfigMap
          name: fortune-config # ConfigMap 名称
          key: sleep-interval # ConfigMap 对应键的值
... ...

在 pod 中引用不存在的 ConfigMap,如果没有设置 configMapKeyRef.optional: true 则容器会启动失败,如果之后创建了这个缺失的 ConfigMap,失败容器会自动启动,无须重新创建 pod

3.3. 一次性传递 ConfigMap 的所有条目作为环境变量

spec:
  containers:
  - image: some-image
    envFrom:
    - prefix: CONFIG_ # 所有环境变量的前缀,可以不设置
      configMapRef: 
        name: my-config-map
... ...

3.4. 传递 ConfigMap 条目作为命令行参数

apiVersion: v1
kind: Pod
metadata:
  name: fortune-args-from-configmap
spec:
  containers:
  - image: luksa/fortune:args
    env:
    - name: INTERVAL
      valueFrom: 
        configMapKeyRef:
          name: fortune-config
          key: sleep-interval
    args: ["$(INTERVAL)"] # 在参数设置中引用环境变量
... ...

3.5. 使用 ConfigMap 卷将条目暴露为文件

3.5.1. 创建 ConfigMap

新建文件夹 configmap-files 并将 my-nginx-config.conf 和 sleep-interval 放置在文件夹下:

# my-nginx-config.conf

server {
    listen              80;
    server_name         www.kubia-example.com;

    gzip on;
    gzip_types text/plain application/xml;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}
# sleep-interval

25

先删除之前创建的 ConfigMap:

kubectl delete configmap fortune-config

新建 ConfigMap:

kubectl create configmap fortune-config --from-file=configmap-files

查看:

kubectl get configmap fortune-config -o yaml

3.5.2. 在卷内使用 ConfigMap 的条目

# fortune-pod-configmap-volume.yaml
# 详见:https://github.com/luksa/kubernetes-in-action/blob/master/Chapter07/fortune-pod-configmap-volume.yaml

apiVersion: v1
kind: Pod
metadata:
  name: fortune-configmap-volume
spec:
  containers:
  ... ...
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    ... ...
    - name: config
      mountPath: /etc/nginx/conf.d # 挂载 configMap 卷至这个位置
      readOnly: true
    ... ...
  volumes:
  ... ...
  - name: config
    configMap:
      name: fortune-config # 卷引用 fortune-config

创建:

kubectl create -f fortune-pod-configmap-volume.yaml

验证:

kubectl port-forward fortune-configmap-volume 8080:80 &

curl -H "Accept-Encoding: gzip" -I localhost:8080

输出:

... ...
Content-Encoding: gzip

注意:挂载某一文件夹会隐藏该文件夹中已存在的文件

3.5.3. 卷内暴露指定的 ConfigMap 条目

  volumes:
  - name: config
    configMap:
      name: fortune-config
      items: # 选择包含在卷中的条目
      - key: my-nginx-config.conf # 该键对应的条目被包含
        path: gzip.conf # 条目的值被存储在该文件中
`

3.5.4. ConfigMap 独立条目作为文件被挂载且不隐藏文件夹中的其他文件

spec:
  containers:
  - image: some/image
    volumeMounts:
    - name: myvolume
      mountPath: /etc/someconfig.conf # 挂载至某一文件而不是文件夹
      subPath: myconfig.conf #仅挂载指定的条目 myconfig.conf 并非完整的卷

3.5.5. 为 configMap 卷中的文件设置权限

configMap 卷中所有文件的权限默认被设置为644 (-rw-r-r—)。可以通过卷规格定义中的 defaultMode 属性改变默认权限:

  volumes:
  - name: html
    emptyDir: {}
  - name: config
    configMap:
      name: fortune-config
      defaultMode: 0660 # 设置所有文件的权限为 -rw-rw------

3.6. 更新应用配置且不重启应用程序

使用环境变量或者命令行参数作为配置源的弊端在于无法在进程运行时更新配置。将 ConfigMap 暴露为卷可以达到配置热更新的效果,无须重新创建 pod 或者重启容器。ConfigMap 被更新之后,卷中引用它的所有文件也会相应更新,进程发现文件被改变之后进行重载。Kubernetes 同样支待文件更新之后手动通知容器。

修改 ConfigMap:

kubectl edit configmap fortune-config

将 gzip on 改为 gzip off,ConfigMap 被更新不久之后会自动更新卷中的对应文件,查看:

kubectl exec fortune-configmap-volume -c web-server -- cat /etc/nginx/conf.d/my-nginx-config.conf

通知 Nginx 重载配置:

kubectl exec fortune-configmap-volume -c web-server -- nginx -s reload

四、使用 Secret 给容器传递敏感数据

Secret 的使用方法也与ConfigMap 相同,可以:

  • 将 Secret 条目作为环境变量传递给容器
  • 将 Secret 条目暴露为卷中的文件

Secret 只会存储在节点的内存中,永不写入物理存储,这样从节点上删除 Secret 时就不需要擦除磁盘

创建 Secret:

kubectl create secret generic fortune-https --from-file=https.key --from-file=https.cert --from-file=foo

https.key, https.cert, foo 文件内容见 链接

查看:

kubectl get secret fortune-https -o yaml

Secret 条目的内容会被以 Base64 格式编码,这种区别导致在处理 YAML 和 JSON 格式的 Secret 时会稍许有些麻烦,可以通过 stringData 字段设置条目的纯文本值。

通过 secret 卷将 Secret 暴露给容器之后,Secret 条目的值会被解码并以真实形式(纯文本或二进制)写入对应的文件。通过环境变量暴露 Secret 条目亦是如此。在这两种情况下,应用程序均无须主动解码,可直接读取文件内容或者查找环境变量。

修改 fortune-config ConfigMap 以开启 HTTPS:

kubectl edit configmap fortune-config

... ...

server {
    listen              80;
    listen              443 ssl;
    server_name         www.kubia-example.com;
    ssl_certificate     certs/https.cert; # /etc/nginx 的相对位置
    ssl_certificate_key certs/https.key; # /etc/nginx 的相对位置
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;    
... ...

挂载 fortune-secret 至 pod:

# fortune-pod-https.yaml

apiVersion: v1
kind: Pod
metadata:
  name: fortune-https
spec:
  containers:
  - image: luksa/fortune:env
    name: html-generator
    env:
    - name: INTERVAL
      valueFrom: 
        configMapKeyRef:
          name: fortune-config
          key: sleep-interval
    volumeMounts:
    - name: html
      mountPath: /var/htdocs
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    - name: config
      mountPath: /etc/nginx/conf.d
      readOnly: true
    - name: certs
      mountPath: /etc/nginx/certs/ # 配置 Nginx 从 /etc/nginx/certs 中读取证书和密钥文件
      readOnly: true
    ports:
    - containerPort: 80
    - containerPort: 443
  volumes:
  - name: html
    emptyDir: {}
  - name: config
    configMap:
      name: fortune-config
      items:
      - key: my-nginx-config.conf
        path: https.conf
  - name: certs
    secret:
      secretName: fortune-https # secret 卷

kubectl create -f fortune-pod-https.yaml

测试:

kubectl port-forward fortune-https 8443:443 &

curl https://localhost:8443 -k

posted @ 2021-01-25 11:17  VictorBu  阅读(446)  评论(0编辑  收藏  举报