
就在最近,一家知名公司宣布将其笔记本电脑系列转移到 ARM 架构。 当我听到这个消息时,我想起:当我再次查看AWS中EC2的价格时,我发现Gravitons的价格非常可观。 当然,问题在于它是 ARM。 当时我没想到ARM是这么认真的……
对我来说,这种架构一直是移动和其他物联网事物的领域。 ARM 上的“真正”服务器在某种程度上是不寻常的,在某些方面甚至是疯狂的……然而,一个新的想法浮现在我的脑海中,所以一个周末我决定检查一下今天可以在 ARM 上运行什么。 为此,我决定从一些亲近的东西开始——Kubernetes 集群。 不仅仅是某种传统的“集群”,而是一切“以成人的方式”,因此它尽可能地与我习惯在生产中看到的相同。
根据我的想法,集群应该可以从互联网访问,一些Web应用程序应该在其中运行,并且至少应该有监控。 要实现这个想法,您需要一对(或更多)至少型号为 3B+ 的 Raspberry Pi。 AWS 本来可以成为一个实验平台,但我对“树莓派”(仍然闲置)感兴趣。 因此,我们将部署一个带有 Ingress、Prometheus 和 Grafana 的 Kubernetes 集群。
准备“覆盆子”
安装操作系统和 SSH
我并没有太费心去选择要安装的操作系统:我只是选择了最新的 Raspberry Pi OS Lite 。 在那里可用 ,其中的所有操作都必须在未来集群的所有节点上执行。 接下来,您将需要执行以下操作(也在所有节点上)。
连接显示器和键盘后,必须首先配置网络和 SSH:
- 为了使集群运行,master 必须有一个静态 IP 地址,worker 节点也必须有一个静态 IP 地址。 为了便于设置,我自始至终更喜欢静态地址。
- 静态地址可以在操作系统中配置(在文件中
/etc/dhcpcd.conf有一个合适的例子)或通过修复所用(在我的例子中是家庭)路由器的 DHCP 服务器中的租约。 - ssh-server 只是包含在 raspi-config 中(接口选项 → ssh).
之后就可以通过SSH登录了(默认登录是 pi密码是 raspberry 或您更改为的那个)并继续设置。
其他设置
- 让我们设置主机名。 在我的例子中他们将使用
pi-controlиpi-worker. - 让我们检查一下文件系统是否已扩展以覆盖整个磁盘(
df -h /)。 如果需要,可以使用 raspi-config 进行扩展。 - 让我们更改 raspi-config 中的默认用户密码。
- 让我们关闭交换文件(这是 Kubernetes 的要求;如果您对此主题的详细信息感兴趣,请参阅 ):
dphys-swapfile swapoff systemctl disable dphys-swapfile - 让我们将软件包更新到最新版本:
apt-get update && apt-get dist-upgrade -y - 让我们安装 Docker 和其他软件包:
apt-get install -y docker docker.io apt-transport-https curl bridge-utils iptables-persistent当您安装
iptables-persistent您需要保存 ipv4 的 iptables 设置,并在文件中/etc/iptables/rules.v4- 添加规则到链中FORWARD,像这样:# Generated by xtables-save v1.8.2 on Sun Jul 19 00:27:43 2020 *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A FORWARD -s 10.1.0.0/16 -j ACCEPT -A FORWARD -d 10.1.0.0/16 -j ACCEPT COMMIT - 剩下的就是重新启动。
现在您已准备好安装 Kubernetes 集群。
安装 Kubernetes
在这个阶段,我特意搁置了我和我们公司的所有开发,以自动化K8s集群的安装和配置。 相反,让我们使用官方文档 (稍微补充了评论和缩写)。
让我们添加 Kubernetes 存储库:
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update在文档中进一步建议安装 CRI(容器运行时接口)。 由于 Docker 已经安装,让我们继续安装主要组件:
sudo apt-get install -y kubelet kubeadm kubectl kubernetes-cni 在安装主要组件的步骤中,我立即添加了 kubernetes-cni,这是集群运行所必需的。 这里有一个重要的点:包 kubernetes-cni 由于某种原因,它没有为 CNI 接口设置创建默认目录,因此我必须手动创建它:
mkdir -p /etc/cni/net.d为了使网络后端正常工作(将在下面讨论),您需要安装 CNI 插件。 我选择了我熟悉且易于理解的portmap插件 (有关完整列表,请参阅 ):
curl -sL https://github.com/containernetworking/plugins/releases/download/v0.7.5/cni-plugins-arm-v0.7.5.tgz | tar zxvf - -C /opt/cni/bin/ ./portmap配置 Kubernetes
具有控制平面的节点
安装集群本身非常简单。 为了加快此过程并验证 Kubernetes 映像是否可用,您可以首先运行:
kubeadm config images pull现在我们执行安装本身 - 初始化集群的控制平面:
kubeadm init --pod-network-cidr=10.1.0.0/16 --service-cidr=10.2.0.0/16 --upload-certs请注意,服务和 Pod 的子网不应相互重叠或与现有网络重叠。
最后,我们将看到一条消息,表示一切正常,同时他们将告诉我们如何将工作节点附加到控制平面:
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
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
You can now join any number of the control-plane node running the following command on each as root:
kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4
--discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050
--contrl-plane --certificate-key 72a3c0a14c627d6d7fdade1f4c8d7a41b0fac31b1faf0d8fdf9678d74d7d2403
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4
--discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050让我们按照建议为用户添加配置。 同时,我建议立即为 kubectl 添加自动补全功能:
kubectl completion bash > ~/.kube/completion.bash.inc
printf "
# Kubectl shell completion
source '$HOME/.kube/completion.bash.inc'
" >> $HOME/.bash_profile
source $HOME/.bash_profile到了这个阶段,你已经可以看到集群中的第一个节点了(虽然还没有准备好):
root@pi-control:~# kubectl get no
NAME STATUS ROLES AGE VERSION
pi-control NotReady master 29s v1.18.6网络配置
接下来,如安装后消息中所述,您需要将网络安装到集群中。 文档提供了 Calico、Cilium、contiv-vpp、Kube-router 和 Weave Net 的选择...这里我偏离了官方说明,选择了一个对我来说更熟悉和易于理解的选项: 在 host-gw 模式下(有关可用后端的更多信息,请参阅 ).
在集群中安装它非常简单。 首先,下载清单:
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 然后在设置中更改类型 vxlan 上 host-gw:
sed -i 's/vxlan/host-gw/' kube-flannel.yml...以及 Pod 子网 - 从默认值到集群初始化期间指定的值:
sed -i 's#10.244.0.0/16#10.1.0.0/16#' kube-flannel.yml之后,我们创建资源:
kubectl create -f kube-flannel.yml 准备好! 一段时间后,第一个K8s节点将切换到状态 Ready:
NAME STATUS ROLES AGE VERSION
pi-control Ready master 2m v1.18.6添加工作节点
现在您可以添加一名工人。 要在其上执行此操作 - 在根据上述场景安装 Kubernetes 本身之后 - 您只需运行之前收到的命令:
kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4
--discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050此时我们可以假设集群已准备就绪:
root@pi-control:~# kubectl get no
NAME STATUS ROLES AGE VERSION
pi-control Ready master 28m v1.18.6
pi-worker Ready <none> 2m8s v1.18.6我手头只有两个 Raspberry Pi,所以赠送了其中一个 仅 我不想让它处于控制平面之下。 因此,我通过运行以下命令从 pi-control 节点中删除了自动安装的污点:
root@pi-control:~# kubectl edit node pi-control...并删除行:
- effect: NoSchedule
key: node-role.kubernetes.io/master用所需的最小值填充集群
首先我们需要 头盔。 当然,您可以在没有它的情况下完成所有操作,但 Helm 允许您自行决定自定义某些组件,而无需编辑文件。 事实上,它只是一个“不求面包”的二进制文件。
那么让我们去 到 docs/installation 部分并从那里执行命令:
curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash之后,添加图表存储库:
helm repo add stable https://kubernetes-charts.storage.googleapis.com/现在让我们按计划安装基础设施组件:
- 入口控制器;
- 普罗米修斯;
- 格拉法纳;
- 证书经理。
入口控制器
第一个组件是 入口控制器 - 安装非常简单,开箱即可使用。 要执行此操作,只需转到 并从那里运行安装命令:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/baremetal/deploy.yaml然而,此时“树莓派”开始紧张并遇到磁盘IOPS问题。 事实是,与 Ingress 控制器一起安装了大量资源,对 API 发出了许多请求,相应地,大量数据被写入 etcd。 一般来说,要么class 10存储卡生产力不高,要么SD卡基本上不足以满足这样的负载。 然而,大约 5 分钟后,一切都开始了。
创建了一个命名空间,一个控制器及其所需的一切都出现在其中:
root@pi-control:~# kubectl -n ingress-nginx get pod
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-2hwdx 0/1 Completed 0 31s
ingress-nginx-admission-patch-cp55c 0/1 Completed 0 31s
ingress-nginx-controller-7fd7d8df56-68qp5 1/1 Running 0 48s普罗米修斯
接下来的两个组件很容易通过 Helm 从图表存储库安装。
找 普罗米修斯,创建一个命名空间并将其设置为:
helm search repo stable | grep prometheus
kubectl create ns monitoring
helm install prometheus --namespace monitoring stable/prometheus --set server.ingress.enabled=True --set server.ingress.hosts={"prometheus.home.pi"}默认情况下,Prometheus 订购 2 个磁盘:用于 Prometheus 数据本身和 AlertManager 数据。 由于集群中尚未创建存储类,因此不会对磁盘进行排序,并且 Pod 也不会启动。 对于裸机 Kubernetes 安装,我们通常使用 Ceph rbd,但对于 Raspberry Pi,这显然是多余的。
因此,让我们在主机路径上创建一个简单的本地存储。 prometheus-server 和 prometheus-alertmanager 的 PV(持久卷)清单合并在一个文件中 prometheus-pv.yaml в 。 需要PV目录 提前 在我们要绑定Prometheus的节点的磁盘上创建:示例中是这样写的 nodeAffinity 按主机名 pi-worker 并在其上创建目录 /data/localstorage/prometheus-server и /data/localstorage/prometheus-alertmanager.
下载(克隆)清单并将其添加到 Kubernetes:
kubectl create -f prometheus-pv.yaml在这个阶段,我首先遇到了ARM架构的问题。 Prometheus 图表中默认安装的 Kube-state-metrics 拒绝启动。 它给出了一个错误:
root@pi-control:~# kubectl -n monitoring logs prometheus-kube-state-metrics-c65b87574-l66d8
standard_init_linux.go:207: exec user process caused "exec format error"事实上,kube-state-metrics 使用的是 CoreOS 项目的映像,该映像不是为 ARM 编译的:
kubectl -n monitoring get deployments.apps prometheus-kube-state-metrics -o=jsonpath={.spec.template.spec.containers[].image}
quay.io/coreos/kube-state-metrics:v1.9.7我必须进行一些谷歌搜索并找到,例如, 。 要使用它,我们需要更新版本以指示哪个镜像用于 kube-state-metrics:
helm upgrade prometheus --namespace monitoring stable/prometheus --set server.ingress.enabled=True --set server.ingress.hosts={"prometheus.home.pi"} --set kube-state-metrics.image.repository=carlosedp/kube-state-metrics --set kube-state-metrics.image.tag=v1.9.6让我们检查一切是否已经开始:
root@pi-control:~# kubectl -n monitoring get po
NAME READY STATUS RESTARTS AGE
prometheus-alertmanager-df65d99d4-6d27g 2/2 Running 0 5m56s
prometheus-kube-state-metrics-5dc5fd89c6-ztmqr 1/1 Running 0 5m56s
prometheus-node-exporter-49zll 1/1 Running 0 5m51s
prometheus-node-exporter-vwl44 1/1 Running 0 4m20s
prometheus-pushgateway-c547cfc87-k28qx 1/1 Running 0 5m56s
prometheus-server-85666fd794-z9qnc 2/2 Running 0 4m52sGrafana 和证书管理器
对于图表和仪表板,我们设置 格拉法纳:
helm install grafana --namespace monitoring stable/grafana --set ingress.enabled=true --set ingress.hosts={"grafana.home.pi"}在输出的最后,我们将看到如何获取访问密码:
kubectl get secret --namespace monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo要订购证书,我们将安装 证书经理。 安装方法请参考 ,它为 Helm 提供了相应的命令:
helm repo add jetstack https://charts.jetstack.io
helm install
cert-manager jetstack/cert-manager
--namespace cert-manager
--version v0.16.0
--set installCRDs=true对于家庭使用的自签名证书来说,这已经足够了。 如果您需要获得相同的 让我们加密,那么还需要配置集群发行者。 有关详细信息,请参阅我们的文章““。
我自己选择了以下选项 ,决定 LE 的分期选项就足够了。 我们更改示例中的电子邮件,将其保存到文件中并将其添加到集群中():
kubectl create -f cert-manager-cluster-issuer.yaml现在您可以订购证书,例如 Grafana 的证书。 这将需要一个域并从外部访问集群。 我有一个域,我根据创建的入口控制器服务在家庭路由器上转发端口 80 和 443 来配置流量:
kubectl -n ingress-nginx get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.2.206.61 <none> 80:31303/TCP,443:30498/TCP 23d在本例中,第 80 个端口被转换为 31303,443 被转换为 30498。 (端口是随机生成的,因此您的端口会有所不同。)
这是证书的示例():
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: grafana
namespace: monitoring
spec:
dnsNames:
- grafana.home.pi
secretName: grafana-tls
issuerRef:
kind: ClusterIssuer
name: letsencrypt-staging将其添加到集群中:
kubectl create -f cert-manager-grafana-certificate.yaml之后,将出现 Ingress 资源,通过该资源进行 Let's Encrypt 验证:
root@pi-control:~# kubectl -n monitoring get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
cm-acme-http-solver-rkf8l <none> grafana.home.pi 192.168.88.31 80 72s
grafana <none> grafana.home.pi 192.168.88.31 80 6d17h
prometheus-server <none> prometheus.home.pi 192.168.88.31 80 8d 验证通过后,我们会看到该资源 certificate 准备好,并在上面的秘密 grafana-tls - 证书和密钥。 您可以立即检查谁颁发了证书:
root@pi-control:~# kubectl -n monitoring get certificate
NAME READY SECRET AGE
grafana True grafana-tls 13m
root@pi-control:~# kubectl -n monitoring get secrets grafana-tls -ojsonpath="{.data['tls.crt']}" | base64 -d | openssl x509 -issuer -noout
issuer=CN = Fake LE Intermediate X1让我们回到 Grafana。 我们需要通过更改 TLS 设置以匹配生成的证书来稍微修复其 Helm 版本。
为此,请下载图表,从本地目录进行编辑和更新:
helm pull --untar stable/grafana 在文件中编辑 grafana/values.yaml TLS 参数:
tls:
- secretName: grafana-tls
hosts:
- grafana.home.pi 在这里您可以立即将安装的 Prometheus 配置为 datasource:
datasources:
datasources.yaml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus-server:80
access: proxy
isDefault: true现在我们从本地目录更新 Grafana 图表:
helm upgrade grafana --namespace monitoring ./grafana --set ingress.enabled=true --set ingress.hosts={"grafana.home.pi"} 检查 Ingress 中有什么 grafana 已添加443端口,可通过HTTPS访问:
root@pi-control:~# kubectl -n monitoring get ing grafana
NAME CLASS HOSTS ADDRESS PORTS AGE
grafana <none> grafana.home.pi 192.168.88.31 80, 443 63m
root@pi-control:~# curl -kI https://grafana.home.pi
HTTP/2 302
server: nginx/1.19.1
date: Tue, 28 Jul 2020 19:01:31 GMT
content-type: text/html; charset=utf-8
cache-control: no-cache
expires: -1
location: /login
pragma: no-cache
set-cookie: redirect_to=%2F; Path=/; HttpOnly; SameSite=Lax
x-frame-options: deny
strict-transport-security: max-age=15724800; includeSubDomains要演示 Grafana 的实际应用,您可以下载并添加 。 它看起来是这样的:

