最开始,我服务器的反向代理使用caddy,配置非常方便

后面,脑子抽抽了换了nginx+certbot,也还算方便

后面不知道为啥又换了caddy,不过当时选择了docker中的caddy

再后来,也就是现在,脑子抽抽换了traefik,也不知道为啥

还是记录一下折腾traefik的操作,希望以后反复横跳的我不用再踩坑

之前

我的阿里云ECS上主要运行以下几个服务:

  • Lobe Chat

  • Minio(为Lobe Chat提供s3存储)

  • Logto(单点登录)

  • Vaultwarden

  • Halo

如果使用caddy的话,配置反向代理非常方便

{
    email personal@zhelearn.com
}

vaultwarden.zhelearn.com {
    encode zstd gzip

    header / {
        Strict-Transport-Security "max-age=31536000;"
        X-XSS-Protection "0"
        X-Frame-Options "DENY"
        X-Robots-Tag "noindex, nofollow"
        X-Content-Type-Options "nosniff"
        -Server
        -X-Powered-By
        -Last-Modified
    }

    reverse_proxy {$TARGET_IP}:11001 {
        header_up X-Real-IP {remote_host}
        header_up Host {host}
    }
}

mail.zhelearn.com {
    encode zstd gzip

    header {
        Strict-Transport-Security "max-age=63072000"
        Content-Security-Policy-Report-Only "default-src 'self' https:; script-src 'self' https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'; frame-src 'self';"
    }

    reverse_proxy https://qiye.aliyun.com
}

lobe.zhelearn.com {
    header Strict-Transport-Security "max-age=63072000"
    reverse_proxy {$TARGET_IP}:3210
}

auth-api.zhelearn.com {
    header Strict-Transport-Security "max-age=63072000"
    reverse_proxy {$TARGET_IP}:3001
}

auth-ui.zhelearn.com {
    header Strict-Transport-Security "max-age=63072000"
    reverse_proxy {$TARGET_IP}:3002
}

s3-api.zhelearn.com {
    header Strict-Transport-Security "max-age=63072000"
    reverse_proxy {$TARGET_IP}:9000
}

s3-ui.zhelearn.com {
    header Strict-Transport-Security "max-age=63072000"
    reverse_proxy {$TARGET_IP}:9001
}

blog.zhelearn.com {
    header Strict-Transport-Security "max-age=63072000"
    reverse_proxy {$TARGET_IP}:8090
}

然后在docker-compose.yaml 中,启动caddy容器即可

services:
    caddy:
        image: caddy:latest
        container_name: caddy
        restart: always
        ports:
            - "80:80"
            - "443:443"
        environment:
            - TARGET_IP=172.20.164.215
        volumes:
            - ./data/caddy/Caddyfile:/etc/caddy/Caddyfile

(其中TARGET_IP是我服务器的内网IP)

之后

现在切换到traefik,事情貌似变得复杂了起来,但是似乎也容易了一些(?)

可以直接在docker-compose中为对应的容器添加label来配置反向代理

首先是添加traefik的容器

services:
    traefik:
        image: traefik:v3.1
        command:
            - "--api.insecure=true"
            - "--api.dashboard=true"
            - "--providers.docker=true"
            - "--entrypoints.web.address=:80"
            - "--entrypoints.websecure.address=:443"
            - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
            - "--certificatesresolvers.myresolver.acme.email=personal@zhelearn.com"
            - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
        ports:
            - "80:80"
            - "8080:8080"
            - "443:443"
        volumes:
            - "./traefik/letsencrypt:/letsencrypt"
            - "/var/run/docker.sock:/var/run/docker.sock"
        labels:
            - "traefik.enable=true"

            # Global Settings
            - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

            # Traefik dashboard basic auth
            - "traefik.http.middlewares.traefik-auth.basicauth.users=username:encoded password"

            - "traefik.http.routers.traefik-http.rule=Host(`traefik.zhelearn.com`)"
            - "traefik.http.routers.traefik-http.entrypoints=web"
            - "traefik.http.routers.traefik-http.middlewares=redirect-to-https"
            - "traefik.http.routers.traefik-https.rule=Host(`traefik.zhelearn.com`)"
            - "traefik.http.routers.traefik-https.entrypoints=websecure"
            - "traefik.http.routers.traefik-https.tls=true"
            - "traefik.http.routers.traefik-https.tls.certresolver=myresolver"
            - "traefik.http.routers.traefik-https.service=traefik"
            - "traefik.http.routers.traefik-https.middlewares=traefik-auth"
            - "traefik.http.services.traefik.loadbalancer.server.port=8080"
        restart: always

其中我开启了traefik dashboard,使用basic auth进行认证,生成密码的方法是:

echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g

一般容器

接下来只需要配置其他容器的label,traefik就可以为他们进行反向代理

例如 Vaultwarden

