Ubuntu从零安装Docker全攻略:避坑指南与MySQL/Redis容器部署
2026/9/16 10:15:01 网站建设 项目流程

刚帮朋友从零把一台Ubuntu 22.04装到Docker能正常跑MySQL和Redis,前后折腾了近两个小时,踩了权限、源、镜像拉取超时好几个坑。回来把整个过程整理成这篇沉淀,希望帮你绕开我走过的弯路。

这篇内容适合所有想在Ubuntu上安装Docker的同学参考,不管是服务器、虚拟机,还是WSL2里的Ubuntu环境,套路基本一致。我会从为什么不用apt install docker.io讲起,到逐步安装Docker Engine、配置镜像加速、部署第一个容器,最后附上高频问题排查清单。确保你看完能独立搞定安装,也能知道出了问题去哪里看日志、怎么定位。

1. 装之前先想清楚的三件事

1.1 你的Ubuntu版本和内核是否达标

Docker对Linux内核有要求,最低是3.10版本,但实际生产中我会建议直接用内核4.0以上。Ubuntu 20.04 LTS默认内核是5.4,Ubuntu 22.04 LTS是5.15,Ubuntu 24.04 LTS是6.8,这几个版本装上Docker都没问题。如果你还在用Ubuntu 16.04或者更老的版本,建议先升级系统,因为老版本的内核和systemd都可能存在兼容性问题。

检查内核版本很简单,一条命令:

uname -r

另外,如果你是在虚拟机里装Ubuntu,比如VMware或VirtualBox,记得开启CPU的虚拟化功能(VT-x/AMD-V)。不开启的话,Docker本身还能跑,但如果你后续想用Docker Desktop或者跑KVM类的虚拟化应用,就会直接报错。这个点我要重点提一下,因为“virtualization support not detected”这个报错是高频问题,后面专门讲。

1.2 选Docker Engine还是Docker Desktop

很多新手上来就想装Docker Desktop,因为它有图形界面,看起来友好。但我要说句实话,在Ubuntu服务器上,我们绝大多数场景只需要Docker Engine,也就是命令行版本。Docker Desktop是为桌面开发场景设计的,它内部会起虚拟机,占内存更多,而且在Ubuntu上还要求必须开启KVM虚拟化,环境要求更苛刻。

我常用的选择方式是这样:

场景推荐方案
服务器/虚拟机部署Docker Engine + docker compose
本地Ubuntu桌面开发Docker Engine 即可,想用图形界面可以考虑 Portainer
想用完整Docker Desktop优先Windows + WSL2,Ubuntu桌面场景反而不推荐

Docker Engine和Docker Desktop的核心容器运行时是同一个,命令也完全一样,所以不存在“装了Engine就低人一等”的说法。反而Engine更轻量、更稳定、更贴近生产环境,排错也更简单。

1.3 不要直接用apt install docker.io

这是新手最容易踩的第一个坑。Ubuntu的官方软件源里确实有docker.io这个包,直接装确实方便,但它有几个问题:版本更新滞后严重,可能落后官方大半年;默认没有自动补全、日志清理插件等周边工具;后续你想用官方源升级,还得先卸掉这个包,迁移麻烦。

正确的做法是从Docker官方仓库安装,这样你能拿到最新稳定版,后续apt upgrade也能直接更新,切源路径顺畅。官方仓库的地址虽然在国内有时不稳,但配置镜像源之后基本没有问题,这个在第三节我会给出具体方案。

2. 官方仓库方式安装Docker Engine

2.1 先清理环境

如果系统之前装过Docker相关的包,先用下面的命令把这些删干净,避免残留配置干扰后续安装:

sudo apt remove docker docker-engine docker.io containerd runc

这一步不必须每次都执行,但如果你不确定系统之前是否装过,建议跑一遍。不执行也没关系,官方安装脚本会自己处理冲突。真正需要留神的是/var/lib/docker目录,这是Docker默认的数据目录,里面存放了所有镜像、容器和卷。如果之前的数据还要用,先备份再操作,否则卸载后数据不会自动清掉,但重装后可能会因为版本不同导致数据不兼容。

2.2 安装依赖和添加官方GPG密钥

执行下面的命令,安装必要的软件包:

sudo apt update sudo apt install -y ca-certificates curl

然后创建密钥管理目录,并导入Docker官方的GPG密钥:

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 a+r /etc/apt/keyrings/docker.asc

