离线部署Rancher 2.x高可用(二)Helm离线部署Rancher Server-使用外部 LB 终止 SSL/TLS

一、前言
作为K8S的新手小白,从docker转移阵地过来,直接上手学习Rancher2.x和RKE2,且是比较麻烦的离线安装,架构也较为复杂,经过几个月的摸索试验,基本算是“头破血流”,最终完成了第一步部署的工作。翻阅官方文档和百度文章以及论坛前辈的指导,总结出了些许自认为对初学者(大神请自动略过 :joy:)有用的Rancher的部署经验,供大家参考。

关于证书
对于才接触 Rancher 的用户,很多都是卡在了证书的配置上,本人也卡了很久才搞明白。其实 Rancher 的证书配置理解简单,一共分为以下三种:

  • Rancher 生成的证书(默认)
  • Let’s Encrypt
  • 你已有的证书(个人理解在私有网络中均可看做私有证书)
    -自签名证书
    -颁发机构颁发的证书

本文介绍如何使用 颁发机构颁发的证书来安装高可用的 Rancher ,更多安装方式,可参考[官网文档 ]和如下权威教程。

权威教程参考

二、背景
1、拓扑架构
keepalived+Nginx(7层负载)+RKE2集群(3server节点)部署Rancher Server

2、Rancher Server 设置

  • Rancher 版本:2.7.5
  • 安装选项: Helm Chart
  • RKE2版本:v1.26.5+rke2r1 (GitHub有Rancher和RKE2版本对应关系)
  • 在线或离线部署:离线部署

下游集群信息

  • Kubernetes 版本: v1.26.5+rke2r1

3、部署前准备
1)为Rancher Server准备高可用RKE2集群,请参考离线部署Rancher 2.x高可用(一)RKE2集群部署
2)为Rancher Server准备高可用K3S集群,请参考Rancher 高可用安装–使用外部 LB 终止 SSL/TLS,其中外部终止RKE2集群TSL的配置会在下文中介绍
3)收集镜像并发布到私有仓库
参考官网 收集镜像并发布到私有仓库 收集Rancher部署所需的镜像并导入到私有仓库,Rancher 2.7.5的镜像文件大概32G左右。
4)外部L7负载均衡器上终止SSL/TLS设置

  • 使用 –set tls=external 选项,将负载均衡器指向所有Rancher集群节点上的端口HTTP 80。这将在HTTP端口80上暴露Rancher接口。请注意,允许直接连接到Rancher集群的客户端不会被加密。如果选择这样做,建议将网络级别的直接访问限制为仅负载均衡器。

  • 为安装Rancher Server高可用的本地RKE2集群设置Ingress Controller

通过创建Helm Chart Config清单来指定配置选项,从而自定义rke2-ingress-nginx Helm Chart值。例如,/var/lib/rancher/rke2/server/manifests/rke2-ingress-nginx-config.yaml中的Helm Chart Config具有如下内容,在存储NGINX配置的ConfigMap中,将 use-forwarded-headers设为"true":

#/var/lib/rancher/rke2/server/manifests/rke2-ingress-nginx-config.yaml
---
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: rke2-ingress-nginx
  namespace: kube-system
spec:
  valuesContent: |-
    controller:
      config:
        use-forwarded-headers: "true"

三、Rancher Server部署
1、helm离线安装
通过Rancher Releases Mirror下载最新版本的helm,以helm-v3.12.2为例:

#解压文件
tar -zxvf helm-helm-v3.12.2-linux-amd64.tar.gz
#移动到用户目录
mv linux-amd64/helm /usr/local/bin/helm

2、获取Rancher Helm Chart
从可以访问互联网的系统中,获取最新的Rancher Helm Chart,然后将内容复制到可以访问Rancher Server集群的系统中。
1)如果还没有安装helm,请在可访问互联网的工作站上进行本地安装。注意:参考Helm版本要求选择Helm版本来安装Rancher。
2)执行helm repo add命令,以添加包含安装Rancher的Chart的Helm Chart仓库。有关如何选择仓库,以及哪个仓库最适合你的用例,请参见选择Rancher版本

  • latest:最新版,建议在尝试新功能时使用。
  • stable:稳定版,建议生产环境中使用。
  • alpha:预览版,未来版本的实验性预览。
helm repo add rancher-<CHART_REPO> https://releases.rancher.com/server-charts/<CHART_REPO>

