Kubernetes 使用 Service 把前端连接到后端

本任务会描述如何创建前端微服务和后端微服务。后端微服务是一个 hello 欢迎程序。 前端和后端的连接是通过 Kubernetes Service对象完成的。

Objectives

  • 使用Deployment 对象创建并运行一个微服务
  • 从后端将流量路由到前端
  • 使用Service对象把前端应用连接到后端应用

Before you begin

  • You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube.
  • 本任务使用 外部负载均衡服务, 所以需要对应的可支持此功能的环境。如果你的环境不能支持,你可以使用 NodePort 类型的服务代替。

使用Deployment对象创建后端

后端是一个简单的 hello 欢迎微服务应用。这是后端应用的 Deployment 配置文件:

hello.yaml 
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 7
  template:
    metadata:
      labels:
        app: hello
        tier: backend
        track: stable
    spec:
      containers:
        - name: hello
          image: "gcr.io/google-samples/hello-go-gke:1.0"
          ports:
            - name: http
              containerPort: 80

创建后端 Deployment:

kubectl create -f https://k8s.io/docs/tasks/access-application-cluster/hello.yaml

查看后端的 Deployment 信息:

kubectl describe deployment hello

输出类似于:

Name:                           hello
Namespace:                      default
CreationTimestamp:              Mon, 24 Oct 2016 14:21:02 -0700
Labels:                         app=hello
                                tier=backend
                                track=stable
Selector:                       app=hello,tier=backend,track=stable
Replicas:                       7 updated | 7 total | 7 available | 0 unavailable
StrategyType:                   RollingUpdate
MinReadySeconds:                0
RollingUpdateStrategy:          1 max unavailable, 1 max surge
OldReplicaSets:                 <none>
NewReplicaSet:                  hello-3621623197 (7/7 replicas created)
Events:
...

创建后端Service对象

前端连接到后端的关键是 Service。Service 创建一个固定 IP 和 DNS 解析名入口, 使得后端微服务可达。Service 使用 selector 标签来寻找目标 Pod。

首先,浏览 Service 的配置文件:

hello-service.yaml 
kind: Service
apiVersion: v1
metadata:
  name: hello
spec:
  selector:
    app: hello
    tier: backend
  ports:
    - protocol: TCP
      port: 80
      targetPort: http

配置文件中,你可以看到 Service 将流量路由到包含 app: hello 和 tier: backend 标签的 Pod。

创建 hello Service:

kubectl create -f https://k8s.io/docs/tasks/access-application-cluster/hello-service.yaml

此时,你已经有了一个在运行的后端 Deployment,你也有了一个 Service 用于路由网络流量。

创建前端应用

既然你已经有了后端应用,你可以创建一个前端应用连接到后端。前端应用通过 DNS 名连接到后端的工作 Pods。 DNS 名是 “hello”,也就是 Service 配置文件中 name 字段的值。

前端 Deployment 中的 Pods 运行一个 nginx 镜像,这个已经配置好镜像去寻找后端的 hello Service。 只是 nginx 的配置文件:

frontend/frontend.conf 
upstream hello {
    server hello;
}

server {
    listen 80;

    location / {
        proxy_pass http://hello;
    }
}

与后端类似,前端用包含一个 Deployment 和一个 Service。Service 的配置文件包含了 type: LoadBalancer, 也就是说,Service 会使用你的云服务商的默认负载均衡设备。

frontend.yaml 
kind: Service
apiVersion: v1
metadata:
  name: frontend
spec:
  selector:
    app: hello
    tier: frontend
  ports:
    - protocol: "TCP"
      port: 80
      targetPort: 80
  type: LoadBalancer
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
        tier: frontend
        track: stable
    spec:
      containers:
        - name: nginx
          image: "gcr.io/google-samples/hello-frontend:1.0"
          lifecycle:
            preStop:
              exec:
                command: ["/usr/sbin/nginx","-s","quit"]

创建前端 Deployment 和 Service:

kubectl create -f https://k8s.io/docs/tasks/access-application-cluster/frontend.yaml

通过输出确认两个资源都已经被创建:

deployment "frontend" created
service "frontend" created

注意:这个 nginx 配置文件是被打包在 容器镜像 里的。 更好的方法是使用 ConfigMap,这样的话你可以更轻易地更改配置。

与前端 Service 交互

一旦你创建了 LoadBalancer 类型的 Service,你可以使用这条命令查看外部 IP:

kubectl get service frontend

外部 IP 字段的生成可能需要一些时间。如果是这种情况,外部 IP 会显示为 <pending>。

NAME       CLUSTER-IP      EXTERNAL-IP   PORT(S)  AGE
frontend   10.51.252.116   <pending>     80/TCP   10s

