Kubernetes :Launcher 基于 kubeadm 的部署工具

支持的集群类型

单主机集群

可增删主节点的高可用集群

可增删主节点的高可用集群

用途:建议用户在生产环境中使用

功能:高可用的Etcd集群;支持至少2个Master节点;支持高可用的Vespace存储;局域网内的时间同步组件;内置的镜像仓库(Alpha);应用级别的负载均衡(Alpha)等

其实,在内部实现中,为了使整个逻辑更加的简单,我们将主节点上的组件,只与本节点的apiserver联系,当然,Etcd节点为独立的模块,我们当前的Etcd节点都运行在master节点上。

我们默认增加的服务

增加时间同步模块

  1. Ufleet内置时间同步服务器Ufleet-ntpd,支持局域网环境
  2. 每个节点创建或者添加前,都会先与Ufleet-ntpd同步时间,同步完成后再执行安装任务
  3. 在前期测试中发现,如果时间不同步,将导致etcd集群极其不稳定,故建议在创建高可用集群时,优先保证各个节点时间同步
  4. 时间同步对于之后日志定位,监控等亦有重要的作用,不可忽视

集群内置traefik-ingress服务

  1. 用户创建ingress服务后,external-dns将自动获取到相关信息,并自动生成服务Host到IP的DNS记录,写入到etcd数据库中
  2. 用户通过ingress中指定的host,访问服务(用户配置:只需要将集群的Vip添加到用户使用的机器的DNS服务器地址中即可)
  3. traefik将自动做负载均衡

存储集群

  1. 新的版本,内置Vespace存储,即对于每个k8s集群,同时也是一个完整的存储集群,包含strategy策略节点,和storage存储节点

Launcher的架构设计

Launcher各模块简图及调用关系

流程介绍:

  1. 客户端通过restful 接口调用,进入sub模块
  2. 如果是删除,或者查询,直接调用Cluster的方法执行
  3. 如果是创建或者添加任务(创建集群,添加主节点,添加Node节点),则调用api模块,创建Job任务
  4. scheduler定时向api模块查询,获取任务和已注册worker(Ufleet有高可用版本,高可用版本下,同时存在多个worker,可提高多人共同使用下的创建和添加任务的效率);检查任务是否存在已过时,或者是否存在新的任务,存在则将任务分配给已注册的worker,并调用api模块写入数据库
  5. worker定时向api模块查询属于自己的任务,如果存在未执行任务,则调用任务执行

kubeadm与launcher

kubeadm的不足和可扩展

kubeadm的不足:

  1. 当前kubeadm不支持高可用集群的创建
  2. 只安装kubernetes组件

kubeadm可扩展:

  1. 对证书的处理方式很好:

如果存在证书,则读取并使用,不存在则生成,即使部分不存在也只生成对应的证书。基于此,我们生成证书时,没有生成apiserver.crt/key,因为服务器的证书中包含了主机IP或者域名,将导致apiserver证书不可重复使用,故存储也没有多大意义

  1. 可配置性好:

提供非常完整的配置项,将在下一节详细介绍。但注意,根据我们的经验,一般文档要晚于代码,对于新的特性,有时会发现文档中没有描述,这时如果是研发人员,可参考kubeadm的代码进行配置

launcher创建流程介绍

  1. 创建集群流程:

流程如右图所示,在第7步,launcher利用kubeadm完成kubernetes组件的创建。

  1. 添加Master节点流程与创建集群流程的差别:

添加Master节点流程与创建集群流程基本一致,差别在于:从数据库中获取本集群CA证书,集群已有配置,生成对应主机的证书和配置文件;往已存在etcd集群添加节点,并等待数据同步完成后开始执行安装任务。

  1. 添加Node节点流程:

从数据库中获取证书信息,根据节点和集群信息生成对应的配置文件,采用kubelet主动注册的功能,在kubelet启动后自动向集群主节点注册自己。

基于Kubeadm和我们的配置修改

