yaml 结构和k8s 中的建构组件

603 阅读5分钟

yaml

在 k8s 中使用 yaml 会带给你几项好处

  • 方便:部署不需要再输入一大堆参数
  • 维护:可以利用版本控制 (git, bitbucket, ..) 追踪变化
  • 弹性:很容易新增或删除项目 使用 yaml 基本上只要弄清楚 map 与 list,大致上就没有阅读或写作上的问题。
$ curl http://localhost:8001
{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "/apis/",
    "/apis/admissionregistration.k8s.io",
    "/apis/admissionregistration.k8s.io/v1alpha1",
    "/apis/apiextensions.k8s.io",
    "/apis/apiextensions.k8s.io/v1beta1",
    "/apis/apiregistration.k8s.io",
    "/apis/apiregistration.k8s.io/v1beta1",
    "/apis/apps",

请注意,查询 APIs 必须要先执行 kubectl proxy 通过认证后才可执行

map

map 是由 key-value 组成。很方便用于描述设定,例如

# 注解内容    <=== 注解就用 # 符号
---          <=== 利用这个来区隔不同物件的设定
apiVersion: v1   <=== 指定使用 v1 版本  api
kind: Pod        <=== 指定类型为 Pod

第一行 --- 是分隔线,如果想在一个 yaml 撰写多个物件设定的话,可以利用 --- 做区分。

如果只有一组设定,---可以省略。 如果熟悉 JSON 的话,上述的设定等于以下 JSON

{
   "apiVersion": "v1",
   "kind": "Pod"
}

再看一个更复杂的结构

apiVersion: v1
kind: Pod
metadata:
  name: web   <=== 这里的意思就是指定 Pod 的名称为 web
  labels:     
    app: web  <===  Pod 打上 app:web 的标记

这里都只是用 map 组合成较复杂的结构,其中 metadata 中包含了 name 与labels,而 labels 又包含了app。习惯上,我们会使用两个空白来描述被包含的内容

对齐与空格数很重要,必须要一致。如果你想使用四个空格,那整份文件就必须使用四个空格来组成

对应的 JSON 会长这样

{
   "apiVersion": "v1",
   "kind": "Pod",
   "metadata":{
   	  "name": "web",
   	  "labels":{
   	    "app": "web"
   	  }
   }
}

list

list 由 - 开头并加入一个空格,例如

args:
- parameters
- "custom message"

JSON 会变成

{
  "args": ["parameters", "custom message"]
}

当然,map 也可以是 list 的成员,例如

spec:
  containers:   <=== 底下的 map 都是 list 的成员
  - name: frontend
    image: nginx
    port:       <=== 底下的 map 都是 list 的成员
    - containerPort: 80

第一个pod yaml

# pod.yaml

---
apiVersion: v1   <=== 指定使用 v1 版本的 api
kind: Pod        <=== 指定类型为 Pod
metadata:        <=== 将 Pod 命名为 web,并加入 app:web 的标记
  name: web
  labels:
    app: web
spec:                <=== 规格描述
  containers:        <=== 描述容器
  - name: frontend   <=== 将容器命名为 frontend
    image: nginx     <=== 使用 nginx 映像档
    ports:
    - containerPort: 80   <=== 指定使用 80 port

还记得如何利用 kubectl 部署 yaml 档吗?试试底下指令

$ kubectl apply -f pod.yaml
pod "web" created

恭喜你!终于完成了第一个 Pod 物件的部署。

k8s 中的建构组件

在 k8s 中有着各式各样不同的物件 (Pod, Service, ...),而这些物件在 k8s 中描述着

  • 运行何种应用程式 (application) 且在哪运行 (node, namespace, ...)
  • 资源使用状态 (resource limits/occupied)
  • 应用程式运行策略,例如是否要重启 (restart policy)

简单来说,使用者的任务就是清楚地描述每个物件 “想要” 的样子。当 k8s 接收到描述的物件后,便会 “努力” 的将物件调整至使用者想要的样子

那么使用者要如何描述物件呢?答案是透过攥写 spec。而在 k8s 中,我们可以建立.yaml 档案来攥写 spec,也就是描述物件。底下是一个描述 Deployment 物件,名为 simple.yaml 的简单范例

# simple.yaml

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  template:
    metadata:
      labels: 
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

以下针对上述 simple.yaml 档案进行说明

  • apiVerion:通知 api server 要存取的 api 版本

  • kind:物件种类。范例中为 Deployment 物件

  • metadata:用来描述目前物件的资料,另如 name: nginx

  • spec:这里就是描述 “想要” 的样子。就本例来说,就是描述 Deployment 这个物件” 想要 “ 的样子,包含

    • 运行三个 Pod (replicas: 3)

    • 描述每个 Pod “想要 “ 的样子 (spec.template)

      • 标记为 app: nginx (labels)
      • 使用 nignx 映像档 (image: nginx)
      • 使用 80 Port (containerPort: 80)

有了 yaml 档后,我们可以透过 kubectl 执行下列命令进行部署

$ kubectl apply -f simple.yaml
deployment "nginx" created

你有看出来 nginx 是哪里的设定吗?再看一下 metadata 吧!

由于我们并没有指定命名空间 (namespace) ,因此 nginx 会被部署到 default 这个预设的命名空间内,可执行下列指令查询

$ kubectl get pods
NAME                    READY     STATUS    RESTARTS   AGE
nginx-75f4785b7-6fbd4   1/1       Running   0          1m
nginx-75f4785b7-g284n   1/1       Running   0          1m
nginx-75f4785b7-vd4tt   1/1       Running   0          1m

会发现命名为nginx-75f4785b7-6fbd4,nginx-75f4785b7-g284n与 nginx-75f4785b7-vd4tt 共三个 Pod 正在运行,即表示我们已经成功完成一次部署

每次物件建立会被赋予不同的名称,因此上面的 Pod 的名称可能会有所不同。

看到这里,你应该会很好奇该如何连到这个运行 nignix 的 Pod。先别着急,接下来我们来讨论一下 k8s 中常用的物件。

Pod

k8s 中最小单位的物件,也是 k8s 中部署的基本单位。你也可以把 Pod 想成是应用程式的一个 instance 。而一个 Pod 会运行一个到多个容器 (container)

一个应用程式可能会有很多个 instances ,即很多个 Pods

可以把应用程式想像成网站,而该网站可以在 k8s 中被部署多份 (多个 Pod),用来达到水平扩展 (Horizontal Scaling) 以应付更多的存取需求。

image.png

而运行在同一个 Pod 中的容器,会

  • 被安排到同一台主机
  • 共享 network namespace
  • 连接同一个外部储存区 (Volume)

image.png

请注意!Pod 在特性上是属于临时性的 (ephemeral),意思就是需要的时候被产生而不需要的时候就结束 (包含储存区)。另外,Pod 本身无法自行完成复制 (Replication)、修复 (self-heal) 等等不同的功能,因此需要透过其他的控制项目 (Controller) 来达到。而常见的控制项目有 Deployment, ReplicaSets 等等。

ReplicaSet

ReplicaSet 最主要的功能是在确保 Pod 指定的运行数量能被达到。上例中,我们希望能够运行三个 Pod 在 k8s 丛集中。当 ReplicaSet 侦测到这个需求时,它会去检查目前状态是否满足,如果不满足,则会尝试将状态调整为指定状态。

在大多数情况下,我们不需要直接去操作 ReplicaSet,而是透过宣告 Deployment 物件。因为 Deployment 就已经包含了 ReplicaSet 的处理

Deployment

Deployment 物件内可以描述 Pod 的内容 (spec) 以及想要运行的个数 (ReplicaSet)。另外,如果想要变更 Pod 的内容,也可以直接修改 Deployment 物件后再套用新内容即可。

更多的物件说明请参考Understanding Kubernetes Objects

Label

在应用上,k8s 丛集中可能会运行数十到数百个物件,而 k8s 使用 Label (key-value) 来标记 k8s 物件 (不仅是 Pod )。透过适当的标记,k8s 可以区别不同物件所对应的环境。例如app:fronend,app:backend可区分为前端与后端应用程式,env:dev,env:pro可以区分开发与正式环境。我们用 Pod 物件当例子

image.png 上图表示 k8s 内有 6 个 Pod 正在运行,而其中 Frontend 与 Backend 各占三个。正式环境有两个 Pod 而测试环境运行一个 Pod。

Label 内容 (key-value) 可视环境自行定义,并无硬性规定

Namespace

如果有不同的群组想使用同一个 k8s 但是又不想要互相影响。又或者想要区分开发环境与正式环境 (上述例子是运行在同一个 Namespace),这个时候可以利用 k8s 提供的 Namespace 来区隔。 Namespace 如果有不同的群组想使用同一个 k8s 但是又不想要互相影响。又或者想要区分开发环境与正式环境 (上述例子是运行在同一个 Namespace),这个时候可以利用 k8s 提供的 Namespace 来区隔。

image.png

如果你想要替不同部门分配 k8s 资源,利用命名空间可以很容易做到。不但可以不互相影响还可以限定每个命名空间能使用的资源。例如:限定开发部门使用 develop 这个命名空间,然后限制 develop 最多只能使用 2 GB 的记忆体以及 30% CPU 资源等等。 建立 Namespace

轻松掌握 k8s 指令kubectl就有说明过如何建立一个 namespace 如下

$ kubectl create namespace [name]

[name]可以自行命名,例如建立一个 develop 的命名空间

$ kubectl create namespace develop
namespace "develop" created

取得 Namespace

$ kubectl get namespace
NAME          STATUS    AGE
default       Active    20h
develop       Active    1m    <===== 建立的
kube-public   Active    20h
kube-system   Active    20h

你会发现 develop 已经被建立在 k8s 中。

k8s 预设有三个 namespaces,分别是default,kube-publickube-system。如未特别指定,k8s 会使用 default 这个 namespace 当作预设的命名空间部署物件。