请教个关于 docker 中 init 进程的问题

104 天前
 yezheyu

我想用 docker 的官方镜像仓库 registry 搭建一个本地镜像仓库。registry 在删除镜像后默认只会删除镜像的 manifest ,需要手动 gc(garbage-collect)来释放 layer 。

而调用 gc 又要求需要把 registry 设置为 readonly 模式来保证不会误删用户正在上传的镜像。

我想设计一个镜像,tini 作为 init 进程,执行 registry 进程来接收用户的 CRUD 镜像请求,再使用 cron 执行一个定时任务:一段时间后停止 registry ,然后以 readonly 方式重启动 registry ,再执行 gc ,执行完 gc ,再去掉 readonly ,重启 registry

下面是涉及到的所有文件:

$ ls
readonly_config.yml  normal_config.yml  Dockerfile  gc.sh  registry_with_gc.sh


$ cat Dockerfile 
FROM registry
WORKDIR /
RUN apk add dcron
RUN apk add tini
COPY *.sh /
COPY *.yml /etc/docker/registry
ENTRYPOINT ["tini", "--"]
CMD ["./registry_with_gc.sh"]



# 添加一个 gc.sh 的定时任务,启动 registry 服务
$ cat registry_with_gc.sh 
#!/bin/sh
crond
{
  crontab -l 2>/dev/null;
  echo "* * * * * sh /gc.sh >> /tmp/gc.log 2>&1";
} | crontab -
registry serve /etc/docker/registry/normal_config.yml



# registry 切换到 readonly 模式,在进行 gc ,最后再切换回来
$ cat gc.sh 
#!/bin/sh
# stop registry serve
killall registry
# wait for registry stop
sleep 1
# start registry serve with readonly_config.yml
registry serve /etc/docker/registry/readonly_config.yml &
# gc
registry garbage-collect /etc/docker/registry/normal_config.yml 
# stop registry serve
killall registry
# wait for registry stop
sleep 1
# start registry serve with normal_config.yml
registry serve /etc/docker/registry/normal_config.yml



$ cat normal_config.yml 
version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
  delete:
    enabled: true
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3



# 多一个 readonly.enabled: true 字段
$ cat readonly_config.yml 
version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
  maintenance:
    readonly:
      enabled: true
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

问题:

镜像 build 后,容器启动一会,当定时任务 gc.sh 执行时,执行到第一行停止 registry ,就会杀掉registry_with_gc.sh中的 registry ,导致registry_with_gc.sh结束,接着容器就结束,没法执行后续的 gc 任务。

这种该怎么解决呢?registry_with_gc.sh命令结束了,但是 tini 作为 init 进程不是没有结束吗?为啥容器的生命周期就结束呢?

难道要把registry_with_gc.sh变成死循环吗?

1121 次点击
所在节点    Docker
5 条回复
loveqianool
103 天前
为什么不愿意死循环,有什么说法吗?
povsister
103 天前
你 CMD 直接替换了 entrypoint ,1 号进程是 sh 不是 tini 了

问个题外话,你这么直接杀,正在上传镜像的用户就不管了?直接上传失败?
yezheyu
103 天前
@povsister
嗯,如果你是指 Dockerfile 中这两行会发生替换,我认为你可能理解有偏差,可以自己翻下文档
ENTRYPOINT ["tini", "--"]
CMD ["./registry_with_gc.sh"]

至于第二点,我这是随便设计的,上传失败大不了重新上传,也没太大代价。如果真要较真,引入 Redis 作为上传请求数的计数器?
yezheyu
103 天前
@loveqianool
没啥说法吧,我只是不明白,既然我使用了 tini 作为 PID 1 的进程,registry_with_gc 作为其子进程就应该可以使用完就释放掉,要不然 tini 的作用在哪?只是作为信号转发器?

按我的理解,tini 作为 init 进程,只要其还在,容器的生命周期就不会结束,registry_with_gc 挂了也就挂了,不会导致容器结束
julyclyde
103 天前
@yezheyu 你可以给各个进程挂上 strace 看一下

按说 tini 作为 init 只要还在,整个容器不会结束
但是你咋知道 tini 确实还在呢?这个先入为主的思维不对啊
你应该去研究为什么它退出了,而不是问为什么它没退出却容器停了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1059262

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX