Linux 跑 Docker 总报错?3 个坑提前避开一步到位
Docker 把“环境”变成镜像,Jenkins 把“流程”变成流水线——自动化测试的交付地基就这两步。
文 / 测试逆转大师
在 Linux 上跑自动化测试,第一步就是装 Docker。为什么用 Docker 而不是直接在服务器上装 Python 和浏览器?因为 Docker 把整个运行环境打包成镜像——Python 版本、浏览器驱动、系统依赖全都固化在里面,换个服务器只需要拉同一个镜像,不需要重新配置环境。 安装步骤看似简单,但每一步都有坑。提前知道,不踩不浪费时间。 1,安装依赖包:
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
这三个包各有用途:yum-utils 提供 yum-config-manager 命令(后面添加仓库要用),device-mapper-persistent-data 和 lvm2 是 Docker 存储驱动需要的底层依赖。 2,添加阿里云下载源:
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
Docker 默认的下载源在国外,国内下载速度极慢甚至超时。阿里云镜像站同步了 Docker 官方仓库,速度能提升 10 倍以上。 若报 failovermethod=priority 错误,编辑 /etc/yum.repos.d/CentOS-epel.repo,把 failovermethod=priority 注释掉。这个错误的原因是 CentOS 8 的 EPEL 仓库配置格式和 yum 不兼容,注释掉这个配置项不影响功能。 3,更新缓存。CentOS 8 下 yum makecache fast 会报错,改用:
CentOS 8 把 yum 替换成了 dnf(yum 的下一代),yum makecache fast 在 dnf 里不支持。直接用 dnf makecache 就行。 踩坑提醒:如果你用的是 CentOS 7,继续用 yum makecache fast 就行,不需要 dnf。CentOS 7 和 8 的包管理器不同,命令要区分。 4,安装 Docker(不指定版本):
sudo yum install docker-ce
或指定版本:
sudo yum install docker-ce-17.12.1.ce
为什么有时要指定版本:Docker 的版本更新很快,有些新版本和旧版 CentOS 不兼容(比如 Docker 20+ 要求 CentOS 8+)。如果你的系统是 CentOS 7,建议装一个兼容的旧版本,而不是最新的。
安装后先启动服务。以下是常用操作命令,每个都要熟记:
查看版本:`docker version`。安装后第一步就执行这个,确认安装成功。输出包含 Client 和 Server 两个版本号,Server 版本为空说明 Docker daemon 没启动;
开机启动:`sudo systemctl enable docker`。确保服务器重启后 Docker 自动启动,不用手动拉起来;
查看状态:`sudo systemctl status docker`。看 Docker 是否在运行,不在运行就 `start`;
启动:`sudo systemctl start docker`;
停止:`sudo systemctl stop docker`;
卸载:`sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine`。卸载后镜像和容器数据不会自动删除,需要手动清理 `/var/lib/docker` 目录。
踩坑提醒:
如果 `systemctl start docker` 报错,常见原因有两个:一是 SELinux 没关闭(CentOS 默认开启,会阻止 Docker 访问文件系统),二是内核版本太低(Docker 要求 3.10+)。检查方式:`getenforce` 看 SELinux 状态,`uname -r` 看内核版本;
`docker version` 只显示 Client 版本、Server 版本为空时,说明 Docker daemon 没启动或者权限不够。用 `sudo docker version` 试试,如果 sudo 能看到完整版本,说明当前用户没加到 docker 组。执行 `sudo usermod -aG docker $USER` 把当前用户加进去,然后重新登录。
日常操作中,以下 8 个命令覆盖了 90% 的场景:
1搜索镜像:docker search tomcat。在 Docker Hub 上搜索公开镜像。看 NAME、STARS、OFFICIAL 三列——OFFICIAL 是 [OK] 的表示官方镜像,更可靠;
2拉取镜像:docker pull tomcat。从 Docker Hub 下载镜像到本地。不指定标签默认拉最新版(latest),建议指定版本比如 docker pull tomcat:9.0;
3查看本地镜像:docker images。列出本地所有镜像,看 REPOSITORY(名字)、TAG(版本)、SIZE(大小)。镜像很占磁盘,定期清理不用的;
4运行镜像生成容器:docker run -d -p 9999:8080 镜像ID(-d 后台,-p 端口映射)。这是最核心的命令。-p 左边是宿主机端口,右边是容器内端口——访问宿主机的 9999 端口就能访问容器里的 8080 服务;
5查看运行容器:docker ps。只显示正在运行的容器。加 -a 参数可以看所有容器(包括停止的);
6查看容器日志:docker logs -f 容器id。-f 实时跟踪日志输出,类似 tail -f。排查容器内问题时第一步就是看日志;
7进入容器:docker exec -it 容器id /bin/bash(exit 退出)。进入容器内部查看文件、执行命令。-it 保持交互式终端。容器里没有 bash 的(比如 Alpine 镜像),改成 /bin/sh;
8启停容器:docker start/stop/restart 容器id。重启容器比重新 docker run 快——因为镜像已经在本地了,不需要重新下载。
踩坑提醒:
`docker run` 每次都会创建新容器。如果只是想重启一个已有的容器,用 `docker start` 而不是 `docker run`;
`docker ps` 显示的 CONTAINER ID 是完整 ID 的前 12 位。命令里可以用完整 ID 或前几位(只要能唯一区分就行);
端口冲突:如果宿主机的 9999 端口已经被占用,`docker run -p 9999:8080` 会报错。换个端口或者先停掉占用 9999 的服务。
自动化测试经常需要数据库——测试注册功能要查用户表,测试订单功能要查订单表。用 Docker 跑 MySQL 比在服务器上直接装方便得多:一台机器装多个 MySQL,只需一个镜像、起多个容器。
docker pull mysql:5.7
docker images
mkdir -p ~/mysqlvar/mysql/conf ~/mysqlvar/mysql/data
指定版本号 5.7 很重要:MySQL 8.0 的认证协议变了,有些旧客户端连不上。测试环境用 5.7 更稳定,兼容性好。 启动容器并挂载:
docker run -p 3309:3306 --name mysql \
-v ~/mysqlvar/mysql/conf:/etc/mysql/conf.d \
-v ~/mysqlvar/mysql/logs:/logs \
-v ~/mysqlvar/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 -d mysql
参数逐个解读:
`--name mysql`:给容器起名字,后续操作用名字代替 ID 更方便(`docker start mysql` 比 `docker start a3f2b1` 好记);
`-p 3309:3306`:容器内的 MySQL 默认监听 3306,映射到宿主机的 3309。为什么不用 3306?因为宿主机可能已经装了 MySQL 占了 3306,用 3309 避冲突;
`-v ~/mysqlvar/mysql/conf:/etc/mysql/conf.d`:配置文件挂载。把宿主机的配置目录映射到容器内,修改 MySQL 配置只需改宿主机上的文件,不用进容器;
`-v ~/mysqlvar/mysql/data:/var/lib/mysql`:数据文件挂载。**最关键的一个挂载**——如果不挂载,容器删除后数据库数据就没了。挂载后数据持久化在宿主机上,容器删了重建数据还在;
`-e MYSQL_ROOT_PASSWORD=123456`:初始化 root 密码。只在第一次启动时生效——容器创建后密码就固化了,第二次启动不会再改;
`-d`:后台运行,不占终端。
进入容器与 MySQL:
docker exec -it mysql bash
mysql -uroot -p
开放远程访问权限:
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
踩坑提醒:
`root@‘%’` 表示允许任何 IP 远程连接。测试环境没问题,生产环境绝对不能用——改成指定 IP 比如 `root@‘192.168.1.%’`;
数据目录挂载是必须的。不挂载的话,容器重启或删除后所有数据丢失——你的测试数据、用户注册数据全没了。这不是“可能发生”,而是“一定会发生”;
如果容器启动后 `docker ps` 看不到 MySQL 容器,说明启动失败了。用 `docker logs mysql` 看错误日志,常见原因是数据目录有残留文件(旧的 MySQL 数据格式不兼容),清空 `~/mysqlvar/mysql/data` 再重新启动;
`MYSQL_ROOT_PASSWORD` 只在第一次创建容器时生效。如果密码设错了,删容器、清数据目录、重新 `docker run` 才能改密码。
自动化测试的 CI/CD 流程需要 Jenkins 来调度——定时触发、拉代码、跑测试、出报告。Docker 部署 Jenkins 比直接装在宿主机上更干净:Jenkins 的所有数据和配置都在容器里,升级时只需要换镜像。 拉取镜像:
REFERENCE
注意:docker pull jenkins 拉的是旧版 Jenkins(LTS 2.x)。如果需要最新版,用 docker pull jenkins/jenkins:lts 或 docker pull jenkins/jenkins:latest。旧版镜像已经不再维护,建议用新版的镜像名。
建工作目录并赋权(ID 1000):
mkdir /home/jenkins
ls -nd /home/jenkins
chown -R 1000:1000 /home/jenkins
为什么是 1000?Jenkins 容器内部的进程用 UID 1000 运行,如果宿主机上的目录权限不是 1000,Jenkins 写文件时会报 Permission denied。chown -R 1000:1000 把目录所有权改成容器内用户能访问的。 运行容器:
docker run -itd -p 9090:8080 -p 50000:50000 \
--name jenkins --privileged=true \
-v /home/jenkins:/var/jenkins_home jenkins
逐参数解读:
`-p 9090:8080`:Web 访问端口。浏览器访问 `http://ip:9090/` 就能看到 Jenkins 页面。8080 是容器内 Jenkins 的默认端口,9090 是宿主机暴露的端口;
`-p 50000:50000`:Jenkins 的 slave agent 通信端口。多节点构建时 slave 通过这个端口和 master 通信。单机跑不需要这个端口,但加上不碍事;
`--privileged=true`:给容器 root 权限,方便进容器操作。注意这是安全风险——容器有了 root 权限可以访问宿主机的所有资源。生产环境不建议用,测试环境可以;
`-v /home/jenkins:/var/jenkins_home`:映射宿主机目录。和 MySQL 的数据挂载一样,Jenkins 的所有数据(配置、任务、构建历史)都在这个目录里持久化。
浏览器访问 http://ip:9090/。不通就查防火墙 service iptables status,开放 9090 端口:
firewall-cmd --permanent --add-port=9090/tcp
firewall-cmd --reload
初始密码在 /var/jenkins_home/secrets/initialAdminPassword,宿主机对应 /home/jenkins/secrets/initialAdminPassword:
cat /home/jenkins/secrets/initialAdminPassword
踩坑提醒:
初次访问 Jenkins 页面加载很慢(可能要几分钟),这是因为 Jenkins 在初始化插件。耐心等,不要反复刷新;
如果页面打开但提示“Jenkins is not fully set up”,说明初始化还没完成。等页面自动跳转到设置向导;
`/home/jenkins/secrets/initialAdminPassword` 的密码只在首次安装时生成,输入后进入设置向导,设置完管理员密码后这个初始密码就没用了。**第一时间设置管理员密码并记住**;
防火墙问题是最常见的“访问不通”原因。CentOS 7 用 `firewall-cmd`(上面已给出命令),CentOS 6 用 `iptables -I INPUT -p tcp --dport 9090 -j ACCEPT`。
REFERENCE
Jenkins 的具体使用看同专栏另一篇。Docker 把环境固化,Jenkins 把流程固化,两者到位,持续集成就立起来了。
这篇是 Playwright 持续集成的前置地基。Docker + MySQL + Jenkins 三件套跑顺,后面接 Playwright 和 Allure 就是水到渠成。 回头看三个核心坑:
1Docker 安装时 yum/dnf 命令版本不匹配——CentOS 7 用 yum、CentOS 8 用 dnf,搞混就报错;
2MySQL 容器不挂载数据目录——容器删了数据就没了,测试白跑;
3Jenkins 容器目录权限不对——UID 1000 不匹配就 Permission denied。
这三个坑提前避开,后面的事情就顺了。 学习自动化测试,关注 测试逆转大师。
既然看到这里了,如果觉得有用,随手点个赞、在看、转发三连吧。
—— 测试逆转大师