使用相同的命令直到它显示外部 IP 地址:

NAME       CLUSTER-IP      EXTERNAL-IP        PORT(S)  AGE
frontend   10.51.252.116   XXX.XXX.XXX.XXX    80/TCP   1m

通过前端发送流量

前端和后端已经完成连接了。你可以使用 curl 命令通过你的前端 Service 的外部 IP 访问服务端点。

curl http://<EXTERNAL-IP>

后端生成的消息输出如下:

{"message":"Hello"}

What’s next

原文:https://k8smeetup.github.io/docs/tasks/access-application-cluster/connecting-frontend-backend/

译者:zhangqx2010

K8S中文社区微信公众号

Kubernetes 同 Pod 内的容器使用共享卷通信

本文旨在说明如何让一个 Pod 内的两个容器使用一个卷(Volume)进行通信。

Before you begin

You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube.

创建一个包含两个容器的 Pod

在这个练习中,你会创建一个包含两个容器的 Pod。两个容器共享一个卷用于他们之间的通信。 Pod 的配置文件如下:

two-container-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: two-containers
spec:

  restartPolicy: Never

  volumes:
  - name: shared-data
    emptyDir: {}

  containers:

  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html

  - name: debian-container
    image: debian
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data
    command: ["/bin/sh"]
    args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]

在配置文件中,你可以看到 Pod 有一个共享卷,名为 shared-data。

配置文件中的第一个容器运行了一个 nginx 服务器。共享卷的挂载路径是 /usr/share/nginx/html。 第二个容器是基于 debian 镜像的,有一个 /pod-data 的挂载路径。第二个容器运行了下面的命令然后终止。

echo Hello from the debian container > /pod-data/index.html

注意,第二个容器在 nginx 服务器的根目录下写了 index.html 文件。

创建一个包含两个容器的 Pod:

kubectl create -f https://k8s.io/docs/tasks/access-application-cluster/two-container-pod.yaml

查看 Pod 和容器的信息:

kubectl get pod two-containers --output=yaml

这是输出的一部分:

apiVersion: v1
kind: Pod
metadata:
  ...
  name: two-containers
  namespace: default
  ...
spec:
  ...
  containerStatuses:

  - containerID: docker://c1d8abd1 ...
    image: debian
    ...
    lastState:
      terminated:
        ...
    name: debian-container
    ...

  - containerID: docker://96c1ff2c5bb ...
    image: nginx
    ...
    name: nginx-container
    ...
    state:
      running:
    ...

你可以看到 debian 容器已经被终止了,而 nginx 服务器依然在运行。

进入 nginx 容器的 shell:

kubectl exec -it two-containers -c nginx-container -- /bin/bash

在 shell 中,确认 nginx 还在运行。

root@two-containers:/# ps aux

输出类似于这样:

USER       PID  ...  STAT START   TIME COMMAND
root         1  ...  Ss   21:12   0:00 nginx: master process nginx -g daemon off;

回忆一下,debian 容器在 nginx 的根目录下创建了 index.html 文件。 使用 curl 向 nginx 服务器发送一个 GET 请求:

root@two-containers:/# apt-get update
root@two-containers:/# apt-get install curl
root@two-containers:/# curl localhost

输出表示 nginx 提供了 debian 容器写的页面:

Hello from the debian container

讨论

Pod 能有多个容器的主要原因是为了支持辅助应用(helper applications),以协助主应用(primary application)。 辅助应用的典型例子是数据抽取,数据推送和代理。辅助应用和主应用经常需要相互通信。 就如这个练习所示,通信通常是通过共享文件系统完成的,或者,也通过回环网络接口 localhost 完成。 举个网络接口的例子,web 服务器带有一个协助程序用于拉取 Git 仓库的更新。

在本练习中的卷为 Pod 生命周期中的容器相互通信提供了一种方法。如果 Pod 被删除或者重建了, 任何共享卷中的数据都会丢失。

What’s next

原文:https://k8smeetup.github.io/docs/tasks/access-application-cluster/communicate-containers-same-pod-shared-volume/

译者:zhangqx2010

K8S中文社区微信公众号

Kubernetes 使用 Bootstrap Tokens 认证

概述

启动引导令牌是一种简单的持有者令牌(Bearer Token),这种令牌是在新建集群或者在现有集群中添加新加新节点时使用的。 它被设计成能够支持 kubeadm,但是也可以被用在其他 context 中以便用户在 不使用 kubeadm 的情况下启动集群。它也被设计成可以通过 RBAC 策略,结合 Kubelet TLS Bootstrapping 系统进行工作。

