基于 k3s 和两台轻量服务器搭建自己的集群
如果你看到了这篇文章,那么说明该博客已成功部署到了 k3s 集群上,并成功签发了 SSL/TLS 证书以支持 Https
由于白嫖的腾讯云服务器到期,并且我不再想续费,所以博客已经去集群化 :-P,但下面的步骤仍可参考
概述
两台轻量应用服务器部署 k3s 并通过 GitHub Action 完成 Hugo 博客的更新,同时给集群上 SSL/TLS 证书 支持对静态 WEB 的 Https 访问
所有静态页全部保存在 master
节点上,通过挂载的方式使用 Nginx 部署,
同时将 Nginx 的配置文件,一并挂载,方便后续添加路由反代等
原先使用的是 Caddy,但 Caddy 会出现反复重定向的问题
当前配置
-
阿里云香港 2vCPU 2G Debain
Debian GNU/Linux 11 (bullseye) 5.10.0-15-amd64 containerd://1.7.6-k3s1.27
-
腾讯云上海 2vCPU 2G Debain
Debian GNU/Linux 12 (bookworm) 6.1.0-9-amd64 containerd://1.7.6-k3s1.27
k3s 的安装与节点添加
在 k3s 中,分为 master
和 agent
两种类型的 node
Server 节点指的是运行 k3s server 命令的主机,control plane 和数据存储组件由 K3s 管理。
Agent 节点指的是运行 k3s agent 命令的主机,不具有任何数据存储或 control plane 组件。
我将拥有域名解析的香港阿里云服务器当做 master
,腾讯云做 agent
k3s 配置要求
https://docs.k3s.io/zh/installation/requirements
安装
开放端口
公网组网需要开放两个端口1
master
agent
51821 适用于 Ipv6
添加节点
k3s 有国内的镜像脚本用来加速安装,但由于我们还没有在两台服务器之间组网,所以需要在安装时加点参数2
感谢某小只叶师傅 x
对于 master
节点
(
SERVER_EXTERNAL_IP=<Yours>
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -s - \
--node-external-ip=$SERVER_EXTERNAL_IP --flannel-backend=wireguard-native --flannel-external-ip
)
上面的 SERVER_EXTERNAL_IP
是 master
的公网地址,<Yours> 需替换成您服务器的公网 IP
在接下来安装 agent
之前,我们需要知道 master
也就是当前节点的 token:
cat /var/lib/rancher/k3s/server/node-token
接下来安装配置 agent
节点
(
AGENT_EXTERNAL_IP=<Yours>
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn K3S_URL=https://<SERVER_EXTERNAL_IP>:6443 K3S_TOKEN=<MasterToken> sh - \
--node-external-ip=$AGENT_EXTERNAL_IP
)
这里需要三个参数:
- 当前
agent
服务器的公网 IP master
的公网 IPMasterToken
:上一步获取的master
的 nodetoken
等 agent
执行完毕后可以在 master
上查看当前集群节点信息:
kubectl get node
如果类似上面那样就 Ok 了
卸载
因为 k3s 十分的轻量,所以安装卸载的成本很低,如果出现了什么棘手的问题,比如 namespace 卡在 Terminating
无论怎样都删不掉等
可以直接将 k3s 扬掉
下面是卸载脚本,在 k3s 安装的时候已经安装好了
master
/usr/local/bin/k3s-uninstall.sh
agent
/usr/local/bin/k3s-agent-uninstall.sh
部署 Hugo 静态博客
下面使用 Nginx 和 Hugo 来构建
GitHub Action 通过 SFTP 部署到服务器
在 Hugo 项目目录中新建 .github/workflows/deploy.yaml
:
name: Deploy blog
on:
push:
branches:
- master
permissions:
contents: write
jobs:
build-and-deploy:
name: Build & Deploy
runs-on: ubuntu-latest
steps:
- name: Check out blog code
uses: actions/checkout@v3
with:
ref: master
submodules: recursive
fetch-depth: 1
- name: Add Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: latest
- name: Build posts
run: hugo
- name: Deploy posts to server
uses: wlixcc/SFTP-Deploy-Action@v1.2.4
with:
username: ${{ secrets.USERNAME }}
server: ${{ secrets.SERVER_IP }}
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
local_path: "./public/*"
remote_path: ${{ secrets.REMOTE_PATH }}
sftpArgs: "-o ConnectTimeout=5"
上面的配置基本不需要修改,只需要注意几点:
- 当前 Action 是否有相应权限
- 正确配置好
secrets
:
- secrets.USERNAME: 服务器用户名
- secrets.SERVER_IP: 服务器公网 IP
- secrets.SSH_PRIVATE_KEY: 私钥
- secrets.REMOTE_PATH: 博客静态页部署在服务器上的位置,比如
/srv/blog/
关于 SSH 的问题可自行查资料解决,一般以下几个步骤:
- 使用
ssh-keygen
生成公私钥 - 将公钥
*.pub
上传到服务器,并添加到authorized_keys
中
编写 k3s 资源配置文件
# nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: zako-api-gateway
labels:
app: nginx-deploy
spec:
selector:
matchLabels:
app: nginx-deploy
replicas: 2
template:
metadata:
labels:
app: nginx-deploy
spec:
nodeName: zako-master # 固定 Pod 在 zako-master 节点,便于访问静态文件
containers:
- name: nginx-deploy
image: nginx
ports:
- containerPort: 80
- containerPort: 443
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d
- name: nginx-log
mountPath: /var/log/nginx
- name: blog
mountPath: /usr/share/zako.top/blog
volumes:
- name: nginx-conf
hostPath:
path: /srv/k3s/zako.top/nginx/conf
- name: nginx-log
hostPath:
path: /srv/k3s/zako.top/nginx/log
- name: blog
hostPath:
path: <前面通过 SFTP 上传的文件地址>
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: zako-api-gateway
spec:
selector:
app: nginx-deploy
type: ClusterIP
ports:
- name: nginx-http
protocol: TCP
port: 80
targetPort: 80
- name: nginx-https
protocol: TCP
port: 443
targetPort: 443
上面涉及到的 nginx.conf:
server {
listen 80;
listen 443;
server_name zako.top;
charset utf-8;
gzip on;
set $static /usr/share/zako.top;
location / {
alias $static/blog/;
index index.html index.htm;
}
}
开放博客外部访问
配置 Ingress
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: zako-ingress
spec:
rules:
- host: zako.top
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc
port:
number: 80
部署后应该能通过域名,我这里是 zako.top
访问到了,但是证书是 k3s 自签的,下面我们换成一个免费的 CA 证书
添加并续签 SSL/TLS 证书3
我选择 Let‘s Encrypt,它提供了免费的 CA 证书: https://letsencrypt.org/
下面的步骤来自:https://juejin.cn/post/7139150146452848648
安装 cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml
检查状态:
kubectl get pods -n cert-manager
部署 Issuing Certificates
创建 letsencrypt.yaml 文件:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
namespace: cert-manager
name: letsencrypt
spec:
acme:
email: <替换成您的邮箱>
privateKeySecretRef:
name: prod-issuer-account-key
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- http01:
ingress:
class: traefik
selector: {}
部署(需等待前面 cert-manager 全部进入 Running 状态):
kubectl apply -f letsencrypt.yaml
下面给我们之前部署的 Ingress 添加 TLS:
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: zako-ingress
+ annotations:
+ cert-manager.io/cluster-issuer: letsencrypt
spec:
+ tls:
+ - secretName: zako-ingress
+ hosts:
+ - zako.top
rules:
- host: zako.top
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc
port:
number: 80
过一小会儿后再次访问,就会发现浏览器没有做出警告了
查看已部署的证书
上面模式是 default namespace,所以没有指定命名空间
kubectl describe certificate
如果您的 Ingress 部署在特定的 namespace 中,需要加上参数:
kubectl describe certificate -n <Namespace>
重定向 http 到 https4
前面我们支持了 Https,现在我希望所有的 Http 请求全被重定向,使用 Https 来访问
办法有两种:
- 修改 k3s 的 HelmChart,全局 Https
- 为 ingress 添加
middleware
,单独对一个 ingress 作用
这里我选择第二种:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: zako-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
spec:
tls:
- secretName: zako-ingress
hosts:
- zako.top
rules:
- host: zako.top
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc
port:
number: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: redirect-https
spec:
redirectScheme:
scheme: https
permanent: true
前后对比:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: zako-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt
+ traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
spec:
tls:
- secretName: zako-ingress
hosts:
- zako.top
rules:
- host: zako.top
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc
port:
number: 80
+ ---
+ apiVersion: traefik.containo.us/v1alpha1
+ kind: Middleware
+ metadata:
+ name: redirect-https
+ spec:
+ redirectScheme:
+ scheme: https
+ permanent: true
注意上面的注解(annotations) traefik.ingress.kubernetes.io/router.middlewares
格式需要为:
<namespace>-<middleware-name>@kubernetescrd
缺省的 namespace 在 k3s 中为 default
应用更改:
kubectl apply -f ingress.yaml
现在所有 Http 访问就会重定向到 Https 了
其他问题
Hugo 访问丢失 CSS 样式
如果 Hugo 的 baseURL 直接指明了协议,比如 https,那么在使用 http 访问时博客会丢失 CSS
可行的办法是将协议去掉5,如:https://zako.top/
换成 //zako.top/
Nginx 日志时间不对
因为 Nginx 默认的是格林尼治时间,所以如果有相应需求,需要在部署时设置好时区:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: zako-api-gateway
labels:
app: nginx-deploy
spec:
selector:
matchLabels:
app: nginx-deploy
replicas: 2
template:
metadata:
labels:
app: nginx-deploy
spec:
nodeName: zako-master
containers:
- name: nginx-deploy
image: nginx
+ env:
+ - name: TZ
+ value: Asia/Shanghai
ports:
- containerPort: 80
- containerPort: 443
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d
- name: nginx-log
mountPath: /var/log/nginx
- name: blog
mountPath: /usr/share/zako.top/blog
volumes:
- name: nginx-conf
hostPath:
path: /srv/k3s/zako.top/nginx/conf
- name: nginx-log
hostPath:
path: /srv/k3s/zako.top/nginx/log
- name: blog
hostPath:
path: <前面通过 SFTP 上传的文件地址>
restartPolicy: Always
kubectl apply -f nginx.yaml
-
https://docs.k3s.io/zh/installation/requirements#%E7%BD%91%E7%BB%9C ↩︎
-
https://docs.k3s.io/zh/installation/network-options#%E5%B5%8C%E5%85%A5%E5%BC%8F-k3s-%E5%A4%9A%E4%BA%91%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88 ↩︎
-
https://aqibrahman.com/set-up-traefik-kubernetes-ingress-for-http-and-https-with-redirect-to-https ↩︎
-
https://discourse.gohugo.io/t/is-it-possible-to-get-static-sites-to-work-with-both-https-and-http/6261/4 ↩︎