这里有个小细节:install -m 0755 -d是创建目录并设置权限,比mkdir -p多做了权限管理这一件事。Docker官方安装文档现在推荐的做法就是先建目录,再放keyring文件,再设置644权限。如果你少了chmod a+r这一步,后面apt update会报错说keyring不可读,这是我在实际运维中见过特别多次的问题。

2.3 把Docker官方源写入apt源列表

这一步要区分架构。我在Ubuntu x86_64上用的是amd64,树莓派等ARM设备用arm64。用dpkg --print-architecture看一下:

dpkg --print-architecture

然后添加源。我把命令写在下面,这是Ubuntu 22.04/24.04通用的写法:

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo ${UBUNTU_CODENAME:-$VERSION_CODENAME}) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

这里$(. /etc/os-release && echo ${UBUNTU_CODENAME:-$VERSION_CODENAME})是动态获取系统版本的代号,比如Ubuntu 22.04会解析出jammy,24.04会解析出noble。好处是你换了系统版本这段命令不用改。如果解析失败,也可以手动写死:deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu jammy stable

添加完源之后,再执行一次sudo apt update,让系统识别新源。如果这一步网络超时,那就到了该配置镜像源的时候了,不急,第三节专门解决这个问题。

2.4 安装Docker Engine和配套工具

现在可以正式安装了:

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

解释一下这些包各自是干什么的:

  • docker-ce:Docker社区版引擎主程序,就是真正干活的守护进程。
  • docker-ce-cli:客户端命令行工具,就是我们敲的docker命令。
  • containerd.io:容器运行时,负责实际创建和管理容器生命周期。
  • docker-buildx-plugin:用于构建多架构镜像的插件,默认会装上。
  • docker-compose-plugin:Docker Compose v2插件,后面编排MySQL、Redis的时候要用。

安装完成后,启动并设置开机自启:

sudo systemctl enable docker sudo systemctl start docker

systemctl status docker看一眼运行状态,看到active (running)就说明守护进程起来了。这里多说一句,很多教程让你装完直接跑docker version,这是不对的,docker version只能验证客户端和服务端能否对接,如果服务端没起,它会只显示客户端信息,容易让人误判。先看服务状态更靠谱。

2.5 把当前用户加进docker组

这一步是很多教程会忽略但实际极其重要的一步。默认情况下,只有root用户和docker组的成员才能访问Docker守护进程。你不加组,就得每次都敲sudo docker,非常痛苦。

加到docker组:

sudo usermod -aG docker $USER

然后重新登录,或者执行下面的命令让组变更立即生效:

newgrp docker

验证一下:

docker run hello-world

这条命令会先从Docker Hub拉取hello-world镜像(很小,大约几KB),然后在容器里运行它,最后输出一段说明文字。如果你看到Hello from Docker!,说明整个安装已经成功了。这也是个人尽皆知的验证方式,但我建议你留意一下这段输出里面“容器在完成工作后就退出了”这个概念,这对理解容器的生命周期很有帮助。

3. 配置镜像加速,解决拉取超时

3.1 为什么需要配置加速

Docker默认的镜像仓库是Docker Hub,架设在国外。从国内网络直接拉取大型镜像,比如MySQL、Redis、Ubuntu基础镜像,经常出现进度条卡住不动,或者等半天提示net/http: TLS handshake timeout。最难受的是,这类超时错误是间歇性的,有时候重试能成,有时候连续失败,非常浪费耐心。

解决方法就是在Docker守护进程配置里加入国内公共镜像源。注意,这不是什么黑科技,就是把镜像拉取的访问路径指向国内的速度更快的镜像仓库。Docker官方本身也支持配置多个镜像源,拉取同一个镜像时如果第一个源失败会自动切换下一个。

3.2 修改daemon.json配置文件

你需要创建或修改/etc/docker/daemon.json

sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.mirrors.ustc.edu.cn" ] } EOF

上面这几个源是我实际用下来相对稳定的。其中docker.m.daocloud.io是DaoCloud提供的公共镜像加速,覆盖范围广;中科大源是老牌教育网源,稳定性好。不同地区、不同运营商网络,表现会有差异,建议保留两到三个做备选。

改完配置后,重启Docker服务:

sudo systemctl daemon-reload sudo systemctl restart docker

重启完之后可以用docker info查看配置是否生效:

docker info | grep -A 5 "Registry Mirrors"

