如果你想从一个基础镜像开始构建一个自定义的镜像,可以选择一步步的操作,也可以选择编写一个配置脚本,然后一条docker build
命令完成构建,显然使用配置脚本的方式能更好地应对需求的变更,这里的配置脚本也就时Dockerfile。
- Dockerfile文件用于构建镜像
- Dockfile语法由两部分构成分别是注释、指令+参数
- Dockerfile的指令时忽略大小写的,建议使用小写。
- Dockerfile注释使用
#
标记 - Dockerfile每行只支持一条指令
例如:Swoft默认的Dockerfile文件
FROM php:7.1
MAINTAINER huangzhhui <h@swoft.org>
# Version
ENV PHPREDIS_VERSION 4.0.0
ENV HIREDIS_VERSION 0.13.3
ENV SWOOLE_VERSION 4.0.3
# Timezone
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo 'Asia/Shanghai' > /etc/timezone
# Libs
RUN apt-get update \
&& apt-get install -y \
curl \
wget \
git \
zip \
libz-dev \
libssl-dev \
libnghttp2-dev \
libpcre3-dev \
&& apt-get clean \
&& apt-get autoremove
# Composer
RUN curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer \
&& composer self-update --clean-backups
# PDO extension
RUN docker-php-ext-install pdo_mysql
# Bcmath extension
RUN docker-php-ext-install bcmath
# Redis extension
RUN wget http://pecl.php.net/get/redis-${PHPREDIS_VERSION}.tgz -O /tmp/redis.tar.tgz \
&& pecl install /tmp/redis.tar.tgz \
&& rm -rf /tmp/redis.tar.tgz \
&& docker-php-ext-enable redis
# Hiredis
RUN wget https://github.com/redis/hiredis/archive/v${HIREDIS_VERSION}.tar.gz -O hiredis.tar.gz \
&& mkdir -p hiredis \
&& tar -xf hiredis.tar.gz -C hiredis --strip-components=1 \
&& rm hiredis.tar.gz \
&& ( \
cd hiredis \
&& make -j$(nproc) \
&& make install \
&& ldconfig \
) \
&& rm -r hiredis
# Swoole extension
RUN wget https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz -O swoole.tar.gz \
&& mkdir -p swoole \
&& tar -xf swoole.tar.gz -C swoole --strip-components=1 \
&& rm swoole.tar.gz \
&& ( \
cd swoole \
&& phpize \
&& ./configure --enable-async-redis --enable-mysqlnd --enable-openssl --enable-http2 \
&& make -j$(nproc) \
&& make install \
) \
&& rm -r swoole \
&& docker-php-ext-enable swoole
ADD . /var/www/swoft
WORKDIR /var/www/swoft
RUN composer install --no-dev \
&& composer dump-autoload -o \
&& composer clearcache
EXPOSE 80
ENTRYPOINT ["php", "/var/www/swoft/bin/swoft", "start"]
操作流程
- 创建一个Dockerfile文件
- 编写Dockerfile指令
- 使用
docker build
构建镜像 - 使用
docker run
运行容器 - 检查镜像与容器
构建流程
指令组成
- 基础镜像信息:
FROM
- 维护者信息:
MAINTAINER
- 镜像操作指令:
RUN
、COPY
、ADD
、EXPOSE
、WORKING
、USER
、VOLUME
... - 容器启动时执行指令:
CMD
、ENTRYPOINT
指令解析
ADD
ADD <源文件><容器目录>
ADD指令会从源系统的文件系统上复制文件到目标容器的文件系统,若源系统是一个URL,那么该URL的内容将被下载并复制到容器中。
如果本地源路径的文件是一个tar
压缩文件的话,压缩格式为gzip
、bzip2
、xz
的情况下,ADD指令将会自动解压到目标路径,但是来自URL的远程文件时不会被解压的。
例如:ADD application.yml /etc/service
CMD
CMD <命令><参数>
CMD指令只要是为一个正在运行的容器提供默认的执行命令,如果存在多个CMD指令,那么只有最后一个会被执行。如果在容器运行时指定了命令,则CMD指定的默认命令会被替代。
CMD指令用于执行特定的命令,命令不是在镜像构建过程中执行,而是在用镜像构建容器后被调用。
CMD有三种格式:
- CMD ["executable", "parameter1", "parameter2"]
常见执行形式:CMD ["echo", "hello world"]
- CMD ["parameter1", "parameter2"]
以JSON数组的形式将两个参数存储起来,在指定了ENTRYPOINT入口点指定后,使用CMD指定具体的参数,此处必须用双引号将变量包裹。 - CMD command parameter1 parameter2
使用Shell形式:CMD echo "hello world"
ENTRYPOINT
ENTRYPOINT
ENTRYPOINT指令的格式和RUN指令格式一样,分为exec
格式和shell
格式,与CMD指令目的一样,都是在指定容器启动程序及参数。
ENTRYPOINT在运行时可以替代,不过比CMD要略显繁琐,需要通过docker run
的参数--entrypoint
来指定。当指定了ENTRYPOINT后,CMD的含义就发生了改变,不再是直接运行其命令,而是将CMD的内容作为参数传递给ENTRYPOINT指令。
ENTRYPOINT指令配置容器启动后执行的命令,不会被docker run
命令提供的参数覆盖。
每个Dockfile中只能有一个ENTERPINT入口点,当指定多个时只有最后一个起效。
ENTERPOINT入口点帮助你配置一个容器使之可执行化,如果结合CMD使用时可从CMD命令中删除"application"而仅仅保留参数,参数将传递给ENTERPOINT命令。
ENV
ENV <键名><键值>
ENV指令用于设置键值对的环境变量,可在容器内被脚本或程序调用。并且可以引用已经存在的环境变量,例如HOME、HOSTNAME、PATH。
Dockerfile中ENV支持以下变量的访问:ADD、COPY、ENV、EXPOSE、FROM、LABEL、STOPSIGNAL、USER、VOLUME、WORKDIR。
例如:ENV SERVER_WORKS 4
EXPOSE
EXPOSE <端口>
EXPOSE指令用于指定容器在运行时监听的端口,使容器内的应用可通过端口与外界交互。
EXPOSE指令用于暴露镜像的端口供宿主机(主机)做映射,启动镜像时,可使用-P
参数将镜像端口与宿主机的随机端口做映射。
默认情况下,EXPOSE指定的时TCP端口,若要指定监听UDP端口:EXPOSE 80/udp
FROM
FROM <镜像名称>
FROM <镜像名称>[:<标签名称>]
每个Dockerfile必须以FROM
指令开头,FROM
指明了当前镜像创建的基镜像,也就是说每个镜像必须基于一个已经存在的镜像才能创建。
FROM
指令后面直接跟基镜像的名称或【镜像名称+标签】,镜像名称和标签可以去DockerHub或使用docker search keyword
进行搜索。
FROM
指令定义使用哪个基础镜像启动构建流程,基本镜像可以为任意镜像。如果基础镜像没有被发现,Docker将试图从docker image index
来查找该镜像。
MAINTAINER
MAINTAINER <作者名称>
MAINTAINER指令用于声明作者,建议放在FROM
命令后,理论上可放在任意位置。MAINTAINER在新版中已经被废弃,可使用LABEL来替代进行声明。
例如:MAINTAINER author_name
指定镜像作者与电子邮箱
例如:MAINTAINER junchow "1021013123@qq.com"
RUN
RUN <命令>
RUN指令是Dockerfile执行命令的核心部分,它接收命令作为参数并用于创建镜像,会在之前的Commit的层之上形成新的层。
RUN指令在当前镜像的顶层中执行命令并提交结果,新产生的镜像用于下一步的Dockerfile。
例如:RUN aptitude install -y docker
USER
USER <user>[:<group>]
USER指令用于设置运行容器的用户ID,主要是为Dockerfile中全部RUN、CMD、ENTRYPOINT设置运行镜像Image时使用的用户名或UID。这个用户或组必须事先在系统中存在,若不存在则下一层镜像会以root
用户进行执行。
例如:USER 751
VOLUME
VOLUME <宿主机目录>
VOLUME旨在创建一个具有名称的挂载点,用于让容器访问宿主机上的目录。容器在运行时尽量保持存储层不发生数据写入操作。一个卷可以存在于一个或多个容器的特定目录,这个目录可以绕过联合文件系统,并提供数据共享或数据持久化功能。卷可以在容器间共享或重用,对卷的修改是即时生效的。对卷的修改不会对新的镜像产生影响,卷会一直存在直到没有容器使用它。
例如:VOLUME /share
可以使用数组的形式指定多个卷
例如:VOLUME ['data', 'test']
VOLUME可以在创建容器时声明
例如:docker run -it -v /share --name mycontainer chatdevops
WORKING
WORKING <CMD命令的运行目录>
WOKKING
指令用于设置CMD指定的命令的运行目录
例如:WORKING ~/
CMD与ENTRYPOINT的区别
CMD指令与ENTRYPOINT指令有什么异同点呢?
CMD和ENTRYPOINT都可以作为容器启动时执行的命令,区别在于:
- CMD指令会被
docker run
命令覆盖,ENTRYPOINT
则不会。
如果设置CMD ['/bin/bash']
或ENTRYPOINT ['/bin/bash']
后,再使用docker run -it image
启动容器时会自动进入容器内部的交互终端,效果等同于docker run -it image /bin/bash
。
如果使用docker run -it image /bin/ps
启动镜像,此时CMD指定的参数就会被覆盖,转而执行/bin/ps
命令。而ENTRYPOINT指定的参数则不会被覆盖,它会把docker run
的参数作为ENTRYPOINT执行命令的参数。
- 当CMD和ENTRYPOINT都存在时
当CMD和ENTRYPOINT指令都存在时,CMD指令会变成ENTRYPOINT的参数,并且CMD提供的参数会被docker run
覆盖。
未完待续...