注意:不支持升级到 Alpha 版、从 Alpha 版升级或在 Alpha 版之间升级。

3)获取最新的Rancher Chart。此操作将获取Chart并将其作为.tgz文件保存在当前目录中。

helm fetch rancher-<CHART_REPO>/rancher

如果需要下载特定版本Rancher的,可以用–version参数指定版本,例如,以下代码示例通过–version=v2.7.5,指定下载Rancher的版本为v2.7.5:

helm fetch rancher-stable/rancher --version=v2.7.5

3、选择SSL配置
Rancher Server 默认设计为安全的,并且需要 SSL/TLS 配置,参照官方文档选择SSL配置,选择需要的SSL配置。
本文使用的是颁发机构颁发的已有证书,ingress.tls.source=secret。

4、添加TLS密文
参照官网文档添加 TLS 密文配置部署Rancher所需的证书密文。
将服务器证书和所需的所有中间证书合并到名为tls.crt的文件中。将证书密钥复制到名为tls.key的文件中。
使用 kubectl 创建 tls 类型的密文。

kubectl -n cattle-system create secret tls tls-rancher-ingress \
--cert=tls.crt \
--key=tls.key

使用私有CA签名证书
如果使用的是私有CA,Rancher需要私有CA的根证书或证书链的副本,Rancher Agent使用它来校验与Server的连接。
创建一个名为cacerts.pem的文件,该文件仅包含私有CA的根CA证书或证书链,并使用kubectl在cattle-system命名空间中创建tls-ca Secret。

kubectl -n cattle-system create secret generic tls-ca \
--from-file=cacerts.pem=./cacerts.pem

备注 Rancher启动时会检索配置的tls-ca密文。如果Rancher在运行中,更新的CA会在新的Rancher Pod启动后生效。
5、安装Rancher
将获取的Rancher Helm Chart复制到有权访问Rancher Server集群的系统以完成安装,建议放到准备部署Rancher Server的RKE2本地集群任意server节点主机上。
1)创建命名空间
使用kubectl为Rancher创建命名空间:

kubectl create namespace cattle-system

2)使用私有CA签名的证书helm离线安装

helm install rancher ./rancher-<VERSION>.tgz \ #互联网Helm渲染生成的压缩包文件
    --namespace cattle-system \
    --set hostname=<RANCHER.YOURDOMAIN.COM> \  #指向负载均衡器的DNS名称
    --set rancherImage=<REGISTRY.YOURDOMAIN.COM:PORT>/rancher/rancher \  #私有镜像仓库的DNS名称,用于pull安装Rancher所需镜像
    --set ingress.tls.source=secret \ #SSL选择已有的证书
    --set privateCA=true \  #使用私有CA签名的证书
    --set systemDefaultRegistry=<REGISTRY.YOURDOMAIN.COM:PORT> \ #设置在Rancher中使用的默认私有镜像仓库
    --set useBundledSystemChart=true \  #使用打包的Rancher System Chart
    --set global.cattle.psp.enabled=false \  #(可选)禁用PSP
    --set tls=external  #将负载均衡器指向所有Rancher集群节点上的端口HTTP 80

可选: 要安装指定的Rancher版本,请设置rancherImageTag的值,例如:

--set rancherImageTag=v2.7.2

至此,Rancher Server离线高可用安装完成。

四、Rancher Server发布使用
1、外部NGINX L7配置负载均衡器
外部Nginx TLS终止config主要配置如下:

  • 将IP_NODE1,IP_NODE2和IP_NODE3替换为你集群中节点的IP地址。
  • 将两处的FQDN均替换为Rancher的DNS名称。
  • 把/certs/fullchain.pem和/certs/privkey.pem分别替换为服务器证书和服务器证书密钥的位置。
worker_processes 4;
worker_rlimit_nofile 40000;

events {
    worker_connections 8192;
}

http {
    upstream rancher {
        server IP_NODE_1:80;
        server IP_NODE_2:80;
        server IP_NODE_3:80;
    }

    map $http_upgrade $connection_upgrade {
        default Upgrade;
        ''      close;
    }

    server {
        listen 443 ssl http2;
        server_name FQDN;
        ssl_certificate /certs/fullchain.pem;
        ssl_certificate_key /certs/privkey.pem;

        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-Port $server_port;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://rancher;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            # 此项允许执行的 shell 窗口保持开启,最长可达15分钟。不使用此参数的话,默认1分钟后自动关闭。
            proxy_read_timeout 900s;
            proxy_buffering off;
        }
    }

    server {
        listen 80;
        server_name FQDN;
        return 301 https://$server_name$request_uri;
    }
}

