基本操作

1. 拉取镜像 (docker pull)

  • 作用: 从镜像仓库 (默认是 Docker Hub) 下载镜像到你的本地机器。
  • 语法: docker pull <仓库名>/<镜像名>:<标签>
    • <仓库名>: 可选,如果是 Docker Hub 上的官方镜像或知名镜像,通常可以省略。如果是个人或其他组织的镜像,需要指定,例如 osrf/ros
    • <镜像名>: 必须,例如 ubuntu, ros
    • <标签>: 可选,用于指定镜像的版本。如果不指定,默认拉取 latest 标签。强烈建议总是指定明确的标签,以保证环境的可复现性。例如 ubuntu:22.04, ros:noetic-ros-base
  • 示例:
    • 拉取官方的 Ubuntu 22.04 镜像:你会看到 Docker 开始下载镜像的各个层 (layer)。

      1
      docker pull ubuntu:22.04
    • 拉取 ROS Noetic 的基础镜像 (来自 Open Source Robotics Foundation):(这个镜像会大一些,因为它包含了 ROS Noetic 的核心组件)

      1
      docker pull ros:noetic-ros-base

2. 查看本地镜像 (docker images)

  • 作用: 列出你本地已经下载的所有镜像。

  • 语法: docker images

  • 示例: 你会看到类似以下的输出:

    1
    docker images
    1
    2
    3
    4
    REPOSITORY          TAG               IMAGE ID       CREATED        SIZE
    ubuntu 22.04 xxxxxxxxxxxx xx weeks ago 77.8MB
    ros noetic-ros-base yyyyyyyyyyyy xx weeks ago 1.07GB
    hello-world latest zzzzzzzzzzzz xx months ago 13.3kB
    • REPOSITORY: 镜像所在的仓库名。
    • TAG: 镜像的标签 (版本)。
    • IMAGE ID: 镜像的唯一标识符。
    • CREATED: 镜像创建的时间。
    • SIZE: 镜像的大小。

