Minikube 架构图

Minikube 利用本地虚拟机环境部署 Kubernetes,其基本架构如图。
image.png

Reference
https://github.com/kubernetes/minikube
https://minikube.sigs.k8s.io/docs/

Kubernetes 里面与开发者关系最密切的几个概念

Kubernetes 跟 Docker 等很多项目最大的不同,在于它不推荐使用命令行的方式直接运行容器(但也支持:kubectl run),而是用 YAML 文件的方式,即:把容器的定义、参数、配置,统统记录在一个 YAML 文件中,然后用一句指令把它运行起来:

  1. $ kubectl create -f 我的配置文件
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. replicas: 2
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: nginx:1.7.9
  18. ports:
  19. - containerPort: 80

这样一个 YAML 文件,对应到 Kubernetes 中,就是个 API Object,Kubernetes 负责创建出这些对象所定义但容器或者其它类型的 API 资源。

  • Deployment
    YAML 文件中 Kind 字段,指定了这个 API 对象的类型(Type),是一个 Deployment。
    定义多副本应用(即多个副本 Pod)的对象。Deployment 还负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。
    上面的 YAML 文件中,定义的 Pod 副本个数(spec.replicas)为:2。
  • Pod
    Pod 模板(spec.template) 描述了要创建的 Pod 的细节。上例中,Pod 里只有一个容器,容器的镜像(spec.containers.image) 是 nginx:1.7.9,容器监听端口(containerPort) 是 80。
    Pod 就是 Kubernetes 世界里的“应用”;而一个应用,可以由多个容器组成。
    像这样使用一种 API 对象 (Deployment) 管理另一种 API 对象 (Pod) 的方式,在Kubernetes 中叫“控制器”模式(controller pattern)。
  • Metadata
    API 对象的“标识”,元数据。从 Kubernetes 里找到这个对象的主要依据。其中最主要使用字段是 Labels。
  • Labels
    一组 key-value 格式的标签。像 Deployment 这样的控制器对象,可以通过 Labels 字段从 Kubernetes 中过滤出它所关心的被控制对象。
    如上例:Deployment 会把所有正在运行的、携带“app: niginx”标签的 Pod 识别为被管理对象,并确保这些 Pod 的总数严格等于两个。过滤规则 Label Selector。
  • Label Selector
    过滤规则的定义,在 Deployment 的“spec.selector.matchLabels”字段。
  • Annotations
    在 Metadata 中,与 Labels 格式、层级完全相同。专门用来携带 key-value 格式的内部信息。内部信息指的是对这些信息感兴趣的,是 Kubernetes 组件本身,而不是用户。所以大多 Annotations,都是在 Kubernetes 运行过程中,被自动加在这个 API 对象上。

一个 Kubernetes 的 API 对象的定义,大多可分为 Metadata 和 Spec 两部分。Metadata 存放的是对象的元数据,对所有 API 对象来说,这部分的字段和格式基本是一样的;Spec 存放的是属于这个对象独有的定义,用来描述它所要表达的功能。

运行 YAML 文件

  • kubectl get 从 Kubernetes 里 GET 指定的 API 对象。 ```bash $ kubectl create -f nigix-deployment.yaml

$ kubectl get pods -l app=nginx NAME READY STATUS RESTARTS AGE nginx-deployment-67594d6bf6-9gdvr 1/1 Running 0 10m nginx-deployment-67594d6bf6-v6j7w 1/1 Running 0 10m

`-l` 获取所有匹配 app: nginx 标签 的 Pod。在命令中,所有 key-value 格式的参数,都是用“=”而非“:”。<br />从返回看出,两个 Pod 处于 Running 状态,意味着 Deployment 所管理的 Pod 都处于预期的状态。

- `kubectl describe` 查看 API 对象的细节。
```bash
$ kubectl describe pod nginx-deployment-67594d6bf6-9gdvr
Name:               nginx-deployment-67594d6bf6-9gdvr
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               node-1/10.168.0.3
Start Time:         Thu, 16 Aug 2018 08:48:42 +0000
Labels:             app=nginx
                    pod-template-hash=2315082692
Annotations:        <none>
Status:             Running
IP:                 10.32.0.23
Controlled By:      ReplicaSet/nginx-deployment-67594d6bf6
...
Events:

  Type     Reason                  Age                From               Message

  ----     ------                  ----               ----               -------

  Normal   Scheduled               1m                 default-scheduler  Successfully assigned default/nginx-deployment-67594d6bf6-9gdvr to node-1
  Normal   Pulling                 25s                kubelet, node-1    pulling image "nginx:1.7.9"
  Normal   Pulled                  17s                kubelet, node-1    Successfully pulled image "nginx:1.7.9"
  Normal   Created                 17s                kubelet, node-1    Created container
  Normal   Started                 17s                kubelet, node-1    Started container

其中有一部分值得特别关注, Events(事件)

Events

