Kubernetes 使用 PodPreset 将信息注入 Pods

在 pod 创建时,用户可以使用 podpreset 对象将特定信息注入 pod 中,这些信息可以包括 secret、 卷、 卷挂载和环境变量。

查看 PodPreset 提案 了解更多信息。

什么是 Pod Preset?

Pod Preset 是一种 API 资源,在 pod 创建时,用户可以用它将额外的运行时需求信息注入 pod。 使用标签选择器(label selector)来指定 Pod Preset 所适用的 pod。 查看更多关于 标签选择器 的信息。

使用 Pod Preset 使得 pod 模板编写者不必显式地为每个 pod 设置信息。 这样,使用特定服务的 pod 模板编写者不需要了解该服务的所有细节。

准入控制

准入控制 是指 Kubernetes 如何将 Pod Preset 应用于接收到的创建请求中。 当出现创建请求时,系统会执行以下操作:

  1. 检索全部可用 PodPresets 。
  2. 对 PodPreset 的标签选择器和要创建的 pod 进行匹配。
  3. 尝试合并 PodPreset 中定义的各种资源,并注入要创建的 pod。
  4. 发生错误时抛出事件,该事件记录了 pod 信息合并错误,同时_不注入_ PodPreset 信息创建 pod。

行为

当 PodPreset 应用于一个或多个 Pod 时, Kubernetes 修改 pod spec。 对于 Env、 EnvFrom 和 VolumeMounts 的改动, Kubernetes 修改 pod 中所有容器的规格, 对于卷的改动,Kubernetes 修改 Pod spec。

Kubernetes 为改动的 pod spec 添加注解,来表明它被 PodPreset 所修改。 注解形如: podpreset.admission.kubernetes.io/podpreset-<pod-preset name>": "<resource version>"。

启用 Pod Preset

为了在集群中使用 Pod Preset,必须确保以下内容

  1. 已启用 api 类型 settings.k8s.io/v1alpha1/podpreset
  2. 已启用准入控制器 PodPreset
  3. 已定义 pod preset

为 Pod 禁用 Pod Preset

在一些情况下,用户不希望 pod 被 pod preset 所改动,这时,用户可以在 pod spec 中添加形如 podpreset.admission.kubernetes.io/exclude: "true" 的注解。

创建 Pod Preset

简单的 Pod Spec 示例

这里是一个简单的示例,展示了如何通过 Pod Preset 修改 Pod spec 。

用户提交的 pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
spec:
  containers:
    - name: website
      image: ecorp/website
      ports:
        - containerPort: 80

Pod Preset 示例:

kind: PodPreset
apiVersion: settings.k8s.io/v1alpha1
metadata:
  name: allow-database
  namespace: myns
spec:
  selector:
    matchLabels:
      role: frontend
  env:
    - name: DB_PORT
      value: "6379"
  volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
    - name: cache-volume
      emptyDir: {}

通过准入控制器后的 Pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
  annotations:
    podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
  containers:
    - name: website
      image: ecorp/website
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
      ports:
        - containerPort: 80
      env:
        - name: DB_PORT
          value: "6379"
  volumes:
    - name: cache-volume
      emptyDir: {}

带有 ConfigMap 的 Pod Spec 示例

这里的示例展示了如何通过 Pod Preset 修改 Pod spec,Pod Preset 中定义了 ConfigMap 作为环境变量取值来源。

用户提交的 pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
spec:
  containers:
    - name: website
      image: ecorp/website
      ports:
        - containerPort: 80

用户提交的 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: etcd-env-config
data:
  number_of_members: "1"
  initial_cluster_state: new
  initial_cluster_token: DUMMY_ETCD_INITIAL_CLUSTER_TOKEN
  discovery_token: DUMMY_ETCD_DISCOVERY_TOKEN
  discovery_url: http://etcd_discovery:2379
  etcdctl_peers: http://etcd:2379
  duplicate_key: FROM_CONFIG_MAP
  REPLACE_ME: "a value"

Pod Preset 示例:

kind: PodPreset
apiVersion: settings.k8s.io/v1alpha1
metadata:
  name: allow-database
  namespace: myns
spec:
  selector:
    matchLabels:
      role: frontend
  env:
    - name: DB_PORT
      value: 6379
    - name: duplicate_key
      value: FROM_ENV
    - name: expansion
      value: $(REPLACE_ME)
  envFrom:
    - configMapRef:
        name: etcd-env-config
  volumeMounts:
    - mountPath: /cache
      name: cache-volume
    - mountPath: /etc/app/config.json
      readOnly: true
      name: secret-volume
  volumes:
    - name: cache-volume
      emptyDir: {}
    - name: secret-volume
      secret:
         secretName: config-details

通过准入控制器后的 Pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
  annotations:
    podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
  containers:
    - name: website
      image: ecorp/website
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
        - mountPath: /etc/app/config.json
          readOnly: true
          name: secret-volume
      ports:
        - containerPort: 80
      env:
        - name: DB_PORT
          value: "6379"
        - name: duplicate_key
          value: FROM_ENV
        - name: expansion
          value: $(REPLACE_ME)
      envFrom:
        - configMapRef:
          name: etcd-env-config
  volumes:
    - name: cache-volume
      emptyDir: {}
    - name: secret-volume
      secret:
         secretName: config-details

带有 Pod Spec 的 ReplicaSet 示例

以下示例展示了(通过 ReplicaSet 创建 pod 后)只有 pod spec 会被 Pod Preset 所修改。

用户提交的 ReplicaSet:

apiVersion: settings.k8s.io/v1alpha1
kind: ReplicaSet
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
    matchExpressions:
      - {key: tier, operator: In, values: [frontend]}
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
          - name: GET_HOSTS_FROM
            value: dns
        ports:
          - containerPort: 80

Pod Preset 示例:

kind: PodPreset
apiVersion: settings.k8s.io/v1alpha1
metadata:
  name: allow-database
  namespace: myns
spec:
  selector:
    matchLabels:
      tier: frontend
  env:
    - name: DB_PORT
      value: "6379"
  volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
    - name: cache-volume
      emptyDir: {}

通过准入控制器后的 Pod spec:

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: guestbook
    tier: frontend
  annotations:
    podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
  containers:
  - name: php-redis
    image: gcr.io/google_samples/gb-frontend:v3
    resources:
      requests:
        cpu: 100m
        memory: 100Mi
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
    env:
    - name: GET_HOSTS_FROM
      value: dns
    - name: DB_PORT
      value: "6379"
    ports:
    - containerPort: 80
  volumes:
  - name: cache-volume
    emptyDir: {}

多 PodPreset 示例

这里的示例展示了如何通过多个 Pod 注入策略修改 Pod spec。

用户提交的 pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
spec:
  containers:
    - name: website
      image: ecorp/website
      ports:
        - containerPort: 80

Pod Preset 示例:

kind: PodPreset
apiVersion: settings.k8s.io/v1alpha1
metadata:
  name: allow-database
  namespace: myns
spec:
  selector:
    matchLabels:
      role: frontend
  env:
    - name: DB_PORT
      value: "6379"
  volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
    - name: cache-volume
      emptyDir: {}

另一个 Pod Preset:

kind: PodPreset
apiVersion: settings.k8s.io/v1alpha1
metadata:
  name: proxy
  namespace: myns
spec:
  selector:
    matchLabels:
      role: frontend
  volumeMounts:
    - mountPath: /etc/proxy/configs
      name: proxy-volume
  volumes:
    - name: proxy-volume
      emptyDir: {}

通过准入控制器后的 Pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
  annotations:
    podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
    podpreset.admission.kubernetes.io/podpreset-proxy: "resource version"
spec:
  containers:
    - name: website
      image: ecorp/website
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
        - mountPath: /etc/proxy/configs
          name: proxy-volume
      ports:
        - containerPort: 80
      env:
        - name: DB_PORT
          value: "6379"
  volumes:
    - name: cache-volume
      emptyDir: {}
    - name: proxy-volume
      emptyDir: {}

冲突示例

这里的示例展示了 Pod Preset 与原 Pod 存在冲突时,Pod spec 不会被修改。

用户提交的 pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
spec:
  containers:
    - name: website
      image: ecorp/website
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
      ports:
  volumes:
    - name: cache-volume
      emptyDir: {}
        - containerPort: 80

Pod Preset 示例:

kind: PodPreset
apiVersion: settings.k8s.io/v1alpha1
metadata:
  name: allow-database
  namespace: myns
spec:
  selector:
    matchLabels:
      role: frontend
  env:
    - name: DB_PORT
      value: "6379"
  volumeMounts:
    - mountPath: /cache
      name: other-volume
  volumes:
    - name: other-volume
      emptyDir: {}

因存在冲突,通过准入控制器后的 Pod spec 不会改变:

apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
spec:
  containers:
    - name: website
      image: ecorp/website
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
      ports:
        - containerPort: 80
  volumes:
    - name: cache-volume
      emptyDir: {}

如果运行 kubectl describe... 用户会看到以下事件:

$ kubectl describe ...
....
Events:
  FirstSeen             LastSeen            Count   From                    SubobjectPath               Reason      Message
  Tue, 07 Feb 2017 16:56:12 -0700   Tue, 07 Feb 2017 16:56:12 -0700 1   {podpreset.admission.kubernetes.io/podpreset-allow-database }    conflict  Conflict on pod preset. Duplicate mountPath /cache.

删除 Pod Preset

一旦用户不再需要 pod preset,可以使用 kubectl 进行删除:

$ kubectl delete podpreset allow-database
podpreset "allow-database" deleted

译者:lichuqiang / 原文链接

K8S中文社区微信公众号

Kubernetes 备份

Kubernetes 集群的状态保存在 etcd 数据存储区中。本页展示了如何对标准 Kubernetes 发行版中的 etcd 进行备份和恢复。至于一些通常保存在持久卷上的应用数据备份并不在此文档进行说明。

Before you begin

本页面假设您有一个 Juju 部署的集群。

etcd 数据快照

etcd 的 snapshot 操作能够让操作员给正在运行的集群数据建立快照,快照数据可用于复制、备份或者迁移到一个新的集群。

juju run-action etcd/0 snapshot target=/mnt/etcd-backups
  • 参数 target:用于存储快照数据的目录。

恢复 etcd 数据

etcd 能够通过 restore 操作从一个集群数据快照中恢复集群数据。这里有一个注意事项:集群必须只能有一个 member,并且需要指定一个具体的目录来恢复集群数据。所以最好能够使用 etcd charm 来部署一个新的集群,并且不要添加任何额外的单元。

juju deploy etcd new-etcd

上面这段代码将会部署一个单独的 etcd 单元,称为 ‘new-etcd’。

juju run-action etcd/0 restore target=/mnt/etcd-backups

当恢复操作完成后,您应评估一下集群的健康状态。如果集群运行良好,就可以按照您的需求来扩展应用程序规模。

  • 参数 target: 保存现有数据的目录地址。
  • 参数 skip-backup: 不要备份任何现有数据。

迁移 etcd 集群

通过使用上述的 snapshot 和 restore 操作,就能很容易地迁移 etcd 集群。

第一步: 给现有的集群建立快照。这个已经封装在 snapshot 操作中。

juju run-action etcd/0 snapshot

结果:

Action queued with id: b46d5d6f-5625-4320-8cda-b611c6ae580c

第二步: 检查操作状态,以便您能抓取快照并且验证 sum。您可以直接使用 copy.cmd 中的结果来下载您刚刚创建的快照数据,copy.cmd 中的结果可以直接复制/粘贴使用。

从节点上下载刚刚创建的快照数据并且验证 sha256 sum

juju show-action-output b46d5d6f-5625-4320-8cda-b611c6ae580c

结果:

results:
  copy:
    cmd: juju scp etcd/0:/home/ubuntu/etcd-snapshots/etcd-snapshot-2016-11-09-02.41.47.tar.gz
      .
  snapshot:
    path: /home/ubuntu/etcd-snapshots/etcd-snapshot-2016-11-09-02.41.47.tar.gz
    sha256: 1dea04627812397c51ee87e313433f3102f617a9cab1d1b79698323f6459953d
    size: 68K
status: completed

拷贝快照数据到本地磁盘并且检查 sha256sum。

juju scp etcd/0:/home/ubuntu/etcd-snapshots/etcd-snapshot-2016-11-09-02.41.47.tar.gz .
sha256sum etcd-snapshot-2016-11-09-02.41.47.tar.gz

第三步: 使用快照数据部署一个新的集群 leader:

juju deploy etcd new-etcd --resource snapshot=./etcd-snapshot-2016-11-09-02.41.47.tar.gz

第四步: 使用在第三步中的快照数据来重新初始化 master:

juju run-action new-etcd/0 restore

已知的不足

缺少 PKI 警告

如果您损坏了 leader - 通过 status 中紧接着 unit number 的 * 号来标识,那么所有的 TLS pki 将会丢失。对于需要请求和注册证书的 unit 将不会有 PKI 迁移。

警告: 如果该项配置缺失那么您将无法从外部访问集群,并且很可能会使得现有的 deployment 出现 x509 证书验证相关的奇怪问题,这些都会对服务端和客户端造成影响。

在一个扩展集群上恢复数据

在一个扩展集群上恢复数据将会导致集群损坏。Etcd 在 unit 启动时开始管理集群,并且将状态保存在 Etcd 中。在恢复快照数据时,etcd 将会初始化一个新的集群 ID,并且丢弃其它 peer 节点以保证快照数据的恢复。请严格遵照上述集群迁移中的恢复操作来进行操作。

译者:tianshapjq / 原文链接

K8S中文社区微信公众号

Kubernetes 控制节点上的CPU管理策略

按照设计,Kubernetes 对 pod 执行相关的很多方面进行了抽象,使得用户不必关心。然而,为了正常运行,有些工作负载要求在延迟和/或性能方面有更强的保证。 为此,kubelet 提供方法来实现更复杂的负载放置策略,同时保持抽象,避免显式的放置指令。

CPU 管理策略

默认情况下,kubelet 使用 CFS 配额 来执行 pod 的 CPU 约束。当节点上运行了很多 CPU 密集的 pod 时,工作负载可能会迁移到不同的 CPU 核,这取决于调度时 pod 是否被扼制,以及哪些 CPU 核是可用的。许多工作负载对这种迁移不敏感,因此无需任何干预即可正常工作。

然而,有些工作负载的性能明显地受到 CPU 缓存亲和性以及调度延迟的影响,对此,kubelet 提供了可选的 CPU 管理策略,来确定节点上的一些分配偏好。

配置

CPU 管理器(CPU Manager)作为 alpha 特性引入 Kubernetes 1.8 版本。 必须在 kubelet 特性开关中显式启用: --feature-gates=CPUManager=true。

CPU 管理策略通过 kubelet 参数 --cpu-manager-policy 来指定, 有两种支持策略:

  • none:默认策略,表示现有的调度行为。
  • static:允许为节点上具有某些资源特征的 pod 赋予增强的 CPU 亲和性和独占性。

CPU管理器定期通过 CRI 写入资源更新,以保证内存中 CPU 分配与 cgroupfs 一致。同步频率通过新增的 Kubelet 配置参数 --cpu-manager-reconcile-period 来设置。 如不指定,默认与 --node-status-update-frequency 的周期相同。

None 策略

none 策略显式地启用现有的默认 CPU 亲和方案,不提供操作系统调度器默认行为之外的亲和性策略。 通过 CFS 配额来实现 Guaranteed pods 的 CPU 使用限制。

Static 策略

static 策略针对具有整数型 CPU requests 的 pod ,它允许该类 pod 中的容器访问节点上的独占 CPU 资源。这种独占性是使用 cpuset cgroup 控制器 来实现的。

注意: 诸如容器运行时和 kubelet 本身的系统服务可以继续在这些独占 CPU 上运行。独占性仅针对其他 pod。

注意: 该策略的 alpha 版本不保证 Kubelet 重启前后的静态独占性分配。

该策略管理一个共享 CPU 资源池,最初,该资源池包含节点上所有的 CPU 资源。可用的独占性 CPU 资源数量等于节点的 CPU 总量减去通过 --kube-reserved 或 --system-reserved 参数保留的 CPU 。通过这些参数预留的 CPU 是以整数方式,按物理内核 ID 升序从初始共享池获取的。 共享池是 BestEffort 和 Burstable pod 运行的 CPU 集合。Guaranteed pod 中的容器,如果声明了非整数值的 CPU requests ,也将运行在共享池的 CPU 上。只有 Guaranteed pod 中,指定了整数型 CPU requests 的容器,才会被分配独占 CPU 资源。

注意: 当启用 static 策略时,要求使用 --kube-reserved 和/或 --system-reserved 来保证预留的 CPU 值大于零。 这是因为零预留 CPU 值可能使得共享池变空。

