Docker鏡像管理及優化最佳實踐

前面提到,Docker通過從一個Dockerfile包含所有命令的文本文件中讀取指令來自動構建鏡像,這些命令按順序構建給定鏡像。

一個Dockerfile遵循特定的格式和指令集,常用指令參考這裏:https://blog.csdn.net/miss1181248983/article/details/88718517


Docker Build命令

docker build 命令是根據上下文自動構建鏡像。構建上下文是指定位置PATH或文件集URL,PATH是本地文件系統上的目錄,URL是一個Git倉庫地址。

  • 示例:
Usage: docker build [OPTIONS] PATH | URL | - [flags]

# docker build .

# docker build -t lzx/myapp .

# docker build -t lzx/myapp -f /path/Dockerfile /path

# docker build -t lzx/myapp http://www.example.com/Dockerfile

構建由Docker守護程序運行,而不是CLI。構建過程第一件事是將整個上下文(遞歸)發送到守護進程。

建議將空目錄作爲上下文,並將Dockerfile保存在該目錄中,目錄中僅包含構建Dockerfile所需的文件。


構建PHP網站並部署

  • 目錄結構:
# mkdir -p lnmp/{base,project}

# tree lnmp/
lnmp/
├── base
│   ├── Dockerfile-nginx
│   └── Dockerfile-php
└── project
    ├── Dockerfile-nginx
    ├── Dockerfile-php
    ├── nginx.conf
    └── wwwroot
        └── phpinfo.php
  • 編輯 Dockerfile-nginx:
# cd lnmp/base/

