使用Dockerfile定制自己的docker镜像

我们都知道,在Linux系统下可以通过shell脚本来自动安装部署应用,这样不但免去了手动操作的麻烦,而且还可以通过一些自动化工具来实现批量安装部署。那么docker是否也可以通过脚本的方式定制镜像呢?当然可以,那就是Dockerfile,我们可以把创建应用镜像的操作都写入一个Dockerfile文件里,然后通过docker build命令来构建自已的镜像,这个过程类似shell脚本的功能。
docker环境的安装部署可以参考我的另一篇博文《Centos 7部署docker环境、基本命令使用及简单实战》,http://blog.51cto.com/andyxu/2174652 本文的所有操作都是基于这个环境执行的。
在使用dockerfile之前,我们先来看一个简单的例子:
实例1:查看本机公网IP地址

docker pull ubuntu
mkdir -p dockerfile/myip

创建Dockerfile文件
vim dockerfile/myip/Dockerfile

FROM ubuntu:latest   #基于ubuntu:latest镜像来构建新的镜像
MAINTAINER xuad   #描述镜像的作者信息
ARG APT=apt-get   #临时变量,只有在执行docker build命令构建容器时有效
RUN $APT update \   #构建容器时执行的命令
&& $APT install -y curl && rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://ip.cn" ]   #运行容器时执行的命令

构建镜像

cd dockerfile/myip/
docker build -t myip .


运行这个镜像
docker run myip

通过以上实例我们可以了解到使用Dockerfile来构建镜像的整个流程:
1、编写dockerfile文件;
2、通过docker build来创建新的镜像;
3、通过docker run来创建并运行新的容器。

build命令

Usage:  docker build [OPTIONS] PATH | URL | -
OPTIONS:
-t, --tag list??#指定构建的镜像名称和标记名称
-f, --file string?#指定Dockerfiile文件路径

示例:
1、docker build . #不指定镜像名称的话,将会默认以<none>作为镜像名称和标记名称
2、docker build -t myip:v1 . #镜像名称为nginx,标记名称为v1
3、docker build -t myip:v1 -f /path/Dockerfile /path #指定dockerfile文件路径
注:在构建镜像时,Docker daemon会首先将Dockerfile所在的目录构建成一个context(上下文),然后通过Dockerfile里的COPY或者ADD语句将context里的文件复制到指定的镜像目录里。所以需要复制到镜像里的文件,无论是脚本、安装包还是配置文件,都需要跟Dockerfile文件放在同一个目录下。
当我们执行docker build命令后,返回的第一条信息便是在构建上下文。
Sending build context to Docker daemon 2.048kB

Dockerfile指令详解

1、FROM指令
语法格式:

FROM <image>   #<tag>是可选项,没有指定<tag>的话,表示使用latest
FROM <image>:<tag>

说明:基于哪个镜像来构建新的镜像,FROM指令必须是dockerfile文件的第一行。
例如:FROM ubuntu:latest
2、MAINTAINER指令
语法格式:
MAINTAINER [作者信息]
说明:这个指令用于声明作者,用于将image的制作者相关的信息写入到image中。
例如:MAINTAINER xuad或者MAINTAINER xuad.com
3、ENV指令
语法格式:

ENV <key> <value>   #设置一个环境变量
ENV <key1>=<value1> <key2>=<value2>...   #设置多个环境变量

说明:定义环境变量,永久变量,容器运行后仍然有效,即容器内的永久变量。
例如:ENV pashname /usr/local/nginx或者ENV pashname=/usr/local/nginx VERSION=1.0
4、ARG指令
语法格式:

ARG <参数名>   #不设置默认值的话,需要使用--build-arg来设置参数的值
ARG <参数名>[=<默认值>]   #设置参数的默认值

说明:定义参数,即临时变量,只限于构建镜像时使用,容器运行后是不会存在的。
例如:ARG APT=apt-get
注:在构建命令docker build中用--build-arg <参数名>=<值> 可以覆盖此参数的值。
例如:docker build --build-arg APT=yum -t myip:v1 .
5、RUN指令
语法格式:

RUN <command>
RUN ["executable", "param1", "param2" ...]

说明:构建镜像时运行的shell命令
例如:RUN yum install httpd或者RUN ["yum", "install", "httpd"]
Dockerfile中每一个指令都会建立一层,RUN也不例外。多少个RUN就构建了多少层镜像,多个RUN会产生非常臃肿、多层的镜像,不仅仅增加了构建部署的时间,还容易出错。我们在写Dockerfile的时候,要尽量避免使用多个RUN,尽量将需要执行的命令都写在一个RUN里。多条命令可使用\来换行,换行的命令前面加上&&,最后还需要将不需要的文件及目录删除,比如安装包、临时目录等,减少容器的大小。
注:Union FS是有最大层数限制的,比如AUFS,曾经是最大不得超过42层,现在是不得超过127层。
以下是安装redis的一个例子:

FROM debian:jessie
RUN buildDeps=‘gcc libc6-dev make‘     && apt-get update     && apt-get install -y $buildDeps     && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"     && mkdir -p /usr/src/redis     && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1     && make -C /usr/src/redis     && make -C /usr/src/redis install     && rm -rf /var/lib/apt/lists/*     && rm redis.tar.gz     && rm -r /usr/src/redis     && apt-get purge -y --auto-remove $buildDeps

通过上面的命令我们可以看到后面添加的清理命令,删除了下载的安装包及解压出来的文件,删除了apt缓存文件,还卸载了编译安装所需要用到的软件。镜像是多层存储,每一层的东西都不会在下一层被删除,会一直跟随着镜像。因此在构建镜像时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。
6、WORKDIR指令
语法格式:

WORKDIR <工作目录路径>

说明:指定工作目录,以后各层指令的当前目录就是此目录。
例如:WORKDIR /usr/local/nginx
注:如果目录不存在,WORKDIR会自动创建这个目录。
7、COPY指令
语法格式:

COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]

说明:将上下文目录中的文件或目录复制到镜像内指定的目录下。
例如:COPY nginx.conf /usr/local/nginx/conf/或者COPY ["nginx.conf","/usr/local/nginx/conf/"]
注:源路径可以是多个,甚至可以使用通配符;目标路径可以是容器内的绝对路径,也可以是相对于WORKDIR指定的工作目录的相对路径。
8、ADD指令
语法格式:

ADD <源路径>... <目标路径>
ADD ["<源路径1>",... "<目标路径>"]

说明:更高级的复制指令,与COPY不同的是,针对压缩包会自动解压处理。
例如:html.tar.gz /var/www/html或者ADD https://xuad.com/html.tar.gz /var/www/html
注:源路径是一个压缩文件的话,将会解压到容器的目标路径下;源路径是一个URL的话,会下载或者解压到容器的目标路径下。
9、VOLUME指令
语法格式:

VOLUME <路径>
VOLUME ["<路径1>", "<路径2>"...]

说明:将本地卷挂载到容器中
例如:VOLUME /data
注:容器使用的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。当数据需要持久化时使用VOLUME指令,向挂载目录里写入的任何信息都不会写进容器存储层,从而保证了容器存储层的无变化。
10、EXPOSE指令
语法格式:

EXPOSE <端口1> [<端口2>...]

说明:声明容器运行时提供的服务端口
例如:EXPOSE 80 443
注:docker run命令可使用-p <宿主端口>:<容器端口>将声明的端口映射到本地指定的端口。
11、USER指令
语法格式:

USER <用户名>
USER <UID>

说明:指定运行的用户,以后各层的RUN、CMD或者ENTRYPOINT等指令都会以这个用户身份执行。
12、CMD指令
语法格式:

CMD <命令>
CMD ["可执行文件", "参数1", "参数2"...]

说明:容器运行时执行的shell命令,CMD指令一般应为Dockerfile文件的最后一行。
例如:CMD echo $HOME或者CMD [ "sh", "-c", "echo $HOME" ]
举个例子,启动nginx服务不能使用CMD systemctl start nginx,而应该使用CMD ["nginx", "-g", "daemon off;"]。因为docker会将CMD systemctl start nginx命令理解为CMD ["sh", "-c", "systemctl start nginx"],主进程实际上是sh,当systemctl start nginx命令执行完后,sh作为主进程退出了,自然容器也会退出。这就是为什么使用CMD systemctl start nginx指令,容器运行不起来的原因。正确的作法是将nginx以前台形式运行,即CMD ["nginx", "-g", "daemon off;"]。
13、ENTRYPOINT指令
语法格式:

ENTRYPOINT <命令>
ENTRYPOINT ["可执行文件", "参数1", "参数2"...]

说明:容器运行时执行的shell命令,一般应为Dockerfile文件的最后一行。
例如:ENTRYPOINT ["nginx", "-g", "daemon off;"]
注:与CMD不同的是,当指定了ENTRYPOINT后,CMD不再是直接运行命令,而是将CMD的内容作为参数传给ENTRYPOINT指令。
例如在Dockerfile文件中同时写了ENTRYPOINT和CMD,而CMD指令不是一个完整的命令,而是一个参数,如下:

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

此时CMD的内容会作为参数传递给ENTRYPOINT,相当于ENTRYPOINT ["nginx", "-g", "daemon off;"]。
如果CMD是一个完整的命令,如下:

ENTRYPOINT ["nginx", "-g", "daemon off;"]
CMD echo $HOME

那么ENTRYPOINT和CMD指令会互相覆盖,谁在最后谁生效。即ENTRYPOINT ["nginx", "-g", "daemon off;"]不会执行,执行的是CMD echo $HOME。
通过docker run命令给ENTRYPOINT传递参数,例如上面查看本机公网IP地址的例子,如果我们想查看HTTP头信息,那curl命令需要加上-i参数,运行docker run myip -i时会报下图错误。

将Dockerfile文件中CMD指令替换为ENTRYPOINT,即ENTRYPOINT [ "curl", "-s", "http://ip.cn" ],然后再重新构建镜像。

docker rmi -f myip:latest
docker build -t myip .
docker run myip -i


此时HTTP头信息成功打印出来了。
14、HEALTHCHECK指令
语法格式:

HEALTHCHECK [选项] CMD <命令>
HEALTHCHECK NONE   #如果基础镜像有健康检查指令,使用这行可以取消健康检查指令

说明:容器健康状态检查指令
例如:HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://localhost/ || exit 1

HEALTHCHECK的选项有:
--interval=<间隔>:两次健康检查的间隔时间,默认为30秒
--timeout=<时长>:每次检查的超时时间,超过这个时间,本次检查就视为失败,默认30秒
--retries=<次数>:指定健康检查的次数,默认3次,如果指定次数都失败后,则容器的健康状态为unhealthy(不健康)

CMD命令的返回值决定了本次健康检查是否成功,命令的返回值如下:
0: success - 成功 1: unhealthy - 失败 2: reserved - 保留
如果镜像添加了健康状态检查,容器运行后,在STATUS下会显示健康状态。
15、ONBUILD指令
语法格式:

ONBUILD <其它指令>

说明:指定以当前镜像为基础镜像构建的下一级镜像运行的命令
例如:ONBUILD COPY ./package.json /app或者ONBUILD RUN [ "npm", "install" ]
注:在当前镜像构建时不会执行,只有以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

Dockerfile实例

实例2:通过Dockerfile部署nginx容器
nginx版本是1.14.0,使用源码编译安装
(1)Dockerfile文件所在目录结构

(2)Dockerfile文件内容

FROM centos:7
MAINTAINER http://blog.51cto.com/andyxu
ENV TIME_ZOME Asia/Shanghai
ARG NV="nginx-1.14.0"

COPY nginx.conf /data/nginx/conf/
ADD $NV.tar.gz /tmp
RUN yum -y install gcc gcc-c++ make openssl-devel pcre-devel         && mkdir -p /data         && cd /tmp/$NV         && ./configure --prefix=/data/nginx         && make -j 2         && make install         && echo "${TIME_ZOME}" > /etc/timezone         && ln -sf /usr/share/zoneinfo/${TIME_ZOME} /etc/localtime         && rm -rf /tmp/nginx*         && yum clean all         && yum -y remove gcc gcc-c++ make

WORKDIR /data/nginx/
EXPOSE 80
CMD ["./sbin/nginx","-g","daemon off;"]

(3)准备好nginx配置文件
nginx配置文件的内容我就不贴上来了,请自行准备好修改后的nginx配置文件。
(4)构建nginx镜像

cd dockerfile/nginx/
docker build -t nginx:1.14.0 .

此时会先下载centos镜像,然后再构建新镜像,时间会比较久,可喝杯茶耐心等待

(5)创建并运行nginx容器
运行nginx镜像的脚本内容如下:

#!/bin/bash
docker run --name nginx --restart=always -p 8080:80     -v /data/docker/nginx/html:/data/nginx/html     -v /data/docker/nginx/logs:/data/nginx/logs     -d nginx:1.14.0

查看nginx容器是否成功启动

(6)测试
创建一个index.html文件来做测试

echo "Welcome to nginx" > /data/docker/nginx/html/index.html

通过浏览器访问http://192.168.2.227:8080/

原文地址:http://blog.51cto.com/andyxu/2296339

时间: 2024-08-30 14:47:31

使用Dockerfile定制自己的docker镜像的相关文章

docker镜像、容器

第一部分:Docker镜像的基本知识 1.1 什么是Docker镜像 从整体的角度来讲,一个完整的Docker镜像可以支撑一个Docker容器的运行,在 Docker容器运行过程中主要提供文件系统视角.例如一个ubuntu:14.04的镜像,提供了一个基本的ubuntu:14.04的发行版,当然此 镜像是不包含操作系统Linux内核的. 说到此,可能就需要注意一下,linux内核和ubuntu:14.04Docker镜像的区别了.传统虚拟机安装ubuntu:14.04会包含两部分,第一,某一个L

深入分析Docker镜像原理(下)

第二部分 Dockerfile.Docker镜像和Docker容器的关系 Dockerfile 是软件的原材料,Docker 镜像是软件的交付品,而 Docker 容器则可以认为是软件的运行态.从应用软件的角度来看,Dockerfile.Docker 镜像与 Docker 容器分别代表软件的三个不同阶段,Dockerfile 面向开发,Docker 镜像成为交付标准,Docker 容器则涉及部署与运维,三者缺一不可,合力充当 Docker 体系的基石. 简单来讲,Dockerfile构建出Doc

深入分析 Docker 镜像原理

摘要:近日, DaoCloud 软件工程师孙宏亮在 CSDN Container 微信群为大家带来了 Docker 镜像原理的深度分享,本次分享的重点是 Docker 镜像,分享的内容主要包含两个部分:1)Docker 镜像的基本知识:2)Dockerfile,Docker 镜像与 Docker 容器的关系. 嘉宾介绍:孙宏亮,硕士,浙江大学毕业,现为 DaoCloud 软件工程师,出版有<Docker 源码分析>,目前主要负责企业级容器云平台的研发工作.数年来一直从事云计算.PaaS 领域的

ARM64架构下,OpenJDK的官方Docker镜像为何没有8版本

为什么需要ARM64架构的OpenJDK8的Docker镜像 对现有的Java应用,之前一直运行在x86处理器环境下,编译和运行都是JDK8,如今在树莓派的Docker环境运行(也可能是其他ARM环境,如华为的泰山ARM服务器),需要JDK8镜像作为基础镜像. OpenJDK的官方Dockerfile 去OpenJDK的docker镜像官网查找找,地址是:https://hub.docker.com/r/arm64v8/openjdk ,如下图,只有JDK11的镜像: 为啥没有OpenJDK8的

实战docker,编写Dockerfile定制tomcat8镜像,实现web应用在线部署

最初在tomcat上部署web应用的方式,是通过maven的maven-compiler-plugin插件先打成war包,再将war包复制到tomcat的webapps目录下,后来用上了tomcat7-maven-plugin插件,可以直接在maven上编译,打包,部署一次性完成,这个方法的关键是在tomcat上创建一个用户账号,然后maven插件用此账号和密码来执行在线部署. 本次实践中,我们要动手制作一个镜像,这个镜像run起来后是个tomcat server,这个server支持maven

实战docker,编写Dockerfile定制tomcat镜像,实现web应用在线部署

最初在tomcat上部署web应用的方式,是通过maven的maven-compiler-plugin插件先打成war包,再将war包复制到tomcat的webapps目录下,后来用上了tomcat7-maven-plugin插件,可以直接在maven上编译,打包,部署一次性完成,这个方法的关键是在tomcat上创建一个用户账号,然后maven插件用此账号和密码来执行在线部署. 本次实践中,我们要动手制作一个镜像,这个镜像run起来后是个tomcat server,这个server支持maven

转:如何制作一个定制的 PHP 基础 Docker 镜像(一)

原文来自于:http://open.daocloud.io/ru-he-zhi-zuo-yi-ge-ding-zhi-de-php-ji-chu-docker-jing-xiang/ 目标:准备一个定制的 PHP 基础镜像.基础镜像,通常为含最小功能的系统镜像,之后的应用镜像都以此为基础. 本项目代码维护在 DaoCloud/php-apache-image 项目中. 制作基础镜像 选择 Ubuntu 官方的 14.04 版本为我们依赖的系统镜像. FROM ubuntu:trusty 因所有官

Docker镜像与仓库(二)Dockerfile

Docker镜像文件与仓库(二) Docker镜像文件与仓库(二) Dockerfile指令 Dockerfile格式: 1.#Comment注释2.INSTRUCTION大写的指令名 argument参数 FROM 1.FROM <image>2.FROM <image>:<tag>3.#通过FROM指定的镜像名都必须是已经存在的镜像,这个镜像叫做基础镜像,而且必须是第一条非注释指令 MAINTAINER 1.MAINTAINER <name>2.#指定镜

Dockerfile创建自定义Docker镜像以及CMD与ENTRYPOINT指令的比较

1.概述 创建Docker镜像的方式有三种 docker commit命令:由容器生成镜像: Dockerfile文件+docker build命令: 从本地文件系统导入:OpenVZ的模板. 关于这三种方式的大致说明请参考yeasy/docker_practice的创建镜像. 最近学习了Dockerfile文件的相关配置,这里做一下简单的总结,并对之前一直感到有些迷惑的CMD和ENTRYPOINT指令做个差异对比. 2.Dockerfile文件总结 Dockerfile 由一行行命令语句组成,