Docker 构建 Tengine 2.2.2 镜像


Tengine简介

Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验。它的最终目标是打造一个高效、稳定、安全、易用的Web平台。

Docker 简介

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。

一个完整的Docker有以下几个部分组成:

  • dockerClient客户端
  • Docker Daemon守护进程
  • Docker Image镜像
  • DockerContainer容器

Docker 是 PaaS 提供商 dotCloud 开源的一个基于 LXC 的高级容器引擎,源代码托管在 Github 上, 基于go语言并遵从Apache2.0协议开源。

Docker自2013年以来非常火热,无论是从 github 上的代码活跃度,还是Redhat在RHEL6.5中集成对Docker的支持, 就连 Google 的 Compute Engine 也支持 docker 在其之上运行。

一款开源软件能否在商业上成功,很大程度上依赖三件事 - 成功的 user case(用例), 活跃的社区和一个好故事。 dotCloud 自家的 PaaS 产品建立在docker之上,长期维护且有大量的用户,社区也十分活跃,接下来我们看看docker的故事。

  • 环境管理复杂 - 从各种OS到各种中间件到各种app, 一款产品能够成功作为开发者需要关心的东西太多,且难于管理,这个问题几乎在所有现代IT相关行业都需要面对。
  • 云计算时代的到来 - AWS的成功, 引导开发者将应用转移到 cloud 上, 解决了硬件管理的问题,然而中间件相关的问题依然存在 (所以openstack HEAT和 AWS cloudformation 都着力解决这个问题)。开发者思路变化提供了可能性。
  • 虚拟化手段的变化 - cloud 时代采用标配硬件来降低成本,采用虚拟化手段来满足用户按需使用的需求以及保证可用性和隔离性。然而无论是KVM还是Xen在 docker 看来,都在浪费资源,因为用户需要的是高效运行环境而非OS, GuestOS既浪费资源又难于管理, 更加轻量级的LXC更加灵活和快速。
  • LXC的移动性 - LXC在 linux 2.6 的 kernel 里就已经存在了,但是其设计之初并非为云计算考虑的,缺少标准化的描述手段和容器的可迁移性,决定其构建出的环境难于迁移和标准化管理(相对于KVM之类image和snapshot的概念)。docker 就在这个问题上做出实质性的革新。这是docker最独特的地方。

Docker 用法

在 Docker 发展的早期,由于 busybox 等轻量化镜像不完备,所以各大发行版的缩减瘦身镜像得到了更多的使用,特别是由于 Docker 本身是在 Ubuntu 环境下开发的,所以 Ubuntu 和 Debian 在很多镜像中作为基镜像,以此作为基础产生目标镜像。但是随着在实践中的使用,其弊端也暴露出来了,就是太过于重量化,比如 systemd 的日志功能和 Docker 本身的日志功能被重复使用,镜像很难缩小到 300M 以内。而且 Docker 的推荐使用方式就是单进程模型,而并非是多个进程如同一个完备的操作系统一般。所以就产生了 alpine 等轻量级基镜像,alpine 是什么则可以自行百度,这个镜像是 Docker 官方推荐的镜像,未来官方镜像将会迁移到 alpine 作为基础的镜像上,所以,我们应当早日熟悉此镜像。

构建 Dockerfile

本文讲述的是 Docker 容器的 Tengine 实践,虽然有Nginx,但是还是忍不住用 Tengine,毕竟中国几亿人用的 Tengine 并不比 Nginx 查,由于我的网站都是在 Docker 上运行的,所以就根据官方 Dockerfile 的参考和一些其他大神们的文档,编写了最新的 Tengine 2.2.2 镜像的 Dockerfile,希望大神们看到缺陷可以多多指导!!!!

Dockerfile

FROM alpine:3.4
MAINTAINER Fanchcho <fanchcho@sina.com>

