建议学习过程
值得考虑的问题
- @傅飞–Docker与虚拟机的区别
- @黄庆兵–如何精简压缩image
- 精简为王:Docker镜像体积详解
- 孤天浪雨–Docker实践(七):Docker Hub(镜像分发、自动化构建)
- 还有很多,以后工程上遇到再贴
按照学习的过程我造你肯定走马观花的差不多了,接下来还是仔细的实操一遍吧~
Docker 的一张神图
开局一张图,剩下全靠编~
当大概知晓
docker
的概念后,看这张图应该会非常的舒服,docker
以image
为基础,从下到上构建自己定制的image
,一般我们称为image layer
,里面可以add
各种环境,堆叠。而启动这个image
的最小单位为container
,一个image
可以有启动很多containers
,他们相互隔离,可以独立运行,container
上做出的改变,比如装个python
,如果不进行commit
到image
,那么退出后不会影响image
,当然也不会影响正在运行的其他container
了。远端是类似github
的一个仓库,用于存放官方和个人镜像,全世界都可以pull
和push
,进行分发和转送。image
的改变可以有两种方式,一是用dockerfile
进行build
,这是一种类似配置文件的东西,build
这个配置文件,命令一条条被执行,简洁快速。而另一种是由container
修改后进行commit
提交到image
,相当于对此修改进行从上到下的”覆盖”,虽然我们对第二种方式更加适应(就相当于操作服务器,而且调试的时候方便),但是对于工程规范严谨来说,建议使用第一种方式,原因我个人认为有三个,一是对日后review
来说,有个dockerfile
能够更清楚的了解自己构建的image是什么包含什么,二是给别人看的时候方便review
,相当于给了个README
,三是与github
关联进行自动化更新分发image
时,docker hub
只关心dockerfile
的更新。
Image 镜像相关
相关的资料太多了,以下只是在自己学习过程中的例子摆出来方便看
创建自己的新image
一般我们都是从原有的别人的或者官方的镜像也就是
image
上挂载自己所需要的东西,举一个最简单的例子,Docker hub
官方的Ubuntu
的image
都没有vim的说~
方式1:使用commit进行增量更新
比如我启动一个image
的container
,然后在这个container
上安装一个vim,并把这个拥有vim的增量打包成自己的新image
,装python
同理
- 搜索一下
docker hub
中的东西~,本质来说就是个类似于github的仓库啦
~> docker search ubuntu //这里docker支持模糊搜索
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating sys… 7198 [OK]
dorowu/ubuntu-desktop-lxde-vnc Ubuntu with openssh-server and NoVNC 159 [OK]
...
- 首先下载一个”底层”
image
,我们当做基础包,然后再做修改
~> docker pull ubuntu //这里下的是官方的image,官方的是没有前面那个用户名的,如dorowu/ubuntu-desktop-lxde-vnc表示docker用户名为dorowu的image名叫ubuntu-desktop-lxde-vnc的image
- 查看
image
~> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 0458a4468cbc 10 days ago 112MB
- *启动该
image
的一个container***
~> docker run -i -t 0458a4468cbc
root@b42e3c1b7bca:/# vim
bash: vim: command not found
root@b42e3c1b7bca:/# apt-get update && apt-get install -y vim
...
其中,-t
选项让Docker
分配一个伪终端(pseudo-tty
)并绑定到容器的标准输入上, -i
则让容器的标准输入保持打开,run
的作用相当于启动image
并且开辟一个容器container
,如果本地没有image
,那么它会去云端拉一个符合该名字的image
- 使用
commit
进行提交
~> docker commit -m 'install vim' --author='mrlevo' b42e3c1b7bca mrlevo/ubuntuvim:v1
sha256:1ab17ff9221c1dedd419d760135ce87a33a374cc0420590a13345796a3da4af5
~> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mrlevo/ubuntuvim v1 1ab17ff9221c Less than a second ago 209MB
其中root
后面的b42e3c1b7bca
是你这个container
唯一的标识符,是一个Hash值,在后续commit
的时候需要用到,提交commit
的方式和git
的方式几乎一致呢,git commit -m 'xxx update'
,也就是说image
会从你在当前container
操作当做增量进行image layer
的构建
方式2:使用Dockerfile
进行build
从这个
ubuntu
的基础上进行往上添加vim,你把它想做一台虚拟机,你怎么配置环境或者说怎么安装vim呢?apt-get install vim
,是不是简单轻松呢~,其实docker
的image
也一样,只是为了批量操作,比如说,你有装一堆环境,那么明智的做法肯定是写一个配置文件,然后直接运行这个配置文件让他批处理就行了是吧~,在docker
这里,这样的文件叫做Dockerfile
*
~/myubuntu> cat Dockerfile //我已经写好了这个文件,是放在自己的一个叫做myubuntu的文件夹中~
# ubuntu 14.04 with vim and gcc
FROM ubuntu
MAINTAINER mrlevo [email protected]
RUN apt-get update && apt-get -y install vim
具体含义不再解释,一些个参数请直接自己参考Docker Dockerfile详解,这里不再赘述,只是参数而已嘛~大家按照规则写就ok辣,最核心的就是配置文件里写什么也就是这段话中的RUN
部分,我们的目的明确,就是装一个vim
而已,这里是不是和服务器上装vim
一样呢~
然后就开始
build
吧~
~/myubuntu> docker build -t mrlevo/newubuntu:v2 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu
---> 0458a4468cbc
Step 2/3 : MAINTAINER mrlevo [email protected]
...
Successfully built ab33d086befd
Successfully tagged mrlevo/newubuntu:v2
注意mrlevo/newubuntu:v2 .
中的.
千万别忘了,.
是Dockerfile
所在的路径(当前目录),也可以替换为一个具体的Dockerfile
的路径。而且为了你以后推到自己的仓库,名字的修改也比较重要,如mrlevo
表示DockerHub
上的用户名,newubuntu
是repo
的名字,v2
表示tag
号。若在创建docker image
的时候。其中-t
标记来添加tag
,指定新的镜像的用户信息,-t
选项不是mrlevo/newubuntu:v2
,而是newubuntu:v2
,那么即使用mrlevo
用户名登录了DockerHub
,最后也无法push
的。
注意!创建镜像的时候尽量不要用“docker commit”命令来创建。用这种办法建镜像是完全不可取的,因为这种办法是不能重复的。我们在建镜像的时候应该从Dockerfile创建,或者用其他S2I(从源文件构建镜像)的方式来创建,这样镜像才具有可再生性,而且如果我们把镜像存在git之类提供版本控制能的系统里的话,还可以对Dockerfile的改动进行跟踪。
上传自己的image到Docker Hub
先不说这样做明不明智(当然不),只是讲如何将自己的image推到自己的仓库
~> docker push mrlevo/newubuntu:v2
The push refers to repository [docker.io/mrlevo/newubuntu]
ae59a8755490: Pushed
6f4ce6b88849: Pushed
92914665e7f6: Pushed
c98ef191df4b: Pushed
9c7183e0ea88: Pushed
ff986b10a018: Pushed
然后登陆自己的
Docker hub
上去看看,其实就是和github
上push
项目是一样的
现在我们来讨论这样做成本其实很大,其实你只需要让对方拥有你的Dockerfile
就可以了,他可以直接下载官方的基础image
,然后bulid
你的Dockerfile
即可重构你的image
,没必要把自己的image
整个push
到docker hub
上(其实还有更骚的操作,就是github
上更新dockerfile
,然后docker hub
上自动化更新image
),浪费资源不说,还占带宽~,另一个解决的思路就是压缩image
的大小了,参考自@黄庆兵–如何精简压缩image,简单说就是将RUN
命令全部写在一起,使用&&
串联命令
一个实际的��~ 安装xgboost+python环境
举一个��,我要配置一个含有
xgboost
这个包的python3
环境,其他的都不需要,那么可以简化为在ubuntu
的image
上,装python
以及装第三方包
- 写
Dockerfile
配置文件,把要做的都写下来,思路可以是自己在操作虚拟机时候的操作,这里先不论是否高效。我先创建本地ubuntuxgboost
文件夹,然后再其中创建一个Dockerfile
文件,内容如下
~/ubuntxgboost> cat Dockerfile
# ubuntu 14.04 with vim and gcc
FROM ubuntu
MAINTAINER mrlevo [email protected]
RUN apt-get update && apt-get -y install python3 python3-pip
RUN pip3 install pandas scikit-learn==0.18.1 xgboost
当然建议是将RUN的命令用
&&
串起来,属于一种减少堆叠layer的方法
- 进行
build
~/ubuntxgboost> docker build -t mrlevo/ubuntuxgboost:v1 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu
...
Successfully built 8eddac9d6c4a
Successfully tagged mrlevo/ubuntuxgboost:v1
- 查看
images
~/ubuntxgboost> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mrlevo/ubuntuxgboost v1 8eddac9d6c4a 3 minutes ago 980MB
- 启动
image
并且开辟一个容器,查看环境是否可用
~/ubuntxgboost> docker run -i -t 8eddac9d6c4a
root@c2061b5db824:/# pip3 freeze
numpy==1.14.0
pandas==0.22.0
python-dateutil==2.6.1
pytz==2017.3
scikit-learn==0.18.1
scipy==1.0.0
six==1.11.0
xgboost==0.7.post3
root@c2061b5db824:/# python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import xgboost
>>>
注:这是一个非常现实的目的,如果需要有一个机器学习平台可供大家直接进行算法测试,那么这个方式就很有用了。别人就不需要在配置各种乱七八糟的环境而头疼了,更专注于实现本质
科学计算的开发环境已经有小伙伴们开源了,这就可以直接用,看,这就是docker的厉害之处~详见:如何使用 Docker 快速配置数据科学开发环境?
Container 容器相关
参考:@孤天浪雨 – Docker实践(二):容器的管理(创建、查看、启动、终止、删除)
若干问题
无法删除image问题
错误描述:
unable to remove repository reference
~> docker rmi newubuntu
Error response from daemon: conflict: unable to remove repository reference "newubuntu" (must force) - container 043b9f03b795 is using its referenced image d1ee33a2d62d
首先停止和删除容器,因为一个image上可运行很多container
$ docker stop $(docker ps -a | grep "Exited" | awk '{print $1 }') //停止容器
$ docker rm $(docker ps -a | grep "Exited" | awk '{print $1 }') //删除容器
$
为获取变量,其中grep
是全局正则匹配,先是匹配已经退出的container
。而awk
就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理,这里是获取container
的id
。
然后选择删除指定的image
$ docker rmi d1ee33a2d62d //删除IMAGE ID=d1ee33a2d62d的image
当然如果你是删掉为none的临时image
$ docker rmi $(docker images | grep "none" | awk '{print $3}') //删除为none的中间镜像
PS. 当然如果要删除所有容器,那么只需要 先获取所有容器的
CONTAINER ID
,然后进行批量删除,注意这里是rm
而不是rmi
~> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c2061b5db824 8eddac9d6c4a "/bin/bash" 30 hours ago Exited (0) 30 hours ago vigilant_albattani
~> docker ps -a -q
c2061b5db824
~> docker rm $(docker ps -a -q)
c2061b5db824
~> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Image 精简压缩
镜像层依赖于一系列的底层技术,比如文件系统(filesystems)、写时复制(copy-on-write)、联合挂载(union mounts)等,详见@黄庆兵–如何精简压缩image
每次在Dockerfile中执行RUN命令的时候系统都会在镜像中新建一个层,每个镜像层都会占用一定的磁盘空间可以使用history观察构建的命令及产生的磁盘空间
~> docker history 885fd3133327
IMAGE CREATED CREATED BY SIZE COMMENT
885fd3133327 5 hours ago /bin/sh -c apt-get update && apt-get -y inst… 260MB
<missing> 5 hours ago /bin/sh -c apt-get update && apt-get -y inst… 97.5MB
<missing> 5 hours ago /bin/sh -c #(nop) MAINTAINER mrlevo mrlevo@… 0B
<missing> 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 2 weeks ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 2.76kB
<missing> 2 weeks ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 2 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B
<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:a3344b835ea6fdc56… 112MB
解决方案总结为以下几点
使用更小的基础
image
,如使用debian
替换ubuntu
Dockerfile
中的RUN
指令通过&&
和\
支持将命令串联在一起- 在
yum update
和apt-get update
必须时,把update
和清理命令都放在同一行RUN
底下,在执行update
的同时释放多余的空间 - 用命令或工具压缩
image
,docker
自带的一些命令还能协助压缩镜像,比如export
和import
。工具如docker-squash,用起来更简单方便,并且不会丢失原有镜像的自带信息。
Docker hub 与 Github 的互联,自动化构建image
详细请参考
按照步骤来没什么问题,使用git更新存放在
github上
的dockerfile
文件,然后docker hub
会根据dockerfile
在服务器云端开始构建image
,值得注意的是,建议一个工程一个github
的repo
,这样方便管理和更新。如果不打上tag
进行dockerfile
的更新,则把两个版本拉倒本地会出现旧版的tag显示为<none>
的情况,而最新版为latest
~> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mrlevo/dockerfiles latest 885fd3133327 16 minutes ago 469MB
mrlevo/dockerfiles <none> 56fcce54eb10 32 minutes ago 209MB
以下是我更新
github
上的的dockerfile
文件后,docker hub
自己关联github
并进行image
的更新,可以看到dockerfile
更新的内容。从Dockerfile
可以至少知道这个版本里面是个啥了~,不过貌似我没看到更新日志。看来只能从github
的commit
上看了
Docker 镜像的版本控制
如果需要升级某个docker
镜像,我们可以这样做。
- 给每个新生成的镜像都打上相应版本的tag。此时可能存在
image:latest
、image:v1
、image:v2
等。 - 我们要从v1升级到v2,首先我们将导入的v2镜像强制重命名为image:latest,命令为
docker tag -f image:v2 image:latest
docker stop
之前正在运行的容器- 启用
docker run image
,此时image
的等价镜像image:latest
就是最新的V2镜像。
总结下步骤:load/tag/stop/run
致谢
哎呀,越来越懒了,上面超链接的都是哦~谢谢大家的无私贡献才能不断添砖加瓦!