2、配置DNS记录
配置完负载均衡器后,需要创建DNS记录,以将流量发送到该负载均衡器。
根据环境,DNS记录可以是指向负载均衡器IP的A记录,也可以是指向负载均衡器主机名的CNAME。无论是哪种情况,请确保该记录是Rancher进行响应的主机名。

3、创建下游集群
本例采用 RKE2 集群,rke2和 k3s 集群也是一样的,流程基本没区别:
使用默认参数,创建 RKE2 集群,然后将生产的注册节点命令复制到下游集群主机执行即可。
注:
要确保下游集群主机与私有镜像仓库网络可达,即可以直接pull相关的镜像。

提醒:
下游集群主机名不能带有下划线"_",实测是RKE2无法识别此字符,建议使用横线"-"。具体的报错如下:
rancher-system-agent.service 的日志报错与下图类似:


下游集群主机kubelet日志/var/lib/rancher/rke2/agent/logs/kubelet.log日志报错如下:

日志
I0808 10:26:22.232751   29566 kubelet_node_status.go:70] "Attempting to register node" node="rancher_rke2_test1"
E0808 10:26:22.233090   29566 kubelet_node_status.go:92] "Unable to register node with API server" err="Post \"https://127.0.0.1:6443/api/v1/nodes\": dial tcp 127.0.0.1:6443: connect: connection refused" node="rancher_rke2_test1"
E0808 10:26:25.483901   29566 file.go:187] "Could not process manifest file" err="invalid pod: [metadata.name: Invalid value: \"etcd-rancher_rke2_test1\": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*') spec.nodeName: Invalid value: \"rancher_rke2_test1\": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')]" path="/var/lib/rancher/rke2/agent/pod-manifests/etcd.yaml"
E0808 10:26:27.136305   29566 event.go:276] Unable to write event: '&v1.Event{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""}, ObjectMeta:v1.ObjectMeta{Name:"rancher_rke2_test1.1779474a87b3d5d5", GenerateName:"", Namespace:"default", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string(nil), Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:[]v1.ManagedFieldsEntry(nil)}, InvolvedObject:v1.ObjectReference{Kind:"Node", Namespace:"", Name:"rancher_rke2_test1", UID:"rancher_rke2_test1", APIVersion:"", ResourceVersion:"", FieldPath:""}, Reason:"Starting", Message:"Starting kubelet.", Source:v1.EventSource{Component:"kubelet", Host:"rancher_rke2_test1"}, FirstTimestamp:time.Date(2023, time.August, 8, 10, 25, 20, 484193749, time.Local), LastTimestamp:time.Date(2023, time.August, 8, 10, 25, 20, 484193749, time.Local), Count:1, Type:"Normal", EventTime:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Series:(*v1.EventSeries)(nil), Action:"", Related:(*v1.ObjectReference)(nil), ReportingController:"", ReportingInstance:""}': 'Post "https://127.0.0.1:6443/api/v1/namespaces/default/events": dial tcp 127.0.0.1:6443: connect: connection refused'(may retry after sleeping)
W0808 10:26:28.495315   29566 reflector.go:424] k8s.io/client-go/informers/factory.go:150: failed to list *v1.RuntimeClass: Get "https://127.0.0.1:6443/apis/node.k8s.io/v1/runtimeclasses?limit=500&resourceVersion=0": dial tcp 127.0.0.1:6443: connect: connection refused
E0808 10:26:28.495390   29566 reflector.go:140] k8s.io/client-go/informers/factory.go:150: Failed to watch *v1.RuntimeClass: failed to list *v1.RuntimeClass: Get "https://127.0.0.1:6443/apis/node.k8s.io/v1/runtimeclasses?limit=500&resourceVersion=0": dial tcp 127.0.0.1:6443: connect: connection refused
E0808 10:26:29.099642   29566 controller.go:146] failed to ensure lease exists, will retry in 7s, error: Get "https://127.0.0.1:6443/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/rancher_rke2_test1?timeout=10s": dial tcp 127.0.0.1:6443: connect: connection refused

2 个赞

如何查看下游集群的容器部署情况

RKE2 commands