SSD Nodes Learn
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-07-24

Cara guna Traefik v3 untuk 5 aplikasi

Pelajari cara konfigurasi Traefik v3 pada Docker Compose untuk 5 aplikasi dengan satu IP. Elakkan ralat acme.json yang menghalang proses startup kontena.

Satu IP, lima aplikasi, satu port 443

VPS anda mempunyai satu alamat IPv4 awam dan satu port TCP 443. Anda ingin menjalankan Gitea, salinan staging aplikasi anda, papan pemuka dalaman, halaman status, dan penerima webhook pada satu pelayan — lima nama hos, satu mesin. Reverse proxy adalah proses yang mengawal port :80 dan :443, membaca pengepala Host pada setiap permintaan, dan menghantarnya ke kontena yang betul. Traefik melakukan tugas ini, dan ia memperoleh serta memperbaharui sijil untuk setiap nama hos tanpa anda perlu menjalankan certbot secara manual.

Perbezaan antara Traefik dengan blok nginx server {} adalah sumber konfigurasinya. Dengan nginx, anda mengedit fail dan melakukan reload, manakala kitaran hayat sijil kekal sebagai tugas berasingan — aliran kerja yang anda ikuti apabila anda mengeluarkan sijil Let's Encrypt dengan certbot pada nginx, di mana pemasa pembaharuan berada di luar pelayan web sepenuhnya. Docker provider dalam Traefik memantau aliran acara Docker dan membaca labels daripada kontena anda: mulakan kontena yang mempunyai label peraturan Host() dan ia boleh dihalakan dalam masa satu saat; hentikan kontena tersebut dan laluan tersebut akan hilang. Ini juga merupakan satu risiko. Konfigurasi yang berada dalam labels wujud di lima tempat serentak, dan label yang salah tidak akan mengeluarkan ralat — kontena tersebut hanya tidak dihalakan, dan Traefik tidak memberikan sebarang makluman.

Empat kata nama

  • Entrypoints ialah soket yang mendengar. Anda perlu menetapkan dua: web pada :80 dan websecure pada :443.
  • Routers memadankan permintaan (Host(...)) dan menyambungkannya ke perkhidmatan. Sijil dipohon bagi setiap router melalui tls.certresolver.
  • Services ialah bahagian backend — sebuah kontena dan port yang didengari di dalam rangkaian Docker.
  • Middlewares terletak di antara router dan service: pengesahan asas, senarai putih IP, penulisan semula header, dan penghalaan semula.

Konfigurasi statik (entrypoints, providers, ACME) dihantar melalui baris perintah Traefik atau dalam traefik.yml, dan sebarang perubahan memerlukan Traefik dimulakan semula. Konfigurasi dinamik (routers, services, middlewares) diterima daripada label kontena dan dimuat semula secara langsung. Kekeliruan antara kedua-duanya adalah punca biasa masalah "flag saya tidak berfungsi".

Fail compose

Satu rangkaian Docker kongsi bernama proxy adalah tunjang utama. Traefik hanya boleh mencapai kontena jika kedua-duanya berada dalam rangkaian tersebut.

name: edge

networks:
  proxy:
    name: proxy

services:
  traefik:
    image: traefik:v3.5
    restart: unless-stopped
    command:
      - --providers.docker=true
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=proxy
      - --entryPoints.web.address=:80
      - --entryPoints.websecure.address=:443
      - --entryPoints.web.http.redirections.entryPoint.to=websecure
      - --entryPoints.web.http.redirections.entryPoint.scheme=https
      - --certificatesresolvers.le.acme.email=you@example.com
      - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.le.acme.tlschallenge=true
      # while you iterate, point at staging so a mistake costs nothing:
      # - --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
      - --api.dashboard=true
      - --log.level=INFO
      - --accesslog=true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
      - traefik.http.routers.dashboard.entrypoints=websecure
      - traefik.http.routers.dashboard.tls.certresolver=le
      - traefik.http.routers.dashboard.service=api@internal
      - traefik.http.routers.dashboard.middlewares=dashboard-auth
      - traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$REPLACE$$THIS

  gitea:
    image: gitea/gitea:1  # major-only pin keeps this demo copy-pasteable; pin an exact release in production
    restart: unless-stopped
    volumes:
      - ./gitea:/data
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.gitea.rule=Host(`git.example.com`)
      - traefik.http.routers.gitea.entrypoints=websecure
      - traefik.http.routers.gitea.tls.certresolver=le
      - traefik.http.services.gitea.loadbalancer.server.port=3000

