共计 13932 个字符,预计需要花费 35 分钟才能阅读完成。
本文通过实际操作来演示 Kubernetes 的使用,因为环境有限,集群部署在本地 3 个 Ubuntu 上,主要包括如下内容:
- 部署环境介绍,以及 Kubernetes 集群逻辑架构
- 安装部署 Open vSwitch 跨机器容器通信工具
- 安装部署 Etcd 和 Kubernetes 的各大组件
- 演示 Kubernetes 管理容器和服务
关于 Kubernetes 系统架构及组件介绍见这里。http://www.linuxidc.com/Linux/2015-12/125757.htm
1. 部署环境及架构
- vSphere: 5.1
- 操作系统: ubuntu 14.04 x86_64
- Open vSwith 版本: 2.0.2
- Kubernetes: v0.7.2
- Etcd 版本: 2.0.0-rc.1
- Docker 版本: 1.4.1
- 服务器信息:
Role | Hostname | IP Address |
---|---|---|
APIServer | kubernetes | 172.29.88.206 |
Minion | minion1 | 172.29.88.207 |
Minion | minion2 | 172.29.88.208 |
在详细介绍部署 Kubernetes 集群前,先给大家展示下集群的逻辑架构。从下图可知,整个系统分为两部分,第一部分是 Kubernetes APIServer,是整个系统的核心,承担集群中所有容器的管理工作;第二部分是 minion,运行 Container Daemon,是所有容器栖息之地,同时在 minion 上运行 Open vSwitch 程序,通过 GRE Tunnel 负责 minions 之间 Pod 的网络通信工作。
2. 安装 Open vSwitch 及配置 GRE
为了解决跨 minion 之间 Pod 的通信问题,我们在每个 minion 上安装 Open vSwtich,并使用 GRE 或者 VxLAN 使得跨机器之间 P11od 能相互通信,本文使用 GRE,而 VxLAN 通常用在需要隔离的大规模网络中。对于 Open vSwitch 的介绍请参考另一篇文章 Open vSwitch。
sudo apt-get install openvswitch-switch bridge-utils
安装完 Open vSwitch 和桥接工具后,接下来便建立 minion0 和 minion1 之间的隧道。首先在 minion1 和 minion2 上分别建立 OVS Bridge:
# ovs-vsctl add-br obr0
接下来建立 gre,并将新建的 gre0 添加到 obr0,在 minion1 上执行如下命令:
# ovs-vsctl add-port obr0 gre0 -- set Interface gre0 type=gre options:remote_ip=172.29.88.208
上面的 remoute_ip 是另一台服务 minion2 上的对外 IP。
在 minion2 上执行:
# ovs-vsctl add-port obr0 gre0 -- set Interface gre0 type=gre options:remote_ip=172.29.88.207
至此,minion1 和 minion2 之间的隧道已经建立。然后我们在 minion1 和 minion2 上创建 Linux 网桥 kbr0 替代 Docker 默认的 docker0(我们假设 minion1 和 minion2 都已安装 Docker),设置 minion1 的 kbr0 的地址为 172.17.1.1/24,minion2 的 kbr0 的地址为 172.17.2.1/24,并添加 obr0 为 kbr0 的接口,以下命令在 minion1 和 minion2 上执行:
# brctl addbr kbr0 // 创建 linux bridge 代替 docker0
# brctl addif kbr0 obr0 // 添加 obr0 为 kbr0 的接口
# ip link set dev docker0 down // 设置 docker0 为 down 状态
# ip link del dev docker0 // 删除 docker0,可选
查看这些接口的状态:
# service openvswitch-switch status
# ovs-vsctl show
9d248403-943c-41c0-b2d0-3f9b130cdd3f
Bridge "obr0"
Port "gre0"
Interface "gre0"
type: gre
options: {remote_ip="172.29.88.207"}
Port "obr0"
Interface "obr0"
type: internal
ovs_version: "2.0.2"
# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.56847afe9799 no
kbr0 8000.620ff7ee9c49 no obr0
为了使新建的 kbr0 在每次系统重启后任然有效,我们在 minion1 的 /etc/network/interfaces
文件中追加内容如下:(在 CentOS 上会有些不一样)
# vi /etc/network/interfaces
auto kbr0
iface kbr0 inet static
address 172.17.1.1
netmask 255.255.255.0
gateway 172.17.1.0
dns-nameservers 172.31.1.1
同样在 minion2 上追加类似内容,只需修改 address 为 172.17.2.1 和 gateway 为 172.17.2.0 即可,然后执行ip link set dev kbr0 up
,你能在 minion1 和 minion2 上发现 kbr0 都设置了相应的 IP 地址。为了验证我们创建的隧道是否能通信,我们在 minion1 和 minion2 上相互 ping 对方 kbr0 的 IP 地址,从下面的结果发现是不通的,经查找这是因为在 minion1 和 minion2 上缺少访问 172.17.1.1 和 172.17.2.1 的路由,因此我们需要添加路由保证彼此之间能通信:
minion1 上执行:
# ip route add 172.17.2.0/24 via 172.29.88.208 dev eth0
minion2 上执行:
# ip route add 172.17.1.0/24 via 172.29.88.207 dev eth0
现在可以 ping 通对方的虚拟网络了:
$ ping 172.17.2.1
PING 172.17.2.1 (172.17.2.1) 56(84) bytes of data.
64 bytes from 172.17.2.1: icmp_seq=1 ttl=64 time=0.334 ms
64 bytes from 172.17.2.1: icmp_seq=2 ttl=64 time=0.253 ms
^C
--- 172.17.2.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.253/0.293/0.334/0.043 ms
下面安装 Kubernetes APIServer 及 kubelet、proxy 等服务。
3. 安装 Kubernetes APIServer
3.1 下载安装 kubernetes 各组件
可以自己从源码编译 kubernetes(需要安装 golang 环境),也可以从 GitHub Kubernetes repo release page. 选择编译好的二进制版本(v0.7.2)下载,为了方便后面启动或关闭 kubernetes 组件,我们同时下载二进制包和源码包:
# cd /usr/local/src
# wget https://github.com/coreos/etcd/releases/download/v2.0.0-rc.1/etcd-v2.0.0-rc.1-linux-amd64.tar.gz
# wget https://github.com/GoogleCloudPlatform/kubernetes/releases/download/v0.7.2/kubernetes.tar.gz
# wget https://github.com/GoogleCloudPlatform/kubernetes/archive/v0.7.2.zip
然后解压下载的 kubernetes 和 etcd 包,并在 kubernetes(minion1)、minion2 上创建目录/opt/bin
# mkdir /opt/bin // 这一步 APIserver 和所有 minions 上都要创建
解压 kubernetes
src# tar xf kubernetes.tar.gz
# ll
drwxr-xr-x 3 501 staff 4096 Dec 19 02:32 etcd-v2.0.0-rc.1-linux-amd64/
-rw-r--r-- 1 root root 6223584 Jan 6 14:39 etcd-v2.0.0-rc.1-linux-amd64.tar.gz
drwxr-xr-x 7 root root 4096 Nov 20 06:35 kubernetes/
-rw-r--r-- 1 root root 82300483 Jan 6 14:37 kubernetes.tar.gz
-rw-r--r-- 1 root root 9170754 Jan 9 14:47 v0.7.2.zip
# cd kubernetes/server
# tar xf kubernetes-server-linux-amd64.tar.gz
# cd kubernetes/server/bin/
APIserver 本身需要的是 kube-apiserver kube-scheduler kube-controller-manager kubecfg 四个
# cp -a kube* /opt/bin/
把 proxy 和 kubelet 复制到其他 minions,确保这些文件都是可执行的
# scp kube-proxy kubelet root@172.29.88.207:/opt/bin
# scp kube-proxy kubelet root@172.29.88.208:/opt/bin
/opt/bin
并没有加入系统 PATH
,所以kube-apiserver -version
是看不到结果,但在后面配置的服务中会自动加入(PATH=$PATH:/opt/bin
)。
3.2 解压安装 etcd
etcd
在这里的作用是服务发现存储仓库,通俗的来讲就是记录 kubernetes 启动了多少 pods、services、replicationController 以及它们的信息等,详细介绍见这里。此外版本 2.0 与 v0.4.6 在启动参数上的写法有一定差别。
# tar xf etcd-v2.0.0-rc.1-linux-amd64.tar.gz && cd etcd-v2.0.0-rc.1-linux-amd64/
# cp -a etcd etcdctl /opt/bin
3.3 配置 kube-apiserver 等为 upstart 脚本启动
这一步主要是为了管理 kube-apiserver 等进程的方便,避免每次都手动启动各服务、添加冗长的启动参数选项,而且在不同的系统平台下 kubernetes 已经提供了相应的工具。
解压 kubernetes* 源码包 *
src# unzip xf v0.7.2.zip && cd kubernetes-0.7.2
这里比较奇怪的是最新 release 版本源码的 cluster 目录下是有 ubuntu 子目录的,但 latest 之前的下载后没有 ubuntu 目录
# cd cluster/ubuntu
# ll
.. 2 root root 4096 Jan 8 17:39 default_scripts/ 各组件默认启动参数
.. 2 root root 4096 Jan 8 17:39 init_conf/ upstart 启动方式
.. 2 root root 4096 Jan 8 17:39 initd_scripts/ service 启动方式,与 upstart 选其一
.. 1 root root 1213 Jan 8 08:53 util.sh*
# ./util.sh
util.sh
脚本就是把当前目录下的 service/upstart 脚本、默认参数配置文件复制到 /etc
下,可以通过 service etcd start
的形式管理 kubernetes。由于 kubernetes 更新速度极快,项目的文件和目录结构经常变化,请找准文件。接下来我们需要修改那些只适合本机使用的默认参数。(请注意备份先,因为后面能否正常跨机器管理 docker 与这些选项有关,特别是 IP)
etcd 官方建议使用新的 2379 端口代替4001
# vi /etc/default/etcd
ETCD_OPTS="-listen-client-urls=http://0.0.0.0:4001"
# vi /etc/default/kube-apiserver
KUBE_APISERVER_OPTS="--address=0.0.0.0 \
--port=8080 \
--etcd_servers=http://127.0.0.1:4001 \
--logtostderr=true \
--portal_net=11.1.1.0/24"
# vi /etc/default/kube-scheduler
KUBE_SCHEDULER_OPTS="--logtostderr=true \
--master=127.0.0.1:8080"
# vi /etc/default/kube-controller-manager
KUBE_CONTROLLER_MANAGER_OPTS="--master=127.0.0.1:8080 \
--machines=172.29.88.207,172.29.88.208 \
--logtostderr=true"
* 复制 kubelet、kube-proxy 等到 minion1:# scp /etc/default/{kubelet,kube-proxy} 172.29.88.207:/etc/default/
# scp /etc/init.d/{kubelet,kube-proxy} 172.29.88.207:/etc/init.d/
# scp /etc/init/{kubelet.conf,kube-proxy.conf} 172.29.88.207:/etc/init/
* 在 minion1 端进行
# vi /etc/default/kubelet
KUBELET_OPTS="--address=172.29.88.207 \
--port=10250 \
--hostname_override=172.29.88.207 \
--etcd_servers=http://172.29.88.206:4001 \
--logtostderr=true"
# vi /etc/default/kube-proxy
KUBE_PROXY_OPTS="--etcd_servers=http://172.29.88.207:4001 \
--logtostderr=true"
(对 minion2 重复上面 * 两个步骤,把上面.207 改成.208)
上面的各配置文件就是对应命令的选项,具体含义使用-h
。这里只简单说明:
etcd
服务 APIserver 和 minions 都要访问,也就是其他组件的--etcd_servers
值(带 http 前缀)kube-apiserver
监听在 8080 端口,也就是其他组件的--master
值;--portal_net
地址段不能与 docker 的桥接网卡 kbr0 重复,指定 docker 容器的 IP 段etcd
、kube-apiserver
、kube-scheduler
、kube-controller-manager
运行在 apiserver(服务)端,kubelet
、kube-proxy
运行在 minion(客户端)kube-controller-manager
使用预先定义 pod 模板创建 pods,保证指定数量的 replicas 在运行,默认监听在 master 的 127.0.0.1:10252kubelet
默认监听端口 10250,也正是 apiserver 的--kubelet_port
的值
3.4 启动
重启 docker
接下来重启 minion1、minion2 上的 Docker daemon(注意使用的网桥):
# docker -d -b kbr0
由于后面的测试可能需要在线下载 images,所以如果你的服务器无法访问 docker hub,上面启动时记得设置 HTTP_PROXY
代理。
启动 apiserver
# service etcd start
# service kube-apiserver start
kube-apiserver
启动后会自动运行 kube-scheduler
、kube-controller-manager
,但修改配置后依然可以单独重启各个服务如service kube-contoller-manager restart
。这些服务的日志可以从/var/log/upstart/kube*
找到。
在 minion1、minion2 上启动 kubelet、kube-proxy:
# service kubelet start
# service kube-proxy start
4. 使用 kubecfg 部署测试应用
为了方便,我们使用 Kubernetes 提供的例子 Guestbook(下载的源码 example 目录下可以找到)来演示 Kubernetes 管理跨机器运行的容器,下面我们根据 Guestbook 的步骤创建容器及服务。在下面的过程中如果是第一次操作,可能会有一定的等待时间,状态处于 pending,这是因为第一次下载 images 需要一段时间。
4.1 创建 Redis-master Pod 和 redis-master 服务
配置管理操作都在 apiserver 上执行,并且都是基于实现编写好的 json 格式。涉及到下载 docker 镜像的部分,如果没有外网,可能需要修改 image 的值或使用自己搭建的 docker-registry:
# cd kubernetes-0.7.2/examples/guestbook/
# cat redis-master.json
{"id": "redis-master",
"kind": "Pod",
"apiVersion": "v1beta1",
"desiredState": {"manifest": {"version": "v1beta1",
"id": "redis-master",
"containers": [{"name": "master",
"image": "dockerfile/redis",
"cpu": 100,
"ports": [{"containerPort": 6379,
"hostPort": 6379
}]
}]
}
},
"labels": {"name": "redis-master"
}
}
# kubecfg -h http://172.29.88.206:8080 -c redis-master.json create pods
# kubecfg -h http://172.29.88.206:8080 -c redis-master-service.json create services
完成上面的操作后,我们可以看到如下 redis-master Pod 被调度到 172.29.88.207:
(下面直接 list 实际上是省略了-h http://127.0.0.1:8080
)
# kubecfg list pods
Name Image(s) Host Labels Status
---------- ---------- ---------- ---------- ----------
redis-master dockerfile/redis 172.29.88.207/ name=redis-master Running
查看 services:# kubecfg list services
Name Labels Selector IP Port
---------- ---------- ---------- ---------- ------
kubernetes component=apiserver,provider=kubernetes 11.1.1.233 443
kubernetes-ro component=apiserver,provider=kubernetes 11.1.1.204 80
redis-master name=redis-master name=redis-master 11.1.1.175 6379
发现除了 redis-master 的服务之外,还有两个 Kubernetes 系统默认的服务 kubernetes-ro 和 kubernetes。而且我们可以看到每个服务都有一个服务 IP 及相应的端口,对于服务 IP,是一个虚拟地址,根据 apiserver 的 portal_net
选项设置的 CIDR
表示的 IP 地址段来选取,在我们的集群中设置为 11.1.1.0/24。为此每新创建一个服务,apiserver 都会在这个地址段中随机选择一个 IP 作为该服务的 IP 地址,而端口是事先确定的。对 redis-master 服务,其服务地址为 11.1.1.175,端口为 6379,与 minion 主机映射的端口也是 6379。
4.2 创建 redis-slave Pod 和 redis-slave 服务
# kubecfg -h http://172.29.88.206:8080 -c redis-slave-controller.json create replicationControllers
# kubecfg -h http://172.29.88.206:8080 -c redis-slave-service.json create services
注意上面的 redis-slave-controller.json
有个"replicas": 2
、"hostPort": 6380
,因为我们的集群中只有 2 个 minions,如果为 3 的话,就会导致有 2 个 Pod 会调度到同一台 minion 上,产生端口冲突,有一个 Pod 会一直处于 pending 状态,不能被调度(可以通过日志看到原因)。
# kubecfg list pods
Name Image(s) Host Labels Status
---------- ---------- ---------- ---------- --------
2c2a06...c2971614d brendanburns/redis-slave 172.29.88.208/ name=redisslave,uses=redis-master Running
2c2ad5...c2971614d brendanburns/redis-slave 172.29.88.207/ name=redisslave,uses=redis-master Running
redis-master dockerfile/redis 172.29.88.207/ name=redis-master Running
# kubecfg list services
Name Labels Selector IP Port
---------- ---------- ---------- ---------- --------
kubernetes component=apiserver,provider=kubernetes 11.1.1.233 443
kubernetes-ro component=apiserver,provider=kubernetes 11.1.1.204 80
redis-master name=redis-master name=redis-master 11.1.1.175 6379
redisslave name=redisslave name=redisslave 11.1.1.131 6379
4.3 创建 Frontend Pod 和 Frontend 服务
前面 2 步都是 guestbook 的 redis 数据存储,现在部署应用:(修改 frontend-controller.json
的replicas
为 2)
# kubecfg -h http://172.29.88.206:8080 -c frontend-controller.json create replicationControllers
# kubecfg -h http://172.29.88.206:8080 -c frontend-service.json create services
# kubecfg -h http://172.29.88.206:8080 list pods
Name Image(s) Host Labels Status
---------- ---------- ---------- ---------- ----------
2c2a06...c2971614d brendanburns/redis-slave 172.29.88.208/ name=redisslave,uses=redis-master Running
2c2ad5...c2971614d brendanburns/redis-slave 172.29.88.207/ name=redisslave,uses=redis-master Running
d87744...c2971614d kubernetes/example-guestbook-php-redis 172.29.88.207/ name=frontend,uses=redisslave,redis-master Running
redis-master dockerfile/redis 172.29.88.207/ name=redis-master Running
1370b9...c2971614d kubernetes/example-guestbook-php-redis 172.29.88.208/ name=frontend,uses=redisslave,redis-master Running
# kubecfg -h http://172.29.88.206:8080 list services
Name Labels Selector IP Port
---------- ---------- ---------- ---------- ------
redis-master name=redis-master name=redis-master 11.1.1.175 6379
redisslave name=redisslave name=redisslave 11.1.1.131 6379
frontend name=frontend name=frontend 11.1.1.124 80
kubernetes component=apiserver,provider=kubernetes 11.1.1.233 443
kubernetes-ro component=apiserver,provider=kubernetes 11.1.1.204 80
<pre><code>通过查看可知 Frontend Pod 也被调度到两台 minion,服务 IP 为 11.1.1.124,端口是 80,映射到外面 minions 的端口为 8000(可以通过 `ps -ef|grep docker-proxy` 发现)。### 4.4 其他操作(更新、删除、查看)##
** 删除 **
除此之外,你可以删除 Pod、Service,如删除 minion1 上的 redis-slave Pod: kubecfg -h http://172.29.88.206:8080 delete pods/2c2ad505-96fd-11e4-9c0b-000c2971614d
Status
----------
Success
格式为`services/ 服务 Name`、`pods/pods 名字 `,不必关心从哪个 minion 上删除了。需要提醒的是,这里 pods 的 replcas 为 2,所以即使删除了这个 pods,kubernetes 为自动为你重新启动一个。** 更新 **
更新 ReplicationController 的 Replicas 数量:</code></pre>
# kubecfg list replicationControllers
Name Image(s) Selector Replicas
---------- ---------- ---------- ----------
frontendController kubernetes/example-guestbook-php-redis name=frontend 2
redisSlaveController brendanburns/redis-slave name=redisslave 2
<pre><code>把 frontendController 的 Replicas 更新为 1,则这行如下命令,然后再通过上面的命令查看 frontendController 信息,发现 Replicas 已变为 1: kubecfg -h http://172.29.88.206:8080 resize frontendController 1
** 查看 **
Kubernetes 内置提供了一个简单的 UI 来查看 pods、services、replicationControllers,但极其简陋,暂时可以忽略,访问`http://172.29.88.206:8080/static/#/groups//selector/`:![kubernetes-simpleui][5]
在浏览器访问 api:`http://172.29.88.206:8080/api/v1beta1/replicationControllers`。![kubernetes-api][2]
etcd 做服务发现,可以通过 api 访问其内容,访问`http://172.29.88.206:4001/v2/keys/registry/services/endpoints/default`,得到 json 格式数据。### 4.5 演示 guestbook ##
通过上面的结果可知当前提供前端服务的 PHP 和提供数据存储的后端服务 Redis master 的 Pod 分别运行在 172.29.88.208 和 172.29.88.207 上,即容器运行在不同主机上,还有 Redis slave 也运行在两台不同的主机上,它会从 Redis master 同步前端写入 Redis master 的数据。下面我们从两方面验证 Kubernetes 能提供跨机器间容器的通信:** 浏览器访问留言簿 **
在浏览器打开`http://${IPAddress}:8000`,IPAddress 为 PHP 容器运行的 minion 的 IP 地址,其暴漏的端口为 8000,这里 IP_Address 为 172.29.88.208。打开浏览器会显示如下信息:![kubernetes-guestbook1][3]
你可以输入信息并提交,然后 Submit 按钮下方会显示你输入的信息:![kubernetes-guestbook2][4]
由于前端 PHP 容器和后端 Redis master 容器分别在两台 minion 上,因此 PHP 在访问 Redis master 服务时一定得跨机器通信,可见 Kubernetes 的实现方式避免了用 link 只能在同一主机上实现容器间通信的缺陷。** 从 redis 后端验证 **
我们从后端数据层验证不同机器容器间的通信。根据上面的输出结果发现 Redis slave 和 Redis master 分别调度到两台不同的 minion 上,在 172.29.88.207 主机上执行`docker exec -ti e5941db7e424 /bin/sh`,e5941db7e424 master 的容器 ID(`docker ps`),进入容器后通过 redis-cli 命令查看从浏览器输入的信息如下:</code></pre>
# docker exec -ti e5941db7e424 /bin/sh
# redis-cli
127.0.0.1:6379> keys *
1) "messages"
127.0.0.1:6379> get messages
",Hi, Sean,Kubernetes,,llll,abc,\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\xd4\xb0\xef\xbf\xbd,sync info,"
类似可以在 172.29.88.208 的 redis-slave 上看到同样的内容。由此可见 Redis master 和 Redis slave 之间数据同步正常,OVS GRE 隧道技术使得跨机器间容器正常通信。
4.6 排错提示
- 所有的 kubelet 必须起来,否则报错
F0319 16:56:08.058335 9960 kubecfg.go:438] Got request error: The requested resource does not exist.
- 必须使用 - b 启动 docker,否则无法访问 8000 端口,redis-slave 也没同步
- 注意 pods 一直处于 Pending 或 Failed 状态时去 apiserver 或其他组件日志里查看错误,是否是由于端口绑定冲突导致。
参考
- CentOS 7 实战 Kubernetes 部署
- kubernetes-examples-guestbook
- getting_started_guides-ubuntu_single_node
- 基于 kubernetes 构建 Docker 集群管理详解
OpenStack, Kubernetes, Mesos 谁主沉浮 http://www.linuxidc.com/Linux/2015-09/122696.htm
Kubernetes 集群搭建过程中遇到的问题及解决 http://www.linuxidc.com/Linux/2015-12/125735.htm
Kubernetes 的详细介绍:请点这里
Kubernetes 的下载地址:请点这里
本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-12/125758.htm