ARTICLE DETAIL

资讯详情

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

Ubuntu 安装 Docker 完全指南:从系统准备到生产环境部署

Ubuntu 安装 Docker 完全指南:从系统准备到生产环境部署 1. 准备阶段装 Docker 之前必须想清楚的几件事很多人在 Ubuntu 上装 Docker 装到一半卡住不是因为命令敲错而是因为一开始就走错了路。我见过不少朋友直接apt install docker装完了发现版本老得离谱或者装完启动不了又或者每次执行都得加sudo搞得人很崩溃。这些问题的根源大多在安装之前的准备工作没做扎实。1.1 别再直接用 apt 默认源装 Docker这里要先聊一个很关键的概念Docker 这个软件其实经历过好几次大的命名变化。很多教程里让你执行的apt install docker装的实际是docker.io这个包它是 Ubuntu 官方仓库里维护的 Docker 版本更新节奏远落后于 Docker 官方。我刚接触 Docker 那会儿就吃过这个亏用系统源装完发现很多新特性用不了折腾半天还得卸载重装。打个比方系统源里的 Docker 就像超市货架上摆的罐头能吃饱但口味固定而 Docker 官方源里的版本相当于直接去原产地菜市场买当天的新鲜食材想要什么风味自己加工。对于需要跑容器化应用的人来说官方仓库里的docker-ce社区版才是正确的选择。所以我们要做的事情就是先把 Docker 官方提供的软件源接入到 Ubuntu 的 apt 体系里这比用系统默认源稳妥得多。1.2 卸载旧版本防止装出两个 Docker的奇葩局面如果你之前用系统源装过 Docker或者系统里残留着旧版本最好先清干净再开始。否则装完docker-ce之后/usr/bin/docker和/usr/bin/dockerd这些命令可能会出现冲突执行docker version的时候会显示两套不相关的信息。清理操作很直接执行下面的命令sudo apt remove docker docker-engine docker.io containerd runc注意这几个名字里docker和docker-engine是早期版本的包名现在基本见不到了但执行一下也无妨确保系统里没有历史遗留。执行完后Docker 的配置文件、镜像和容器数据其实还在/var/lib/docker目录下如果你确定之前的数据都不需要了可以直接删掉sudo rm -rf /var/lib/docker这一步千万别手软也不要跳过。我见过有人在没清理干净的情况下安装结果dockerd进程起来了但容器网络一直报错查来查去发现是旧版本残留的配置文件和新的冲突了。当然如果你打算升级而不是全新安装那/var/lib/docker里的数据要保留镜像也能继续用。1.3 检查系统版本和内核这步花两分钟能省两小时Ubuntu 装 Docker 对系统版本有基本要求。64 位架构是硬性要求现在基本没有人在 x86 上跑 32 位系统了但有些老设备上可能还在用需要先确认一下。uname -m看到x86_64或者aarch64就没问题。然后看看内核版本uname -rDocker 对内核版本要求不算苛刻但太老的内核会导致存储驱动和网络功能出问题。Ubuntu 20.04 之后的内核版本都在 5.4 以上装 Docker 完全够用。如果你还在用 Ubuntu 16.04 这类老版本先升级系统再做打算别硬装。另外还得确认网络状况。Docker 安装过程需要从官方源下载一些东西如果在某些受限网络环境下可能需要提前把源切换到国内镜像。这个话题下面会专门讲这里先记住一个原则装 Docker 之前先确认你的网络环境能访问到 Docker 的官方源。2. 官方仓库安装全流程从添加 GPG 密钥到 apt 源配置准备工作做完接下来就是正式安装了。这一整套流程在 Docker 官方文档里有标准步骤但很多人照着文档敲完还是迷迷糊糊。这里我把每一步的原理讲清楚这样哪怕以后命令有变化你也能自己应变。2.1 添加官方 GPG 密钥为什么不能省这一步GPG 密钥可以理解为软件包的指纹加签名。apt 源里的所有软件包都会带一个数字签名系统装包之前会先校验这个签名确保你下载的包确实是官方发布的没人动过手脚。Docker 官方把所有的包都放在自己的仓库里我们必须先把官方公钥添加进系统apt 才能正确校验 Docker 仓库里的包。sudo apt update sudo apt install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod ar /etc/apt/keyrings/docker.asc这段命令有几个细节需要说明。ca-certificates和curl是后续命令的基础工具有些精简版系统可能没装。/etc/apt/keyrings这个目录是新版 Debian/Ubuntu 系统的规范位置专门用来存放第三方软件源的密钥比以前的apt-key add方式安全得多。最后chmod ar设置的是读取权限确保 apt 能读到这个密钥文件。为什么这里不推荐用老教程里的apt-key add因为apt-key的方式会把密钥直接加进系统的全局信任列表如果某个源的密钥被你误删了其他源也会受影响。而且现在官方文档都已经改成了 keyrings 目录的方式说明这确实是更合理的做法跟着新规范走不容易踩坑。2.2 配置软件源并安装 Docker 全家桶密钥加好了接着就是把 Docker 的源写入 apt 的源列表。这里要根据你的 Ubuntu 版本选择合适的代号比如 24.04 对应的是noble22.04 是jammy20.04 是focal。用命令自动检测最稳妥sudo sh -c echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable /etc/apt/sources.list.d/docker.list这段命令看着复杂拆开来看其实就几件事用dpkg --print-architecture自动获取你的系统架构用/etc/os-release里的VERSION_CODENAME自动匹配对应的 Ubuntu 版本代号最后把整个源配置写入docker.list文件。你不需要手动改任何内容执行完就配好了。然后更新源并安装sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里一次装了五个包它们的职责分别是docker-ceDocker 的核心服务端也就是常说的守护进程负责管理和运行容器docker-ce-cli命令行客户端就是你在终端里敲的docker命令containerd.io容器运行时负责实际创建和运行容器是 Docker 底层的那个引擎docker-buildx-plugin构建镜像的插件用来打包自定义镜像docker-compose-pluginCompose 插件用于管理多容器应用有很多人只装了前三个结果后面想用docker compose命令时发现没有。一步到位把这些都装上能避免后面很多麻烦。2.3 启动 Docker 服务并设置开机自启装完之后Docker 服务不会自动启动需要手动把它拉起来。执行sudo systemctl start docker如果想设置开机自动启动sudo systemctl enable docker这两条命令有什么区别start是立即启动服务enable是设置开机启动。一条管现在一条管将来。enable本质上是创建一个符号链接指向 systemd 的服务单元文件这样系统开机时会自动拉起 Docker。启动完成后可以验证一下服务状态sudo systemctl status docker看到active (running)就说明服务在运行。但这里有个很容易被忽略的点在终端里跑service docker status和跑systemctl status docker显示的信息可能略有不同后者更详细也更可靠。如果你用的是 WSL 环境或者某些精简版系统用service docker start可能更顺手但正常情况下 systemctl 才是正统方式。3. 非 root 用户跑 Docker权限问题与启动失败排查新系统装完 Docker你高高兴兴执行docker ps结果大概率会看到一句permission denied while trying to connect to the Docker daemon socket这个报错可以说是新手遇见的头号杀手。好多人卡在这里以为自己装坏了其实完全不是。3.1 为什么非 root 用户会报权限错误Docker 的守护进程默认监听在/var/run/docker.sock这个 Unix 套接字上而这个文件的所有者是 root权限是rw-rw----属于docker组的用户才有权读写。普通用户没在这个组里自然连不上。大多数教程会告诉你一个简单的解决办法所有 docker 命令前面加sudo。这确实能用但副作用很明显——每次都要多敲几个字母而且如果你的用户本身不在 sudoers 列表里根本行不通。更重要的是长期用sudo docker会让文件权限变得混乱因为宿主机上以 root 身份创建的容器卷文件经常会遇到权限问题。3.2 docker 组官方给出的标准答案正确做法是把你的用户加进docker组。执行sudo usermod -aG docker $USER然后退出登录重新登录或者直接执行newgrp docker让当前会话立即生效。重新登录后你再跑docker ps就不会有权限问题了。但这里必须提醒一句把用户加进 docker 组相当于给了这个用户接近 root 的系统权限。因为 Docker 容器可以通过挂载宿主机目录等方式访问系统文件一个能控制 Docker 的用户实际上能间接控制宿主机。所以如果你是在多人共用的服务器上给不相关的人加 docker 组一定要慎重。个人开发机或者虚拟机里就无所谓了方便优先。3.3 Docker 启动失败一份贴了半天的排查清单装完 Docker 最常见的启动失败是类似这种Failed to start Docker Application Container Engine.遇到这种情况首先要做的是看日志sudo journalctl -u docker --no-pager | tail -50日志里会写出具体原因。根据我的经验常见的坑大概有这么几类iptables 相关问题。Docker 默认会修改宿主机的 iptables 规则来实现容器网络如果你的系统启用了 firewalld或者手动配置了一大堆 iptables 规则就有可能导致 Docker 启动失败。处理方式是先停掉 firewalldsudo systemctl stop firewalld sudo systemctl disable firewalld或者如果你必须保留防火墙可以在 Docker 配置文件/etc/docker/daemon.json里指定iptables: false但这种做法会影响容器网络隔离不推荐新手使用。存储驱动问题。老版本的内核或者某些特殊文件系统上Docker 可能无法初始化 overlay2 存储驱动。如果你用了旧版本内核解决思路是升级内核而不是去配置其他存储驱动。SELinux 问题。这是 CentOS 上常见的问题Ubuntu 上基本碰不到但如果你的 Ubuntu 手动装过 SELinux也归入这一类。解决办法是在/etc/docker/daemon.json里设置selinux-enabled: false。资源不足。Docker 启动时会申请一些系统资源如果磁盘空间满了或者 inode 耗尽也会启动失败。检查一下df -h df -i这一步看着简单我以前真遇到过一台老服务器上/var/lib/docker所在分区写满了导致 Docker 起不来的情况当时排查了老半天才发现是磁盘占满。4. Docker Compose大概是你在生产环境最常用的工具装完 Docker 核心组件我强烈建议顺手把 Compose 弄好。很多人刚开始用 Docker 时只跑单容器觉得 Compose 没必要但一旦要部署多服务比如一个 Web 应用加一个数据库再加一个缓存没有 Compose 会非常痛苦。4.1 Docker 和 Compose 是什么关系Docker 解决的是如何打包和运行一个容器Compose 解决的是如何用一条命令管理一组关联容器。可以打比方说Docker 是一个快递车Compose 是快递调度中心。车能单独送货但要处理多辆车、多个目的地、复杂的配送路线调度中心省事得多。Compose 用 YAML 文件描述应用的一整套服务。比如你有一个项目需要 MySQL 和 Redis可以写一个docker-compose.yml然后执行docker compose up -d一次性把两个服务都拉起来。之后想停docker compose down就全部停掉。尤其适合开发环境调试和中小型项目的部署。4.2 两种安装方式与避坑要点刚才装 Docker 的时候我们已经把docker-compose-plugin装上了所以系统里已经有了docker compose命令。验证一下docker compose version注意这个命令中间有空格是docker compose而不是docker-compose。老版本的 Compose 是一个独立的二进制文件叫docker-compose新版本作为 Docker 的插件集成在docker compose命令里。很多老教程还在教你用docker-compose如果你按新方式装的执行那个命令会提示找不到。如果你确实需要独立的docker-compose二进制比如在 CI/CD 脚本里用了可以去 GitHub Release 页面下载sudo curl -L https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose sudo chmod x /usr/local/bin/docker-compose这个版本号要根据实际情况更新。但就日常使用来说插件版已经足够不用额外折腾。4.3 一个能直接套用的 Compose 配置这里给一个 MySQL 加 Redis 的小例子很多项目起步阶段都用得上。在项目目录下创建docker-compose.ymlservices: mysql: image: mysql:8.0 container_name: demo-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: demo ports: - 3306:3306 volumes: - mysql-data:/var/lib/mysql redis: image: redis:7 container_name: demo-redis restart: unless-stopped ports: - 6379:6379 volumes: mysql-data:然后docker compose up -d几分钟后MySQL 和 Redis 就都跑起来了。这个配置里有几个细节值得说restart: unless-stopped表示除非你手动停掉容器否则开机和崩溃后都会自动重启对服务类容器非常友好。volumes把 MySQL 的数据持久化到命名卷里容器删了重建数据还在。ports部分把容器内的 3306 映射到宿主机这样宿主机上其他应用才能通过127.0.0.1:3306访问到数据库。5. 验证安装与实际部署从 hello-world 到 MySQL 容器装是装完了但到底能不能干活还得拉个镜像实际跑一把。5.1 用 hello-world 验证 Docker 运行正常docker run hello-world如果安装一切正常会看到一段提示信息告诉你 Docker 工作正常。这个镜像极小下载很快主要用于验证。如果这一步报错了多半是网络问题或者守护进程没起来参考前面排查步骤即可。再进阶一点可以跑一个带交互的容器docker run -it --rm ubuntu bash进入容器的 bash 后你会发现自己处于一个干净的 Ubuntu 环境里面没有你宿主机上的文件除非挂载了。退出容器后--rm参数会自动帮你把容器删除不留垃圾。这个命令非常适合初学者体会容器隔离是什么意思。5.2 实战部署MySQL 8.0 容器部署全流程装 Docker 之后大多数人会尝试部署的第一个数据库就是 MySQL。这一步看着简单实际上有不少人翻车。我见过的问题集中在密码设置、数据持久化、字符集三方面。以 MySQL 8.0 为例一条命令就能起一个实例docker run -d \ --name mysql-test \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDyourpassword \ -e MYSQL_DATABASEtestdb \ -v mysql-test-data:/var/lib/mysql \ mysql:8.0这条命令里-d表示后台运行--name给容器起个名字-p把宿主机的 3306 端口映射到容器的 3306 端口-e设置环境变量-v用卷持久化数据。跑起来之后执行docker ps看到状态是Up就说明容器运行正常。再试一下是否能连上docker exec -it mysql-test mysql -uroot -p输入密码后进入 MySQL 命令行执行show databases;能看到刚才创建的testdb。此时容器部署已经完全没问题了。5.3 数据持久化和备份最容易被忽视的坑很多新手部署 MySQL 容器时不挂载数据卷容器一删所有数据全没了。这在开发环境还能忍生产环境就是事故。前面命令里的-v mysql-test-data:/var/lib/mysql已经做了持久化。MySQL 官方镜像里的数据默认存在/var/lib/mysql目录挂载之后数据写入命名卷mysql-test-data中。无论容器怎么删除重建只要指定同一个卷数据就还在。日常备份也很简单一条命令导出docker exec mysql-test mysqldump -uroot -p testdb backup.sql恢复cat backup.sql | docker exec -i mysql-test mysql -uroot -p testdb这里面有个细微的坑导出时用了docker exec和重定向是在宿主机上执行的所以备份文件落在宿主机当前目录。而恢复时cat backup.sql在第一段管道因此backup.sql是在宿主机上读取两条命令配合起来正好形成宿主机文件 → 容器内数据库的流转。如果你反着来把文件路径和重定向搞混很容易出现命令执行成功但数据没进去的情况排查时注意一下。5.4 宿主机运行 MySQL 端口冲突的解决办法顺便提一个问题如果你宿主机 3306 端口已经被其他程序占用了docker run会报错说端口被占用。这种情况下有两个选择一是停掉占用端口的程序二是换一个宿主机端口映射docker run -d \ --name mysql-test \ -p 3307:3306 \ ...这里3307:3306表示宿主机 3307 端口映射到容器内 3306 端口外部应用通过127.0.0.1:3307连接即可。这个思路对所有端口冲突问题都适用。6. 日常使用中的高频坑和对应解法这段时间帮朋友排查了不少 Docker 相关的问题总结下来有几个高频坑很有代表性这里集中列一下方便你遇到的时候快速对照。现象原因解决办法Cannot connect to the Docker daemon服务没启动sudo systemctl start docker并排查日志确认活跃状态permission denied on /var/run/docker.sock用户不在 docker 组sudo usermod -aG docker $USER重新登录docker pull速度慢或超时网络受限配置镜像加速器改 daemon.json 的 registry-mirrors容器启动了但外部访问不了映射端口配错docker port 容器名查看实际映射docker run后瞬间退出容器内前台进程结束理解容器不会自己常驻按官方用法传参磁盘空间被镜像和容器占满未定期清理docker system prune清理悬空资源MySQL 容器中文乱码字符集配置问题启动时加--character-set-serverutf8mb4参数6.1 镜像加速配置下载速度慢的一劳永逸办法如果你发现镜像下载很慢可以通过修改/etc/docker/daemon.json配置加速器{ registry-mirrors: [ https://docker.m.daocloud.io ] }改完执行sudo systemctl daemon-reload sudo systemctl restart docker注意daemon-reload是让 systemd 重新读取配置restart是让 Docker 进程重启。两个命令都需要执行顺序不能反。这里要说明白的是加速器本质上是一个镜像仓库的代理缓存功能是拉取时提速不影响其他使用方式如果你不方便访问官方仓库这个方案能解决很大问题。6.2 容器日志无限增长怎么办有时候容器跑着跑着宿主机磁盘被 /var/lib/docker/containers 目录下的 json 日志文件塞满了。默认情况下 Docker 不会限制单个容器的日志大小对于输出日志比较多的服务比如流量大的 Nginx几天就能占满几个 G。解决办法是在 daemon.json 里设置全局日志限制{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }意思是单个日志文件最大 10MB最多保留 3 个超过就滚动覆盖。改完同样要重启 Docker。这个配置对生产环境非常重要我见过不止一次因为日志爆盘导致整个服务宕机的事故配完之后省心多了。6.3 容器与宿主机文件互换的两种方式开发中经常需要把文件从宿主机拷进容器或者从容器里拷贝出来。这里提供两个易用的命令docker cp /宿主机/文件路径 容器名:/容器/目标路径 docker cp 容器名:/容器/文件路径 /宿主机/目标路径还有一种是挂载目录的方式在docker run时指定docker run -d --name web-demo -v /home/user/website:/usr/share/nginx/html -p 8080:80 nginx这样宿主机目录下的网页文件能实时同步到容器里非常适合本地开发修改后马上看效果。6.4 清理 Docker 占用的磁盘空间镜像越拉越多容器删了又建悬空镜像和停止的容器会白白占着磁盘。定期执行docker system prune -a这条命令会把所有没有在用的镜像、已停止的容器、无用的网络和构建缓存全部清理掉。-a参数表示连没有被任何容器引用的镜像都一并删除。如果有些镜像你还想留着将来用可以在清理前用docker images确认一下或者不加-a只清理悬空镜像。7. 版本升级与跨版本迁移的注意事项Docker 本身也在持续迭代过上一段时间你会需要升级到新版本这个过程有不少细节值得留个心眼。7.1 升级 Docker 的正确姿势升级命令很简单本质上就是重新安装最新版sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io但升级之前有几个问题要先确认首先是镜像兼容性尤其是老镜像在底层containerd升级后可能出现启动异常。其次是插件兼容性比如之前自行安装的独立版docker-compose与新版 Docker 之间可能产生冲突。最稳妥的做法是先执行docker info --format {{.ServerVersion}}记录当前的版本号升级完再对比确认。升级过程中如果遇到问题还可以通过 apt 安装指定版本号回退apt list -a docker-ce sudo apt install docker-ce版本号7.2 跨机器迁移容器的简单方案有时候你要把开发环境里的容器迁移到服务器上如果容器是内用数据不敏感最省事的办法是导出再导入docker commit 容器名 my-image:backup docker save -o my-image.tar my-image:backup把.tar文件拷贝到新机器上执行docker load -i my-image.tar这样一来镜像连同容器当前的配置和部分状态都迁移过去了。这里补充一下docker commit打包的是容器运行过程中生成的修改如果容器里装了什么软件包或者改了什么配置用这种方式能一次带走。但生产环境不建议依赖这种方法做持久化因为commit出来的镜像有些细节很难追溯不如直接用 Dockerfile 重新构建镜像来得干净。这些内容基于我自己在 Ubuntu 上折腾 Docker 的实际经历整理。安装的过程不难难的是装完之后遇到问题知道从哪查起。希望这篇内容能让你少走一些弯路。你在使用中如果遇到别的奇怪问题欢迎在评论区分享我再补充。
返回列表