当前位置: 首页 > news >正文

配置Kubernetes资源管理Secret与ConfigMap

前言

Kubernetes 中的 Secret(提供加密模式)ConfigMap(提供配置)是关键的资源,用于安全地存储和管理敏感信息和配置数据。它们在应用程序开发和部署过程中扮演着重要的角色。本文将介绍如何有效地配置和管理这些资源,以便更好地利用 Kubernetes 的强大功能。

目录

一、Secret

1. 概述

2. Secret 类型

2.1 kubernetes.io/service-account-token

2.2 Opaque

2.3 kubernetes.io/dockerconfigjson

2.4 kubernetes.io/tls

2.5 secrets 可选参数

3. Secret 使用方式 

4. 创建 Secret

4.1 使用 kubectl create secret 命令陈述式创建 Secret

4.2 使用 yaml 文件声明式创建 Secret

5. 使用 Secret

5.1 以 Volume 的形式挂载

5.2 使用环境变量的方式创建

5.3 小结

二、ConfigMap 

1. 概述

2. ConfigMap 作用

3. ConfigMap 和 Secret 对比

4. 创建 ConfigMap 

4.1 使用目录创建

4.2 使用文件创建

4.3 使用字面值创建

5. 使用 ConfigMap 

5.1 使用 ConfigMap 来替代环境变量

5.2 用 ConfigMap 设置命令行参数

5.3 通过数据卷插件使用 ConfigMap 

5.4 ConfigMap 的热更新 

5.4.1 更新 ConfigMap 中的配置数据

5.4.2 subPath 字段热更新

5.5 ConfigMap 更新后滚动更新 Pod

三、总结

1. Secret 四种类型

2. 创建 Secret

3. 创建ConfigMap

4. 查看资源中的数据是以明文的格式去显示key的值

5. ConfigMap使用

6. ConfigMap 的热更新,有两种方式


一、Secret

1. 概述

在 K8s 中,Secret 是用于存储敏感信息的对象,如密码、token 令牌和 SSH 密钥。这类数据虽然也可以存放在 Pod 或者镜像中,但是放在 Secret 中是为了更方便的控制如何使用数据,并减少暴露的风险;这些信息以加密形式存储,并且只能被授权的 Pod 访问。通过使用 Secret,可以更安全地管理应用程序所需的敏感数据。

也就是说 K8s 解决了我们和 Docker 使用的一些弊端,比如:环境变量、存储问题都要去重新编辑 Dockerfile,而且密码还是明文的。

2. Secret 类型

2.1 kubernetes.io/service-account-token

由 Kubernetes 自动创建,用来加载 service-account 文件、访问 APIServer 的 Secret,Pod 会默认使用这个 Secret 与 APIServer 通信, 并且会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。

[root@master01 ~]# kubectl describe pod nginx-daemonset-bqmtgMounts:/var/run/secrets/kubernetes.io/serviceaccount from default-token-n27bv (ro)
# /v……t 是容器内的路径,表示该容器可以在此路径下访问 Kubernetes 提供的服务账户相关的凭据信息
# default-token-n27bv 是挂载的名称,用于标识挂载点的来源
# (ro) 表示这个挂载是以只读(read-only)方式进行的

每一个容器启动的时候都会使用 secrets 下面的账号来进行挂载的,而且权限为只读;不可能让所有的 pod 都有权限访问 api-server。

2.2 Opaque

base64 编码格式的 Secret,用来存储用户自定义的密码、密钥等,默认的 Secret 类型;这是默认的 Secret 类型,用于存储任意类型的密钥-值对。

2.3 kubernetes.io/dockerconfigjson

用来存储私有 docker registry(标识符)的认证信息;这种类型的 Secret 主要用于存储 Docker 镜像仓库的认证信息,包括服务器地址、用户名和密码等。这种 Secret 类型通常用于在 Kubernetes 中拉取私有 Docker 镜像时进行身份验证。

2.4 kubernetes.io/tls

用于为 ssl 通信模式存储证书和私钥文件、存储与 TLS(传输层安全)相关的证书和密钥,命令创建时类型标识符为 tls。这种类型的 Secret 通常用于安全地存储与 HTTPS 或其他加密通信相关的证书、私钥和 CA(证书颁发机构)证书等敏感信息。

2.5 secrets 可选参数

① docker-registry(镜像)

  • 当创建用于拉取私有 Docker 镜像的 Secret 时,可以指定该类型。这种 Secret 类型存储用于访问私有 Docker 镜像仓库所需的认证信息,包括服务器地址、用户名和密码等;

② tls(证书)

  • 用于存储与 TLS(传输层安全)相关的证书和密钥;

③ generic

  • 这是默认的 Secret 类型,用于存储任意类型的密钥-值对。它可以包含任何需要保密的信息,如用户名/密码、API 令牌等。

3. Secret 使用方式 

① 作为挂载到一个或多个容器上的卷 中的文件;

② 作为容器的环境变量;

③ 由 kubelet 在为 Pod 拉取镜像时使用。

应用场景:官网

https://kubernetes.io/docs/concepts/configuration/secret/

4. 创建 Secret

4.1 使用 kubectl create secret 命令陈述式创建 Secret

① 指定用户名和密码文件信息

[root@master01 ~]# mkdir secrets;cd secrets
[root@master01 secrets]# echo -n 'lisi' > username.txt
[root@master01 secrets]# echo -n '1234' > password.txt

② 使用 kubectl 工具在 K8s 中创建一个名为 "mysecret" 的通用类型 Secret

[root@master01 secrets]# kubectl create secret generic mysecret --from-file=username.txt --from-file=password.txt
secret/mysecret created# kubectl create secret generic mysecret
这部分指定了要创建一个名为 "mysecret" 的通用类型的 Secret
# --from-file=username.txt
这表示将 username.txt 文件的内容作为一个键值对添加到 Secret 中,键是文件名,值是文件的内容
# --from-file=password.txt
同样地,这表示将 password.txt 文件的内容作为另一个键值对添加到 Secret 中

③ 列出当前 K8s 集群中的所有 Secret 

[root@master01 secrets]# kubectl get secret
NAME                                 TYPE                                  DATA   AGE
mysecret                             Opaque                                2      77s
# Opaque类型用于存储任意类型的密钥-值对

