Featured image of post 使用caddy通过https代理docker服务

使用caddy通过https代理docker服务

本文介绍了如何在docker中运行caddy并代理宿主机或docker中的服务,提供https安全连接,并支持自动更新证书。

引言

什么是 Caddy?

Caddy 是一款开源的现代化 Web 服务器,具有自动 HTTPS 配置、内建反向代理、负载均衡、以及简洁的配置文件等特点。Caddy 比较特别的是,它可以自动获取和更新 SSL/TLS 证书,用户不需要手动配置 SSL/TLS,且支持多种 DNS 提供商的 API 集成,如阿里云和 Cloudflare。

前置条件

  • 一个域名,并且将dns解析配置在阿里云或者cloudflare中(其他云厂商也可以,但是需要自行配置caddy)

  • 一个或多个需要暴露的服务(例如 alist自建网盘、博客、nas管理页面 等)

  • 已经安装了docker

    • 对于大多数操作系统,都可以用官方提供的脚本安装docker

    • curl -fsSL https://get.docker.com -o get-docker.sh
      sh get-docker.sh --version 20.10 --mirror Aliyun
      

准备云服务商token

启动caddy

caddy对于阿里云或者cloudflare访问的兼容性依赖插件,如果在docker中使用,需要使用docker重新构建caddy以包含我们需要的插件,更多详情可以查看caddy docker文档

方便起见可以直接使用我打好的镜像registry.cn-hangzhou.aliyuncs.com/adpc/caddy:2.9.1-dns(2.9.1版本、支持x86和arm64架构),如果需要其他版本,可以使用如下的dockerfile自行构建,docker buildx build --platform=linux/amd64,linux/arm64 -t caddy:2.9.1-dns -f caddy.dockerfile -o type=registry .

FROM caddy:2.9.1-builder AS builder

RUN go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
RUN xcaddy build \
    --with github.com/caddy-dns/alidns \
    --with github.com/caddy-dns/cloudflare

FROM caddy:2.9.1
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

编写caddyfile

caddyfile类似nginx的配置,但是更简单,详见官方文档,示例配置

{
    # 设置 HTTP 和 HTTPS 端口
    http_port 8080
    https_port 8443
}

tool.vinf.top {
    # 使用cloudflare DNS API 获取证书
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }

    # 反向代理到名为it-tools的Docker容器内服务
    reverse_proxy it-tools:80
}

# 多个服务 用域名区分
music.vinf.top {
    # 使用阿里云 DNS API 获取证书
    tls {
        dns alidns {
            access_key_id {env.ALIYUN_ACCESS_KEY_ID}
            access_key_secret {env.ALIYUN_ACCESS_KEY_SECRET}
        }
    }

    # 反向代理到 主机的8000端口 服务
    # 其中host.docker.internal是主机在容器中的dns
    reverse_proxy host.docker.internal:8000
}

网络

容器之间访问

如果使用docker run -d --name nginx nginx:latest命令,启动一个nginx容器,docker默认会将容器连接到brige网络。

使用docker network ls可以看到如下内容。

NETWORK ID     NAME      DRIVER    SCOPE
3f8c43283486   bridge    bridge    local
b1784a11c007   host      host      local
aa57c193cefa   none      null      local

对于连接到同一个bridge网络的容器,容器之间可以直接使用ip访问。

如果使用的是自定义网络,还可以使用容器名作为dns访问容器。详见docker network dns。示例:

# 创建网络
docker network create <network-name>
# 使用该网络创建容器
docker run --net=<network-name> --name=<container-name> ...
# 或者将该网络连接到一个已经存在的容器
docker network connect <network-name> <container-name>
# 测试,使用容器名 ping
docker exec -it <container-name-A> ping <container-name-B>

如此一来我们就能在caddy容器中,使用容器名称直接访问容器,而不用担心容器重新创建时ip变动的问题。

容器访问主机

在容器内部使用127.0.0.1并不能访问到主机的服务(除非使用host网络),在使用bridge网络时,我们可以通过网桥的网关访问到主机的服务

docker network  inspect <network-name> | grep Gateway
# 返回 "Gateway": "172.18.0.1"
# 此时我们在容器中curl 172.18.0.1:8000 发现能通

为了使用更方便,我们可以在容器启动时添加--add-host=host.docker.internal:host-gateway参数,docker会自动将主机ip和host.docker.internal写入到hosts文件中,后续我们可以通过host.docker.internal访问主机

运行caddy

# 创建一个bridge网络,用来运行服务
docker network create service-net
# 阿里云使用以下命令运行caddy
docker run -d --name caddy --restart unless-stopped \
  --network service-net
  --add-host=host.docker.internal:host-gateway
  -p 8080:8080 \
  -p 8443:8443 \
  -v $(pwd)/Caddyfile:/etc/caddy/Caddyfile \
  -v $(pwd)/data:/data \
  -v $(pwd)/config:/config \
  -e ALIYUN_ACCESS_KEY_ID=填写你的ACCESS_KEY \
  -e ALIYUN_ACCESS_KEY_SECRET=填写你的ACCESS_KEY_SECRET \
  registry.cn-hangzhou.aliyuncs.com/adpc/caddy:2.9.1-dns

# cloudflare
docker run -d --name caddy --restart unless-stopped \
  --network service-net
  --add-host=host.docker.internal:host-gateway
  -p 8080:8080 \
  -p 8443:8443 \
  -v $(pwd)/Caddyfile:/etc/caddy/Caddyfile \
  -v $(pwd)/data:/data \
  -v $(pwd)/config:/config \
  -e CF_API_TOKEN=填写你的cloudflare token \
  registry.cn-hangzhou.aliyuncs.com/adpc/caddy:2.9.1-dns