《树莓派4B家庭服务器搭建指南》第十四期:使用树莓派4B搭建k8s集群


title: 《树莓派4B家庭服务器搭建指南》第十四期:使用树莓派4B搭建k8s集群

k8s是谷歌推出一款服务器集群管理工具, 开源免费, 功能强大, 可以创造一个人管理服务器集群的奇迹。

k8s 通用型极强,是一套标准的运维策略,只要程序部署到服务器,就要考虑程序稳定性的问题,而k8s的弹性扩容,以及多主机相互备份策略,即使部分服务器物理宕机,被黑客DDOS攻击,也能通过内置策略自动进行应对。

k8s由谷歌开源,而且谷歌自家也在用这套方案,我们能免费获得持续稳定的技术支持和版本迭代。

k8s是一套商业级的完整解决方案,如果想要踏入专业的运维领域, 获得一份糊口的运维工作, 也要熟悉k8s这套堪称业界标杆的工具。

这篇博客记录zhaoolee使用树莓派建立k8s集群的全过程,如果你对树莓派k8s集群感兴趣,这篇博客会给你很多帮助。

首先,将树莓派连接到同一个局域网环境下, 为了保证稳定性,最好将路由器与树莓派通过网线相连,为了避开乱七八糟的依赖包下载问题,路由器需要支持科学上网~

本文使用的三台树莓派4B均为8GB版本,镜像选的是Ubuntu20.04, 如果对树莓派刷镜像有疑问,可以点击往期 《树莓派4B家庭服务器搭建指南》刷Ubuntu Server 20.04,绑定公网域名,对公网提供http服务,SSH登录服务 https://www.v2fy.com/p/2021-10-01-pi-server-1633066843000/

本文安装的K8s版本为v1.23.1

首先三台树莓派运行的ubuntu20.04,安装网络工具包

sudo apt install net-tools -y

为三台树莓派设置固定的ip

可以在路由器Web端通过树莓派mac地址与ip进行绑定完成设置

mac地址与ip进行绑定

也可以通过树莓派直接设置静态ip, 参考教程 为Ubuntu 20.04 设置静态IP简明教程(和把大象装冰箱一样简单)https://www.v2fy.com/p/2022-01-01-ip-1641016585000/

通过 hostnamectl 设置主机名

我的路由器为三台树莓派分别分配了192.168.50.10, 192.168.50.20, 192.168.50.30, 后面的内容也会反复提到这几个ip,如果你的ip与我不同,请自行替换即可。

在ip为192.168.50.10的 master主机执行

hostnamectl set-hostname master

执行hostnamectl set-hostname

在ip为192.168.50.20的 node1主机执行

hostnamectl set-hostname node1

在ip为192.168.50.30的 node2主机执行

hostnamectl set-hostname node2

设置完成后,可通过以下命令查看本机信息

hostnamectl status

本机信息

如果你的shell终端显示主机名,则可以通过主机名进行方便的区分

主机名区分

修改host并创建别名

在三台树莓派的/etc/hosts内添加以下配置

192.168.50.10 master
192.168.50.20 node1
192.168.50.30 node2

Ubuntu20.04 已经放弃的SELinux,所以关于SELinux的设置,我们无需处理

K8s V1.22版本开始已经支持使用swap,我们也无需关闭swap内存

为了保证master与node之间的通信,我们需要关闭树莓派防火墙

ufw,或称Uncomplicated Firewall,是iptables的一个接口,为不熟悉防火墙概念的初学者提供了易于使用的界面,同时支持IPv4和IPv6,广受欢迎。Ubuntu20.04 默认安装了防火墙管理工具ufw,我们可以在三台树莓派执行以下命令,关闭防火墙。

# 关闭防火墙
sudo ufw disable
# 检测防火墙状态
sudo ufw status

关闭防火墙

树莓派一般在内网,不容易遭受攻击,如果是外网的机器,建议还是只开放必要的端口

需要开放端口的参考资料:https://kubernetes.io/docs/reference/ports-and-protocols/

安装Docker

三台树莓派各自运行以下命令,完成Docker的安装

sudo apt update

sudo apt install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release -y

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update

sudo apt install docker-ce=5:20.10.8~3-0~ubuntu-focal docker-ce-cli=5:20.10.9~3-0~ubuntu-focal containerd.io=1.4.11-1 -y

docker -v

docker

