动手动脑学Kubernetes系列教程之Secret介绍

查理谈科技 2024-05-09 00:09:22

在之前的文章中, 介绍了搭建好的#minikube#环境,如果你现在还没有一个可用的minikube环境, 那么可以去中直接下载;

在之前的文章中, 先后介绍了如何从源代码开始构建一个Node.js应用和Spring Boot 应用, 并且部署到Kubernetes 中(这个Kubernetes 环境主要是之前建好的#minikube#) , 现在我们开始进一步深入的学习Kubernetes, 用一个个可以实际运行的例子的形式, 深入理解#Kubernetes#的概念以及原理.

在#动手动脑学Kubernetes#系列教程中, 我们展示了Kubernetes的基本用法

在里, 学习了Pod的基本知识;

在里, 学习了标签(Label)的语法, 使用Label来选择和过滤Kubernetes 资源;

在里, 介绍了Deployment的使用, 介绍了Deployment与Replica Set、Pod的关系, 并展示了如何进行应用的版本回滚;

在里, 介绍了Service的使用,使用Replication Controller创建Pod, 并创建Service, 展示了从Service 调用应用的方法; 随后又展示了扩展 Pod的数量为2, 比较了Service和之前的不同, 基本展示了Cluster IP 类型的Service的基本用法.

在里, 介绍了Namespace的使用, 除了创建,列出系统的Namespace之外, 还说明Namespace 如何对资源进行隔离, 建立Development, Staging, Production等环境的方法.

在里, 介绍了Service Discovery的使用, 讲解了如何检查Kube-dns, 如何检查和使用Service的FQDN等知识, 对Kubernetes的DNS 系统有整体的理解.

在里, 介绍了Port Forwards, 端口转发的用法, 在本地程序开发的时候, 使用端口转发可以简化本地测试的工作, 同时介绍了其他几种本地端口转发的用法.

在里, 介绍了Probe(探针)的知识, 介绍了livenessProbe 和readinessProbe的用法,同时介绍了Pod 容器中的几个状态变化, 以及2个容器生命周期回调接口.

在里, 介绍了环境变量的用法, 使用环境变量可以把Pod 定义的信息传递给运行其中的镜像.

在里, 我们介绍了Volume, 卷的用法, 主要展示了emptyDir卷的使用, emptyDir卷的生命周期是和Pod 生命周期同步的.

在里, 我们介绍了Persistent Volumes, 也就是持久卷的用法,展示了当数据存入到PV 之后,数据超乎Pod 生命周期之外的情况.

在本篇里, 我们将介绍#Secret#, 机密,也就是Kubernetes对机密信息的存储和分享, 继续学习吧!

#Secret#, 敏感信息, 例如数据库密码或API token等, 都是项目中必须的配置信息, 这些信息涉及公司的机密, 一般不希望以明文形式保留, 而Kubernetes为此提供了一种Secret 机制,可以安全可靠地使用具有以下属性的此类信息:

机密是命名空间对象,即存在于命名空间的上下文中您可以通过在容器中运行的容器中的卷或环境变量来访问它们节点上的秘密数据存储在tmpfs卷中每个秘密的大小限制为1MBAPI服务器将机密信息以纯文本格式存储在etcd中

先从例子开始吧!

创建一条API key

先来创建一条API key, 暂时先放到纯文本里面:

$ echo -n "A19fh68B001j" > ./apikey.txt

有了这条apikey之后, 下面开始放到Kubernetes里面:

$ kubectl create secret generic apikey --from-file=./apikey.txt secret/apikey created$ kubectl describe secrets/apikeyName:         apikeyNamespace:    defaultLabels:       <none>Annotations:  <none>Type:  OpaqueData====apikey.txt:  12 bytes

可以看到, 我们使用kubectl create 创建了secret, 我们之前一直使用的是kubectl apply命令, 但其实kubectl create 也是一个极其强大的命令, 可以创建deployment, service等资源对象:

$ kubectl create --helpCreate a resource from a file or from stdin. JSON and YAML formats are accepted.Examples:  # Create a pod using the data in pod.json.  kubectl create -f ./pod.json    # Create a pod based on the JSON passed into stdin.  cat pod.json | kubectl create -f -    # Edit the data in docker-registry.yaml in JSON then create the resource using the edited data.  kubectl create -f docker-registry.yaml --edit -o jsonAvailable Commands:  clusterrole         Create a ClusterRole.  clusterrolebinding  Create a ClusterRoleBinding for a particular ClusterRole  configmap           Create a configmap from a local file, directory or literal value  cronjob             Create a cronjob with the specified name.  deployment          Create a deployment with the specified name.  ingress             Create an ingress with the specified name.  job                 Create a job with the specified name.  namespace           Create a namespace with the specified name  poddisruptionbudget Create a pod disruption budget with the specified name.  priorityclass       Create a priorityclass with the specified name.  quota               Create a quota with the specified name.  role                Create a role with single rule.  rolebinding         Create a RoleBinding for a particular Role or ClusterRole  secret              Create a secret using specified subcommand  service             Create a service using specified subcommand.  serviceaccount      Create a service account with the specified name

来具体看看kubectl create secret 命令:

$ kubectl create secret --helpCreate 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

可以看到, create secret 可以创建三种格式的secret, 第一种是为docker-registry 创建的secret, 这个我们暂时不用理会; 第二种是generic类型, 就是从指定的文件,目录或者文本中创建机密信息;第三种是tls, 这是通过公钥/私钥来为TLS创建的一种密钥.

我们来看看我们创建的名为apikey的机密:

$ kubectl describe secrets/apikeyName:         apikeyNamespace:    defaultLabels:       <none>Annotations:  <none>Type:  OpaqueData====apikey.txt:  12 bytes

从上面的信息中可以看到, Secret 是和Namespace联系在一起的(如果对Namespace不太了解, 可以回顾内容), Opaque 是Secret的一种内置的类型, 表示这种Secret可以存储用户定义的任意数据.

下面我们来看看怎么在Pod 中使用Secret.

使用Secret

下面让我们在Pod中使用这个Secret试试:

Github地址:

https://raw.githubusercontent.com/hintcnuie/kbe/main/specs/secrets/pod.yaml

文件内容:

apiVersion: v1kind: Podmetadata: name: consumesecspec: containers: - name: shell image: centos:7 command: - "bin/bash" - "-c" - "sleep 10000" volumeMounts: - name: apikeyvol mountPath: "/tmp/apikey" readOnly: true volumes: - name: apikeyvol secret: secretName: apikey

可以看到, Secret 是作为卷来使用的, 先声明创建这个卷, 然后再引用为本地的一个目录:

volumeMounts: - name: apikeyvol mountPath: "/tmp/apikey" readOnly: true volumes: - name: apikeyvol secret: secretName: apikey

创建:

$ kubectl apply -f https://raw.githubusercontent.com/hintcnuie/kbe/main/specs/secrets/pod.yamlpod/consumesec created$ kubectl describe pod consumesecName: consumesecNamespace: defaultPriority: 0Node: localhost.localdomain/10.0.2.15Start Time: Thu, 18 Mar 2021 06:39:42 +0000Labels: <none>Annotations: <none>Status: RunningIP: 172.17.0.10IPs: IP: 172.17.0.10Containers: shell: Container ID: docker://73facc18ef835d09a4987ddc09206720b33371524f514f325e6a090cc8c0db39 Image: centos:7 Image ID: docker-pullable://centos@sha256:0f4ec88e21daf75124b8a9e5ca03c37a5e937e0e108a255d890492430789b60e Port: <none> Host Port: <none> Command: bin/bash -c sleep 10000 State: Running Started: Thu, 18 Mar 2021 06:39:43 +0000 Ready: True Restart Count: 0 Environment: <none> Mounts: /tmp/apikey from apikeyvol (ro) /var/run/secrets/kubernetes.io/serviceaccount from default-token-q77th (ro)Conditions: Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: apikeyvol: Type: Secret (a volume populated by a Secret) SecretName: apikey Optional: false default-token-q77th: Type: Secret (a volume populated by a Secret) SecretName: default-token-q77th Optional: falseQoS Class: BestEffortNode-Selectors: <none>Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300sEvents: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 3m14s default-scheduler Successfully assigned default/consumesec to localhost.localdomain Normal Pulled 3m13s kubelet Container image "centos:7" already present on machine Normal Created 3m13s kubelet Created container shell Normal Started 3m13s kubelet Started container shell

下面到Pod 中访问一下这个机密信息:

$ kubectl exec -it consumesec -c shell -- bash[root@consumesec /]# mount | grep apikeytmpfs on /tmp/apikey type tmpfs (ro,relatime)[root@consumesec /]# cat /tmp/apikey/cat: /tmp/apikey/: Is a directory[root@consumesec /]# cat /tmp/apikey/apikey.txt A19fh68B001j

可以看到, 我们已经成功地获得之前设定的apikey的值A19fh68B001j, 验证成功!

关于Secret 操作部分就到这里, 下面来看一下相关的理论知识吧.

什么是Secret--机密?

Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 SSH 密钥。 将这些信息放在 secret 中比放在 Pod 的定义或者 容器镜像中来说更加安全和灵活。

Secret 是一种包含少量敏感信息例如密码、令牌或密钥的对象。 这样的信息可能会被放在 Pod 规约中或者镜像中。 用户可以创建 Secret,同时系统也创建了一些 Secret。

注意:

Kubernetes Secret 默认情况下存储为 base64-编码的、非加密的字符串。 默认情况下,能够访问 API 的任何人,或者能够访问 Kubernetes 下层数据存储(etcd) 的任何人都可以以明文形式读取这些数据。

为了能够安全地使用 Secret,建议(至少):

1.为 Secret 启用静态加密;

2.启用 RBAC 规则来限制对 Secret 的读写操作。

要注意,任何被允许创建 Pod 的人都默认地具有读取 Secret 的权限。

Secret 概览

要使用 Secret,Pod 需要引用 Secret。 Pod 可以用三种方式之一来使用 Secret:

作为挂载到一个或多个容器上的 卷 中的文件。作为容器的环境变量由 kubelet 在为 Pod 拉取镜像时使用

Secret 对象的名称必须是合法的 DNS 子域名。

什么是DNS 子域名呢?

很多资源类型需要可以用作 DNS 子域名的名称。 DNS 子域名的定义可参见 RFC 1123。 这一要求意味着名称必须满足如下规则:

不能超过253个字符

只能包含字母数字,以及'-' 和 '.'

须以字母数字开头

须以字母数字结尾

在为创建 Secret 编写配置文件时,你可以设置 data 与/或 stringData 字段。 data 和 stringData 字段都是可选的。data 字段中所有键值都必须是 base64 编码的字符串。如果不希望执行这种 base64 字符串的转换操作,你可以选择设置 stringData 字段,其中可以使用任何字符串作为其取值。

Secret 的类型

在创建 Secret 对象时,你可以使用 Secret 资源的 type 字段,或者与其等价的 kubectl 命令行参数(如果有的话)为其设置类型。 Secret 的类型用来帮助编写程序处理 Secret 数据。

Kubernetes 提供若干种内置的类型,用于一些常见的使用场景。 针对这些类型,Kubernetes 所执行的合法性检查操作以及对其所实施的限制各不相同。

内置类型

用法

Opaque

用户定义的任意数据

kubernetes.io/service-account-token

服务账号令牌

kubernetes.io/dockercfg

~/.dockercfg 文件的序列化形式

kubernetes.io/dockerconfigjson

~/.docker/config.json 文件的序列化形式

kubernetes.io/basic-auth

用于基本身份认证的凭据

kubernetes.io/ssh-auth

用于 SSH 身份认证的凭据

kubernetes.io/tls

用于 TLS 客户端或者服务器端的数据

bootstrap.kubernetes.io/token

启动引导令牌数据

通过为 Secret 对象的 type 字段设置一个非空的字符串值,你也可以定义并使用自己 Secret 类型。如果 type 值为空字符串,则被视为 Opaque 类型。 Kubernetes 并不对类型的名称作任何限制。不过,如果你要使用内置类型之一, 则你必须满足为该类型所定义的所有要求。

Opaque 类型的Secret

当 Secret 配置文件中未作显式设定时,默认的 Secret 类型是 Opaque。 当你使用 kubectl 来创建一个 Secret 时,你会使用 generic 子命令来标明 要创建的是一个 Opaque 类型 Secret。 例如,下面的命令会创建一个空的 Opaque 类型 Secret 对象:

kubectl create secret generic empty-secretkubectl get secret empty-secret

输出类似于

NAME TYPE DATA AGEempty-secret Opaque 0 2m6s

DATA 列显示 Secret 中保存的数据条目个数。 在这个例子种,0 意味着我们刚刚创建了一个空的 Secret。

服务账号令牌 Secret

类型为 kubernetes.io/service-account-token 的 Secret 用来存放标识某 服务账号的令牌。使用这种 Secret 类型时,你需要确保对象的注解 kubernetes.io/service-account-name 被设置为某个已有的服务账号名称。 某个 Kubernetes 控制器会填写 Secret 的其它字段,例如 kubernetes.io/service-account.uid 注解以及 data 字段中的 token 键值,使之包含实际的令牌内容。

下面的配置实例声明了一个服务账号令牌 Secret:

apiVersion: v1kind: Secretmetadata: name: secret-sa-sample annotations: kubernetes.io/service-account.name: "sa-name"type: kubernetes.io/service-account-tokendata: # 你可以像 Opaque Secret 一样在这里添加额外的键/值偶对 extra: YmFyCg==

Kubernetes 在创建 Pod 时会自动创建一个服务账号 Secret 并自动修改你的 Pod 以使用该 Secret。该服务账号令牌 Secret 中包含了访问 Kubernetes API 所需要的凭据。

如果需要,可以禁止或者重载这种自动创建并使用 API 凭据的操作。 不过,如果你仅仅是希望能够安全地访问 API 服务器,这是建议的工作方式。

编辑 Secret

你可以通过下面的命令编辑现有的 Secret:

kubectl edit secrets mysecret

这一命令会打开默认的编辑器,允许你更新 data 字段中包含的 base64 编码的 Secret 值:

# 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: v1data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rmkind: Secretmetadata: annotations: kubectl.kubernetes.io/last-applied-configuration: { ... } creationTimestamp: 2016-01-22T18:41:56Z name: mysecret namespace: default resourceVersion: "164619" uid: cfee02d6-c137-11e5-8d73-42010af00002type: Opaque使用 Secret

Secret 可以作为数据卷被挂载,或作为环境变量 暴露出来以供 Pod 中的容器使用。它们也可以被系统的其他部分使用,而不直接暴露在 Pod 内。 例如,它们可以保存凭据,系统的其他部分将用它来代表你与外部系统进行交互。

在 Pod 中使用 Secret 文件

在 Pod 中使用存放在卷中的 Secret:

创建一个 Secret 或者使用已有的 Secret。多个 Pod 可以引用同一个 Secret。修改你的 Pod 定义,在 spec.volumes[] 下增加一个卷。可以给这个卷随意命名, 它的 spec.volumes[].secret.secretName 必须是 Secret 对象的名字。将 spec.containers[].volumeMounts[] 加到需要用到该 Secret 的容器中。 指定 spec.containers[].volumeMounts[].readOnly = true 和 spec.containers[].volumeMounts[].mountPath 为你想要该 Secret 出现的尚未使用的目录。修改你的镜像并且/或者命令行,让程序从该目录下寻找文件。 Secret 的 data 映射中的每一个键都对应 mountPath 下的一个文件名。

这是一个在 Pod 中使用存放在挂载卷中 Secret 的例子:

apiVersion: v1kind: Podmetadata: name: mypodspec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret

您想要用的每个 Secret 都需要在 spec.volumes 中引用。

如果 Pod 中有多个容器,每个容器都需要自己的 volumeMounts 配置块, 但是每个 Secret 只需要一个 spec.volumes。

您可以打包多个文件到一个 Secret 中,或者使用的多个 Secret,怎样方便就怎样来。

好了, 关于Secret 知识就讲到这里, 继续学习吧!

-----------------------不断学习, 努力进取, 方能天地广阔----------------------

0 阅读:0

查理谈科技

简介:感谢大家的关注