Τι θα λέγατε να φιλοξενήσουμε σε έναν μόνο server, πολλαπλές ιστοσελίδες – web εφαρμογές, το καθένα με το δικό του domain, με SSL χωρίς να μπλέκουμε με ports και αλλαγές σε κάθε apache/nginx conf αλλά μόνο με έναν docker reverse proxy container;
Αν πάμε να στήσουμε σε έναν server όλες τις υπηρεσίες που έχουμε δει μέχρι στιγμής στα «Μαθήματα Docker«, θα ανακαλύψουμε ότι πρέπει να ανοίξουμε πολλές πόρτες στο router μας (ή στον VPS μας), αν θέλουμε να έχουμε πρόσβαση σε αυτές εκτός του τοπικού μας δικτύου. Αυτό από μόνο του είναι ένα τεράστιο πρόβλημα για την ασφάλεια του server μας, το οποίο πρέπει να αποφύγουμε και ένας εξαιρετικός τρόπος για να το κάνουμε αυτό είναι να στήσουμε έναν reverse proxy server. Σήμερα θα δούμε πώς γίνεται αυτό χρησιμοποιώντας το Traefik Proxy.
Πάμε να δούμε πώς λειτουργεί.
Όλη η σειρά οδηγών «Πως χρησιμοποιούμε το Docker βρίσκεται στον σύνδεσμο»:
Τι είναι ο reverse proxy server;
Μια γεύση του τι είναι proxy είχαμε δει όταν κάναμε Εγκατάσταση και ρύθμιση Squid Proxy Server.
Κάθε υπηρεσία που τρέχουμε στο Docker μας δίνει πρόσβαση μέσα από συγκεκριμένες πόρτες, οι οποίες για τις web εφαρμογές είναι συνήθως η 80 (http) και η 443 (https). Για παράδειγμα το Nextcloud χρειάζεται την 80 και την 443, το Home Assistant χρειάζεται την 8123 και το Portainer την 8000 και την 9000. Για να μην ανοίξουμε λοιπόν όλες αυτές τις πόρτες στο δίκτυό μας, μπορούμε να στήσουμε έναν reverse proxy server στην 80 και την 443, ο οποίος θα κατευθύνει τα αιτήματά μας στην υπηρεσία που ζητάμε.
Με απλά λόγια ο reverse proxy είναι ένας πορτιέρης! Στέκεται μπροστά στις ανοιχτές πόρτες, ελέγχει τα αιτήματα σύνδεσης και τα προωθεί στις σωστές υπηρεσίες. Αν για παράδειγμα γράψουμε στον browser ότι θέλουμε να μπούμε στο hass.doctorandoid.gr
, ο proxy θα μας στείλει στο Home Assistant, ενώ αν ζητήσουμε το next.doctorandroid.gr
, θα μας στείλει αντίστοιχα στο Nextcloud.
Οι σύγχρονοι reverse proxy όπως ο Traefik μπορούν επίσης να φέρουν πιστοποιητικά SSL για τις υπηρεσίες που εξυπηρετούν και να ασφαλίσουν τη σύνδεσή μας, ενώ διαθέτουν και load balancer σε περίπτωση που πολλοί χρήστες ζητήσουν ταυτόχρονα την ίδια υπηρεσία. Το καλύτερο από όλα όμως είναι ότι ειδικά ο Traefik Proxy είναι εύκολος στην εγκατάσταση και τη συντήρηση, κάτι που θα δούμε στην πράξη ευθύς αμέσως.

