简介:这份《Docker新手完全指南:从入门到实战万字大全》面向零基础初学者与有一定经验的技术人员,尤其适合对容器化技术感兴趣的开发者、运维人员和架构师。内容从容器与虚拟机的本质差异切入,系统梳理镜像、容器、仓库三大核心概念,并覆盖Linux、Windows、macOS跨平台安装配置及镜像加速避坑要点。资源包为1个PDF文件,约1004KB,篇幅精炼却信息密度高,便于随时查阅。文档深入讲解容器生命周期管理、镜像管理与诊断调试命令,通过多阶段构建等Dockerfile实践指导高效镜像编写,并延伸至数据卷持久化、网络模式选择与Docker Compose多容器编排,最后附常见问题排错指南与进阶学习路径。目前已有201人学习,适合希望从零掌握容器化应用开发、测试与部署全流程的读者按章节循序渐进地实操演练。
1. 从一台干净的 Linux 机器说起:Docker 到底解决了谁的痛
刚拿到一台全新的 Ubuntu 服务器,或者本地刚装好 Windows 想跑个 Redis 练手,第一反应往往是「我该装什么、装哪个版本、装完怎么不冲突」。传统做法是 apt 装一遍、pip 装一遍、手动改配置文件、再 export 一堆环境变量,换台机器重来一遍,版本对不上就翻车。Docker 把「应用 + 依赖 + 配置」打包成一个镜像,容器启动时按镜像还原,环境一致性问题基本被抹平。这篇指南面向的是刚接触容器、想在自己机器上把 Docker 跑起来并部署几个真实服务的人,从安装、镜像、Dockerfile、Docker Compose 一路讲到资源隔离和排错。读完你应该能独立写出一个能用的 Dockerfile,用 Compose 拉起 MySQL、Redis、Nacos 这类常见服务,并且知道容器网络不通、权限报错时该往哪查。
2. 安装 Docker 与 Docker Desktop:三条路径怎么选
2.1 Linux 上用官方脚本装 Docker Engine
Linux 服务器是最常见的落地场景,Ubuntu、CentOS 7、UOS 这类系统都适用。官方提供了一键脚本,但生产环境我更建议走 apt 仓库,方便后续升级和锁版本。
# 卸载可能存在的旧版本,避免依赖冲突 sudo apt-get remove docker docker-engine docker.io containerd runc # 安装基础依赖:ca 证书、curl、gnupg sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg # 添加 Docker 官方 GPG key,用于校验包来源 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 a+r /etc/apt/keyrings/docker.gpg # 写入 apt 源,注意 $(lsb_release -cs) 会取当前发行版代号 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 # 安装 engine、cli、containerd 和 compose 插件 sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 把当前用户加入 docker 组,避免每条命令都 sudo sudo usermod -aG docker $USER逻辑上分四步:清旧、加源、装包、配权限。docker-compose-plugin装完后命令是docker compose(中间空格),不是老的docker-compose。如果你敲docker compose报unknown command,八成是只装了老的独立二进制或者插件没装上,用docker compose version验证一下。加入 docker 组后要重新登录 shell 才生效,否则还是得 sudo。
CentOS 7 的步骤类似,把 apt 换成 yum,源地址换成https://download.docker.com/linux/centos/docker-ce.repo。CentOS 7 已停止维护,很多镜像源同步慢,建议提前配好可用的镜像加速地址,否则docker pull会卡到怀疑人生。
2.2 Windows 上装 Docker Desktop 与 WSL2 前置条件
Windows 用户最省事的是 Docker Desktop,但它对系统有硬性要求:Win10 2004 以上或 Win11,开启 WSL2 或 Hyper-V。装完启动如果报Virtualization support not detected,说明 BIOS 里虚拟化没开,或者 Hyper-V/WSL2 没启用。先在「启用或关闭 Windows 功能」里勾上「虚拟机平台」和「适用于 Linux 的 Windows 子系统」,再wsl --update更新内核。
Docker Desktop 装好后,Settings 里可以调 CPU、内存、磁盘镜像位置。默认镜像放在 C 盘,跑几个大镜像 C 盘就红了,建议在 Resources 里把 Disk image location 改到其他盘。Windows 下挂载目录要注意路径写法,-v D:\data:/data这种反斜杠在部分终端会被转义,稳妥写法是-v /d/data:/data或者用双反斜杠。
2.3 国内网络下配置镜像加速
不管哪个平台,拉镜像慢是常态。配置镜像加速的入口在/etc/docker/daemon.json(Linux)或 Docker Desktop 的 Docker Engine 设置里。
{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com" ], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }registry-mirrors是镜像加速地址列表,按顺序尝试。log-opts限制单个容器日志大小,不加这个,跑久了的容器日志能把磁盘写满,这是血泪经验。改完执行sudo systemctl daemon-reload && sudo systemctl restart docker生效。注意镜像加速地址会失效,用之前先确认当前可用性,别照抄一个几年前的地址。
3. 镜像与容器:把「一次构建,到处运行」落到命令上
3.1 镜像分层与常用拉取、查看命令
镜像不是一整块文件,而是若干只读层叠加,最上面加一个可写层就是容器。这个设计让相同基础层的镜像共享磁盘,也解释了为什么docker pull有时只下几 MB——公共层本地已经有了。
# 拉取指定版本,别用 latest,版本不可控 docker pull redis:7.2-alpine # 查看本地镜像,含镜像 ID、大小、创建时间 docker images # 查看镜像分层历史,排查为什么镜像这么大 docker history redis:7.2-alpine # 查看镜像详细信息,含环境变量、入口命令 docker inspect redis:7.2-alpinedocker images里的 IMAGE ID 是短 ID,docker inspect用短 ID 或完整 ID 都行。docker history能看出每层多大,如果某层异常大,通常是 COPY 了一个大文件或者 apt 缓存没清。选基础镜像时,alpine体积小但用的是 musl libc,某些依赖 glibc 的二进制跑不起来;slim是 Debian 精简版,兼容性好一些,体积居中。生产上我一般优先slim,除非对体积极度敏感。
3.2 启动、进入、清理容器的完整命令链
# 后台启动一个 Redis,映射端口并挂载数据目录 docker run -d --name my-redis \ -p 6379:6379 \ -v /data/redis:/data \ --restart unless-stopped \ redis:7.2-alpine redis-server --appendonly yes # 进入运行中的容器,调试用 docker exec -it my-redis sh # 查看容器日志,-f 持续输出 docker logs -f --tail 100 my-redis # 停止并删除容器,-v 同时删匿名卷 docker rm -f my-redis # 清理无用镜像、容器、网络、构建缓存 docker system prune -a-d后台运行,--name指定名字方便后续操作,-p 6379:6379是「宿主端口:容器端口」,-v挂载目录保证数据不随容器删除而丢失,--restart unless-stopped让容器随 Docker 启动自动拉起。docker exec进去后如果容器里没有 bash,用sh。docker system prune -a会删掉所有没在用的镜像,执行前想清楚,别把正在用的基础镜像删了。
提示:
-v挂载的宿主目录如果不存在,Docker 会自动创建,但属主是 root。容器内非 root 用户写不进去时,先chown宿主目录,或者用--user指定 UID。
3.3 容器网络模式与端口映射的取舍
默认的 bridge 网络下,容器之间要通过 IP 通信,IP 会变,不靠谱。自定义 bridge 网络支持用容器名当主机名解析,这是 Compose 能工作的基础。
# 创建自定义网络 docker network create my-net # 两个容器加入同一网络,可直接用名字互访 docker run -d --name app --network my-net my-app docker run -d --name db --network my-net mysql:8.0 # 此时 app 容器内 ping db 能通host模式让容器直接用宿主网络栈,性能好但端口会冲突,且失去隔离。none模式只有 loopback,适合纯计算任务。排查「docker 网络不通」时,先docker network inspect my-net看容器是否真的加入了同一网络,再进容器ping对方名字,最后查宿主防火墙。很多时候不是 Docker 的问题,是宿主 iptables 或安全组拦了。
4. 写一个能上生产的 Dockerfile:从能跑到跑得好
4.1 多阶段构建把镜像从 1G 压到 100M
以 Java 应用为例,如果直接在构建镜像里编译再运行,镜像里会带上 Maven、JDK 全套,动辄 800M 起步。多阶段构建把编译和运行分开,运行阶段只留 JRE 和 jar 包。
# 阶段一:构建,用带 Maven 的镜像 FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /build # 先拷 pom,利用层缓存,依赖没变就不重新下载 COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests # 阶段二:运行,只留 JRE FROM eclipse-temurin:17-jre-jammy WORKDIR /app # 从构建阶段拷贝产物 COPY --from=builder /build/target/*.jar app.jar # 用非 root 用户运行,降低权限风险 RUN useradd -r -u 1001 appuser USER appuser EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]关键点有三个:COPY pom.xml单独一层,让依赖下载结果被缓存,改代码不会触发重新下依赖;COPY --from=builder只搬产物,不搬构建工具;USER appuser切非 root。最后一条经常被忽略,容器默认 root 运行,一旦被突破就是宿主 root 权限,这是镜像安全和容器安全里最基础的一条。
4.2 层缓存、.dockerignore 与构建参数
.dockerignore的作用和.gitignore一样,但很多人不写,结果COPY . .把node_modules、.git、日志全打进镜像,构建慢、镜像大。
# .dockerignore .git node_modules target *.log .env .idea构建参数用ARG声明,--build-arg传入,适合区分环境的配置。但注意ARG的值会留在镜像历史里,密码这类敏感信息别用 ARG,用运行时环境变量或 secret 挂载。
# 构建并打标签,标签带版本号 docker build -t my-app:1.0.0 --build-arg PROFILE=prod . # 查看构建缓存命中情况,CACHED 说明复用了层 docker build -t my-app:1.0.1 .层缓存的规则是:某层变了,它之后的所有层都失效。所以 Dockerfile 里把变化频率低的放前面,变化频率高的放后面。RUN apt-get install后面记得跟&& rm -rf /var/lib/apt/lists/*,否则 apt 缓存会留在层里,白白多几十 MB。
4.3 镜像瘦身与安全扫描的实操
瘦身三板斧:换小基础镜像、多阶段构建、清理临时文件。安全扫描可以用docker scout或trivy,扫出基础镜像里的已知漏洞。
# 用 trivy 扫描镜像漏洞 trivy image my-app:1.0.0 # 只看高危和严重 trivy image --severity HIGH,CRITICAL my-app:1.0.0扫描结果里基础镜像的漏洞占大头,所以选一个维护活跃的基础镜像比事后修补更重要。alpine漏洞少但兼容性差,debian:slim折中。扫描不是一次性的,基础镜像更新后要重新构建重新扫。别把扫描当形式,真出问题时它就是那个后悔药。
5. Docker Compose 编排:一条命令拉起 MySQL、Redis 和 Nacos
5.1 compose 文件结构与 depends_on 的真实语义
Compose 用一个 YAML 描述多个服务,docker compose up -d一键拉起。下面是一个 MySQL 8.0 + Redis 主从 + Nacos 3.x 的典型组合。
services: mysql: image: mysql:8.0 container_name: mysql8 environment: MYSQL_ROOT_PASSWORD: root_pwd MYSQL_DATABASE: nacos ports: - "3306:3306" volumes: - ./mysql/data:/var/lib/mysql - ./mysql/conf:/etc/mysql/conf.d command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s retries: 5 redis-master: image: redis:7.2-alpine container_name: redis-master ports: - "6379:6379" command: redis-server --appendonly yes redis-slave: image: redis:7.2-alpine container_name: redis-slave command: redis-server --replicaof redis-master 6379 depends_on: - redis-master nacos: image: nacos/nacos-server:v3.0.0 container_name: nacos environment: MODE: standalone SPRING_DATASOURCE_PLATFORM: mysql MYSQL_SERVICE_HOST: mysql MYSQL_SERVICE_DB_NAME: nacos MYSQL_SERVICE_USER: root MYSQL_SERVICE_PASSWORD: root_pwd ports: - "8848:8848" - "9848:9848" depends_on: mysql: condition: service_healthydepends_on默认只保证启动顺序,不保证依赖服务「就绪」。MySQL 容器起来了但还没初始化完,Nacos 连上去照样报错。所以 MySQL 配了healthcheck,Nacos 用condition: service_healthy等健康检查通过再启动。这是 Compose 里最容易踩的坑之一。
5.2 环境变量、卷与网络在 Compose 里的写法
环境变量可以写在environment里,也可以放.env文件让 Compose 自动读取。密码这类别硬编码在 YAML 里提交到仓库,用.env并加进.gitignore。
# .env 文件 MYSQL_ROOT_PASSWORD=root_pwd NACOS_VERSION=v3.0.0# compose 里引用 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} image: nacos/nacos-server:${NACOS_VERSION}卷分两种:./mysql/data:/var/lib/mysql是绑定挂载,数据落在项目目录,方便备份;mysql-data:/var/lib/mysql是命名卷,由 Docker 管理,跨项目复用。绑定挂载要注意宿主目录权限,MySQL 容器内是 mysql 用户,宿主目录属主不对会启动失败,日志里报Permission denied。
Compose 默认给项目创建一个网络,所有服务自动加入,服务名就是主机名。所以 Nacos 里写MYSQL_SERVICE_HOST: mysql就能连上,不用写 IP。跨项目通信才需要显式声明 external 网络。
5.3 常用 Compose 命令与滚动更新
# 后台启动全部服务 docker compose up -d # 只看某个服务日志 docker compose logs -f nacos # 重新构建并重启某个服务,改代码后用 docker compose up -d --build nacos # 停止并删除容器、网络,保留卷 docker compose down # 连卷一起删,数据没了,慎用 docker compose down -v # 查看服务状态和健康检查结果 docker compose psdocker compose ps会显示健康状态,healthy才算真正就绪。滚动更新时,Compose 默认先停旧再起新,有短暂中断;要零中断得配合deploy.replicas和反向代理,那是 Swarm 或 K8s 的范畴了。单机场景下,up -d --build够用。
注意:
docker compose和docker-compose是两个东西。前者是插件,后者是老的 Python 独立版。命令报unknown command: docker compose说明插件没装,装docker-compose-plugin即可。
6. 避坑与排查:容器跑不起来时先看这几处
6.1 容器启动即退出,日志只有一行
现象:docker run -d后docker ps看不到容器,docker ps -a显示Exited (1)。原因通常是主进程前台运行失败,比如命令写错、配置文件缺失、端口被占。解决:docker logs <容器名>看退出前的输出,再docker run -it <镜像> sh手动进去跑一遍命令,定位是哪一步挂的。容器的主进程必须前台运行,如果 Dockerfile 里 CMD 跑的是后台脚本,进程一退容器就退。
6.2 挂载目录权限报 Permission denied
现象:MySQL、Nginx 这类容器挂载宿主目录后启动失败,日志报无法写入。原因是容器内进程以非 root 用户运行,而宿主目录属主是 root。解决:chown -R 1001:1001 ./data把宿主目录属主改成容器内用户 UID,或者在 compose 里加user: "1001:1001"。别图省事直接chmod 777,那是把权限问题变成安全问题。
6.3 容器间网络不通,ping 名字失败
现象:两个容器在同一 Compose 项目里,A 连不上 B。原因可能是没在同一网络、服务名写错、或者 B 还没就绪。解决:docker network inspect <网络名>确认两个容器都在;进 A 容器getent hosts B看能否解析;如果解析正常但连不上,查 B 的端口是否监听在 0.0.0.0 而不是 127.0.0.1。很多应用默认只监听 localhost,容器外自然连不上,改配置监听0.0.0.0。
6.4 磁盘被日志和镜像撑满
现象:docker ps正常但宿主磁盘 100%,服务陆续挂掉。原因:容器日志没限制大小,或者docker system prune长期没跑,悬空镜像堆积。解决:daemon.json 里配log-opts限制单容器日志大小;定期docker system df看占用,docker image prune清悬空镜像。生产上建议把 Docker 数据目录迁到独立大盘,别和系统盘混用。
6.5 Windows 下挂载目录路径与换行符问题
现象:Windows 上-v D:\code:/app挂载后容器里文件为空或路径报错。原因:路径分隔符和盘符写法在 Git Bash、PowerShell、CMD 里不一致。解决:统一用/d/code:/app这种形式,或者在 Docker Desktop 的 File Sharing 里确认该盘已授权。另外 Windows 的 CRLF 换行符会让 shell 脚本在 Linux 容器里报\r: command not found,用dos2unix转一下或者编辑器设成 LF。
7. 把容器资源管起来:CPU、内存限制与运行时调优
容器默认不限制资源,一个跑飞的 Java 进程能把宿主内存吃光,拖垮其他服务。限制资源在docker run或 compose 里都能配。
# 限制 CPU 为 1.5 核,内存 1G,内存+swap 1.5G docker run -d --name app \ --cpus="1.5" \ --memory="1g" \ --memory-swap="1.5g" \ my-app:1.0.0--cpus是软限制,按比例分配;--memory是硬限制,超了容器内进程会被 OOM Killer 干掉。--memory-swap要大于等于--memory,等于时表示禁用 swap。Java 应用尤其要注意,JVM 默认按宿主内存算堆大小,容器里不设-Xmx会按宿主内存分配,一启动就被 OOM。JDK 10 以后支持-XX:MaxRAMPercentage,按容器内存比例设堆,比写死-Xmx更灵活。
# 容器内 JVM 按容器内存的 75% 设堆 java -XX:MaxRAMPercentage=75.0 -jar app.jar验证限制是否生效,用docker stats实时看 CPU、内存、网络 IO。如果内存一直贴着上限,说明限制太紧或者有内存泄漏,先调大再排查。CPU 限制下如果docker stats显示 CPU 长期 100%,说明该加核或者优化代码了。
进阶一点,可以用--cpuset-cpus把容器绑到特定核,减少上下文切换,适合对延迟敏感的服务。--blkio-weight调磁盘 IO 权重,多容器争抢磁盘时有用。这些参数不是越多越好,先跑起来观察docker stats,有瓶颈再针对性调。我自己习惯是每个生产容器都设内存上限,哪怕设得宽松,也比不设强——不设的话,一个内存泄漏就能让整台机器失联,那种半夜被叫起来重启服务器的经历,一次就够了。希望帮到你。
本文还有配套的精品资源,点击获取