前面提到,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最佳實踐
- 減少鏡像層
一次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
參數,用於拷貝某個階段的文件到當前階段。
鏡像小有很多好處,例如快速部署、快速回滾;減少服務中斷時間,同時鏡像倉庫佔用磁盤空間也會減少。