Dockerfile 是造出镜像的基础,是必须熟知并了解的知识:
一、编写Dockerfile
先给个例子,是 minio 代理访问阿里的 OSS
1FROM alpine:3.12
2
3RUN apk add --update bash && rm -rf /var/cache/apk/*
4
5COPY minio.RELEASE.2020-04-15T19-42-18Z /data/minio.RELEASE.2020-04-15T19-42-18Z
6
7ENV MINIO_ACCESS_KEY=LTAI5tFFTbsxxxxxuLb
8ENV MINIO_SECRET_KEY=t78PyGnHZilxxxxxdxBCjvNgtVC5Y
9
10WORKDIR /data
11EXPOSE 9000
12
13CMD ["/data/minio.RELEASE.2020-04-15T19-42-18Z","gateway","oss","http://oss-cn-shanghai-internal.aliyuncs.com"]
14# CMD /bin/sh -c "while true; do echo hi; sleep 10; done"
详细解释每一条语句:
-
FROM
基板,alpine 3.12 是个比较微小的版本,注意它的毛病,/bin/sh其实是busybox,没有/bin/bash,某些bash的函数功能支持不全,比如for循环
-
RUN
在容器中运行命令,上例中我们添加了 bash ,并清理了缓存。命令间用 && 可以避免镜像过多分层。
RUN分两种模式shell和exec模式:
我们只用 exec 模式,因为在 image 里装入多个 shell,没什么意义。
-
COPY 和 ADD
作用都是将文件或目录复制到 Dockerfile 构建的镜像中
我们只用COPY,如果遇到要把URL的文件放进去,可以先wget,然后放;如果要解压tarr包放进去,那就先解压再放。
注意源文件路径都使用相对路径,目标路径使用绝对路径。
如果dest不指定绝对路径,则是想对于WORDIR的相对路径
-
CMD 入口
用 [] 分割, 把所有 "" 的部分合并为一行,中间用空格隔开执行;或者直接一行没任何分割符。
所以上面的例子就是执行了一句:
1/data/minio.RELEASE.2020-04-15T19-42-18Z gateway oss http://oss-cn-shanghai-internal.aliyuncs.com
技巧:
把几个命令合在一起执行
()表示在当前shell合并执行
{}表示派生出一个子shell,在子shell中合并执行,{ echo “aaa” }必须有空格
&表示后台运行
命令之间使用 && 连接,实现逻辑与的功能。
- 只有在 && 左边的命令返回真(命令返回值 $? == 0),&& 右边的命令才会被执行。
- 只要有一个命令返回假(命令返回值 $? == 1),后面的命令就不会被执行。
&&左边的命令(命令1)返回真(即返回0,成功被执行)后,&&右边的命令(命令2)才能够被执行;换句话说,“如果这个命令执行成功&&那么执行第二个命令”。
最下的语法用了seq而不是for循环,是因为busybox的sh不支持for语法
所以才有如此怪异的语法,在容器中后台跑10个php think queue,1个crond,前台跑一个php-fpm:
1CMD for i in $(seq 10); do (php think queue &) ; done & crond && php-fpm
-
ARG 参数
ARG VERSION=7.6.1
定义后可以用${VERSION}引用,build的时候可以加–build-arg 传参数进去
1docker build --build-arg VERSION=${LATEST} -t $(ORG)/$(NAME):$(BUILD) .
还有很多 Build 的技巧,如果造一个 go 语言编译环境的中间层镜像,然后造最终镜像。
但是八戒还是推荐直接造出二进制可执行文件,然后直接拷贝进去就好,不要弄的过于麻烦,中间层那种适用于用源码 CI/CD 中无编译环境的情况。
二、调试容器
很多情况下我们造好了 image ,一跑就掉下来了,也不知道是什么情况
这个时候,我们把 CMD 换成一个 sh 执行一个死循环
1CMD /bin/sh -c "while true; do echo hi; sleep 10; done"
然后进入容器,然后再执行之前的 CMD 命令,看看报错信息是什么,就可以调试了
1$ docker exec -it 89174asklja /bin/sh
三、pod的等待技巧
这里启动正式的pod之前,先临时起了两个容器等待其他服务的完成
1 containers:
2 - name: myapp-container
3 image: busybox
4 command: ['sh', '-c', 'echo The app is running! && sleep 3600']
5 initContainers:
6 - name: init-myservice
7 image: busybox
8 command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
9 - name: init-mydb
10 image: busybox
11 command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']