10 月,开发者不可错过的开源大数据大会-2021 WeDataSphere 社区大会深圳站 了解详情
写点什么

零基础教程!一文教你使用 Rancher 2.3 和 Terraform 运行 Windows 容器

2020 年 5 月 18 日

零基础教程!一文教你使用Rancher 2.3和Terraform运行Windows容器

介绍

在 Kubernetes 1.14 版本中已经 GA 了对 Windows 的支持。这一结果凝结了一群优秀的工程师的努力,他们来自微软、Pivotal、VMware、红帽以及现在已经关闭的 Apprenda 等几家公司。我在 Apprenda 工作时,不定时会为 sig-windows 社区做出一些贡献。即便现在在 Rancher Labs 任职,也一直关注它的动向。所以当公司决定在 Rancher 中增加对 Windows 支持时,我极为兴奋。


Rancher 2.3已于本月月初发布,这是首个 GA 支持 Windows 容器的 Kubernetes 管理平台。它极大降低了企业使用 Windows 容器的复杂性,并为基于 Windows 遗留应用程序的现代化提供快捷的途径——无论这些程序是在本地运行还是在多云环境中运行。此外,Rancher 2.3 还可以将它们容器化并将其转换为高效、安全和可迁移的多云应用程序,从而省去重写应用程序的工作。


在本文中,我们将在运行 RKE 的 Kubernetes 集群之上配置 Rancher 集群。我们也将会配置同时支持 Linux 和 Windows 容器的集群。完成配置后,我们将聊聊操作系统的定位,因为 Kubernetes scheduler 需要指导各种 Linux 和 Windows 容器在启动时的部署位置。


我们的目标是以完全自动化的方式执行这一操作。由于 Rancher 2.3 目前尚未 stable,因此这无法在生产环境中使用,但如果你正需要使用 Azure 和 Rancher 来完成基础架构自动化,那么这对你们的团队而言将会是一个良好的开端。退一万步而言,即使你不使用 Azure,在本例中的许多概念和代码都可以应用于其他环境中。


考虑因素

Windows vs. Linux

在我们正式开始之前,我需要告知你许多注意事项和“陷阱”。首先,最显而易见的就是:Windows 不是 Linux。Windows 新增了支持网络网格中的容器化应用程序所需的子系统,它们是 Windows 操作系统专有的,由 Windows 主机网络服务和 Windows 主机计算服务实现。操作系统以及底层容器运行时的配置、故障排除以及运维维护将会有显著区别。而且,Windows 节点收到 Windows Server licensing 的约束,容器镜像也受 Windows 容器的补充许可条款的约束。


Windows OS 版本

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 之前的版本中运行。


持久性支持和 CSI 插件

Kubernetes 1.16 版本以来,CSI 插件支持 alpha 版本。Windows 节点支持大量的 in-tree 和 flex volume。


CNI 插件

Rancher 支持仅限于 flannel 提供的主机网关(L2Bridge)和 VXLAN(Overlay)网络支持。在我们的方案中,我们将利用默认的 VXLAN,因为当节点并不都在同一个网络上时,主机网关选项需要 User Defined Routes 配置。这取决于提供商,所以我们将利用 VXLAN 功能的简单性。根据 Kubernetes 文档,这是 alpha 级别的支持。当前没有支持 Kubernetes 网络策略 API 的开源 Windows 网络插件。


其他限制

请确保你已经阅读了 Kubernetes 文档,因为在 Windows 容器中有许多功能无法实现,或者其功能和 Linux 中的相同功能实现方式有所不同。


Kubernetes 文档:


https://kubernetes.io/docs/setup/production-environment/windows/intro-windows-in-kubernetes/#limitations


基础架构即代码

自动化是实现 DevOps 的第一种方式。我们将自动化我们的 Rancher 集群和要在该集群中配置的 Azure 节点的基础架构。


Terraform

Terraform 是一个开源的基础架构自动化编排工具,它几乎支持所有市面上能见到的云服务提供商。今天我们将使用这一工具来自动化配置。确保你运行的 Terraform 版本至少是 Terraform 12。在本文中,使用 Terraform 版本是 v0.12.9。


$ terraform versionTerraform v0.12.9
复制代码


