SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-29

Cara Jalankan 5 Aplikasi Dengan Traefik Docker Compose

Ketahui cara mengurus lima aplikasi di bawah satu IP menggunakan Traefik v3. Kami tunjukkan konfigurasi Host rule, TLS automatik, dan cara elak ralat fail acme.json.

Satu IP, lima aplikasi, satu port 443

VPS anda mempunyai satu alamat IPv4 awam dan satu port TCP 443. Anda mahu menjalankan Gitea, salinan staging aplikasi anda, papan pemuka dalaman, halaman status dan penerima webhook di atasnya; lima nama hos, satu pelayan. Reverse proxy ialah proses yang menguasai port 80 dan 443, membaca header Host pada setiap permintaan, lalu menyerahkannya kepada container yang betul. Traefik melakukan perkara ini, serta memperoleh dan memperbaharui sijil untuk setiap nama hos tanpa anda perlu menjalankan certbot secara manual. Nginx dan Caddy juga boleh menamatkan kelima-lima nama hos tersebut dengan sempurna, jadi jika pilihan masih terbuka, adalah berbaloi untuk menimbang ketiga-tiga proksi tersebut dari segi pengendalian sijil dan kos konfigurasi bagi setiap aplikasi sebelum anda menyambungkan segala-galanya kepada salah satu daripadanya.

Perkara yang membezakan Traefik daripada blok server {} nginx ialah punca konfigurasinya. Dengan nginx, anda menyunting fail dan memuat semula, manakala kitaran hayat sijil kekal sebagai tugasan berasingan, iaitu aliran kerja yang anda ikuti apabila anda mengeluarkan sijil Let's Encrypt dengan certbot pada nginx, di mana pemasa pembaharuan wujud di luar pelayan web sepenuhnya. Penyedia Docker bagi Traefik memantau aliran acara Docker dan membaca label daripada container anda: mulakan container yang membawa label peraturan Host() dan ia boleh dihalakan dalam masa satu saat; hentikannya dan laluan tersebut akan hilang. Itu juga merupakan perangkapnya. Konfigurasi yang berada dalam label wujud di lima tempat serentak, dan label yang salah tidak akan mengeluarkan amaran; container tersebut hanya tidak dihalakan, dan Traefik tidak memberikan sebarang maklum balas.

Empat kata nama

  • Entrypoints ialah soket yang mendengar trafik. Anda akan mentakrifkan dua: web pada :80 dan websecure pada :443.
  • Routers memadankan permintaan (Host(...)) dan menyambungkannya kepada servis. Sijil diminta bagi setiap router, melalui tls.certresolver.
  • Services ialah bahagian backend, iaitu kontena dan port yang didengarinya di dalam rangkaian Docker.
  • Middlewares terletak di antara router dan service: basic auth, senarai putih IP, penulisan semula header, dan redirect.

Empat kata nama ini ialah istilah Traefik bagi tugasan yang biasanya anda tulis secara manual: router ialah server_name, service ialah sasaran proxy_pass, dan middlewares ialah arahan header serta auth yang anda tetapkan sendiri apabila anda membina blok pelayan reverse proxy nginx baris demi baris.

Konfigurasi statik (entrypoints, providers, ACME) dihantar melalui baris perintah Traefik atau dalam traefik.yml, dan mengubahnya bermakna anda perlu memulakan semula Traefik. Konfigurasi dinamik (routers, services, middlewares) diperoleh daripada label kontena dan dimuatkan semula secara langsung (hot-reloaded). Mengelirukan kedua-duanya adalah punca biasa mengapa "flag saya tidak berfungsi".

Fail compose

Satu rangkaian Docker berkongsi bernama proxy menjadi tulang belakang. Traefik hanya boleh mencapai kontena jika kedua-duanya berada pada 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 blok gitea dengan nama router sendiri, Host() sendiri, dan port dalaman sendiri. Pemasangan Nextcloud yang berjalan dalam Docker dengan TLS dan sandaran boleh dimasukkan dengan cara yang sama; buang port yang diterbitkan, lampirkannya pada proxy, dan biarkan label router menguruskan nama hos serta sijil.