在Kubernetes 执行过程中,对 API 对象对所有重要操作,都会被记录在这个对象对 Events,并显示在 kubectl describe 返回结果中。
如上,对于这个 Pod,它被创建之后,被调度器调度(Successfully assigned) 到 node-1,拉取指定镜像(pulling image) ,启动 Pod 里定义对容器(Started container)。
这部分是进行 Debug 对重要依据。 如果有异常发生,一定要第一时间查看 Events ,往往可以看到非常详细对错误信息。

服务升级

对 Nginx 服务进行升级。修改 Yaml 文件。

...    
    spec:
      containers:
      - name: nginx
        image: nginx:1.8 #这里被从1.7.9修改为1.8
        ports:
      - containerPort: 80
  • kubectl replace 完成更新。

    $ kubectl replace -f niginx-deployment.yaml
    
  • kubectl apply 来统一进行 Kubernetes 对象的创建和更新操作。 ```bash $ kubectl apply -f nginx-deployment.yaml

修改nginx-deployment.yaml的内容

$ kubectl apply -f nginx-deployment.yaml

这种是 Kubernetes “声明式 API” 推荐的使用方法。作为用户,不必关心当前的操作是创建,还是更新,执行的命令始终是 `kubectl apply` ,Kubernetes 会根据 YAML 文件的内容变化,自动进行具体的处理。<br />开发和运维围绕可版本化管理的 YAML 文件进行协作。<br />容器镜像保证应用本身在开发和部署环境里的一致性,YAML 文件保证应用的“部署参数”在开发和部署环境中的一致性。

<a name="MTcog"></a>
#### Volume
Volume 属于 Pod 的一部分,需修改 template.spec 字段。
```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.8
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: nginx-vol
      volumes:
      - name: nginx-vol
        emptyDir: {}

在 Deployment 的 Pod 模板部分添加了 volumes 字段,定义了这个 Pod 声明的所有 Volume。叫 nigin-vol,类型是 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:

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

这样容器挂载的宿主机目录就为 /var/data。
修改完成后,使用 kubectl apply 更新 Deployment。
可使用 kubectl get 查看两个 Pod 被逐一更新的过程:

$ kubectl get pods
NAME                                READY     STATUS              RESTARTS   AGE
nginx-deployment-5c678cfb6d-v5dlh   0/1       ContainerCreating   0          4s
nginx-deployment-67594d6bf6-9gdvr   1/1       Running             0          10m
nginx-deployment-67594d6bf6-v6j7w   1/1       Running             0          10m
$ kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-5c678cfb6d-lg9lw   1/1       Running   0          8s
nginx-deployment-5c678cfb6d-v5dlh   1/1       Running   0          19s

可看出,新旧两个 Pod,被交替创建、删除,最后剩下新版本的 Pod。
使用 kubectl describe 查看最新的 Pod,Container 描述部分包含了 Volume 信息。

...
Containers:
  nginx:
    Container ID:   docker://07b4f89248791c2aa47787e3da3cc94b48576cd173018356a6ec8db2b6041343
    Image:          nginx:1.8
    ...
    Environment:    <none>
    Mounts:
      /usr/share/nginx/html from nginx-vol (rw)
...
Volumes:
  nginx-vol:
    Type:    EmptyDir (a temporary directory that shares a pod's lifetime)

使用 kubectl exec 进入 Pod 中(即容器的 Namesapce 中),查看 Volume 目录。

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

从 Kubernetes 集群中删除这个 Nginx Deployment,执行:

$ kubectl delete -f nginx-deployment.yaml

总结

Kubernetes 推荐使用方式:用一个 YAML 文件描述所要部署的 API 对象。统一使用 kubectl apply 完成对对象对创建和更新操作。
Kubernetes “最小”的 API 对象是 Pod。Pod 可等价为一个应用,所以 Pod 可由多个紧密协作的容器组成。
Kubernetes 经常通过一种 API 对象来管理另一种 API 对象,如 Deployment 和 Pod 。而 Pod 是“最小”的对象,所以往往都是被其他对象控制的。这种组合方式,是 Kubernetes 进行容器编排的重要模式。
这样的 Kubernetes API 对象,往往由 Metadata 和 Spec 两部分组成,其中 Metadata 里的 Labels 字段是 Kubernetes 过滤对象的主要手段。
在这些字段里面,容器想要使用的数据卷,也就是 Volume,是 Pod 的 Spec 字段的一部分。而 Pod 里的每个容器,则需要显式的声明自己要挂载哪个 Volume。
上面这些基于 YAML 文件的容器管理方式,跟 Docker、Mesos 的使用习惯都是不一样的,需从 docker run 这样的命令行操作,向 kubectl apply YAML 文件这样的声明式 API 的转变。
所以,按照下面的流程进行练习 Kubernetes:

  • 首先,在本地通过 Docker 测试代码,制作镜像;
  • 然后,选择合适的 Kubernetes API 对象,编写对应 YAML 文件(比如,Pod,Deployment);
  • 最后,在 Kubernetes 上部署这个 YAML 文件。

在部署到 Kubernetes 之后,接下来的所有操作,要么通过 kubectl 来执行,要么通过修改 YAML 文件来实现,尽量不要再碰 Docker 的命令行了。