④ 获取名为 "mysecret" 的 Secret 的详细信息

[root@master01 secrets]# kubectl describe secret mysecret
Name:         mysecret
Namespace:    default
Labels:       <none>
Annotations:  <none>Type:  OpaqueData
====
password.txt:  4 bytes  # 保密的数据,包含4字节的数据
username.txt:  4 bytes

注:get 或 describe 指令都不会展示 secret 的实际内容,这是出于对数据的保护的考虑 。

4.2 使用 yaml 文件声明式创建 Secret

虽然 base64 编码并不等同于加密,但它确实提供了一种简单有效的方式来存储和传输敏感信息,同时也有助于保护这些信息不被轻易泄露。

① 将输入的文本内容进行 base64 编码

[root@master01 secrets]# echo -n lisi | base64  # 进行base64编码
bGlzaQ==
[root@master01 secrets]# echo -n 1234 | base64
MTIzNA==

② 使用 yaml 文件创建

apiVersion: v1
kind: Secret          # 这是一个 Secret 对象
metadata:             # 包含有关对象的元数据name: mysecret1     # Secret 的名称
type: Opaque          # Secret 的类型
data:                 # 包含了实际的密钥-值对数据username: bGlzaQ==  # 经过 base64 编码的值password: MTIzNA==

③ 创建 mysecret1 的 Secret 对象

[root@master01 secrets]# kubectl create -f secret.yaml

④ 列出当前 K8s 集群中的所有 Secret 

[root@master01 secrets]# kubectl get secrets
NAME                                 TYPE                                  DATA   AGE
mysecret                             Opaque                                2      20m
mysecret1                            Opaque                                2      33s

④ 获取名为 "mysecret1" 的 Secret 的详细信息

[root@master01 secrets]# kubectl describe secret mysecret1
Name:         mysecret1
Namespace:    default
Labels:       <none>
Annotations:  <none>Type:  OpaqueData
====
password:  4 bytes
username:  4 bytes
# 两种格式基本一样

⑤  yaml 输出显示 Secret 对象的详细信息

[root@master01 secrets]# kubectl get secret mysecret1 -o yaml
# 包括它的类型、元数据以及经过 base64 编码的用户名和密码信息

5. 使用 Secret

上文介绍了陈述式和声明式简便的创建 Secret 的方法,当然了我们还可以通过挂载存储的方式来去挂载到 Pod 内部,或者导出到环境变量中。

5.1 以 Volume 的形式挂载

将 Secret 挂载到 Volume 中,以 Volume 的形式挂载到 Pod 的某个目录下

① 定义 yaml 文件

[root@master01 secrets]# vim secret-test.yaml
apiVersion: v1     
kind: Pod
metadata:name: mypod 
spec:                            # Pod 的规格containers:                    # 列出了 Pod 中的容器- name: nginx                  # 容器的名称image: nginx                 # 镜像volumeMounts:                # 将卷挂载到容器中- name: secrets              # 将volumeMount与名为"secrets"的卷相关联mountPath: "/etc/secrets"  # 指定容器内卷将被挂载的路径,Volume将在这个路径下可见readOnly: true             # 该卷将以只读方式挂载volumes:                       # 定义可以由容器挂载的卷- name: secrets                # 卷名,需与volumeMounts中的name一致secret:                      # 卷的类型secretName: mysecret       # 引用将作为卷挂载到Pod内部的Kubernetes Secret资源的名称

② 启动 pod

[root@master01 secrets]# kubectl create -f secret-test.yaml
[root@master01 secrets]# kubectl get pods
NAME                                      READY   STATUS        RESTARTS   AGE
mypod                                     1/1     Running       0          18s

③ 进入容器查看用户名和密码文件信息

[root@master01 secrets]# kubectl exec -it mypod sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
# cd /etc/secrets/
# ls
password.txt  username.txt
# cat username.txt   
lisi# 
# cat password.txt
1234#

5.2 使用环境变量的方式创建

将 Secret 导出到环境变量中

① 定义 yaml 文件

[root@master01 secrets]# vim secret-test1.yaml
apiVersion: v1
kind: Pod
metadata:name: mypod1
spec:containers:- name: nginximage: nginxenv:                    # 将环境变量传递给容器- name: TEST_USER     # 设置一个名为 "TEST_USER" 的环境变量valueFrom:          # 环境变量的值来源secretKeyRef:     # 该值来自于一个 Secret 中的特定键name: mysecret1 # 包含该值的 Secret 的名称为 "mysecret1"key: username   # 要使用的 Secret 中的键名为 "username"- name: TEST_PASSWORD # 设置一个名为 "TEST_PASSWORD" 的环境变量valueFrom:          # 指定该环境变量的值来源secretKeyRef:     # 该值来自于一个 Secret 中的特定键name: mysecret1 # 包含该值的 Secret 的名称为 "mysecret1"key: password   # 要使用的 Secret 中的键名为 "password"

② 启动 pod

[root@master01 secrets]# kubectl apply -f secret-test1.yaml[root@master01 secrets]# kubectl get pods
mypod1                                    1/1     Running       0          21s

③ 进入容器查看用户名和密码文件信息

[root@master01 secrets]# kubectl exec -it mypod1 bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@mypod1:/# echo $TEST_USER
lisi
root@mypod1:/# echo $TEST_PASSWORD
1234
root@mypod1:/# env | grep TEST  # 使用env指令查看系统所有内置变量
TEST_USER=lisi
TEST_PASSWORD=1234

5.3 小结

挂载的方式:

  • volumes 定义类型 secret 的存储卷
  • volumeMounts 把存储卷挂载到容器目录,secret 资源数据中的键将以文件名的形式显示,值文件内容

容器环境变量的方式:

  • env 定义容器的环境变量名
  • 使用 valueFrom.secretKeyRef.name 指定 secret 资源的名称
  • valueFrom.secretKeyRef.name 指定这个 secret 资源数据的键名,从而确定引用那个键的值

二、ConfigMap 

1. 概述

