懒猫微服进阶心得(二):一台机器跑三套 Docker?深入解析懒猫容器的共存机制(上)

忘机山人

发布于379天前
博客图片修整中,看不了可以先搜索公众号“忘机山人”看。
> 本文仅代表个人视角对懒猫 Docker 的拆解分析,内容为基于现象的倒推推测,不代表懒猫官方实现方式。

拿到任何NAS的第一件事是开启SSH功能,第二步就是用Docker启动容器。

懒猫微服这个docker还不太一样,一个有三个Docker:

> docker : 运行系统组件
>
> pg-docker: 普通的docker,让我们拿来玩
>
> lzc-docker:运行懒猫商店的docker



### 三套 Docker 引擎初探
我们先来看看这三套docker引擎跑了些什么,从ps看起:

##### docker ps

```
CONTAINER ID   IMAGE                                                                              COMMAND                  CREATED      STATUS                PORTS     NAMES
1838d4f379e5   registry.lazycat.cloud/lzc/lzcsys:latest                                           "/sspk/bin/pd-service"   9 days ago   Up 9 days                       lzc-runtime-peripheral-device-1
c2f6d791181b   registry.lazycat.cloud/lzc/lzcsys:latest                                           "/sspk/bin/lzc-ingre…"   9 days ago   Up 9 days (healthy)             lzc-runtime-ingress-control-1
9699c428d2b0   registry.lazycat.cloud/dexidp/dex:v2.42.0-alpine                                   "/usr/local/bin/dock…"   9 days ago   Up 9 days                       lzc-runtime-dex
57952c3e4ba5   registry.lazycat.cloud/lzc/lzcsys:latest                                           "/sspk/bin/lzc-apise…"   9 days ago   Up 9 days (healthy)             lzc-runtime-api-servers-1
cde0eba62fd2   registry.lazycat.cloud/lzc/lzcsys:latest                                           "/sspk/bin/lzc-pkgm"     9 days ago   Up 9 days (healthy)             lzc-runtime-pkgm-1
8e9c780c012c   registry.corp.lazycat.cloud/homecloud/lzc-registry-proxy:v0.0.0-2887-gd16c7f25.m   "/bin/sh -c /lzc-reg…"   9 days ago   Up 9 days             80/tcp    lzc-registry-proxy
59d3803ef304   registry.corp.lazycat.cloud/homecloud/lzc-installer:v0.0.0-2887-gd16c7f25.m        "/docker-entrypoint.…"   9 days ago   Up 9 days                       lzc-installer
c7192a7fd471   registry.corp.lazycat.cloud/homecloud/lzc-hal:v0.0.0-2887-gd16c7f25.m              "/bin/sh -c /sspk/bi…"   9 days ago   Up 9 days                       lzc-hal
1d194e975117   registry.corp.lazycat.cloud/homecloud/lzc-recovery:v0.0.0-2887-gd16c7f25.m         "/docker-entrypoint.…"   9 days ago   Up 9 days                       lzc-recovery
8338ce6a5c17   registry.corp.lazycat.cloud/homecloud/lzc-recovery:v0.0.0-2887-gd16c7f25.m         "/sspk/bin/entrypoin…"   9 days ago   Up 9 days 
```

这里可以看到,系统级组件都跑在默认的 `docker` 下。

#####  pg-docker ps

```
CONTAINER ID   IMAGE                                                                     COMMAND                  CREATED      STATUS        PORTS                                     NAMES
d0ae10b8fc8f   registry.lazycat.cloud/u04123229/qilinzhu/ql-play:fbf2e99a00ef9a7f        "sh /app/start.sh"       3 days ago   Up 26 hours                                             ql-play
0cb9ec655c16   registry.lazycat.cloud/u04123229/cloudsmithy/shuangpin:2a8ede2b23c38be8   "/docker-entrypoint.…"   6 days ago   Up 6 days     0.0.0.0:5004->80/tcp, [::]:5004->80/tcp   unruffled_lichterman
```

> `pg-docker` 实际上就是日常部署、测试容器最常用的那一套运行时环境, Dockge 默认连接的运行时也是这个。只是这里为了区分系统docker做了改名,playground就是随便玩的意思。

