spawn ./petalinux-v2014.4-final-installer.run $install_dir
expect "Press Enter to display the license agreements"
send "\r"
expect "*>*"
send "y\r"
expect "*>*"
send "y\r"
expect eof
第一行声明使用 expect 这个工具来解释此脚本,/usr/bin/env 会遍历 PATH 变量来寻找后面的可执行文件,这样避免了依赖于 expect 的安装路径。
第二行设置等待超时,因为 PetaLinux 的安装过程比较慢,这里将其设为 -1,即一直等待。
第三行设置一个变量来接收此脚本的参数,我们借此来指定希望将 PetaLinux 安装到哪个目录下。注意 expect 脚本设置参数的方式和 bash 脚本不同,参数 0 代表我们调用脚本时给出的第一个参数,而在 bash 脚本中,参数 0 代表脚本本身的名字。
第五行用 spawn 命令去执行安装程序。PetaLinux 的安装程序的第一个参数也是安装路径。
接下来我们用 expext 命令来捕获程序的输出,用 send 命令发送预设的输入。安装程序提示你确认协议的语句是这样的:Do you accept this license? [y/N] >,直接用 expext 匹配这一句会有问题,因为至少 [] 在 expect 的语法中是有特定含义的,需要转义。已无心情研究 expect 那奇怪的语法,所幸它有很棒的模糊匹配功能,我们只需要匹配最后一个 > 字符就可以了。
1.5 减小镜像体积
PetaLinux 的安装包在 Xilinx 官方网站上可以下载,但需要先注册,没有固定的下载链接。所以要么需要在构建前把它下载到本地,要么在互联网上寻找一个合适的托管地点,可以提供稳定的下载链接。
PetaLinux 的安装包比较大(2014.4 版有 1.2GB,而 2016.4 已经到了丧心病狂的 8.3 GB),在安装完成后,安装包再留在镜像中已经没有什么意义了,还会显著的增加镜像的体积。这里要理解 Docker 的镜像是由一个个的层(layer)组成的,Dockerfile 中的每一条指令都对应于一层,每一层都是在前一层的基础上进行的增量的改变。这意味着,一旦我们在某一层中引入了一个文件,即使在下一层中将其删除,对体积的减小也无济于事,我们只是无法在最终的容器中“看见”它们而已。如果我们使用 COPY 指令将 PetaLinux 的安装包添加进去,则 COPY 指令会生成一个层,我们无法再把它产生的体积抹除掉。Stack Overflow 上有一个关于这个问题的讨论6,主要提到了三种方式:一是在本地构建一个网络服务器,通过网络的方式传到 Docker 容器的内部,我采用了这种方式,后面详述;二是不能使用 Dockerfile 的方式构建容器,而是在容器中完成安装和清理工作后手动提交更改到镜像;三是使用第三方工具对生成的镜像进行再压缩。
这里使用网络是更好的方式,一方面如果我们在互联网或者私有服务器上存放了安装包,通过更改 installer_url 变量就可以使用新的地址获取文件;另一方面,在本地可以使用 Python 轻松的创建一个 HTTP 服务器。在 Dockerfile 中,我们使用 wget 下载安装包、配置其权限、运行自动安装脚本,最后删除安装包。这些步骤必须在一个 RUN 指令下完成,这样安装包才不会留在最终的镜像里。
WORKDIR $install_dir
COPY ./auto-install.sh .
RUN wget -q $installer_url/petalinux-v2014.4-final-installer.run && \
chmod a+x petalinux-v2014.4-final-installer.run && \
./auto-install.sh $install_dir && \
rm -rf petalinux-v2014.4-final-installer.run
在外部,我使用了一个脚本来封装启动 HTTP 服务器、构建 Docker 镜像、停止服务器的步骤:
#!/usr/bin/env bash
installer_dir=$1
docker_context=`pwd`
echo "Start to build petalinux tools docker image ..."
echo "-----------------------------------------------"
cd $installer_dir
python3 -m http.server &
server_pid=$!
cd $docker_context
installer_ip=`ifconfig docker0 | grep 'inet\s' | awk '{print $2}'`
docker build -t petalinux-docker:2014.4 \
--build-arg installer_url=${installer_ip}:8000 \
.
kill $server_pid
echo "---------------"
echo " Finish. ^_^ "
echo "---------------"
这个脚本的第一个参数是安装包在本地的路径。首先让服务器在后台建立,并记录下其对应的 pid,在完成镜像的构建后再将其杀死。Python 创建的服务器会默认监听 8000 端口。在容器内部(Docker 构建的过程即相当于在临时的容器中执行 Dockerfile 的过程)可以通过 Docker 的默认网桥(docker0)的 IP 地址来访问本地主机。网桥对应的 IP 地址并不是唯一的,Docker 是根据主机中网卡的配置不同,选择一个没有被占用的私有网段(如果 3 类私有 IP 网段都被占用了,Docker 启动时会报错),也可以自行更改,所以这里我们从 ifconfig 的输出中提取 docker0 对应得 IP 地址。docker build 命令中 -t 参数为镜像指定标签,--build-arg 参数用来覆盖我们在 Dockerfile 内部设置的参数,最后一个参数 . 指的是构建环境(build context)为当前路径。注意,如果你将安装包放在了这个构建环境的同一个目录下,一定要通过 .dockerignore 文件来忽略这个安装包文件,因为否则它会被发送到 Docker daemon 上,增加构建时间且毫无用处,除非你要使用 COPY 指令的方式导入安装包。
1.6 其他
PetaLinux 会检查 shell 环境,并推荐使用 Bash。在 Ubuntu:16.04 的镜像中 /bin/sh 这个软连接指向的是 /bin/dash,这里我们将其更改为 /bin/bash。
RUN ln -fs /bin/bash /bin/sh # bash is PetaLinux recommended shell
使用 WORKDIR 指令新建了一个 /workspace 的路径用于连接数据卷。最后一个 WORKDIR 指定的路径就会是进入容器后的所在路径,这一点似乎官方文档没有明说。
WORKDIR /workspac
1.7 镜像的构建
如果安装包放在本地,则如 1.5 节所述,使用 build-image.sh 脚本构建镜像。如果安装包在互联网或本地服务器上,则直接使用 docker build 命令,并使用 installer_url 参数指定访问地址。
2. 测试
你可以自行按照上面的方法自行构建镜像,也可以从 Docker Hub 上下载我上传好的镜像:
docker pull xaljer/petalinux:2014.4
运行容器:
docker run -ti -v /path/to/projects:/workspace xaljer/petalinux:2014.4
在容器中创建工程并编译:
petalinux-create -t project -s
-n
cd
petalinux-build # 构建整个工程,会比较慢
3. 现有问题和下一步工作
3.1 现有问题
PetaLinux 2014.4 支持的原本是 Ubuntu 14.04,但使用此版本的镜像时发现,其软件源似乎有些问题,经常安装失败,故没有使用。
PetaLinux 会提示找不到 tftp,这是因为没有对其进行进一步的配置。如果不使用 tftp 可以忽略这个问题。
在 Docker for Windows 下构建时,可能会出现错误7,将存储驱动更改为 aufs 后可修复。然而 Windows 下构建的镜像仍有其他问题,无法使用,作者尚未对其作更多的测试和探究。
3.2 下一步工作
添加 Vivado SDK 的一些工具。
PetaLinux 工具的名字都有点长,可以考虑在镜像里对常用的操作添加别名。但在容器外部作可能会更方便一些,因为我们不必交互式的进入容器,而是使用 docker exec 来执行命令,此时可以在容器外面为整个命令添加别名。
有了标准化环境,不仅可以在自己的电脑上运行,还希望放在私有服务器上,让大家通过网络访问。要达到这样的目的,一要能通过 SSH 访问容器,二要能在服务器的数据卷和本地计算机之间同步数据(源码及编译结果)。对于 SSH,初步设想可以通过外部的一些 Docker 工具来完成,而不是在容器内部建立 SSH 服务器,因为有多个容器时,要对应多个不同端口等问题。对于数据同步,可以在本地的 Windows 系统上通过 Linux 子系统(WSL)建立 NFS 服务器,在容器内部挂载 NFS,或者通过 Docker 的插件实现直接将远端的 NFS 作为数据卷挂载。
4. 总结
如果并不需要 Docker 的一些优势,我们也可以考虑将 PetaLinux 装进 Windows 的 Linux 子系统(WSL),这样可以有更好的性能和更无缝的操作。
使用虚拟机在 Windows 下搭建嵌入式开发环境是以往非常常用的方式,但也是一种比较笨重的方式。随着一些新的技术、平台的出现,如 Docker 和 WSL,我们可以尝试利用它们搭建开发环境,提升开发的效率。
参考
Dockerfile reference
Best practices for writing Dockerfiles
PetaLinux Reference Guide
unable to locate packag lib32bz2-1-0
how to run 32 bit app in ubuntu 64 bit
how to add a file to an image in dockerfile without using the add or copy direct
fails on ‘tar’ with: “Directory renamed before its status could be extracted”
电子发烧友App














评论