我们之前聊了把OpenStack跑在K8S上,如何基于Kubernetes在TCP云端创建私有云解决方法,运用在生产或在OpenStack启动虚拟化。今天换个姿势,我们来看看如何在OpenStack虚拟机上运行Kubernetes集群。
最近的Austin OpenStack峰会上,参会者中对容器感兴趣的人数之多,让我印象深刻。几乎所有跟容器相关的会议现在都意识到了它的优点。通过将应用程序容器化,就能够将主机操作系统虚拟化。这也就意味着,你可以在主机操作系统中为每个容器创建隔离环境,比如文件系统,网络堆栈和进程空间,令容器之间互相可见。此外,容器又是轻量级、轻便的,不仅仅跨操作系统,还可以跨云端。这些功能令开发者可以快速构建,部署,运输和扩容应用程序,而这些动作在虚拟机环境中是不可能实现的。
在峰会上,我有幸将自己参与的一个对我们团队有很大帮助的项目呈现给观众们看。可以点击这里查看我的presentation:这里。
这个项目是为了在我们的OpenStack Kilo 环境中建立起一个自动Kubernetes部署。在这篇博客帖中,我会描述我们的解决方法,并且在Github上面提供代码回购的总览。你可以利用其中一些代码来创建你自己的Kubernetes集群自动部署。记住,软件只在开发环境被测试,对于任何生产部署,都要确认你跟往常一样进行了必要的尽职调查。
首先要回答的问题就是kubernetes和ansible是什么,为什么要选择他们?
Kubernetes(K8S)是一个通过调用API来编排和管理Docker容器的平台。除了基本的编排功能,它还有持续驱动控制进程的功能,面向用户指定所需的状态。当使用这个平台的时候,你能将你的应用程序容器分组到一个叫pod的组合单元。pod是一个分享网络和存储的容器组。当你创建Docker容器的时候,默认设置下,每个容器都会获得自己的网络命名空间也就是它自己的TCP/IP堆栈。Kubernetes用-net=”<container-name>|<container-id>”设置到Docker里,以此将所有的pod容器的网络空间结合到一起。这个设置令容器可以再次使用另一个容器的网络堆栈。K8S通过创建一个pod层面保持容器和它自己的网络堆栈来完成,所有的pod容器被配置来重新使用保持容器的网络空间。
在pod层面,Kubernetes提供各种services,比如调度,副本,自我修复,监控,命名/发现,身份识别,验证授权等等。Kubernetes也有可以让开发者写自己模块的插件模版,然后在这个平台上面创建services。就像这篇博客写得,Kubernetes是最先进的、可编写和管理Docker容器的开源平台之一。
我们选择Ansible是因为它是当下最火、最直接、最容易使用的自动化平台之一。它运行代理较少,在基础架构上使用ssh来登录系统,执行你在playbook文件中描述的策略。这些策略被模式化为一个yaml格式的任务清单。在没有自动化的时候,这些就是必须由管理员来执行部署基础设施软件的手动任务。
这篇博客帖描述了在OpenStack虚拟机上面运行的Kubernetes集群。一个K8S集群有一个master节点,这个节点运行API server和一套运行在pod容器上的worker节点。设置使用的是Ansible(>2.0),Ubuntu和Neutron网络。测试后,使用OpenStack kilo来发布。Ansible部署K8S软件组件,启动虚拟机,将虚拟机分类到master和worker节点,然后部署Kubernetes密钥清单。我们使用neutron来给OpenStack虚拟机和K8S pod容器提供网络连接。所有在测试环境中的虚拟机都运行Ubuntu 14.04 服务器操作系统。
下图展示的是运行中的各种软件组件,以及他们是如何在集群中交互的。我会把这个图表作为资料来阐述自动进程,当你浏览这篇博客的时候,看到这个框图就会觉得说得通了。
设置
这个设置是假设你已经有一个OpenStack云运行核心services,比如Nova,Neutron,Glance和Keystone。你还需要>2.X的ansible版本在一个有凭证和网络连通ssh到计算节点和虚拟机。这个ansible节点也需要能够访问openStack API。我在我的Macbook上面用这些命令安装了ansible:
sudo easy_install pip
sudo pip install ansible
在你安装了ansible之后,用命令行:“ansible-version”来验证它的版本。它应该输出一个2.X发布版本。
Kubernetes集群Deployment
自动化集群配置由三个ansible playbooks控制。你可以点击这里拉取playbooks,模版和代码:这里。这三个playbooks是:
· launch-instances.yml — launches kubernetescluster instances
· deploy-docker.yml — deploys docker onall of the cluster instances
· deploy-kubernetes.yml — deploys kubernetescontrol and worker software components and brings up the cluster
所有的playbooks 从一个叫做settings.yml的文件中获取他们输入变量,这是根据设置文件参考的。设置文件中的节点代码字典和他们的原数据(也叫做标签)在集群指定节点的名字,标签在应用程序启动的时候被注入到节点里面。这些标签在运行playbooks的时候,被云库存脚本(这里)用来将节点分类为master和worker。比如,标签为ansible_host_groups的节点是k8s_master会被分类为master节点,而标签值等于k8s_worker会被分类为workers。设置文件也包括名为os_cloud_profile的代码字典,它给ansible提供nova虚拟机启动设置。为了开启实例,如下运行playbook:
ansible-playbook -i hosts launch-instances.yml.
如果一切进行顺利,你会看到所有的Nova实例已经在OpenStack云上准确无误地创建好了。这些实例会提供底层基础设施来运行K8S集群。在增加实例之后,你可以运行剩下的playbooks来部署Docker和Kubernetes。在playbook运行的时候,使用名为‘inventory.py’库存脚本来分类节点,这样control和worker组件就会被部署到正确的虚拟机上。
按如下所示运行playbook:
ansible-playbook -i scripts/inventory.py deploy-docker.yml
ansible-playbook -i scripts/inventory.py deploy-kubernetes.yml
K8S集群的控制面板包括了API服务器,调度器,etcd数据库和kube controller manager通过一个master密钥清单文件。这个文件名为master-manifest.j2可以在模版文件夹里面找到。K8S控制面板软件的版本是由设置文件决的。这个名为deploy-kubernetes.yml的playbook是第一次下载和部署kubelet和kube-proxy二进制,并且在所有节点上开启这两个services。然后master-manifest模版文件就会在master节点上被部署到一个叫做/etc/kubernetes/manifest的config目录。这个目录被kubelet daemon进程监视,它开启了所有的提供控制面板services的Docker容器。当你使用Docker ps命令的时候,你会看到kube-apiserver,kube-controller-manager,etcd和kube-schedules进程在master节点里运行在他们自己的容器上。
API服务器被配置来使用HTTPS服务API。SSL所需的证书是通过将make-ca-cert.sh脚本作为playbook任务之一来运行生成的。这个脚本在每个节点上的证书目录中生成了以下证书。这个在每个节点上都有生成,因为Docker daemon也使用相同的服务器证书来配置TLS。cert文件目录值在设置文件中也是可配置的。
ca.pem——自签CA证书
Server.crt/server.key——签署的kube服务器端证书和它的密钥文件。这个cert文件也可以被Docker Daemon进程用来确保客户端安全访问。
cert.pem/key.pem——签署的客户端证书和它的密钥文件。kubectl和docker客户使用。
在客户机上面,你可以在repo里面找到这些certs文件夹。在客户机里用convention<nodename>.env为每个节点都创建了Docker环境文件。你可以追踪这个环境变量的来源,然后运行Docker客户端而不是Docker主机。比如,为了在名为master1的master节点上运行Docker命令,第一步就是执行“source master1.env”,然后运行命令。同样,对于kubectl客户端来说,config文件是由必要的凭证和集群master IP地址来创建的。Config文件可以在$HOME/.kube/config中找。这样你可以在集群上的终端窗口运行kubectl命令。
在这篇博客帖中,我会描述如何使用OpenStack neutron service来连接K8S pods。这跟GCE的设置有些相似。其实也可以选择其他的,比如Flannel,使用UDP封装在现有租户neutron中为路由pod创建一个覆盖网络选项。使用neutron为pod网络删除这个为容器覆盖又覆盖的网络构架。
要重点注意的是在K8S中每个pod(也就是一组容器)都有一个IP地址。这就区别于在Docker中的网络模版,在这里每个容器有主机的私有IP地址。为了让K8S网络运行起来,pod的IP地址必须是不需要NAT的,可路由的。这也就意味着两件事情:
a)当一个pod容器与其它pod中的容器交流的时候,通信必须是直接路由,不需要NAT的。
b)当一个pod容器与虚拟机的IP地址交流的时候,通信必须是直接路由,不需要NAT的。
为了完成以上目的,第一步就是,在每个节点中名为docker0的默认docker桥被一个名为cbr0的Linux桥所替代。跨过所有节点,一个IP模块被分配给pod网络,比如说/16。这个模块被抽象化了,节点到pod的映射被创建在一个设置文件里。在以上图表中,我把10.1.0.0/16分配给pod网络,然后创建了以下映射:
node1 : 10.1.1.1/24
node2: 10.1.2.1/24
nodeN: 10.1.n.1/24
create-bridge.sh(create-bridge.sh)脚本创建cbr0,然后使用在设置文件中定义好的映射来配置pod子网络的IP地址。
第二步就是配置租户路由器到路由流量,再到pod子网络。比如在以上的框图中,租户路由器肯定是被配置到路径中,再配置流量到pod子网络10.1.1.0/24,配置到位于private-subnet#1上node#1的以太网络地址。同样的,路径必须添加在集群中指向每个节点的目的站来路由流量到pod网络。使用add_neutron_routes.py脚本完成这个步骤。
第三步就是添加IP tables规则到冒充流量,从pod子网络到为出站连接的网络。这是因为neutron租户路由器不知道它需要从pod子网络SNAT流量。
最后一步就是打开在每个节点的Linux内核上的IP转发,到路径包,再到网桥容器网络。这些任务都由playbook deploy-kubernetes.yml执行的。
运行这个playbook的最终结果就是,neutron网络现在被编程来进行pods到网络间的路由通信。
注意:默认状态下,作为一个抗欺骗安全措施,neutron在超管理器上安装iptables防火墙规则,来控制流量在虚拟机端口的来去。所以,当路由流量注入pod网络到虚拟机的端口,它是被超管理器防火墙过滤过的。所幸,有一个叫做AllowedAddressPairs的neutron扩展,它允许如pod子网络的Havana发布版本,来通过虚拟机监控程序防火墙。
暴露Pod Services
出于实用性目的,每个pod必须放在服务抽象的前面。这个服务使用可以连接到pod容器里面运行的应用程序,来提供稳定的IP地址。这是因为pod能够在任意节点上被调度,而且可以从分配好的node_pod_cidr范围获取任意IP地址。同样的,当你扩展/缩减这些pods来容纳流量变化,或者当运行失败的pods通过平台再次创建,他们的IP地址就会改变。从客户角度来看,服务抽象要确保pods的IP地址保持固定。要重点注意的是,对于服务来说,CIDR,也就是cluster_cidr,只在每个节点本地存活,并不需要被neutron租户路由器路由。这个服务IP流量被K8S用proxy功能(kube-proxy)分布到备份pod,proxy功能通常用iptables在每个节点中实施。
这个稳定的service IP可以用Kubernetes的NodePort性能暴露到外面。节点端口所做的事情就是,它使用worker节点的IP地址和一个高TCP端口31000,来暴露服务IP地址和端口到外部。所以如果你分配一个浮动IP到节点,应用程序会在那个IP和它的节点IP提供流量。如果你使用一个neutron负载平衡器,那就添加worker节点成员,编写vip分布流量到节点端口。这个方法在以上框图中已经阐述。
服务发现
服务发现可以使用DNS集群add-on服务实现完全自动化。可以使用skydns-manifest和skydns-service来部署。K8S会自动给每个在集群中定义的服务分配一个DNS名字。所以运行在pod里面的程序可以查找集群DNS服务器来解决服务名称和位置。集群DNS服务支持A和SRV记录查找。
结论
我希望这篇博客帖子阐明了如何用Ansible在OpenStack上创建Kubernetes集群。
(如果需要转载,请联系我们哦,尊重知识产权人人有责;)