Dockerfile的编写与调试技巧

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” }必须有空格

    &表示后台运行

    命令之间使用 && 连接,实现逻辑与的功能。

    1. 只有在 && 左边的命令返回真(命令返回值 $? == 0),&& 右边的命令才会被执行。
    2. 只要有一个命令返回假(命令返回值 $? == 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;'] 

PHP程序如何发送syslog到远程服务器
Linux下的程序限速软件Trickle
comments powered by Disqus