ARTICLE DETAIL

资讯详情

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

Docker入门实战指南:从核心概念到部署MySQL与Redis

Docker入门实战指南:从核心概念到部署MySQL与Redis 1. 为什么我建议你学Docker从一次“环境地狱”说起你有没有遇到过这种情形本地跑得好好的代码一部署到服务器就各种报错不是缺依赖就是版本不对折腾一整天最后发现是操作系统环境差异。或者是新入职一家公司光配开发环境就花了两天数据库、缓存、消息队列一个个装版本还对不上。我之前带过一个实习生他花了整整三天在一台Windows机器上装一个老项目的依赖最后崩溃到想换电脑。Docker就是专门来解决这类问题的。它不装完整的操作系统而是把应用和它需要的运行环境一起打包成一个“标准集装箱”这个集装箱叫镜像运行起来叫容器。只要宿主机上有Docker引擎就能保证这个容器在任何地方表现一致。这就是“构建一次到处运行”的意义。这篇指南面向的是刚接触Docker的开发者不要求你有Linux基础也不要求你懂虚拟化原理。我会从核心概念讲起然后带你一步步完成安装、常用操作再跑两个实战案例MySQL和Redis最后给你一些我实际踩坑后的排查心得。整个过程以Windows为主但Ubuntu和CentOS上的差异我也会提到。跟着操作一遍你就能把Docker真正用起来而不是只看文档。2. Docker到底解决了什么镜像、容器、仓库拆开讲2.1 镜像就像一个“只读模板”你可以把镜像理解成一个操作系统的精简快照里面包含了应用运行所需的一切代码、运行时、系统库、配置文件、环境变量。比如一个mysql:8.0镜像里面就已经安装好了MySQL 8.0的二进制文件、默认配置文件、初始化脚本。它本身是只读的你启动容器时Docker会在镜像上叠加一层可写层所有写入都发生在这一层。镜像是由多层文件系统叠加起来的UnionFS每一层代表Dockerfile中的一条指令。这也是为什么拉取镜像时可以分层下载很多基础层可以复用。比如你拉取不同的Python应用镜像它们可能共享同一个python:3.9-slim基础层第二次拉的时候只需要下载新增的层快很多。2.2 容器就是镜像的运行实例容器是镜像运行起来后的进程集合。它有自己的文件系统、网络、进程空间但共享宿主机内核。相比虚拟机它不需要模拟硬件不需要完整的Guest OS所以启动速度是毫秒级磁盘占用也小得多。你可以在一个服务器上同时跑几十个容器而不会觉得卡顿这是Docker能在微服务架构中流行的根本原因。一个常见的误区是“容器里可以随便玩坏了就删”。容器确实可以随便删但你在容器里写的数据容器一删除就没了除非挂载了数据卷。生产环境里容器大多是无状态的有状态的数据必须持久化到宿主机或外部存储。2.3 仓库用来分发镜像仓库分公开和私有Docker Hub就是最大的公开仓库。你可以从仓库拉取镜像docker pull也可以把自己构建的镜像推上去docker push。企业内部一般用Harbor或Registry搭建私有仓库。在中国网络环境下直接访问Docker Hub经常超时所以我们需要配置镜像加速器这个后面单独讲。理解这三者关系后Docker的基本工作流就清楚了从仓库拉取镜像通过镜像启动容器在容器里跑应用用数据卷持久化数据用Dockerfile构建自定义镜像用Compose编排多个容器。2.4 数据卷和网络容器间的两个“基建”容器是临时的那数据库文件怎么办这就需要数据卷Volume。数据卷是宿主机上的一个目录挂载到容器内部容器对数据卷的写入会直接落在宿主机上容器删除后数据还在。我实际项目中基本都会用-v /host/path:/container/path这种bind mount方式管理配置和数据这样备份、迁移都容易。容器网络这块默认情况下容器有自己的IP宿主机和外部需要靠端口映射-p 8080:80才能访问。多个容器之间可以通过自定义网络互相通信使用服务名代替IP这也是Docker Compose里服务能互相发现的原理。刚开始不用深入但你得知道端口映射是宿主机端口:容器内端口顺序别搞反了。3. 实操把Docker装进你的电脑Windows / Ubuntu / CentOS3.1 Windows上安装Docker Desktop的完整步骤大多数Windows开发者会选Docker Desktop它自带图形界面和Docker CLI底层用WSL2或Hyper-V虚拟化。安装步骤其实很简单但细节破坏体验。先检查系统要求Windows 10 64位版本2004以上或者Windows 11。BIOS里必须开启虚拟化Intel VT-x或AMD-V。确认方法很简单打开任务管理器性能选项卡看“虚拟化”是否“已启用”。然后去Docker官网下载Docker Desktop安装包安装时勾选“Use WSL 2 instead of Hyper-V”推荐项。如果之前没安装WSL2安装过程中会提示你打开PowerShell执行wsl --install装完重启再打开Docker Desktop等右下角鲸鱼图标不再转圈就表示引擎启动成功了。验证方法是在终端执行docker version docker run hello-world如果看到“Hello from Docker”说明一切正常。最常见的启动失败virtualization support not detected。我在公司给同事排查过很多次这个报错不是说你的CPU不支持虚拟化而是虚拟化功能被关闭或冲突。处理顺序重启进BIOS找到Intel Virtualization Technology或AMD SVM设为Enabled。Windows功能里确保“虚拟机平台”和“适用于Linux的Windows子系统”已勾选执行bcdedit /set hypervisorlaunchtype auto管理员PowerShell后重启。如果装了VMware或VirtualBox它们和Hyper-V可能冲突要么升级到兼容版本要么卸掉其中一个。特别注意部分主板同时存在“虚拟化”和“VT-d”两个开关两个都要打开。3.2 Ubuntu和CentOS上的安装路径Ubuntu上装Docker最稳妥的方式是用官方安装脚本curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun执行完启动服务并设置开机自启sudo systemctl enable --now docker sudo usermod -aG docker $USERusermod那句一定要做不然以后每条docker命令都要加sudo。你可能会看到很多教程让你配/etc/apt/sources.list换源实际上用官方脚本加--mirror Aliyun已经在帮你换源了可以直接安装成功。CentOS 7比较老旧自带的yum源里Docker版本很旧建议先升级内核或者直接卸载旧版本。我一般是这样处理sudo yum remove docker docker-common docker-selinux docker-engine 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 start docker sudo systemctl enable dockerCentOS 7上还有个小坑如果遇到iptables: No chain/target/match by that name通常是firewalld和Docker的规则冲突重启firewalld或者直接systemctl stop firewalld可以缓解但生产环境建议你保留firewalld然后仔细配置规则。3.3 Docker Desktop和纯命令行引擎怎么选如果你是Windows/Mac用户用Docker Desktop省心图形界面能看日志、管理容器、配置资源配额。但它默认使用的是Linux内核所以不适合跑Windows容器当然Docker Desktop也支持Windows容器模式但一般开发场景用不到。Linux服务器上就装Docker Engine没有图形界面用命令行完全够用。另外还有一点Docker Desktop对商业公司有收费要求个人学习完全免费如果你在公司商用要么付费要么想办法用别的运行时比如Podman但这不是本文范围。4. 高频Docker命令背熟这几条就够了命令不用全记下面这些覆盖了日常90%的使用场景。4.1 镜像管理docker search mysql # 搜索镜像 docker pull mysql:8.0 # 拉取指定版本 docker images # 查看本地镜像 docker rmi mysql:8.0 # 删除镜像 docker tag mysql:8.0 my-mysql:latest # 给镜像打标签拉不到镜像时先确认是不是镜像源问题或者镜像名是否写错。比如官方MySQL镜像里已经包含了可执行的mysqld不需要再装任何东西。4.2 容器生命周期docker run -d --name my-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 mysql:8.0 docker ps # 查看运行中的容器 docker ps -a # 查看所有容器包括已停止 docker stop my-mysql # 停止 docker start my-mysql # 启动已停止的容器 docker restart my-mysql # 重启 docker rm my-mysql # 删除容器需要先停止docker run有很多参数新手最容易忽略-d后台运行和--name指定名字。不加-d终端会挂在前台一关终端容器就停了其实不会停但日志会刷屏。--name不指定的话容器会得到一个随机名字管理起来很别扭。4.3 进入容器和查看日志docker exec -it my-mysql bash docker logs -f my-mysql docker logs --tail 100 my-mysql-it是交互模式不加的话在容器里执行命令后马上退出。日志排查是日常必须-f是跟随输出相当于tail -f。如果容器启动后立刻退出先看日志找原因多半是环境变量没配好、端口被占、或者启动命令有问题。4.4 资源清理docker system df # 查看磁盘占用 docker system prune -a # 清理所有未使用的镜像、容器、网络慎用 docker container prune # 只清理停止的容器 docker volume ls # 查看数据卷 docker volume rm name我经常遇到的情况是磁盘被Docker占满了尤其是日志文件。Docker的json日志会无限增长最好在daemon.json里限制日志大小后面会讲到。5. 实战一次把MySQL和Redis部署起来5.1 部署MySQL 8.0并初始化数据库部署MySQL要关心的不是“能跑”而是“数据别丢”、“能连上”、“字符集正确”。我的推荐命令是docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDMyStr0ngPass \ -e TZAsia/Shanghai \ -v /data/mysql/conf:/etc/mysql/conf.d \ -v /data/mysql/data:/var/lib/mysql \ --restartalways \ mysql:8.0-v /data/mysql/data:/var/lib/mysql是灵魂MySQL的数据文件就在/var/lib/mysql宿主机挂载目录后容器重装数据都不丢。-e TZ设置时区不然MySQL默认UTC时间和本地时间会差8小时。--restartalways让容器在Docker重启或容器崩溃时自动拉起生产环境基本必加。MySQL镜像第一次启动时会自动执行/docker-entrypoint-initdb.d目录下的.sql或.sh脚本你可以用它来初始化表结构和账户# 初始化脚本目录 mkdir -p /data/mysql/init cp init.sql /data/mysql/init/ docker run -d \ ... 加上 -v /data/mysql/init:/docker-entrypoint-initdb.d \ mysql:8.0注意初始化脚本只在数据目录为空时执行一次如果数据目录已经有了数据就不会再执行了。所以初期设计好初始化方案很重要。连接测试用宿主机上的mysql -h 127.0.0.1 -P 3306 -u root -p如果提示密码错误检查是不是环境变量里密码含特殊字符导致解析问题。手动执行一个关键点MySQL 8.0默认认证插件是caching_sha2_password有些老客户端连不上要在创建用户时改成mysql_native_passwordCREATE USER app% IDENTIFIED WITH mysql_native_password BY app_pass; GRANT ALL PRIVILEGES ON *.* TO app%; FLUSH PRIVILEGES;5.2 部署Redis顺便把主从复制配起来单机Redis部署很轻量docker run -d --name redis \ -p 6379:6379 \ -v /data/redis/data:/data \ redis:7.2 redis-server --appendonly yes--appendonly yes开启AOF持久化数据保存在/data目录下正好匹配挂载点。热词里有“redis主从”我也给你一套主从部署方案。主从共用一个配置文件我们分别启动两个容器建立一个网络docker network create redis-net docker run -d --name redis-master \ --network redis-net \ -p 6379:6379 \ redis:7.2 redis-server --appendonly yes docker run -d --name redis-slave \ --network redis-net \ -p 6380:6379 \ redis:7.2 redis-server --appendonly yes --replicaof redis-master 6379这里的关键是--replicaof redis-master 6379容器网络内直接用服务名容器名解析IP。在从节点执行redis-cli info replication看到role:slave且master_link_status:up就说明主从正常了。热词里的“redis docker compose 生产环境部署”可以用Compose来做我在后面会给出配置文件。不过在简单场景下docker run完全够用不要为了炫技而强行上Compose。6. 构建自己的镜像Dockerfile和Docker Compose实战6.1 写一个能用的Dockerfile部署现成镜像解决的是“用别人的应用”而我们自己写的代码就要靠Dockerfile打包成镜像。以一个小型Java Spring Boot项目为例FROM maven:3.8.6-eclipse-temurin-11 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests FROM eclipse-temurin:11-jre WORKDIR /app COPY --frombuild /app/target/demo-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]这里用了多阶段构建第一个阶段编译第二个阶段只保留运行时环境。最终镜像体积会小很多不包含Maven和源码这是优化镜像体积的基本思路也是我建议所有Java/前端项目都采用的方式。前端项目如Vue/React也一样先用Node镜像构建产出dist目录再COPY到Nginx镜像FROM node:18-alpine AS build WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:alpine COPY --frombuild /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80编译完成后在项目根目录执行docker build -t my-demo:latest .如果推送镜像到仓库先登录docker login docker tag my-demo:latest yourname/my-demo:latest docker push yourname/my-demo:latest6.2 用Docker Compose编排多个服务当你一个项目同时依赖MySQL、Redis、后端、前端时一条条执行docker run管理起来就是灾难。Docker Compose用YAML文件描述一组容器一条命令全部启动。我先给你一个生产环境级别的Redis主从Compose文件正好回应前面埋的坑version: 3.8 services: redis-master: image: redis:7.2-alpine container_name: redis-master restart: always command: [redis-server, --appendonly, yes, --requirepass, MasterPass123] ports: - 6379:6379 volumes: - redis-master-data:/data networks: - redis-net redis-slave: image: redis:7.2-alpine container_name: redis-slave restart: always depends_on: - redis-master command: [redis-server, --replicaof, redis-master, 6379, --appendonly, yes, --requirepass, SlavePass456] ports: - 6380:6379 volumes: - redis-slave-data:/data networks: - redis-net volumes: redis-master-data: redis-slave-data: networks: redis-net: driver: bridge在redis-slave的配置中--replicaof指定的是服务名redis-master而不是IPCompose会自动把服务名解析到容器IP。生产环境建议把requirepass、masterauth都设好否则主从同步会因为认证失败而中断。Compose常用命令docker compose up -d docker compose ps docker compose logs -f docker compose down docker compose down -v # 连数据卷一并删除慎用部署微服务项目时每个服务对应一个service配合构建上下文选择不同的Dockerfile再通过depends_on控制启动顺序。但depends_on只控制容器的启动顺序不等待服务内部就绪。比如后端依赖MySQLMySQL容器虽然起来了但mysqld还没准备好。你可以用restart: on-failure或者健康检查来做更优雅的方案是加healthcheck。我在生产环境一般是这样给MySQL加健康检查的services: mysql: image: mysql:8.0 healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -uroot, -p$MYSQL_ROOT_PASSWORD] interval: 5s timeout: 5s retries: 20然后后端服务用depends_on里的condition: service_healthy确保数据库就绪后再启动后端这个细节很实用。7. 镜像下载慢、拉取失败的解决办法配置镜像加速器在国内docker pull经常卡住错误信息千奇百怪但根子基本都是网络问题。最直接的解决办法是配置镜像加速器。Docker daemon的配置文件在Linux下的/etc/docker/daemon.jsonDocker Desktop的用户可以在Settings - Docker Engine里改。{ registry-mirrors: [ https://docker.m.daocloud.io, https://docker.1panel.live, https://dockerproxy.com ], log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }在这个文件里我还加上了日志限制这个是实际生产中非常重要的配置。生产服务器如果不限制Docker日志文件能把磁盘撑爆容器正常运行却无法写日志应用就挂了。这里指定的max-size10m表示单个日志文件最大10MBmax-file3保留3个文件。配置完重启Dockersudo systemctl restart docker不同加速源时效性不一样如果你发现某个加速地址失效了多试几个或者用你云厂商提供的专属加速器。注意改完加速器后第一次docker pull可能还是慢因为要解析域名和建立连接耐心等一会儿。热词里出现“docker镜像下载慢”、“docker unexpected eof”这个unexpected eof一般是拉取镜像时连接中断可能原因有网络不稳定、镜像源限流、磁盘空间不足。你先清理磁盘空间再换加速源重试。也可以考虑使用docker pull时加--platform参数比如在Apple Silicon上拉x86镜像时会报错需要指定--platform linux/amd64。8. 实战中躲不开的坑故障排查完整链路8.1 Docker Desktop启动失败排查热词里有两个特别具体的问题我来说说排查过程。第一个是virtualization support not detected前面提过BIOS设置。第二个是failed to connect to the docker api at npipe:////./pipe/dockerdesktoplinuxen。这个通常出现在Docker Desktop里的Linux引擎没有启动成功时或者是Docker Desktop已经开启但CLI连不上。我的排查顺序看Docker Desktop图标状态转圈还是停止。打开Docker Desktop的Troubleshoot按钮查看日志。尝试wsl --shutdown后重新打开Docker Desktop。在PowerShell里执行wsl --status确认WSL发行版状态正常没用的旧发行版会导致问题。如果还是连不上在我印象中最可能的原因是WSL2发行版损坏或内核不是最新版。执行wsl --update再重启。这个问题折磨了很多Windows用户但流程对了其实能快速解决。另一个高频问题是“docker权限错误怎么解决”。Linux上执行docker ps如果报permission denied while trying to connect to the docker daemon socket说明当前用户不在docker组里。执行sudo usermod -aG docker $USER newgrp docker然后重新登录终端即可。如果是远程连接还要确认/var/run/docker.sock的权限。8.2 容器启动后立即退出的定位思路新手常遇到docker run后容器不到一秒就退出。这时候不要反复重试先看日志docker logs 容器名日志里通常会写明原因。我之前遇到过MySQL启动退出日志里有[ERROR] [MY-011087] [Server] Different lower_case_table_names settings for server (1) and data dictionary (0)这是因为我拿旧版本MySQL的数据目录挂载到了8.0版本上解决方法是清掉数据目录重新初始化或者调整--lower_case_table_names参数。很多类似的问题都是因为数据目录和镜像版本不匹配导致的。如果日志没写清楚可以进入容器手动执行启动命令docker run -it --rm my-image /bin/bash在容器内部手动跑应用的启动命令能看到更直接的错误。这种调试方法比看Docker日志更深入一层。8.3 端口映射冲突和网络问题docker run -p 3306:3306报bind: address already in use说明宿主机3306端口已经被占用。用netstat -tlnp | grep 3306查一下是哪个进程要么换宿主机端口比如-p 3307:3306要么杀掉占用进程。容器A要访问容器B的端口时注意别写localhost或127.0.0.1。在同一个Docker网络里容器之间要用服务名或容器名互访。比如前面Redis从节点连接主节点写的是redis-master不是localhost。如果你用--link老式方式我建议尽快迁移到自定义网络--link已经过时了。8.4 磁盘爆满的急救方法生产服务器最怕的就是Docker日志或镜像残留占满磁盘。我先查看占用docker system df然后清掉停止的容器和无用的镜像、构建缓存docker system prune -a --volumes这个命令会删掉所有未在运行容器中使用的镜像、数据卷执行前务必确认。如果只想清理构建缓存docker builder prune就够了。更保险的做法是定期用docker system df观察同时在daemon.json里设置日志上限从根源防止磁盘被日志拉爆。9. 我的个人经验和几个额外建议写到这里我觉得已经覆盖了从概念到实战的完整链路。最后分享几个提升效率、避免返工的习惯。第一个是给容器打标签。生产环境不要用latest当版本号镜像标签固定到具体版本如myapp:1.2.3这样回滚时才能精确定位。我见过生产环境latest被意外推送覆盖然后所有节点拉取到错误的镜像排查半天。第二个是用好docker inspect。这个命令能看到容器的完整配置比如挂载路径、环境变量、网络模式等。遇到“容器没生效”的怪问题先docker inspect很多原因是环境变量写错了。第三个是不要在生产环境用docker restart代替进程守护。容器内部的应用如果崩溃docker restart策略可以拉起但应用自身的信号处理、优雅关闭还是得靠应用代码实现。--restartalways配合合理的启动命令能最大程度保证可用性。第四个是尽量用Docker Compose管理多容器项目。即使只有一个服务也用Compose把你的端口映射、数据卷、环境变量写在文件里至少代码审查时能看清楚整个部署拓扑比命令行参数清晰得多。Docker入门没有想象中那么难最关键的还是动手。你把MySQL和Redis两个容器跑起来再把一个自己的应用打成镜像基本就算入门了。遇到报错别慌按照日志一层层剥大部分都是端口、权限、网络这三类问题。按照我上面给出的思路去排查你也能成为同事眼里的Docker“老司机”。
返回列表