docker是什么?docker可以做什么?
Docker的思想来自于集装箱,集装箱解决了什么问题?在一艘大船上,可以把货物规整的摆放起来。并且各种各样的货物被集装箱标准化了,集装箱和集装箱之间不会互相影响。那么我就不需要专门运送水果的船和专门运送化学品的船了。只要这些货物在集装箱里封装的好好的,那我就可以用一艘大船把他们都运走。
Docker的五大优势:持续集成、版本控制、可以执行、隔离性、安全性。详细的优势介绍:http://dockone.io/article/389
本文章将以 CentOS:7 + tomcat-6 + jdk_1.8.0 为基础讲解Docker的基本使用。
Docker的介绍
- 为什么要使用Docker?
- 更快速的交付和部署
-
对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。 Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。 Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间。
-
-
更高效的虚拟化
- Docker 容器的运行不需要额外的 hypervisor 支持,它是内核级的虚拟化,因此可以实现更高的性能和效率。
- 更轻松的迁移和扩展
- Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。
- 更快速的交付和部署
- Docker的相关概念
- Docker是CS架构,主要有两个以下两个部分组成:
- Docker daemon: 运行在宿主机上,Docker守护进程,用户通过Docker client(Docker命令)与Docker daemon交互。
- Docker client: Docker 命令行工具,是用户使用Docker的主要方式,Docker client与Docker daemon通信并将结果返回给用户,Docker client也可以通过socket或者RESTful api访问远程的Docker daemon。
- 了解了Docker的组成,再来了解一下Docker的三个主要概念:
- Docker image:镜像是只读的,镜像中包含有需要运行的文件。镜像用来创建container,一个镜像可以运行多个container;镜像可以通过Dockerfile创建,也可以从Docker hub/registry上下载。
- Docker container:容器是Docker的运行组件,启动一个镜像就是一个容器,容器是一个隔离环境,多个容器之间不会相互影响,保证容器中的程序运行在一个相对安全的环境中。
- Docker hub/registry: 共享和管理Docker镜像,用户可以上传或者下载上面的镜像,官方地址为
https://registry.hub.docker.com/
,也可以搭建自己私有的Docker registry。
- Docker是CS架构,主要有两个以下两个部分组成:
Docker的安装
- Docker要求CentOS系统的内核版本大于等于3.10,所有首先要查看你的CentOS的内核版本是否指出Docker。可以通过命令:uname -r 来查看你当前的内核版本。
- Docker 软件包和依赖包已经包含在默认的 CentOS-Extras 软件源里,安装命令:yum -y install docker
- 查看docker的版本:docker version
- 启动docker,命令:serivce docker start
- 关闭docker,命令:serivce docker stop
- 重启docker,命令:serivce docker restart
Docker的基本使用
-
修改 Docker 仓库地址
因为国内连接 Docker 的官方仓库很慢,因此我们在日常使用中会使用Docker 中国加速器。通过 Docker 官方镜像加速,中国区用户能够快速访问最流行的 Docker 镜像。该镜像托管于中国大陆,本地用户现在将会享受到更快的下载速度和更强的稳定性,从而能够更敏捷地开发和交付 Docker 化应用。Docker 中国官方镜像加速可通过
registry.docker-cn.com
访问。该镜像库只包含流行的公有镜像,私有镜像仍需要从美国镜像库中拉取。修改系统中docker对应的配置文件即可,如下:vi /etc/docker/daemon.json #添加后 { "registry-mirrors": ["https://registry.docker-cn.com"], "live-restore": true }
-
搜索镜像
docker search mysql
-
获取镜像
docker pull 镜像名称
从远程仓库里获取镜像到本地。
- 构建镜像的两种方式
- 从容器构建镜像(以下简称为容器镜像)
-
创建一个容器,比如使用 tomcat:latest 镜像创建一个tomcat-test容器
-
修改tomcat-test容器的文件系统,比如修改tomcat的server.xml文件中的默认端口
-
使用commit命令提交镜像
-
- 使用Dockerfile构建镜像(以下简称Dockerfile镜像)
- 编写Dockerfile文件
- 使用build命令构建镜像
- 容器镜像的构建者可以任意修改容器的文件系统后进行发布,这种修改对于镜像使用者来说是不透明的,镜像构建者一般也不会将对容器文件系统的每一步修改,记录进文档中,供镜像使用者参考。
- 容器镜像不能(更准确地说是不建议)通过修改,生成新的容器镜像。
从镜像运行容器,实际上是在镜像顶部上加了一层可写层,所有对容器文件系统的修改,都在这一层中进行,不影响已经存在的层。比如在容器中删除一个1G的文件,从用户的角度看,容器中该文件已经没有了,但从文件系统的角度看,文件其实还在,只不过在顶层中标记该文件已被删除,当然这个标记为已删除的文件还会占用镜像空间。从容器构建镜像,实际上是把容器的顶层固化到镜像中。
也就是说, 对容器镜像进行修改后,生成新的容器镜像,会多一层,而且镜像的体积只会增大,不会减小。长此以往,镜像将变得越来越臃肿。Docker提供的 export 和 import 命令可以一定程度上处理该问题,但也并不是没有缺点。 - 容器镜像依赖的父镜像变化时,容器镜像必须进行重新构建。如果没有编写自动化构建脚本,而是手工构建的,那么又要重新修改容器的文件系统,再进行构建,这些重复劳动其实是没有价值的。
- Dockerfile镜像是完全透明的,所有用于构建镜像的指令都可以通过Dockerfile看到。甚至你还可以递归找到本镜像的任何父镜像的构建指令。也就是说,你可以完全了解一个镜像是如何从零开始,通过一条条指令构建出来的。
- Dockerfile镜像需要修改时,可以通过修改Dockerfile中的指令,再重新构建生成,没有任何问题。
- Dockerfile可以在GitHub等源码管理网站上进行托管,DockerHub自动关联源码进行构建。当你的Dockerfile变动,或者依赖的父镜像变动,都会触发镜像的自动构建,非常方便。
- 从容器构建镜像(以下简称为容器镜像)
-
查看本地的镜像
docker images
镜像名称 版本 镜像ID 创建时间 镜像大小 -
删除本地镜像
docker rmi -f imagesName/imagesID --删除某个镜像
docker rmi -f `docker images -q` --删除所有镜像
docker:表示为docker命令 。 rmi:删除镜像 。 -f :强制删除。 imagesName/imagesID : 镜像名称或镜像ID。
-
创建一个新的容器
docker run [OPTIONS] 镜像名称或镜像ID [COMMAND][ARG...]
OPTIONS说明:
-a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
-d: 后台运行容器,并返回容器ID;
-i: 以交互模式运行容器,通常与 -t 同时使用;
-p: 端口映射,格式为:主机(宿主)端口:容器端口
-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
--name="nginx-lb": 为容器指定一个名称;
--dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;
--dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;
-h "mars": 指定容器的hostname;
-e username="ritchie": 设置环境变量;
--env-file=[]: 从指定文件读入环境变量;
--cpuset="0-2" or --cpuset="0,1,2": 绑定容器到指定CPU运行;
-m :设置容器使用内存最大值;
--net="bridge": 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
--link=[]: 添加链接到另一个容器;
--expose=[]: 开放一个端口或一组端口;重点理解:镜像和容器之间的关系
-
镜像是静态的,镜像的每一层都只是可读的,而容器是动态的里面运行着我们指定的应用,容器里面的应用可能会新建一个文件,修改一个目录,这些操作所带来的改变并不会作用到镜像里面,因为镜像只是可读的。所以通过镜像创建容器就是在镜像上加一个可读写的层。
-
一个镜像可创建多个容器,每个容器都有各自的一个可读写层,这些层相互独立共享下面的镜像。
重点谨记:1. docker中必须要保持一个进程的运行,要不然整个容器就会退出。
2. Docker容器后台运行,就必须有一个前台进程。
首先解释一下第一句话:
通俗的讲:一个容器里如果没有任何进程在运行,那么Docker就会认为这个容器已经没有用了,就会结束掉这个容器。
第二句:相信有很多人第一次使用Dockerfile创建一个tomcat镜像并创建容器,例如:docker run -d -p 8080:8080 --name tomcat-6 镜像ID --使用docker ps -a会发现容器已经停止了。
这是我的Dockerfile文件
解决办法:
将运行进程发放入前台启动。
使用 tail , top 这种可以前台运行的程序。(特别推荐 tail 输出你的log文件 )
修改Dockerfile最后一条 ENTRYPOINT /usr/local/yplus/tomcat-admin/bin/startup.sh && tail -f /usr/local/yplus/tomcat-admin/logs/catalina.out
-
-
查看容器
docker ps
[OPTIONS] --没有任何参数表示查看正在运行的容器OPTIONS说明:
-a :显示所有的容器,包括未运行的。
-f :根据条件过滤显示的内容。
--format :指定返回值的模板文件。
-l :显示最近创建的容器。
-n :列出最近创建的n个容器。
--no-trunc :不截断输出。
-q :静默模式,只显示容器编号。
-s :显示总的文件大小。容器ID 镜像名称 启动命令 创建时间 启动时长 端口映射 容器名称
-
进入容器
进入容器常用的4种方式:
-
使用docker attach进入Dokcer容器
docker attach [OPTIONS] CONTAINER
注意:如果container当前在运行bash,那么使用attach进入后可以使用CTRL+C退出容器,容器不会退出;如果container当前正在前台运行进程,如输出nginx的access.log日志,CTRL-C不仅会导致退出容器,而且还stop了。我们可以在attach后带上--sig-proxy=false来确保CTRL-D或CTRL-C不会关闭容器。
-
使用docker exec进入Dokcer容器(需要docker的版本在1.3.X版本之后)
docker exec [OPTIONS] CONTAINER
- 使用nsenter进入Docker容器
- 使用SSH进入Docker容器
-
-
退出容器
方法1:如果要正常退出不关闭容器,请安Ctrl+P+Q进行退出容器
方法2:使用exit退出
注意:使用exit退出容器可能会使容器关闭。
-
停止容器/重启容器/删除容器
docker
start
容器名称或容器ID --启动容器docker stop
容器名称或容器ID --停止容器docker restart
容器名称或容器ID --重启容器docker rm -f
容器名称或容器ID --强制删除容器docker rm -f `docker ps -a -q` --强制删除所有容器
Dockerfile的介绍与使用
-
dockerfile是什么?
Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。它们简化了从头到尾的流程并极大的简化了部署工作。Dockerfile从FROM命令开始,紧接着跟随者各种方法,命令和参数。其产出为一个新的可以用于创建容器的镜像。下边是一个简单的docekrfile:#VERSION 0.0.1
FROM ubuntu:latest
MAINTAINER longwentao “longwentao255@126.com”
#install jdk and tomcat
ADD jdk-7u79-macosx-x64.dmg /usr/local/
ADD apache-tomcat-7.0.72 /usr/local/apache-tomcat-7.0.72
ADD onStart.sh /usr/local/
ENV JAVA_HOME=/usr/local/jdk1.7.0_79 CLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar PATH=$PATH:$JAVA_HOME/bin
RUN echo "JAVA_HOME=/usr/local/jdk1.7.0_79.jdk CALSSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar\nPATH=$PATH:$JAVA_HOME/bin" >> /etc/profile
#add execute privilege
RUN chmod +x /usr/local/onStart.sh
EXPOSE 8888
#start tomcat
ENTRYPOINT /usr/local/onStart.sh
-
为什么使用Dockerfile?
- 比commit方式透明
- 构建的镜像体积小
- 可复用,便于管理
- dockerfile的常用命令
- FORM
-
功能为指定基础镜像,并且必须是第一条指令。
如果不以任何镜像为基础,那么写法为:FROM scratch。
同时意味着接下来所写的指令将作为镜像的第一层开始
语法:
FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>三种写法,其中<tag>和<digest> 是可选项,如果没有选择,那么默认值为latest
-
- RUN
-
功能为运行指定的命令
RUN命令有两种格式
1. RUN <command>
2. RUN ["executable", "param1", "param2"]第一种后边直接跟shell命令
-
在linux操作系统上默认 /bin/sh -c
-
在windows操作系统上默认 cmd /S /C
第二种是类似于函数调用。
可将executable理解成为可执行文件,后面就是两个参数。
两种写法比对:
-
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME
-
RUN ["/bin/bash", "-c", "echo hello"]
注意:多行命令不要写多个RUN,原因是Dockerfile中每一个指令都会建立一层.
多少个RUN就构建了多少层镜像,会造成镜像的臃肿、多层,不仅仅增加了构件部署的时间,还容易出错。
RUN书写时的换行符是\
-
-
-
CMD
-
功能为容器启动时要运行的命令
语法有三种写法
1. CMD ["executable","param1","param2"]
2. CMD ["param1","param2"]
3. CMD command param1 param2第三种比较好理解了,就时shell这种执行方式和写法
第一种和第二种其实都是可执行文件加上参数的形式
举例说明两种写法:
-
CMD [ "sh", "-c", "echo $HOME"
-
CMD [ "echo", "$HOME" ]
补充细节:这里边包括参数的一定要用双引号,就是",不能是单引号。千万不能写成单引号。
原因是参数传递后,docker解析的是一个JSON array
-
-
-
RUN & CMD
-
不要把RUN和CMD搞混了。
RUN是构件容器时就运行的命令以及提交运行结果
CMD是容器启动时执行的命令,在构件时并不运行,构件时紧紧指定了这个命令到底是个什么样子
-
-
LABEL
- 功能是为镜像指定标签语法:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
一个Dockerfile种可以有多个LABEL,如下:LABEL "com.example.vendor"="ACME Incorporated"
但是并不建议这样写,最好就写成一行,如太长需要换行的话则使用\符号如下:
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."LABEL multi.label1="value1" \
说明:LABEL会继承基础镜像种的LABEL,如遇到key相同,则值覆盖
multi.label2="value2" \
other="value3"
- 功能是为镜像指定标签语法:
- MAINTAINER
-
指定作者
语法:
MAINTAINER <name>
-
-
EXPOSE
-
功能为暴漏容器运行时的监听端口给外部
但是EXPOSE并不会使容器访问主机的端口
如果想使得容器与主机的端口有映射关系,必须在容器启动的时候加上 -P参数
-
-
ENV
-
功能为设置环境变量
语法有两种
1. ENV <key> <value> 2. ENV <key>=<value> ...
两者的区别就是第一种是一次设置一个,第二种是一次设置多个
-
-
ADD
-
一个复制命令,把文件复制到景象中。
如果把虚拟机与容器想象成两台linux服务器的话,那么这个命令就类似于scp,只是scp需要加用户名和密码的权限验证,而ADD不用。
语法如下:
1. ADD <src>... <dest> 2. ADD ["<src>",... "<dest>"]
<dest>路径的填写可以是容器内的绝对路径,也可以是相对于工作目录的相对路径
<src>可以是一个本地文件或者是一个本地压缩文件,还可以是一个url
如果把<src>写成一个url,那么ADD就类似于wget命令
如以下写法都是可以的:
-
ADD test relativeDir/
-
ADD test /relativeDir
-
ADD http://example.com/foobar /
尽量不要把<scr>写成一个文件夹,如果<src>是一个文件夹了,复制整个目录的内容,包括文件系统元数据
-
-
-
COPY
-
看这个名字就知道,又是一个复制命令
语法如下:
1. COPY <src>... <dest> 2. COPY ["<src>",... "<dest>"]
与ADD的区别
COPY的<src>只能是本地文件,其他用法一致
-
-
ENTRYPOINT
-
功能是启动时的默认命令
语法如下:
1. ENTRYPOINT ["executable", "param1", "param2"]
2. ENTRYPOINT command param1 param2如果从上到下看到这里的话,那么你应该对这两种语法很熟悉啦。
第二种就是写shell
第一种就是可执行文件加参数
与CMD比较说明(这俩命令太像了,而且还可以配合使用):
1. 相同点:
-
只能写一条,如果写了多条,那么只有最后一条生效
-
容器启动时才运行,运行时机相同
2. 不同点:
-
ENTRYPOINT不会被运行的command覆盖,而CMD则会被覆盖
-
如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD指令不是一个完整的可执行命令,那么CMD指定的内容将会作为ENTRYPOINT的参数
如下:
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]-
如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD是一个完整的指令,那么它们两个会互相覆盖,谁在最后谁生效
如下:
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ls -al那么将执行ls -al ,top -b不会执行。
-
-
- FORM
-
使用dockerfile创建镜像
例1:dockerfile
dockerfile的位置:创建镜像:docker build -t 镜像名称:TAG .
注意最后有个点,代表使用当前路径的 Dockerfile 进行构建
除此之外还可以使用 -f 参数来显示指定 Dockerfile 文件所在的位置。
最后使用docker images查看是否构建成功。