Kubernetes In Action :2开始使用Kubernetes和Docker
Posted 沛沛老爹
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kubernetes In Action :2开始使用Kubernetes和Docker相关的知识,希望对你有一定的参考价值。
2、开始使用Kubernetes和Docker
说明
本章内容涵盖
- 使用Docker创建、运行及共享容器镜像
- 在本地部署单节点的Kubernetes集群
- 在Google Kubernetes Engine上部署Kubernetes集群
- 配置和使用命令行客户端 —— kubectl
- 在Kubernetes上部署应用并进行水平伸缩
在深入学习Kubernetes的概念之前,先来看看如何创建一个简单的应用,把它打包成容器镜像并在远端的Kubernetes集群(如托管在Google Kubernetes Engine中)或本地单节点集群中运行。这会对整个Kubernetes体系有较好的了解,并且会让接下来几个章节对Kubernetes基本概念的学习变得简单。
2.1 创建、运行及共享容器镜像
正如在之前章节所介绍的,在Kubernetes中运行应用需要打包好的容器镜像。本节将会对Docker的使用做简单的介绍。接下来的几节中将会介绍:
- 安装Docker并运行第一个 “Hello world” 容器
- 创建一个简单的Node.js应用并部署在Kubernetes中
- 把应用打包成可以独立运行的容器镜像
- 基于镜像运行容器
- 把镜像推送到Docker Hub,这样任何人在任何地方都可以使用
2.1.1 安装Docker并运行Hello World容器
首先,需要在Linux主机上安装Docker。如果使用的不是Linux操作系统,就需要启动Linux虚拟机(VM)并在虚拟机中运行Docker。如果使用的是Mac或Windows系统,Docker将会自己启动一个虚拟机并在虚拟机中运行Docker守护进程。Docker客户端可执行文件可以在宿主操作系统中使用,并可以与虚拟机中的守护进程通信。
根据操作系统的不同,按照http://docs.docker.com/engine/installation/上的指南安装Docker。安装完成后,可以通过运行Docker客户端可执行文件来执行各种Docker命令。例如,可以试着从Docker Hub的公共镜像仓库拉取、运行镜像,Docker hub中有许多随时可用的常见镜像,其中就包括 busybox,可以用来运行简单的 echo "Hello world" 命令。
运行Hello World容器
busybox是一个单一可执行文件,包含多种标准UNIX命令行工具,如:echo、ls、gzip 等。除了包含 echo 命令的 busybox 命令,也可以使用如Fedora、Ubuntu等功能完备的镜像。
如何才能运行 busybox 镜像呢?无须下载或者安装任何东西。使用 docker run 命令然后指定需要运行的镜像的名字,以及需要执行的命令(可选),如下面这段代码。
代码清单2.1 使用Docker运行一个Hello world容器
$ docker run busybox echo "Hello world"
Unable to find image 'busybox:latest' locally
latest: Pulling from docker.io/busybox
9al63e0b8d13: Pull complete
fef924a0204a: Pull complete
Digest: sha256:97473e34e31le6c1b3f61f2a721d038dle5eef17d98d1353a513007cf46ca6bd
Status: Downloaded newer image for docker.io/busybox:latest
Hello world
这或许看起来并不那么令人印象深刻,但非常棒的是仅仅使用一个简单的命令就下载、运行一个完整的“应用”,而不用安装应用或是做其他的事情。目前的应用是单一可执行文件(busybox),但也可以是一个有许多依赖的复杂应用。整个配置运行应用的过程是完全一致的。同样重要的是应用是在容器内部被执行的,完全独立于其他所有主机上运行的进程。
背后的原理
图 2.1 展示了执行 docker run 命令之后发生的事情。首先,Docker会检查busybox:latest 镜像是否已经存在于本机。如果没有,Docker会从http://docker.io的Docker镜像中心拉取镜像。镜像下载到本机之后,Docker基于这个镜像创建一个容器并在容器中运行命令。echo 命令打印文字到标准输出流,然后进程终止,容器停止运行。
图2.1 在一个基于busybox镜像的容器中运行echo "Hello world"
运行其他镜像
运行其他的容器镜像和运行busybox镜像是一样的,甚至可能更简单,因为你可以不需要指定执行命令。就像例子中的 echo "Hello world",被执行的命令通常都会被包含在镜像中,但也可以根据需要进行覆盖。在浏览器中搜索http://hub.docker.com或其他公开的镜像中心的可用镜像之后,可以像这样在Docker中运行镜像:
$ docker run <image>
容器镜像的版本管理
当然,所有的软件包都会更新,所以通常每个包都不止一个版本。Docker支持同一镜像的多个版本。每一个版本必须有唯一的tag名。当引用镜像没有显式地指定tag时,Docker会默认指定tag为latest。如果想要运行别的版本的镜像,需要像这样指定镜像的版本:
$ docker run <image>:<tag>
2.1.2 创建一个简单的 Node.js 应用
现在有了一个可以工作的Docker环境来创建应用。接下来会构建一个简单的Node.js Web应用,并把它打包到容器镜像中。这个应用会接收HTTP请求并响应应用运行的主机名。这样,应用运行在容器中,看到的是自己的主机名而不是宿主机名,即使它也像其他进程一样运行在宿主机上。这在后面会非常有用,当应用部署在Kubernetes上并进行伸缩时(水平伸缩,复制应用到多个节点),你会发现HTTP请求切换到了应用的不同实例上。
应用包含一个名为app.js的文件,详见下面的代码清单。
代码清单2.2 一个简单的Node.js应用:app.js
const http = require('http');const os = require('os');
console.log("Kubia server starting...");
var handler = function(request, response)
console.log("Received request from " + request.connection.remoteAddress);
response.writeHead(200);
response.end("You've hit " + os.hostname() + "\\n");
;
var www = http.createServer(handler);
www.listen(8080);
代码清晰地说明了实现的功能。这里在8080端口启动了一个HTTP服务器。服务器会以状态码 200 OK 和文字 "You've hit <hostname>" 来响应每个请求。请求handler会把客户端的IP打印到标准输出,以便日后查看。注意 返回的主机名是服务器真实的主机名,不是客户端发出的HTTP请求中头的 Host 字段。
现在可以直接下载安装Node.js来测试代码了,但是这不是必需的,因为可以直接用Docker把应用打包成镜像,这样在需要运行的主机上就无须下载和安装其他的东西(当然不包括安装Docker来运行镜像)。
2.1.3 为镜像创建Dockerfile
为了把应用打包成镜像,首先需要创建一个叫Dockerfile的文件,它包含了一系列构建镜像时会执行的指令。Dockerfile文件需要和app.js文件在同一目录,并包含下面代码清单中的命令。
代码清单2.3 构建应用容器镜像的Dockerfile
FROM node:7
ADD app.js /app.js
ENTRYPOINT ["node", "app.js"]
From 行定义了镜像的起始内容(构建所基于的基础镜像)。这个例子中使用的是 node 镜像的tag 7 版本。第二行中把app.js文件从本地文件夹添加到镜像的根目录,保持app.js这个文件名。最后一行定义了当镜像被运行时需要被执行的命令,这个例子中,命令是 node app.js。
选择基础镜像
你或许在想,为什么要选择这个镜像作为基础镜像。因为这个应用是Node.js应用,镜像需要包含可执行的 node 二进制文件来运行应用。你也可以使用任何包含这个二进制文件的镜像,或者甚至可以使用Linux发行版的基础镜像,如 fedora或ubuntu,然后在镜像构建的时候安装Node.js。但是由于 node镜像是专门用来运行Node.js应用的,并且包含了运行应用所需的一切,所以把它当作基础镜像。
2.1.4 构建容器镜像
现在有了Dockerfile和app.js文件,这是用来构建镜像的所有文件。运行下面的Docker命令来构建镜像:
$ docker build -t kubia
图2.2展示了镜像构建的过程。用户告诉Docker需要基于当前目录(注意命令结尾的点)构建一个叫kubia的镜像,Docker会在目录中寻找Dockerfile,然后基于其中的指令构建镜像。
图2.2 基于Dockerfile构建一个新的容器镜像
镜像是如何构建的
构建过程不是由Docker客户端进行的,而是将整个目录的文件上传到Docker守护进程并在那里进行的。Docker客户端和守护进程不要求在同一台机器上。如果你在一台非Linux操作系统中使用Docker,客户端就运行在你的宿主操作系统上,但是守护进程运行在一个虚拟机内。由于构建目录中的文件都被上传到了守护进程中,如果包含了大量的大文件而且守护进程不在本地运行,上传过程会花费更多的时间。
提示 不要在构建目录中包含任何不需要的文件,这样会减慢构建的速度——尤其当Docker守护进程运行在一个远端机器的时候。
在构建过程中,Docker首次会从公开的镜像仓库(Docker Hub)拉取基础镜像(node:7),除非已经拉取过镜像并存储在本机上了。
镜像分层
镜像不是一个大的二进制块,而是由多层组成的,在运行busybox例子时你可能已经注意到(每一层有一行Pull complete),不同镜像可能会共享分层,这会让存储和传输变得更加高效。比如,如果创建了多个基于相同基础镜像(比如例子中的 node:7)的镜像,所有组成基础镜像的分层只会被存储一次。拉取镜像的时候,Docker会独立下载每一层。一些分层可能已经存储在机器上了,所以Docker只会下载未被存储的分层。
你或许会认为每个Dockerfile只创建一个新层,但是并不是这样的。构建镜像时,Dockerfile中每一条单独的指令都会创建一个新层。镜像构建的过程中,拉取基础镜像所有分层之后,Docker在它们上面创建一个新层并且添加app.js。然后会创建另一层来指定镜像被运行时所执行的命令。最后一层会被标记为kubia:latest。图2.3 展示了这个过程,同时也展示另外一个叫 other:latest 的镜像如何与我们构建的镜像共享同一层Node.js镜像。
图2.3 容器镜像是由多层组成的,每一层可以被不同镜像复用
构建完成时,新的镜像会存储在本地。下面的代码展示了如何通过Docker列出本地存储的镜像:
代码清单2.4 列出本地存储的镜像
s docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
kubia latest d30ecc7419e7 1 minute ago 637.1 MB
比较使用Dockerfile和手动构建镜像
Dockerfile是使用Docker构建容器镜像的常用方式,但也可以通过运行已有镜像容器来手动构建镜像,在容器中运行命令,退出容器,然后把最终状态作为新镜像。用Dockerfile构建镜像是与此相同的,但是是自动化且可重复的,随时可以通过修改Dockerfile重新构建镜像而无须手动重新输入命令。
2.1.5 运行容器镜像
以下的命令可以用来运行镜像:
$ docker run --name kubia-container -p 8080:8080 -d kubia
这条命令告知Docker基于 kubia 镜像创建一个叫 kubia-container 的新容器。这个容器与命令行分离(-d 标志),这意味着在后台运行。本机上的8080端口会被映射到容器内的8080端口(-p 8080:8080 选项),所以可以通过http://localhost:8080 访问这个应用。
如果没有在本机上运行Docker守护进程(比如使用的是Mac或Windows系统,守护进程会运行在VM中),需要使用VM的主机名或IP来代替localhost运行守护进程。可以通过 DOCKER_HOST 这个环境变量查看主机名。
访问应用
现在试着通过 http://localhost:8080 访问你的应用(确保使用Docker主机名或IP替换localhost):
$ curl localhost:8080
You've hit 44d76963e8e1
这是应用的响应。现在应用运行在容器中,与其他东西隔离。可以看到,应用把 44d76963e8e1 作为主机名返回,这并不是宿主机的主机名。这个十六进制数是Docker容器的ID。
列出所有运行中的容器
下面的代码清单列出了所有的运行中的容器,可以查看列表(为了更好的可读性,列表被分成了两行显示)。
代码清单2.5 列出运行中的容器
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9d3e6e24ab3c kubia "node app.js" 4 minutes ago Up 4 minutes 0.0.0.0:8080->8080/tcp kubia-container
有一个容器在运行。Docker会打印出每一个容器的ID和名称、容器运行所使用的镜像,以及容器中执行的命令。
获取更多的容器信息
docker ps 只会展示容器的大部分基础信息。可以使用 docker inspect查看更多的信息:
$ docker inspect kubia-container
Docker会打印出包含容器底层信息的长JSON。
2.1.6 探索运行容器的内部
我们来看看容器内部的环境。由于一个容器里可以运行多个进程,所以总是可以运行新的进程去看看里面发生了什么。如果镜像里有可用的shell二进制可执行文件,也可以运行一个shell。
在已有的容器内部运行shell
镜像基于的Node.js镜像包含了bash shell,所以可以像这样在容器内运行shell:
$ docker exec -it kubia-container bash
这会在已有的kubia-container容器内部运行bash。bash 进程会和主容器进程拥有相同的命名空间。这样可以从内部探索容器,查看Node.js和应用是如何在容器里运行的。-it 选项是下面两个选项的简写:
- -i,确保标准输入流保持开放。需要在shell中输入命令。
- -t,分配一个伪终端(TTY)。
如果希望像平常一样使用shell,需要同时使用这两个选项(如果缺少第一个选项就无法输入任何命令。如果缺少第二个选项,那么命令提示符不会显示,并且一些命令会提示 TERM 变量没有设置)。
从内部探索容器
下面的代码展示了如何使用shell查看容器内运行的进程。
代码清单2.6 从容器内列出进程
# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 1.4 614436 26376 ? Ssl 08:55 0:00 node app.js
root 12 0.1 0.1 20248 3196 pts/0 Ss 09:01 0:00 bash
root 19 0.0 0.1 17504 2000 pts/0 R+ 09:02 0:00 ps aux
只看到了三个进程,宿主机上没有看到其他进程。
容器内的进程运行在主机操作系统上
如果现在打开另一个终端,然后列出主机操作系统上的进程,连同其他的主机进程依然会发现容器内的进程,如代码清单2.7所示。
注意 如果使用的是Mac或者Windows系统,需要登录到Docker守护进程运行的VM查看这些进程。
代码清单2.7 运行在主机操作系统上的容器进程
$ ps aux | grep app.js
USER PID CPU MEM vsz RSS TTY STAT START TIME COMMAND
root 382 0.0 0.1 676380 16504 ? S1 12:31 0:00 node app.js
这证明了运行在容器中的进程是运行在主机操作系统上的。如果你足够敏锐,会发现进程的ID在容器中与主机上不同。容器使用独立的PID Linux命名空间并且有着独立的系列号,完全独立于进程树。
容器的文件系统也是独立的
正如拥有独立的进程树一样,每个容器也拥有独立的文件系统。在容器内列出根目录的内容,只会展示容器内的文件,包括镜像内的所有文件,再加上容器运行时创建的任何文件(类似日志文件),如下面的代码清单所示。
代码清单2.8 容器拥有完整的文件系统
root@44d76963e8el:/# 1s
app.js boot etc lib mediabin dev home 1ib64 mnt
opt root sbin sys usrpros run srv tmp var
其中包含app.js文件和其他系统目录,这些目录是正在使用的 node:7 基础镜像的一部分。可以使用 exit 命令来退出容器返回宿主机(类似于登出ssh session)。
提示 进入容器对于调试容器内运行的应用来说是非常有用的。出错时,需要做的第一件事是查看应用运行的系统的真实状态。需要记住的是,应用不仅拥有独立的文件系统,还有进程、用户、主机名和网络接口。
2.1.7 停止和删除容器
可以通过告知Docker停止 kubia-container 容器来停止应用:
$ docker stop kubia-container
因为没有其他的进程在容器内运行,这会停止容器内运行的主进程。容器本身仍然存在并且可以通过 docker ps-a 来查看。-a 选项打印出所有的容器,包括运行中的和已经停止的。想要真正地删除一个容器,需要运行 docker rm :
$ docker rm kubia-container
这会删除容器,所有的内容会被删除并且无法再次启动。
2.1.8 向镜像仓库推送镜像
现在构建的镜像只可以在本机使用。为了在任何机器上都可以使用,可以把镜像推送到一个外部的镜像仓库。为了简单起见,不需要搭建一个私有的镜像仓库,而是可以推送镜像到公开可用的Docker Hub(http://hub.docker.com)镜像中心。另外还有其他广泛使用的镜像中心,如http://Quay.io和Google Container Registry。
在推送之前,需要重新根据Docker Hub的规则标注镜像。Docker Hub允许向以你的Docker Hub ID开头的镜像仓库推送镜像。可以在http://hub.docker.com上注册Docker Hub ID。下面的例子中会使用笔者自己的ID(luksa),请在每次出现时替换自己的ID。
使用附加标签标注镜像
一旦知道了自己的ID,就可以重命名镜像,现在镜像由 kubia 改为luksa/kubia(用自己的Docker Hub ID代替 luksa):
$ docker tag kubia luksa/kubia
这不会重命名标签,而是给同一个镜像创建一个额外的标签。可以通过docker images 命令列出本机存储的镜像来加以确认,如下面的代码清单所示。
代码清单2.9 一个容器镜像可以有多个标签
# docker images | head
REPOSITORY TAG IMAGE ID CREATED SIZE
kubia v2 8af4d2d1750a 2 minutes ago 82.7MB
正如所看到的,kubia 和 luksa/kubia 指向同一个镜像ID,所以实际上是同一个镜像的两个标签。
向Docker Hub推送镜像
在向Docker Hub推送镜像之前,先需要使用 docker login 命令和自己的用户ID登录,然后就可以像这样向Docker Hub推送 yourid/kubia 镜像:
$ docker push luksa/kubia
在不同机器上运行镜像
在推送完成之后,镜像便可以给任何人使用。可以在任何机器上运行下面的命令来运行镜像:
$ docker run -p 8080:8080 luksa/kubia
这非常简单。最棒的是应用每次都运行在完全一致的环境中。如果在你的机器上正常运行,也会在所有的Linux机器上正常运行。无须担心主机是否安装了Node.js。事实上,就算安装了,应用也并不会使用,因为它使用的是镜像内部安装的。
2.2 配置Kubernetes集群
现在,应用被打包在一个容器镜像中,并通过Docker Hub给大家使用,可以将它部署到Kubernetes集群中,而不是直接在Docker中运行。但是需要先设置集群。
设置一个完整的、多节点的Kubernetes集群并不是一项简单的工作,特别是如果你不精通Linux和网络管理的话。一个适当的Kubernetes安装需要包含多个物理或虚拟机,并需要正确地设置网络,以便在Kubernetes集群内运行的所有容器都可以在相同的扁平网络环境内相互连通。
安装Kubernetes集群的方法有许多。这些方法在http://kubernetes.io的文档中有详细描述。我们不会在这里列出所有,因为内容在不断变化,但Kubernetes可以在本地的开发机器、自己组织的机器集群或是虚拟机提供商(Google Compute Engine、Amazon EC2、Microsoft Azure等)上运行,或者使用托管的Kubernetes集群,如Google Kubernetes Engine(以前称为Google Container Engine)。
在这一章中,将介绍用两种简单的方法构建可运行的Kubernetes集群,你将会看到如何在本地机器上运行单节点Kubernetes集群,以及如何访问运行在Google Kubernetes Engine(GKE)上的托管集群。
第三个选项是使用 kubeadm 工具安装一个集群,这会在附录B中介绍,这里的说明向你展示了如何使用虚拟机建立一个三节点的Kubernetes集群,但是建议你在阅读本书的前11章之后再尝试。
另一个选择是在亚马逊的AWS(Amazon Web Services)上安装Kubernetes。为此,可以查看 kops 工具,它是在前面一段提到的 kubeadm 基础之上构建的,可以在http://github.com/kubernetes/kops中找到。它帮助你在AWS上部署生产级、高可用的Kubernetes集群,并最终会支持其他平台(Google Kubernetes Engine、VMware、vSphere等)。
2.2.1 用Minikube运行一个本地单节点Kubernetes集群
使用Minikube是运行Kubernetes集群最简单、最快捷的途径。Minikube是一个构建单节点集群的工具,对于测试Kubernetes和本地开发应用都非常有用。
虽然我们不能展示与管理多节点应用相关的一些Kubernetes特性,但是单节点集群足以探索本书中讨论的大多数主题。
安装Minikube
Minikube是一个需要下载并放到路径中的二进制文件。它适用于OSX、Linux和Windows系统。最好访问GitHub上的Minikube代码仓库(http://github.com/kubernetes/minikube),按照说明来安装它。
例如,在OSX和Linux系统上,可以使用一个命令下载Minikube并进行设置。对于OSX系统,命令是这样的:
$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.23.0/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube/usr/local/bin/
在Linux系统中,可以下载另一个版本(将URL中的 “darwin” 替换为“linux”)。在Windows系统中,可以手动下载文件,将其重命名为minikube.exe,并把它加到路径中。Minikube在VM中通过VirtualBox或KVM运行Kubernetes,所以在启动Minikube集群之前,还需要安装VM。
使用Minikue启动一个Kubernetes集群
当你在本地安装了Minikube之后,可以立即使用下面的命令启动Kubernetes集群。
代码清单2.10 启动一个Minikube虚拟机
$ minikube start
starting local Kubernetes cluster...
starting vM...
SSH-ing files into vM....
...
kubectl is now configured to use the cluster.
启动集群需要花费超过一分钟的时间,所以在命令完成之前不要中断它。
安装Kubernetes客户端(kubectl)
要与Kubernetes进行交互,还需要 kubectl CLI客户端。同样,需要做的就是下载它,并放在路径中。例如,OSX系统的最新稳定版本可以通过以下命令下载并安装:
$ /$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt) /bin/darwin/amd64/kubectl
&& chmod +x kubectl
&& sudo mv kubectl /usr/local/bin/
要下载用于Linux或Windows系统的kubectl,用 linux 或 windows 替换URL中的 darwin。
注意 如果你需要使用多个Kubernetes集群(例如,Minikube和GKE),请参考附录A,了解如何在不同的 kubectl 上下文中设置和切换。
使用kubectl查看集群是否正常工作
要验证集群是否正常工作,可以使用以下所示的 kubectl cluster-info命令。
代码清单2.11 展示集群信息
$ kubectl cluster-info
Kubernetes master is running at https://192.168.99.100:8443
KubeDNS is running at https://192.168.99.100:8443/api/v1/proxy/...
kubernetes-dashboard is running at https://192.168.99.100:8443/api/v1/.....
这里显示集群已经启动。它显示了各种Kubernetes组件的URL,包括API服务器和Web控制台。
提示 可以运行 minikube ssh 登录到Minikube VM并从内部探索它。例如,可以查看在节点上运行的进程。
2.2.2 使用Google Kubernetes Engine托管Kubernetes集群
如果你想探索一个完善的多节点Kubernetes集群,可以使用托管的Google Kubernetes Engine(GKE)集群。这样,无须手动设置所有的集群节点和网络,因为这对于刚开始使用Kubernetes的人来说太复杂了。使用例如GKE这样的托管解决方案可以确保不会出现配置错误、不工作或部分工作的集群。
配置一个Google Cloud项目并且下载必需的客户端二进制
在设置新的Kubernetes集群之前,需要设置GKE环境。因为这个过程可能会改变,所以不在这里列出具体的说明。阅读https://cloud.google.com/containerengine/docs/before-begin中的说明后就可以开始了。
整个过程大致包括:
- 注册谷歌账户,如果你还没有注册过。
- 在Google Cloud Platform控制台中创建一个项目。
- 开启账单。这会需要你的信用卡信息,但是谷歌提供了为期12个月的免费试用。而且在免费试用结束后不会自动续费。
- 开启Kubernetes Engine API。
- 下载安装Google Cloud SDK(这包含 gcloud 命令行工具,需要创建一个Kubernetes集群)。
- 使用 gcloud components install kubectl 安装 kubectl 命令行工具。
注意 某些操作(例如步骤2中的操作)可能需要几分钟才能完成,所以在此期间可以喝杯咖啡放松一下。
创建一个三节点Kubernetes集群
完成安装后,可以使用下面代码清单中的命令创建一个包含三个工作节点的Kubernetes集群。
代码清单2.12 在GKE上创建一个三节点集群
$ gcloud container clusters create kubia --num-nodes 3 --machine-tvpe fl-micro
Creating cluster kubia... done
Created [https://container.googleapis.com/y1/proiects/kubial-1227/zones/europe-west1-d/clusters/kubia]
kubeconfig entry generated for kubia.
NAME ZONE MST VER MASTER IP TYPE NODE VER NUM NODES STATUSkubia eu-wld 1.5.3 104.155.92.30 fl-micro 1.5.3: RUNNING
现在已经有一个正在运行的Kubernetes集群,包含了三个工作节点,如图 2.4所示。你在使用三个节点来更好地演示适用于多节点的特性,如果需要的话可以使用较少数量的节点。
获取集群概览
图 2.4 能够让你对集群,以及如何与集群交互有一个初步的认识。每个节点运行着Docker、Kubelet和kube-proxy。可以通过 kubectl 命令行客户端向运行在主节点上的Kubernetes API服务器发出REST请求以与集群交互。
图2.4 如何与三节点Kubernetes集群进行交互
通过列出集群节点查看集群是否在运行
现在可以使用kubectl命令列出集群中的所有节点,如下面的代码清单所示。
代码清单2.13 使用kubectl列出集群节点
# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master NotReady control-plane,master 10h v1.20.2
k8s-node1 NotReady <none> 9h v1.20.2
k8s-node2 NotReady <none> 9h v1.20.2
kubectl get 命令可以列出各种Kubernetes对象。你将会经常使用到它,但它通常只会显示对象最基本的信息。
提示 可以使用 gcloud compute ssh <node-name> 登录到其中一个节点,查看节点上运行了什么。
查看对象的更多信息
要查看关于对象的更详细的信息,可以使用 kubectl describe 命令,它显示了更多信息:
# kubectl describe node k8s-master
Name: k8s-master
Roles: control-plane,master
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=k8s-master
kubernetes.io/os=linux
node-role.kubernetes.io/control-plane=
node-role.kubernetes.io/master=
Annotations: kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
node.alpha.kubernetes.io/ttl: 0
volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp: Fri, 12 Feb 2021 21:32:22 +0800
Taints: node-role.kubernetes.io/master:NoSchedule
node.kubernetes.io/not-ready:NoSchedule
Unschedulable: false
Lease:
HolderIdentity: k8s-master
AcquireTime: <unset>
RenewTime: Sat, 13 Feb 2021 07:41:25 +0800
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
MemoryPressure False Sat, 13 Feb 2021 07:38:24 +0800 Fri, 12 Feb 2021 21:32:19 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Sat, 13 Feb 2021 07:38:24 +0800 Fri, 12 Feb 2021 21:32:19 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure
PIDPressure False Sat, 13 Feb 2021 07:38:24 +0800 Fri, 12 Feb 2021 21:32:19 +0800 KubeletHasSufficientPID kubelet has sufficient PID available
Ready False Sat, 13 Feb 2021 07:38:24 +0800 Fri, 12 Feb 2021 21:32:19 +0800 KubeletNotReady runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
Addresses:
InternalIP: 192.168.137.100
Hostname: k8s-master
Capacity:
cpu: 2
ephemeral-storage: 38770180Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3880576Ki
pods: 110
Allocatable:
cpu: 2
ephemeral-storage: 35730597829
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3778176Ki
pods: 110
System Info:
Machine ID: 4eec0f78eb7f4eb8b33480fe97fca874
System UUID: 5A5A9919-F309-7748-9C28-CB280C32FD39
Boot ID: dc707f60-c19b-4883-889a-81cac673950c
Kernel Version: 3.10.0-1160.15.2.el7.x86_64
OS Image: CentOS Linux 7 (Core)
Operating System: linux
Architecture: amd64
Container Runtime Version: docker://20.10.3
Kubelet Version: v1.20.2
Kube-Proxy Version: v1.20.2
PodCIDR: 10.244.0.0/24
PodCIDRs: 10.244.0.0/24
Non-terminated Pods: (5 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits AGE
--------- ---- ------------ ---------- --------------- ------------- ---
kube-system etcd-k8s-master 100m (5%) 0 (0%) 100Mi (2%) 0 (0%) 10h
kube-system kube-apiserver-k8s-master 250m (12%) 0 (0%) 0 (0%) 0 (0%) 10h
kube-system kube-controller-manager-k8s-master 200m (10%) 0 (0%) 0 (0%) 0 (0%) 10h
kube-system kube-proxy-mskzh 0 (0%) 0 (0%) 0 (0%) 0 (0%) 10h
kube-system kube-scheduler-k8s-master 100m (5%) 0 (0%) 0 (0%) 0 (0%) 10h
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 650m (32%) 0 (0%)
memory 100Mi (2%) 0 (0%)
ephemeral-storage 100Mi (0%) 0 (0%)
hugepages-1Gi 0 (0%) 0 (0%)
hugepages-2Mi 0 (0%) 0 (0%)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal NodeHasSufficientMemory 10h (x5 over 10h) kubelet Node k8s-master status is now: NodeHasSufficientMemory
Normal NodeHasNoDiskPressure 10h (x5 over 10h) kubelet Node k8s-master status is now: NodeHasNoDiskPressure
Normal NodeHasSufficientPID 10h (x4 over 10h) kubelet Node k8s-master status is now: NodeHasSufficientPID
Normal Starting 10h kubelet Starting kubelet.
Normal NodeHasSufficientMemory 10h kubelet Node k8s-master status is now: NodeHasSufficientMemory
Normal NodeHasNoDiskPressure 10h kubelet Node k8s-master status is now: NodeHasNoDiskPressure
Normal NodeHasSufficientPID 10h kubelet Node k8s-master status is now: NodeHasSufficientPID
Normal NodeAllocatableEnforced 10h kubelet Updated Node Allocatable limit across pods
Normal Starting 10h kube-proxy Starting kube-proxy.
Normal Starting 18m kubelet Starting kubelet.
Normal NodeHasSufficientMemory 18m (x8 over 18m) kubelet Node k8s-master status is now: NodeHasSufficientMemory
Normal NodeHasNoDiskPressure 18m (x8 over 18m) kubelet Node k8s-master status is now: NodeHasNoDiskPressure
Normal NodeHasSufficientPID 18m (x7 over 18m) kubelet Node k8s-master status is now: NodeHasSufficientPID
Normal NodeAllocatableEnforced 18m kubelet Updated Node Allocatable limit across pods
Normal Starting 18m kube-proxy Starting kube-proxy.
这里省略了 describe 命令的实际输出,因为内容非常多且在书中是完全不可读的。输出显示了节点的状态、CPU和内存数据、系统信息、运行容器的节点等。
在前面的 kubectl describe 示例中,显式地指定了节点的名称,但也可以执行一个简单的 kubectl describe node 命令,而无须指定节点名,它将打印出所有节点的描述信息。
提示 当只有一个给定类型的对象存在时,不指定对象名就运行 description和 get 命令是很提倡的,这样不会浪费时间输入或复制、粘贴对象的名称。
当我们讨论减少输入的时候,开始在Kubernetes运行第一个应用程序之前,先学习如何让 kubectl 命令的使用变得更容易。
2.2.3 为kubectl配置别名和命令行补齐
kubectl 会被经常使用。很快你就会发现每次不得不打全命令是非常痛苦的。在继续之前,花一分钟为kubectl 设置别名和tab命令补全可让使用变得简单。
创建别名
在整本书中,一直会使用 kubectl 可执行文件的全名,但是你可以添加一个较短的别名,如 k,这样就不用每次都输入 kubectl 了。如果还没有设置别名,这里会告诉你如何定义。将下面的代码添加到 ~/.bashrc 或类似的文件中:
alias k=kubectl
注意 如果你已经在用 gcloud 配置集群,就已经有可执行文件 k 了。
为kuebctl配置tab补全
即使使用短别名k,仍然需要输入许多内容。幸运的是,kubectl命令还可以配置bash和zsh shell的代码补全。tab补全不仅可以补全命令名,还能补全对象名。例如,无须在前面的示例中输入整个节点名,只需输入
$ kubectl desc<TAB> no<TAB> gke-ku<TAB>
需要先安装一个叫作 bashcompletion 的包来启用bash中的tab命令补全,然后可以运行接下来的命令(也需要加到 ~/.bashrc 或类似的文件中):
$ source <(kubectl completion bash)
但是需要注意的是,tab命令行补全只在使用完整的 kubectl 命令时会起作用(当使用别名 k 时不会起作用)。需要改变 kubectl completion 的输出来修复:
$ source <(kubectl completion bash | sed s/kubectl/k/g)
注意 不幸的是,在写作本书之时,别名的shell命令补全在MacOS系统上并不起作用。如果需要使用命令行补全,就需要使用完整的 kubectl 命令。
现在你已经准备好无须输入太多就可以与集群进行交互。现在终于可以在Kubernetes上运行第一个应用了。
2.3 在Kubernetes上运行第一个应用
因为这可能是第一次,所以会使用最简单的方法在Kubernetes上运行应用程序。通常,需要准备一个JSON或YAML,包含想要部署的所有组件描述的配置文件,但是因为还没有介绍可以在Kubernetes中创建的组件类型,所以这里将使用一个简单的单行命令来运行应用。
2.3.1 部署Node.js应用
部署应用程序最简单的方式是使用 kubectl run 命令,该命令可以创建所有必要的组件而无需JSON或YAML文件。这样的话,我们就不需要深入了解每个组件对象的结构。试着运行之前创建、推送到Docker Hub的镜像。下面是在Kubernetes中运行的代码:
kubectl run kubia --image=luksa/kubia:v1 --port=8080 --generator=run/v1
--image=luksa/kubia 显示的是指定要运行的容器镜像,
--port=8080 选项告诉Kubernetes应用正在监听8080端口。
最后一个标志(--generator)需要解释一下,通常并不会使用到它,它让Kubernetes创建一个ReplicationController,而不是Deployment。稍后你将在本章中了解到什么是ReplicationController,但是直到第9章才会介绍Deployment,所以不会在这里创建Deployment。
正如前面命令的输出所示,已经创建了一个名为kubia的ReplicationController。如前所述,我们将在本章的后面看到。从底层开始,把注意力放在创建的容器上(可以假设已经创建了一个容器,因为在 run 命令中指定了一个容器镜像)。
冬日暖阳的笔记(新版本中已
以上是关于Kubernetes In Action :2开始使用Kubernetes和Docker的主要内容,如果未能解决你的问题,请参考以下文章
Kubernetes In Action :2开始使用Kubernetes和Docker
Kubernetes in Action 3 pod:运行于Kubernetes中的容器
Kubernetes in Action 3 pod:运行于Kubernetes中的容器
Kubernetes in Action 3 pod:运行于Kubernetes中的容器