Docker 入门:概念、用法与 Jetson 注意事项
面向完全初学者的 Docker 入门文档,介绍 Docker 的核心概念、基本用法、Dockerfile 与 Compose 的作用,以及 Jetson 平台上的常见注意事项。
Docker 入门:概念、用法与 Jetson 注意事项
初次接触 Docker 时,常见疑问通常集中在以下几件事上:
- Docker 到底是什么?
- 它和虚拟机有什么区别?
- 为什么大家总说“用容器把环境装起来”?
- 初学阶段最值得优先掌握哪些命令?
- 放到 Jetson 上时,又为什么总会多出一堆和 GPU、架构、驱动有关的问题?
下文面向完全初学者展开,重点是建立最基本但足够稳定的理解框架:知道 Docker 是什么、为什么使用、最常见的命令是什么,以及放到 Jetson 上时应优先关注哪些问题。
Docker 到底是什么
Docker 是一套打包、分发和运行应用环境的工具链。它的核心能力,是把“程序运行所需的一整套环境”封装成镜像(Image),再在安装了 Docker 的机器上以容器(Container)的形式运行。
可以先做如下理解:
- 镜像(Image):一份“已经准备好的运行环境模板”
- 容器(Container):从镜像启动出来的一个正在运行的实例
例如,一个 Python 项目需要:
- Python 3.11
- 某几个 pip 依赖
- 某些系统库
- 某个固定版本的 Ubuntu
传统做法往往需要在每台机器上手动安装一遍;Docker 的做法则是把这套环境写进镜像中,随后直接在其他机器上运行该镜像。
Docker 官方文档将其描述为一套用于开发、交付和运行应用的平台,容器提供了一种轻量级、隔离的运行方式。
参考:https://docs.docker.com/get-started/docker-overview/
Docker 不是虚拟机
这里最容易产生误解。Docker 经常被粗略理解成“轻量虚拟机”,但这个说法并不准确。
虚拟机的思路
虚拟机一般是:
- 在宿主机上虚拟出一整台机器
- 再在里面装一个完整操作系统
- 然后在那套系统里运行程序
所以虚拟机通常更重,启动更慢,但隔离也更完整。
Docker 的思路
Docker 容器一般是:
- 直接复用宿主机的 Linux 内核
- 只隔离进程、文件系统、网络、用户空间等运行视图
- 不再启动一整套新的操作系统
所以 Docker 通常:
- 启动更快
- 占用更小
- 更适合做开发环境、服务部署、实验环境复现
可以用一句话概括:
虚拟机更像“再开一台电脑”,Docker 更像“给这个程序单独划一个隔离的运行空间”。
为什么要用 Docker
Docker 的价值主要体现在环境一致性、隔离性和部署统一性上。只要项目存在依赖版本、系统库、运行方式等要求,Docker 的作用通常都会很直接。
1. 环境可复现
这是 Docker 最大的价值。
依赖版本、系统库和启动命令都可以被固定下来,从而让环境可复制、可重建。
这意味着:
- 今天能运行的环境,在未来更容易被重新构建
- 开发机上的环境,与服务器环境更容易保持一致
- 团队成员拿到同一个镜像后,行为通常更接近
2. 隔离不同项目
例如存在两个项目:
- 项目 A 需要 Python 3.10
- 项目 B 需要 Python 3.12
如果全部直接安装在宿主机上,环境很容易互相污染;使用 Docker 后,可以把它们分别放在各自的容器中。
3. 部署更统一
开发阶段运行的是容器,测试阶段运行的是容器,部署到服务器时运行的仍然是容器。
这样“开发环境”和“线上环境”之间的差异就会显著减小。
4. 试错成本低
容器可以随时:
- 新建
- 测试
- 删除
不用担心把宿主机搞乱。
Docker 世界里最常见的几个概念
在真正使用之前,先把几个高频词认清会更有效。
Image
镜像。
它是一个只读模板,里面包含程序和运行环境。
例如:
ubuntu:22.04
python:3.11
nginx:latest
Container
容器。
它是镜像启动出来的运行实例。
同一个镜像可以启动多个容器。
Registry
镜像仓库。
用于存放和分发镜像,比如:
- Docker Hub
- GitHub Container Registry
- NVIDIA NGC
Dockerfile
构建镜像的脚本。
Dockerfile 中通常会写:
- 基础镜像是什么
- 要复制哪些文件
- 要安装哪些依赖
- 默认启动命令是什么
Volume / Bind Mount
用于把宿主机数据挂进容器。
这很重要,因为容器本身通常不应该作为长期数据存储的地方。
Compose
用于同时管理多个容器。
如果一个项目包含:
- Web 服务
- 数据库
- Redis
更常见的做法不是手写多条冗长的 docker run,而是通过 compose.yaml 统一管理。
先学会最小可用命令
完全初学阶段不需要一次背很多命令。下面这些命令先熟悉即可。
验证 Docker 是否可用
完成安装后,最常见的验证方式是运行一个最小测试镜像:
docker run --rm hello-world
如果终端输出欢迎信息,说明 Docker 服务、镜像拉取和容器启动链路都已经打通。
拉镜像
docker pull ubuntu:22.04
作用:把镜像从远端仓库下载到本地。
启动一个交互式容器
docker run --rm -it ubuntu:22.04 bash
这个命令可以拆开理解:
run:创建并启动容器--rm:退出后自动删除容器-it:以交互方式运行,附着当前终端ubuntu:22.04:使用哪个镜像bash:容器启动后执行什么命令
这基本是最适合初学者的第一条 Docker 命令。
查看正在运行的容器
docker ps
查看所有容器(包括已停止)
docker ps -a
停止容器
docker stop <container_id>
删除容器
docker rm <container_id>
查看镜像
docker images
删除镜像
docker rmi <image_id>
查看日志
docker logs <container_id>
进入一个正在运行的容器
docker exec -it <container_id> bash
这个命令非常常用。很多时候不需要重新创建容器,只需要通过 exec 进入现有容器进行查看或调试。
一个适合初学者的最小工作流
对于绝大多数入门场景,可以把 Docker 的使用流程压缩成下面四步:
- 先找一个现成镜像并运行,确认环境链路没问题。
- 再把本地目录挂载进容器,理解代码和数据应放在哪里。
- 然后编写 Dockerfile,把依赖和启动方式固定下来。
- 最后在服务变多时,再引入 Compose 管理多个容器。
这个顺序的好处是,概念和操作是同步建立的,不容易在一开始就被过多工具细节淹没。
一个最简单的“跑起来”的例子
最简单的体验方式之一,是先运行一个现成的 Web 服务容器。
docker run --rm -p 8080:80 nginx:latest
然后打开浏览器访问:
http://localhost:8080
如果浏览器出现 Nginx 欢迎页,说明最基本的 Docker 运行流程已经打通。
这里的 -p 8080:80 表示:
- 宿主机的
8080 - 映射到容器内的
80
也就是说,外部访问 localhost:8080,实际上会被转发给容器里的 Nginx。
容器里改了文件,为什么一删就没了
这是第二个高频困惑。
因为容器默认不是你长期保存数据的地方。
错误直觉
初学阶段很容易把容器当成“一台长期存在的小机器”,然后在里面改文件、装依赖、存数据。
这通常不是 Docker 推荐的使用方式。
正确直觉
容器更应该被看成:
一个可以随时被销毁和重建的运行实例
真正应该长期保存的内容通常包括:
- 代码:放在宿主机目录并挂载进去
- 数据:放在 volume 或数据库里
- 环境定义:写进 Dockerfile / Compose 文件
例如,把当前目录挂进容器:
docker run --rm -it -v "$PWD":/workspace -w /workspace python:3.11 bash
这样容器里的 /workspace 就对应宿主机当前目录。
Dockerfile 是什么
当工作流从“运行别人提供的镜像”走向“打包自己的项目”时,就会用到 Dockerfile。
一个最简单的 Python Flask 示例:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
然后构建镜像:
docker build -t my-flask-app:0.1 .
运行:
docker run --rm -p 5000:5000 my-flask-app:0.1
Dockerfile 的意义主要在于:
- 环境定义可保存
- 团队成员可共享
- 构建过程可自动化
Docker Compose 解决什么问题
当一个项目不只一个进程时,Compose 会显著降低管理复杂度。
比如一个典型 Web 项目可能需要:
- 一个应用服务
- 一个 PostgreSQL
- 一个 Redis
理论上可以手写多条 docker run,但维护成本较高。Compose 的作用,就是把这些服务统一写进一个文件里。
最小示例:
services:
web:
build: .
ports:
- "8000:8000"
volumes:
- .:/app
working_dir: /app
command: python app.py
redis:
image: redis:7
然后:
docker compose up
停止并删除:
docker compose down
对初学者来说,docker compose up/down 往往比背很多长命令更值得优先学。
初学者最常见的误区
1. 把容器当虚拟机养
前面已经提到,容器最好是可销毁、可重建的。
如果在容器里手工改了很多内容,但没有把它们写进 Dockerfile,那么这些改动通常不可复现。
2. 所有数据都放容器里
这样容器一删,数据就没了。
数据库、模型、日志、工作目录,应该考虑挂载或 volume。
3. latest 用太多
latest 用起来省事,但不利于复现。
如果是长期项目,尽量固定版本。
4. 一上来就追求复杂编排
没有必要在起步阶段引入过多概念。
先掌握 pull / run / exec / logs / build / compose up,已经可以覆盖很多实际场景。
在 Jetson 上用 Docker,为什么经常更麻烦
因为 Jetson 不是普通 x86 Linux 机器,它在下面几个维度上都更特殊:
- CPU 架构通常是 ARM64 / aarch64
- GPU 不是桌面独显的工作方式
- NVIDIA 运行时、JetPack、L4T、CUDA、TensorRT 等版本关系更紧
- 板子上的存储和内存通常也更紧张
因此,把 PC 上的 Docker 教程原样搬到 Jetson 上后直接遇到问题,是非常常见的情况。
Jetson 上最该先记住的注意事项
1. 先确认镜像是不是 ARM64
这是最容易忽略的问题。
很多教程默认环境是:
- x86_64
- Ubuntu PC
- 桌面 NVIDIA GPU
而 Jetson 通常是 ARM64。
这意味着并不是所有 Docker Hub 镜像都能直接在 Jetson 上跑。
优先确认的事项包括:
- 镜像是否支持
arm64 - 或者是否存在 Jetson 专用 / L4T 专用镜像标签
否则最常见的结果就是:
- 拉不下来
- 能拉下来但启动失败
- 或者程序本身依赖的二进制根本不兼容
2. GPU 容器不是“装个 Docker 就结束”
在 Jetson 上,如果容器需要访问 GPU,通常还需要:
- NVIDIA Container Toolkit / NVIDIA Container Runtime
- Docker runtime 的额外配置
在 NVIDIA 近年的官方文档里,常见做法是用 nvidia-ctk 为 Docker 配置 NVIDIA runtime。
参考:
- NVIDIA Container Toolkit 安装文档:https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/1.17.8/install-guide.html
- Jetson Docker Setup 文档:https://docs.nvidia.com/jetson/agx-thor-devkit-4fed1671/user-guide/latest/setup_docker.html
一个较新的通用流程大致是:
sudo apt-get update
sudo apt install -y nvidia-container
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
注意:
这部分在不同 JetPack / Jetson 文档里会有差异。上面的命令是基于官方近期文档整理的通用流程;实际操作时,应以设备对应 JetPack / L4T 版本的官方文档为准。
3. Jetson 上容器标签经常要和 JetPack / L4T 对齐
这是 Jetson 平台和普通 PC 很不一样的一点。
在很多 NVIDIA 提供的 Jetson 容器中:
- 宿主机 BSP / JetPack 版本
- 容器镜像标签
- CUDA / TensorRT / DeepStream 等运行时
往往不是随便混用的。
也就是说,不能简单地把“容器里什么都有,所以宿主机无所谓”当成前提。
更稳妥的原则是:
- 先确认板子的 JetPack / L4T 版本
- 再选择与之匹配的 NVIDIA 容器镜像,例如 NVIDIA NGC 中带有
l4t标识的 Jetson 镜像 - 不要把 dGPU 教程和 Jetson 教程混着看
如果一个教程默认使用的是面向桌面显卡或服务器显卡的 CUDA 镜像,那么它在 Jetson 上往往并不能直接复用。
4. Jetson 上磁盘空间很容易先炸
很多 Jetson 板子:
- eMMC 容量有限
- 系统盘空间紧张
- 镜像层和构建缓存又很占地方
因此,在 Jetson 上使用 Docker 时,最好尽早养成以下习惯:
- 定期清理不用的镜像和容器
- 尽量避免在板子上盲目
docker build大镜像 - 模型、数据、日志放挂载目录,而不是全堆在镜像层里
常用清理命令:
docker ps -a
docker images
docker system df
docker system prune
如果使用的是开发板而不是服务器,空间管理几乎一定会成为实际问题。
5. 摄像头、串口、USB、GPIO 这类外设要单独考虑
Jetson 上很多项目不只是“跑个程序”,还会碰到:
- 摄像头
/dev/video*- 串口
- USB 设备
- I2C / SPI / GPIO
这些资源并不会因为进入容器就自动变得可用。
常见做法包括:
- 显式映射设备
- 映射必要目录
- 使用
--privileged(谨慎) - 某些场景下使用
--network host
例如,最粗暴但最不精细的方式是:
docker run --rm -it --privileged --network host <image>
但这并不是默认推荐做法,因为它会显著降低隔离性。
更稳妥的方式通常是按需映射实际需要的设备和权限。
6. Jetson 上构建大型项目时,内存往往比 CPU 先不够
这一点在板子上非常真实。
如果容器中执行的是:
- 编译大 C++ 项目
- 编译 PyTorch 扩展
- 构建大型 ROS / AI 依赖
更可能先遇到的是:
- OOM
- 编译卡死
- swap 不足
因此,在 Jetson 上,很多“容器问题”本质上并不只来自 Docker 本身,而是来自:
- 板子资源有限
- 容器把问题暴露得更明显
7. 不要照搬桌面 NVIDIA GPU 的排错方法
很多 x86 教程会先执行:
nvidia-smi
但 Jetson 并不是桌面独显环境,这条命令通常并不是首选检查手段。
在 Jetson 上,更常见的观察方式是:
- 先确认 JetPack / L4T 版本
- 再确认容器 runtime 是否配置完成
- 然后结合
tegrastats或其他 Jetson 平台工具观察资源占用
这也是为什么 Jetson 平台上的 Docker 文档,通常需要优先看面向 Jetson 的官方材料,而不是直接照搬 PC 服务器上的 CUDA 容器说明。
官方最速示例:在 Jetson 上用 Docker 拉起 Qwen3 30B-A3B
如果目标不是理解全部 Docker 概念,而是尽快在 Jetson 上验证一次大模型推理链路,那么 NVIDIA 当前给出的官方示例已经足够直接。
根据 NVIDIA Jetson AI Lab 当前模型页,Qwen3 30B-A3B (MoE) 在 Jetson 上支持通过 vLLM 容器直接启动。对应命令如下。
Jetson Orin
sudo docker run -it --rm --pull always --runtime=nvidia --network host \
ghcr.io/nvidia-ai-iot/vllm:latest-jetson-orin \
vllm serve RedHatAI/Qwen3-30B-A3B-quantized.w4a16
Jetson Thor
sudo docker run -it --rm --pull always --runtime=nvidia --network host \
ghcr.io/nvidia-ai-iot/vllm:latest-jetson-thor \
vllm serve RedHatAI/Qwen3-30B-A3B-quantized.w4a16
这两条命令的意义分别是:
--pull always:每次启动前都尝试拉取最新容器版本--runtime=nvidia:让容器接入 Jetson 的 NVIDIA runtime--network host:直接使用宿主机网络,省去额外端口映射vllm serve ...:直接在容器内启动模型推理服务
如果已经按照 NVIDIA 的 Jetson Docker Setup 文档把 Docker 默认 runtime 设为 nvidia,那么 --runtime=nvidia 可以省略。
启动前的最低前提
在执行上面的模型命令之前,至少需要先完成 NVIDIA 官方文档中的 Docker 基础配置:
sudo apt-get update
sudo apt install -y nvidia-container curl
curl https://get.docker.com | sh && sudo systemctl --now enable docker
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl daemon-reload && sudo systemctl restart docker
如果还没有把默认 runtime 设成 nvidia,还需要继续执行:
sudo apt install -y jq
sudo jq '. + {"default-runtime": "nvidia"}' /etc/docker/daemon.json | \
sudo tee /etc/docker/daemon.json.tmp && \
sudo mv /etc/docker/daemon.json.tmp /etc/docker/daemon.json
完成后,/etc/docker/daemon.json 中应能看到 default-runtime 为 nvidia。
如何确认服务已经起来
容器启动后,终端会停留在前台输出日志。这时可以在另一终端发一个最小测试请求:
curl http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "RedHatAI/Qwen3-30B-A3B-quantized.w4a16",
"messages": [
{"role": "user", "content": "用一句话介绍 Docker。"}
]
}'
如果返回了标准的 JSON 推理结果,说明容器、模型和服务接口都已经正常工作。
这个示例的边界
这条命令强调的是“尽快验证推理链路”,不是完整生产部署方案。实际使用时仍然需要继续考虑:
- 首次拉取镜像和模型时的磁盘占用
- Jetson 板卡的内存容量是否足够
- 模型容器版本与 Jetson 平台版本是否匹配
- 长时间运行时是否需要改为后台服务、日志落盘和重启策略
Jetson 上一个更稳的思路
在 Jetson 上,更稳妥的排查顺序如下:
-
先在宿主机确认基础功能能工作
例如摄像头、CUDA、基础驱动链路是否正常。 -
再确认 Docker 本身正常
先跑普通 Ubuntu / Alpine 容器。 -
再确认 GPU runtime 正常
确认 NVIDIA runtime 已配置成功。 -
最后再跑具体业务镜像
例如 DeepStream、PyTorch、ROS、推理服务等。
这样排错会轻松很多。否则,一上来就运行大型 AI 镜像时,很难判断问题究竟来自:
- Docker 没装好
- runtime 没配好
- 镜像架构不对
- 版本不匹配
- 外设权限不够
- 板子资源不足
一个适合 Jetson 的心智模型
Jetson 上的 Docker,可以被概括为:
在“资源有限、版本耦合更强、外设更多”的环境里,尽量把软件栈封装稳定。
所以,Jetson 上使用 Docker 的重点并不是“把所有问题都藏起来”,而是:
- 让依赖更清楚
- 让环境更容易重建
- 让实验和部署更容易复制
但与此同时,Jetson 平台的宿主机环境依然很重要,尤其是:
- BSP / JetPack
- NVIDIA runtime
- 外设访问
- 存储和内存
初学阶段的最小学习顺序
初学阶段可按下面的顺序推进:
第一阶段:先学会“会跑”
只学这些:
docker pulldocker rundocker psdocker logsdocker execdocker build
第二阶段:学会“会保存环境”
开始写:
Dockerfilecompose.yaml
第三阶段:学会“会挂数据、会排错”
重点理解:
- 端口映射
- 目录挂载
- 容器日志
- 镜像层和容器层
第四阶段:再上 Jetson
在这个阶段,再进入以下内容会更稳妥:
- ARM64 镜像
- NVIDIA runtime
- JetPack 版本匹配
- 设备映射
成功率会高很多。
一个常见排错顺序
如果容器没有按预期工作,建议按下面的顺序缩小问题范围:
- 先看容器是否真的启动成功:
docker ps -a - 再看主进程输出了什么:
docker logs <container_id> - 如果容器还活着,再进入容器检查文件、依赖和环境变量:
docker exec -it <container_id> bash - 如果涉及端口,再确认映射关系和监听地址是否正确
- 如果涉及 Jetson GPU 或外设,再额外检查 runtime、设备映射和宿主机版本匹配
这个顺序的重点是先分清问题是在 Docker 层、应用层,还是宿主机和设备层。
小结
Docker 的核心价值,不是“炫酷”,而是:
- 把环境写下来
- 把环境打包起来
- 把环境稳定地复用起来
对于初学者,真正值得先建立的认识只有三个:
- 镜像是模板,容器是实例
- 容器是可销毁的,不要把它当长期主机养
- 宿主机、镜像、挂载、网络这四件事,决定了大多数 Docker 问题
而在 Jetson 上,再额外多记住三件事:
- 先确认架构是不是 ARM64
- GPU 容器需要 NVIDIA runtime 配置
- 镜像标签最好和 JetPack / L4T 版本对应
做到这里,已经足以脱离“完全不会 Docker”的阶段。
参考文档
- Docker 官方概览:https://docs.docker.com/get-started/docker-overview/
- NVIDIA Container Toolkit 安装文档:https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/1.17.8/install-guide.html
- Jetson Docker Setup(官方近期文档入口):https://docs.nvidia.com/jetson/agx-thor-devkit-4fed1671/user-guide/latest/setup_docker.html
- NVIDIA Jetson AI Lab, Qwen3 30B-A3B 页面:https://www.jetson-ai-lab.com/models/qwen3-30b-a3b/
- DeepStream Jetson 容器文档(可帮助理解 Jetson 容器的版本依赖与限制):https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_docker_containers.html