Προϋποθέσεις
Πριν ξεκινήσουμε όμως θα χρειαστεί να κάνουμε μία προεταοιμασία. Συγκεκριμένα θα πρέπει:
- να έχουμε εγκαταστήσει το Docker και το Docker Compose στο server μας,
- Αν ο server είναι πίσω από router πρέπει να έχουμε ανοίξει τις πόρτες 80 και 443 στο router και να τις έχουμε προωθήσει στη διεύθυνση IP του server μας,
- να έχουμε αποκτήσει το δικό μας domain (π.χ. doctorandroid.gr) και να έχουμε προωθήσει τα subdomain που θα χρησιμοποιήσουμε (π.χ. next.doctorandroid.gr, hass.doctorandroid.gr) στην δημόσια IP που βρίσκεται ο server μας.
- Αν το τρέχουμε σε ένα φθηνό server (π.χ. στην DigitalOcean) θα πρέπει να έχουμε κάνει τις ανάλογες ενέργειες για προώθηση στην IP που μας δίνει.
Pro tip:Όσον αφορά την προώθηση των subdomain στο δίκτυό μας, μπορούμε να τα κάνουμε όλα με μία καταχώρηση και να μην ασχοληθούμε ξανά μαζί τους. Μπαίνουμε στη σελίδα του registrar από τον οποίο αγοράσαμε το domain (π.χ. namecheap) και δημιουργούμε ένα «catch all» A record δίνοντας ως Host την διεύθυνση που θέλουμε με το αστεράκι μπροστά π.χ. *.doctorandroid.gr. Με αυτόν τον τρόπο, όποιο subdomain και αν χρειάζεστε δεν θα ασχολείστε με την προώθησή του.
Ρυθμίζουμε το Traefik Proxy
Ο Traefik Proxy χρειάζεται δύο αρχεία ρυθμίσεων για να λειτουργήσει. Το «traefik.toml» διαθέτει τις στατικές ρυθμίσεις της υπηρεσίας και το «traefik_dynamic.toml» τις δυναμικές. Δημιουργούμε και ανοίγουμε το πρώτο με τον αγαπημένο μας editor (π.χ. VIM):
vim traefik.toml
Κάνουμε αντιγραφή και επικόλληση τα ακόλουθα και αποθηκεύουμε το αρχείο μας:
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.web.http.redirections.entryPoint]
to = "websecure"
scheme = "https"
[entryPoints.websecure]
address = ":443"
[api]
dashboard = true
[certificatesResolvers.lets-encrypt.acme]
email = "to-email-mas@gmail.gr"
storage = "acme.json"
[certificatesResolvers.lets-encrypt.acme.tlsChallenge]
[providers.docker]
watch = true
network = "web"
[providers.file]
filename = "traefik_dynamic.toml"
Με λίγα λόγια αυτό που κάναμε με το συγκεκριμένο αρχείο είναι το εξής:
- [entrypoints]: Δηλώσαμε ότι ο proxy μας θα παρακολουθεί την πόρτα 80 (web) και την 443 (websecure) για νέα αιτήματα, ενώ ρυθμίσαμε επίσης τα αιτήματα «http» να προωθούνται αυτόματα σε «https» ().
- [api]: Ενεργοποιήσαμε το «Dashboard» (γραφικό περιβάλλον) του Traefik.
- [certificatesResolvers.lets-encrypt.acme]: Ορίσαμε το αρχείο «acme.json» στο οποίο θα αποθηκεύονται οι πληροφορίες των πιστοποιητικών SSL που θα λαμβάνουν οι υπηρεσίες μας από το «Let’s Encrypt». Εκεί δηλώσαμε επίσης και το email μας (π.χ. «to-email-mas@gmail.gr») για τυχόν μελλοντικές ειδοποιήσεις σχετικά με τα SSL μας.
- [providers.docker]: Ενημερώσαμε τον proxy μας να εξυπηρετεί τα Docker container που είναι συνδεδεμένα στο δίκτυο «web», το οποίο θα δημιουργήσουμε στη συνέχεια.
- [providers.file]: Δηλώσαμε ότι οι δυναμικές ρυθμίσεις του Traefik βρίσκονται στο αρχείο «traefik_dynamic.toml».
Πάμε τώρα να δημιουργήσουμε το αρχείο «traefik_dynamic.toml». Εκεί περιέχονται και οι ρυθμίσεις για το «Dashboard» του Traefik, μέσα από το οποίο μπορούμε να βλέπουμε με μια ματιά την υγεία των υπηρεσιών μας. Θα χρειαστούμε ένα username (π.χ. «doctor») και ένα password για να το ασφαλίσουμε, αλλά θα πρέπει πρώτα να το κρυπτογραφήσουμε, κάτι που γίνεται ως εξής:
sudo apt install apache2-utils
htpasswd -nb doctor to-password-mas
Το τερματικό θα μας απαντήσει με τον κρυπτογραφημένο κωδικό ως εξής:
doctor:$apr1$q9cQzs5M$VsAyJGznYd1HTNY8qiI7C.
Δημιουργούμε και ανοίγουμε το traefik_dynamic.toml με τον αγαπημένο μας editor (π.χ. VIM):
vim traefik_dynamic.toml
Κάνουμε αντιγραφή και επικόλληση τα ακόλουθα και το αποθηκεύουμε:
[http.middlewares.simpleAuth.basicAuth]
users = [
"doctor:$apr1$q9cQzs5M$VsAyJGznYd1HTNY8qiI7C."
]
[http.routers.api]
rule = "Host(`monitor.doctorandroid.gr`)"
entrypoints = ["websecure"]
middlewares = ["simpleAuth"]
service = "api@internal"
[http.routers.api.tls]
certResolver = "lets-encrypt"
Αυτό που κάναμε με το συγκεκριμένο αρχείο είναι το εξής:
- [http.middlewares.simpleAuth.basicAuth]: Ορίσαμε ένα username και ένα password για το «Dashboard» του Traefik (είναι αυτό που δημιουργήσαμε πριν με το «htpasswd»).
- [http.routers.api]: Ορίσαμε τη διεύθυνση από την οποία θα μπαίνουμε στο «Dashboard» (monitor.doctorandroid.gr). Κάναμε επίσης αυτόματη προώθηση όλων των αιτημάτων από την πόρτα 80 στην ασφαλή 443 (websecure).
- [http.routers.api.tls]: Τέλος ενημερώσαμε τον proxy μας ότι τα πιστοποιητικά SSL θα έρχονται από την υπηρεσία «Let’s Encrypt».
Δημιουργούμε το Docker container του Traefik Proxy
Είπαμε πριν ότι τα container μας θα τρέχουν στο δίκτυο «web» του Docker. Ας το δημιουργήσουμε με την εντολή:
docker network create web
Δημιουργούμε επίσης το αρχείο «acme.json» και του δίνουμε τα σωστά δικαιώματα στο σύστημα με τις εντολές:
touch acme.jsonchmod 600 acme.json
Η εντολή για σηκώσουμε το container του Traefik Proxy στο Docker είναι η εξής:
docker run -d \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ./traefik.toml:/traefik.toml \
-v ./traefik_dynamic.toml:/traefik_dynamic.toml \
-v ./acme.json:/acme.json \
-p 80:80 \
-p 443:443 \
--network web \
--name traefik \
traefik:v2.2
Εναλλακτικά, την παραπάνω εντολή μπορούμε να την μετατρέψουμε σε docker-compose όπως μάθαμε στο σχετικό μάθημα Πώς να χρησιμοποιήσετε το Docker (Μέρος 4 – Docker Compose). Φτιάχνουμε λοιπόν το αρχείο docker-compose-yml
και του προσθέτουμε τα παρακάτω:
version: '3.3'
services:
traefik:
volumes:
- '/var/run/docker.sock:/var/run/docker.sock'
- './traefik.toml:/traefik.toml'
- './traefik_dynamic.toml:/traefik_dynamic.toml'
- './acme.json:/acme.json'
ports:
- "80:80"
- "443:443"
network_mode: web
container_name: traefik
image: traefik:v2.2
Αποθηκεύουμε και το τρέχουμε με docker-compose up
.
Δε χρειάζεται να εξηγήσουμε τι κάνει η συγκεκριμένη εντολή, καθώς είμαστε πλέον έμπειροι από τα προηγούμενα «Μαθήματα Docker«. Τώρα που το Traefik είναι έτοιμο, μπορούμε να μπούμε στην υπηρεσία από τη διεύθυνση (είχαμε δηλώσει το domain στο [http.routers.api]):
https://monitor.doctorandroid.gr/dashboard/
Συνδέουμε το Heimdall στο Traefik Proxy
Αφού τελειώσαμε με τον proxy server μας, ήρθε επιτέλους η στιγμή να συνδέσουμε τις υπηρεσίες μας επάνω του. Το μόνο που θα χρειαστεί να κάνουμε είναι να προσθέσουμε κάποιες ετικέτες και κάποια δίκτυα στο αρχείο Docker Compose της κάθε υπηρεσίας.
Θα δούμε μία απλή υπηρεσία όπως το Heimdall, αλλά και μία εξαρτώμενη από βάση δεδομένων όπως το Nextcloud για να καλύψουμε όλες τις περιπτώσεις.
Ξεκινάμε με το Heimdall και δημιουργούμε το αρχείο «docker-compose.yml» με την εντολή:
vim docker-compose.yml
Κάνουμε αντιγραφή και επικόλληση τα εξής:
version: '3'
services:
heimdall:
container_name: heimdall
environment:
- PUID=1000
- PGID=1000
- TZ=europe/athens
volumes:
- ./heimdall:/config
labels:
- traefik.http.routers.heimdall.rule=Host(`heimdall.doctorandroid.gr`)
- traefik.http.routers.heimdall.tls=true
- traefik.http.routers.heimdall.tls.certresolver=lets-encrypt
- traefik.port=80
networks:
- internal
- web
restart: unless-stopped
image: linuxserver/heimdall
networks:
web:
external: true
internal:
external: false
Αυτό που αλλάξει εδώ σε σχέση με το επίσημο Docker Compose αρχείο του Heimdall είναι το εξής:
- Αφαιρέσαμε τα «ports» καθώς τη διαχείρισή τους θα την κάνει πλέον ο proxy.
- Προσθέσαμε τα «networks» στα οποία θα συνδεθεί το container μας. Το «web» είναι το βασικό δίκτυο του proxy, ενώ με το «internal» το container μας μπορεί να επικοινωνεί με άλλα container (δε χρειάζεται στην συγκεκριμένη περίπτωση).
- Προσθέσαμε τα απαραίτητα «labels» στα οποία δηλώσαμε τη διεύθυνση μέσα από την οποία θα έχουμε πρόσβαση (
heimdall.doctorandroid.gr
), καθώς και ότι θέλουμε ένα SSL πιστοποιητικό από το «Let’s Encrypt» για αυτήν.
Το αποθηκεύουμε και το τρέχουμε με την εντολή:
docker-compse up -d
Πλέον μπορούμε να μπούμε στην υπηρεσία του Heimdall από τη διεύθυνση:
https://heimdall.doctorandroid.gr
Συνδέουμε το Nextcloud στο Traefik proxy
Πάμε να ρίξουμε και μια ματιά στην περίπτωση του Nextcloud, το οποίο χρειάζεται και μία βάση δεδομένων για να λειτουργήσει. Στην περίπτωση αυτή θέλουμε να στείλουμε στον proxy μόνο την υπηρεσία του Nextcloud και όχι τη βάση δεδομένων, η οποία θέλουμε να λειτουργεί τοπικά.
Δημιουργούμε το αρχείο «docker-compose.yml» με την εντολή:
vim docker-compose.yml
Κάνουμε αντιγραφή και επικόλληση τα εξής:
version: '3'
services:
db:
image: mariadb
container_name: nextcloud_db
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --innodb-file-per-table=1 --skip-innodb-read-only-compressed
restart: always
volumes:
- ./database:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_PASSWORD=password
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=doctor
networks:
- internal
labels:
- traefik.enable=false
app:
image: nextcloud
container_name: nextcloud_app
restart: always
labels:
- traefik.http.routers.cloud.rule=Host(`cloud.doctorandroid.gr`)
- traefik.http.routers.cloud.tls=true
- traefik.http.routers.cloud.tls.certresolver=lets-encrypt
- traefik.port=80
networks:
- internal
- web
volumes:
- ./html:/var/www/html
depends_on:
- db
networks:
web:
external: true
internal:
external: false
Σε αυτό το αρχείο βλέπουμε δύο υπηρεσίες, το db
που είναι η βάση δεδομένων και το app
το οποίο είναι η εφαρμογή του Nextcloud. Το db
λοιπόν δε θέλουμε να έχει πρόσβαση στο ίντερνετ οπότε το προσθέσαμε μόνο στο internal
network και όχι στο web
. Με το labels: traefik.enable=false
λέμε επίσης στο Traefik να μην το λαμβάνει υπ’ όψη.
Αντίθετα στο app
προσθέσαμε τα labels
και networks
που χρησιμοποιήσαμε και στο Heimdall (εδώ το «internal» είναι απαραίτητο για να επικοινωνεί το app
με το db
). Η διαφορά εδώ είναι ότι προσθέσαμε επίσης και το depends_on: db
, το οποίο σημαίνει ότι το app
δεν μπορεί να λειτουργήσει χωρίς το db
, κάτι το οποίο είναι λογικό.
Το αποθηκεύουμε και το τρέχουμε με την εντολή:
docker-compse up -d
Πλέον μπορούμε να μπούμε στην υπηρεσία του nextcloud από τη διεύθυνση:
https://cloud.doctorandroid.gr
Στην οθόνη ρυθμίσεων που θα μας ζητήσει να συμπληρώσουμε, δίνουμε τα συνθηματικά και τον χρήστη της MySQL που γράψαμε στο αρχείο docker compose, ενώ στην διεύθυνση της βάσης δεδομένων δίνουμε το όνομα της υπηρεσίας που την τρέχει (στο παράδειγμά μας το όνομα είναι ‘db’)
Εν κατακλείδι
Η πρώτη προτεραιότητα σε έναν server είναι πάντα η ασφάλεια. Όσο βολικό και να είναι το self-hosting με το Docker, δεν μπορείς να ανοίγεις πόρτες στο δίκτυό σου για κάθε υπηρεσία. Αργά ή γρήγορα λοιπόν ένας reverse proxy server θα σου χρειαστεί και αυτός που μας προσφέρει η Traefik Labs είναι εξαιρετικά εύκολος στην εγκατάσταση, ενώ δε χρειάζεται σχεδόν καθόλου συντήρηση.
Προσθέστε σε αυτά το γεγονός ότι είναι ανοιχτού κώδικα και ότι διαθέτει ένα πλήρες documentation, σε περίπτωση που θέλουμε να κάνουμε κάτι πιο εξεζητημένο στο μέλλον. Περισσότερα για τον Traefik Proxy θα βρείτε στην επίσημη σελίδα του.
crosspost doctorandroid.gr
Πολύ καλό και χρήσιμο.
Στην περίπτωση μου, δεν μου λειτούργησε με το label:
– traefik.port=80
Επρεπε να το αλλάξω σε:
– traefik.http.services.cloud.loadbalancer.server.port=80