Docker 可以通过 Dockerfile 的内容来自动构建镜像。
文后包含三个使用示例
Dockerfile 是一个包含创建镜像所有命令的文本文件,通过docker build命令可以根据 Dockerfile 的内容构建镜像,在介绍如何构建之前先介绍下 Dockerfile 的基本语法结构。
Dockerfile 有以下指令选项:
FROM
MAINTAINER
RUN
CMD
EXPOSE
ENV
ADD
COPY
ENTRYPOINT
VOLUME
USER
WORKDIR
ONBUILD
1. FROM
FROM
FROM指定构建镜像的基础源镜像,如果本地没有指定的镜像,则会自动从 Docker 的公共库 pull 镜像下来。
FROM必须是 Dockerfile 中非注释行的第一个指令,即一个 Dockerfile 从FROM语句开始。
FROM可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像。
如果FROM语句没有指定镜像标签,则默认使用latest标签。
2. MAINTAINER
MAINTAINER
指定创建镜像的用户
RUN 有两种使用方式
RUN "executable", "param1", "param2"
每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像,后续的RUN都在之前RUN提交后的镜像为基础,镜像是分层的,可以通过一个镜像的任何一个历史提交点来创建,类似源码的版本控制。
exec 方式会被解析为一个 JSON 数组,所以必须使用双引号而不是单引号。exec 方式不会调用一个命令 shell,所以也就不会继承相应的变量,如:
RUN [ "echo", "$HOME" ]
这种方式是不会达到输出 HOME 变量的,正确的方式应该是这样的
RUN [ "sh", "-c", "echo", "$HOME" ]
RUN产生的缓存在下一次构建的时候是不会失效的,会被重用,可以使用--no-cache选项,即docker build --no-cache,如此便不会缓存。
3. CMD
CMD有三种使用方式:
CMD "executable","param1","param2"
CMD "param1","param2"
CMD command param1 param2 (shell form)
CMD指定在 Dockerfile 中只能使用一次,如果有多个,则只有最后一个会生效。
CMD的目的是为了在启动容器时提供一个默认的命令执行选项。如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令。
CMD会在启动容器的时候执行,build 时不执行,而RUN只是在构建镜像的时候执行,后续镜像构建完成之后,启动容器就与RUN无关了,这个初学者容易弄混这个概念,这里简单注解一下。
4. EXPOSE
EXPOSE [...]
告诉 Docker 服务端容器对外映射的本地端口,需要在 docker run 的时候使用-p或者-P选项生效。
5. ENV
ENV # 只能设置一个变量
ENV = ... # 允许一次设置多个变量
指定一个环节变量,会被后续RUN指令使用,并在容器运行时保留。
例子:
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
等同于
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
6. ADD
ADD ...
ADD复制本地主机文件、目录或者远程文件 URLS 从 并且添加到容器指定路径中 。
支持通过 Go 的正则模糊匹配,具体规则可参见 Go filepath.Match
ADD hom* /mydir/ # adds all files starting with "hom"
ADD hom?.txt /mydir/ # ? is replaced with any single character
路径必须是绝对路径,如果 不存在,会自动创建对应目录
路径必须是 Dockerfile 所在路径的相对路径
如果是一个目录,只会复制目录下的内容,而目录本身则不会被复制
7. COPY
COPY ...
COPY复制新文件或者目录从 并且添加到容器指定路径中 。用法同ADD,唯一的不同是不能指定远程文件 URLS。
8. ENTRYPOINT
ENTRYPOINT "executable", "param1", "param2"
ENTRYPOINT command param1 param2 (shell form)
配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖,而CMD是可以被覆盖的。如果需要覆盖,则可以使用docker run --entrypoint选项。
每个 Dockerfile 中只能有一个ENTRYPOINT,当指定多个时,只有最后一个生效。
通过ENTRYPOINT使用 exec form 方式设置稳定的默认命令和选项,而使用CMD添加默认之外经常被改动的选项。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
通过 Dockerfile 使用ENTRYPOINT展示前台运行 Apache 服务
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
9. VOLUME
VOLUME ["/data"]
创建一个可以从本地主机或其他容器挂载的挂载点
10. USER
USER daemon
指定运行容器时的用户名或 UID,后续的RUN、CMD、ENTRYPOINT也会使用指定用户。
11. WORKDIR
WORKDIR /path/to/workdir
为后续的RUN、CMD、ENTRYPOINT指令配置工作目录。可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
最终路径是/a/b/c。
WORKDIR指令可以在ENV设置变量之后调用环境变量:
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
最终路径则为 /path/$DIRNAME。
12. ONBUILD
ONBUILD [INSTRUCTION]
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,Dockerfile 使用如下的内容创建了镜像 image-A:
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
如果基于 image-A 创建新的镜像时,新的 Dockerfile 中使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。
# Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用ONBUILD指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild。
13. docker build
$ docker build –help
Usage: docker build [OPTIONS] PATH | URL | -
Build a new image from the source code at PATH
--force-rm=false Always remove intermediate containers, even after unsuccessful builds # 移除过渡容器,即使构建失败
--no-cache=false Do not use cache when building the image # 不实用 cache
-q, --quiet=false Suppress the verbose output generated by the containers
--rm=true Remove intermediate containers after a successful build # 构建成功后移除过渡层容器
-t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
14. dockerfile 最佳实践
使用.dockerignore文件
为了在docker build过程中更快上传和更加高效,应该使用一个.dockerignore文件用来排除构建镜像时不需要的文件或目录。例如,除非.Git在构建过程中需要用到,否则你应该将它添加到.dockerignore文件中,这样可以节省很多时间。避免安装不必要的软件包为了降低复杂性、依赖性、文件大小以及构建时间,应该避免安装额外的或不必要的包。例如,不需要在一个数据库镜像中安装一个文本编辑器。
最好新建一个目录来编写Dockerfile
[root@lockey6 dockfile]# cat Dockerfile
FROM rhel7:v1
COPY yum.repo /etc/yum.repos.d/
MAINTAINER [email protected]
ENV HOSTNAME server1
EXPOSE 80
RUN yum clean all && yum install -y httpd
CMD systemctl start httpd
ADD index.html /var/www/html
至于yum源文件和网站默认测试页自己按实际情况写
运行以下命令生成镜像:
[root@lockey6 dockfile]# docker build -t httpd:v1 .
但是运行容器的时候httpd服务并未启动
就算打开交互模式也启动不了,于是我发现自己入坑了
[root@server1 /]# systemctl start httpd
Failed to get D-Bus connection: Operation not permitted
上网一搜,果然屎坑,幸好有一个不算完美的解决方法:
[root@lockey6 dockfile]# docker run --privileged -ti -e "container=docker" -v /sys/fs/cgroup:/sys/fs/cgroup --name http -p 8888:80 httpserver:v1 /usr/sbin/init bash
这个时候访问宿主机的ip:8888,可以看到服务启动成功,并且端口映射也没问题
但是启动容器的时候出现这个情况,一直卡主了,既rm 不了也kill 不掉容器,没办法只好kill掉相关的进程(或者重启容器也行)
[root@foundation5 docker]# cat Dockerfile
FROM rhel7:v1
COPY yum.repo /etc/yum.repos.d/
MAINTAINER [email protected]
ENV HOSTNAME server1
EXPOSE 22 80
RUN yum install -y openssh-server openssh-clients httpd && ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -N "" -q && ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N "" -q && echo root:redhat | chpasswd
ADD index.html /var/www/html
CMD ["/usr/sbin/sshd","-D"]
运行以下命令生成镜像:
[root@lockey6 dockfile]# docker build -t httpssh:v1 .
开启容器进行测试
[root@lockey6 dockfile]# docker run --privileged -d -e "container=docker" -v /sys/fs/cgroup:/sys/fs/cgroup --name http -p 8888:80 -p 2222:22 httpssh:v1 /usr/sbin/init
63f2d6b7c17ab46246e25b179a4d18391d8e7c5e5eda9f60717137b367752008
[root@lockey6 dockfile]# ssh -p 2222 -l root localhost
root@localhost's password:
[root@63f2d6b7c17a ~]# systemctl start httpd
[root@63f2d6b7c17a ~]#
CMD 如果只有一个命令,那如果我们需要运行多个服务怎么办呢?最好的办法是分别在不同的容器中运行,通过 link 进行连接,比如先前实验中用到的 web,app,db 容器。如果一定要在一个容器中运行多个服务可以考虑用 Supervisord 来进行进程管理,方式就是将多个启动命令放入到一个启动脚本中。
[root@foundation5 docker]# cat Dockerfile
FROM rhel7:v1
MAINTAINER [email protected]
ENV HOSTNAME server1
EXPOSE 22 80
COPY yum.repo /etc/yum.repos.d/
RUN yum install -y openssh-server openssh-clients httpd supervisor && ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -N "" -q && ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N "" -q && echo root:redhat | chpasswd
ADD index.html /var/www/html
COPY supervisord.conf /etc/supervisord.conf
CMD ["/usr/bin/supervisord","-D"]
[root@foundation5 docker]# cat supervisord.conf
[supervisord]
nodaemon=true
[program:httpd]
command=/usr/sbin/httpd
[program:sshd]
command=/usr/sbin/sshd -D
Docker 官方已经把仓库封装为镜像,直接通过启动容器就可以部署完成仓库:
首先pull一个镜像下来
docker pull registry
vim /etc/systemd/system/docker.service
标记处的位置添加以下内容,注意对应修改ip:
--insecure-registry 192.168.1.102:5000
[root@lockey6 ~]# docker run -d --name registry -p 5000:5000 -v /opt/registry:/var/lib/registry registry:latest
[root@lockey6 ~]# cd /opt/registry/
发现镜像已经T推送到本地仓库!