ConfigMap 是 K8s 中的一种资源对象,用于存储非敏感数据的配置信息,例如环境变量、命令行参数、配置文件等。ConfigMap 可以被挂载到 Pod 中,供应用程序使用其中的配置数据。这样做的好处是可以将配置信息与应用程序代码分离,使得配置更易管理和更新。

与Secret类似,区别在于 ConfigMap 保存的是不需要加密配置的信息。ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制大对象。应用场景:应用配置。

2. ConfigMap 作用

配置数据的集中管理

  • ConfigMap 允许将配置数据集中存储在 Kubernetes 集群中,而不是分散在各个 Pod 中。这样可以简化配置管理,提高可维护性。

与Pod解耦

  • 将配置信息存储在 ConfigMap 中可以使应用程序与配置数据解耦,使得配置可以独立于应用程序进行更新和修改,而无需修改应用程序本身。

动态配置更新

  • 通过使用 ConfigMap,可以实现动态更新应用程序的配置信息,而无需重新部署 Pod。这样可以减少应用程序 downtime,提高系统的可用性。

跨环境配置

  • 可以为不同的环境(例如开发、测试、生产环境)创建不同的 ConfigMap,从而实现跨环境的配置管理,方便在不同环境中部署应用程序。

3. ConfigMap 和 Secret 对比

① 数据类型

  • ConfigMap: 用于存储非敏感的配置数据,如环境变量、命令行参数、配置文件等。
  • Secret: 用于存储敏感数据,如密码、API密钥、证书等。Secret会对存储的数据进行Base64编码,但并不提供加密功能。

② 安全性

  • ConfigMap: 存储的数据是明文的,适用于不敏感的配置信息。不建议将敏感数据存储在ConfigMap中。
  • Secret: 存储的数据会被Base64编码,但并不提供加密功能。虽然Secret中的数据相对于ConfigMap更安全,但仍然需要注意敏感数据的保护。

③ 用途

  • ConfigMap: 用于存储应用程序的配置信息,如环境变量、配置文件等。
  • Secret: 用于存储敏感数据,如密码、密钥等,以便应用程序安全地访问这些数据。

④ 访问方式

  • ConfigMap: 可以通过环境变量、命令行参数或卷挂载的方式访问其中的数据。
  • Secret: 可以通过环境变量、命令行参数或卷挂载的方式访问其中的数据,但需要注意敏感数据的保护和访问权限控制。

4. 创建 ConfigMap 

4.1 使用目录创建

① 创建配置文件

[root@master01 ~]# mkdir /opt/configmap/
[root@master01 ~]# cd /opt/configmap/
[root@master01 configmap]# vim /opt/configmap/game.properties
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30[root@master01 configmap]# vim /opt/configmap/ui.properties
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

② 使用命令行创建

[root@master01 configmap]# kubectl create configmap game-config --from-file=/opt/configmap/
configmap/game-config created
# --from-file 指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容

③ 获取 ConfigMap 资源的信息

[root@master01 configmap]# kubectl get cm
NAME               DATA   AGE
game-config        2      7s

④ 以 yaml 格式查看了 game-config 的详细信息 

[root@master01 configmap]# kubectl get cm game-config -o yaml

4.2 使用文件创建

只要指定为一个文件就可以从单个文件中创建 ConfigMap;--from-file 这个参数可以使用多次,即可以使用两次分别指定上个实例中的那两个配置文件,效果就跟指定整个目录是一样的。

① 创建 ConfigMap

[root@master01 configmap]# kubectl create configmap game-config-2 --from-file=/opt/configmap/game.properties --from-file=/opt/configmap/ui.properties

② 列出所有在 Kubernetes 中创建的 ConfigMap

[root@master01 configmap]# kubectl get cm
NAME               DATA   AGE
game-config        2      7m26s
game-config-2      2      2m15s

③ 以 yaml 格式查看了 game-config-2 的详细信息 

[root@master01 configmap]# kubectl get configmaps game-config-2 -o yaml

④ 获取名为 game-config-2 的 ConfigMap 的详细描述信息 

[root@master01 configmap]# kubectl describe cm game-config-2

4.3 使用字面值创建

使用文字值创建,利用 --from-literal 参数传递配置信息,该参数可以使用多次,格式如下:

kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=good查看ConfigMap详情:
kubectl get configmaps special-config -o yaml删除所有ConfigMap和Pod:
kubectl delete cm --all
kubectl delete pod --all

示例:

[root@master01 configmap]# kubectl create configmap special-config --from-literal=name=wangwu --from-literal=age=10
configmap/special-config created[root@master01 configmap]# kubectl get configmaps special-config -o yaml
apiVersion: v1
data:age: "10"name: wangwu
kind: ConfigMap
metadata:creationTimestamp: "2024-06-02T16:00:51Z"managedFields:- apiVersion: v1fieldsType: FieldsV1fieldsV1:f:data:.: {}f:age: {}f:name: {}manager: kubectl-createoperation: Updatetime: "2024-06-02T16:00:51Z"name: special-confignamespace: defaultresourceVersion: "231163"selfLink: /api/v1/namespaces/default/configmaps/special-configuid: cfc5fbe1-52f6-43d0-9d7d-8dc87d72dd4f

5. 使用 ConfigMap 

5.1 使用 ConfigMap 来替代环境变量

① 定义环境变量

[root@master01 configmap]# vim env.yaml
apiVersion: v1
kind: ConfigMap         # 是一个ConfigMap对象
metadata: name: special-confignamespace: default
data:special.how: very     # 存储了一个名为"special.how"的键值对,其值为"very"special.type: good    # 存储了一个名为"special.type"的键值对,其值为"good"
---
apiVersion: v1
kind: ConfigMap
metadata:name: env-confignamespace: default
data:log_level: INFO       # 存储了一个名为"log_level"的键值对,其值为"INFO"

② 创建资源

[root@master01 configmap]# kubectl delete configmaps --all
# 删除之前创建的configmaps[root@master01 configmap]# kubectl create -f env.yaml
[root@master01 configmap]# kubectl get cm
NAME               DATA   AGE
env-config         1      24s
special-config     2      24s

③ 定义 Pod

[root@master01 configmap]# vim test-pod.yaml
apiVersion: v1
kind: Pod
metadata:name: test-pod
spec:containers:- name: busyboximage: busybox:1.28.4command: [ "/bin/sh", "-c", "env" ]env:                          # 容器内的环境变量- name: SPECIAL_HOW_KEY     # 定义了一个名为"SPECIAL_HOW_KEY"的环境变量 valueFrom:                # 指定了该环境变量的值来源configMapKeyRef:        # 表明该值来自ConfigMap的特定键name: special-config  # 指定了所使用的ConfigMap的名称key: special.how      # 指定了要使用的ConfigMap中的键- name: SPECIAL_TYPE_KEY    # 定义了另一个名为"SPECIAL_TYPE_KEY"的环境变量valueFrom:                # 指定了该环境变量的值来源configMapKeyRef:        # 表明该值来自ConfigMap的特定键name: special-config  # 指定了所使用的ConfigMap的名称key: special.type     # 指定了要使用的ConfigMap中的键envFrom:                      # 从ConfigMap中注入所有键值对作为环境变量- configMapRef:             # 指定了要引用的ConfigMap对象name: env-config        # 指定了要使用的ConfigMap的名称restartPolicy: Never            # 指定了Pod的重启策略为"Never"

④ 启动 Pod

[root@master01 configmap]# kubectl create -f test-pod.yam[root@master01 configmap]# kubectl get pod
test-pod                                  0/1     Completed     0          10s

⑤ 获取日志信息

[root@master01 configmap]# kubectl logs test-pod
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=test-pod
SHLVL=1
SPECIAL_HOW_KEY=very
# 赋值变量 SPECIAL_HOW_KEY 的值为 special-config 的 special.how: very
HOME=/root
NGINX_PORT_80_TCP=tcp://10.96.75.23:80
NGINX_SERVICE_PORT_80_TCP=tcp://10.96.193.207:80
SPECIAL_TYPE_KEY=good
# 赋值变量 SPECIAL_TYPE_KEY 的值为 special-config 的 special.type: good
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NGINX_SERVICE_HOST=10.96.75.23
NGINX_SERVICE_SERVICE_HOST=10.96.193.207
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
NGINX_DEPLOYMENT_PORT_30000_TCP_ADDR=10.96.101.251
NGINX_DEPLOYMENT_PORT_30000_TCP_PORT=30000
NGINX_DEPLOYMENT_PORT_30000_TCP_PROTO=tcp
NGINX_DEPLOYMENT_SERVICE_HOST=10.96.101.251
NGINX_PORT=tcp://10.96.75.23:80
NGINX_SERVICE_PORT=tcp://10.96.193.207:80
NGINX_SERVICE_SERVICE_PORT=80
log_level=INFO
# 引入 env-config 的变量 log_level: INFO
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/
NGINX_PORT_80_TCP_ADDR=10.96.75.23
NGINX_DEPLOYMENT_PORT=tcp://10.96.101.251:30000
NGINX_SERVICE_PORT_80_TCP_ADDR=10.96.193.207
NGINX_DEPLOYMENT_SERVICE_PORT=30000
NGINX_DEPLOYMENT_PORT_30000_TCP=tcp://10.96.101.251:30000
NGINX_PORT_80_TCP_PORT=80
NGINX_SERVICE_PORT_80_TCP_PORT=80
NGINX_PORT_80_TCP_PROTO=tcp
NGINX_SERVICE_PORT_80_TCP_PROTO=tcp

5.2 用 ConfigMap 设置命令行参数

① 定义环境变量的值

[root@master01 configmap]# vim test-pod2.yaml
apiVersion: v1
kind: Pod
metadata:name: test-pod2
spec:containers:- name: busyboximage: busybox:1.28.4command: - /bin/sh- -c- echo "$(SPECIAL_HOW_KEY) $(SPECIAL_TYPE_KEY)" 
# 在容器内部执行的命令。这个命令会打印环境变量"SPECIAL_HOW_KEY"和"SPECIAL_TYPE_KEY"的值env:                          # 设置了容器内的环境变量- name: SPECIAL_HOW_KEY     # 定义了一个名为"SPECIAL_HOW_KEY"的环境变量  valueFrom:                # 指定了该环境变量的值来源configMapKeyRef:        # 表明该值来自ConfigMap的特定键name: special-config  # 指定了所使用的ConfigMap的名称key: special.how      # 指定了要使用的ConfigMap中的键- name: SPECIAL_TYPE_KEY    # 定义了另一个名为"SPECIAL_TYPE_KEY"的环境变量valueFrom:                # 指定了该环境变量的值来源configMapKeyRef:        # 表明该值来自ConfigMap的特定键name: special-config  # 指定了所使用的ConfigMap的名称key: special.type     # 指定了要使用的ConfigMap中的键envFrom:                      # 从ConfigMap中注入所有键值对作为环境变量- configMapRef:             # 指定了要引用的ConfigMap对象name: env-config        # 指定了要使用的ConfigMap的名称restartPolicy: Never            # 指定了Pod的重启策略为"Never"

② 启动 Pod

kubectl create -f test-pod2.yaml

③ 查看 Pod 信息

kubectl get pods

④ 获取日志信息

kubectl logs test-pod2

5.3 通过数据卷插件使用 ConfigMap 

在数据卷里面使用 ConfigMap,就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容。创建一个名为 test-pod3 的 Pod,该 Pod 使用 ConfigMapspecial-config 作为数据卷,并将 ConfigMap 中的键值对作为文件内容挂载到容器的文件系统中。

① 定义 Pod

[root@master01 configmap]# vim test-pod3.yaml
apiVersion: v1
kind: Pod
metadata:name: test-pod3
spec:containers:- name: busyboximage: busybox:1.28.4command: [ "/bin/sh", "-c", "sleep 36000" ]  # 定义了容器的启动命令volumeMounts:             # 挂载路径到容器内部- name: config-volume     # 挂载的卷的名称mountPath: /etc/config  # 将卷挂载到容器内的路径 "/etc/config"volumes:                    # 定义了Pod中的卷- name: config-volume     # 卷的名称configMap:              # 表明这是一个ConfigMap类型的卷name: special-config  # 指定了要挂载的ConfigMap的名称restartPolicy: Never        # 指定了Pod的重启策略为"Never"

② 启动 Pod

