k8s实战之reader项目

k8s实战之reader项目

目录

项目简介

reader是阅读3的服务器版本,github项目如下:

reader项目

该项目主要是一个承担一个阅读器的作用,并且支持书库和书源功能,同时也可以为阅读app提供webdav的后台用于数据同步/备份,同时该项目还支持多用户使用,RSS订阅等功能。

所使用的技术和工具:

  1. k8s
  2. traefik
  3. acme
  4. 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

更多内容可以直接去sealos官网查看,国内版

下载 Sealos 命令行工具 | sealos

Sealos 命令说明 | sealos

人家官网时好时坏,也挺离谱的

安装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

从中我们可以得到:

  1. 环境变量
    1. SPRING_PROFILES_ACTIVE=prod
    2. READER_APP_SECURE=true
    3. READER_APP_SECUREKEY=管理密码
    4. READER_APP_INVITECODE=注册邀请码
  2. 路径映射
    1. /storage
    2. /logs
  3. 容器端口: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属性部分:

  1. capacity 其下有storage键对,表示这个pv的大小,这里使用的是5Gi的大小,其表示方法为:

    可以看到其中i表示的是2的幂数,即k=1024,而不是1000,其余单位同理

  2. 之后是volumeMode(卷模式)键对,申明这个pv的 卷模式,有两个选项,一个是Filesystem,一个是Block是一个原始块,不会被认为是一个文件系统,默认就是使用Filesystem,表示这个卷是挂载到容器文件路径中的

  3. accessModes (访问模式)是表示这个pv在可以以何种方式多少个节点使用,例如上述的ReadWriteOnce就意味着可以被一个节点使用(节点上的pod都可以使用),并且是读写的方式。而ReadOnlyMany就表示可以被多个节点使用,但是是只读。

  4. 后面的回收策略总共有三种:

    • Retain -- 手动回收
    • Recycle -- 简单擦除 (rm -rf /thevolume/*)
    • Delete -- 删除关联存储资产
      并且只有hostPathnfs才有(Recycle),不过我们不考虑回收,就默认手动回收即可
  5. 后面的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

  1. replicas副本,这里表示创建了3个副本,我们运行查看一下:

    运行三个副本,使用同样的资源,这样当请求量很大的时候,就可以通过负载均衡到这三个pod中从而减轻单个pod的压力

  2. 之后是设置需要管理的pod标志,这里面是通过选择标签来确定的,其中matchLables中的每个{key,value}都表示对应的pod信息

  3. 之后是最重要的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

在这个配置中:

  1. 我们没有设置副本数量(.spec.replicas),默认就是1个

  2. 选择算符(.spec.selector),就是默认使用方法,没有过多设置

  3. 选择策略(.spec.strategy)我们是使用了Recreate,默认是RollingUpdate 滚动升级

    这个感觉在这里无所谓

  4. 最后就是最重要的template部分了

    1. 首先是规定了标签,以便于deployment创建并管理
    2. spec.containers中首先是设置了容器所使用的镜像,并且将环境变量写入到其中了(后面会考虑使用configmap进行优化),然后就是设置端口和挂载路径
    3. 由于在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支持三种路由的写法:

  1. 原生ignress
  2. crd的ingressRoute
  3. 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的使用的时候

  1. 在环境变量中,我们使用valueFrom代替value,并使用configMapKeyRef来锁定一个配置文件中的key所代表的value值作为这个env中的value
  2. 同样也可以使用文件的方式,使用方式还是特有的| 多行内容方式,到时候就会将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后,就可以了,因为命苦空间关闭后,其中的配置也会自动删除。

ConfigMap的K8s官方文档Secret的K8s官方文档

配置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有两种申请的验证方式:

  1. 使用域名厂商的接口证明域名的所有权
  2. 使用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来取得联系。

与君共勉!

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本站及文章作者不为此承担任何责任。

本站拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经本站允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