# vim Dockerfile-nginx
FROM centos
MAINTAINER lzx [email protected]
RUN yum install -y gcc gcc-c++ make openssl-devel pcre-devel gd-devel libxslt-devel \
    iproute net-tools telnet wget curl lrzsz vim-enhanced  epel-release rsync unzip && \
    yum clean all && \
    rm -rf /var/cache/yum/*
RUN wget http://nginx.org/download/nginx-1.14.1.tar.gz && \
    tar zxf nginx-1.14.1.tar.gz && \
    cd nginx-1.14.1 && \
    ./configure --prefix=/usr/local/nginx \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_realip_module \
    --with-http_image_filter_module \
    --with-http_gunzip_module \
    --with-http_gzip_static_module \
    --with-http_secure_link_module \
    --with-http_stub_status_module \
    --with-stream \
    --with-stream_ssl_module && \
    make -j 4 && make install && \
    mkdir -p /usr/local/nginx/conf/vhost && \
    rm -rf /usr/local/nginx/html/* && \
    echo "OK" >> /usr/local/nginx/html/status.html && \
    cd / && rm -rf nginx-1.14.1*
ENV PATH $PATH:/usr/local/nginx/sbin
WORKDIR /usr/local/nginx
EXPOSE 80
CMD ["nginx","-g","daemon off;"]
  • 構建nginx的基礎鏡像:
# docker build -t nginx-141 -f Dockerfile-nginx .

# docker container run -d -p 80:80 nginx-141

在這裏插入圖片描述

  • 編輯 Dockerfile-php:
# vim Dockerfile-php
FROM centos
MAINTAINER lzx [email protected]
RUN yum install -y epel-release && \
    yum install -y gcc gcc-c++ make gd-devel libxml2-devel libcurl-devel \
    libjpeg-devel libpng-devel openssl-devel libmcrypt-devel libxslt-devel libtidy-devel \
    autoconf iproute net-tools telnet wget curl lrzsz vim-enhanced  rsync unzip nload git && \
    yum clean all && \
    rm -rf /var/cache/yum/*
RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
    tar zxf php-5.6.36.tar.gz && \
    cd php-5.6.36 && \
    ./configure --prefix=/usr/local/php \
    --with-config-file-path=/usr/local/php/etc \
    --with-config-file-scan-dir=/usr/local/php/etc/php.d \
    --enable-fpm --enable-opcache --enable-static=no \
    --with-mysql --with-mysqli --with-pdo-mysql \
    --enable-phar --with-pear --enable-session \
    --enable-sysvshm --with-tidy --with-openssl \
    --with-zlib --with-curl --with-gd --enable-bcmath \
    --with-jpeg-dir --with-png-dir --with-freetype-dir \
    --with-iconv --enable-posix --enable-zip \
    --enable-mbstring --with-mhash --with-mcrypt --enable-hash \
    --enable-xml --enable-libxml --enable-debug=no && \
    make -j 4 && make install && \
    cp php.ini-production /usr/local/php/etc/php.ini && \
    cp sapi/fpm/php-fpm.conf /usr/local/php/etc/php-fpm.conf && \
    sed -i "90a \daemonize = no" /usr/local/php/etc/php-fpm.conf && \
    mkdir /usr/local/php/log && \
    cd / && rm -rf php*
ENV PATH $PATH:/usr/local/php/sbin
WORKDIR /usr/local/php
EXPOSE 9000
CMD ["php-fpm"]
  • 構建php的基礎鏡像:
# docker build -t php-56 -f Dockerfile-php .
  • 構建部署lnmp的php鏡像php:v1:
# cd ../project/

# mkdir wwwroot

# cat wwwroot/phpinfo.php 
<?php
phpinfo();
?>

# vim Dockerfile-php

FROM php-56
COPY wwwroot /wwwroot
CMD ["php-fpm"]

# docker build -t php:v1 -f Dockerfile-php .
  • nginx.conf:
# cat nginx.conf

user    nobody;
worker_processes        1;
error_log       logs/error.log  info;
events {
        worker_connections      1024;
}

http {
        include         mime.types;
        default_type    application/octet-stream;
        log_format      main    '$remote_addr - $remote_user [$time_local] "$request"'
                                '$status $body_bytes_sent "$http_referer"'
                                '"$http_user_agent" "$http_x_forwarded_for"';
        sendfile        on;
        keepalive_timeout       65;
        server {
                listen  80;
                server_name     localhost;
                access_log      logs/host.access.log    main;

                location / {
                        root    html;
                        index   index.html index.htm index.php;
                }
                location ~\.php$ {
                        fastcgi_pass    127.0.0.1:9000;
                        fastcgi_index   index.php;
                        fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
                        include         fastcgi_params;
                }
        }
}
  • 構建部署lnmp的nginx鏡像nginx:v1:
# vim Dockerfile-nginx

FROM nginx-141
COPY nginx.conf /usr/local/nginx/conf/

# docker build -t nginx:v1 -f Dockerfile-nginx .
  • 部署LNMP:
# docker network create lnmp

# docker volume create wwwroot

# docker container run -d --name lnmp_nginx -p 80:80 --net lnmp --mount src=wwwroot,dst=/usr/local/nginx/html nginx:v1

# docker container run -d --name lnmp_php --net container:lnmp_nginx --mount src=wwwroot,dst=/usr/local/nginx/html php:v1

創建php容器時指定與nginx容器同一網絡,這樣nginx就可以代理127.0.0.1:9000到php-fpm了。

  • 訪問宿主機IP:

在這裏插入圖片描述


構建JAVA網站鏡像並部署

  • 在宿主機安裝JDK,容器以掛在形式使用:
# tar zxf jdk-8u191-linux-x64.tar.gz

# mv jdk1.8.0_191/ /usr/local/jdk
  • 編輯 Dockerfile-java:
# vim Dockerfile-java
FROM centos:latest
MAINTAINER lzx

ENV VERSION=8.5.39
ENV JAVA_HOME /usr/local/jdk

RUN yum install -y wget curl unzip iproute net-tools && \
    yum clean all && \
    rm -rf /var/cache/yum/*

RUN wget http://mirrors.shu.edu.cn/apache/tomcat/tomcat-8/v${VERSION}/bin/apache-tomcat-${VERSION}.tar.gz && \
    tar zxf apache-tomcat-${VERSION}.tar.gz && \
    mv apache-tomcat-${VERSION} /usr/local/tomcat && \
    rm -rf apache-tomcat-${VERSION}.tar.gz /usr/local/tomcat/webapps/* && \
    mkdir /usr/local/tomcat/webapps/ROOT && \
    echo "OK" > /usr/local/tomcat/webapps/ROOT/status.html && \
    sed -i '1a JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"' /usr/local/tomcat/bin/catalina.sh && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ENV PATH $PATH:/usr/local/tomcat/bin
WORKDIR /usr/local/tomcat
EXPOSE 8080
CMD ["catalina.sh","run"]
  • 構建tomcat的基礎鏡像:
# docker build -t tomcat-85 -f Dockerfile-java .

# docker container run -d -p 89:8080 -v /usr/local/jdk:/usr/local/jdk tomcat-85
  • 訪問網頁:

在這裏插入圖片描述


編寫Dockerfile最佳實踐

  1. 減少鏡像層

一次RUN指令形成新的一層鏡像層,儘量shell命令都寫在一行,減少鏡像層。

  • 例如:
FROM centos
MAINTAINER lzx [email protected]
RUN yum install -y epel-release
RUN yum install -y gcc gcc-c++ make 
RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz
RUN tar zxf php-5.6.36.tar.gz
RUN cd php-5.6.36
RUN ./configure --prefix=/usr/local/php
RUN make -j 4 
RUN make install 
EXPOSE 9000
CMD ["php-fpm"]

應該寫成:

FROM centos
MAINTAINER lzx [email protected]
RUN yum install -y epel-release && \
    yum install -y gcc gcc-c++ make
RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
    tar zxf php-5.6.36.tar.gz && \
    cd php-5.6.36 && \
    ./configure --prefix=/usr/local/php && \
    make -j 4 && make install 
EXPOSE 9000
CMD ["php-fpm"]

結果:12層 → 6層

  1. 優化鏡像大小,清理無用數據

一次RUN形成新的一層鏡像層,如果沒有在同一層刪除,無論文件是否在最後刪除,都會帶到下一層,所以要在每一層清理對應的殘留數據,減小鏡像大小。

FROM centos
MAINTAINER lzx [email protected]
RUN yum install -y epel-release && \
    yum install -y gcc gcc-c++ make gd-devel libxml2-devel libcurl-devel \
    libjpeg-devel libpng-devel openssl-devel libmcrypt-devel libxslt-devel libtidy-devel \
    autoconf iproute net-tools telnet wget curl lrzsz vim-enhanced  rsync unzip&& \
    yum clean all && \
    rm -rf /var/cache/yum/*
RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
    tar zxf php-5.6.36.tar.gz && \
    cd php-5.6.36 && \
    ./configure --prefix=/usr/local/php && \
    make -j 4 && make install && \
    cp php.ini-production /usr/local/php/etc/php.ini && \
    cp sapi/fpm/php-fpm.conf /usr/local/php/etc/php-fpm.conf && \
    sed -i "90a \daemonize = no" /usr/local/php/etc/php-fpm.conf && \
    mkdir /usr/local/php/log && \
    cd / && rm -rf php*

這樣至少可以節省幾十M,甚至上百M。

  1. 減少網絡傳輸時間

最好在內部有一個存放軟件包的地方,即應用倉庫,類似於上面的PHP官方下載地址,如果用到maven構建這樣的操作,同時也更改爲私有的maven倉庫,減少網絡傳輸時間,提高鏡像構建速度。

  1. 多階段鏡像構建

如果運行一個項目,根據上面的做法,是把代碼拷貝到基礎鏡像裏,如果是一個需要預先代碼編譯的項目呢?例如JAVA語言,如何代碼編譯、部署在一起完成呢?

上面做法需要事先在一個Dockerfile構建一個基礎鏡像,包括項目運行時環境及依賴庫,再寫一個Dockerfile將項目拷貝到運行環境中,有點繁瑣了。

像JAVA這類語言如果代碼編譯是在Dockerfile裏操作,還需要把源代碼構建進去,但實際運行時只需要構建出的包,這種把源代碼放進去會有一定的安全風險,也增加了鏡像體積。

爲了解決上述問題,Docker 17.05開始支持多階段構建(multi-stage builds),可以簡化Dockerfile,減少鏡像大小。

  • 例如:
# git clone https://github.com/b3log/solo.git

# cd solo/

# vim Dockerfile-solo

FROM maven AS build
ADD ./pom.xml pom.xml
ADD ./src src/
RUN mvn clean package

FROM tomcat-85
RUN rm -rf /usr/local/tomcat/webapps/ROOT
COPY --from=build target/*.war /usr/local/tomcat/webapps/ROOT.war
CMD ["catalina.sh","run"]

# docker build -t solo:v1 -f Dockerfile-solo .

通過pom.xml中的配置,就能夠獲取到相應的war包,target/ 目錄是maven的輸出目錄。

首先,第一個FROM後面多了個AS關鍵字,可以給這個階段起個名字。
然後,第二部分FROM用的之前構建的Tomcat鏡像,COPY關鍵字增加了--from參數,用於拷貝某個階段的文件到當前階段。

鏡像小有很多好處,例如快速部署、快速回滾;減少服務中斷時間,同時鏡像倉庫佔用磁盤空間也會減少。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章