Docker 入门:从"能跑"到"跑得明白"
Docker 入门:从"能跑"到"跑得明白"
第一次听说 Docker 的人,常常会被一堆名词绕晕:镜像、容器、仓库、Dockerfile、Compose……其实 Docker 的核心思想一句话就能讲清:把应用和它运行所需的环境打包在一起,走到哪里都能以同样的方式跑起来。这篇文章从最基础的概念讲起,带你走完安装验证、第一个容器、自己写镜像、数据持久化这几步,帮你把 Docker 从“听说过”变成“会用”。
一、为什么需要 Docker
没有容器之前,部署应用最常见的尴尬是“在我机器上能跑”。开发机装的是 Python 3.11,服务器上却是 3.9;本地依赖装了一大堆,换台机器就缺这个少那个;两个人开发环境不一致,联调时问题层出不穷。
Docker 解决这个问题的办法是打包:应用代码、运行环境、系统工具、依赖库、配置文件,一起打进一个叫“镜像”的包里。镜像在任何装了 Docker 的机器上跑起来,里面的环境都是一样的。开发、测试、生产用的是同一个镜像,环境差异自然就少了很多。
容器和虚拟机常常被拿来比较,区别可以这样记:虚拟机是连操作系统一起虚拟,笨重但隔离彻底;容器是共用宿主机的操作系统内核,只隔离应用运行所需的进程、文件系统和网络,启动快、占用资源少。日常开发和小服务部署,容器通常是更轻量的选择。
二、三个核心概念:镜像、容器、仓库
1. 镜像(Image)
镜像是只读的模板,相当于应用的“安装包”。一个镜像由一层一层的文件叠加而成,每一层对应构建时的一条指令。镜像可以从别人做好的基础镜像开始,比如官方的 Python、Node、Nginx 镜像,在上面加自己的代码和配置。
2. 容器(Container)
容器是镜像跑起来的实例,相当于“安装好的程序正在运行”。同一个镜像可以启动多个容器,容器之间相互隔离,互不影响。容器被删掉之后,容器里产生的数据默认也会跟着消失——这一点后面讲数据持久化时还会提到。
3. 仓库(Registry)
仓库是存放镜像的地方,类似代码的远程仓库。最常用的是 Docker Hub,上面有大量官方镜像和社区镜像。docker pull 是从仓库下载镜像,docker push 是把自己做好的镜像推上去。
镜像、容器、仓库三者的关系可以简单记成:仓库里放镜像,镜像跑起来是容器。
三、装好之后先验证
安装方式因操作系统而异:Windows 和 macOS 一般装 Docker Desktop,Linux 服务器一般按发行版的官方文档装 Docker Engine。装好之后,先跑下面两条命令验证:
docker version
docker run hello-world
docker version 能输出客户端和服务端的版本信息,说明 Docker 服务在正常工作。docker run hello-world 会下载一个很小的测试镜像并运行,终端里打印出欢迎信息,说明“下载镜像—创建容器—运行”这条链路是通的。如果这一步报错,先检查 Docker 服务有没有启动、当前用户有没有权限操作 Docker,再往下排查。
四、跑第一个真正有用的容器
以 Nginx 为例,一条命令就能在本地跑起一个 Web 服务器:
docker run -d -p 8080:80 --name my-nginx nginx
这条命令里的参数值得逐个弄明白:
-d:后台运行,不占用当前终端。-p 8080:80:端口映射,把宿主机的 8080 端口映射到容器里的 80 端口。浏览器访问http://localhost:8080,看到 Nginx 欢迎页,就说明映射生效了。--name my-nginx:给容器起个名字,之后管理起来不用记一串容器 ID。nginx:要用的镜像名。本地没有这个镜像时,Docker 会自动去仓库下载。
容器跑起来之后,常用的管理命令有这几个:
docker ps # 查看正在运行的容器
docker ps -a # 查看所有容器,包括已停止的
docker logs my-nginx # 查看容器日志,排错先看它
docker stop my-nginx # 停止容器
docker start my-nginx # 重新启动已停止的容器
docker rm my-nginx # 删除容器
新手最常见的困惑是:容器停了,数据还在吗?容器本身可以随时删了重建,但容器里写入的数据默认不会自动保留。需要长期保存的数据,要用下一节讲的挂载办法。
五、自己写一个镜像:Dockerfile
只用别人做好的镜像远远不够,迟早要把自己的应用打包。Dockerfile 就是告诉 Docker“怎么做出镜像”的说明书。一个 Python Web 应用的 Dockerfile 大致长这样:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
逐行解释:
FROM python:3.11-slim:以官方 Python 3.11 精简镜像为基础,省去自己装解释器的麻烦。WORKDIR /app:把容器里的工作目录定为/app,后面的命令都在这个目录下执行。COPY requirements.txt .:先只复制依赖清单,再安装依赖。这样做的好处是:代码改了但依赖没变时,构建可以复用前面已经装好的依赖层,速度快很多。RUN pip install -r requirements.txt:在构建镜像时安装依赖。COPY . .:把应用代码复制进镜像。CMD ["python", "app.py"]:容器启动时默认执行的命令。
构建和运行的命令是:
docker build -t my-app:1.0 .
docker run -d -p 5000:5000 my-app:1.0
-t my-app:1.0 是给镜像打标签,名字加版本号,方便区分不同版本。构建时的 . 表示 Dockerfile 所在的当前目录就是构建上下文,.dockerignore 文件可以把不需要进镜像的东西(比如虚拟环境目录、.git)排除掉,镜像会小很多,构建也更快。
六、数据持久化:别让数据跟着容器一起消失
容器删了数据就没了,这对数据库、上传文件这类场景是不能接受的。解决办法是把宿主机的目录或 Docker 卷挂载进容器:
docker run -d -v my-data:/data my-app:1.0
-v my-data:/data 的意思是:把一个叫 my-data 的卷挂载到容器里的 /data 目录。容器往 /data 写的数据,实际存在卷里;容器删了重建,只要挂载同一个卷,数据还在。卷由 Docker 管理,不用操心它在宿主机上的具体路径;如果想直接看到文件,也可以用绑定挂载,把宿主机某个目录映射进容器,开发时改代码不用重建镜像就能生效。
七、多个容器一起跑:Docker Compose
真实项目很少只有一个容器:Web 应用后面往往还跟着数据库、缓存。用一条条 docker run 命令管理多个容器很快就会乱,这时可以用 Docker Compose:把多个容器的配置写进一个 docker-compose.yml 文件,一条命令全部拉起来:
services:
web:
build: .
ports:
- "5000:5000"
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example-password
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
docker compose up -d 启动所有服务,docker compose down 停掉并清理容器和网络,卷默认保留,数据不会丢。Compose 文件本身就是项目的基础设施文档,新人接手时看一眼就知道整个项目由哪些服务组成。
八、新手最容易踩的五个坑
- 容器退出了,先看日志。 容器一启动就退出,十有八九是启动命令报错,用
docker logs 容器名看最后一行报错信息,比反复重建管用。 - 端口冲突先查占用。 映射的宿主机端口已经被别的程序占用时容器起不来,换一个宿主机端口,或者停掉占用端口的程序。
- 不要把密码写进镜像。 密码、密钥、Token 应该用环境变量或密钥管理工具在运行时注入,写进 Dockerfile 或镜像层里等于公开。
- 镜像越小越好,但别盲目精简。 精简基础镜像能减小体积、加快传输,但缺了常用工具排错会很麻烦,初期用带常用工具的基础镜像更省心。
- 生产环境别用 latest 标签。 latest 指向的版本会变,今天能跑的配置明天可能就变了,固定写明版本号,升级时心里有数。
九、学完这篇之后可以往哪走
掌握了上面这些,日常开发基本够用了。接下来可以按这个顺序深入:先熟练 Compose 管理多服务,再学镜像分层和缓存优化把构建速度提上去,然后了解容器网络和资源限制,最后再去看 Kubernetes 这类编排工具——那是容器数量多到一台机器管不过来时才需要的东西。Docker 本身不难,难的是养成“环境即代码”的习惯:凡是部署需要的东西都写进 Dockerfile 和 Compose 文件里,而不是记在某台服务器的手工操作里。做到这一点,换机器、换同事、换环境,应用都能一样跑起来。
- 点赞
- 收藏
- 关注作者
评论(0)