Skip to content

Uruchomienie instancji Mastodon przy użyciu Docker

Posted on:25 listopada 2022 at 11:35
Reading TIme:11 min czytania
Feature Image for  Uruchomienie instancji Mastodon przy użyciu Docker

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:

Po co?

Poza oczywistym - nowe zabawki - powody, dla których chcę mieć własną instancję mastodonową:

Konfiguracja

Wstępne informacje

Do uruchomienia własnej instancji potrzebne są

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,

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

Na szczęśćie wszystko jest w katalogu, w którym uruchamy mastodonowym serwis

Dodatkowe czynności

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.

Źródła



Możesz napisać do mnie e-mail, wiadomość na Telegramie lub wyszukać mnie na Mastodonie.
Loading...