Kubeadm功能介绍

kubeadm init :引导master节点的创建(to bootstrap a Kubernetes master node)

kubeadm join :添加一个worker节点到集群中(to bootstrap a Kubernetes worker node and join it to the cluster) (注:由于我们使用kubelet主动注册的功能,故这个功能我们没有用)

kubeadm upgrade :将集群升级到新的版本(to upgrade a Kubernetes cluster to a newer version)

kubeadm config :管理集群配置文件,可修改或者查看(if you initialized your cluster using kubeadm v1.7.x or lower, to configure your cluster for )

kubeadm upgrade :升级kubernetes组件

kubeadm token : 管理kubeadm join 使用的token (to manage tokens for kubeadm join) (注:由于我们使用kubelet主动注册的功能,故这个功能我们没有用)

kubeadm reset :恢复 kubeadm init或者 kubeadm join对主机的修改(to revert any changes made to this host by kubeadm init or kubeadm join)

Kubeadm 创建集群流程

对于每一步的详细内容,在下面介绍

Kubeadm 创建集群详细介绍

  1. preflight-checks:在对系统修改前,运行一系列的检查,可通过—skip-preflight-checks跳过这一步的检查(注:这里由于有对证书目录是否为空进行检查,而我们的证书是生成好以后再拷贝到主机上,并在运行时希望kubeadm加载我们的证书,故这里我们跳过了这一步的检查)
  2. generate self-signed CA: 生成一个自签名的CA证书(或者使用一个已存在的证书),在集群的各个组件中认证时使用.如果用户有指定他们自己的CA证书或者key,通过—cert-dir指定(默认为/etc/kubernetes/pki目录),则使用用户指定的证书。(注:我们就是这样做的,先把除了apiserver.crt/key外,其他的证书放到/etc/kubernetes/pki目录中,并通过参数 cert-dir,对应配置文件中的certificatesDir,指定了证书的位置,这里利用了kubeadm自动生成没有找到的证书的机制,生成apiserver.crt/key)
  3. generate kubeconfig files: 在/etc/kubernetes/目录中为kubelet,controller-manager和scheduler生成kubeconfig文件,访问API server时使用,每个组件都有他们自己的凭证;同时,也会生成一个具有管理员权限的admin.conf配置文件(注:可拷贝成~/.kube/config,执行kubectl时可读取到)
  4. dynamic kubelet config: 如果通过—feature-gates=DynamicKubeletConfig 启动了kubelet动态配置项,它将会生成kubelet init配置文件到 /var/lib/kubelet/config/init/kubelet文件中.这个功能当前默认是关闭,因为这个功能的优先级不高,但它在将来新的版本会是一个默认的选项(注:在v1.8.6版本中还不是一个默认的功能,我们当前也没有打开这个功能)
  5. generate manifests files and wait for control plane running: 生成API server, controller manager 和scheduler的manifest文件.如果没有指定etcd,则还会生成etcd的manifest文件(注:我们指定了etcd,故我们没有生成etcd.yaml文件)

静态Pod的manifests默认都要求放在/etc/kubernetes/manifests; kubelet会监视这个目录,如果有修改,或者新创建的文件,则会自动删除并启动或者创建新的静态Pod.(注:这里有个迟迟没有解决的问题,如果尝试去修改这个目录下的文件,就会发现,有时候修改后,Pod被删除了,但是新的Pod没有创建出来,这时,可以尝试将文件拷贝出这个目录,等个10几秒,然后拷贝回来,则可以正常启动静态Pod)

