最近,一家著名的公司宣布将笔记本电脑产品线转移到ARM体系结构。听到这个消息,我想起来了:在再次查看AWS中EC2的价格时,我注意到Gravitons的价格非常可口。当然,要抓住的是它是ARM。那时我什至没有想到ARM是非常认真的……
对我而言,这种架构一直是移动和其他物联网领域的重要内容。 ARM上的“真实”服务器在某种程度上是不寻常的,甚至在某些方面甚至是荒谬的。但是,一个新的想法陷入了我的头脑,因此一个周末,我决定检查一下今天可以在ARM上启动什么。为此,我决定从一个亲密的亲爱的Kubernetes集群开始。不仅是一些有条件的“集群”,还包括“成人方式”的所有事物,因此它与我在生产中看到的尽可能多。
根据我的想法,应该可以从Internet访问该群集,应该在其中运行一些Web应用程序,并且至少应该进行监视。要实现此想法,您将需要一对(或更多)Raspberry Pi 3B +或更高版本。AWS可能已经成为进行实验的平台,但是对我来说很有趣的是“树莓”(它们仍然闲置)。因此,我们将部署一个带有Ingress,Prometheus和Grafana的Kubernetes集群。
制备“覆盆子”
安装操作系统和SSH
我对选择要安装的操作系统并没有太在意:我只是从官方网站上获取了最新的Raspberry Pi OS Lite 。那里也提供安装文档,所有步骤必须在以后的群集的所有节点上执行。接下来,您需要执行以下操作(也在所有节点上)。
连接显示器和键盘后,必须首先配置网络和SSH:
- 为了使群集正常工作,主服务器必须具有静态IP地址,工作节点必须具有静态IP地址。为了便于设置,我更喜欢静态地址。
- 可以在OS中配置静态地址(文件中
/etc/dhcpcd.conf有一个合适的示例),也可以通过在二手(在我的情况下为家用)路由器的DHCP服务器中修复租约的方式进行配置。 - ssh-server仅包含在raspi-config中(接口选项-> ssh)。
之后,您已经可以通过SSH登录(默认情况下,登录名是
pi,密码是raspberry您更改的密码)并继续设置。
其他设定
- 让我们设置主机名。在我的例子,
pi-control并且将使用pi-worker。 - 让我们检查一下文件系统是否已扩展到整个磁盘(
df -h /)。如果需要,可以使用raspi-config对其进行扩展。 - 在raspi-config中更改默认用户密码。
- 关闭交换文件(这是Kubernetes的要求;如果您对此主题的详细信息感兴趣,请参阅问题#53533):
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.io的官方文档(稍加补充说明和缩写)。
添加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部分中的helm.sh并从此处执行命令:
curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
之后,添加图表存储库:
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
现在,让我们根据以下想法安装基础结构组件:
- 入口控制器;
- 普罗米修斯
- 格拉法纳
- 证书经理。
入口控制器
第一个组件Ingress控制器相当容易安装,可以直接使用。为此,只需转到站点上的裸机部分,然后从那里执行安装命令:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/baremetal/deploy.yaml
但是,此刻,“树莓”开始紧张,并进入磁盘IOPS。事实是,与Ingress控制器一起安装了大量资源,发出了许多API请求,因此,有很多数据写入etcd。通常,第10类存储卡的生产率不是很高,或者SD卡基本上不足以承受这种负载。但是,五分钟后一切开始。
创建了一个名称空间,并在其中显示了一个控制器及其所需的一切:
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安装。
找到Prometheus,创建一个名称空间并安装在其中:
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到Git存储库中的文件中,并附带了本文的示例。必须在要绑定Prometheus的节点的磁盘上预先创建PV的目录:在示例中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 4m52s
Grafana和证书经理
对于图表和仪表板,请安装Grafana:
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
要订购证书,请安装cert-manager。要安装它,请参阅文档,其中提供了适用于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
对于家庭使用的自签名证书,这就足够了。如果您需要接收相同的Let's Encrypt,则需要配置另一个群集发行者。可以在我们的文章“ Kubernetes上的使用cert-manager进行加密的SSL证书”中找到更多详细信息。
我本人决定使用文档中示例中的版本,并确定LE的暂存版本就足够了。更改示例中的电子邮件,将其保存到文件并将其添加到群集(cert-manager-cluster-issuer.yaml):
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。(端口是随机生成的,因此您将拥有不同的端口。)
以下是示例证书(cert-manager-grafana-certificate.yaml):
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资源,通过该资源,我们将进行加密验证:
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。我们需要对其Helm版本进行一些修复,根据生成的证书更改TLS的设置。
为此,请从本地目录下载图表,进行编辑和更新:
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"}
我们检查
grafana端口443是否已添加到Ingress并通过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的实际效果,您可以下载并添加kube-state-metrics的仪表板。外观如下:我
还建议为节点导出器添加一个仪表板:它将详细显示“树莓”发生了什么(CPU负载,内存,网络,磁盘使用情况等)。
之后,我相信集群已准备就绪,可以接收和运行应用程序!
组装说明
至少有两个选择可以为ARM体系结构构建应用程序。首先,它可以构建在ARM设备上。但是,在查看了两个Raspberry Pi的当前配置之后,我意识到它们也将无法在装配中幸存下来。因此,我订购了新的Raspberry Pi 4(它更强大,并且具有4 GB的内存)-我计划在其上构建它。
第二种选择是在功能更强大的机器上构建多架构Docker映像。有一个docker buildx扩展名。如果应用程序使用的是编译语言,则需要针对ARM的交叉编译。我不会描述此路径的所有设置,因为 这将导致另外一篇文章。在实施这种方法时,您可以实现“通用”映像:在ARM机器上运行的Docker将自动加载与架构相对应的映像。
结论
进行的实验超出了我的所有期望:[至少]具有必要基础的“香草” Kubernetes在ARM上感觉良好,并且凭借其配置,仅产生了一些细微差别。
Raspberry Pi 3B +本身使CPU繁忙,但它们的SD卡是一个明显的瓶颈。同事建议,在某些版本中,可以从USB引导,您可以在其中连接SSD:然后情况可能会有所好转。
这是安装Grafana时CPU负载的示例:
我认为,对于实验和“尝试”,“树莓”上的Kubernetes集群比同一个Minikube更好地传达了运行的感觉,因为该集群的所有组件都已安装并且可以正常工作“以成人的方式。”
将来,有一种想法可以将整个CI / CD周期添加到群集中,该过程完全在Raspberry Pi上实现。如果有人分享他们在AWS Gravitons上设置K8的经验,我也将感到高兴。
PS:是的,“生产”可能比我想象的要近:
PPS
另请参阅我们的博客: