网站建设费用无形资产如何摊销如何评价小米的网站建设

news/2025/10/7 0:00:06/文章来源:
网站建设费用无形资产如何摊销,如何评价小米的网站建设,网站的第二域名怎么用,霸州放心的网络建站DevOps前言上一篇文章 中#xff0c;我们介绍了 k8s 中的 Master、Node、Pod、Label、RC RS、Deployment、HPA VPA、DaemonSet 这些资源对象信息#xff0c;接下来我们继续介绍 k8s 中常用的资源对象。StatefulSet在 k8s 系统中#xff0c;Pod 的管理对象 RC、D…DevOps前言上一篇文章 中我们介绍了 k8s 中的 Master、Node、Pod、Label、RC RS、Deployment、HPA VPA、DaemonSet 这些资源对象信息接下来我们继续介绍 k8s 中常用的资源对象。StatefulSet在 k8s 系统中Pod 的管理对象 RC、Deployment、DaemonSet 和 Job CronJob 都是面向 “无状态” 的服务。但现实中有很多服务是 “有状态” 的特别是一些复杂的中间件集群环境例如下图列举这些产品等有状态服务列举这些应用集群之间有 4 个共同特点(1) 每个节点都有固定的身份ID通过这个ID集中的双方可以相互发现并通信。(2) 集群的规模是比较固定的集群规模不能随意变动。(3) 集群中的每个节点都是有状态的通常会持久化数据到永久存储中。(4) 如果磁盘损坏则集群里的某个节点无法正常运行集群功能受损。Deplovment/RS 的一个特殊变种如果通过 RC 或 Deployment 控制 Pod 副本数量来实现上述 “有状态” 的集群就会发现第 1 点是无法满足的因为 Pod 的名称是随机产生的Pod 的 IP 地址也是在运行期才确定且可能有变动的我们事先无法为每个 Pod 都确定唯一不变的 ID。另外为了能够在其他节点上恢复某个失败的节点这种集群中的 Pod 需要挂接某种共享存储为了解决该问题k8s 从 v1.4 版本开始引入了 PetSet 这个新的资源对象并且在 v1.5 版本时更名为 StatefulSetStatefulSet 从本质上来说可以看作 Deplovment/RS 的一个特殊变种它有如下特性StatefulSet 里的每个 Pod 都有稳定、唯一的网络标识可以用来发现集群内的其他成员。假设 StatefulSet 的名称为 pulsar那么第 1个 Pod 叫 pulsar-0第 2个叫 pulsar-1 以此类推。StatefulSet 控制的 Pod 副本的启停顺序是受控的操作第 n 个 Pod 时前 0 到 n-1 个 Pod 已经是运行且准备就绪的状态。StatefulSet 里的 Pod 采用稳定的持久化存储卷通过 PV 或 PVC 来实现删除 Pod 时默认不会删除与 StatefulSet  相关的存储卷为了保证数据的安全。StatefulSet 除了要与 PV 卷捆绑使用以存储 Pod 的状态数据还要与 Headless Service“无头服务” 配合使用即 在每个StatefulSet 定义中都要声明它属于哪个 Headless Service。HeadlessService 与普通 Service 的关键区别在于它没有 Cluster IPspec:clusterIP 表示为 None如果解析 Headless Service 的 DNS 域名则返回的是该 Service 对应的全部 Pod 的 Endpoint 列表。StatefulSet 在 Headless Service 的基础上又为 StatefulSet 控制的每个 Pod 实例都创建了一个 DNS 域名这个域名的格式为$ (podname).$(headless service name)关于 Headless Services 的更多信息请查看https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#headless-servicesStatefulSet 示例首先使用下面的示例创建一个 StatefulSet 。它创建了一个 Headless Service nginx 用来发布 StatefulSet web 中的 Pod 的 IP 地址。apiVersion: v1 kind: Service metadata:name: nginxlabels:app: nginx spec:ports:- port: 80name: webclusterIP: None  # 集群内部 IP 设置为 Noneselector:app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata:name: web spec:serviceName: nginxreplicas: 2selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: k8s.gcr.io/nginx-slim:0.8ports:- containerPort: 80name: webvolumeMounts:- name: wwwmountPath: /usr/share/nginx/htmlvolumeClaimTemplates:- metadata:name: wwwspec:accessModes: [ ReadWriteOnce ]resources:requests:storage: 1Gi监视 StatefulSet 的 Pod 的创建情况kubectl get pods -w -l appnginx查看  nginx 的 Servicekubectl get service nginx ... NAME      TYPE         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE nginx     ClusterIP    None         none        80/TCP    12s查看 StatefulSet 的 webkubectl get statefulset web ... NAME      DESIRED   CURRENT   AGE web       2         1         20s对于一个拥有 n 个副本的 StatefulSetPod 被部署时是按照 “0 — (n-1)” 的序号顺序创建的。在第一个终端中使用 kubectl get 检查输出。查看 StatefulSet 创建的 Pod 的顺序信息kubectl get pods -w -l appnginx ... NAME      READY     STATUS    RESTARTS   AGE web-0     0/1       Pending   0          0s web-0     0/1       Pending   0         0s web-0     0/1       ContainerCreating   0         0s web-0     1/1       Running   0         19s web-1     0/1       Pending   0         0s web-1     0/1       Pending   0         0s web-1     0/1       ContainerCreating   0         0s web-1     1/1       Running   0         18s请注意直到 web-0 Pod 处于 Running请参阅 Pod 阶段 并 Ready请参阅 Pod 状况中的 type状态后web-1 Pod 才会被启动。再比如一个 6 节点的 Pulsar 的 StatefulSet 集群Pulsar 集群组成如下对应的 Headless Service 的名称为 pulsarStatefulSet 的名称为 pulsar则 StatefulSet 里的 6 个 Pod 的 DNS 名称分别为 pulsar-0.pulsar、pulsar-1.pulsar、pulsar-2.pulsar、pulsar-3.pulsar、pulsar-4.pulsar、pulsar-5.pulsar这些 DNS 名称可以直接在集群的配置文件中固定下来。Pulsar 集群组成搭建 Pulsar 集群至少需要 3 个组件ZooKeeper 集群、BookKeeper 集群和 broker 集群Broker 是 Pulsar 的自身实例)。这三个集群组件如下ZooKeeper 集群3(或多) 个 ZooKeeper 节点组成。bookie 集群也称为 BookKeeper 集群3(或多) 个 BookKeeper 节点组成。broker 集群3(或多) 个 Pulsar 节点组成。按照 Pulsar 组件的组成部分可以形成 3 种集群部署方案每个组件独立集群环境部署生产环境推荐至少需要 9 台宿主机。所有组件部署一个集群环境开发环境推荐至少需要 3 台宿主机。组件分组部署ZooKeeper 组件独立部署一个集群环境bookie broker 组件部署在另一个集群环境至少需要 6 台宿主机。官方建议 6 台机器部署 Pulsar 集群环境我们此处采用方案 3部署规划如下3 台用于运行 Zookeeper 集群建议使用性能较弱的机器Pulsar 仅将 Zookeeper 用于与协调有关的定期任务和与配置有关的任务而不用于基本操作。3 台用于运行 bookie 集群和 broker 集群建议使用性能强劲的机器。关于 StatefulSet 更多信息请查看https://kubernetes.io/zh-cn/docs/tutorials/stateful-application/basic-stateful-set/ServiceService 简介Service 将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法Service 也是 k8s 里的 核心资源 对象之一。使用 k8s你无需修改应用程序即可使用 Service 的服务发现机制。k8s 为 Pod 提供自己的 IP 地址并为一组 Pod 提供相同的 DNS 名 并且可以在 Pod 之间进行负载均衡。k8s 里的每个 Service 其实就是 微服务架构Microservice Architecture中的一个微服务上篇文章中讲解的 Pod、RS、Deployment 等资源对象其实都是为 k8s Service 做铺垫的。pod-service从上图中可以看到在 k8s 的 Service 定义了一个服务的访问入口地址前端应用 Frontend (Pod) 通过该入口地址访问其背后的一组由 Pod 副本组成的集群实例Service 与其后端 Pod 副本集群之间则是通过 Label Selector 来实现无缝对接的。RC、RS、Deployment 的作用实际上是保证 Service 的服务能力和服务质量始终符合预期标准。注意上一篇文章我们讲到 RC 被 RS 替代RC 的功能逐步由 RS Deployment 替换实际上 Deployment 底层调用了 RS 。k8s 提供的微服务网格架构通过分析、识别并建模系统中的所有服务为微服务 —— Kubernees Service我们的 系终由多个提供不同业务能力而又彼此独立的微服务单元组以的服分之间通过 TCP/IP 进行通信从而形成了强大而又灵活的 弹性网格拥有强大的 分布式能刀、弹性扩展能力、容错能力程序架构也变得简单和直观许多如下图所示上图中每个 Pod 都会被分配一个单独的 IP 地址而且每个 Pod 都提供了一个独立 Endpoint ( Pod IP ContainerPort 以被客户端访问。现在多个 Pod 副本组成了一个集群来提供服务那么 客户端 Client 如何来访问它们呢?负载均衡与服务发现一般的做法是部署一个负载衡器软件或硬件)为这组 Pod 开启一个对外的服务端口如 8000 端口并且将这些 Pod 的 Endpoint 列表加入 8000 端口的转发列表客户端就可以通过负载均衡器的对外 IP 地址 服务端口来访问此服务。客户端的请求最后会被转发到哪个 Pod由 负载均衡器的算法 所决定。k8s 也遵循上述常规做法运行在每个 Node 上的 kube-proxy 进程其实就是一个智能的软件负载均衡器负责把对 Service 的请求转发到后端的某个 Pod 实例上并在内部实现服务的负载均衡与会话保持机制。但 k8s 发明了一种很巧妙又影响深远的设计Service 没有共用一个负载均衡器的 IP 地址每个 Service 都被分配了一个全局唯一的虚拟 IP ClusterIP 地址这样一来每个服务就变成了具备唯一 IP 地址的通信节点服务调用就变成了最基础的 TCP 网络通信问题。我们知道Pod 的 Endpoint 地址会随着 Pod 的销毁和重建而发生改变因为新 Pod 的 IP 地址与之前旧 Pod 的不同。而 Service 一旦被创建k8s 就会自动为它配一个可用的 Cluster IP而且在 Service 的整个生命周期内它的 Cluster IP 是不会随之发生改变的。这样一来服务发现 这个棘手的问题在 k8s 的架构里也得以轻松解决只要 用 Service 的 Name 与 Service 的 Cluster IP 地址做一个 DNS 域名映射 即可完美解决问题。Service 示例Service 在 k8s 中是一个 REST 对象和 Pod 类似。像所有的 REST 对象一样Service 定义可以基于 POST 方式请求 API server 创建新的实例。Service 对象的名称必须是合法的 RFC 1035 标签名称。通过上面基本概念的介绍接下来我们看下 Service 定义的 yaml 文件apiVersion: v1 kind: Service metadata:name: my-redis-servicelabel: name: my-redis-service spec:selector:app: my-redisports:- protocol: TCPport: 6379targetPort: 30001上述 yaml 配置文件创建一个名称为 my-redis-service 的 Service 对象它会将请求代理到使用 TCP 端口 30001并且具有标签 appmy-redis 的 Pod 上。k8s 为该 Service 服务分配一个 IP 地址 “ClusterIP/集群 IP”该 IP 地址由 服务代理kube-proxy 使用。(请参见下面的 VIP/虚拟IP 和 Service 代理 )。服务选择符Label Selector 的控制器不断扫描与其选择算符匹配的 Pod然后将所有更新发布到也称为 “my-redis-service” 的 Endpoint 对象。说明需要注意的是Service 能够将一个接收 port 映射到任意的 targetPort。默认情况下targetPort 将被设置为与 port 字段相同的值。虚拟 IP 和 Service 代理在 k8s 集群中每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP虚拟 IP的形式。为什么不使用 DNS 轮询或许有人会问到为什么 k8s 要依赖代理将入站流量转发到后端。是否有其他方法呢例如是否可以配置具有多个 A 值或 IPv6 为 AAAA的 DNS 记录并依靠轮询名称解析答案是当然不可以使用服务代理有以下几个原因DNS 实现的历史由来已久它不遵守记录 TTL并且在名称查找结果到期后对其进行缓存。有些应用程序仅执行一次 DNS 查找并无限期地缓存结果。即使应用和库进行了适当的重新解析DNS 记录上的 TTL 值低或为零也可能会给 DNS 带来高负载从而使管理变得困难。k8s 支持以下三种代理模式userspace 代理模式。iptables 代理模式。IPVS 代理模式。userspace 代理模式这种模式kube-proxy 会监视 k8s 控制平面对 Service 对象和 Endpoints 对象的添加和移除操作。对每个 Service它会在本地 Node 上打开一个端口随机选择。任何连接到 “代理端口” 的请求都会被代理到 Service 的后端 Pods 中的某个上面如 Endpoints 所报告的一样。使用哪个后端 Pod是 kube-proxy 基于 SessionAffinity 来确定的。最后它配置 iptables 规则捕获到达该 Service 的 clusterIP是虚拟 IP 和 Port 的请求并重定向到代理端口代理端口再代理请求到后端 Pod。默认情况下用户空间模式下的 kube-proxy 通过轮转算法选择后端。userspace 代理模式iptables 代理模式这种模式kube-proxy 会监视 k8s 控制节点对 Service 对象和 Endpoints 对象的添加和移除。对每个 Service它会配置 iptables 规则从而捕获到达该 Service 的 clusterIP 和端口的请求进而将请求重定向到 Service 的一组后端中的某个 Pod 上面。对于每个 Endpoints 对象它也会配置 iptables 规则这个规则会选择一个后端组合。默认的策略是kube-proxy 在 iptables 模式下随机选择一个后端。使用 iptables 处理流量具有较低的系统开销因为流量由 Linux netfilter 处理 而 无需在用户空间和内核空间之间切换。这种方法也可能更可靠。如果 kube-proxy 在 iptables 模式下运行并且 所选的第一个 Pod 没有响应则连接失败。这与 用户空间模式 不同在这种情况下kube-proxy 将检测到与第一个 Pod 的连接已失败 并会自动使用其他后端 Pod 重试。你可以使用 Pod 就绪探测器 验证后端 Pod 可以正常工作以便 iptables 模式下的 kube-proxy 仅看到测试正常的后端。这样做意味着你 避免将流量通过 kube-proxy 发送到已知已失败的 Pod。iptables 代理模式IPVS 代理模式特性状态Kubernetes v1.11 [stable]在 ipvs 模式下kube-proxy 监视 Kubernetes 服务和端点调用 netlink 接口相应地创建 IPVS 规则 并定期将 IPVS 规则与 k8s 服务和端点同步。该控制循环可确保 IPVS 状态与所需 状态匹配。访问服务时IPVS 将流量定向到后端 Pod 之一。IPVS 代理模式基于类似于 iptables 模式的 netfilter 挂钩函数 但是 使用哈希表作为基础数据结构并且在内核空间中工作。这意味着与 iptables 模式下的 kube-proxy 相比IPVS 模式下的 kube-proxy 重定向通信的延迟要短并且在同步代理规则时具有更好的性能。与其他代理模式相比IPVS 模式还支持更高的网络流量吞吐量。IPVS 提供了更多选项来平衡后端 Pod 的流量。这些是rr轮替Round-Robinlc最少链接Least Connection即打开链接数量最少者优先dh目标地址哈希Destination Hashingsh源地址哈希Source Hashingsed最短预期延迟Shortest Expected Delaynq从不排队Never Queue说明要在 IPVS 模式下运行 kube-proxy必须在启动 kube-proxy 之前使 IPVS 在节点上可用。当 kube-proxy 以 IPVS 代理模式启动时它将验证 IPVS 内核模块是否可用。如果未检测到 IPVS 内核模块则 kube-proxy 将退回到以 iptables 代理模式运行。IPVS 代理模式在这些代理模型中绑定到服务 IP 的流量在客户端不了解 k8s 或 Service 服务或 Pod 的任何信息的情况下将 Port 代理到适当的后端。如果要 确保每次都将来自特定 Client 客户端的连接传递到同一 Pod 则可以通过将 service.spec.sessionAffinity 设置为 ClientIP 默认值是 None来基于 Client 客户端的 IP 地址选择会话亲和性。你还可以通过适当设置 service.spec.sessionAffinityConfig.clientIP.timeoutSeconds 来设置最大会话停留时间。默认值为 10800 秒即 3 小时。说明在 Windows 上不支持为服务设置最大会话停留时间。k8s 中 Service 端口的区分在 k8s 中的 Service 存在以下这些端口他们各自的职责如下nodePort外部流量访问 k8s 集群中 service 入口的一种方式另一种方式是 LoadBalancer即nodeIP:nodePort 是提供给外部流量访问 k8s 集群中 service 的入口。portk8s 集群内部服务之间访问 service 的入口。即 clusterIP:port 是 service 暴露在 clusterIP 上的端口。targetPort容器暴露EXPOSE的端口最终的流量端口即具体业务进程在容器内的 targetPort 上提供 TCP/IP 接入。targetPort 是 pod 上的端口从 port 和nodePort 上来的流量经过 kube-proxy 流入到后端 pod 的 targetPort 上最后进入容器。制作容器时暴露的端口一致使用DockerFile中的EXPOSEhostPort这是一种直接定义 Pod 网络的方式。hostPort 是直接将容器的端口与所调度的节点上的端口路由这样用户就可以通过宿主机的 IP 加上来访问 Pod 了。说明hostPort 有个缺点因为 Pod 重新调度的时候该Pod被调度到的宿主机可能会变动这样就变化了用户必须自己维护一个Pod与所在宿主机的对应关系。使用了 hostPort 的容器只能调度到端口不冲突的 Node 上除非有必要比如运行一些系统级的 daemon 服务不建议使用端口映射功能。如果需要对外暴露服务建议使用 NodePort Service。如果未指定 targetPort默认情况下 targetPort 与 port 相同。Service 多端口问题很多服务都存在多端口的问题通常一个端口提供业务服务另外一个端口提供管理服务比如MyCat基于 java 语言编写的数据库中间件类比数据库代理、Codis分布式 Redis 解决方案、RabbitMQRabbitMQ Management 等常见中间件。k8s 的 Service 支持多个 Endpoint 有多个 Endpoint 存在的情况下要求每个 Endpoint 都定义一个名称来区分。举个例子下面以 Tomcat 多端口的 Service 定义为例apiVersion: v1 kind: Service metadata:name: tomcat-service spec:selector:app: MyTomcatports:- name: http-service-portprotocol: TCPport: 8080targetPort: 8080- name: https-service-portprotocol: TCPport: 443targetPort: 9100- name: shutdown-portprotocol: TCPport: 8005targetPort: 8005Service 存在多端口时为什么要给每个端口都命名其实这个问题涉及到上面我们提到的 服务发现机制在 k8s 中每个 Service 都分配有一个唯一的 Cluster IP只需 Service 的 Name 与 Service 的 Cluster IP 地址做一个 DNS 域名映射这样就可以通过 Service 的名称 Name 找到对应的 Cluster IP。外部系统访问 Service为了更深入地理解和掌握 k8s我们需要弄明白 k8s 的 3 种 IP这 3 种 IP 分别如下。Node IPNode 的 IP 地址。Pod IPPod 的 IP 地址。Cluster IPScrvice 的 IP 地址。首先Node IP 是 k8s 集群中每个节点的物理网卡的 IP 地址是一个真实存在的物理网络所有属于这个网络的服务器都能通过这个网络直接通信不管其中是否有部分节点不属于这个 k8s 集群。这也表明在 k8s 集群之外的节点访问 k8s 集群之内的某个节点或者 TCP/IP 服务时都必须通过 Node IP 通信。其次Pod IP 是每个 Pod 的 IP 地址它是 Docker Engine 根据 docker0 网桥的 IP 地址段进行分配的通常是一个虚拟的二层网络前面说过 k8s 要求位于不同 Node 上的 Pod 都能够彼此直接通信所以 k8s 里一个 Pod 里的容器访问另外一个 Pod 里的容器时就是通过 Pod IP 所在的虚拟二层网络进行通信的而真实的 TCP/IP 流量是通过 Node IP 所在的物理网卡流出的。最后说说 Service 的 Cluster IP它也是一种虚拟的IP但更像一个 “伪造” 的 IP 网络原因有以下几点Cluster IP 仅仅作用于 k8s Service 这个对象并由 k8s 管理和分配 IP 地址来源于 Cluster IP 地址池)。Cluster IP 无法被 Ping因为没有一个 “实体网络对象” 来响应。Cluster IP 只能结合 Service Port 组成一个具体的通信口单独的 Cluster IP 不具备 TCP/IP 通信的基础并且它们属于 k8s 集群这样一个封闭的空间集群外的节点如果要访问这个通信端口则需要做一些额外的工作。在 k8s 集群内 Node IP、Pod IP 与 Culster IP 网络之间的通信采用的是 k8s 自己设计的一种编程方式的特殊路由规则与我们熟知的 TCP/IP 路由有很大的不同。根据上面的分析和总结我们基本明白了Service 的 Culster IP 属于 k8s 集群内部的地址无法在集群外部直接使用这个地址。那么问题来了实际上在我们开发的业务系统中肯定或多或少的有一部分服务是要提供给 k8s 集群外部的应用程序或者用户来访问使用的。典型的例子就是 web 端的服务模块上面我们定义 tomcat-service 的例子怎么提供给外部用户访问呢采用 NodePort 是解决上述问题的最直接有效的常见做法修改如下apiVersion: v1 kind: Service metadata:name: tomcat-service spec:# 提供集群外部用户访问type: NodePortselector:app: MyTomcatports:- name: http-service-portprotocol: TCPport: 8080targetPort: 8080nodePort: 30002selector:tier: frontend接下来浏览器访问地址为http://nodePort IP:30002/就可以看到 Tomcat 的欢迎界面了。NodePort 的实现方式是在 k8s 集群里的每个 Node 上都为需要外部访问的 Service 开启一个对应的 TCP 监听端口外部系统只要用任意一个 Node IP   具体的 NodePort 即可访问此服务在任意 Node 上运行 netstat 命令就可以看到有 NodePort 被监听netstart -tlp | grep 30002 ... tcp6  0  0  [::]:30002         [::]:*          LISTEN     1125/kube-proxy但 NodePort 并没有完全解决外部访问 Service 的问题比如负载均衡访问。此时通常需要引入 Load balancerLB组件它独立于 k8s 集群之外通常有硬件或软件的负载均衡器比如硬件的有 F5软件的有 HAProxy 或者 Nginxingress-nginx访问结构如下所示NodePort-LoadBalancer谷歌共有云 GCE 上只要把 Service 的 typeNodePort 修改为 typeLoadBalancerk8s 就会自动创建一个对应的 LB 实例并返回它的 IP 地址供给外部客户端使用。其他公有云上面实现了此特性也可以实现上述功能。另外裸机上面的类似机制Bare Metal Service Load Balancers也在被开发。关于 Service 的更多信息请查看https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/Job CronJob批处理任务通常并行或者串行启动多个计算进程去处理一批工作项 (work item)在处理完成后整个批处理任务结束。从 k8s v1.2 版本开始支持批处理类型的应用我们可以通过 k8s Job 这种新的资源对象定义并启动一个批处理任务 Job。与 RC、Deployment、ReplicaSet、DaemonSet 类似Job 也控制一组 Pod 容器。从这个角度来看Job 也是一种特殊的 Pod 副本自动控制器同时 Job 控制 Pod 副本与 RC 等控制器的工作机制有以下重要差别。(1) Job 所控制的 Pod 副本是短暂运行的可以将其视为一组 Docker 容器其中的每个 Docker 容器都仅仅运行一次。当 Job 控制的所有 Pod 副本都运行结束时对应的 Job 也就结束了。Job 在实现方式上与 RC 等副本控制器不同Job 生成的 Pod 副本是不能自动重启的对应 Pod 副本的 RestartPoliy 都被设置为 Never。因此当对应的 Pod 副本都执行完成时相应的 Job 也就完成了控制使命即 Job 生成的 Pod 在 k8s 中是短暂存在的。k8s v1.5版本之后又提供了类似 crontabLinux crontab 是用来定期执行程序的命令的定时任务——CronJob解决了某些批处理任务需要定时周期性重复执行的问题。(2) Job 所控制的 Pod 副本的工作模式能够多实例并行计算以 TensorFlow 框架为例可以将一个机器学习的计算任务分布到 10 台机器上在每台机器上都运行一个 worker 执行计算任务这很适合通过 Job 生成 10 个Pod 副本同时启动运算。运行示例 Job下面是一个 Job 配置示例。它负责计算 π 到小数点后 2000 位并将结果打印出来。此计算大约需要 10 秒钟完成。apiVersion: batch/v1 kind: Job metadata:name: pi spec:template:spec:containers:- name: piimage: perl:5.34.0command: [perl,  -Mbignumbpi, -wle, print bpi(2000)]restartPolicy: NeverbackoffLimit: 4检查 Job 的状态kubectl describe jobs/pi关于 Job 的更多信息请查看https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/job/运行示例 CronJobCronJob 用于执行周期性的动作按某种排期表Schedule运行 Job单个任务或多个并行任务例如备份、报告生成、数据同步等。这些任务中的每一个都应该配置为周期性重复的例如每天/每周/每月一次你可以定义任务开始执行的时间间隔。下面的 CronJob 示例清单会在每分钟打印出当前时间和问候消息apiVersion: batch/v1 kind: CronJob metadata:name: hello spec:schedule: * * * * *jobTemplate:spec:template:spec:containers:- name: helloimage: busybox:1.28imagePullPolicy: IfNotPresentcommand:- /bin/sh- -c- date; echo Hello from the Kubernetes clusterrestartPolicy: OnFailureCron 时间表语法# ┌───────────── 分钟 (0 - 59) # │ ┌───────────── 小时 (0 - 23) # │ │ ┌───────────── 月的某天 (1 - 31) # │ │ │ ┌───────────── 月份 (1 - 12) # │ │ │ │ ┌───────────── 周的某天 (0 - 6)周日到周一在某些系统上7 也是星期日 # │ │ │ │ │                          或者是 sunmontuewebthufrisat # │ │ │ │ │ # │ │ │ │ │ # * * * * *输入描述相当于yearly (or annually)每年 1 月 1 日的午夜运行一次0 0 1 1 *monthly每月第一天的午夜运行一次0 0 1 * *weekly每周的周日午夜运行一次0 0 * * 0daily (or midnight)每天午夜运行一次0 0 * * *hourly每小时的开始一次0 * * * *要生成 CronJob 时间表表达式你还可以使用 crontab.guru 之类的 Web 工具。crontab.guru 》https://crontab.guru/crontab guru关于 CronJob 更多信息请查看https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/cron-jobs/VolumeContainer 中的文件在磁盘上是临时存放的这给 Container 中运行的较重要的应用程序带来一些问题。当容器崩溃时文件丢失。kubelet 会重新启动容器但容器会以干净的状态重启。在同一 Pod 中运行 多个容器并共享文件时出现。k8s 卷Volume 这一抽象概念能够解决这两个问题。k8s 支持很多类型的卷。Pod 可以同时使用任意数目的卷类型。临时卷类型的生命周期与 Pod 相同但持久卷可以比 Pod 的存活期长。当 Pod 不再存在时k8s 也会销毁临时卷不过 k8s 不会销毁持久卷。对于给定 Pod 中任何类型的卷在容器重启期间数据不会丢失。卷的核心是一个目录其中可能存有数据Pod 中的容器可以访问该目录中的数据。所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。k8s 中支持的 Volume 类型很多例如glusterfs、cephfs、configMap、emptyDir、hostPath、local、nfs、persistentVolumeClaim/PersistentVolume、portworxVolume、secret 等。如何使用 VolumeVolume 的使用也比较简单在大多数情况下我们 先在 Pod 上声明一个 Volume然后在容器里引用该Volume 并挂载(Mount到容器里的某个目录上。emptyDir当 Pod 分派到某个 Node 上时emptyDir 卷会被创建并且在 Pod 在该节点上运行期间卷一直存在。就像其名称表示的那样卷最初是空的。尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同这些容器都可以读写 emptyDir 卷中相同的文件。当 Pod 因为某些原因被从节点上删除时emptyDir 卷中的数据也会被永久删除。emptyDir 卷也称为 “临时卷”。说明容器崩溃并不会导致 Pod 被从节点上移除因此容器崩溃期间 emptyDir 卷中的数据是安全的。emptyDir 的一些用途缓存空间例如基于磁盘的归并排序。为耗时较长的计算任务提供检查点以便任务能方便地从崩溃前状态恢复执行。在 Web 服务器容器服务数据时保存内容管理器容器获取的文件。取决于你的环境emptyDir 卷存储在该节点所使用的介质上这里的介质可以是磁盘或 SSD 或网络存储。但是你可以将 emptyDir.medium 字段设置为 Memory以告诉 k8s 为你挂载 tmpfs基于 RAM 的文件系统。虽然 tmpfs 速度非常快但是要注意它与磁盘不同。tmpfs 在节点重启时会被清除并且你所写入的所有文件都会计入容器的内存消耗受容器内存限制约束。说明当启用 SizeMemoryBackedVolumes 特性门控 时你可以为基于内存提供的卷指定大小。如果未指定大小则基于内存的卷的大小为 Linux 主机上内存的 50。emptyDir 配置示例apiVersion: v1 kind: Pod metadata:name: test-pd spec:containers:- image: k8s.gcr.io/test-webservername: test-containervolumeMounts:- mountPath: /cachename: cache-volumevolumes:- name: cache-volumeemptyDir: {}hostPath警告HostPath 卷存在许多安全风险最佳做法是尽可能避免使用 HostPath。当必须使用 HostPath 卷时它的范围应仅限于所需的文件或目录并以只读方式挂载。如果通过 AdmissionPolicy 限制 HostPath 对特定目录的访问则必须要求 volumeMounts 使用 readOnly 挂载以使策略生效。hostPath 卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中。虽然这不是大多数 Pod 需要的但是它为一些应用程序提供了强大的逃生舱。例如hostPath 的一些用法有运行一个需要访问 Docker 内部机制的容器可使用 hostPath 挂载 /var/lib/docker 路径。在容器中运行 cAdvisor 时以 hostPath 方式挂载 /sys。允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在是否应该创建以及应该以什么方式存在。除了必需的 path 属性之外你可以选择性地为 hostPath 卷指定 type。支持的 type 值如下取值行为空字符串默认用于向后兼容这意味着在安装 hostPath 卷之前不会执行任何检查。DirectoryOrCreate如果在给定路径上什么都不存在那么将根据需要创建空目录权限设置为 0755具有与 kubelet 相同的组和属主信息。Directory在给定路径上必须存在的目录。FileOrCreate如果在给定路径上什么都不存在那么将在那里根据需要创建空文件权限设置为 0644具有与 kubelet 相同的组和所有权。File在给定路径上必须存在的文件。Socket在给定路径上必须存在的 UNIX 套接字。CharDevice在给定路径上必须存在的字符设备。BlockDevice在给定路径上必须存在的块设备。当使用这种类型的卷时要小心因为HostPath 卷可能会暴露特权系统凭据例如 Kubelet或特权 API例如容器运行时套接字可用于容器逃逸或攻击集群的其他部分。具有相同配置例如基于同一 PodTemplate 创建的多个 Pod 会由于节点上文件的不同而在不同节点上有不同的行为。下层主机上创建的文件或目录只能由 root 用户写入。你需要在 特权容器中以 root 身份运行进程或者修改主机上的文件权限以便容器能够写入 hostPath 卷。hostPath 配置示例apiVersion: v1 kind: Pod metadata:name: test-pd spec:containers:- image: k8s.gcr.io/test-webservername: test-containervolumeMounts:- mountPath: /test-pdname: test-volumevolumes:- name: test-volumehostPath:# 宿主上目录位置path: /data# 此字段为可选支持的 type 如上表type: Directorynfsnfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。不像 emptyDir 那样会在删除 Pod 的同时也会被删除nfs 卷的内容在删除 Pod 时会被保存卷只是被卸载。这意味着 nfs 卷可以被预先填充数据并且这些数据可以在 Pod 之间共享。注意在使用 NFS 卷之前你必须运行自己的 NFS 服务器并将目标 share 导出备用。nfs 配置示例使用 NFS 网络文件系统 的共享目录存储数据时我们需要在系统中需要在系统中部署一个 NFS Server。定义 NFS 类型的 Volume 示例如下volumes:- name: nfsnfs:# 修改为你部署的 NFS Server 地址server: nfs-server.localhostpath: /dataportworxVolumeportworxVolume 是一个 可伸缩的块存储层能够以 超融合hyperconverged 的方式与 k8s 一起运行。Portworx 支持对服务器上存储的指纹处理、基于存储能力进行分层以及跨多个服务器整合存储容量。Portworx 可以以 in-guest 方式在虚拟机中运行也可以在裸金属 Linux 节点上运行。Portworx Volume 配置示例portworxVolume 类型的卷可以通过 k8s 动态创建也可以预先配备并在 Pod 内引用。下面是一个引用预先配备的 portworxVolume 的示例 PodapiVersion: v1 kind: Pod metadata:name: test-portworx-volume-pod spec:containers:- image: k8s.gcr.io/test-webservername: test-containervolumeMounts:- mountPath: /mntname: pxvolvolumes:- name: pxvol# 此 Portworx 卷必须已经存在portworxVolume:volumeID: pxvolfsType: fs-type说明在 Pod 中使用 portworxVolume 之前你要确保有一个名为 pxvol 的 PortworxVolume 存在。关于 Volume 的更多信息请查看https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/PV PVC之前提到的 Volume 是被定义在 Pod 上的属于计算资源的一部分而实际上网络存储是相对独立于计算资源而存在的一种实体资源。比如在使用虚拟机VM的情况下我们通常会先定义一个网络存储然后从中划出一个 “网盘” 并挂接到虚拟机上。Persistent Volume(PV) 和与之相关联的 Persistent Volume Claim(PVC) 也起到了类似的作用。存储的管理是一个与计算实例的管理完全不同的问题。持久卷PersistentVolumePV  是集群中的一块存储可以由管理员事先制备 或者使用存储类Storage Class来动态制备。持久卷是集群资源就像节点也是集群资源一样。持久卷申领PersistentVolumeClaimPVC  表达的是用户对存储的请求。概念上与 Pod 类似Pod 会耗用节点资源而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源CPU 和 Memory 内存同样 PVC 申领也可以请求特定的大小和访问模式 例如可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载。尽管 PersistentVolumeClaim 允许用户消耗抽象的存储资源 常见的情况是针对不同的问题用户需要的是具有不同属性如性能的 PersistentVolume 卷。集群管理员需要能够提供不同性质的 PersistentVolume 并且这些 PV 卷之间的差别不仅限于卷大小和访问模式同时又不能将卷是如何实现的这些细节暴露给用户。为了满足这类需求就有了 存储类StorageClass 资源。PV 可以被理解成 k8s 集群中的某个网络存储对应的一块存储它与 Volume 类似但有以下区别。PV 只能是网络存储不属于任何 Node但可以在每个 Node 上访问。PV 并不是被定义在 Pod 上的而是独立于 Pod 之外定义的。PV 目前支持的类型包括gcePersistentDisk已弃用、awsElasticBlockStore已弃用、azureFile已弃用、azureDisk已弃用、fc(Fibre Channel)、flocker已弃用、nfs、iscsi、rbd(Rados Block Device)cephfs、cinder已弃用、 glusterfs、vsphereVolume已弃用、Quobyte Volumes、VMware Photon、Portworx Volumes、ScalelO Volumes 和 HostPath仅供单机测试)。注意 PV 支持的类型有部分随着 k8s 版本的升级已经被弃用了了解最新情况请自行查看 k8s 官网。持久卷PersistentVolumePV每个 PV 对象都包含 spec 部分和 status 部分分别对应卷的 规约 和 状态。PersistentVolume 对象的名称必须是合法的 DNS 子域名。下面给出了 NFS 类型的 PV 的一个 YAML 定义文件声明了需要 5Gi 的存储空间apiVersion: v1 kind: PersistentVolume metadata:name: pv0003 spec:# 指定 PV 卷容量capacity:storage: 5Gi# PV 卷模式volumeMode: Filesystem# PV 卷访问模式accessModes:- ReadWriteOnce# PV 卷回收策略persistentVolumeReclaimPolicy: Recycle# 指定 PV 卷所属类storageClassName: slow# 挂载选项mountOptions:- hard- nfsvers4.1# 指定 PV 卷类型 nfsnfs:path: /tmpserver: 172.17.0.2容量capacity通常情况每个 PV 卷都有确定的存储容量。容量属性是使用 PV 对象的 capacity 属性来设置的。目前存储大小是可以设置和请求的唯一资源。未来可能会包含 IOPS、吞吐量等属性。卷模式volumeMode特性状态Kubernetes v1.18 [stable]针对 PV 持久卷k8s 支持两种卷模式volumeModesFilesystem文件系统 和 Block块。volumeMode 是一个可选的 API 参数。如果该参数被省略默认的卷模式是 Filesystem。volumeMode 属性设置为 Filesystem 的卷会被 Pod 挂载Mount 到某个目录。如果卷的存储来自某块设备而该设备目前为空k8s 会在第一次挂载卷之前在设备上创建文件系统。volumeMode 设置为 Block以便将卷作为原始块设备来使用。这类卷以块设备的方式交给 Pod 使用其上没有任何文件系统。这种模式对于为 Pod 提供一种使用最快可能方式来访问卷而言很有帮助 Pod 和卷之间不存在文件系统层。另外Pod 中运行的应用必须知道如何处理原始块设备。关于如何在 Pod 中使用 volumeMode: Block 的卷 可参阅 原始块卷支持。访问模式accessModesPersistentVolume 卷可以用资源提供者所支持的任何方式挂载到宿主系统上。在命令行接口CLI中访问模式也使用以下缩写形式RWO - ReadWriteOnce卷可以被一个 Node 以读写方式挂载。ReadWriteOnce 访问模式也允许运行在同一节点上的多个 Pod 访问卷。ROX - ReadOnlyMany卷可以被多个 Node 以只读方式挂载。RWX - ReadWriteMany卷可以被多个 Node 以读写方式挂载。RWOP - ReadWriteOncePod卷可以被单个 Pod 以读写方式挂载。如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC 请使用该访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。说明k8s 使用卷访问模式来匹配 PersistentVolumeClaim 和 PersistentVolume。在某些场合下卷访问模式也会限制 PersistentVolume 可以挂载的位置。卷访问模式并不会在存储已经被挂载的情况下为其实施写保护。即使访问模式设置为 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany它们也不会对卷形成限制。例如即使某个卷创建时设置为 ReadOnlyMany也无法保证该卷是只读的。如果访问模式设置为 ReadWriteOncePod则卷会被限制起来并且只能挂载到一个 Pod 上。类StorageClass每个 PV 可以属于某个类Class通过将其 storageClassName 属性设置为某个 StorageClass 的名称来指定。特定类的 PV 卷只能绑定到请求该类存储卷的 PVC 申领。未设置 storageClassName 的 PV 卷没有类设定只能绑定到那些没有指定特定存储类的 PVC 申领。早前k8s 使用 Annotation (注解) volume.beta.kubernetes.io/storage-class 而不是 storageClassName 属性。这一注解目前仍然起作用不过在将来的 k8s 发布版本中该注解会被彻底废弃。回收策略persistentVolumeReclaimPolicy目前的回收策略有Retain -- 手动回收。Recycle -- 基本擦除 (rm -rf /thevolume/*)。Delete -- 诸如 AWS EBS、GCE PD、Azure Disk 或 OpenStack Cinder 卷这类关联存储资产也被删除。目前仅 NFS 和 HostPath 支持回收Recycle。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷都支持删除Delete。挂载选项mountOptionsk8s 管理员可以指定持久卷被挂载到 Node 节点上时使用的附加挂载选项mountOptions。说明并非所有持久卷类型都支持挂载选项。以下卷类型支持挂载选项awsElasticBlockStore已弃用azureDisk已弃用azureFile已弃用cephfscinder (已弃用于 v1.18)gcePersistentDisk已弃用glusterfsiscsinfsquobyte (已弃用于 v1.22)rbdstorageos (已弃用于 v1.22)vsphereVolumek8s 不对挂载选项mountOptions执行合法性检查。如果挂载选项是非法的挂载就会失败。早前k8s 使用 Annotation (注解) volume.beta.kubernetes.io/mount-options 而不是 mountOptions 属性。这一注解目前仍然起作用不过在将来的 k8s 发布版本中该注解会被彻底废弃。节点亲和性nodeAffinity每个 PV 卷可以通过 设置节点亲和性nodeAffinity来定义一些约束进而限制从哪些节点上可以访问此卷。使用这些卷的 Pod 只会被调度到节点亲和性规则所选择的节点上执行。要设置节点亲和性配置 PV 卷 .spec 中的 nodeAffinity。说明对大多数类型的卷而言你不需要设置节点亲和性字段。AWS EBS、 GCE PD 和 Azure Disk 卷类型都能自动设置相关字段。你需要为 local 卷显式地设置此属性。阶段状态Phase每个卷会处于以下阶段Phase之一Available可用-- 卷是一个空闲资源尚未绑定到任何 PVCBound已绑定-- 该卷已经绑定到某 PVCReleased已释放-- 所绑定的 PVC 已被删除但是资源尚未被集群回收Failed失败-- 卷的自动回收操作失败。命令行接口能够显示绑定到某 PV 卷的 PVC 对象。持久卷申领PersistentVolumeClaimPVC每个 PVC 对象都有 spec 和 status 部分分别对应 PVC 申领的 规约 和 状态。PersistentVolumeClaim 对象的名称必须是合法的 DNS 子域名。apiVersion: v1 kind: PersistentVolumeClaim metadata:name: myclaim spec:accessModes:- ReadWriteOncevolumeMode: Filesystem# 资源限制resources:requests:storage: 8GistorageClassName: slowselector:matchLabels:release: stablematchExpressions:- {key: environment, operator: In, values: [dev]}上面 PVC 的 YAML 定义文件其中 accessModes、volumeMode 和 PV 是一样的。选择符selectorPVC 申领可以设置标签选择算符来进一步过滤卷集合。只有标签与选择算符相匹配的卷能够绑定到申领上。选择符包含两个字段matchLabels - 卷 必须包含 带有该值的标签。matchExpressions - 通过设定 键key、值value列表和操作符operator 来构造的需求。合法的操作符有 In、NotIn、Exists 和 DoesNotExist。来自 matchLabels 和 matchExpressions 的所有需求都按逻辑与的方式组合在一起。这些需求都必须被满足才被视为匹配。类 StorageClassPVC 申领 可以通过为 storageClassName 属性设置 StorageClass 的名称来请求 特定的存储类。只有所请求的类的 PV 卷即 storageClassName 值与 PVC 设置相同的 PV 卷 才能绑定到 PVC 申领。PVC 申领不必一定要请求某个类。如果 PVC 的 storageClassName 属性值设置为 则被视为要请求的是没有设置存储类的 PV 卷因此这一 PVC 申领只能绑定到未设置存储类的 PV 卷未设置注解或者注解值为 的 PV 对象在系统中不会被删除 因为这样做可能会引起数据丢失。未设置 storageClassName 的 PVC 与此大不相同 也会被集群作不同处理。具体筛查方式取决于 DefaultStorageClass 准入控制器插件 是否被启用。如果 准入控制器插件被启用则管理员可以设置一个默认的 StorageClass。所有未设置 storageClassName 的 PVC 都只能绑定到隶属于默认存储类的 PV 卷。设置默认 StorageClass 的工作是通过将对应 StorageClass 对象的注解 storageclass.kubernetes.io/is-default-class 赋值为 true 来完成的。如果管理员未设置默认存储类集群对 PVC 创建的处理方式与未启用准入控制器插件时相同。如果设定的默认存储类不止一个准入控制插件会禁止所有创建 PVC 操作。如果 准入控制器插件被关闭则不存在默认 StorageClass 的说法。所有未设置 storageClassName 的 PVC 都只能绑定到未设置存储类的 PV 卷。在这种情况下未设置 storageClassName 的 PVC 与 storageClassName 设置为 的 PVC 的处理方式相同。取决于安装方法默认的 StorageClass 可能在集群安装期间由 插件管理器Addon Manager 部署到集群中。当某 PVC 除了请求 StorageClass 之外还 设置了 selector则这两种需求会按 逻辑与 关系处理只有隶属于所请求类且带有所请求标签的 PV 才能绑定到 PVC。说明目前设置了非空 selector 的 PVC 对象无法让集群为其动态制备 PV 卷。早前Kubernetes 使用注解 volume.beta.kubernetes.io/storage-class 而不是 storageClassName 属性。这一注解目前仍然起作用不过在将来的 Kubernetes 发布版本中该注解会被彻底废弃。NamespaceNamespace命名空间简称 ns是 k8s 系统中另外一个重要的概念Namespace 在很多情况下用于实现多租户的资源隔离。它提供一种机制将同一集群中的资源划分为相互隔离的组。Namespace 将通过集群内部的资源对象 “分配” 到不同的 Namespace 中形成逻辑上分组的不同项目、小组或用户组便于不同的分组在共享使用整个集群资源的同时还能被分别管理。k8s 集群在启动后会创建 3 个 Namespace通过 kubectl 可以查看kubectl get ns --show-labels # 显示namespace的labeldefault 没有指明使用其它命名空间的对象所使用的默认命名空间。kube-system k8s 系统创建对象所使用的命名空间。kube-public 这个命名空间是自动创建的所有用户包括未经过身份验证的用户都可以读取它。这个命名空间主要用于集群使用以防某些资源在整个集群中应该是可见和可读的。这个命名空间的公共方面只是一种约定而不是要求。说明如果不特别指明 ns则用户创建的 Pod、RC、Service 都将被系统创建到 default 的命名空间。Namespace 的定义Namespace 的定义很简单。如下所示的 YAML 定了名为 development 的 ns apiVersion: v1 kind: Pod metadata:name: busyboxnamespace: development spec:containers:- name: busyboximage: busybox:1.33.2command: - sleep- 3600使用 kubectl 查看上面创建的 Pod # 指定命名空间查看 Pod kubectl get pod --namespacedevelopment # 以下命令等效 kubectl get pod -n development当每个租户创建一个 ns 来实现多租户的资源隔离时还能结合 k8s 的资源配额resource管理限定不同租户能使用的资源例如CPU、Memory 使用量 等。AnnotationAnnotation (注解与 Label 类似也使用 key/value 键值对 的形式进行定义。不同的是 Label 具有严格的命名规则它定义的是 k8s 对象的元数据Metadata并且作用于 Label Selector。Annotation 则是用户任意定义的附加信息以便于外部工具查找。在很多时候 k8s 的模块自身会通过 Annotation 标记资源对象的一些特殊信息。通常来说用 Annotation 来记录的信息如下。build 信息、release 信息、Docker 镜像image信息等例如 timestamp时间截、release id号、PR号、镜像 Hash 值、Docker Registry 地址等。日志库、监控库、分析库等资源库的地址信息。程序调试工具信息例如工具名称、版本号等。团队的联系信息,例如电话号码、负责人名称、网址等。ConfigMap存在的问题和 Docker 提供的解决方案为了能够准确和深刻理解 k8s 中 ConfigMap 资源对象的功能和价值需要从 Docker 说起。我们知道Docker通过将程序、依赖库、数据及配置文件 “打包固化” 到一个不变的镜像文件中的做法解决了应用的部署的难题但这同时带来了棘手的问题即 配置文件中的参数在运行期如何修改的问题。我们不可能在启动 Docker 容器后再修改容器里的配置文件然后用新的配置文件重启容器里的用户主进程。为了解决这个问题Docker 提供了两种方式在运行时通过容器的 环境变量 来传递参数。通过 Docker Volume 将容器外的配置文件映射到容器内。这两种方式都有其优势和缺点在大多数情况下后一种方式更合适我们的系统因为 大多数应用通常从一个或多个配置文件中读取参数。但这种方式也有明显的缺陷我们必须在目标主机上先创建好对应的配置文件然后才能映射到容器里。上述缺陷在 分布式环境 下变得更为严重或突显因为无论采用哪种方式写入修改多台服务器上的某个指定文件并确保这些文件保持一致都是一个很难完成的目标。此外在大多数情况下我们都希望能集中管理系统的配置参数而不是管理一堆配置文件。针对上述问题k8s 给出了一个很巧妙的设计实现如下所述。k8s 如何巧妙的解决上述问题首先把所有的配置项都当作 key-value 字符串当然 value 可以来自某个文本文件比如配置项 password123456、userroot、host192.168.8.4 用于表示连接 FTP 服务器的配置参数。这些配置项可以作为 Map表中的一个项整个 Map 的数据可以被持久化存储在 k8s 的 etcd 数据库中然后提供 API 以方便 k8s 相关组件或客户应用 CRUD 操作这些数据上述专门用来保存配置参数的 Map 就是 k8s 的 ConfigMap 资源对象。接下来k8s 提供了一种内建机制将存储在 etcd 中的 ConfigMap 通过 Volume 映射的方式变成目标 Pod 内的配置文件不管目标 Pod 被调度到哪台服务器上都会完成自动映射。进一步地如果 ConfigMap 中的 key-value 数据被修改则映射到 Pod 中的 “配置文件” 也会随之自动更新。于是k8s ConfigMap 就成了分布式系统中最为简单使用方法简单但背后实现比较复杂且对应用无侵入的配置中心。ConfigMap 配置集中化的一种简单方案如下图所示k8s ConfigMap 集中化配置中心ConfigMap 示例ConfigMap 的示例它的 一些键只有一个值其他键的值看起来像是 配置的片段格式。apiVersion: v1 kind: ConfigMap metadata:name: game-demo data:# 类属性键每一个键都映射到一个简单的值player_initial_lives: 3ui_properties_file_name: user-interface.properties# 类文件键game.properties: |enemy.typesaliens,monstersplayer.maximum-lives5    user-interface.properties: |color.goodpurplecolor.badyellowallow.textmodetrue你可以使用四种方式来使用 ConfigMap 配置 Pod 中的容器在容器 container 命令和参数内。在容器 container 的环境变量 env。在只读卷里面添加一个文件让应用来读取。编写代码在 Pod 中运行使用 k8s API 来读取 ConfigMap。配置 Pod 使用上面创建的名称叫 game-demo 的 ConfigMap 对象资源apiVersion: v1 kind: Pod metadata:name: configmap-demo-pod spec:containers:- name: demoimage: alpinecommand: [sleep, 3600]env:# 定义环境变量- name: PLAYER_INITIAL_LIVES # 请注意这里和 ConfigMap 中的键名是不一样的valueFrom:configMapKeyRef:name: game-demo           # 这个值来自 ConfigMapkey: player_initial_lives # 需要取值的键- name: UI_PROPERTIES_FILE_NAMEvalueFrom:configMapKeyRef:name: game-demokey: ui_properties_file_namevolumeMounts:- name: configmountPath: /configreadOnly: truevolumes:# 你可以在 Pod 级别设置卷然后将其挂载到 Pod 内的容器中- name: configconfigMap:# 提供你想要挂载的 ConfigMap 的名字name: game-demo# 来自 ConfigMap 的一组键将被创建为文件items:- key: game.propertiespath: game.properties- key: user-interface.propertiespath: user-interface.properties关于 ConfigMap 更多信息请查看https://kubernetes.io/zh-cn/docs/concepts/configuration/configmap/SecretSecret 是一种包含少量敏感信息例如密码、令牌或密钥的对象。这样的信息可能会被放在 Pod 规约中或者镜像中。使用 Secret 意味着你不需要在应用程序代码中包含机密数据。由于创建 Secret 可以独立于使用它们的 Pod 因此在创建、查看和编辑 Pod 的工作流程中暴露 Secret及其数据的风险较小。k8s 和在集群中运行的应用程序也可以对 Secret 采取额外的预防措施例如避免将机密数据写入非易失性存储。Secret 类似于 ConfigMap 但专门用于保存机密数据base64 编码值。注意默认情况下Kubernetes Secret 未加密地存储在 API 服务器的底层数据存储etcd中。任何拥有 API 访问权限的人都可以检索或修改 Secret任何有权访问 etcd 的人也可以。此外任何有权限在命名空间中创建 Pod 的人都可以使用该访问权限读取该命名空间中的任何 Secret这包括间接访问例如创建 Deployment 的能力。为了安全地使用 Secret请至少执行以下步骤为 Secret 启用静态加密启用或配置 RBAC 规则来限制读取和写入 Secret 的数据包括通过间接方式。需要注意的是被准许创建 Pod 的人也隐式地被授权获取 Secret 内容。在适当的情况下还可以 使用 RBAC 等机制来限制允许哪些主体创建新 Secret 或替换现有 Secret。Secret 的应用Pod 可以用三种方式之一来使用 Secret作为挂载到一个或多个容器上的卷volume中的文件。作为容器container的环境变量。由 kubelet 在为 Pod 拉取镜像时使用。k8s 控制面kube-controller-manager也使用 Secret例如引导 Token 令牌 Secret 是一种帮助自动化节点注册的机制。配置 Pod 文件以使用 Secret打开默认编辑器更新 data 字段中存放的 base64 编码的 Secret 值然后配置 Pod 通过 卷volumes来挂载volumeMounts名为 mysecret 的 Secret。例如apiVersion: v1 data:# 填写 base 64 编码值username: YWRtaW4password: MWYyZDFlMmU2N2Rm kind: Secret metadata:annotations:kubectl.kubernetes.io/last-applied-configuration: { ... }creationTimestamp: 2020-01-22T18:41:56Zname: mysecretnamespace: defaultresourceVersion: 164619uid: cfee02d6-c137-11e5-8d73-42010af00002 type: Opaque --- apiVersion: v1 kind: Pod metadata:name: mypod spec:containers:- name: mypodimage: redisvolumeMounts:- name: foomountPath: /etc/fooreadOnly: truevolumes:- name: foosecret:secretName: mysecretoptional: false # 默认设置意味着 mysecret 必须已经存在关于 Secret 更多信息请查看https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/总结上述这些组件是 k8s 系统的核心组件它们共同构成了 k8s 系统的框架和计算模型。通过对它们进行灵活组合用户就可以快速、方便地对容器集群进行配置、创建和管理。除了本章所介绍的核心组件在 k8s 系统中还有许多辅助配置的资源对象例如 LimitRange限制范围 、ResourceOuota资源配额、Pod ReadinessPod 就绪态、容器探针livenessProbe、readinessProbe 和 startupProbe。另外一些系统内部使用的对象 Binding绑定Event事件 等请参考 Kubernetes 的 API 文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/929836.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实验1-8

