Docker Compose 部署 Caddy 服务

如果将服务都写在一个 docker-compose.yml 文件里面,那么服务之间可以使用 <服务名>:<端口> 的形式互相访问。

比如下面的 docker-compose.yml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
services:
  caddy:
    image: caddy:alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - $PWD/config:/etc/caddy
      - caddy_data:/data
      - caddy_config:/config

  miniflux:
    image: miniflux/miniflux:latest
    container_name: miniflux
    depends_on:
      db:
        condition: service_healthy
    environment:
      - DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable
      - RUN_MIGRATIONS=1
      - CREATE_ADMIN=1
      - ADMIN_USERNAME=admin
      - ADMIN_PASSWORD=test123
      - BASE_URL=https://miniflux.example.site

  db:
    image: postgres:15
    container_name: postgres
    environment:
      - POSTGRES_USER=miniflux
      - POSTGRES_PASSWORD=secret
    volumes:
      - miniflux-db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "miniflux"]
      interval: 10s
      start_period: 30s

volumes:
  caddy_data:
  caddy_config:
  miniflux-db:

想通过 Caddy 反向代理本地的 Miniflux,只需将 Caddyfile 配置如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    http_port 80
    https_port 443
    email example@email.site
}

miniflux.example.site {
    encode zstd gzip
    reverse_proxy miniflux:8080
}

文件列表:

1
2
3
4
.
├── config
│   └── Caddyfile
└── docker-compose.yml

但是这样的 Docker Compose 是将所有的服务捆绑在了一起。如果后期想要再添加一个本地服务交给 Caddy 反向代理,又或者想删除某一个服务,是比较麻烦的。

不同 Docker Compose 文件启动的容器,默认会使用带文件夹名前缀的网络来隔离它们的信息,它们之间默认不能用服务名称访问。要实现多个 Docker Compose 文件启动的容器网络互通,需要配置 networks 指令。

首先使用 docker network create 命令手动创建网络。

1
docker network create caddy_network

然后在Caddy 的 docker-compose.yml 配置好 netwroks:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
services:
  caddy:
    image: caddy:alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - $PWD/config:/etc/caddy
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - caddy_network

volumes:
  caddy_data:
  caddy_config:

networks:
  caddy_network:
    external: true

external 参数表示使用已有的外部网络,而不是新建带文件夹名前缀的网络。

同样配置好 Miniflux 的 docker-compose.yml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
services:
  miniflux:
    image: miniflux/miniflux:latest
    container_name: miniflux
    depends_on:
      db:
        condition: service_healthy
    environment:
      - DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable
      - RUN_MIGRATIONS=1
      - CREATE_ADMIN=1
      - ADMIN_USERNAME=admin
      - ADMIN_PASSWORD=test123
      - BASE_URL=https://miniflux.example.site
    networks:
      - database_network
      - caddy_network

  db:
    image: postgres:15
    container_name: postgres
    environment:
      - POSTGRES_USER=miniflux
      - POSTGRES_PASSWORD=secret
    volumes:
      - miniflux-db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "miniflux"]
      interval: 10s
      start_period: 30s
    networks:
      - database_network

volumes:
  miniflux-db:

networks:
  database_network: # Provide the communication between miniflux and database only
    internal: true
  caddy_network:
    external: true

Caddyfile 的配置不需要改变:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    http_port 80
    https_port 443
    email example@email.site
}

miniflux.example.site {
    encode zstd gzip
    reverse_proxy miniflux:8080
}

文件列表:

1
2
3
4
5
6
7
.
├── miniflux
│   └── docker-compose.yml
└── caddy
    ├── config
    │   └── Caddyfile
    └── docker-compose.yml

这样避免了 Miniflux 示例文档 中暴露 8080 端口的做法,使用更安全的 https,也不用将 Miniflux 和 Caddy 绑定在同一个 Docker Compose 文件中。

上面的例子中 volumes 挂载了 Caddyfile 所在的文件夹,而不是只挂载 Caddyfile 文件,这是为了更好的将主机文件的更新实时反映到容器。File mount does not update with changes from host 讨论了这个问题。

简单来说,Docker 的绑定挂载基于 inode,Vim 等编辑器修改文件时,不是直接在原文件上直接修改,而是复制了一个原文件的副本,保存时用副本替换掉原文件,副本的 inode 和原文件不一样。

服务运行时,如果在主机里修改了 Caddyfile,下面的命令可以平滑重启 Caddy 服务:

1
2
3
4
# 格式化 Caddyfile
docker container exec -it caddy caddy fmt --overwrite /etc/caddy/Caddyfile
# 更新 Caddyfile 并重启
docker container exec -it caddy caddy reload --config /etc/caddy/Caddyfile