安装k8s

  • 在三台树莓派 安装 kubectl kubelet kubeadm
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl

sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg


echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list


sudo apt-get update
sudo apt-get install -y kubectl kubelet kubeadm
  • 查看kubectl是否安装成功
kubectl version --client

检测kubectl

  • 查看kubelet是否安装成功
kubelet --version

检测kubelet

  • 查看kubeadm是否安装成功
kubeadm version

检测kubeadm

在三台树莓派 运行以下命令,保证kubelet和docker启动,并设置开机启动

sudo systemctl enable kubelet
sudo systemctl start kubelet
sudo systemctl enable docker
sudo systemctl start docker

修改docker 配置

sudo chmod 777 -R /opt/
cd /opt
# kubernetes 官方推荐 docker 等使用 systemd 作为 cgroupdriver,否则 kubelet 启动不了
cat <<EOF > daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
sudo mv daemon.json /etc/docker/

# 重启生效
sudo systemctl daemon-reload
sudo systemctl restart docker

修改树莓派的配置,并重启

sudo vim /boot/firmware/cmdline.txt

新增修改

cgroup_enable=memory cgroup_memory=1

修改完成后,重启树莓派

reboot

在master节点初始化集群

sudo kubeadm init --pod-network-cidr=10.244.0.0/16

注意这里要追加--pod-network-cidr=10.244.0.0/16参数

获得token文本

最后的token文本要好好保存起来,子节点加入master要用到

kubeadm join 192.168.50.10:6443 --token tn3*********9f7  --discovery-token-ca-cert-hash sha256:3862***46739303********94
  • 将kubeadm授权文件复制到用户根目录,以便kubectl 可以有权限访问集群
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

最终授权文件的内容,如下图的格式

授权文件

  • 在master节点安装一个网络插件,方便node节点后续连接通信
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
  • 安装插件后,等待各节点Ready
kuberctl get nodes

安装网络插件,等待master节点Ready

在node1和node2运行以下命令,将node1和node2加入到master节点

sudo kubeadm join 192.168.50.10:6443 --token tn3*********9f7  --discovery-token-ca-cert-hash sha256:3862***46739303********94

以上命令中的token参数,都由master节点,运行 sudo kubeadm init 时产生,如果忘记了可以通过在master节点运行sudo kubeadm token create --print-join-command 重新获取

  • 在master节点运行kubectl get nodes 查看各节点状态,当所有节点Ready, 即大功告成!

安装Kuboard

mkdir ~/kuboard-data
sudo docker run -d --restart=unless-stopped --name=kuboard -p 30080:80/tcp -p 10081:10081/tcp -e KUBOARD_ENDPOINT="http://192.168.50.10:30080" -e KUBOARD_AGENT_SERVER_TCP_PORT="10081" -v ~/kuboard-data:/data eipwork/kuboard:v3

访问 http://192.168.50.10:30080 即可访问到Kuboard

登录用户名: admin
登录密码: Kuboard123

Kuboard

  • 添加集群

添加集群

  • 选择.kubeconfig方式导入集群

导入集群

  • 获取 ~/.kube/config信息
cat ~/.kube/config

将~/.kube/config的文本信息贴入页面

导入集群

导入成功

导入成功

所有namespace和节点信息尽收眼底

信息尽收眼底

创建service

创建Service

apiVersion: apps/v1
kind: Deployment
metadata:
  # 部署名字
  name: pi-k8s-test
  namespace: ingress-nginx
spec:
  replicas: 2
  # 用来查找关联的 Pod,所有标签都匹配才行
  selector:
    matchLabels:
      app: pi-k8s-test
  # 定义 Pod 相关数据
  template:
    metadata:
      labels:
        app: pi-k8s-test
    spec:
      # 定义容器,可以多个
      containers:
      - name: pi-k8s-test # 容器名字
        image: zhaoolee/pi-k8s-test:001 # 镜像

---
apiVersion: v1
kind: Service
metadata:
  name: pi-k8s-test
  namespace: ingress-nginx
spec:
  selector:
    app: pi-k8s-test
  type: NodePort
  ports:
    - port: 3000        # 本 Service 的端口
      targetPort: 3000  # 容器端口

从上面的yaml文本中,可以看出,我们使用镜像zhaoolee/pi-k8s-test:001, 新建了两个pod(pod可以容纳N个镜像运行产生的容器),这两个pod会被k8s自动调度到不同的节点上,node1和node2各一个

