写点什么

著名 FinTech 公司如何使用 k3s+ 树莓派在生产中构建轻量 K8S 裸机集群

  • 2020-05-18
  • 本文字数:5249 字

    阅读完需:约 17 分钟

著名FinTech公司如何使用k3s+树莓派在生产中构建轻量K8S裸机集群

Boogie Software 是欧洲著名的金融科技公司,多年来致力于为银行提供 Fintech、AI、大数据高性能后端、移动应用程序、数据分析及 UX 等创新服务,帮助银行推动数字化转型。凭借过去十多年在该领域的独特经验,Boogie 已成为数字银行服务提供商中的领导者。

本文作者是 Boogie Software 的资深软件架构师 Jari Tenhunen。他拥有超过 15 年的软件开发经验,擅长信息安全和网络协议。并且长期管理项目和团队,在其中主导软件架构和技术,成功将多个产品推向市场。


Boogie Software 的 IT 团队在很多客户银行的核心银行业务数字化的项目中使用到了 Kubernetes 和容器技术,因此我们始终在想 Kubernetes 能如何使用更合适的硬件在本地工作。在本文中,我将详细介绍我们如何在树莓派上构建轻量级裸机集群,以在公司网络中运行应用程序和服务。


我们之所以这么做,有两个原因:第一,通过建立集群,我们将可以拥有一个平台,来可靠、灵活地运行公司网络内部应用程序和服务;第二,我们可以通过这次机会学习更多关于 Kubernetes、微服务以及容器的技能。如果你也想要参照我们的经验来构建一个相似的系统,我建议你至少要了解关于 Docker 容器、Kubernetes 关键概念(节点、pod、服务、deployment 等)以及 IP 网络的基础知识。

硬件准备

你需要准备以下设备:


  • 树莓派 2B/3B/3B+ 的型号,至少一个。你甚至在单个开发板上运行某些应用程序,但是建议使用两个或更多的开发板来分散负载并增加冗余。

  • 电源和可用于树莓派的 SD 卡,现有的以太网交换机或空闲端口以及一些电缆。


在我们的设置中,我们目前有 4 个树莓派 3 代 B+开发板,所以在集群中有一个 master/server 和 3 个代理节点。如果树莓派有外壳当然更好,我们的同事用 3d 打印机设计了一个。此外,机壳的背面有两个用于冷却的风扇,每个开发板都位于一个托盘上,该托盘可以热插拔以进行维护。这些托盘前面还设有 activity/heartbeat LED 和电源开关的位置,它们都连接到开发板的 GPIO 接头。



软件准备

对于 Kubernetes 的实现,我们使用的是 k3s。k3s是由 Rancher Labs 推出的一款轻量级、 通过 CNCF 一致性认证 的 Kubernetes 发行版。尽管这是一款刚推出不久的产品,但它真的 十分稳定和易用,可以实现秒级启动 。让 k3s 从其他轻量的 Kubernetes 发行版脱颖而出的原因是,k3s 可供生产使用,而诸如 microk8s 或 Minikube 之类的项目则无法实现这一目的,并且 k3s 十分轻巧,还可以在基于 ARM 的硬件上很好地运行。在 k3s 中,任何设备上安装 Kubernetes 所需的一切都包含在这一个 40MB 的二进制文件当中。


k3s 几乎能在任何 Linux 发行版中很好地运行,因此我们决定将 Raspbian Stretch Lite 作为基础 OS,因为我们不需要在开发板上添加任何额外的服务或者桌面 UI。k3s 确实需要在 Linux 内核中启用 cgroup,这可以在 Raspbian 上通过向/boot/cmdline.txt:添加以下参数来实现:


cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory
复制代码

安装 k3s

k3s 非常友好的地方在于,它可以实现平滑安装过程。你准备好你的 server 硬件之后,仅需几分钟就可以完成设置,因为它仅需一行命令就能安装 server(主节点):


curl -sfL https://get.k3s.io | sh -
复制代码


代理节点也是如此:


curl -sfL https://get.k3s.io | K3S_TOKEN=<token_from_server> K3S_URL=https://<server_ip>:6443 sh -
复制代码


其中 token_from_server 是来自服务器的文件/ var / lib / rancher / k3s / server / node-token 的内容,server_ip 是服务器节点的 IP 地址。至此,我们的集群已经启动并正在运行,我们可以开始部署工作负载:


root@k3s-server:~# kubectl get nodesNAME         STATUS   ROLES    AGE    VERSIONk3s-node1    Ready    <none>   40s    v1.13.4-k3s.1k3s-server   Ready    <none>   108s   v1.13.4-k3s.1
复制代码


