Docker 部署项目与容器管理:挂载卷、查看、启动、停止和重启

Docker 部署项目时,通常会经历三个步骤:先把项目构建成镜像,再根据镜像创建容器,最后通过端口映射和卷挂载让服务对外提供访问,并把需要长期保存的数据放到容器之外。
本文使用一个监听容器内 3000 端口、数据目录为 /app/data 的通用项目作为示例。不同项目的启动命令、端口和数据目录可能不同,使用时需要替换成项目实际配置。
封面图来源:Docker Workflow,用于直观展示从 Dockerfile、镜像到容器和部署环境的基本流程。
一、镜像、容器和卷是什么
- 镜像(image):项目运行所需的程序、依赖和基础系统的只读模板。
- 容器(container):镜像启动后的运行实例,可以启动、停止和重启。
- 卷(volume):独立于容器生命周期保存数据的存储。删除并重新创建容器后,只要卷还在,数据就不会丢失。
如果把数据库文件、上传文件或配置文件只写在容器内部,容器被删除后这些数据也可能一起消失。因此,部署有状态项目时,卷挂载是必须认真配置的一步。
本文假设已经安装 Docker,并且当前用户可以直接执行 docker 命令。若执行 Docker 命令时出现 socket 权限错误,可以把用户加入 docker 用户组后重新登录:
sudo usermod -aG docker "$USER"二、构建项目镜像
在项目根目录准备 Dockerfile。下面是 Node.js 项目的一个通用示例:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./RUN npm ci --omit=dev
COPY . .
EXPOSE 3000CMD ["npm", "run", "start"]如果项目使用 pnpm、Python 或其他运行时,应将基础镜像、依赖安装命令和启动命令替换为项目自己的配置。EXPOSE 只是镜像中的说明,真正让宿主机可以访问服务,还需要在启动容器时进行端口映射。
进入项目目录后构建镜像:
cd /path/to/your-projectdocker build -t my-app:1.0 .其中:
-t my-app:1.0为镜像设置名称和标签。- 最后的
.表示使用当前目录作为构建上下文。
查看已经构建的镜像:
docker image ls三、创建容器并挂载卷
1. 使用宿主机目录挂载
如果希望直接看到宿主机上的文件,可以使用 bind mount:
mkdir -p data
docker run -d \ --name my-app \ --restart unless-stopped \ -p 8080:3000 \ -v "$(pwd)/data:/app/data" \ my-app:1.0这条命令完成了几件事:
-d:让容器在后台运行。--name my-app:为容器指定一个便于管理的名称。--restart unless-stopped:容器异常退出时自动尝试启动,手动停止后不会自动恢复。-p 8080:3000:把宿主机的8080端口映射到容器的3000端口。-v "$(pwd)/data:/app/data":把当前项目下的data目录挂载到容器内的/app/data。
端口映射的格式是 宿主机端口:容器端口。启动后,访问 http://服务器地址:8080 就会请求容器内监听的 3000 端口。
也可以使用可读性更好的 --mount 写法:
docker run -d \ --name my-app \ --mount type=bind,src="$(pwd)/data",dst=/app/data \ -p 8080:3000 \ my-app:1.0如果容器只需要读取配置,不应该修改宿主机文件,可以加上只读选项:
docker run -d \ --name my-app \ --mount type=bind,src="$(pwd)/config",dst=/app/config,readonly \ -p 8080:3000 \ my-app:1.02. 使用 Docker 命名卷
如果不需要直接通过固定路径管理文件,可以使用 Docker 命名卷。命名卷由 Docker 管理,适合数据库、缓存和应用数据:
docker volume create my-app-data
docker run -d \ --name my-app \ --restart unless-stopped \ -p 8080:3000 \ -v my-app-data:/app/data \ my-app:1.0查看命名卷:
docker volume lsdocker volume inspect my-app-data宿主机目录挂载和命名卷的区别可以简单理解为:宿主机目录挂载更直观,适合配置文件和需要人工查看的文件;命名卷更容易交给 Docker 管理,适合不关心宿主机具体存储路径的数据。
如果挂载的是宿主机目录,必须先确认目录存在,并检查目录权限。容器内进程使用的用户可能不是 root,因此宿主机目录即使存在,也不一定可写。
四、查看已经部署的容器
查看当前正在运行的容器:
docker ps查看所有容器,包括已经停止的容器:
docker ps -a输出中的几个常用字段如下:
| 字段 | 含义 |
|---|---|
CONTAINER ID | 容器 ID,可以使用前几位作为简写 |
IMAGE | 容器使用的镜像 |
COMMAND | 容器启动时执行的命令 |
STATUS | 当前运行状态或退出状态 |
PORTS | 端口映射关系 |
NAMES | 容器名称 |
只查看某个容器的状态:
docker inspect -f '{{.State.Status}}' my-app查看容器的完整配置,包括环境变量、挂载卷、网络和启动命令:
docker inspect my-app查看端口映射:
docker port my-app查看容器日志。排查容器启动后立即退出的问题时,通常应该先看日志:
docker logs --tail 100 my-appdocker logs -f my-app其中 -f 会持续跟随输出,按 Ctrl+C 只会退出日志查看,不会停止容器。
查看容器中的进程和资源占用:
docker top my-appdocker stats my-app如果需要进入正在运行的容器进行检查,可以执行:
docker exec -it my-app sh有些镜像内置的是 Bash,也可以使用 bash;Alpine 镜像通常只有 sh。
五、启动、停止和重启容器
启动已经存在但当前停止的容器
docker start 会使用容器原本的配置重新启动它,不会创建新的容器:
docker start my-app启动后确认状态:
docker ps --filter name=my-app停止容器
优先使用 docker stop,它会先向应用发送停止信号,给程序留出清理资源的机会:
docker stop my-app如果程序没有在默认等待时间内退出,可以强制终止:
docker kill my-appdocker kill 不会等待应用完成清理,应该只作为异常情况下的处理手段。
重启容器
docker restart my-app修改了宿主机挂载目录中的配置文件后,通常可以重启容器让应用重新读取配置:
docker restart my-appdocker logs --tail 50 my-app但如果修改了 Dockerfile、依赖或镜像内的文件,单纯重启并不会重新构建镜像。此时应重新构建镜像,并重新创建容器:
docker build -t my-app:1.1 .docker stop my-appdocker rm my-appdocker run -d \ --name my-app \ --restart unless-stopped \ -p 8080:3000 \ -v "$(pwd)/data:/app/data" \ my-app:1.1删除容器不会自动删除命名卷,但会删除容器本身的临时文件层。删除前可以先确认卷和挂载关系:
docker inspect my-app --format '{{json .Mounts}}'六、使用 Docker Compose 管理项目
当项目包含多个服务,或者不希望每次手动输入很长的 docker run 命令时,可以在项目根目录创建 compose.yaml:
services: app: build: . container_name: my-app ports: - "8080:3000" volumes: - app-data:/app/data # 需要从宿主机读取配置时,可以改用下面的 bind mount: # - ./config:/app/config:ro restart: unless-stopped
volumes: app-data:进入 compose.yaml 所在目录后,常用操作如下:
# 构建镜像并在后台启动项目docker compose up -d --build
# 查看项目中的容器docker compose ps
# 查看日志docker compose logs -f app
# 启动已经停止的服务docker compose start app
# 停止服务,但保留容器和数据docker compose stop app
# 重启服务docker compose restart app
# 停止并删除容器、网络,但保留命名卷docker compose down修改 Dockerfile 或项目代码后,常用的更新命令是:
docker compose up -d --build这会根据新的配置重新构建镜像,并让 Compose 重新创建需要更新的容器。docker compose stop 和 docker compose down 不同:前者只停止服务,后者还会删除 Compose 创建的容器和网络。
注意不要随意执行下面的命令:
docker compose down -v-v 会同时删除 Compose 声明的命名卷,数据库和应用数据可能因此丢失。只有在确认数据已经备份,或者明确希望清空环境时才使用它。
七、常见问题排查
1. 端口已被占用
如果出现 address already in use,说明宿主机端口已经被其他进程或容器占用。可以查看端口和容器映射:
docker ps --format 'table {{.Names}}\t{{.Ports}}'sudo ss -lntp | grep ':8080'确认占用者后,可以停止它,或者把项目的宿主机端口改成其他端口,例如 8081:3000。
2. 容器启动后马上退出
先查看状态和日志:
docker ps -a --filter name=my-appdocker logs my-app常见原因包括启动命令错误、环境变量缺失、应用没有监听正确端口,以及应用只绑定了 127.0.0.1。在容器中运行的 Web 服务通常需要监听 0.0.0.0,否则即使端口映射正确,宿主机也可能无法访问。
3. 挂载目录不可写
确认宿主机目录权限:
ls -ld datadocker exec my-app id如果容器内应用使用普通用户运行,需要让宿主机目录的用户或用户组与容器进程匹配,或者根据镜像文档设置正确的权限。不要为了绕过问题而长期使用 chmod -R 777。
4. 修改文件后数据看起来没有变化
检查容器实际使用的挂载关系:
docker inspect my-app --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'重点确认宿主机路径、容器内路径和读写模式是否正确。还要注意,挂载卷会覆盖容器内同一目录原有的文件;如果把一个空目录挂载到包含默认配置的目录,镜像中的默认文件可能会暂时不可见。
总结
单容器部署的核心命令可以记成下面这条链路:
docker build → docker run → docker ps / docker logs → docker stop / start / restart需要持久化的数据使用 -v 或 --mount 挂载;需要长期维护的多服务项目使用 Docker Compose。日常排错时,先看 docker ps -a 和 docker logs,更新镜像时使用 docker compose up -d --build,清理环境前则务必确认不会误删命名卷中的数据。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














