它能干嘛

Docker 把应用和环境打包在一起,让它跑到哪都一样。你在本地跑的容器,丢到服务器上行为一致,不会出现"我电脑上能跑啊"的尴尬。

安装

# macOS
brew install docker

# 或者用 OrbStack(轻量替代 Docker Desktop)
brew install orbstack

# Linux
curl -fsSL https://get.docker.com | sh

核心概念

Docker 就三个东西来回转:

  • 镜像(Image) — 一个只读模板,比如 nginx:latestpython:3.12
  • 容器(Container) — 镜像跑起来之后的实例,可读可写
  • 仓库(Registry) — 存镜像的地方,Docker Hub 是默认的公共仓库

再加两个配角:

  • 卷(Volume) — 容器删了数据还在,持久化存储
  • 网络(Network) — 容器之间怎么通信

镜像操作

# 拉镜像
docker pull nginx:latest
docker pull python:3.12-slim    # slim 版本体积小,生产常用

# 看本地有哪些镜像
docker images
docker image ls

# 看镜像的分层历史
docker history nginx:latest

# 给镜像打标签
docker tag nginx:latest my-nginx:v1

# 删镜像
docker rmi nginx:latest
docker image rm nginx:latest

# 清理没用的镜像
docker image prune

容器操作

跑起来

# 最基本的
docker run nginx:latest

# 后台跑,给个名字
docker run -d --name my-nginx nginx:latest

# 映射端口(本机8080 → 容器80)
docker run -d -p 8080:80 --name my-nginx nginx:latest

# 挂载目录(本地目录 → 容器目录)
docker run -d -v $(pwd)/html:/usr/share/nginx/html -p 8080:80 nginx:latest

# 用完即删
docker run --rm nginx:latest

# 交互式进去
docker run -it ubuntu:latest /bin/bash

日常管理

# 看运行中的
docker ps

# 看所有(包括停掉的)
docker ps -a

# 看最近创建的 N 个
docker ps -n 5

# 进去正在运行的容器
docker exec -it my-nginx /bin/bash
docker exec my-nginx cat /etc/nginx/nginx.conf

# 查看日志
docker logs my-nginx
docker logs -f my-nginx       # 实时跟踪
docker logs --tail 50 my-nginx # 最后50行

# 看容器详情
docker inspect my-nginx

# 复制文件
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf   # 容器→本机
docker cp ./index.html my-nginx:/usr/share/nginx/html/   # 本机→容器

# 启停
docker stop my-nginx
docker start my-nginx
docker restart my-nginx
docker pause my-nginx          # 暂停进程但不停止容器
docker unpause my-nginx

# 删容器
docker rm my-nginx
docker rm -f my-nginx           # 强制删,不管停没停
docker container prune          # 清掉所有已停止的容器

docker run 常用参数

参数 说明
-d 后台跑 -d
--name 给容器起名 --name my-app
-p 端口映射 -p 8080:80
-v 挂载目录/卷 -v ./data:/data
-e 环境变量 -e MYSQL_ROOT_PASSWORD=123
--rm 停了自动删 --rm
--restart 重启策略 --restart always
--network 加入网络 --network my-net
-it 交互式 -it

Dockerfile 写镜像

一个简单的例子,把 Go 项目打包成镜像:

# 构建阶段
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o server .

# 运行阶段
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]

多阶段构建的好处:构建依赖留在第一阶段,最终镜像只包含运行需要的文件,体积小很多。

常用指令

指令 干嘛的
FROM 基于哪个镜像
WORKDIR 设工作目录
COPY 从本机复制文件到镜像
ADD 类似 COPY,但支持自动解压 tar 和远程 URL
RUN 构建时执行命令
ENV 设环境变量
ARG 构建参数(--build-arg 传入)
EXPOSE 声明容器监听的端口(文档性质,实际靠 -p 映射)
CMD 容器启动时默认执行的命令
ENTRYPOINT 容器入口,CMD 可作为它的默认参数
VOLUME 声明匿名卷
USER 以哪个用户运行

构建和推送

# 构建
docker build -t my-app:v1 .

# 指定 Dockerfile
docker build -f Dockerfile.prod -t my-app:v1 .

# 加构建参数
docker build --build-arg VERSION=1.0 -t my-app:v1 .

# 不看缓存重新构建
docker build --no-cache -t my-app:v1 .

# 推送到仓库
docker tag my-app:v1 username/my-app:v1
docker push username/my-app:v1

# 登录
docker login
docker login registry.example.com

卷(Volume)

容器删了,卷里的数据还在:

# 创建卷
docker volume create my-data

# 挂载卷
docker run -d -v my-data:/data --name db mysql:8

# 挂载本地目录
docker run -d -v $(pwd)/data:/data --name db mysql:8

# 列出卷
docker volume ls

# 删卷
docker volume rm my-data
docker volume prune     # 清掉没在用的卷

卷 vs 绑定挂载:卷由 Docker 管理,绑定挂载(-v /本地路径:/容器路径)直接指向宿主机目录。开发时用绑定挂载方便热更新,生产环境用卷。

网络(Network)

# 创建网络
docker network create my-net

# 在指定网络中跑容器
docker run -d --network my-net --name app1 my-app
docker run -d --network my-net --name app2 my-app

# 同一网络内的容器可以用容器名互相访问
# 比如 app1 里可以 ping app2

# 把已有容器连到网络
docker network connect my-net existing-container

# 查看
docker network ls
docker network inspect my-net

# 删
docker network rm my-net
docker network prune

默认情况下 Docker 会创建 bridge 网络,容器之间只能用 IP 互通。自己建网络后,容器可以用名字互相访问。

Docker Compose

多个容器一起管,写一个 docker-compose.yml

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=db
      - DB_PASSWORD=secret
    depends_on:
      - db
    restart: always

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=myapp
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: always

volumes:
  pgdata:

常用命令:

# 启动所有服务
docker compose up -d

# 重新构建并启动
docker compose up -d --build

# 看状态
docker compose ps

# 看日志
docker compose logs -f

# 在某个服务里执行命令
docker compose exec app /bin/sh

# 停止并清理
docker compose down

# 停止并连卷一起删
docker compose down -v

清理与空间

# 看磁盘占用
docker system df

# 一键清理(停止的容器、未使用的网络、悬空镜像、构建缓存)
docker system prune

# 更狠,连未使用的镜像和卷也清
docker system prune -a --volumes

# 分别清理
docker container prune   # 停止的容器
docker image prune       # 未使用的镜像
docker volume prune      # 未使用的卷
docker network prune     # 未使用的网络
docker builder prune     # 构建缓存

磁盘告急时 docker system prune -a 能回收不少空间。

常用模式

开发时热更新

把源码目录挂进去,改代码不用重新构建镜像:

docker run -d -v $(pwd):/app -p 3000:3000 node:20-alpine sh -c "cd /app && npm start"

一次性任务

# 用 alpine 跑个命令就走
docker run --rm alpine echo "hello"

# MySQL 客户端连另一个容器里的数据库
docker run --rm -it mysql:8 mysql -h db-container -u root -p

查看容器资源占用

docker stats
docker stats my-container

把容器保存为镜像

docker commit my-container my-custom-image:v1

导出导入镜像

docker save -o my-image.tar my-app:v1
docker load -i my-image.tar

适合没有 registry 或者离线环境的场景。


没了。Docker 上手不难,runpsexeclogsstop 五个命令熟悉了基本就够日常用了。