Opisuję sposób na uruchomienie skonteneryzowaną instancję mastodonową, która będzie funkcjonować pod własną domeną. Na końcu szybkie podsumowanie - reflekcja, czy warto to robić.
https://bobiko.blog/2022/11/mastodon-mam-i-ja/ ↗
Zastrzeżenie
Posiadanie własnej instancji wynika z ambicji w posiadaniu własnego kawałka w sieci - nie jest ona przymusem a opcją, którą daje Fediwersum.
Niemniej, nie polecam tego kroku osobom, dopiero co zaczynającym przygodę z Mastodonem. Trzeba mieć świadomość, że posiadanie własnego kawałka utrudni odkrywanie ciekawych tematów, dotarcie do wartościowego contentu, wygenerowanego w ramach tejże federacji. Jak to w życiu bywa, na wszystko trzeba mieć czas, trzeba być cierpliwym w oczekiwaniu na pierwsze efekty.
Reasumując:
-
Celem tego postu jest dzielenie się zdobytym doświadczeniem z innymi w stawianiu mastodona na Dockerze.
-
Na początek warto mieć konto na popularnej instancji (zorientowanej np. na język, zainteresowania czy zagadnienia techniczne) i rozpocząć interakcję (nawiązanie relacji, obserwacje, lajkowanie czy podbicie etc.)
Po co?
Poza oczywistym - nowe zabawki - powody, dla których chcę mieć własną instancję mastodonową:
-
Wyższy poziom weryfikacji - jest to moje autentyczne konto;
-
Większa kontrola nad własnym kawałkiem social-media, działającymi w ramach federacji ;
-
Większa świadomość w serwowaniu informacji ze światem
-
Niektóre instancje - szczególnie na fali popularności przypływu z twittera
#ExiTT
- działają dużo wolniej (a nawet blokują rejestrację) -
Masz pewność, że nikt nie zabierze ci konta
-
Chyba że zrobi to za siebie dostawca VPS ¯_(ツ)_/¯
-
Chęć poznania możliwości działania instancji od strony technicznej
-
Szczególnie stawianie kontenerów dockerowych na VPS
-
utrzymanie “produkcji” i ewentualne upgrade’y
-
W ramach powyższego eksperymentu chcę utrwalić zdobytą wiedzę oraz zniwelować braki, które gdzieś tam wyjdą
-
nie muszę dodawać, ze
done is better than perfect
to najlepsza droga rozwoju?
Konfiguracja
Wstępne informacje
Do uruchomienia własnej instancji potrzebne są
-
Wiedza w zakresie:
-
podstawowej obsługi dockera i nginx
-
podstawowej obsługi terminala i linuksa
-
Skonfigurowany serwer VPS z zainstalowanymi pakietami
docker
docker-compose
nginx
-
działająca usługa DNS z domeną - polecam tutaj Cloudflare - The Web Performance & Security Company | Cloudflare ↗
-
działająca usługa SMTP - z rodzimego rynku polecam EmailLabs - email API i SMTP w chmurze cloud. Best e-mail delivery service ↗ (darmowy plan w zupełności wystarczy)
W moim przypadku posłużyłem się VPS-em od MIKR.US - VPSy dla pasjonatów ↗ (uwaga: to reflink; osoba, która skorzysta z linka, dostaje 1 miesiąc usługi gratis) po upewnieniu się, że nie ma żadnych przeszkód ↗ w uruchomieniu własnej instancji. Co do wiedzy, to fajnym uzupełnieniem / powtórką są kursy, które serwuje Kuba Mrugalski w ramach swojej platformy szkoleniowej ↗.
1. Konfiguracja docker-compose.yml
Mastodon w repozytorium ↗ ma przygotowane konfigurację dla dockera, które można skopiować do innego katalogu i uruchomić cały proces.
# ściągnięcie repo
cd ~
git clone https://github.com/mastodon/mastodon.git
cd mastodon
latest=$(git describe --tags `git rev-list --tags --max-count=1`) git checkout $lastest -b ${latest}-branch
2. Konfiguracja `docker-compose.yml
Jak słusznie zauważył Ben ↗ warto zmodyfikować docker-compose.yml
, który pomija konieczność budowania lokalnego obrazu, przechodząc bezposednio do opublikowanego w sieci. Na ten moment (tj. 23/11/2022) jest dostepna wersja tootsuite/mastodon:v4.0
.
version: '3'
services:
db:
restart: always
image: postgres:14-alpine
shm_size: 256mb
networks:
- internal_network
healthcheck:
test: ['CMD', 'pg_isready', '-U', 'postgres']
volumes:
- ./postgres14:/var/lib/postgresql/data
environment:
- 'POSTGRES_HOST_AUTH_METHOD=trust'
redis:
restart: always
image: redis:7-alpine
networks:
- internal_network
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
volumes:
- ./redis:/data
es:
restart: always
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4
environment:
- "ES_JAVA_OPTS=-Xms512m -Xmx512m -Des.enforce.bootstrap.checks=true"
- "xpack.license.self_generated.type=basic"
- "xpack.security.enabled=false"
- "xpack.watcher.enabled=false"
- "xpack.graph.enabled=false"
- "xpack.ml.enabled=false"
- "bootstrap.memory_lock=true"
- "cluster.name=es-mastodon"
- "discovery.type=single-node"
- "thread_pool.write.queue_size=1000"
networks:
- external_network
- internal_network
healthcheck:
test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"]
volumes:
- ./elasticsearch:/usr/share/elasticsearch/data
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
ports:
- '127.0.0.1:9200:9200'
web:
# build: .
image: tootsuite/mastodon
restart: always
env_file: .env.production
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
networks:
- external_network
- internal_network
healthcheck:
# prettier-ignore
test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1']
ports:
- '127.0.0.1:3000:3000'
depends_on:
- db
- redis
- es
volumes:
- ./public/system:/mastodon/public/system
streaming:
#build: .
image: tootsuite/mastodon
restart: always
env_file: .env.production
command: node ./streaming
networks:
- external_network
- internal_network
healthcheck:
# prettier-ignore
test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1']
ports:
- '127.0.0.1:4000:4000'
depends_on:
- db
- redis
sidekiq:
#build: .
image: tootsuite/mastodon
restart: always
env_file: .env.production
command: bundle exec sidekiq
depends_on:
- db
- redis
networks:
- external_network
- internal_network
volumes:
- ./public/system:/mastodon/public/system
healthcheck:
test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"]
networks:
external_network:
internal_network:
internal: true
Informacyjne:
Powyższa konfiguracja uruchamia ElasticSearch do wyszukiwania kontekstu na swoim mastodontowym serwerze. Jest ona domyślnie ukryta z pewnych technicznych powodów, więc jeśli nie potrzebujemy, to zakomentujmy sekcję es
wraz z zależnościami w depends_on
.
Konfiguracja PostgreSQL
Przygotowujemy bazę danych, tworząc odpowiednią rolę (konto).
Warto zwrócić uwagę na wersję PostgreSQL w konfiguracji (image: postgres:14-alpine
), gdyż do niej będziemy się odwoływać bezpośrednio.
W pierwszej kolejności generujemy hasło, by je wykorzystać w kolejnych krokach. Drugie polecenie uruchamia kontener z bazą danych, tworząc katalog dla bazy. W trzecim logujemy się do bazy z pomocą hasła i tworzymy odpowiednią rolę. NA końcu ubijamy.
Warto zapisać gdzieś to hasło.
# zwroci np.: Kww8eYAdSJwLYppybzhffdZM
cat /dev/urandom | tr -dc "a-zA-Z0-9" |fold -w 24 | head -n 1
docker run --rm --name postgres \ -v $PWD/postgres14:/var/lib/postgresql/data \ -e POSTGRES_PASSWORD="Kww8eYAdSJwLYppybzhffdZM" \ -d postgres:14-alpine
docker exec -it postgres psql -U postgres
CREATE USER mastodon WITH PASSWORD 'Kww8eYAdSJwLYppybzhffdZM' CREATEDB; exit
docker stop postgres
4. Konfiguracja .env.production
Do pełni szczęścia brakuje nam zmiennych środowiskowych zapisanych w .env.production
.
We wspomnianym repozytorium Mastodona jest dostępnym wzorzec, ale po kilku próbach nieudanych próbach doszedłem do wniosku, iż lepiej stworzyć własny plik z minimalną ilością danych, a następnie uruchomić właściwy instalator poleceniem docker-compose run --rm web bundle exec rake mastodon:setup
.
Początkowa zawartość .env.production
DB_HOST=db
DB_PORT=5432
DB_NAME=mastodon
DB_USER=mastodon
DB_PASS=Kww8eYAdSJwLYppybzhffdZM
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
Instalator będzie chciał uzyskać informacje, niezbędne do uruchomienia instancji. Na końcu powinien wyświetlić listę zmiennych środowiskowych, które należy wkleić do .env.production
.
Poniższy fragment konfiguracji zawiera informację nt. konfiguracji SMTP pod kątem usługi EmailLabs - wszelkie informacje są dostępne w panelu administracyjnym. Z racji włączonej usługi Elasticsearch uwzględniłem też konfigurację dla niej.
Pierwsze uruchomienie instalatora zakończyło się niepowodzeniem (to bardzo dobry znak - nie pytajcie się dalszego). Po ustaleniu przyczyny ponownie uruchomiłem, ale znów wywaliło się - tym razem bez przyczyny. Stąd ta flaga DISABLE_DATABASE_ENVIRONMENT_CHECK
.
SMTP_SERVER=smtp.emaillabs.net.pl
SMTP_PORT=587
SMTP_LOGIN=1.<LOGIN>.smtp
SMTP_PASSWORD=<PASSWORD>
SMTP_AUTH_METHOD=plain
SMTP_OPENSSL_VERIFY_MODE=client_once
SMTP_ENABLE_STARTTLS=auto
SMTP_FROM_ADDRESS=<ADRESS MMAIL>
# Elasticsearch (optional)
# ------------------------
ES_ENABLED=true
ES_HOST=es
ES_PORT=9200
DISABLE_DATABASE_ENVIRONMENT_CHECK=1
Konto admina
Ostatnim pytaniem, jakie zada powyższy instalator to utworzenie konta administratora naszej instancji. Po podaniu login i adresu email zostanie wyświetlone hasło, które należy zmienić po pierwszy zalogowaniu.
5. Pierwsze uruchomienie i nadawanie uprawnien
Uruchamianie kontenerów kończy sie z niepowodzeniem ale zdąży wygenerować odpowiednią strukturę plików, dla których warto ustawić odpowiednie uprawnienia.
sudo docker-compose up -d
sudo docker-compose down
sudo chown -R 70:70 ./postgres
sudo chown -R 991:991 ./public
sudo chown -R 1000:1000 ./elascticsearch
sudo docker-compose up -d
## sprawdzamy, czy wsyzstko działa jak nalezy
sudo docker ps
6. NGINX
Instancja mastodonowa jest gotowa,
-
port
3000
- działa web -
port
4000
- działa streaming
ale nie jest dostępna na zewnątrz (w przypadku VPS Mikrus). Dlatego wykorzystamy NGINX
by móc wyświetlić instancję pod konrketnym adresem www ↗.
Moja konfiguracja w nginx
po użyciu ceberot
wygląda tak:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
location / { return 301 https://$host$request_uri; }
if ($host = wlasna_domena.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name wlasna_domena.com;
return 404; # managed by Certbot
}
server {
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
index index.html index.htm;
ssl_certificate /etc/letsencrypt/live/wlasna_domena.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/wlasna_domena.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
keepalive_timeout 1000;
sendfile on;
client_max_body_size 80m;
root /root/<KATALOG>/live/public;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
add_header Strict-Transport-Security "max-age=31536000";
location / {
try_files $uri @proxy;
}
location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
add_header Cache-Control "public, max-age=31536000, immutable";
try_files $uri @proxy;
}
location /sw.js {
add_header Cache-Control "public, max-age=0";
try_files $uri @proxy;
}
location @proxy {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Proxy "";
proxy_pass_header Server;
proxy_pass http://127.0.0.1:3000;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
tcp_nodelay on;
}
location /api/v1/streaming {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Proxy "";
proxy_pass http://127.0.0.1:4000;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
tcp_nodelay on;
}
error_page 500 501 502 503 504 /500.html;
}
Po czym sprawdzany poprawność konfigurcji i uruchamiamy usługę
nginx -T
nginx -s reload
I można przystąpić do ustawienia serwera mastodon z poziomu przeglądarki www ↗.
7. Postscriptum
Problemy z elasticsearch
Może się zdarzyć, ze Elastic Search będzie ciągle restartować się. Warto podejrzeć logi kontenera, w którym siedzi dana usługa. W moim przypadku był problem z pamięcia:
ERROR: [1] bootstrap checks failed. You must address the points described in the following [1] lines before starting Elasticsearch. bootstrap check failure [1] of [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] ERROR: Elasticsearch did not exit normally - check the logs at /usr/share/elasticsearch/logs/es-mastodon.log
Więc w terminalu należy wklepać poniższą informację:
sysctl -w vm.max_map_count=262144
I zrestartować usługe.
Kopie zapasowe
Uruchomienie instancji to jedno ale utrzymanie i tworzenie regularnych backupów, o czym wspomina oficjalna dokumentacja ↗. Niestety, nie ma mechanizmu do robienia kopii, jeteśmy zdani na siebie i własne skrypty, które można zautomatyzować.
Katalogi i pliki do backupowania
-
konfiguracja
docker-compose.yml
-
zmienne środowiskowe
.env.production
-
baza danych
bash docker exec postgres pg_dumpall -U postgres > postgres_backup.sql
-
pliki
/<sciezka>/mastodon/public/system
-
redis
/<sciezka>/mastodon/redis/dump.rdb
-
elasticsearch
/<sciezka>/mastodon/elasticsearch/*
Na szczęśćie wszystko jest w katalogu, w którym uruchamy mastodonowym serwis
Dodatkowe czynności
-
Monitorowanie serwisu (może być to portaneiner albo kolejna usługa oparta na Telegraf + Grafana)
-
Uruchomienie 2FA na koncie admina
-
Konfiguracja Mastodonowego serwera
-
wyłączenie publicznej rejestracji
-
ustawienie moderacji tagów
-
ustawienie przekaźników
-
Ustawienie aliasów na pozostałych profilach
Podsumowanie
Technicznie
Działa ;-)
Po kilku dniach obserwacji instancja mastodonowa działa nawet płynnie i nie zżera zasobów. Wciąż optymalizuję konfigurację i zdarzają się chwile przerwy ale to i tak mówimy o przestoju na poziomie 3-4 minut.
Problemy z propagacją serwera
W tej chwili potrzebuję czasu w dotarciu do jak największej ilości serwerów (społeczności), co utrudnia mi obserwację ciekawych wątków per tagi / profil. Upłynęło kilka dni i wciąż siedzę w swojej bańce medialnej.
Niuanse serwerowe
Trzeba liczyć się z problemami z dostępnością grafik, wynikających z nałożónych blokad na konkretne IP na poziomie serwerów. Tak mam w przypadku multimediów, uploadowanych na serwerze 101010.pl, hostowanych na cdn w OVH Cloud (prawd. nałożyli bana na IP, rezerwowane przez Hetznera). Powinienem pisać z tym do OVH ale uznałem, ze szkoda mojego czasu na to, bo poza tym wszystko działa, jak należy.
Oczywiście, jest to problem bardziej po mojej stronie (dokłądnie z VPS) i możliwe, ze w innych prywatnych instancjach takich akcji nie bedzie.
Obciążenie
W międzyczasie dodałem monitoring, logujący wszelkie dane, jakie można zebrać i je wyświetlić za pomocą influxdb/grafana. Inspirowałem się ↗ wpisem Bena ale nie zadziałało w 100% ze względu na problem z dostępnością usług redisa czy postresq w dockerowym telegrafie. Jestem pozytywnie zaskoczony tym, jak funkcjonuje dockerowy mastodon, wbrew pozorom nie jest mega dokuczliwy dla VPS ani też nie zaliczyłem żadnej poważnej awarii.
Jest jedna rzecz, która mocno mnie martwi to stale zwiększająca się objętość multimediów ./mastodon/public/system
,. Po kilku dniach “zabawy” mamy spory przyrost danych
❯ du -s -h ./
9.9G ./
Jak na prywatną instancję, to sporo. Z czystej ciekawości zmieniłem konfigurację odnośnie retencji danych (z 10d na 5d) i zobaczmy, jak to wpłynie po kilku dniach.
Poza techniczne
Kwestie prawne
Jak słusznie zauważyl Xpill ↗, jest spory problem od strony prawnej - szczególnie w Europie, gdzie mamy RODO/GDPR. Stawiając taki serwer z otwartą rejestracją, musisz liczyć się z konsekwencjami prawnymi. W moim przypadku nie jest to problem, bo to prywatna instancja i ja jestem gościem / userem / adminem w jednej osobie.
Paradoksalnie taka sytuacja ma wpływ, ze świadomie publikuję wpisy (tooty) z ewentualnymi konsekwencjami. Żadna to nowość, bo przecież robię to z każdym serwisem, w którym mam konto.
Kwestie techniczne
Nie sposób się nie zgodzić się z komentarzami Xpilla ↗ i Roziego z tego wpisu ↗ w kwestii utrzymania serwera i ewentualnych awarii a także dostępności prywatnej instancji w świecie fediwersum.
Z pewnością, nie jest to zabawa dla każdego chcącego. Mimo dostępnej konfiguracji w sieci, bariera wejścia w temat jest nieco wyższa. Wymaga dobrej znajomości obsługi dockera, serwera czy linuksa, czasu i chęci na podstawienie i prowadzenie instancji. Ważna jest też umiejętności łączenia informacji z Google / Stack Overflow / Github Issues w jedną całość ale to chleb powszechny dla webdevelopera czy devops’a
Czy warto mieć własny serwer?
Odpowiem klasycznie: To zależy
Jeśli akceptujesz wady, związane z własną (prywatną) instancją i jesteś gotów ponieść koszt y (prywatny czas na tego typu zabawy też trzeba liczyć) a także masz wiedzę nt temat, to czemu nie? Warto spróbować, bo jestem zdania, że zdobyta wiedza z pewnością przełoży na późniejsze projekty, w których będziemy zaangażowani, gdzie będzie liczyć się interdyscyplinarność.
W najgorszym (a może w najlepszym) przypadku załóż konto na której z istniejącej i popularnej instancji i działaj tam. Warto pamiętać o co miesięcznym przelewie na konto admina, by w ten sposób docenić jego pracę i poświęcenie.
Tak wygląda mój plan A i plan B.
Na koniec warto zajrzeć też na podsumowanie Łukasza ↗, który znacznie wcześnie zaczął zabawę z instacjami mastodonowymi. Czy moja przygoda z własnym Mamutem skończy się podobnie? Czas pokaże.