Dockerfile 介绍

Dockerfile 是由一行一行的命令语句组成,并且从上到下执行,支持以#注释行。一般 Dockerfile 的内容分为四个部分,基础镜像信息、维护者信息、镜像操作指令、容器启动时执行指令

Dockerfile 常用指令

指令 描述
FROM 指定构建新镜像时是基于那个镜像,Dockerfile的第一条指令必须为FROM指令,如果在同一个Dockerfile中创建多个镜像可以使用多个FROM指令
LABEL 为镜像添加标签
RUN 每条RUN指令将在当前镜像的基础上执行指定shell命令,并提交为新的镜像
COPY 拷贝宿主机(Dockerfile所在目录的相对路径)的文件或目录到镜像中
ADD 复制指定的<src>到容器中的<dest>,<src>可以是Dockerfile所在目录的文件或目录,可以是一个URL,还可以是一个tar文件(自动解压缩)
ENV 指定一个环境变量,会被后续RUN指令使用,并在容器运行时保持
USER 指定运行容器时的用户名或UID,后续的RUN也会使用指定用户
EXPOSE 声明容器运行的服务端口,启动容器时可以将这些端口转发到宿主机或者指定宿主机那个端口映射过来
WORKDIR 为后续的RUN、CMD、ENTRYPOINT指令配置工作目录
VOLUME 在镜像中创建挂载点,这样只要通过该镜像创建的容器都有了挂载点,查看容器详细信息可以看到容器挂载点映射到宿主机的目录
CMD 容器启动时执行指令,每个Dockerfile只能有一条CMD指令,如果有多个CMD指令只有最后一个生效
ENTRYPOINT ENTRYPOINT如果与CMD一起使用,CMD将作为ENTRYPOINT的默认参数,如果有多个ENTRYPOINT指令只有最后一个生效

构建镜像

Dockerfile demo

# This dockerfile demo for project build to docker images

FROM centos:7

LABEL maintainer www.missf.top

USER root

RUN yum install -y nginx

EXPOSE 80 443

VOLUME ["/usr/local/nginx/"]

CMD ["/usr/local/nginx/bin"]

Docker build构建镜像

# 在Dockerfile所在的目录下构建镜像,后面的"."表示当前目录
docker build -t demo:1.0 .
# 构建过程如下
Sending build context to Docker daemon  2.048kB
Step 1/8 : FROM centos:7
7: Pulling from library/centos
524b0c1e57f8: Pull complete 
Digest: sha256:e9ce0b76f29f942502facd849f3e468232492b259b9d9f076f71b392293f1582
Status: Downloaded newer image for centos:7
 ---> b5b4d78bc90c
Step 2/8 : LABEL maintainer mownejie
 ---> Running in 7dbcab7ef3ce
Removing intermediate container 7dbcab7ef3ce
 ---> 4db1e9da6977
Step 3/8 : ENV JAVA_HOME /usr/local/java
 ---> Running in b896cedee458
Removing intermediate container b896cedee458
 ---> f8991838d97e
Step 4/8 : USER root
 ---> Running in 8252457198f0
Removing intermediate container 8252457198f0
 ---> 96ef213928ad
Step 5/8 : RUN yum install -y nginx
 ---> Running in 8807973810c5......

# -t 指定这个镜像的tag
# -f 指定这个Dockerfile文件的位置

CMD 与 ENTRYPOINT 区别

CMD用法

# exec形式,首选形式,传参不支持引用变量
CMD ["executable", "param1", "param2"]

# CMD作为ENTRYPOINT的默认参数
CMD ["param1", "param2"]

# Shell形式
CMD command param1 param2

ENTRYPOINT用法

ENTRYPOINT ["executable", "param1", "param2"]    # 假如配合CMD一起使用,那么["param1", "param2"]可以写在CMD作为ENTRYPOINT的默认参数

ENTRYPOINT command param1 param2

总结

1. CMD和ENTRYPOINT指令都可以用来定义运行容器时所使用的命令
2. Dockerfile至少指定一个CMD或ENTRYPOINT
3. CMD可以用作ENTRYPOINT默认参数,或者用作容器的默认命令
4. docker run启动容器时指定<command>,将会覆盖dockerfile定义的CMD

构建 Nginx 容器镜像

Dockerfile 内容

FROM centos:7.7.1908

LABEL maintainer www.missf.top

