Docker入门到实践:容器化部署核心概念与实操指南

Docker入门到实践:容器化部署核心概念与实操指南 1. 先想清楚Docker解决的是环境搬运这件事1.1 从在我电脑上是好的说起在聊Docker的具体命令之前我想先花点时间把为什么要容器化这件事说透。很多新手一上来就急着敲 docker run跑到一半遇到各种环境问题然后就放弃了。其实Docker真正解决的是环境不一致的问题你在开发环境里跑得好好的到测试环境、生产环境就起不来数据库连不上、依赖缺失、版本冲突最后只能无奈地说在我电脑上是好的。Docker把应用代码和它依赖的运行时、配置、库文件打包成一个标准化的镜像然后在任何装了Docker的机器上用同一个镜像启动容器。容器不是虚拟机它共享宿主机的内核但拥有独立的文件系统、进程、网络和用户空间。所以同一个MySQL 8.0镜像在你的Windows上用在云服务器Linux上用跑出来的行为基本一致差别最多是映射端口、数据卷位置这些外部配置。对于刚入门的人来说这种环境搬运的能力意味着你不需要再为每个项目手动安装一堆依赖也不需要担心卸载后残留文件。你只需要把镜像拉下来、启动容器必要时挂载数据卷服务就能跑起来。这也是为什么容器化部署已经成为当下服务交付的主流方式Docker则是绝大多数人接触容器化的第一站。这篇文章就围绕这个主题从安装讲到常见实操再到Compose编排和排障希望你看完能自己动手把MySQL、Redis、Nextcloud这类服务跑起来。1.2 镜像、容器、仓库这三个概念必须拧清Docker体系里最常被混淆的三个词是镜像、容器和仓库。我习惯用生活里的类比来理解镜像是一个只读的安装包模板容器是这个安装包运行起来之后的实例仓库是存放镜像的远程或本地存储中心。具体来说镜像由一层一层的只读文件系统组成每一条构建指令产生一个层这些层可以复用。拉取镜像时如果本地已经有相同层Docker会直接复用不会重复下载。容器则是在镜像上增加一个可写层你可以在里面安装软件、修改配置但这些修改在容器删除后会丢失除非你显式提交成新镜像或使用数据卷持久化。仓库的作用则类似应用商店Docker官方的Docker Hub就是最大的公共仓库你还可以搭建私有仓库存内部镜像。我们可以用一个表格来快速区分概念简单理解生命周期核心命令镜像只读模板可一直存在docker build / pull / tag容器镜像的运行实例可创建、启停、删除docker run / start / stop / rm仓库镜像的存储与分发中心长期存在docker push / pull把这三个概念分开后很多命令就不难记了docker pull是把镜像从仓库拉到本地docker run是根据镜像创建并启动容器docker exec是进入运行中的容器执行命令。后面所有实操都是围绕这条链路展开的。1.3 一个容器生命周期里发生的事新手容易把 docker run 当成一个简单的启动命令其实一个完整的容器生命周期包含镜像获取、容器创建、启动、运行、停止、删除等阶段理解这个流程对排查问题很有帮助。假设你在命令行执行 docker run -d --name web -p 8080:80 nginx:alpineDocker会先检查本地有没有 nginx:alpine 镜像没有就去配置的仓库拉取拉下来后基于镜像创建一个可写层容器分配一个容器ID和默认网络端口然后执行镜像里设置的启动命令通常是CMD或ENTRYPOINT指定的脚本如果启动命令在前台运行且没有自动退出容器就会保持运行状态。当你执行 docker stop webDocker会先给容器内主进程发送SIGTERM信号等待超时后再发SIGKILL强制结束执行 docker rm web 才会删除这个容器包括它的可写层。容器里的进程是隔离的但从系统角度看它仍然是宿主机上的进程只是通过命名空间限制了对其他资源的可见性。这也是为什么容器启动很快因为不需要像虚拟机那样引导整个操作系统。理解了这一层你再去理解 docker logs、docker restart、docker inspect 这些命令就会清楚它们分别作用于生命周期的哪个阶段。我自己排查问题时的顺序通常是docker ps 看容器在不在docker logs 看输出docker inspect 看配置定位到原因再决定是重启、删除重建还是进入容器内修改。2. 装好DockerWindows、Linux和macOS的路数不一样2.1 Windows装Docker Desktop前先搞定WSL2和虚拟化Windows上安装Docker最主流的方式是Docker Desktop但很多人安装后第一步就卡住了最常见的报错是Docker Desktop failed to start because virtualisation support wasnt detected。这个报错本质是Windows没有检测到虚拟化能力或者虚拟化没对齐。处理步骤依次是先确认CPU虚拟化是否已在BIOS/UEFI中开启Intel平台叫Intel VT-xAMD平台叫AMD-V再检测Windows的虚拟化相关功能是否启用包括虚拟机平台和适用于Linux的Windows子系统WSL。在PowerShell管理员模式下执行 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart然后再执行一条开启虚拟机平台的命令dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart完成后重启系统。Docker Desktop依赖WSL2作为Linux后端。很多情况下即使虚拟化已开启WSL2内核没更新或没有安装发行版也会导致启动失败。启动WSL报wsl --install是一种方式但老版本Windows需要手动从微软官网下载WSL2内核更新包并执行 wsl --set-default-version 2。装好WSL2后打开Docker Desktop的Settings在Resources-WSL Integration里确认你要用的发行版已经启用。如果还看到weve detected that you have an incompatible version of windows这类提示那就是Windows版本过旧Docker Desktop新版本要求Win10 21H2或更高版本的系统升级系统或安装对应旧版Docker Desktop是两条常见出路。2.2 Linux下安装Docker Engine别偷懒用在线脚本在Linux服务器上安装Docker很多教程会让你直接执行 curl -fsSL https://get.docker.com | bash -s -- 这个在线脚本确实很快但我建议你在生产环境里走官方仓库安装原因有两点一是网络脚本下载的内容不确定你无法完全掌控它装了哪个版本二是企业内部环境往往有代理和yum或apt源规范直接在线脚本容易绕过这些配置。以Ubuntu为例常规安装流程是先更新apt索引然后安装 ca-certificates curl gnupg lsb-release 这些依赖之后添加Docker官方GPG密钥和仓库再 apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin。CentOS 7升级Docker则会涉及移除旧版本、安装yum-utils、配置仓库、安装docker-ce、设置开机自启等步骤。最容易被忽略的一步是给当前用户加docker组避免每次敲命令都要sudo执行 sudo usermod -aG docker $USER然后重新登录当前会话。Linux安装完成后建议先验证一下三件事docker --version 看客户端版本docker compose version 看Compose插件是否存在sudo systemctl enable --now docker 设置开机启动。如果你安装的是老版本没有compose插件也可以单独安装docker-compose二进制文件。Docker服务启动失败时用 journalctl -u docker 查看日志比盲目重装高效得多。2.3 装到D盘这种需求其实是改WSL的数据目录很多Windows用户安装Docker Desktop时关心一件事能不能装到D盘别把C盘塞满。这里的装其实分两部分安装程序本身可以指定安装目录但Docker Desktop默认的镜像、容器、卷数据都存放在C盘的用户目录下更准确的说是放在WSL2虚拟磁盘文件里。Docker Desktop on Windows的背后是一个叫 docker-desktop 的WSL发行版和一个 docker-desktop-data 的数据发行版这两个虚拟磁盘的vhdx文件通常位于C:\Users你的用户名\AppData\Local\Docker\wsl。想把Docker数据迁移到D盘一个常见的办法是先在Settings里关闭Docker Desktop然后使用 wsl --shutdown 停掉WSL再导出这两个发行版的vhdx文件再注销并导入到D盘目标路径。具体命令类似 wsl --export docker-desktop D:\docker\docker-desktop.tar然后 wsl --unregister docker-desktop再 wsl --import docker-desktop D:\docker\docker-desktop D:\docker\docker-desktop.tardocker-desktop-data也照做。操作完成后重新启动Docker Desktop再观察磁盘占用。需要特别注意的是这种操作对新手来说有一定风险如果操作不当可能导致Docker Desktop无法启动。我现在会更推荐一个相对简单的方案先安装Docker Desktop到D盘安装完成后把WSL数据目录通过上述方式迁移或者干脆直接使用Windows下的WSL发行版作为工作环境在WSL内安装Docker Engine这样虚拟磁盘文件也可以自定义位置。无论哪种方式迁移前都先备份重要容器和数据卷迁移后再用 docker images 和 docker ps -a 验证数据是否完整。这也是docker desktop installer.exe怎么装到D盘这类问题背后真正需要做的事。3. 第一次实操从运行nginx到操作MySQL容器3.1 拉镜像慢先换一个能用的镜像加速源镜像下载慢几乎是所有国内新手遇到的第一个真实痛点默认连接Docker Hub速度不稳定几十MB的镜像可能要几分钟甚至失败。解决思路是为Docker配置镜像加速源注意这只是加速拉取公共镜像不影响你后续构建私有镜像。在Docker Desktop中配置镜像源比较直接打开Settings-Docker Engine在JSON里加入registry-mirrors: [https://你的加速地址]保存并重启。在Linux上则是修改 /etc/docker/daemon.json同样加入registry-mirrors数组然后执行 sudo systemctl daemon-reload sudo systemctl restart docker。云厂商一般会为用户提供专属加速地址阿里云、腾讯云、华为云都有对应文档你在自己账号的控制台里能找到专属URL填进去即可。这里提醒一句镜像源只影响从仓库拉取镜像的速度不影响容器运行时的网络。如果你用的是自定义证书仓库或私有仓库还需要在daemon.json里配置insecure-registries但公共环境不建议这么干。配置完之后可以用 docker info 查看Registry Mirrors是不是生效拉一个大镜像测试比如 docker pull mysql:8.0速度如果明显提升就说明配置成功了。3.2 跑一个前台容器把端口映射和日志输出看明白第一次实操我建议先用nginx因为镜像小、启动快不容易出幺蛾子。执行 docker run -d --name myweb -p 8080:80 nginx:alpine这条命令里-d表示后台运行--name给容器起名字-p 8080:80把宿主机的8080端口映射到容器内的80端口。启动后访问 http://localhost:8080看到Nginx欢迎页就说明你的第一个容器已经跑起来了。但仅仅跑起来还不够你得学会观察它。 docker ps 看运行中的容器加-a参数看所有容器 docker logs -f myweb 实时看日志输出 docker stats myweb 看CPU、内存和网络吞吐。端口映射这里最容易踩坑的是端口被占用和短时间内反复重启当你发现浏览器访问不了时先看 docker ps -a 里容器的STATUS是不是Up或ExitedExited说明启动后进程退出要立刻 docker logs myweb 查原因。如果想更深入理解容器可以执行 docker inspect myweb里面会输出容器的完整元数据包括网络IP、挂载卷、环境变量、健康检查等排查问题非常有用。我个人习惯把 docker inspect 当作容器的身份证体检报告很多诡异问题都能在这里找到线索。等nginx跑通后我建议你执行 docker stop myweb docker rm myweb 清理掉再带着 --restart unless-stopped 参数启动这样机器重启后容器能自动恢复是生产环境里常用的做法。3.3 在容器里执行命令docker exec -it 的完整用法实操中你经常会需要进入容器内部比如查看配置文件、手动执行某个数据库命令这时候就要用 docker exec。最经典的写法是 docker exec -it mycontainer bash-i表示保持标准输入-t分配一个伪终端合起来才能获得一个可以交互的shell。以MySQL容器为例假设你运行了 docker run -d --name mysql8 -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 mysql:8.0想在里面执行SQL可以先 docker exec -it mysql8 bash 进入容器然后执行 mysql -uroot -p123456 登录。不过并不是所有镜像都自带bash比如alpine基础镜像只有一个sh这时你要把命令改成 docker exec -it 容器名 sh。为了避免反复进入容器也可以直接执行 docker exec -it mysql8 mysql -uroot -p123456Docker会把命令直接交给容器内进程执行输出直接显示在宿主机终端上。还有一个常被忽略的细节docker exec 和 docker attach 不一样。attach是把当前终端挂到容器的主进程上一旦容器主进程退出终端也会跟着退出exec则是创建一个新的进程并在其中执行命令不会干扰容器主进程。所以日常调试优先用execdocker logs 看日志不到万不得已不要用attach。4. 数据卷和网络让容器不可怕的关键两步4.1 容器一删数据就没了所以要用volume有了一次实操经验后你会遇到一个很现实的问题容器删除后里面的数据也跟着没了。比如跑MySQL容器在容器里创建了数据库和表执行 docker rm mysql8 清掉容器再重新run数据全部消失。这是因为容器可写层和容器生命周期绑定在一起容器删除后随之销毁。Docker提供的解决方案是数据卷volume。卷本质上是一个宿主机上的目录被挂载进容器的指定路径容器读写这个路径时实际读写的是宿主机目录和容器生命周期解耦。Docker Compose和运行命令里常见的卷类型有三种命名卷、匿名卷和绑定挂载。类型创建方式适用场景命名卷docker volume create mydata或 -v mydata:/var/lib/mysql持久化数据推荐匿名卷-v /var/lib/mysql临时数据容器删除后卷可能残留绑定挂载-v /host/path:/container/path开发环境改代码即时生效MySQL官方镜像在Dockerfile里声明了VOLUME /var/lib/mysql所以即使你没手动指定卷docker run时也会创建一个匿名卷来保存数据。但匿名卷很难管理下次启动时不容易复用所以我建议你始终显式指定命名卷比如 docker run -d --name mysql8 -e MYSQL_ROOT_PASSWORD123456 -v mysql-data:/var/lib/mysql mysql:8.0。这样删掉容器后卷还在重新run同一个镜像并挂载同一个卷数据就能回来。4.2 自定义网络和容器名解析Redis主从就是这么连起来的单容器玩明白后自然会遇到多个容器需要互相通信的场景。比如Redis主从架构主节点和从节点都在Docker里跑从节点需要连到主节点。很多人这时候会直接用IP地址连接但容器重启后IP会变配置就不稳定。Docker里的更优雅做法是创建自定义网络然后通过容器名互相访问。执行 docker network create redis-net 创建一个bridge网络然后分别用 --network redis-net 启动两个Redis容器。只要在同一个自定义网络里容器之间就能通过容器名解析到对方IP这是内置DNS服务提供的能力。启动主节点时加 --name redis-master启动从节点时加 --name redis-slave从节点内部执行 redis-cli replicaof redis-master 6379Redis会自动把 redis-master 解析成主节点容器的IP。这个特性不仅对Redis好用对任何需要服务发现的架构都通用。默认bridge网络虽然也有DNS但它需要在启动时用--link参数做链接已经是老方法了自定义网络要干净得多。所以我现在遇到多个容器需要协作的场景第一反应是先 docker network create而不是手动查IP写死配置。你可以把自定义网络理解成容器之间的专用局域网容器名就是局域网里的主机名这样整个方案就非常好理解。4.3 MySQL数据持久化与密码配置的实操细节前面提到MySQL容器这里展开说说参数细节。正常运行一个MySQL 8.0容器我通常会这样写docker run -d --name mysql8-p 3306:3306-e MYSQL_ROOT_PASSWORDStrongPass123-e TZAsia/Shanghai-v mysql-data:/var/lib/mysql-v $(pwd)/conf:/etc/mysql/conf.d--restart unless-stoppedmysql:8.0这里关键点有四个。第一MYSQL_ROOT_PASSWORD是初始化数据库时设置root密码的环境变量只在数据目录为空、首次初始化时生效如果卷里已有数据改环境变量不会改密码。第二TZAsia/Shanghai是时区设置虽然容器底层默认是UTC但很多业务日志和函数依赖本地时间建议一开始就配好。第三数据卷挂到/var/lib/mysql日志打印到stdout你可以用docker logs看MySQL的启动日志。第四第二个-v是把宿主机当前目录下的conf目录挂到容器内的MySQL配置扩展目录想调字符集、慢查询日志等参数直接改宿主机配置文件再重启容器即可。还有一个容易忽略的点宿主机3306端口如果被其他MySQL占用了容器启动会失败。你可以换一个端口映射比如-p 3307:3306这样容器内MySQL还是监听3306宿主机用3307访问。连接信息里的端口要注意区分外部工具连的是宿主机的3307容器内部进程互访用3306初学者经常在这里踩坑。5. 多容器编排用Docker Compose把Nextcloud和Kodbox跑起来5.1 Compose文件的基本骨骼services、images、volumes、networks手动用docker run启动多个容器虽然在理解原理上很有帮助但一旦服务多起来命令会非常长而且容易漏参数。Docker Compose的价值就是把多容器的启动配置写在一个YAML文件里通过 docker compose up -d 一次性拉起所有服务。Compose文件的核心结构是services、volumes、networks三层。services下定义每个服务对应一个容器volumes声明命名卷networks声明自定义网络。一个最基本的Compose例子如下services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: myapp volumes: - db-data:/var/lib/mysql networks: - app-net app: image: nginx:alpine ports: - 8080:80 networks: - app-net volumes: db-data: networks: app-net:这个文件看起来不长但信息密度很高。image指定镜像environment传环境变量volumes挂载卷ports映射端口networks绑定网络。Compose会自动为同一个项目创建独立网络所有服务默认可以互相使用服务名访问所以你不需要手动创建网络。开发环境里还可以用 version字段但新版Compose已经不太要求显式指定版本了。刚开始写Compose时我建议先把单个服务跑通再加另一个每次修改后执行 docker compose config 校验文件格式没问题再 docker compose up -d 应用。不要一次性堆一大堆服务否则某个服务启动失败时排查成本会变大。实践下来Compose文件本身也是一种文档它能清楚表达服务之间的依赖关系比口头说明这个服务怎么启动靠谱得多。5.2 Nextcloud自带Apache为什么还要自己写端口和目录热词里有一条是容器化部署nextcloud自带apache很多人困惑Nextcloud官方镜像是不是自带Web服务器答案是自带的。官方镜像基于Apache和PHP镜像内已经装好了Apache你不需要额外跑一个nginx或httpd容器。你只要把镜像的80端口映射到宿主机并挂载数据目录就能访问Nextcloud的安装页。一个能用的Compose片段可以这样写services: nextcloud: image: nextcloud:apache ports: - 8081:80 volumes: - nextcloud-data:/var/www/html environment: - NEXTCLOUD_ADMIN_USERadmin - NEXTCLOUD_ADMIN_PASSWORDadminpass - MYSQL_HOSTdb - MYSQL_DATABASEnextcloud - MYSQL_USERnextcloud - MYSQL_PASSWORDsecret depends_on: - db db: image: mariadb:10.6 volumes: - db-data:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORDrootpass - MYSQL_DATABASEnextcloud - MYSQL_USERnextcloud - MYSQL_PASSWORDsecret volumes: nextcloud-data: db-data:注意这里我把Nextcloud的Web端口映射成了8081避免和本机80端口冲突。NEXTCLOUD_ADMIN_USER和NEXTCLOUD_ADMIN_PASSWORD是首次安装时自动创建管理员用的环境变量。Nextcloud不接数据库时默认会用SQLite但生产环境一般选MySQL或MariaDB所以在环境变量里指定了数据库连接信息。depends_on表示Nextcloud要在数据库服务启动后再启动但注意它只控制顺序不保证数据库已经可用必要时可以加健康检查。数据目录挂在/var/www/html下这个目录是Nextcloud应用代码和数据默认的根目录。挂载后应用文件、用户上传文件都会持久化在宿主机卷里。Nextcloud在后端需要定期执行cron任务这个在Compose里可以额外加一个cron容器但作为入门先不展开。能通过浏览器打开安装页并创建管理员就算是把这个经典网盘应用跑通了。