当 Guaranteed pod 调度到节点上时,如果其容器符合静态分配要求,相应的 CPU 会被从共享池中移除,并放置到容器的 cpuset 中。因为这些容器所使用的 CPU 受到调度域本身的限制,所以不需要使用 CFS 配额来进行 CPU 的绑定。换言之,容器 cpuset 中的 CPU 数量与 pod 规格中指定的整数型 CPU limit 相等。这种静态分配增强了 CPU 亲和性,减少了 CPU 密集的工作负载在节流时引起的上下文切换。

考虑以下 Pod 规格的容器:

spec:
  containers:
  - name: nginx
    image: nginx

该 pod 属于 BestEffort 服务质量类型,因为其未指定 requests 或 limits 值。 所以该容器运行在共享 CPU 池中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

该 pod 属于 Burstable 服务质量类型,因为其资源 requests 不等于 limits, 且未指定 cpu 数量。所以该容器运行在共享 CPU 池中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "100Mi"
        cpu: "1"

该 pod 属于 Burstable 服务质量类型,因为其资源 requests 不等于 limits。所以该容器运行在共享 CPU 池中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "200Mi"
        cpu: "2"

该 pod 属于 Guaranteed 服务质量类型,因为其 requests 值与 limits相等。 同时,容器对 CPU 资源的限制值是一个大于或等于 1 的整数值。所以,该 nginx 容器被赋予 2 个独占 CPU。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "1.5"
      requests:
        memory: "200Mi"
        cpu: "1.5"

该 pod 属于 Guaranteed 服务质量类型,因为其 requests 值与 limits相等。但是容器对 CPU 资源的限制值是一个小数。所以该容器运行在共享 CPU 池中。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"

该 pod 属于 Guaranteed 服务质量类型,因其指定了 limits 值, 同时当未显式指定时,requests 值被设置为与 limits 值相等。同时,容器对 CPU 资源的限制值是一个大于或等于 1 的整数值。所以,该 nginx 容器被赋予 2 个独占 CPU。

译者:lichuqiang / 原文链接

K8S中文社区微信公众号

kubectl Cheat Sheet

更多内容请参考: Kubectl 概览 和 JsonPath 手册

Kubectl 自动补全

$ source <(kubectl completion bash) # setup autocomplete in bash, bash-completion package should be installed first.
$ source <(kubectl completion zsh)  # setup autocomplete in zsh

Kubectl 上下文和配置

设置 kubectl 命令交互的 kubernetes 集群并修改配置信息。参阅 使用 kubeconfig 文件进行跨集群验证 获取关于配置文件的详细信息。

$ kubectl config view # 显示合并后的 kubeconfig 配置

# 同时使用多个 kubeconfig 文件并查看合并后的配置
$ KUBECONFIG=~/.kube/config:~/.kube/kubconfig2 kubectl config view

# 获取 e2e 用户的密码
$ kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'

$ kubectl config current-context              # 显示当前的上下文
$ kubectl config use-context my-cluster-name  # 设置默认上下文为 my-cluster-name

# 向 kubeconf 中增加支持基本认证的新集群
$ kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword

# 使用指定的用户名和 namespace 设置上下文
$ kubectl config set-context gce --user=cluster-admin --namespace=foo \
  && kubectl config use-context gce

创建对象

Kubernetes 的清单文件可以使用 json 或 yaml 格式定义。可以以 .yaml、.yml、或者 .json 为扩展名。

$ kubectl create -f ./my-manifest.yaml           # 创建资源
$ kubectl create -f ./my1.yaml -f ./my2.yaml     # 使用多个文件创建资源
$ kubectl create -f ./dir                        # 使用目录下的所有清单文件来创建资源
$ kubectl create -f https://git.io/vPieo         # 使用 url 来创建资源
$ kubectl run nginx --image=nginx                # 启动一个 nginx 实例
$ kubectl explain pods,svc                       # 获取 pod 和 svc 的文档

# 从 stdin 输入中创建多个 YAML 对象
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep-less
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000"
EOF

# 创建包含几个 key 的 Secret
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: $(echo "s33msi4" | base64)
  username: $(echo "jane" | base64)
EOF

显示和查找资源

# Get commands with basic output
$ kubectl get services                          # 列出所有 namespace 中的所有 service
$ kubectl get pods --all-namespaces             # 列出所有 namespace 中的所有 pod
$ kubectl get pods -o wide                      # 列出所有 pod 并显示详细信息
$ kubectl get deployment my-dep                 # 列出指定 deployment
$ kubectl get pods --include-uninitialized      # 列出该 namespace 中的所有 pod 包括未初始化的

# 使用详细输出来描述命令
$ kubectl describe nodes my-node
$ kubectl describe pods my-pod

$ kubectl get services --sort-by=.metadata.name # List Services Sorted by Name

# 根据重启次数排序列出 pod
$ kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'

# 获取所有具有 app=cassandra 的 pod 中的 version 标签
$ kubectl get pods --selector=app=cassandra rc -o \
  jsonpath='{.items[*].metadata.labels.version}'

# 获取所有节点的 ExternalIP
$ kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'

# 列出属于某个 PC 的 Pod 的名字
# “jq”命令用于转换复杂的 jsonpath,参考 https://stedolan.github.io/jq/
$ sel=${$(kubectl get rc my-rc --output=json | jq -j '.spec.selector | to_entries | .[] | "\(.key)=\(.value),"')%?}
$ echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name})

# 查看哪些节点已就绪
$ JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}' \
 && kubectl get nodes -o jsonpath="$JSONPATH" | grep "Ready=True"

# 列出当前 Pod 中使用的 Secret
$ kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq

更新资源

$ kubectl rolling-update frontend-v1 -f frontend-v2.json           # 滚动更新 pod frontend-v1
$ kubectl rolling-update frontend-v1 frontend-v2 --image=image:v2  # 更新资源名称并更新镜像
$ kubectl rolling-update frontend --image=image:v2                 # 更新 frontend pod 中的镜像
$ kubectl rolling-update frontend-v1 frontend-v2 --rollback        # 退出已存在的进行中的滚动更新
$ cat pod.json | kubectl replace -f -                              # 基于 stdin 输入的 JSON 替换 pod

# 强制替换,删除后重新创建资源。会导致服务中断。
$ kubectl replace --force -f ./pod.json

# 为 nginx RC 创建服务,启用本地 80 端口连接到容器上的 8000 端口
$ kubectl expose rc nginx --port=80 --target-port=8000

# 更新单容器 pod 的镜像版本(tag)到 v4
$ kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f -

$ kubectl label pods my-pod new-label=awesome                      # 添加标签
$ kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq       # 添加注解
$ kubectl autoscale deployment foo --min=2 --max=10                # 自动扩展 deployment “foo”

修补资源

使用策略合并补丁并修补资源。

$ kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}' # 部分更新节点

# 更新容器镜像; spec.containers[*].name 是必须的,因为这是合并的关键字
$ kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'

# 使用具有位置数组的 json 补丁更新容器镜像
$ kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'

# 使用具有位置数组的 json 补丁禁用 deployment 的 livenessProbe
$ kubectl patch deployment valid-deployment  --type json   -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'

编辑资源

在编辑器中编辑任何 API 资源。

$ kubectl edit svc/docker-registry                      # 编辑名为 docker-registry 的 service
$ KUBE_EDITOR="nano" kubectl edit svc/docker-registry   # 使用其它编辑器

Scale资源

$ kubectl scale --replicas=3 rs/foo                                 # Scale a replicaset named 'foo' to 3
$ kubectl scale --replicas=3 -f foo.yaml                            # Scale a resource specified in "foo.yaml" to 3
$ kubectl scale --current-replicas=2 --replicas=3 deployment/mysql  # If the deployment named mysql's current size is 2, scale mysql to 3
$ kubectl scale --replicas=5 rc/foo rc/bar rc/baz                   # Scale multiple replication controllers

删除资源

$ kubectl delete -f ./pod.json                                              # 删除 pod.json 文件中定义的类型和名称的 pod
$ kubectl delete pod,service baz foo                                        # 删除名为“baz”的 pod 和名为“foo”的 service
$ kubectl delete pods,services -l name=myLabel                              # 删除具有 name=myLabel 标签的 pod 和 serivce
$ kubectl delete pods,services -l name=myLabel --include-uninitialized      # 删除具有 name=myLabel 标签的 pod 和 service,包括尚未初始化的
$ kubectl -n my-ns delete po,svc --all                                      # 删除 my-ns namespace 下的所有 pod 和 serivce,包括尚未初始化的

