docker
Docker
安装
Windows下安装包及下载的镜像之类的默认是放在C盘,通过以下方式可以安装到别的盘
1 | start /w "" "Docker Desktop Installer.exe" install -accept-license --installation-dir="F:\Docker" --wsl-default-data-root="F:\Docker\wsl-data" --windows-containers-default-data-root="F:\Docker-windows-data" |
常用CLI指令
镜像相关
- docker pull:下载一个镜像
- docker build -t
:编译一个镜像 - docker imags:查看所有已有镜像
- docker image rm
:删除一个镜像 - docker commit :把当前容器导出成一个镜像
容器相关
- docker run:创建并运行一个容器,处于运行状态,它包含以下常见指令:
- 基础参数:
- -d:后台运行容器
- -it:交互终端运行
- –name:指定容器名称
- –rm:容器退出后自动删除
- -w:设置工作目录
- 资源限制相关
- –gpus all:给容器共享GPU
- 网络配置:
- -p:端口映射
- –net:网络模式
- 环境变量:
- -e / –env: 设置环境变量
- 用户:
- -u:指定用户
- 基础参数:
- docker pause:让一个运行的容器暂停
- docker unpause:让一个容器从暂停状态恢复运行
- docker stop:停止一个运行的容器
- docker start:让一个停止的容器再次运行
- docker rm:删除一个容器
- docker ps:查看当前正在运行的容器
- docker ps -a:查看所有容器(包含已经停止了的)
- docker exec:在运行中的容器执行命令,一般用来进入一个创建了的容器,它的参数其实和run命令的差不多
- ctrl + D:==退出容器==
数据卷相关
- docker volume create:创建一个数据卷
- docker volume ls:列出所有数据卷
- docker volume inspect :查看某个数据卷
- docker volume rm :删除某个数据卷
为什么要用数据卷?
- 容器里的数据和镜像是无关的,如果使用镜像创建个新的容器,是无法继承原来的数据的,数据卷相当于在容器里创建个软链接,真正存储的位置是宿主机的磁盘,这样多个容器就可以共享数据了,删掉某个容器数据也不会消失,直到显式删掉数据卷
删除缓存
1 | docker system df # 查看各部分占用磁盘大小 |
Dockerfile
如果我们要制作自己的景象,则需要编写dockerfile文件并进行编译,它的常见指令如下:
| 指令 | 说明 | 示例 |
|---|---|---|
| FROM | 指定基础镜像 | FROM ubuntu:20.04 |
| RUN | 执行命令并创建新的镜像层 | RUN apt-get update && apt-get install -y python3 |
| COPY | 复制宿主机的文件/目录到镜像中 | COPY . /app |
| ADD | 类似COPY,但支持URL和自动解压 | ADD https://example.com/file.tar.gz /tmp/ |
| CMD | 容器启动时的默认命令(可被docker run覆盖) |
CMD ["python", "app.py"] |
| ENTRYPOINT | 容器启动时的主要命令(不会被docker run覆盖) |
ENTRYPOINT ["python"] |
| ENV | 设置环境变量 | ENV PYTHONUNBUFFERED=1 |
| ARG | 定义构建时的变量(仅在构建过程中有效) | ARG VERSION=1.0 |
| WORKDIR | 设置镜像的工作目录,相当于在镜像中运行cd,dockerfile中后续指令以及进入容器时都在该目录下 |
WORKDIR /app |
| EXPOSE | 声明容器运行时监听的端口 | EXPOSE 80 |
| VOLUME | 创建挂载点 | VOLUME /data |
| USER | 指定运行容器时的用户名或UID | USER nobody |
参考链接:
环境搭建步骤示例
1.拉取docker镜像
1 | docker pull dockerpull.org/ubuntu:20.04 |
- 通过这种方式使用镜像站
dockerpull.org - 下载完成后可以通过
docker images查看是否拉取成功
2.创建容器
1 | # 创建新容器 |
--name: 给容器起一个名字,比如叫做risc-v-it: 交互模式(-i)和分配一个终端(-t)-d: 后台运行容器--net host:使用主机的网络堆栈,与主机共享网络配置-v:挂载数据卷
3.进入容器
1 | # 启动容器(关机的话容器就进入stop态了,需要重新启动容器) |
-u root:以root用户登录,后面就不需要sudo了。如果普通的-u则是非root用户
4.容器换源
Docker内的Ubuntu默认使用的是apt官方的源,下载东西很慢,所以要换国内源
如何更换?
- 原理:修改
/etc/apt/sources.list
可以通过下面的方式实现:
1 | sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list |
5.下载依赖
1 | apt-get update |
到此环境就搭建完毕了!后续直接运行enter_container.sh进入容器就可以了
镜像在硬盘中的存储方式
不用担心镜像过多太占磁盘,新镜像只存增量部分,其余部分和父镜像占用一个磁盘空间
Docker 镜像是由多层(layers)组成的,每一条 Dockerfile 指令(如 RUN, COPY, ADD)都会生成一个新层
每一层都被缓存成一个只读的、可复用的文件系统层。Docker 引擎在构建镜像时会尽可能重用已有层来节省磁盘空间
可以用docker history <image>来看某镜像使用了哪些层
遇到的问题
容器内如何使用GPU
法一:
- 1.创建容器时要用GPU,如果创建时没使用GPU,就得重新创建个新的容器了
- 2.容器内手动安装NVIDIA Container Toolkit并重启docker
1 | # 配置软件源 |
法二:
- 直接使用装好了CUDA相关库的镜像
重新启动容器ROS2不见了
- 每次启动时,需要手动运行ROS2里的
setup.sh来添加环境变量
1 | enter_container: |
宿主机如何显示容器内的GUI程序
参考资料:
这个问题其实和Linux的图形栈强相关,只有理解了Linux图形栈的结构,才能明白到底如何解决
Linux下每个GUI程序都可以看成是X11的Client,其通过X11协议将要显示的内容发给X11 Server,又其控制底层硬件进行GUI的显示。Linux自带了X11 Server,所以设置好套接字的IP和端口就可以显示容器的GUI了,而Windows没有默认X11 Server,所以稍微麻烦点
Linux宿主机
1.宿主机允许容器访问X11
1 | xhost +local:root # 允许本地 root 用户访问 X11 |
2.创建和启动容器时,设置挂载和环境变量
1 | docker run -it \ |
Windows宿主机
需要在有X11server(比如MobaXterm或者VSCode的DevContainer插件)的环境中进入docker
法一:VSCode的DevContainer插件,直接就可以显示
法二:MobaXterm
在docker容器的shell中,执行:
1 | export DISPALY=<你的宿主机的IP(不是WSL)>:0 |
之后就可以显示了
可以下载x11-apps进行测试,里面自带很多CLI工具比如xlock
宿主机无法修改容器内创建的文件
引起这个问题的原因是进入容器的时候使用的是
root用户,这样容器内创建的文件都是root的,而宿主机默认不是root用户,所以就访问不了解决办法:进入容器的时候使用和宿主机一样的用户。但是普通镜像并不会包含宿主机用户的账号密码,如果直接在创建容器时设置使用的用户,会出现密码错误的问题。必须重新创建个镜像,添加用户信息
1 | FROM osrf/ros:humble-desktop-full-jammy |
使用docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t my_ros2 .编译上述Dockerfile
1 | create_container: |
过度共享导致隔离降低
在上面的例子中,创建容器的时候挂载了/home/$$(whoami),这其实导致了过度共享,容器可以访问宿主机的可执行文件和环境,破坏了容器的隔离性
如果宿主机的一些程序(可执行文件)放在~下,那么容器内就可以直接访问了,比如宿主机在~安装的miniconda就直接到容器内了,这会污染容器的python环境
解决办法:
- Ubuntu下很多安装的软件的路径都会被添加到环境变量里,可以通过修改环境变量来消除某软件的可见性从而避免污染。比如miniconda其实就是在
~/.bashrc中被添加的,那么我们在启动bash时,可以通过--norc来不执行它