为了管理和监控集群,我们安装了 Kubernetes Dashboard,它能够提供给非常方便的 web 界面来查看整个系统的状态、执行管理员操作并访问日志。同时,本地安装和运行 kubectl 命令也非常有帮助,因为它可以让你从自己的计算机管理集群,而无需 ssh 进入集群。为此,你只需要安装 kubectl,然后将集群信息从服务器节点 config /etc/rancher/k3s/k3s.yaml 复制到本地 kubeconfig 文件中(通常是 ${HOME}/.kube/config)。

使用负载均衡器暴露服务

默认情况下,部署在 Kubernetes 集群上的应用程序仅可以在集群中获取(默认服务类型是 ClusterIP)。如果想要从集群外部获取应用程序,有两个选项。你可以使用 NodePort 类型配置服务,该服务在静态端口的每个节点 IP 上暴露服务,你也可以使用负载均衡器(服务类型 LoadBalancer)。然而,NodePort 服务有限制:它们使用自己专用的端口范围,我们只能通过端口号来区分应用。k3s 内置了一个简单的负载均衡器,但由于它使用的是节点的 IP 地址,我们可能很快就会用完 IP/端口组合并且无法将服务绑定到某个虚拟 IP。基于这些原因,我们决定部署 MetalLB——一种用于裸机集群的负载均衡器实现。


只需应用 YAML manifest 即可安装 MetalLB。在现有网络中运行 MetalLB 的最简单方法是使用所谓的第 2 层模式,这意味着集群节点通过 ARP 协议宣布本地网络中服务的虚拟 IP。为此,我们从内部网络保留了一小部分 IP 地址用于集群服务。MetalLB 的配置如下所示:


apiVersion: v1kind: ConfigMapmetadata:  namespace: metallb-system  name: configdata:  config: |    address-pools:    - name: company-office      protocol: layer2      addresses:      - 10.10.10.50-10.10.10.99

复制代码


使用此配置,集群服务将被暴露在范围为 10.10.10.50—10.10.10.99 的地址中。为了绑定服务到指定的 IP,你可以在服务清单中使用 loadBalancerIP 参数:


apiVersion: v1kind: Servicemetadata:  name: my-web-appspec:  ports:  - name: http    port: 80    protocol: TCP    targetPort: 8080  loadBalancerIP: 10.10.10.51  selector:    app: my-web-app  type: LoadBalancer
复制代码


在负载均衡中,我们面临诸多挑战。例如,Kubernetes 中限制在单个负载均衡器中同时使用 TCP 和 UDP 端口。要解决这一问题,你可以定义两个服务实例,一个用于 TCP 端口,另一个用于 UDP 端口。其缺点是,除非启用 IP 地址共享,否则你需要在不同的 IP 地址中运行这两个服务。而且,由于 MetalLB 是一个年轻项目,因此也存在一些小问题,但我们相信这些很快都会得到解决。

添加存储

k3s 暂时没有内置的存储解决方案,所以为了使 Pod 能够访问持久性文件存储,我们需要使用 Kubernetes 的插件来创建一个。由于 Kubernetes 的目标之一是使应用程序与基础架构解耦并使其可移植,因此 Kubernetes 中用 PersistentVolume(PV)和 PersistentVolumeClaim(PVC)的概念定义了用于存储的抽象层。详细的概念解释可以参照我们之前发过的文章:详解Kubernetes存储关键概念。PV 是通常由管理员配置并可供应用程序使用的存储资源。另一方面,PVC 描述了应用程序对某种类型和一定数量的存储的需求。创建 PVC(通常作为应用程序的一部分)时,如果有一个尚未使用且满足应用程序 PVC 要求的可用 PVC,它将绑定到 PV。配置和维护所有这些需要手动工作,因此动态配置卷应运而生。


在我们的基础架构中,我们已经有一个现有的 NFS 服务器,因此我们决定将其用于集群持久性文件存储。在我们的案例中,最简单的方法是使用支持动态配置 PV 的 NFS-Client Provisioner。Provisioner 只需在现有的 NFS 共享上为每个新 PV(集群映射到 PVC)上创建新目录,然后将 PV 目录挂载在使用它的容器中。这样就无需配置 NFS 共享到单个 pod 中的卷,而是全部动态运行。

为 ARM 交叉构建容器镜像

显然,在基于 ARM 的硬件上(如树莓派)运行应用程序容器时,需要根据 ARM 的架构构建容器。在 ARM 架构容器中构建自己的应用程序时,可能会遇到一些陷阱。首先,基础镜像需要可用于你的目标架构体系。对于树莓派 3 来说,通常需要使用 arm32v7 的基础镜像,它们可以在大部分 Docker 镜像仓库中被调用。所以,当交叉构建应用程序时,确保你的 Dockerfile 包含以下代码:


FROM arm32v7/alpine:latest
复制代码


第二件需要注意的事是,你的主机 Docker 需要能够运行 ARM 二进制文件。如果你在 mac 上运行 Docker,那操作将十分轻松,因为它对此有内置支持。如果是在 Linux 上,你需要执行一些步骤:

添加 QEMU 二进制文件到你的基础镜像

为了在 Linux 上的 Docker 中运行 ARM 二进制文件,镜像需要一个 QEMU 二进制文件。你可以选择一个已经包含了 QEMU 二进制文件的基础镜像,也可以在镜像构建过程中复制


qemu-arm-static 二进制文件到其中,例如,通过将以下行添加到你的 Dockerfile 中:


COPY --from=biarms/qemu-bin /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static
复制代码


安全警示:请注意下载和运行未知的容器就如同下载和运行位置的.exe 文件。除业余项目外,其他任何项目都应使用扫描/审核过的镜像(如 Docker 官方镜像)或来自信任的组织和公司的容器镜像。


然后,你需要在创建 Docker 镜像的主机 OS 上注册 QEMU。这可以简单地通过以下方式实现:


docker run --rm --privileged multiarch/qemu-user-static:register --reset
复制代码


可以在构建实际镜像之前将该命令添加到你的构建脚本中。总结一下,你的 Dockerfile.arm 应该看起来像这样:


FROM arm32v7/alpine:latestCOPY --from=biarms/qemu-bin /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static# commands to build your app go here…# e.g. RUN apk add --update <pkgs that you need…>
复制代码


并且你的 build /CI 脚本应该是:


docker run --rm --privileged multiarch/qemu-user-static:register --resetdocker build -t my-custom-image-arm . -f Dockerfile.arm
复制代码


这将为你提供 ARM 架构的容器镜像。如果你对细节很感兴趣,请参阅:


https://www.ecliptik.com/Cross-Building-and-Running-Multi-Arch-Docker-Images/

自动化构建和上传到镜像仓库

最后一步是自动化整个流程,以便容器镜像可以自动构建并且自动上传到一个镜像仓库,在那里可以轻松地将其部署到我们地 k3s 集群。在内部,我们使用 GitLab 进行源代码管理和 CI/CD,因此我们自然希望在其中运行这些构建,它甚至包括一个内置的容器镜像仓库,因此不需要设置单独的镜像仓库。


关于构建 Docker 镜像,GitLab 有十分完善的文档(https://docs.gitlab.com/ee/ci/docker/using_docker_build.html),因此我们不在此赘述。在为 docker 构建配置 GitLab Runner 之后,剩下要做的就是为该项目创建.gitlab-ci.yml 文件。在我们的例子中,它看起来像这样:


image: docker:stable
stages: - build - release variables: DOCKER_DRIVER: overlay2 CONTAINER_TEST_IMAGE: ${CI_REGISTRY_IMAGE}/${CI_PROJECT_NAME}-arm:${CI_COMMIT_REF_SLUG} CONTAINER_RELEASE_IMAGE: ${CI_REGISTRY_IMAGE}/${CI_PROJECT_NAME}-arm:latest before_script:- docker info- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY build_image: stage: build script: - docker pull $CONTAINER_RELEASE_IMAGE || true - docker run --rm --privileged multiarch/qemu-user-static:register --reset - docker build --cache-from $CONTAINER_RELEASE_IMAGE -t $CONTAINER_TEST_IMAGE . -f Dockerfile.arm - docker push $CONTAINER_TEST_IMAGE release: stage: release script: - docker pull $CONTAINER_TEST_IMAGE - docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE - docker push $CONTAINER_RELEASE_IMAGE
复制代码


既然在容器镜像仓库中我们有了我们的镜像,我们只需要将它们部署到我们的集群中。为了授予集群访问镜像仓库的权限,我们在 GitLab 中创建了一个 deploy 令牌,然后将令牌凭据作为 docker-registry 密钥添加到集群中:


kubectl create secret docker-registry deploycred --docker-server=<your-registry-server> --docker-username=<token-username> --docker-password=<token-password> --docker-email=<your-email>
复制代码


之后,可以在 YAML 文件 PodSpec 中使用 deploy 令牌密钥:


    imagePullSecrets:      - name: deploycred     containers:      - name: myapp        image: gitlab.mycompany.com:4567/my/project/my-app-arm:latest
复制代码


完成所有这些步骤之后,我们终于拥有了一个从私有镜像仓库中的源代码到 ARM 容器镜像的自动 CI / CD 流水线,可以将其部署到集群中。

结语

总而言之,事实证明,建立和运行自己的裸机 Kubernetes 集群比预期的要容易。而且 k3s 确实是在边缘计算场景中和一般配置较低的硬件上运行容器化服务的明智选择。


一个小缺点是 k3s 尚不支持高可用(多主设备配置)。尽管单个主服务器设置已经具有相当的弹性,因为即使主服务器离线,服务仍可在代理节点上继续运行,我们还是希望为主节点提供一些冗余。显然,此功能正在开发中,但在此功能可用之前,我们建议从服务器节点配置中进行备份。


2020-05-18 18:01988

评论

发布
暂无评论
发现更多内容

Python 教程之变量

芯动大师

变量 9月月更 Python语法

PLG SaaS 产品 Figma 商业模式拆解

程序员泥瓦匠

SaaS

在windows电脑上配置kubectl远程操作kubernetes

程序员欣宸

Kubernetes 9月月更

Qt|ListWidget控件总结

中国好公民st

List qt 9月月更

面试突击85:为什么事务@Transactional会失效?

王磊

Java 面试

2022-09-20:以下go语言代码输出什么?A:8 8;B:8 16;C:16 16;D:16 8。 package main import ( “unsafe“ “fmt“ )

福大大架构师每日一题

golang 福大大 选择题

Python语法之数据类型

芯动大师

Python 数据类型 9月月更

Chrome操作指南——入门篇(五)Snippets

Augus

Chrome开发者工具 9月月更

Web3.0杂谈-#001(47/100)

hackstoic

Web3.0

【JVM】深入解析G1的并发标记与三色标记

小明Java问道之路

GC 三色标记 G1垃圾回收器 9月月更 并发标记

挑战30天学完Python:Day3夯实基础-布尔值和运算符

MegaQi

9月月更 挑战30天学完Python

《简单记个笔记》之部分CSS选择器介绍

吉师职业混子

9月月更

《简单记个笔记》之表单标签加CSS选择器

吉师职业混子

9月月更

面对全新的编程语言,这些思路可以帮助你察觉漏洞

网络安全学海

黑客 网络安全 信息安全 渗透测试 漏洞利用

【字符串函数内功修炼】strlen + strstr + strtok + strerror(三)

Albert Edison

C语言 9月月更 strlen strstr strtok

4 分钟优化 Fetch 函数写法~

掘金安东尼

前端 9月月更

【云原生 | 从零开始学Kubernetes】一、kubernetes到底是个啥

泡泡

云计算 云原生 k8s 9月月更

概述构建应用智能运维系统的核心能力

阿泽🧸

智能运维 9月月更

Python教程之Python简介

芯动大师

编程语言 9月月更 Python简介

ESP32-C3入门教程 基础篇(四、I2C总线 — 与SHT21温湿度传感器通讯)

矜辰所致

I2C I2C协议 ESP32-C3 9月月更

开发者有话说|情分 or 本分

卷卷龙

个人成长 职场 PUA

通过爬虫爬取一些图片

吉师职业混子

9月月更

【数据结构】五分钟带你了解及自定义有向图

迷彩

数据结构 算法 无向图 9月月更 有向图

[极致用户体验] 多页面应用里,「网页内返回」按钮,何时用 history.back 何时用 replaceState?

HullQin

CSS JavaScript html 前端 9月月更

ESP32-C3入门教程 基础篇(三、UART模块 — 与Enocean无线模块串口通信)

矜辰所致

ESP32-C3 9月月更 UART

监控系统的阶段建设

穿过生命散发芬芳

监控系统 9月月更

C++学习---cstdio的源码学习分析04-创建临时文件函数tmpfile

桑榆

c++ 源码阅读 9月月更

【JVM】深入解读G1垃圾回收器

小明Java问道之路

并发 垃圾回收 GC G1垃圾回收器 9月月更

开发者有话说|时间过得真快,我也是一个“奔三”的人了

武师叔

个人成长

2022-09-19:给定字符串 S and T,找出 S 中最短的(连续)子串 W ,使得 T 是 W 的 子序列 。 如果 S 中没有窗口可以包含 T 中的所有字符,返回空字符串 ““。 如果有不

福大大架构师每日一题

算法 rust 福大大

史上最详细vue的入门基础

楠羽

Vue 笔记 9月月更

著名FinTech公司如何使用k3s+树莓派在生产中构建轻量K8S裸机集群_文化 & 方法_Rancher_InfoQ精选文章