RUN yum install -y gcc gcc-c++ make \
    openssl-devel pcre-devel gd-devel \
    iproute net-tools telnet wget curl && \
    yum clean all && rm -rf /var/cache/yum/*

ADD nginx-1.16.1.tar.gz /
RUN cd nginx-1.16.1 && \
    ./configure --user=nginx --group=nginx \
    --prefix=/usr/local/nginx \
    --with-http_stub_status_module \
    --with-http_ssl_module \
    --with-http_gzip_static_module \
    --with-http_sub_module && \
    make -j4 && make install && \
    mkdir /usr/local/nginx/conf/vhost && \
    cd / && rm -rf nginx* && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

RUN useradd -s /sbin/nologin nginx

ENV PATH $PATH:/usr/local/nginx/sbin

ENV LANG="en_US.utf8"

COPY nginx.conf /usr/local/nginx/conf/nginx.conf

COPY php.conf /usr/local/nginx/conf/vhost/php.conf

WORKDIR /usr/local/nginx

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

目录结构

[root@localhost /Dockerfile/nginx]# ll
total 1028
-rw-r--r-- 1 root root     890 Jul  6 18:58 Dockerfile
-rw-r--r-- 1 root root 1032630 Jan 14 09:53 nginx-1.16.1.tar.gz
-rw-r--r-- 1 root root    3297 Jul  6 18:46 nginx.conf
-rw-r--r-- 1 root root     362 Jul  6 20:13 php.conf
-rw-r--r-- 1 root root     128 Jul  6 18:51 start

构建PHP容器镜像

Dockerfile 内容

FROM centos:7.7.1908

LABEL maintainer www.missf.top

RUN yum install -y epel-release && \
    yum install -y sqlite-devel libmcrypt-devel mhash-devel libxslt-devel \
    libjpeg-devel libpng libpng-devel freetype freetype-devel \
    libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel libjpeg \
    glib2 glib2-develbzip2 bzip2-devel ncurses ncurses-devel \
    curl-devel e2fsprogs e2fsprogs-devel krb5 gcc krb5-devel libidn \
    openssl-devel libsqlite3x-devel oniguruma-devel openssl libidn-devel \
    iproute net-tools telnet wget curl && \
    yum clean all && rm -rf /var/cache/yum/*

ADD php-7.4.0.tar.gz /

RUN cd /php-7.4.0 && \
    ./configure --prefix=/usr/local/php \
    --with-config-file-path=/usr/local/php/etc \
    --enable-opcache --with-curl --enable-fpm \
    --enable-gd --with-iconv --enable-mbstring \
    --with-mysqli --with-openssl --enable-static \
    --enable-sockets --enable-inline-optimization \
    --with-zlib --disable-ipv6 --disable-fileinfo \
    --with-mcrypt --enable-hash --with-jpeg-dir --with-png-dir \
    --with-freetype-dir --with-pdo-mysql --disable-debug && \
    make -j 4 && make install && \
    cp /php-7.4.0/php.ini-production /usr/local/php/etc/php.ini && \
    cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf && \
    cp /usr/local/php/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf && \
    sed -i "90a \daemonize = no" /usr/local/php/etc/php-fpm.conf && \
    mkdir /usr/local/php/log && \
    cd / && rm -rf php* && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ENV PATH $PATH:/usr/local/php/sbin

ENV LANG="en_US.utf8"

COPY php.ini /usr/local/php/etc/

COPY php-fpm.conf /usr/local/php/etc/

COPY www.conf /usr/local/php/etc/php-fpm.d/

WORKDIR /usr/local/php

EXPOSE 9000

CMD ["php-fpm"]

目录结构

[root@localhost /Dockerfile/php]# ll
total 16144
-rw-r--r-- 1 root root     1758 Jul  6 18:53 Dockerfile
-rw-r--r-- 1 root root 16418792 Jul  1 10:39 php-7.4.0.tar.gz
-rw-r--r-- 1 root root     5394 Jul  1 21:51 php-fpm.conf
-rw-r--r-- 1 root root    72953 Jul  1 22:09 php.ini
-rw-r--r-- 1 root root       93 Jul  6 18:56 start
-rw-r--r-- 1 root root    19616 Jul  6 18:53 www.conf

容器化搭建个人博客

自定义网络

docker network create lnmp    # 将多个容器加入到一个自定义网络

创建 MySQL 容器

docker volume create mysql
docker run -e MYSQL_ROOT_PASSWORD=mwj123456 -e MYSQL_DATABASE=wordpress -p 53306:3306 --name "mysql" --network lnmp --mount src=mysql,dst=/var/lib/mysql/ -d mysql:5.7
# 将MySQL数据库的数据持久化到mysql这个数据卷

创建 PHP 容器

docker volume create nginx
docker run --name php --network lnmp --mount src=nginx,dst=/usr/local/nginx/html/ -d php:1.0    
# 这里先启动PHP容器再启动Nginx容器,因为Nginx要去连接PHP容器,如果PHP容器没有启动,那Nginx就因为无法连接到PHP所有退出了
# 这里需要把Nginx代码也挂载到PHP容器内,而且容器内的路径要与Nginx配置文件路径一致
# 因为Nginx配置文件将所有*.php的请求都通过fastcgi_pass代理到PHP容器去处理,所有需要把代码也挂载到PHP容器内,不然访问php文件会提示未找到文件

创建 Nginx 容器

docker container run --name "nginx" --mount src=nginx,dst=/usr/local/nginx/html --network lnmp -p 80:80 -p 443:443 -d nginx:1.0

部署 WordPress 代码

docker volume inspect nginx        # 先查看数据卷在宿主机上的目录,然后把代码解压到对应的目录下

tar xf wordpress-5.4.2-zh_CN.tar.gz -C /var/lib/docker/volumes/nginx/_data/

# 这时候通过访问宿主机的IP就能看到WordPress的安装页面了,如果无法对wp-config.php文件写入,就手动创建并写入

构建 Tomcat 容器镜像

Dockerfile 内容

FROM centos:7.7.1908

LABEL maintainer www.missf.top

ADD jdk-8u211-linux-x64.tar.gz /usr/local/

ADD apache-tomcat-8.5.57.tar.gz /usr/local/

RUN mv /usr/local/jdk1.8.0_211 /usr/local/jdk && \
    mv /usr/local/apache-tomcat-8.5.57 /usr/local/tomcat && \
    rm -rf /usr/local/tomcat/webapps/*

ENV JAVA_HOME /usr/local/jdk

ENV CLASSPATH ${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar

ENV CATALINA_HOME /usr/local/tomcat

ENV PATH $PATH:${JAVA_HOME}/bin:${CATALINA_HOME}/lib:${CATALINA_HOME}/bin

RUN sed -i '1a JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"' ${CATALINA_HOME}/bin/catalina.sh && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

WORKDIR ${CATALINA_HOME}

EXPOSE 8080

CMD ["catalina.sh", "run"]

目录结构

[root@localhost /Dockerfile/tomcat]# ll
total 200568
-rw-r--r-- 1 root root  10379806 Jul  7 11:19 apache-tomcat-8.5.57.tar.gz
-rw-r--r-- 1 root root       728 Jul  7 19:41 Dockerfile
-rw-r--r-- 1 root root 194990602 Jul  2  2019 jdk-8u211-linux-x64.tar.gz

部署测试代码

docker volume inspect tomcat    # 查看Tomcat容器代码目录持久化到宿主机的目录

ll /var/lib/docker/volumes/tomcat/_data        # 放到这个目录的war包会被自动解压
total 17840
drwxr-x--- 4 root root       37 Jul  7 21:34 ROOT
-rw-r--r-- 1 root root 18265402 Jun 20 13:08 ROOT.war

构建 Java 微服务项目镜像

Dockerfile 内容

# 一个容器内只跑一个jar包
FROM java:8-jdk-alpine

LABEL maintainer www.missf.top

ENV JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF8 -Duser.timezone=GMT+08 -Xms128m -Xmx128m"

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
    apk add -U tzdata && \
    mkdir /projects && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

COPY hello.jar /projects/

EXPOSE 8888

CMD ["/bin/sh", "-c", "java -jar $JAVA_OPTS /projects/hello.jar"]

Dockerfile 最佳实践

减少镜像层:一次 RUN 指令形成新的一层镜像,shell 命令尽量写在一行,减少镜像层

优化镜像大小:在形成新的一层镜像之后,如果没有在同一层删除缓存或者没用的文件,那么这些文件都会被带到下一层,所有要在每一层清理对应的残留数据,减少镜像大小

减少网络传输:例如镜像所需要下载的软件包,mvn 仓库

多阶段构建:代码编译、部署在一个 Dockerfile 完成,只会保留部署阶段产生的数据

选择最小的基础镜像:例如 alpine