如果输出列表里能看到你配置的几个地址,说明加速已经生效。

3.3 配置完加速还慢怎么办

有时候配置了镜像加速,拉取仍然很慢,这时候问题往往出在“镜像源没有对应的镜像”上。比如一些很偏门的第三方镜像,国内源根本没同步,Docker会退回到Docker Hub原始地址拉取,慢就是正常的。

另外,有些镜像在Docker Hub上本身多层合并后的体积很大,比如node:20完整版、python:3.12这种开发镜像,实际下载接近1GB,再快的源也要花时间。如果项目场景允许,优先选alpine版本的基础镜像,体积能小一个数量级,这在后面的MySQL、Nginx部署里我会演示。

最后说一个老实但有效的土办法:如果你用的是国内云服务器,比如阿里云、腾讯云,它们的控制台里都有专属镜像加速地址,一步到位,速度比公共源还快。很多人买了云服务器却在网上搜索公共加速源,根本没意识到自己的云厂商就送了加速服务。

4. 部署第一个容器:Nginx和MySQL

4.1 用docker run跑一个Nginx

Docker装好,光看hello-world肯定不够过瘾,我们用Nginx来体验一下完整的容器部署流程。

拉取镜像并运行:

docker run -d --name web-test -p 8080:80 nginx:alpine

我拆开解释一下这几个参数:

  • -d:后台运行容器,让出终端。
  • --name web-test:给容器起一个名字,方便后续管理。
  • -p 8080:80:端口映射,把宿主机8080端口转发到容器的80端口。
  • nginx:alpine:基于Alpine Linux精简版的Nginx镜像,体积小,资源占用少。

执行完之后,访问http://你的服务器IP:8080就能看到Nginx默认欢迎页。如果打不开,第一件事检查防火墙是否放行了8080端口,第二件事用docker ps -a看容器是否在运行,第三件事看容器日志。

查看容器日志的命令:

docker logs web-test

如果你看到类似bind() to 0.0.0.0:80 failed (13: Permission denied)的报错,那是因为容器内进程没有权限绑定80端口,解决办法是更换映射端口,或者用root用户运行容器。这个在实际生产中遇到不少。

4.2 部署MySQL 8,理解数据卷

接下来部署一个更贴近业务场景的MySQL 8.0。你直接在热词里能看到“docker安装mysql8.0并使用”排在很前面,说明这是大家真正关心的需求。

先拉镜像:

docker pull mysql:8.0

然后运行:

docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=yourpassword \ -e MYSQL_DATABASE=testdb \ -v /opt/mysql-data:/var/lib/mysql \ mysql:8.0

这里重点讲两条参数:

第一,-e是环境变量。MySQL镜像启动时会读取这些变量来初始化数据库。MYSQL_ROOT_PASSWORD设置root密码,MYSQL_DATABASE会额外创建一个数据库。如果你不设置密码相关变量,容器会因为安全机制直接启动失败。

第二,-v /opt/mysql-data:/var/lib/mysql是数据卷挂载。它的意思是把宿主机/opt/mysql-data目录映射到容器内MySQL的数据目录。容器删了、重建了,只要这个目录还在,数据就还在。这个习惯一定要养成,否则哪天不小心删了容器,数据库文件跟着一起没了,那种教训一次就够痛。

映射完了还需要注意目录权限问题。MySQL容器内有自己的运行用户mysql,它需要能读写挂载目录。如果你在宿主机上创建的目录权限过严,比如直接用root创建且权限是700,容器内用户写入就会报Permission denied。解决办法是先创建目录并授权:

mkdir -p /opt/mysql-data chown -R 1000:1000 /opt/mysql-data

1000:1000是MySQL官方镜像中mysql用户的UID:GID。不同镜像的用户ID可能不一样,这个要留意。

4.3 容器生命周期管理四件套

跑起来之后,日常维护主要就四个命令:

查看容器列表:

docker ps docker ps -a # 包含已停止的

停止、启动、重启、删除:

docker stop mysql8 docker start mysql8 docker restart mysql8 docker rm mysql8 # 删除前要先stop

进入容器内部:

docker exec -it mysql8 bash

在容器内敲mysql -uroot -p就能连上数据库,这对排查数据库问题是必备操作。进入容器后,容器本质是一个精简版Linux环境,很多命令可能没有安装,比如vim默认没有,这是正常的,不用慌。

5. 常见安装与运行问题排查清单