![image-20250520102656467](https://raw.githubusercontent.com/cloudsmithy/picgo-imh/master/image-20250520102656467.png)

##### lzc-docker ps

```
lzc-docker ps
CONTAINER ID   IMAGE                                                                                                                              COMMAND                  CREATED        STATUS                  PORTS                                         NAMES
80c88ae6aa8b   registry.lazycat.cloud/app-tv-controller:1.0                                                                                       "/lzcinit/cloud.lazy…"   16 hours ago   Up 16 hours (healthy)                                                 cloudlazycatapplzctvcontroller-app-1
fdb2211b210e   registry.lazycat.cloud/lzc/tvos-release:v0.1.219                                                                                   "/home/tvos/run.sh"      16 hours ago   Up 16 hours             5500/tcp                                      cloudlazycatapplzctvcontroller-tvos-1
```

这个是懒猫商店的Docker,实测在客户端中停止应用是是把对应的docker删除了,无论是从`docker ps -a | grep auth`还是可视化工具看来。这也很符合使用容器的习惯,不需要的时候就删除,随用随启动,但是数据仍然还在。



### 版本和运行时对比

我们再来看一下版本,都还是一样的。所以这个就很有意思了。

```bash
lzcbox-029c588e ~ # docker --version
Docker version 27.5.1, build 9f9e405
lzcbox-029c588e ~ # pg-docker --version
Docker version 27.5.1, build 9f9e405
lzcbox-029c588e ~ # lzc-docker --version
Docker version 27.5.1, build 9f9e405
```

再看看存储后端,那是不是有什么魔改呢?看的出来后端都是containerd。

```
lzcbox-029c588e ~ # docker info | grep -i 'Runtimes\|Default Runtime'
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
lzcbox-029c588e ~ # pg-docker info | grep -i 'Runtimes\|Default Runtime'
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
lzcbox-029c588e ~ # lzc-docker info | grep -i 'Runtimes\|Default Runtime'
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
```

甚至连containerd的版本都一样

```
# docker info | grep -i "containerd"  
 Runtimes: io.containerd.runc.v2 runc
 containerd version: bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
lzcbox-029c588e ~ # pg-docker info | grep -i "containerd"  
 Runtimes: io.containerd.runc.v2 runc
 containerd version: bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
lzcbox-029c588e ~ # lzc-docker info | grep -i "containerd"  
 Runtimes: io.containerd.runc.v2 runc
 containerd version: bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
lzcbox-029c588e ~ # 
```



一开始以为是魔改看了下我的mac 运行的Orbstack的配置,好像也没啥差别。

```
docker info | grep -i 'Runtimes\|Default Runtime'      
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc


docker info | grep -i "containerd"                     

 
 Runtimes: io.containerd.runc.v2 runc
 containerd version: 06b99ca80cdbfbc6cc8bd567021738c9af2b36ce
                                                    
```


### 多引擎共存的实现方式


#### DOCKER_HOST的封装

既然三个docker都出奇的一致,到底是类似命名空间的隔离嘛?

```
lzcbox-029c588e ~ # which docker
/usr/bin/docker
lzcbox-029c588e ~ # which pg-docker
/lzcsys/bin/pg-docker
lzcbox-029c588e ~ # which lzc-docker
/lzcsys/bin/lzc-docker
lzcbox-029c588e ~ # file $(which docker)
/usr/bin/docker: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4a41bb12cfd0c306a6ede40f41cfc107b2045371, for GNU/Linux 3.2.0, with debug_info, not stripped
lzcbox-029c588e ~ # file $(which pg-docker)
/lzcsys/bin/pg-docker: Bourne-Again shell script, ASCII text executable
lzcbox-029c588e ~ # file $(which lzc-docker)
/lzcsys/bin/lzc-docker: Bourne-Again shell script, ASCII text executable
```

这就可以发现问题了,docker是原来的docker,但是pg-docker和lzc-docker是封装的脚本,来看一下:

> 所以我们可以得出一个关键点:懒猫并不是运行了三套完全独立的 Docker 服务,而是通过 shell 脚本封装,复用同一个 `docker` 客户端,切换不同的 socket 实现了“环境隔离”。这个脚本的作用相当于把 `pg-docker` 当成 `docker` 命令使用,还自动附带了环境变量 `DOCKER_HOST=...`。

```bash
lzcbox-029c588e ~ # cat $(which pg-docker)
#!/bin/bash
set -e

export DOCKER_HOST=unix:///data/playground/docker.sock
exec docker "$@"
```

这设置了 `DOCKER_HOST` 环境变量,使得之后执行的 `docker` 命令会连接到 `/data/playground/docker.sock` 这个 Unix Socket,而**不是默认的 `/var/run/docker.sock`**。

`exec` 是一个 shell 内建命令,它会用新的进程替换当前脚本的进程。

`"$@"` 表示把脚本接收到的所有参数(比如 `pg-docker ps -a`)**原样传递**给 `docker` 命令。



之前想上架一个Docker可视化工具用来,但是总不知道需要映射哪个docker.sock,这下子全都清楚了,有了这个就能在docker里使用宿主机的Docker API了。


#### daemon.json 配置详解

当然与之对应的还有daemon.json,除了用来改代理之外,我们还能修改这些东西:

```bash
lzcbox-029c588e /data/playground/data # cat /lzcsys/var/playground/daemon.json
{
  "bridge": "",
  "containerd-namespace": "playground-docker",
  "containerd-plugins-namespace": "playground-docker",
  "data-root": "/data/playground/data/docker",
  "default-address-pools": [
 
  ],
  "exec-root": "/data/playground/docker",
  "hosts": [
    "unix:///data/playground/docker.sock"
  ],
  "insecure-registries": [
    "registry.lazycat.cloud"
  ],
  "pidfile": "/data/playground/docker.pid"
```

这样多个Docker 环境就能共存了,例如:

- 系统默认的 `/var/run/docker.sock`
- 一个沙箱环境 `/data/playground/docker.sock`

这么设置好之后可以快速切换上下文,而不用每次都手动设置 `DOCKER_HOST`。



在我开发的容器可视化面板总,看到已经可以指定docker sock作为连接了,参考:

```yml
services:
  containly:
    image: registry.lazycat.cloud/u04123229/cloudsmithy/containly:30e4e3279afe9a52
    ports:
      - 5003:5000
    volumes:
      - /data/playground/docker.sock:/var/run/docker.sock
    restart: unless-stopped
```



![image-20250520104141112](https://raw.githubusercontent.com/cloudsmithy/picgo-imh/master/image-20250520104141112.png)


#### 三套 daemon.json 对比分析

从实际查找结果来看,懒猫为三套 Docker 引擎配置了不同的 daemon.json 文件和运行时环境:

- **系统组件专用**(docker):`/etc/docker/daemon.json`
- **用户 playground 环境**(pg-docker):`/lzcsys/var/playground/daemon.json`
- **懒猫商店环境**(lzc-docker):`/lzcsys/etc/docker/daemon.json`

```
sudo find / -type f -name daemon.json 2>/dev/null

/etc/docker/daemon.json
/run/lzcsys/boot/lzc-os-init/var/playground/daemon.json
/run/lzcsys/boot/lzc-os-overlay/lowerdir/lzcsys/etc/docker/daemon.json
/run/lzcsys/boot/lzc-os-overlay/lowerdir/lzcsys/lzcsys/etc/docker/daemon.json
/lzcsys/etc/docker/daemon.json
/lzcsys/var/playground/daemon.json

```



每个配置文件中都指定了独立的:

- `data-root`
- `exec-root`
- `pidfile`
- `hosts(即 sock 文件路径)`

```bash
# 默认的docker引擎
lzcbox-029c588e ~ # cat /etc/docker/daemon.json
{
  "registry-mirrors": [
  ],
  "insecure-registries": [
    "registry.lazycat.cloud"
  ],
  "log-driver": "journald",
  "cgroup-parent": "sys_docker.slice"
}

# 商店的docker引擎
lzcbox-029c588e ~ # cat /lzcsys/etc/docker/daemon.json
{
  "bridge": "none",
  "insecure-registries": [
    "registry.lazycat.cloud"
  ],
  "default-address-pools": [
  ],
  "ipv6": true,
  "hosts": [
    "unix:///lzcsys/run/lzc-docker/docker.sock"
  ],
  "containerd-namespace": "lzc-docker",
  "containerd-plugins-namespace": "lzc-docker-plugins",
  "exec-root": "/lzcsys/run/lzc-docker/docker",
  "pidfile": "/lzcsys/run/lzc-docker/docker.pid",
  "data-root": "/lzcsys/run/data/system/docker",
  "cgroup-parent": "lzc_docker.slice"
}

# playground的docker引擎
cat /lzcsys/var/playground/daemon.json
{
  "bridge": "",
  "containerd-namespace": "playground-docker",
  "containerd-plugins-namespace": "playground-docker",
  "data-root": "/data/playground/data/docker",
  "default-address-pools": [],
  "exec-root": "/data/playground/docker",
  "hosts": [
    "unix:///data/playground/docker.sock"
  ],
  "insecure-registries": [
    "registry.lazycat.cloud"
  ],
  "pidfile": "/data/playground/docker.pid"
```




### 小结

懒猫微服总会给我惊讶,除了极客风格的外壳,性能突出的硬件外,里面的软件设计也同样优秀,这个设计让我对docker有了更加深刻的认识。

三套 docker 共存不是表面上的魔改,而是通过 containerd 的 namespace 配合脚本封装,在容器之上再抽象一层运行时,把 playground、系统和商店隔离成三界,却又共用一套内核,很好玩。

评论

3
u54811115369天前

你好,问一下这个容器可视化面板是在哪里看的?

忘机山人回复u54811115369天前

这个是我写的,最近正在计划上架商店

忘机山人回复u54811115359天前

https://appstore.lazycat.cloud/#/shop/detail/xu.deploy.containly 这个上架商店了~

说点什么呢~
收藏
2
3
0