[root@master01 configmap]# kubectl create -f test-pod3.yaml [root@master01 configmap]# kubectl get pods
test-pod3                                 1/1     Running       0          7s

③ 进入容器

[root@master01 configmap]# kubectl exec -it test-pod3 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # cd /etc/config/
/etc/config # ls
special.how   special.type
/etc/config # cat special.how
very/etc/config # 

示例:

① 准备挂载文件

[root@master01 configmap]# echo 123 > index.html

② 创建 configmap

[root@master01 configmap]# kubectl create configmap web123 --from-file=/opt/configmap/index.html[root@master01 configmap]# kubectl describe configmaps web123 
Name:         web123
Namespace:    default
Labels:       <none>
Annotations:  <none>Data
====
index.html:
----
123Events:  <none>

③ 数据挂载

[root@master01 configmap]# vim web123.yaml
apiVersion: v1
kind: Pod
metadata:name: web-nginx
spec:volumes:- name: cm-webconfigMap:name: web123containers:- name: web-nginximage: nginx:1.14ports:- containerPort: 80volumeMounts:- name: cm-webmountPath: /usr/share/nginx/html

④ 启动 Pod

[root@master01 configmap]# kubectl apply -f web123.yaml [root@master01 configmap]# kubectl get pod
[root@master01 configmap]# kubectl get pod -o wide
NAME                                      READY   STATUS        RESTARTS   AGE     IP             NODE     NOMINATED NODE   READINESS GATES
web-nginx                                 1/1     Running       0          61s     10.244.2.53    node02   <none>           <none>

⑤ 访问页面

[root@master01 configmap]# curl 10.244.2.53
123

5.4 ConfigMap 的热更新 

5.4.1 更新 ConfigMap 中的配置数据

ConfigMap 的热更新指的是在不重启 Pods 的情况下更新 ConfigMap 中的配置数据,从而实现应用程序的动态配置更新。这种热更新的方式可以帮助避免应用程序重启所带来的服务中断,提高了系统的可用性。

① 编辑名为"web123"的 ConfigMap 资源

[root@master01 configmap]# kubectl edit configmap web123 

② 访问页面

[root@master01 configmap]# curl 10.244.2.53
welcome
# 等大概10秒左右,使用该 ConfigMap 挂载的 Volume 中的数据同步更新 

③ 查询容器内、宿主机文件 

[root@master01 configmap]# kubectl exec -it web-nginx -- cat /usr/share/nginx/html/index.html
welcome
# 可以看到容器内的文件会修改
[root@master01 configmap]# cat index.html 
123
# 宿主机的文件未被修改,它只是将宿主机的文件当作键(文件名)值(文件内容),设置为configmap
# 而后通过挂载的方式,将键值同步到容器当中 ,只有修改configmap的键值,容器中的内容才会同步更新

可以将配置文件等信息,使用陈述式命令指定文件,创建ConfigMap,可以做到在宿主机上,实时更新 pod 的配置信息以及其它有关信息。

5.4.2 subPath 字段热更新

通过使用 subPath,可以实现对特定文件的热更新,而不影响 Pod 中其他文件或配置。

① 创建 ConfigMap,将当前目录下的 nginx.conf 文件作为配置数据导入到 ConfigMap 中

[root@master01 configmap]# kubectl create configmap nginx-config --from-file=nginx.conf [root@master01 configmap]# kubectl get cm
NAME               DATA   AGE
nginx-config       1      12s
web123             1      12h

② 定义两个 ConfigMap 来提供应用程序的配置和静态文件 

指定 Pod 中的容器,这里定义了一个名为 web-nginx 的容器,使用了 nginx:1.14 镜像。容器暴露了 80 端口,并且使用了之前定义的两个卷 cm-web 和 cm-nginx。其中 cm-web 卷被挂载到了容器的 /usr/share/nginx/html 目录,cm-nginx 卷被挂载到了容器的 /etc/nginx/nginx.conf 目录,并且使用了 subPath 字段来指定了 nginx.conf 文件的名称。

[root@master01 configmap]# vim web-nginx.yaml
apiVersion: v1
kind: Pod
metadata:name: web-nginx
spec:                      # 描述 Pod 的规范volumes:                 # 列出可以由此 Pod 中的容器挂载的卷- name: cm-web         # 定义名为 "cm-web" 的卷configMap:           # 指定此卷与 ConfigMap 相关联name: web123       # 将此卷与名为 "web123" 的 ConfigMap 关联- name: cm-nginx       # 定义另一个名为 "cm-nginx" 的卷configMap:           # 表明此卷与 ConfigMap 相关联name: nginx-config # 将此卷与名为 "nginx-config" 的 ConfigMap 关联containers:              # 列出构成此 Pod 的容器- name: web-nginx        # 将容器命名为 "web-nginx"image: nginx:1.14      # 指定要在此容器中使用的 Docker 镜像及其版本ports:                 # 列出此容器暴露的端口- containerPort: 80    # 指定此容器监听端口 80volumeMounts:          # 列出要挂载到此容器中的卷- name: cm-web         # 将 "cm-web" 卷挂载到容器中mountPath: /usr/share/nginx/html  # 指定要将卷挂载到容器中的路径- name: cm-nginx       # 将 "cm-nginx" 卷挂载到容器中mountPath: /etc/nginx/nginx.conf  # 指定要将卷挂载到容器中的路径subPath: nginx.conf  # 表示只应挂载 ConfigMap 中的 "nginx.conf" 文件

③ 启动 Pod

[root@master01 configmap]# kubectl apply -f web-nginx.yaml [root@master01 configmap]# kubectl get pod -o wide | grep web-nginx
web-nginx                                 1/1     Running     0          3s      10.244.1.208   node01   <none>           <none>

④ 访问页面