3. 运行容器 (docker run)

  • 作用: 使用指定的镜像创建一个新的容器并运行它。这是 Docker 最核心的命令之一。

  • 语法: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

  • 常用选项 (OPTIONS):

    以下这些选项非常关键:

    1. -it (交互式 TTY)

      • -i (--interactive): 保持标准输入(STDIN)打开,即使没有附加连接。简单说,允许你向容器输入命令。

      • -t (--tty): 分配一个伪终端(pseudo-TTY)。这会模拟一个真实的终端,让你可以像在普通 Linux Shell 里一样交互,比如使用 Tab 补全、看到命令提示符等。

      • 为什么重要? 你通常需要进入容器的 Shell 环境进行编译、运行节点、调试等操作,-it 几乎是必备组合。

      • 示例: 这个命令会启动一个基于 ubuntu:20.04 镜像的容器,并直接进入容器的 bash Shell。你可以像在普通的 Ubuntu 终端里一样操作。退出 Shell(输入 exit 或按 Ctrl+D)后,容器通常会停止。

        1
        docker run -it ubuntu:20.04 bash
    2. --rm (自动移除)

      • 当容器退出时,自动删除容器文件系统。

      • 为什么重要? 每次 docker run 都会创建一个新的容器。如果你只是临时运行一个命令或者进入 Shell 调试,用完就扔,--rm 可以避免产生大量无用的、停止状态的容器,保持 Docker 环境整洁。对于调试和测试非常方便。

      • 示例: 当你退出这个 bash 后,这个容器会被彻底删除,docker ps -a 也看不到它了。

        1
        docker run -it --rm ubuntu:20.04 bash
    3. -d (--detach) (后台运行)

      • 让容器在后台运行,并打印出容器 ID。

      • 为什么重要? 有时候你希望容器作为一个服务在后台持续运行(比如运行一个 ROS Master 或者一个模拟器),而不是占用你的当前终端。

      • 注意: 如果使用 -d,通常容器需要运行一个长期在前台执行的进程(比如一个 web 服务、一个 sleep infinity 或者 ROS 节点),否则容器一启动执行完默认命令(如果该命令会结束)就会立刻退出。

      • 示例:

        1
        2
        3
        4
        # 启动一个后台运行的容器,里面无限睡眠 (常用于创建一个长期运行的基础容器)
        docker run -d --name my_background_ubuntu ubuntu:20.04 sleep infinity
        # 你可以使用 docker exec 进入这个后台运行的容器
        docker exec -it my_background_ubuntu bash
    4. --name <container_name> (指定名称)

      • 给容器指定一个易于记忆的名字。如果不指定,Docker 会随机生成一个(比如 vigilant_mclean)。

      • 为什么重要? 有了名字,你可以方便地通过名字来管理容器,比如 docker stop my_slam_containerdocker start my_slam_containerdocker exec -it my_slam_container bash 等。

      • 示例:

        1
        docker run -it --rm --name slam_dev_session ubuntu:20.04 bash
    5. -v--volume (挂载卷) / --mount (更推荐的挂载方式)

      • 将宿主机(你的 Ubuntu 22.04)的目录或文件挂载到容器内部。这是实现代码/数据共享和持久化的关键

      • 为什么对 SLAM 重要?

        • 代码开发: 你可以在宿主机上用你喜欢的 IDE 编辑代码,然后在容器里编译和运行,代码是实时同步的。
        • 数据集: SLAM 数据集通常很大,你不需要把它们复制到镜像里,直接挂载宿主机上的数据集目录即可。
        • 结果保存: 容器运行产生的结果(地图、轨迹、日志)可以保存到挂载的宿主机目录,容器删除后结果依然存在。
      • 语法 (-v): -v <host_path>:<container_path>[:options]

      • 语法 (--mount): --mount type=bind,source=<host_path>,target=<container_path>[,readonly] (bind mount 是最常用的类型,效果类似 -v)

      • 示例 (使用 -v):

        1
        2
        3
        4
        # 将宿主机的 ~/slam_ws 目录挂载到容器内的 /root/slam_ws 目录
        docker run -it --rm -v ~/slam_ws:/root/slam_ws ubuntu:20.04 bash
        # 进入容器后,你在 /root/slam_ws 看到的就是宿主机 ~/slam_ws 的内容
        # 在容器内修改 /root/slam_ws/some_file.txt,宿主机的 ~/slam_ws/some_file.txt 也会同步改变
      • 示例 (使用 --mount):--mount 语法更清晰,推荐使用。

        1
        docker run -it --rm --mount type=bind,source=~/slam_ws,target=/root/slam_ws ubuntu:20.04 bash
    6. -p--publish (端口映射)

      • 将容器的端口映射到宿主机的端口。格式:-p <host_port>:<container_port>

      • 为什么对 SLAM 重要? 如果你在容器里运行了需要网络访问的服务(比如 RViz 的 Web 版本、或者某些算法的监控界面),需要将容器端口暴露给宿主机或其他机器。

      • 示例:

        1
        2
        3
        # 假设容器内 8080 端口运行了一个 web 服务
        docker run -d -p 8888:8080 my_web_service_image
        # 现在你可以通过访问宿主机的 8888 端口 (http://localhost:8888) 来访问容器内的 8080 服务
      • 对于 ROS: 很多时候直接使用 --net=host (见下一点) 更方便,避免复杂的端口映射。

    7. --net--network (网络模式)

      • 配置容器的网络连接方式。常用模式:

        • bridge (默认): 容器有自己独立的网络栈,通过 Docker 网桥连接宿主机。需要端口映射 (p) 才能从外部访问容器服务。
        • host: 容器共享宿主机的网络栈。容器直接使用宿主机的 IP 地址和端口,无需端口映射。性能最好,但隔离性差。
        • none: 容器没有网络连接。
      • 为什么 host 对 SLAM/ROS 重要? ROS 节点间通信、RViz 连接 ROS Master、与连接到宿主机的传感器(如网络摄像头)通信等,使用 --net=host 可以极大简化网络配置,让容器内的 ROS 环境像直接在宿主机运行一样方便。

      • 示例:

        1
        2
        3
        4
        # 使用 host 网络模式运行容器
        docker run -it --rm --net=host ubuntu:20.04 bash
        # 在容器内运行 ifconfig,你会看到宿主机的网络接口
        # 在容器内运行的 ROS 节点可以直接与宿主机或其他局域网内的 ROS 节点通信
    8. -e--env (环境变量)

      • 设置容器内的环境变量。格式:-e KEY=VALUE

      • 为什么对 SLAM 重要?

        • ROS 配置: 设置 ROS_MASTER_URI, ROS_IP, ROS_HOSTNAME 等。
        • 显示转发 (X11 Forwarding): 让容器内的 GUI 程序(如 RViz, Gazebo, rqt_plot)能显示在宿主机的屏幕上。通常需要设置 DISPLAY 环境变量。
      • **示例 (X11 Forwarding):**注意: X11 Forwarding 配置有时比较棘手,可能因系统而异。-net=host 通常能简化这个问题。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        # 在宿主机先执行 xhost +local:docker (允许本地 docker 容器连接 X server)
        xhost +local:docker
        # 运行容器时传递 DISPLAY 环境变量,并挂载 X11 socket
        docker run -it --rm \
        -e DISPLAY=$DISPLAY \
        -v /tmp/.X11-unix:/tmp/.X11-unix \
        --net=host \
        ubuntu:20.04 bash
        # 在容器内安装并运行一个简单的 GUI 程序试试,比如 xeyes (apt update && apt install -y x11-apps && xeyes)
    9. --device (挂载设备)

      • 将宿主机的设备文件挂载到容器内。

      • 为什么对 SLAM 重要? SLAM 严重依赖传感器!你需要让容器访问连接到宿主机的摄像头、IMU、LiDAR 等设备。

      • **示例:**注意: 确保运行 Docker 的用户在宿主机上对这些设备文件有读写权限(通常需要将用户添加到 video, dialout 等用户组)。

        1
        2
        3
        4
        5
        6
        7
        8
        # 挂载第一个 USB 摄像头 (/dev/video0)
        docker run -it --rm --device=/dev/video0 ubuntu:20.04 bash

        # 挂载串口设备 (例如 IMU)
        docker run -it --rm --device=/dev/ttyUSB0 ubuntu:20.04 bash

        # 挂载多个设备
        docker run -it --rm --device=/dev/video0 --device=/dev/ttyUSB0 ubuntu:20.04 bash
    10. --gpus (GPU 支持)

      • 允许容器访问宿主机的 NVIDIA GPU。这对需要 CUDA 加速的 SLAM 算法(如基于深度学习的特征提取、GPU 加速的优化)至关重要。
      • 前提: 宿主机需要安装 NVIDIA 驱动和 nvidia-docker2 (或称为 NVIDIA Container Toolkit)。
      • 示例:
      1
      2
      3
      4
      5
      6
      # 允许容器访问所有可用的 GPU
      docker run -it --rm --gpus all nvidia/cuda:11.4.0-base-ubuntu20.04 nvidia-smi
      # 上例使用了 NVIDIA 官方提供的包含 CUDA 的基础镜像,并运行 nvidia-smi 检查 GPU 是否可用
      # 你也可以在你自己的 ubuntu:20.04 镜像基础上,在容器内安装 CUDA Toolkit,然后用 --gpus all 启动
      docker run -it --rm --gpus all ubuntu:20.04 bash
      # (进入容器后需要自行安装 CUDA 和相关驱动)

