docker exec
可以在一个运行中的容器中执行一个命令:
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
- 首先我们启动一个docker容器::
docker run --name ubuntu_bash --rm -i -t ubuntu bash
注意:这里案例中容器名字命名为
ubuntu_bash
,后续引用这个容器名
- 通常我们要进入docker容器的shell中执行交互命令
docker exec -it ubuntu_bash bash
或者
docker exec -it ubuntu_bash -- /bin/bash
- 可以在执行shell中设置环境变量:
docker exec -it -e VAR=1 ubuntu_bash bash
注意:这里的环境变量
$VAR
设置为1
,且这个环境变量仅在当前bash会话有效。
- 还可以选择命令执行的工作目录:
docker exec -it -w /root ubuntu_bash pwd
- 在容器中执行一个后台非交互的命令,使用
-d
参数表示detach mode
,在后台运行命令:
docker exec -d ubuntu_bash touch /tmp/execWorks
注意:传递给容器执行的命令不要使用
""
括起,使用引号括起,就会被完整作为一个命令传递个容器,此时容器中就会找不到这个命令,而出现文件不存在报错。例如,想要创建一个临时空文件,应该使用:
docker exec -it ubuntu_bash touch /tmp/execWorks
而不是使用
docker exec -it ubuntu_bash "touch /tmp/execWorks"
使用了引号时,会提示报错:
exec failed: container_linux.go:251: starting container process caused \"exec: \\\"touch /tmp/execWorks\\\": stat touch /tmp/test: no such file or directory
遇到一个麻烦的问题,我执行
docker exec -it ubuntu_bash echo "" > /tmp/execWorks
发现实际上清空的命令并没有在容器中运行,反而清空了本地物理主机上的 /etc/execWorks
文件,原因是shell把 > /tmp/execWorks
解释为在本地主机上执行,而不是传递给远程的Docker容器中执行。
要将一系列命令使用shell包装成单一命令,例如:
docker exec -it CONTAINER_ID bash -c "mydocker_sql /usr/share/zoneinfo | mysql mysql"
上述结合多个命令的方法也可以参考 docker run 在创建启动容器是运行:
docker run image_name /bin/bash -c "cd /path/to/somewhere; python a.py"
docker run image_name /bin/bash -c "cd /path/to/somewhere && python a.py"
所以,我尝试了一下以下命令:
docker exec -it ubuntu_bash sh -c 'echo "" > /tmp/execWorks'
但是,我在 kubernetes 中测试发现报错:
kubectl exec -ti myapp-668677b895-59lt9 bash -c "> /tmp/xxx"
报错显示:
Error from server (BadRequest): container echo "" > /tmp/execWorks is not valid for pod myapp-5f976dffc5-k8jb9
好像kubernetes和docker还是对shell处理还是有些差别,我发现即使使用 |
也会出现报错,例如:
kubectl exec -ti myapp-668677b895-59lt9 bash -c "echo '' | tee /tmp/xxx"
同样报错
Error from server (BadRequest): container echo '' | tee /tmp/xxx.log is not valid for pod myapp-668677b895-59lt9
我突然明白了,原因是 kubernetes 对于pod中有多个container的情况,是将 -c
参数作为容器的意思,也就是 -c ....
被 kubernetes 解析成 pod myapp-5f976dffc5-k8jb9
中的某个容器了,根本没有传递给docker运行。
参考 Get a Shell to a Running Container :
Note: The double dash symbol “–” is used to separate the arguments you want to pass to the command from the kubectl arguments.
原来,很多时候 kubectl
命令中多了 --
不是没有道理的,这两个破折号表示后面的参数绕过kubectl,所以正确的方法:
kubectl exec -ti myapp-668677b895-59lt9 -- bash -c "echo '' | tee /tmp/xxx"
注意:我验证了,必须使用一个容器中的命令,如
echo
,不能直接用shell中的重定向符号来清理容器中的文件内容。例如,常常在shell脚本中使用的> /tmp/xxx
是不能用于kubectl传递给容器的,会出现报错:
error: you must specify at least one command for the container
参考 How to clean Docker container logs? :
检查docker日志的方法:
docker logs --since 30s -f <container_name_or_id>
或者限制输出行数:
docker logs --tail 20 -f <container_name_or_id>
要删除Docker的日志,运行:
echo "" > $(docker inspect --format='{{.LogPath}}' <container_name_or_id>)
但是不建议这样操作,因为如果docker正好在写相同文件的日志时,如果使用上述命令清理日志会损坏日志文件,因为上述清理日志实际上不是在容器内部进行,而是在物理主机上找到对应的日志文件清理,脱离了容器操作会潜在导致冲突。
建议采用docker自动的轮转日志方式,即配置 /etc/docker/daemon.json
文件:
{
"log-driver": "json-file",
"log-opts": {"max-size": "10m", "max-file": "3"}
}
注意:上述配置需要重启docker服务,并且容器需要新创建才生效。
对于kubernetes,应该采用sidecar来处理日志,好像不是直接操作pod中日志,请参考 Logging Architecture
而正确对待docker容器日志的方法,应该参考Docker官方的 Configure logging drivers