使用 Traefik、Let's Encrypt 和 Zookeeper 运行个人云
使用 Traefik 的 Kubernetes 入口
HTTPS 和 Let's Encrypt
GKE抢占式节点,你自己的混沌猴子
Traefik 与 Zookeeper 共享 K/V 商店
所有用于设置的 Kubernetes YAML 文件
最终结果
关于我
使用 Traefik 的 Kubernetes 入口
正如我在上一篇博客文章中提到的,我希望专注于构建一个与供应商无关的云平台,尽可能使用不依赖于任何云服务的技术。
虽然谷歌云默认提供负载均衡的 HTTP 入口,但与运行小型节点相比,它显然非常昂贵,而且我只听说过使用 Traefik 作为 Kubernetes 入口的良好口碑。
我按照Manuel 的优秀指南设置了 Traefik ,并做了一些小的修改(最终文件可以在文章末尾找到)。
HTTPS 和 Let's Encrypt
Traefik 内置了对使用Let's Encrypt自动获取和续订 HTTPS 证书的支持。由于 HTTPS 是最佳实践,而且也是 HTTP2 和 PWA 的必要条件,所以我按照Traefik 文档中的示例配置进行了设置。
因为我只使用了一个 Traefik 节点,所以我选择使用本地 acme.json 文件进行简单设置,该文件在节点运行时存储证书。
GKE抢占式节点,你自己的混沌猴子
为了节省成本,我选择使用“抢占式虚拟机”作为节点来运行我在 GKE 上的 Kubernetes 集群。根据谷歌的文档:“抢占式虚拟机是 Google Compute Engine 虚拟机实例,其最长运行时间为 24 小时,且不提供任何可用性保证。” 这意味着我的 Kubernetes 集群中的节点会随机宕机,并且永远不会运行超过 24 小时。虽然这显然不是生产环境的明智之举,但我还是选择接受它,并将节点宕机视为一种“混沌猴子”,它迫使我编写具有弹性的代码。
我遇到的一个具体例子是:Let's Encrypt 生产 API 对同一 URL 的证书请求次数有限制,每周最多只能请求 5 个。由于我最初的配置比较简单,没有保存证书,所以每次 Traefik 节点终止时,证书都会丢失。虽然 Traefik 在每次启动时都能顺利地重新生成证书……但五次启动后,我就达到了请求次数限制,并收到了一个关于缺少证书的不安全警告。
Traefik 与 Zookeeper 共享 K/V 商店
为 Traefik 添加一个共享的键值存储。如果要在集群模式下运行 Traefik,则必须使用键值存储(而且我认为我的配置很容易扩展)。这也意味着我可以将生成的证书存储在键值存储中,这样 Traefik 重启后证书就不会再丢失了。
由于我之前使用过 Zookeeper,而且设置过程也比较简单,所以我选择了它。
所有用于设置的 Kubernetes YAML 文件
最后是博文的重点,我的完整配置以 yaml 文件的形式呈现,您可以直接将其部署到您的 GKE 集群中:
首先设置 Zookeeper。
参考这份优秀的资源:https://github.com/kow3ns/kubernetes-zookeeper/blob/master/manifests/README.md
apiVersion: v1
kind: Service
metadata:
name: zk-hs
labels:
app: zk
spec:
ports:
- port: 2888
name: server
- port: 3888
name: leader-election
clusterIP: None
selector:
app: zk
---
apiVersion: v1
kind: Service
metadata:
name: zk-cs
labels:
app: zk
spec:
ports:
- port: 2181
name: client
selector:
app: zk
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: zk
spec:
serviceName: zk-hs
replicas: 1
podManagementPolicy: Parallel
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: zk
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- zk
topologyKey: "kubernetes.io/hostname"
containers:
- name: kubernetes-zookeeper
imagePullPolicy: Always
image: "gcr.io/google_containers/kubernetes-zookeeper:1.0-3.4.10"
resources:
requests:
memory: "200M"
cpu: "0.3"
ports:
- containerPort: 2181
name: client
- containerPort: 2888
name: server
- containerPort: 3888
name: leader-election
command:
- sh
- -c
- "start-zookeeper \
--servers=1 \
--data_dir=/var/lib/zookeeper/data \
--data_log_dir=/var/lib/zookeeper/data/log \
--conf_dir=/opt/zookeeper/conf \
--client_port=2181 \
--election_port=3888 \
--server_port=2888 \
--tick_time=2000 \
--init_limit=10 \
--sync_limit=5 \
--heap=512M \
--max_client_cnxns=60 \
--snap_retain_count=3 \
--purge_interval=12 \
--max_session_timeout=40000 \
--min_session_timeout=4000 \
--log_level=INFO"
readinessProbe:
exec:
command:
- sh
- -c
- "zookeeper-ready 2181"
initialDelaySeconds: 10
timeoutSeconds: 5
livenessProbe:
exec:
command:
- sh
- -c
- "zookeeper-ready 2181"
initialDelaySeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: datadir
mountPath: /var/lib/zookeeper
securityContext:
runAsUser: 1000
fsGroup: 1000
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
Traefik 的权限
# create Traefik cluster role
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
---
# create Traefik service account
kind: ServiceAccount
apiVersion: v1
metadata:
name: traefik-ingress-controller
namespace: default
---
# bind role with service account
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller
namespace: default
Traefik 配置
请注意 Zookeeper 的配置,它使用了“客户端服务”(cs) 的服务地址,以及 Let's Encrypt 的配置。
# define Traefik configuration
kind: ConfigMap
apiVersion: v1
metadata:
name: traefik-config
data:
traefik.toml: |
# traefik.toml
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[zookeeper]
endpoint = "zk-cs.default.svc.cluster.local:2181"
watch = true
prefix = "traefik"
[acme]
email = "your@email.com"
storage = "traefik/acme/account"
onHostRule = true
caServer = "https://acme-v02.api.letsencrypt.org/directory"
acmeLogging = true
entryPoint = "https"
[acme.httpChallenge]
entryPoint = "http"
[[acme.domains]]
main = "your.domain.com"
Traefik 的部署
为了节省开发成本,我只运行了一个副本,但我也将其扩展到了三个,以测试即使随机节点宕机,它是否也能始终保持 100% 的运行状态,结果一切正常 :)。
# declare Traefik deployment
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: traefik-ingress-controller
spec:
replicas: 1
template:
metadata:
labels:
app: traefik-ingress-controller
spec:
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 60
volumes:
- name: config
configMap:
name: traefik-config
containers:
- name: traefik
image: "traefik:1.7.14"
volumeMounts:
- mountPath: "/etc/traefik/config"
name: config
args:
- --configfile=/etc/traefik/config/traefik.toml
- --kubernetes
- --logLevel=INFO
Traefik 服务
# Declare Traefik ingress service
kind: Service
apiVersion: v1
metadata:
name: traefik-ingress-controller
spec:
selector:
app: traefik-ingress-controller
ports:
- port: 80
name: http
- port: 443
name: tls
type: LoadBalancer
最终结果
使用 Traefik 和 Zookeeper 的最终工作负载
还有 Kubernetes Ingress(请忽略我在此用作演示的应用程序)。
关于我
我是一名全栈开发人员和数字产品爱好者,目前可以承接自由职业工作,并且一直在寻找下一个令人兴奋的项目 :)。
您可以通过https://heltweg.org在线联系我。
文章来源:https://dev.to/rhanarion/run-a-personal-cloud-with-traefik-let-s-encrypt-and-zookeeper-4a7g