启动引导令牌被定义成一个特定类型的 secrets(bootstrap.kubernetes.io/token),并存在于 kube-system 命名空间中。然后这些 secrets 会被 API 服务器上的启动引导的认证器读取。 过期的令牌与 TokenCleaner 会被控制管理器一起清除。令牌也会被用于创建特定 configmap 的签名, 而这个 configmap 会通过启动引导签名控制器在 “discovery” 过程中使用。

目前,启动引导令牌处于 alpha 阶段,但是预期也不会有大的突破性变化。

令牌格式

启动引导令牌使用 abcdef.0123456789abcdef 的形式。 更加规范地说,它们必须符合正则表达式 [a-z0-9]{6}\.[a-z0-9]{16}。

令牌的第一部分是 “Token ID” ,它是公共信息。它被用于引用一个用于认证的令牌而不会泄漏令牌的保密部分。 第二部分是 “Token Secret”,它应该只能被信任方共享。

启用启动引导令牌

所有启动引导令牌的特性在 Kubernetes v1.6 版本中默认都是禁用的。

你可以在 API 服务器上通过 --experimental-bootstrap-token-auth 参数启用启动引导令牌。 你可以在控制管理器上通过 --controllers 参数,比如 --controllers=*,tokencleaner,bootstrapsigner 来启用启动引导令牌。 在使用 kubeadm 时,这是自动完成的。

HTTPS 调用中的令牌是这样使用的:

Authorization: Bearer 07401b.f395accd246ae52d

启动引导令牌的密文格式

每个合法的令牌是通过一个 kube-system 命名空间中的 secret 隐藏的。 你可以从 这里 找到完整设计文档。

这是 secret 看起来的样子。注意,base64(string) 表示应该通过 base64 对值进行编码。 这里使用的是未解码的版本以便于阅读。

apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-07401b
  namespace: kube-system
type: bootstrap.kubernetes.io/token
data:
  description: base64(The default bootstrap token generated by 'kubeadm init'.)
  token-id: base64(07401b)
  token-secret: base64(f395accd246ae52d)
  expiration: base64(2017-03-10T03:22:11Z)
  usage-bootstrap-authentication: base64(true)
  usage-bootstrap-signing: base64(true)

secret 的类型必须是 bootstrap.kubernetes.io/token ,而且名字必须是 bootstrap-token-<token id>。 description 是人类可读的描述,而不应该是机器可读的信息。令牌 ID 和 Secret 是包含在数据字典中的。

usage-bootstrap-* 成员表示这个 secret 的用途。启用时,值必须设置为 true。

usage-bootstrap-authentication 表示令牌可以用于 API 服务器的认证。认证器会以 system:bootstrap:<Token ID> 认证。它被包含在 system:bootstrappers 组中。 命名和组是故意受限制的,以防止用户在启动引导后再使用这些令牌。

usage-bootstrap-signing 表示令牌应该被用于 cluster-info ConfigMap 的签名,就像下面描述的那样。

expiration 数据成员列举了令牌在失效后的时间。这是遵循 RFC3339 进行编码的 UTC 时间。 TokenCleaner 控制器会删除过期的令牌。

使用 kubeadm 管理令牌

你可以使用 kubeadm 工具管理正在运行集群的令牌。它会从 kubeadm 创建的集群(/etc/kubernetes/admin.conf) 自动抓取默认管理员密码。你可以通过参数 --kubeconfig 对下面命令指定一个另外的 kubeconfig 文件抓取密码。

  • kubeadm token list 列举了令牌,同时显示了它们的过期时间和用途。
  • kubeadm token create 创建一个新令牌。
    • --description 设置新令牌的描述。
    • --ttl duration 设置令牌从 “现在” 起到过期时间的差值。 默认是 0 ,也就是不过期。
    • --usages 设置令牌被使用的方式。默认是 signing,authentication。用途在上面已经描述。
  • kubeadm token delete <token id>|<token id>.<token secret> 删除令牌。 令牌可以只用 ID 来确认,也可以用整个令牌的值。如果只用 ID 的情况下,密文不匹配的令牌也会被删除。

ConfigMap签名

除了认证之外,令牌可以用于签名 ConfigMap。这在集群启动过程的早期,在客户端信任 API 服务器之前被使用。 被签名的 ConfigMap 可以通过共享令牌被认证。

被签名的 ConfigMap 是 cluster-info,存在于 kube-public 命名空间中。 典型的工作流中,客户端在未经认证和忽略 TLS 报错的状态下读取这个 ConfigMap。 通过 ConfigMap 中嵌入的签名校验 ConfigMap 的载荷。

ConfigMap 会是这个样子的:

apiVersion: v1
kind: ConfigMap
metadata:
  name: cluster-info
  namespace: kube-public
