k8s实战之reader项目
目录
项目简介
reader是阅读3的服务器版本,github项目如下:
该项目主要是一个承担一个阅读器的作用,并且支持书库和书源功能,同时也可以为阅读app提供webdav的后台用于数据同步/备份,同时该项目还支持多用户使用,RSS订阅等功能。
所使用的技术和工具:
- k8s
- traefik
- acme
- nginx
k8s的安装
安装k8s最好是使用两台纯净的机器来安装,我这边是使用了debain系统,下面都用debain来演示
机器初始化
这部分主要是进行常见的工具安装以及关闭虚拟内存功能:
swapoff -a
modprobe br_netfilter
echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
echo 1 > /proc/sys/net/ipv4/ip_forward
apt install -y curl wget net-tools iptables vim
Sealos初始化k8s
安装sealos
将Sealos的仓库加入到本地apt的仓库中:
echo "deb [trusted=yes] https://apt.fury.io/labring/ /" | tee /etc/apt/sources.list.d/labring.list
apt update
apt install sealos
这样安装的当然就是最新版了,如果想要安装指定版本就可以使用如下指令:
# 抓取最新版
VERSION=`curl -s https://api.github.com/repos/labring/sealos/releases/latest | grep -oE '"tag_name": "[^"]+"' | head -n1 | cut -d'"' -f4`
curl -sfL https://raw.githubusercontent.com/labring/sealos/${VERSION}/scripts/install.sh |
sh -s ${VERSION} labring/sealos
# 指定版本
wget https://github.com/labring/sealos/releases/download/v4.2.1-rc6/sealos_4.2.1-rc6_linux_amd64.tar.gz \
&& tar -zxvf sealos_4.2.1-rc6_linux_amd64.tar.gz sealos && chmod +x sealos && mv sealos /usr/bin
人家官网时好时坏,也挺离谱的
安装k8s
使用sealos工具集成安装k8s,具体指令为:
sealos run labring/kubernetes:v1.25.0-4.2.0 labring/helm:v3.8.2 labring/calico:v3.24.1 \
--masters 主节点ip \
--nodes node1节点ip
这里推荐的是使用1.25版本的k8s,如果使用更高的k8s版本,那么helm和calico都需要相应调整
安装完成后就可以查看状态了
sealos images # 查看镜像
sealos status # 查看运行状态
sealos reset # 重置集群,快速重开😅
有可能涉及到换源的问题,没有科学上网可以考虑镜像站
k8s配置
这个配置主要就是指的是k8s的代码补全配置,首先需要下载代码补全
apt-get install bash-completion
然后就可以将k8s的补全内容导入到其中:
source <(kubectl completion bash)
其中的bash可以换为zsh,取决于你使用的什么shell
之后就可以试一试,后面好多补全都需要这个,比如helm。
然后将这个命令写入到shell配置文件中,bash为 .bashrc
zsh为.zshrc
例如:
echo "source <(kubectl completion zsh)" >> ~/.zshrc
echo "source <(kubectl completion bash)" >> ~/.bashrc
配置deployment
我们先看下reader的镜像需要什么启动参数:
docker run -d --restart=always --name=reader -e "SPRING_PROFILES_ACTIVE=prod" -e "READER_APP_SECURE=true" -e "READER_APP_SECUREKEY=管理密码" -e "READER_APP_INVITECODE=注册邀请码" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader
从中我们可以得到:
- 环境变量
- SPRING_PROFILES_ACTIVE=prod
- READER_APP_SECURE=true
- READER_APP_SECUREKEY=管理密码
- READER_APP_INVITECODE=注册邀请码
- 路径映射
- /storage
- /logs
- 容器端口:8080
直接往根目录映射两个文件夹,这种映射方式我真的服了
命名空间
k8s默认的命名空间是default,一般不在里面运行服务,所以我们新开一个命令空间reader
apiVersion: v1
kind: Namespace
metadata:
name: reader
之后后面关于这个项目的所有对象就都可以放到这个命名空间中了
配置PV和PVC
这里我们不使用第三方的存储方式,直接使用节点的空间即可
因为人家有两个路径需要映射,所以我们生成两个PVC,这时候一般就直接使用StrageClass对象了
但是StorageClass的,因为不支持本地路径的动态分配
所以就直接手动生成PV和PVC了
如果使用一个PVC的话,就需要把/storage或者/logs路径做成软连接,把他们放到同一个路径中,然后就可以了,但是我这里还是使用了两个pvc
PV
在官方文档中我们得到了如下的配置文件:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp
server: 172.17.0.2
主要看spec属性部分:
-
capacity
其下有storage
键对,表示这个pv的大小,这里使用的是5Gi
的大小,其表示方法为:可以看到其中i表示的是2的幂数,即
k=1024
,而不是1000
,其余单位同理 -
之后是
volumeMode
(卷模式)键对,申明这个pv的 卷模式,有两个选项,一个是Filesystem
,一个是Block
是一个原始块,不会被认为是一个文件系统,默认就是使用Filesystem
,表示这个卷是挂载到容器文件路径中的 -
accessModes
(访问模式)是表示这个pv在可以以何种方式被多少个节点使用,例如上述的ReadWriteOnce就意味着可以被一个节点使用(节点上的pod都可以使用),并且是读写的方式。而ReadOnlyMany
就表示可以被多个节点使用,但是是只读。 -
后面的回收策略总共有三种:
- Retain -- 手动回收
- Recycle -- 简单擦除 (
rm -rf /thevolume/*
) - Delete -- 删除关联存储资产
并且只有hostPath
和nfs
才有(Recycle
),不过我们不考虑回收,就默认手动回收即可
-
后面的
nfs
是挂载选项,这里就是使用的是nfs
方式,我们使用的是hostPath
的方式
最后我们的PV如下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: reader-storage-pv
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/reader/storage/"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: reader-logs-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/reader/logs/"
我们的配置中,需要做的就是申明pv名字,大小,阅读方式和路径即可
生成本地的pv只需要在spec中加入hostPath
属性,然后写上路径就可以了
注意的是PV是基本组件,同节点是一个层次的,所以没有命名空间这一说法,就不要画蛇添足给pv的metadata加入命名空间了
PVC
PV是集群的基本资源,当精确到具体服务的时候,就需要对PV进行划分,或者分配,这个过程就是持久卷声明
先看看官方给出的PVC案例
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: slow
selector:
matchLabels:
release: "stable"
matchExpressions:
- {key: environment, operator: In, values: [dev]}
其中的访问模式(accessModes
),卷模式(volumeMode
),资源(resources
)都和pv一样的使用方式
后面的存储类我们这里不需要就直接去掉就可以了,然后就可以得出我们自己的PVC了,如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: reader-stroage-pvc
namespace: reader
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: reader-logs-pvc
namespace: reader
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
这时候就准备好了deployment所需要的所有东西了,就可以顺利开始制作deployment了
更多信息可以直接去看 持久卷的官方文档
deployment
deploy主要是用来维护一类没有状态的pod的,当这个pod退出后,会重新启动一个新的pod来替换,并统一管理pod的资源。
一个deployment中可以有多个pod,其中有的可以运行,有的是备用,但最终目的都是维护这类pod的正常运行。
我们来看一个官网的介绍:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
这是一个nginx的Deployment
我们着重看一下属性部分spec
-
replicas
副本,这里表示创建了3个副本,我们运行查看一下:运行三个副本,使用同样的资源,这样当请求量很大的时候,就可以通过负载均衡到这三个pod中从而减轻单个pod的压力
-
之后是设置需要管理的pod标志,这里面是通过选择标签来确定的,其中
matchLables
中的每个{key,value}
都表示对应的pod信息 -
之后是最重要的
template
字段这个字段就是用来指定Pod模板规范以及给Pod打上标签
当然Deployment并不是一成不变的,也可以通过set对delpayment进行修改,例如上面的例子中,就可以修改nginx的版本:
kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
或者
kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
我们仅仅通过上面的内容就可以完成reader中的deployment编写:
apiVersion: apps/v1
kind: Deployment
metadata:
name: reader-deployment
namespace: reader
spec:
selector:
matchLabels:
app: reader
strategy:
type: Recreate
template:
metadata:
labels:
app: reader
spec:
containers:
- image: hectorqin/reader:latest
name: reader-pod
env:
- name: SPRING_PROFILES_ACTIVE
value: proc
- name: READER_APP_SECURE
value: "true"
- name: READER_APP_INVITECODE
value: xxx
- name: READER_APP_SECUREKEY
value: xxx
ports:
- containerPort: 8080
volumeMounts:
- name: logs
mountPath: /logs
- name: storage
mountPath: /storage
volumes:
- name: storage
persistentVolumeClaim:
claimName: reader-stroage-pvc
- name: logs
persistentVolumeClaim:
claimName: reader-logs-pvc
在这个配置中:
-
我们没有设置副本数量(
.spec.replicas
),默认就是1个 -
选择算符(
.spec.selector
),就是默认使用方法,没有过多设置 -
选择策略(
.spec.strategy
)我们是使用了Recreate
,默认是RollingUpdate
滚动升级这个感觉在这里无所谓
-
最后就是最重要的
template
部分了- 首先是规定了标签,以便于
deployment
创建并管理 - 在
spec.containers
中首先是设置了容器所使用的镜像,并且将环境变量写入到其中了(后面会考虑使用configmap
进行优化),然后就是设置端口和挂载路径 - 由于在containers中使用了挂载容器,所以需要在spec中申明挂载卷(
volumes
)
- 首先是规定了标签,以便于
以上就是这个简单的deployment的详细信息,之后将这部分和上面的都写到一个文件中(分开也行)就可以启动了
kubectl apply -f reader-deployment.yaml
### 然后使用下面指令查看:
kubectl get pod,pvc,deploy -n reader
Deployment的操作中还有更新,回滚等功能,详情可以查看官网中Deployment部分
到此我们就可以创建一个完整的Deployment运行reader项目了
配置Service
下一步是将这个deployment
投射到网络中,变成对网络公开的网络服务。
为什么需要service
呢,因为deyploy
会动态的创建pod,然后动态的销毁pod,而每次创建和删除都会给这些pod一个新的集群IP,这个集群IP是动态的,没办法直接写到边界服务器中,我们需要一种办法,将流量可以动态的分配到这些pod中的方法,这个部分就是Service完成的
在k8s中,Service也是一个对象,也可以被显示的声明,其中K8s官网的例子为:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
这个例子就充分的展示了Service是如何导向流量的,我们不再通过IP地址来转发流量,而是通过pod的标签或者name来进行流量的导向,这时候我们直接访问Service的ip就可以成功访问到后端的Pod。
我们先来了解一下最重要的端口定义。
简单来说就是将Service的某个端口中的流量导向某个Pod中的某个端口
在spce中还有一个参数type
,这个参数有如下几个值:
其中ClusterIP是默认值,也就表示service是绑定在集群IP的,我们可以通过集群中的service IP来访问Pod,但是为了让服务公开网络上(节点上),我们还需要有服务直接绑定到Node上,这时候就需要使用NodePort了,当我们将Service绑定所有节点上,当我们访问节点ip的时候就可以到达pod了(后面会细说)
我们给出一个较为完整的ports
参数
ports:
- protocol: TCP
tpye: NodePort
port: 80
targetPort: 9376
name: nginx-http
nodePort: 30080 # 只有NodePort类型的时候才使用
其中targetPort是值得pod的端口,这个可以是一个值,也可以是Pod端口的名字,例如:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app.kubernetes.io/name: proxy
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app.kubernetes.io/name: proxy
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: http-web-svc
这个例子中,targetPort
就是一个端口的名字
当然我们的目的仅仅是获得一个可以稳定访问服务的Service罢了,不需要太多高级的功能,更多详细的信息可以去看官方Service部分
给出我们reader的Service
对象:
apiVersion: v1
kind: Service
metadata:
name: reader-svc
namespace: reader
spec:
selector:
app: reader
type: ClusterIP
ports:
- protocol: TCP
port: 8080
targetPort: 8080
配置十分简单,但是我遇到一个坑的地方,就是选择器匹配的标签一定是 pod的标签,而不是deployment
的标签,我就是刚开始在这里选择了deploy
的标签结果流量总是到不了,最后才发现是Pod的标签才有用,这也就说明了一件事,Servie
对象和deployment
对象是同一层的对象,都是建立在pod的操作上的,而不是我之前以为的Service
对象是对deployment
的再次封装,位于其上,控制deployment
,这个错误的认识也导致我这部分卡了很久。
将上面的service加入到之前的deployment
中,并进行运行,访问试试能不能通过Sercive
得到我们想要的信息。
之后查看运行指令:
kubectl get pod,pv,pvc,deploy,svc -n reader
访问Service ip的8080也是成功得到内容:
到此,我们的服务就是顺利构建起来了。
配置traefik
安装traefik
能够使用ip地址访问已经不能满足我们的要求了,现在需要增加URL访问的功能,这个功能需要使用ingress,ingress会提供路由到指定的Service中。
但是使用ingress需要使用ingress控制器来负责ingress的正常使用。
能够使用的ingress控制器有如下:
摘自k8s官网,ingress控制器部分
而我们使用的是其中的Traefik控制器
traefik的安装可以使用helm进行安装,也可以手动安装。
我自己使用helm不是很多,且没有安装成功,所以就找了一篇手动安装的教程
在其中的安装中,需要做修改的地方有两处,一处是deployment
中的手动指定k8s api的地址,这个地址需要从集群中获取,也可以修改为master节点ip。
第二处就是service
对象的修改,其文件可以修改为如下:
apiVersion: v1
kind: Service
metadata:
labels:
app: traefik
name: traefik
namespace: traefik
spec:
type: NodePort
selector:
app: traefik
ports:
- name: web
protocol: TCP
port: 80
targetPort: 80
nodePort: 30080
- name: websecure
protocol: TCP
port: 443
targetPort: 30443
nodePort: 443
- name: dashboard
protocol: TCP
port: 9000
targetPort: 9000
nodePort: 30900
- name: tcpep
protocol: TCP
port: 9200
targetPort: 9200
nodePort: 30920
- name: udpep
protocol: UDP
port: 9300
targetPort: 9300
nodePort: 30930
---
apiVersion: v1
kind: Service
metadata:
labels:
app: traefik-metrics
name: traefik-metrics # metrics 用于给集群内的 prometheus 提供数据
namespace: traefik
spec:
selector:
app: traefik
ports:
- name: metrics
protocol: TCP
port: 9100
targetPort: 9100
这部分的修改主要是因为k8s中node节点的端口绑定必须位于30000-32767之间,所以就需要将端口修改到这个部分中
其实其中大部分都是没有用的,最有用的就是web的80和443了,如果需要dashboard的话,还可以加一个9000端口。
默认情况下请求的都是80和443,这里有两种处理方法,一种是直接修改k8s配置,让node节点可以绑定到80和443中,另一种是再加一层nginx反代,将80和443的流量导入到30080端口中,顺便完成证书的验证。这部分后面配置nginx的时候再细说。
使用如下指令验证traefik是否安装成功
kubectl get pod,cm,sa,svc -n traefik |grep traefik
注意在启动deploy前,需要设置Traefik到边缘节点上部署
kubectl label node k8s-node1 IngressProxy=true
配置路由
上一步不论用helm还是手动部署都可以很轻松完成,下面才是我们需要配置的部分,就是Traefik路由部分
traefik支持三种路由的写法:
- 原生ignress
- crd的ingressRoute
- gateway的API
我们使用常见的IngressRoute的方式来写这个路由:
首先是给出一段IngressRoute的样例
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: dashboard
namespace: traefik
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.test.com`)
kind: Rule
services:
- name: api@internal
kind: TraefikService
namespace: traefik
首先是关于这个路由的命名空间,在样例中该路由的命名空间是traefik
的空间,实际上我们一般放在路由所在的命名空间(当然traefik的dashboard路由可以放在traefik
中)
之后就是需要在属性中指明entryPoints
,这部分就是我们在上面service中看到的那些端口,例如web就是80,绑定到30080中,websecure
就是443,绑定到了30443端口上。
后面就是路由部分(routes
),第一个就是匹配,有如下匹配规则可以使用:
规则 | 描述 |
---|---|
Headers(key , value ) |
检查 headers 中是否有一个键为 key 值为 value 的键值对 |
HeadersRegexp(key , regexp ) |
检查 headers 中是否有一个键位 key 值为正则表达式匹配的键值对 |
Host(example.com , …) |
检查请求的域名是否包含在特定的域名中 |
HostRegexp(example.com , {subdomain:[a-z]+}.example.com , …) |
检查请求的域名是否包含在特定的正则表达式域名中 |
Method(GET , …) |
检查请求方法是否为给定的 methods(GET、POST、PUT、DELETE、PATCH) 中 |
Path(/path , /articles/{cat:[a-z]+}/{id:[0-9]+} , …) |
匹配特定的请求路径,它接受一系列文字和正则表达式路径 |
PathPrefix(/products/ , /articles/{cat:[a-z]+}/{id:[0-9]+} ) |
匹配特定的前缀路径,它接受一系列文字和正则表达式前缀路径 |
Query(foo=bar , bar=baz ) |
匹配查询字符串参数,接受 key=value 的键值对 |
ClientIP(10.0.0.0/16 , ::1 ) |
如果请求客户端 IP 是给定的 IP/CIDR 之一,则匹配。它接受 IPv4、IPv6 和网段格式。 |
在此例中,我们一般都是使用Host来进行域名匹配
最后就是需要匹配的Service部分,一般我们只需要指出Service的名称和端口即可:
最后给出我们的路由规则:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: route-reader
namespace: reader
spec:
entryPoints:
- web # 与 configmap 中定义的 entrypoint 名字相同
routes:
- match: Host(`lib.libestor.top`) # 域名
kind: Rule
services:
- name: reader-svc # 与svc的name一致
port: 8080 # 与svc的port一致
路由规则很简单,就是将Host为lib.libestor.top
的域名发送到reader-svc
的8080中
这里没有写443是因为所有的TLS部分都是有nginx来代劳的
到此我们再将这一部分加入到之前的配置文件中,就算是完成了k8s中的绝大部分任务了。
引用自 vbibir's Blog 感谢大佬的内容分享
加入ConfigMap文件
ConfigMap
到上面一步其实就已经完成的k8s中的所有主要内容了,但是把配置文件解耦出来我认为是十分有必要的,所以就有了这一章,将deploy中的env配置文件写入到一个新的文件中,这样的话,不同的人,不同的环境就可以通过只改变config的方式达到不同的运行了
我们这里看一个k8s官网的例子来简单了解一下如何使用configmap配置一个pod
configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# 类属性键;每一个键都映射到一个简单的值
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"
# 类文件键
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: configmap-demo-pod
spec:
containers:
- name: demo
image: alpine
command: ["sleep", "3600"]
env:
# 定义环境变量
- name: PLAYER_INITIAL_LIVES # 请注意这里和 ConfigMap 中的键名是不一样的
valueFrom:
configMapKeyRef:
name: game-demo # 这个值来自 ConfigMap
key: player_initial_lives # 需要取值的键
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: game-demo
key: ui_properties_file_name
volumeMounts:
- name: config
mountPath: "/config"
readOnly: true
volumes:
# 你可以在 Pod 级别设置卷,然后将其挂载到 Pod 内的容器中
- name: config
configMap:
# 提供你想要挂载的 ConfigMap 的名字
name: game-demo
# 来自 ConfigMap 的一组键,将被创建为文件
items:
- key: "game.properties"
path: "game.properties"
- key: "user-interface.properties"
path: "user-interface.properties"
首先是在configmap中,其中数据都是data属性中的键值对
而在之后的pod的使用的时候
- 在环境变量中,我们使用
valueFrom
代替value
,并使用configMapKeyRef
来锁定一个配置文件中的key
所代表的value
值作为这个env
中的value
值 - 同样也可以使用文件的方式,使用方式还是特有的
|
多行内容方式,到时候就会将configmap中的数据以文件的方式挂载到指定位置。
同样,我们照猫画虎写出我们的configmap对象:
apiVersion: v1
kind: ConfigMap
metadata:
name: reader-config
namespace: reader
data:
SPRING_PROFILES_ACTIVE: proc
READER_APP_SECURE: "true"
READER_APP_INVITECODE: xxx
Secret
到这一步之后是还有一个变量没有加载进来,就是reader_app_securekey
参数,这个参数是一个密码文件,我们如果直接写在configmap中不太合适,好的是k8s也为我们提供了专门的用来存放敏感信息的对象:Secret
同样我们也来看一个官方的例子:
apiVersion: v1
kind: Secret
metadata:
name: dotfile-secret
data:
.secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
name: secret-dotfiles-pod
spec:
volumes:
- name: secret-volume
secret:
secretName: dotfile-secret
containers:
- name: dotfile-test-container
image: registry.k8s.io/busybox
command:
- ls
- "-l"
- "/etc/secret-volume"
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
在使用上,Secret
的使用仅仅是将ConfigMap
的字样换成的Secret
的样子
但是Secret有自己的特点,就是其中的值(value
)是base64编码过的内容
那么我们继续照猫画虎的写出我们的Secret
文件:
apiVersion: v1
kind: Secret
metadata:
name: readersecret
namespace: reader
data:
reader_app_securekey: base64(xxx) # base64编码过的密文
到此所有的配置文件都配置完成了
修改deployment
配置文件写好就需要将我们在deployment的硬编码配置给换下来了,最后部分结果为:
template:
metadata:
labels:
app: reader
spec:
containers:
- image: hectorqin/reader:latest
name: reader-pod
env:
- name: SPRING_PROFILES_ACTIVE
valueFrom:
configMapKeyRef:
name: reader-config
key: SPRING_PROFILES_ACTIVE
- name: READER_APP_SECURE
valueFrom:
configMapKeyRef:
name: reader-config
key: READER_APP_SECURE
- name: READER_APP_INVITECODE
valueFrom:
configMapKeyRef:
name: reader-config
key: READER_APP_INVITECODE
- name: READER_APP_SECUREKEY
valueFrom:
secretKeyRef:
name: readersecret
key: reader_app_securekey
最后我们整理一下文件,将命名空间,pv,pvc,deploy,svc,ingressRoute写入到reader.yaml文件中
将Configmap和Secret写入到reader-config.yaml文件中
启动的时候只能是先启动reader.yaml文件,然后才能有reader命名空间给reader-config.yaml文件来启动,但是由于没有配置文件,所以deploy的pod会启动失败,会等到配置文件启动完成后,才会更新pod状态。删除的时候删除reader.yaml后,就可以了,因为命苦空间关闭后,其中的配置也会自动删除。
配置nginx和acme
最后是关于nginx和acme的配置
Nginx
首先是直接在边缘节点上启动一个nginx,然后命名一个新的配置文件:
lib.libestor.top
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://边缘节点ip:30080; # kube-proxy不支持本地回环,不要写127.0.0.1或者localhost
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;
}
}
首先先写入一个简单的80端口转发服务
之后就可以全部启动服务来验证一下服务是否有启动成功:
kubectl apply -f reader.yaml -f config-reader.yaml
kubectl get svc,deploy,pod -n reader
curl 127.0.0.1 -H "Host:lib.libestor.top" -i
可以看到非常顺利的启动了整个服务
这时候80端口的配置就算是完成了,下一步就是使用443端口了
国内访问服务是需要备案的,这个我就不多说了,需要注意的是,你在第一个服务商处备份后,也可以在另一个服务商处接入备案,接入备案的时候,只需要进度到 提交给管局这一步已经可以正常使用服务器了,而这一步只需要添几个表,半个小时内就可以完成,之后让管局慢慢审核,我们正常使用服务器。
Acme
acme自动申请证书可以有多个证书厂商选择,常见的有let's encrypto
(90天)和zero ssl
(180天),本来是想用zero ssl
的,但是使用acme申请的时候总是各种报错,同样的配置,修改为let's encrypto
后1次成功,估计是zero ssl
有bug吧,所以建议使用let's encrypto
Acme有两种申请的验证方式:
- 使用域名厂商的接口证明域名的所有权
- 使用web页面证明域名的所有权
前者不论是在阿里云还是腾讯云都有很详细的教程可以参考,后者的使用更为广泛,尤其是当域名厂商不支持接口调用的时候就尤为好用(例如cloudflare对cf等的免费域名就不支持接口调用)
我们这里使用第二种方法来申请证书。
安装Acme
首先是Acme的安装
curl https://get.acme.sh | sh
配置并申请
acme验证web页面是通过在web页面写入数据,然后签发机构访问得到成功结果,从而验证。
而我们的web服务器默认是转发流量到集群的,这就需要我们修改一些nginx配置了,加一个验证路径,
nginx配置文件为:
server {
listen 80;
server_name lib.libestor.top; # 替换为你的域名
location / {
proxy_pass http://边缘节点ip:30080;
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;
}
location /.well-known/ {
root /var/www/html/;
index index.html;
}
}
然后就是acme申请证书的指令
acme --server letsencrypt --issue -w /var/www/html/ -d lib.libestor.top --alpn --key-file /etc/nginx/cert.d/lib.libestor.top.key --fullchain-file /etc/nginx/cert.d/lib.libestor.top.pem
--server zerossl
其中重要的参数为:
- -w是告知web理解的位置
- --issue是申请证书
- --server 是申请哪个厂的证书
- -d 是域名
- --key-file是私钥位置
- --fullchain-file 是公钥位置
申请成功后会自动创建crond的更新脚本,可以使用
crontab -l # 查看自动任务
最后将443的TLS流量也加入nginx中,最终配置为:
server {
listen 80;
server_name localhost; # 替换为你的域名
location / {
proxy_pass http://边缘节点ip:30080;
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;
}
location /.well-known/ {
root /var/www/html/;
index index.html;
}
}
server {
listen 443 ssl;
server_name localhost;
ssl_certificate cert.d/lib.libestor.top.pem; # 替换为你的 SSL 证书路径
ssl_certificate_key cert.d/lib.libestor.top.key; # 替换为你的 SSL 私钥路径
location / {
proxy_pass http://边缘节点ip:30080;
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;
}
}
最后运行下来也是十分顺利:
其实这里可以看出,所有流量通过nginx后都是发往k8s默认80的30080端口的,没有用上30443,这就是为什么不写30443端口的路由规则的原因了
总结
本文是从k8s的搭建到最后服务搭建的全过程流程,基本还原了我当初学习的大概流程(具体流程记不太清了,可能有所遗漏),中间也是经历了很多的困难,从开始的sealos安装不了,到后面sealos的k8s镜像拉不下来,再到后面的reader镜像拉不下来(这一系列网络问题快给我整自闭了)节点服务器不上网怎么拉去镜像等等问题,搭建了各种隧道才勉强解决(主要是sealos不太会用)倒是学的隧道知识用到这儿了😂是我没想到的。除过环境问题以外,在k8s上困扰我很多的就是StorageClass和Service部分了。刚开始我就是想的使用本地路径,然后想着然StorageClass对象自动对PVC分配pv,搞了半天都是直接pv全部分配给storage或者logs,不会动态分配,后来才之后StorageClass不能动态分配,要两个StorageClass ,那这样话还不如我直接手动分配pv呢,然后就有了blog中的pv和pvc部分了。解决完pv问题后,从pod到deploy过程中都是比较顺利的(指k8s方面,网络问题快杀死我了)直到面对了service部分,哪个service选择器选择的是pod的标签,不是deploy标签,就这个部分把我就难为坏了,我甚至选择器连端口都选择了,最后还是重新读了文档才发现文档中一直都在提的pod被我忽略了,也狠以前service是deployment的上层的错误概念先入为主导致这个地方卡了一段时间。再到后面都是小问题了,比较可惜的是我本来还想通过安装traefik来使用一下helm,但是不知道为什么安装成功,然后又看到了一个大佬安装traefik的文章,就顺利的手动安装了traefik,还获得了一份traefik的配置文件解析模板(美滋滋),再到后面就是想使用zero ssl来申请证书,毕竟是180天,(以前letsencrypt就有时候会犯病,三个月后申请失败什么的)但是失败了,网上说是zero ssl的问题,这我也没办法解决,就只能使用letsencrypt了。
到此本文中所遇到的坑就算是解决完了,实际上我还做了本地存储到reader文件到坚果云的同步,使用了rsync和davfs2两个工具,最后还是失败了(过程也是十分痛苦的)唯一好的就算是学会了如何挂载webdav为本地路径和rsync的配置备份策略。
最后这次是实战也算是自己心血来潮做出来的,中间也是卡了很久才逐渐了解k8s,到现在能部署一个简单的项目(毕竟自己服务器买了几个月了,没用也太亏了doge)。其实自己本来是想先学着在k8s上搭建自己的饥荒服务器的,结果这个reader倒是抢了头筹,不过这个感觉是简单一些的。
第一次写关于k8s的教学式笔记,内容难免有所错误和欠缺,如果又发现错误或者有意见和建议的都可以直接通过邮箱,qq来取得联系。
与君共勉!