【6】使用Namespace、ConfigMap和Secret
使用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