在这篇文章中,我们来扮演一个应用开发者的角色,使用我们已经搭建好的 Kubernetes 集群发布第一个容器化应用。
在开始实践之前,我们先来了解一下 Kubernetes 里面与开发者关系最密切的几个概念。
作为一个应用开发者,你首先要做的,是制作容器镜像。
而有了容器镜像之后,你需要按照 Kubernetes 项目的规范和要求,将你的镜像组织为它能够“认识”的方式,然后提交上去。
那么,什么才是 Kubernetes 项目能“认识”的方式呢?
这就是使用 Kubernetes 的必备技能:编写配置文件。
Kubernetes 跟 Docker 等很多项目最大的不同,就在于它不推荐你用命令行的方式直接运行容器(虽然 Kubernetes 项目也支持这种方式,比如:kubectl run),而是希望你用 YAML 文件的方式,即:容器的定义、参数、配置,统统记录在一个 YAML 文件中,然后用这样一句指令把它运行起来:
1 | $ kubectl create -f 我的配置文件 |
这么做的最直接的好处就是,你会有一个文件能记录下 Kubernetes 到底“run”了什么。比如下面这个例子:
1 | apiVersion: apps/v1 |
像这样的一个 YAML 文件,对应到 Kubernetes 中,就是一个 API Object(API 对象)。当你为这个对象的各个字段填好值并提交给 Kubernetes 之后,Kubernetes 就会负责创建出这些对象所定义的容器或者其他类型的 API 资源。
可以看到,这个 YAML 文件中的 Kind 字段,指定了这个 API 对象的类型是一个 Deployment。所谓的 Deployment,是一个定义多副本应用(即多个副本 Pod)的对象。此外,Deployment 还负责在 Pod 定义发生变化时,对多个副本进行滚动更新。在上面这个 YAML 文件中,定义的 Pod 副本个数(spec.replicas)为 2。
而这些具体的 Pod 又长什么样子呢?为此,定义了一个 Pod 模版(spec.template),这个模版描述了我想要创建的 Pod 的细节。在上面的例子里,这个 Pod 里只有一个容器,这个容器的镜像(spec.containers.image)是 nginx:1.7.9,这个容器监听端口(containerPort)是80。
关于 Pod 的设计和用法在之前的文章中有简单介绍过。而在这里,你只需要记住这样的一句话:
Pod 就是 Kubernetes 世界里的“应用”;而一个应用,可以由多个容器组成。
需要注意的是,像这样使用一种 API 对象(Deployment)管理另一种 API 对象(Pod)的方法,在 Kubernetes 中,叫做“控制器”模式。在我们的例子中,Deployment 扮演的正是 Pod 的控制器的角色。关于 Pod 的控制器模式的更多细节,会在后续编排部分做进一步讲解。
你可能还注意到,这样的每一个 API 对象都有一个叫做 Metadata 的字段,这个字段就是 API 对象的“标识”,即元数据,它也是我们从 Kubernetes 里找到这个对象的主要依据。这其中最主要使用到的字段是 Labels。
顾名思义,Labels 就是一组 key-value 格式的标签。而像 Deployment 这样的控制器对象,就可以通过这个 Labels 字段从 Kubernetes 中过滤出它所关心的被控制对象。比如,在上面的这个 YAML 文件中,Deployment 会把所有正在运行的、携带 app: nginx 标签的 Pod 识别为被管理的对象,并确保这些 Pod 的总数严格等于两个。而这个过滤规则的定义,是在 Deployment 的 spec.selector.matchLabels 字段。一般称之为 Label Selector。
另外,在 Metadata 中,还有一个与 Labels 格式、层级完全相同的字段叫 Annotations,它专门用来携带 key-value 格式的内部信息。所谓内部信息,指的是对这些信息感兴趣的,是 Kubernetes 组件本身,而不是用户。所以大多数的 Annotations,都是在 Kubernetes 运行过程中,被自动加在这个 API 对象上。
一个 Kubernetes 的 API 对象的定义,大多可以分为 Metadata 和 Spec 两个部分。前者存放的是这个对象的元数据,对所有 API 对象来说,这一部分的字段和格式基本上是一样的;而后者存放的,则是属于这个对象独有的定义,用来描述它所要表达的功能。
在了解了上述 Kubernetes 配置文件的基本常识之后,我们现在就可以把这个 YAML 文件“运行”起来。你可以使用 kubectl create 指令来完成这个动作:
1 | $ kubectl create -f nginx-deployment.yaml |
然后,通过 kubectl get 命令检查这个 YAML 运行起来的状态是不是与我们预期的一致:
1 | $ kubectl get pods -l app=nginx |
在这里,加上了一个 -l 的参数,即获取所有匹配 app: nginx 标签的 Pod。需要注意的是,在命令行中,所有 key-value 格式的参数,都使用“=”而非“:”表示。这里可以看到,两个 nginx 的 Pod 都处于 Running 状态,也就意味着我们这个 Deployment 所管理的 Pod 都处于预期状态。
此外,你还可以用 kubectl describe 命令,查看一个 API 对象的细节,比如:
1 | $ kubectl describe pod nginx-deployment-b96f959-zhr4g |
在这里返回的结果中,你可以清楚地看到这个 Pod 的详细信息,比如 IP 地址等等。其中,有一个部分值得你关注,它就是 Events(事件)。
在 Kubernetes 执行的过程中,对 API 对象的所有重要操作,都会被记录在这个对象的 Events 里,并且显示在 kubectl describe 指令返回的结果中。所以,这个部分正是我们将来进行 Debug 的重要依据。如果有异常产生,一定要第一时间查看这些 Events,往往可以看到非常详细的错误信息。
接下来,如果我们要对这个 Nginx 服务进行版本升级,要怎么做呢?
很简单,只需要修改这个 YAML 文件即可:
1 | ... |
然后可以用 kubectl replace 来完成这个更新:
1 | $ kubectl replace -f nginx-deployment.yaml |
不过,更推荐的做法是使用 kubectl apply 命令,来统一进行 Kubernetes 对象的创建和更新操作:
1 | $ kubectl apply -f nginx-deployment.yaml |
这样的操作方法,是 Kubernetes “声明式API”所推荐的使用方法。也就是说,作为用户,你不必关心当前的操作是创建还是更新,你执行的命令始终是 kubectl apply,而 Kubernetes 则会根据 YAML 文件的内容变化,自动进行具体的处理。
当应用本身发生变化时,开发人员和运维人员可以依靠容器镜像来进行同步;当应用部署参数发生变化时,这些 YAML 文件就是他们相互沟通和信任的媒介。
以上就是 Kubernetes 发布应用的最基本操作了。
接下来,我们再在这个 Deployment 里面尝试声明一个 Volume。
在 Kubernetes 中,Volume 是属于 Pod 对象的一部分。所以,我们就需要修改这个 YAML 文件里的 template.spec 字段,如下所示:
1 | apiVersion: apps/v1 |
可以看到,我们在 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 文件:
1 | ... |
这样,容器 Volume 挂载的宿主机目录,就变成了 /var/data。
在上述修改完成后,我们还是使用 kubectl apply 指令,更新这个 Deployment:
1 | $ kubectl apply -f nginx-deployment.yaml |
然后,你可以使用 kubectl describe 查看一下最新的 Pod,就会发现 Volume 的信息已经出现在了 Container 描述部分:
1 | ... |
最后,你还可以使用 kubectl exec 指令,进入到这个 Pod 当中(即容器的 Namespace中)查看这个 Volume 目录:
1 | $ kubectl exec -it nginx-deployment-579ffb469d-85b4d -- /bin/bash |
此外,如果想要从 Kubernetes 集群中删除这个 Nginx Deployment 的话,执行:
1 | $ kubectl delete -f nginx-deployment.yaml |
就可以了。