本文来自Rancher Labs前端
在Kubernetes 1.14版本中已经GA了对Windows的支持。这一结果凝结了一群优秀的工程师的努力,他们来自微软、Pivotal、VMware、红帽以及如今已经关闭的Apprenda等几家公司。我在Apprenda工做时,不定时会为sig-windows社区作出一些贡献。即使如今在Rancher Labs任职,也一直关注它的动向。因此当公司决定在Rancher中增长对Windows支持时,我极为兴奋。node
Rancher 2.3已于本月月初发布,这是首个GA支持Windows容器的Kubernetes管理平台。它极大下降了企业使用Windows容器的复杂性,并为基于Windows遗留应用程序的现代化提供快捷的途径——不管这些程序是在本地运行仍是在多云环境中运行。此外,Rancher 2.3还能够将它们容器化并将其转换为高效、安全和可迁移的多云应用程序,从而省去重写应用程序的工做。linux
在本文中,咱们将在运行RKE的Kubernetes集群之上配置Rancher集群。咱们也将会配置同时支持Linux和Windows容器的集群。完成配置后,咱们将聊聊操做系统的定位,由于Kubernetes scheduler须要指导各类Linux和Windows容器在启动时的部署位置。android
咱们的目标是以彻底自动化的方式执行这一操做。因为Rancher 2.3目前还没有stable,所以这没法在生产环境中使用,但若是你正须要使用Azure和Rancher来完成基础架构自动化,那么这对大家的团队而言将会是一个良好的开端。退一万步而言,即便你不使用Azure,在本例中的许多概念和代码均可以应用于其余环境中。nginx
在咱们正式开始以前,我须要告知你许多注意事项和“陷阱”。首先,最显而易见的就是:Windows不是Linux。Windows新增了支持网络网格中的容器化应用程序所需的子系统,它们是Windows操做系统专有的,由Windows主机网络服务和Windows主机计算服务实现。操做系统以及底层容器运行时的配置、故障排除以及运维维护将会有显著区别。并且,Windows节点收到Windows Server licensing的约束,容器镜像也受Windows容器的补充许可条款的约束。git
WindowsOS版本须要绑定到特定的容器镜像版本,这是Windows独有的。使用Hyper-V隔离能够克服这一问题,可是从Kubernetes 1.16开始,Kubernetes不支持Hyper-V隔离。所以,Kubernetes和Rancher仅能在Windows Server 1809/Windows Server 2019以及Windows Server containers Builds 17763 以及Docker EE-basic 18.09以前的版本中运行。github
Kubernetes 1.16版本以来,CSI插件支持alpha版本。Windows节点支持大量的in-tree和flex volume。docker
Rancher 支持仅限于flannel提供的主机网关(L2Bridge)和VXLAN(Overlay)网络支持。在咱们的方案中,咱们将利用默认的VXLAN,由于当节点并不都在同一个网络上时,主机网关选项须要User Defined Routes配置。这取决于提供商,因此咱们将利用VXLAN功能的简单性。根据Kubernetes文档,这是alpha级别的支持。当前没有支持Kubernetes网络策略API的开源Windows网络插件。bootstrap
请确保你已经阅读了Kubernetes文档,由于在Windows容器中有许多功能没法实现,或者其功能和Linux中的相同功能实现方式有所不一样。windows
Kubernetes文档:
https://kubernetes.io/docs/setup/production-environment/windows/intro-windows-in-kubernetes/#limitations
自动化是实现DevOps的第一种方式。咱们将自动化咱们的Rancher集群和要在该集群中配置的Azure节点的基础架构。
Terraform是一个开源的基础架构自动化编排工具,它几乎支持全部市面上能见到的云服务提供商。今天咱们将使用这一工具来自动化配置。确保你运行的Terraform版本至少是Terraform 12。在本文中,使用Terraform 版本是v0.12.9。
$ terraform version Terraform v0.12.9
用于Terraform 的RKE provider是一个社区项目,并不是由Rancher官方进行研发的,但包括我在内的Rancher的不少工程师都在使用。由于这是一个社区provider而不是Terraform官方的provider,所以你须要安装最新版本到你的Terraform插件目录中。对于大部分的Linux发行版来讲,你可使用本文资源库中包含的setup-rke-terraform-provider.sh
脚本。
用于Terraform的Rancher2 provider是Terraform支持的provider,它经过Rancher REST API来自动化Rancher。咱们将用它从Terraform的虚拟机中建立Kubernetes集群,这一虚拟机须要使用Azure Resource Manager和Azure Active Directory Terraform Provider进行建立。
本文中的Terraform模型的每一个步骤都会被拆分红子模型,这将加强模型可靠性而且未来若是你建立了其余自动化架构,这些模型均可以从新使用。
Azure Resource Manager和Azure Active Directory Terraform Provider将使用一个激活的Azure Cli登陆以访问Azure。他们可使用其余认证方法,但在本例中,我在运行Terraform以前先登陆。
az login Note, we have launched a browser for you to login. For old experience with device code, use "az login --use-device-code" You have logged in. Now let us find all the subscriptions to which you have access... [ { "cloudName": "AzureCloud", "id": "14a619f7-a887-4635-8647-d8f46f92eaac", "isDefault": true, "name": "Rancher Labs Shared", "state": "Enabled", "tenantId": "abb5adde-bee8-4821-8b03-e63efdc7701c", "user": { "name": "jvb@rancher.com", "type": "user" } } ]
Azure Resource Group是Rancher集群的节点和其余虚拟硬件存储的位置范围。咱们实际上将会建立两个组,一个用于Rancher集群,另外一个用于Kubernetes集群。那将在resource-group module
中完成。
https://github.com/JasonvanBrackel/cattle-drive/tree/master/terraform-module/resourcegroup-module
resource "azurerm_resource_group" "resource-group" { name = var.group-name location = var.region }
虚拟网络
咱们将须要一个虚拟网络和子网。咱们将使用network-module
在各自的资源组中分别设置它们。
咱们将使用node-module设置每一个节点。既然每一个节点都须要安装Docker,那么咱们在使用Rancher install-docker脚本配置和安装Docker时,咱们须要运行cloud-init文件。这个脚本将检测Linux发行版而且安装Docker。
os_profile { computer_name = "${local.prefix}-${count.index}-vm" admin_username = var.node-definition.admin-username custom_data = templatefile("./cloud-init.template", { docker-version = var.node-definition.docker-version, admin-username = var.node-definition.admin-username, additionalCommand = "${var.commandToExecute} --address ${azurerm_public_ip.publicIp[count.index].ip_address} --internal-address ${azurerm_network_interface.nic[count.index].ip_configuration[0].private_ip_address}" }) }
#cloud-config repo_update: true repo_upgrade: all runcmd: - [ sh, -c, "curl https://releases.rancher.com/install-docker/${docker-version}.sh | sh && sudo usermod -a -G docker ${admin-username}" ] - [ sh, -c, "${additionalCommand}"]
模板中的附加命令块用这些节点的sleep 0填充,可是稍后该命令将用于Linux节点,以将Rancher管理的自定义集群节点加入平台。
设置节点
接下来,咱们将为每一个角色建立几组节点:控制平面、etcd和worker。咱们须要考虑几件事,由于Azure处理其虚拟网络的方式有一些独特之处。它会保留前几个IP供本身使用,所以在建立静态IP时须要考虑这一特性。这就是在NIC建立中的4个IP,因为咱们也管理子网的IP,所以咱们在每一个IP中都进行了处理。
resource "azurerm_network_interface" "nic" { count = var.node-count name = "${local.prefix}-${count.index}-nic" location = var.resource-group.location resource_group_name = var.resource-group.name ip_configuration { name = "${local.prefix}-ip-config-${count.index}" subnet_id = var.subnet-id private_ip_address_allocation = "static" private_ip_address = cidrhost("10.0.1.0/24", count.index + var.address-starting-index + 4) public_ip_address_id = azurerm_public_ip.publicIp[count.index].id } }
在建立并彻底配置节点以前,Azure的Terraform provider将没法获取IP地址。而经过静态处理,咱们能够在生成RKE集群期间使用地址。固然,还有其余方法也能解决这一问题,如将基础架构配置分红多个来运行。可是为简单起见,仍是对IP地址进行静态管理。
设置前端负载均衡器
默认状况下,Rancher安装程序将会在每一个worker节点安装一个ingress controller,这意味着咱们应该在可用的worker节点之间负载均衡任何流量。咱们也将会利用Azure的功能来为公共IP建立一个公共的DNS入口,而且将其用于集群。这能够在loadbalancer-module
中完成。
https://github.com/JasonvanBrackel/cattle-drive/tree/master/terraform-module/loadbalancer-module
resource "azurerm_public_ip" "frontendloadbalancer_publicip" { name = "rke-lb-publicip" location = var.resource-group.location resource_group_name = var.resource-group.name allocation_method = "Static" domain_name_label = replace(var.domain-name-label, ".", "-") }
做为替代方案,其中包含使用cloudflare DNS的代码。咱们在这篇文章中没有使用这一方案,可是你能够不妨一试。若是你使用这个方法,你将须要重置DNS缓存或主机文件入口,以便你的本地计算机能够调用Rancher来使用Rancher terraform provider。
provider "cloudflare" { email = "${var.cloudflare-email}" api_key = "${var.cloudflare-token}" } data "cloudflare_zones" "zones" { filter { name = "${replace(var.domain-name, ".com", "")}.*" # Modify for other suffixes status = "active" paused = false } } #Add a record to the domain resource "cloudflare_record" "domain" { zone_id = data.cloudflare_zones.zones.zones[0].id name = var.domain-name value = var.ip-address type = "A" ttl = "120" proxied = "false" }
使用RKE安装Kubernetes
咱们将使用Azure和Terraform的动态代码块建立的节点与开源RKE Terraform Provider来建立一个RKE集群。
dynamic nodes { for_each = module.rancher-control.nodes content { address = module.rancher-control.publicIps[nodes.key].ip_address internal_address = module.rancher-control.privateIps[nodes.key].private_ip_address user = module.rancher-control.node-definition.admin-username role = ["controlplane"] ssh_key = file(module.rancher-control.node-definition.ssh-keypath-private) } }
有不少种方式能够安装Tiller,你可使用Rancher官方文档中的方法,可是在本教程中咱们将利用RKE Add-On的特性。
官方文档:
https://rancher.com/docs/rancher/v2.x/en/installation/ha/helm-init/
addons = <<EOL --- kind: ServiceAccount apiVersion: v1 metadata: name: tiller namespace: kube-system --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: tiller namespace: kube-system subjects: - kind: ServiceAccount name: tiller namespace: kube-system roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io EOL }
初始化Helm
Terraform能够运行本地脚本。既然咱们将要使用Helm来安装cert-manager
和Rancher,咱们须要初始化Helm。
安装cert-manager
这一步和Rancher文档中【使用Tiller安装cert-manager】的内容同样。
https://rancher.com/docs/rancher/v2.x/en/installation/ha/helm-rancher/#let-s-encrypt
resource "null_resource" "install-cert-manager" { depends_on = [null_resource.initialize-helm] provisioner "local-exec" { command = file("../install-cert-manager.sh") } }
安装Rancher
这一步和官方文档中【安装Rancher】的内容同样。
https://rancher.com/docs/rancher/v2.x/en/installation/ha/helm-rancher/
install-rancher脚本有不少个版本,咱们所使用的版本要求要有Let’s Encrypt的证书。若是你更习惯使用自签名的证书,你须要将install-rancher.sh
的symlink更改成指向其余版本,并从下面的示例代码中删除let-encrypt变量。
resource "null_resource" "install-rancher" { depends_on = [null_resource.install-cert-manager] provisioner "local-exec" { command = templatefile("../install-rancher.sh", { lets-encrypt-email = var.lets-encrypt-email, lets-encrypt-environment = var.lets-encrypt-environment, rancher-domain-name = local.domain-name }) } }
Rancher引导程序
Terraform的Rancher2 Provider包含了一个引导模式。这容许咱们能够设置一个admin密码。你能够在rancherbootstrap-module
中看到这一步。
provider "rancher2" { alias = "bootstrap" api_url = var.rancher-url bootstrap = true insecure = true } resource "rancher2_bootstrap" "admin" { provider = rancher2.bootstrap password = var.admin-password telemetry = true }
咱们在这里设置集群url。
provider "rancher2" { alias = "admin" api_url = rancher2_bootstrap.admin.url token_key = rancher2_bootstrap.admin.token insecure = true } resource "rancher2_setting" "url" { provider = rancher2.admin name = "server-url" value = var.rancher-url }
在咱们可使用Azure cloud来建立Load Balancer 服务和Azure存储以前,咱们须要先为Cloud Controller Manager配置connector。所以,咱们在cluster-module和serviceprincipal-module中建立了一个服务主体,其做用域为集群的Resource Group。
resource "azuread_application" "ad-application" { name = var.application-name homepage = "https://${var.application-name}" identifier_uris = ["http://${var.application-name}"] available_to_other_tenants = false } resource "azuread_service_principal" "service-principal" { application_id = azuread_application.ad-application.application_id app_role_assignment_required = true } resource "azurerm_role_assignment" "serviceprincipal-role" { scope = var.resource-group-id role_definition_name = "Contributor" principal_id = azuread_service_principal.service-principal.id } resource "random_string" "random" { length = 32 special = true } resource "azuread_service_principal_password" "service-principal-password" { service_principal_id = azuread_service_principal.service-principal.id value = random_string.random.result end_date = timeadd(timestamp(), "720h") }
咱们须要设置flannel 网络选项以支持Windows flannel驱动。你将会注意到Azure provider的配置。
resource "rancher2_cluster" "manager" { name = var.cluster-name description = "Hybrid cluster with Windows and Linux workloads" # windows_prefered_cluster = true Not currently supported rke_config { network { plugin = "flannel" options = { flannel_backend_port = 4789 flannel_backend_type = "vxlan" flannel_backend_vni = 4096 } } cloud_provider { azure_cloud_provider { aad_client_id = var.service-principal.client-id aad_client_secret = var.service-principal.client-secret subscription_id = var.service-principal.subscription-id tenant_id = var.service-principal.tenant-id } } } }
这些虚拟机的建立过程与早期计算机相同,而且包含Docker安装脚本。这里惟一的改变是使用来自以前建立集群的linux节点命令的附加命令。
module "k8s-worker" { source = "./node-module" prefix = "worker" resource-group = module.k8s-resource-group.resource-group node-count = var.k8s-worker-node-count subnet-id = module.k8s-network.subnet-id address-starting-index = var.k8s-etcd-node-count + var.k8s-controlplane-node-count node-definition = local.node-definition commandToExecute = "${module.cluster-module.linux-node-command} --worker" }
Windows worker进程相似于Linux进程,但有一些例外。因为Windows不支持cloud-init文件,咱们须要建立一个Windows自定义脚本扩展。你能够在windowsnode-module中看到它:
https://github.com/JasonvanBrackel/cattle-drive/tree/master/terraform-module/windowsnode-module
Windows worker使用密码进行认证,还须要VM Agent来运行自定义脚本扩展。
os_profile { computer_name = "${local.prefix}-${count.index}-vm" admin_username = var.node-definition.admin-username admin_password = var.node-definition.admin-password } os_profile_windows_config { provision_vm_agent = true }
节点配置完成以后,自定义脚本扩展将运行Windows节点命令。
注意:
这是与Terraform文档中不一样类型的自定义脚本扩展,适用于Linux虚拟机。Azure能够容许你对Windows节点尝试使用Terraform类型的扩展,但它最终会失败。
整个进程须要花费一些时间,须要耐心等待。当Terraform完成以后,将会有一些项目依旧在配置。即便在Kubernetes集群已经启动以后,Windows节点将至少须要10分钟来彻底初始化。正常工做的Windows节点看起来相似于下面的终端输出:
C:\Users\iamsuperman>docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 832ef7adaeca rancher/rke-tools:v0.1.50 "pwsh -NoLogo -NonIn…" 10 minutes ago Up 9 minutes nginx-proxy 7e75dffce642 rancher/hyperkube:v1.15.4-rancher1 "pwsh -NoLogo -NonIn…" 10 minutes ago Up 10 minutes kubelet e22b656e22e0 rancher/hyperkube:v1.15.4-rancher1 "pwsh -NoLogo -NonIn…" 10 minutes ago Up 9 minutes kube-proxy 5a2a773f85ed rancher/rke-tools:v0.1.50 "pwsh -NoLogo -NonIn…" 17 minutes ago Up 17 minutes service-sidekick 603bf5a4f2bd rancher/rancher-agent:v2.3.0 "pwsh -NoLogo -NonIn…" 24 minutes ago Up 24 minutes gifted_poincare
Terraform将为新平台输出凭据。
Outputs: lets-encrypt-email = jason@vanbrackel.net lets-encrypt-environment = production rancher-admin-password = {REDACTED} rancher-domain-name = https://jvb-win-hybrid.eastus2.cloudapp.azure.com/ windows-admin-password = {REDACTED}
由于Windows容器镜像和Linux容器镜像并不相同,咱们须要使用Kubernetes节点的关联性来肯定咱们的部署目标。每一个节点都有OS标签以帮助实现这一目的。
> kubectl get nodes NAME STATUS ROLES AGE VERSION control-0-vm Ready controlplane 16m v1.15.4 etcd-0-vm Ready etcd 16m v1.15.4 win-0-vm Ready worker 5m52s v1.15.4 worker-0-vm Ready worker 12m v1.15.4 > kubectl describe node worker-0-vm Name: worker-0-vm Roles: worker Labels: beta.kubernetes.io/arch=amd64 beta.kubernetes.io/os=linux kubernetes.io/arch=amd64 kubernetes.io/hostname=worker-0-vm kubernetes.io/os=linux node-role.kubernetes.io/worker=true ... > kubectl describe node win-0-vm Name: win-0-vm Roles: worker Labels: beta.kubernetes.io/arch=amd64 beta.kubernetes.io/os=windows kubernetes.io/arch=amd64 kubernetes.io/hostname=win-0-vm kubernetes.io/os=windows
由Rancher 2.3部署的集群会自动使用NoSchedule污染Linux worker节点,这意味着工做负载将始终流向Windows节点,除非特别调度了Linux节点而且配置为可容忍污染。
根据计划使用集群的方式,您可能会发现,设置相似的Windows或Linux默认首选项能够在启动工做负载时减小开销。
欢迎添加微信助手(rancher2),进官方技术群,了解更多Kubernetes使用攻略