创建容器使用hook-spec
原理及使用场景
docker支持hook的扩展特性,hook应用与底层runc的执行过程中,遵循OCI标准:https://github.com/opencontainers/runtime-spec/blob/master/config.md#hooks 。
hook主要有三种类型:prestart,poststart,poststop。分别作用于容器内用户应用程序启动之前,容器应用程序启动之后,容器应用程序停止之后。
接口参考
当前为docker run和create命令增加了参数“—hook-spec”,后面接spec文件的绝对路径,可以指定容器启动时的需要添加的hook,这些hook会自动附加在docker自己动态创建的hook后面(当前docker只有一个libnetwork的prestart hook),随容器的启动/销毁过程执行用户指定的程序。
spec的结构体定义为:
// Hook specifies a command that is run at a particular event in the lifecycle of a container
type Hook struct{
Path string `json:"path"`
Args []string `json:"args,omitempty"`
Env []string `json:"env,omitempty"`
Timeout *int `json:"timeout,omitempty"`
}
// Hooks for container setup and teardown
type Hooks struct{
// Prestart is a list of hooks to be run before the container process is executed.
// On Linux, they are run after the container namespaces are created.
Prestart []Hook `json:"prestart,omitempty"`
// Poststart is a list of hooks to be run after the container process is started.
Poststart []Hook `json:"poststart,omitempty"`
// Poststop is a list of hooks to be run after the container process exits.
Poststop []Hook `json:"poststop,omitempty"`
}
- Spec文件的path、args、env 都是必填信息;
- Timeout选填(建议配置),参数类型为int,不接受浮点数,范围为[1, 120]。
- Spec内容应该是json格式的,格式不对会报错,示例参考前面。
- 使用的时候既可以`docker run —hook-spec /tmp/hookspec.json xxx`, 也可以 `docker create —hook-spec /tmp/hookspec.json xxx && docker start xxx`。
为容器定制特有的hook
以启动过程中添加一个网卡的过程来说明。下面是相应的hook spec文件内容:
{
"prestart": [
{
"path": "/var/lib/docker/hooks/network-hook",
"args": ["network-hook", "tap0", "myTap"],
"env": [],
"timeout": 5
}
],
"poststart":[],
"poststop":[]
}
指定prestart hook增加一个网络hook的执行。路径是/var/lib/docker/hooks/network-hook,args代表程序的参数,第一个参数一般是程序名字,第二个是程序接受的参数。对于network-hook这个hook程序,需要两个参数,第一个是主机上的网卡名字,第二个是在容器内的网卡重命名。
注意事项
hook path必须为docker的graph目录(—graph)下的hooks文件夹下,默认一般为 /var/lib/docker/hooks,可以通过docker info命令查看root路径。
[root@localhost ~]# docker info
...
Docker Root Dir: /var/lib/docker
...
这个路径可能会跟随用户手动配置,以及user namespace的使用(daemon —userns-remap)而变化。 path进行软链接解析后,必须以Docker Root Dir/hooks开头(如本例中使用 /var/lib/docker/hooks开头),否则会直接报错。
hooks path必须指定绝对路径,因为这个是由daemon处理,相对路径对daemon无意义。同时绝对路径也更满足安全要求。
- hook程序打印到stderr的输出会打印给客户端并对容器的声明周期产生影响(比如启动失败),而输出到stdout的打印信息会被直接忽略。
- 严禁在hook里反向调用docker的指令。
- 配置的hook执行文件必须要有可执行权限,否则hook执行会报错。
使用hook时,执行时间应尽量短。如果hook中的prestart时间过长(超过2分钟),则会导致容器启动超时失败,如果hook中的poststop时间过长(超过2分钟),也会导致容器异常。
目前已知的异常如下:执行docker stop命令停止容器时,2分钟超时执行清理时,由于hook还没执行结束,因此会等待hook执行结束(该过程持有锁),从而导致和该容器相关的操作都会卡住,需要等到hook执行结束才能恢复。另外,由于docker stop命令的2分钟超时处理是异步的过程,因此即使docker stop命令返回了成功,容器的状态也依然是up状态,需要等到hook执行完后状态才会修改为exited。
- 使用建议
- 建议配置hook的Timeout超时时间阈值,超时时间最好在5s以内。
- 建议不要配置过多hook,每个容器建议prestart、poststart、poststop这三个hook都只配置一个,过多hook会导致启动时间长。
- 建议用户识别多个hook之间的依赖关系,如果存在依赖关系,在组合hook配置文件时要根据依赖关系灵活调整顺序,hook的执行顺序是按照配置的spec文件上的先后顺序。
多个hook-spec
当有多个hook配置文件,要运行多个hook时,用户必须自己手工将多个hook配置文件组合成一个配置文件,使用—hook-spec参数指定此合并后的配置文件,方可生效所有的hook;如果配置多个—hook-spec参数,则只有最后一个生效。
配置举例:
hook1.json内容如下:
# cat /var/lib/docker/hooks/hookspec.json
{
"prestart": [
{
"path": "/var/lib/docker/hooks/lxcfs-hook",
"args": ["lxcfs-hook", "--log", "/var/log/lxcfs-hook.log"],
"env": []
}
],
"poststart":[],
"poststop":[]
}
hook2.json内容如下:
# cat /etc/isulad-tools/hookspec.json
{
"prestart": [
{
"path": "/docker-root/hooks/docker-hooks",
"args": ["docker-hooks", "--state", "prestart"],
"env": []
}
],
"poststart":[],
"poststop":[
{
"path": "/docker-root/hooks/docker-hooks",
"args": ["docker-hooks", "--state", "poststop"],
"env": []
}
]
}
手工合并后的json内容如下:
{
"prestart":[
{
"path": "/var/lib/docker/hooks/lxcfs-hook",
"args": ["lxcfs-hook", "--log", "/var/log/lxcfs-hook.log"],
"env": []
},
{
"path": "/docker-root/hooks/docker-hooks",
"args": ["docker-hooks", "--state", "prestart"],
"env": []
}
],
"poststart":[],
"poststop":[
{
"path": "/docker-root/hooks/docker-hooks",
"args": ["docker-hooks", "--state", "poststop"],
"env": []
}
]
}
需要注意的是,docker daemon会按照数组顺序依次读取hook配置文件中prestart等action中的hook二进制,进行执行动作。用户需要识别多个hook之间的依赖关系,如果有依赖关系,在组合hook配置文件时要根据依赖关系灵活调整顺序。
为所有容器定制默认的hook
Docker daemon同样可以接收—hook-spec的参数,—hook-spec的语义与docker create/run的—hook-spec参数相同,这里不再复述。也可以在/etc/docker/daemon.json里添加hook配置:
{
"hook-spec": "/tmp/hookspec.json"
}
容器在运行时,会首先执行daemon定义的—hook-spec中指定的hooks,然后再执行每个容器单独定制的hooks。