白嫖coding,利用k3s自建CI/CD流水线并使用let's encrypt 自动获取证书(附mysql\redis k8s 配置文件)

3,879 阅读9分钟

本篇文章会教你部署k3s、traefik、基于coding平台的CI/CD流水线以及使用Let's Encrypt获取证书

2022-11-05 更新

  • 添加了 ingressrouter 配置
  • 修改了k3s 的安装地址及方式
  • 完善了一些流程

准备工作

  • 云主机一台
  • 开通coding coding.net 并建立仓库,选中持续集成,持续部署,制品管理三个模块

流程: 白嫖腾讯云构建能力,构建镜像 -> 使用部署能力,部署到自建K3s上

过程中如果对k8s不熟悉,建议去查看官方文档,文档还是挺全的,主要理解

  • pods
  • deployments
  • configMaps
  • services
  • ingresses

这些看一下基本上够用了,再看下配置文件怎么写的

安装 helm 会有个大坑,这里尽量使用 root 用户安装 k3s & helm

构建镜像

先在你的github上创建一个仓库,coding的仓库也可以

然后点击: 持续集成 - 构建计划 - 新建构建计划

Docker 镜像推送

此处可选 github 的代码,给个授权就可以

填好镜像名称、位置、目录

镜像版本可以自己手写,自己的项目用 latest 就好

推送制品库时新建一个即可

完成后构建一下试试,构建完成后在制品管理中可以看到该制品

在持续集成 - 构建计划中找到你的计划,点开后打开触发规则,简单配置就是master有推送时就构建

至此,构建镜像完成,接下来搭建k3s

K3s 搭建

安装

curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_EXEC='server --tls-san xx.xx.xx.xx' sh -

xx.xx 为你的IP地址,外网访问需要让K3s生成你服务器IP的自签名证书

安装完成后输入 kubectl get pods 此时应该可以看到一些pod,如果提示没有权限,去搜一下如何开启root账户

然后下载 /etc/rancher/k3s/k3s.yaml 如果里面的 server 为127.0.0.1,就改成https://服务器IP:6443

在腾讯云服务器安全组里开放端口

  • TCP:30000-32768 - K8S POD 直接暴露外网的端口
  • UDP:30000-32768 - K8S POD 直接暴露外网的端口
  • TCP:6443 k3s外网访问端口

管理

打开 Lens (一个k8s管理工具,非常好用,自行下载)

选择右下角+,添加 kubeconfig,将k3s.yaml粘贴进去,Lens首页就能看见新增的项目,点卡,连接,理论上现在能连上了。

这是我部署完服务的状态

lens连接上机器后下面有个+,选择Create resource,可以部署文件,稍后会用到

密钥

拉取镜像需要密钥

参考这个文章 > help.coding.net/docs/cd/que…

项目地址类似这个

--docker-server=xxx-docker.pkg.coding.net/xxx/xxx/

部署服务

持续部署 - K8s - 立即关联

接下来会跳转到部署控制台

左侧选择云账号,绑定云账号,选择K8S,认证方式用Kubeconfig,将之前的k3s.yaml粘贴进去

勾上 接受非认证证书 & 允许持续部署管理集群已有资源

左侧选择主机管理,添加堡垒机,根据要求执行一下命令

左侧选择应用,右上角创建应用,进去后创建流程,直接使用空白流程即可

此处需要使用的为Manifest,建议新建一个仓库扔到coding上,只存放配置文件

这里给出三个示例

deployment 与 service 必须有,config可选

apiVersion: v1
kind: Service
metadata:
  name: backend
  annotations:
    app.kubernetes.io/version: 0.0.1
  labels:
    app: backend
    version: backend1
spec:
  selector:
    app: backend
    version: backend1
  ports:
    - port: 3000 # 暴露的端口
      name: http
      targetPort: 3000
      nodePort: 30008 # 暴露的pod端口
  type: NodePort
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  annotations:
    strategy.spinnaker.io/versioned: "false"
    app.kubernetes.io/version: 0.0.1
  labels:
    app: backend
    version: backend1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
      version: backend1
  template:
    metadata:
      labels:
        app: backend
        version: backend1
    spec:
      containers:
        - name: xxx-backend
          image: xxx-backend:latest # 镜像地址
          imagePullPolicy: Always
          ports:
            - containerPort: 3000
              name: http
          volumeMounts:
            - mountPath: xxx/config.docker.yaml # 挂载的config 没有的话可以去掉
              name: backendconfig
              subPath: config.docker.yaml
          resources:
            limits:
              cpu: 1000m
              memory: 2000Mi
            requests:
              cpu: 100m
              memory: 200Mi
      volumes:
        - name: backendconfig
          configMap:
            name: backendconfig
      imagePullSecrets:
        - name: coding-docker # 拉取容器的证书,参考上面拉取镜像的密钥部分