不同节点

而yaml文件的下半部分,则创建了一个服务service,这个服务是pod的父级,我们可以通过service暴露的3000端口来访问pod,service也就是一个原子化的服务,我们可以创建大量的service, 来应对不同的场景

但大量的service管理起来很麻烦,占用的端口也很杂乱,我们还需要一个类似Nginx的网关,来统一接收外部请求,然后通过在Nginx设置好的规则,将请求分发到对应的service, 这个类似Nginx的部分,在k8s中被称为ingress, 而ingress恰好有一个基于nginx开发的版本,被称为NGINX Ingress Controller,下面我将开始配置安装NGINX Ingress Controller

安装helm

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

这个helm可以方便我们方便的安装NGINX Ingress Controller, 以及后续白嫖很多别人现成的服务配置。

安装NGINX Ingress Controller

helm upgrade --install ingress-nginx ingress-nginx --repo https://kubernetes.github.io/ingress-nginx  --namespace ingress-nginx --create-namespace

参考地址 https://kubernetes.github.io/ingress-nginx/deploy/#quick-start

与nginx类型,NGINX Ingress Controller需要配置规则才能按照我们的需求转发请求,下面我们配置一个简单规则。

为ingress添加规则

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress
  namespace: ingress-nginx
spec:
  ingressClassName: nginx
  rules:
    - http:
        paths:
          - backend:
              service:
                name: pi-k8s-test
                port:
                  number: 3000
            path: /
            pathType: ImplementationSpecific

上面的配置文件,可以通过kuboard直接在页面上,通过从YAML创建出来,它配置的规则就是,把指向根路径/的请求,统统发送到pi-k8s-test服务的3000端口。

如果报错Internal error occurred: failed calling webhook “validate.nginx.ingress.kubernetes.io, 则

kubectl get validatingwebhookconfigurations
kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission

虽然ingress也是个服务,但ingress这个服务比较特别,它需要获得和树莓派级别的ip才能运行(类似192.168.50.*这种格式),ingress被称为loadbalancer类型的服务。

但k8s 本体不提供对 loadbalancer类型的支持,我们需要自己额外去安装一个名为metallb的开源软件,获得k8s对loadbalancer类型支持。如果我们不安装metallb,loadbalancer类型的服务,只能一直保持在pending的状态。

安装metallb

首先开启strictARP选项

kubectl edit configmap -n kube-system kube-proxy

输入以上命令后,会进入vim编辑器模式,我们将配置中的strictARP: false 改为 strictARP: true

strictARP: true
  • 创建metallb专用的namespace
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.5/manifests/namespace.yaml
  • 部署metallb
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.5/manifests/metallb.yaml
  • 创建memberlist secret, 这个secret是用来加密speaker之间的通信
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
  • 通过kuboard 创建ConfigMap

导入

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.50.240-192.168.50.250

这里注意最后一行addresses配置,我树莓派上层的路由器是192.160.50.* 段,所以配置为192.168.50.240-192.168.50.250 请按照自己路由器的配置,按需更改。

  • 查看LoadBalancer类型的ingress-nginx-controller
kubectl get svc -A

分配到ip

ingress-nginx-controller被分配到的ip为192.168.50.240

我们在局域网的电脑中访问http://192.168.50.240

访问

我们访问192.168.50.240,就相当于访问了ingress-nginx-controller, 而ingress-nginx-controller会把我们的请求,按照规则,转发到我们前面建立的service中,service自动将请求转发到两个Pod中进行分别处理,而多个Pod本身运行在不同的node节点上(不同树莓派上),即使某个node挂掉,也可对外稳定提供服务。

如果我们收到的请求量变大,我们通过增大pod数量,或增加更多树莓派(node节点)的方式,处理请求。

参考资料:https://www.bboy.app/2021/01/11/metallb%E9%83%A8%E7%BD%B2%E4%BD%BF%E7%94%A8/

如何快速变更镜像?

我们前面使用了zhaoolee/pi-k8s-test:001镜像,如果要变更到zhaoolee/pi-k8s-test:002镜像,只需打开kuboard,改镜像版本即可。

更新镜像

k8s集群会自动拉取镜像,完成更新

  • 内容更新成功

更新成功

将树莓派服务穿透到外网