5.1Got permission denied while trying to connect

这个报错是权限问题最常见的表现。你执行docker ps的时候提示这个,说明Docker守护进程正常工作,但你的用户没有权限连接它。

确认一下:

groups $USER

如果你的用户名没有出现在docker组里,就回头执行第2.5小节的用户组配置。如果已经加入了docker组但还是报这个错,那很可能是因为当前shell会话还是旧的用户身份,执行newgrp docker或者重新登录一次就好。

5.2Cannot connect to the Docker daemon

这个报错含义是客户端找不到Docker守护进程。先确认服务是否在运行:

sudo systemctl status docker

如果服务是inactive (dead),直接启动:

sudo systemctl start docker

如果服务启动失败,查看详细错误:

sudo journalctl -u docker.service --no-pager -n 50

我遇到过一个比较隐蔽的情况:Docker和本机防火墙iptables规则冲突,导致服务起不来。这种时候看日志会发现Failed to start Docker Application Container Engine,后面跟着iptables相关错误。临时解法是把/etc/docker/daemon.json里加上"iptables": false,但这个操作会影响容器网络隔离,我不推荐长期使用,最好还是排查具体是哪个防火墙规则冲突了。

5.3 端口冲突

如果你启动容器时报Bind for 0.0.0.0:8080 failed: port is already allocated,说明宿主机上8080端口已经被占了。

查一下是谁占了:

sudo lsof -i:8080 # 或者 sudo ss -tlnp | grep 8080

处理方式有两个:杀掉占用进程,或者改容器映射端口。我建议优先改端口,因为杀进程有风险,你不一定知道那个进程是干什么的。比如在同一台机器上还要跑另一个Nginx,你就可以把容器的映射改成8081:80

还有一种情况是MySQL的3306端口被本机已有的MySQL服务占用。如果你是为了开发环境用Docker起MySQL,建议先把宿主机自带的MySQL服务停掉,或者直接改Docker映射端口为3307:3306,避免冲突。

5.4 老版本Ubuntu装不上Docker

如果你还在用Ubuntu 18.04,会发现添加Docker官方源后,apt update可能报错找不到Release文件。原因是Docker官方已经停止对18.04及以下版本的仓库更新了,你要么装旧版Docker,要么老实升级系统。

我个人建议是升级系统,别在旧版本上浪费时间。Docker对内核版本有硬性要求,旧的发行版内核太老,某些新特性用不上,运行时稳定性也差。从投入产出比看,花两小时升级系统比重装十次旧版Docker值得多。

5.5Virtualization support not detected怎么处理

这个报错在热词里出现频率很高,虽然它更多出现在Docker Desktop上,但如果你在WSL2里的Ubuntu上使用Docker,也可能撞上。

先检查虚拟化是否开启:

grep -E --color '(vmx|svm)' /proc/cpuinfo

如果没输出,说明CPU虚拟化没有开启或者当前环境不支持嵌套虚拟化。解决办法:

  • 物理机:重启进BIOS/UEFI,找到Intel Virtualization Technology或AMD SVM Mode,设置为Enabled。
  • VMware虚拟机:关掉虚拟机,在VMware设置里勾选“虚拟化Intel VT-x/EPT”或“虚拟化AMD-V/RVI”。
  • VirtualBox虚拟机:设置里找到“启用VT-x/AMD-V嵌套分页”并勾选。
  • 云服务器:本身不支持嵌套虚拟化,但云服务器上的Ubuntu通常不需要Docker Desktop,直接用Docker Engine即可,完全不依赖这个特性。

所以我的观点是,如果你是服务器上跑Docker,看到这个报错不用慌,你根本不需要管虚拟化支持,直接用Engine就好;如果你是本地桌面环境用Docker Desktop,那就老老实实去BIOS打开虚拟化。

5.6 排查问题前三步,始终不变

无论遇到什么Docker问题,我都推荐从三个方向切入:看服务状态、看容器状态、看日志。

第一步,确认Docker服务在跑,systemctl status docker。 第二步,确认容器在跑,docker ps -a,尤其注意STATUS列是Up还是Exited。 第三步,看日志,运行中的容器用docker logs 容器名,异常退出的容器同样可以用这个命令查看原因。

日志永远是最直接的线索,别靠猜。几次找不到原因,基本都是因为懒得看日志,凭感觉在那里试错,浪费时间。

6. 进阶:用Docker Compose管理多容器环境