[root@master01 configmap]# curl 10.244.1.208
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.14.2</center>
</body>
</html>[root@master01 configmap]# kubectl edit cm nginx-config 
……location / {root   /usr/share/nginx/html;  # 修改web页面路径index  index.html index.htm;}
……[root@master01 configmap]# kubectl delete -f web-nginx.yaml 
pod "web-nginx" deleted
[root@master01 configmap]# kubectl apply -f web-nginx.yaml 
pod/web-nginx created
[root@master01 configmap]# kubectl get pod -o wide | grep web-nginx
web-nginx                                 1/1     Running     0          4s      10.244.1.209   node01   <none>           <none>
[root@master01 configmap]# curl 10.244.1.209
welcome

5.5 ConfigMap 更新后滚动更新 Pod

更新 ConfigMap 目前并不会触发相关 Pod 的滚动更新,可以通过在 .spec.template.metadata.annotations 中添加 version/config ,每次通过修改 version/config 来触发滚动更新。

① 定义 yaml

[root@master01 configmap]# vim nginx-d.yaml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginxname: nginx
spec:replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- image: nginx:1.14imagePullPolicy: IfNotPresentname: nginxports:- containerPort: 80protocol: TCPrestartPolicy: Always[root@master01 configmap]# kubectl apply -f nginx-d.yaml
[root@master01 configmap]# kubectl get pod -o wide | grep nginx
nginx-d9d8cf5c7-q8mmn                     1/1     Running     0          4s      10.244.1.216   node01   <none>           <none>[root@master01 configmap]# curl 10.244.1.216 -I
HTTP/1.1 200 OK
Server: nginx/1.14.2

② 修改 Deployment 对象的 Pod 模板

[root@master01 configmap]# kubectl patch deployment nginx --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "202406" }}}}}'
deployment.apps/nginx patchednginx-78dc66b55c-7sllr                    0/1     ContainerCreating   0          1s
nginx-78dc66b55c-7sllr                    1/1     Running             0          2s
nginx-d9d8cf5c7-q8mmn                     1/1     Terminating         0          2m30s

注意更新 ConfigMap 后:

  • 使用该 ConfigMap 挂载的 Env 不会同步更新;
  • 使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新。 

三、总结

1. Secret 四种类型

(1)Opaque:
通用类型(可以通过文件 目录、变量创建)默认类型
(2)kubernetes.io/service-account-token:
k8s自动创建的,给serviceaccount服务账号(pod在k8s集群内部的专属服务用户)访问APiserver使用
(3)kubernetes.io/dockerconfigjson:
给k8s从harbor私有镜像仓库取镜像认证使用的
(4)kubernetes.io/tls:
通过TLS证书来认证的(私有文件、秘钥)

2. 创建 Secret

(1)陈述式创建Secret,格式:
① kubectl cerate secret generic “secret名字” --from-file=“文件/指定文件名”
--from-file选项可以多次使用,也可以指定多个文件目录
② kubectl cerate secret generic “secret名字” --from-literal=“键值对(key-value)”
--from-literal选项可以引用一个键值对,也可以多次

(2)挂载的方式:
① volumes定义类型secret的存储卷
② volumeMounts把存储卷挂载到容器目录,secret资源数据中的键将以文件名的形式显示,值是文件内容

(3)容器环境变量的方式
① env定义容器的环境变量名
② valueFrom.configMapKeyRef.name指定这个secret资源数据的键名,从而确定引用那个键的值

搭载K8s时:
k8s从hatbor私有仓库拉取镜像的时使用imagePullsecret指定kubernetes.io/dockerconfigjson类型的secret来作为连接私有仓库的认证信息

3. 创建ConfigMap

kubectl create configmap “configmap名字” --from-file=文件/目录
                                         --from-literal=“键值对(key-value)引用

4. 查看资源中的数据是以明文的格式去显示key的值

kubectl descrbe cm “configmap名字”
kubectl get cm “configmap名字” -o yaml

5. ConfigMap使用

(1)容器环境变量的方式:
env 需要另外自定义环境变量名,通过cm资源名称和key名称来给这个变量赋值
envFrom 不需要另外自定义环境变量名,直接使用cm资源的key作为容器中的环境变量名,value作为这个环境变量的值

(2)挂载的方式:用的最多的方式
volumes 定义类型为configmap的存储卷
volumeMounts 把存储卷挂载到容器目录,cm资源数据中的键将以文件的形式显示,值为文件内容
如果把存储卷挂载到容器中的文件,subPath指定的文件nginx.conf(参考5.4.2 subPath 字段热更新)

6. ConfigMap 的热更新,有两种方式

CorfigMap挂载的Volume中的数据同步更新,你修改当中配置他会更新策略

(1)可以根据kubectl edit修改当中配置来去做热更新(在线更新)
{"apiversion": apps/vl", kind"."Deployment", ………………"name": "cm-nginx")]}}}}

(2)根据kubectl patch deployment “控制器名字” --patch 对应层级 和列表的方式 来完成热更新
示例:kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "202406" }}}}}'

相关文章:

配置Kubernetes资源管理Secret与ConfigMap

前言 Kubernetes 中的 Secret&#xff08;提供加密模式&#xff09;和 ConfigMap&#xff08;提供配置&#xff09;是关键的资源&#xff0c;用于安全地存储和管理敏感信息和配置数据。它们在应用程序开发和部署过程中扮演着重要的角色。本文将介绍如何有效地配置和管理这些资…...

韩顺平0基础学java——第19天

p396-406 final关键字 1.final修饰的为“常量”&#xff0c;需要给初始值。1可以直接定义时赋值&#xff0c;2在构造器中&#xff0c;3在代码块中。 注意静态代码块只能访问静态变量。 2.如果final修饰的关键字是静态的&#xff0c;那就不能在构造器中赋值&#xff0c;只能…...

嵌入式学习——Linux高级编程复习(目录IO、软硬连接、makefile)——day38

1. 目录IO 目录IO是指在计算机程序中对文件目录进行操作的一系列输入输出功能。这些操作允许程序创建、读取、修改和删除目录结构。在Unix/Linux系统和类似系统中&#xff0c;目录被视为特殊的文件&#xff0c;可以对其进行打开、读取、写入&#xff08;修改&#xff09;和关闭…...

makefile与进度条

Linux项目自动化构建工具-make/makefile make是一个命令&#xff0c; makefile是一个文件&#xff0c;保存依赖关系和依赖方法。‘ touch Makefile/makefile mybin:mytest.c//依赖关系 目标文件&#xff1a;依赖文件列表 文件列表的文件之间以空格分隔 gcc -o mybin mytest.…...

