中所周知,一个Dockerfile唯一定义了一个Docker镜像。如此依赖,Docker必须提供一种方式,将Dockerfile转换为Docker镜像,采用的方式就是docker build。
FROM ubuntu:14.04
RUN apt-get update
ADD ./run.sh /
VOLUME /data
CMD ["./run.sh"]
docker容器运行必须有一个前台进程,一般此Dockerfile的当前目录下,必须包含文件run.sh。通过执行命令
docker build -t my_new_image .
可以将当前目录下的Dockerfile构建成一个名为my_new_image的镜像,镜像的默认tag为latest。对于以上docker build请求,Docker Daemon新创建了4层镜像,除了FROM命令,其余的RUN、ADD、VOLUME以及CMD都会创建一层镜像。
Docker daemon通过dockerfile构建镜像时,当发现即将新构建出的镜像与以后的某镜像重复时,可以选则放弃构建新建的镜像。而是选择用已有的镜像作为构建结构,也就是采取本地已经cache的镜像作为结果
cache机制很大程度上做到了镜像的复用,降低存储空间的同是,还大大缩短了构建时间。但是要想用好cache机制,就必须了解利用cache的一些注意事项
ADD run.sh /
中Dockerfile当前目录下的run.sh却发生了改变,从而将直接导致镜像层文件系统内容的更新,原则上不应该再使用cache。那么判断ADD命令或者COPY命令后紧接的文件是否发生变化,则成为是否延用cache的重要依据。Docker采取的策略是:获取Dockerfile下内容(包括文件的部分inode信息),计算出一个唯一的hash值,若hash值未发生变化,则认为文件内容没有发生变化,可以使用cache机制。RUN apt-get update
,那么随着时间的推移,基于同一个基础镜像,一年的apt-get update和一年后的apt-get update,由于软件源软件的更新,从而导致产生的镜像理论上应该不同。如果继续使用cache机制,将存在不满足用户需求的情况。Docker一开始的设计既考虑了外部依赖的问题,用户可以使用参数–no-cache确保最新的外部依赖,命令docker build --no-cache -t my_new_image
创建编译文件的环境的dockerfile
FROM golang:.1.7.3
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
创建运行环境的dockerrfile
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
使用脚本创建
#!/bin/bash
echo building sparkdevo/href-counter:build
#构建编译应用程序的镜像
docker build --na-cache -t sparkdevo/href-counter:build . -f dockerfile.build
#创建应用程序
docker create --name extract sparkdevo/href-counter:build
#拷贝编译好的应用程序
docker cp extract:/go/src/github.com/sparkdevo/href-counter/app ./app
docker rm -f extract
echo building sparkdevo/href-counter:latest
#构建运行应用程序的镜像
docker build --na-cache -t sparkdevo/href-counter:latest . -f dockerfile.latest
FROM golang:1.7.3
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/sparkdevo/href-counter/app .
CMD ["./app"]
或者用as玩一下
FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/sparkdevo/href-counter/app .
CMD ["./app"]
Docker build的cache机制实现了镜像的复用,不仅节省了镜像的存储空间,也为镜像构建节省了大量的时间。同时,如何命中cahce镜像,也是衡量Dockerfile书写时候合理的重要标准
1如何把镜像变小
kill的参数
1 | SIGHUP | 启动被终止的程序,可以让该进程重新读取自己的配置文件,类似重新启动 |
---|---|---|
2 | SIGNT | 相当于用键盘输入ctrl+c来中断一个程序的进行 |
9 | SIGKILL | 代表强制中断一个程序的进行,如果该进程进行到一半,那么尚未完成的部分可能会有“半产品”产生,类似vim会有.filename.swp保留下来 |
15 | SIGTERM | 以正常的方式来终止该程序。由于是正常的终止,所以后续的动作会将他完成。不过,如果该进程已经发生问题,就是无法使用正常的方法终止时,输入这个signal也是没有用的。 |
19 | SIGSTOP | 相当于键盘输入ctrl+z来暂停一个程序的进行 |
信号是一种进程之间的通信形式。一个信号就是内核发送个进程的一个信号,告诉进程发生了某种事件。当一个信号被发送给一个进程后,进程会立即中断当前的执行流并开始执行信号的处理程序。如果没有为这个信号执行处理程序。就执行默认的处理程序。
进程需要为自己感兴趣的信号注册处理程序,比如为了能让程序优雅的退出(接到退出的请求后能够对资源进行清理)一般程序都会处理,SIGKILL信号会粗暴的结束一个进程。因此我们的应用应该实现这样的目录:捕捉并处理SIGTERM信号,从而优雅的退出程序。如果失败了,用户只能通过SIGKILL信号这一手段了。除了SIGTERM和SIGKILL,还有像SIGUSR1这样的专门自定义行为的信号
docker stop命令为什么有的快有的慢??
首先向进程发送SIGTERM信号,如果进程无响应,则会再次发送SIGTERM信号,如果进程还是不响应,则会发送SIGKILL信号,强行杀死进程。当然也有可能进程在停止前,需要做一些停止前的工作;还有一中情况就是进程识别不了我们发送的信号,这里就需要我们使用脚本来实现了。
#!/bin/bash
# 打开调试级别
set -x
# 指定当前 pid 号
pid=0
# SIGUSR1-handler
my_handler() {
echo "my_handler"
}
# SIGTERM-handler
term_handler() {
if [ $pid -ne 0 ]; then
kill -SIGTERM "$pid"
# wait是用来阻塞当前进程的执行,直至指定的子进程执行结束后,才继续执行
wait "$pid"
fi
exit 143; # 128 + 15 -- SIGTERM
}
# setup handlers
# on callback, kill the last background process, which is `tail -f /dev/null` and execute the specified handler
# trap 'commands' signal-list 当脚本收到 signal-list 清单内列出的信号时, trap 命令执行双引号中的命令
trap 'kill ${!}; my_handler' SIGUSR1
trap 'kill ${!}; term_handler' SIGTERM
# run application
node app &
pid="$!"
# wait forever
while true
do
tail -f /dev/null & wait ${!}
done
创建 Dockerfile2 文件
FROM iojs:onbuild
COPY ./app.js ./app.js
COPY ./app2.sh ./app2.sh
COPY ./package.json ./package.json
RUN chmod +x ./app2.sh
EXPOSE 3000
ENTRYPOINT ["./app2.sh"]
$ docker build --no-cache -t signal-app2 -f Dockerfile2 .
$ docker run -it --rm -p 3000:3000 --name="my-app2" signal-app2