Napisano dnia 16.06.2022 r. o godzinie 20:46
Autor: Piotr Sperka
Cześć, witaj po długiej przerwie! VPS, który powstał na podstawie poprzednich odcinków działa już prawie rok. Przez ten czas poprawiałem drobne niedogodności w jego funkcjonowaniu, a także kilkukrotnie zaktualizowałem zarówno pakiety systemu, jak i wersje kontenerów działające na Dockerze. W dzisiejszym odcinku przestawię po kolei rozwiązanie napotkanych przeze mnie problemow oraz jak aktualizować kontenery.
Certbot, którego użyłem do pobrania certyfikatów LetsEncrypt powinien automatycznie je odnawiać przed upływem ich ważności. Stało się tak raz, drugi, ale w pewnym momencie, po którejś aktualizacji systemu Certbot odmówił dalszej współpracy. Krótka analiza pokazała, że uruchamiany przez CRONa skrypt sprawdza, czy w systemie jest usługa timer systemd. Jeśli jest, zaprzestaje działania. Problem w tym, że w moim przypadku żaden timer nie był stworzony. Szybkim rozwiązaniem tego problemu była modyfikacja pliku /etc/cron.d/certbot do następującej postaci:
0 */12 * * * root perl -e 'sleep int(rand(43200))' && certbot -q renew
Odświeżanie certyfikatu zaczęło działać ponownie. W ten sposób pominąłem sprawdzanie istnienia usługi timerów i zdałem się na działanie CRONa. Z pewnością nie jest to rozwiązanie idealne i eleganckie, jednak w tamtym momencie nie miałem czasu na więcej. Mogę za to zapewnić, że odśweżanie certyfikatów działa od tamtej pory bezbłędnie.
Przejdę teraz płynnie do restartu kontenerów Dockerowych wykorzystujących certyfikaty. Restart ten jest oczywiście konieczny po tym, jak Certbot pobierze nowe certyfikaty. Analizując wyniki komendy ls /etc/cron.*/* widać, że jest tam wpis /etc/cron.d/certbot. Rzeczywiście, Certbot cyklicznie odpala się, widać to nawet w logu (grep CRON /var/log/syslog). Pokazuje to, że Certbot pobierze nowe certyfikaty, gdy obecne będą bliskie wygaśnięcia, jednak restart chcę wykonać tylko, gdy rzeczywiście nowe certyfikaty się pojawią, a nie za każdym wywołaniem Certbota przez CRON. Nada się do tego post deploy hook. Stworzyłem plik /docker/cert-post-deploy-hook.sh o następującej treści:
#!/bin/bash cd /docker/mailu cp /etc/letsencrypt/live/sperka.pl/fullchain.pem /docker/mailu/certs/cert.pem cp /etc/letsencrypt/live/sperka.pl/privkey.pem /docker/mailu/certs/key.pem /bin/docker-compose restart cd /docker/nginx-proxy /bin/docker-compose restart
Skrypt ten kopiuje certyfikaty do odpowiedniego katalogu dla Mailu, restartuje usługę, a także restartuje reverse proxy oparte na Nginxie. Trzeba jeszcze nadać skrytpowi odpowiednie uprawnienia komendą:
chmod 700 /docker/cert-post-deploy-hook.sh
Następnie wyedytowałem plik /etc/letsencrypt/cli.ini i dopisałem linię
deploy-hook = /docker/cert-post-deploy-hook.sh
Skrypt powinien wykonać się po odnowieniu certyfikatów, i po kilku kolejnych odświeżeniach mogę potwierdzić, że uruchamia się.
Przy okazji analizy logów podczas poprawiania problemu z odświeżaniem certyfikatów zauważyłem, że nie zgadza się godzina. W związku z tym sprawdziłem komendę timedatectl. Oto co wykazała:
root@sperka:/var/log# timedatectl Local time: Tue 2021-09-21 10:02:12 EDT Universal time: Tue 2021-09-21 14:02:12 UTC RTC time: Tue 2021-09-21 14:02:13 Time zone: America/New_York (EDT, -0400) System clock synchronized: yes NTP service: active RTC in local TZ: no
W związku z tym wykonałem polecenie timedatectl set-timezone Europe/Warsaw i ponownie sprawdziłem czas:
root@sperka:/var/log# timedatectl Local time: Tue 2021-09-21 16:03:13 CEST Universal time: Tue 2021-09-21 14:03:13 UTC RTC time: Tue 2021-09-21 14:03:14 Time zone: Europe/Warsaw (CEST, +0200) System clock synchronized: yes NTP service: active RTC in local TZ: no
Jak widać, tym razem strefa czasowa była już poprawna.
Na początek wątku na temat aktualizacji zajmę się aktualizacją Nextcloud. Ma to swoje uzasadnienie. Okazuje się, że czasami aktualizacja tej usługi nie jest taka prosta jak być powinna. Do tematu aktualizacji Nextcloud przystąpiłem po tym, jak zauważyłem problem z jego obsługą poprzez aplikację Pliki na iOS. Aplikacja pobiera wszystkie pliki z danego katalogu zaraz po wejściu do niego, a nie po wejściu w plik. Jest to okropnie irytujące i niepotrzebnie zużywa przestrzeń na urządzeniu. Po dłuższych poszukiwaniach w czeluściach Internetu nie znalazłem żadnych konkretów, poza kilkoma opisami podobnych problemów – niestety, bez rozwiązania. Z braku lepszych pomysłów postanowiłem zaktualizować aplikację na serwerze.
Mogłoby się wydawać, że aktualizacja to nic trudnego. Jednak w przypadku Nextcloud (a zapewne także i innych bardziej złożonych usług) należy pamiętać, żeby nie próbować za jednym strzałem podbić wersji o więcej niż jedno główne wydanie. Na przykład próba podbicia wersji z 21 na 23 skończy się błędem i sporymi problemami. Dlatego na początek apeluję – zrób backup bazy i plików Nextcloud przed przystąpieniem do akcji!
Tym razem robiłem aktualizację z wersji 21.0.3 do aktualnej z tagu production, czyli 22.2.3. Na początek wykonałem docker-compose down, zrobiłem kopię zapasową i wyedytowałem plik docker-compose.yml. Jeśli korzystasz z konfiguracji z tej serii poradników, musisz najpierw rozwinąć nieco parametr command w konfiguracji bazy danych MariaDB. Poniżej przedstawiłem aktualny fragment:
nextcloud-db: image: mariadb:latest restart: always command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --innodb-file-per-table=1 --skip-innodb-read-only-compressed volumes: - /docker/nextcloud/mysql:/var/lib/mysql environment: - MYSQL_RANDOM_ROOT_PASSWORD='1' - MYSQL_PASSWORD=supertajnehaslo - MYSQL_DATABASE=nextcloud - MYSQL_USER=nextcloud networks: - reverse_proxy
Pozwoli Ci to z góry uniknąć nieudanej migracji bazy danych zakończonej błędem: Nextcloud 4047 InnoDB refuses to write tables with ROW_FORMAT=COMPRESSED or KEY_BLOCK_SIZE. Dodatkowo zmodyfikowałem też nazwę tagu obrazu Nextcloud z latest na production. Jest to istotne – gdybym nie zmienił tagu i wykonał aktualizację obrazu, utknąłbym na etapie migracji bazy danych, ponieważ podbiłbym wersję z 21 do 23. Który tag odpowiada której wersji można sprawdzić w opisie kontenera na Docker Hubie.
Po modyfikacji pliku docker-compose.yml wykonałem polecenia docker-compose pull a następnie docker-compose up -d. Po uruchomieniu kontenerów wykonałem zgodnie z dokumentacją na Docker Hubie polecenie:
docker exec --user www-data CONTAINER_ID php occ upgrade
Na koniec zrestartowałem reverse proxy i Nextcloud zaczął być ponownie dostępny, już w nowej wersji. Kilka miesięcy później ponownie podbijałem wersję Nextclouda, i tym razem jedyne co musiałem wykonać do polecenia:
docker-compose pull docker-compose up -d
oraz zrestartować reverse proxy.
Po nieco złożonej aktualizacji Nextcloud przejdę płynnie do aktualizacji pozostałych kontenerów. Jest to prosta sprawa i polega na wykonaniu poleceń w każdym z katalogów z docker-compose:
docker-compose pull docker-compose up -d
Na koniec warto zrestartować reverse proxy poleceniami (wykonanymi w katalogu reverse proxy):
docker-compose down docker-compose up -d
Od początku borykałem się z problemem automatycznego startu kontenerów. Problem był, dokładniej rzecz biorąc taki, że kontenery startowały w nieodpowiedniej kolejności. W efekcie przynajmniej jeden z nich nie wstawał poprawnie, co skutkowało błędem startu reverse proxy. Przez to żadna usługa nie była dostępna aż do momentu ręcznego uruchomienia brakującego kontenera i restartu reverse proxy. Było to szczególnie drażniące w przypadku usług mailowych, ponieważ po odnowieniu certyfikatu lub restarcie Dockera (na przykład po aktualizacji) mail przestawał działać.
W moim przypadku kłopoty sprawiała usługa apache-lamp, której nie opisywałem w tej serii poradników. Jest to kontener zawierający serwer Apache wraz z PHP oraz bazą MariaDB. Jednakże, jeśli chodzi o wszystkie pozostałe usługi, należy zadbać, żeby w plikach docker-compose.yml miały ustawiony parametr restart: always. Zapewni to, że w przypadku błędu podczas startu, usługa zrestartuje się. Dzięki temu wszystkie usługi powinny wstać automatycznie po starcie Dockera, nawet, jeśli zajmie im to kilka restartów.
Wydaje mi się, że jest to oczywista sprawa, ale dla spokoju sumienia o tym wspomnę. Każdy system, w szczególności serwerowy warto regularnie aktualizować, żeby zapewnić wszystkie najnowsze poprawki bezpieczeństwa. W tym celu z konta roota w systemie należy wywołać polecenia:
apt update apt upgrade
Warto co jakiś czas (na przykład po aktualizacji kontenerów) uruchomić polecenia, które kolejno pozwalają usunąć nieużywane już obrazy dockerowe, oraz usunąć pozostałości po usuniętych kontenerach (na przykład nieużywane sieci). Są to polecenia:
docker system prune docker image prune -af
Ten odcinek może wydawać się nieco chaotyczny i nie dotyczący niczego konkretnego, jednak z założenia miał być rodzajem zbioru wniosków i note to self. Zdaję sobie sprawę, że ta część nie ukazała się w czasie, w jakim powinna była się ukazać (czyli tak z 8 miesięcy temu…). Cóż mogę napisać – praca i nawał obowiązków. Mam jedynie nadzieję, że kolejne artykuły ukażą się z nieco mniejszymi opóźnieniami. Tym samym powoli dobiegliśmy do końca serii „VPS dla początkujących”. Nie wykluczam, że jeszcze pojawi się jakiś dodatkowy odcinek, jeśli będę miał coś ciekawego do pokazania. Mam nadzieje, że zawarte tu porady okazały się pomocne. Do następnego 😉