与运行中的 Pod 交互

$ kubectl logs my-pod                                 # dump 输出 pod 的日志(stdout)
$ kubectl logs my-pod -c my-container                 # dump 输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用)
$ kubectl logs -f my-pod                              # 流式输出 pod 的日志(stdout)
$ kubectl logs -f my-pod -c my-container              # 流式输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用)
$ kubectl run -i --tty busybox --image=busybox -- sh  # 交互式 shell 的方式运行 pod
$ kubectl attach my-pod -i                            # 连接到运行中的容器
$ kubectl port-forward my-pod 5000:6000               # 转发 pod 中的 6000 端口到本地的 5000 端口
$ kubectl exec my-pod -- ls /                         # 在已存在的容器中执行命令(只有一个容器的情况下)
$ kubectl exec my-pod -c my-container -- ls /         # 在已存在的容器中执行命令(pod 中有多个容器的情况下)
$ kubectl top pod POD_NAME --containers               # 显示指定 pod 和容器的指标度量

与节点和集群交互

$ kubectl cordon my-node                                                # 标记 my-node 不可调度
$ kubectl drain my-node                                                 # 清空 my-node 以待维护
$ kubectl uncordon my-node                                              # 标记 my-node 可调度
$ kubectl top node my-node                                              # 显示 my-node 的指标度量
$ kubectl cluster-info                                                  # 显示 master 和服务的地址
$ kubectl cluster-info dump                                             # 将当前集群状态输出到 stdout                                    
$ kubectl cluster-info dump --output-directory=/path/to/cluster-state   # 将当前集群状态输出到 /path/to/cluster-state

# 如果该键和影响的污点(taint)已存在,则使用指定的值替换
$ kubectl taint nodes foo dedicated=special-user:NoSchedule

资源类型

下表列出的是 kubernetes 中所有支持的类型和缩写的别名。

资源类型 缩写别名
clusters
componentstatuses cs
configmaps cm
daemonsets ds
deployments deploy
endpoints ep
event ev
horizontalpodautoscalers hpa
ingresses ing
jobs
limitranges limits
namespaces ns
networkpolicies
nodes no
statefulsets
persistentvolumeclaims pvc
persistentvolumes pv
pods po
podsecuritypolicies psp
podtemplates
replicasets rs
replicationcontrollers rc
resourcequotas quota
cronjob
secrets
serviceaccount sa
services svc
storageclasses
thirdpartyresources

格式化输出

要以特定的格式向终端窗口输出详细信息,可以在 kubectl 命令中添加 -o 或者 -output 标志。

输出格式 描述
-o=custom-columns=<spec> 使用逗号分隔的自定义列列表打印表格
-o=custom-columns-file=<filename> 使用 文件中的自定义列模板打印表格
-o=json 输出 JSON 格式的 API 对象
-o=jsonpath=<template> 打印 jsonpath 表达式中定义的字段
-o=jsonpath-file=<filename> 打印由 文件中的 [jsonpath](/docs/user-guide/jsonpath) 表达式定义的字段
-o=name 仅打印资源名称
-o=wide 以纯文本格式输出任何附加信息,对于 Pod ,包含节点名称
-o=yaml 输出 YAML 格式的 API 对象

Kubectl 详细输出与调试

使用 -v 或 --v 标志跟着一个整数来指定日志级别。这里 描述了通用的 kubernetes 日志约定和相关的日志级别。

详细等级 描述
--v=0 总是对操作人员可见。
--v=1 合理的默认日志级别,如果您不需要详细输出。
--v=2 可能与系统的重大变化相关的,有关稳定状态的信息和重要的日志信息。这是对大多数系统推荐的日志级别。
--v=3 有关更改的扩展信息。
--v=4 调试级别详细输出。
--v=6 显示请求的资源。
--v=7 显示HTTP请求的header。
--v=8 显示HTTP请求的内容。

译者:pigletfly / 原文链接

K8S中文社区微信公众号

Kubernetes 使用 kubeconfig 文件组织集群访问

kubeconfig 文件用于组织关于集群、用户、命名空间和认证机制的信息。命令行工具 kubectl 从 kubeconfig 文件中得到它要选择的集群以及跟集群 API server 交互的信息。

注意: 用于配置集群访问信息的文件叫作 kubeconfig 文件,这是一种引用配置文件的通用方式,并不是说它的文件名就是 kubeconfig。

默认情况下,kubectl 会从 $HOME/.kube 目录下查找文件名为 config 的文件。您可以通过设置环境变量 KUBECONFIG 或者通过设置 --kubeconfig 去指定其它 kubeconfig 文件。

关于怎么样一步一步去创建和配置 kubeconfig 文件,请查看 配置访问多个集群

支持多个集群、用户和身份验证机制

假设您有几个集群,并且用户和组件以多种方式进行身份验证。例如:

  • 运行中的 kubelet 可能使用证书进行身份认证。
  • 用户可能使用令牌进行身份验证。
  • 管理员可能有一组提供给各个用户的证书。

使用 kubeconfig 文件,可以组织您的集群、用户和命名空间的信息。并且,您还可以定义 context,以便快速轻松地在集群和命名空间之间进行切换。

Context

kubeconfig 文件可以包含 context 元素,每个 context 都是一个由(集群、命名空间、用户)描述的三元组。您可以使用 kubectl config use-context 去设置当前的 context。命令行工具 kubectl 与当前 context 中指定的集群和命名空间进行通信,并且使用当前 context 中包含的用户凭证。

环境变量 KUBECONFIG

环境变量 KUBECONFIG 保存一个 kubeconfig 文件列表。对于 Linux 和 Mac 系统,列表使用冒号将文件名进行分隔;对于 Windows 系统,则以分号分隔。环境变量 KUBECONFIG 不是必需的,如果它不存在,kubectl 就使用默认的 kubeconfig 文件 $HOME/.kube/config。

如果环境变量 KUBECONFIG 存在,那么 kubectl 使用的有效配置,是环境变量 KUBECONFIG 中列出的所有文件融合之后的结果。

融合 kubeconfig 文件

想要查看您的配置,请输入命令:

kubectl config view

如前所述,输出的内容可能来自单个 kubeconfig 文件,也可能是多个 kubeconfig 文件融合之后的结果。

当配置是由多个 kubeconfig 文件融合而成时,kubectl 使用的规则如下:

  1. 如果设置了 --kubeconfig,那么只使用指定的文件,不需要融合。该标志只允许设置一次。如果设置了环境变量 KUBECONFIG,那么应该融合文件之后再来使用。 根据如下规则融合环境变量 KUBECONFIG 中列出的文件:
    • 忽略空的文件名。
    • 文件内容存在不能反序列化的情况时,融合出错。
    • 多个文件设置了特定的值或者映射键时,以第一个查找到的文件中的内容为准。
    • 永远不要更改值或映射键。 例如:保存第一个文件的 context,将其设置为 current-context。 例如:如果两个文件中都指定了 red-user,那么只使用第一个指定的 red-user。 即使第二个文件的 red-user 下的条目跟第一个文件中指定的没有冲突,也丢弃它们。

    设置环境变量 KUBECONFIG 的例子,请查看 设置环境变量 KUBECONFIG

    如果 --kubeconfig 和环境变量 KUBECONFIG 都没有设置,则使用默认的 kubeconfig 文件:$HOME/.kube/config,不需要融合。

  2. 确定要使用的 context 时按照以下顺序查找,直到找到一个可用的context:
    1. 如果命令行参数 --context 存在的话,使用它指定的值。
    2. 使用融合 kubeconfig 文件之后的 current-context 。

    如果还未找到可用的 context,此时允许使用空的 context。

  3. 确定集群和用户。此时,可能存在 context,也可能没有。 按照以下顺序查找,直到找到一个可用的集群或用户。该链查找过程运行两次:一次用于查找用户,另一次用于查找集群:
    1. 如果存在命令行参数:--user 或者 --cluster,则使用它们指定的值。
    2. 如果 context 非空,则从 context 中取用户或者集群。

    如果还未找到可用的用户或者集群,此时用户和集群可以为空。

  4. 确定要使用的实际集群信息。此时,集群信息可能存在,也可能不存在。 按照以下顺序查找,选择第一个查找到的内容:
    1. 如果存在命令行参数:--server、--certificate-authority 和 --insecure-skip-tls-verify,则使用它们指定的值。
    2. 融合 kubeconfig 文件后,如果有任何集群属性存在,都使用它们。
    3. 如果没有指定服务位置,则确定集群信息失败。
  5. 确定要使用的实际用户信息。除了每个用户只能使用一个身份验证技术之外,使用与构建集群信息相同的规则来构建用户信息:
    1. 如果存在命令行参数:--client-certificate、--client-key、--username、--password 和 --token,使用它们指定的值。
    2. 融合 kubeconfig 文件后,使用 user 字段。
    3. 如果存在两种矛盾的身份验证技术,则确定用户信息失败。
  6. 对于仍然缺失的任何信息,使用默认值,并潜在地提示身份验证信息。

