docker
Docker
常用CLI指令
镜像相关
- docker pull:下载一个镜像
- docker build -t
:编译一个镜像 - docker imags:查看所有已有镜像
- docker image rm
:删除一个镜像
容器相关
docker run:创建并运行一个容器,处于运行状态,它包含以下常见指令:
基础参数:
- -d:后台运行容器
- -it:交互终端运行
- –name:指定容器名称
- –rm:容器退出后自动删除
- -w:设置工作目录
资源限制
网络配置:
- -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
- 需要安装NVIDIA Container Toolkit并重启docker才行
1 | # 配置软件源 |
重新启动容器ROS2不见了
- 每次启动时,需要手动运行ROS2里的
setup.sh
来添加环境变量
1 | enter_container: |
容器内无法显示GUI
1.宿主机允许容器访问X11
1 | xhost +local:root # 允许本地 root 用户访问 X11 |
2.创建和启动容器时,设置挂载和环境变量
1 | docker run -it \ |
宿主机无法修改容器内创建的文件
引起这个问题的原因是进入容器的时候使用的是
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
来不执行它