通过前几步的准备,我们已经将 192.168.50.240 做为我们的k8s集群入口,接下来我们只需通过frp将 192.168.50.240 穿透到公网的k8s.v2fy.com, 即可实现外网访问树莓派集群提供的服务

  • 最终效果展示

    k8s.v2fy.com

192.168.50.10负责运行master, 并不能访问我们的负载均衡ip 192.168.50.240, 而node1(192.168.50.20)节点和node2(192.168.50.20)节点所在的机器可以顺利访问负载均衡ip

我们选择在node1节点所在的树莓派上,开启frp客户端服务

  • frpc.ini配置文件
[common]
server_addr = 120.76.136.220
server_port = 7000
token = '********'
log_file = './frpc.log'

[pi-k8s]
type = tcp
local_ip = 192.168.50.240
local_port = 80
remote_port = 9666

配置文件的作用是,将内网192.168.50.240:80 穿透到公网120.76.136.220:9666

frp安装使用教程请参考 《树莓派4B家庭服务器搭建指南》刷Ubuntu Server 20.04,绑定公网域名,对公网提供http服务,SSH登录服务 https://www.v2fy.com/p/2021-10-01-pi-server-1633066843000/

  • 将自己的域名,(比如k8s.v2fy.com) ,解析到公网服务器ip (比如120.76.136.220)

然后通过公网服务器Nginx,代理k8s.v2fy.com, 将指向k8s.v2fy.com 80端口和443端口的请求,全部转发到9666端口

Nginx参考配置文件 /etc/nginx/conf.d/k8s.v2fy.com.conf

upstream k8s_v2fy_com { server 127.0.0.1:9666; }

server {
    server_name      k8s.v2fy.com;
    listen       80;
    listen       [::]:80;
    rewrite ^(.*)$ https://$host$1 permanent;
}


server {
    listen       443 ssl http2;
    listen       [::]:443 ssl http2;
    server_name  k8s.v2fy.com;

    location / {
        proxy_pass http://k8s_v2fy_com;
        proxy_set_header Host $host:443;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    ssl_certificate "/etc/nginx/ssl/k8s.v2fy.com/fullchain.cer";
    ssl_certificate_key "/etc/nginx/ssl/k8s.v2fy.com/k8s.v2fy.com.key";
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  10m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

    error_page 404 /404.html;
        location = /40x.html {
    }

    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}
  • 如何获取自动续期的https证书?

请参考 零依赖!使用acme.sh设置nginx多个https证书自动更新,无限续期https证书 https://www.v2fy.com/p/2021-06-27-nginx-https-1624774964000/

安装证书完成后,记得重启Nginx

sudo nginx -t
sudo systemctl restart nginx
  • 最后我们可以通过https://k8s.v2fy.com 访问到我们内网k8s的服务了

小动物图片

椅子

冬日

小结

如果你只是想学习k8s技术,而不是搭建集群,完全可以用minikube来学习, Ubuntu20.04试水k8s单机版minikube部署实录 https://www.v2fy.com/p/2021-07-26-k8s-1627292526000/

我从2022年元旦开始,便开始折腾树莓派裸机搭建k8s,时至发文,已有5天。k8s确实是诱人的技术,技术爱好者经过几天的学习,便能顺利搭建自己的集群,并跑起自建的服务。k8s集群设计的思想,也确实称得上极好的教材,通过层层抽象分离,让各种服务pod的调度变得明确,排查问题也变得简单,同时让众多服务的管理变得简单。

2022年, 投机者依然想着发各种币来割韭菜, 挖矿消耗了大量电力, 只是为了算一个无意义的字符串, 即使不懂计算机的人, 喊两句分布式, 就能成为一名合格的韭菜, 生生不息, 真正通过分布式管理服务器集群的k8s系统却无人问津, 毕竟提供稳定的系统产生的价值无法被大多数人理解, 著名艺术家孙宇晨老师拿着祖传100万到处炒热度, 蹭热点远比理解分布式系统的人来钱快, 但能提升生产力的k8s技术, 学得好, 不用反复横跳, 也能卖个好价钱, 即使是挖矿, 用k8s集群批量管理挖矿主机的人, 也能对那些开着N台Windows挖矿的人形成降维打击。

本文永久更新地址(欢迎来读留言,写评论):

https://www.v2fy.com/p/2022-01-05-k8s-pi-1641393748000