K8S之secret
secret是k8s中用来存储敏感认证信息的一种重要资源,大致可以分为三种类型:docker-registry
,generic
和tls
,从名称上就可以看出来,分别用于存储镜像仓库认证信息,一般信息和证书信息。其中generic类型的最常用,比较典型的就是用来存放数据库的认证信息。
创建secret
使用kubectl create命令创建
我们先来查看一下命令帮助:
[root@center-188 pod]# kubectl create secret --help
Create a secret using specified subcommand.
Available Commands:
docker-registry Create a secret for use with a Docker registry
generic Create a secret from a local file, directory or literal value
tls Create a TLS secret
Usage:
kubectl create secret [flags] [options]
Use "kubectl <command> --help" for more information about a given command.
Use "kubectl options" for a list of global command-line options (applies to all
commands).
可以看到有子命令,及使用方式说明,可以继续跟上子命令,查看更详细的帮助信息:
[root@center-188 pod]# kubectl create secret generic --help
Create a secret based on a file, directory, or specified literal value.
A single secret may package one or more key/value pairs.
When creating a secret based on a file, the key will default to the basename of
the file, and the value will default to the file content. If the basename is an
invalid key or you wish to chose your own, you may specify an alternate key.
When creating a secret based on a directory, each file whose basename is a
valid key in the directory will be packaged into the secret. Any directory
entries except regular files are ignored (e.g. subdirectories, symlinks,
devices, pipes, etc).
Examples:
# Create a new secret named my-secret with keys for each file in folder bar
kubectl create secret generic my-secret --from-file=path/to/bar
# Create a new secret named my-secret with specified keys instead of names on
disk
kubectl create secret generic my-secret
--from-file=ssh-privatekey=path/to/id_rsa
--from-file=ssh-publickey=path/to/id_rsa.pub
# Create a new secret named my-secret with key1=supersecret and key2=topsecret
kubectl create secret generic my-secret --from-literal=key1=supersecret
--from-literal=key2=topsecret
# Create a new secret named my-secret using a combination of a file and a
literal
kubectl create secret generic my-secret
--from-file=ssh-privatekey=path/to/id_rsa --from-literal=passphrase=topsecret
# Create a new secret named my-secret from an env file
kubectl create secret generic my-secret --from-env-file=path/to/bar.env
Options:
--allow-missing-template-keys=true: If true, ignore any errors in
templates when a field or map key is missing in the template. Only applies to
golang and jsonpath output formats.
--append-hash=false: Append a hash of the secret to its name.
--dry-run=false: If true, only print the object that would be sent,
without sending it.
--from-env-file='': Specify the path to a file to read lines of key=val
pairs to create a secret (i.e. a Docker .env file).
--from-file=[]: Key files can be specified using their file path, in which
case a default name will be given to them, or optionally with a name and file
path, in which case the given name will be used. Specifying a directory will
iterate each named file in the directory that is a valid secret key.
--from-literal=[]: Specify a key and literal value to insert in secret
(i.e. mykey=somevalue)
--generator='secret/v1': The name of the API generator to use.
-o, --output='': Output format. One of:
json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file.
--save-config=false: If true, the configuration of current object will be
saved in its annotation. Otherwise, the annotation will be unchanged. This flag
is useful when you want to perform kubectl apply on this object in the future.
--template='': Template string or path to template file to use when
-o=go-template, -o=go-template-file. The template format is golang templates
[http://golang.org/pkg/text/template/#pkg-overview].
--type='': The type of secret to create
--validate=true: If true, use a schema to validate the input before
sending it
Usage:
kubectl create secret generic NAME [--type=string] [--from-file=[key=]source]
[--from-literal=key1=value1] [--dry-run] [options]
Use "kubectl options" for a list of global command-line options (applies to all
commands).
这里已经说明的非常详细了,我再举几个例子说明一下:
# 例一:
# --from-literal:从命令行直接指定键值对信息
[root@center-188 pod]# kubectl create secret generic ssh-user --from-literal=user=root -n wuvikr
secret/ssh-user created
# 可以看到我们在wuvikr名称空间下创建了一个名为ssh-user的secret
[root@center-188 pod]# kubectl get secret -n wuvikr
NAME TYPE DATA AGE
ssh-user Opaque 1 28s
# 查看详细信息
# 其中data字段中,保存的就是我们指定的信息
# 需要注意的是这里的cm9vdA==并不是加密信息,仅仅是root的base64转码后的信息
# 生产中如果安全要求很高,可以使用secret加密插件
[root@center-188 pod]# kubectl get secret ssh-user -n wuvikr -o yaml
apiVersion: v1
data:
user: cm9vdA==
kind: Secret
metadata:
creationTimestamp: "2021-02-26T01:38:27Z"
name: ssh-user
namespace: wuvikr
resourceVersion: "46489313"
selfLink: /api/v1/namespaces/wuvikr/secrets/ssh-user
uid: 5d16c41f-1b70-4d3c-934e-ecfa7b29ab5e
type: Opaque
# 例二:
# --from-file:从文件中读取键值对信息
[root@center-188 pod]# cat passwd.txt
123456
# 这里--from-file后也可以跟上键名,如果不指定,默认键名为文件名
[root@center-188 pod]# kubectl create secret generic ssh-pass --from-file=./passwd.txt -n wuvikr
secret/ssh-pass created
[root@center-188 pod]# kubectl get secret -n wuvikr
NAME TYPE DATA AGE
ssh-pass Opaque 1 8s
ssh-user Opaque 1 9m21s
# 查看详细信息
[root@center-188 pod]# kubectl get secret ssh-pass -n wuvikr -o yaml
apiVersion: v1
data:
passwd.txt: MTIzNDU2Cg== #由于没有指定键名,键名默认为文件名了
kind: Secret
metadata:
creationTimestamp: "2021-02-26T01:47:40Z"
name: ssh-pass
namespace: wuvikr
resourceVersion: "46491627"
selfLink: /api/v1/namespaces/wuvikr/secrets/ssh-pass
uid: 1bd4650a-b58c-4e4a-9f15-cf0234bb10b6
type: Opaque
# 这次我们指定下键名
[root@center-188 pod]# kubectl create secret generic ssh-pass1 --from-file=pass=./passwd.txt -n wuvikr
[root@center-188 pod]# kubectl get secret ssh-pass1 -n wuvikr -o yaml
apiVersion: v1
data:
pass: MTIzNDU2Cg== # 指定了键名为pass
kind: Secret
metadata:
creationTimestamp: "2021-02-26T01:54:26Z"
name: ssh-pass1
namespace: wuvikr
resourceVersion: "46493370"
selfLink: /api/v1/namespaces/wuvikr/secrets/ssh-pass1
uid: dddb33d7-af66-44c0-b2f4-15d96ec64ef2
type: Opaque
# 当然也可以一次性将多个文件加入到一个secret中,具体怎么使用怎么方便就怎么来
[root@center-188 pod]# kubectl create secret generic ssh-account -n wuvikr --from-file=username=./username.txt --from-file=password=./passwd.txt
[root@center-188 pod]# kubectl get secret ssh-account -n wuvikr -o yaml
apiVersion: v1
data:
password: MTIzNDU2Cg==
username: YWRtaW4K
kind: Secret
metadata:
creationTimestamp: "2021-02-26T05:09:37Z"
name: ssh-account
namespace: wuvikr
resourceVersion: "46541985"
selfLink: /api/v1/namespaces/wuvikr/secrets/ssh-account
uid: 13a19839-ae6b-4b6b-bfe1-b2026dfeb668
type: Opaque
# 你可能会想,要是我们需要用到的secret值很多,一个个添加实在太麻烦了
# 不用担心,--from-file还可以直接指定一个目录
# 其目录中所有的文件都会被加载到secret中去
# 但是这种情况下,就不能在命令行中直接指定键名了,因为目录下有多个文件。
[root@center-188 pod]# ll files
total 8
-rw-r--r-- 1 root root 4 Feb 26 09:49 01.txt
-rw-r--r-- 1 root root 4 Feb 26 09:49 02.txt
[root@center-188 pod]# cat files/01.txt files/02.txt
aaa
bbb
[root@center-188 pod]# kubectl create secret generic test01 --from-file=./files/ -n wuvikr
[root@center-188 pod]# kubectl get secret test01 -n wuvikr -o yaml
apiVersion: v1
data:
01.txt: YWFhCg==
02.txt: YmJiCg==
kind: Secret
metadata:
creationTimestamp: "2021-02-26T01:50:59Z"
name: test01
namespace: wuvikr
resourceVersion: "46492414"
selfLink: /api/v1/namespaces/wuvikr/secrets/test01
uid: 65a61fa1-c9f0-4a9c-bf84-a1bf7e97fdcb
# 是不是很方便,files目录下的每个文件都被添加到这个secret中来了,并且键名都为文件名。
# 例三:
# --from-env-file:环境变量键值对文件
# 有时候我们在写yaml的时候,可能要为某个pod指定多个ENV
# 一个一个添加真是太麻烦了,我们可以直接将这些ENV都写入一个文件中
# 然后创建一个ENV的secret,再直接挂载到容器中使用。
[root@center-188 pod]# cat env-mysql.txt
MYSQL_ROOT_PASSWORD=744123
MYSQL_DATABASE=testdb
MYSQL_USER=test
MYSQL_PASSWORD=123456
[root@center-188 pod]# kubectl create secret generic env-mysql --from-env-file=./env-mysql.txt -n wuvikr
secret/env-mysql created
[root@center-188 pod]# kubectl get secret env-mysql -n wuvikr -o yaml
apiVersion: v1
data:
MYSQL_DATABASE: dGVzdGRi
MYSQL_PASSWORD: MTIzNDU2
MYSQL_ROOT_PASSWORD: NzQ0MTIz
MYSQL_USER: dGVzdA==
kind: Secret
metadata:
creationTimestamp: "2021-02-26T02:09:58Z"
name: env-mysql
namespace: wuvikr
resourceVersion: "46497156"
selfLink: /api/v1/namespaces/wuvikr/secrets/env-mysql
uid: 3e706ce9-c260-495f-ba98-9cca55de8f12
type: Opaque
直接写yaml文件创建
如果我们很熟悉secret的格式的话,完全可以自己手写一份yaml文件来创建新的secret。从已有的secret导出一个当做模板,再按需修改就行了。
# 导出已有模板
[root@center-188 secret]# kubectl get secret ssh-user -n wuvikr -o yaml > my-secret.yaml
# 按需修改,保留以下基础部分即可
[root@center-188 secret]# cat my-secret.yaml
apiVersion: v1
data:
user: cm9vdA==
kind: Secret
metadata:
name: ssh-user
namespace: wuvikr
# 创建Secret
[root@center-188 secret]# kubectl apply -f ./my-secret.yaml
编辑修改secret
可以kubectl edit命令来编辑修改已有的 Secret:
[root@center-188 secret]# kubectl edit secrets ssh-user -n wuvikr
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
user: cm9vdA==
kind: Secret
metadata:
creationTimestamp: "2021-02-26T01:38:27Z"
name: ssh-user
namespace: wuvikr
resourceVersion: "46489313"
selfLink: /api/v1/namespaces/wuvikr/secrets/ssh-user
uid: 5d16c41f-1b70-4d3c-934e-ecfa7b29ab5e
type: Opaque
需要注意的是:data字段中保存的是base64 编码的 Secret 值。
# 先将要编辑的内容转换为base64编码,再编辑修改secret文件。
[root@center-188 secret]# echo 'passwd' | base64
cGFzc3dkCg==
secret的使用
以volume挂载的方式使用secret
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-test
namespace: wuvikr
spec:
containers:
- name: secret-volume-test
image: busybox
args:
- sleep
- "86400"
volumeMounts:
- name: ssh-cred
mountPath: "/tmp"
readOnly: true
volumes:
- name: ssh-cred
secret:
secretName: ssh-account
执行上面的yaml,并进入到容器中:
[root@center-188 pod]# kubectl apply -f secret-volume-test.yaml
pod/secret-volume-test created
[root@center-188 pod]# kubectl exec -it secret-volume-test -n wuvikr -- sh
/ # cd /tmp
/tmp # ls -al
total 0
drwxrwxrwt 3 root root 120 Feb 26 05:29 .
drwxr-xr-x 1 root root 29 Feb 26 05:29 ..
drwxr-xr-x 2 root root 80 Feb 26 05:29 ..2021_02_26_05_29_13.312279811
lrwxrwxrwx 1 root root 31 Feb 26 05:29 ..data -> ..2021_02_26_05_29_13.312279811
lrwxrwxrwx 1 root root 15 Feb 26 05:29 password -> ..data/password
lrwxrwxrwx 1 root root 15 Feb 26 05:29 username -> ..data/username
/tmp # cat username password
admin
123456
这里的password 和username 文件其实只是一个软连接,这是k8s为了保持安全更新的一个机制,kubelet 会周期性检查被挂载的Secret
是不是最新的,当etcd
中的secret被更新后,pod中挂载的secret也会得到更新,但并不是立即更新,中间会有一个延迟。
上面的挂载方式默认会将secret中的所有键全部挂载进容器中去,有时候我们或许只是想挂载secret中的某几个键,这种情况下可以使用spec.volumes[].secret.items
字段来实现:
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-item-test
namespace: wuvikr
spec:
containers:
- name: secret-volume-item-test
image: busybox
args:
- sleep
- "86400"
volumeMounts:
- name: ssh-cred
mountPath: "/tmp"
readOnly: true
volumes:
- name: ssh-cred
secret:
secretName: ssh-account
items:
- key: username
path: my-path/username
和之前的yaml文件几乎一样,唯一不同的就是在spec.volumes[].secret
下使用了items
字段,只有在 items
中指定的键会加载。
运行一下上面的yaml,并进入到容器中:
[root@center-188 pod]# kubectl apply -f secret-volume-item-test.yaml
pod/secret-volume-test created
[root@center-188 pod]# kubectl exec -it secret-volume-item-test -n wuvikr -- sh
/ # cd /tmp
/tmp # ls -al
total 0
drwxrwxrwt 3 root root 100 Feb 26 05:46 .
drwxr-xr-x 1 root root 29 Feb 26 05:46 ..
drwxr-xr-x 3 root root 60 Feb 26 05:46 ..2021_02_26_05_46_42.795865831
lrwxrwxrwx 1 root root 31 Feb 26 05:46 ..data -> ..2021_02_26_05_46_42.795865831
lrwxrwxrwx 1 root root 14 Feb 26 05:46 my-path -> ..data/my-path
/tmp # cd my-path/
/tmp/..2021_02_26_05_46_42.795865831/my-path # ls
username
除了只加载了username之外,还可以发现username
Secret 这次存储在了 /tmp/my-path/username
文件中而不是 /tmp/username
中了,这是spec.volumes[].secret.items[].path
字段所实现的,而且这还是一个必须字段。
以环境变量的形式使用 Secrets
环境变量引用有两种方式,下面示例中的第一种写法是从secret中引用某个环境变量,而第二种写法会直接加载secret中全部的环境变量。
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
namespace: wuvikr
spec:
containers:
- name: mycontainer
image: mysql:latest
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: env-mysql
key: MYSQL_ROOT_PASSWORD
#########################################
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
namespace: wuvikr
spec:
containers:
- name: mycontainer
image: mysql:latest
envFrom:
- secretRef:
name: env-mysql
执行上面的yaml,并进入到容器中:
[root@center-188 secret]# kubectl apply -f secret-env-pod.yaml
pod/secret-env-pod created
# 打印一下环境变量
[root@center-188 secret]# kubectl exec -it secret-env-pod -n wuvikr -- bash
root@secret-env-pod:/# printenv
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
MYSQL_MAJOR=8.0
HOSTNAME=secret-env-pod
PWD=/
MYSQL_ROOT_PASSWORD=744123
MYSQL_PASSWORD=123456
MYSQL_USER=test
HOME=/root
KUBERNETES_PORT_443_TCP=tcp://10.68.0.1:443
MYSQL_VERSION=8.0.23-1debian10
GOSU_VERSION=1.12
TERM=xterm
SHLVL=1
KUBERNETES_PORT_443_TCP_PROTO=tcp
MYSQL_DATABASE=testdb
KUBERNETES_PORT_443_TCP_ADDR=10.68.0.1
KUBERNETES_SERVICE_HOST=10.68.0.1
KUBERNETES_PORT=tcp://10.68.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/printenv
这里需要注意的是:Secret 更新之后对应的环境变量并不会被更新
如果某个secret已经被更新,对于已经引用过该secret的容器来说,并不会主动更新ENV,除非该pod被重启。