Docker 基礎 - 3

東風微鳴發表於2023-01-26

Web 伺服器與應用

Nginx

我的Nginx Docker映象

## 設定繼承自己建立的 sshd 映象
FROM caseycui/ubuntu-sshd

## 維護者
LABEL maintainer="CaseyCui cuikaidong@foxmail.com"

## 安裝 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 cuikaidong@foxmail.com"

## 建立 /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 cuikaidong@foxmail.com'
 
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`裡存在一個空的`<tomcat-users>`到`</tomcat-users>`的欄位,直接用sed刪除最後一行,即`</tomcat-users>`
## 然後補上我們生成的密碼的相關內容,最後再加上`</tomcat-users>`
 
 
sed -i '$d' ${CATALINA_HOME}/conf/tomcat-users.xml
echo '<role rolename="manager-gui"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '<role rolename="manager-script"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '<role rolename="manager-jmx"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '<role rolename="admin-gui"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '<role rolename="admin-script"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo "<user username=\"admin\" password=\"${PASS}\" roles=\"manager-gui, manager-script, manager-jmx, admin-gui, admin-script\"/>" >> ${CATALINA_HOME}/conf/tomcat-users.xml
echo '</tomcat-users>' >> ${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 檔案:

<?xml version='1.0' encoding='utf-8'?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at
      http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->

<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
              version="1.0">

<!--
  NOTE:  By default, no user is included in the "manager-gui" role required
  to operate the "/manager/html" web application.  If you wish to use this app,
  you must define such a user - the username and password are arbitrary. It is
  strongly recommended that you do NOT use one of the users in the commented out
  section below since they are intended for use with the examples web
  application.
-->

<!--
  NOTE:  The sample user and role entries below are intended for use with the
  examples web application. They are wrapped in a comment and thus are ignored
  when reading this file. If you wish to configure these users for use with the
  examples web application, do not forget to remove the <!.. ..> that surrounds
  them. You will also need to set the passwords to something appropriate.
-->

<!--
  <role rolename="tomcat"/>
  <role rolename="role1"/>
  <user username="tomcat" password="<must-be-changed>" roles="tomcat"/>
  <user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
  <user username="role1" password="<must-be-changed>" roles="role1"/>
-->

<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<role rolename="admin-gui"/>
<role rolename="admin-script"/>
<user username="admin" password="vNc8guWjdrBP" roles="manager-gui, manager-script, manager-jmx, admin-gui, admin-script"/>
</tomcat-users>

本章小結

中介軟體伺服器是 Docker 容器應用的最佳實踐, 理由如下:

  • 中介軟體伺服器是除資料庫伺服器外的主要計算節點, 很容易成為效能瓶頸, 所以通常需要大批次部署, 而Docker 對於批次部署有著許多的先天優勢
  • 中介軟體伺服器結構清晰, 在剝離了配置檔案 日誌 程式碼目錄 之後, 容器幾乎可以處於零增長狀態, 這使得容器的遷移和批次部署更加方便.
  • 中介軟體伺服器很容易實現叢集, 在使用硬體的F5, 軟體的Nginx 等負載均衡後, 中介軟體伺服器叢集變得非常容易

在使用中介軟體容器的時候, 需要事先規劃好容器的用途和可能開放的網路埠等資源.

❗ 對於程式程式碼 程式的資源目錄 日誌 資料庫檔案 等需要實時更新的資料一定要透過 -v 引數對映到宿主主機的目錄中來, 使用 Docker 的 AUFS 檔案格式, 會產生較大的效能問題.

IBM研究院關於Docker各項效能的測試報告

三人行, 必有我師; 知識共享, 天下為公. 本文由東風微鳴技術部落格 EWhisper.cn 編寫.

相關文章