4. 查看容器 (docker ps)

  • 作用: 列出正在运行的容器。
  • 语法: docker ps
  • 查看所有容器 (包括已停止的):
    • 语法: docker ps -a

5. 进入正在运行的容器 (docker exec)

  • 作用: 在一个已经在后台运行的容器内部执行命令。
  • 语法: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
  • 常用选项:
    • -i, -t: 与 docker run 中的含义相同,通常一起使用 (-it) 来获取交互式 Shell。
  • 示例:
    • 进入我们之前后台运行的 my-nginx 容器,并启动一个 bash shell:执行后你将进入 my-nginx 容器的 Shell。输入 exit 退出该 Shell,但容器本身仍在后台运行。

      1
      docker exec -it my-nginx bash

6. 管理容器 (停止、启动、删除)

  • 停止容器:
    • 语法: docker stop <容器ID或名称>
    • 示例: docker stop my-nginx
  • 启动已停止的容器:
    • 语法: docker start <容器ID或名称>
    • 示例: docker start my-nginx (容器会继续在后台运行)
  • 删除容器:
    • 注意: 只能删除已停止的容器。
    • 语法: docker rm <容器ID或名称>
    • 示例: (先停止) docker stop my-nginx (再删除) docker rm my-nginx
    • 强制删除运行中的容器 (不推荐,除非你知道后果): docker rm -f <容器ID或名称>
  • 清理所有已停止的容器:
    • 语法: docker container prune (会提示确认)