Lima perincian di sini sangat penting.

exposedByDefault=false menjadikan kontena tidak kelihatan kepada Traefik sehingga ia membawa traefik.enable=true. Jika ditinggalkan, setiap kontena yang anda mulakan, termasuk postgres sementara yang anda jalankan untuk memeriksa sesuatu, akan mempunyai laluan yang dijana untuknya.

providers.docker.network=proxy memberitahu Traefik rangkaian mana yang perlu digunakan apabila sesuatu kontena dilampirkan pada beberapa rangkaian. Jika ditinggalkan, Traefik mungkin memilih IP kontena yang salah, yang akan muncul sebagai ralat 502 yang kelihatan seperti kegagalan aplikasi.

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

Redirect pada entrypoint web menukarkan permintaan teks biasa kepada 308 ke HTTPS. Port 80 tetap dibuka: cabaran ACME HTTP memerlukannya, begitu juga pengguna yang menaip nama hos tanpa protokol.

$$ yang digandakan dalam hash basic-auth adalah cara Compose melakukan escaping, bukan kesilapan taip. Janakannya 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 443 dan Traefik menjawab cabaran tersebut di dalam jabat tangan TLS. Alternatifnya ialah HTTP-01, pada port 80; gantikan baris tlschallenge dalam senarai command: Traefik dengan dua baris ini:

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

Kedua-duanya berfungsi. Kedua-duanya memerlukan DNS awam untuk hostname tersebut sudah menghala ke VPS anda, supaya pihak berkuasa sijil dapat meleraikan 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 sering 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 pengguna lain, Traefik akan mencetak 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 kemas adalah seperti di atas: lakukan bind-mount pada direktori tersebut dan biarkan Traefik mencipta fail itu sendiri dengan mod yang betul. Jika anda mencipta acme.json dengan touch, umask anda mungkin menetapkannya kepada 644. Baiki pada hos:

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

Buat sandaran direktori tersebut bersama volum aplikasi anda. Kehilangannya masih boleh diatasi kerana sijil boleh dikeluarkan semula, tetapi mengeluarkan semula lima hostname serentak akan menyebabkan anda terkena had kadar (rate limits).

Gunakan CA staging semasa anda melakukan iterasi. Nyahkomen baris caserver, pastikan setiap laluan berfungsi, kemudian komen semula dan padam acme.json supaya sijil pengeluaran (production) diminta sebagai baharu. Let's Encrypt pengeluaran membenarkan lima sijil pendua seminggu untuk set hostname yang sama, dan akan menyekat pengesahan gagal yang berulang untuk nama yang sama. Staging mengeluarkan sijil yang tidak dipercayai, pelayar anda akan memberi amaran, dan amaran itu adalah isyarat bahawa ia berfungsi, dengan had yang jauh lebih longgar.

Papan pemuka ialah permukaan kawalan, bukan demo

Kebanyakan panduan permulaan menetapkan --api.insecure=true, yang menghidangkan papan pemuka pada port 8080 tanpa pengesahan. Pada pelayan dengan IP awam, ini mendedahkan topologi penghalaan, nama hos, nama middleware dan port bahagian belakang anda kepada sesiapa sahaja yang mengimbasnya.

Label pada servis traefik di atas adalah alternatifnya: papan pemuka dihalakan seperti aplikasi lain, pada nama hos sebenar, melalui TLS, di sebalik basicauth. service=api@internal ialah perkara yang menghubungkan penghala kepada API terbina dalam Traefik. Perketatkan lagi dengan merantaikan senarai benarkan IP, yang digunakan dari kiri ke kanan. Jika alamat pejabat anda dinamik, tetapkan julat kepada subnet yang diberikan oleh VPN WireGuard yang anda hos sendiri pada VPS yang sama dan capai papan pemuka hanya melalui terowong tersebut:

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

