最早接触Docker那会儿,我正被项目环境问题搞得焦头烂额。本地跑得好好的接口,丢到测试服务器上就各种诡异报错:依赖版本不对、动态库缺失、系统内核不一致,光是Python版本差异就能折腾一下午。后来同事甩给我一个Dockerfile,说“环境跟着镜像走”,我才第一次真正意识到,把程序连同运行环境一起交付,才是解决这种问题的正路。从那时起,Docker就成了我工作流里离不开的工具。
这篇内容就是一篇写给新手的Docker入门讲解,我会把容器、镜像、仓库这三个核心概念先讲明白,再带你走一遍Windows和Linux上安装Docker的完整流程,然后把日常用得最多的命令、最容易踩的坑、以及用Docker部署MySQL、Redis、GitLab这类真实服务的方案都过一遍。适合刚接触容器化、准备在本地或服务器上搭建开发环境的朋友,也适合那些装完Docker Desktop却总报错、卡在第一步的人。
1. Docker是什么?为什么现在的团队几乎都在用它
1.1 用集装箱的比喻理解容器
Docker最常被拿来和虚拟机做对比,我更喜欢用集装箱来理解它。海运里集装箱标准统一,货物装进箱子里,不管里面是什么,到了码头用同样的起重机就能吊走搬上船。Docker容器也是同一个逻辑:把应用和它运行需要的依赖、配置、库文件统统装进一个标准化的“箱子”,换到哪台机器上都能一视同仁地跑起来。
技术层面的解释是,Docker容器基于Linux内核的namespace和cgroups技术,实现独立的文件系统、进程空间、网络栈和资源限制。你启动一个nginx容器,它在里面看到的文件系统、进程列表都是自己那一套,但它并没有装一整个操作系统,而是和宿主机共享内核。这正是容器和虚拟机最大的区别:虚拟机里要运行一个完整的Guest OS,需要Hypervisor模拟硬件,启动时间以分钟计,镜像动辄几个GB;容器直接跑在宿主机内核上,启动基本就是秒级,镜像大小甚至能压到几十MB。
有一句话我经常跟同事说:虚拟机是搬家,把整栋房子都搬过去;容器是装箱子,只把自己的家具和生活用品装进去,到哪都能摆好就开用。因为这个特性,容器特别适合微服务、CI/CD、临时测试环境这类需要频繁启动销毁的场景。
1.2 镜像、容器、仓库这三个词到底什么意思
三个核心概念你必须先记住:镜像(Image)、容器(Container)、仓库(Repository)。
镜像是一个只读的模板,它把应用代码、依赖、基础运行环境都打包在一起。可以把它理解成“类”或者“制作模具”,它定义了容器的初始状态。容器则是镜像的运行实例,相当于用模具生产出来的一个个产品,可以启动、停止、删除。仓库是集中存放镜像的地方,Docker Hub是官方公开仓库,你在里面能找到mysql、redis、nginx、ubuntu等官方维护的镜像。
日常操作中可以这样对应:docker pull把镜像从仓库拉到本地;docker run用镜像启动一个容器;如果你自己改了代码,可以构建成新镜像;docker push把新镜像推到仓库分享给团队。整个过程相当于:下载模板、用模板创建实例、把新模板发布出去。
这也是Docker真正吸引人的地方:很多复杂软件,比如GitLab、Gerrit、Hadoop这样的大组件,官方或社区早已维护好了现成镜像。过去想体验一下这些系统,你得按文档一步步装依赖、改配置,现在一行docker run就能拉起环境,折腾成本大幅降低。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 安装Docker的主流路线:Windows和Linux分开走
2.1 Windows上安装Docker Desktop:WSL2是首选后端
Windows上装Docker,几乎绕不开Docker Desktop。看到“Docker Desktop安装教程”这个关键词的朋友,多半是在这一步卡住了。我建议在Windows 10 64位2004版本以上、Windows 11系统的机器上,优先使用WSL2后端,性能和兼容性都更好。
安装前先检查三样东西:
- 系统版本满足要求,Windows 10 2004及以上或Windows 11。
- BIOS里开启了虚拟化,Intel CPU打开VT-x,AMD CPU打开AMD-V。
- WSL2已安装并设置为默认版本。
具体流程是:以管理员身份打开PowerShell,依次执行下面两条命令,启用“适用于Linux的Windows子系统”和“虚拟机平台”两个Windows功能:
powershell复制dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
执行完重启电脑。重启后去微软官网下载WSL2内核更新包并安装,然后继续在管理员PowerShell里执行:
powershell复制wsl --set-default-version 2
这时候验证一下:wsl --status能看到默认版本是2,wsl -l -v能看到已安装的发行版。确保发行版用的是WSL2而不是WSL1,否则之后Docker Desktop启动很可能报错。之后去官网下载Docker Desktop for Windows安装包,一路下一步就行。装完打开,在设置里确认“Use the WSL 2 based engine”这个选项是勾上的。
这里有个知识点:WSL2本质上还是一个轻量虚拟机,Docker引擎就跑在这个虚拟机内部的Linux发行版里。这样做的优势是,Docker和Windows共享网络、文件系统也比较顺畅,比最早基于Hyper-V的后端速度快不少。
2.2 Linux安装Docker Engine:Ubuntu/CentOS/openEuler都能跑
如果你直接在Linux服务器上装Docker,就走另一条路:安装Docker Engine。不同发行版命令略有差异,但思路一致。
Ubuntu/Debian系最简单,很多云服务器直接执行:
bash复制sudo apt update
sudo apt install -y docker.io
sudo systemctl enable --now docker
用apt安装的是发行版自带仓库里的docker.io包,可能不是最新版本,但对绝大多数生产场景够用了。追求最新版本的话,可以添加Docker官方源后安装docker-ce包,步骤稍微多一些。
CentOS、RHEL、openEuler这类基于RPM的发行版,用yum或dnf。以CentOS为例:
bash复制sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce
sudo systemctl enable --now docker
openEuler 23.09整体思路和CentOS类似,用dnf安装即可。如果包源访问有问题,优先把软件源替换成你所在云厂商的内网源,再继续装。
装完后运行docker version看服务端和客户端版本,然后跑一个测试验证:
bash复制sudo docker run hello-world
能输出Hello from Docker!就说明Docker引擎正常工作。
Linux安装有一个高频问题:每次执行docker命令都要加sudo,否则会报permission denied。这是因为当前用户不在docker用户组里。执行:
bash复制sudo usermod -aG docker $USER
然后注销重新登录,再执行docker ps就不需要sudo了。注意,这条命令要重新登录才生效,很多人加了组却不重新登录,然后来问我为什么还报权限错。
另外,国内服务器拉取镜像经常很慢,这个几乎所有人都会遇到。解决办法是配置镜像加速器。修改/etc/docker/daemon.json:
json复制{
"registry-mirrors": ["https://你的加速地址"]
}
以阿里云容器镜像服务为例,登录控制台后在“镜像加速器”页面能看到专属加速地址,把地址填进去,重启Docker即可。各云厂商基本都提供类似服务,搜索“容器镜像服务”就能找到。加速器的原理是让Docker从国内就近的缓存节点拉取镜像,而不是每次跨越公网去访问官方Registry。
2.3 Docker Desktop启动失败的三个高频原因
结合最近的搜索记录,遇到“virtualization support not detected”的人真不少,你肯定不是一个人在战斗。这个报错翻译过来就是:Docker Desktop要求虚拟化支持,但系统检测不到虚拟化能力。
排查顺序我建议是这样:先确认BIOS虚拟化开关打开,进BIOS找Intel Virtualization Technology或SVM Mode,设为Enabled。如果是虚拟机里跑Windows,还要给虚拟机开启嵌套虚拟化。然后回到Windows,确认“虚拟机平台”这个功能已经打开,我之前给的dism命令就是干这个的。最后用systeminfo命令,在输出里找“Hyper-V要求”一项,如果显示“已检测到虚拟机监控程序”,说明虚拟化没问题。
第二个常见报错是:
text复制failed to connect to the docker api at npipe:////pipe/dockerDesktopLinuxEngine
这个错误的意思是Docker Desktop的Linux引擎没起来,客户端连不上容器运行时。遇到它别急着重装,先执行wsl --shutdown关掉整个WSL,等几秒,再启动Docker Desktop。这个操作能解决八成类似问题,因为WSL子系统一旦卡死或内核没起来,Linux引擎自然连不上。如果还不行,检查WSL内核版本,去微软官网更新WSL2内核包,或者直接升级Docker Desktop到最新版。
第三个容易踩坑的是Windows容器和Linux容器切换问题。Docker Desktop默认跑Linux容器,如果你之前切到Windows容器模式,再切回来时偶尔会卡在引擎切换。在任务栏Docker图标右键,选“Switch to Linux containers”即可。
3. 常用命令与镜像管理:从看到会用到真正会用
3.1 必会命令速查:拉取、运行、查看、进入容器
Docker命令看起来很多,但日常开发最常用的就那么十几个。我把它们整理成一条主线:找镜像、拉镜像、跑容器、看容器、进容器、看日志、删容器。
bash复制# 搜索镜像
docker search nginx
# 拉取镜像
docker pull nginx:1.25
# 运行容器:后台运行,命名web,映射宿主机8080到容器80
docker run -d --name web -p 8080:80 nginx:1.25
# 查看正在运行的容器
docker ps
# 查看所有容器,包括已停止的
docker ps -a
# 进入容器内部(交互式终端)
docker exec -it web bash
# 查看容器日志
docker logs -f web
# 停止、启动、删除容器
docker stop web
docker start web
docker rm web
docker run的参数有几个很重要:-d表示后台运行,-p是端口映射,格式是“宿主机端口:容器端口”,--name给容器起名字,-e设置环境变量,-v挂载数据卷。中间如果加-it,比如docker run -it ubuntu bash,就是以前台交互方式进入容器,适合调试。
3.2 数据卷:让数据不随容器一起消失
新手最容易忽略的就是数据持久化。容器是临时性的,你删除容器,容器里的一切数据都没了。如果你直接运行MySQL容器、Redis容器,存进去的数据会随着容器删除而灰飞烟灭,这个教训真的好多人都踩过。
解决办法是数据卷,关键参数是-v。比如运行MySQL时,把容器内的数据目录挂载到宿主机:
bash复制docker run -d --name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=your_password \
-v /data/mysql:/var/lib/mysql \
mysql:8.0
这样MySQL把数据写到容器里,实际上落到了宿主机的/data/mysql目录。哪怕容器被删了重建,只要挂载同一个目录,数据都还在。这个思路对所有有状态服务都适用:数据库、缓存、文件存储,必须做数据卷挂载。
3.3 用Dockerfile构建自己的镜像
除了拉取现成镜像,你终究会需要把自己写的应用打包成镜像。比如一个Java服务,先准备一个Dockerfile:
dockerfile复制FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
COPY target/app.jar /app/app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
然后执行:
bash复制docker build -t my-app:1.0 .
就能生成名为my-app的镜像,之后docker run就能跑起来。IDEA里也有Docker插件,配置好远程Docker地址后,可以在IDE里直接一键打包镜像,对后端开发来说非常方便。
理解镜像分层是进阶的关键:Dockerfile每一行指令都会生成一个只读层,比如FROM基础镜像是一层,COPY文件是一层。多次构建时,只要历史层没变化,Docker会直接复用缓存,只重新构建变化后的层。所以一个建议是:把变化少的指令写在前面,变化频繁的代码复制写后面,这样每次改代码构建时,大部分层都能命中缓存,构建速度快很多。
4. 用Docker部署真实服务:MySQL、Redis主从、GitLab齐活
4.1 MySQL 8.0部署与远程连接
部署MySQL是我被问得最多的场景之一,基本命令可以覆盖大部分需求:
bash复制docker run -d --name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=你的密码 \
-v /data/mysql:/var/lib/mysql \
mysql:8.0
这个命令在4.2节出现过一次,这里再展开细说。环境变量MYSQL_ROOT_PASSWORD是初始化时设置root密码,MYSQL_DATABASE可以指定创建初始数据库,MYSQL_USER和MYSQL_PASSWORD可以创建额外用户。如果不想让MySQL把数据留在容器里,数据卷挂载一定要做。
验证是否启动成功:
bash复制docker ps
docker exec -it mysql8 mysql -uroot -p
输入密码进入MySQL命令行,看到版本信息就说明服务正常。
远程连接的坑一般集中在三个地方。第一,宿主机防火墙没放行3306端口;第二,docker run时-p写成-p 3306,这只映射到容器的随机端口,实际你应该写成-p 3306:3306;第三,MySQL 8.0默认使用caching_sha2_password认证插件,部分老客户端不支持,连上会报认证错误。老客户端场景可以把用户改为mysql_native_password:
sql复制ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '你的密码';
FLUSH PRIVILEGES;
不过我要提醒,MySQL新版已经逐渐弃用mysql_native_password,最好是升级客户端驱动,而不是改服务端兼容旧协议。
4.2 用docker compose部署Redis主从
单个容器用docker run没问题,但一旦有多个容器配合,比如Redis主从、Web服务加数据库,一条条敲命令就太痛苦了。这时候用docker compose,把所有容器定义写进一个docker-compose.yml,一键管理。
以Redis一主一从为例,创建docker-compose.yml:
yaml复制version: '3'
services:
redis-master:
image: redis:7
container_name: redis-master
ports:
- "6379:6379"
command: redis-server --appendonly yes
redis-slave:
image: redis:7
container_name: redis-slave
ports:
- "6380:6379"
depends_on:
- redis-master
command: redis-server --replicaof redis-master 6379
启动:
bash复制docker compose up -d
docker compose ps
docker compose会自动创建网络,所有服务通过服务名互相通信。从节点配置的redis-master就是容器名,compose内部网络会自动解析成对应IP。验证主从状态可以进入容器执行info replication。
这里有个细节:新版Docker推荐docker compose命令,中间有空格,不带横杠。老版本的docker-compose命令虽然还能用,但属于旧版客户端,建议尽量用新命令。
一个真实心得:compose文件一旦写好,你以后重建环境只需docker compose down && docker compose up -d,不会因为少设一个端口映射或忘配环境变量而重建失败。
4.3 那些“重量级应用”为什么特别适合用容器跑
GitLab是典型的重量级应用,安装包大、依赖多、配置复杂。用镜像gitlab/gitlab-ce部署,一条docker run就能拉起来:
bash复制docker run -d \
--name gitlab \
-p 8081:80 \
-p 10443:443 \
-v /data/gitlab/config:/etc/gitlab \
-v /data/gitlab/logs:/var/log/gitlab \
-v /data/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
但你要做好心理准备:GitLab镜像很大,首次启动要等好几分钟,内存建议至少4G,否则会频繁OOM。8080端口在本地容易跟其他服务冲突,我一般映射到8081。团队做CI/CD时,GitLab Runner同样可以用容器方式注册和运行,整个流水线体系都能容器化,这也是“docker engine ci/cd”这个搜索词背后大家真正在找的东西。
类似的还有安全学习常用的DVWA靶场,docker run -d -p 8080:80 vulnerables/web-dvwa就能拉起一个Web漏洞测试环境,用完即销毁,不影响宿主机,这在做靶场实验时尤其省心。另外像KodBox私有网盘、青龙面板这类定时任务工具、Dify这类AI应用开发平台,也都提供了镜像或compose文件,原因是这些应用内部依赖很重,直接用容器跑能把环境依赖隔离在镜像里,避免污染宿主机,也不会因为跳过某个系统依赖步骤而失败。
5. 网络、镜像加速、权限等疑难问题排查实录
5.1 容器端口映射与容器间通信
Docker的网络问题,问的人特别多,尤其是“docker网络不通”这个关键词。先分清两种场景:外部访问容器、容器间互相访问。
外部访问容器的核心就是端口映射。docker run -p 8080:80,意思是访问宿主机8080端口,会转发到容器内的80端口。检查时用docker port 容器名,看实际映射情况。外部连不上,先看宿主机防火墙有没有放行对应端口,再看容器是不是正常运行,最后用curl测试。
容器间互访又是另一套逻辑。默认情况下,所有容器在同一个bridge网络上,可以通过IP互相访问,缺点是容器重建后IP会变。更优雅的做法是自己创建一个用户自定义网络:
bash复制docker network create my_net
docker run -d --name app --network my_net my-app:1.0
docker run -d --name redis --network my_net redis:7
这样容器之间可以直接用服务名通信,比如应用连接Redis时写redis:6379,Docker内置DNS会自动解析,不用关心IP变化。宿主机的应用要访问容器,用localhost加映射端口;容器里要访问宿主机服务,在WSL2后端下可以用host.docker.internal这个特殊域名。
排查网络不通,正确顺序是:docker ps确认容器在跑,docker port确认映射,进入容器用curl或telnet测试目标端口,最后再查防火墙。注意一点,ping不通不一定是网络真正不通,很多精简镜像里根本没装ping命令,直接测TCP端口更靠谱。
5.2 镜像拉取失败与镜像加速器配置
拉镜像经常遇到的现象是卡住、超时、报tls handshake timeout或EOF。这通常是客户端到镜像仓库的网络路径存在问题。除了我之前提过的配置加速器,还有几个可以尝试的点。
第一,检查DNS。如果宿主机DNS有问题,镜像仓库域名解析不了,也会表现为拉取失败。第二,加速器地址要确认没填错,daemon.json改完后一定要重启Docker。第三,如果镜像名带了特定tag,比如mysql:8.0,拉取失败时可以换官方默认tag再试一次,排除tag不存在的情况。配置加速器后,docker info输出里能看到Registry Mirrors列表,确认配置生效。
老实说,镜像下载慢这个问题在部分云环境里是客观存在的,加速器不是万能的,但绝大多数场景下,用云厂商提供的加速服务能把拉取速度提升到可接受范围。多试几个官方加速地址,找到适合自己网络环境的那个,也是一种合理操作。
5.3 Docker权限错误怎么解决
“permission denied while trying to connect to the Docker daemon socket”是Linux下最常见的权限错误。原因是Docker使用Unix套接字通信,默认只允许root用户和docker组用户访问。
解决方法我在2.2节提过:
bash复制sudo usermod -aG docker $USER
执行后注销或重启,重新登录。如果执行了命令还是不行,看看是不是会话没刷新,或者干脆重启一下机器。有些安全加固过的服务器启用了SELinux,还可能需要处理SELinux上下文,这类情况相对少见。
我现在要给你一个善意的提醒:把用户加入docker组,本质上是给了这个用户接近root的权限,因为容器可以挂载宿主机目录,甚至可以访问宿主机整个文件系统。所以生产环境里,docker组里加人一定要谨慎,别为了让某个同事方便,把服务器安全级别拉低了。
5.4 磁盘占用与日志清理
Docker用久了,磁盘可能会被撑爆,大部分元凶是容器日志。默认的json-file日志驱动不限制文件大小,一个高并发服务跑几个月,日志文件百GB都不稀奇。
查看磁盘占用:
bash复制docker system df
清理悬空镜像、停止的容器、没有用的网络:
bash复制docker system prune
清理所有无用镜像、容器、数据卷(慎用):
bash复制docker system prune -a --volumes
更稳妥的办法是在源头限制日志大小。修改/etc/docker/daemon.json:
json复制{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
重启Docker后,新创建的容器日志单个文件最大10MB,保留3个文件轮转。已存在的容器要重新创建才生效。这一步做完,磁盘焦虑能明显减轻。
回到开头说的那个理念,Docker之所以能成为当代开发和运维的基石,本质上是把“环境”从“机器”里抽离出来,变成了可以打包、分发、复用的“配置文件”和“镜像”。我在实际项目里的体会是,最省心的做法是把MySQL、Redis这些中间件全部用compose管理,团队新人来了不再需要看一长串安装文档,拉下仓库代码,docker compose up -d就能把整个依赖环境拉起来。这比任何“环境部署教程”都更可靠,也更直观。
最后再分享一个小技巧:当你需要临时体验一个新组件,又不想污染自己的开发机,直接docker run跑一个容器,用完docker rm删除,就像借了一件新工具,既不用买,也不用操心怎么还。Docker带来的这种“用完即焚、环境即产物”的体验,用几次就再也回不去了。