7. 删除镜像 (docker rmi)

  • 作用: 删除本地的一个或多个镜像。
  • 注意: 如果有容器 (即使是已停止的) 正在使用该镜像,需要先删除这些容器才能删除镜像。
  • 语法: docker rmi <镜像ID或仓库名:标签>
  • 示例:
    • 删除 hello-world 镜像: docker rmi hello-world:latest
    • 删除 ubuntu:22.04 镜像 (如果之前没有基于它创建并保留的容器): docker rmi ubuntu:22.04
  • 清理悬空镜像 (dangling images): 这些是没有标签且没有被任何容器使用的镜像层,通常是构建过程中产生的中间层或旧版本。
    • 语法: docker image prune

我自己的实例

1. 需要用gpu的话先配置gpu

https://blog.csdn.net/GritYearner/article/details/133679403

1
2
3
4
5
6
7
8
9
10
11
12
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list \
&& \
sudo apt-get update
sudo apt-get install -y nvidia-docker2
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
测试
sudo docker run --rm --gpus all nvidia/cuda:11.0.3-base-ubuntu20.04 nvidia-smi

2. 利用鱼香ros的镜像启动一个容器

1
2
3
4
5
6
7
8
9
10
11
12
sudo docker run -dit \
--gpus all \
-e NVIDIA_DRIVER_CAPABILITIES=all \
--name=[your_container_name] \
--privileged \
-v /dev:/dev \
-v /home/[your_username]:/home/[your_username] \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY=unix$DISPLAY \
-w /home/[your_username] \
--net=host \
fishros2/ros:noetic-desktop-full

这里注意不要直接使用鱼香的容器,他的默认的启动跟这个不太一样。启动时候的这些指令一旦运行,就不能再改了,最好是删除容器重新从镜像用正确指令生成一个容器,实在不想放弃这个容器的话就通过将这个容器生成为新的镜像,再从新镜像用正确指令生成容器

1
docker commit 容器名 新镜像名:标签

要使用图形界面需要在外面终端运行

1
2
3
4
# 这是鱼香ros默认运行的
xhost +local: >> /dev/null
# 这是gpt推荐的
xhost +local:docker

3. 已经装好了ORB-SLAM3和ROS的镜像