Satu kata laluan basicauth yang dikongsi tidak lagi selamat apabila lima aplikasi memerlukan akaun masing-masing, dan slot middleware yang sama boleh menerima forwardauth yang menyerahkan keputusan kepada Authentik, pelayan daftar masuk tunggal yang dihoskan sendiri, supaya papan pemuka dan setiap laluan di sebelahnya berada di sebalik satu log masuk yang boleh anda batalkan di satu tempat sahaja.

Soket Docker adalah root

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

Kekalkan :ro pada mount tersebut, tetapi fahami perkara yang diperoleh: ia menjadikan fail soket tersebut sebagai baca-sahaja (read-only). Ia tidak menghalang permintaan POST kepada API Docker yang dihantar melaluinya. Mitigasi sebenar adalah dengan tidak memberikan soket tersebut kepada Traefik, dan meletakkan proksi 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

Gugurkan volum soket daripada Traefik dan halakan penyedia (provider) kepada proksi tersebut:

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

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

Firewall, port, dan peraturan yang sering disalah faham

Dua port dibuka, berserta 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 miliknya sendiri, yang dinilai sebelum rantaian ufw. Oleh itu, kontena yang dimulakan dengan ports: ["3000:3000"] boleh dicapai dari internet walaupun terdapat peraturan deny pada ufw. Pertahanannya adalah bersifat struktur, bukan konfigurasi firewall: terbitkan port hanya daripada Traefik, dan berikan setiap kontena lain networks: [proxy] dan tiada yang lain. Jika sesuatu servis benar-benar perlu mencapai hos, ikat servis tersebut pada loopback, "127.0.0.1:3000:3000".

Penyelesaian masalah: ralat yang akan anda temui

404 page not found, dihidangkan oleh Traefik. Tiada router yang sepadan. Mengikut kebarangkalian: bekas (container) kekurangan traefik.enable=true (dengan exposedByDefault=false ditetapkan); peraturan Host() tidak sepadan dengan nama yang anda taip; nama router dalam satu label berbeza daripada nama router dalam label lain (routers.gitea.rule dan routers.gitea.entrypoints mestilah perkataan yang sama); atau anda membungkus nama hos dalam tanda petikan dan bukannya backtick. Traefik v3 memerlukan backtick di dalam pemadan (matcher).

