本篇文章会教你部署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
把这里的3个文件扔到lens里去,create出来
然后看这篇,部署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
这里直接使用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
- 第一种方式 helm.sh/zh/docs/int…
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
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
新建文件
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
多等一会儿
失败可以看看这个
以下就是成功了
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
---