docker compose up -d, kemudian docker compose logs -f traefik. Setiap aplikasi tambahan adalah salinan daripada blok gitea dengan nama router, Host(), dan port dalaman sendiri. Pemasangan Nextcloud dalam Docker dengan TLS dan sandaran berfungsi dengan cara yang sama — buang port yang diterbitkan, sambungkan ke proxy, dan biarkan label router menguruskan nama hos serta sijil.

Lima butiran berikut adalah penting.

exposedByDefault=false menjadikan kontena tidak kelihatan kepada Traefik sehingga ia mempunyai traefik.enable=true. Jika anda mengabaikannya, setiap kontena yang anda mulakan — termasuk postgres sementara yang anda jalankan untuk semakan — akan menghasilkan laluan (route) secara automatik.

providers.docker.network=proxy memberitahu Traefik rangkaian mana yang perlu digunakan apabila sesebuah kontena disambungkan kepada beberapa rangkaian. Jika diabaikan, Traefik mungkin memilih IP kontena yang salah, yang akan menyebabkan ralat 502 yang kelihatan seperti kegagalan aplikasi.

loadbalancer.server.port=3000 ialah port di dalam kontena; Gitea mendengar pada port 3000 di sana. Perhatikan bahawa tiada kontena aplikasi yang menerbitkan port — hanya Traefik sahaja.

Laluan semula (redirect) pada entrypoint web menukar permintaan teks biasa kepada 308 ke HTTPS. Port 80 tetap dibuka: cabaran ACME HTTP memerlukannya, begitu juga pengguna yang menaip nama hos tanpa protokol.

Penggunaan $$ berganda dalam hash basic-auth adalah cara Compose melakukan escaping, bukan kesilapan taip. Jana hash tersebut dengan htpasswd -nbB admin 'your-password' (pakej apache2-utils), kemudian gandakan setiap $.

Sijil, dan perangkap acme.json

tlschallenge=true memilih TLS-ALPN-01: Let's Encrypt menyambung ke pelayan anda pada port 443 dan Traefik menjawab cabaran di dalam TLS handshake. Alternatifnya ialah HTTP-01, pada port 80 — tukar baris tlschallenge dalam senarai command: Traefik kepada dua baris ini:

      - --certificatesresolvers.le.acme.httpchallenge=true
      - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web

Kedua-duanya berfungsi. Kedua-duanya memerlukan DNS awam untuk nama hos sudah menghala ke VPS anda — pihak berkuasa sijil akan menyelesaikan nama tersebut dan menyambung dari luar. Cipta rekod A (dan AAAA) terlebih dahulu, sahkan dengan dig +short git.example.com, kemudian mulakan Traefik.

Sekarang, perangkap yang membuang masa pengguna. Traefik menyimpan kunci akaun ACME dan setiap sijil yang dikeluarkan dalam satu acme.json. Jika fail tersebut boleh dibaca oleh kumpulan atau dunia (group- or world-readable), Traefik akan memaparkan baris yang hampir sama dengan ini dan berhenti:

error: unable to get ACME account: permissions 644 for /letsencrypt/acme.json are too open, please use 600

Penyelesaian yang betul adalah seperti di atas: gunakan bind-mount pada direktori dan biarkan Traefik mencipta fail tersebut sendiri dengan mod yang betul. Jika anda mencipta acme.json dengan touch, umask anda telah menetapkannya kepada 644. Baiki fail tersebut pada hos:

