Dockerfile 就是构建docker镜像的源码,Dockerfile 是纯文本文件。
基于Dockerfile制作docker镜像时,必须在特点的某个目录。Dockerfile 首字母必须大写。如果要打包文件,该文件必须放置在当前工作目录下。如果忽视某些文件可以定义 .dockerignore配置文件。 最后使用 docker build
来创建docker 镜像。
语法:
LABEL <key>=<value> <key>=<value> <key>=<value> <key>=<value>
# 一行命令查看容器内的信息
docker run --name tinyweb1 --rm tinyhttpd:v0.1-1 cat /data/web/html/index.html
ADD指令类似于COPY指令,ADD支持使用TAR文件和URL路径
Syntax
操作准则
注意:当需要开放多个端口时,建议在一行中定义,否则会多出来很多层级。
$variable_name
或 ${variable_name}
/bin/sh -c
来发起依赖于此shell特性的话,可以将其替换为类似下面的格式。
RUN ["/bin/sh","-c","<executable>","param1"]
RUN 和 CMD 区别:
RUN 是可以运行多次的,如果多个命令之间有关联关系,建议在一条RUN当中把多个命令写进来。
附加知识点:
用户启动并创建进程的接口就是 shell,这是用户能够与操作系统交互的接口,所以事实上打开命令行提示符,就相当于正在运行的一个shell进程,而后在命令行之下所创建的任何进程都应该属于shell的子进程,而且有些进程还可能直接占据当前shell 的终端设备,将进程送到后台去用 &
符号,加上 &
并不能剥离当前shell的关系,它的父进程依然是shell,如果退出了shell,这个进程依然会被关闭。要实现脱离shell,需要使用命令 nohup
这个命令就表示脱离shell,运行到后台。
在系统进程领域中,从来都是白发人送黑发人。当父进程退出时,一定会把它所属的子进程全部kill,而后在退出。如果父进程意外结束,那么它的子进程就成了孤儿进程,孤儿进程既不会释放内存,也不会终止了。
关键命令:exec
在创建容器时,可通过命令行中定义的命令来覆盖CMD的指令,但是有时候不允许覆盖,CMD 做不到,而 ENTRYPOINT 可以。
在Dockerfile中,如果使用的是 ENTRYPOINT 而不是CMD,定义的命令是不会被覆盖的。
[root@docker ~/img2]#cat Dockerfile
FROM busybox
LABEL maintainer="hukey<hukey@super.com>" app="httpd"
ENV WEB_DOC_ROOT="/data/web/html/"
RUN mkdir -p ${WEB_DOC_ROOT} && echo "<h1>busybox httpd server</h1>" > ${WEB_DOC_ROOT}/index.html
#CMD /bin/httpd -f -h ${WEB_DOC_ROOT}
#CMD ["/bin/sh","-c","/bin/httpd","-f","-h /data/web/html/"]
ENTRYPOINT /bin/httpd -f -h ${WEB_DOC_ROOT}
[root@docker ~/img2]#docker run --name web2 --rm -P tinyhttpd:v0.2-5 ls /data/web/html/
在Dockerfile 中可以定义多个 CMD ,只有最后一个生效。如果 ENTRYPOINT 定义了多个,只有最后一个生效。
如果 CMD 和 ENTRYPOINT 同时定义,CMD 的参数将当作参数传递给 ENTRYPOINT 。多数情况下,ENTRYPOINT是指定一个shell。
容器接收配置要靠环境变量。接下来通过一个示例来说明 CMD 和 ENTRYPOINT 连用。
假如运行一个 nginx,要为nginx 提供一个配置文件要如何实现,或让nginx灵活接受配置该如何实现?
[root@docker ~/img3]#cat Dockerfile
FROM nginx:1.14-alpine
LABEL maintainer="hukey<hukey@super.com>"
ENV NGX_DOC_ROOT=‘/data/web/html/‘
ADD index.html ${NGX_DOC_ROOT}
ADD entrypoint.sh /bin/
CMD ["/usr/sbin/nginx","-g","daemon off;"]
ENTRYPOINT ["/bin/entrypoint.sh"]
------
[root@docker ~/img3]#cat entrypoint.sh
#!/bin/sh
# Author:hukey
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 "$@"
当一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting
,在HEALTHCHECK
指令检查成功后变为 healthy
,如果连续一定次数失败,则会变为 unhealthy
。
HEALTHCHECK
支持下列选项:
--interval=<间隔>
:两次健康检查的间隔,默认为30秒--timeout=<时长>
:健康检查命令运行超时时间,如果超过这个时间,本次健康价差就被视为失败,默认30秒--retries=<次数>
:当连续失败指定次数后,则将容器状态视为 unhealthy
,默认3次。--start-period=<时长>
:定义容器启动多少秒之后再进行检测,默认 0 秒当使用 HEALTHCHECK
时,会返回三种状态:
0:success 状态检测成功
1:unhealthy 状态检测失败
2:reserved 预留,没意义
示例:
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1
和 CMD、ENTRYPOINT 一样,HEALTHCHECK 只能出现一次,如果写了多个,只有最后一个生效。
[root@docker ~/img3]#cat Dockerfile
FROM nginx:1.14-alpine
LABEL maintainer="hukey<hukey@super.com>"
ENV NGX_DOC_ROOT=‘/data/web/html/‘
ADD index.html ${NGX_DOC_ROOT}
ADD entrypoint.sh /bin/
EXPOSE 80/tcp
HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${PORT:-80}/ || exit 1
CMD ["/usr/sbin/nginx","-g","daemon off;"]
ENTRYPOINT ["/bin/entrypoint.sh"]
---
[root@docker ~/img3]#cat entrypoint.sh
#!/bin/sh
# Author:hukey
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 "$@"
...
ONBUILD ADD http://192.168.118.45/nginx-1.18.0.tar.gz /usr/local/src/
...
容器应该是短暂的
通过 Dockerfile 构建的镜像所启动的容器应该尽可能短暂(ephemeral)。短暂意味着可以很快地启动并且终止。
使用 .dockerignore 排除构建无关文件
.dockerignore 语法与 .gitignore 语法一致。使用它排除构建无关的文件及目录
使用多阶段构建
多阶段构建可以有效减小镜像体积,特别是对于需编译语言而言,一个应用的构建过程往往如下:
而在前两步会有大量的镜像体积冗余,使用多阶段构建可以避免这一问题。
避免安装不必要的包
减小体积,减少构建时间。
一个容器只做一件事
如一个 Web 应用将会包含三个部分,Web 服务,数据库与缓存。把他们解耦到多个容器中,方便横向扩展。如果你需要网络通信,则可以将他们至于一个网络下。
减少镜像层数
例如:
# 正确写法
RUN yum install wget net-tools node -y
# 错误写法
RUN yum install -y wget
RUN yum install -y net-tools
RUN yum install -y node
将多行参数排序
便于可读性以及不小心的重复装包
RUN yum install -y wget net-tools node
充分利用构建缓存
在镜像的构建过程中 Docker 会遍历 Dockerfile 文件中的所有指令,顺序执行。对于每一条指令,Docker 都会在缓存中查找是否已存在可重用的镜像,否则会创建一个新的镜像。
我们可以使用 docker build --no-cache 跳过缓存:
以下示例来自网络:
# Dockerfile
FROM centos:centos7
LABEL maintainer="hukey<hukey@super.com>"
RUN yum install -y openssh* net-tools iproute NetworkManager && # mkdir -p /var/run/sshd && echo "UseDNS no" >> /etc/ssh/sshd_config && sed -i ‘/pam_loginuid.so/d‘ /etc/pam.d/sshd && echo ‘123123‘ | passwd --stdin root && /usr/bin/ssh-keygen -A && yum clean all
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
# 制作镜像
docker build -t sshd:v0.1-1 ./
# 创建容器
docker run --name ssh_server -P --rm -d sshd:v0.1-1
(基于上面ssh镜像构建)
# Dockerfile
[root@docker ~/img6]#cat Dockerfile
FROM sshd:v0.1-2
LABEL maintainer="hukey<hukey@super.com>"
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); rm -f /lib/systemd/system/multi-user.target.wants/*; rm -f /etc/systemd/system/*.wants/*; rm -f /lib/systemd/system/local-fs.target.wants/*; rm -f /lib/systemd/system/sockets.target.wants/*udev*; rm -f /lib/systemd/system/sockets.target.wants/*initctl*; rm -f /lib/systemd/system/basic.target.wants/*; rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME ["/sys/fs/cgroup"]
CMD ["/usr/sbin/init"]
# 制作镜像
[root@docker ~/img6]#docker build -t systemd:v0.1-1 ./
# 创建容器
[root@docker ~/img6]#docker run --name sys-1 -d --privileged -v /sys/fs/cgroup/:/sys/fs/cgroup/:ro systemd:v0.1-1 /sbin/init
--privileged:privateged container 内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限。
# 进入容器
[root@docker ~/img6]#docker exec -it sys-1 /bin/bash
# 通过 systemctl 查看sshd状态
[root@ffe6f1ec35d1 /]# systemctl status sshd
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:sshd(8)
man:sshd_config(5)
[root@docker ~]#mkdir -p nginx_img
[root@docker ~]#cd nginx_img/
[root@docker ~/nginx_img]#cat Dockerfile
FROM centos:centos7
LABEL maintainer="hukey<hukey@super.com>"
ENV NGX_PACKAGE="nginx-1.18.0"
#ADD http://nginx.org/download/${NGX_PACKAGE}.tar.gz /usr/local/src/
ADD http://192.168.118.45/${NGX_PACKAGE}.tar.gz /usr/local/src/
WORKDIR /usr/local/src/${NGX_PACKAGE}
RUN tar xf /usr/local/src/${NGX_PACKAGE}.tar.gz && cd ${NGX_PACKAGE} && yum install -y proc-devel gcc gcc-c++ zlib zlib-devel make openssl-devel wget && ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_realip_module && make -j 2 && make install && echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf && yum clean all && rm -rf /var/cache/yum/*
EXPOSE 80/tcp 443/tcp
ADD run.sh /run.sh
CMD ["/run.sh"]
---
[root@docker ~/nginx_img]#cat run.sh
#!/bin/bash
# Author:hukey
/usr/local/nginx/sbin/nginx
# 制作镜像
[root@docker ~/nginx_img]#docker build -t nginx:v0.1-1 ./
# 启动容器
[root@docker ~/nginx_img]#docker run --name web1 --rm -d -P nginx:v0.1-1
# 查看映射端口
[root@docker ~/nginx_img]#docker port web1
443/tcp -> 0.0.0.0:32788
80/tcp -> 0.0.0.0:32789
[root@docker ~/tomcat]#ls
apache-tomcat-8.5.61.tar.gz Dockerfile jdk-8u77-linux-x64.tar.gz
[root@docker ~/tomcat]#cat Dockerfile
FROM centos:centos7
LABEL maintainer="hukey <hukey@super.com>"
ADD jdk-8u77-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-8.5.61.tar.gz /usr/local/
ENV JAVA_HOME="/usr/local/java" JAVA_BIN="/usr/local/java/bin" JRE_HOME="/usr/local/java/jre" PATH="$PATH:/usr/local/java/bin:/usr/local/java/jre/bin" CLASSPATH="/usr/local/java/jre/bin:/usr/local/java/lib:/usr/local/java/jre/lib/charsets.jar"
WORKDIR /usr/local/
RUN mv jdk1.8.0_77 java && mv apache-tomcat-8.5.61 tomcat8
EXPOSE 8080
ENTRYPOINT ["/usr/local/tomcat8/bin/catalina.sh", "run"]
# 制作镜像
[root@docker ~/tomcat]#docker build -t tomcat:v0.1-1 ./
# 启动容器
[root@docker ~/tomcat]#docker run --name tomcat-1 --rm -P -d tomcat:v0.1-1
# 查看容器映射端口
[root@docker ~/tomcat]#docker port tomcat-1
8080/tcp -> 0.0.0.0:32796
原文:https://www.cnblogs.com/hukey/p/14111873.html