操作系统总结

进程和线程的区别 本质区别&#xff1a; 进程是资源调度以及分配的基本单位。线程是 CPU 调度的基本单位。 所属关系&#xff1a;一个线程属于一个进程&#xff0c;一个进程可以拥有多个线程。地址空间&#xff1a; 进程有独立的虚拟地址空间。线程没有独立的虚拟地址空间&…...

SpringCloud整合OpenFeign实现微服务间的通信

1. 前言 1.1 为什么要使用OpenFeign&#xff1f; 虽说RestTemplate 对HTTP封装后, 已经⽐直接使⽤HTTPClient简单⽅便很多, 但是还存在⼀些问题. 需要拼接URL, 灵活性⾼, 但是封装臃肿, URL复杂时, 容易出错. 代码可读性差, ⻛格不统⼀。 1.2 介绍一下微服务之间的通信方式 微…...

老师必备!一文教你如何高效收集志愿填报信息

高考志愿填报季&#xff0c;对于每一位老师来说&#xff0c;无疑是一场信息收集与管理的硬仗。如何在众多的志愿信息中&#xff0c;高效、准确地掌握每位学生的志愿意向&#xff1f; 高考志愿填报的重要性。不仅是学生人生的一个重要转折点&#xff0c;也是老师教育生涯中的一次…...

创建 MFC DLL-使用DEF文件

本文仅供学习交流&#xff0c;严禁用于商业用途&#xff0c;如本文涉及侵权请及时联系本人将于及时删除 利用“MFC动态链接库”项目模板可以创建MFC DLL。DLL文件与可执行文件非常相似&#xff0c;不同点在于DLL包含有导出表(Export Table)。导出表包含DLL中每个导出函数的名字…...

如何将HTTP升级成HTTPS?既简单又免费的方法!

在当今数字化时代&#xff0c;网络安全已成为用户和企业关注的焦点。HTTPS作为一种更加安全的网络通信协议&#xff0c;正逐渐取代传统的HTTP成为新的标准。对于许多网站管理员和内容创作者来说&#xff0c;如何免费升级到HTTPS是一个值得探讨的问题。本文将详细介绍一些免费的…...

数据仓库之核心模型与扩展模型分离

在数据仓库的设计和实施过程中&#xff0c;核心模型与扩展模型分离是一种常见的策略&#xff0c;旨在提高数据仓库的灵活性、可维护性和扩展性。这种分离策略的主要优势和实施方法如下&#xff1a; 核心模型&#xff08;Core Model&#xff09; 核心模型是数据仓库中稳定、共…...

大龄职场人的春招机遇:技术岗位主导,高薪与挑战并存

随着6月毕业季的临近&#xff0c;大批年轻人即将涌入人才市场&#xff0c;为职场注入新鲜血液。然而&#xff0c;这也意味着一些职场人可能面临被“优化”的风险。近几年&#xff0c;职场环境呈现出明显的年轻化趋势&#xff0c;企业更倾向于招聘具有创新活力的青年人才&#x…...

家宽动态公网IP,使用docker+ddns 实现动态域名解析

官方地址&#xff1a;https://github.com/jeessy2/ddns-go 安装docker docker pull jeessy/ddns-godocker run -d --name ddns-go --restartalways --nethost -v /opt/ddns-go:/root jeessy/ddns-go然后访问ip端口 配置时注意如下...

【力扣刷题 动态规划】LeetCode 139 单词拆分、LeetCode 300 最长递增子序列 ✌

文章目录 1. 单词拆分2. 最长递增子序列 1. 单词拆分 题目链接 &#x1f34e; 解题思路&#xff1a; class Solution {bool dp[310] {false};public:bool wordBreak(string s, vector<string>& wordDict) {unordered_set<string> myset;for(auto& str :…...

【位运算】【前缀和】个人练习-Leetcode-1177. Can Make Palindrome from Substring

题目链接&#xff1a;https://leetcode.cn/problems/can-make-palindrome-from-substring/description/ 题目大意&#xff1a;给出一个字符串s&#xff0c;每次query给出l, r, k&#xff0c;要求判断子串s[l:r1]在经过k次操作后是否能变为回文串。一次操作可以将子串内的一个字…...

最小相位系统

最小相位系统 1、传递函数 一个线性系统的响应。 比如一个RC低通滤波器&#xff1a; 交流分量在电容的充放电中被滤除掉&#xff0c;通过设置电容器的电容值&#xff0c;以及电阻值&#xff0c;能够控制这种滤除能力&#xff0c;这个参数为RC。 电容的电抗为 1 / j w C 1/j…...

css系列:进度条

前言 技术来源于需求&#xff0c;近期遇到了做语音的需求&#xff0c;有个调整语速和音量的进度条&#xff0c;UI组件库的进度条大部分不支持拖动和点击修改当前进度&#xff0c;所以自己手写了一个。 实现思路 MDN文档介绍 <input type"range"> - HTML&am…...

QT中为程序加入超级管理员权限

QT中为程序加入超级管理员权限 Chapter1 QT中为程序加入超级管理员权限1. mingw编译器2. MSVC编译器3. CMAKE Chapter2 如何给QT程序添加管理员权限(UAC)的几种方法1、Qt Creator中方案一&#xff1a;&#xff08;仅适用于使用msvc编译器&#xff09;方案二&#xff1a;&#x…...

共识算法之争(PBFT,Raft,PoW,PoS,DPoS)

文章目录 共识算法拜占庭容错技术&#xff08;Byzantine Fault Tolerance&#xff0c;BFT&#xff09;PBFT&#xff1a;Practical Byzantine Fault Tolerance&#xff0c;实用拜占庭容错算法Raft协议POW(Proof of Work)工作量证明机制POSDPoS&#xff08;Delegated Proof of St…...

抽象的java入门1.3.0

前言&#xff1a; 在1.2.0版本中我们介绍了public class hello {}并从中提取出两个新概 修饰符和作用域 public class hello {public static void main(String[] args) {System.out.println("Hello World");} } 正片&#xff1a; 这一期把剩余的内容刨析出来 pub…...

【Oracle生产运维】表空间可用性告警排查处理