chmod 600 ./letsencrypt/acme.json
docker compose restart traefik

Buat sandaran (backup) bagi direktori tersebut dengan volume aplikasi anda. Kehilangan fail ini boleh diatasi — sijil boleh dikeluarkan semula — tetapi mengeluarkan semula lima nama hos secara serentak akan menyebabkan anda terkena had kadar (rate limits).

Gunakan staging CA semasa anda melakukan iterasi. Nyahkomen baris caserver, pastikan setiap laluan berfungsi, kemudian komenkan semula baris tersebut dan padam acme.json supaya sijil produksi diminta semula secara segar. Let's Encrypt produksi membenarkan lima sijil duplikasi seminggu untuk set nama hos yang sama, dan mengehadkan pengesahan gagal yang berulang untuk nama yang sama. Staging mengeluarkan sijil yang tidak dipercayai — pelayar anda akan memberi amaran, dan amaran itu adalah petanda bahawa ia berfungsi — dengan had yang jauh lebih longgar.

Dashboard adalah permukaan kawalan, bukan demo

Kebanyakan panduan pantas menetapkan --api.insecure=true, yang menyajikan dashboard pada port 8080 tanpa pengesahan. Ini berbahaya pada pelayan dengan IP awam kerana ia mendedahkan topologi penghalaan, nama hos, nama perisian tengah, dan port backend kepada sesiapa sahaja yang melakukan imbasan.

Label pada perkhidmatan traefik di atas adalah alternatifnya: dashboard yang dihalat seperti aplikasi lain, menggunakan nama hos sebenar, melalui TLS, dan di belakang basicauth. service=api@internal adalah komponen yang menyambungkan penghala ke API terbina dalam Traefik. Tingkatkan keselamatan dengan menambah senarai benarkan IP (IP allow-list) yang diterapkan dari kiri ke kanan. Jika alamat pejabat anda adalah dinamik, tetapkan julat kepada subnet yang diberikan oleh VPN WireGuard yang anda hoskan sendiri pada VPS yang sama dan akses dashboard hanya melalui terowong tersebut:

- traefik.http.middlewares.office.ipallowlist.sourcerange=10.0.0.7/32
- traefik.http.routers.dashboard.middlewares=office,dashboard-auth

Docker socket adalah root

/var/run/docker.sock ialah API yang boleh mencipta kontena yang memuatkan (mount) / daripada hos. Akses kepadanya adalah setaraf dengan root pada mesin tersebut, dan Traefik memerlukannya untuk membaca label.

Kekalkan :ro pada mount, tetapi fahami kesannya: ia menjadikan fail socket tersebut bersifat baca sahaja (read-only). Ia tidak menghalang permintaan POST ke API Docker yang melalui socket tersebut. Langkah mitigasi yang sebenar adalah dengan tidak memberikan socket kepada Traefik, sebaliknya letakkan proxy penapis di antaranya:

  dockerproxy:
    image: tecnativa/docker-socket-proxy   # pin the current tag
    restart: unless-stopped
    environment:
      CONTAINERS: 1
      NETWORKS: 1
      POST: 0
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - proxy

Buang volume socket daripada Traefik dan halakan provider ke proxy tersebut:

--providers.docker.endpoint=tcp://dockerproxy:2375

Traefik mengekalkan akses baca kepada kontena dan rangkaian, serta kehilangan keupayaan untuk mencipta apa-apa.

Firewall, port, dan peraturan yang sering disalah anggap

Dua port dibuka, ditambah SSH:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Port yang diterbitkan oleh Docker memintas ufw. Docker memasukkan peraturan iptables sendiri yang dinilai sebelum rantaian ufw. Oleh itu, kontena yang dimulakan dengan ports: ["3000:3000"] boleh dicapai dari internet walaupun ufw ditetapkan pada deny. Pertahanan ini bersifat struktur, bukan konfigurasi firewall: terbitkan port daripada Traefik sahaja, dan berikan networks: [proxy] kepada setiap kontena lain dan tiada apa-apa lagi. Jika sesuatu benar-benar perlu mencapai hos, ikat ke loopback — "127.0.0.1:3000:3000".