我通过之前的鱼香镜像生成容器配置好orbslam3自己做了一个镜像上传到了dockerhub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker login
docker pull 用户名/仓库名:标签
sudo docker run -dit \
--gpus all \
-e NVIDIA_DRIVER_CAPABILITIES=all \
--name=rosslam \
--privileged \
-v /dev:/dev \
-v /home/xfy/docker-ros:/home/rosslam/workspace \ //记得改这个挂载目录
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY=unix$DISPLAY \
-w /home/rosslam \
--net=host \
xuefeiyang/fyslam:slam3WithRos
  1. sudo docker run:

    • sudo: 以超级用户(root)权限执行后面的命令。运行 Docker 命令,特别是涉及硬件访问(如 GPU、设备挂载 /dev)或修改网络设置(--net=host)时,通常需要 sudo 权限。
    • docker run: 这是 Docker 的核心命令,用于根据指定的镜像创建一个新的容器并运行它。
  2. -dit: 这是三个选项的缩写合并:

    • -d (--detach): 让容器在后台运行(Detached mode)。执行此命令后,你会立即返回到宿主机的终端提示符,而不是直接进入容器的 Shell。容器会在后台保持运行状态。这对于运行需要长时间提供服务的容器(如 ROS Master、模拟器)很有用。
    • -i (--interactive): 保持标准输入(STDIN)对容器开放,即使没有连接到容器。这通常与 -t 一起使用,以便后续可以通过 docker attachdocker exec 进行交互。
    • -t (--tty): 为容器分配一个伪终端(pseudo-TTY)。这使得你后续通过 docker exec 进入容器时,能获得一个类似真实终端的交互体验(例如,有命令提示符、支持 Tab 补全等)。
    • 组合效果: -dit 启动一个后台运行的容器,但保持其交互接口可用,方便你之后使用 docker exec -it rosslam bash 等命令进入容器内部进行操作。
  3. --gpus all:

    • 允许这个容器访问宿主机上所有可用的 NVIDIA GPU。
    • 前提: 你的宿主机需要正确安装 NVIDIA 显卡驱动,并且安装了 NVIDIA Container Toolkit (nvidia-docker2)。
    • 用途: 对于需要 CUDA 进行加速的 SLAM 算法(例如,基于深度学习的特征提取、GPU 加速的后端优化、仿真渲染等)至关重要。
  4. -e NVIDIA_DRIVER_CAPABILITIES=all:

    • -e (--env): 设置容器内部的环境变量。
    • NVIDIA_DRIVER_CAPABILITIES=all: 这个特定的环境变量告诉 NVIDIA 驱动程序,允许容器使用驱动程序的所有功能(如图形、计算、工具、视频编解码等)。通常与 --gpus 选项配合使用,以确保容器内应用能充分利用 GPU 能力。
  5. --name=rosslam:

    • 给这个新创建的容器指定一个名字,叫做 “rosslam”。
    • 好处: 你可以使用这个名字来方便地管理容器,例如 docker stop rosslamdocker start rosslamdocker logs rosslamdocker exec -it rosslam bash 等,而不需要记住 Docker 自动生成的长 ID。
  6. --privileged:

    • 赋予这个容器扩展的权限。这基本上禁用了容器和宿主机之间的大部分安全隔离机制。
    • 效果: 容器几乎拥有与宿主机上 root 用户相同的权限,可以访问宿主机的所有设备(/dev 下的所有内容),修改内核参数等。
    • 使用场景: 通常是为了确保容器能够无障碍地访问各种硬件设备(摄像头、IMU、LiDAR 等),尤其是在 -v /dev:/dev 挂载了整个设备目录的情况下,确保权限足够。
    • 注意: 这是一个强大的选项,但有安全风险。如果可能,优先考虑使用更精细的 --device 选项来只挂载必要的设备,而不是使用 --privileged。但有时为了方便或解决特定权限问题会使用它。
  7. -v /dev:/dev:

    • -v (--volume): 将宿主机的目录或文件挂载到容器内部。这里是挂载宿主机的 /dev 目录到容器的 /dev 目录。
    • 目的: 让容器可以直接访问宿主机上的所有硬件设备文件。这对于 SLAM 来说非常方便,因为你可以直接在容器内像在宿主机上一样使用 /dev/video0(摄像头)、/dev/ttyUSB0(串口设备如 IMU 或 LiDAR)等设备。
    • --privileged 的关系: 这个挂载让容器 看到 设备文件,而 --privileged 通常用来确保容器 有权限 去操作这些设备文件。
  8. -v /home/xfy/docker-ros:/home/rosslam/workspace:

    • 这是另一个卷挂载。
    • 宿主机路径: /home/xfy/docker-ros这是你宿主机上的一个具体目录,其他人使用时需要修改成他们自己的路径!
    • 容器内路径: /home/rosslam/workspace
    • 目的: 这是实现代码和数据共享的关键。你在宿主机的 /home/xfy/docker-ros 目录下修改代码、存放数据,这些内容会实时同步到容器内的 /home/rosslam/workspace 目录,反之亦然。这样你可以在宿主机使用你喜欢的编辑器,而在容器内编译和运行。容器删除后,这个目录下的工作成果依然保存在宿主机上。
  9. -v /tmp/.X11-unix:/tmp/.X11-unix:

    • 挂载宿主机的 X11 Unix Domain Socket 目录到容器内部对应的位置。
    • 目的: 这是实现 X11 转发(让容器内的 GUI 程序显示在宿主机屏幕上)的关键步骤之一。GUI 程序通过这个 Socket 与宿主机的 X Server 通信。
  10. -e DISPLAY=unix$DISPLAY:

    • 设置容器内的 DISPLAY 环境变量。
    • $DISPLAY: 这会取用你当前宿主机DISPLAY 环境变量的值(通常是 :0:1 等)。
    • unix: 有时会加上这个前缀,但很多时候直接 -e DISPLAY=$DISPLAY 也能工作。
    • 目的: 告诉容器内的 GUI 应用程序应该将图形界面发送到哪个显示服务器(即你的宿主机屏幕)。需要与上一步的 X11 Socket 挂载配合使用。
    • 前提: 在运行这个 docker run 命令之前,你可能需要在宿主机上执行 xhost +local:docker (或者更安全的特定容器授权命令)来允许来自 Docker 容器的 X11 连接。
  11. -w /home/rosslam:

    • -w (--workdir): 指定容器启动后的默认工作目录。
    • 效果: 当你使用 docker exec -it rosslam bash 进入容器时,你的 Shell 会话将直接从容器内的 /home/rosslam 目录开始,而不是默认的根目录 /。这通常是为了方便,让你直接进入项目相关的主目录。
  12. --net=host:

    • 让容器共享宿主机的网络命名空间(Network Namespace)。
    • 效果: 容器不会获得自己独立的 IP 地址,而是直接使用宿主机的网络接口和 IP 地址。容器内监听的端口会直接暴露在宿主机的对应端口上,无需使用 -p 进行端口映射。
    • 对 ROS/SLAM 的好处: 极大地简化了网络配置。容器内的 ROS 节点可以像在宿主机上运行一样,轻松地被局域网内(包括宿主机)的其他 ROS 节点发现和通信(例如 RViz 连接容器内的 ROS Master)。也方便访问连接到宿主机的网络摄像头等设备。
    • 缺点: 降低了网络隔离性。
  13. xuefeiyang/fyslam:slam3WithRos:

    • 这是用来创建容器的镜像(Image)。
    • xuefeiyang/fyslam: 镜像的仓库名/用户名和镜像名,这看起来是一个用户(xuefeiyang)构建并可能分享在 Docker Hub 或私有仓库的自定义镜像。
    • slam3WithRos: 镜像的标签(Tag),通常表示镜像的版本或者配置。这个名字暗示了这个镜像里面很可能已经预装了 ROS (Robot Operating System) 和一些 SLAM 相关的库或工具。

总结

这个命令启动了一个名为 rosslam 的后台容器,该容器基于 xuefeiyang/fyslam:slam3WithRos 镜像。它被赋予了很高的权限 (--privileged),可以直接访问宿主机的所有 GPU (--gpus all) 和所有设备 (-v /dev:/dev),并且共享宿主机的网络 (--net=host)。它还设置了 GUI 转发 (-v /tmp/.X11-unix, -e DISPLAY),并将宿主机的 /home/xfy/docker-ros 目录映射到容器的 /home/rosslam/workspace 以共享工作文件。容器启动后的默认工作目录是 /home/rosslam