ARTICLE DETAIL

资讯详情

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

从零上手Docker:镜像容器原理、安装避坑与Compose编排实战

从零上手Docker:镜像容器原理、安装避坑与Compose编排实战 你说要不要学Docker我的建议是别问直接装一个上手玩两天。这工具刚进新手村的时候确实挺劝退的尤其是Windows上装Docker Desktop一大半人卡在Virtualization support not detected这个报错上连第一个容器都没跑起来就放弃了。我见过太多同事因为这个直接对Docker产生心理阴影一说容器就摇头。这篇我打算换个讲法不空谈概念就按我自己从零折腾Docker的经历来写。目标是让你看完之后能顺畅跑通几个真实场景装个nginx玩一下、把MySQL 8.0和Redis摆上桌面、遇到网络不通和权限报错知道往哪查。里面会穿插一些我踩过的坑和现在还在用的习惯新手照着做就行老手也可以看看里面有没有漏掉的小技巧。1. Docker到底是什么先建立一个不绕晕的心智模型1.1 你其实早就需要它了先聊一个所有开发都逃不掉的痛环境不一致。你今天在本地把项目跑得好好的代码一交到测试那边就崩或者服务器上新装了一套服务从头配环境花掉一下午。原因通常是OS版本不一样、依赖库版本有出入、某个配置文件没同步。Docker解决的就是这种在我机器上是好的问题。它的思路很简单粗暴把你运行这个服务需要的所有东西——操作系统之上的运行时、依赖、代码、环境变量、配置文件——打成一个人为构造的包叫镜像。然后这个包在任何装了Docker的机器上都能以同样的方式启动。镜像是什么跑起来之后就是什么不因为你换了一台机器就变味。我自己的感受是第一次真正体会到Docker的价值不是跑通hello world的时候而是我把自己本地一套环境全部改成docker compose启动之后。重装了一次系统一条docker compose up -dMySQL、Redis、后端服务全部恢复十分钟搞定。那种感觉用过一次就回不去了。1.2 镜像、容器、仓库三个概念一次说清新手最容易搞混的除了容器和虚拟机就是这三个词了。其实可以拿安卓手机的套路来类比镜像Image就像一个手机ROM包它是只读的安装包里面有完整的文件系统和默认配置。容器Container是你用这个ROM包刷到手机里、真正开机运行起来的系统它可以被启动、停止、删除。仓库Registry则是存放这些ROM包的服务器最出名的就是Docker Hub你在那里下载别人做好的包也可以把自己做的传上去。用代码来类比更简单镜像好比类容器就是这个类new出来的实例。类定义静态的结构实例才是运行中的、有自己状态的实体。所以当你听到跑一个容器实际意思是用某个镜像实例化出一个可运行环境。1.3 镜像得分层看为什么常见镜像拉起来没那么慢镜像不是一团巨大的黑盒文件它是分层存储的。一个Dockerfile里每一行指令都会产生一个新层基础系统一层装依赖一层拷贝代码一层。关键在于层可以被多个镜像复用如果你本地已经有Ubuntu系统层那么拉一份ubuntu nginx的镜像时系统层就能直接复用只需要传多出来的那几层。所以你会看到一种情况第一次拉mysql:8.0很慢但拉mysql:8.0.33的时候可能只花了很短时间因为大部分底层都是一样的只需要补版本差异的那几层。多镜像共享层的设计也解释了为什么大家总推荐基于同一个基础镜像去构建存着存着你本地的缓存仓库里公共层越来越多。2. 环境搭建是第一道坎三种平台的安装差异与Virtualization报错排查2.1 Windows装Docker Desktop为什么老卡在Virtualization support not detected这是热搜里出现率极高的一个词。Docker Desktop在Windows上跑容器靠的不是模拟器而是利用Windows自带的虚拟化能力——具体说就是WSL2或者Hyper-V。如果系统检测不到虚拟化支持Docker Desktop在启动阶段就会直接罢工报Virtualization support not detected。排查链路我整理一下按顺序走打开任务管理器切到性能选项卡点CPU一栏看右下角有没有虚拟化已启用。如果显示已禁用说明BIOS/UEFI里没打开CPU的虚拟化指令集。重启电脑进BIOS找到Intel VT-xIntel机器或AMD-VAMD机器把它设为Enabled。不同主板菜单位置不同但关键词基本就是Virtualization或SVM Mode。确认Windows功能里该开的东西开了进入控制面板-程序-启用或关闭Windows功能把虚拟机平台和适用于Linux的Windows子系统WSL两个选项勾上。以管理员身份跑一下wsl --update把WSL组件升级到最新版本然后重启一次机器。这里有个容易误导人的点任务管理器里看到虚拟化已启用不代表Docker Desktop就一定没问题。Docker Desktop的Linux容器后端要的是WSL2的虚拟化平台WSL2依赖Windows的虚拟机平台功能。这两个开关拆开看很多人只开了Hyper-V没开虚拟机平台也能导致报错或者性能异常。2.2 装好之后启动失败failed to connect to the docker api这类错误怎么判断热搜词里还有一个常见报错failed to connect to the docker api at npipe:////./pipe/dockerdesktoplinuxen。这行英文对新手很不友好但翻译过来就一句话Docker引擎还没有正常启动起来。我说说排查思路。先看两件事第一Docker Desktop主界面右下角的鲸鱼图标是不是转圈或者显示Engine stopped第二用管理员开一个PowerShell执行docker version如果client能输出版本但server连不上说明引擎没起。常见的解决办法按顺序试右键Docker Desktop图标选择Restart等鲸鱼图标变绿。关闭Docker Desktop确认WSL运行正常wsl --status。如果WSL状态异常重新wsl --update。在Docker Desktop设置里把引擎从WSL2后端切换到Hyper-V后端或者反过来有一些机器上WSL2的上游连接有问题换到Hyper-V反而稳定。如果还不行看一下是否需要把C盘里的Docker数据迁移到其他盘盘满了引擎也可能起不来。2.3 Linux安装DockerUbuntu、CentOS、Kali其实是一条路Linux装Docker比Windows省心很多它不依赖任何桌面虚拟化层直接用系统内核的容器能力。以Ubuntu/Debian系为例最省事的安装方式sudo apt update sudo apt install docker.io sudo systemctl enable --now docker装完先别急着用还有个权限问题。默认情况下普通用户直接敲docker命令会看到Got permission denied while trying to connect to the Docker daemon因为docker.sock这个Unix套接字的权限默认只属于root和docker组的成员。解决方式sudo usermod -aG docker $USER然后退出终端重新登录让组权限生效。Kali上搭DVWA靶场或者CentOS上升级Docker CE思路也是一样的区别在于各自用了apt或者yum的包管理方式。CentOS 7这类老系统如果默认仓库里的docker版本太旧建议先卸载旧的docker、删除旧的包再通过官方yum源安装docker-ce。2.4 不同平台怎么选后端Docker Desktop在Windows和macOS上是一套图形界面里面嵌了Linux虚拟机。Linux服务器上通常直接安装Docker Engine。选择原则很简单你的日常开发机器如果是Windows/Mac用Docker Desktop最省事如果是远程Linux服务器直接装Engine如果公司内网有特定安全要求再考虑加配置。3. 镜像加速与镜像管理把pull慢这件事彻底解决掉3.1 下载慢的根源与registry-mirrors配置很多新手第一次跑docker run nginx然后看着进度条一动不动第一反应是这工具也太难用了。其实这不怪Docker问题出在网络链路上。Docker默认从Docker Hub拉镜像而默认的Docker Hub访问链路对国内网络环境并不友好。解决思路是用国内可达、且定期维护的镜像加速器通过Docker的registry-mirrors配置让拉取请求先经过加速器。配置方式和平台有关Docker Desktop用户打开Settings找到Docker Engine选项在JSON配置里加registry-mirrors字段{ registry-mirrors: [ https://docker.m.daocloud.io ] }点击Apply并Restart之后生效。Linux用户编辑/etc/docker/daemon.json没有这个文件就新建一个内容同上然后执行sudo systemctl restart docker这里我必须额外说一句网上流传的加速地址很多是一段时间后就失效的包括我上面写的这个示例也不保证永远可用。正确做法是打开浏览器搜索docker镜像加速 当前可用找到还在维护的公共加速地址列表自己挑几个测试docker pull docker.io/library/nginx:alpine的耗时。配置是死的地址是活的别指望一次配置永久有效。3.2 镜像的tag选择版本号、slim、alpine怎么看镜像的tag就是版本标记。新手常见操作是直接写个mysql或者nginx就拉比如docker pull mysql这样默认拿的是latest标签。为了可复现性和兼容性我建议养成带tag的坏习惯给改掉mysql:8.0比mysql:latest更明确避免哪天latest指向大版本升级后行为变了。alpine是简化版镜像基于Alpine Linux体积很小但里面可能没有glibc个别依赖编译的程序跑不了。slim是官方镜像的精简变体比完整版小不少比alpine保留的兼容性更好绝大部分场景够用。3.3 镜像管理的三个命令docker images查看本地已有的镜像列表能看到REPOSITORY、TAG、IMAGE ID、SIZE。docker rmi 镜像名或镜像ID删除本地镜像。注意如果这个镜像还有被容器使用直接删会报错得先删除相关容器再删镜像。docker system prune一键清理没有被容器使用的镜像、网络、构建缓存。我刚用Docker那阵子经常把镜像删错后来才养成先docker ps -a看一眼再清理的习惯。4. 一个nginx实例吃透最常用的Docker命令4.1 第一条命令docker run我建议第一个实验对象用nginx因为它启动快还自带一个能直接访问的网页反馈感强。执行docker run -d -p 8080:80 --name nginx-demo nginx:alpine解释一下这几个参数-d是后台运行-p 8080:80意思是把容器的80端口映射到宿主机的8080端口--name就是给容器取个名字nginx:alpine就是刚才提到的精简镜像。跑完之后打开浏览器访问http://localhost:8080看到Welcome to nginx!页面你就完成了人生中第一个真实容器部署。有人会问为什么容器里80不变而我访问8080因为容器内部服务固定监听80端口你要通过宿主机的8080进去Docker负责把8080的流量转发给容器里的80。这种端口映射是容器与外部通信最常用的一招。4.2 docker ps与docker logsdocker ps显示正在运行的容器docker ps -a连已经退出的容器也算。对于容器排错docker logs是命根子docker logs -f nginx-demo-f是follow的意思能看到实时日志。刚才nginx如果页面起不来第一反应应该是看日志而不是瞎猜。4.3 进容器看看docker exec进入容器内部分两布docker exec -it nginx-demo /bin/sh-it是交互模式加伪终端容器里要执行shell指令就用这个组合。进去之后你能看到root容器ID开头的提示符这时你可以cat配置文件、改文件、看目录结构。需要注意的是容器内部默认是精简环境很多命令不在了。nginx:alpine里没有bash只有sh没有vim只有vi或者直接使用sed来改。这种精简哲学我一开始很不习惯后来才懂得这是够用就好的思路。4.4 改了什么、删了就没了理解容器的临时性进去改了nginx默认页面的内容之后如果你执行docker rm nginx-demo再重新run一个nginx之前改的东西会全部消失因为容器创建时基于镜像容器删除时运行时产生的可写层一并消失。这个特性特别容易让新手误以为我改好的环境应该保存下来了。要保存修改正确的思路是把配置变成新的镜像docker commit但不推荐或者用数据卷挂载下一章细说。记住这句容器是临时的镜像和数据卷才是持久的。4.5 停止、删除、清理docker stop nginx-demo docker rm nginx-demo docker rmi nginx:alpinestop是优雅停止等容器内进程收尾kill是强制杀掉rm删容器rmi删镜像。清理时如果报container is using image之类说明还有容器引用这个镜像先把容器删掉再删镜像。还有一个挺有用的习惯临时实验的容器可以加--rm参数容器停止后自动删除省得积累一堆僵尸容器。5. 数据卷与网络容器不丢数据、机器之间能通信5.1 为什么数据必须持久化上一章说到容器删了数据就没了那有状态的服务MySQL、Redis这类怎么搞答案是数据卷。数据卷有两种主流用法第一种是绑定挂载bind mount直接把宿主机的目录挂到容器里docker run -d -p 3306:3306 \ -v /opt/mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORDyourpassword \ mysql:8.0这样MySQL的数据文件直接写在宿主机的/opt/mysql-data目录下容器删了数据还在。第二种是命名卷named volumedocker volume create mysql-data docker run -d -v mysql-data:/var/lib/mysql mysql:8.0命名卷由Docker自己在宿主机管理好处是路径不用记坏的联想是找文件麻烦一点。我个人的习惯需要频繁手动备份或直接用工具查看数据的服务用绑定挂载纯粹跑服务的容器用命名卷。5.2 三种网络模式和自定义网络Docker网络大概是初学者的第二个劝退点。核心网络模式就三种bridge默认容器通过虚拟网桥相互通信可以配置端口映射让外部访问。host容器直接使用宿主机网络栈没有独立IP端口和宿主机关联。none没有网络容器只能本地访问。服务器场景里bridge模式最常见容器在同一个自定义网络里可以通过容器名互相访问比记IP方便。创建自定义网络docker network create my-net启动容器时加--network my-net就能加入。同一个网络里的容器之间直接用容器名当主机名访问比如MySQL容器名叫db后端服务访问db:3306即可。5.3 docker网络不通的完整排查链热搜词里docker网络不通出现的频率不低我总结一套排查优先级先确认容器在跑docker ps如果容器已经退出看日志docker logs 容器名很多“网络不通”其实是程序根本没起来。看端口映射对不对docker port 容器名如果输出是0.0.0.0:8080-80/tcp说明映射正确继续下一步。本机直连验证curl http://localhost:8080如果通说明Docker本身没问题问题在客户端或更外层。宿主机防火墙排查Linux上看看firewalld或ufw有没有放行8080端口Windows检查防火墙是否拦截了端口的入站连接。容器访问外网不通可能是iptables NAT冲突或者DNS问题常见解法是重启docker服务和用--dns参数指定公共DNS。容器之间不通确认它们在同一个自定义网络里不是默认的互相隔离状态。大多数情况走到第3步就能定位问题剩下的就是硬件防火墙或者安全组策略的锅。6. 用Docker Compose编排MySQL 8.0与Redis主从搭建一套能用的开发环境6.1 为什么单跑容器不够用一个正经开发环境永远不止一个容器后端、数据库、缓存、队列可能各占一个。一个一个docker run的话参数越写越长还容易漏。Docker Compose就是解决这个问题的——用一份YAML文件描述整套服务的架构一条命令启动全部。抛开“编排工具”这种高大上的词Compose就是写一份服务清单Docker照着清单把活干完。6.2 一份可直接抄的docker-compose.yml下面这套配置覆盖了热搜里两个高频场景MySQL 8.0安装使用、Redis主从搭建。你可以直接复制成docker-compose.yml文件在空目录里执行docker compose up -d。version: 3.8 services: mysql: image: mysql:8.0 container_name: dev-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: app MYSQL_USER: app_user MYSQL_PASSWORD: app_pass ports: - 3306:3306 volumes: - mysql-data:/var/lib/mysql networks: - dev-net redis-master: image: redis:7 container_name: dev-redis-master restart: unless-stopped command: redis-server --requirepass redispass ports: - 6379:6379 networks: - dev-net redis-slave: image: redis:7 container_name: dev-redis-slave restart: unless-stopped command: redis-server --slaveof redis-master 6379 --masterauth redispass depends_on: - redis-master networks: - dev-net volumes: mysql-data: networks: dev-net:逐段解释mysql服务映射3306端口通过环境变量初始化root密码并创建库和用户数据写到命名卷mysql-data即使整套Compose停止再启动数据不丢。redis-master设置requirepass密码redis-slave通过--slaveof参数指定主节点因为我这里直接用了命令参数简单省事生产环境更推荐挂载配置文件。depends_on保证主节点先启动但这只控制创建顺序不保证主节点已就绪这个我在下面会再提醒。在这份配置里所有服务都接入了同一个dev-net网络所以redis-slave能通过redis-master这个服务名找到主节点这是Compose项目里基于服务名的DNS解析规则。启动之后执行docker compose up -d docker compose ps docker compose logs -f mysql docker compose exec mysql mysql -uapp_user -papp_pass app最后一条命令直接进到MySQL容器内执行SQL等效于你在宿主机上装了mysql客户端。整条链路不用在宿主机上安装任何数据库软件。6.3 compose常用命令和down的坑docker compose up -d后台启动全部服务。docker compose ps列出当前项目服务状态。docker compose logs -f只看某个或全部服务的日志。docker compose exec 服务名 命令进入某个服务内部执行命令。docker compose down停止并删除所有容器和网络。有个特别容易踩的坑docker compose down不会删除命名卷但down -v会删卷数据全没了。我一个同事就是执行down -v清理环境把本地开发库全清了所以我现在对带-v参数的清理命令心有余悸。另外一个细节如果你改了docker-compose.yml里的环境变量或端口映射执行docker compose up -d就能增量应用变更不用先down再up。但如果是镜像版本或数据卷配置大改还是建议down之后up避免状态不一致。6.4 管理类工具的容器化部署思路有了Compose经验之后你会发现在宿主机上安装软件这件事越来越没必要。比如热搜里的“青龙面板”这类带Web管理界面的定时任务工具本质也就是一个容器挂一个数据卷、映射一个端口、设好网络跑起来即可。常见的安装指令模式是docker run -d -p 5700:5700 -v 路径:/ql/data --name qinglong 镜像名。要不要上Compose取决于你是否经常改配置除非是生产级应用否则单容器docker run就够用。这类工具的优势在于宿主机的系统环境再乱容器里都干净如初换一台机器拉镜像、挂卷、映射端口服务原样恢复。7. 用了一段时间之后我想让你记住的几个习惯最后聊几个我踩过坑之后留下的建议不一定成体系但都实用。第一个习惯别用latest要么锁版本号要么锁镜像摘要。锁定版本最直观比如mysql:8.0、redis:7。生产环境更极端的做法是直接锁digest就是Image ID保证镜像内容与预期完全一致。第二个习惯常在临时实验容器的run命令上加--rm。比如docker run -d --rm -p 8080:80 nginx:alpine退出即回收不残留。第三个习惯出问题先看日志别先重启。我在很长一段时间里养成“docker restart一下”的坏习惯后来发现很多问题重启只能掩盖docker logs能直接暴露真正的堆栈信息。顺序应该是docker ps -a看状态docker logs看日志想清楚原因再动手。第四个习惯所有数据卷起名要看得懂。命名卷别只写data得写mysql-data、redis-data这种可识别的名字。时间一久你会感谢自己当初的命名。我个人体会最深的还是那句话容器是临时资源镜像和数据卷是长期资产。你抱着这个心态用Docker遇到大部分问题都不会慌。这篇就先写到这如果你按着步骤把nginx、MySQL和Redis都跑起来Docker对你来说就不再是新概念了。
返回列表