introduction
本文是对于测试环境的容器化部署的一些思路和展望。
1.痛点所在
(1) 服务器资源利用率低,测试环境的服务器配置差异较大,使用情况各异,总体利用率不高。
(2) 测试若刮风频繁,不仅会影响在体验的人,测试自己也不太能自由的刮风。
(3) 服务器没有任何隔离性,多个岛的隔离性只能通过存在于不同服务器来保证,会引发一些问题,例如炸岛可能会极大的占用服务器资源,或者更改时间会影响到其它人或服务。
(4) 扩展不太方便(指的是岛数量增加)。
(5) 不好简单的配套相应的监控,需要比较兼容且轻量级的。
(6) 在大家都不愿手动起岛的情况下,影响团队和谐和开发效率。
2.解决思路
(1) 将岛的进程放到docker
里面,每次启动容器就可以了。但是有个很严重的缺陷,扩展起来十分不易,因为我们测试环境涉及到多个服务器,不易管理。
(2) 所以引入了k8s
集群管理方案,但是我们的起岛进程和传统的微服务又不太一样,我们对状态的要求很严格,而且还存在以下的问题:
1.我们要将1号岛起在52服务器上面,就要先去数据库注册数据,把1号岛写到数据库,然后再起岛。但是k8s
集群分配pod
的时候用户进程是不知道k8s
会把哪个pod
(岛进程),也不应该知道。
2.对于测试环境,我们的ci
会不断的把新的代码推送到仓库,如果每次推送都构建一个镜像,那么每次创建新的pod
就会需要拉取新的镜像,是十分耗时的。
3.对于development
(k8s
的一类创建pod
的资源,会持续管理pod
的生命周期)的起岛方案来说,我们的pod
被损坏后,他会持续的去重启pod
,但是我们的岛却不太适合这种恢复方案,因为我们希望我们的用户进程去管理岛,而不仅仅是配置一些恢复方案去恢复状态,而且对于development
来说每个pod
没有差异性,这是用户进程不能容忍的。
解决:
对于1,我们将注册进程放到pod
里面,每次启动pod
的时候执行注册,并且在创建pod
之前就预计要暴露的端口。
对于2,我们用永久卷的方式,将代码挂载到共享卷里面,就不用每次拉取镜像了,而且也不用去改变ci
在测试环境现存的运作形式。
对于3,我们直接创建pod
,由用户进程管理pod,但是分配还是交给k8s,因为不想放弃k8s分配的诱人。
3.代码实现
(1) 嵌套在自动导表系统里面,耦合性会强一点,有必要再分离吧。
(2) 与自动导表不同的是,这边要与服务器做通信,我们用jsch的shell方式去通信,将读取交给另外的线程处理。
4.最终思路总结
(1) 现在的起岛步骤变为: (824服务器是我们自动导表的服务器,与pod
管理进程放在一起 52服务器是我们k8s
的master
服务器 其它服务器是普通的node
节点)
1.用户点击起岛。
2.824服务器接到这个请求,然后根据自己的计数分配端口和id
(全局唯一),然后通过id
将k8s
的启动pod
的yaml
创建,通过apply
命令通过API
在52服务器进行创建。
3.52服务器的apiapiserver
将其写入etcd
。
4.scheduluer
检测到未绑定 Node
的 Pod
,开始调度并更新 Pod
的 Node
绑定。
5.kubelet
检测到有新的 Pod
调度过来,通过 container
runtime
运行该 Pod
。{
运行该pod
的时候,我们会将此服务器注册进mysql
,并且根据yaml
的参数起岛。
}
6.kubelet 通过 container runtime 取到 Pod 状态,并更新到 apiserver 中。
7.创建成功,我们的824服务器检测到这个状态,最终返回给前端。
5.一些小细节
1.什么时机进行岛信息注册?
容器创建的时候,里面会有一个注册脚本,注册脚本通过http请求,将本岛注册到mysql。
2.side container有什么作用?
防止pod被销毁,保留server container的残留信息。
3.怎么简单监控Pod
和server
container
的状态?
对于server container,使用容器指针,通过检测TCP端口,可以检测到server进程是否已经启动。
对于Pod,使用API Server即可直接看到状态。
4.为什么要以挂载的形式将代码单独挂载?
1是处于对全量复制的速度的担心。
2.因为还没有使用私人仓库,镜像是从docker hub直接拉取的,也会有隐私问题。
5.时间修改
由于容器技术并不保证隔离时间,通过此项目完成时间的隔离https://github.com/wolfcw/libfaketime 可做到进程级别或者pod级别的隔离。
6.展望与改进
(1) 每个岛可能有些特殊化配置,之后评估一下影响。
(2) 目前为了兼容现存ci
环境,所以把集群机器强行分为内测和当周,通过label
分配机器,看后续是否有必要处理。
(3) 有位童鞋提了个思路很好,就是把所有的windows
机器加入集群,这样就不用担心内存不够了,但是这里面可能会涉及到安全的问题,而且windows装虚拟化产品也不易,暂时作为一个思考方向。
(4) 调度策略不行 加了资源限制,不够会被驱逐或者分配时直接失败。
附录:原始yaml,每个pod暴露3个端口,全局管理30000开始
apiVersion: v1
kind: Pod
metadata:
name: altraserver-liudianbo-dev-269
labels:
name: altraserver
spec:
volumes:
- name: server-pv-storage
persistentVolumeClaim:
claimName: server-pv-claim
containers:
- name: altraserver
image: bo602505401/jdk8-curl-libfaketime:latest
resources:
limits:
memory: "2048Mi"
requests:
memory: "1500Mi"
env:
- name: curlServer
value: 10.18.8.97
- name: username
value: 你猜
- name: LD_PRELOAD
value: "/usr/lib64/faketime/libfaketime.so.1"
- name: FAKETIME_TIMESTAMP_FILE
value: "/tmp/server-faketime.rc"
- name: FAKETIME_DONT_RESET
value: "1"
- name: FAKETIME_DONT_FAKE_MONOTONIC
value: "1"
command: ["sh","/home/seed.sh","269","false","1269","2021-09-18 23:30:07"]
securityContext:
capabilities:
add: ["NET_ADMIN","SYS_TIME"]
volumeMounts:
- mountPath: "/home/server"
name: server-pv-storage
ports:
- name: app-common-port
containerPort: 30807
hostPort: 30807
- name: remote-msg-port
containerPort: 30808
hostPort: 30808
- name: server-port
containerPort: 30809
hostPort: 30809
readinessProbe:
tcpSocket:
port: server-port
initialDelaySeconds: 15
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 10
failureThreshold: 10
- name: side-container
image: alpine:3.14.1
command: ["/bin/sh", "-ce", "tail -f /dev/null"]
nodeSelector:
branch: dev
dnsPolicy: Default
restartPolicy: Never
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: server-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/home/server"
---
`apiVersion`: `v1`
`kind`: `PersistentVolumeClaim`
`metadata`:
`name`: `server-pv`-`claim`
`spec`:
`storageClassName`: `manual`
`accessModes`:
- `ReadWriteOnce`
`resources`:
`requests`:
`storage`: 8`Gi`