这篇文章也可以在我的博客中查看
说在前面
本文介绍如何使用Kubeadm在自己的服务器上将Kubernetes应用部署为生产环境,而不使用Kuberentes云服务商。
在进行之前,我建议你先保持理智,问自己为什么需要这么做:
- 你正在学习Kubernetes,你想知道如何安全地将它部署到生产环境
- 你的应用注重硬件性能,你必须使用硬件机器部署Kubernetes
- 云服务商对你而言太过高昂,你接受使用云主机,以降低可用性、运行效率为代价换取性价比
- ......
总之,如果你有充分的理由,请往下进行;
否则,我非常建议你使用Kubernetes云服务商。它虽然贵,但是很省心。
准备工作
服务器
要按照本文方法部署Kubernetes,你需要准备服务器:
- 至少两台服务器
- 一台用作Master:至少2G内存
- 一台用作Worker:至少2G内存
- 这只是可运行配置,一般而言,运行微服务应用,你可能要为Worker提升配置,且多准备几台Worker
- 这些服务器可以是硬件服务器、也可以是vps
- 如果需要执行高强度数据操作(比如数据库查询),请保证这些机器处于同一内网(如果你使用vps的话)
应用
你需要准备能够随时工作于生产环境的Kubernetes应用,包括应用镜像和deployment文件。
知识
你需要对Kubernetes和Linux操作有基本了解。
本文内容
本文介绍以下内容
- 配置Docker和容器运行时
- 安装Kubeadm
- 创建kubernetes集群
- 配置持久存储
- 配置ingress(公网访问)
- 配置https
本文不会展开以下内容
- 将Kubernetes部署到云服务商
- 如何编写Kubernetes应用
- 如何编写Kubernetes deployment
演示操作环境
- 使用Debian12作为所有机器的操作系统
- 所有机器是vps,Master和Worker不在同一内网,但Worker彼此在同一内网
- Master是阿里云ecs
- Worker是国外vps
准备工作
重启所有机器
某些镜像在第一次重启之前可能存在问题。如果你的机器从来没重启过,我建议先重启一遍。
bash
reboot now
网络连通性
非常重要!
在开始前,先检测各个机器之间的连接性。
然后检查所有机器的端口开启情况。某些防火墙或者云服务器会默认关闭端口,你需要确保对应机器的以下端口是可以公开访问的:
Master结点
Protocol | Direction | Port Range | Purpose | Used By |
---|---|---|---|---|
TCP | Inbound | 6443 | Kubernetes API server | All |
TCP | Inbound | 2379-2380 | etcd server client API | kube-apiserver, etcd |
TCP | Inbound | 10250 | Kubelet API | Self, Control plane |
TCP | Inbound | 10259 | kube-scheduler | Self |
TCP | Inbound | 10257 | kube-controller-manager | Self |
Worker结点
Protocol | Direction | Port Range | Purpose | Used By |
---|---|---|---|---|
TCP | Inbound | 10250 | Kubelet API | Self, Control plane |
TCP | Inbound | 30000-32767 | NodePort Services (default) | All |
详情请见Kubernetes文档 Ports and Protocols
提示:
- 为了避免意外,我的建议是先开放全部端口,搞定了再关闭不需要的端口。
- 请仔细检查防火墙设置,并且尝试在服务器之间测试tcp连接
你可以跳过这步,但如果出了问题,请不要忘记排查防火墙/安全组。
提供一个检测tcp连接的方法:
A主机作为服务端,开启端口的服务
bash
nc -l -p 6443
B主机作为客户端,接通端口
bash
nc <address-of-A> 6443
发点东西过去看看A能不能收到
关闭swap
Kubernetes暂时无法直接运行于开启交换区(虚拟内存)的环境,因此请确保你关闭了所有交换区。
查看是否有交换区(swap容量不为0):
bash
free -h
关闭交换区直到关机/重启:
bash
swapoff -a
禁止开机启动交换区:
打开以下文件
bash
vim /etc/fstab
将里面所有交换区注释掉,示意:
bash
# <file system> <mount point> <type> <options> <dump> <pass>
/dev/vda1 / ext4 defaults 1 1
# /dev/vda2 swap swap defaults 0 0
同步所有服务器上的时间
如果服务器的时间不准确,可能会造成证书过期,CoreDNS失联等问题。
所以请务必保证所有服务器处于相同时区,并且与时间服务器同步时间。
设置系统时区
bash
timedatectl set-timezone Asia/Shanghai
设置本地时钟使用UTC时间
bash
timedatectl set-local-rtc 0
重启相关服务
bash
systemctl restart rsyslog
systemctl restart crond
更改服务器的主机名称(非必须)
服务器的主机名称(hostname)将成为Kubernetes的结点名称。
名称无法(很难)在部署后更改,请在部署前决定每个机器的名称。
建议:
- 主机名应识别出是Master还是Worker
- 如果你的服务器数量很少,你可以使用数字作为结点后缀
- 如果服务器很多,而且每个配置不同,建议附加简洁的配置信息,或者用途
- 总之你需要一眼就能认出这是哪个服务器
- 主机名绝对不能重复
如何更改
vps
如果你是云服务商的vps机器,先看看控制面板能不能改
硬件服务器
如果不能改或者硬件主机,按以下操作:
修改主机名
bash
hostnamectl set-hostname <your-host-name>
修改hosts文件
bash
vi /etc/hosts
将里面出现的所有旧名称项,都替换为新主机名
注意: 某些主机的hosts文件会有托管,此时直接修改hosts无效。请注意hosts文件是否有相关注释说明。
重新登陆ssh就可以看到新主机名
安装docker和containerd容器运行时
这一步需要在Master和Worker进行
请参考官方教程进行安装
按照教程安装完后,你应该会同时安装完docker和containerd
验证安装
验证docker和containerd成功安装并启动:
bash
systemctl status docker
systemctl status containerd
配置containerd
此步具有时效性,只保证编写时刻(2023-11-5)需要如此操作。
- containerd安装时默认关闭cri(容器运行时)插件,需要手动打开
- 另外kuberadm需要使用cgroup v2,因此需要开启containerd的cgroup v2支持
打开/etc/containerd/config.toml
文件,作两处修改
bash
vi /etc/containerd/config.toml
- 将
cri
从disabled_plugins
中移除
bash
# disabled_plugins = ["cri"];
disabled_plugins = [];
- 在文件中加入以下内容以启用cgroup v2
bash
version = 2
[plugins]
[plugins."io.containerd.grpc.v1.cri"]
[plugins."io.containerd.grpc.v1.cri".containerd]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
编辑完毕后重启服务
bash
systemctl restart containerd
安装kubeadm
这一步需要在Master和Worker进行
请参考官方教程进行安装
验证安装
验证已经安装kubeadm、kubelet和kubectl
bash
kubeadm
systemctl status kubelet
kubectl
配置vpc网络的外部访问
这步仅需要在满足以下条件的机器上执行:
- 机器希望以公网身份接入网络
- 机器网卡的IP并不是机器的公网IP
检测公网IP是否挂载在网卡上
请输入以下命令检测默认网卡是否为公网IP
bash
ip route get 1.1.1.1 | grep -oP 'src \K\S+'
或者使用以下命令,观察默认网卡(比如eth0)是否存在你的公网IP
bash
ip a
如果显示的是你的公网IP,或者你不需要公网接入,你可以跳过这一节;否则,继续往下执行。
创建公网IP的虚拟网卡
输入以下命令,为默认网卡增加临时的IP地址。这里假设你的默认网卡名为eth0
,如果不是,你需要修改:
bash
ifconfig eth0:0 <公网IP>/24 up
这将持续到机器重启,重启后你需要重新创建。
关于持久化......每个镜像使用的网络服务不同,会有不同的搞法,这里就不展开了
就单纯给点提示吧:
- 确定自己系统使用的是什么网络服务
bash
systemctl list-unit-files | grep -i network
- 谷歌一下这个东西怎么增加IP地址
验证IP是否生效
使用以下命令,检测公网IP是否出现在你的默认网卡中
bash
ip a
修改kubeadm启动kubelet时使用的ip
查看kubelet的环境变量文件:
bash
systemctl cat kubelet.service | grep EnvironmentFile
你会看到类似以下的输出:
bash
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
EnvironmentFile=-/etc/default/kubelet
kubeadm官方说第一个文件是自动生成的,不建议修改。因此我们修改第二个(不存在则创建)
bash
vi /etc/default/kubelet
在打开的文件中,指定--node-ip
参数为公网IP
bash
KUBELET_EXTRA_ARGS=--node-ip=<公网IP>
关于这点的更多阅读资料
初始化Kubernetes Master
这一步只需要在Master进行
中途出现问题可能会导致kubeadm初始化中断,你可以使用kubeadm reset
重置到初始前状态
配置仓库镜像
这步需要在无法连接到
k8s.gcr.io
的Master执行(大陆机器)
如果是国内环境,很遗憾,无法访问默认的k8s.gcr.io
容器仓库。
没有关系😭,我们可以使用镜像仓库
此处需要将容器仓库修改为国内镜像
如果你想了解更多,请参考这里:
配置containerd sandbox-image的镜像
我们使用阿里云作为镜像服务器
打开containerd配置文件
bash
vi /etc/containerd/config.toml
在文件中加入以下结点:
注意,如果文件中有相同的结点,将
sandbox_image
附加至下面即可,不要创建重复结点。
bash
version = 2
[plugins]
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"
初始化
使用适当的参数初始化kubernetes:
--pod-network-cidr
是设置后面使用的flannel网络插件的网段。- 如果你按照本文操作,必填。
--image-repository
: 使用阿里云镜像- 如果机器无法访问
k8s.gcr.io
需要填写。
- 如果机器无法访问
--apiserver-advertise-address
: 指定API Server的监听地址。- 如果你的机器处于vpc网络(公网IP不在网卡上),且你希望支持公网访问,需要在创建了虚拟网卡的基础上,填写公网IP。
- 其它可配置参数请看官方文档
指令示意:
bash
kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--image-repository=registry.aliyuncs.com/google_containers \
--apiserver-advertise-address=<公网IP>
启动集群
在上一步顺利完成后,会显示以下信息:
bash
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
......
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join <ip-address>:6443 --token nrlc17.k0ypuceqs0nmv6i2 \
--discovery-token-ca-cert-hash sha256:c8fe4911959673b6392f46abd89d744de86584225202396cd5f13aec76be53b0
然而无论你是否为root用户,都请按照这个提示启动kubernetes集群:
bash
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
而且请记住最后输出的加入token,workers需要使用这个加入集群。(如果没记住也不慌,后文教你重新生成)
验证集群状态
验证状态:
bash
kubectl cluster-info
bash
Kubernetes control plane is running at https://<ip-address>:6443
CoreDNS is running at https://<ip-address>:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
观察结点状态:
bash
kubectl get node
bash
NAME STATUS ROLES AGE VERSION
master-1 NotReady control-plane 4m46s v1.28.3
此处NoReady
是正常现象,因为我们还没安装网络插件
存档点!
恭喜你已经成功渡过第一阶段,你还好吗😊?
接下来需要进行kubernetes内部的配置,在此之前我需要做一些提醒:
如何排错
在接下来的过程中,由于环境问题你可能会出现各种各样的状态异常、容器Crash。本文不可能涵盖所有的情况,但我会给你解决问题的方向。
出现这种问题是你应该:
- 首先使用
kubectl get pods -A -o wide
等命令查看出错的容器。- 使用
kubectl describe pod <pod-name> -n <namespace>
查看错误的信息 - 使用
kubectl logs <pod-name> -n <namespace>查看容器日志
- 使用
- 得到错误信息后,请善用搜索引擎和社区解决问题
- 如果你使用的国内的服务器,建议你也查看国内社区的信息。因为这可能涉及到主机服务商虚拟化技术不同导致的,就如上面提到的阿里;也有可能说是某种不可抗力导致的,比如......
做好准备
因为下一步"安装网络插件"对环境是破坏性的。如果安装失败了,它很难简单使用kubeadm reset
重置。它会给你的机器引入永久性的改变......
我想说的是,如果网络插件安装失败、或者想改变使用的网络插件,你可能会陷入一种"无论如何配置都不行"的境地。
你仍然可以反复尝试,但我需要提醒的是:如果你多次尝试都失败,很可能是机器环境已经被网络插件改变过了,你可能需要回滚这些设置。
所以:
- 如果你有条件,可以考虑在此时创建一个系统镜像,以方便回滚。
- 如果不行,你可以查找如何彻底卸载网络插件的方法, 这个东西因插件、版本而异。
- 但较简单的做法,可能是重装系统,然后重新执行一次上述步骤。
部分地区网络连接问题
下面的步骤我们需要拉取各种容器镜像。
由于部分镜像需要在registry.k8s.io
或quay.io
下载。然而这几个源在天朝强大的防火墙下已经无法访问了。
不同于hub.docker.com
,这些网站是无法通过docker配置镜像解决的......
得益于此,要拉取镜像,你需要化简为繁:
- 寻找一个镜像站
- 从镜像站下载镜像
- 将下载的镜像tag为
registry.k8s.io/xxx...
- 修改部署的yaml文件,将每个
ImagePullPolicy
修改成Always
以外的值,使其使用本地镜像
至此,我是完全投降了......
虽然不是不能做,而是实在太麻烦了!!!
以后还有其它需要registry.k8s.io/xxx...
的容器怎么办,又重复这四步吗???
我的建议是,收手吧阿祖,外面全是防火墙😅
如果没有魔法 ,我建议你选一个只在hub.docker.com
拉镜像的存储方案
因此,国内Worker机器的部署请自求多福,并没有一劳永逸的做法,这个东西会非常麻烦
下面的操作如果涉及内陆无法下载的源,我会尽量提醒
好了,该说的就这么多了,祝你好运!
安装网络插件(flannel)
这一步只需要在Master进行
coredns服务需要网络插件支持。
kubernetes支持许多网络插件,这里使用flannel。
关于选择哪个网络插件
并不是所有网络插件都适用于你的网络环境。
因为机器的网络环境不同,某些网络插件可能不支持你的环境。
比较老牌的是calico,但是如果是阿里云等vpc网络环境(公网IP不在网卡),目前(2023-11-8)的calico很难在公网正常运作。
因此我选择了flannel。
这并不代表flannel是最好的。我想说的是:如果你(我😅)折腾一个网络插件很久,重装多很多次都不行,也有可能是该插件本身不适用于你的网络环境。不妨换个试试。
部署flannel
如果你不需要进行额外配置,直接下载部署即可:
bash
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
如果需要改变默认配置,你可以先wget下来,修改后再apply。
等待所有pod安装完毕
bash
watch kubectl get pods -A -o wide
按ctrl+c
退出
验证node准备完毕
bash
kubectl get nodes
bash
NAME STATUS ROLES AGE VERSION
master-1 Ready control-plane 40m v1.28.3
Worker加入集群
这一步只需要在每个Worker进行
生成连接串
如果你没有记录连接串,或者连接串已过期,可以在Master结点使用以下命令创建连接串:
bash
kubeadm token create --print-join-command
加入集群
使用生成的join命令加入集群:
bash
kubeadm join <ip-address>:6443 --token zuyxod.5yglqucae2hejoqj --discovery-token-ca-cert-hash sha256:ff5ab3bcc2eb8aa9b4fb17eee028359fce1799984282cca01d4960a3a84369ce
配置持久存储(Persistent Volume)
如果你的应用需要存储状态(使用数据库等)需要执行
选择哪种持久存储?
Kubernetes的持久存储卷 (Persistent Volume,下简称PV) 可以大致分为两类:
- 网络存储
- 本地存储
一般使用网络存储,因为它与pod所在的node无关。无论pod被部署到哪个结点,它都能访问到位于网络的持久存储卷。
但是如果你的应用需要高强度IO,你可能需要使用本地存储。Kubernetes本地存储插件主要有两个,生产环境中请使用local
而非hostPath
。
local
虽然比hostPath
更好,但也有明显的缺点:
- pod会被绑定至存储所在的node
- 必须手动分配空间,无法使用动态空间分配
所以请权衡性能与高可用性。
对于内网环境中,一般情况下使用网络存储并不会有明显的IO瓶颈。因此本文配置网络存储。
数据冗余
存储冗余可以保证数据不会丢失。
冗余可以产生在两个层面:
- 存储层面:可以在将文件写入到PV时就将数据存储多份
- 应用层面:比如镜像数据库和分片数据库
在存储层进行冗余并不是没有缺点,它会导致占用的存储空间成倍增长。
而且存储层的冗余并不能带来数据的高可用性。因为如果你只有一个数据库实例,在这个数据库宕机时,服务仍然无法访问(当然数据是不会丢失)。
所以如果你打算为高可用而组建分布式数据库,存在多个数据库实例,其实数据库本身就给你产生了冗余。此时在底层再次冗余只是浪费空间。
是否需要在PV层存储冗余?
对PV而言,本节主要讨论你是否需要在PV层面进行冗余。
现在,假设你的数据非常宝贵,你不希望丢失数据。
你打算将数据存储到自己的服务器中,而且你至少存在两个Worker,否则白搭。
- 如果你使用的是裸机组网,并且裸机的存储不存在RAID,即底层不存在任何冗余。那你需要在PV层使用冗余。
- 如果你使用的是云服务主机,并且你确信云服务主机已经为你提供了底层冗余,即只要它不跑路,你的数据不可能丢。那你不必在PV层使用冗余。
- 如果你打算在应用层(数据库)对数据进行冗余,此时在底层冗余也是不必要的。
安装持久存储
云原生存储
云原生存储是专为容器环境打造的存储方案。在Kubernetes中,推荐使用云原生存储,而非直接操作文件系统。
云原生存储带来的好处是:
- 它本身也部署于Kubernetes中,比较方便
- 它为你管理、分配存储资源
- 大部分方案提供备份、数据恢复、数据冗余等功能
云原生存储一览
目前比较火的有这些:
自己选一个吧
配置OpenEBS
**注意:**OpenEBS的部分镜像源无法在内陆访问,请自行权衡
我选择安装OpenEBS,当然你可以选择其它的
安装iSCSI
这一步需要在每个需要OpenEBS分配空间的机器上执行
如果你使用默认的配置安装,那OpenEBS会在所有Worker上分配
OpenEBS需要iSCSI支持,这个需要安装在系统上,不同系统的安装方法不一,可参考这里
在debian中,你可以使用以下命令检测iSCSI是否启动:
bash
systemctl status iscsid
如果找到服务,并且启动,你可以跳到下一步
如果找到服务,但已关闭,请启动:
bash
systemctl enable --now iscsid
如果没找到服务,请安装:
bash
apt-get install open-iscsi
systemctl enable --now iscsid
最后,检测服务确实正常运行:
bash
systemctl status iscsid
部署OpenEBS
按官网指示安装OpenEBS Operator
以及需要的存储支持
OpenEBS提供两种存储方案:
- 本地存储
- 镜像重复存储(远程存储)
首先查看默认的安装给你装了什么?
bash
kubectl get pods -n openebs -o wide | grep operator
如果没出现你想要的engine,你需要自己安装:
两种方案均有多种存储引擎可供选择,你可以根据自己的需求安装。
比如,你需要使用远程存储,你可以安装cStor
,Jiva
,Mayastor
中的一个。
使用Jiva引擎建立持久存储
我这里安装Jiva作为远程存储引擎
安装的具体步骤请参考官网指示
主要做三个事情:
- 安装Jiva引擎
- 配置Jiva
- 关于Jiva的镜像重复数(replicationFactor),请按需求填写,一般不超过安装了OpenEBS的Worker数
- 创建使用Jiva的Storage Class
提醒:创建完Policy和Storage Class记得apply
bash
kubectl apply -f <jiva-policy>
kubectl apply -f <jiva-storage-class>
最后,检测storage class正确创建:
bash
kubectl get sc
输出应该存在刚刚创建的storage class:
bash
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
openebs-device openebs.io/local Delete WaitForFirstConsumer false 3h46m
openebs-hostpath openebs.io/local Delete WaitForFirstConsumer false 3h46m
openebs-jiva-csi-sc jiva.csi.openebs.io Delete Immediate true 6s
由于Jiva Storage Class是支持动态提供(Dynamic Provisioning)的,因此我们不需要手动创建PV
设置默认存储类(可选)
设置默认存储类可以使其它应用在不指明存储类的时候也能使用持久存储。非常建议为集群设置一个默认存储类。
但这并不是必须的
默认存储类会在kubectl get sc
的输出中显示default
字样。按我们现在的做法,是没有默认存储类的(见上节输出)
我们可以使用以下指令设置/取消一个默认存储类:
bash
kubectl patch storageclass <storage-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
注意<storage-class-name>
填写存储类的名字;最后的布尔值控制是设置还是取消。
设置后再次检测storage class的情况,输出应如下:
bash
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
openebs-device openebs.io/local Delete WaitForFirstConsumer false 3h54m
openebs-hostpath openebs.io/local Delete WaitForFirstConsumer false 3h54m
openebs-jiva-csi-sc (default) jiva.csi.openebs.io Delete Immediate true 8m30s
配置外网入口
如果你需要支持公网访问,你需要进行这一步
本节将使用ingress nginx controller配置公网访问。
安装ingress nginx controller
安装ingress非常简单,根据官方教程,使用deployment安装:
bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
输入以下命令等待容器创建完毕
bash
watch kubectl get pods -n ingress-nginx
最后你会看到类似这样的结果,只要看到controller为Running
就是正常:
bash
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-6vlrc 0/1 Completed 0 5m12s
ingress-nginx-admission-patch-7wvqp 0/1 Completed 1 5m12s
ingress-nginx-controller-8558859656-zskdq 1/1 Running 0 5m12s
解决无法访问
此时查看支持ingress正常运作的LoadBalancer服务:
bash
kubectl get svc -n ingress-nginx
你会发现输出如下,EXTERNAL-IP
永远为pending
:
bash
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.111.15.62 <pending> 80:32503/TCP,443:30116/TCP 7m18s
ingress-nginx-controller-admission ClusterIP 10.97.192.44 <none> 443/TCP 7m18s
随之你会发现ingress nginx controller无法外部访问......
这是因为自建Kubernetes与云环境不同:自建集群并不自带负载均衡器实现,如果你此时创建LoadBalancer
,将永远处于pending
状态。
对此我们有两种做法:
- 安装负载均衡实现
- 不使用负载均衡直接访问
下面将介绍这两种做法,注意,并不是所有做法都适合你的情况,请注意阅读后续提及的先决条件和弊端。
**注意:**总共有3种做法,选择1种执行即可。
这些做法出自ingress nginx官方文档
安装负载均衡
方法1:MetalLB
裸机配置首选!!!
该方法只适合 所有结点(至少是所有Worker结点)处于同一内网。
也就是:硬件裸机,或者某些支持组内网的云服务商。(但我发现某些国外的vps商若处于同一机房也能用)
首先,请按照官方指南安装metallb
然后,配置IP池。最简单且内网通用的做法是使用Layer 2(数据链路)模式
你只需要将你的公网IP段加入IP池就好。
简单来说就是把你希望作为外网访问入口的IP段加入到IP池,MetalLB会将这些IP依次分配给出现的LoadBalancer
网段支持子网写法和范围写法
如果你的服务器只有单个IP,可以视为只有单个IP的子网<ip-address>/32
写法示意如下:
yaml
addresses:
- 123.123.10.0/24
- 123.123.9.1-123.123.9.5
- fc00:f853:0ccd:e799::/124
- 123.123.11.10/32
由于LoadBalancer是交通枢纽。如果你的服务器各自有流量限制,请尽量选择流量充足的服务器写进IP池。
因为MetalLB版本迭代非常快,因此这里也不放具体的配置了,请按照官方指南进行配置。
错误排查
你可能会遇到webhook错误,请尝试将MetalLB的Controller转移到Master结点。
错误原因不明,详情请参考这个issue。
执行结束后,输入以下命令可以查看nginx ingress的LoadBalancer IP:
bash
kubectl get svc -n ingress-nginx
如无意外,此时LoadBalancer显示的将是一个公网IP。
bash
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.111.15.62 <Public-IP> 80:32503/TCP,443:30116/TCP 21m
ingress-nginx-controller-admission ClusterIP 10.97.192.44 <none> 443/TCP 21m
方法2:外部LoadBalancer
效仿云服务,我们也可以自己整一个外部负载均衡器。这个负载均衡器可以是硬件或软件。
软件负载均衡器,推荐使用HAProxy
但这个方法我没试,看起来非常专业,有兴趣的朋友可以配置一下😘
直接连接
方法3:暴露主机网络
我们也可以不使用任何LoadBalancer,ingress nginx controller提供了直接对外访问的做法。
首先我们将部署文件下载下来:
bash
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
在里面找到kind: Deployment
的项,加入以下配置项(注意不要引入重复的项):
yaml
template:
spec:
hostNetwork: true
保存退出后,将其应用:
bash
kubectl apply -f deploy.yaml
此时检查nginx controller的情况:
bash
kubectl get pods -n ingress-nginx -o wide
你会发现nginx controller的IP变成了pod所在node的公网IP(前提是你的node真的有公网IP)
bash
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-admission-create-6vlrc 0/1 Completed 0 140m 10.244.7.11 rn-l-3500m7t <none> <none>
ingress-nginx-admission-patch-7wvqp 0/1 Completed 1 140m 10.244.6.13 rn-m-2g4t <none> <none>
ingress-nginx-controller-7f8dd58d99-s9qmg 1/1 Running 0 50s <public-ip> rn-m-2g4t <none> <none>
注意事项:
- 每个node最多只能运行一个nginx controller,因为它使用到了宿主机的资源。你无法将同一端口分配给两个nginx实例。
- 此时nginx controller的IP地址取决于其运行的node,这个IP可能并不是你所期望的。
那么怎么指定访问nginx的公网IP呢?
只能间接指定,有两个做法:
做法1:使用结点选择/亲和性配置,让nginx controller优先创建于你指定的node
首先为希望运行ingress的node创建一个type
为ingress
的标签(你可以更改为任意的键值对)
bash
kubectl label nodes <name_of_your_node> type=ingress
在部署nginx controller前,修改Deployment
在里面找到kind: Deployment
的项,加入以下配置项(注意不要引入重复的项):
yaml
template:
spec:
nodeSelector:
type: ingress
修改完后重新apply
做法2:使用daemon set部署nginx controller,让每个node(包括Master)都运行nginx controller
- 如果你希望健壮一点,可能这个比较好
- 当然它的运行开销也更大
验证外网访问
使用上步暴露的公网IP(各个做法不同),在外部浏览器中尝试进行访问。
如果显示nginx 404
页面,说明配置正确。
部署你的APP
恭喜,到这一步你已经配置好生产环境下使用的Kubernetes了
接下来你只需要:
- 将你的APP部署到Kubernetes
- 编写Ingress文件将流量从ingress nginx转发到应用中
- 请参考ingress nginx官方文档
- 注意:如果你使用IP访问,请不要 指明
spec.rules.host
;如果使用域名访问,请务必指明
- 访问自己的应用,确认已正常运作
配置域名和https
非必须,单如果是公开访问的应用,建议进行
这一步你需要准备:
- 一个解析至你应用的域名
- 一个合法的TLS证书
首先将证书内容复制到服务器,比如我的:
bash
root@master-1:~/cert# ls
mysite.com.crt mysite.com.key
创建一个包含证书内容的secret
bash
kubectl create secret tls <your-secret-name> --cert=mysite.com.crt --key=mysite.com.key
修改你的ingress(nginx)部署文件,加入以下结点:
yaml
spec:
tls:
- hosts:
- mysite.com
secretName: your-secret-name # 这个是上步的secret名称
rules:
- host: mysite.com # 站点名必须对应,否则返回404
重新apply
使用https://mysite.com
访问你的站点,确认TLS证书已生效
通关
大功告成!至此我们终于成功自建了Kubernetes网络
这一路上并不容易,但这仅仅只是一个开始
你还需要关注如何进行维护、备份和迁移......
但到此我有一个疑问
Docker、Kubernetes的出现不是为了简化应用部署的过程吗?
但搞起来怎么这么复杂呢?
程序员的工作是降低了,但运维真就哭了......
但无所谓,反正我这种oneman从来都是一个人全部搞定,四舍五入没节省任何时间😭
好吧,牢骚是发到这......
其实Kubernetes部署到部分云服务商还是很舒服的,只是我看到那个价格就已经饱了
所以自建Kubernetes虽然能达到生产环境的要求,但需要额外的维护!
除非你真的有充分的理由,否则云服务商还是一个不错的选择啊(除了很贵😅)