1 `#include <stdio.h> int main() { printf(" o \n"); printf("\n"); printf("I I\n"); return 0;`2 `#include <stdio.h> include <math.h> int main() { double a…

网站集约化建设意见和建议网站建设需要的设备

线上问题&#xff1a;所有用户页面无法打开 1 线上问题2 问题处理3 复盘3.1 第二天观察 1 线上问题 上午进入工作时间&#xff0c;Cat告警出现大量linda接口超时Exception。 随后&#xff0c;产品和运营反馈无法打开页面&#xff0c;前线用户大量反馈无法打开页面。 2 问题处…

自己做网站推广济源网站建设

1 torch.rand&#xff1a;构造均匀分布张量 torch.rand是用于生成均匀随机分布张量的函数&#xff0c;从区间[0,1)的均匀分布中随机抽取一个随机数生成一个张量&#xff0c;其调用方法如下所示&#xff1a; torch.rand(sizes, outNone) ➡️ Tensor 参数&#xff1a; sizes&…

征婚网站建设wordpress 哪些网站吗

题目&#xff1a; 创建职工表以及职工工资表职工表字段&#xff1a;工号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄工资表字段&#xff1a;编号自增&#xff0c;职工工号&#xff0c;基础工资10000通过触发器实现&#xff1a;对职工进行添加时 工资表中也要体现当前职…

惠州专业网站建设公司哪里有昌吉 建设局 网站

HTML 中的表格用于在网页上展示和组织数据。表格由行和列组成&#xff0c;每个单元格可以包含文本、图像或其他 HTML 元素。下面是一些常用的 HTML 表格标签和属性的应用示例&#xff1a; <table> 标签: 定义表格的起始和结束标记。所有的表格元素应该放在这对标签之间。…

培训网站建设的背景怀化老年网站

让父子进程来执行不相干的操作 能够替换进程地址空间的代码.text段 执行另外的程序&#xff0c;不需要创建额外的的地址空间 当前程序中调用另外一个应用程序 指定执行目录下的程序 int execl(const char *path, const char *arg&#xff0c;/* (char *) NULL */); /* pat…

保定网站建设价格低网站排名应该怎么做

文章目录 Upload-labs 1~15 通关详细教程Pass-01-前端js验证Pass-02-后端MIME验证Pass-03-黑名单验证Pass-04-黑名单验证.htaccessPass-05-文件后缀名大小写绕过Pass-06-文件后缀名空格绕过Pass-07-文件后缀名点绕过Pass-08-文件后缀名::$DATA绕过Pass-09-点空格点空格绕过Pass…

电脑网站模板淘宝店铺推广渠道有哪些

1. 题目 给你两个单词 s 和 t&#xff0c;请你计算出将 s 转换成 t 所使用的最少操作数。 你可以对一个单词进行如下两种操作&#xff1a; 删除一个字符替换一个字符 注意&#xff1a; 不允许插入操作 题目保证有解 示例&#xff1a; 输入&#xff1a;s "abcdefg&qu…

教会音控组侍奉中的工序主义实践

两年前在教堂播放过一段时间PPT。 这个工作的主要内容是,在开场前播放一些简介性的幻灯页。 开场后一般为赞美团献唱,播放歌词PPT,这种PPT背景是黑色的,歌词为黄色文字,黑色被镂空,黄色文字叠加在摄像头画面,在…

网站开发 简单优秀网站建设多少钱

matlab连续型随机变量的分布.doc 连续型随机变量的分布及其数字特征一、基本概念设随机变量X的分布函数为F(x)&#xff0c;若存在非负函数f(x)&#xff0c;使对任意实数x&#xff0c;有≤X{Pxd}则称X为连续型随机变量&#xff0c;并称f(x)为X的概率密度&#xff0c;它满…

长沙建站网购物网站建设过程

文章目录 推荐写在前面一、熟悉Gateway基本概念与原理1、三大概念2、工作流程 二、基本使用路由断言的两种写法 三、路由断言工厂1、After路由断言工厂2、Before路由断言工厂3、Between路由断言工厂4、Cookie路由断言工厂5、Header路由断言工厂6、Host路由断言工厂7、Method路由…

用 Kotlin 调用 Tesseract 实现验证码识别

一、引言 验证码作为防止自动化攻击的重要手段,常见形式是数字、字母或组合图片。要实现自动识别,可以使用 OCR 技术。本文介绍如何通过 Kotlin 调用 Tesseract OCR 完成验证码识别。 二、技术选型 更多内容访问ttoc…

Kotlin 调用 Tesseract 实现验证码识别

一、引言 验证码作为防止自动化攻击的重要手段,常见形式是数字、字母或组合图片。要实现自动识别,可以使用 OCR 技术。本文介绍如何通过 Kotlin 调用 Tesseract OCR 完成验证码识别。 二、技术选型 编程语言:Kotlin…

长春市长春网站建设可以做直播卖产品的网站

当需要在方法前后做一些操作就需要借助动态代理来实现 一、动态代理实现方法 1、jdk自带实现方式 jdk实现代理是被代理类实现接口的方式 public interface UserInterface {void test(); }public class UserService implements UserInterface {public void test() {System.o…

肇庆建设网站手机建网站制作

python script常见格式: import sys #导入sys模块 from util import * #从util模块导入全部需要的变量,函数等 module[webview,content_shell,chrome] #列表数据结构 number(1,2,5,6,9) #元祖数据结构,不可改变 转载于:https://www.cnblogs.com/hanying/p/3792601.html

Dart 调用 Tesseract 实现验证码识别

一、背景介绍 验证码(CAPTCHA)常用于防止恶意程序批量请求。通常我们需要通过 OCR 技术识别验证码,从而实现自动化。 本文将介绍如何使用 Dart 调用 Tesseract OCR 来识别验证码。 二、技术选型 语言:Dart OCR 引擎…

淘宝美工网站怎么做万网网站空间

在本篇文章中我们主要围绕下面几个问题来介绍async 和await &#x1f370;Generator的作用&#xff0c;async 及 await 的特点&#xff0c;它们的优点和缺点分别是什么&#xff1f;await 原理是什么&#xff1f; &#x1f4c5;我的感受是我们先来了解Generator&#xff0c;在去…

汽车之家这样的网站怎么做企业网站要求

C左值右值左值和右值的由来什么是左值和右值左值右值的本质引用左值引用右值引用 移动语句与完美转发移动语句实现移动构造函数和转移赋值函数stdmove完美转发Perfect Forwarding C左值右值 自从C11发布之后&#xff0c;出现了一个新的概念&#xff0c;即左值和右值&#xf…

关于做ppt的网站有哪些内容吗东莞品牌设计公司

中国大数据技术大会&#xff08;BDTC&#xff09;由中国计算机学会&#xff08;CCF&#xff09;创立于2008年&#xff0c;已经成为国内外极具行业实践的专业大数据交流平台。12月22日-24日&#xff0c;第十七届中国大数据技术大会&#xff08;BDTC 2023&#xff09;在广州举行。…

Audacity导出音频后发声提醒

这么一个看似简单的功能,实现起来却颇费周折。 据ai说,Audacity 3.3版本只需要简单设置一下就可以了。但后来的版本莫名其妙去掉了这个功能。 ai给了大量的错误信息。首先用Nyquist写插件,没用,它只能生成一个音频…