1.......................................................
搜索镜像 mysql 为例
Docker search mysql
拉取镜像
Docker pull mysql:5.7
查看镜像
docker images
启动镜像
docker run -p 12345:3306 \
--name mysql-01 \
-v /tmp/mysql/conf:/etc/mysql/conf.d \
-v /tmp/mysql/logs:/logs \
-v /tmp/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7
进入容器
docker exec -it 4787a6d530ad /bin/bash
2.......................................................
在docker镜像的帮助下,你只能部署一个容器。然而,如果你需要从同一个镜像部署多个容器(每个容器用于不同的任务),那么会发生什么?你可以借助dockerfile来解决这个问题。
Dockerfile是一个简单的文本文件,它包含了用户可以在命令行上调用的所有命令来组装或构建一个镜像。在docker build的帮助下,你可以很容易地自动构建,连续运行dockerfile中定义的多个命令。
例如,你想从docker hub下载docker镜像,以满足你的特定开发需求。然后,你想更新镜像,为你的开发过程安装一些包。在这种情况下,你可以创建一个dockerfile,包含所有的参数,以满足你的特定需求,并构建你的自定义镜像。在创建dockerfile后,你可以反复使用它来构建相同的镜像,而不需要手动安装你的开发过程所需的所有包。
Dockerfile基本功能
在使用dockerfile之前,如果知道如何制作dockerfile是很重要的。Dockerfile包括特定的关键字,可以用来构建特定的镜像。下面列出了dockerfile中使用的所有关键字的简要解释。
FROM:用于定义基础镜像,我们将在此基础上构建镜像。
ADD:它用于向正在构建的容器中添加文件。简单来说,RUN用于运行命令并提交结果。
RUN:它用于通过安装组件向基础镜像添加镜像。
CMD:用于在容器开始时运行命令。这些命令只有在运行容器时没有指定参数时才会运行。
ENTRYPOINT:它用于在容器初始化过程中运行命令。如果要在构建镜像后自动启动容器,必须在 dockefile 中使用 ENTRYPOINT。
ENV:它用于在容器运行时定义环境变量。
EXPOSE:它用于指定监听端口,以便在运行时启用网络。
MAINTAINER:它用于指定镜像创建者的名称和电子邮件 ID。
USER:它用于指定用于运行容器的用户名。
VOLUME:它用于允许从容器访问 Docker 主机上的目录。
WORKDIR:它用于指定运行时要执行的命令的路径。
LABEL:用来给docker镜像添加标签。
创建Dockerfile
在本节中,我们将创建一个dockerfile,以从Ubuntu基本镜像构建LAMP服务器镜像。
首先,您将需要创建一个目录来存储dockerfile。您可以使用以下命令创建它:
mkdir LAMP
接下来,在目录内创建一个名为Dockerfile的目录:
nano LAMP/Dockerfile
添加以下行:
FROM ubuntu:latest
MAINTAINER Hitesh Jethva
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y && apt-get install apache2 mariadb-server
libapache2-mod-php php php-cli php-common php-mysql php-json php-curl -y
CMD ["apachectl","-D","FOREGROUND"]
EXPOSE 8080
完成后,保存并关闭文件。
使用Dockerfile构建镜像
创建Dockerfile之后,您可以在Dockerfile的帮助下轻松创建自定义LAMP镜像。
首先,将目录更改为LAMP并运行以下命令以从该文件构建镜像:
cd LAMP
docker build -t "lamp:Dockerfile".
DEBIAN_FRONTEND这个环境变量,告知操作系统应该从哪儿获得用户输入。如果设置为”noninteractive”,你就可以直接运行命令,而无需向用户请求输入(所有操作都是非交互式的)。这在运行apt-get命令的时候格外有用,因为它会不停的提示用户进行到了哪步并且需要不断确认。非交互模式会选择默认的选项并以最快的速度完成构建。请确保只在Dockerfile中调用的RUN命令中设置了该选项,而不是使用ENV命令进行全局的设置。因为ENV命令在整个容器运行过程中都会生效,所以当你通过BASH和容器进行交互时,如果进行了全局设置那就会出问题。
正确的做法 – 只为这个命令设置ENV变量
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python3
错误地做法 – 为接下来的任何命令都设置ENV变量,包括正在运行地容器
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get install -y python3
我的示例如下:
FROM ubuntu:trusty
MAINTAINER mryqu
RUN \
DEBIAN_FRONTEND=noninteractive apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get -y install wget curl && \
DEBIAN_FRONTEND=noninteractive apt-get -y autoremove && \
DEBIAN_FRONTEND=noninteractive apt-get clean
3.......................................................
一、关于 Dockerfile
我们知道 Docker 镜像的生成方式有两种,一种是基于现有容器进行 commit 生成一个镜像,另外一种就是通过 Docker 提供的 Dockerfile 构建一个镜像。
Dockerfile 其实就是一个包含了 build image 过程中需要执行的所有命令的文本文件,这些指令就是 Dockerfile 构建时规定的一些指令,不过区区不到二十个指令。另外,也可以理解为 Dockfile 是一种被 Docker 程序解释的脚本,由一条一条的指令组成,每条指令对应 Linux 系统下面的一条命令,由 Docker 程序将这些 Dockerfile 指令翻译成真正的 Linux 命令。
我们通过一定的格式和指令编写 Dockerfile 文件,然后通过 docker build 命令来读取 Dockerfile 文件中的内容从而构建一个完整的镜像。相比 image 这种黑盒子,Dockerfile 这种显而易见的脚本更容易被使用者接受,它明确的表明这个 image 是怎么产生的。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。
二、编写 Dockerfile
其中 Dockerfile 的格式其实很简单,就两类,一类是注释信息,以 # 号作为注释;另外一类就是一条条的指令了。其中指令是忽略大小写的,但建议使用大写,每一行只支持一条指令,每条指令可以携带多个参数。
而 Dockerfile 的指令根据作用可以分为两种:构建指令和设置指令。构建指令用于构建 image,其指定的操作不会在运行 image 的容器上执行;设置指令用于设置 image 的属性,其指定的操作将在运行 image 的容器中执行。
在通过 Dockerfile 构建镜像时是按照指令的顺序运行的,所以需要自己根据相互的依赖关系调整好指令的相应顺序。但需要特别注意的一点就是,在 Dockerfile 中的第一个指令必须是 FROM 指令,除注释信息。FROM 指令是用来引入一个基础镜像,我们后续的所有指令操作都是基于这个基础镜像而进行的,如果没有,那么 Dockerfile 是做不了的。
FROM(指定基础 image)
属于构建指令,用来加载一个基础镜像,比如 CentOS。此指令必须指定且需要是 Dockerfile 中的第一个指令,除注释信息外;后续的指令都依赖于该指令引入的基础镜像而工作。
实践中,FROM 指令指定的基础镜像可以是官方远程仓库中的,也可以位于本地仓库。默认情况下,docker build 会在 docker 主机上查找指定的镜像文件,在其不存在时,则会从 Docker Hub Registry 上拉取所需的镜像文件。如果找不到指定的镜像文件,docker build 会返回一个错误信息。
该指令有两种格式:
1,FROM指定基础 image 的名称。
FROM ubuntu
2,FROM指定基础 image 的名称和标签,tag 为可选选项,省略时默认为 latest。
FROM ubuntu:latest
MAINTAINER(用来指定镜像创建者信息)
属于构建指令,用于将 image 的制作者相关的信息写入到 image 中。当我们对该 image 执行 docker inspect 命令时,输出中有相应的字段记录该信息。
Dockerfile 并不限制 MAINTAINER 指令可在出现的位置,但推荐将其放置于 FROM 指令之后。格式:
MAINTAINER mryqu
此指令在新版本 Docker 中已经被标识为即将被废弃,由 LABEL 指令替代,LABEL 指令相比较 MAINTAINER 更加丰富。
LABEL(镜像标签信息)
属于构建指令,LABEL 相比于 MAINTAINER 有了更宽泛的适用领域。用来指令一个镜像的各种元数据,当然其中就包括 MAINTAINER 信息。
LABEL==
可以看出 LABEL 支持的都是键值对,比如 author=’dkey’。
ENV(用于设置环境变量)
属于构建指令,用于为镜像定义所需的环境变量,并可被 Dockerfile 文件中位于其后的其它指令(如 ENV、ADD、COPY 等)所调用。语法格式如下:
ENV
或
ENV=
第一种格式中, 之后的所有内容均会被视作其 的组成部分,包括空格,因此,一次只能设置一个变量。
第二种格式中,可用一次设置多个变量,每个变量为一个“=”的键值对,如果 中包含空格,可以以反斜线 () 进行转义,也可通过对 加引号进行标识;另外,反斜线也可用于续行。
在定义多个变量时,建议使用第二种方式,以便构建镜像时在同一层中完成所有功能。
需要注意一点,ENV 定义的变量是在 docker build 时替换的,但同时也会注入到以此镜像启动的容器中,这是两个不同的阶段。容器启动后,可以通过 docker inspect 查看这个环境变量。如果想修改容器中的此环节变量,可以通过在启动容器时修改,比如使用 docker run –env key=value 命令修改环境变量,如果变量名称相同,那么就会替换掉容器中对应的环境变量,这个很好理解。
假如你安装了 JAVA 程序,需要设置 JAVA_HOME,那么可以在 Dockerfile 中这样写:
ENV JAVA_HOME=/path/to/java/dirent
那么在 Dockerfile 中引用这个变量时,其实跟 shell 中使用方式一样了,比如引用方式是 ${variable_name},其中 {} 可省略,同样可以使用默认值,比如 ${variable_name: – WORD} 就是变量没有值时就显示 WORD 值,${variable_name: + WORD} 表示变量有值时就显示 WORD 值,而 ${variable_name: = WORD} 表示变量没有值时就把 WORD 赋值给变量。
同样,这个变量会被注入到以这个镜像启动的容器中,就是这个容器启动后就会有 JAVA_HOME 变量及对应的值。
COPY(从 src 复制文件到容器的 dest 路径)
用于从 Docker 主机复制文件至创建的新镜像。
COPY
:要复制的源文件或目录,支持使用通配符。
:目标路径,即正在创建的 image 的文件系统路径;建议为 使用绝对路径,否则 COPY 指定则以 WORKDIR 为其其实路径。
注意:在路径中有空白字符时,通常使用第二种格式。另外, 必须是 build 上下文中的路径,不能是其父目录中的文件。如果 是目录,则其内部文件或子目录会被递归复制,但 目录自身不会被复制。如果指定了多个 ,或在 中使用了通配符,则 必须是一个目录,且必须以 / 结尾,否则被视为一个普通文件, 内容直接被写入到 中。如果 事先不存在,它将会被自动创建,这包括其父目录路径。
ADD(从 src 复制文件到容器的 dest 路径)
属于构建指令,ADD 指令类似于 COPY 指令,包括指令格式,但 ADD 支持使用 TAR 文件和 URL 路径。如果 是一个可识别的压缩格式,则 docker 会帮忙解压缩,它将会被展开为一个目录,其行为类似于 “tar -x” 命令。然而通过 URL 获取到的 tar 文件将不会自动展开。
RUN(镜像构建时的执行命令)
属于构建指令,RUN 是用来运行命令的,但是属于构建镜像阶段,RUN 可以运行任何被基础镜像支持的命令。比如基础镜像选择了 centos,那么软件管理部分只能使用 centos 的命令,就可以使用 RUN 来运行 yum 命令安装一些软件包。
该指令有两种格式:
RUN (the command is run in a shell - `/bin/sh -c`)
RUN ["", "", "" ... ] (exec form)
第一种格式中, 通常是一个 shell 命令,系统会自动以“/bin/sh -c”来运行此 ,这意味着 属于 shell 的子进程,那么此进程在容器中的 PID 不为 1,不能接收 Unix 信号,因此,当使用 docker stop 命令停止容器时,此进程接收不到 SIGTERM 信号。
Tips:bash -c STRING 的含义,如果存在 -c 选项,则表示从字符串中读取命令。如果字符串有多个空格,第一个空格前面的字符串是要执行的命令,也就是 $0,后面的是参数,即$1,$2….。比如执行命令 bash -c “./test hello world”,那么 ./test 就是 $0,hello 就是 $1,world 就是 $2,以此类推。
第二种语法格式中的参数是一个 JSON 格式的数组(数组中的元素要使用双引号,不能使用单引号,不然会报错),其中 为要运行的命令,后面的 为传递给命令的选项或参数;然而,此种格式指定的命令系统不会以“/bin/sh -c”来发起,由内核创建,因此常见的 shell 操作,如变量替换以及通配符(?,*)替换将不会进行;不过,如果要运行的命令依赖于此 shell 特性的话,可以将其替换为类似下面的格式。
RUN ["/bin/bash", "-c", "executable", "param1", "param2" ... ]
比如我需要构建一个 web 镜像,运行一个 httpd 服务并设置一个默认页面,如下:
FROM Busybox
ENV WEB_DOC_ROOT=“/opt/web/"
RUN mkdir -p ${DIR} && \
echo "
CMD(容器启动时的执行命令)
属于设置指令,CMD 跟 RUN 类似,也是用来运行命令的。但两者运行的时间点不同,RUN 用于构建镜像时运行命令,而 CMD 用于容器启动时运行的命令,可以是一个服务启动命令,也可以是一个系统命令或脚本程序,且其运行结束后,容器也将终止。但该指令只能在文件中存在一次,如果有多个,则只执行最后一条。这主要是因为 Docker 容器的启动只能运行一个进程,并且这个进程 ID 号为 1 在容器中。至于为什么,这就涉及到了 namespaces 了,也就是容器的原理部分了。
我们知道在 Linux 中,如果我们在当前 shell 下启动了一个前台进程,那么这个进程就属于这个 shell 的子进程了。当我们退出或 kill 了这个 shell 进程时,此 shell 进程会一并关闭掉它的相关子进程。注意,就算我们使用 & 把这个前台进程放置到后台运行,它还是属于当前 shell 进程的。所以,大多数时候我们可能都是会使用一个叫 nohup 的命令配合 & 符号使用,nohub 其实主要就是把我们要运行的进行剥离当前 shell,也就是不归属于 shell 的子进程了,这个时候我们退出 shell 时,那么自然就不会关闭我们使用 nohub 运行的进程了。
比如说我们使用 RUN 命令运行一个 CentOS 系统的容器,并且启动时执行一个 bash 程序,如下:
$ docker run -ti centos /bin/bash
正常情况下,我们就会进入到这个容器的 shell 交互式模式,此时这个 shell 进程就是一个 ID 号为 1 的进程。如果说我们在当前 shell 下启动一个 nginx 服务,那么这个 nginx 服务就属于这个 shell 的子进程。当我们使用 exit 命令退出这个 shell 时,可以想到此时整个容器都会退出,父进程退出子进程也会退出,这个容器都没有进程存在了,自然也就退出了。
该指令有三种格式:
CMD command
CMD ["executable","param1","param2"] (like an exec, this is the preferred form)
CMD ["" ""] (as a shell)
前两种语法格式的意义同 RUN 指令。
然后,我们接着 RUN 命令时用到的 Dockerfile 信息,运行起来这个容器,就需要使用 CMD 指令来启动 httpd 服务,如下:
FROM Busybox
ENV WEB_DOC_ROOT="/opt/web"
RUN mkdir -p ${DIR} && \
echo "
EXPOSE(暴露容器端口)
EXPOSE 可以用来暴露端口,或者在 docker run 时指定--expose=1234,这两种方式作用相同。但是,--expose可以接受端口范围作为参数,比如--expose=2000-3000。EXPOSE 和--expose都不依赖于宿主机器。默认状态下,这些规则并不会使这些端口可以通过宿主机来访问。
基于 EXPOSE 指令的上述限制,Dockerfile 的作者一般在包含 EXPOSE 规则时都只将其作为哪个端口提供哪个服务的提示。使用时,还要依赖于容器的操作人员进一步指定网络规则,需要配合docker run -p PORT:EXPORT使用,这样 EXPOSE 设置的端口号会被指定需要映射到宿主机器的端口,这时要确保宿主机器上的端口号没有被使用。如果直接指定 docker run -p EXPORT,这样 EXPOSE 设置的端口号会被随机映射成宿主机器中的一个端口号。不过通过 EXPOSE 命令文档化端口的方式十分有用。
本质上说,EXPOSE 或者--expose只是为其他命令提供所需信息的元数据(比如容器间 link 操作就依赖 EXPOSE 元数据),或者只是告诉容器操作人员有哪些已知选择。
语法格式:
EXPOSE [...]
EXPOSE 指令可以一次设置多个端口号,相应的运行容器的时候,可以配套的多次使用 -p 选项。
暴露一个端口;
EXPOSE port1
如果想代理EXPOSE端口, 相应的运行容器使用的命令;
docker run -p port1 image
暴露多个端口;
EXPOSE port1 port2 port3
如果想代理EXPOSE端口, 相应的运行容器使用的命令;
docker run -p port1 -p port2 -p port3 image
还可以指定需要映射到宿主机器上的某个端口号;
docker run -p host_port1:port1 -p host_port2:port2 -p host_port3:port3 image
注意,EXPOSE 仅仅是暴露一个端口,一个标识,在没有定义任何端口映射时,外部是无法访问到容器提供的服务。而端口映射(-p)是 docker 比较重要的一个功能,原因在于我们每次运行容器的时候容器的 IP 地址不能指定,而是在桥接网卡的地址范围内随机生成的。宿主机器的 IP 地址是固定的,我们可以将容器的端口的映射到宿主机器上的一个端口,免去每次访问容器中的某个服务时都要查看容器的IP的地址。对于一个运行的容器,可以使用 docker port 加上容器 ID 和 EXPOSE 暴露的端口来查看该端口号在宿主机器上的映射端口。
$ docker port redis 6379
0.0.0.0:6380
VOLUME(挂载卷)
属于设置指令,使容器中的一个目录具有持久化存储数据的功能,该目录可以被容器本身使用,也可以共享给其他容器使用。我们知道容器使用的是 AUFS 联合文件系统,这种文件系统不能持久化数据,当容器删除后,所有读写层的数据都会丢失。当容器中的应用有持久化数据的需求时可以在 Dockerfile 中使用该指令。格式:
######VOLUME [""]
比如我们在 Dockerfile 中添加 VOLUME /data,然后通过该 Dockerfile 生成 image 的容器,/data 目录中的数据在容器关闭后,里面的数据还存在。当我们使用 docker inspect 命令查看已经启动的容器信息时,可以看到 Mounts 信息,如下:
"Mounts": [
{
"Type": "volume",
"Name": "c3e3bda4e64cc51af272bb7bb4a75f45b07216b6b34e87f24261813caf4e0347",
"Source": "/var/lib/docker/volumes/c3e3bda4e64cc51af272bb7bb4a75f45b07216b6b34e87f24261813caf4e0347/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
可以看到容器的 /data 目录挂载在了 docker host 的具体路径,如果使用 docker rm -f 命令删除容器,此数据目录也还是存在的。但如果使用 docker rm -fv 命令删除容器,那么数据目录会被一并删除,这是需要注意的。
另外,另一个容器也有持久化数据的需求,且想使用上面容器共享的 /data 目录,那么可以运行下面的命令启动一个容器:
$ docker run -ti -volumes-from container_name image_name /bin/bash
其中 container_name 为 /data 目录所在的容器名称,image_name 为第二个容器运行 image 的名字。
WORKDIR(切换目录)
属于设置指令,可以多次切换(相当于 cd 命令),对 RUN、CMD、ENTRYPOINT 生效。格式:
WORKDIR /path/to/workdir
在/p1/p2下执行vim a.txt;
WORKDIR /p1 WORKDIR p2 RUN vim a.txt
USER(设置容器的用户)
属于设置指令,用于指定运行镜像时的或运行 Dockerfile 中任何 RUN、CMD或ENTRYPOINT 指令指定的程序时的用户名或 UID。默认容器的运行身份是 root 用户。语法格式如下:
USER |
需要注意的是,可以为任意数字,但实践中其必须为 /etc/passwd 中某用户的有效 UID,否则,docker run 命令将运行失败。
HEALTHCHECK(健康检查)
我们知道容器就是被隔离的进程,当一个容器中的进程退出,那么意味着容器也就退出了。最常见的场景就是一个运行一个容器的时候,如果进程起不来,那么容器自然也起不来。Docker 引擎判断一个容器是否能正常工作,是根据进程的状态,而不是根据进程运行的服务是否正常来判断的。比如我们运行一个 nginx,进程都没正常,但是 nginx 里面的静态资源没有,自然对用户来说访问就是有问题的,但 Docker 引擎并知道。
那么 Docker 就提供了 HEALTHCHECK 指令,让我们可以定义服务层面的健康状况检查。一个简单的示例如下:
HEALTHCHECK --interval=5m --timeout=3s --start-period=1m --retries=3 CMD curl -f http://localhost || exit 1
其中--interval用来指定每次检查的间隔时间,默认 30s; --timeout是指检测一次的等待超时时间,默认 30s;--start-period表示容器启动后多久开始检测,有的服务启动时间较长,默认 0s;--retries表示检测重试的次数,避免误杀,默认 3 次。
当检测命令发出后,我们可以定义返回检测码,0 表示健康,1 表示不健康,2 暂时是预留的。
STOPSIGNAL(定义停止容器的信号)
一个容器就是运行了一个 PID 为 1 的进程,也只有 PID 为 1 的进程才可以接收信号。当我们执行 docker stop 命令时,其实就是给进程发送了一个 -15 的信号,进程接收到了之后就退出了,进程退出容器自然也就退出了,从而实现关闭容器的功能。
如果我们想使用别的信号来杀死进程也可以的,比如 -9 信号,强制杀死进程。就可以使用 STOPSIGNAL 来指定,大多数情况下也不需要改。
ARG(构建镜像传参)
ENV 指令可以用来定义变量,其定义的变量可以在构建镜像和容器运行时使用。但是细心就会发现,ENV 定义的变量在 Dockerfile 中都是固定的值,如果我们想在构建镜像时传参,根据同一个 Dockerfile 可以构建不同的镜像,那么就需要 ARG 指令了。ARG 指令就是用来定义镜像构建时传参,然后 docker build --build-arg key="value" 指定要传给镜像构建时的变量即可。
但需要注意,ARG 指令也没有办法放在 FROM 指令的前面,也就是说如果使用传参的方式来构建不同版本的 nginx,是不支持的。因为 ARG 没办法放在 FROM 指令前面,但 ARG 放在 FROM 后面时,FROM 指令又无法找到它要引用的变量,这就成了一个死循环了。
ONBUILD(子镜像中执行操作)
用于在 Dockerfile 中定义一个触发器,Dockerfile 用于 build 映象文件,此映象文件亦可作为 base image 被另一个 Dockerfile 用作 FROM 指令的参数,并以之构建新的映象文件。
在后面的这个 Dockerfile 中的 FROM 指令在 build 过程中被执行时,将会“触发”创建其 base image 的 Dockerfile 文件中的 ONBUILD 指令定义的触发器。简单说就是,你定义了 Dockerfile,并使用了 ONBUILD 指令,在你进行镜像构建时,这个 ONBUILD 指令并不会执行;然后,其他人以你这个镜像作为他编写 Dockerfile 时的 base image,也就是 FROM 引用,他在进行构建时会执行你的 ONBUILD 指令定义的动作。
ONBUILD
尽管任何指令都可注册成为触发器指令,但 ONBUILD 不能自我嵌套,且不会触发 FROM 和 MAINTAINER 指令。
在 ONBUILD 指令中使用 ADD 或 COPY 指令时应该格外小心,因为新构建过程的上下文在缺少指定的源文件时会报错。但如果你使用 ADD 添加一个互联网的 URL 就没啥问题了。
构建Dockerfile文件
创建一个构建 nginx 镜像的 Dockerfile 文件,Git地址:https://github.com/dongwenpeng/nginx。
FROM nginx
LABEL MAINTAINER="dkey"
ENV RUN_USER nginx
ENV RUN_GROUP nginx
ENV DATA_DIR /data/web
ENV LOG_DIR /data/log/nginx
RUN mkdir /data/log/nginx -p
RUN chown nginx.nginx -R /data/log/nginx
ADD nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
ENTRYPOINT nginx -g "daemon off;"
此 Dockerfile 很简单,做了这么几件事:
拉取一个 nginx 镜像。
设置了几个变量。
创建了几个需要的目录。
把当前目录下的 web 程序复制到镜像的 /data/web 目录。
把 nginx.conf 配置文件和 default.conf 配置文件复制到镜像中。
设置一个默认端口。
最后设置了容器启动时执行的命令,我用来启动nginx程序,注意这个命令不能错,不然容器启动不了。这样设置后,当你docker run运行此镜像时不需要在后面再次执行需要执行的命令了。
如果我能提前把这个 nginx 容器可能需要更改的配置都进行变量化,那么对于用户来说是不是就可以不用关心这个容器的构建过程,下载容器之后想改什么配置都可以通过传递变量的方式来进行,是不是就方便很多。我们可以简单模拟一下这个场景,当然,不会把所有参数都进行变量化,只是作为演示。
FROM nginx
LABEL MAINTAINER="dkey"
ENV RUN_USER nginx
ENV RUN_GROUP nginx
ENV NGX_DOC_ROOT="/data/web/html/"
ADD entrypoint.sh /bin
EXPOSE 80
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
ENTRYPOINT ["/bin/entrypoint.sh"]
entrypoint.sh 脚本的内容:
#!/bin/sh
cat > /etc/nginx/conf.d/www.conf << EOF
server {
server_name ${HOSTNAME};
listen ${IP:-0.0.0.0}:${PORT:-80};
root ${NGX_DOC_ROOT:-/usr/share/nginx/html};
}
EOF
exec "$@"
最后这个 exec 特别关键了,其中 $@ 是用来取所有参数的,也就是 CMD 指令指定的容器运行程序。使用 exec 来执行命令,表示替换执行 entrypoint.sh 的进程,从而把 nginx 进程变成 1 号进程,只有成为 1 号进程才能接受信号,比如 stop、kill 等指令。
构建nginx镜像
Dockerfile 编写好了之后就可以通过 docker build 来进行构建了,需要注意 docker build 命令的执行需要在 dockerfile 文件所在目录,会自动找到以 dockerfile 命令的文件,然后就会开始进行镜像构建。可以使用 -t 参数指定镜像的名称或名称加 tag。
$ docker build -t nginx_01 .
Sending build context to Docker daemon 3.43 MB
Step 1 : FROM nginx
latest: Pulling from library/nginx
64f0219ba3ea: Pull complete
325b624bee1c: Pull complete
Digest: sha256:2a07a07e5bbf62e7b583cbb5257357c7e0ba1a8e9650e8fa76d999a60968530f
Status: Downloaded newer image for nginx:latest
---> 19146d5729dc
Step 2 : MAINTAINER dkey
---> Running in a9d12e4fa972
---> 3e6bcc394986
Removing intermediate container a9d12e4fa972
Step 3 : ENV RUN_USER nginx
---> Running in 55e165c28124
---> 024ba866269e
Removing intermediate container 55e165c28124
Step 4 : ENV RUN_GROUP nginx
---> Running in 577cb4556709
---> 95f5b6de5080
Removing intermediate container 577cb4556709
Step 5 : ENV DATA_DIR /data/web
---> Running in e472aaa991f2
---> d50decbaf7bc
Removing intermediate container e472aaa991f2
Step 6 : ENV LOG_DIR /data/log/nginx
---> Running in 5ccf9955685d
---> 00f1deefcc1b
Removing intermediate container 5ccf9955685d
Step 7 : RUN mkdir /data/log/nginx -p
---> Running in 63a61c463ad4
---> 467d1548998c
Removing intermediate container 63a61c463ad4
Step 8 : RUN chown nginx.nginx -R /data/log/nginx
---> Running in 00d559b2de7b
---> a550d1639d98
Removing intermediate container 00d559b2de7b
Step 9 : ADD web /data/web
---> e75181ff11e0
Removing intermediate container b4c03ff5df61
Step 10 : ADD nginx.conf /etc/nginx/nginx.conf
---> 00dcb2ec895f
Removing intermediate container 4a75f67c4f1c
Step 11 : ADD default.conf /etc/nginx/conf.d/default.conf
---> 9b2668d59294
Removing intermediate container d57c3e297464
Step 12 : EXPOSE 80
---> Running in 076209139acf
---> fa3247254200
Removing intermediate container 076209139acf
Step 13 : ENTRYPOINT nginx -g "daemon off;"
---> Running in e0a4845172cb
---> fa37625af3ac
Removing intermediate container e0a4845172cb
Successfully built fa37625af3ac
从执行过程可以看出一共十三个步骤,都完成了。
下面可以看看镜像了。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx_01 latest fa37625af3ac 38 seconds ago 182.7 MB
nginx latest 19146d5729dc 6 days ago 181.6 MB
可以看到从官方拉取了一个 nginx 镜像,然后在此镜像的基础上构建了 nginx_01 镜像。注意,由于 nginx_01 是在 nginx 镜像的基础上构建出来的,所以如果你要删除 nginx 镜像是不允许的,只有先删除 nginx_01 镜像后才可以删除 nginx 镜像。
同理,你可以接着创建第二个镜像,这个时候你会发现构建速度非常之快。
$ docker build -t nginx_02 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx_02 latest fa37625af3ac 54 seconds ago 182.7 MB
nginx_01 latest fa37625af3ac 54 seconds ago 182.7 MB
nginx latest 19146d5729dc 6 days ago 181.6 MB
但你也应该发现,这两个镜像的 IMAGE ID 是相同的,因为 docker 检测出你的 dockerfile 文件没有任何内容改变(包括要复制文件的内容),所以只是做了一个链接。这个时候你可以运行这个镜像看看。
$ docker run --name nginx_01 -d -p 80:80 nginx_01
$ docker exec -ti nginx_01 bash
root@0dbeb4d3852f:/# ss -nplt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:80 :
root@0dbeb4d3852f:/# cat /data/log/nginx/access.log
172.28.24.96 - - [27/Dec/2016:08:37:58 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) Chrome/55.0.2883.87 Safari/537.36" "-"
从上面的结果可以看出,这个 nginx 容器运行一切正常。你可以在浏览器访问看看。
这个时候你可以选择删除一个镜像看看。
$ docker rmi nginx_02
Untagged: nginx_02:latest
成功删除,不管你删除哪个镜像都可以删除,但是只能删除一个。再次删除另一个时就会报错。
$ docker rmi nginx_01
Error response from daemon: conflict: unable to remove repository reference "nginx_01"
如果我们改变了网站代码或者改变了配置文件,再次构建镜像时会怎么样呢?
其实 dockerfile 中有任何的改动,再次构建镜像时都会重新构建,但是只会从改动的地方开始重新构建,速度很快,这就是镜像分层的好处。
$ docker build -t nginx_02 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx_02 latest 9868ca64a680 9 seconds ago 182.7 MB
使用commit命令提交新镜像
通过上面我们知道,通过 dockerfile 可以构建一个镜像。同样使用 commit 也可以提交一个新镜像。跟 build 不同的是,commit 只能从现有的容器之上提交出一个新的镜像。比如,你对 nginx 镜像做好了基本配置,然后就可以把这个镜像提交为一个新的镜像。
commit 语法:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
dockercommit[OPTIONS]CONTAINER[REPOSITORY[:TAG]]
提交一个新的镜像。
$ docker commit -a pengdongwen nginx_01 nginx_10
sha256:2cdae15e08fd9160fc24998a4f6cae270cd2130980fd7c48cc9da70b1b67671e
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx_10 latest 2cdae15e08fd 6 seconds ago 182.7 MB
原文链接:https://blog.csdn.net/weixin_29065015/article/details/112045524
dockerfile文件内容
FROM ubuntu:16.04
MAINTAINER [email protected]
ENV REFRESHED_AT 2018-12-18 16:57
ADD jdk8.tar.gz /usr/local/
ADD apache-tomcat-9.0.8.tar.gz /usr/local/
ADD entrypoint.sh /usr/local
RUN apt-get update && apt-get install -yqq net-tools telnet localepurge
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV JAVA_HOME /usr/local/jdk1.8.0_111
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.8
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.8
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
ENV ORACCONFIG_HOME /usr/local/docker/orac_home
ENV JAVA_OPTS="$JAVA_OPTS -server -Xms1024m -Xmx2048m -XX:PermSize=512M -XX:MaxNewSize=1024m -XX:MaxPermSize=1024m"
EXPOSE 8080
EXPOSE 1090
ENTRYPOINT ["sh","/usr/local/entrypoint.sh"]
编译并生成镜像 (. 表示当前路径)
docker build -t tomcat9.0 .
(docker rm fb93e677d775)删除容器
(docker rmi 镜像ID)删除指令
查看生成镜像
docker images
创建容器并运行
docker run -di -u root -p 8080:8080 --name=orac tomcat9.0
进入容器
docker exec -it 77ba19db7129 /bin/bash
entrypoint.sh内容
!/bin/bash
/usr/local/apache-tomcat-9.0.8/bin/startup.sh
tail -f /usr/local/apache-tomcat-9.0.8/logs/catalina.out