codecamp

Kubernetes 管理资源

管理资源

你已经部署了应用并通过服务暴露它。然后呢? Kubernetes 提供了一些工具来帮助管理你的应用部署,包括扩缩容和更新。 我们将更深入讨论的特性包括 配置文件和 标签。

组织资源配置 

许多应用需要创建多个资源,例如 Deployment 和 Service。 可以通过将多个资源组合在同一个文件中(在 YAML 中以 ​---​ 分隔) 来简化对它们的管理。例如:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-svc
  labels:
    app: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

可以用创建单个资源相同的方式来创建多个资源:

kubectl apply -f https://k8s.io/examples/application/nginx-app.yaml
service/my-nginx-svc created
deployment.apps/my-nginx created

资源将按照它们在文件中的顺序创建。 因此,最好先指定服务,这样在控制器(例如 Deployment)创建 Pod 时能够 确保调度器可以将与服务关联的多个 Pod 分散到不同节点。

kubectl create​ 也接受多个 ​-f​ 参数:

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-svc.yaml -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml

还可以指定目录路径,而不用添加多个单独的文件:

kubectl apply -f https://k8s.io/examples/application/nginx/

kubectl ​将读取任何后缀为 ​.yaml​、​.yml​ 或者 ​.json​ 的文件。

建议的做法是,将同一个微服务或同一应用层相关的资源放到同一个文件中, 将同一个应用相关的所有文件按组存放到同一个目录中。 如果应用的各层使用 DNS 相互绑定,那么你可以将堆栈的所有组件一起部署。

还可以使用 URL 作为配置源,便于直接使用已经提交到 Github 上的配置文件进行部署:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/zh/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx created

kubectl 中的批量操作

资源创建并不是 ​kubectl ​可以批量执行的唯一操作。 ​kubectl ​还可以从配置文件中提取资源名,以便执行其他操作, 特别是删除你之前创建的资源:

kubectl delete -f https://k8s.io/examples/application/nginx-app.yaml
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

在仅有两种资源的情况下,可以使用"资源类型/资源名"的语法在命令行中 同时指定这两个资源:

kubectl delete deployments/my-nginx services/my-nginx-svc

对于资源数目较大的情况,你会发现使用 ​-l​ 或 ​--selector​ 指定筛选器(标签查询)能很容易根据标签筛选资源:

kubectl delete deployment,services -l app=nginx
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

由于 ​kubectl ​用来输出资源名称的语法与其所接受的资源名称语法相同, 你可以使用 ​$()​ 或 ​xargs ​进行链式操作:

kubectl get $(kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service)
kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service | xargs -i kubectl get {}
NAME           TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)      AGE
my-nginx-svc   LoadBalancer   10.0.0.208   <pending>     80/TCP       0s

上面的命令中,我们首先使用 ​examples/application/nginx/​ 下的配置文件创建资源, 并使用 ​-o name​ 的输出格式(以"资源/名称"的形式打印每个资源)打印所创建的资源。 然后,我们通过 ​grep ​来过滤 "service",最后再打印 ​kubectl get​ 的内容。

如果你碰巧在某个路径下的多个子路径中组织资源,那么也可以递归地在所有子路径上 执行操作,方法是在 ​--filename,-f​ 后面指定 ​--recursive​ 或者 ​-R​。

例如,假设有一个目录路径为 ​project/k8s/development​,它保存开发环境所需的 所有清单,并按资源类型组织:

project/k8s/development
├── configmap
│   └── my-configmap.yaml
├── deployment
│   └── my-deployment.yaml
└── pvc
    └── my-pvc.yaml

默认情况下,对 ​project/k8s/development​ 执行的批量操作将停止在目录的第一级, 而不是处理所有子目录。 如果我们试图使用以下命令在此目录中创建资源,则会遇到一个错误:

kubectl apply -f project/k8s/development
error: you must provide one or more resources by argument or filename (.json|.yaml|.yml|stdin)

正确的做法是,在 ​--filename,-f​ 后面标明 ​--recursive​ 或者 ​-R​ 之后:

kubectl apply -f project/k8s/development --recursive
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

--recursive​ 可以用于接受 ​--filename,-f​ 参数的任何操作,例如: ​kubectl {create,get,delete,describe,rollout}​ 等。

有多个 ​-f​ 参数出现的时候,​--recursive​ 参数也能正常工作:

kubectl apply -f project/k8s/namespaces -f project/k8s/development --recursive
namespace/development created
namespace/staging created
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

有效地使用标签

到目前为止我们使用的示例中的资源最多使用了一个标签。 在许多情况下,应使用多个标签来区分集合。