502 Bad Gateway. Router sepadan tetapi backend tidak dapat dicapai. Hampir selalu disebabkan bekas tidak berada pada rangkaian proxy, semak docker inspect -f '{{json .NetworkSettings.Networks}}' gitea. Punca lain ialah loadbalancer.server.port yang salah: anda memberikan port yang diterbitkan, atau aplikasi mendengar pada port lain. Log menamakan percubaan 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 nama hos tersebut dan Traefik menghidangkan sijil placeholder yang ditandatangani sendiri (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 pelayan tersebut. Betulkan rekod, tunggu tempoh TTL tamat, dan mulakan semula Traefik.

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

Sijil tidak pernah dikeluarkan, dan DNS anda berada di Cloudflare dengan awan jingga diaktifkan. Cloudflare menamatkan TLS di pinggirnya dan TLS-ALPN-01 tidak dapat diselesaikan melaluinya. Tetapkan rekod kepada DNS-only semasa pengeluaran sijil, atau beralih kepada cabaran DNS-01 dengan token API. DNS-01 juga merupakan satu-satunya cabaran yang mengeluarkan sijil wildcard.

Redirect loop. Sesuatu di hadapan Traefik sudah menamatkan TLS dan menghantar teks biasa ke :80; pengalihan (redirect) entrypoint menghantarnya semula ke HTTPS. Buang salah satu daripada dua pengalihan tersebut.

Memastikan ia terus berjalan

Unit Docker mestilah didayakan semasa but (systemctl is-enabled docker), dan restart: unless-stopped akan mengembalikan stack selepas but semula. Untuk pengendalian yang 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 (traefik:v3.5, jangan gunakan latest). Naik taraf daripada v2 ke v3 telah mengubah sintaks peraturan dan nama penyedia, dan latest yang tidak dipantau akan memuatkan semula konfigurasi yang tidak lagi difahaminya dengan senang hati. Lakukan naik taraf secara sengaja: baca nota migrasi, kemas kini tag, docker compose up -d traefik, dan perhatikan log. Jika anda masih menggunakan tag v2, panduan migrasi Traefik v2 ke v3 akan membimbing anda melalui setiap penamaan semula, mod keserasian, dan cara rollback yang mengekalkan sijil anda.

Buat sandaran ./letsencrypt dan setiap volum data aplikasi. Traefik tidak menyimpan sebarang keadaan lain yang tidak boleh anda bina semula daripada fail compose.

Perkara yang tergendala pada skala besar

Had pertama bukanlah daya pemprosesan, tetapi pelayan tunggal: satu Traefik pada satu VPS merupakan titik kegagalan tunggal untuk lima aplikasi, dan acme.json ialah storan fail rata, dua instans Traefik yang menulis kepadanya akan menyebabkan kerosakan data. Penskalaan keluar bermaksud memindahkan storan sijil keluar daripada fail, atau menamatkan TLS di tempat lain.

Had kedua ialah sambungan jangka hayat panjang. Server-sent events, muat naik bersaiz besar dan klien yang perlahan akan bertemu dengan tamat masa respons entrypoint; --entryPoints.websecure.transport.respondingTimeouts.readTimeout serta rakan-rakannya writeTimeout dan idleTimeout ialah kawalan yang perlu dilaraskan. WebSockets akan melaluinya tanpa memerlukan 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 access log ke dalam fail dan lakukan penggiliran (rotate) fail tersebut.

Tiada satu pun daripada ini memerlukan orkestrator. Ia hanya memerlukan pelayan yang anda kawal, dengan IP sebenar serta port 80 dan 443 yang dibuka kepada dunia; satu VPS kecil sudah memadai sebagai keseluruhan senarai dependensi.

FAQ

Adakah saya masih memerlukan certbot jika saya menjalankan Traefik?

Tidak. ACME resolver milik Traefik meminta dan memperbaharui sijil untuk setiap hostname yang dihalanya, serta menyimpan kesemuanya di dalam acme.json. Certbot kekal sebagai alat yang tepat apabila nginx atau pelayan lain melakukan TLS termination sendiri; menjalankan kedua-duanya untuk hostname yang sama hanya akan menghabiskan had kadar (rate limits) Let's Encrypt.

Mengapa container saya memulangkan ralat 404 melalui Traefik?

Ralat 404 yang diberikan oleh Traefik bermaksud tiada router yang sepadan dengan permintaan tersebut. Pastikan container mempunyai traefik.enable=true (wajib apabila exposedByDefault=false ditetapkan), nilai Host() sepadan dengan nama yang anda taip, dan nama router adalah sama pada setiap label untuk aplikasi tersebut. Traefik v3 juga memerlukan backtick di dalam matcher, bukannya tanda petikan.

Apakah perbezaan antara 404 dan 502 dalam konteks ini?

Ralat 404 bermaksud penghalaan tidak pernah berlaku; ralat 502 bermaksud router telah sepadan tetapi backend menolak sambungan tersebut. Punca biasa bagi 502 ialah container tidak disambungkan ke rangkaian proxy, atau loadbalancer.server.port menghala ke port yang diterbitkan (published port) dan bukannya port yang didengar oleh aplikasi di dalam container. Access log akan menamakan alamat tepat yang dihubungi oleh Traefik.

Adakah memasang Docker socket sebagai read-only sudah memadai?

Flag :ro menjadikan fail socket sebagai read-only, bukan API di sebaliknya. Permintaan POST masih boleh dihantar melaluinya, dan akses kepada Docker API adalah setara dengan akses root pada host. Susunan yang lebih kukuh ialah container docker-socket-proxy seperti yang ditunjukkan di atas, yang hanya mendedahkan bacaan container dan rangkaian kepada Traefik serta menyekat penulisan sepenuhnya.

Bolehkah Traefik mengeluarkan wildcard certificate?

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