前面提到,Docker通过从一个Dockerfile包含所有命令的文本文件中读取指令来自动构建镜像,这些命令按顺序构建给定镜像。
一个Dockerfile遵循特定的格式和指令集,常用指令参考这里:https://blog.csdn.net/miss1181248983/article/details/88718517
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所需的文件。
# mkdir -p lnmp/{base,project} # tree lnmp/ lnmp/ ├── base │ ├── Dockerfile-nginx │ └── Dockerfile-php └── project ├── Dockerfile-nginx ├── Dockerfile-php ├── nginx.conf └── wwwroot └── phpinfo.php
# 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;"]
# docker build -t nginx-141 -f Dockerfile-nginx . # docker container run -d -p 80:80 nginx-141
# 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"]
# docker build -t php-56 -f Dockerfile-php .
# 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 .
# 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; } } }
# vim Dockerfile-nginx FROM nginx-141 COPY nginx.conf /usr/local/nginx/conf/ # docker build -t nginx:v1 -f Dockerfile-nginx .
# 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了。
# tar zxf jdk-8u191-linux-x64.tar.gz # mv jdk1.8.0_191/ /usr/local/jdk
# 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"]
# docker build -t tomcat-85 -f Dockerfile-java . # docker container run -d -p 89:8080 -v /usr/local/jdk:/usr/local/jdk tomcat-85
一次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
参数,用于拷贝某个阶段的文件到当前阶段。
镜像小有很多好处,例如快速部署、快速回滚;减少服务中断时间,同时镜像仓库占用磁盘空间也会减少。