ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Ubuntu虚拟机安装Docker完整教程:从环境配置到MySQL部署

Ubuntu虚拟机安装Docker完整教程:从环境配置到MySQL部署 Ubuntu虚拟机安装DockerDocker安装教程前阵子帮一台刚装好 Ubuntu 的虚拟机配置 Docker 环境本以为是apt install docker.io一条命令就能收工的事结果还是折腾了半天——旧版本残留、daemon 起不来、容器连不上网、权限报错一个问题接着一个。后来把整个流程重新撸了一遍从虚拟机平台选型到 Docker 引擎安装、再到拉镜像跑 MySQL每一步都理顺了这篇就当是给自己留的备忘录也给同样在虚拟机上折腾 Docker 的朋友一个参考。这篇教程适合这几类人刚接触 Linux 的新手想在虚拟机里搭一套干净的容器环境练手已经在 Windows/macOS 上用 Docker Desktop、但对虚拟机里原生安装 Docker Engine 不太熟的人以及打算用 Ubuntu 虚拟机做本地开发环境、需要跑 MySQL、Redis 这些中间件的同学。我会把每一步的“为什么这么干”也讲清楚不光是复制粘贴命令关键的地方会让你知道背后在发生什么。1. 准备工作与环境选型1.1 为什么要在虚拟机里装 Docker很多人会问我直接用 Docker Desktop 不就行了为什么非要绕一圈装虚拟机这里面的逻辑其实很实在。Docker Desktop 在 Windows 和 macOS 上本质也是“借”了一层轻量虚拟机来跑 Linux 容器只是这层虚拟化被封装得看不出来了。但封装得越顺滑出问题时排查起来就越费劲——资源占用、网络模式、内核兼容性每一个环节都隔着一层黑盒。而在 Ubuntu 虚拟机里直接装 Docker Engine是原汁原味的 Linux 原生环境容器跑起来和物理服务器上的行为完全一致排查问题也更直观。另一个实际场景是很多时候我们需要同时模拟多台服务器比如一套开发环境要起三个节点做分布式测试。用 Docker Desktop 的虚拟机方案来搞资源隔离和网络拓扑都不如“多台虚拟机各自跑 Docker”来得灵活。还有一点如果你用的是一台配置不高的老机器Docker Desktop 全家桶吃内存吃得厉害虚拟机里轻装一个 Docker Engine 反而更省。1.2 虚拟机平台与 Ubuntu 版本选择虚拟机平台我推荐 VMware Workstation Pro 或 VirtualBox。过去 VMware Workstation Pro 需要授权但现在官方已经宣布对个人用户免费直接去官网注册账号就能拿到免费许可证非常方便。VirtualBox 则完全开源免费胜在轻量缺点是某些高级功能比如 GPU 直通、嵌套虚拟化支持不如 VMware 顺手。如果你之前被“vmware workstation 无法连接到虚拟机”这类错误坑过大概率是 Windows 的 Hyper-V 和 VMware 抢虚拟化权限导致的。后面常见问题章节我会专门说这个。Ubuntu 版本选哪个我的建议是选 LTS 长期支持版。当前最新的 LTS 是 24.04之前是 22.04这些版本的 Docker 官方源支持都很完善。不要选非 LTS 的中间版本比如 23.10 那种生命周期短软件源容易失效Docker 官方源对非 LTS 版本的支持也可能滞后。镜像下载直接从 Ubuntu 官网ubuntu.com/download/server拿 Server 版或 Desktop 版都行。如果只是跑 Docker 做实验我更推荐 Server 版——没有图形界面省内存省得厉害2GB 内存跑起来也很稳。Desktop 版好看是好看了但开机就吃掉 1GB 多内存留给容器用的就少了。1.3 虚拟机资源配置的几个关键参数配置虚拟机时有四个参数直接影响 Docker 的使用体验我踩过坑后才摸清楚的内存至少 2GB建议 4GB。Docker 守护进程本身占用不大但一旦跑 MySQL、Redis、Nginx 等容器内存上去得很快。2GB 内存跑两三个容器基本就是极限了我实测试过 4GB 内存跑四个容器还算流畅。CPU 核心数至少 2 核建议 4 核。容器虽轻但编译镜像、并发处理请求时单核会明显卡顿。磁盘至少 40GB建议 60GB。Docker 镜像、容器层、数据卷都会占空间特别是镜像仓库拉多了之后几十 GB 悄没声就没了。磁盘分配时建议直接选“立即分配所有空间”虽然创建时慢一点但后续运行性能比动态扩展好。网络模式保持 NAT 模式即可。NAT 模式下虚拟机可以共享宿主机网络上网容器再通过桥接模式对外提供服务。如果选了桥接模式在某些路由器环境下反而容易出网络问题。1.4 进入系统后的三个基础动作虚拟机装好 Ubuntu进入系统后不要急着装 Docker先把三件基础事情做了# 1. 更新软件包索引 sudo apt update # 2. 升级已有软件包可选但强烈建议 sudo apt upgrade -y # 3. 安装基础工具 sudo apt install -y curl ca-certificates gnupg lsb-releaseapt update更新的是软件包索引这样后续安装软件时才能找到最新的版本。curl ca-certificates gnupg这几个包是 Docker 官方安装流程里必须要用到的——curl用来下载 Docker 官方 GPG 密钥ca-certificates确保能正确验证 HTTPS 证书链gnupg用来处理密钥。这里有一个很多新手会忽略的细节装好系统后第一件事是把 apt 源换成国内镜像源。Ubuntu 默认官方源在中国大陆访问很慢apt update可能要等好几分钟。国内有多个公共镜像站如果服务器在国外也可以保留官方源根据自己的网络环境来调。换源的方法很简单编辑/etc/apt/sources.list文件把archive.ubuntu.com替换对应的镜像域名即可。这一步做对了后面的安装全程都会顺畅很多。2. Docker 引擎安装全流程解析2.1 官方 apt 源安装 vs 系统自带 docker.io 包在 Ubuntu 上装 Docker有两条主要路径一条是直接用sudo apt install docker.io装系统源里的老版本另一条是走 Docker 官方 apt 源装最新的 Docker Engine。我强烈建议用官方 apt 源。原因很简单Ubuntu 自带的docker.io版本通常比官方要落后很多比如 Ubuntu 22.04 自带的 docker.io 是 20.x 版本而官方源已经到 26.x 甚至更高。Docker 的版本迭代非常快新版本不仅引入新功能更多是修复了网络栈、存储驱动等底层问题。用旧版本跑容器遇到某些镜像启动异常你甚至说不出是版本兼容问题还是配置问题。另一个原因是卸载旧版本的体验差异巨大。用系统源装的 docker.io卸载时容易残留很多配置文件之前见过一位同事卸载后docker命令还在、但 service 文件已经丢失的诡异状态。而官方源的包管理路径很清晰卸载也干净利落。2.2 卸载可能残留的旧版本 Docker如果你之前用其他方式装过 Docker先彻底卸掉。这一步很重要否则后面装新版本时可能遇到conflicting packages冲突报错sudo apt remove -y docker docker-engine docker.io containerd runc # 如果之前是从官方源装的用这个命令卸干净 sudo apt remove -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin卸完之后再检查一下有没有残留目录sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd/var/lib/docker是 Docker 的数据目录里面存着镜像、容器、卷。如果你确定不要旧数据了连同它一起删掉避免新旧版本数据冲突。如果只是卸载不删数据下次安装时会自动沿用旧目录出问题的概率不小。2.3 安装依赖与添加官方软件源接下来加 Docker 官方源。先下载并校验官方 GPG 密钥sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpgGPG 密钥的作用是验证软件包签名——Docker 官方源里的.deb包都带数字签名apt 安装时会用这个密钥验证包没被篡改过。--dearmor的作用是把从网上下载的 ASCII 格式密钥转成二进制格式便于 apt 程序读取。然后添加软件源描述文件echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null这行命令比较长但拆开来看很清晰$(dpkg --print-architecture)会自动输出你的 CPU 架构一般是 amd64$(lsb_release -cs)会输出你的 Ubuntu 版本代号比如 noble、jammy。这两个变量让这份 source 配置适配不同的硬件和系统版本。如果此前 apt 源换过国内镜像这行里的/etc/apt/keyrings/docker.gpg路径也要确保存在。添加完源之后再执行一次sudo apt update此时应该能看到 Docker 官方源的软件包信息了。2.4 安装与启动 Docker Engine执行安装命令sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这几个包分别对应docker-ceDocker 引擎本体、docker-ce-cliDocker 命令行工具、containerd.io容器运行时负责镜像拉取和容器生命周期管理、docker-buildx-plugin构建镜像的扩展插件、docker-compose-pluginDocker Compose编排多容器应用。一次装全后面就不用再补了。安装完成后启动服务并设置开机自启sudo systemctl enable --now docker sudo systemctl status dockersystemctl enable --now docker是enable和start的合并操作——enable让 Docker 开机自启start立刻启动服务。执行status后应该能看到active (running)的状态。验证安装是否成功sudo docker version正确输出会分两段一段是 Client客户端一段是 Server服务端。只看到 Client 段、没有 Server 段说明 Docker 引擎没起来需要回到systemctl status docker排查。2.5 免 sudo 使用 Docker 的权限配置装好后你会发现一个问题每次用 Docker 命令都要加sudo很烦。这是因为 Docker 守护进程默认只给root用户和docker用户组的成员开放访问权限。把当前用户加入docker组就能免 sudosudo usermod -aG docker $USER # 重新登录当前用户使组权限生效 newgrp docker这里有个细节值得注意newgrp docker只对当前终端会话有效如果你重新开了一个终端需要重新执行一次newgrp docker或者直接注销重新登录权限才会在全局生效。我在实际测试中发现很多人在这一步改了不生效就是因为没重新登录就直接敲命令结果遇到permission denied报错。改完之后跑一下docker run hello-world验证如果输出正常说明配置成功。如果不行就重新登录再试。3. Docker 核心配置与实战场景3.1 配置镜像加速源装好 Docker 后第一件事是配置镜像加速。这一步在国内网络环境下几乎是必须的不配置的话从 Docker Hub 拉镜像会慢到怀疑人生。Docker 的镜像加速配置统一放在/etc/docker/daemon.json文件里sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json -EOF { registry-mirrors: [ https://docker.m.daocloud.io, https://docker.1ms.run, https://docker.xuanyuan.me ], log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } } EOFregistry-mirrors是镜像加速列表Docker 拉镜像时会优先尝试这里配置的地址。这里选几个目前公共可用的镜像加速地址具体哪个能用、哪个速度快会随时间变化建议自行实测。log-driver和log-opts是我额外加的日志配置——限制单个容器日志文件大小不超过 10MB最多保留 3 个日志文件。这招能在长期运行时帮你省下大量磁盘空间。没有限制的话一个日志狂魔容器可以把磁盘写满别问我怎么知道的。配置完成后重启 Docker 生效sudo systemctl daemon-reload sudo systemctl restart docker重启后可以验证加速是否生效docker info | grep -A 3 Registry Mirrors输出中能看到你配置的镜像加速地址就说明配置成功了。3.2 最基础的验证跑一个 hello-world 容器配置完毕跑一个最简单的容器验证整条链路docker run hello-world这个命令背后发生的事情值得说一下。当你敲下docker run hello-worldDocker 会按这个顺序行动先在本地镜像库找有没有hello-world镜像找不到就去镜像源拉取。从镜像源拉取镜像到本地。基于镜像创建容器并运行。容器内的程序输出一段欢迎信息后立即退出。如果看到输出中有一行Hello from Docker!说明你的 Docker 安装、配置、网络链路全部正常。此时用docker ps -a能看到那个已经退出Exited的容器记录说明容器生命周期管理也没问题。3.3 实战用 Docker 部署 MySQL 8.0光跑 hello-world 肯定不过瘾我们直接用 Docker 部署一个 MySQL 8.0这是真实的开发场景。docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDYourPassword123 \ -v /opt/mysql/data:/var/lib/mysql \ -v /opt/mysql/conf:/etc/mysql/conf.d \ --restartalways \ mysql:8.0拆开来看每个参数的意义-d后台运行容器也叫 detached 模式日志输出到后台终端不会被占住。--name mysql8给容器命名方便后续用docker logs mysql8、docker restart mysql8等操作。-p 3306:3306端口映射。宿主机 3306 端口转发到容器内 3306 端口这样宿主机和局域网内的机器都能连这个 MySQL。格式是宿主机端口:容器内端口。-e MYSQL_ROOT_PASSWORDYourPassword123设置环境变量。MySQL 官方镜像启动时会读取这个变量来初始化 root 密码。-v /opt/mysql/data:/var/lib/mysql数据卷挂载。容器内的 MySQL 数据目录映射到宿主机的/opt/mysql/data这样即使容器删了数据还在宿主机上。-v /opt/mysql/conf:/etc/mysql/conf.d配置文件挂载可以在宿主机直接写自定义 MySQL 配置。--restartalways容器退出后自动重启包括开机自动启动。这个参数对生产环境尤其重要。首次执行这个命令时Docker 会从镜像源拉取 mysql:8.0 镜像——镜像比较大大约 600MB网速快慢决定等待时间。拉取完成后容器自动启动可以用docker logs mysql8观察 MySQL 的初始化过程。容器启动后验证一下数据库能不能连docker exec -it mysql8 mysql -uroot -pdocker exec是在容器内部执行命令-it是交互模式进去后输入密码就能进入 MySQL 命令行。如果这一步通了说明端口映射、环境变量、数据卷挂载都正常。补充一个实际测试中的体会数据卷挂载-v参数极其重要但没有实际使用过的人很难立刻意识到它的价值。我有一次直接拉了 MySQL 容器跑了一周期间塞了不少测试数据某天手滑执行了docker rm mysql8数据全没了——当时没挂载数据卷。从那之后我再也不敢不挂载数据卷跑数据库容器。3.4 实战部署 Redis 7 并验证读写MySQL 部署完成再装一个 Redis体验一下多容器管理的节奏docker run -d \ --name redis7 \ -p 6379:6379 \ -v /opt/redis/data:/data \ --restartalways \ redis:7Redis 比 MySQL 轻量很多镜像只有 150MB 左右。启动后用命令验证# 进入容器内的 redis-cli docker exec -it redis7 redis-cli # 在 redis-cli 里执行 127.0.0.1:6379 set test_key hello 127.0.0.1:6379 get test_key看到输出hello就说明 Redis 正常工作了。这里有一个小细节Redis 容器挂载的/data目录对应到容器内 Redis 的持久化目录。如果后续要看持久化文件比如 AOF 或 RDB直接访问宿主机的/opt/redis/data就能看到。3.5 容器与宿主机的数据管理说到数据卷展开讲一下 Docker 的数据管理方式这是很多人用了一两年 Docker 也没完全搞明白的部分。Docker 的数据管理有三种核心方式第一种数据卷挂载volume。这是最推荐的方式数据完全由 Docker 管理存放位置是/var/lib/docker/volumes/下。用-v volume_name:/容器内路径的格式。优点是迁移方便docker compose和docker stack对这种方式支持最好。缺点是路径不够直观只想看一眼数据文件得翻到 Docker 的目录深处。第二种绑定挂载bind mount。就是刚才 MySQL 用的那种方式-v /宿主机路径:/容器内路径。数据直接存在你指定的目录看得见摸得着顺手就能备份。缺点是对宿主机目录有依赖迁移时要把目录一起迁走。第三种tmpfs 挂载。数据只存在于宿主机内存中容器停了数据就没了。适合放临时文件、缓存和敏感信息性能极佳但一定不能放持久化数据。我的实践建议是数据库、缓存这类需要持久化的数据用绑定挂载方便检查和备份配置文件用绑定挂载方便修改临时文件用 tmpfs。3.6 用 docker compose 管理多容器最后引入 Docker Compose虚拟机上装多个容器之后命令行逐个docker run会非常低效Compose 是更好的方式。我们创建一个项目目录写一个编排文件把 MySQL 和 Redis 一起管起来mkdir -p ~/docker-app cd ~/docker-app vim docker-compose.yml文件内容services: mysql8: image: mysql:8.0 container_name: mysql8 restart: always ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: YourPassword123 volumes: - /opt/mysql/data:/var/lib/mysql - /opt/mysql/conf:/etc/mysql/conf.d redis7: image: redis:7 container_name: redis7 restart: always ports: - 6379:6379 volumes: - /opt/redis/data:/data然后用一行命令启动所有服务docker compose up -ddocker compose up -d会读取当前目录下的docker-compose.yml把里面定义的服务全部拉起来。相比逐个docker runCompose 的好处主要有三个一是配置文件是代码化的方便分享和复查二是docker compose restart等操作可以批量执行三是服务之间可以共享网络和数据卷定义。找一台机器试一次就明白了尤其是需要同时起五六个服务的开发环境没有 Compose 我会疯掉。4. 常见问题与排查技巧实录4.1 Docker 服务启动失败现象sudo systemctl status docker显示failed或inactive日志里能看到dockerd进程没有正常起来。排查看这里# 查看服务状态 sudo systemctl status docker # 查看详细日志 sudo journalctl -u docker.service -n 50最常见的两个原因一个是/etc/docker/daemon.json语法错误。这个文件是 JSON 格式多一个逗号、少一个引号Docker 都会拒绝启动。我建议写完后先执行cat /etc/docker/daemon.json检查一遍或者用python3 -m json.tool /etc/docker/daemon.json做语法验证——这个命令能准确地告诉你 JSON 哪一行有语法错误。另一个是端口冲突。比如系统里已经有一个 MySQL 占了 3306 端口再启动一个映射 3306 的容器就会失败。可以用sudo ss -tlnp | grep 3306查看端口占用情况找到冲突方后换个端口映射。4.2 遇到 permission denied 无法执行 docker 命令现象不加 sudo 执行docker ps报permission denied while trying to connect to the Docker daemon socket。这个我在前面提过大概率是用户没有成功加入 docker 组或者加入后没有重新登录。按顺序检查# 确认当前用户是否在 docker 组里 groups $USER # 如果不在添加 sudo usermod -aG docker $USER # 然后注销重新登录或者执行 newgrp docker执行完newgrp docker后立刻测试docker ps如果还不行说明当前终端会话没有刷新组权限重新开一个终端或者注销一次就解决了。4.3 Windows 宿主机上 VMware 无法启动虚拟机现象VMware 启动虚拟机时报 “VMware Workstation 无法连接到虚拟机” 或 Unable to open kernel device 之类的错误。这个情况在 Windows 上因为 Hyper-V 或内核隔离开启过而触发过。VMware 和 Hyper-V 都想要虚拟化权限两者同时开启会冲突。解决方案是在 Windows 的“启用或关闭 Windows 功能”里关闭 Hyper-V 和“虚拟机平台”然后重启电脑。如果确实需要 Hyper-V比如要用 WSL2那就改用 VirtualBox它对新版 Windows 的 Hyper-V 兼容做得更好。注意关闭 Hyper-V 会影响 Windows 上的 WSL2如果你也在用 WSL2这一步要考虑清楚建议用 VirtualBox 替代方案解决。4.4 容器内无法访问外网现象容器能启动但容器内执行curl www.baidu.com报网络超时。网络问题的排查思路按层级来# 第 1 步先在宿主机上测外网 curl -I http://www.baidu.com # 第 2 步测宿主机到容器的网络 docker exec -it 容器名 ping 8.8.8.8 # 第 3 步测容器 DNS 解析 docker exec -it 容器名 cat /etc/resolv.conf如果宿主机能联网、容器 ping 不同 IP说明容器网络模式有问题如果容器能 ping IP 但不能解析域名说明是 DNS 配置问题。常见的解决办法重启 Docker 时把网络栈也重建一遍——sudo systemctl restart docker后容器要重新创建才能拿到新网络配置docker restart 容器名即可。如果问题依旧检查虚拟机的 NAT 网络设置有时候主机换了网络环境比如从公司网络切到家庭网络虚拟机 NAT 连接会失效重启虚拟机的网络服务能解决。4.5 磁盘空间被 Docker 占满现象运行一段时间后宿主机磁盘突然告警df -h看到/var/lib/docker占用空间巨大。排查和清理流程# 查看磁盘使用情况 sudo df -h # 查看 Docker 磁盘占用明细 sudo docker system df # 一键清理悬空镜像和停止的容器 sudo docker system prune -a # 如果日志文件占用大量空间清理日志文件 sudo find /var/lib/docker/containers -name *-json.log -exec rm -f {} \;docker system prune -a会删除所有未被容器引用的镜像、停止的容器、悬空的数据卷执行前要谨慎确认因为删除后恢复不了。日志文件的清理则是我个人的常用手段——容器的 JSON 日志会无限增长尤其是跑 Nginx、应用服务的容器一天几个 GB 不是开玩笑。这也是为什么我在前面的daemon.json里配置了日志轮转这是我用经历换来的教训。5. 进阶技巧与收尾提醒5.1 为容器网络做一次自定义前文所有-p 端口映射都是通过 Docker 默认的 bridge 网络完成的。多容器应用还有一个比较核心的用法是自定义网络# 创建自定义网络 docker network create my-net # 运行容器时指定网络 docker run -d --name nginx-test --network my-net nginx # 容器之间直接通过容器名相互访问 docker exec -it nginx-test ping nginx-other自定义网络里的容器会有一个内置 DNS可以用容器名代替 IP 地址互相通信。这意味着你不需要关心容器的 IP 是多少只要知道容器名就行。这个特性在多服务联调、微服务开发场景下能省去大量手工维护 IP 的工作。“本地虚拟机 多端口 nginx 开发环境多站点自定义域名配置”这个场景也依赖这个能力。在虚拟机的 Nginx 容器里配多个 server_name把宿主机的/etc/hosts文件指向虚拟机 IP就能实现本地域名访问多站点开发环境。5.2 备份与迁移的简单思路虚拟机上跑了一堆容器之后备份是个绕不开的诉求。最简单的备份方式是导出容器# 导出容器为 tar 文件 docker export mysql8 -o mysql8.tar # 导出镜像为 tar 文件 docker save -o mysql8-image.tar mysql:8.0备份容器数据卷的方式更直接——把挂载目录整个打包tar -czvf mysql-data.tar.gz -C /opt/mysql/data .真实的技能点不在于“导出/导入”命令本身而在于你必须清楚哪些东西需要备份数据卷目录比如/opt/mysql/data、配置文件比如docker-compose.yml、环境变量定义。只要有这三样在任何一台新机器上都能用docker compose up -d恢复服务。5.3 最后的小经验走到这一步你的 Ubuntu 虚拟机里已经有一个能正常工作的 Docker 环境了。说说我个人的体会。第一次在虚拟机上配 Docker 的人总想把所有东西都弄到完美再去使用我觉得没必要。先把 hello-world 跑起来再跑一个 MySQL感受一下容器的生命周期再慢慢加 Compose、自定义网络、主机网络这些进阶功能。用得多了很多概念不用刻意背就会了。还有一点关于版本升级。Docker 本身升级比较频繁我的习惯是每隔一段时间执行一次sudo apt update sudo apt upgrade把 Docker 引擎和插件升到稳定版最新。升级后建议至少跑一下docker ps确认守护进程正常如果有容器在跑先重启一下它们。最后再分享一个小技巧如果你发现宿主机和容器的时钟不一致启动容器时加上-v /etc/localtime:/etc/localtime:ro挂载把宿主机的时区同步给容器。日志时间和容器时间统一这件事在排查问题时非常有用我之前因为时区问题排查了一个小时的日志错位加了这个挂载之后再也没出现类似情况。Ubuntu 虚拟机上的 Docker 环境虽然折腾起来花点时间但配好之后它就是一台轻量、干净、可复现的容器运行载体。按照上面的流程走从系统安装到 MySQL、Redis 跑起来一小时内基本能搞定祝顺利。
返回列表