data:
  jws-kubeconfig-07401b: eyJhbGciOiJIUzI1NiIsImtpZCI6IjA3NDAxYiJ9..tYEfbo6zDNo40MQE07aZcQX2m3EB2rO3NuXtxVMYm9U
  kubeconfig: |
    apiVersion: v1
    clusters:
    - cluster:
        certificate-authority-data: <really long certificate data>
        server: https://10.138.0.2:6443
      name: ""
    contexts: []
    current-context: ""
    kind: Config
    preferences: {}
    users: []

ConfigMap 的 kubeconfig 成员是一个填好了集群信息的配置文件。 这里主要交换的信息是 certificate-authority-data。在将来可能会有扩展。

签名是一个 JWS 签名,使用了 “detached” 模式。为了检验签名,用户应该按照 JWS 规则 (base64 编码而忽略结尾的 =)对 kubeconfig 的载荷进行编码。完成编码的载荷会被通过插入 JWS 并存在于两个点的中间 ,用于形成一个完整的 JWS。可以使用令牌的完整信息(比如 07401b.f395accd246ae52d)作为共享密钥, 通过 HS256 方式 (HMAC-SHA256) 对 JWS 进行校验。 用户 必须 确保使用了 HS256。

原文:https://k8smeetup.github.io/docs/admin/bootstrap-tokens/

译者: zhangqx2010

K8S中文社区微信公众号

Kubernetes 搭建大型集群

支持

在 v1.7 版本中,Kubernetes 支持集群节点(node)数可达1000个。更具体地说,我们配置能够支持所有如下条件:

  • 不超过2000个节点
  • 不超过总共6000个 pod
  • 不超过总共12000个 container
  • 单节点不超过100个 pod

安装

集群是一组运行着 Kubernetes 代理的节点(物理或者虚机),被 “主服务器” (集群层面的控制台)所管理。

通常来说,集群中的节点数是通过平台特定的 config-default.sh 文件(例子参考 GCE’s config-default.sh )中的 NUM_NODES 值控制的。

然而,单单把这个值更改到很大的数值会导致安装脚本在许多云服务商平台上运行失败。例如 GCE 部署,会有配额问题导致集群启动失败。

当需要建立大规模 Kubernetes 集群时,必须考虑下列问题:

配额问题

为了避免在云服务商平台上发生配额问题,当创建一个许多节点的集群时,要考虑:

  • 增加这些资源的配额,比如 CPU ,IP 地址等等。
    • 在 GCE 中,举个例子 你会需要增加:
      • CPU
      • 虚拟机实例
      • 永久磁盘的预留总量
      • 在使用的 IP 地址
      • 防火墙规则
      • 转发规则
      • 路由
      • 目标池
  • 调整好安装脚本,让它能够在创建虚拟机节点的批处理中有等待时间,因为很多云服务商平台对于虚拟机的创建频率有限制。

Etcd 存储

为了提高大规模集群的性能,我们将 event 存储在一个独立的 etcd 实例中。

当创建一个集群时,现有的 salt 脚本会:

  • 启动并配置额外的 etcd 实例
  • 配置 api-server 用于储存 event

主服务器和主服务器组件的规格

在 GCE/GKE 和 AWS 上,kube-up 自动为你的主服务器配置合适的虚拟机规格,规格取决于集群中的节点数量。 对于其他云服务商平台,你需要手工配置。作为参考,我们在 GCE 上使用的规格是:

  • 1-5 节点: n1-standard-1
  • 6-10 节点: n1-standard-2
  • 11-100 节点: n1-standard-4
  • 101-250 节点: n1-standard-8
  • 251-500 节点: n1-standard-16
  • 超过 500 节点: n1-standard-32

在 AWS 上我们使用的规格:

  • 1-5 节点: m3.medium
  • 6-10 节点: m3.large
  • 11-100 节点: m3.xlarge
  • 101-250 节点: m3.2xlarge
  • 251-500 节点: c4.4xlarge
  • 超过 500 节点: c4.8xlarge

注意,主服务器节点规格只能在集群启动时设置,如果后续对于集群扩容或者缩容(比如,使用手工或集群自动扩展器进行增加或删除节点),规格是不会调整的。

插件(Addon)资源