RKE Provider


用于 Terraform 的 RKE provider 是一个社区项目,并非由 Rancher 官方进行研发的,但包括我在内的 Rancher 的很多工程师都在使用。因为这是一个社区 provider 而不是 Terraform 官方的 provider,因此你需要安装最新版本到你的 Terraform 插件目录中。对于大部分的 Linux 发行版来说,你可以使用本文资源库中包含的 setup-rke-terraform-provider.sh 脚本。


Rancher Provider


用于 Terraform 的 Rancher2 provider 是 Terraform 支持的 provider,它通过 Rancher REST API 来自动化 Rancher。我们将用它从 Terraform 的虚拟机中创建 Kubernetes 集群,这一虚拟机需要使用 Azure Resource Manager 和 Azure Active Directory Terraform Provider 进行创建。


本例的 Format

本文中的 Terraform 模型的每个步骤都会被拆分成子模型,这将增强模型可靠性并且将来如果你创建了其他自动化架构,这些模型都可以重新使用。


Part1:设置 Rancher 集群

登录到 Azure

Azure Resource Manager 和 Azure Active Directory Terraform Provider 将使用一个激活的 Azure Cli 登录以访问 Azure。他们可以使用其他认证方法,但在本例中,我在运行 Terraform 之前先登录。


az loginNote, 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"    }  }]
复制代码


设置 Resource Group

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-configrepo_update: truerepo_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 }}
复制代码


为什么不对私有 IP 使用动态分配?

在创建并完全配置节点之前,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 domainresource "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)    }  }
复制代码


使用 RKE 安装 Tiller

有很多种方式可以安装 Tiller,你可以使用 Rancher 官方文档中的方法,但是在本教程中我们将利用 RKE Add-On 的特性。


官方文档:


https://rancher.com/docs/rancher/v2.x/en/installation/ha/helm-init/


  addons = <<EOL---kind: ServiceAccountapiVersion: v1metadata:  name: tiller  namespace: kube-system---kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:  name: tiller  namespace: kube-systemsubjects:- kind: ServiceAccount  name: tiller  namespace: kube-systemroleRef:  kind: ClusterRole  name: cluster-admin  apiGroup: rbac.authorization.k8s.ioEOL}
复制代码


初始化 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}
复制代码


Part2:设置 Rancher 管理的 Kubernetes 集群

为 Azure 创建服务主体

在我们可以使用 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 Workers

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}
复制代码


加入 Rancher

节点配置完成之后,自定义脚本扩展将运行 Windows 节点命令。


注意:

这是与 Terraform 文档中不同类型的自定义脚本扩展,适用于 Linux 虚拟机。Azure 可以允许你对 Windows 节点尝试使用 Terraform 类型的扩展,但它最终会失败。


耐心一点噢

整个进程需要花费一些时间,需要耐心等待。当 Terraform 完成之后,将会有一些项目依旧在配置。即使在 Kubernetes 集群已经启动之后,Windows 节点将至少需要 10 分钟来完全初始化。正常工作的 Windows 节点看起来类似于下面的终端输出:


C:\Users\iamsuperman>docker psCONTAINER ID        IMAGE                                COMMAND                  CREATED             STATUS              PORTS               NAMES832ef7adaeca        rancher/rke-tools:v0.1.50            "pwsh -NoLogo -NonIn…"   10 minutes ago      Up 9 minutes                            nginx-proxy7e75dffce642        rancher/hyperkube:v1.15.4-rancher1   "pwsh -NoLogo -NonIn…"   10 minutes ago      Up 10 minutes                           kubelete22b656e22e0        rancher/hyperkube:v1.15.4-rancher1   "pwsh -NoLogo -NonIn…"   10 minutes ago      Up 9 minutes                            kube-proxy5a2a773f85ed        rancher/rke-tools:v0.1.50            "pwsh -NoLogo -NonIn…"   17 minutes ago      Up 17 minutes                           service-sidekick603bf5a4f2bd        rancher/rancher-agent:v2.3.0         "pwsh -NoLogo -NonIn…"   24 minutes ago      Up 24 minutes                           gifted_poincare
复制代码


Terraform 将为新平台输出凭据。


