Docker 学习笔记

作者: mrlevo520 | 来源:发表于2018-04-23 21:48 被阅读83次

    建议学习过程

    1. @S_gy_Zetrov--一篇很棒的入门教程
    2. Docker — 从入门到实践--粗略的看下,各个名词部分,进阶部分可以看情况看
    3. @孤天浪雨--Docker系列,建议从第一篇开始看

    值得考虑的问题

    1. @傅飞--Docker与虚拟机的区别
    2. @黄庆兵--如何精简压缩image
    3. 精简为王:Docker镜像体积详解
    4. 孤天浪雨--Docker实践(七):Docker Hub(镜像分发、自动化构建)
    5. 还有很多,以后工程上遇到再贴

    按照学习的过程我造你肯定走马观花的差不多了,接下来还是仔细的实操一遍吧~

    Docker 的一张神图

    开局一张图,剩下全靠编~

    image

    当大概知晓docker的概念后,看这张图应该会非常的舒服,dockerimage为基础,从下到上构建自己定制的image,一般我们称为image layer,里面可以add各种环境,堆叠。而启动这个image的最小单位为container,一个image可以有启动很多containers,他们相互隔离,可以独立运行,container上做出的改变,比如装个python,如果不进行commitimage,那么退出后不会影响image,当然也不会影响正在运行的其他container了。远端是类似github的一个仓库,用于存放官方和个人镜像,全世界都可以pullpush,进行分发和转送。image的改变可以有两种方式,一是用dockerfile进行build,这是一种类似配置文件的东西,build这个配置文件,命令一条条被执行,简洁快速。而另一种是由container修改后进行commit提交到image,相当于对此修改进行从上到下的"覆盖'',虽然我们对第二种方式更加适应(就相当于操作服务器,而且调试的时候方便),但是对于工程规范严谨来说,建议使用第一种方式,原因我个人认为有三个,一是对日后review来说,有个dockerfile能够更清楚的了解自己构建的image是什么包含什么,二是给别人看的时候方便review,相当于给了个README,三是与github关联进行自动化更新分发image时,docker hub只关心dockerfile的更新。

    Image 镜像相关

    相关的资料太多了,以下只是在自己学习过程中的例子摆出来方便看

    创建自己的新image

    一般我们都是从原有的别人的或者官方的镜像也就是image上挂载自己所需要的东西,举一个最简单的例子,Docker hub官方的Ubuntuimage都没有vim的说~

    方式1:使用commit进行增量更新

    比如我启动一个imagecontainer,然后在这个container上安装一个vim,并把这个拥有vim的增量打包成自己的新image,装python同理

    1. 搜索一下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]
    ...
    
    1. 首先下载一个"底层"image,我们当做基础包,然后再做修改
    ~> docker pull ubuntu  //这里下的是官方的image,官方的是没有前面那个用户名的,如dorowu/ubuntu-desktop-lxde-vnc表示docker用户名为dorowu的image名叫ubuntu-desktop-lxde-vnc的image
    
    1. 查看image
    ~> docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    ubuntu              latest              0458a4468cbc        10 days ago         112MB
    
    1. ***启动该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

    1. 使用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 mrlevo@outlook.com
    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 mrlevo@outlook.com
    ...
    
    Successfully built ab33d086befd
    Successfully tagged mrlevo/newubuntu:v2
    

    注意mrlevo/newubuntu:v2 .中的.千万别忘了,.Dockerfile 所在的路径(当前目录),也可以替换为一个具体的Dockerfile 的路径。而且为了你以后推到自己的仓库,名字的修改也比较重要,如mrlevo表示DockerHub上的用户名,newubunturepo的名字,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上去看看,其实就是和githubpush项目是一样的

    image

    **现在我们来讨论这样做成本其实很大,其实你只需要让对方拥有你的Dockerfile就可以了,他可以直接下载官方的基础image,然后bulid你的Dockerfile即可重构你的image,没必要把自己的image整个pushdocker hub上(其实还有更骚的操作,就是github上更新dockerfile,然后docker hub上自动化更新image),浪费资源不说,还占带宽~,另一个解决的思路就是压缩image的大小了,参考自@黄庆兵--如何精简压缩image,简单说就是将RUN命令全部写在一起,使用&&串联命令 **

    一个实际的?~ 安装xgboost+python环境

    举一个?,我要配置一个含有xgboost这个包的python3环境,其他的都不需要,那么可以简化为在ubuntuimage上,装python以及装第三方包

    1. Dockerfile配置文件,把要做的都写下来,思路可以是自己在操作虚拟机时候的操作,这里先不论是否高效。我先创建本地ubuntuxgboost文件夹,然后再其中创建一个Dockerfile文件,内容如下
    ~/ubuntxgboost> cat Dockerfile
    # ubuntu 14.04 with vim and gcc
    FROM ubuntu
    MAINTAINER mrlevo mrlevo@outlook.com
    RUN apt-get update && apt-get -y install python3 python3-pip
    RUN pip3 install pandas scikit-learn==0.18.1 xgboost
    

    当然建议是将RUN的命令用&&串起来,属于一种减少堆叠layer的方法

    1. 进行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
    
    1. 查看images
    ~/ubuntxgboost> docker images
    REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
    mrlevo/ubuntuxgboost   v1                  8eddac9d6c4a        3 minutes ago       980MB
    
    1. 启动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 image

    若干问题

    无法删除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就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理,这里是获取containerid

    然后选择删除指定的image

    $ docker rmi d1ee33a2d62d   //删除IMAGE ID=d1ee33a2d62d的image
    

    当然如果你是删掉为none的临时image

    $ docker rmi $(docker images | grep "none" | awk '{print $3}')    //删除为none的中间镜像
    

    参考:docker 删除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

    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 updateapt-get update必须时,把update和清理命令都放在同一行RUN底下,在执行update的同时释放多余的空间
    • 用命令或工具压缩imagedocker 自带的一些命令还能协助压缩镜像,比如 exportimport。工具如docker-squash,用起来更简单方便,并且不会丢失原有镜像的自带信息。

    Docker hub 与 Github 的互联,自动化构建image

    详细请参考

    按照步骤来没什么问题,使用git更新存放在github上dockerfile文件,然后docker hub会根据dockerfile在服务器云端开始构建image,值得注意的是,建议一个工程一个githubrepo,这样方便管理和更新。如果不打上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可以至少知道这个版本里面是个啥了~,不过貌似我没看到更新日志。看来只能从githubcommit上看了

    image image

    Docker 镜像的版本控制

    参考:@小小工匠--Docker-tag

    如果需要升级某个docker镜像,我们可以这样做。

    1. 给每个新生成的镜像都打上相应版本的tag。此时可能存在image:latestimage:v1image:v2等。
    2. 我们要从v1升级到v2,首先我们将导入的v2镜像强制重命名为image:latest,命令为docker tag -f image:v2 image:latest
    3. docker stop之前正在运行的容器
    4. 启用docker run image,此时image的等价镜像image:latest就是最新的V2镜像。

    总结下步骤:load/tag/stop/run

    致谢

    哎呀,越来越懒了,上面超链接的都是哦~谢谢大家的无私贡献才能不断添砖加瓦!

    相关文章

      网友评论

      本文标题:Docker 学习笔记

      本文链接:https://www.haomeiwen.com/subject/whjrlftx.html