搭建个人项目时,如何节省Kubernetes集群的费用?
作者 | David Griffiths译者 | Rayden运行 Kubernetes 集群并不一定很昂贵。本文,我将讨论如何建立一个对个人项目来说可负担的 Kubernetes 集群。目 标我希望 Kubernetes 集群的价格低于每月 20 美元。我特别喜欢容器化和 Kubernetes (k8s) 编排系统,我也喜欢摆弄个人项目,但我发现在 k8s 集群中托管自己的项目总是非常昂贵。
因为必须为至少有两个 vCPU 的虚拟机(k8s 中的节点)付费(这是现实情况);通常要为管理 k8s 服务的主节点付费;通过负载均衡器使用 ingress 可能很花钱;然后还有磁盘开销、公共 IP 地址等,所有这些都会导致费用上升。在 AWS 上尝试这一切,很容易就会累计超过每月 100 美元。
与其他常见的网络托管服务对比,你就会发现问题所在。这些网络托管服务使用共享资源,每月只需很少的花费就可以托管网站。我这里不会把它们放在一起做比较,但我随时可以租一个 VPS 来运行自己的项目,这样可以轻松将预算控制在每月 20 美元以内。
但让我感兴趣的不仅仅是运行一些东西,管理和部署也是乐趣所在。找到很酷的开源技术,获取最新的容器镜像,使用一些 YAML 配置并与其他想法集成,然后执行 kubectl apply,我就可以运行自己的 X、Y 和 Z,以上这些都是极大的乐趣。如果想在博客中添加评论功能,我不想使用像 Disqus 这样的评论服务,因为使用该服务需要支付费用来取消广告,接受它会减慢网站的速度,追踪网站的访问者还可能导致他们的个人数据被用来做无人知晓的事情。我想使用 Commento,快速、无广告且是开源软件,还可以使用一个预编译的容器镜像来轻松托管。
因此,我最近试着想找到一种方法,让 Kubernetes 以一种更经济的方式运行,尽管并不特别优雅,但这是可以做到的。下面是对我设置的 k8s 集群的描述。它的费用为每天 43 便士,约合每月 18 美元(13.50 英镑)。我当然可以买更便宜的,但我用那笔钱得到了很好的服务,所以我很高兴。我当然欢迎所有的改进意见。
一个廉价集群这是对集群的概述。我已经删除掉除了网站、反向代理和 kubeip 之外的所有内容,我将在稍后简单介绍 kubeip。
总结起来,我是通过以下几个途径实现低成本的:
选择一个不为控制集群的主节点收费的 k8s 服务提供者。
使用 GKE 的可抢占节点,这些节点的价格约为原本的三分之一。
不使用云负载均衡器做 ingress
保持较低的存储使用量,并在可能的情况下使用共享磁盘。
每一条分别能省多少钱?这里是一个简要分析:
免费的 master 节点许多云提供商对主节点不收费。在撰写本文时,Azure、IONOS 和 DigitalOcean 就是提供 完全免费 的集群管理的几家云服务商。这种情况是否会继续下去,我们将拭目以待。从 2020 年 6 月起,GCP 的 GKE 服务开始对主节点收取每小时 0.10 美元(每月 72 美元)的费用,但计费帐户的第一个集群除外。AWS 的 EKS 甚至对第一个集群仅收取每小时 0.10 美元的费用。
节约:每月 0 美元 vs72 美元
GKE 可抢占节点谷歌的 GKE(谷歌 Kubernetes 引擎)非常出色。从开发人员的角度来看,我认为它是最好的,因为他们是 Kubernetes 的最初开发者。GKE 的一大优势是其可抢占节点。
可抢占节点是指可能随时被谷歌抢占(终止)的虚拟机。他们基本上是多余的谷歌计算引擎资源,所以他们会有所不同。谷歌 方面表示:计算引擎由于系统事件停止可抢占实例的概率通常较低。计算引擎总会在可抢占实例运行 24 小时后停止它们。
然而,它们很快就会被新的节点取代,因此,如果将服务设计为可容错的,并且可以接受少许停机时间,那么可抢占节点将节省大量成本。
E2-medium 节约的成本:每月 7.34 vs 24.46 美元。这就节省了 70%!
避免使用云负载均衡器提供一个云负载均衡器是管理进入集群流量的最简单方法,但是它太贵了!显然,它们具有许多功能,对于任何生产系统来说都是必不可少的,但在我看来,对于个人项目来说,它们太昂贵了。GCP 的负载均衡器是每小时 0.025 美元,相当于每月 18 美元左右。
管理 ingress 的困难在于(例如使用 nginx)运行它的 Kubernetes 节点(VM)将有一个临时的 IP 地址。动态 DNS 是一种可能的解决方案,但我认为,当 DNS 服务器指向错误的位置时会花费大量时间。
可以查看 kubeip 项目,这个开源的 Golang 服务从一个静态的外部 IP 地址池中为 GKE 节点分配 IP。下面有更多的介绍。
节约:每月 0 vs 18 美元(免责声明:在我下面的设置中,我专门为 ingress 提供了一个 e2-micro 实例,每月 6.11 美元。不过还是能省一大笔钱。)
减少持久性存储分配由于是个人项目,我避免使用固态硬盘存储(SSD),并继续使用机械硬盘(HDD)。在 GCE 中,机械硬盘存储是每 GB 每月 0.04 美元,这非常便宜,但是 GKE 中节点的默认磁盘大小是 100GB,也就是每个月 4 美元。
此外,在 GCE 中你可以分配的最低硬盘大小是 10GB,花费为每月 0.40 美元。您可能会说,这不是很多,但是如果您想运行一堆需要持久性存储的容器,那么花费也会很快增长。
我的方法是:为 GKE 节点分配 10-20GB 存储空间,并在可能的情况下使用 NFS 共享持久性存储(这将是以后文章的主题)。
这里节约多少依赖于集群大小和服务,但对于我的特定配置,我运行了几个需要持久性存储的不同服务,它们都共享 10GB 磁盘,这大约每月花费 1.60 美元,而不是一开始的8 美元。
集群花费毫无疑问,这样做可以节省更多的钱,但我的集群成本以英国货币计算大约是每月 13.50 英镑或每天 43 便士(约合每月 18.22 美元)。
详情
接下来,我们假设将在 GKE 中创建一个名为 my-cluster 的 k8s 集群,并在 my-cluster.co.uk 上托管一个网站(域名可用)。
第一步:创建一个集群和两个节点池
使用 GCP 的 GKE。这对于可抢占节点是必需的。在最便宜的区域创建 my-cluster 集群,如 us-central1。
创建两个节点池:
ingress-pool:
1 个 e2-micro,带有 10GB 机械硬盘
将只运行 nginx ingress 容器
给这个节点池添加污点 dedicated=ingress:NoSchedule
web-pool:
1 个 e2-medium,可抢占节点,带有 20GB 机械硬盘
将运行 kubeip 和其他一切
Ingress-pool 是 nginx ingress 的运行位置,它将终止 TLS 和代理集群中的服务。kubeip 将该节点的 IP 地址设置为静态外部 IP 地址。
web-pool 是其他所有东西运行的地方,包括 kubeip。目前这只是一个节点,但将来有必要时可以扩大。
设置节点污点
我们为 ingress-pool 设置污点,以便只允许 nginx ingress 在这个池中运行。其他的一切包括 kubeip,都将在另一个池中运行。
顺便说一下,这是 kubeip 的一个限制;您必须在一个节点池中运行它,而这个节点池不是您希望设置静态 IP 的那个。参考 kubeip readme:我们建议 KUBEIP_NODEPOOL 不要与 KUBEIP_SELF_NODEPOOL 相同
我试过使用同一个节点池,但是没有成功。
在创建 ingress 节点池时,可以将污点应用到整个节点池,但不幸的是,之后不能这样做。如果您像我一样忘记了,可以这样将它应用到 ingress-pool 的每个节点:
kubectl taint node gke-my-cluster-ingress-pool-69bb8d97-sft0 dedicated=ingress:NoSchedule
我不确定如果我的节点被替换,污点是否会继续存在。请把答案写在明信片上或评论里。
磁盘大小
GKE 中节点的默认磁盘大小为 100GB。假设您没有使用大量的主机存储(例如通过 emptyDir 卷),那么我认为您可以使用比这少得多的存储。容器镜像可能会占用大量空间,但如果磁盘压力变得很大,Kubernetes 将清除未使用的映像。
我为 ingress 节点设置了 10GB,因为我不打算在其上运行其他任何东西。对于 web-pool 节点,我选择了 20GB。也许我可以少花点钱使用更小的存储,或者几个月后我就会意识到我需要更多存储空间。我们拭目以待。
第二步:部署 kubeip
下一步是在 web-pool 节点上安装 kubeip,以便将 ingress 节点的 IP 地址设置为静态 IP。
安装 kubeip 的详细说明请参见 kubeip GitHub 页面。以下是我所做的:
首先克隆 kubeip 仓库:
git clone https://github.com/doitintl/kubeip.git
设置一些参数:
gcloud config set project {insert-your-GCP-project-name-here}
export PROJECT_ID=$(gcloud config list --format 'value(core.project)')
export GCP_REGION=us-central1
export GKE_CLUSTER_NAME=my-cluster
export KUBEIP_NODEPOOL=ingress-pool
export KUBEIP_SELF_NODEPOOL=web-pool
创建 kubeIP 服务帐号、角色和 iam 策略绑定:
gcloud iam service-accounts create kubeip-service-account --display-name "kubeIP"
gcloud iam roles create kubeip --project $PROJECT_ID --file roles.yaml
gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:kubeip-service-account@$PROJECT_ID.iam.gserviceaccount.com --role projects/$PROJECT_ID/roles/kubeip
获取服务帐户的 key 并将其写入 secret:
gcloud iam service-accounts keys create key.json --iam-account kubeip-service-account@$PROJECT_ID.iam.gserviceaccount.com
kubectl create secret generic kubeip-key --from-file=key.json -n kube-system
给自己集群管理员身份:
kubectl create clusterrolebinding cluster-admin-binding \ --clusterrole cluster-admin --user `gcloud config list --format 'value(core.account)'`
创建 IP 地址并标记它们。我在这里只创建了一个,因为 ingress 池中只有一个节点:
for i in {1..1}; do gcloud compute addresses create kubeip-ip$i --project=$PROJECT_ID --region=$GCP_REGION; done
for i in {1..1}; do gcloud beta compute addresses update kubeip-ip$i --update-labels kubeip=$GKE_CLUSTER_NAME --region $GCP_REGION; done
由于我使用 MacOS,kubeip 文档中的下一步不能正常工作,因为 MacOS 附带 FreeBSD 版本的 sed 不支持 sed -i。这里我只是手动进行了替换,下面是替换方法:
sed -i "s/reserved/$GKE_CLUSTER_NAME/g" deploy/kubeip-configmap.yaml
sed -i "s/default-pool/$KUBEIP_NODEPOOL/g" deploy/kubeip-configmap.yaml
sed -i "s/pool-kubip/$KUBEIP_SELF_NODEPOOL/g" deploy/kubeip-deployment.yaml
然而,最后一行是多余的,因为我们已经给 ingress-pool 加上了污点,所以 kubeip 将自然地被调度到 web-pool 节点上。deploy/kubeip-deployment.yaml 中的以下几行可以被注释掉:
# nodeSelector:
# cloud.google.com/gke-nodepool: web-pool
现在我们只需要部署它:
kubectl apply -f deploy/.
好了,坐下来使用以下命令看看 kubeip 做了什么:
kubectl get nodes -w -o wide
Ingress-pool 节点的 IP 地址应为静态 IP 地址。检查云控制台中的 IP 地址也很有用:https://console.cloud.google.com/networking/addresses
第三步:在集群中部署一些东西
我们需要在集群中部署一些服务,以便在下一步中测试配置的代理。用 nginx 做一个简单的静态网站就可以了。创建一个你选择的 index.html 文件,例如:
!DOCTYPE html
html lang="en"
head
meta charset="utf-8"
titleMy Website/title
/head
body
Hello world!/h1
/body
/html
你将需要一个 default.conf 文件,例如:
server {
listen 8080;
server_name my-cluster.co.uk;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ /index.html;
}
}
以及一个 Dockerfile:
FROM nginx
COPY index.html /usr/share/nginx/html
COPY default.conf /etc/nginx/conf.d/
创建镜像并推送它:
docker build -t gcr.io/my-gcp-project/website:1 .
docker push gcr.io/my-gcp-project/website:1
使用一个基本的 k8s 的 yaml 文件来部署:
apiVersion: v1
kind: Service
metadata:
name: website
labels:
app: website
spec:
type: ClusterIP
ports:
- port: 8080
selector:
app: website
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: website
labels:
app: website
spec:
replicas: 1
selector:
matchLabels:
app: website
template:
metadata:
labels:
app: website
spec:
containers:
- name: website
image: 'gcr.io/my-cluster/website:1'
ports:
- containerPort: 8080
运行 kubectl apply 来部署它。你可以通过端口转发并访问 http://localhost:8080 来检查它是否正在运行,如下所示:
kubectl port-forward website-74b66cf9d8-db69j 8080
第四步:部署一个 nginx ingress 代理
现在将 ingress 反向 web 代理部署到 ingress-pool 节点。关于这个 pod 需要注意以下几点:
需要容忍入口节点上的污点和并选择 ingress-pool。
它将使用主机网络,这样我们就能在没有云负载均衡器的情况下访问它。事实上,我们根本不会使用任何服务。
它需要一个 ClusterFirstWithHostNet 的 DNS 策略,因为 pod 运行在 hostNetwork 模式下。这样我们就可以使用这个代理来访问集群中的服务。
首先,我们需要一个 configMap 来配置 nginx 的 default.conf。例如:
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-proxy
default.conf: |
# This is needed to allow variables in proxy_pass directives, and bypass upstream startup checks
# Variables must use the service FQDN servicenamespace.svc.cluster.local
resolver kube-dns.kube-system.svc.cluster.local valid=30s ipv6=off;
server {
listen 80;
listen [::]:80;
server_name my-cluster.co.uk;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name my-cluster.co.uk;
ssl_certificate /cert/fullchain.pem;
ssl_certificate_key /cert/privkey.pem;
ssl_protocols TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
set $upstream "http://website.default.svc.cluster.local:8080";
proxy_pass $upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
使用 kuberctl apply 命令部署它,注意,以上配置假设你已经为 my-cluster.co.uk 创建了一个 web 服务的 certs,并且将其部署到名为 web-certs 的 k8s secret 中。并且,你已经为 my-cluster.co.uk 设置了一个 DNS A 记录,指向之前的静态 IP 地址。这里没有讨论这些内容。
现在是 deployment:
kind: Deployment
metadata:
name: ingress-proxy
labels:
app: ingress-proxy
spec:
replicas: 1
selector:
matchLabels:
app: ingress-proxy
template:
metadata:
labels:
app: ingress-proxy
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
nodeSelector:
cloud.google.com/gke-nodepool: ingress-pool
tolerations:
- key: dedicated
operator: Equal
value: ingress
effect: NoSchedule
containers:
- name: ingress-proxy
image: 'nginx'
ports:
- name: https
containerPort: 443
hostPort: 443
- name: http
containerPort: 80
hostPort: 80
volumeMounts:
- name: web-certs
mountPath: /cert
readOnly: true
- name: nginx-conf
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
readOnly: true
volumes:
- name: web-certs
secret:
secretName: web-certs
- configMap:
name: ingress-proxy
name: nginx-conf
使用 kubectl apply 部署它。
第五步:打开 VPC 防火墙
最后一步是使 ingress 节点可达;需要配置 VPC 的防火墙,以允许 http 和 https 流量通过:
gcloud compute firewall-rules create http-node-port --allow tcp:80
gcloud compute firewall-rules create tls-node-port --allow tcp:443
幸运的话,你现在应该有一个 nginx 服务来代理我们在上一步中创建的网站,并可以通过 https://my-cluster.co.uk 来访问。
结论
希望本文对开发有所帮助,你也可以在尽量少花钱的情况下享受 Kubernetes。
我确信这里可以进行一些改进(例如,让 ingress 节点池可抢占——我不认为有任何理由不去这样做),从而进一步降低价格,或者采用完全不同的方法。我很乐意在下面的评论中听到你的任何想法或主意。
原文链接:https://dzone.com/articles/affordable-kubernetes-for-personal-projects
你也「在看」吗???
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线