虽然 DockerHub 提供了大量的镜像,但是由于企业环境的多样性,并不是每个应用都能在 DockerHub 找到对应的镜像来使用。那就要求企业的运维人员掌握制作 Docker 镜像的技能。在开始打包应用前,你首先要明白这两件事:第一件事是选择适合你的方式来生成镜像: 1. 通过 Dockerfile 来自动编译生成镜像,实现构建镜像的需求。 2.通过容器内操作,并进行 Commit 来实现打包生成镜像。第一种思路多用于用户交互较少的时刻,比如软件部署时无需输入任何命令的应用。第二种思路用于用户交互较多、安装过程中配置较多的应用。第二件事是你需要注意,如何拆分现有的应用,来实现打包。Docker 容器化的应用应当是功能最小化的应用。这里的拆分并不是指将每个软件拆分成为一个容器,而是强调实现功能最小化。拿最常见的 LAMP 架构来讨论。最小化的结构应当是 Apache 和 PHP 在一个容器内, MySQL 在一个容器内。而不是将 Apache 、 PHP 、 MySQL 各自拆分一个容器。 Apache 和 PHP 的有机结合使结构内的应用完整的实现了 PHP 解析和 Web 页面的功能,而 MySQL 独自承担了数据存储的功能。而各自拆分一个容器会导致架构极为复杂,在进行容器扩容时也容易受到影响。一般来说,你可以将应用拆分成为 2-3 个容器, 2 个容器的情况下,是数据存储和应用程序各自一个容器。对于部分应用,可能还有缓存系统,同样也要单独放置在一个容器内。这样的结构下,在你需要扩容时,就比较容易,根据你的需要,可以随时扩容缓存系统或应用程序。
明了了上面的两件事,接下来进入应用打包的环节。
Dockerfile 打包
首先,我们先看下 Docker 官方打包的 Tomcat 镜像的 Dockerfile.
我们可以看到,里面大量执行了 Linux 命令,来安装,部署软件,并且对于需要确认的命令都加入-f 来强制执行。确保没有交互,因为在编译镜像的过程中,无法进行交互操作,如果碰到交互操作,卡住就会导致镜像编译的失败,故而 DockerFile 不支持需要输入命令的安装脚本等
当然,对于 Dockerfile,我们同样也有一些最佳实践:
- 通过 Dockerfile 构建的镜像应当尽可能精简。
- 尽量不安装非必要的软件包。
- 一个容器只运行一个单独的实例,将具有耦合度的应用分别安装到不同的容器里面。
- 慎重引入新的数据层
- 将准备安装的软件包按安装的字母顺序排列,这样可以避免重复安装软件包的情况,同时也有助于进行软件更新。通过添加 “\” 分割命令,增加代码的可读性
- 尽量选择官方提供的镜像版本来作为基础镜像,减小镜像的体积。
- 将多条 RUN 命令使用"/"连接起来,这样更易于理解,可以方便维护。
- 为镜像定义一个比较通用的端口,比如提供 HTTP Web 服务的镜像,最好是暴露 80 端口。
- Dockerfile 的开头几行的指令应当固定下来,不要每次都随意更改,这样可以利用缓存
- 通过 – t 标记来构建镜像,有助于用户管理每个创建的镜像。
- 不要在 Dockerfile 中映射公有端口。
- 使用 CMD 和 ENTRYPOINT 时,一定要用数组语法,而且 CMD 和 ENTRYPOINT 结合使用更好
- 不要开机初始化
- 在 Push 之前,现在本地构建运行一下,确保本地构建的镜像可以在任何地方正常运行。
- 不要在构建中升级版本,如果更新时试图修改 Init 或改变容器的内容,更新可能会失败,还可能产生不一致的镜像。
- FROM 命令应该包含基础镜像的完整仓库名和标签
- 使用指令组合,比如 apt-get update 应该和 apt-get install 结合。
基础镜像 Commit 生成镜像
除了通过 Dockerfile 来打包生成镜像外,也可以通过 Docker Commit 来生成镜像。通过 Commit 打包的镜像多是由于应用本身在部署时有大量的交互内容,无法通过命令来指定。在通过 Commit 打包镜像时,我们需要如下操作:
1.启动一个基础镜像容器,并进入 consoledocker run -i – t centos:6.7
上面的命令创建了一个基于 CentOS 6.7 的容器,并进入到容器内
2.执行配置环境的命令登陆容器后,可以执行各种 Linux 下的命令。
等配置完环境后,打开一个新的控制台执行命令docker ps
,可以看到正在运行的容器
比如我的容器的 ID 就是 35f1c2ae1f7e
3.将容器打包成镜像执行命令 docker commit 35f1c2ae1f7e mynewimage
就将容器35f1c2ae1f7e打包为新的镜像mynewimage了
可以执行docker images
查看镜像
另外,蜂巢的保存为镜像的功能,就是基于此功能制作的,通过云端容器 Console 的操作,并打包成为镜像,大大降低了上云的难度。无需在本地部署 Docker 环境,即可实现应用的容器化;同时云端打包镜像比本地打包速度也要更快些,大大提升了上云的速度。
PS.其实官方推荐:单个镜像单个进程。这样的情况下,有利有弊利在于,单进程容器更加适合纵向扩展和复用。但是,在较大的产品框架下,可能容器之间的关联会非常复杂。创建一个应用可能会需要创建非常多的容器。所以,在我看来如何划分,还是应该看使用场景和技术路线,量身定制.
例一:jpress应用容器 镜像搭建
例二:
# Base
FROM java:jdk-7
COPY ./.src/target/app-1.0.jar /app/
# ENTRYPOINT
WORKDIR /app
CMD [ "java", "-Dfile.encoding=UTF-8", "-jar", "./app-1.0.jar" ]
这是很简单的 Dockerfile,不要看他简单,我推荐的是各位用 Docker 就应该这么用。不需要在 Dockerfile 里写一堆 apt-get install ,一大堆 run 命令这些东西,记住 Dockerfile 就是声明应用环境和应用本身。Docker 现在做的功能太多了,很多都是不怎么靠谱的,Docker 需要更专注于它本身作为容器的技术。完成无状态化应用和写完 Dockerfile 之后,这个程序就可以被打报成 Docker image 了,放到一个 Docker Host 上运行起来就得到了无状态的应用容器,也就完成了把应用装容器的过程。