零基礎教程!一文教你使用Rancher 2.3和Terraform運行Windows容器
- 2019 年 10 月 28 日
- 筆記
本文來自Rancher Labs
介 紹
在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 version Terraform 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 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": "[email protected]", "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-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
}
}
## 為什麼不對私有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 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)
}
}
“`
使用RKE安裝Tiller
有很多種方式可以安裝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 }
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:Usersiamsuperman>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 = [email protected] lets-encrypt-environment = production rancher-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 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使用攻略