文件引用

kubeconfig 文件中的文件和路径引用,都是相对 kubeconfig 文件存在的。命令行中的文件引用则是相对于当前工作目录。在文件 $HOME/.kube/config 中,相对路径按照相对关系存储,绝对路径按照绝对关系存储。

译者:pigletfly / 原文链接

K8S中文社区微信公众号

Kubernetes从零开始搭建自定义集群

本指南适用于想要搭建一个定制化 Kubernetes 集群的人员。如果您在 列表 中找到现有的入门指南可以满足您的需求,那么建议使用它们,因为可从他人的经验中获益。但是,如果您使用特定的 IaaS,网络,配置管理或操作系统,同时又不符合这些指南的要求,那么本指南会为您提供所需的步骤大纲。请注意,比起其他预定义的指南,研习本指南需做出相当多的努力。

本指南对那些想要从更高层次了解现有集群安装脚本执行步骤的人员也很有用。

设计和准备

学习

  1. 您应该已经熟悉使用 Kubernetes 集群。建议按照如下入门指南启动一个临时的集群。首先帮您熟悉 CLI(kubectl)和概念(podsservices等)。
  2. 您的工作站应该已经存在 ‘kubectl’。这是完成其他入门指南后的一个附加安装。如果没有,请遵循 说明

Cloud Provider

Kubernetes 的 Cloud Provider 是一个模块,它提供一个管理 TCP 负载均衡,节点(实例)和网络路由的接口。此接口定义在 pkg/cloudprovider/cloud.go。未实现 Cloud Provider 也可以建立自定义集群(例如使用裸机),并不是所有的接口功能都必须实现,这取决于如何在各组件上设置标识。

节点

  • 您可以使用虚拟机或物理机。
  • 虽然可以使用一台机器构建集群,但为了运行所有的例子和测试,至少需要4个节点。
  • 许多入门指南对主节点和常规节点进行区分。 这不是绝对必要的。
  • 节点需要使用 x86_64 架构运行某些版本的 Linux。在其他操作系统和架构上运行是可行的,但本指南不会协助指导。
  • Apiserver 和 etcd 可以运行在1个核心和 1GB RAM 的机器上,这适用于拥有数十个节点的集群。 更大或更活跃的集群可能受益于更多的核心。
  • 其他节点可以配备任何合理的内存和任意数量的内核。它们不需要相同的配置。

网络

网络连接

Kubernetes 有一个独特的 网络模型。

Kubernetes 为每个 pod 分配一个 IP 地址。创建集群时,需要为 Kubernetes 分配一段 IP 以用作 pod 的 IP。最简单的方法是为集群中的每个节点分配不同的 IP 段。 pod 中的进程可以访问其他 pod 的 IP 并与之通信。这种连接可以通过两种方式实现:

  • 使用 overlay 网络
    • overlay 网络通过流量封装(例如 vxlan)来屏蔽 pod 网络的底层网络架构。
    • 封装会降低性能,但具体多少取决于您的解决方案。
  • 不使用 overlay 网络
    • 配置底层网络结构(交换机,路由器等)以熟知 Pod IP 地址。
    • 不需要 overlay 的封装,因此可以实现更好的性能。

选择哪种方式取决于您的环境和需求。有多种方法来实现上述的某种选项:

  • 使用 Kubernetes 调用的网络插件
  • 将网络插件直接编译进 Kubernetes
    • 可以通过 cloud provider 模块的 “Routes” 接口来实现。
    • Google Compute Engine(GCE)和 AWS 指南使用此方法。
  • 为 Kubernetes 配置外部网络
    • 这可以通过手工执行命令或通过一组外部维护的脚本来完成。
    • 您不得不自己实现,此功能可以带来额外的灵活性。

需要为 Pod IP 选择一个地址范围。请注意,Pod IP 尚不支持 IPv6。

  • 多种方法:
    • GCE:每个项目都有自己的 10.0.0.0/8。从该空间为每个 Kubernetes 集群分配子网 /16,多个集群都拥有自己的空间。 每个节点从该网段获取进一步的子网细分。
    • AWS:整个组织使用一个 VPC ,为每个集群划分一个块,或者为不同的集群使用不同的 VPC。
  • 为每个节点的 PodIP 分配同一个 CIDR 子网,或者分配单个大型 CIDR,该大型 CIDR 由每个节点上较小的 CIDR 所组成的。
    • 您一共需要 max-pods-per-node * max-number-of-nodes 个 IP。每个节点配置子网 /24,即每台机器支持 254 个 pods,这是常见的配置。如果 IP 不充足,配置 /26(每个机器62个 pod)甚至是 /27(30个 pod)也是足够的。
    • 例如,使用 10.10.0.0/16 作为集群范围,支持最多256个节点各自使用 10.10.0.0/24 到 10.10.255.0/24 的 IP 范围。
    • 需要使它们路由可达或通过 overlay 连通。

Kubernetes 也为每个 service 分配一个 IP。但是,Service IP 无须路由。在流量离开节点前,kube-proxy 负责将 Service IP 转换为 Pod IP。您需要利用 SERVICE_CLUSTER_IP_RANGE 为 service 分配一段 IP。例如,设置 SERVICE_CLUSTER_IP_RANGE="10.0.0.0/16" 以允许激活 65534 个不同的服务。请注意,您可以增大此范围,但在不中断使用它的 service 和 pod 时,您不能移动该范围(指增大下限或减小上限)。

此外,您需要为主节点选择一个静态 IP。

  • 称为 MASTER_IP。
  • 打开防火墙以允许访问 apiserver 的端口 80 和/或 443。
  • 启用 ipv4 转发,net.ipv4.ip_forward = 1

网络策略

Kubernetes 可以在 Pods 之间使用 网络策略 定义细粒度的网络策略。

并非所有网络提供商都支持 Kubernetes NetworkPolicy API,参阅 使用网络策略 获取更多内容。

集群命名

您应该为集群选择一个名称。为每个集群选择一个简短的名称并在以后的集群使用中将其作为唯一命名。以下几种方式中都会用到集群名称:

  • 通过 kubectl 来区分您想要访问的各种集群。有时候您可能想要第二个集群,比如测试新的 Kubernetes 版本,运行在不同地区的 Kubernetes 等。
  • Kubernetes 集群可以创建 cloud provider 资源(例如AWS ELB),并且不同的集群需要区分每个创建的资源。称之为 CLUSTER_NAME。

软件的二进制文件

您需要以下二进制文件:

  • etcd
  • 以下 Container 运行工具之一:
    • docker
    • rkt
  • Kubernetes
    • kubelet
    • kube-proxy
    • kube-apiserver
    • kube-controller-manager
    • kube-scheduler

下载并解压 Kubernetes 二进制文件

Kubernetes 发行版包括所有的 Kubernetes 二进制文件以及受支持的 etcd 发行版。 您可以使用 Kubernetes 的发行版(推荐)或按照 开发人员文档 中的说明构建您的 Kubernetes 二进制文件。本指南仅涉及使用 Kubernetes 发行版。

下载并解压 最新的发行版。服务器二进制 tar 包不再包含在 Kubernetes 的最终 tar 包中,因此您需要找到并运行 ./kubernetes/cluster/get-kube-binaries.sh 来下载客户端和服务器的二进制文件。 然后找到 ./kubernetes/server/kubernetes-server-linux-amd64.tar.gz 并解压缩。接着在被解压开的目录 ./kubernetes/server/bin 中找到所有必要的二进制文件。

选择镜像

您将在容器之外运行 docker,kubelet 和 kube-proxy,与运行系统守护进程的方式相同,这些程序需要单独的二进制文件。对于 etcd,kube-apiserver,kube-controller-manager 和 kube-scheduler,我们建议您将其作为容器运行,因此需要构建相应的镜像。