例如,不同的应用可能会为 ​app ​标签设置不同的值。 但是,类似 guestbook 示例 这样的多层应用,还需要区分每一层。前端可以带以下标签:

     labels:
        app: guestbook
        tier: frontend

Redis 的主节点和从节点会有不同的 ​tier ​标签,甚至还有一个额外的 ​role ​标签:

     labels:
        app: guestbook
        tier: backend
        role: master

以及

     labels:
        app: guestbook
        tier: backend
        role: slave

标签允许我们按照标签指定的任何维度对我们的资源进行切片和切块:

kubectl apply -f examples/guestbook/all-in-one/guestbook-all-in-one.yaml
kubectl get pods -Lapp -Ltier -Lrole
NAME                           READY     STATUS    RESTARTS   AGE       APP         TIER       ROLE
guestbook-fe-4nlpb             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-fe-ght6d             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-fe-jpy62             1/1       Running   0          1m        guestbook   frontend   <none>
guestbook-redis-master-5pg3b   1/1       Running   0          1m        guestbook   backend    master
guestbook-redis-slave-2q2yf    1/1       Running   0          1m        guestbook   backend    slave
guestbook-redis-slave-qgazl    1/1       Running   0          1m        guestbook   backend    slave
my-nginx-divi2                 1/1       Running   0          29m       nginx       <none>     <none>
my-nginx-o0ef1                 1/1       Running   0          29m       nginx       <none>     <none>
kubectl get pods -lapp=guestbook,role=slave
NAME                          READY     STATUS    RESTARTS   AGE
guestbook-redis-slave-2q2yf   1/1       Running   0          3m
guestbook-redis-slave-qgazl   1/1       Running   0          3m

金丝雀部署(Canary Deployments) 

另一个需要多标签的场景是用来区分同一组件的不同版本或者不同配置的多个部署。 常见的做法是部署一个使用金丝雀发布来部署新应用版本 (在 Pod 模板中通过镜像标签指定),保持新旧版本应用同时运行。 这样,新版本在完全发布之前也可以接收实时的生产流量。

例如,你可以使用 ​track ​标签来区分不同的版本。

主要稳定的发行版将有一个 ​track ​标签,其值为 ​stable​:

     name: frontend
     replicas: 3
     ...
     labels:
        app: guestbook
        tier: frontend
        track: stable
     ...
     image: gb-frontend:v3

然后,你可以创建 guestbook 前端的新版本,让这些版本的 ​track ​标签带有不同的值 (即 ​canary​),以便两组 Pod 不会重叠:

     name: frontend-canary
     replicas: 1
     ...
     labels:
        app: guestbook
        tier: frontend
        track: canary
     ...
     image: gb-frontend:v4

前端服务通过选择标签的公共子集(即忽略 ​track ​标签)来覆盖两组副本, 以便流量可以转发到两个应用:

  selector:
     app: guestbook
     tier: frontend

你可以调整 ​stable ​和 ​canary ​版本的副本数量,以确定每个版本将接收 实时生产流量的比例(在本例中为 3:1)。 一旦有信心,你就可以将新版本应用的 ​track ​标签的值从 ​canary ​替换为 ​stable​,并且将老版本应用删除。

想要了解更具体的示例,请查看 Ghost 部署教程

更新标签 

有时,现有的 pod 和其它资源需要在创建新资源之前重新标记。 这可以用 ​kubectl label​ 完成。 例如,如果想要将所有 nginx pod 标记为前端层,运行:

kubectl label pods -l app=nginx tier=fe
pod/my-nginx-2035384211-j5fhi labeled
pod/my-nginx-2035384211-u2c7e labeled
pod/my-nginx-2035384211-u3t6x labeled

首先用标签 "app=nginx" 过滤所有的 Pod,然后用 "tier=fe" 标记它们。 想要查看你刚才标记的 Pod,请运行:

kubectl get pods -l app=nginx -L tier
NAME                        READY     STATUS    RESTARTS   AGE       TIER
my-nginx-2035384211-j5fhi   1/1       Running   0          23m       fe
my-nginx-2035384211-u2c7e   1/1       Running   0          23m       fe
my-nginx-2035384211-u3t6x   1/1       Running   0          23m       fe

这将输出所有 "app=nginx" 的 Pod,并有一个额外的描述 Pod 的 tier 的标签列 (用参数 ​-L​ 或者 ​--label-columns​ 标明)。

更新注解 

有时,你可能希望将注解附加到资源中。注解是 API 客户端(如工具、库等) 用于检索的任意非标识元数据。这可以通过 ​kubectl annotate​ 来完成。例如:

kubectl annotate pods my-nginx-v4-9gw19 description='my frontend running nginx'
kubectl get pods my-nginx-v4-9gw19 -o yaml
apiVersion: v1
kind: pod
metadata:
  annotations:
    description: my frontend running nginx
