Docker容器的Tengine实践
作为目前最火的应用,Docker确实存在着其独到之处,无论是程序猿还是运维都应该听说过Docker的大名,Docker已经走过了许多的坑,目前最新版本是v1.11.0版本,应该说是完全能承载开发使用和运维监控,这款工具能帮助我们高效的打包、发布和运行承载着应用程序的容器系统。而且收集日志、帮助App的快速开发都有很大作用。
容器和虚拟机,经常是被拿出来对比的两款产品,实际上两者有着根本的差别,虚拟机是完全模拟了一台真实计算机,在上面运行的系统可能或者不可能知道自己运行在虚拟化环境下,并且虚拟机承载了将用户指令转换为特权指令的功能,所以虚拟机非常复杂,但是很完备,而Docker则完全不同。Docker使用主机自身的Linux内核,然后从镜像中产生磁盘目录和软件,所有的进程都运行在主机上,如果有兴趣的话完全可以psaux查询一下,就能发现在Docker中运行的进程,只不过Docker对其做了如同chroot差不多概念的封装。
Docker真正用法
在Docker发展的早期,由于busybox等轻量化镜像不完备,所以各大发行版的缩减瘦身镜像得到了更多的使用,特别是由于Docker本身是在Ubuntu环境下开发的,所以Ubuntu和Debian在很多镜像中作为基镜像,以此作为基础产生目标镜像。但是随着在实践中的使用,其弊端也暴露出来了,就是太过于重量化,比如systemd的日志功能和Docker本身的日志功能被重复使用,镜像很难缩小到300M以内。而且Docker的推荐使用方式就是单进程模型,而并非是多个进程如同一个完备的操作系统一般。所以就产生了alpine等轻量级基镜像,alpine是什么则可以自行百度,这个镜像是Docker官方推荐的镜像,未来官方镜像将会迁移到alpine作为基础的镜像上,所以,我们应当早日熟悉此镜像。
构建Dockerfile
本文讲述的是Docker容器的Nginx实践,不过官方实际上已经有了关于Nginx的alpine镜像。而在实际使用过程中,笔者更多的是使用Tengine,所以根据官方Dockerfile的参考,笔者自行编写了Tengine镜像的Dockerfile,希望能抛砖引玉,各位能够批评指正。
FROMalpine:3.3
MAINTAINERChasonTang<chasontang@gmail.com>
ENVTENGINE_VERSION2.1.2
ENVCONFIG"\
--prefix=/etc/nginx\
--sbin-path=/usr/sbin/nginx\
--conf-path=/etc/nginx/nginx.conf\
--error-log-path=/var/log/nginx/error.log\
--http-log-path=/var/log/nginx/access.log\
--pid-path=/var/run/nginx.pid\
--lock-path=/var/run/nginx.lock\
--http-client-body-temp-path=/var/cache/nginx/client_temp\
--http-proxy-temp-path=/var/cache/nginx/proxy_temp\
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp\
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp\
--http-scgi-temp-path=/var/cache/nginx/scgi_temp\
--user=nginx\
--group=nginx\
--with-http_ssl_module\
--with-http_realip_module\
--with-http_addition_module\
--with-http_sub_module\
--with-http_dav_module\
--with-http_flv_module\
--with-http_mp4_module\
--with-http_gunzip_module\
--with-http_gzip_static_module\
--with-http_random_index_module\
--with-http_secure_link_module\
--with-http_auth_request_module\
--with-mail\
--with-mail_ssl_module\
--with-file-aio\
--with-http_spdy_module\
--with-ipv6\
--with-jemalloc\
"
ADDngx_user.patch/
ADDrepositories/etc/apk/repositories
RUN\
addgroup-Snginx\
&&adduser-D-S-h/var/cache/nginx-s/sbin/nologin-Gnginxnginx\
&&apkadd--no-cache--virtual.build-deps\
gcc\
libc-dev\
make\
openssl-dev\
pcre-dev\
zlib-dev\
linux-headers\
curl\
jemalloc-dev\
&&curl"http://tengine.taobao.org/download/tengine-$TENGINE_VERSION.tar.gz"-otengine.tar.gz\
&&mkdir-p/usr/src\
&&tar-zxC/usr/src-ftengine.tar.gz\
&&rmtengine.tar.gz\
&&cd/usr/src/tengine-$TENGINE_VERSION/src/os/unix/\
&&mv/ngx_user.patch./ngx_user.patch\
&&patchngx_user.cngx_user.patch\
&&rmngx_user.patch\
&&cd../../../\
#&&cd/usr/src/tengine-$TENGINE_VERSION\
&&./configure$CONFIG--with-debug\
&&make\
&&mvobjs/nginxobjs/nginx-debug\
&&./configure$CONFIG\
&&make\
&&makeinstall\
&&rm-rf/etc/nginx/html/\
&&mkdir/etc/nginx/conf.d/\
&&mkdir-p/usr/share/nginx/html/\
&&install-m644html/index.html/usr/share/nginx/html/\
&&install-m644html/50x.html/usr/share/nginx/html/\
&&install-m755objs/nginx-debug/usr/sbin/nginx-debug\
&&strip/usr/sbin/nginx*\
&&runDeps="$(\
scanelf--needed--nobanner/usr/sbin/nginx\
|awk'{gsub(/,/,"\nso:",$2);print"so:"$2}'\
|sort-u\
|xargs-rapkinfo--installed\
|sort-u\
)"\
&&apkadd--virtual.nginx-rundeps$runDeps\
&&apkdel.build-deps\
&&rm-rf/usr/src/nginx-$NGINX_VERSION\
&&apkadd--no-cachegettext\
\
#forwardrequestanderrorlogstodockerlogcollector
&&ln-sf/dev/stdout/var/log/nginx/access.log\
&&ln-sf/dev/stderr/var/log/nginx/error.log
COPYnginx.conf/etc/nginx/nginx.conf
COPYnginx.vh.default.conf/etc/nginx/conf.d/default.conf
EXPOSE80443
CMD["nginx","-g","daemonoff;"]
我们知道,Docker可以根据Dockerfile构建镜像,上面就是笔者写的Dockerfile。首先,使用FROM指令指定此镜像的基镜像为alpine:3.3,第二行为Dockerfile维护者声明,然后使用两个ENV指令声明两个环境变量,一个指定Tengine需要获取的版本号,一个则是编译安装选项。这里暂时不讲解,然后将一个补丁文件和alpine镜像源配置文件复制到容器内,实际上是因为tenginev2.1.2存在着一个遗留的glibcbug,会导致编译时出错,上游Nginx的最新代码已经修复,而tengine的开发分支上面也已经修复了这个问题,笔者前不久提issue将此补丁修正了tenginev2.1.3分支的代码,但是很可惜,v2.1.3版本尚未有正式发布,所以只能先使用补丁手动修复此问题。至于镜像源,则是因为国内存在着网络问题,导致apk包管理命令无法成功下载各个依赖项,所以将其指定为了国内源,如果正式使用则可以移除这两个文件。
然后就是使用RUN命令执行代码,这里大家可以看到笔者使用&&和\将所有的指令都压缩为了一行,这里是有两个原因:
RUN指令不会保存上一条指令的工作路径,每条RUN指令都只会将工作目录指定为/目录
一条Dockerfile中的指令就会产生一次镜像的提交,换言之,减少Dockerfile中的指令就可以提高镜像的复用水平
然后就是使用apk包管理命令下载安装包括编译器等依赖项,并且将这些依赖项标记为.build-deps组,便于后面将其卸载清理。然后就是非常常规的思路,./configure&&make&&makeinstall,编译选项都是非常中规中矩的,基本熟悉Nginx编译的朋友都能看懂。但是上面可以注意到,Nginx被编译了两次,一次开启了--with-debug参数,一次没有,这是因为在很多情况下,我们需要Nginx提供debug级别的监控日志,特别是在开发环境下,所以就编译了两次,便于使用。然后后面使用字符串分析处理将Tengine的运行时依赖项提取出来,标记为.nginx-rundeps然后卸载.build-deps,最后则是两个符号链接将accessLog和errorLog链接到标准输入输出,这样我们就能使用dockerlogs命令方便的查看日志了。最后则是复制自定义的Nginx配置文件,然后使用nginx-gdaemonoff;让Nginx以前台进程方式运行。
总结
到这里已经讲完了Docker在生产开发中的正确使用方法,Docker也确实是一样不可多得的好工具,祝愿大家早日使用Docker提升自己的生产力。