本文经过实际操做来演示Kubernetes的使用,由于环境有限,集群部署在本地3个ubuntu上,主要包括以下内容:php
关于 Kubernetes 系统架构及组件介绍见这里。前端
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的网络通讯工做。node
为了解决跨minion之间Pod的通讯问题,咱们在每一个minion上安装Open vSwtich,并使用GRE或者VxLAN使得跨机器之间P11od能相互通讯,本文使用GRE,而VxLAN一般用在须要隔离的大规模网络中。对于Open vSwitch的介绍请参考另外一篇文章Open vSwitch。linux
sudo apt-get install openvswitch-switch bridge-utils
安装完Open vSwitch和桥接工具后,接下来便创建minion0和minion1之间的隧道。首先在minion1和minion2上分别创建OVS Bridge:git
# ovs-vsctl add-br obr0
接下来创建gre,并将新建的gre0添加到obr0,在minion1上执行以下命令:github
# ovs-vsctl add-port obr0 gre0 -- set Interface gre0 type=gre options:remote_ip=172.29.88.208
上面的remoute_ip是另外一台服务minion2上的对外IP。golang
在minion2上执行:redis
# 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上执行:docker
# 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,可选
查看这些接口的状态:json
# 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等服务。
能够本身从源码编译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
)。
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
这一步主要是为了管理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
的值重启docker
接下来重启minion一、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*
找到。
在minion一、minion2上启动kubelet、kube-proxy:
# service kubelet start # service kube-proxy start
为了方便,咱们使用Kubernetes提供的例子Guestbook(下载的源码example目录下能够找到)来演示Kubernetes管理跨机器运行的容器,下面咱们根据Guestbook的步骤建立容器及服务。在下面的过程当中若是是第一次操做,可能会有必定的等待时间,状态处于pending,这是由于第一次下载images须要一段时间。
配置管理操做都在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。
# 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
前面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隧道技术使得跨机器间容器正常通讯。
F0319 16:56:08.058335 9960 kubecfg.go:438] Got request error: The requested resource does not exist.
参考
原文连接地址:http://seanlook.com/2015/02/07/docker-kubernetes-deploy2/