跳到主要内容

【6】使用Namespace、ConfigMap和Secret

参考 k8s-tutorial-cn

使用Namespace

Namespace(命名空间)用来隔离集群内不同环境下的资源。仅同一namespace下的资源命名需要唯一,它的作用域仅针对带有名字空间的对象,例如 Deployment、Service 等。如果未指定则默认使用的namespace是default。

创建多个namespace:

#  namespace-test.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev

---

apiVersion: v1
kind: Namespace
metadata:
name: test
> kubectl apply -f namespace-test.yaml 
namespace/dev created
namespace/test created

# 查看所有Namespace
kubectl get ns
# 获取指定namespace下的资源
kubectl get pods -n dev
# 删除namespace
kubectl delete ns dev
危险

需要注意的是,删除namespace时,会默认删除该空间下的所有资源,需要谨慎操作。

使用ConfigMap和Secret

ConfigMap 和 Secret 都是用来保存配置数据的,在模板定义和使用上没有大太差别。唯一的区别就是Secret是用来保存敏感型的配置数据,比如证书密钥、token之类的。

ConfigMap

ConfigMap用来将配置数据和应用程序代码分开,将非机密性的数据保存到键值对中。在ConfigMap中保存的数据不可超过 1MiB。如果需要保存超出此尺寸限制的数据,可考虑挂载存储卷。

ConfigMap 可以用四种方式进行使用:

  • 在容器命令和参数内
  • 容器的环境变量(常见)
  • 在只读卷里面添加一个文件,让应用来读取(常见)
  • 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap(不常见)

测试使用ConfigMap来保存apphellok8s的配置信息

# configmap-test.yaml
# configmap设置
apiVersion: v1
kind: ConfigMap
metadata:
name: hellok8s-configmap
data: # 用来保存UTF8字符串
DB_URL: "http://mydb.example123.com"
binaryData: # 用来保存二进制数据作为 base64 编码的字串。
app-config.json: eyJkYl91cmwiOiJteXNxbC5leGFtcGxlLmNvbSJ9Cg== # echo '{"db_url":"mysql.example.com"}' |base64

# 对于一个大量使用 configmap 的集群,禁用 configmap 修改会带来以下好处
# 1. 保护应用,使之免受意外(不想要的)更新所带来的负面影响。
# 2. 通过大幅降低对 kube-apiserver 的压力提升集群性能, 这是因为系统会关闭对已标记为不可变更的 configmap 的监视操作。
# 一旦标记为不可更改,这个操作就不可逆,再想要修改就只能删除并重建 configmap
immutable: true

---

# 此模板演示了两种使用configmap的方式
# - 1. 环境变量方式
# - 2. 挂载volume方式
apiVersion: apps/v1
kind: Deployment
metadata:
name: hellok8s-go-http
spec:
replicas: 1 # 当使用 hostPort 时,每个节点只能运行一个 pod
strategy:
type: Recreate # 因为下面使用hostPort进行测试,所以更新时只能先销毁再创建
selector:
matchLabels:
app: hellok8s
template:
metadata:
labels:
app: hellok8s
spec:
containers:
- image: leigg/hellok8s:v4_configmap
name: hellok8s
ports:
- containerPort: 3000
hostPort: 3000
env: # 以环境变量的方式读取data
- name: DB_URL
valueFrom:
configMapKeyRef:
name: hellok8s-configmap
key: DB_URL
volumeMounts: # 以挂载卷的方式读取二进制数据
- name: configmap-volume
mountPath: "/etc/configmap_vol"
volumes:
- name: configmap-volume
configMap:
name: hellok8s-configmap
// hellok8s:v4_configmap
package main

import (
"fmt"
"io"
"net/http"
"os"
)

func main() {
readFile := func(f string) string {
nbytes, err := os.ReadFile("/etc/configmap_vol/" + f)
if err != nil {
panic(err)
}
return string(nbytes)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
host, _ := os.Hostname()
dbURL := os.Getenv("DB_URL")
io.WriteString(w, fmt.Sprintf("[v4] Hello, Kubernetes! From host: %s\n"+
"Get Database Connect URL: %s\n"+
"app-config.json:%s", host, dbURL, readFile("app-config.json")))
})
http.ListenAndServe(":3000", nil)
}
kubectl apply -f configmap-test.yaml 
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
traefik-546fddfdb8-79h6w 1/1 Running 0 42h 10.42.0.33 k8s-master <none> <none>
hellok8s-go-http-bc9f68f99-fq7zs 1/1 Running 0 62s 10.42.1.67 k8s-node1 <none> <none>