为了防止 集群插件 内存泄漏或者其他资源问题导致消耗完节点的所有资源,Kubernetes 对插件容器设定了资源限制,以限制他们使用 CPU 和内存资源。(参见 PR #10653 和 #10778

举例:

  containers:
  - name: fluentd-cloud-logging
    image: gcr.io/google_containers/fluentd-gcp:1.16
    resources:
      limits:
        cpu: 100m
        memory: 200Mi

除了 Heapster 之外,这些限制是固定的,并且是基于我们对于插件运行在4节点集群的采样数据(见 #10335)。运行在大规模集群上时,插件会消耗更多的资源(见 #5880)。所以,如果大规模集群没有调整这些参数时,插件容器可能会被持续杀死,因为他们总是达到限制。

为了避免集群的插件资源问题出现,当创建一个许多节点的集群时,考虑如下问题:

  • 为以下每个插件调整内存和 CPU 限制,使用时,随着你的集群扩容(每个插件有一个 replica 来处理整个集群,所以 CPU/内存 使用量会随着集群的 规模/负载 按比例增加):
  • 为以下这些插件调整 replicas 数量, 使用时, 随着集群规模数量一起调整(每个插件会有多个 replicas, 所以增加 replicas 应该能帮助处理增加的负载,但是,由于每个 replica 的负载也稍稍增加, 同时需要考虑增加 CPU/内存 限制):
  • 为以下这些插件稍稍增加内存和 CPU 使用限制, 使用时, 随着集群规模数量一起调整(每个节点有一个 replica,但是 CPU/内存 使用量随着集群的 负载/规模 会稍稍增加):

Heapster 的资源限制是基于集群的初始规模动态配置的(见 #16185 和 #22940)。 如果你发现 Heapster 的资源不够,你应该调整 Heapster 对于内存的请求的计算公式(详见这些 PRs)。

对于如何检查插件容器是否达到了资源使用限制,参考 计算资源的问题排查章节

在 未来,我们预期会基于集群规模来设置所有集群插件的资源限制,并且会在你的集群扩容或缩容时进行动态调整。 我们欢迎致力于实现这些功能的 PR 。

允许少数节点在启动时失败

由于各种原因 (详细信息见 #18969),运行 kube-up.sh 建立非常大 的 NUM_NODES 数量的集群会由于个别的节点的启动失败而失败。目前你有两个选择:重启集群(再次运行 kube-down.sh 和 kube-up.sh), 或者,在运行 kube-up.sh 之前,将 ALLOWED_NOTREADY_NODES 环境变量设置成合适的值。这会让 kube-up.sh 在少于 NUM_NODES 节点启动的时候成功完成。根据不同的失败原因,这些额外的节点可以在稍后再加入集群,或者集群可以保持在 NUM_NODES - ALLOWED_NOTREADY_NODES 的规模。

原文:https://k8smeetup.github.io/docs/admin/cluster-large/

译者:zhangqx2010

K8S中文社区微信公众号

Dynamic Admission Control

概述

admission controllers 文档介绍了怎么使用标准的,插件化的 admission controller。然而,由于以下原因,admission controller 对于大多数用户来说还不够灵活:

  • 需要把它编译到kube-apiserver 二进制文件中
  • 只能在启动时进行配置

1.7 引入了2个alpha的功能,Initializers 和 外部 Admission Webhooks 来解决这些限制。这些功能可以让我们在它之外进行开发和在运行时改变它的配置

这篇文档详细介绍了怎么使用 Initializers 和 外部 Admission Webhooks.

Initializers

什么是 Initializers

Initializers 有两层意思

  • 存储在每一个对象元数据中的一系列的预初始化的任务, (例如,”AddMyCorporatePolicySidecar”)。
  • 用户自定义的 controller,用来执行那些初始化任务。任务的名称跟执行该任务的控制器是相关联的, 我们在这里称之为 initializer controllers

一旦一个controller执行了分配给它的任务,他就会把他的名字从这个列表中删除。例如,发送一个PATCH请求,在pod中添加一个container, 然后把它的名字从metadata.initializers.pending中删除。 Initializers 有可能造成这个对象突变。

一个对象如果有一个 initializer 的非空列表,我们就认为它没有被初始化,而且不能在API中看到,除非我们用这个查询参数发了一个特殊的请求, ?includeUninitialized=true

什么时候使用 initializers?

Initializers 对管理员用来强加一些策略是非常有用的 (例如,AlwaysPullImages)

<– Note: If your use case does not involve mutating objects, consider using external admission webhooks, as they have better performance. –>

注释 如果你的使用场景并不总会突变这个资源,可以考虑使用外部的 admission webhooks, 它的性能会更好

<–

How are initializers triggered?

–>

initializers 是怎么被触发的

当向一个资源发送一个POST请求的时候,所有的initializerConfiguration 对象(下文有介绍)都会对它进行检查。所有匹配的这些对象的 spec.initializers[].name会被附加在这个资源的metadata.initializers.pending 字段.

一个 initializer controller 应该通过查询参数?includeUninitialized=true 来 list 和 watch 没有被初始化的对象。 如果使用 client-go, 只需要设置listOptions.includeUninitialized 为 true.

对于观察到的没有被初始化的对象, initializer controller 应该首先检查它的名称是否和 metadata.initializers.pending[0] 匹配。如果匹配他就会被分配的任务然后在列表中删除它的名字

启用 initializers alpha 功能

Initializers 目前是alpha的功能,默认是被关闭的。要打开它,你需要:

  • 当你启动 kube-apiserver 的时候,在--admission-control 这个参数加上”Initializers”。 如果你有多个kube-apiserver副本, 你需要在所有的上面加上这个参数。
  • 启动kube-apiserver的时候,在--runtime-config 参数加上admissionregistration.k8s.io/v1alpha1 来打开动态 admission controller 注册 API 的功能。再一次,如果你有多个kube-apiserver, 你需要在所有的上面加上这个参数

部署 initializer controller

你应该通过deployment API 来部署 initializer controller

运行时配置 initializer controller

你可以通过创建 initializerConfiguration 资源来配置开启哪个 initializer controller 和初始化那种资源

首先你需要部署一个 initializer controller 并且确保在创建 initializerConfiguration 之前工作正常。 否则,任何新创建的资源都会处在 uninitialized 的状态

下面是一个 initializerConfiguration 的例子:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: InitializerConfiguration
metadata:
  name: example-config
initializers:
  # the name needs to be fully qualified, i.e., containing at least two "."
  - name: podimage.example.com
    rules:
      # apiGroups, apiVersion, resources all support wildcard "*".
      # "*" cannot be mixed with non-wildcard.
      - apiGroups:
          - ""
        apiVersions:
          - v1
        resources:
          - pods

你在创建了 initializerConfiguration 之后,系统会花费几妙中时间响应这个新的配置, 然后, "podimage.example.com" 回被添加到新创建Pod的 metadata.initializers.pending 字段。 你应该已经提前有一个工作正常的 initializer controller 来处理这些 metadata.initializers.pending[0].name="podimage.example.com"的Pod。 否则这些Pod会一直出在 uninitialized 状态

确保每一条 rule 内所有的 <apiGroup, apiVersions, resources> 展开的元组是有效的,如果不是的话 把他们放在不同的 rule 下面。

外部 Admission Webhooks

什么是外部 admission webhooks?

外部 admission webhooks 是一些 HTTP 的 callback, 用来接受 admission 的请求, 并且做一些相应的处理。 外部 admission webhook 做什么是由你来决定的, 但是有一个 接口 必须遵从, 来响应是否允许这个 admission 请求。

跟 initializers 或者插件化的 admission controllers 不一样的是, 外部的 admission webhooks 不允许以任何方式突变这个 admission request

因为 admission 是一个很高级别的安全相关的操作, 外部的 admission webhooks 必须支持TLS。

什么时候使用 admission webhooks?

使用外部 admission webhook 的一个简单的例子就是对 kubernetes 的资源进行语义验证。假如说你的架构需要所有的 Pod 资源有一些共同的 label, 而且如果这些 Pod 没有满足需要的话你不想对其持久化。 你可以写一个你自己的外部 的 admission webhook 来做这些验证并且对其做相应的响应。

外部 admission webhooks 是怎么触发的?

无论什么时候接受到请求, GenericAdmissionWebhook admission 插件会从 externalAdmissionHookConfiguration 对象 (下文有介绍)拿到相应的外部 admission webhooks 的一个列表然后并行的调用它们。如果所有的外部 admission webhooks 允许了这个 admission 请求,那么请求就会继续。 如果任何一个 外部 admission webhooks 拒绝了这个 admission 请求, 那么这个请求就会被拒绝,拒绝请求的原因就是第一个拒绝 请求的 admission webhook 的拒绝原因。这意味着如果如果多个外部 admission webhook 拒绝了请求,只有第一个会 被返回给用户。 如果当调用外部 admission webhook 发生错误, 这个 admission webhook 将被忽略掉, 不会用来 允许/拒绝这个 admission 请求。

注释 admission 链的执行顺序仅依赖 kube-apiserver 的 --admission-control 选项配置的顺序

启用外部 admission webhooks

外部 admission webhooks 是一个 alpha 的功能,默认似乎被关闭的,要打开它你需要:

  • 当启动 apiserver 的时候在 --admission-control 选项中包含 “GenericAdmissionWebhook”。 如果你有多个 kube-apiserver, 你需要在所有的上面加上这个参数。
  • 在启动kube-apiserver的时候,在--runtime-config 选项加上admissionregistration.k8s.io/v1alpha1 来打开动态 admission controller 注册 API 的功能。再一次,如果你有多个kube-apiserver, 你需要在所有的上面加上这个参数。

实现 webhook admission controller

caesarxuchao/example-webhook-admission-controller 是一个 webhook admission controller 的例子.

webhook admission controller, 更准取得说是 GenericAdmissionWebhook admission controller 和 apiserver 之间的通信,需要TLS加密。 你需要生更一个CA证书然后用它来对你 webhook admission controller 使用的服务器证书进行签名。 这个CA证书需要在动态注册API的时候通过 externaladmissionhookconfigurations.clientConfig.caBundle 提供给 apiserver

对于每一个 apiserver 接收到的请求,GenericAdmissionWebhook admission controller 会发送一个 admissionReview 到对应的 webhook admission controller。 webhook admission controller 会从 admissionReview.spec 收集 object, oldobject, and userInfo 等信息 并且以 admissionReview 的格式发送一个响应。 在 status 字段填充 admission 的结果。

部署 webhook admission controller

caesarxuchao/example-webhook-admission-controller deployment 是一个部署的例子

我们应该通过 deployment API 来部署 webhook controller 并且需要创建一个 service 作为部署的 前端

运行时配置 webhook admission controller

你可以通过创建 externaladmissionhookconfigurations 来配置启动哪些 webhook admission controllers 和作用于哪些目标对象

首先你需要部署一个 webhook admission controller 并且确保在创建 externaladmissionhookconfigurations 之前工作正常。否则,处理结果将会有条件的接受或者拒绝,这取决于你的 webhook 配置为 fail open 还是 fail closed

下面是一个 externaladmissionhookconfiguration 的例子

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ExternalAdmissionHookConfiguration
metadata:
  name: example-config
externalAdmissionHooks:
- name: pod-image.k8s.io
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    resources:
    - pods
  failurePolicy: Ignore
  clientConfig:
    caBundle: <pem encoded ca cert that signs the server cert used by the webhook>
    service:
      name: <name of the front-end service>
      namespace: <namespace of the front-end service>

对于 apiserver 接受到的请求,如果这个请求满足任何一个 externalAdmissionHook rules 中 的 rule,GenericAdmissionWebhook admission controller 会发送一个 admissionReview 请求到 externalAdmissionHook 来获取 admission 结果。

这里的 rule 和 externalAdmissionHook 中的 rule 非常相似,但有两点不同:

  • 新添加了 operations 字段,来描述这个 webhook 会做什么样的操作
  • 添加 resources 字段支持 resource/subresource 方式的 subresources

确保每一条 rule 内所有的<apiGroup, apiVersions, resources> 展开的元组是有效的,如果不是的话 把他们放在不同的 rule 下面。

你也可以指定 failurePolicy. 在1.7的时候, 系统支持 Ignore 和 Fail 策略,当与webhook admission controller 通信发生错误的时候,GenericAdmissionWebhook 可以根据配置的策略来确定、 是允许还是拒绝请求。

你在创建了 externalAdmissionHookConfiguration 之后,系统会花费几妙中时间响应这个新的配置

原文:https://k8smeetup.github.io/docs/admin/extensible-admission-controllers/

译者:wanghaoran1988

K8S中文社区微信公众号

Kubernetes Webhook 模式

概述

启动引导令牌是一种简单的 bearer token ,这种令牌是在新建集群或者在现有集群中添加新加新节点时使用的。 它被设计成能支持 kubeadm,但是也可以被用在其他上下文中以便用户在 不使用 kubeadm 的情况下启动cluster。它也被设计成可以通过 RBAC 策略,结合Kubelet TLS Bootstrapping 系统进行工作。

启动引导令牌被定义成一个特定类型的secrets (bootstrap.kubernetes.io/token),并存在于 kube-system 命名空间中。然后这些 secrets 会被 API 服务器上的启动引导的认证器读取。 过期的令牌与令牌清除控制器会被控制管理器一起清除。令牌也会被用于创建签名,签名用于 启动引导签名控制器在 “discovery” 进程中特定的 configmap 。

目前,启动引导令牌处于 alpha 阶段,但是预期也不会有大的突破性的变化。

令牌格式

启动引导令牌使用 abcdef.0123456789abcdef 的形式。 更加规范地说,它们必须符合正则表达式 [a-z0-9]{6}\.[a-z0-9]{16}。

令牌的第一部分是 “Token ID” ,它是公共信息。它被用于引用一个 token 用于认证而不会泄漏保密部分。 第二部分是 “Token Secret”,它应该只能被信任方共享。

启用启动引导令牌

所有启动引导令牌的特新在 Kubernetes v1.6 版本中都是默认禁用的。

你可以在 API 服务器上通过 --experimental-bootstrap-token-auth 参数启用启动引导令牌。 你可以在控制管理器上通过 --controllers 参数,比如 --controllers=*,tokencleaner,bootstrapsigner 来启用启动引导令牌。 在使用 kubeadm 时,这个是自动完成的。

HTTPS 调用中的令牌是这样使用的:

Authorization: Bearer 07401b.f395accd246ae52d

启动引导令牌的密文格式

每个合法的令牌是通过一个 kube-system 命名空间中的 secret 隐藏的。 你可以从这里找到完整设计文档。

这是 secret 看起来的样子。注意,base64(string) 表示值应该是通过 base64 编码的。 这里使用的是解码版本以便于阅读。

apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-07401b
  namespace: kube-system
type: bootstrap.kubernetes.io/token
data:
  description: base64(The default bootstrap token generated by 'kubeadm init'.)
  token-id: base64(07401b)
  token-secret: base64(f395accd246ae52d)
  expiration: base64(2017-03-10T03:22:11Z)
  usage-bootstrap-authentication: base64(true)
  usage-bootstrap-signing: base64(true)

secret的类型必须是 bootstrap.kubernetes.io/token ,而名字必须是 bootstrap-token-<token id>。 description 是人类可读的描述,而不应该是机器可读的信息。令牌 ID 和 Secret 是包含在数据字典中的。

The usage-bootstrap-* members indicate what this secret is intended to be used for. A value must be set to true to be enabled. usage-bootstrap-* 成员代表了这个 secret 被用于什么。启用时,值必须设置为 true。

usage-bootstrap-authentication 表示令牌可以用于 API 服务器的认证。认证器会以 system:bootstrap:<Token ID> 认证。它被包含在 system:bootstrappers 组中。 命名和组是故意受限制的,以阻碍用户在启动引导后再使用这些令牌。

usage-bootstrap-signing 表示令牌应该被用于 cluster-info ConfigMap 的签名,就像下面描述的那样。

expiration 数据成员列举了令牌在失效后的时间。这是通过 RFC3339 进行编码的 UTC 时间。 令牌清理控制器会删除过期的令牌。

使用 kubeadm 管理令牌

你可以是用 kubeadm 工具管理正在运行集群的令牌。它会从 kubeadm 创建的集群(/etc/kubernetes/admin.conf) 自动抓取默认管理员密码。你可以对下面命令指定一个另外的 kubeconfig 文件抓取密码,参数使用 --kubeconfig。

  • kubeadm token list 列举了令牌,同时显示了它们的过期时间和用途。
  • kubeadm token create 创建一个新令牌。
    • --description 设置新令牌的描述。
    • --ttl duration 设置令牌从 “现在” 算起到过期的时间增量。 默认是 0 ,也就是不过期。
    • --usages 设置令牌被使用的方式。默认是 signing,authentication。用途在上面已经描述。
  • kubeadm token delete <token id>|<token id>.<token secret> 删除令牌。 令牌可以只用 ID 来确认,或者用整个令牌的值。如果只用 ID,密文不符合的令牌也会被删除。

ConfigMap签名

除了认证之外,令牌可以用于签名 ConfigMap。这在集群启动流程的早期,在客户端信任 API服务器之前被使用。 签名过的 ConfigMap 可以通过共享令牌被认证。

签名过的 ConfigMap 是 kube-public 命名空间中的 cluster-info。 典型的工作流中,客户端读取这个 ConfigMap 而不管认证和 TLS 报错。 它会通过 ConfigMap 中嵌入的签名校验 ConfigMap 的载荷。

ConfigMap 会是这个样子的:

apiVersion: v1
kind: ConfigMap
metadata:
  name: cluster-info
  namespace: kube-public
data:
  jws-kubeconfig-07401b: eyJhbGciOiJIUzI1NiIsImtpZCI6IjA3NDAxYiJ9..tYEfbo6zDNo40MQE07aZcQX2m3EB2rO3NuXtxVMYm9U
  kubeconfig: |
    apiVersion: v1
    clusters:
    - cluster:
        certificate-authority-data: <really long certificate data>
        server: https://10.138.0.2:6443
      name: ""
    contexts: []
    current-context: ""
    kind: Config
    preferences: {}
    users: []

ConfigMap 的 kubeconfig 成员是一个填好了集群信息的配置文件。 这里主要交换的信息是 certificate-authority-data。在将来可能会被扩展。

签名是一个 JWS 签名,使用了 “detached” 模式。为了检验签名,用户应该按照 JWS 规则 (base64 编码而忽略结尾的 =)对 kubeconfig 载荷进行编码。那样编码过载荷会被通过插入 JWS 并存在于两个点的中间 ,用于形成一个完整的 JWS。你可以使用令牌的完整信息(比如 07401b.f395accd246ae52d)作为共享密钥, 通过 HS256 方式 (HMAC-SHA256) 对 JWS 进行校验。 用户 必须 确保使用了 HS256。

原文:https://k8smeetup.github.io/docs/admin/bootstrap-tokens/

译者:zhangqx2010

K8S中文社区微信公众号