...

扩缩你的应用

当应用上的负载增长或收缩时,使用 ​kubectl ​能够实现应用规模的扩缩。 例如,要将 nginx 副本的数量从 3 减少到 1,请执行以下操作:

kubectl scale deployment/my-nginx --replicas=1
deployment.extensions/my-nginx scaled

现在,你的 Deployment 管理的 Pod 只有一个了。

kubectl get pods -l app=nginx
NAME                        READY     STATUS    RESTARTS   AGE
my-nginx-2035384211-j5fhi   1/1       Running   0          30m

想要让系统自动选择需要 nginx 副本的数量,范围从 1 到 3,请执行以下操作:

kubectl autoscale deployment/my-nginx --min=1 --max=3
horizontalpodautoscaler.autoscaling/my-nginx autoscaled

就地更新资源 

有时,有必要对你所创建的资源进行小范围、无干扰地更新。

kubectl apply

建议在源代码管理中维护一组配置文件 (参见配置即代码), 这样,它们就可以和应用代码一样进行维护和版本管理。 然后,你可以用 ​kubectl apply​ 将配置变更应用到集群中。

这个命令将会把推送的版本与以前的版本进行比较,并应用你所做的更改, 但是不会自动覆盖任何你没有指定更改的属性。

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx configured

注意,​kubectl apply​ 将为资源增加一个额外的注解,以确定自上次调用以来对配置的更改。 执行时,​kubectl apply​ 会在以前的配置、提供的输入和资源的当前配置之间 找出三方差异,以确定如何修改资源。

目前,新创建的资源是没有这个注解的,所以,第一次调用 ​kubectl apply​ 时 将使用提供的输入和资源的当前配置双方之间差异进行比较。 在第一次调用期间,它无法检测资源创建时属性集的删除情况。 因此,kubectl 不会删除它们。

所有后续的 ​kubectl apply​ 操作以及其他修改配置的命令,如 ​kubectl replace​ 和 ​kubectl edit​,都将更新注解,并允许随后调用的 ​kubectl apply​ 使用三方差异进行检查和执行删除。

Note: 想要使用 apply,请始终使用 ​kubectl apply​ 或 ​kubectl create --save-config​ 创建资源。

kubectl edit

或者,你也可以使用 ​kubectl edit​ 更新资源:

kubectl edit deployment/my-nginx

这相当于首先 ​get ​资源,在文本编辑器中编辑它,然后用更新的版本 ​apply ​资源:

kubectl get deployment my-nginx -o yaml > /tmp/nginx.yaml
vi /tmp/nginx.yaml
# do some edit, and then save the file

kubectl apply -f /tmp/nginx.yaml
deployment.apps/my-nginx configured

rm /tmp/nginx.yaml

这使你可以更加容易地进行更重大的更改。 请注意,可以使用 ​EDITOR ​或 ​KUBE_EDITOR ​环境变量来指定编辑器。

想要了解更多信息,请参考 kubectl edit 文档。

kubectl patch

你可以使用 ​kubectl patch​ 来更新 API 对象。此命令支持 JSON patch、 JSON merge patch、以及 strategic merge patch。

破坏性的更新 

在某些情况下,你可能需要更新某些初始化后无法更新的资源字段,或者你可能只想立即进行递归更改, 例如修复 Deployment 创建的不正常的 Pod。若要更改这些字段,请使用 ​replace --force​, 它将删除并重新创建资源。在这种情况下,你可以修改原始配置文件:

kubectl replace -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml --force
deployment.apps/my-nginx deleted
deployment.apps/my-nginx replaced

在不中断服务的情况下更新应用

在某些时候,你最终需要更新已部署的应用,通常都是通过指定新的镜像或镜像标签, 如上面的金丝雀发布的场景中所示。​kubectl ​支持几种更新操作, 每种更新操作都适用于不同的场景。

我们将指导你通过 Deployment 如何创建和更新应用。

假设你正运行的是 1.14.2 版本的 nginx:

kubectl create deployment my-nginx --image=nginx:1.14.2
deployment.apps/my-nginx created

要更新到 1.16.1 版本,只需使用我们前面学到的 kubectl 命令将 ​.spec.template.spec.containers[0].image​ 从 ​nginx:1.14.2​ 修改为 ​nginx:1.16.1​。

kubectl edit deployment/my-nginx

没错,就是这样!Deployment 将在后台逐步更新已经部署的 nginx 应用。 它确保在更新过程中,只有一定数量的旧副本被开闭,并且只有一定基于所需 Pod 数量的新副本被创建。


Kubernetes 调度器性能调优
Kubernetes 集群网络系统
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

Kubernetes 管理集群

Kubernetes Service

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }