Boogie Software是欧洲著名的金融科技公司,多年来致力于为银行提供Fintech、AI、大数据高性能后端、移动应用程序、数据分析及UX等创新服务,帮助银行推进数字化转型。凭借过去十多年在该领域的独特经验,Boogie已成为数字银行服务提供商中的领导者。html
本文做者是Boogie Software的资深软件架构师Jari Tenhunen。他拥有超过15年的软件开发经验,擅长信息安全和网络协议。而且长期管理项目和团队,在其中主导软件架构和技术,成功将多个产品推向市场。node
Boogie Software的IT团队在不少客户银行的核心银行业务数字化的项目中使用到了Kubernetes和容器技术,所以咱们始终在想Kubernetes能如何使用更合适的硬件在本地工做。在本文中,我将详细介绍咱们如何在树莓派上构建轻量级裸机集群,以在公司网络中运行应用程序和服务。git
咱们之因此这么作,有两个缘由:第一,经过创建集群,咱们将能够拥有一个平台,来可靠、灵活地运行公司网络内部应用程序和服务;第二,咱们能够经过此次机会学习更多关于Kubernetes、微服务以及容器的技能。若是你也想要参照咱们的经验来构建一个类似的系统,我建议你至少要了解关于Docker容器、Kubernetes关键概念(节点、pod、服务、deployment等)以及IP网络的基础知识。web
你须要准备如下设备:docker
树莓派2B/3B/3B+ 的型号,至少一个。你甚至在单个开发板上运行某些应用程序,可是建议使用两个或更多的开发板来分散负载并增长冗余。后端
电源和可用于树莓派的SD卡,现有的以太网交换机或空闲端口以及一些电缆。api
在咱们的设置中,咱们目前有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很是友好的地方在于,它能够实现平滑安装过程。你准备好你的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 nodes NAME STATUS ROLES AGE VERSION k3s-node1 Ready <none> 40s v1.13.4-k3s.1 k3s-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: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: 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: v1 kind: Service metadata: name: my-web-app spec: 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架构容器中构建本身的应用程序时,可能会遇到一些陷阱。首先,基础镜像须要可用于你的目标架构体系。对于树莓派3来讲,一般须要使用arm32v7的基础镜像,它们能够在大部分Docker镜像仓库中被调用。因此,当交叉构建应用程序时,确保你的Dockerfile包含如下代码:
FROM arm32v7/alpine:latest
第二件须要注意的事是,你的主机Docker须要可以运行ARM二进制文件。若是你在mac上运行Docker,那操做将十分轻松,由于它对此有内置支持。若是是在Linux上,你须要执行一些步骤:
为了在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:latest COPY --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 --reset docker 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尚不支持高可用(多主设备配置)。尽管单个主服务器设置已经具备至关的弹性,由于即便主服务器离线,服务仍可在代理节点上继续运行,咱们仍是但愿为主节点提供一些冗余。显然,此功能正在开发中,但在此功能可用以前,咱们建议从服务器节点配置中进行备份。