获取 Kubernetes 镜像的几种方式:

  • 使用谷歌容器仓库(GCR)上托管的镜像:
    • 例如 gcr.io/google_containers/hyperkube:$TAG,其中 TAG 是最新的版本标签,可在 最新版本页面 上找到。
    • 确保 $TAG 与您使用的 kubelet 和 kube-proxy 的发行版标签相同。
    • [hyperkube](https://releases.k8s.io/ master/cmd/hyperkube)是一个包含全部组件的二进制文件。
      • hyperkube kubelet ... 表示运行 kubelet,hyperkube apiserver ... 表示运行一个 apiserver,以此类推。
  • 构建私有镜像
    • 在使用私有镜像库时很有用
    • 包含诸如 ./kubernetes/server/bin/kube-apiserver.tar 之类的文件,可以使用诸如 docker load -i kube-apiserver.tar 之类的命令将其转换为 docker 镜像。
    • 您可以使用命令 docker images 验证镜像是否加载正确的仓库和标签。

使用 etcd:

  • 使用谷歌容器仓库(GCR)上托管的 gcr.io/google_containers/etcd:2.2.1
  • 使用 Docker Hub 或 Quay.io 上托管的镜像,比如 quay.io/coreos/etcd:v2.2.1。
  • 使用操作系统安装源中的 etcd 发行版。
  • 构建自己的镜像
    • 执行:cd kubernetes/cluster/images/etcd; make

我们建议您使用 Kubernetes 发行版中提供的 etcd。Kubernetes 程序已经使用此版本的 etcd 进行了广泛的测试,而不是与任何其他版本的 etcd。推荐的版本号也可以在 kubernetes/cluster/images/etcd/Makefile 中作为 TAG 的值被找到。

该文档的剩余部分假定镜像标签已被选定并存储在相应的环境变量中。例子(替换最新的标签和适当的仓库源):

  • HYPERKUBE_IMAGE=gcr.io/google_containers/hyperkube:$TAG
  • ETCD_IMAGE=gcr.io/google_containers/etcd:$ETCD_VERSION

安全模型

两种主要的安全方式:

  • 用 HTTP 访问 apiserver
    • 使用防火墙进行安全防护
    • 安装简单
  • 用 HTTPS 访问 apiserver
    • 使用带证书的 https 和用户凭证。
    • 这是推荐的方法
    • 配置证书可能很棘手。

如果遵循 HTTPS 方法,则需要准备证书和凭证。

准备证书

您需要准备几种证书:

  • 作为 HTTPS 服务端的主节点需要一个证书。
  • 作为主节点的客户端,kubelet 可以选择使用证书来认证自己,并通过 HTTPS 提供自己的 API 服务。

除非您打算用一个真正的 CA 生成证书,否则您需要生成一个根证书,并使用它来签署主节点,kubelet 和 kubectl 证书。在 认证文档 中描述了如何做到这一点。

最终您会得到以下文件(稍后将用到这些变量)

  • CA_CERT
    • 放在运行 apiserver 的节点上,例如位于 /srv/kubernetes/ca.crt。
  • MASTER_CERT
    • 被 CA_CERT 签名
    • 放在运行 apiserver 的节点上,例如位于 /srv/kubernetes/server.crt。
  • MASTER_KEY
    • 放在运行 apiserver 的节点上,例如位于 /srv/kubernetes/server.key。
  • KUBELET_CERT
    • 可选
  • KUBELET_KEY
    • 可选

准备凭证

管理员用户(和任何用户)需要:

  • 用于识别他们的令牌或密码。
  • 令牌只是长字母数字的字符串,例如32个字符,生成方式
  • TOKEN=$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64 | tr -d "=+/" | dd bs=32 count=1 2>/dev/null)

您的令牌和密码需要存储在文件中才能被 apiserver 读取。本指南使用 /var/lib/kube-apiserver/known_tokens.csv。此文件的格式在 身份验证文档 中有描述。

为了向客户端分发凭证,Kubernetes 约定将凭证放入 kubeconfig 文件 中。

可以创建管理员的 kubeconfig 文件,如下所示:

  • 如果您已经使用非定制集群的 Kubernetes(例如,遵循入门指南),那么您已拥有 $HOME/.kube/config 文件。
  • 您需要添加证书,密钥和主节点 IP 到 kubeconfig 文件:
    • 如果使用仅防火墙安全选项,则以此方式设置 apiserver:
      • kubectl config set-cluster $CLUSTER_NAME --server=http://$MASTER_IP --insecure-skip-tls-verify=true
    • 否则,请设置 apiserver ip,客户端证书和用户凭证。
      • kubectl config set-cluster $CLUSTER_NAME --certificate-authority=$CA_CERT --embed-certs=true --server=https://$MASTER_IP
      • kubectl config set-credentials $USER --client-certificate=$CLI_CERT --client-key=$CLI_KEY --embed-certs=true --token=$TOKEN
    • 将集群设置为要使用的默认集群
      • kubectl config set-context $CONTEXT_NAME --cluster=$CLUSTER_NAME --user=$USER
      • kubectl config use-context $CONTEXT_NAME

接下来,为 kubelet 和 kube-proxy 创建一个 kubeconfig 文件。创建多少不同文件有几种选择:

  1. 使用与管理员相同的凭证 - 这是最简单的设置。
  2. 所有 kubelet 用一套令牌和 kubeconfig 文件,kube-proxy 用一套,管理端用一套。 - 这是如今在 GCE 上的方式
  3. 每个 kubelet 使用不同的凭证 - 我们正在改进,但所有的细节还没有准备好。

您可以通过拷贝 $HOME/.kube/config、参考 cluster/gce/configure-vm.sh 中的代码或者使用下面的模板来创建这些文件:

apiVersion: v1
kind: Config
users:
- name: kubelet
  user:
    token: ${KUBELET_TOKEN}
clusters:
- name: local
  cluster:
    certificate-authority: /srv/kubernetes/ca.crt
contexts:
- context:
    cluster: local
    user: kubelet
  name: service-account-context
current-context: service-account-context

将 kubeconfig 文件放在每个节点上。以后本指南的例子假设在 /var/lib/kube-proxy/kubeconfig 和 /var/lib/kube-proxy/kubeconfig 中有 kubeconfig。

在节点上配置和安装基础软件

本节讨论如何将机器配置为 Kubernetes 节点。

您应该在每个节点上运行三个守护进程:

  • docker 或者 rkt
  • kubelet
  • kube-proxy

您还需要在安装操作系统后进行各种其他配置。

提示:比较可行的方法是先使用现有的入门指南来设置集群。在集群运行后,您可以从该集群复制 init.d 脚本或 systemd 单元文件,然后修改它们以便在您的自定义集群上使用。

Docker

所需 Docker 的最低版本随 kubelet 版本的更改而变化。推荐使用最新的稳定版。如果版本太旧,Kubelet 将抛出警告并拒绝启动 pod,请尝试更换合适的 Docker 版本。

如果您已经安装过 Docker,但是该节点并没有配置过 Kubernetes,那么节点上可能存在 Docker 创建的网桥和 iptables 规则。您可能需要像下面这样删除这些内容,然后再为 Kubernetes 配置 Docker。

iptables -t nat -F
ip link set docker0 down
ip link delete docker0

配置 docker 的方式取决于您是否为网络选择了可路由的虚拟 IP 或 overlay 网络方式。Docker 的建议选项:

  • 为每个节点的 CIDR 范围创建自己的网桥,将其称为 cbr0,并在 docker 上设置 --bridge=cbr0 选项。
  • 设置 --iptables=false,docker 不会操纵有关主机端口的 iptables(Docker 的旧版本太粗糙,可能会在较新的版本中修复),以便 kube-proxy 管理 iptables 而不是通过 docker。
  • --ip-masq=false
    • 在您把 PodIP 设置为可路由时,需要设置此选项,否则,docker 会将 PodIP 源地址重写为 NodeIP。
    • 某些环境(例如 GCE)需要您对离开云环境的流量进行伪装。这类环境比较特殊。
    • 如果您正在使用 overlay 网络,请参阅这些说明。
  • --mtu=
    • 使用 Flannel 时可能需要该选项,因为 udp 封装会增大数据包
  • --insecure-registry $CLUSTER_SUBNET
    • 使用非 SSL 方式连接到您设立的私有仓库。

如果想增加 docker 的文件打开数量,设置:

  • DOCKER_NOFILE=1000000

此配置方式取决于您节点上的操作系统。例如在 GCE 上,基于 Debian 的发行版使用 /etc/default/docker。