1 前言 在生产环境中&#xff0c;一般设置表空间告警阈值是90%&#xff0c;在接到监控报警后&#xff0c;并不是需要立刻对表空间进行扩容。 决定是否扩容主要看表空间最近的增量是多少&#xff0c;假如剩余10%的空间还能支持1个月的增量&#xff0c;那就不需要急着扩容。如果…...

mac Network: use --host to expose

本地启动无法访问&#xff0c;这个不是权限问题是mac 主机端口安全策略&#xff0c;现在我们只需要开启端口自动检测就可以 npm run dev --host 网络&#xff1a;未暴露 方案一 1、执行 npm run dev -- --host 方案二 1、请在 vite.config.js server: {host: true } 1…...

ChatGPT-4o体验demo

OpenAI 最近推出了其最新的人工智能语言模型——GPT-4O。该模型是在原有 GPT-4 的基础上进行优化而成&#xff0c;旨在提升生成质量和响应速度。GPT-4O 采用了更加高效的架构设计&#xff0c;使其在处理复杂文本时表现出更快的速度和更高的准确性。GPT-4O 在训练过程中融入了最…...

FPGA SPI采集ADC7606数据

一,SPI总线的构成及信号类型 SPI总线只需四条线(如图1所示)就可以完成MCU与各种外围器件的通讯: 1)MOSI – Master数据输出,Slave数据输入 2)MISO – Master数据输入,Slave数据输出 3)SCK – 时钟信号,由Master产生 4)/CS – Slave使能信号,由Master控制。 在一个SPI时…...

html three.js 引入.stl模型示例

1.新建一个模块用于放置模型 <div id"chart_map" style"width:800px;height:500px"></div> 2. 引入代码根据需求更改 <!-- 在head或body标签内加入以下链接 --> <script src"https://cdn.jsdelivr.net/npm/three0.137/build/t…...

从零手写实现 nginx-11-文件处理逻辑与 range 范围查询合并

前言 大家好&#xff0c;我是老马。很高兴遇到你。 我们为 java 开发者实现了 java 版本的 nginx https://github.com/houbb/nginx4j 如果你想知道 servlet 如何处理的&#xff0c;可以参考我的另一个项目&#xff1a; 手写从零实现简易版 tomcat minicat 手写 nginx 系列 …...

Java算法-力扣leetcode-167. 两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 ****非递减顺序排列 ** &#xff0c;请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] &#xff0c;则 1 < index1 < index2 < n…...

实战 | YOLOv10 自定义数据集训练实现车牌检测 (数据集+训练+预测 保姆级教程)

导读 本文主要介绍如何使用YOLOv10在自定义数据集训练实现车牌检测 (数据集训练预测 保姆级教程)。 YOLOv10简介 YOLOv10是清华大学研究人员在Ultralytics Python包的基础上&#xff0c;引入了一种新的实时目标检测方法&#xff0c;解决了YOLO以前版本在后处理和模型架构方面…...

自定义类型:结构体+结构体内存对齐+结构体实现位段

结构体内存对齐实现位段 一.结构体1.结构体的声明2.结构体变量成员访问操作符3.结构体传参4.匿名结构体5.结构的自引用 二.结构体内存对齐1.对齐规则2.为什么存在内存对齐&#xff1f;3.修改默认对齐数 三.结构体实现位段1.什么是位段2.位段的内存分配3.位段的跨平台问题4.位段…...

0109__strip(1) command

strip(1) command_linux strip-CSDN博客...

英码科技推出鸿蒙边缘计算盒子:提升国产化水平,增强AI应用效能,保障数据安全

当前&#xff0c;随着国产化替代趋势的加强&#xff0c;鸿蒙系统Harmony OS也日趋成熟和完善&#xff0c;各行各业都在积极拥抱鸿蒙&#xff1b;那么&#xff0c;边缘计算要加快实现全面国产化&#xff0c;基于鸿蒙系统开发AI应用势在必行。 关于鸿蒙系统及其优势 鸿蒙系统是华…...

hao123手机浏览器/天天seo百度点击器

https://blog.csdn.net/xqhrs232/article/details/13507543 2种方法&#xff1a;1&#xff1a;在PCB中进入ECO模式-选择要更新的元件-点击change component图标-&#xff08;选中元件后&#xff09;右键点击元件-在弹出的菜单中选择libraby browse-勾选update part type from l…...

发布网站建设需求的经验/色盲测试图第六版及答案大全

离我上次写的随笔到现在一年了, 日子真的好快. 又一年了. 一看内我收获了什么呢? 好像没有...... 有的只是现在以一年来到底做了什么的遗憾...... 转载于:https://www.cnblogs.com/Jeffrey/archive/2007/05/12/744125.html...

wordpress 结构分析/简述网站内容如何优化

前段时间参加了邀请的《仙风道》内测。一天挑了 25 个问题。下边是一周之后《仙风道》第二次邀请我的时候&#xff0c;我写给他们的信。我在游戏界一年多了&#xff0c;我不想越陷越深…… 《仙风道》确实需要改进。不过按照一般的游戏开发周期&#xff0c;现在这个阶段已经不方…...

wordpress 帝国cmd/申请自媒体平台注册

计算机二级office题库视频链接百度网盘&#xff1a;https://pan.baidu.com/s/1y39KO4OENDUEbwASzh-RcQ 提取码&#xff1a;e9dg 计算机二级office&#xff0c;我考过两次&#xff0c;第二次过的小时候喜欢做ppt&#xff0c;对word、ppt有一定了解。前言&#xff1a;小编给大家准…...

设计中国第一架飞机/武汉seo外包平台

GPIO库函数介绍 重要函数 //1个初始化函数 void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); //2个读取输入电平函数 uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 作用:读取某个GPIO的输入电平,实际操作GPIOx_IDR寄存…...

建设中小企业网站/精准推广引流5000客源

2019独角兽企业重金招聘Python工程师标准>>> 开源IDE,LightTable&#xff0c;比较小巧&#xff0c;居然有种完爆我大Sublime的赶脚&#xff0c;可惜全是英文的&#xff0c;特此记录一些用法。 其实就是它自带的帮助文档&#xff0c;不过全是英文&#xff0c;全当练翻…...