docker 随笔

 

使用 docker的一些体会

容器的使用范围

docker 中最重要的两个概念就是容器 container和镜像 image。对于镜像,我的理解是镜像相当于硬件设施,一旦构建,不能改变,容器相当于操作系统,运行在镜像之上,与通用的操作系统不同的是,容器只提供有限但足够的功能,为应用的运行提供一致的环境。因此,容器应该主要是作为一种虚拟的环境来使用,而不能把它当作真正的操作系统,比如容器本身不能提供文件的挂载,依赖宿主的上下文信息等。

如果docker的使用还有限制,显然是不合理的,那么多优秀的开发者也能够想到这些问题,为啥不能去除限制?我们使用docker,最主要的因素就是隔离宿主环境以及程序的可移植性。docker的限制主要是一种规范化的目的,比如我想在Dockerfile中挂载本地目录到容器中,这个功能看似比较容易想到,但是官方是禁止这样的行为的,未提供相关的命令支持,主要目的就是防止开发者破坏docker的可移植性,如果在Dockerfile中挂载外部目录,会导致在另一个环境中难以复现,而且 docker 主要应用在生产环境中,例如 tomcat web应用部署等,因此不需要实时更改代码,代码的copy可以在 Dockerfile 中使用COPY命令。不同容器数据的共享也可以通过卷的形式,虽然这样会提高学习的成本,但是对于生产环境是有利的。

我目前使用docker主要是想用docker代替conda,conda虽然能够提供不同的python环境,但是仅限于python环境的隔离,如果有其他的依赖那么conda就无能为力了。因为是开发,需要修改代码,因此需要构建镜像,挂载本地代码目录,原本想通过 docker-compose 的方式一键构建环境,减少启动容器需要输入的指令,例如需要输入开放端口号等。但是发现docker-compose 这些docker全家桶对于开发不是很好的支持,容器的运行伴随程序的运行,如果没有程序在前台执行,容器立即终止。因此费了很大劲才勉强构建了一个基于Dockerfile的镜像,安装了代码的必要环境,例如 ssh服务 ,python依赖等,方便远程调试。

磨人的bug

今天遇到一个很奇怪的bug,大概是我的需求也比较奇怪吧,不常见。我构建了一个 cuda 环境的镜像,安装 openssh-server后,在外部主机能够通过ssh连接到容器中,但是奇怪的是,通过docker run 命令打开的shell下能够通过命令python test.py运行如下的程序:

# test.py
print("工分割")

但是通过ssh连接的shell,运行该脚本时却报这样的错:

Traceback (most recent call last):
  File "test.py", line 1, in <module>
    print("\u5de5\u5206\u5272")
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

如果 print() 是英文的就没有问题,ssh难道不支持中文?不可能啊。我首先测试了不同的镜像,又在不同的主机上测试了一遍,同时改变python版本,这几个方法都没有解决这个问题。我的Dockerfile如下:

FROM python:3.6
ENV LANG C.UTF-8
WORKDIR /workspace
COPY test.py id_rsa.pub ./
RUN apt-get update && apt-get install openssh-server -y 
RUN mkdir /root/.ssh \
    && cat id_rsa.pub >> /root/.ssh/authorized_keys \
    && rm id_rsa.pub
EXPOSE 22
# 后台执行ssh服务
ENTRYPOINT /etc/init.d/ssh start && /bin/bash

在脚本前面加上参数可以解决这个问题 PYTHONIOENCODING=utf-8 python test.py 但是,当我执行训练脚本时又出现ascii错误。 解决方案: 弄了半天,始终无法从配置层面上解决这个问题,只得低下头,通过改代码编码的方式。具体的讲是将 open(file) -> open(file,encoding='utf-8')

docker 命令

  • 清理无用镜像 docker rmi $(docker images | grep "^<none>" | awk "{print $3}")