通过 Docker 文档中给出的示例,确保在继续安装其余部分之前,您系统上的 docker 工作正常。

rkt

rkt 是 Docker 外的另一种选择,您只需要安装 Docker 或 rkt 之一。其最低版本要求 v0.5.6

您的节点需要 systemd 来运行 rkt。匹配 rkt v0.5.6 的最小版本是 systemd 215

对 rkt 的网络支持还需要 rkt 元数据服务。您可以使用命令启动 rkt 元数据服务 sudo systemd-run rkt metadata-service

然后,您需要将该参数配置到 kubelet:

  • --container-runtime=rkt

kubelet

所有节点都应该运行 kubelet。参阅 软件的二进制文件。

要考虑的参数:

  • 如果遵循 HTTPS 安全方法:
    • --api-servers=https://$MASTER_IP
    • --kubeconfig=/var/lib/kubelet/kubeconfig
  • 否则,如果采取基于防火墙的安全方法
    • --api-servers=http://$MASTER_IP
  • --config=/etc/kubernetes/manifests
  • --cluster-dns= 指定要设置的 DNS 服务器地址,(请参阅启动群集服务。)
  • --cluster-domain= 指定用于集群 DNS 地址的 dns 域前缀。
  • --docker-root=
  • --root-dir=
  • --configure-cbr0= (如下面所描述的)
  • --register-node (在 节点 文档中描述 )

kube-proxy

所有节点都应该运行 kube-proxy。(并不严格要求在“主”节点上运行 kube-proxy,但保持一致更简单。) 下载使用 kube-proxy 的方法和 kubelet 一样。

要考虑的参数:

  • 如果遵循 HTTPS 安全方法:
    • --master=https://$MASTER_IP
    • --kubeconfig=/var/lib/kube-proxy/kubeconfig
  • 否则,如果采取基于防火墙的安全方法
    • --master=http://$MASTER_IP

网络

每个节点需要分配自己的 CIDR 范围,用于 pod 网络。称为 NODE_X_POD_CIDR。

需要在每个节点上创建一个名为 cbr0 的网桥。在 网络文档 中进一步说明了该网桥。网桥本身需要从 $NODE_X_POD_CIDR 获取一个地址,按惯例是第一个 IP。称为 NODE_X_BRIDGE_ADDR。例如,如果 NODE_X_POD_CIDR 是 10.0.0.0/16,则NODE_X_BRIDGE_ADDR 是 10.0.0.1/16。 注意:由于以后使用这种方式,因此会保留后缀 /16。

  • 推荐自动化方式:
    1. 在 kubelet init 脚本中设置 --configure-cbr0=true 选项并重新启动 kubelet 服务。Kubelet 将自动配置 cbr0。 直到节点控制器设置了 Node.Spec.PodCIDR,网桥才会配置完成。由于您尚未设置 apiserver 和节点控制器,网桥不会立即被设置。
  • 或者使用手动方案:
    1. 在 kubelet 上设置 --configure-cbr0=false 并重新启动。
    2. 创建一个网桥。
         ip link add name cbr0 type bridge
      
    3. 设置适当的 MTU。 注意:MTU 的实际值取决于您的网络环境。
         ip link set dev cbr0 mtu 1460
      
    4. 将节点的网络添加到网桥(docker 将在桥的另一侧)。
         ip addr add $NODE_X_BRIDGE_ADDR dev cbr0
      
    5. 启动网桥
         ip link set dev cbr0 up
      

如果您已经关闭 Docker 的 IP 伪装,以允许pod相互通信,那么您可能需要为集群网络之外的目标 IP 进行伪装。例如:

iptables -t nat -A POSTROUTING ! -d ${CLUSTER_SUBNET} -m addrtype ! --dst-type LOCAL -j MASQUERADE

对于集群外部的流量,这将重写从 PodIP 到节点 IP 的源地址,并且内核 连接跟踪 将确保目的地为节点地址的流量仍可抵达 pod。

注意:以上描述适用于特定的环境。其他环境根本不需要伪装。如 GCE,不允许 Pod IP 向外网发送流量,但在您的 GCE 项目内部之间没有问题。

其他

  • 如果需要,为您的操作系统软件包管理器启用自动升级。
  • 为所有节点组件配置日志轮转(例如使用 logrotate)。
  • 设置活动监视(例如使用supervisord)。
  • 设置卷支持插件(可选)
    • 安装可选卷类型的客户端,例如 GlusterFS 卷的 glusterfs-client。

使用配置管理

之前的步骤都涉及用于设置服务器的“常规”系统管理技术。您可能希望使用配置管理系统来自动执行节点配置过程。在各种入门指南中有 Ansible,Juju 和 CoreOS Cloud Config 的示例 Saltstack

引导集群启动

虽然基础节点服务(kubelet, kube-proxy, docker)还是由传统的系统管理/自动化方法启动和管理,但是 Kubernetes 中其余的 master 组件都由 Kubernetes 配置和管理:

  • 它们的选项在 Pod 定义(yaml 或 json)中指定,而不是 /etc/init.d 文件或 systemd 单元中。
  • 它们由 Kubernetes 而不是 init 运行。

etcd

您需要运行一个或多个 etcd 实例。

  • 高可用且易于恢复 - 运行3或5个 etcd 实例,将其日志写入由持久性存储(RAID,GCE PD)支持的目录,
  • 非高可用,但易于恢复 - 运行一个 etcd 实例,其日志写入由持久性存储(RAID,GCE PD)支持的目录, 注意: 如果实例发生中断可能导致操作中断。
  • 高可用 - 运行3或5个非持久性存储 etcd 实例。 注意: 由于存储被复制,日志可以写入非持久性存储。

运行一个 etcd 实例:

  1. 复制 cluster/saltbase/salt/etcd/etcd.manifest
  2. 按需要进行修改
  3. 通过将其放入 kubelet 清单目录来启动 pod。

Apiserver, Controller Manager, and Scheduler

Apiserver,Controller Manager和 Scheduler 将分别以 pod 形式在主节点上运行。。

对于这些组件,启动它们的步骤类似:

  1. 从所提供的 pod 模板开始。
  2. 将 选取镜像 中的值设置到 HYPERKUBE_IMAGE。
  3. 使用每个模板下面的建议,确定集群需要哪些参数。
  4. 将完成的模板放入 kubelet 清单目录中启动 pod。
  5. 验证 pod 是否启动。

Apiserver pod 模板

{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "kube-apiserver"
  },
  "spec": {
    "hostNetwork": true,
    "containers": [
      {
        "name": "kube-apiserver",
        "image": "${HYPERKUBE_IMAGE}",
        "command": [
          "/hyperkube",
          "apiserver",
          "$ARG1",
          "$ARG2",
          ...
          "$ARGN"
        ],
        "ports": [
          {
            "name": "https",
            "hostPort": 443,
            "containerPort": 443
          },
          {
            "name": "local",
            "hostPort": 8080,
            "containerPort": 8080
          }
        ],
        "volumeMounts": [
          {
            "name": "srvkube",
            "mountPath": "/srv/kubernetes",
            "readOnly": true
          },
          {
            "name": "etcssl",
            "mountPath": "/etc/ssl",
            "readOnly": true
          }
        ],
        "livenessProbe": {
          "httpGet": {
            "scheme": "HTTP",
            "host": "127.0.0.1",
            "port": 8080,
            "path": "/healthz"
          },
          "initialDelaySeconds": 15,
          "timeoutSeconds": 15
        }
      }
    ],
    "volumes": [
      {
        "name": "srvkube",
        "hostPath": {
          "path": "/srv/kubernetes"
        }
      },
      {
        "name": "etcssl",
        "hostPath": {
          "path": "/etc/ssl"
        }
      }
    ]
  }
}

以下是您可能需要设置的一些 apiserver 参数:

  • --cloud-provider= 参阅 cloud providers
  • --cloud-config= 参阅 cloud providers
  • --address=${MASTER_IP} 或者 --bind-address=127.0.0.1 和 --address=127.0.0.1 如果要在主节点上运行代理。
  • --service-cluster-ip-range=$SERVICE_CLUSTER_IP_RANGE
  • --etcd-servers=http://127.0.0.1:4001
  • --tls-cert-file=/srv/kubernetes/server.cert
  • --tls-private-key-file=/srv/kubernetes/server.key
  • --admission-control=$RECOMMENDED_LIST
  • --allow-privileged=true,只有当您信任您的集群用户以 root 身份运行 pod 时。