我还建议为节点导出器添加一个仪表板:它将详细显示“树莓派”发生的情况(CPU 负载、内存、网络、磁盘使用情况等)。
在此之后我认为 集群已准备好接受并运行应用程序!
组装注意事项
至少有两种用于构建 ARM 架构应用程序的选项。 首先,您可以在 ARM 设备上进行构建。 然而,在查看了目前对两个 Raspberry Pi 的处置后,我意识到它们也无法在组装后幸存下来。 因此,我为自己订购了一台新的 Raspberry Pi 4(它功能更强大,内存高达 4 GB) - 我计划在它的基础上进行构建。
第二种选择是在更强大的机器上构建多架构 Docker 镜像。 为此有 。 如果应用程序采用编译语言,则需要针对 ARM 进行交叉编译。 我不会描述这条路径的所有设置,因为...... 这需要一篇单独的文章。 通过实施这种方法,可以实现“通用”镜像:运行在 ARM 机器上的 Docker 会自动下载与架构相对应的镜像。
结论
这个实验超出了我的所有预期:[至少]具有必要基础的“普通”Kubernetes 在 ARM 上感觉良好,并且其配置只出现了一些细微差别。
Raspberry Pi 3B+ 本身将负载保持在 CPU 上,但它们的 SD 卡是一个明显的瓶颈。 同事建议,在某些版本中可以从USB启动,您可以在其中连接SSD:那么情况很可能会变得更好。
以下是安装 Grafana 时的 CPU 负载示例:

对于实验和“尝试”,在我看来,“树莓派”上的 Kubernetes 集群比同一个 Minikube 传达的操作感觉要好得多,因为集群的所有组件都已安装并“像成人一样”工作。
未来,有一个想法是向集群添加整个 CI/CD 周期,完全在 Raspberry Pi 上实现。 如果有人分享他们在 AWS Graviton 上设置 K8 的经验,我也会很高兴。
PS 是的,“生产”可能比我想象的更接近:

聚苯硫醚
另请阅读我们的博客:
- «“。
来源: habr.com
