《精通Docker第三版》完整目录:
第一章 Docker概览
第二章 创建容器镜像
第三章 存储和发布镜像
第四章 管理容器
第五章 Docker Compose
第六章 Windows容器
第七章 Docker Machine
第八章 Docker Swarm
第九章 Docker和Kubernetes
第十章 在公有云上运行Docker
第十一章 Portainer – 一个Docker的GUI
第十二章 Docker安全
第十三章 Docker工作流
第十四章 Docker进阶
本章中,我们一起来看Kubernetes。和Docker Swarm相似,我们可以使用Kubernetes来创建和管理用于运行基于容器的应用集群。
本章涵盖的主要内容有:
- Kubernetes的介绍
- 启动Kubernetes
- 使用Kubernetes
- Kubernetes和其它Docker工具
技术准备
Docker中的Kubernetes只支由Docker for Mac和Docker for Windows支持。和此前章节一样,作者将使用自己偏好的操作系统macOS。同样,其中极少数据的支持命令可能只在macOS中使用。
观看如下视频来查看代码的实时操作:
Kubernetes简介
如果你有了解容器,那么总会在某个地方碰到Kubernetes,在Docker桌面软件中启动它之前,我们先花点时间了解下Kubernetes的前世今身。
Kubernetes(音koo-ber-net-eez)来自给予舵手或船长的希腊名称。Kubernetes(也称为K8s),是一个起源于Google的开源项目,允许我们自动化部署、管理和扩容容器化应用。
Google的容器简史
Google对于Linux容器解决方案的研究历时已久。2016年迈出了第一步,开发Linux内核称作控制组(cgroups)的功能。这一功能于2008年合并到了Linux发行版2.6.24的内核中。该功能让我们可以隔离资源,如CPU、RAM、网络和磁盘I/O,或一个或多个进程。控制组仍然是Linux容器的核心组成并为Docker及其它容器工具所使用。
Google接着以名为lmctfy的容器栈涉足容器领域,它是Let Me Contain That For You的缩写。这是LXC的工具集和库的一个替代。这是 Google 内部在自己应用内用于管理容器的工具的一个开源版本。
译者注:LXC 表示 Linux Container,即 Linux 容器。
接着Google再次成为容器使用新闻热点是在2014年5月的Gluecon上Joe Beda进行讲话之后 。在讲话期间,Beda透露Google当时几乎所有业务都基于容器,以及他们每周会启动近20亿个容器。并声称这一数字并不包含长期运行的容器,也就是说这些容器都是供短期使用的。但是进行快速的数学计算之后,这表示Google平均每秒会启动3000个容器。
在讲话的后半段,Beda提及Google使用了调度器这样他们无需每周手动管理20亿个容器,或担心容器在哪里启动乃至容器的可用性。
Google还发布了一篇名为Large-scale cluster management at Google with Borg(Google 使用 Borg 进行大规模集群的管理)的论文。该论文不仅让Google以外的人知道了他们使用的调度器的名称,Borg,还深入到了在设计调度器时做设计决策的细节。
该论文也提到了他们的内部工具,Google在容器运行集群中运行面向用户的应用,如 Google Docs、Gmail和Google 搜索,都是由Borg进行管理。
Borg使用电视节目星际迷航:下一代的外星种族Borg来进行命名。在电视节目中,Borg是一种半机械生物,它们的文明基于称为“集合体”的集体意识。这使他们不仅可以共享相同的想法,同时通过子空间网络,确保每个集合体的成员透过集体意识给予指导和监督。相信你也会同意,Borg种族的特征与我们希望运行的容器集群匹配度极高。
Borg在Google内部使用了好几年,最终由名为Omega的调试器替代。大约是这个时候Google声明会拿出一些Borg的核心功能,将其重塑为一个开源项目。这个项目内部称之为Seven,由多名Borg的核心贡献人员操刀。目标在于创建一个版本更为友好的Borg,脱离Google自己的内部流程和工作方式。
Seven,取名自星际迷航:航海家号中的人物,九之七(Seven of Nine)是从集合体中解放出来的博格个体,最终在首次对外提交时使用了Kubernetes这一名称。
Kubernetes概览
那么,现在我们知道了Kubernetes的由来,可以更深入的挖掘Kubernetes是什么了。该项目的大部分,确切地说是88.5%,由 Go 语言编写,这不足为奇,因为Go在2011年开源之前是由Google内部开发的一种编程语言。此项目的其它部分文件由Python和Shell帮助脚本和HTML文档组成。
一个典型的Kubernetes集群由角色为master或node服务器组成。你可以对这两种角色的节点进行单独安装。
master角色是神奇发生的地方,同时也是集群的大脑。它负责决定在何处启动pod,并且监控集群本身以及集群内运行的pod的健康状况。我们在了解了这两个角色之后会来讨论pod。
通常,核心组件会部署到角色分配为master的主机上:
- kube-apiserver:这个组件暴露主Kubernetes API。它设计为横向扩展,这表示你可以持续添加更多的实例来让集群保持高可用。
- etcd:这是一个高可用一致性键值存储。用于存储集群的状态。
- kube-scheduler:这个组件负责决定pod在哪里启动。
- kube-controller-manager:这一组件运行控制器。这些控制器在Kubernetes中有多个功能,如监控节点,监测应用、管理端点(endpoint)以及生成服务账号和令牌。
- cloud-controller-manager:这一组件管理处个控制器,与第三方去进行交互来启动和配置支持服务。
上面讲解了管理组件,下面需要讨论它们所管理的内容是什么。node由如下组件构成:
- kubelet:这个代理(agent)运行在集群内的node上,它是管理节点与node进行交互的方式。也负责管理各个pod。
- kube-proxy::这个组件管理所有的请求路由以及node和pod的通讯。
- container runtime:可以是Docker RKT或任意遵循OCI(Oracle Call Interface)的运行时。
你可能注意到了到此我们没提到容器。这是因为Kubernetes实际上不直接与容器进行交互,它与pod进行通讯。把pod看作一个完整的应用,有点像我们使用Docker Compose启动多个容器的应用。
Kubernetes和Docker
Kubernetes最早被视作Docker自己的集群技术Docker Swarm的竞品。但通过这几年的发展,Kubernetes实际上已成为容器编排的标准了。
所有主流云提供商都提供Kubernetes即服务。有如下这些:
- 谷歌云: Google Kubernetes引擎(GKE)
- Microsoft Azure: Azure Kubernetes服务(AKS)
- Amazon Web Services: Amazon的Kubernetes弹性容器服务 (EKS)
- IBM: IBM云Kubernetes服务
- 甲骨文云: Oracle的Kubernetes容器引擎
- DigitalOcean: DigitalOcean上的Kubernetes
表面上,所有这些主流玩家支持Kubernetes可能并没有什么了不起的。但别忘了我们可以在跨多平台部署容器化应用并保持一致性。以前,这些平台被称作后花园,与它们交互的方式非常的不同。
在Docker在2017年10月初次发表声明时普遍表示惊讶,但尘埃落定时又是那么的理所当然。使用Docker for Mac和Docker for Windows为开发人员提供可以在本地操作应用,然后使用Docker企业版来部署和管理自己的Kubernetes集群,甚至是使用前述的一种云服务,与我们在第一章 Docker概览中讨论的“像在本机操作”非常契合。
下面我们来看如何在Docker软件中启动Kubernetes支持并进一步的进行使用。
启动Kubernetes
Docker上Kubernetes的安装过程极为简单。启动Kubernetes支持,我们只需要打开Preferences并点击Kubernetes选项卡:
可以看到,有三个主选项。勾选Enable Kubernetes复选框,然后选择Deploy Docker Stacks to Kubernetes by default。先不勾选Show systems containers,我们会在启动服务后再来看更多的细节。点击Apply会弹出如下信息:
点击Install按钮会下载所需的容器来在Docker软件上启动Kubernetes的支持:
像前面的一个对话框中所提示的那样,Docker会需要不些时间来进行下载、配置和启动该集群。完成后,你会看到Kubernetes is running旁边有绿点:
打开Terminal并运行如下命令:
1 |
$ docker container ls -a |
正常运行后不会显示任何容器。运行如下命令:
1 |
$ docker image ls |
这会列出Kubernetes相关的镜像:
1 2 3 4 5 6 7 8 9 10 11 |
k8s.gcr.io/kube-proxy-amd64 k8s.gcr.io/kube-apiserver-amd64 k8s.gcr.io/kube-controller-manager-amd64 k8s.gcr.io/kube-scheduler-amd64 docker/kube-compose-controller docker/kube-compose-api-server k8s.gcr.io/etcd-amd64 k8s.gcr.io/k8s-dns-dnsmasq-nanny-amd64 k8s.gcr.io/k8s-dns-sidecar-amd64 k8s.gcr.io/k8s-dns-kube-dns-amd64 k8s.gcr.io/pause-amd64 |
这些镜像的源为Docker和Google容器仓库(k8s.gcr.io)所提供的官方Kubernetes。
你可能已经猜到,勾选Show system containers (advanced)复选框,然后运行以下命令会列出在本地Docker软件上启动Kubernetes服务所有运行的容器:
1 |
$ docker container ls -a |
因为运行上述命令会有大量输出,以下仅显示容器的名称。通过运行如下命令来进行实现:
1 |
$ docker container ls --format {{.Names}} |
运行该命令会得到如下输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ docker container ls --format {{.Names}} k8s_compose_compose-74649b4db6-j4t8r_docker_184e337c-658d-11e9-9fe6-025000000001_0 k8s_compose_compose-api-79c95fc7d6-gbkq4_docker_18464718-658d-11e9-9fe6-025000000001_0 k8s_POD_compose-74649b4db6-j4t8r_docker_184e337c-658d-11e9-9fe6-025000000001_0 k8s_POD_compose-api-79c95fc7d6-gbkq4_docker_18464718-658d-11e9-9fe6-025000000001_0 k8s_sidecar_kube-dns-86f4d74b45-mjtp8_kube-system_f0ce645a-658c-11e9-9fe6-025000000001_0 k8s_dnsmasq_kube-dns-86f4d74b45-mjtp8_kube-system_f0ce645a-658c-11e9-9fe6-025000000001_0 k8s_kubedns_kube-dns-86f4d74b45-mjtp8_kube-system_f0ce645a-658c-11e9-9fe6-025000000001_0 k8s_kube-proxy_kube-proxy-jsf94_kube-system_f0c7ceb6-658c-11e9-9fe6-025000000001_0 k8s_POD_kube-dns-86f4d74b45-mjtp8_kube-system_f0ce645a-658c-11e9-9fe6-025000000001_0 k8s_POD_kube-proxy-jsf94_kube-system_f0c7ceb6-658c-11e9-9fe6-025000000001_0 k8s_kube-scheduler_kube-scheduler-docker-for-desktop_kube-system_ecf299f4fa454da5ab299dffcd70c70f_0 k8s_kube-apiserver_kube-apiserver-docker-for-desktop_kube-system_7fbb107871a3c31e9b65e5e5aa8427e9_0 k8s_kube-controller-manager_kube-controller-manager-docker-for-desktop_kube-system_5fd9f57b2e532f401708dfe8a8bcc3c2_0 k8s_etcd_etcd-docker-for-desktop_kube-system_37c1035b2a8313813aad96455cd61937_0 k8s_POD_kube-scheduler-docker-for-desktop_kube-system_ecf299f4fa454da5ab299dffcd70c70f_0 k8s_POD_kube-controller-manager-docker-for-desktop_kube-system_5fd9f57b2e532f401708dfe8a8bcc3c2_0 k8s_POD_kube-apiserver-docker-for-desktop_kube-system_7fbb107871a3c31e9b65e5e5aa8427e9_0 k8s_POD_etcd-docker-for-desktop_kube-system_37c1035b2a8313813aad96455cd61937_0 |
有18个运行中的容器,这也是存在隐藏它们的原因。可以看到几乎所有我们在前一部分讨论的组件以及其它一些提供Docker集成的组件。我会推荐不勾选Show system containers的选项,因为我们不需要在每次查看运行中的容器时看到这18个容器。
这里另一件需要注意的事情是Kubernetes的菜单项中有内容了。这一菜单可用于在Kubernetes集群间进行切换。因为当前我们只有一个活跃的集群,因此只列出了一个:
现在我们本地的Kubernetes已启动并运行,下面就开始使用它吧。
使用Kubernetes
我们已经在Docker桌面软件上启动和运行了Kubernetes集群,可以开始与其进行交互了。先来看与Docker桌面组件一起安装的命令kubectl。
前面已提到,kubectl同时进行了安装。如下命令会显示客户端及其所连接到的集群的相关信息:
1 2 3 |
$ kubectl version Client Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.2", GitCommit:"17c77c7898218073f14c8d573582e8d2313dc740", GitTreeState:"clean", BuildDate:"2018-11-26T13:25:32Z", GoVersion:"go1.11.2", Compiler:"gc", Platform:"darwin/amd64"} Server Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.11", GitCommit:"637c7e288581ee40ab4ca210618a89a555b6e7e9", GitTreeState:"clean", BuildDate:"2018-11-26T14:25:46Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"} |
接下来运行如下命令来看kubectl是否能识别到我们的node:
1 2 3 |
$ kubectl get nodes NAME STATUS ROLES AGE VERSION docker-for-desktop Ready master 1h v1.10.11 |
既然我们已经有了与node进行交互的客户端,就可以通过运行如下命令来查看在Kubernetes中默认配置的命名空间:
1 |
$ kubectl get namespaces |
然后通过如下命令来在命名空间中查看pod:
1 |
$ kubectl get --namespace kube-system pods |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ kubectl get namespaces NAME STATUS AGE default Active 1h docker Active 1h kube-public Active 1h kube-system Active 1h $ kubectl get --namespace kube-system pods NAME READY STATUS RESTARTS AGE etcd-docker-for-desktop 1/1 Running 0 1h kube-apiserver-docker-for-desktop 1/1 Running 0 1h kube-controller-manager-docker-for-desktop 1/1 Running 0 1h kube-dns-86f4d74b45-mjtp8 3/3 Running 0 1h kube-proxy-jsf94 1/1 Running 0 1h kube-scheduler-docker-for-desktop 1/1 Running 0 1h |
Kubernetes中的命名空间是集群内隔离资源的一种很好的方式。可以在Terminal的输出中看到,集群内有四个命名空间。有default命名空间,通常为空。两个主Kubernetes服务的命名空间:docker和kube-system。这些包含组成我们集群的pod以及最后一个命名空间,kube-public,类似default,为空。
在我们启动自己的pod之前,先快速地查看下如何与我们运行的pod进行交互,首先了解如何查找我们pod的更多信息:
1 |
$ kubectl describe --namespace kube-system pods kube-scheduler-docker-for-desktop |
以上命令会打印出kube-scheduler-docker-for-desktop这一pod的明细信息。你可能注意到我们需要使用–namespace 来传递命名空间。如果不传递,那么kubectl会默认使用没有名为kube-scheduler-docker-for-desktop在运行的default命名空间。
该命令的完整输出如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
Name: kube-scheduler-docker-for-desktop Namespace: kube-system Node: docker-for-desktop/192.168.65.3 Start Time: Tue, 23 Apr 2019 15:00:43 +0800 Labels: component=kube-scheduler tier=control-plane Annotations: kubernetes.io/config.hash: ecf299f4fa454da5ab299dffcd70c70f kubernetes.io/config.mirror: ecf299f4fa454da5ab299dffcd70c70f kubernetes.io/config.seen: 2019-04-23T05:56:03.9921627Z kubernetes.io/config.source: file scheduler.alpha.kubernetes.io/critical-pod: Status: Running IP: 192.168.65.3 Containers: kube-scheduler: Container ID: docker://ceb5a8dbb3b984f785d0c96a98e41a662b2dfb2eb52fb241bf3edd62a98ed717 Image: k8s.gcr.io/kube-scheduler-amd64:v1.10.11 Image ID: docker-pullable://k8s.gcr.io/kube-scheduler-amd64@sha256:3f40a5beec15fe39300d5bac56d6d7b72957afca51d3353aeb77a563f889973c Port: <none> Host Port: <none> Command: kube-scheduler --leader-elect=true --kubeconfig=/etc/kubernetes/scheduler.conf --address=127.0.0.1 State: Running Started: Tue, 23 Apr 2019 15:00:44 +0800 Ready: True Restart Count: 0 Requests: cpu: 100m Liveness: http-get http://127.0.0.1:10251/healthz delay=15s timeout=15s period=10s #success=1 #failure=8 Environment: <none> Mounts: /etc/kubernetes/scheduler.conf from kubeconfig (ro) Conditions: Type Status Initialized True Ready True PodScheduled True Volumes: kubeconfig: Type: HostPath (bare host directory volume) Path: /etc/kubernetes/scheduler.conf HostPathType: FileOrCreate QoS Class: Burstable Node-Selectors: <none> Tolerations: :NoExecute Events: <none> |
可以看到有关这一pod的大量信息,包含容器列表,我们只有一个,名为kube-scheduler。我们还可以看到镜像所使用的容器ID,容器启动使用的标记,以及Kubernetes调度器用于启动和维护该pod的数据。
我们已经知道了容器名,可以开始与其进行交互。例如,运行如下命令会打印出我们这一容器的日志:
1 2 3 4 5 6 7 8 |
$ kubectl logs --namespace kube-system kube-scheduler-docker-for-desktop -c kube-scheduler W0423 07:00:44.837759 1 server.go:163] WARNING: all flags other than --config are deprecated. Please begin using a config file ASAP. I0423 07:00:44.844505 1 server.go:555] Version: v1.10.11 I0423 07:00:44.844928 1 server.go:574] starting healthz server on 127.0.0.1:10251 E0423 07:00:51.126897 1 reflector.go:205] k8s.io/kubernetes/cmd/kube-scheduler/app/server.go:594: Failed to list *v1.Pod: pods is forbidden: User "system:kube-scheduler" cannot list pods at the cluster scope E0423 07:00:51.127282 1 reflector.go:205] k8s.io/kubernetes/vendor/k8s.io/client-go/informers/factory.go:87: Failed to list *v1.StorageClass: storageclasses.storage.k8s.io is forbidden: User "system:kube-scheduler" cannot list storageclasses.storage.k8s.io at the cluster scope E0423 07:00:51.140152 1 reflector.go:205] k8s.io/kubernetes/vendor/k8s.io/client-go/informers/factory.go:87: Failed to list *v1beta1.ReplicaSet: replicasets.extensions is forbidden: User "system:kube-scheduler" cannot list replicasets.extensions at the cluster scope ... |
运行如下命令会获取pod中的每个容器的日志:
1 |
$ kubectl logs --namespace kube-system kube-scheduler-docker-for-desktop |
类似Docker,你还可以对pod和容器执行命令。例如,如下命令会运行uname -a 命令:
小贴士:确保在如下两个命令的 — 后添加空格。不加的话会产生报错。
1 2 |
$ kubectl exec --namespace kube-system kube-scheduler-docker-for-desktop -c kube-scheduler -- uname -a $ kubectl exec --namespace kube-system kube-scheduler-docker-for-desktop -- uname -a |
同样,我们可以对指定名称的容器或pod中的所有容器运行该命令:
1 2 |
$ kubectl exec --namespace kube-system kube-scheduler-docker-for-desktop -c kube-scheduler -- uname -a Linux linuxkit-025000000001 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 GNU/Linux |
我们再来通过安装和向基于 web 的仪表盘记录日志来进一步了解Kubernetes。虽然Docker默认并不包含,通过Kubernetes项目提供的定义文件来安装非常之简单。我们只需要运行如下命令:
1 2 3 4 5 6 7 8 |
$ kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml secret/kubernetes-dashboard-certs created secret/kubernetes-dashboard-csrf created serviceaccount/kubernetes-dashboard created role.rbac.authorization.k8s.io/kubernetes-dashboard-minimal created rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard-minimal created deployment.apps/kubernetes-dashboard created service/kubernetes-dashboard created |
服务和部署创建之后,会需要几分钟来进行启动。你可以通过运行如下命令来查看状态:
1 2 |
$ kubectl get deployments --namespace kube-system $ kubectl get services --namespace kube-system |
在输出像下面这样时,仪表盘就安装并准备好了:
1 2 3 4 5 6 7 8 |
$ kubectl get deployments --namespace kube-system NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE kube-dns 1 1 1 1 1h kubernetes-dashboard 1 1 1 1 3m $ kubectl get services --namespace kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 1h kubernetes-dashboard ClusterIP 10.103.58.114 <none> 443/TCP 3m |
此时我们的仪表盘已处于运行中,我们要找到一种访问它的方式。这可以通过kubectl中内置的proxy服务。仅需运行如下命令来将其启动:
1 2 |
$ kubectl proxy Starting to serve on 127.0.0.1:8001 |
这会启动proxy,打开浏览器访问http://127.0.0.1:8001/version/会显示集群的相关信息:
但我们想要看的是仪表盘。可通过http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/来进行访问。
首次在浏览器中打开这个URL时会出现登录界面。因为我们是通过proxy来访问仪表盘的,仅需点击SKIP按钮:
译者注:新版的 Kubernetes 强制要求输出验证信息,没有再发现 SKIP 按钮,可通过如下命令来进行测试的 Token
1 |
$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') |
登录之后,就可以看到集群上的很多信息了:
现在我们的集群已经启动并运行了,我们可以启动一些示例应用。
Kubernetes和其它Docker工具
在启动Kubernetes时,我们选择了将Kubernetes作为Docker的stack命令的默认编排器。前一章中,Docker的stack会在Docker Swarm中启动我们的Docker Compose文件。我们所使用的Docker Compose文件像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
version: "3" services: cluster: image: russmckendrick/cluster ports: - "80:80" deploy: replicas: 6 restart_policy: condition: on-failure placement: constraints: - node.role == worker |
我们在Kubernetes上启动应用之前,需要做一些微调并删除placement,修改后文件的内容如下:
1 2 3 4 5 6 7 8 9 10 |
version: "3" services: cluster: image: russmckendrick/cluster ports: - "80:80" deploy: replicas: 6 restart_policy: condition: on-failure |
编辑了该文件之后,运行如下命令就会启动栈:
1 2 3 4 5 6 7 8 9 10 11 |
$ docker stack deploy --compose-file=docker-compose.yml cluster Waiting for the stack to be stable and running... cluster: Pending [pod status: 0/1 ready, 1/1 pending, 0/1 fail cluster: Pending [pod status: 0/2 ready, 2/2 pending, 0/2 fail cluster: Pending [pod status: 0/3 ready, 3/3 pending, 0/3 fail cluster: Pending [pod status: 0/4 ready, 4/4 pending, 0/4 fail cluster: Pending [pod status: 0/5 ready, 5/5 pending, 0/5 fail cluster: Pending [pod status: 0/6 ready, 6/6 pending, 0/6 fail cluster: Ready [pod status: 1/6 ready, 5/6 pending, 0/6 failed] Stack cluster is stable and running |
可以看到,Docker会等待栈可用后再回到命令提示符。我们还可以运行在Docker Swarm上启动栈后查看栈相关信息相同的命令:
1 2 3 |
$ docker stack ls $ docker stack services cluster $ docker stack ps cluster |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ docker stack ls NAME SERVICES ORCHESTRATOR NAMESPACE cluster 1 Kubernetes default $ docker stack services cluster ID NAME MODE REPLICAS IMAGE PORTS 90fb7c7b-65a cluster_cluster replicated 4/6 russmckendrick/cluster *:80->80/tcp $ docker stack ps cluster ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 90fe64f2-65a cluster_cluster-84879989d7-d4wrd russmckendrick/cluster docker-for-desktop Running Running 54 seconds ago *:0->80/tcp 9100830d-65a cluster_cluster-84879989d7-gqk52 russmckendrick/cluster docker-for-desktop Running Running 54 seconds ago *:0->80/tcp 90ff3630-65a cluster_cluster-84879989d7-jpvlr russmckendrick/cluster docker-for-desktop Running Running 54 seconds ago *:0->80/tcp 90ff33ef-65a cluster_cluster-84879989d7-rrmht russmckendrick/cluster docker-for-desktop Running Running 54 seconds ago *:0->80/tcp 91008a63-65a cluster_cluster-84879989d7-rvpnl russmckendrick/cluster docker-for-desktop Running Running 54 seconds ago *:0->80/tcp 9100924e-65a cluster_cluster-84879989d7-zdpwk russmckendrick/cluster docker-for-desktop Running Running 54 seconds ago *:0->80/tcp |
我们还可以使用kubectl来查看一些细节:
1 2 |
$ kubectl get deployments $ kubectl get services |
1 2 3 4 5 6 7 8 |
$ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE cluster 6 6 6 6 1m n$ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE cluster ClusterIP None <none> 55555/TCP 1m cluster-published LoadBalancer 10.96.156.192 localhost 80:31662/TCP 1m kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28m |
你可能注意到我们并不需要提供命名空间。这是因为我们的栈在默认命名空间中启动。同时,在列出服务时,会列出集群栈的集群 ClusterIP和LoadBalancer。查看LoadBalancer,你可以看到其外部 IP 为localhost、端口为80。
在浏览器中打开http://localhost/会显示该应用:
如果你的仪表盘还在打开的话,可以查看栈,甚至是打开其中一个容器的Terminal:
你可以通过运行如下命令来删除该栈:
1 |
$ docker stack rm cluster |
最后再提一点,你可能会想,太好了,我可以在Kubernetes集群的任何地方运行我的 Docker Compose文件了。额,严格意义上说并不是这样的。我们说到,在第一次启动Kubernetes时,启动了一些Docker组件。它们确保Docker尽可能紧密地进行集成。但是,这些组件在非Docker管理的集群中并不存在,因此你会无法使用docker stack命令。
并非没有补救措施。Kubernetes项目中提供了一个名为Kompose工具,可接收一个Docker Compose 文件并将其实时转化为一个Kubernetes定义文件。
要在macOS上安装Kompose,运行如下命令:
1 2 |
$ curl -L https://github.com/kubernetes/kompose/releases/download/v1.18.0/kompose-darwin-amd64 -o /usr/local/bin/kompose $ chmod +x /usr/local/bin/kompose |
Windows 10的用户可以使用 Chocolatey来安装这一二进制:
ℹ️Chocolatey是一个基于命令行的包管理器,可用于在Windows机器上安装各类软件包,与在Linux机器上使用yum或apt-get或者macOS上使用brew相似。
1 |
$ choco install kubernetes-kompose |
最后,Linux用户可以运行如下命令:
1 2 |
$ curl -L https://github.com/kubernetes/kompose/releases/download/v1.18.0/kompose-linux-amd64 -o /usr/local/bin/kompose $ chmod +x /usr/local/bin/kompose |
安装完成后,可以通过运行如下命令来启动你的Docker Compose文件:
1 |
$ kompose up |
你会得到类似下面的输出:
1 2 3 4 5 6 7 8 |
$ kompose up INFO We are going to create Kubernetes Deployments, Services and PersistentVolumeClaims for your Dockerized application. If you need different kind of resources, use the 'kompose convert' and 'kubectl create -f' commands instead. INFO Deploying application in "default" namespace INFO Successfully created Service: cluster INFO Successfully created Pod: cluster Your application has been deployed to Kubernetes. You can run 'kubectl get deployment,svc,pods,pvc' for details. |
根据输出中的提示,运行如下命令会给出刚刚启动的服务和pod的细节信息:
1 2 3 4 5 6 7 |
$ kubectl get deployment,svc,pods,pvc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/cluster ClusterIP 10.111.18.21 <none> 80/TCP 40s service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8h NAME READY STATUS RESTARTS AGE pod/cluster 1/1 Running 0 40s |
你可以通过运行如下命令来删除这些服务和pod:
1 2 3 4 |
$ kompose down INFO Deleting application in "default" namespace INFO Successfully deleted Service: cluster INFO Successfully deleted Pod: cluster |
虽然你可以使用kompose up和kompose down,我会建议生成Kubernetes定义文件并对它们按需进行修改。只需运行如下命令:
1 |
$ kompose convert |
这会生成pod和服务文件:
1 2 3 |
$ kompose convert INFO Kubernetes file "cluster-service.yaml" created INFO Kubernetes file "cluster-pod.yaml" created |
你会看到Docker Compose文件和生成的两个文件之间有相当大的不同。cluster-pod.yaml文件类似下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: io.kompose.service: cluster name: cluster spec: containers: - image: russmckendrick/cluster name: cluster ports: - containerPort: 80 resources: {} restartPolicy: OnFailure status: {} |
cluster-service.yaml文件类似下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
apiVersion: v1 kind: Service metadata: annotations: kompose.cmd: kompose convert kompose.version: 1.18.0 (06a2e56) creationTimestamp: null labels: io.kompose.service: cluster name: cluster spec: ports: - name: "80" port: 80 targetPort: 80 selector: io.kompose.service: cluster status: loadBalancer: {} |
然后你可以通过运行如下命令来启动这些文件:
1 2 3 |
$ kubectl create -f cluster-pod.yaml $ kubectl create -f cluster-service.yaml $ kubectl get deployment,svc,pods,pvc |
1 2 3 4 5 6 7 8 9 10 11 |
$ kubectl create -f cluster-pod.yaml pod/cluster created $ kubectl create -f cluster-service.yaml service/cluster created $ kubectl get deployment,svc,pods,pvc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/cluster ClusterIP 10.100.207.92 <none> 80/TCP 5s service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8h NAME READY STATUS RESTARTS AGE pod/cluster 1/1 Running 0 10s |
要删除该集群的pod和服务,我们只需要运行如下命令即可:
1 |
$ kubectl delete service/cluster pod/cluster |
虽然在后面的章节还会使用到Kubernetes,建议还是先在Docker桌面软件中先取消对Kubernetes的集成,因为它在空闲时会带来一些系统资源占用。只需取消勾选Enable Kubernetes即可。点击Apply时,Docker会停止所有运行Kubernetes所需的容器,但它不会删除这些镜像,这样在你重新启动它时可以很快的完成。
总结
本章中,我们通过Docker桌面软件来学习了Kubernetes。Kubernetes的内容远不止本章中所讲解的这些,所以千万不要觉得它只有这些。在讨论了Kubernetes起源之后,我们看了如何使用Docker for Mac或Docker for Windows在本地机器上启动它。
然后我们讨论了kubectl的基本使用,接着学习像Docker Swarm那样使用docker stack启动并运行应用。
本章的最后,我们讨论了Kompose,它是Kubernetes项目下的一个工具。帮助我们将Docker Compose文件转化为Kubernetes所用的文件,让我们可以将应用迁移到纯Kubernetes上。
下一章中,我们将来学习公有云上的Docker,比如Amazon Web Services,并简短地再次使用Kubernetes。
课后问题
- 是非题:勾选了Show system containers (advanced)之后,你就看不到用于启动Kubernetes的镜像了。
- 哪四个命名空间托管用于运行Kubernetes和在Docker中启动支持的容器?
- 你会运行哪个命令来查看pod中运行的容器的详细信息?
- 你会使用哪个命令来启动Kubernetes的YAML定义文件?
- 通常,kubectl proxy命令会在本地机器上打开哪个端口?
- Google的容器编排平台的最初名称是什么?
扩展阅读
本章开始处提及的一些Google工具、PPT 和白皮书参见如下链接:
- cgroups: http://man7.org/linux/man-pages/man7/cgroups.7.html
- lmctfy: https://github.com/google/lmctfy/
- Joe Beda在GluCon上的幻灯片 扩展中的容器 : https://pdfs.semanticscholar.org/presentation/4df0/b2bcd39b7757867b1ead3009a628e07d8b57.pdf
- Google 使用 Borg 进行大规模集群的管理: https://ai.google/research/pubs/pub43438
- LXC – https://linuxcontainers.org/
本章说涉及到的云服务的详细信息参见:
- Google Kubernetes引擎(GKE): https://cloud.google.com/kubernetes-engine/
- Azure Kubernetes服务 (AKS): https://azure.microsoft.com/en-gb/services/kubernetes-service/
- Amazon的Kubernetes弹性容器服务(Amazon EKS): https://aws.amazon.com/eks/
- IBM云Kubernetes服务: https://www.ibm.com/cloud/container-service
- Oracler的Kubernetes容器引擎: https://cloud.oracle.com/containers/kubernetes-engine
- DigitalOcean上的Kubernetes: https://www.digitalocean.com/products/kubernetes/
以下可以查看到Docker有关对于Kubernetes支持的声明:
- Docker Enterprise的Kubernetes声明:https://blog.docker.com/2017/10/docker-enterprise-edition-kubernetes/
- Kubernetes稳定版发布: https://blog.docker.com/2018/07/kubernetes-is-now-available-in-docker-desktop-stable-channel/
最后,Kompose的主页为:
- Kompose – http://kompose.io/