S3 w środowisku lokalnym

W niedawnym projekcie postanowiłem użyć S3 zamiast NFS do przechowywania danych dzielonych przez wszystkie instancje aplikacji. Co za tym idzie musiałem przygotować serwer S3 w środowisku lokalnym wykorzystującym Dockera. Poniżej zamieszczam fragment pliku compose.yml.

services:
   rustfs:
       image: rustfs/rustfs
       ports:
           - "33900:9000" # API port
           - "33901:9001" # Console port
       networks:
           default:
               aliases:
                   - admin.rustfs
                   - asset-cache.rustfs
                   - asset.rustfs
                   - recycle-bin.rustfs
                   - thumbnail.rustfs
                   - version.rustfs
                   - temp.rustfs
       environment:
           RUSTFS_VOLUMES: /data
           RUSTFS_ADDRESS: ":9000"
           RUSTFS_ACCESS_KEY: ${S3_ACCESS_KEY}
           RUSTFS_SECRET_KEY: ${S3_SECRET_KEY}
           RUSTFS_CONSOLE_ENABLE: true
           RUSTFS_CONSOLE_ADDRESS: ":9001"
       healthcheck:
           test:
             [
                 "CMD",
                 "sh", "-c",
                 "curl -f http://localhost:9000/health && curl -f http://localhost:9001/rustfs/console/health"
             ]
           interval: 30s
           timeout: 10s
           retries: 3
           start_period: 40s
       volumes:
           - rustfs-data:/data

   rustfs-init:
       image: minio/mc
       entrypoint: [ "/bin/sh", "-c" ]
       command: >
           '
           until mc alias set rustfs http://rustfs:9000 ${S3_ACCESS_KEY} ${S3_SECRET_KEY}; do
               echo "Waiting for RustFS..."
               sleep 2
           done;
           mc mb --ignore-existing rustfs/admin;
           mc mb --ignore-existing rustfs/asset-cache;
           mc mb --ignore-existing rustfs/asset;
           mc mb --ignore-existing rustfs/recycle-bin;
           mc mb --ignore-existing rustfs/thumbnail;
           mc mb --ignore-existing rustfs/version;
           mc mb --ignore-existing rustfs/temp;
           mc anonymous set public rustfs/admin;
           mc anonymous set public rustfs/asset;
           mc anonymous set public rustfs/thumbnail;
           '
       depends_on:
           rustfs:
               condition: service_healthy

volumes:
   rustfs-data:

Serwis rustfs to serwer S3 wykorzystujący RustFS. Pierwotnie planowałem użyć MinIO, ale po rozważeniu kontrowersyjnych opinii postanowiłem poszukać czegoś innego. Brałem pod uwagę Garage, ale ten projekt chyba bardziej nadaje się do środowiska produkcyjnego niż lojalnego. RustFS jest prostszy i ma wbudowaną konsolę administracyjną dostępną przez HTTP, co przydaje się w środowisku lokalnym. W sekcji networks zdefiniowałem dodatkowe aliasy domenowe ponieważ paczka league/flysystem-aws-s3-v3 spodziewa się nazwy wiaderka w domenie. Serwis rustfs ma healtcheck, dzięki czemu serwisy, które korzystają z S3 (w tym rustfs-init) są uruchamiane dopiero kiedy serwer jest gotowy.

Drugi serwis rustfs-init automatycznie tworzy wiaderka, których spodziewa się aplikacja oraz ustawia niektóre z nich jako widoczne publicznie.

Inicjalizacja nowego projektu Symfony

W dokumentacji Symfony napisane jest, żeby do inicjalizacji nowego projektu użyć polecenia symfony new albo composer create-project. Jeśli podobnie jak ja nie lubicie instalować PHP z przyległościami lokalnie to można to zrobić z użyciem Dockera.

docker run --rm --interactive --tty --volume $PWD:/app --user $(id -u):$(id -g) composer create-project symfony/skeleton my_project_directory

pg_restore w Dockerze

Taka sytuacja: mamy bazę danych PostgreSQL działającą w Dockerze. Chcemy wgrać dane z kopii zapasowej, ale nie chcemy wrzucać pliku z bazą do kontenera postgresa. Jak to zrobić?

Możemy obok wystartować drugi kontener, w którym plik z bazą udostępnimy przez bind mount i w tym kontenerze uruchomimy pg_restore, który będzie wyrzucać dane na standardowe wyjście, które rurą przekierujemy do właściwego kontenera z bazą. Uff, brzmi skomplikowanie, ale samo polecenie mieści się w jednej linijce:

docker run -v "$(PWD)":/backup postgres:9.4 pg_restore --clean --no-owner --no-privileges /backup/dump.sql | docker-compose exec -T db psql -U dbuser -d dbname

Powyższy przykład zakłada, że kontener Postgresa został uruchomiony przez docker-compose, co najczęściej będzie miało miejsce na lokalnym środowisku developerskim. Jeśli projekt działa w roju (swarm) to polecenie można zmodifikować następująco:

docker run -v "$PWD":/backup postgres:9.4 pg_restore --clean --no-owner --no-privileges /backup/dump.sql | docker exec -i project_db.1.st5j3pz7rre153z7ficb59c9n psql -U dbuser -d dbname