Troubleshooting: ralat yang akan anda lihat

404 page not found, dihantar oleh Traefik. Tiada router yang sepadan. Mengikut kebarangkalian: container kekurangan traefik.enable=true (dengan exposedByDefault=false ditetapkan); rule Host() tidak sepadan dengan nama yang anda taip; nama router dalam satu label berbeza dengan nama router dalam label lain (routers.gitea.rule dan routers.gitea.entrypoints mestilah perkataan yang sama); atau anda menggunakan tanda petik untuk hostname dan bukannya backticks. Traefik v3 memerlukan backticks di dalam matcher.

502 Bad Gateway. Router sepadan tetapi backend tidak dapat dicapai. Hampir sentiasa disebabkan container tidak berada dalam network proxy — semak docker inspect -f '{{json .NetworkSettings.Networks}}' gitea. Punca lain adalah loadbalancer.server.port yang salah: anda memberikan port yang telah diterbitkan, atau aplikasi mendengar pada port lain. Log menyatakan cubaan tersebut: dial tcp 172.18.0.5:8080: connect: connection refused.

Pelayar memberi amaran, dan sijil dikeluarkan kepada TRAEFIK DEFAULT CERT. Tiada sijil wujud untuk hostname tersebut dan Traefik menghantar placeholder self-signed. Baca baris ACME:

unable to obtain ACME certificate for domains "git.example.com" ...
acme: error: 400 ... DNS problem: NXDOMAIN looking up A for git.example.com

DNS belum menghala ke server tersebut. Betulkan rekod, tunggu tamatnya TTL, dan mulakan semula Traefik.

Invalid response from http://git.example.com/.well-known/acme-challenge/... pada HTTP challenge: port 80 tidak sampai ke Traefik dari luar — biasanya disebabkan firewall pada tahap penyedia VPS, bukan ufw.

Sijil tidak pernah dikeluarkan, dan DNS anda menggunakan Cloudflare dengan ikon awan oren diaktifkan. Cloudflare menamatkan TLS pada edge mereka dan TLS-ALPN-01 tidak dapat diselesaikan melaluinya. Tetapkan rekod kepada DNS-only semasa proses pengeluaran, atau tukar kepada DNS-01 challenge dengan token API. DNS-01 juga merupakan satu-satunya challenge yang mengeluarkan wildcard.

Redirect loop. Sesuatu di hadapan Traefik telah menamatkan TLS dan menghantar plaintext ke :80; redirect entrypoint menghantarnya semula ke HTTPS. Buang salah satu daripada dua redirect tersebut.

Mengekalkan operasi

Unit Docker mesti diaktifkan semasa but (systemctl is-enabled docker), dan restart: unless-stopped akan memulihkan stack selepas but semula. Untuk kawalan eksplisit, unit systemd kecil yang menjalankan docker compose -f /srv/edge/compose.yml up -d dengan RemainAfterExit=yes memberikan anda systemctl status edge dan kawalan susunan.

Tetapkan tag Traefik secara tetap (traefik:v3.5, jangan gunakan latest). Naik taraf v2-ke-v3 telah mengubah sintaks peraturan dan nama provider. latest yang tidak terkawal akan memuat semula konfigurasi yang tidak lagi difahami. Lakukan naik taraf secara sengaja: baca nota migrasi, kemas kini tag, docker compose up -d traefik, dan pantau log. Jika anda masih menggunakan tag v2, panduan migrasi Traefik v2 ke v3 menerangkan setiap penamaan semula, mod keserasian, dan proses rollback yang mengekalkan sijil anda.

Sandarkan ./letsencrypt dan setiap volum data aplikasi. Traefik tidak menyimpan sebarang keadaan lain yang tidak boleh dibina semula daripada fail compose.

