02-容器化应用

使用Kubernetes的必备技能:编写配置文件。这些配置文件可以是 YAML 或者 JSON 格式的,一般都是用YAML格式

Kubernetes不推荐直接使用命令行的方式运行容器,而是使用YAML文件的方式,即:把容器的定义、参数、配置都记录在一个YAML文件中,然后使用如下命令:

kubectl create -f  <xxx.yaml>

这么做的最大好处,有一个文件能记录Kubernetes到底运行了什么

0.1. YAML示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

这个 YAML 文件,对应到 Kubernetes 中,就是一个 API Object(API 对象)。

为这个对象的各个字段填好值并提交给Kubernetes之后,Kubernetes就会负责创建出这些对象所定义的容器或者其他类型的API 资源

可以看到,这个 YAML 文件中的 Kind 字段,指定了这个API对象的类型(Type),是一个Deployment

所谓 Deployment,是一个定义多副本应用(即多个副本 Pod)的对象,此外,Deployment 还负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。

这个 YAML 文件中:

  • 定义的 Pod 副本个数 (spec.replicas) 是:2
  • 定义了一个 Pod 模版(spec.template),这个模版描述了要创建的 Pod 的细节
  • 这个 Pod 里只有一个容器,这个容器的镜像(spec.containers.image)是nginx:1.7.9,这个容器监听端口(containerPort)是 80

0.2. API对象

Pod就是Kubernetes世界里的“应用”,而一个应用,可以由多个容器组成

像这样使用一种 API 对象(Deployment)管理另一种 API 对象(Pod)的方法,在 Kubernetes 中,叫作“控制器”模式(controller pattern)。

在上面的例子中,Deployment扮演的正是 Pod 的控制器的角色。

0.2.1. Metadata

这样的每一个 API 对象都有一个叫作 Metadata 的字段,这个字段就是 API 对象的“标识”,即元数据,它也是从 Kubernetes 里找到这个对象的主要依据,这其中最主要使用到的字段是Labels

Labels 就是一组 key-value 格式的标签。而像 Deployment 这样的控制器对象,就可以通过这个 Labels 字段从 Kubernetes 中过滤出它所关心的被控制对象。

比如,在上面这个 YAML 文件中,Deployment 会把所有正在运行的、携带“app: nginx”标签的Pod 识别为被管理的对象,并确保这些 Pod 的总数严格等于两个。

0.2.2. Label Selector

过滤规则的定义,是在 Deployment 的“spec.selector.matchLabels”字段,一般称之为:Label Selector。

0.2.3. Annotations

在 Metadata中,还有一个与Labels格式、层级完全相同的字段叫Annotations,它专门用来携带 key-value 格式的内部信息。

内部信息,指的是对这些信息感兴趣的是Kubernetes 组件本身而不是用户。所以大多数Annotations,都是在 Kubernetes 运行过程中,被自动加在这个 API 对象上。

一个 Kubernetes 的 API 对象的定义,大多可以分为 MetadataSpec 两个部分:

  • 前者存放的是这个对象的元数据,对所有 API 对象来说,这一部分的字段和格式基本上是一样的
  • 后者存放的是属于这个对象独有的定义,用来描述它所要表达的功能

0.3. 运行API对象

# 创建API对象
kubectl create -f nginx-deployment.yaml

# 查看API对象,-l参数获取所有匹配标签的pod
kubectl get pods -l app=nginx

# 查看一个 API 对象的细节
kubectl describe pod nginx

kubectl get 指令的作用,就是从 Kubernetes 里面获取(GET)指定的 API 对象。需要注意的是,在命令行中,所有 key-value格式的参数,都使用“=”而非“:”表示。

kubectl describe 命令返回的结果中,可以清楚地看到这个 Pod 的详细信息,比如它的 IP 地址等等。其中,有一个部分值得特别关注,就是Events(事件)。

在 Kubernetes 执行的过程中,对 API 对象的所有重要操作,都会被记录在这个对象的 Events 里,并且显示在 kubectl describe 指令返回的结果中。这个部分正是我们将来进行 Debug 的重要依据。如果有异常发生,要第一时间查看这些 Events,往往可以看到非常详细的错误信息。

0.3.1. 示例

上述deployment中的pod运行的是1.7.9的nginx容器,如何升级成1.8?

只要修改刚才的YAML文件即可:

...
    spec:
      containers:
      - name: nginx
        image: nginx:1.8 # 这里从 1.7.9 修改为 1.8
        ports:
        - containerPort: 80

这样对YAML配置文件的本地修改就完成了,通过如下命令更新到kubernetes集群中:

kubectl replace -f nginx-deployment.yaml

# 1
kubectl create -f   file.yaml

# 2
kubectl replace -f  file.yaml

# 3
kubectl apply -f    file.yaml

# 上述的命令1和2可以用3替换掉,这也是kubernetes“声明式API”推荐的做法。
  • 通过容器镜像,保证了应用本身在开发和部署环境里的一致性(当应用发生变化时,开发和运维可以依靠容器进行同步)
  • 通过YAML配置文件,保证了应用“部署参数”在开发和部署环境中的一致性(当应用部署参数发生变化时,开发和运维可以依靠YAML配置文件进行沟通)

0.4. 挂载volume

在 Kubernetes 中,Volume 是属于 Pod 对象的一部分。所以,我们就需要修改这个 YAML 文件里的 template.spec 字段,如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.8
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: nginx-vol
      volumes:
      - name: nginx-vol
        emptyDir: {}

在 Deployment 的 Pod 模板部分添加了一个 volumes 字段,定义了这个 Pod 声明的所有 Volume。它的名字叫作 nginx-vol,类型是 emptyDir。

那什么是 emptyDir类型呢?

它其实就等同于Docker的隐式Volume参数,即:不显式声明宿主机目录的Volume。所以,Kubernetes也会在宿主机上创建一个临时目录,这个目录将来就会被绑定挂载到容器所声明的 Volume 目录上。

Kubernetes 的 emptyDir类型,只是把Kubernetes 创建的临时目录作为Volume的宿主机目录交给Docker,因为 Kubernetes 不想依赖 Docker 创建的 _data 目录。

而 Pod 中的容器,使用的是 volumeMounts 字段来声明自己要挂载哪个 Volume,并通过mountPath 字段来定义容器内的 Volume 目录,比如:/usr/share/nginx/html

当然,Kubernetes 也提供了显式的 Volume 定义,它叫做 hostPath。比如下面的这个 YAML 文件:

 ...
    volumes:
    - name: nginx-vol
      hostPath:
        path: /var/data

这样volume挂载的宿主机目录,就变成了/var/data

0.5. 进入容器

使用如下命令,进入到Pod中,即容器的Namespace中:

kubectl exec -it nginx-deployment-5c678cfb6d-lg9lw -- /bin/bash
ls /usr/share/nginx/html

从Kubernetes集群中删除部署的Deployment的命令:

kubectl delete -f nginx-deployment.yaml
上次修改: 14 April 2020