接下来一段时间会推出一系列关于微服务的文章,本文则是这个系列的先导文章,主要介绍下docker的基本知识,为后面的微服务部署做个铺垫。
为什么要用Docker而不用虚拟机?
- 相比虚拟机,容器嫩更方便地与主机共享数据。
- 启动和停止容器几乎只需一瞬间,更为适合敏捷开发。
- 和虚拟机相比,容器的性能损耗要低得多。因为它没有类似虚拟机管理程序执行所带来的损耗。
- 隔离性更好。docker是容器级的隔离,容器无法窥探其他容器内的进程情况。
对比上面两幅图可以发现:和虚拟机不同,容器只能运行与主机一样的内核。且当两个程序依赖的库相同时无需多次复制库,只需直接调用该库生成新容器即可。
获取镜像
获取镜像由两种方法。
第一种是直接从远程镜像库获取镜像。这里只介绍几个常用的相关指令。
$ docker search redis
查询镜像库中(默认是docker官方镜像库,库源可以自行更改)中redis的相关镜像信息
$ docker pull redis
下载redis镜像
$ docker images
查询本地镜像库
$ docker rmi ce25c7293564
删除本地镜像库中image id为ce25c7293564的镜像
我们重点介绍第二种方法:通过Dockerfile创建镜像。
先了解下Docker中镜像、容器和文件系统的概念及它们之间的关系:
联合文件系统:允许多个文件系统叠加,并表现为一个单一的文件系统。如果有两个来自不同文件系统的文件的路径完全相同,则最后挂载的文件会覆盖掉前面挂载的文件。而Docker支持多种联合文件系统的实现,具体实现方式与你使用的系统有关,具体可通过docker info命令中的Storage Driver项的值了解。
Docker的镜像由很多个不同的“层”组成,每个“层”都是一个 只读的文件系统。 Dockerfile里的每个指令都会创建一个新的层,新的层位于上一层之上。当镜像被转换成一个容器时(例如docker run或docker create),Docker会在该镜像的最上层新建一个 可读写文件系统。
学习完一些基本概念,再来了解一下Dockerfile的相关指令:
- From 设置Dockerfile使用的基础镜像,接下来的操作均执行于该镜像之上。镜像格式为“镜像:标签”(例如debian:wheezy),若标签省略,则默认为最新(latest)。Dockerfile中第一个非注解指令必须是From指令。
- RUN 执行命令并创建新的镜像层,RUN 经常用于安装软件包。
- CMD 设置容器启动后默认执行的命令及其参数,但 它 能够被
docker run
后面跟的命令行参数替换。且如果 Dockerfile 中有多个 CMD 指令,只有最后一个 CMD 有效。 - ENTRYPOINT 配置容器启动时运行的命令。其可让容器以应用程序或者服务的形式运行,且它一定会被执行,不会被忽略。
以上的ENTRYPOINT、RUN、CMD命令都有shell和exec两种命令格式。shell格式的命令就像平时在shell中输入的命令一样是自由的字符串形式。例如ENTRYPOINT echo "Hello World",而shell格式的命令均会传给/bin/sh -c执行。而exec格式的命令则是一个JSON数组,例如ENTRYPOINT ["/bin/echo", "Hello world"]
三者最佳的实践方式为:
RUN指令用于安装软件包和应用
运行应用程序或服务则优先使用Exec 格式的 ENTRYPOINT 指令
为容器设置默认的启动命令,则使用 CMD 指令
5. VOLUME 指定为数据卷的文件或目录,与主机进行数据共享。如果该文件或目录已在镜像中存在,则容器启动时它会被复制到这个卷。如果有多个参数,则会被解释成多个数据卷。处于对可移植性和安全性的考虑,不能在Dockerfile中指定数据卷所使用的主机目录。
6. ADD 从构建环境的上下文或者远程URL中复制文件到镜像中。
7. COPY 从构建目录的上下文复制文件到镜像。它有COPY src dest和COPY["src", "dest"]两种格式,两者皆是从上下文的src复制文件或目录到容器的dest中。如果路径中有空格则必须使用JSON格式。
补充说明:
- dockerfile注释方法:以#作为一行的开头。
- 当执行docker build的时候,Docker会检查本地是否存在FROM指令所指定的镜像,若本地不存在该镜像,Docker会尝试下载该镜像;如果本地已存在该镜像,则Docker会直接使用它。这意味着仅仅通过执行docker build无法保证你使用的镜像是否为最新的可用版本,所以你需要对所依赖的父镜像进行pull或者删除掉它们。因为一般的基础镜像(例如debian、centos)都会发布安全补丁更新。
有了镜像后,就可以创建容器了
run常用参数:
- -d, --detach=false 指定容器运行于前台还是后台,默认为false
- --rm=false 指定容器停止后自动删除容器(不支持以docker run -d启动的容器)
- --name="" 指定容器名字
- -e username="root" 设置环境变量
- -p, --publish=[] 配置主机与容器之间端口的映射
容器和主机间的端口映射有几种常用方法:
# 单个端口的映射 下面例子表示将容器的80端口映射到主机的8080端口
$ docker run --name=demo -d -p 8080:80 demo-image
# 多个端口的映射 下面例子表示将容器的80和81端口分别映射到主机的8080和8081
$ docker run --name=demo -d -p 8080:80 -p 8081:81 demo-image
#批量端口的映射 下面例子表示将容器的80到100端口映射到主机的800-820
$ docker run --name=demo -d -p 800-820:80-100 demo-image
- -v, --volume=[] 给容器挂载存储卷,挂载到容器的某个目录
$ docker run --name=demo -d -v /root:/data/conf demo-image
将主机的/root目录挂载到容器demo的/data/conf目录
- --restart="no" 指定容器停止后的重启策略,共有4种策略:
no 容器退出时不重启
on-failure[:max-retries] 容器故障退出(返回值非零)时重启,还可以限制Docker守护程序尝试的重新启动容器的重试次数
always 无论退出状态如何,始终重新启动容器。
unless-stopped 无论退出状态如何,包括在守护程序启动时,始终重新启动容器,除非在Docker守护程序停止之前容器已进入停止状态。
$ docker run --restart=on-failure:5 demo-image
- -m 用户内存限制,默认不限制。
- --memory-swap 总虚拟内存大小,默认值为用户内存限制的两倍。
$ docker run -m 100M demo-image
使用-m但不使用--memory-swap时,用户内存被限制为100M,而总虚拟内存限额为200M(其中100M为交换内存,如果主机支持的话)
$ docker run -m 100M --memory-swap 500M demo-image
两个参数均使用时,限额显而易见,而两者的差则为交换内存的大小。
默认情况下,当内存不足时,内核会终止容器中的进程;如果是单进程容器,终止运行后的容器status显示为EXITED(137)
如果你不希望发生这样的情况,可以使用参数--oom-kill-disable来阻止这种行为。
强烈建议该参数配合-m使用,不要单独使用!!!
我们以创建一个Spring Boot项目的镜像为例进行实操
首先将Spring Boot项目打包成jar文件:
新建dockerfile文件:
并将jar包和dockerfile放在同一个文件夹,使用命令
$ docker build -t my_demo_image .
即可成功创建镜像。(注意镜像名不能包含大写字母)
然后就可以通过run指令生成并运行容器了
$ docker run --name=demo2 -p 19000:9000 -d my_demo_image
想了解更多docker知识的朋友欢迎点击官方文档进行进一步的学习。