apiVersion: v1
kind: ConfigMap
metadata:
  name: backendconfig
  annotations:
    app.kubernetes.io/version: 0.0.1
    strategy.spinnaker.io/versioned: "false"
  labels:
    app: backend
    version: backend1
data:
  config.docker.yaml: |
    # redis configuration
    redis:
      db: 0
      addr: 'xxx:30379'
      password: ''

    mysql:
      path: 'xxx:30060'
      config: 'charset=utf8mb4&parseTime=True&loc=Local'
      db-name: ''
      username: 'root'
      password: ''
      max-idle-conns: 0
      max-open-conns: 0
      log-mode: false
      log-zap: ""

点击基础配置,弹出的右侧菜单添加一个触发器

点击基础配置后面的+,添加一个流程,命名为 部署configMap (如果你需要这个流程)

这里 configMap 的配置有个坑 metadata.annotations 里添加

strategy.spinnaker.io/versioned: "false"

不然部署的时候会在 name 后加个随机字符串,会导致你的服务找不到这个 configMap

确定后在 configMap 流程后新建一个流程,添加 services 的配置,配置与 configMap 差不多,选择的文件不同而已

最后再部署你的服务

这里有应该可以自动获取到你的镜像地址

此时返回,启动一下试试,部署时可以在Lens里看到对应的服务被部署了

如果报错可以在Lens里找到Pods,右边三个点,选择Logs查看

如果部署失败,则在Lens里,Deployments 里删除即可,coding里正在跑的部署直接取消掉

此时服务应该部署好了,快去看看能不能连接上

这里如果你的 deployment 里镜像设置的 latest ,那么你需要添加 imagePullPolicy: Always,使其更新时能重新拉取镜像部署pod

traefik ingress

先搭建好 traefik

doc.traefik.io/traefik/ref…

把这里的3个文件扔到lens里去,create出来

doc.traefik.io/traefik/use…

然后看这篇,部署services和deployments即可

先部署dashboard

注意 xxx.xxx.com 是你的域名

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: traefikingress
  namespace: default
spec:
  entryPoints:
    - web
  routes:
    - match: Host(`xxx.xxx.com`)
      kind: Rule
      services:
        - name: api@internal
          kind: TraefikService

部署完后访问一下,应该能打开 traefik dashboard

Auth 2.0

由于traefik自带的中间件有auth2.0的能力,先控制访问 traefik dashboard 吧

lens连接上机器后下面有个+,选择Create resource

左上角选择 Select Template - Secret

name 改为 web-secret

username 后面写你需要的用户名

password 为你的密码

之后添加这个

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: web-auth
spec:
  basicAuth:
    secret: web-secret

再试试访问 traefik dashboard ,会提示需要用户名和密码

删除无用配置

在lens左边最下面有traefik的资源,custom Resources - traefik.containo.us - ingressRouteTCP / UDP / TraefikServices/ ServersTransport / TLSOption 中的资源全部删掉

证书

关于traefik获取证书可以参考下面的文章,但是我部署失败了,提示No ACME certificate generation required for domains有兴趣的可以自行尝试

zhuanlan.zhihu.com/p/431491328

www.qikqiak.com/post/traefi…

这里直接使用dv的证书,后面有另一种获取自动证书的方法。

将你的证书放到服务器上,执行下面的命令

kubectl create secret tls xxx-tls --cert=xxx.cn_bundle.crt --key=xxx.cn.key

以下为两种方式均要安装的文件

首先是中间件,http 跳转 https

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: redirect-https-middleware
  namespace: default
spec:
  redirectScheme:
    scheme: https

接下来配置router

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: traefikingress-web
  namespace: default
spec:
  entryPoints:
    - web
  routes:
    - kind: Rule
      match: Host(`xxx.cn`)
      middlewares:
        - name: redirect-https-middleware
      services:
        - kind: TraefikService
          name: api@internal
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: traefikingress-tls
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(`xxx.cn`)
      middlewares:
        - name: web-auth
      services:
        - kind: TraefikService
          name: api@internal
  tls:
    secretName: traefik-tls