services:
    ......
    vaultwarden:
        image: vaultwarden/server:latest
        ports:
            - "11001:80"
        volumes:
            - "./vaultwarden/data:/data"
        env_file:
            - ./vaultwarden/.env
        labels:
            - "traefik.enable=true"

            - "traefik.http.routers.vaultwarden-http.rule=Host(`vaultwarden.zhelearn.com`)"
            - "traefik.http.routers.vaultwarden-http.entrypoints=web"
            - "traefik.http.routers.vaultwarden-http.middlewares=redirect-to-https"
            - "traefik.http.routers.vaultwarden-https.rule=Host(`vaultwarden.zhelearn.com`)"
            - "traefik.http.routers.vaultwarden-https.entrypoints=websecure"
            - "traefik.http.routers.vaultwarden-https.tls=true"
            - "traefik.http.routers.vaultwarden-https.tls.certresolver=myresolver"
        restart: always

有多个端口的容器

不过也有特殊情况,例如minio,要开放两个端口,并代理到不同的域名上,配置就稍微复杂一点

如下

services:
    ......
    minio:
        image: minio/minio
        ports:
            - "9000:9000"
            - "9001:9001"
        volumes:
            - "./minio/data:/etc/minio/data"
        env_file:
            - ./minio/.env
        labels:
            - "traefik.enable=true"

            - "traefik.http.routers.minio-api-http.rule=Host(`minio-api.lobe.zhelearn.com`)"
            - "traefik.http.routers.minio-api-http.entrypoints=web"
            - "traefik.http.routers.minio-api-http.middlewares=redirect-to-https"
            - "traefik.http.routers.minio-api-http.service=minio-api"
            - "traefik.http.routers.minio-api-https.rule=Host(`minio-api.lobe.zhelearn.com`)"
            - "traefik.http.routers.minio-api-https.entrypoints=websecure"
            - "traefik.http.routers.minio-api-https.tls=true"
            - "traefik.http.routers.minio-api-https.tls.certresolver=myresolver"
            - "traefik.http.routers.minio-api-https.service=minio-api"
            - "traefik.http.services.minio-api.loadbalancer.server.port=9000"

            - "traefik.http.routers.minio-ui-http.rule=Host(`minio-ui.lobe.zhelearn.com`)"
            - "traefik.http.routers.minio-ui-http.entrypoints=web"
            - "traefik.http.routers.minio-ui-http.middlewares=redirect-to-https"
            - "traefik.http.routers.minio-ui-http.service=minio-ui"
            - "traefik.http.routers.minio-ui-https.rule=Host(`minio-ui.lobe.zhelearn.com`)"
            - "traefik.http.routers.minio-ui-https.entrypoints=websecure"
            - "traefik.http.routers.minio-ui-https.tls=true"
            - "traefik.http.routers.minio-ui-https.tls.certresolver=myresolver"
            - "traefik.http.routers.minio-ui-https.service=minio-ui"
            - "traefik.http.services.minio-ui.loadbalancer.server.port=9001"
        restart: always
        command: >
            server /etc/minio/data --address ":9000" --console-address ":9001"

这样就能够使9000对应api,而9001对应ui了

容器之外

如果在需要代理的端口在docker之外,那么接下来的配置就麻烦一点了

首先要修改traefik容器的配置,添加一个文件的provider

services:
    traefik:
        image: traefik:v3.1
        command:
            - "--api.insecure=true"
            - "--api.dashboard=true"
            - "--providers.docker=true"
            - "--providers.file=true"
            - "--providers.file.watch=true"
            - "--providers.file.filename=/etc/traefik/rules.yaml"
            - "--entrypoints.web.address=:80"
            - "--entrypoints.websecure.address=:443"
            - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
            - "--certificatesresolvers.myresolver.acme.email=personal@zhelearn.com"
            - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
        ports:
            - "80:80"
            - "8080:8080"
            - "443:443"
        volumes:
            - "./traefik/letsencrypt:/letsencrypt"
            - "./traefik/rules.yaml:/etc/traefik/rules.yaml"
            - "/var/run/docker.sock:/var/run/docker.sock:ro"
    ......

接下来添加rules.yaml,例如我这里

http:
    routers:
        webhook-http:
            rule: "Host(`webhook.zhelearn.com`)"
            entryPoints:
                - web
            middlewares:
                - redirect-to-https
            service: webhook
        webhook-https:
            rule: "Host(`webhook.zhelearn.com`)"
            entryPoints:
                - websecure
            tls:
                certResolver: myresolver
            service: webhook

    services:
        webhook:
            loadBalancer:
                servers:
                    - url: "http://172.20.164.215:5000"

    middlewares:
        redirect-to-https:
            redirectScheme:
                scheme: "https"
                permanent: true

这样就可以对docker网络之外的端口进行代理了

最后

除了配置traefik的字数多一点,好像也没啥不好的,当然也没啥好的)