ARTICLE DETAIL

资讯详情

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

Docker实战入门:容器化、镜像与Compose部署避坑指南

Docker实战入门:容器化、镜像与Compose部署避坑指南 1. 先搞懂Docker是什么容器化技术的核心逻辑很多人在接触Docker的时候第一反应都是这不就是个轻量虚拟机吗。我第一次看Docker文档脑子里也是这么想的后来真正用起来才发现完全不是一回事。Docker提供的是一种操作系统级别的虚拟化手段——它共享宿主机的内核但对文件系统、网络、进程、用户权限做了隔离让每个应用都跑在自己的小盒子里这个盒子在Docker的世界里就叫容器。容器化技术其实不是什么新概念Linux上早就有了LXCLinux Containers但Docker真正把它变成了普通开发者也能顺手用的工具。它解决的是最古老也最折磨人的问题代码在本地跑得好好的一到同事电脑上就崩测试环境配了三天的环境变量生产环境一个都对不上。有了Docker之后整个应用连带它的运行环境一起被打包像搬家一样把卧室里的床、衣柜、窗帘原封不动塞进集装箱搬到哪间房子里都一样住。这套思路落到实际开发里价值极其明显。团队里来了新人不用花两天时间配Java、Node、MySQL、Redis一条命令拉镜像启动容器开发环境就有了。上线部署也不用再纠结服务器上缺了哪个依赖、系统版本差了多少Docker镜像里什么都有到哪都能跑出完全一致的行为。所以现在不管是Java后端、Python数据分析、前端跑自动化测试还是运维做微服务发布几乎都绕不开Docker。这篇文章适合谁看我建议所有准备进入后端开发、运维、DevOps方向的人都认真读一遍前端和算法同学学它也不亏毕竟现在连跑个Node脚本都可能被要求容器化。下文的内容会从基础概念讲起把镜像、容器、仓库这几个最核心的词解释透然后带你把Docker在Windows、Linux上装好最后把我踩过的坑和排查经验一并列出来。看到最后你会发现Docker入门真的不难难的是对那些概念的理解——概念一旦通了后面全是体力活。1.1 从装环境的痛苦说起在Docker普及之前我们部署一个Web应用大概是什么流程呢先买服务器再装操作系统然后装Nginx、装数据库、装运行时环境每一步都可能出幺蛾子。我记得有一次给客户部署一套Ruby写的系统客户服务器上的OpenSSL版本太老编译扩展直接报错我花了整整一个下午去升级依赖库最后还是没成功。这种痛苦的本质在于应用和环境之间是紧耦合的。环境和应用必须精确匹配但环境又会随着时间变化——系统补丁打了、别人装了个新库、yum升级了个包都可能让原本能跑的应用突然挂掉。容器化技术做的第一件事就是把应用和环境一起固化下来形成一个自包含的交付物。这个交付物不关心跑在哪台机器上只要那台机器有Docker就能把它原样运行起来。1.2 镜像与容器一次构建处处运行这是Docker里最基础、也最容易混淆的两个概念。镜像Image是一个只读模板你可以把它理解成安装光盘或虚拟机的快照文件。它里面包含了一个完整Linux文件系统的所有内容程序代码、依赖库、配置文件、环境变量连系统底层的/bin目录、/etc目录都在里面。容器Container则是镜像运行起来之后的实例它是活着的、可以被修改的。打个比方镜像是菜谱和食材的完美组合容器是按照这个菜谱实际做出来的一道菜。菜可以被吃掉、被回锅、被倒掉但菜谱本身永远不变。同一个镜像可以启动任意多个容器这些容器彼此独立互不影响——就像同一个菜谱能同时做出五份完全相同但各自独立的菜。有一点必须强调容器的可写层只在容器运行期间存在一旦容器被删除你在容器里做的所有修改都会丢失。很多新手在容器里装了个Vim改了配置文件容器删了之后发现全没了就是这个原因。所以生产环境里数据要么放在数据卷Volume里要么在镜像构建阶段就固化到镜像中绝不能依赖容器运行期间的手工修改。1.3 Docker仓库与Dockerfile镜像从哪里来镜像不会凭空出现它的来源有三个从仓库拉取、从Dockerfile构建、从已有容器导出。平时接触最多的是通过docker pull从镜像仓库拉取现成的镜像。镜像仓库最出名的就是Docker Hub上面有官方维护的MySQL、Redis、Nginx、Ubuntu等海量镜像基本你想要的开源软件都能直接拿来用。拉镜像的方式也很简单比如拉一个MySQL 8.0的镜像只需执行docker pull mysql:8.0tag用来指定版本。如果不写tag默认拉取最新版latest这个习惯我不推荐勘误一下生产环境一定要显式指定版本号否则哪天镜像作者更新了最新版你的行为就可能变得不可控。如果Docker Hub上没有现成镜像或者你需要定制自己的环境那就得写Dockerfile。Dockerfile就是一份构建镜像的配方它用一系列指令描述基础镜像选择、依赖安装、文件拷贝、启动命令。你不需要把整个环境手动配好只需要写好配方docker build这个命令就会自动帮你把镜像一层层构建出来。每个RUN指令都会产生一个只读层这既是Docker的精妙之处也是初学者经常搞不懂的地方——镜像是分层构建的这一特性让镜像复用变得异常高效两个不同的镜像完全可以共享底层的几条只读层。1.4 数据卷、网络、Compose容器化技术的完整拼图镜像和容器只是地基真正要把Docker用在实战中还有三个关键组件你必须了解。**数据卷Volume**解决的是容器持久化问题。容器一删全没但数据库的数据、日志文件显然不能跟着一起没。数据卷就是宿主机上的一个特殊目录挂载进容器之后容器读写这个目录的数据会直接落到宿主机上容器删了数据还在再启动一个新容器重新挂载同一个数据卷就能恢复。网络解决的是容器通信问题。默认情况下每个Docker容器都有自己的IP地址单机部署时容器间可以通过虚拟网络互相通信也可以把容器的端口映射到宿主机的一个端口比如把容器里的MySQL默认3306端口映射成宿主机的3307这样外部程序既不会和已有服务冲突又能正常访问数据库。Docker Compose则是用来编排多个容器的工具。一个现代应用少说也要数据库、缓存、后端三个容器用docker run一条条敲太痛苦。Compose允许你写一个YAML文件把服务定义、镜像版本、端口映射、数据卷、环境变量全部声明好一条docker compose up -d就能把整套环境起起来。后面我们会演示怎么用Compose一键搭一套MySQL加Redis的环境。2. 安装前的环境评估与方案选型Docker的安装是整个入门过程中劝退率最高的一环不是因为它难而是因为很多人一上来就走错了路。打开浏览器搜Docker安装教程结果五花八门有人让你装Docker Desktop有人让你用命令行装Docker Engine还有人让你在Windows下用老版本Docker Toolbox。其实他们都没说全关键要分清楚自己的操作系统和需求。先说结论Windows用户优先装Docker DesktopLinux服务器或开发机优先装Docker EnginemacOS用户同样建议Docker Desktop。但装Docker Desktop之前有一个隐形的硬性条件你必须先确认——你的CPU要支持虚拟化并且得在操作系统中把它打开。这个问题太常出现了下文会专门拿一节出来讲排查办法。2.1 Windows平台的正确姿势如果你用的是Windows 10或Windows 11现代主流的Docker安装方式只剩一个Docker Desktop配合WSL 2Windows Subsystem for Linux 2。为什么要扯上WSL 2因为Docker的容器本质上是Linux那边的技术需要Linux内核环境才能运行。WSL 2会给Windows提供一个轻量级的真实Linux内核Docker Desktop就借着这个内核在Windows上跑Linux容器。这比早期用Hyper-V虚拟机跑Docker容器的方案轻盈不少启动速度更快资源占用也更小。装Docker Desktop之前需要先做三件事打开Windows功能里的虚拟机平台Virtual Machine Platform打开适用于Linux的Windows子系统Windows Subsystem for Linux重启电脑然后在命令行执行wsl --set-default-version 2确认默认WSL版本是2这三步做完再去Docker官网下载Docker Desktop安装包接下来就是一路Next了。提示如果你在BIOS里没有开启CPU虚拟化Intel VT-x或AMD-V或者Windows功能没装全Docker Desktop启动时会弹窗报virtualization support not detected或者failed to start because virtualization support isnt detected之类的错误。我见过至少十个人卡在这步后面排查章节会给你完整清单。2.2 Linux与macOS的安装路径Linux上装Docker就简单粗暴多了。它没有GUI讲解面板就是一个后台守护进程Docker daemon加上一套命令行工具。官方提供了自动化脚本你在终端执行curl -fsSL https://get.docker.com -o get-docker.sh再运行sh get-docker.sh它会自动检测发行版、配置仓库、安装Docker Engine并设置开机自启。手动安装也完全不复杂。以Ubuntu/Debian系为例核心步骤就三步更新apt索引、安装必要的依赖包、配置Docker官方的软件仓库然后apt install docker-ce docker-ce-cli containerd.io。CentOS/RHEL系则是用dnf/yum安装docker-ce。说白了不同发行版的差异仅限于包管理器命令安装逻辑是完全一致的。macOS用户装Docker Desktop比Windows还省事因为MacOS自带Hypervisor frameworkDocker Desktop可以直接用不需要额外开虚拟化开关。唯一要留意的是苹果芯片Apple Silicon和老款Intel芯片的Mac下载的安装包不同Intel Mac要选x86_64版M1/M2/M3系列要选Arm版选错了装不上。2.3 硬件检查与虚拟化验证很多Windows用户卡在Docker Desktop启动失败这道坎上其实根源在虚拟化没开。这里给你一个完整的检查顺序Windows自带任务管理器里性能标签页看CPU右下角有没有虚拟化已启用。如果显示已启用说明BIOS层面没问题如果显示已禁用你得进BIOS把它打开不同品牌主板进入BIOS的快捷键不一样常见的有F2、Del找到Intel Virtualization TechnologyIntel平台或SVM ModeAMD平台把它设为Enabled保存退出重装系统后忘开虚拟化的情况很常见特别是从旧机器迁过来的配置BIOS开了虚拟化还不够Windows还需要把Hyper-V相关组件打开。在控制面板-程序和功能-启用或关闭Windows功能里勾选Hyper-V和虚拟机平台或者直接用命令bcdedit /set hypervisorlaunchtype auto。做完之后重启就稳了。macOS平台几乎不用操心这些Linux平台的Docker Engine直接跑在原生内核上压根不需要额外的虚拟化层所以这三种平台的安装节奏完全不一样这也是为什么你搜攻略时会看到各种互相矛盾的教程——他们大概率是在讲不同平台。3. 安装配置全过程实录到这一步环境评估做完了接下来就是动手装。我会把Windows和Linux两条路线都说清楚以Windows Docker Desktop为主因为大部分入门读者用的是Windows。3.1 Windows安装Docker Desktop逐步操作去Docker官网下载Docker Desktop安装包下载完成后双击运行。安装界面会有两个勾选项Use WSL 2 instead of Hyper-V——建议勾上这是Windows下最推荐的运行方式Add shortcut to desktop——看你个人喜好安装过程一般两三分钟装完后系统会提示重启重启后打开Docker Desktop它会自动启动WSL 2后端。首次启动可能会让你接受服务条款还会提示登录Docker账号可以直接跳过登录。启动成功之后任务栏的Docker图标是稳定的鲸鱼标志然后打开命令行PowerShell或CMD都行输入docker version如果能看到Client和Server两段信息说明Docker已经跑起来了。如果你只看到Client段没有Server段那就是daemon没启动多半是WSL 2或虚拟化那边的问题跳到最后排查章节对照处理。有个小细节值得注意Docker Desktop启动时消耗的内存和CPU不少Windows上默认是2GB内存起步。如果你的电脑配置不高可以在Docker Desktop的Settings里调低Resources的分配实测下来开发场景1.5GB也能流畅用。3.2 Linux安装Docker Engine并配置开机自启Linux的安装过程我用Ubuntu 22.04举例。登录服务器后第一件事是更新索引sudo apt update然后安装依赖包sudo apt install -y ca-certificates curl gnupg lsb-release接着添加Docker官方GPG密钥和软件源这两步是为了让apt能识别和下载Docker官方仓库的软件sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null然后更新索引并安装sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io装完之后验证一下状态sudo systemctl status docker sudo docker version如果一切正常设置开机自启sudo systemctl enable docker sudo systemctl start dockerLinux下还有个额外操作我不建议省略把当前用户加到docker组里不然每次执行docker命令都要加sudo太烦。sudo usermod -aG docker $USER改完组之后记得重新登录或者执行newgrp docker让组权限生效。3.3 镜像加速配置与基础设置装好Docker第一个头疼的问题往往不是用法而是拉镜像的速度太慢或者直接超时。这不是因为你网络不好而是Docker Hub官方仓库在国外直连的体验通常不理想。解决办法是给Docker配置镜像加速器registry mirror。原理很简单Docker拉镜像时先去镜像加速器拉加速器上可以缓存最常用的镜像大幅缩短拉取时间。这个配置在产品上完全合规Docker官方就支持这个机制它只是帮你把镜像从公开仓库搬到离你更近的节点而已。在Docker Desktop的Settings → Docker Engine里有一个JSON配置文件追加一行{ registry-mirrors: [https://你的加速地址.example.com] }Linux上则是编辑/etc/docker/daemon.json没有就新建一个然后重启Dockersudo systemctl restart docker这里我不推荐具体的第三方加速地址因为这类服务的稳定性随时可能变化。更可靠的做法是在你自己能正常访问Docker Hub的镜像时直接用官方源当你觉得慢时就去查一下当前可用的公共镜像加速服务选一个填入即可。配置完加速之后拉镜像速度会快好几倍亲测从等一分钟超时变成十几秒完成。注意registry-mirrors配置只影响docker pull拉镜像时的下载来源不影响你运行容器时的网络转发。两者是两回事别混为一谈。3.4 验证安装跑起第一个容器安装配置做完了用最经典的hello-world容器验证一下成果docker run hello-world这条命令的执行过程其实包含了好几个环节首先Docker会去本地仓库找有没有hello-world镜像没有就去远程仓库拉取拉完再基于这个镜像创建一个容器并运行它。hello-world容器运行完就退出并输出一段欢迎信息。看到这段欢迎信息你的Docker安装就算完整验证成功了。但我想多说一句hello-world这套验证太浅它只证明了Docker能跑起来证明不了你对Docker的理解。更好的做法是直接跑一个真正的服务容器比如docker run -d --name test-nginx -p 8080:80 nginx:latest这条命令会下载Nginx镜像在后台启动一个Nginx容器并把它里面的80端口映射到宿主机的8080端口。启动成功后打开浏览器访问http://localhost:8080如果你能看到Nginx的欢迎页面说明镜像拉取、容器运行、端口映射、网络互通这整条链路全部打通了。这一步做完才算真正踏入容器化的大门。4. 新手必踩的坑与排查实录Docker本身不算难难的是遇到问题不知道怎么排查。把你最可能碰到的问题列个清单我一个一个跟你拆。4.1 Docker Desktop无法启动虚拟化支持检测不到这是Windows用户报错率最高的一句话Docker Desktop failed to start because virtualization support wasnt detected翻译成中文大概就是启动失败未检测到虚拟化支持。这句话出现的原因就两类硬件虚拟化在BIOS里没开或者Windows功能没配好。排查步骤按顺序走打开任务管理器 → 性能 → CPU看虚拟化那栏是不是已启用。如果是已禁用先去BIOS开Intel VT-x或AMD SVM如果显示已启用还是报错去启用或关闭Windows功能里把虚拟机平台和适用于Linux的Windows子系统都勾上重启电脑命令行执行wsl --status确认WSL版本如果显示的是1执行wsl --set-default-version 2如果以上都正常执行bcdedit /set hypervisorlaunchtype auto然后重启排除了这四个选项之后再打开Docker Desktop99%能解决。剩下1%可能是你装了第三方安全软件拦截了虚拟化把Docker和相关WSL进程加入白名单再试。4.2 镜像下载慢、拉取超时镜像拉取超时是所有人都绕不过去的一道坎解决思路我在3.3节已经给了——配置registry mirror。这里补充一个排查技巧拉镜像失败时先用docker pull看具体的报错信息。如果是connection refused或I/O timeout基本是网络层面问题配置加速就能解决。另外容器启动后如果Nginx等服务无法访问不要急着怀疑Docker先检查端口映射有没有冲突。docker ps看端口映射是否生效netstat -ano | findstr 你的端口号看端口是否被占用。我遇到过一次Tomcat容器起不来日志里全是端口占用查了半天才发现是宿主机上本来就跑着一个Tomcat把8080占掉了。解决办法就是换个映射端口比如-p 8081:8080。4.3 容器网络不通容器间互相访问失败默认网络模式下容器和宿主机之间、容器和容器之间的通信是有点讲究的。新手最容易犯的错是把端口映射混同于内部网络。比如你在容器A里跑了一个MySQL监听3306端口想从容器B里通过localhost:3306访问它这肯定不通——因为每个容器自己的localhost指向的是它自己。容器之间要通信应该使用Docker网络里的容器名称作为hostname或者用宿主机IP加映射端口。解决容器间通信最优雅的方式是用Docker Compose。它会在项目内自动创建一个网络所有服务在这个网络里都能通过服务名互相访问。举个例子version: 3.8 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 ports: - 3306:3306 redis: image: redis:7 ports: - 6379:6379在这个Compose文件里其他服务要连MySQL直接连mysql:3306就通了连IP都不用查。这种声明式描述让容器编排变得极度直观也是Docker Compose那么受欢迎的原因。4.4 磁盘空间被容器和镜像吃满Docker用久了之后你会发现磁盘空间一个劲往下掉。原因很简单你拉的每个镜像、启动的每个容器、创建的每个数据卷都会在宿主机上占空间。特别是频繁构建镜像之后那些无用的中间层、悬挂镜像dangling images指没有被任何容器引用也没有tag标记的镜像加起来体积非常可观。日常维护用这三条命令就够了docker system df这条命令能显示Docker占用的磁盘概况——镜像占了多少、容器占了多少、数据卷占了多少、构建缓存占了多少。看完再决定清理什么。docker system prune -a --volumes这条是大扫除会删除所有未被运行中容器引用的镜像、停止的容器、网络和数据卷。注意-a会把你可能还想用的非latest镜像也删掉所以执行前想清楚或者只用不带-a的docker system prune做个轻量清理。如果你只想单独清某类资源也有对应命令docker image prune清悬挂镜像docker container prune清停止的容器docker volume prune清无主的数据卷。建议每个季度做一次清理我自己的服务器上每次清理都能释放几十GB的空间。4.5 常见问题速查表报错/现象可能原因解决方向Docker Desktop启动失败virtualization support not detectedBIOS未开虚拟化 / Windows功能未开启检查BIOS的VT-x或SVM开启虚拟机平台和WSL重启docker命令要sudo才能用当前用户不在docker组执行sudo usermod -aG docker $USER重新登录docker pull超时官方仓库不稳定配置registry-mirrors加速器或用已有镜像缓存容器起来了但浏览器访问不了端口映射没生效 / 宿主机端口被占用用docker ps检查端口映射netstat排查端口占用容器内环境变量没生效环境变量传入方式不对用-e KEYVALUE或在Compose的environment里声明容器一删数据全没没有挂载数据卷启动时加-v参数或Compose里配置volumesWSL 2占用内存过大WSL默认使用主机50%内存在%UserProfile%\.wslconfig里配置memory4GB限制镜像名带none悬挂镜像太多构建过程被中断或镜像升级执行docker image prune清理悬挂镜像5. 基于Docker的一键搭建MySQL 8.0实例概念和安装都过了一遍接下来用实际案例固化学到的东西。这里我带你把最常用的中间件MySQL装在Docker里整个过程你至少能复习到镜像拉取、容器运行、数据卷、端口映射、日志查看这五个关键知识点。5.1 用docker run快速启动MySQL 8.0容器单容器启动MySQL是最直接的用法。执行下面的命令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123 \ -v mysql_data:/var/lib/mysql \ mysql:8.0拆解一下参数的含义-d后台运行不占用终端--name mysql8给容器起个名字之后所有操作都用这个名字引用它-p 3306:3306宿主机3306映射容器3306外部程序通过宿主机IP加3306访问-e MYSQL_ROOT_PASSWORDroot123传入环境变量设置MySQL的root密码。这是一个明文风险点真实生产环境建议用MYSQL_ROOT_PASSWORD_FILE配合配置文件或密钥管理-v mysql_data:/var/lib/mysql把Docker管理的匿名数据卷挂载到容器内MySQL数据目录容器即使删了数据也还在执行完之后用docker ps看容器状态出现Up字样说明已经在运行了。再用docker exec -it mysql8 mysql -p进入MySQL命令行输入密码就能操作数据库。5.2 用Docker Compose管理多服务环境单容器能跑是因为MySQL本身依赖少但真实项目里往往一个后端要同时连MySQL和Redis这时用docker run写一堆命令就太傻了。来看Compose写法的实际效果。新建一个项目目录myapp在里面创建docker-compose.yml文件version: 3.8 services: mysql: image: mysql:8.0 container_name: myapp-mysql restart: always environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: myapp MYSQL_USER: myapp_user MYSQL_PASSWORD: myapp_pass ports: - 3306:3306 volumes: - mysql_data:/var/lib/mysql redis: image: redis:7 container_name: myapp-redis restart: always ports: - 6379:6379 volumes: - redis_data:/data volumes: mysql_data: redis_data:在终端进入项目目录执行docker compose up -d。Docker会拉取mysql:8.0和redis:7两个镜像创建两个容器并搭建一个内部网络。MySQL容器里会自动创建一个myapp数据库和对应的用户后端程序连接时host写myapp-mysql或myapp-mysql对应的容器IP都能通Redis连接host写myapp-redis即可。这种基础设施即代码的思维方式我强烈建议你再忙也要上手试一遍。它把你记忆中所有的环境配置、启动参数、网络关系都沉淀成一份可版本管理的文件比零散的shell命令靠谱得多。后续项目改端口、换版本只需要编辑YAML文件再执行一次docker compose up -d整个环境的变更就完成了这在没有Docker之前是不可想象的效率提升。6. 一些经验之谈Docker学习路线的个人建议前面讲了这么多概念和操作最后聊聊我自己的心得体会算是给刚入门的人指一条相对顺畅的路。第一不要一开始就沉迷于背命令。docker run的参数有一二十个靠背根本记不住。更好的方式是先理解每个参数解决什么问题——-p解决外部访问问题-v解决数据持久化问题-e解决配置注入问题。理解了问题参数自然就记住了。忘了具体写法也没关系docker run --help随时可以看但知道我需要解决什么问题这件事命令本身不会告诉你。第二刻意练习容器思维。容器思维其实就是问自己三个问题这个进程需要什么运行环境它的数据存在哪里才能不丢它要被谁访问、通过什么端口把这三个问题盘清楚再复杂的服务部署也能拆成一段清晰明了的docker run或Compose配置。第三慢慢从用Docker过渡到读Dockerfile。我在5.1节里给你举例的是用现成镜像跑容器但真实项目里更要紧的是自己去写Dockerfile。因为真正能提升工程效率的定制化环境还得靠自定义镜像来固化。你可以从最简单的把Node.js应用容器化开始练手写一个三行的Dockerfile构建一次之后那种这么复杂的环境也能被固化成一个小文件的成就感是光跑现成镜像体会不到的。第四Docker Compose一定要尽早学习不要等需要了才学。很多初学者在单容器上折腾了一个月都不碰Compose觉得一条命令能跑起来的事用什么编排工具。但等你接触到微服务、自动化测试、多环境部署你会发现Compose不是可选方案而是基础技能。说实话Docker这套东西的门槛不在技术上而在理解方式的转变上。你以前伺候的是服务器上的操作系统现在伺候的是一个个集装箱。集装箱里面是什么对你并不重要重要的是你如何快速造箱、搬箱、开箱、回收箱。想通这一层后面Kubernetes、服务网格那些更复杂的容器编排技术学起来都会轻松不少。
返回列表