我们之前聊了把 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=”|”设置到 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:
在你安装了 ansible 之后,用命令行:“ansible-version”来验证它的版本。它应该输出一个 2.X 发布版本。
Kubernetes 集群 Deployment
自动化集群配置由三个 ansible playbooks 控制。你可以点击这里拉取 playbooks,模版和代码这三个 playbooks 是:
所有的 playbooks 从一个叫做 settings.yml 的文件中获取他们输入变量,这是根据设置文件参考的。设置文件中的节点代码字典和他们的原数据(也叫做标签)在集群指定节点的名字,标签在应用程序启动的时候被注入到节点里面。这些标签在运行 playbooks 的时候,被云库存脚本用来将节点分类为 master 和 worker。比如,标签为 ansible_host_groups 的节点是 k8s_master 会被分类为 master 节点,而标签值等于 k8s_worker 会被分类为 workers。设置文件也包括名为 os_cloud_profile 的代码字典,它给 ansible 提供 nova 虚拟机启动设置。为了开启实例,如下运行 playbook:
如果一切进行顺利,你会看到所有的 Nova 实例已经在 OpenStack 云上准确无误地创建好了。这些实例会提供底层基础设施来运行 K8S 集群。在增加实例之后,你可以运行剩下的 playbooks 来部署 Docker 和 Kubernetes。在 playbook 运行的时候,使用名为‘inventory.py’库存脚本来分类节点,这样 control 和 worker 组件就会被部署到正确的虚拟机上。
按如下所示运行 playbooks:
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.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 网络,然后创建了以下映射:
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 集群。
本文转载自才云 Caicloud 公众号。
原文链接:https://mp.weixin.qq.com/s/LZefE8cRDiGlMmlGnxm2Cw
评论