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
MAINTAINER lzx [email protected]
ADD jdk-8u191-linux-x64.tar.gz /usr/local/
RUN yum install -y vim wget curl unzip iproute net-tools && \
yum clean all && \
rm -rf /var/cache/yum/* && \
mv /usr/local/jdk1.8.0_191/ /usr/local/jdk && \
wget http://mirrors.shu.edu.cn/apache/tomcat/tomcat-8/v8.5.39/bin/apache-tomcat-8.5.39.tar.gz && \
tar zxf apache-tomcat-8.5.39.tar.gz && \
mv apache-tomcat-8.5.39 /usr/local/tomcat && \
rm -rf apache-* jdk-* && \
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 JAVA_HOME /usr/local/jdk
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最佳实践
- 减少镜像层
一次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层
- 优化镜像大小,清理无用数据
一次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。
- 减少网络传输时间
最好在内部有一个存放软件包的地方,即应用仓库,类似于上面的PHP官方下载地址,如果用到maven构建这样的操作,同时也更改为私有的maven仓库,减少网络传输时间,提高镜像构建速度。
- 多阶段镜像构建
如果运行一个项目,根据上面的做法,是把代码拷贝到基础镜像里,如果是一个需要预先代码编译的项目呢?例如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
参数,用于拷贝某个阶段的文件到当前阶段。
镜像小有很多好处,例如快速部署、快速回滚;减少服务中断时间,同时镜像仓库占用磁盘空间也会减少。