# 访问接口
> curl http://10.42.1.67:3000
[v4] Hello, Kubernetes! From host: hellok8s-go-http-bc9f68f99-fq7zs, Get Database Connect URL: http://mydb.example123.com
app-config.json:{"db_url":"mysql.example.com"}

如果需要更新配置则直接更改configmap的yaml文件然后应用,然后重启业务pod即可(kubectl rollout restart deployment <deployment-name>),但如果configmap配置了immutable: true,则无法再进行修改。
在上面提到的四种使用configmap的方式中,只有挂载volume和调用k8s API的方式会接收到configmap的更新(具有一定延迟),其余两种则需要重启Pod才能看到更新。

Secret

Secret用于存储敏感信息,例如密码、Token、(证书)密钥等,在使用上与ConfigMap不会有太大差别,但需要注意下面两点。

  • data的value部分必须是base64编码后的字符串(创建时会执行base64解码检查),但Pod中获取到的仍然是明文;
  • 模板语法上稍有不同:
    • Secret 支持的是stringData而不是binaryData,它的value可以是任何UTF8字符
    • 额外支持type字段,用来在创建时检查资源合法性

Secret的类型

创建 Secret 时,还可以使用 Secret 资源的 type 字段(可选),它用来告诉k8s我要创建何种类型的secret,并根据类型对其进行基础的合法性检查。 官方文档:Secret的类型

下面是一个tls证书例子

# secret-hellok8s-cert.yaml
apiVersion: v1
kind: Secret
metadata:
name: hellok8s-tls
namespace: default
type: kubernetes.io/tls
# data 只接收base64编码
data:
# 这里的示例数据是随意填写的,base64编码后的证书数据
tls.crt: <Base64字符串>
tls.key: <Base64字符串>
# 也可以换成 stringData 存明文证书,在kubectl get secret hellok8s-tls -o json 时看到的都一样
#stringData:
# tls.crt: |-
# -----BEGIN CERTIFICATE-----
# ...
# qnQZm3sTMTXu0FB8CeTQFSK7/mLB/lmyqVMC2uRrhpHWk2N7JTroUdg56PVaqC8y
# yGe/JG/tnZetmFAoLnJiYshv/ZnpnRRgKvPXdhKrzUJkcjHf+bgq9MZamiBAPciJ
# /juLt2tPnQoj9H1lZihjFz7YLbgBPQ==
# -----END CERTIFICATE-----
# tls.key: |-
# -----BEGIN PRIVATE KEY-----
# ...
# vrBbeOQcq5qG7hL3Do3yNYXj58pBAkBYpL+yawMxtMFDnckJ0hdkcpxTG0e0qeKy
# KeAS3QlFFgNOj3n3EV3/AmKnN3DSDcSgu+J9Tyw8L5jkq87WX4p5AkBGeD+A6baD
# ssitmZdrtJ6zLGbFGFkWk34mRq8csIcxnwhzVgIpJF6oPnreE6FgTh3GwBzguNsy
# Vnr96rwkf345
# -----END PRIVATE KEY-----

引用时设置可选key

...
- name: LOG_LEVEL
valueFrom:
secretKeyRef:
name: hellok8s-secret # name必须是有效且存在的
key: not_found_key
# optional: true

k8s允许在引用Secret时添加optional: true属性,以确保Secret中的字段不存在时不会影响Pod启动(但Secret对象本身必须存在)。

拉取私有镜像使用Secret

使用命令创建secret

# generic表示常规类型,通过 kubectl create secret -h 查看参数说明
$ kubectl create secret generic db-user-pass \
--from-literal=username=admin \
--from-literal=password='S!B\*d$zDsb='
secret/db-user-pass created

# 或者通过文件创建
$ kubectl create secret generic db-user-pass \
--from-file=./username.txt \
--from-file=./password.txt

创建后直接查看Secret明文:

$ kubectl describe secret db-user-pass                                                                         
Name: db-user-pass
Namespace: default
Labels: <none>
Annotations: <none>

Type: Opaque

Data
====
password: 12 bytes
username: 5 bytes
$ kubectl get secret db-user-pass -o jsonpath='{.data}'
{"password":"UyFCXCpkJHpEc2I9","username":"YWRtaW4="} # value是base64编码
$ echo UyFCXCpkJHpEc2I9 |base64 --decode
S!B\*d$zDsb=

$ kubectl get secret db-user-pass -o jsonpath='{.data.password}' | base64 --decode
S!B\*d$zDsb=#
$ kubectl get secret db-user-pass -o jsonpath='{.data.username}' | base64 --decode
admin

删除Secret:

kubectl delete secret db-user-pass