6.1 Docker Compose解决什么问题

到这一步,你已经能单容器跑了。但实际开发中,一个项目往往需要MySQL、Redis、Nginx多个容器协作,每次都手动敲docker run不仅长,还容易记错参数。Docker Compose的作用就是把这些容器的启动参数统一写在一个docker-compose.yml文件里,一条命令全部启动。

它的好处有两个:第一,配置可视化,所有人通过一个文件就能看懂整个项目的依赖关系;第二,可复制性强,新同事拉到代码,一条docker compose up -d就能把整个环境拉起来。

之前有朋友问过我:“我有构建好的镜像,只是想在别的机器上跑起来,还需要Compose吗?”如果只是单容器,没有依赖关系,直接docker run就够了。但如果涉及多容器、网络互通、数据卷共享,Compose的优势就体现出来了。

6.2 写一个Compose文件部署MySQL和Redis

在项目目录下创建docker-compose.yml

version: '3.8' services: mysql: image: mysql:8.0 container_name: dev-mysql restart: always ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: demo volumes: - ./mysql-data:/var/lib/mysql networks: - dev-net redis: image: redis:7-alpine container_name: dev-redis restart: always ports: - "6379:6379" volumes: - ./redis-data:/data command: redis-server --appendonly yes networks: - dev-net networks: dev-net: driver: bridge

说明几个关键点:

  • restart: always:容器异常退出后自动重启,适合长期运行的服务。
  • volumes里的./mysql-data是相对路径,会在当前目录下创建数据目录,方便整个项目目录一起迁移。
  • command:覆盖镜像默认启动命令,Redis这里开启AOF持久化。
  • networks:让MySQL和Redis在同一个自定义网络中,后续微服务连接直接用容器名dev-mysqldev-redis就能解析到IP,不需要网卡地址。

启动:

docker compose up -d

查看状态:

docker compose ps

停止:

docker compose down

如果你还要加一个Redis主从,可以在services下再加一个redis-slave节点,配置里加上--slaveof dev-redis 6379,一条命令一起启动。热词里“docker安装redis主从”就是这么玩的,加两个service、两行配置而已。

6.3 IDEA里怎么用Docker镜像

现在开发流程里还有个高频场景叫“IDEA打包Docker镜像”。本质上就是把项目代码打进一个包含Java运行环境的镜像里,让容器可以直接启动项目。

传统开发流程:

  1. 本地把Java项目打成Jar包。
  2. 在服务器上装JDK、配环境变量。
  3. 把Jar包上传到服务器,手动启动。

Docker化之后:

  1. 项目根目录写一个Dockerfile,内容类似这样:
FROM openjdk:17-jdk-alpine VOLUME /tmp COPY target/demo.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"]
  1. 执行构建:
docker build -t demo-app:latest .
  1. 运行:
docker run -d -p 8080:8080 demo-app:latest

IDEA本身也集成Docker插件,装了docker插件之后可以直接在Services面板看到本地Docker实例,右键DockerfileBuild Image就能打包,不需要切换到命令行。

这里我再强调一个开发小技巧:如果你的镜像在IDEA里构建特别慢,先看看是不是在拉基础镜像。openjdk:17-jdk-alpine这种镜像全量拉取可能要好几分钟,但只要你本地已经拉过一次,后续构建都是秒级。

7. 最后再分享一个日常维护技巧

装好Docker之后,日常维护中最重要的习惯之一就是定期清理无用镜像和容器。开发调试几天后,本机经常堆积一大堆<none>标签的悬空镜像,占用的磁盘空间可能有好几个G。

清理命令:

docker system prune

这条命令会把所有停止的容器、悬空镜像、未使用的网络都删掉。如果想连没被引用的镜像一起清理,加个-a参数:

docker system prune -a

注意,这条命令会清理所有未被运行中容器引用的镜像,如果你本机保存了一些不常用的自定义镜像,建议先确认哪些还需要。我自己的习惯是每周跑一次docker system df看看磁盘占用情况,做到心中有数。

另外,如果你经常改容器配置,记得“容器重启不等于配置生效”。Docker容器是不变的,docker restart只是重启了进程,不会重新读取docker run时的参数。如果改了端口映射、环境变量、挂载目录,正确做法是删掉旧容器、用新参数重新docker run,或者用Compose文件改完重新docker compose up -d。这算是很多人容易搞混的地方,记住这一点,能省去不少排查时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询