Docker Kubenetes 微服务教程(一)
一、使用 Docker 安装 Kubernetes
Kubernetes 是管理 Docker 容器集群的软件。Kubernetes 流程编排包括调度、分配工作负载和扩展。Kubernetes 通过引入 Pods 进一步发展了 Docker 提供的软件封装。Pod 是一个或多个 Docker 容器的集合,这些容器具有单个接口特性,例如在 Pod 级别而不是在容器级别提供网络和文件系统。Kubernetes 还引入了“标签”,服务和复制控制器(复制控制器用于扩展集群)使用这些标签来识别或选择它们管理的容器或单元。Kubernetes 是轻量级的、可移植的(适合云架构)和模块化的。
Kubernetes 几乎可以在任何平台上运行。本地机器解决方案包括基于本地 Docker 的、无虚拟机的本地集群。托管解决方案包括谷歌容器引擎。Kubernetes 支持的其他一些平台有 Fedora (Ansible 和 Manual)、Amazon Web Services、Mesos、vSphere 和 CoreOS。Kubernetes 是 Docker 容器的编排软件;推荐的安装解决方案是使用 Docker 引擎。本章我们将在 Docker 上安装 Kubernetes,Docker 运行在 Ubuntu 上。我们将使用托管 Ubuntu 的 Amazon EC2 实例作为操作系统。本章将讨论 Kubernetes 的单节点安装。Kubernetes 的多节点安装将在第十四章中讨论。本章包括以下几节。
- 设置环境
- 安装 Docker
- 正在安装 kubernetes
- 正在启动 etcd
- 启动 kubernetes master
- 正在启动服务代理
- 列出非库坞站容器
- 安装 kubectl
- 列表服务
- 列出节点
- 测试 Kubernetes 安装
设置环境
本章需要以下软件。
-
- Docker 引擎(最新版本)
- -Kubernetes(1.01 版)
Linux 需要支持 64 位软件。我们使用了从 AMI Ubuntu Server 14.04 LTS (HVM)创建的 Amazon EC2 实例,SSD 卷类型- ami-d05e75b8。一个基于 Ubuntu AMI 的 Amazon EC2 实例如图 1-1 所示。
图 1-1。
Amazon EC2 Instance Based on Ubuntu AMI
如果满足 64 位架构的要求,可以使用不同的 Ubuntu 版本。最低内核版本要求是 3.10。内核版本可以用下面的命令来验证。
uname –r
对于不同的用户,公共 IP 将是不同的。多个 Amazon EC2 实例,因此书中使用了多个公共 IP 地址,因为每次启动 Amazon EC2 实例时都会分配不同的公共 IP。Amazon EC2 实例的私有 IP 地址在重启时是相同的。SSH 到 Amazon EC2 上的 Ubuntu 实例(在下面的命令中,公共 IP 是 52.91.80.173)。
ssh -i "docker.pem" ubuntu@52.91.80.173
Amazon EC2 实例被登录,如图 1-2 所示。命令提示符变成“ubuntu@ip-172-30-1-190 ”,而不是 root@localhost。Ip 172.30.1.190 是 Amazon EC2 实例的私有 Ip,对于不同的用户也是不同的。
图 1-2。
Loging into an Amazon EC2 instance
在下一节中,我们将在 Amazon EC2 实例上的 Ubuntu 上安装 Docker。
安装 Docker
Ubuntu 使用 apt 进行包管理;apt 将存储库列表存储在/etc/apt/sources.list 列表中。Docker 的 apt 存储库保存在/etc/apt/sources . list . d/docker . list 文件中。首先,使用下面的命令为 Docker 存储库添加新的存储库密钥(gpg 密钥)。
sudo apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
添加新的 gpg 密钥,如图 1-3 所示。
图 1-3。
Adding a new gpg key
接下来,根据 Ubuntu 发行版更新/etc/apt/sources . list . d/Docker . list 文件中 Docker 存储库的 apt 源代码,可以使用以下命令找到它。
lsb_release –a
对于 Ubuntu Trusty,在/etc/apt/sources . list . d/docker . list 文件中添加以下行;docker.list 文件可以用 sudo VI/etc/apt/sources . list . d/docker . list 打开。
deb https://apt.dockerproject.org/repo ubuntu-trusty main
如果/etc/apt/sources . list . d/docker . list 文件不存在,则创建该文件。更新后的文件如图 1-4 所示。如果在 vi 编辑器中打开文件,用:wq 命令保存文件。
图 1-4。
Creating the docker.list file
如表 1-1 所列,对于不同的 Ubuntu 发行版,要添加的条目会有所不同。
表 1-1。
The docker.list file Entry Based on Ubuntu Distribution
| Ubuntu 发行版 | 进入 | | --- | --- | | Ubuntu Precise 12.04 (LTS) | deb[`https://apt.dockerproject.org/repo`](https://apt.dockerproject.org/repo)Ubuntu-精确主 | | Ubuntu trust 14.04(lt) | deb[`https://apt.dockerproject.org/repo`](https://apt.dockerproject.org/repo)Ubuntu-trusty main | | Ubuntu 生动 15.04 | deb[`https://apt.dockerproject.org/repo`](https://apt.dockerproject.org/repo)Ubuntu-生动主 |在更新/etc/apt/sources . list . d/docker . list 文件以更新 apt 包索引后,运行以下命令。
sudo apt-get update
Apt 包索引得到更新,如图 1-5 所示。
图 1-5。
Updating Ubuntu Package List
使用以下命令清除旧的存储库(如果存在)。
sudo apt-get purge lxc-docker*
图 1-6 中的输出表明旧包 lxc-docker 和 lxc-docker-virtual-package 没有安装,因此没有删除。
图 1-6。
Purging the Old Repository
运行以下命令来验证 apt 是否正在从 Docker 的更新存储库中进行提取。
sudo apt-cache policy docker-engine
图 1-7 中的输出表明正在使用/etc/apt/sources . list . d/docker . list 中指定的新存储库 ubuntu-trusty。
图 1-7。
Using the Updated Repository verification
接下来,安装 Ubuntu 的必备组件,但是首先用下面的命令更新包管理器。
sudo apt-get update
包管理器得到更新,如图 1-8 所示。
图 1-8。
Updating the Package Manager
使用以下命令安装必备的 linux-image-extra 包。
sudo apt-get install linux-image-generic-lts-trusty
运行上述命令时,如果出现以下消息提示,请选择 Y。执行此操作后,将使用 281 MB 的额外磁盘空间。您想继续吗?【是/否】
信息提示如图 1-9 所示。
图 1-9。
Message Prompt to Continue
随后,在该命令完成之前,软件包配置对话框可能会提示以下消息:
The new version of /boot/grub/menu.lst is available, but the currently installed version has been modified locally. What do you want to do with menu.lst?
选择默认选项“保持当前安装的本地版本”,点击回车,如图 1-10 所示。
图 1-10。
Selecting the Default Package Configuration
使用以下命令重新启动系统。
sudo reboot
当 sudo reboot 命令运行时,AmazonEC2 实例退出。使用与之前相同的 ssh 命令重新连接 Amazon EC2 Ubuntu 实例。
ssh -i "docker.pem" ubuntu@52.91.80.173
主机系统重新引导后,使用以下命令再次更新软件包管理器。
sudo apt-get update
包管理器得到更新,如图 1-11 所示。
图 1-11。
Updating Package Manager List after Reboot
用下面的命令安装 Docker。
sudo apt-get install docker-engine
如果显示,在以下提示中选择 Y,如图 1-12 所示。
After this operation, 60.3 MB of extra disk space will be used. Do you want to continue? [Yes/No]
图 1-12。
Message Prompt about the additional disk space being added
对接引擎的安装如图 1-13 所示。
图 1-13。
Installing the Docker Engine
使用以下命令启动 Docker 服务。
sudo service docker start
要验证 Docker 服务的状态,请运行以下命令。
sudo service docker status
前述命令的输出如图 1-14 所示。docker 引擎被指示为作为进程 2697 运行。
图 1-14。
Starting Docker and verifying its Status
安装了 Docker 之后,接下来我们将安装 Kubernetes。
正在安装 kubernetes
Kubernetes 是一个开源的容器集群管理器。Kubernetes 的主要成分如下:
etcd Kubernetes master Service proxy kubelet
etcd 是一个简单、安全、快速和可靠的分布式键值存储。
Kubernetes master 公开了 Kubernetes API,使用它在节点上运行容器来处理任务。
kubelet 是一个运行在每个节点上的代理,用于监控节点上运行的容器,如果需要保持复制级别,可以重新启动它们。
服务代理在每个节点上运行,为客户机提供 Kubernetes 服务接口。服务是对服务所代表的单元的逻辑集合的抽象,服务选择器用于选择服务所代表的单元。服务代理将客户端流量路由到匹配的 pod。标签用于将服务与 pod 相匹配。
可以选择创建一个目录(/kubernetes)来安装 kubernetes,并将其权限设置为 global (777)。
sudo mkdir /kubernetes
sudo chmod -R 777 /kubernetes
前述命令的输出如图 1-15 所示。
图 1-15。
Creating a Directory to install Kubernetes
将目录切换到/kubernetes 目录并启动 Docker 引擎。
cd /kubernetes
sudo service docker start
如果 Docker 引擎没有运行,它就会启动。图 1-16 显示 Docker 引擎已经运行。
图 1-16。
Starting Docker if not already running
作为先决条件,我们需要设置一些 Linux 内核参数,如果还没有设置的话。添加对内存和交换记帐的支持。应该在内核中打开以下配置。
CONFIG_RESOURCE_COUNTERS=y
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
CONFIG_MEMCG_SWAP_ENABLED=y
CONFIG_MEMCG_KMEM=y
当 Ubuntu 系统引导并且内核配置文件在/boot 目录中时,内核配置被启用。将目录(cd)更改为/boot 目录,并列出文件/目录。
cd /boot
ls –l
/boot 目录中的文件被列出,如图 1-17 所示。内核配置在 config-3.13.0-48-generic 文件中进行配置。对于不同的用户,内核版本可能不同;例如,内核配置文件可以是/boot/config-3.13.0-66-generic。
图 1-17。
Listing the Files in the /boot Directory
在 vi 编辑器中打开 config-3.13.0-48-generic 文件。
sudo vi /boot/config-3.13.0-48-generic
内核配置参数被列出,如图 1-18 所示。
图 1-18。
Kernel Configuration Parameter
前面列出的大多数配置已经打开,如图 1-19 所示。未设置 CONFIG_MEMCG_SWAP_ENABLED 配置。
图 1-19。
Most of the Required Kernel Parameters are already Set
设置 CONFIG_MEMCG_SWAP_ENABLED = y,保存内核配置文件,如图 1-20 所示。
图 1-20。
Setting the CONFIG_MEMCG_SWAP_ENABLED Kernel Parameter
接下来,我们需要为内核添加对内存和交换记账的支持。提供给内核的命令行参数可以用下面的命令列出。
cat /proc/cmdline
如图 1-21 所示,内存和交换记账未打开。
图 1-21。
Listing the Command-Line Parameters
Grub 2 是 Ubuntu 的默认引导加载程序。要打开内存和交换记帐,请在 vi 编辑器中打开/etc/default/grub 文件。GRUB_CMDLINE_LINUX 设置为空字符串,如图 1-22 所示。
图 1-22。
The /etc/default/grub file
如下设置 GRUB_CMDLINE_LINU,这将在启动时启用内核中的内存和交换记帐。
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
修改后的/etc/default/grub 文件如图 1-23 所示。用:wq 命令保存文件。
图 1-23。
Modified /etc/default/grub file
使用以下命令更新 grub.cfg 文件。
sudo update-grub
grub 配置文件的生成如图 1-24 所示。
图 1-24。
Generating an Updated Grub Configuration file
重启系统。
sudo reboot
到 Ubuntu Amazon EC2 实例的连接被关闭,如图 1-25 所示。
图 1-25。
Rebooting Ubuntu Instance
SSH 重新登录到 Ubuntu 实例。重新运行命令以列出命令行内核参数。
cat /proc/cmdline
cgroup _ enable = memory swap account = 1 设置的输出如图 1-26 所示。
图 1-26。
Updated Settings
设置好必备的内核参数后,接下来我们将启动 Kubernetes 组件 etcd、master 和服务代理。
正在启动 etcd
使用以下 docker run 命令运行 etcd。
sudo docker run --net=host -d gcr.io/google_containers/etcd:2.0.12 /usr/local/bin/etcd --
addr=127.0.0.1:4001 --bind-addr=0.0.0.0:4001 --data-dir=/var/etcd/data
docker 运行命令参数如下(表 1-2 )。
表 1-2。
The docker run Command Parameters to start etcd
| 参数 | 描述 | | --- | --- | | - net =主机 | 利用容器内部的主机容器网络将 Docker 容器连接到网络 | | -d | 在后台启动容器 | | gcr.io/google_containers/etcd:2.0.12 | 容器映像 | | /usr/local/bin/etcd --addr = 127.0.0.1:4001 --bind-addr = 0.0.0.0:4001 --data-dir=/var/etcd/date | 要运行的命令 |前面命令的输出如图 1-27 所示。
图 1-27。
Starting etcd
每次启动 Kubernetes 集群管理器时,都需要运行 docker run 命令来启动 etcd。后续启动 etcd 不需要下载如图 1-28 所示的容器镜像。
图 1-28。
Subsequent Start of etcd does not need to download the container Image again
启动 kubernetes master
Kubernetes 主机使用 kubelet 启动,kube let 还启动其他主机组件 apiserver、调度器、控制器和暂停,这些组件在表 1-3 中讨论。
表 1-3。
The docker run Command Parameters to start etcd
| 主组件 | 描述 | | --- | --- | | appserver(伺服器) | apiserver 接受 API 请求,处理它们,如果需要的话,将结果存储在 etcd 中,并返回结果。 | | 调度程序 | 调度程序监控 API 以发现未调度的 pod,并在节点上调度它们运行,还将同样的情况通知 API。 | | 控制器 | 控制器管理单元的复制级别,在扩大事件中启动新单元,在缩小事件中停止一些单元。 | | 中止 | 暂停会保留 pod 中所有容器或 pod 网络端点的端口映射。 |用下面的命令运行 Kubernetes master。
sudo docker run \
--volume=/:/rootfs:ro \
--volume=/sys:/sys:ro \
--volume=/dev:/dev \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--volume=/var/lib/kubelet/:/var/lib/kubelet:rw \
--volume=/var/run:/var/run:rw \
--net=host \
--pid=host \
--privileged=true \
-d \
gcr.io/google_containers/hyperkube:v1.0.1 \
/hyperkube kubelet --containerized --hostname-override="127.0.0.1" --address="0.0.0.0" --api-
servers=http://localhost:8080 --config=/etc/kubernetes/manifests
表 1-4 中讨论了 docker 运行命令参数。
表 1-4。
The docker run Command Parameters to start etcd
| 参数 | 描述 | | --- | --- | | -volume =/:/rootfs:ro \-volume =/sys:/sys:ro \-volume =/dev:/dev \-volume =/var/lib/docker/ | 要使用的 Docker 卷 | | - net =主机 | 利用容器内部的主机容器网络将 Docker 容器连接到网络 | | - pid =主机 | 设置 pid 名称空间 | | -特权=真 | 在内核特性和主机访问方面,提供对主机大部分功能的访问 | | -d | 在后台启动容器 | | gcr.io/google_containers/hyperkube:v1.0.1 | 容器映像 | | hyperkube kube let-contained-hostname-override = " 127 . 0 . 0 . 1 "-address = " 0 . 0 . 0 . 0 "-API-servers =`http://localhost:8080`-config =/etc/kubernetes/manifests | 该命令运行 |启动主机的 docker run 命令的输出如图 1-29 所示。
图 1-29。
The docker run Command to start Kubernetes Master
每次启动 Kubernetes 集群管理器时,都需要启动主服务器。仅在第一次运行命令时下载容器映像,在随后的运行中不下载映像,如图 1-30 所示。
图 1-30。
Subsequent starts of Kubernetes Master do not need to download Container image again
正在启动服务代理
要启动服务代理,它是 Kubernetes 服务的代理,使用带有标签的服务选择器提供 pod/s 接口,通过运行以下 docker run 命令启动服务代理。
sudo docker run -d --net=host --privileged gcr.io/google_containers/hyperkube:v1.0.1 /hyperkube proxy -- master=http://127.0.0.1:8080 --v=2
前面命令的命令参数在表 1-5 中讨论。
表 1-5。
The docker run Command Parameters to start service proxy
| 参数 | 描述 | | --- | --- | | -d | 在后台运行容器 | | - net =主机 | 将容器的网络设置为主机的网络 | | -特权 | 在内核特性和主机访问方面,提供对主机大部分功能的访问 | | gcr.io/google_containers/hyperkube:v1.0.1 | 容器映像 | | 超立方体代理-- master= `http://127.0.0.1:8080` --v = 2 | 要运行的命令。主 url 被设置为`http://127.0.0.1:8080`。 |前面 docker run 命令的输出如图 1-31 所示。
图 1-31。
Starting the Service proxy
列出非库坞站容器
为 Kubernetes 集群管理器启动的 Docker 容器可以用下面的命令列出。
sudo docker ps
列出的 Docker 容器包括用于服务代理的容器;库伯莱的容器;用于 etcd 的容器;以及分别用于主调度器、控制器和 apiserver 的容器,并暂停如图 1-32 所示。
图 1-32。
Listing the Docker Containers
可以使用对接容器 id 找到对接容器信息。例如,获取运行控制器的 Docker 容器的容器 id,如图 1-33 所示。
图 1-33。
Obtaining the Docker Container Id
运行以下命令来查找 Docker 容器的详细信息。
sudo docker inspect 37971b53f2c1
诸如主 ip 和关于运行控制器管理器的 Docker 容器的细节得到如图 1-34 所示的输出。
图 1-34。
Listing Docker Container Information
安装 kubectl
kubectl 用于控制 Kubernetes 集群管理器,包括运行映像、获取 pod、获取复制控制器、将应用作为服务在指定端口公开,以及扩展集群。用下面的命令下载 Kubectl 二进制文件。
sudo wget https://storage.googleapis.com/kubernetes-release/release/v1.0.1/bin/linux/amd64/kubectl
kubectl 二进制文件被下载,如图 1-35 所示。
图 1-35。
Installing Kubectl
通过应用+ x 权限使 kubectl 应用可执行。
sudo chmod +x kubectl
将 kubectl 二进制文件移动到/usr/local/bin/目录。
sudo mv kubi ctel/usr/local/bin/
前述命令的输出如图 1-36 所示。
图 1-36。
Moving and making kubectl Binaries executable
kubectl 命令列出了如图 1-37 所示的用法。
图 1-37。
Kubectl Command Usage
命令参数也被列出,如图 1-38 所示。
图 1-38。
Command Parameters for Kubectl
列表服务
以下命令应该列出 Kubernetes 服务。
kubectl get services
kubernetes 服务被列出,如图 1-39 所示。
图 1-39。
Listing the Kubernetes Service
列出节点
以下命令应该列出 Kubernetes 节点。
kubectl 获取节点
集群中的单个节点被列出,如图 1-40 所示。
图 1-40。
Listing the Nodes
测试 Kubernetes 安装
要测试 Kubernetes 集群管理器,使用以下命令运行 nginx 应用。
kubectl -s http://localhost:8080 run nginx --image=nginx --port=80
kubectl run 命令的输出列出了复制控制器、容器、映像/sm 选择器和副本,如图 1-41 所示。
图 1-41。
Running he nginx Application on Kubernetes Cluster
使用 kubectl expose 命令将 nginx 应用复制控制器作为服务公开。
kubectl expose rc nginx --port=80
nginx Kubernetes 服务在端口 80 上运行,如图 1-42 所示。
图 1-42。
Creating a Kubernetes Service for nginx Application
用 kubectl get svc 命令列出 nginx 服务的详细信息。
kubectl get svc nginx
nginx 服务详情如图 1-43 所示。
图 1-43。
Listing the Kubernetes Service nginx
可以使用以下命令获取集群 IP。
kubectl get svc nginx --template={{.spec.clusterIP}}
集群 ip 被列为 10.0.0.146,如图 1-44 所示。
图 1-44。
Listing the Cluster IP
可以使用以下命令调用 web 服务器来利用集群 ip。
curl 10.0.0.146
文本形式的 html 输出如图 1-45 所示。
图 1-45。
Using curl to invoke Application
摘要
在本章中,我们使用 Docker 安装了 Kubernetes。运行 Ubuntu 的 Amazon EC2 实例用于安装 Docker 和 Kubernetes。运行 nginx 应用只是为了测试 Kubernetes 集群管理器的安装。创建应用、复制控制器和服务的 kubectl 命令将在下一章详细讨论。
二、你好,Kubernetes
Kubernetes 是 Linux 容器的集群管理器。虽然 Kubernetes 支持其他类型的容器,如 Rocket,并且还将增加对更多类型的支持,但我们将只在 Docker 容器的上下文中讨论 Kubernetes。Docker 是一个开源容器虚拟化平台,用于在容器中构建、打包和运行分布式应用,容器是底层操作系统的轻量级快照。特定于应用的 Docker 映像封装了所有需要的软件,包括应用的依赖关系,并用于创建 Docker 容器以运行容器中的应用。Docker 容器相互隔离,拥有自己的网络和文件系统,并提供容器即服务(CaaS)。Docker 类似于基于虚拟化平台(如 Oracle VirtualBox 和 VMWare Player)的虚拟机,因为它是底层操作系统上的虚拟化,但不同之处在于,虽然虚拟机使用整个操作系统,但多个 Docker 容器共享内核并在主机操作系统上独立运行。Docker 容器运行在 Docker 引擎上,Docker 引擎运行在底层操作系统内核上。
在本章中,我们将使用 Hello-World 应用介绍 Kubernetes 的概念。本章包括以下几节。
- 概观
- 为什么是 Kubernetes
- 设置环境
- 强制创建应用
- 以声明方式创建应用
- 将 JSON 用于资源定义
概观
Kubernetes 的概念包括 Pod、服务和复制控制器,将在以下小节中进行定义。
什么是节点?
节点是一台运行 Kubernetes 的机器(物理的或虚拟的), Pods 可以在其上进行调度。该节点可以是主节点,也可以是工作节点之一。在安装 Kubernetes 的前一章中,只使用了一个节点。在后面的章节,第十四章中,我们将讨论如何创建一个包含主节点和工作节点的多节点集群。
什么是集群?
集群是节点的集合,包括运行 Kubernetes 应用的存储等其他资源。一个集群有一个 Kubernetes 主节点和零个或多个工作节点。高可用性集群由多个主节点或主节点组成。
什么是 Pod?
Pod 是并置在一起的容器的集合,形成一个原子单元。多个应用可以在一个 Pod 内运行,尽管 Pod 内的不同容器可以用于相同的应用,但是通常不同的容器用于不同的应用。Pod 是用于管理一组具有共享卷和网络名称空间的容器的高级抽象。Pod 中的所有应用(容器)共享相同的文件系统和 IP 地址,每个应用暴露的端口是不同的。在 Pod 中运行的应用可以在“本地主机”上相互访问。调度和复制是在 Pod 级别而不是在单个容器级别执行的。例如,如果 Pod 为不同的应用定义了两个容器,并且复制级别设置为 1,则 Pod 的单个复制副本由两个容器组成,两个应用各有一个容器。Pods 促进了资源共享和通信,否则在单独运行的 Docker 容器中使用- link 将会实现这些。由多个容器组成的容器通常用于紧密耦合的应用。例如,如果一个nginx应用使用 MySQL 数据库,那么这两个应用能够通过 Kubernetes 在同一个 Pod 中为每个应用运行容器来进行交互。
什么是服务?
服务是一个或多个提供端点的 pod 的外部接口,在端点上可以调用由服务表示的应用。服务托管在单个 IP 地址,但根据服务连接的应用提供零个或多个端点。使用标签选择器将服务连接到 pod。Pod 上有标签,具有与 Pod 标签相同的选择器表达式的服务向外部客户端表示 Pod。外部客户机不知道或不需要知道服务所代表的 pod。外部客户机只需要知道服务的名称和特定应用暴露的端口。该服务基于循环方式将对应用的请求路由到使用标签选择器/选择的一个 pod。因此,服务是对应用集合的高级抽象,留下了将请求路由到哪个 Pod 的细节。服务也可以用于负载均衡。
什么是复制控制器?
复制控制器管理由复制控制器定义中的“副本”设置或命令行上的–replicas参数指定的 pod 复制级别。复制控制器确保 Pod 副本的配置级别在任何给定时间都在运行。如果复制失败或被故意停止,新的复制会自动启动。复制控制器用于扩展集群中的单元。副本是在 Pod 级别定义的,这意味着如果 Pod 由两个容器组成,则一组两个配置的容器构成一个副本。
什么是标签?
标签是标识诸如 Pod、服务或复制控制器之类的资源的键值对:最常见的是 Pod。标签用于标识任务的一组资源或资源子集,例如将它们分配给服务。服务使用标签选择器来选择它们管理的窗格。例如,如果一个 Pod 被标记为“app = helloApp ”,并且服务“选择器”被设置为“app = helloApp ”,则该 Pod 由该服务表示。服务选择器是基于标签的,而不是基于它们管理的应用的类型。例如,一个服务可以代表一个运行 hello-world 应用容器并带有特定标签的 Pod。另一个 Pod 也运行 hello-world 容器,但其标签不同于服务选择器表达式,该服务不会表示该 Pod。并且运行不是 hello-world 应用但是具有与服务选择器相同的标签的应用的第三个 Pod 也将由相同的服务表示。
什么是选择器?
选择器是使用匹配标签来标识资源的键值表达式。如前一小节所述,服务选择器表达式“app = helloApp”将选择所有标签为“app = helloApp”的 pod。虽然通常服务定义一个选择器来选择 pod,但是服务可以被定义为不包括选择器,并且被定义为抽象其他种类的后端。支持两种选择器:基于等式的和基于集合的。选择器可以由多个需求组成,这意味着可以指定由“,”分隔的多个表达式(基于等式或基于集合)。所有的要求必须由匹配的资源来满足,例如要选择的资源的 Pod。像 Pod 这样的资源可以有附加标签,但是必须为要选择的资源指定选择器中的标签。基于等式的选择器更常用,也是本书中使用的,它支持=,!=、==运算符,=与==同义。
什么是名字?
名称标识了一个资源。名称不同于标签。为了将资源与服务匹配,使用标签而不是名称。
什么是名称空间?
名称空间是名称之上的一个级别,用于区分项目或团队的一组资源,以防止名称冲突。不同命名空间内的资源可以具有相同的名称,但是一个命名空间内的资源具有不同的名称。
什么是卷?
卷是容器文件系统中的一个目录。卷可以用来存储数据。Kubernetes 卷从 Docker 卷演变而来。
为什么是 Kubernetes?
Docker containers 为应用引入了一个新的模块化和流动性级别,提供了对应用(包括依赖项)的打包,以及跨不同环境传输和运行应用。但是随着 Docker 容器在生产中的使用,实际问题变得很明显,例如哪个容器在哪个节点上运行(调度),如何增加/减少应用的运行容器的数量(伸缩),以及如何在容器内通信。Kubernetes 旨在克服容器集群管理的所有这些和其他实际问题。Kubernetes 实时提供动态容器集群编排。Kubernetes 作为集群管理器提供了以下好处。
- -微服务,将应用分解成更小的、可管理的、可扩展的组件,供具有不同需求的群体使用。
- -容错群集,其中如果一个 Pod 副本出现故障(例如,由于节点故障),另一个副本会自动启动。
- -水平扩展,通过修改复制控制器中的“副本”设置或使用
kubectl scale命令中的–replicas参数,可以运行 Pod 的更多或更少副本。 - -更高的资源利用率和效率。
- -关注点分离。服务开发团队不需要与集群基础设施团队交互。
设置环境
本章需要以下软件。
- -Docker 引擎(最新版本)
- -Kubernetes(1.01 版)
- -Kubernetes(1.01 版)
我们使用了从 AMI Ubuntu Server 14.04 LTS (HVM)创建的 Amazon EC2 Linux 实例,SSD 卷类型- ami-d05e75b8。
SSH 登录到 Ubuntu 界面(公共 IP 地址因用户不同而不同,本章可能会用到多个 IP 地址)。
ssh -i "docker.pem" ubuntu@54.152.82.142
按照第一章所述安装 Docker,启动 Docker 引擎,并使用以下命令验证其状态。
sudo service docker start
sudo service docker status
按照第一章所述,安装 kubectl 并启动 Kubernetes 集群管理器。使用以下命令输出 Kubernetes 集群信息。
kubectl cluster-info
在图 2-1 中,Kubernetes Master 在http://localhost:8080运行。
图 2-1。
Getting Cluster Info
在下面几节中,我们将使用 Kubernetes 集群管理器运行一个hello-world应用。可以在命令行上使用kubectl工具强制运行应用,或者使用 Pod、复制控制器和服务的定义文件以声明方式运行应用。我们将讨论每一种方法。本章及后续章节都使用 kubectl 工具,完整的命令参考可在 https://cloud.google.com/container-engine/docs/kubectl/ 找到。
强制创建应用
当 Kubernetes 主程序在http://localhost:8080上运行时,如前一节所述,运行下面的kubectl run命令,使用映像tutum/hello-world运行一个hello-world应用。–s选项指定了 Kubernetes API 服务器主机和端口。–image命令参数指定 Docker 映像作为tutum/hello-world运行。–replicas参数指定要创建的副本数量为 1。即使没有指定–replicas参数,也会创建一个复制控制器。默认的副本数量是 1。–port参数将应用所在的容器端口指定为 80。
kubectl -s http://localhost:8080 run hello-world --image=tutum/hello-world --replicas=1 --port=80
一个名为hello-world的新应用容器被创建,如图 2-2 所示。还创建了一个名为“hello-world”的复制控制器。Pod 是隐式创建的,并且标签“run = hello-world”被添加到 Pod 中。创建的副本数量为 1。复制控制器的选择器字段也被设置为“run=hello-world”。由复制控制器管理的 pod 必须指定与在复制控制器级别指定的选择器相同的标签。默认情况下,复制控制器选择器设置为与 Pod 标签相同的表达式。
图 2-2。
Creating an Application including a Replication Controller and Pod Replica/s
创建的复制控制器可以用以下命令列出。
kubectl get rc
hello-world复制控制器列表如图 2-3 所示。
图 2-3。
Listing the Replication Controllers
使用以下命令列出创建和启动的 pod。
kubectl get pods
创建的单个 Pod 被列出,如图 2-4 所示。Pod 名称是自动分配的。会列出 Pod 状态“正在运行”,但 Pod 可能仍未就绪且不可用。“就绪”列值 0/1 表示 Pod 中的 1 个容器中有 0 个已就绪,这意味着 Pod 已创建并正在运行,但尚未就绪。Pod 可能需要几秒钟才能准备好。
图 2-4。
Listing the Pods
几秒钟或一分钟后再次运行相同的命令。
kubectl get pods
如图 2-5 中就绪栏的 1/1 所示,Pod 被列为就绪。“就绪”列中的值 1/1 表示 Pod 中的 1 个容器中的 1 个已经就绪。就绪列值的语法是nReady/nTotal,这意味着 Pod 中所有nTotal容器中的nReady已经就绪。例如,Kubernetes Pod k8s-master-127.0.0.1的就绪列值为 3/3,这意味着 Kubernetes Pod 中的 3 个容器中有 3 个已经就绪。
图 2-5。
Listing a Pod as ready with all containers in the Pod as ready
运行 Pod 和复制控制器不会隐式创建服务。在下一小节中,我们将为hello-world应用创建一个服务。
创建服务
使用kubectl expose命令创建一个 Kubernetes 服务,该命令从一个 Pod、复制控制器或另一个服务创建一个服务。因为我们创建了一个名为hello-world的复制控制器,所以使用下面的命令创建一个服务,其中公开服务的端口设置为 8080,服务类型为LoadBalancer。
kubectl expose rc hello-world --port=8080 --type=LoadBalancer
一个名为hello-world的 Kubernetes 服务被创建,如图 2-6 所示。服务标签和选择器也被设置。图 2-6 中列出的服务选择器被设置为与复制控制器选择器相同的表达式run=hello-world,如图 2-3 所示,这意味着服务管理复制控制器hello-world中的 pod。
图 2-6。
Creating a Kubernetes Service
不同类型的服务是 ClusterIp、NodePort 和 LoadBalancer,默认为 ClusterIP,如表 2-1 中所述。
表 2-1。
Types of Services
| 通用式 | 描述 | | --- | --- | | ClusterIp(群集 Ip) | 仅使用群集内部 IP。 | | 节点端口 | 除了群集之外,IP 还在群集的每个节点上公开服务。 | | LoadBalancer(负载均衡器) | 除了在集群内部 Ip 和集群上每个节点的端口上公开服务之外,还请求云提供商为服务提供负载均衡器。负载均衡器平衡服务中各单元之间的负载。 |用下面的命令列出所有 Kubernetes 服务。
kubectl get services
除了用于 kubernetes 集群管理器的“Kubernetes”服务之外,还创建了一个“hello-world”服务,如图 2-7 所示。
图 2-7。
Listing the Services
描述一个 Pod
使用从kubectl get pods命令结果中的名称列获得的 Pod 名称hello-world-syrqz,使用kubectl describe pod命令列出关于 Pod 的详细信息。
kubectl describe pod hello-world-syrqz
如图 2-8 所示,列出了包括 IP 地址在内的 Pod 的详细信息。Pod 有一个标签 run=hello-world,它与复制控制器selector相同,也与服务selector相同,这意味着复制控制器例如在扩展 Pod 的集群时管理 Pod,服务向外部客户端表示 Pod。
图 2-8。
Describing a Pod
接下来,我们将使用 IP 字段中列出的 IP 地址172.0.17.2调用应用。
调用 Hello-World 应用
使用图 2-8 中列出的应用 IP,通过以下curl命令,可以调用hello-world应用。
curl 172.17.0.2
应用的 HTML 输出如图 2-9 所示。
图 2-9。
Invoking a Application using Pod IP with curl
为了在浏览器中显示 HTML 输出,我们需要使用 URL 172.17.0.2:80从浏览器中调用应用。如果 Amazon EC2 Ubuntu 实例上没有浏览器(默认情况下没有),我们需要使用本地端口转发建立一个到应用 IP 地址的 SSH 隧道。获取 Amazon EC2 实例的公共 DNS(示例中的 ec2-52-91-200-41.compute-1.amazonaws.com ),并运行以下命令来建立从本地机器到172.17.0.2:80 host:port的 SSH 隧道。–L表示本地端口转发用于将本地端口 80 转发到172.17.0.2:80。
ssh -i "docker.pem" -f -nNT -L 80:172.17.0.2:80 ubuntu@ec2-52-91-200-41.compute-1.amazonaws.com
在本地机器的浏览器中调用 URL http://localhost。来自hello-world应用的 HTML 输出显示如图 2-10 所示。主机名与图 2-5 中的 Pod 名称相同。
图 2-10。
Invoking the Hello-World Application in a Browser
扩展应用
当我们创建副本设置为 1 的hello-world应用时,默认情况下会创建一个复制控制器。接下来,我们将把 pod 的数量增加到 4 个。kubectl scale命令用于扩展复制控制器。运行以下命令将复制控制器hello-world扩展到 4 个。
kubectl scale rc hello-world --replicas=4
随后,使用以下命令列出 pod。
kubectl get pods
额外的窗格会被列出,但一些新窗格可能会以各种状态列出,如运行但未就绪,或映像就绪和容器创建,如图 2-11 所示。
图 2-11。
Scaling the Cluster of Pods with the Replication Controller
几秒钟后,再次运行相同的命令来列出 pod。
kubectl get pods
如果吊舱已经启动,所有吊舱都以状态->运行和就绪状态 1/1 列出,如图 2-12 所示。缩放到 4 个副本不会创建 4 个新的 Pod,但是 Pod 的总数会缩放到 4,并且最初创建的单个 Pod 会包含在 4 个新的缩放副本中。
图 2-12。
Listing all the Pods as Running and Ready
使用以下命令描述hello-world服务。
kubectl describe svc hello-world
如图 2-13 所示,列出服务名称、标签、选择器、类型、IP 和端点。可以使用各种 Pod 副本的端点来调用服务。
图 2-13。
Describing the Service hello-world
如前所述,为新添加的端点设置带端口转发的 SSH 隧道。下面的命令在 Amazon EC2 实例上建立一个 SSH 隧道,从localhost端口 8081 转发到172.17.0.3:80。
ssh -i "docker.pem" -f -nNT -L 8081:172.17.0.3:80 ubuntu@ec2-52-91-200-41.compute-1.amazonaws.com
随后在 url 为http://localhost:8081的本地机器上的浏览器中调用hello-world应用,显示如图 2-14 所示的应用输出。
图 2-14。
Invoking an Application in a Local Browser
类似地,来自本地机器的以下命令建立了一个 SSH 隧道,在 Amazon EC2 实例上从localhost端口 8082 到172.17.0.4:80进行端口转发。
ssh -i "docker.pem" -f -nNT -L 8082:172.17.0.4:80 ubuntu@ec2-52-91-200-41.compute-1.amazonaws.com
随后使用 url http://localhost:8082调用hello-world应用,显示如图 2-15 所示的应用输出。
图 2-15。
Invoking the second Service Endpoint in a Local Browser
删除复制控制器
可以使用以下命令删除复制控制器hello-world。
kubectl delete rc hello-world
复制控制器被删除,如图 2-16 所示。随后调用以下命令列出复制控制器。
图 2-16。
Deleting a Replication Controller
kubectl get rc
hello-world复制控制器未列出,如图 2-16 所示。
删除复制控制器会删除复制控制器以及与复制控制器相关联的 pod,但不会删除代表复制控制器的服务。kubectl get services命令仍然列出服务,如图 2-17 所示。
图 2-17。
Deleting a Replication Controller does not delete the Service
删除服务
要删除服务hello-world,运行以下命令。
kubectl delete svc hello-world
随后调用以下命令来列出服务。
kubectl get services
前两个命令的输出如图 2-18 所示,没有列出hello-world服务。
图 2-18。
Deleting the hello-world Service
以声明方式创建应用
接下来,我们将使用 Pod、服务和复制控制器的定义文件,以声明方式创建相同的 hello-world 应用。定义文件可以在 YAML 或 JSON 中配置。我们最初使用了 YAML,后来也讨论了 JSON 替代方案。
创建 Pod 定义
创建一个hello-world.yaml文件,并在文件中指定一个 Pod 的定义。对于hello-world应用,使用下面的定义,其中apiVersion映射用于 API 模式版本(v1 ), kind映射是资源并被设置为Pod。元数据映射指定 Pod 的元数据,并将名称设置为hello-world(任意)。spec映射指定了 Pod 行为。spec->-containers映射指定要运行的映像集合。hello-world.yaml为映像tutum/hello-world指定一个容器。容器名被设置为hello-world,容器ports映射是一个端口列表,8080 端口只有一个containerPort映射。
apiVersion: v1
kind: Pod
metadata:
name: hello-world
spec:
containers:
-
image: tutum/hello-world
name: hello-world
ports:
-containerPort: 8080
上述内容等效于以下命令。
kubectl run hello-world --image=tutum/hello-world --port=8080
在hello-world.yaml中只使用了少数模式元素。完整的 Pod 模式参见 http://kubernetes.io/v1.1/docs/api-reference/v1/definitions.html#_v1_pod 。
接下来,使用带有以下kubectl create命令的hello-world.yaml定义文件创建hello-world应用。–validate选项验证 Pod 定义文件。YAML 皮棉验证器( http://www.yamllint.com/ )可用于验证hello-world.yaml中的 YAML 语法。语法验证不会验证定义文件是否符合 Pod 架构。
kubectl create -f hello-world.yaml --validate
名为hello-world的 Pod 被创建,如图 2-19 所示。
图 2-19。
Creating a Pod using a Definition File
使用以下命令列出 Pod,无论 Pod 是如何创建的,该命令都是相同的。
kubectl get pods
hello-world吊舱被列出,如图 2-20 所示。最初,Pods 可能没有准备好- > 1/1。就绪列值“0/1”意味着 Pod 中的 1 个容器中的 0 个已经就绪。
图 2-20。
Listing the Pods soon after creating the Pods
几秒钟后再次运行相同的命令。
kubectl get pods
hello-world箱被列为“运行中”状态,就绪状态为“1/1”,这意味着箱中 1 个容器中的 1 个已经就绪,如图 2-21 所示。
图 2-21。
Listing the Pod as Ready and Running
使用以下命令描述hello-world Pod。
kubectl describe pod hello-world
前面命令的输出如图 2-22 所示。
图 2-22。
Describing the hello-world Pod
使用 IP 172.17.0.2调用hello-world Pod 应用。
curl 172.17.0.2
来自hello-world应用的 HTML 输出如图 2-23 所示。
图 2-23。
Invoking the hello-world Application with curl
设置从本地机器到hello-world Pod 的 IP 地址的端口转发。
ssh -i "docker.pem" -f -nNT -L 80:172.17.0.2:80 ubuntu@ec2-52-91-200-41.compute-1.amazonaws.com
随后在本地机器的浏览器中调用 url http://localhost:80来显示应用的 HTML 输出,如图 2-24 所示。默认的超文本传输协议端口为 80,在 URL 中被省略,如图 2-24 所示。
图 2-24。
Invoking the hello-world Application in a Browser on a local machine
创建服务定义
我们创建了一个 Pod 定义文件并启动了一个 Pod,但是该 Pod 不与任何服务或复制控制器相关联。外部客户端必须直接访问 Pod,并且不能仅通过一个不关联的 Pod 来扩展应用。创建如下所示的服务定义文件hello-world-service.yaml。如果复制和粘贴本章和其他章节中列出的 YAML 文件,建议在应用中使用之前使用 YAML Lint ( http://www.yamllint.com/ )来格式化文件。
apiVersion: v1
kind: Service
metadata:
labels:
app: hello-world
name: hello-world
spec:
ports:
-
name: http
port: 80
targetPort: http
selector:
app: hello-world
type: LoadBalancer
服务定义文件的主要映射是kind、metadata和spec。kind被设置为Service以指示 Kubernetes 服务。标签app和name构成了元数据。spec映射包括名为http的端口 80 的ports映射。可选地,可以设置一个targetPort,默认为与端口相同的值。selector是spec中的主映射,指定了用于选择通过服务公开的 pod 的映射。app:hello-world选择器意味着所有标签为app=hello-world的 pod 被选中。定义文件可以在 vi 编辑器中创建,并用:wq命令保存,如图 2-25 所示。
图 2-25。
Service Definition File hello-world-service.yaml
在 http://kubernetes.io/v1.1/docs/api-reference/v1/definitions.html#_v1_service 可以找到 Kubernetes 服务模式的完整参考。
使用定义文件和kubectl create命令创建一个服务。
kubectl create -f hello-world-service.yaml
hello-world服务被创建,如图 2-26 所示。
图 2-26。
Creating the hello-world Service using the Definition File
使用以下命令列出服务。
kubectl get services
除了kubernetes服务外,还列出了hello-world服务,如图 2-27 所示。
图 2-27。
Listing the hello-world Service
用下面的命令描述hello-world服务。
kubectl describe svc hello-world
列出服务名、名称空间、标签、选择器、类型、Ip,如图 2-28 所示。因为使用 Pod 定义文件创建的hello-world Pod 不包含与服务选择器匹配的标签,所以它不由服务管理。由于hello-world服务没有管理任何 pod,因此没有列出端点。
图 2-28。
Describing the hello-world Service
创建复制控制器定义
接下来,我们将创建一个复制控制器,并将复制控制器标记为与之前创建的服务选择器相匹配。创建服务定义文件hello-rc.yaml。复制控制器的种类映射是ReplicationController。spec映射中的replicas子映射被设置为 2,以从spec中指定的 Pod 创建两个副本。模板- >元数据- >标签中至少有一个标签必须与服务定义文件中的服务选择器相匹配,以便服务公开 Pod。因为hello-world服务中的服务选择器是app:hello-world,所以将app:hello-world标签添加到复制控制器模板中。YAML 的 app:hello-world 设置翻译成 app=hello-world。该模板可以定义一个或多个容器,这些容器将被包括在从复制控制器创建的 Pod 中。我们只为映像tutum/hello-world的一个容器包含了容器定义。下面列出了hello-rc.yaml。可以使用 YAML lint ( http://www.yamllint.com/ )来验证 YAML 语法。
apiVersion: v1
kind: ReplicationController
metadata:
name: hello-world
spec:
replicas: 2
template:
metadata:
labels:
app: hello-world
spec:
containers:
-
image: tutum/hello-world
name: hello-world
ports:
-
containerPort: 8080
name: http
在 http://kubernetes.io/v1.1/docs/api-reference/v1/definitions.html#_v1_replicationcontroller 可以获得复制控制器的完整方案。
使用带有kubectl create命令的定义文件创建复制控制器,该命令与用于创建 Pod 和服务的命令相同。
kubectl create -f hello-rc.yaml
随后运行以下命令列出复制控制器。
kubectl get rc
一个hello-world复制控制器被创建并被列出,如图 2-29 所示。根据定义文件中的指定,副本的数量列为 2。
图 2-29。
Creating a Replication Controller
使用以下命令列出使用复制控制器创建的 pod。
kubectl get pods
从定义文件创建的两个 pod 被列出,如图 2-30 所示。创建了 Pod 定义文件的 Pod 也会列出,但不会与复制控制器相关联。最初,一些或所有新的吊舱可能被列为未准备好,如图 2-30 中一个吊舱的准备好列中的 0/1 值所示。
图 2-30。
Listing the Pods soon after creating a Replication Controller
几秒钟后再次调用相同的命令来列出窗格。
kubectl get pods
如图 2-31 所示,所有吊舱被列为就绪- > 1/1 并正在运行。
图 2-31。
Listing all the Pods as Running and Ready
要描述hello-world服务,请运行以下命令。
kubectl describe service hello-world
包括端点在内的服务细节如图 2-32 所示。服务选择器是 app = hello-world,服务端点是 172.17.0.3:8080 和 172.17.0.4:8080。
图 2-32。
Describing the Service hello-world
前面所有创建hello-world复制控制器的命令,列出了它与图 2-33 中所示的hello-world服务的关联。
图 2-33。
Summary of Commands to create a Replication Controller
调用 Hello-World 应用
可以使用图 2-33 中服务描述中列出的服务端点来调用与 hello-world 复制控制器和同名服务相关联的 pod。例如,用下面的curl命令调用172.17.0.3端点。
curl 172.17.0.3
Pod 的 HTML 输出得到如图 2-34 所示的输出。
图 2-34。
HTML Output from invoking the hello-world Application with curl
类似地,用下面的 curl 命令调用 172.17.0.4 端点。
curl 172.17.0.4
另一个 Pod 的 HTML 输出如图 2-35 所示。
图 2-35。
Invoking another Service Endpoint with curl
要在本地计算机上的浏览器中调用服务端点,请为服务端点配置本地端口转发。
ssh -i "docker.pem" -f -nNT -L 8081:172.17.0.3:8080 ubuntu@ec2-52-91-200-41.compute-1.amazonaws.com
ssh -i "docker.pem" -f -nNT -L 8082:172.17.0.4:8080 ubuntu@ec2-52-91-200-41.compute-1.amazonaws.com
随后在本地机器上的浏览器中调用localhost:8081 URL,如图 2-36 所示,以显示端点172.17.0.3:8080处 Pod 的 HTML 输出。
图 2-36。
Invoking the hello-world Application in a Local machine Browser with its Service Endpoint
类似地,在本地机器上的浏览器中调用localhost:8082 URL,如图 2-37 所示,以显示端点172.17.0.4:8080处 Pod 的 HTML 输出。
图 2-37。
Invoking another Service Endpoint in a Browser
扩展应用
例如,要将hello-world复制控制器扩展到 6 个副本,请运行下面的kubectl scale命令。
kubectl scale rc hello-world --replicas=6
如图 2-38 所示的“缩放”输出表示复制控制器已被缩放。
图 2-38。
Scaling an Application
当复制控制器扩展到 6 个时,hello-world 复制控制器的 pod 数量会增加。要列出窗格,请运行以下命令。
kubectl get pods
除了最初使用 Pod 定义文件创建的hello-world Pod 之外,还列出了六个 Pod,如图 2-39 所示。前面的命令可能需要运行多次才能列出所有状态为 Running、就绪状态为 1/1 的 pod。hello-world Pod 与hello-world复制控制器没有关联,因为它不包含与复制控制器中的选择器标签(与模板标签相同)相匹配的标签。
图 2-39。
Listing Pods after Scaling
在前面的例子中,我们放大了复制控制器,但是kubectl scale命令也可以用来缩小复制控制器。例如,将hello-world复制控制器缩减为 2 个副本。
kubectl scale rc hello-world --replicas=2
随后列出 POD。
kubectl get pods
除了图 2-40 所示的hello-world Pod 之外,副本的数量被列为 2。
图 2-40。
Scaling Down to 2 Replicas
将 JSON 用于资源定义
在前面的部分中,我们使用了 YAML 格式来创建 Pod、服务和复制控制器定义文件。定义文件可以用 JSON 格式开发。YAMLToJSON 实用程序( http://yamltojson.com/ )可用于从 YAML 转换为 JSON,JSON lint ( http://jsonlint.com/ )可用于验证 JSON。在 http://jsontoyaml.com/ 也有一个 JSON 到 YAML 的实用程序。列出了hello-world服务的 JSON 定义文件hello-world-service.json:
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"name": "hello-world",
"labels": {
"app": "hello-world"
}
},
"spec": {
"ports": [
{
"name": "http",
"port": 80,
"targetPort": "http"
}
],
"selector": {
"app": "hello-world"
},
"type": "LoadBalancer"
}
}
使用 vi 编辑器创建一个hello-world-service.json文件,并将前面的清单复制并粘贴到该文件中。使用:wq 保存文件,如图 2-41 所示。
图 2-41。
Service Definition File in JSON Format
删除之前创建的hello-world服务和hello-world复制控制器。运行以下命令,从 JSON 格式定义文件创建服务。
kubectl create –f hello-world-service.json
hello-world服务被创建,如图 2-42 所示。
图 2-42。
Creating a Service from the JSON Definition File
随后列出所有的 Kubernetes 服务。
kubectl get services
hello-world服务列表如图 2-43 所示。
图 2-43。
Listing the Service s
复制控制器定义文件hello-rc.json,的 JSON 格式版本如下。
{
"apiVersion": "v1",
"kind": "ReplicationController",
"metadata": {
"name": "hello-world"
},
"spec": {
"replicas": 2,
"template": {
"metadata": {
"labels": {
"app": "hello-world"
}
},
"spec": {
"containers": [
{
"image": "tutum/hello-world",
"name": "hello-world",
"ports": [
{
"containerPort": 8080,
"name": "http"
}
]
}
]
}
}
}
}
在 vi 编辑器中创建hello-rc.json文件,用:wq 保存,如图 2-44 所示。
图 2-44。
Creating the hello-rc.json File in vi Editor
删除所有以前创建的 pod 和复制控制器。运行以下命令来创建hello-world复制控制器。
kubectl create –f hello-rc.json
如图 2-45 所示创建hello-world复制控制器。随后运行以下命令列出复制控制器。
图 2-45。
Creating a Replication Controller from the JSON format Definition File
kubectl get rc
hello-world复制控制器列表如图 2-45 所示。使用以下命令列出复制控制器创建的 pod。
kubectl get pods
由于replicas设置为 2,两个吊舱被列出,如图 2-45 所示。
用下面的命令描述hello-world服务。
kubectl describe svc hello-world
因为hello-world复制控制器上的标签与服务选择器相匹配,所以使用复制控制器创建的两个 pod 由服务来表示,并且在服务中具有端点,如图 2-46 所示。
图 2-46。
Describing the hello-world Service
使用 curl 命令调用服务端点,如下所示。
curl 172.17.0.2
curl 命令的 HTML 输出如图 2-47 所示。
图 2-47。
Invoking the hello-world Application with curl
设置到服务端点的本地端口转发。
ssh -i "docker.pem" -f -nNT -L 80:172.17.0.2:8080 ubuntu@ec2-52-91-200-41.compute-1.amazonaws.com
随后在本地机器的浏览器中调用服务端点来显示 HTML 输出,如图 2-48 所示。
图 2-48。
Displaying hello-world Application HTML in a Browser
摘要
在本章中,我们介绍了 Kubernetes 的概念,如 Pod、服务、复制控制器、标签和选择器。我们还开发了一个 hello-world 应用,既可以在命令行上强制使用,也可以使用定义文件以声明方式使用。我们讨论了定义文件支持的两种不同格式:YAML 和 JSON。在下一章,我们将讨论在 Pod 定义中使用环境变量。
三、使用自定义命令和环境变量
Kubernetes 编排 Docker 容器,Docker 映像的运行指令在Dockerfile中指定。ENTRYPOINT指令指定要运行的命令,而CMD指令指定了ENTRYPOINT命令的默认参数。Kubernetes 提供了两个字段,"Command"和"Args",为 Pod 定义中的容器映像指定,以覆盖默认设置ENTRYPOINT和CMD。我们将在本章中讨论这些字段。我们还将讨论在 Pod 定义的容器映射中使用环境变量和"env"字段映射。
本章包括以下几节。
- 设置环境
- 入口点和 CMD 指令
- Pod 定义中的命令和参数字段
- 环境变量
- 使用 Docker 映像中的默认入口点和 CMD
- 覆盖 Docker 映像中的入口点和 CMD
- 在命令映射中指定可执行文件和参数
- 指定 Args 映射中的可执行文件和参数
设置环境
本章使用了以下软件。
- -Docker 引擎(最新版本)
- -Kubernetes(1.01 版)
- -Kubernetes(1.01 版)
按照第一章所述安装 Docker 引擎、Kubernetes 和 Kubectl。使用以下命令启动 Docker 引擎并验证其状态。
sudo service docker start
sudo service docker status
图 3-1 所示的输出表明 Docker 正在运行。
图 3-1。
Starting Docker and Verifying Its Status
入口点和 CMD 指令
Docker 映像的Dockerfile中的ENTRYPOINT指定映像运行时要运行的命令。ENTRYPOINT有两种形式,在表 3-1 中讨论。一只Dockerfile可能只有一只ENTRYPOINT。如果指定了多个ENTRYPOINT,则运行最后一个ENTRYPOINT条目。
表 3-1。
ENTRYPOINT Forms
| 形式 | 描述 | 格式 | | --- | --- | --- | | 执行表单 | 使用指定的参数运行可执行文件。如果不使用环境变量替换,exec 形式是首选形式。但是如果使用环境变量替换,就必须使用外壳形式。exec 表单不执行任何环境变量替换。 | ENTRYPOINT ["可执行"、" param1 "、" param2"] | | 外壳形式 | 在 shell 中运行命令,并防止任何 CMD 或 run 命令行参数与 ENTRYPOINT 一起使用。shell 形式使用/bin/sh -c 启动 shell,即使没有显式调用 shell。 | ENTRYPOINT 命令 param1 param2 |CMD指令指定 exec 格式的ENTRYPOINT命令的参数。CMD有三种形式,如表 3-2 所述。一个 Dockerfile 可能只有一个CMD条目。如果指定了多个CMD,则运行最后一个CMD条目。CMD指令可以包括可执行程序。
表 3-2。
CMD Forms
| 形式 | 描述 | 格式 | | --- | --- | --- | | 执行表单 | exec 表单以 JSON 数组格式指定要调用的命令和命令参数。exec 表单不执行环境变量替换。如果要执行环境变量替换,请使用 shell 形式或在 exec 形式中显式调用 shell。在 JSONs 数组格式中,必须用双引号""将名称括起来。 | CMD [“可执行文件”、“param1”、“param2”] | | 入口点的默认参数 | 指定 ENTRYPOINT 命令的默认参数。必须指定入口点和 CMD。必须使用 JSON 数组格式指定 ENTRYPOINT 和 CMD。在 JSONs 数组格式中,必须用双引号""将名称括起来。 | CMD [“param1”,“param2”] | | 外壳形式 | 使用参数调用 shell 来调用指定的命令。该命令作为/bin/sh–c 的子命令调用。 | CMD 命令 param1 param2 |如果向docker run命令提供命令行参数,这些参数会覆盖CMD指令中的默认参数。ENTRYPOINT指令也可以与帮助脚本结合使用。接下来,我们将讨论两个字段,“command”和“args ”,它们可以分别用于覆盖Dockerfile中的ENTRYPOINT和CMD指令。
Pod 定义中的命令和参数字段
Kubernetes 可以覆盖 Dockerfile 文件中指定的ENTRYPOINT(命令)和CMD(参数)指令。Pod 定义文件中的两个字段映射可用于覆盖ENTRYPOINT和CMD指令。这些字段是“Command”和“Args”,它们分别覆盖 Dockerfile 的“ENTRYPOINT”和“CMD”指令。基于指定了这些指令和字段中的哪一个,覆盖适用。表 3-3 中讨论了一些超越的例子。
表 3-3。
Examples of Overriding ENTRYPOINT and CMD with Command and Args
| | ENTRYPOINT(入口点) | 煤矿管理局 | 命令 | 一个参数名 | 二手的 | | --- | --- | --- | --- | --- | --- | | 示例 1 | 是 | 是 | 是 | 是 | Pod 定义文件中的命令和参数字段映射会覆盖 Dockerfile 中的 ENTRYPOINT 和 CMD 指令。 | | 例 2 | 是 | 是 | 不 | 不 | 使用 Dockerfile ENTRYPOINT 命令和 CMD 参数。 | | 例 3 | 是 | 是 | 是 | 不 | 只使用命令中的命令,忽略 Dockerfile ENTRYPOINT 和 CMD 指令。 | | 例 4 | 是 | 是 | 不 | 是 | 入口点中指定的 Docker 映像命令与 Pod 定义的 args 中指定的 Args 一起使用。Dockerfile 的 CMD 中的参数被忽略。 | | 例 5 | 不 | 是 | 不 | 不 | 运行 CMD 指令中的命令和参数。 | | 例 6 | 不 | 是 | 是 | 是 | 使用 Pod 定义文件中的命令和参数字段映射。Dockerfile 中的 CMD 指令被覆盖。 | | 例 7 | 不 | 是 | 不 | 是 | 使用 Pod 定义文件中的 Args 字段映射。Dockerfile 中的 CMD 指令被覆盖。 | | 例 8 | 不 | 是 | 是 | 不 | 使用命令映射中的命令,忽略 Dockerfile CMD 指令。 |环境变量
Pod 的模式具有指定环境变量的规定。环境变量被指定为“名称”和“值”字段映射,作为容器定义的“env”映射中的一个集合。指定环境变量的格式如下。
spec:
containers:
-
image: "image name"
name: "container name "
env:
-
name: "env variable 1"
value: " env variable 1 value"
-
name: "env variable 2"
value: " env variable 2 value"
当 Docker 映像由 Kubernetes 运行时,使用–e将环境变量添加到docker run命令中。如果使用 shell 来运行 Docker image 命令,则使用环境变量替换也可以在“command”和“args”映射中使用环境变量。如果使用了以下一个或多个选项,将调用 shell:
- -使用入口点或 CMD 的外壳形式
- -外壳在 ENTRYPOINT 或 CMD 指令中显式调用
在接下来的部分中,我们将使用“Ubuntu”Docker 镜像来演示如何覆盖默认的ENTRYPOINT命令和默认的CMD参数。我们将从使用默认的ENTRYPOINT和CMD指令开始。
使用 Docker 映像中的默认入口点和 CMD
Ubuntu 镜像的Dockerfile不提供ENTRYPOINT指令,但是CMD指令被设置为CMD ["/bin/bash"]。在本节的示例中,我们将创建一个不覆盖 Docker 映像中的ENTRYPOINT或CMD指令的 Pod 定义。如下创建一个 Pod 定义文件,映像为“ubuntu ”,并设置一些环境变量。
apiVersion: v1
kind: Pod
metadata:
name: "hello-world"
labels:
app: "helloApp"
spec:
restartPolicy: Never
containers:
-
image: "ubuntu"
name: "hello"
ports:
containerPort: 8020
env:
-
name: "MESSAGE1"
value: "hello"
-
name: "MESSAGE2"
value: "kubernetes"
可以在 vi 编辑器中创建env.yaml文件,并用:wq 命令保存,如图 3-2 所示。
图 3-2。
A Pod definition file env.yaml to demonstrate Environment Variables
运行以下命令,从定义文件env.yaml创建一个 Pod。
kubectl create –f env.yaml
hello-world pod 被创建,如图 3-3 所示。运行以下命令列出窗格。
图 3-3。
Creating and listing a Pod
kubectl get pods
hello-world pod 被创建,但创建的 Docker 容器被列为“正在创建”,如图 3-3 所示。
当 Docker 容器被创建时,STATUS列值转变为“运行中”,READY列值变为 1/1,这表示 Pod 中的 1 个容器中的 1 个准备好了,这在图 3-4 中没有示出,因为此后READY状态迅速转变为 0/1。Pod 命令/参数运行后,Pod 终止,STATUS变为ExitCode:0,如图 3-4 所示。
图 3-4。
After the Command/Args have run, a Pod terminates and the Pod’s Status becomes ExitCode:0
运行以下命令列出 Pod 的输出。
kubectl logs hello-world
由于“Ubuntu”Docker 镜像中的默认CMD ["/bin/bash"]只是使用/bin/bash,对 bash shell 的调用,所以不会产生任何输出,如图 3-5 所示。
图 3-5。
No output generated with Default CMD [“/bin/bash”] in “ubuntu” Docker Image
覆盖入口点和 CMD
在第二个例子中,我们将使用 Pod 定义文件中的Command和Args映射来覆盖Dockerfile中的ENTRYPOINT和CMD。结合使用ENTRYPOINT和 CMD 将有助于我们为映像指定默认的可执行文件,并且它还将为该可执行文件提供默认的参数。环境变量替换用于带有$(VARIABLE_NAME)语法的MESSAGE1和MESSAGE2环境变量。
command: ["/bin/echo"]
args: [" $(MESSAGE1)", " $(MESSAGE2)"]
列出env.yaml Pod 定义文件:
apiVersion: v1
kind: Pod
metadata:
name: "hello-world"
labels:
app: "helloApp"
spec:
restartPolicy: Never
containers:
-
image: "ubuntu"
name: "hello"
ports:
-
containerPort: 8020
env:
-
name: "MESSAGE1"
value: "hello"
-
name: "MESSAGE2"
value: "kubernetes"
command: ["/bin/echo"]
args: [" $(MESSAGE1)", " $(MESSAGE2)"]
可以在 vi 编辑器中打开和修改env.yaml文件,并使用:wq 命令保存,如图 3-6 所示。
图 3-6。
Modifying env.yaml in a vi Editor
首先,我们需要用下面的命令删除第一个例子中创建的hello-world pod。
kubectl delete pod hello-world
hello-world pod 被删除,如图 3-7 所示。
图 3-7。
Deleting the hello-world Pod
运行kubectl create命令,从定义文件env.yaml创建一个 Pod。
kubectl create –f env.yaml
hello-world Pod 被创建,如图 3-8 所示。
图 3-8。
Creating the hello-world Pod from definition file env.yaml
运行kubectl get命令列出窗格。
kubectl get pods
hello-world吊舱被列出,如图 3-9 所示。如图 3-9 所示,Pod 从“运行”的STATUS快速转换到ExitCode:0。
图 3-9。
Listing the Pods with transitioning STATUS value
运行以下命令列出 Pod 的输出。
kubectl logs hello-world
使用替换从环境变量MESSAGE1和MESSAGE2创建的消息如图 3-10 所示。
图 3-10。
Outputting Message Generated from Environment Variables using Value Substitution
在命令映射中指定可执行文件和参数
在第三个示例中,指定可执行文件和参数都在 Pod 定义文件的命令映射中指定。环境变量替换用于MESSAGE1和MESSAGE2环境变量。如果使用了环境变量语法$(VARIABLE_NAME),就不需要显式地调用/启动 shell,这正是我们所使用的。
command: ["/bin/echo", " $(MESSAGE1)", " $(MESSAGE2)"]
列出env.yaml Pod 定义文件:
apiVersion: v1
kind: Pod
metadata:
name: "hello-world"
labels:
app: "helloApp"
spec:
restartPolicy: Never
containers:
-
image: "ubuntu"
name: "hello"
ports:
-
containerPort: 8020
env:
-
name: "MESSAGE1"
value: "hello"
-
name: "MESSAGE2"
value: "kubernetes"
command: ["/bin/echo", " $(MESSAGE1)", " $(MESSAGE2)"]
可以在 vi 编辑器中打开和修改env.yaml文件,并使用:wq 命令保存,如图 3-11 所示。
图 3-11。
The Command mapping with both the Command Executable and the Parameters
运行kubectl create命令,从定义文件env.yaml创建一个 Pod。
kubectl create –f env.yaml
hello-world pod 被创建,如图 3-12 所示。运行kubectl get命令列出 pod。
图 3-12。
Creating and Listing the Pod with Definition file from Figure 3-11
kubectl get pods
如图 3-12 所示,hello-world pod 被列出,尽管最初 Pod STATUS未被列为“运行中”。Pod 快速转换到 1/1 的READY值,然后是 0/1。图 3-12 中未显示 1/1 就绪值,因为它快速转换到 0/1。命令运行后,Pod 终止,STATUS变为ExitCode:0,如图 3-12 所示。
随后调用以下命令列出 Pod 生成的输出。
kubectl get logs
环境变量MESSAGE1和MESSAGE2创建的消息被列出,如图 3-13 所示。
图 3-13。
Message output by Pod created in Figure 3-12
指定 Args 映射中的可执行文件和参数
在第四个例子中,指定可执行文件和 Pod 定义文件中的Args映射中的参数,作为覆盖Dockerfile中的CMD指令的结果。环境变量替换用于环境变量语法为$(VARIABLE_NAME)的MESSAGE1和MESSAGE2环境变量。
args: ["/bin/echo", " $(MESSAGE1)", " $(MESSAGE2)"]
列出env.yaml Pod 定义文件:
apiVersion: v1
kind: Pod
metadata:
name: "hello-world"
labels:
app: "helloApp"
spec:
restartPolicy: Never
containers:
-
image: "ubuntu"
name: "hello"
ports:
-
containerPort: 8020
env:
-
name: "MESSAGE1"
value: "hello"
-
name: "MESSAGE2"
value: "kubernetes"
args: ["/bin/echo", " $(MESSAGE1)", " $(MESSAGE2)"]
可以在 vi 编辑器中打开和修改env.yaml文件,并使用:wq 命令保存,如图 3-14 所示。
图 3-14。
The args Mapping in the Pod definition file specifies both the Command Executable and the Parameters
必须删除前一示例中创建的hello-world Pod,否则在运行kubectl create命令时会产生如图 3-15 所示的错误。
图 3-15。
Error Generated if hello-world Pod already exists
运行kubectl create命令,从定义文件env.yaml创建一个 Pod。
kubectl create –f env.yaml
该命令的输出如图 3-16 所示。
图 3-16。
Creating a Pod from definition file in Figure 3-14
hello-world pod 被创建,如图 3-17 所示。运行kubectl get命令列出 pod。
图 3-17。
The Pod terminates and its Status transitions to ExitCode:0 after the command has run
kubectl get pods
hello-world吊舱被列出,如图 3-17 所示。Pod 快速转换到就绪值 1/1,然后是 0/1。图 3-17 中未显示 1/1 就绪值,因为它快速过渡到 0/1。命令运行后,Pod 终止,状态变为 ExitCode:0,如图 3-17 所示。
随后调用以下命令列出 Pod 生成的输出。
kubectl get logs
用MESSAGE1和MESSAGE2的环境变量替换创建的消息被列出,如图 3-18 所示。
图 3-18。
Outputting the Message Generated by Pod
摘要
在本章中,我们讨论了 Docker 映像Dockerfile中的ENTRYPOINT和CMD指令:当映像在 Kubernetes Pod 中运行时,这些指令用于运行带有默认参数的默认命令。我们还讨论了 Pod 定义文件中的Command和Args映射,它们可以用来覆盖ENTRYPOINT和CMD指令。我们讨论了用 Pod 定义文件中的“command”和“args”字段映射覆盖“Ubuntu”Docker 映像的默认指令的各种例子。我们还演示了在 Pod 定义文件中使用环境变量。在下一章,我们将讨论在 Kubernetes 中使用 MySQL 数据库。