Outputs:
lets-encrypt-email = jason@vanbrackel.netlets-encrypt-environment = productionrancher-admin-password = {REDACTED}rancher-domain-name = https://jvb-win-hybrid.eastus2.cloudapp.azure.com/windows-admin-password = {REDACTED}
复制代码


Part3:使用 Windows 工作负载

通过 OS 定位工作负载

因为 Windows 容器镜像和 Linux 容器镜像并不相同,我们需要使用 Kubernetes 节点的关联性来确定我们的部署目标。每个节点都有 OS 标签以帮助实现这一目的。


> kubectl get nodesNAME           STATUS   ROLES          AGE     VERSIONcontrol-0-vm   Ready    controlplane   16m     v1.15.4etcd-0-vm      Ready    etcd           16m     v1.15.4win-0-vm       Ready    worker         5m52s   v1.15.4worker-0-vm    Ready    worker         12m     v1.15.4> kubectl describe node worker-0-vmName:               worker-0-vmRoles:              workerLabels:             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-vmName:               win-0-vmRoles:              workerLabels:             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 默认首选项可以在启动工作负载时减少开销。


2020 年 5 月 18 日 18:05265

评论

发布
暂无评论
发现更多内容

Go语言的初体验

Phoenix

Go

设计与思考,关于资源和生命周期(三)

程序员架构进阶

设计模式 架构设计 28天写作 3月日更 池化技术

蚂蚁开源增强版 SpringBoot,都有高级特性?

Java小咖秀

开源 springboot 蚂蚁金服 SOFA

小白也能看懂的锁升级过程和锁状态

牧小农

乐观锁 悲观锁 锁升级

火爆全网!2021年最新发布Java面试清单(九大技术点)

比伯

Java 程序员 架构 程序人生 架构师

Redis 内存碎片清理

escray

redis 学习 极客时间 3月日更 Redis 核心技术与实战

阿里二面,面试官居然把 TCP 三次握手问的这么细致

程序员肖邦

面试 TCP 三次握手 四次挥手

mysql 检索分享上篇

new life

Spring Boot Feign 使用与源码学习

Yangjing

Feign Spring boot Feign 源码解读

Wireshark数据包分析学习笔记Day24

穿过生命散发芬芳

Wireshark 数据包分析 3月日更

那些不得不知的缓存知识

云流

Java 缓存 程序员 面试

架构师知识笔记2

felix徐

Laravel使用中间件记录用户请求日志

autofelix

php laravel 日志

软件工程笔记:学习方法

风翱

软件工程 笔记 3月日更

Redis - RDB

insight

redis 3月日更

go-admin开源项目分析-1[安装、配置、启动]

happlyfox

Go 学习 3月日更 go-admin

LeetCode链表专题01: 反转链表

小马哥

Java 链表 LeetCode 数据结构与算法 七日更

初识Golang之条件语句

Kylin

Go 基础语法 3月日更

10年后端开发程序员精心整理「C/C++ Linux服务器」 成长路线(附思维导图)

Linux服务器开发

Linux C/C++ 后端开发 Linux服务器开发 Linux后台开发

极客时间·产品训练营·第八周作业

二大爷

极客大学产品经理训练营

畅玩国服LOL?MacBook M1 Windows虚拟机体验

Zhendong

arm MacBook m1

面试三轮我倒在了一道sql题上——sql性能优化

牧小农

MySQL 数据库 面试 索引 索引优化

Linux下安装PHP7.3.0

autofelix

php Linux 经验分享 软件安装

看完这篇,再也不怕面试官问我线程池了

牧小农

多线程 高并发 线程池 阻塞队列 拒绝策略

Go Modules 常见使用陷阱

Rayjun

Go go modules

一束光的旅程

脑极体

一篇文章让你彻彻底底理解 I/O 多路复用

云流

Java 程序员 架构 面试 计算机

Redis - AOF

insight

redis 3月日更

Python OpenCV 图像标记,取经之旅第 12 天

梦想橡皮擦

3月日更

程序思维与架构思维

Simon

架构实战营

京东资深架构师带你深入理解 Spring Cloud 核心组件与底层原理

互联网架构师小马

零基础教程!一文教你使用Rancher 2.3和Terraform运行Windows容器-InfoQ