Apa yang gagal pada skala besar

Had pertama bukanlah throughput, tetapi kotak tunggal: satu Traefik pada satu VPS adalah titik kegagalan tunggal (single point of failure) untuk lima aplikasi, dan acme.json adalah storan fail — dua instans Traefik yang menulis ke dalamnya akan merosakkan fail tersebut. Skalabiliti bermaksud memindahkan storan sijil keluar daripada fail, atau menamatkan TLS di tempat lain.

Had kedua ialah sambungan jangka panjang. Server-sent events, muat naik besar, dan klien yang perlahan akan terkena had masa respons (responding timeouts) pada entrypoint; --entryPoints.websecure.transport.respondingTimeouts.readTimeout serta rakan writeTimeout dan idleTimeout adalah tetapan yang boleh dilaraskan. WebSocket akan diteruskan tanpa konfigurasi tambahan.

Had ketiga ialah cakera. --accesslog=true menulis ke stdout, dan pemacu json-file Docker akan menyimpannya selama-lamanya kecuali jika dihadkan. Tetapkan logging.options.max-size pada servis Traefik, atau tulis log akses ke dalam fail dan lakukan rotasi.

Semua ini tidak memerlukan orkestrator. Ia memerlukan pelayan yang anda kawal, dengan IP sebenar dan port 80 serta 443 yang terbuka kepada dunia — satu VPS kecil sudah mencukupi untuk semua keperluan.

FAQ

Adakah saya masih memerlukan certbot jika saya menjalankan Traefik?

Tidak. ACME resolver Traefik meminta dan memperbaharui sijil bagi setiap hostname yang dihalakan, dan menyimpan semuanya dalam acme.json. Certbot kekal sebagai alat yang tepat apabila nginx atau pelayan lain menamatkan TLS sendiri; menjalankan kedua-duanya pada hostname yang sama hanya akan menghabiskan had kadar Let's Encrypt.

Mengapa kontena saya memulangkan 404 melalui Traefik?

404 yang dibekalkan oleh Traefik bermaksud tiada penghala (router) yang sepadan dengan permintaan tersebut. Pastikan kontena mempunyai traefik.enable=true (wajib sebaik sahaja exposedByDefault=false ditetapkan), nilai Host() sepadan dengan nama yang anda taip, dan nama penghala adalah identik pada setiap label untuk aplikasi tersebut. Traefik v3 juga memerlukan tanda backtick di dalam pemadan (matcher), bukan tanda petik.

Apakah perbezaan antara 404 dan 502 di sini?

404 bermaksud penghalaan tidak pernah berlaku; 502 bermaksud penghala telah sepadan tetapi backend menolak sambungan. Punca biasa 502 adalah kontena yang tidak disambungkan ke rangkaian proxy, dan loadbalancer.server.port yang menghala ke port yang diterbitkan (published port) dan bukannya port yang didengari oleh aplikasi di dalam kontena. Log akses akan menyatakan alamat tepat yang didail oleh Traefik.

Adakah memasang Docker socket secara read-only sudah mencukupi?

Bendera :ro menjadikan fail soket tersebut read-only, bukan API di belakangnya — permintaan POST masih dihantar melaluinya, dan akses API Docker adalah setaraf dengan root pada hos. Susunan yang lebih kuat adalah kontena docker-socket-proxy yang ditunjukkan di atas, yang hanya mendedahkan pembacaan kontena dan rangkaian kepada Traefik dan menyekat penulisan sepenuhnya.

Bolehkah Traefik mengeluarkan sijil wildcard?

Hanya melalui cabaran DNS-01, dengan token API untuk pembekal DNS anda. TLS-ALPN-01 dan HTTP-01 masing-masing mengesahkan satu hostname sahaja dan tidak boleh menghasilkan wildcard. DNS-01 juga merupakan penyelesaian apabila CDN seperti Cloudflare menamatkan TLS di hadapan VPS anda dan dua cabaran lain tidak pernah selesai.