如果您遵循仅防火墙的安全方法,请使用以下参数:

  • --token-auth-file=/dev/null
  • --insecure-bind-address=$MASTER_IP
  • --advertise-address=$MASTER_IP

如果您使用 HTTPS 方法,请设置:

  • --client-ca-file=/srv/kubernetes/ca.crt
  • --token-auth-file=/srv/kubernetes/known_tokens.csv
  • --basic-auth-file=/srv/kubernetes/basic_auth.csv

pod 使用 hostPath 卷挂载几个节点上的文件系统目录。这样的目的是:

  • 挂载 /etc/ssl 以允许 apiserver 找到 SSL 根证书,以便它可以验证外部服务,例如一个 cloud provider。
    • 如果未使用 cloud provider,则不需要(例如裸机)。
  • 挂载 /srv/kubernetes 以允许 apiserver 读取存储在节点磁盘上的证书和凭证。这些(证书和凭证)也可以存储在永久性磁盘上,例如 GCE PD,或烧录到镜像中。
  • 可选,您也可以挂载 /var/log,并将输出重定向到那里。(未显示在模板中)。
    • 如果您希望使用 journalctl 等工具从根文件系统访问日志,请执行此操作。

待办 文档化 proxy-ssh 的安装

Cloud Provider

Apiserver 支持若干 cloud providers。

  • --cloud-provider 标志的选项有 aws,azure,cloudstack,fake, gce, mesos, openstack, ovirt,photon,rackspace,vsphere 或者不设置。
  • 在使用裸机安装的情况下无需设置。
  • 通过在 这里 贡献代码添加对新 IaaS 的支持。

一些 cloud provider 需要一个配置文件。如果是这样,您需要将配置文件放入 apiserver 镜像或通过 hostPath 挂载。

  • --cloud-config= 在 cloud provider 需要配置文件时设置。
  • 由 aws, gce, mesos, openshift, ovirt 和 rackspace 使用。
  • 您必须将配置文件放入 apiserver 镜像或通过 hostPath 挂载。
  • Cloud 配置文件语法为 Gcfg
  • AWS 格式由 AWSCloudConfig 定义。
  • 其他 cloud provider 的相应文件中也有类似的类型。
  • GCE 例子:在 这个文件 中搜索gce.conf。

Scheduler pod 模板

完成 Scheduler pod 模板:

{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "kube-scheduler"
  },
  "spec": {
    "hostNetwork": true,
    "containers": [
      {
        "name": "kube-scheduler",
        "image": "$HYBERKUBE_IMAGE",
        "command": [
          "/hyperkube",
          "scheduler",
          "--master=127.0.0.1:8080",
          "$SCHEDULER_FLAG1",
          ...
          "$SCHEDULER_FLAGN"
        ],
        "livenessProbe": {
          "httpGet": {
            "scheme": "HTTP",
            "host": "127.0.0.1",
            "port": 10251,
            "path": "/healthz"
          },
          "initialDelaySeconds": 15,
          "timeoutSeconds": 15
        }
      }
    ]
  }
}

通常,调度程序不需要额外的标志。

或者,您也可能需要挂载 /var/log,并重定向输出到这里。

Controller Manager 模板

Controller Manager pod 模板

{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "kube-controller-manager"
  },
  "spec": {
    "hostNetwork": true,
    "containers": [
      {
        "name": "kube-controller-manager",
        "image": "$HYPERKUBE_IMAGE",
        "command": [
          "/hyperkube",
          "controller-manager",
          "$CNTRLMNGR_FLAG1",
          ...
          "$CNTRLMNGR_FLAGN"
        ],
        "volumeMounts": [
          {
            "name": "srvkube",
            "mountPath": "/srv/kubernetes",
            "readOnly": true
          },
          {
            "name": "etcssl",
            "mountPath": "/etc/ssl",
            "readOnly": true
          }
        ],
        "livenessProbe": {
          "httpGet": {
            "scheme": "HTTP",
            "host": "127.0.0.1",
            "port": 10252,
            "path": "/healthz"
          },
          "initialDelaySeconds": 15,
          "timeoutSeconds": 15
        }
      }
    ],
    "volumes": [
      {
        "name": "srvkube",
        "hostPath": {
          "path": "/srv/kubernetes"
        }
      },
      {
        "name": "etcssl",
        "hostPath": {
          "path": "/etc/ssl"
        }
      }
    ]
  }
}

使用 controller manager 时需要考虑的标志:

  • --cluster-cidr=,集群中 pod 的 CIDR 范围。
  • --allocate-node-cidrs=,如果您使用 --cloud-provider=,请分配并设置云提供商上的 pod 的 CIDR。
  • --cloud-provider= 和 --cloud-config 如 apiserver 部分所述。
  • --service-account-private-key-file=/srv/kubernetes/server.key,由 service account 功能使用。
  • --master=127.0.0.1:8080

启动和验证 Apiserver,Scheduler 和 Controller Manager

将每个完成的 pod 模板放入 kubelet 配置目录中(kubelet 的参数 --config= 参数设置的值,通常是 /etc/kubernetes/manifests)。顺序不重要:scheduler 和 controller manager 将重试到 apiserver 的连接,直到它启动为止。

使用 ps 或 docker ps 来验证每个进程是否已经启动。例如,验证 kubelet 是否已经启动了一个 apiserver 的容器:

$ sudo docker ps | grep apiserver:
5783290746d5        gcr.io/google_containers/kube-apiserver:e36bf367342b5a80d7467fd7611ad873            "/bin/sh -c '/usr/lo'"    10 seconds ago      Up 9 seconds                              k8s_kube-apiserver.feb145e7_kube-apiserver-kubernetes-master_default_eaebc600cf80dae59902b44225f2fc0a_225a4695

然后尝试连接到 apiserver:

$ echo $(curl -s http://localhost:8080/healthz)
ok
$ curl -s http://localhost:8080/api
{
  "versions": [
    "v1"
  ]
}

如果您为 kubelet 选择了 --register-node=true 选项,那么它们向 apiserver 自动注册。 您应该很快就可以通过运行 kubectl get nodes 命令查看所有节点。 否则,您需要手动创建节点对象。

启动集群服务

您将希望通过添加集群范围的服务来完成您的 Kubernetes 集群。这些有时被称为 addons,查阅在 管理指南 中的概述。

下面给出了设置每个集群服务的注意事项:

  • 集群 DNS:
    • 许多 Kubernetes 的例子都需要
    • 安装说明
    • 管理指南
  • 集群级日志
    • 集群级日志概述
    • 使用 Elasticsearch 的集群级日志
    • 使用 Stackdriver 的集群级日志
  • 容器资源监控
  • GUI

故障排除

运行 validate-cluster

cluster/kube-up.sh 调用 cluster/validate-cluster.sh 用于确定集群启动是否成功。

使用和输出示例:

KUBECTL_PATH=$(which kubectl) NUM_NODES=3 KUBERNETES_PROVIDER=local cluster/validate-cluster.sh
Found 3 node(s).
NAME                    STATUS    AGE     VERSION
node1.local             Ready     1h      v1.6.9+a3d1dfa6f4335
node2.local             Ready     1h      v1.6.9+a3d1dfa6f4335
node3.local             Ready     1h      v1.6.9+a3d1dfa6f4335
Validate output:
NAME                 STATUS    MESSAGE              ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-1               Healthy   {"health": "true"}
etcd-2               Healthy   {"health": "true"}
etcd-0               Healthy   {"health": "true"}
Cluster validation succeeded

检查 pod 和 service

尝试通过其他入门指南中的 “检查集群” 部分,例如 GCE。您应该看到一些服务。还应该看到 apiserver,scheduler 和 controller-manager 的 “镜像 pod”,以及您启动的任何加载项。

试一试例子

此时,您应该能够运行一个基本的例子,例如 nginx 例子

运行一致性测试

您可能想尝试运行 一致性测试。任何失败都会给一个提示,您需要更多地关注它们。

网络

节点必须能够使用其私有 IP 通讯。从一个节点到另一个节点使用 ping 或 SSH 命令进行验证。

帮助

如果您遇到问题,请参阅 troubleshooting,联系 kubernetes 用户组,或者在 Slack 提问。

支持级别

IaaS 提供商 配置管理 系统 网络 文档 整合 支持级别
任何 任何 任何 任何 文档 社区 (@erictune)

有关所有解决方案的支持级别信息,请参阅图表 解决方案表。

译者:bjdzliu / 原文链接

K8S中文社区微信公众号