执行到这一步,回阻塞,但一旦控制组件起来并且正常运行,kubeadm init就会继续执行(注:这里要注意,由于google的镜像,在国内下载不了,早前的版本会一直等待,后来的版本会失败结束.如果有其他的镜像仓库,则可以通过配置项imageRepository指定为自己的镜像仓库)

  1. add labels and taints: 如果通过—feature-gates=DynamicKubeletConfig 启动了kubelet动态配置,它将会通过创建一个ConfigMap和一些RBAC规则来保证kubelet能够正常的访问,修改通过Node.spec.configSource指定的node,新创建的ConfigMap,这个功能默认关闭.(注:我们没有启用这个功能)
  2. generate bootstrap token: 添加一些标签和taints在主节点上,这样就没有额外的工作负载在这上面(注:我们当前在单主机和高可用集群中都删除了这个taint,并添加新的标签,以标记这个节点既是Master节点,又是Node节点)
  3. make node join works via join token: 生成token,使其他的Node节点可以注册到这个Master节点。可选,也可以通过—token指定(注:我们早先的版本,有使用kubeadm join的功能,这个token我们是自己生成后,通过这个配置指定,以保证多Master集群情况下,使用的是同一个token)
  4. make node join works via join token: 为了允许Node通过kubeadm join token 加入到集群,以及启动TLS,需要做的必要的配置:

1.加入所需要的全部的ConfigMap,启动相关的RBAC访问规则

2.使Bootstrap Token可以访问CSR的API

3.设置新的CSR请求自动批准

  1. install dns: 通过API server安装内部的DNS服务器(kube-dns)和kube-proxy.如果kubeadm通过–feature-gates=CoreDNS=true配置了使用CoreDNS,则CoreDNS会替代kube-dns作为默认的DNS服务安装到集群中.注意到,如果DNS服务器被部署,就不会执行调度任务直到CNI插件被安装.(注:这个特性在v1.9版本之后支持,当前我们使用的是v1.8的版本,并且由于当前我们提供的应用级别的负载均衡,通过DNS可直接访问应用的功能还处于测试阶段,我们采用kube-dns作为默认DNS,而CoreDNS作为额外的DNS服务器。这里的CNI即Container Network Interface,是指实现了CNI接口的网络插件,我们当前使用calico)
  2. change control plane if set self-hosting: 如果kubeadm init通过 alpha self-hosting功能启动了单主机的特性,(—feature-gates=SelfHosting=true),则静态Pod的控制器就会转成self-host模式,与没有指定的区别是,控制组件会有对应的leader(注:这里主要是指 kube-controller-manager和schduler,我们在单主机集群中没有启动这个选项)

Kubeadm 提供的参数

apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
api:
  advertiseAddress: <address|string>
  bindPort: <int>
etcd:
  endpoints:
  - <endpoint1|string>
  - <endpoint2|string>
  caFile: <path|string>
  certFile: <path|string>
  keyFile: <path|string>
  dataDir: <path|string>
  extraArgs:
    <argument>: <value|string>
    <argument>: <value|string>
  image: <string>
kubeProxy:
  config:
    mode: <value|string>
networking:
  dnsDomain: <string>
  serviceSubnet: <cidr>
  podSubnet: <cidr>
kubernetesVersion: <string>
cloudProvider: <string>
nodeName: <string>
authorizationModes:
- <authorizationMode1|string>
- <authorizationMode2|string>
token: <string>
tokenTTL: <time duration>
selfHosted: <bool>
apiServerExtraArgs:
  <argument>: <value|string>
  <argument>: <value|string>
controllerManagerExtraArgs:
  <argument>: <value|string>
  <argument>: <value|string>
schedulerExtraArgs:
  <argument>: <value|string>
  <argument>: <value|string>
apiServerExtraVolumes:
- name: <value|string>
  hostPath: <value|string>
  mountPath: <value|string>
controllerManagerExtraVolumes:
- name: <value|string>
  hostPath: <value|string>
  mountPath: <value|string>
schedulerExtraVolumes:
- name: <value|string>
  hostPath: <value|string>
  mountPath: <value|string>
apiServerCertSANs:
- <name1|string>
- <name2|string>
certificatesDir: <string>
imageRepository: <string>
unifiedControlPlaneImage: <string>
featureGates:
  <feature>: <bool>
  <feature>: <bool>

Kubeadm 我们当前使用的参数模板

apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
api:
  advertiseAddress: {{HOSTIP}}
  bindPort: 6443
etcd:
  endpoints:
  {{ETCD_ENDPOINTS}}
kubernetesVersion: {{KUBERNETES_VERISON}}
networking:
  dnsDomain: cluster.local
  serviceSubnet: {{SERVICE_SUBNET}}
  podSubnet: {{POD_SUBNET}}
token: {{JOIN_TOKEN}}
nodeName: {{HOSTNAME}}
certificatesDir: /etc/kubernetes/pki
imageRepository: ufleet.io/google_containers

与Kops和Kubespray的比较

Kops和Launcher的比较

  1. 对于k8s组件,kops需预安装kubectl;launcher都自带有对应版本的kubectl,所以不需要预先安装(但由于部署存储节点时需要等包,故还需要安装)
  2. kops负责基础设施流程和kubernetes的创建流程;launcher只能在已运行的linux主机上运行
  3. kops以普通服务的方式运行kubernetes;launcher以容器化方式运行kubernetes服务
  4. kops与AWS和GCP等公有云关系密切,不能在裸机上执行;launcher针对裸主机设计,对公有云环境没有要求
  5. kops通过命令行运行,同步方式;launcher是独立的服务,通过restful api交互,发送请求后立即返回,异步执行
  6. kops高可用集群中的etcd节点,允许运行在node节点上;launcher高可用集群,etcd节点都运行在master节点上
  7. kops当前已经支持升级kubernetes,可指定版本安装,可指定网络插件;launcher当前不支持升级kubernetes版本,并且不能指定版本安装,默认使用calico插件,不可配置

Kubespray和Launcher的比较

  1. kubespray属于kubernetes孵化器中的项目,通过ansible playbook来定义系统和执行kubernetes集群安装任务的流程;launcher没有依赖其他的自动化运维工具,定义自己的安装流程,只支持安装部署kubernetes集群,但针对性更强,使用也更加简单
  2. kubespray需要先配置节点名到主机IP的DNS映射;launcher支持自动按照规则修改主机名,不需额外配置
  3. kubespray以普通服务的方式运行kubernetes;launcher以容器化方式运行kubernetes服务
  4. kubespray通过命令行运行,同步方式;launcher是独立的服务,通过restful api交互,发送请求后立即返回,异步执行
  5. kubespray高可用集群中的etcd节点,允许运行在node节点上;launcher高可用集群,etcd节点都运行在master节点上
  6. kubespray当前已经支持升级kubernetes,可指定版本安装,可指定网络插件;launcher当前不支持升级kubernetes版本,并且不能指定版本安装,默认使用calico插件,不可配置

支持多版本以及自动升级的思考

支持指定k8s版本安装

历史原因:在老的版本,我们为了支持在无法连接外网的局域网内执行安装任务,内置了特定版本的kubernetes文件和镜像,支持多个版本也没有特定的需求。

目标:考虑到将来的发展,在新的launcher版本中,会将与镜像和二进制文件等部分抽离,launcher将作为类似于框架代码,在kubeadm支持下,完成对指定版本的kubernetes执行安装部署任务。

方案:当前Ufleet已经内置镜像仓库,并且我们已经实现了kubelet等组件的容器化,对于kubectl、kuberadm等,将按版本存放到某一个特定的仓库容器镜像中。launcher将定时查询并更新支持的kubernetes版本。在安装过程中,自动更新配置文件,并通过镜像仓库获取到指定版本的kubernetes组件,执行安装任务。

支持k8s版本升级

目标:我们考虑在高可用集群,支持k8s版本升级,并最小化对用户的影响。

方案:其实对我们来说,在不影响用户的正常使用情况下升级,并不是很难。我们当前已经支持主节点的增加和删除,就是考虑到将来升级的。针对主节点,将按照如下流程:主节点切换为不可调度模式->迁移运行的Pod->删除主节点->使用新版本添加主节点->切换为正常可调度模式。