ENV TENGINE_VERSION 2.2.2
ENV CONFIG "\
        --user=nginx \
        --group=nginx \
        --prefix=/etc/nginx \
        --sbin-path=/usr/sbin/nginx \
        --conf-path=/etc/nginx/nginx.conf \
        --lock-path=/var/lock/nginx.lock \
        --pid-path=/var/run/nginx.pid \
        --error-log-path=/var/log/nginx/error.log \
        --http-log-path=/var/log/nginx/access.log \
        --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 \
        --with-md5=/usr/include/openssl \
        --with-http_ssl_module \
        --with-http_gzip_static_module \
        --with-http_gunzip_module \
        --with-sha1-asm \
        --with-md5-asm \
        --with-http_auth_request_module \
        --with-http_image_filter_module \
        --with-http_addition_module \
        --with-http_dav_module \
        --with-http_realip_module \
        --with-http_v2_module \
        --with-http_stub_status_module \
        --with-http_sub_module \
        --with-http_xslt_module \
        --with-http_flv_module \
        --with-http_mp4_module \
        --with-http_degradation_module \
        --with-http_upstream_check_module \
        --with-http_upstream_consistent_hash_module \
        --with-http_upstream_ip_hash_module=shared \
        --with-http_upstream_least_conn_module=shared \
        --with-http_upstream_session_sticky_module=shared \
        --with-http_map_module=shared \
        --with-http_user_agent_module=shared \
        --with-http_split_clients_module=shared \
        --with-http_access_module=shared \
        --with-http_user_agent_module=shared \
        --with-http_random_index_module \
        --with-http_secure_link_module \
        --with-http_auth_request_module \
        --with-ipv6 \
        --with-file-aio \
        --with-mail \
        --with-mail_ssl_module \
        --with-pcre \
        --with-pcre-jit \
        --with-jemalloc \
        "

ADD repositories /etc/apk/repositories

RUN \
    addgroup -S nginx \
    && adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx \
    && apk add --no-cache --virtual .build-deps \
        gcc \
        geoip \
        geoip-dev \
        libxslt-dev \
        libxml2-dev \
        gd-dev \
        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" -o tengine.tar.gz \
    && mkdir -p /usr/src \
    && tar -zxC /usr/src -f tengine.tar.gz \
    && rm tengine.tar.gz \
    && cd ../../../ \
    && cd /usr/src/tengine-$TENGINE_VERSION \
    && ./configure $CONFIG --with-debug \
    && make \
    && mv objs/nginx objs/nginx-debug \
    && ./configure $CONFIG \
    && make \
    && make install \
    && rm -rf /etc/nginx/html/ \
    && mkdir /etc/nginx/conf.d/ \
    && mkdir -p /usr/share/nginx/html/ \
    && install -m644 html/index.html /usr/share/nginx/html/ \
    && install -m644 html/50x.html /usr/share/nginx/html/ \
    && install -m755 objs/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 -r apk info --installed \
            | sort -u \
    )" \
    && apk add --virtual .nginx-rundeps $runDeps \
    && apk del .build-deps \
    && rm -rf /usr/src/nginx-$NGINX_VERSION \
    && apk add --no-cache gettext \
    \
    # forward request and error logs to docker log collector
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

我们知道,Docker 可以根据 Dockerfile 构建镜像,上面就是Dockerfile。首先,使用 FROM 指令指定此镜像的基镜像为 alpine:3.4,第二行为 Dockerfile 维护者声明,然后使用两个 ENV 指令声明两个环境变量,一个指定 Tengine 需要获取的版本号,一个则是编译安装选项。repositories是镜像源,则是因为国内存在着网络问题,导致 apk 包管理命令无法成功下载各个依赖项,所以将其指定为了国内源,如果正式使用则可以移除这两个文件。

然后就是使用 RUN 命令执行代码,这里大家可以看到使用 && 和 \ 将所有的指令都压缩为了一行,这里是有两个原因:

RUN 指令不会保存上一条指令的工作路径,每条 RUN 指令都只会将工作目录指定为 / 目录

一条 Dockerfile 中的指令就会产生一次镜像的提交,换言之,减少 Dockerfile 中的指令就可以提高镜像的复用水平!

然后就是使用 apk 包管理命令下载安装包括编译器等依赖项,并且将这些依赖项标记为 .build-deps 组,便于后面将其卸载清理。然后就是非常常规的思路,./configure && make && make install ,编译选项都是非常中规中矩的,基本熟悉 Nginx 编译的朋友都能看懂。但是上面可以注意到,Nginx 被编译了两次,一次开启了 --with-debug 参数,一次没有,这是因为在很多情况下,我们需要 Nginx 提供 debug 级别的监控日志,特别是在开发环境下,所以就编译了两次,便于使用。然后后面使用字符串分析处理将 Tengine 的运行时依赖项提取出来,标记为 .nginx-rundeps 然后卸载 .build-deps ,最后则是两个符号链接将 accessLogerrorLog 链接到标准输入输出,这样我们就能使用 docker logs 命令方便的查看日志了。最后则是复制自定义的 Nginx 配置文件,然后使用 nginx -g daemon off ; 让 Nginx 以前台进程方式运行。

随后附上 Github地址:https://github.com/fanchcho/docker-tengine ,欢迎各位 Star 和 Fork!

如果大家使用 Docker 的话也可以通过以下命令直接 pull 我的镜像!

docker pull fanchcho/tengine