段⼦
今年基本已经结束了,我问了很多朋友今年挣钱了没?⼤多朋友都有挣,⽽且挣得五花⼋门:有挣个屁的,有挣个锤⼦的,有挣个⽑的,更有甚者挣个妹的,奢侈之极!最恐怖的是挣个⿁的!有的还可以,挣个球,下午我碰见⼀朋友,问今年挣了吗?他望着天空喃喃⾃语:挣个鸟!看吧,只要肯努⼒,什么都能挣到。
年末将⾄,忽然发现,从创建开始到现在,整整⼀年时间,没有写过多少东西。为了留下⼀点痕迹,也是为了整理⼀下⾃⼰的收获,为17年画上⼀笔浓郁的⾊彩。
最近在看⼀本⼊门级机器学习的书,⾥⾯的案例基本上是python实现的,所以想搭建python相关的环境,然后⼜想偷懒,有⼀个运⾏环境,可以轻松安装和编写使⽤,也可以在其他地⽅使⽤,编写⼯具⾸选jupyter notebook,当然,在⼤多数的书中也是⽐较推荐这个⼯具,⾃⼰之前也使⽤过,觉得不错。还有个问题就是想在其他地⽅使⽤python环境和这个⼯具,不需要重复安装,此刻,我想到的是docker。之前对docker只是简单的理解,为此,特意学习了⼀下docker,现做分享。
上图就是docker的图标,这个图标对docker的含义阐释的还是⽐较全⾯:⼩鲸鱼代表的是船,船上的就是集装箱,所有的东西不管是什么,只要装在集装箱中,就可以⽅便的运输。docker公司的⼝号是Build,Ship,and Run Any App,Anywhere。docker的本意是码头⼯⼈,⽽在这⾥说是集装箱的话,⽐较贴切。所有需要运⾏的环境和程序,装⼊docker,然后需要运⾏的时候,就运⾏这个特定的docker容器,提供特定的服务。 docker的通俗解析:
刚开始的时候,搞不清楚docker和虚拟机有什么区别,总感觉docker能⼲的事虚拟机也能,并且在使⽤的时候,总按照虚拟机的操作思路去做。那docker为什么会出现?
我在docker的官⽅⽹站找到了两张关于虚拟机和容器的区别:
容器 VS 虚拟机
容器和虚拟机具有相似的资源隔离和分配优势,但功能有所不同,因为容器虚拟化的是操作系统,⽽不是硬件,因此容器更容易移植,效率也更⾼。
关于容器:
对于docker做了简单的了解之后,就需要实际去体验⼀下安装和构建容器,本例使⽤centos6.5:
1.安装docker相关软件
[root@bogon ubuntu-16.04]# rpm -ivh http://dl.Fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpmRetrieving http://dl.Fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
warning: /var/tmp/rpm-tmp.KYucBm: Header V3 RSA/SHA256 Signature, key ID 0608b895: NOKEYPreparing... ########################################### [100%] 1:epel-release ########################################### [100%][root@bogon ubuntu-16.04]# yum -y install docker-io
Loaded plugins: fastestmirror, refresh-packagekit, securityLoading mirror speeds from cached hostfile...
Complete!
[root@bogon ubuntu-16.04]# service docker startStarting cgconfig service: [ OK ]Starting docker: [ OK ][root@bogon ubuntu-16.04]# chkconfig docker on[root@bogon ubuntu-16.04]#
使⽤service docker status查看docker服务状态的时候,发现没有启动 docker dead but pid file exists,执⾏docker相关命令(如docker ps)的时候会出现Cannot connect to the Docker daemon. Is 'docker -d' running on this host?,需要解决这个问题,如下:
[root@bogon ubuntu-16.04]# service docker statusdocker dead but pid file exists
[root@bogon ubuntu-16.04]#yum-config-manager --enable public_ol6_latestLoaded plugins: fastestmirror, refresh-packagekit
[root@bogon ubuntu-16.04]# yum install -y device-mapper-event-libsLoaded plugins: fastestmirror, refresh-packagekit, security...
2.构建基础镜像
在使⽤docker的时候后,可以通过命令docker pull <镜像名称>从镜像库中获取,但是有时候会出现⽹络问题或是其他原因,导致⽆法拉取,在docker中国官⽹介绍使⽤通过 Docker 官⽅镜像加速来解决⽆法拉取:您可以使⽤以下命令直接从该镜像加速地址进⾏拉取:$ docker pull registry.docker-cn.com/myname/myrepo:mytag例如:
$ docker pull registry.docker-cn.com/library/ubuntu:16.04 原⽂如下:
⽽在本⽂中,我使⽤Dockerfile来构建基础镜像ubuntu 16.04(xenial),其对应的Dockerfile的Github地址为:,搜索⽅式为在中搜索ubuntu,
即可看见对应的镜像信息。Dockerfile内容如下:
FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
# a few minor docker-specific tweaks
# see https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrapRUN set -xe \\ \\
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L40-L48 && echo '#!/bin/sh' > /usr/sbin/policy-rc.d \\ && echo 'exit 101' >> /usr/sbin/policy-rc.d \\ && chmod +x /usr/sbin/policy-rc.d \\ \\
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L54-L56 && dpkg-divert --local --rename --add /sbin/initctl \\ && cp -a /usr/sbin/policy-rc.d /sbin/initctl \\ && sed -i 's/^exit.*/exit 0/' /sbin/initctl \\ \\
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L71-L78 && echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup \\ \\
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L85-L105
&& echo 'DPkg::Post-Invoke { \"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true\"; };' > /etc/apt/apt.conf.d/docker-clean \\
&& echo 'APT::Update::Post-Invoke { \"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true\"; };' >> /etc/apt/apt.conf.d/docker-clean \\ && echo 'Dir::Cache::pkgcache \"\"; Dir::Cache::srcpkgcache \"\";' >> /etc/apt/apt.conf.d/docker-clean \\ \\
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L109-L115 && echo 'Acquire::Languages \"none\";' > /etc/apt/apt.conf.d/docker-no-languages \\ \\
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L118-L130 && echo 'Acquire::GzipIndexes \"true\"; Acquire::CompressionTypes::Order:: \"gz\";' > /etc/apt/apt.conf.d/docker-gzip-indexes \\ \\
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L134-L151 && echo 'Apt::AutoRemove::SuggestsImportant \"false\";' > /etc/apt/apt.conf.d/docker-autoremove-suggests# delete all the apt list files since they're big and get stale quicklyRUN rm -rf /var/lib/apt/lists/*
# this forces \"apt-get update\" in dependent images, which is also good# enable the universe
RUN sed -i 's/^#\\s*\\(deb.*universe\\)$/\\1/g' /etc/apt/sources.list
# make systemd-detect-virt return \"docker\"
# See: https://github.com/systemd/systemd/blob/aa0c34279ee40bce2f9681b496922dedbadfca19/src/basic/virt.c#L434RUN mkdir -p /run/systemd && echo 'docker' > /run/systemd/container# overwrite this with 'CMD []' in a dependent DockerfileCMD [\"/bin/bash\"]
现在对Dockerfile中的相关命令解释⼀下:
FROM 指的是依赖的基础镜像,如scratch表⽰的是空⽩的,从零开始的。依赖的镜像可以是本地的,也可以是远程库的
ADD 指的是添加本地⽂件到镜像中,如果遇到linux可解压格式⽂件,会⾃动解压,这就是为什么整个⽂件中没有对tar.gz进⾏显式解压RUN 运⾏命令,如安装软件的相关命令
CMD 设置启动Container时默认执⾏的命令,这个可以在启动容器时覆盖
⽬前,这个Dockerfile中涉及的命令就这⼏个,其他等以后遇到再进⾏说明。解释完毕,开始构建:
[root@bogon ubuntu-16.04]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES[root@bogon ubuntu-16.04]# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE[root@bogon ubuntu-16.04]# pwd/home/ml/ubuntu-16.04
[root@bogon ubuntu-16.04]# ll -htotal 40M
-rw-rw-r--. 1 ml ml 2.8K Dec 19 12:37 Dockerfile
-rw-rw-r--. 1 ml ml 40M Dec 19 12:39 ubuntu-xenial-core-cloudimg-amd64-root.tar.gz[root@bogon ubuntu-16.04]#
[root@bogon ubuntu-16.04]# docker build -t ubuntu:16.04 .Sending build context to Docker daemon 41.94 MBSending build context to Docker daemon Step 0 : FROM scratch --->
Step 1 : ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz / ---> 537c2f6dd023
Removing intermediate container dee7679a7ee2
Step 2 : RUN set -xe && echo '#!/bin/sh' > /usr/sbin/policy-rc.d && \\
echo 'exit 101' >> /usr/sbin/policy-rc.d && chmod +x /usr/sbin/policy-rc.d && \\
dpkg-divert --local --rename --add /sbin/initctl && cp -a /usr/sbin/policy-rc.d /sbin/initctl && sed -i 's/^exit.*/exit 0/' /sbin/initctl && \\ ...---> Running in 41d719b68981+ echo #!/bin/sh+ echo exit 101
+ chmod +x /usr/sbin/policy-rc.d
+ dpkg-divert --local --rename --add /sbin/initctl
Adding 'local diversion of /sbin/initctl to /sbin/initctl.distrib'+ cp -a /usr/sbin/policy-rc.d /sbin/initctl+ sed -i s/^exit.*/exit 0/ /sbin/initctl+ echo force-unsafe-io
+ echo DPkg::Post-Invoke { \"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true\"; };
+ echo APT::Update::Post-Invoke { \"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true\"; };+ echo Dir::Cache::pkgcache \"\"; Dir::Cache::srcpkgcache \"\";+ echo Acquire::Languages \"none\";
+ echo Acquire::GzipIndexes \"true\"; Acquire::CompressionTypes::Order:: \"gz\";+ echo Apt::AutoRemove::SuggestsImportant \"false\"; ---> c49bdbf61888
Removing intermediate container 41d719b68981Step 3 : RUN rm -rf /var/lib/apt/lists/* ---> Running in 6389964016a2 ---> 4508181f7442
Removing intermediate container 6389964016a2
Step 4 : RUN sed -i 's/^#\\s*\\(deb.*universe\\)$/\\1/g' /etc/apt/sources.list ---> Running in cbed2b28c988 ---> 8eed06df8f19
Removing intermediate container cbed2b28c988
Step 5 : RUN mkdir -p /run/systemd && echo 'docker' > /run/systemd/container ---> Running in aff40dbc6e05 ---> 19c96e7912a4
Removing intermediate container aff40dbc6e05Step 6 : CMD /bin/bash
---> Running in 2469ee9d7251 ---> 77e565a65647
Removing intermediate container 2469ee9d7251Successfully built 77e565a65647
[root@bogon ubuntu-16.04]# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEubuntu 16.04 77e565a65647 33 seconds ago 110.5 MB[root@bogon ubuntu-16.04]#
从构建⽇志可以看出,每条命令为⼀个step,执⾏完成之后会产⽣⼀个id,类似于6389964016a2,其实,这就是镜像的分层,⼀层层堆积在⼀起。
到此,⼀个ubuntu16.04版的docker镜像构建完成,那么接下来就是运⾏
3.运⾏镜像
使⽤docker run命令运⾏:
[root@bogon ubuntu-16.04]# docker run -it ubuntu:16.04root@5ea0b95e8641:/# cat /etc/issueUbuntu 16.04.3 LTS \\n \\l
root@5ea0b95e8641:/# ps -ef
UID PID PPID C STIME TTY TIME CMDroot 1 0 0 22:47 ? 00:00:00 /bin/bashroot 11 1 0 22:47 ? 00:00:00 ps -efroot@5ea0b95e8641:/#
其中5ea0b95e8641为当前容器的ID,进⼊容器查看所有进程,pid为1的时bash,linux不应该时init吗?其实,这就是容器与虚拟机的差别,容器的init进程就是主机上docker服务进程,每个容器只是⼀个进程⽽已。其中的参数-it指的是前端打开并分配⼀个终端,-d为在后台运⾏,我们试试当前这个可不可以使⽤-d:
[root@bogon ~]# docker run -d ubuntu:16.04
43ae7ded8e6920b55b8e744b52ffce37b89b25182fcacdc10a5414e6621abff3[root@bogon ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES[root@bogon ~]# docker run -d ubuntu:16.04 /bin/bash
77f3ec2ebfb3f154772683eeea8ca7e2ba3b7756b1488f5f09818af424e0298e[root@bogon ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
可以明显的开出来,使⽤-d后,docker ps查不到任何运⾏的容器,如果使⽤-it的话,在别的shell下使⽤docker ps查看:
[root@bogon ml]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES8341a332c788 ubuntu:16.04 \"/bin/bash\" 18 seconds ago Up 18 seconds drunk_cori
可以看到,有容器在运⾏,因为我们没有退出。由此可以看出,容器其实以进程⽅式运⾏,执⾏完成/bin/bash之后,进程消亡,所以容器也就不存在,如果容器⾥⾯是⼀个tomcat服务,则是另外⼀种情况了。
基础镜像基本构建完成,后⾯的环境搭建,都将基于这个镜像构建。
因篇幅问题不能全部显示,请点此查看更多更全内容