Kubernetes(后面简称为K8S)作为目前热门的容器集群管理软件, 很多人都希望能一探究竟, 但是它的文档大部分都是运行在IaaS平台之上, 对于没有IaaS环境的人来说, 还是有一定门槛, 这篇文章的目的就是让你能在自己的机器上搭建一个K8S集群.
这里我采用的方式Kelsey Hightower提供的: [[https://github.com/kelseyhightower/kubernetes-fleet-tutorial][Elastic Kubernetes cluster with fleet and flannel]], 建议你先看看链接里的项目, 有一个大概的了解.
主要用到的技术栈包括: - KVM 底层虚拟化平台 - OpenVSwitch 虚拟交换机(取代bridge) - CoreOS 轻量级的操作系统, 和Docker天然集成, 独特的升级机制(和Google Chrome一样[fn:1], 不是一个一个包升级, 而是Core的完整升级, 这样能保证整个环境的一致性) - Fleet CoreOS管理集群管理工具, 就是一个配置管理工具, 当然只支持CoreOS, 类比的工具就是Chef/SaltStack一类的东西 - Flannel CoreOS提供的overlay网络软件, 为K8S提供网络服务 - Etcd CoreOS提供的配置共享/服务发现的软件 - K8S Google开源的容器集群管理软件
要完成整个配置, 你需要有一台至少8G RAM的机器, 少点估计也行, 但是建议还是多点, 毕竟要完成整个测试, 你需要有至少4个虚拟机, 如果需要跑监控的话 ,4G是打不住的.
首先我们需要准备好的是我们的底层虚拟化环境, 其实没有想象的那么麻烦, 配置还是比较方便的.
虚拟化环境准备
我们这里测试机器用的是操作系统是Ubuntu 14.04, 最小安装即可, 配置好SSH, 如果你没有物理机, 也可以用虚拟机代替, 没啥影响, 保证虚拟机网络通就行.
- 安装虚拟化支持软件:
apt-get install qemu-kvm openvswitch-switch
配置OpenVSwitch
$cat /etc/network/interface
auto ovsbr0
iface ovsbr0 inet static
address 172.16.11.1
network 172.16.11.0
netmask 255.255.255.0
broadcast 172.16.11.255
$ovs-vsctl add-br ovsbr0
$cat /etc/openvswitch/ovs-ifup
#!/bin/sh
switch='ovsbr0'
/sbin/ifconfig $1 0.0.0.0 up
ovs-vsctl add-port ${switch} $1
$cat /etc/openvswitch/ovs-ifdown
#!/bin/sh
switch='ovsbr0'
/sbin/ifconfig $1 0.0.0.0 down
ovs-vsctl del-port ${switch} $1
添加一个bridge ,然后添加到OVS里面, 同时编写两个脚本, 后面KVM命令行启动虚拟机要用到.
- 完成之后, 配置机器路由转发功能, 首先打开转发功能, 编辑/etc/sysctl.conf, 打开ip_forward=1那一行, 同时让配置生效:
- sysctl -p
配置iptables:
iptables -I FORWARD -j ACCEPT
iptables -t nat -A POSTROUTING -s 172.16.11.0/24 ! -d 172.168.11.0/24 -j MASQUERADE
对所有172.16.11.0/24网段出去的流量做NAT, 同时转发所有的包. 这里只是测试环境, 所以我打开了所有包的转发, 当然你也能细粒度控制包转发.
完成上面两步只后, 你还需要有一个dhcp server, 为你创建的虚拟机提供DHCP服务, 可以用dnsmasq这个软件来完成.
$apt-get install dnsmasq-base
$cat resolv.dnsmasq.conf
# Google's nameservers, for example
nameserver 8.8.8.8
nameserver 8.8.4.4
$dnsmasq --bind-interfaces --listen-address 172.16.11.1 --dhcp-range 172.16.11.100,172.16.11.200 --resolv-file=/home/tacy/coreos/config/resolv.dnsmasq.conf --dhcp-boot=pxelinux.0,roo,172.16.11.2
这样, 一个基本的虚拟化环境就差不多了.
配置etcd
这里需要简单介绍一下CoreOS这个操作系统, 不像其他传统的Linux发行版本, 他没有包管理机制, 只包括核心的一些软件, 升级操作是原子的, 不存在不一致问题.
当运行系统检测到CoreOS有升级包的时候, 他会根据你选定的升级策略, 自动下载升级包更新, 但是他不会更新你当前正在运行的根分区(/usr被mount为只读), 因为他存在两个根分区, Active/Passive, 他升级的是Passive这个分区, 重启的时候, 切换Passive成为Active, 完成升级操作.
CoreOS和Docker无缝集成, 预安装Docker, 如果你需要在CoreOS里面跑其他诊断工具, 比如tcpdump, 他通过创建一个fedora container来完成[fn:2].
CoreOS采用Systemd做服务管理, Systemd也是目前热门的服务管理工具, 不远的将来会替换SysV和Upstart, 好处就是服务并行启动和好的依赖设计. 结合Cloud-Config, 解决CoreOS系统的预配置工作, 不依赖其他配置管理工具, 比如Chef等.
同时CoreOS提供Fleet管理工具来管理CoreOS集群, Fleet通过发布Systemd Unit到集群成员, 实现服务配置.
当然, CoreOS的整个体系中, 我们将要配置的Etcd无疑是最重要的一个, Etcd负责配置管理和服务发现, 是CoreOS的核心. K8S也用Etcd.
介绍完了CoreOS, 我们来看怎么配置一个运行Etcd service的CoreOS.
- Kelsey Hightower已经帮你编写好了Cloud-Config文件, 你需要先Clone一下上面提到的项目.
git clone https://github.com/kelseyhightower/kubernetes-fleet-tutorial.git
找到config目录下的etcd.yml文件, 根据你的实际情况, 修改里面的ip地址信息, 我测试环境用的是172.16.11.10, 做相应修改, 同时吧ssh_authorized_keys替换成你的的公钥. 另外需要特别注意的是, 这里的网卡名称需要修改一下, 不是eno16777736, 改成eth0.
- 然后我们还需要下载一个CoreOS的KVM image. 去 [[https://coreos.com/docs/running-coreos/platforms/qemu/][这里]] 下载:
wget http://stable.release.core-os.net/amd64-usr/current/coreos_production_qemu_image.img.bz2 -O - | bzcat > coreos_production_qemu_image.img
准备好之后, 我们就可以来启动一个etcd服务的CoreOS虚拟机了. 由于我们需要使用Cloud-Config文件启动, 你需要把配置文件放置到一个特定的位置, 我们先创建一个目录:
$mkdir -p tmp/openstack/latest
$cp etcd.yml tmp/openstack/latest/user_data
也就是把我们修改好的etcd.yml文件更名为user_data, 放置到/tmp/openstack/lastest目录下, 目录名的后两层不能修改, tmp可以任意. 这样CoreOS才能找到配置文件.
- 现在可以启动etcd server了, 在启动之前, 我们还做最后一步工作, 用coreos_production_qemu_image.img做base image, 建立一个新的img文件来写入etcd server的修改:
- $qemu-img create -f qcow2 -b ../template/coreos.img etcd.img
这里我重命名coreos_production_qemu_image.img为coreos.img, 启动etcd server:
$kvm -m 2048 -smp 2 \
-drive file=../images/etcd.img,if=virtio,cache=none,aio=native \
-fsdev local,id=conf,security_model=none,readonly,path=../config/etcd \
-device virtio-9p-pci,fsdev=conf,mount_tag=config-2 \
-net nic,vlan=0,model=virtio,macaddr=52:54:00:24:93:29\
-net tap,vlan=0,vhost=on,script=/etc/openvswitch/ovs-ifup,downscript=/etc/openvswitch/ovs-ifdown \
-nographic -serial file:../logs/etcd.log
我的目录结构如下, Cloud-Config文件统一放到config目录, images下面存放虚拟机镜像, 虚拟机启动日志存放到logs目录:
|-coreos
|---bin
|---config
|-----etcd
|-------openstack
|---------latest
|-----node
|-------openstack
|---------latest
|---example
|-----guestbook
|---images
|---logs
|---temp
|---template
|---tools
|---units
好了, 现在你的etcd server就配置好了, 当然你还需要配置两个环境变量:
$ export ETCDCTL_PEERS=http://172.16.11.10:4001
$ export FLEETCTL_ENDPOINT=http://172.16.11.10:4001
- 同时在etcd创建flannel网络节点:
- $etcdctl mk /coreos.com/network/config ‘{“Network”:“10.0.0.0/16”}’
用fleetctl验证一下:
tacy@momo1:~/coreos$ fleetctl list-machines
MACHINE IP METADATA
6a6e4c00... 172.16.11.10 role=etcd
现在你可以继续配置其他虚拟机节点了, 这些节点用来运行K8S
配置其他Node
- 同样你需要基于coreos.img创建相应的img, 修改node.yml配置文件, 修正etcd server地址, 同时注意, node.yml里面的K8S下载用的是Google的云存储空间, 国内访问不了, 而且版本也是老的, 建议自己下载最新版本, 放置到本地, 如果你没有相关服务, 也可以临时用下面命令应急:
$python -m SimpleHTTPServer
配置完成之后验证一下:
tacy@momo1:~/kubernetes/server/kubernetes/server/bin$ fleetctl list-machines
MACHINE IP METADATA
127d44a9... 172.16.11.165 role=kubernetes
6a6e4c00... 172.16.11.10 role=etcd
7b0826fb... 172.16.11.132 role=kubernetes
bb8edb08... 172.16.11.178 role=kubernetes
可以看到我这里创建了3个节点, 创建节点相关工作比较繁琐, 我写了一个脚本来完成:
tacy@momo1:~/coreos/bin$ cat microcloud.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import random
import pickle
import argparse
import subprocess
parser = argparse.ArgumentParser(description='MicroCloud Ctl')
parser.add_argument('-i',
'--image',
help='kvm node name',
required = True)
parser.add_argument('-c',
'--clouddriver',
help='cloud config driver directory',
required = True)
args = parser.parse_args()
img = args.image
cdf = args.clouddriver
pypath = os.path.dirname(os.path.abspath(__file__))
node = img.split('/')[-1]
macaddrfile = '{0}/../config/macaddr.list'.format(pypath)
if not os.path.isfile(macaddrfile):
with open(macaddrfile, 'wb+') as fd:
pickle.dump({}, fd)
macs = {}
with open(macaddrfile, 'rb+') as fd:
macs = pickle.load(fd)
mac = ''
if node in macs:
mac = macs[node]
else:
rands = [ 0x52, 0x54, 0x00,
random.randint(0x00, 0x7f),
random.randint(0x00, 0xff),
random.randint(0x00, 0xff) ]
mac = ':'.join(map(lambda x: "%02x" % x, rands))
macs[node] = mac
with open(macaddrfile, 'wb+') as fd:
pickle.dump(macs, fd)
args = ['qemu-system-x86_64', '-enable-kvm',
'-m', '2048',
'-smp', '2',
'-drive', 'file={0},if=virtio,cache=none,aio=native'.format(img),
'-fsdev', 'local,id=conf,security_model=none,readonly,path={0}'.format(cdf),
'-device', 'virtio-9p-pci,fsdev=conf,mount_tag=config-2',
'-net', 'nic,vlan=0,model=virtio,macaddr={0}'.format(mac),
'-net', 'tap,vlan=0,vhost=on,script=/etc/openvswitch/ovs-ifup,downscript=/etc/openvswitch/ovs-ifdown',
'-nographic',
'-serial', 'file:{0}/../logs/{1}.log'.format(pypath, node)]
#os.execv('/usr/bin/qemu-system-x86_64', args)
process = subprocess.Popen(['/usr/bin/qemu-system-x86_64'] + args[1:],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
#stdout, stderr = process.communicate()
#print stdout, stderr
print process.returncode
有了这个脚本之后, 新建一个节点只需要创建一下img, 然后运行这个脚本就行了.
通过fleet部署K8S
CoreOS部署好之后, 你现在可以用fleet完成K8S的部署, 使用Kelsey Hightower提供的units, 直接用下面命令完成部署:
$ fleetctl start kube-proxy.service
$ fleetctl start kube-kubelet.service
$ fleetctl start kube-apiserver.service
$ fleetctl start kube-scheduler.service
$ fleetctl start kube-controller-manager.service
同样的问题, 这里下载地址都是Google存储, 国内无法访问, 而且版本也是老的, 建议自己下载最新版本和配置下载地址.
完成之后, 你就有了一个本地运行的K8S集群.
需要特别注意的是, 由于K8S是一个快速变化的项目, 很多信息容易过期, 建议去关注项目的Issue列表, 由于0.5版本的K8S里面的服务重新设计, Kelsey Hightower文档中相关信息就不对, 注意看CoreOS的日志.
现在可以去部署Guestbook这个例子了.
部署Guestbook
直接下载最新发布的K8S版本, 解压缩之后里面有example/guestbook目录, 这就是我们需要部署的例子, guestbook部署包括一个redis master, 2个redis slave和3个web节点.
部署起来也和很简单, 直接通过kubectl工具即可完成:
kubectl create -f redis-master.json
kubectl create -f redis-master-service.json
kubectl create -f redis-slave-controller.json
kubectl create -f redis-slave-service.json
kubectl create -f frontend-controller.json
看看结果:
tacy@momo1:~$ kubectl get pods
NAME IMAGE(S) HOST LABELS STATUS
8f85c3e8-7481-11e4-8c27-52540015fd4a brendanburns/redis-slave 172.16.11.132/ name=redisslave,uses=redis-master Running
8f85ff95-7481-11e4-8c27-52540015fd4a brendanburns/redis-slave 172.16.11.165/ name=redisslave,uses=redis-master Running
4229efd3-751b-11e4-8c27-52540015fd4a brendanburns/php-redis 172.16.11.132/ name=frontend,uses=redisslave,redis-master Running
422ac57d-751b-11e4-8c27-52540015fd4a brendanburns/php-redis 172.16.11.178/ name=frontend,uses=redisslave,redis-master Running
422bd487-751b-11e4-8c27-52540015fd4a brendanburns/php-redis 172.16.11.165/ name=frontend,uses=redisslave,redis-master Running
redis-master dockerfile/redis 172.16.11.165/ name=redis-master Running
这样就把guestbook部署完成了. 你在宿主机上可以直接通过80端口访问
部署监控
K8S的监控可以用cAdvisor + heapster + Influxdb + Grafana来实现. 下面我们来说说怎么去部署这样一个方案
首先需要做的是在每一个minions上运行cAdvisor作为一个Pod, 可以通过[[https://github.com/GoogleCloudPlatform/kubernetes/blob/master/cluster/saltbase/salt/cadvisor/cadvisor.manifest#L1][static manifest file]]来实现, 我们这里需要修改一下node节点的
K8S存在的问题
服务对外暴露问题
对于服务的对外暴露问题, service提供的是一个虚的地址, 这对于内部路由没有问题(kube-proxy+iptables), 但是对于需要对外暴露的服务, 比如guestbook里面的web服务, 就没有很好的办法了(在GCE上是有办法解决的, 会判断service的createExternalLoadBalancer属性, 建立LB)
相关Issue: [[https://github.com/GoogleCloudPlatform/kubernetes/issues/1161][#1161]]
服务注入问题
需要解决Cluster内部Pod依赖外部服务问题, 比如集群间, 不同命名空间, 数据库等,
相关Issue: [[https://github.com/GoogleCloudPlatform/kubernetes/issues/1542][#1542]], [[https://github.com/GoogleCloudPlatform/kubernetes/issues/260][#260]], [[https://github.com/GoogleCloudPlatform/kubernetes/issues/2358][#2358]]
持久化数据问题
相关Issue: [[https://github.com/GoogleCloudPlatform/kubernetes/pull/1515][#1515]]
部署中碰到的问题
etcd
etcd 0.5之前的版本使用的go-raft实现有问题, 会导致etcd起不来. 0.5版本之后, etcd实现了自己版本的raft, 解决了该问题, 建议用0.5之后的版本.
fleetctl
fleetctl确实非常方便, 但是也有一些问题, 参考这里[fn:3][fn:4], 对于服务的管理还有bug, 期待更稳定
kube-register
老版本有bug, 会有too much open files问题, 需要自己编译
Footnotes
[fn:1] [[https://code.google.com/p/omaha/][Google Omaha]]
[fn:2] [[https://coreos.com/docs/cluster-management/debugging/install-debugging-tools/][Install Debugging Tools]]
[fn:3] [[https://github.com/coreos/fleet/issues/914][How to update unit?]]
[fn:4] [[https://github.com/coreos/fleet/pull/866][fleet agent does not compare contents of units in reconciler]]