一、Docker镜像的基本原理和构建方式
构建一个 Docker 镜像,最常用的有两种:
- 通过docker commit命令,基于一个已存在的容器构建出镜像。
- 编写 Dockerfile 文件,并使用docker build命令来构建镜像。
上面这两种方法中,镜像构建的底层原理是相同的,都是通过下面 3 个步骤来构建镜像:
- 基于原镜像,启动一个 Docker 容器。
- 在容器中进行一些操作,例如执行命令、安装文件等。由这些操作产生的文件变更都会被记录在容器的存储层中。
- 将容器存储层的变更 commit 到新的镜像层中,并添加到原镜像上。
1.1 Docker 镜像的构建方式使用场景和选型
docker commit
这种镜像构建方式通常用在下面两个场景中:
- 构建临时的测试镜像;
- 容器被入侵后,使用docker commit,基于被入侵的容器构建镜像,从而保留现场,方便以后追溯。
除了这两种场景,不建议使用docker commit来构建生产现网环境的镜像。
原因如下:
- 使用docker commit构建的镜像包含了编译构建、安装软件,以及程序运行产生的大量无用文件,这会导致镜像体积很大,非常臃肿。
- 使用docker commit构建的镜像会丢失掉所有对该镜像的操作历史,无法还原镜像的构建过程,不利于镜像的维护。
- Dockerfile 的操作流程可以通过
docker image history [镜像名称]
查询,方便开发者查看变更记录。
在实际开发中,使用Dockerfile来构建是最常用,也最标准的镜像构建方法.
使用 Dockerfile 构建镜像,本质上也是通过镜像创建容器,并在容器中执行相应的指令,然后停止容器,提交存储层的文件变更。和用docker commit构建镜像的方式相比,它有三个好处:
- Dockerfile 包含了镜像制作的完整操作流程,其他开发者可以通过 Dockerfile 了解并复现制作过程。
- Dockerfile 中的每一条指令都会创建新的镜像层,这些镜像可以被 Docker Daemnon 缓存。再次制作镜像时,Docker 会尽量复用缓存的镜像层(using cache),而不是重新逐层构建,这样可以节省时间和磁盘空间。
- 把这一切都放到一个 Dockerfile 里,既没有源码泄漏,又不需要用脚本去跨平台编译,还获得了最小的镜像。
1.2 Dockerfile入门知识
1.2.1什么是Dockerfile
Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。
- dockerfile是自定义镜像的一套规则
- dockerfile由多条指令构成,Dockerfile中的每一条指令都会对应于Docker镜像中的每一层。
docker build
命令用于从Dockerfile
构建映像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile。
docker build
命令会读取Dockerfile的内容,并将Dockerfile的内容发送给 Docker 引擎,最终 Docker 引擎会解析Dockerfile中的每一条指令,构建出需要的镜像。
1.2.2 Dockerfile的基本结构
Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释。
1.2.3 Dockerfile 常用的指令
Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。
1.FROM命令
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
- FROM:指定基础镜像,必须为第一个命令
- FROM指令用于指定基础镜像,ARG是唯一可以位于FROM指令之前的指令,一般用于声明基础镜像的版本。
- –platform选项可用在FROM多平台镜像的情况下指定平台。例如,linux/amd64、lunux/arm64、windows/amd64。
- AS name表示为构建阶段命令,在后续FROM和COPY --from=name说明中可以使用这个名词,引用此阶段构建的映像。
- tag或digest值是可选的。如果您省略其中任何一个,构建器默认使用latest标签。如果找不到指定tag,构建起将返回错误。
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras
2.LABEL
LABEL 指令将元数据添加到镜像。LABEL 是键值对。要在 LABEL 值中包含空格,请像在命令行中一样使用引号和反斜杠。
3.WORKDIR
用来指定当前工作目录(或者称为当前目录)
当使用相对目录的情况下,采用上一个WORKDIR指定的目录作为基准
相当于cd 命令,但不同的是指定了WORKDIR后,容器启动时执行的命令会在该目录下执行
4.RUN
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
RUN 主要用于在Image里执行指令,比如安装软件,下载文件等。
COPY指令从复制文件、目录到镜像文件系统的。
5.COPY
编写Dockerfile的时候copy宿主机文件到镜像中。
6.CMD
Dockerfile只允许使用一次CMD命令。使用多个CMD会抵消之前所有的命令,只有最后一个命令生效。一般来说,这是整个Dockerfile脚本的最后一个命令。
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
CMD有三种形式:
- CMD [“exec”,“param1”,“param2”]:使用exec执行,推荐方式。
- CMD command param1 param2:在/bin/sh中执行,可以提供交互操作。
- CMD [“param1”,“param2”]:提供给ENTRYPOINT的默认参数(极少这样使用)。
7.EXPOSE
EXPOSE指令通知容器在运行时监听某个端口,可以指定TCP或UDP,如果不指定协议,默认为TCP端口。但是为了安全,docker run命令如果没有带上相应的端口映射参数,Docker并不会将端口映射出去。
EXPOSE 80/tcp
EXPOSE 80/udp
指定映射端口方式:
docker run -P:将所有端口发布到主机接口,每个公开端口绑定到主机上的随机端口,端口范围在/proc/sys/net/ipv4/ip_local_port_range定义的临时端口范围内。
docker run -p :显式映射单个端口或端口范围。
8.ENTRYPOINT
ENTRYPOINT 指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有其他传入值作为该命令的参数。
一个Dockerfile中只能有一个ENTRYPOINT命令。如果有多条,只有最后一条有效。
无参的方式:
ENTRYPOINT [“/usr/sbin/nginx"]
指定参数的方式:
ENTRYPOINT [“/usr/sbin/nginx”, “-g”, “deamon off"]
docker run 的–entrypoint 标志可以覆盖原Dockerfile中的ENTRYPOINT 指令。
注意理解该命令, 该命令 是指定你每次 docker run启动容器的时候,都会自己执行的一个程序!!!。
CMD与ENTRYPOINT的关系
- CMD可以为ENTRYPOINT提供参数,ENTRYPOINT本身也可以包含参数,但是可以把需要变动的参数写到CMD里面,而不需要变动的参数写到ENTRYPOINT里面;
- ENTRYPOINT使用第二种shell方式会屏蔽掉CMD里面的命令参数和docker run后面加的命令。
- 在Dockerfile中,ENTRYPOINT指定的参数比运行docker run时指定的参数更靠前。
ENTRYPOINT/CMD最后一条命令为无限运行的命令:
这句话才是使用ENTRYPOINT的精髓。
在Docker Daemon模式下,entrypoint、cmd命令的最后一个命令,一定是要当前容器需要一直运行的,才能防止容器退出。
当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令。换句话说实际执行时,会变成 “”
二、标定docker镜像的搭建
docker build 命令用于使用 Dockerfile 创建镜像。
标定Dockerfile文件如下:
# author: huanghongjiang
# date: 2023-07-21
FROM osrf/ros:melodic-desktop-full
#更换源
RUN apt update &&\
cp /etc/apt/sources.list /etc/apt/sources.list.bak &&\
echo "deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic main restricted universe multiverse" > /etc/apt/sources.list &&\
echo "deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse" >> /etc/apt/sources.list &&\
echo "deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse" >> /etc/apt/sources.list &&\
echo "deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-security main restricted universe multiverse" >> /etc/apt/sources.list
&& apt update
#安装kalibr依赖
RUN apt-get update && DEBIAN_FRONTEND=noninteractive \
apt-get install -y \
git wget autoconf automake nano \
python3-dev python-pip python-scipy python-matplotlib \
ipython python-wxgtk4.0 python-tk python-igraph python-pyx \
libeigen3-dev libboost-all-dev
libsuitesparse-dev \
doxygen \
libopencv-dev \
libpoco-dev libtbb-dev libblas-dev liblapack-dev libv4l-dev \
python-catkin-tools
#安装imu-tk依赖
RUN apt-get install build-essential cmake libeigen3-dev libqt4-dev libqt4-opengl-dev freeglut3-dev gnuplot
#安装Ceres依赖
RUN apt-get install libgoogle-glog-dev libatlas-base-dev &&\
apt-get install software-properties-common &&\
使用位于当前目录下的Dockerfile创建标定docker镜像,标签为xxx:v1。
sudo docker build -t xxx:v1 .
三、上传docker镜像到阿里云
3.1 登录到阿里云的doker仓库
sudo docker login --username=**** registry.cn-shenzhen.aliyuncs.com
–username为阿里云的用户名,另外,密码为开通镜像服务时设置的密码
3.2 为本地docker镜像添加tag
sudo docker tag [镜像ID] registry.cn-shenzhen.aliyuncs.com/xyzxyz/image-test:[镜像版本号]
3.3 推送docker镜像到阿里云的镜像仓库
sudo docker push registry.cn-shenzhen.aliyuncs.com/xyzxyz/image-test:[镜像版本号]
3.4 从阿里云中拉取镜像
sudo docker pull registry.cn-shenzhen.aliyuncs.com/minmirror/minjdk:[镜像版本号]
四、多传感器标定docker镜像的使用方法
4.1 登录阿里云
sudo docker login --username=**** registry.cn-shenzhen.aliyuncs.com
4.2 拉取FPV相机多传感器标定编译环境docker镜像:
sudo docker pull <REPOSITORY_NAME>:<TAG_NAME>
4.3 编译方法
4.3.1 kalibr的编译方法
4.3.1.1下载kalibr源码到本地
mkdir -p ~/kalibr_workspace/src
cd ~/kalibr_workspace/src
git clone https://github.com/ethz-asl/kalibr.git
4.3.1.2 将本地的kalibr源码以及输入输出目录挂载到docker镜像,启动标定编译环境docker镜像
sudo docker run -it -v {your_path}/kalibr_workspace:/kalibr_workspace/ -v {your_path}/kalibr_input:/kalibr_input -v {your_path}/kalibr_output/:/kalibr_output--rm registry.cn-shenzhen.aliyuncs.com/xxx:v1 bash
4.3.1.3 在docker容器里进行kalibr源码编译
在docker容器下终端:
cd kalibr_workspace && \
catkin init && \
catkin config --extend /opt/ros/melodic && \
catkin config --cmake-args -DCMAKE_BUILD_TYPE=Release && \
catkin build -j16
4.3.1.4 在docker容器内运行kalibr相应程序
在docker容器下终端:
cd /kalibr_workspace &&\
source devel/setup.bash &&\
rosrun kalibr kalibr_calibrate_imu_camera \
--target /kalibr_input/april_6x6_80x80cm.yml \
--cam /kalibr_input/cam_april-camchain.yml \
--imu /kalibr_input/imu_adis16448.yml \
--bag /kalibr_input/imu_april.bag \
--bag-from-to 5 45
注:在docker容器中无法可视化结果,需要把对应可视化的代码屏蔽掉再执行。
4.3.2 imu_tk的编译方法
4.3.2.1 下载imu_tk源码到本地
git clone https://github.com/Kyle-ak/imu_tk.git
4.3.2.2 将本地的imu_tk源码以及输入输出目录挂载到docker镜像,启动标定编译环境docker镜像
sudo docker run -it -v {your_path}/imutk_workspace:/imutk_workspace/ -v {your_path}/imutk_input:/imutk_input -v {your_path}/imutk_output/:/imutk_output--rm registry.cn-shenzhen.aliyuncs.com/xxx:v1 bash
4.3.2.3 在docker容器里进行imu_tk源码编译
在docker容器下终端:
cd imu_tk && \
mkdir build && \
cd build && \
cmake .. && \
make -j16
4.3.2.4 在docker容器运行imu_tk相应程序:
在docker容器下终端:
cd /imu_tk/bin/ &&\
./test_imu_calib /imu_tk_input/xsens_acc.mat /imu_tk_input/xsens_gyro.mat