接下来http访问应该可以跳到https了

拯救自动证书

还是想拯救一下自动证书,通过曲线救国的方式实现

这里通过 let's encrypt 签发的证书,可以拿到非常香的通配符证书 *.xxx.com

但是有效期只有3个月,所以需要让他自动续签

先安装helm

curl https://baltocdn.com/helm/signing.asc | sudo apt-key add -
sudo apt-get install apt-transport-https --yes
echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
  • 第二种

鉴于服务器下载太慢了,所以直接把安装包下载下来然后上传到服务器会更好

版本号自己摸索一下最新版,官网有写下一版本号

下载 https://get.helm.sh/helm-v3.10.1-linux-amd64.tar.gz
tar -zxvf helm-v3.10.1-linux-amd64.tar.gz  
cp linux-amd64/helm /usr/local/bin/

安装 cert-manager

cert-manager.io/next-docs/i…

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.7.2/cert-manager.yaml

建议把这个文件下载后扔到服务器上执行,服务器访问github可能会要很久

如果提示

Kubernetes cluster unreachable:Get"http://localhost:8080/version?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused

输入 export KUBECONFIG=/etc/rancher/k3s/k3s.yaml 执行上面的 cert-manager.yaml

这里有个大坑,如果输入了依旧提示8080的问题,换 root 用户安装 k3s & helm 即可

然后找对应你域名代理商的教程,这里我用的 dnspod

imroc.cc/k8s/trick/c…

新建文件
dnspod-webhook-values.yaml

clusterIssuer:
  namespace: default
  enabled: true
  name: dnspod # 自动创建的 ClusterIssuer 名称
  ttl: 600
  staging: false
  secretId: "xxx" # 替换成你的 SecretId
  secretKey: "xxx" # 替换成你的 SecretKey
  email: xxx@xx.com # 用于接收证书过期的邮件告警。如果cert-manager和webhook都正常工作,证书会自动续期不会过期

这里跟原教程不同的是加了个 namespace: default

helm repo add roc https://charts.imroc.cc

helm upgrade --install -f dnspod-webhook-values.yaml cert-manager-webhook-dnspod roc/cert-manager-webhook-dnspod -n cert-manager

接下来签发证书
certificate.yaml

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-crt
  namespace: istio-system
spec:
  secretName: example-crt-secret # 证书保存在这个 secret 中
  issuerRef:
    name: dnspod # 这里使用自动生成出来的 ClusterIssuer
    kind: ClusterIssuer
    group: cert-manager.io
  dnsNames: # 填入需要签发证书的域名列表,支持泛域名,确保域名是使用 dnspod 管理的
  - "example.com"
  - "*.example.com"

然后查看签发状态,整个签发一般要3min

kubectl get certificates.cert-manager.io

失败的话查看日志

kubectl describe certificates.cert-manager.io example-crt

日志看到

Issuing certificate as Secret does not exist

多等一会儿

失败可以看看这个

cert-manager.io/docs/faq/tr…

以下就是成功了

NAME            READY   SECRET                 AGE
xxx-cn-crt   True    xxx-cn-crt-secret   3m40s

之后使用,在 lens 的左侧 Custom Resources - traefik.containo.us - IngressRoute 中找到你的配置,最底下 tls 修改为下面

tls:
    secretName: xxx-tle # certificate.yaml 中的name

然后访问看看,应该有证书了

关于几个配置文件 文件

这里给个 ingressrouter 例子,改改就能用

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: xxxingress-tls
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(`xxx.xxx.cn`)
      services:
        - kind: Service
          name: xxx-server
          port: 8888
  tls:
    secretName: xxx-crt-secret
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: xxxingress-web
  namespace: default
spec:
  entryPoints:
    - web
  routes:
    - kind: Rule
      match: Host(`xxx.xxx.cn`)
      middlewares:
        - name: redirect-https-middleware
      services:
        - kind: Service
          name: xxx-server
          port: 8888

然后是 services

apiVersion: v1
kind: Service
metadata:
  name: mysql-alone
  namespace: default
spec:
  ports:
    - protocol: TCP
      port: 3306 # 容器端口
      targetPort: 3306 # 和上面一样
      nodePort: 30080 # 对外暴露的
  selector:
    app: mysql-alone # 你服务的标签
  type: NodePort
---

附带 Mysql 单机/主从,Redis 单机部署文件

fland.coding.net/public/mysq…