用podman构建ML学习环境
引子
之所以想要做这个项目是因为看到了微软的ML For Beginners课程。
课程分本身的课件部分和练习部分。课件部分在github上和gitee上看都不太舒服,最好还是下载下来通过docsify本地起服务器来看。 而练习部分需要一个Jupyter Notebook环境,单独配置也很麻烦, 不如直接用他们提供的镜像。
另外,好玩起见,我使用buildah来构建、podman来运行、systemd来管理服务的方式。正常情况下用docker和docker compose可以省掉很多事。 不建议模仿。
镜像准备
课件部分
课件部分需要从托管的网站上git clone
课件,再用docsify本地起服务器,而docsify则是需要通过npm install docsify-cli -g
来构建。
在没有npm环境的情况下,为了安装这一个软件就安装一堆杂七杂八的东西,让我有些许难受,因此我决定容器化这一部分。
这一部分自然是需要自行构建。所以我使用docker的多阶段构建功能,写了如下的Dockerfile:
FROM docker.io/alpine/git AS cloner
WORKDIR /app
# 通过码云的镜像下载
RUN git clone https://gitee.com/mirror_microsoft/ML-For-Beginners.git
FROM docker.io/node:lts-slim
# 通过腾讯云镜像加速
RUN npm config set registry http://mirrors.cloud.tencent.com/npm/
RUN npm install docsify-cli -g
COPY --from=cloner /app/ /app
EXPOSE 3000
CMD ["docsify", "serve", "app/ML-For-Beginners"]
之后,只需要用buildah构建即可:buildah bud -f dockerfile -t ml-for-beginners
。
使用方法也很简单:podman run -d -p 3000:3000 --rm=true --name=ml-for-beginners localhost/ml-for-beginners
。
练习部分
练习部分我们选择现成的scipy-notebook。
使用方法中只有两个额外参数,一个是为了挂载卷:-v jupyter-volume:/home/jovyan/work
;另一个是为了使用Jupyter Lab:-e JUPYTER_ENABLE_LAB=yes
,
因为Jupyter Notebook将要过气。
连起来就是:podman run -d -p 3001:8888 -v jupyter-volume:/home/jovyan/work -e JUPYTER_ENABLE_LAB=yes --rm=true --name=jupyter docker.io/jupyter/scipy-notebook
。
配置服务
我使用的操作系统是Debian 11,不过大多数的操作系统都是用systemd来管理服务,因此以下内容应该也差不多。
首先,podman非常友好的准备了podman generate systemd
这个命令。只要我们有一个正在运行的容器,我们就可以基于它生成systemd的配置文件。
根据是否添加--new
参数,生成的配置文件会有区别:
- 如果不加,那么就是生成控制当前正在运行的容器的命令
- 如果添加,那么就是生成可以从头开始运行容器的命令
因此,我们需要分别将两个容器根据上述命令跑起来,之后运行podman generate systemd --new -n -f <container>
两次来获得配置文件。
然后,就是podman不太友好的部分了。podman的一大特点就是rootless,也就是不需要根权限也可以运行。但是带来的问题就是镜像库等均是分离的。
我们之前编译的ml-for-beginners这个镜像如果使用sudo podman images
的话是看不到的。为了把这个镜像给root送过去,我们需要执行:
podman save ml-for-beginners | sudo podman load
。
同样的,root下也需要重新拉取一遍scipy-notebook。
最后就到了设置服务的部分。把生成的配置文件放到/etc/systemd/system
下,
然后运行systemctl enable <.service>
和systemctl start <.service>
。这样我们的服务就跑起来,并且会在开机时自启动了。
总结
这一路上因为podman的特殊性踩了不少坑,尤其是用户之间不共享镜像库的问题,因此如果用docker会简单些。 但是总归还是跑起来了,并且顺便熟悉了下systemd,也算是小有收获。