一、前言
作为K8S的新手小白,从docker转移阵地过来,直接上手学习Rancher2.x和RKE2,且是比较麻烦的离线安装,架构也较为复杂,经过几个月的摸索试验,基本算是“头破血流”,最终完成了第一步部署的工作。翻阅官方文档和百度文章以及论坛前辈的指导,总结出了些许自认为对初学者(大神请自动略过 )有用的Rancher的部署经验,供大家参考。
关于证书
对于才接触 Rancher 的用户,很多都是卡在了证书的配置上,本人也卡了很久才搞明白。其实 Rancher 的证书配置理解简单,一共分为以下三种:
- Rancher 生成的证书(默认)
- Let’s Encrypt
- 你已有的证书(个人理解在私有网络中均可看做私有证书)
-自签名证书
-颁发机构颁发的证书
本文介绍如何使用 颁发机构颁发的证书来安装高可用的 Rancher ,更多安装方式,可参考[官网文档 ]和如下权威教程。
权威教程参考:
- Rancher 高可用安装–使用外部 LB 终止 SSL/TLS
- Rancher 高可用安装–自签名证书+4层 LB
- Rancher 高可用安装–Cert-Manager 签发 TLS 证书+4层 LB
- 使用自定义 CA 证书启动 K3s
二、背景
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