用芯飞云搭测试环境这件事,聊聊我的实际折腾过程
一、为什么选8核16G做测试环境
团队之前搭建测试环境,用的是一台2核4G的机器。平时跑个单体应用还行,但后来项目上了微服务,又加了MySQL和Redis,这台机器就开始力不从心了。
最明显的问题是跑CI的时候。单元测试、镜像构建、静态扫描这些任务堆在一起,CPU直接被打满,开发同事想连上去看个日志都要等半天。有一次新来的同事不知道情况,一口气跑了三个服务的集成测试,结果整台机器卡死,只能重启。
后来决定换配置。选型的时候看了一圈,最后选了芯飞云的8核16G。这个配置CPU和内存是1:2的配比,每颗核心对应2G内存。实际用下来,这个比例比较均衡,不会出现CPU闲着但内存不够用,或者内存空着但CPU跑满的情况。
对于测试环境来说,这个配置能跑4-6个2核4G的容器,基本覆盖了我们团队的需求。前端、后端、数据库、缓存各占一个容器,互不干扰,CI任务也能单独跑一个容器,不会影响其他人调试。
二、测试环境的几个硬性要求
搭建测试环境这些年,踩过不少坑,总结下来有几个要求是必须满足的。
环境要能随时重建。 测试环境和生产环境不一样,经常需要重置。有时候测试数据被改乱了,有时候配置被改错了,直接删掉重建比一点点排查快得多。所以从第一天起,我们就把所有环境配置写成了docker-compose文件,重建只需要一条命令。
资源要隔离。 多个分支同时测试是常态,如果共用一台机器还不做隔离,一个分支的测试把内存吃满,其他分支就全挂了。Docker的资源限制这时候就派上用场了。
环境变量要分清。 这个是血泪教训。有次测试环境的代码连到了生产数据库,差点出事。后来强制要求所有环境变量必须通过配置文件注入,代码里不允许写死任何连接信息。
数据要可丢弃。 测试环境的数据就是用来造的,不需要备份,不需要恢复。想清楚这一点,很多事情就简单了。
三、实际配置过程
3.1 Docker环境准备
芯飞云的控制台预置了主流系统镜像,我选的是Ubuntu 22.04。系统装好后,第一件事是装Docker。
# 更新源
sudo apt update
# 安装Docker和Docker Compose
sudo apt install -y docker.io docker-compose
# 启动Docker服务
sudo systemctl start docker
sudo systemctl enable docker
# 把当前用户加入docker组,省得每次都sudo
sudo usermod -aG docker $USER
装完之后退出重新登录,让用户组生效。
3.2 编写docker-compose配置
测试环境的核心服务有三个:MySQL、Redis和后端应用。用docker-compose统一管理,配置文件长这样:
# docker-compose.yml
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: test-mysql
environment:
MYSQL_ROOT_PASSWORD: test123456
MYSQL_DATABASE: testdb
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
# 资源限制,别让MySQL把内存吃光
deploy:
resources:
limits:
memory: 4G
cpus: '2'
command: --innodb-buffer-pool-size=2G
redis:
image: redis:7-alpine
container_name: test-redis
ports:
- "6379:6379"
deploy:
resources:
limits:
memory: 2G
cpus: '1'
backend:
build: ./backend
container_name: test-backend
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/testdb
SPRING_REDIS_HOST: redis
depends_on:
- mysql
- redis
deploy:
resources:
limits:
memory: 4G
cpus: '2'
volumes:
mysql_data:
这个配置里有几个地方值得说一下。
deploy.resources.limits是给每个容器设置资源上限。测试环境最容易出的问题就是某个服务内存泄漏,把整台机器拖垮。加上这个限制之后,最坏情况就是那个容器被OOM Killer干掉,其他服务不受影响。
MySQL的innodb-buffer-pool-size设成了2G。默认值是128M,对于测试环境来说有点小,查询会频繁读磁盘。设成2G之后,大部分查询都能命中缓存,响应快很多。
depends_on保证启动顺序,MySQL和Redis先起来,后端服务再启动。不过这个只能保证容器启动顺序,不能保证服务真正就绪。如果需要严格等待,得用healthcheck。
3.3 后端服务的Dockerfile
后端是个Spring Boot项目,Dockerfile比较简单:
# backend/Dockerfile
FROM openjdk:17-slim
WORKDIR /app
# 先复制依赖文件,利用Docker缓存
COPY pom.xml .
RUN apt-get update && apt-get install -y maven && mvn dependency:go-offline
# 再复制源码
COPY src ./src
RUN mvn package -DskipTests
# 启动命令,注意JVM参数
CMD ["java", "-Xms2g", "-Xmx2g", "-jar", "target/app.jar"]
JVM参数这里设了-Xms2g -Xmx2g,把堆内存固定住。默认情况下JVM会动态调整堆大小,在测试环境这种流量波动大的场景下,频繁扩容会触发GC停顿,影响测试体验。
四、日常使用中的几个场景
场景一:CI任务隔离
之前CI任务和开发环境跑在同一台机器上,一到下午集中提交代码的时候,CI把CPU占满,开发同事连SSH都卡。
后来把CI单独放了一个容器,限制只能用2核CPU。这样即使CI跑满,也只占用四分之一的CPU资源,开发环境不受影响。
# CI容器的启动命令
docker run -d \
--name ci-runner \
--memory=4g \
--cpus=2 \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkins/jenkins:lts
–cpus=2这个参数很重要。不设的话,CI任务会把8个核心全占了。设成2之后,CI跑得慢一点,但开发环境能正常用。
场景二:分支级测试环境
团队规模大了之后,经常需要同时测试多个分支。之前的做法是共用一套环境,谁要测试就切一下代码,但这样很容易冲突。
后来改成每个分支一套独立的环境,用不同的端口区分。docker-compose支持通过环境变量动态设置端口:
# docker-compose.yml 片段
services:
backend:
ports:
- "${BACKEND_PORT:-8080}:8080"
启动的时候指定端口:
# 分支A用8080端口
BACKEND_PORT=8080 docker-compose -p branch-a up -d
# 分支B用8081端口
BACKEND_PORT=8081 docker-compose -p branch-b up -d
-p参数给项目起了个名字,不同项目的容器不会互相干扰。这样两个分支各自有一套完整的MySQL、Redis和后端服务,测试数据也是独立的。
场景三:快速重置环境
测试环境用久了,数据会变得很乱。有时候想回到干净状态,重新跑一遍完整的测试流程。
之前的方式是手动删数据、清缓存,操作繁琐还容易漏。现在直接用docker-compose重建:
# 停止并删除所有容器和数据卷
docker-compose down -v
# 重新构建并启动
docker-compose up -d --build
-v参数会同时删除数据卷,MySQL的数据也会被清空。整个流程不到一分钟,比手动清理快多了,而且保证每次都是干净的环境。
五、一些使用感受
芯飞云8核16G跑测试环境这段时间,整体比较稳定。独享核心的配置,不会出现邻居跑满导致自己卡顿的情况。SSD存储的IO表现也可以,MySQL的查询响应基本在预期范围内。
成本方面,这台机器的价格是70.30元/月,对于测试环境来说负担不大。相比之前用的2核4G,虽然贵了一点,但省下来的时间成本远超这个差价。
有一点需要注意:测试环境毕竟是测试环境,不适合跑生产业务。如果需要更高可用性或者更强的性能,还是得换更高配置的实例。
总的来说,用芯飞云搭测试环境是个不错的选择。配置够用,价格能接受,稳定性也还行。如果你也在纠结测试环境用什么机器,可以参考一下这个方案。

370

被折叠的 条评论
为什么被折叠?



