Docker 基础 - 3

Web 服务器与应用

Nginx

我的Nginx Docker镜像
## 设置继承自己创建的 sshd 镜像
FROM caseycui/ubuntu-sshd

## 维护者
LABEL maintainer="CaseyCui [email protected]"

## 安装 nginx
RUN apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
        nginx \
        geoip-bin \
        fcgiwrap \
        ssl-cert \
    && rm -rf /var/lib/apt/lists/* \
    && chown -R www-data:www-data /var/lib/nginx

## 添加脚本,并设置权限
COPY run-nginx.sh /run-nginx.sh
##RUN chmod 755 /run-nginx.sh

## 定义工作目录
WORKDIR /etc/nginx

## 添加挂载点 /var/www
VOLUME /var/www

## forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
        && ln -sf /dev/stderr /var/log/nginx/error.log

## 定义输出端口
EXPOSE 80
EXPOSE 443

## 定义输出命令
CMD ["/run-nginx.sh"]

run-nginx.sh 脚本:

## nginx 以 daemon off 形式启动
/usr/sbin/nginx -g "daemon off;"

为什么需要 daemon off; ?

想象这样的场景:

如果没有 daemon off, nginx 后台运行, 这时 nginx 并不是 pid 为 1 的程序, 而是执行的其他(如 bash), 这个 bash 执行了 nginx 指令后就结束了, 容器也会随之退出.

或直接修改/etc/nginx/nginx.conf 文件:

echo -e "\ndaemon off;" >> /etc/nginx/nginx.conf

Tomcat

Tomcat 最初是由 Sun 的软件架构师詹姆斯.邓肯.戴维森 开发的, 后来在他的帮助下称为开源项目, 并由 Sun 贡献给 Apache 软件基金会.

Tomcat主要功能: 运行 JSP 页面和 Servlet.

JAVA

企业通常使用 Sun JDK 6 或 Oracle JDK 7+. Dockerfile 如下:

FROM caseycui/ubuntu-sshd

LABEL maintainer="CaseyCui [email protected]"

## 创建 /java 目录
RUN mkdir /java

## 解压jdk压缩包到/java目录
ADD jdk-7u80-linux-x64.tar.gz /java

## 1. 删除 src.zip,减少镜像size
## ~~2. 配置JAVA环境变量~~
## ~~3. 使生效~~
## 下面为几种不同的 echo 写法(配置ENV则不需要手动在/etc/profile里添加)
## =======================================================
## echo -e "\nexport JAVA_HOME=/java/jdk1.7.0_80\nexport PATH=/java/jdk1.7.0_80/bin:$PATH\nexport CLASSPATH=.:/java/jdk1.7.0_80/lib/dt.jar:/java/jdk1.7.0_80/lib/tools.jar" >> /etc/profile
## =======================================================
## RUN { \
##                 echo; \
##                 echo 'export JAVA_HOME=/java/jdk1.7.0_80'; \
##                 echo 'export PATH=$JAVA_HOME/bin:$PATH'; \            
##                 echo 'export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar'; \
##         } >> /etc/profile \
##     && source /etc/profile \
##     && rm -f /java/jdk1.7.0_80/src.zip
## =======================================================

RUN rm -f /java/jdk1.7.0_80/src.zip

## 配置ENV
## > The environment variables set using `ENV` will persist when a container is run from the resulting image.

ENV JAVA_HOME /java/jdk1.7.0_80
ENV JAVA_VERSION 7u80
ENV PATH $JAVA_HOME/bin:$PATH
ENV CLASSPATH .:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

备注:

  • JDK的压缩包来自 Oracle Support 网站.
  • JAVA环境变量直接在 ENV 里配置即可, 无需手动写入 /etc/profile 中
  • 如何把多行, 且含有多种特殊字符的字符串写入文件? - 用大括号

Tomcat 8

基于 Oracle JDK 7 的 Tomcat 8.0.X 的Dockerfile如下:

FROM caseycui/jdk7:7u80
 
LABEL maintainer='CaseyCui [email protected]'
 
ENV CATALINA_HOME /tomcat
ENV PATH $CATALINA_HOME/bin:$PATH

RUN mkdir -p "$CATALINA_HOME"

WORKDIR $CATALINA_HOME

## let "Tomcat Native" live somewhere isolated 
ENV TOMCAT_NATIVE_LIBDIR $CATALINA_HOME/native-jni-lib
ENV LD_LIBRARY_PATH ${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOMCAT_NATIVE_LIBDIR

## runtime dependencies for Tomcat Native Libraries
## Tomcat Native 1.2+ requires a newer version of OpenSSL than debian:jessie has available
## > checking OpenSSL library version >= 1.0.2...
## > configure: error: Your version of OpenSSL is not compatible with this version of tcnative
## see http://tomcat.10.x6.nabble.com/VOTE-Release-Apache-Tomcat-8-0-32-tp5046007p5046024.html (and following discussion)
## and https://github.com/docker-library/tomcat/pull/31

ENV OPENSSL_VERSION 1.0.2g-1ubuntu4.8

## RUN set -ex; \
##         if ! grep -q stretch /etc/apt/sources.list; then \
## # only add stretch if we're not already building from within stretch
##                 { \
##                         echo 'deb http://deb.debian.org/debian stretch main'; \
##                 } > /etc/apt/sources.list.d/stretch.list; \
##                 { \
## # add a negative "Pin-Priority" so that we never ever get packages from stretch unless we explicitly request them
##                         echo 'Package: *'; \
##                         echo 'Pin: release n=stretch'; \
##                         echo 'Pin-Priority: -10'; \
##                         echo; \
## # ... except OpenSSL, which is the reason we're here
##                         echo 'Package: openssl libssl*'; \
##                         echo "Pin: version $OPENSSL_VERSION"; \
##                         echo 'Pin-Priority: 990'; \
##                 } > /etc/apt/preferences.d/stretch-openssl; \
##         fi

RUN apt-get update && apt-get install -y --no-install-recommends \
                libapr1 \
                openssl="$OPENSSL_VERSION" \
        && rm -rf /var/lib/apt/lists/*

## 安装跟 tomcat 用户认证相关的软件
RUN apt-get update && \
    DIBIAN_FRONTEND=noninteractive \
    apt-get install -yq --no-install-recommends \
        wget \
        pwgen \
        ca-certificates && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

## see https://www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/KEYS
## see also "update.sh" (https://github.com/docker-library/tomcat/blob/master/update.sh)

ENV GPG_KEYS 05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23

RUN set -ex; \
        for key in $GPG_KEYS; do \
                gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
        done

## 设置 tomcat 的环境变量
 
ENV TOMCAT_MAJOR 8
ENV TOMCAT_VERSION 8.0.46
ENV TOMCAT_TGZ_URL http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz
ENV TOMCAT_ASC_URL https://www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc

## 复制 tomcat 到镜像中

RUN set -x \
        \
        && wget -O tomcat.tar.gz "$TOMCAT_TGZ_URL" \
        && wget -O tomcat.tar.gz.asc "$TOMCAT_ASC_URL" \
        && gpg --batch --verify tomcat.tar.gz.asc tomcat.tar.gz \
        && tar -xvf tomcat.tar.gz --strip-components=1  \
        && rm bin/*.bat \
        && rm tomcat.tar.gz* \
        \
        && nativeBuildDir="$(mktemp -d)" \
        && tar -xvf bin/tomcat-native.tar.gz -C "$nativeBuildDir" --strip-components=1 \
        && nativeBuildDeps=" \
                dpkg-dev \
                gcc \
                libapr1-dev \
                libssl-dev \
                make \
        " \
        && apt-get update && apt-get install -y --no-install-recommends $nativeBuildDeps && rm -rf /var/lib/apt/lists/* \
        && ( \
                export CATALINA_HOME="$PWD" \
                && cd "$nativeBuildDir/native" \
                && gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \
                && ./configure \
                        --build="$gnuArch" \
                        --libdir="$TOMCAT_NATIVE_LIBDIR" \
                        --prefix="$CATALINA_HOME" \
                        --with-apr="$(which apr-1-config)" \ 
                        --with-java-home="/java/jdk1.7.0_80" \
                        --with-ssl=yes \
                && make -j "$(nproc)" \
                && make install \
        ) \
        && apt-get purge -y --auto-remove $nativeBuildDeps \
        && rm -rf "$nativeBuildDir" \
        && rm bin/tomcat-native.tar.gz

## verify Tomcat Native is working properly
RUN set -e \
        && nativeLines="$(catalina.sh configtest 2>&1)" \
        && nativeLines="$(echo "$nativeLines" | grep 'Apache Tomcat Native')" \
        && nativeLines="$(echo "$nativeLines" | sort -u)" \
        && if ! echo "$nativeLines" | grep 'INFO: Loaded APR based Apache Tomcat Native library' >&2; then \
                echo >&2 "$nativeLines"; \
                exit 1; \
        fi

## 创建 tomcat 用户脚本
COPY create_tomcat_admin_user.sh /create_tomcat_admin_user.sh
 
## 创建 tomcat 运行脚本
COPY run-tomcat.sh /run-tomcat.sh

RUN chmod +x /*.sh && \
    chmod +x /tomcat/bin/*.sh
 
## 挂载点
## 日志文件
VOLUME $CATALINA_HOME/logs

## 程序文件 
VOLUME $CATALINA_HOME/webapps

EXPOSE 8080

CMD ["/run-tomcat.sh"]

创建 Tomcat 用户和密码脚本文件 create_tomcat_admin_user.sh, 内容如下:

##!/bin/bash
 
if [ -f /.tomcat_admin_created ]; then
    echo "Tomcat 'admin' user already created"
    exit 0 
fi

## generate password
PASS=${TOMCAT_PASS:-$(pwgen -s 12 1)}
_word=$( [ ${TOMCAT_PASS} ] && echo "preset" || echo "random")
echo "=> Creating and admin user with a ${_word} password in Tomcat"

##sed -i -r 's/<\/tomcat-user>//' ${CATALINA_HOME}/conf/tomcat-users.xml
## 这句的主要用法就是原本的`tomcat-users.xml`里存在一个空的``到``的字段,直接用sed删除最后一行,即``
## 然后补上我们生成的密码的相关内容,最后再加上``
 
 
sed -i '$d' ${CATALINA_HOME}/conf/tomcat-users.xml
echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo "" >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo "=> Done!"
touch /.tomcat_admin_created
echo "======================================================"
echo "You can now configure to this Tomcat server using:"
echo ""
echo "      admin:${PASS}"
echo ""
echo "======================================================"

Tomcat 启动脚本 run-tomcat.sh:

##!/bin/bash
 
if [ ! -f /.tomcat_admin_created ]; then
    /create_tomcat_admin_user.sh
fi

exec ${CATALINA_HOME}/bin/catalina.sh run 

tomcat 密码脚本最后生成的 tomcat-users.xml 文件:


















本章小结

中间件服务器是 Docker 容器应用的最佳实践, 理由如下:

  • 中间件服务器是除数据库服务器外的主要计算节点, 很容易成为性能瓶颈, 所以通常需要大批量部署, 而Docker 对于批量部署有着许多的先天优势
  • 中间件服务器结构清晰, 在剥离了配置文件 日志 代码目录 之后, 容器几乎可以处于零增长状态, 这使得容器的迁移和批量部署更加方便.
  • 中间件服务器很容易实现集群, 在使用硬件的F5, 软件的Nginx 等负载均衡后, 中间件服务器集群变得非常容易

在使用中间件容器的时候, 需要事先规划好容器的用途和可能开放的网络端口等资源.

对于程序代码 程序的资源目录 日志 数据库文件 等需要实时更新的数据一定要通过 -v 参数映射到宿主主机的目录中来, 使用 Docker 的 AUFS 文件格式, 会产生较大的性能问题.

IBM研究院关于Docker各项性能的测试报告

你可能感兴趣的:(Docker 基础 - 3)