Klaster OKD 4.19 (OpenShift) – Projekt s3store oparty na MinIO – s3store-manager

Informacje wstępne o projekcie s3store

Opis projektu s3store obejmuje 2 wpisy na tym blogu:

Opis rozwiązania MinIO

MinIO to lekka i „budżetowa” obiektowa pamięć masowa, w 100% zgodna z API Amazon S3 (Simple Storage Service). Najlepiej sprawdza się do trzymania danych nieustrukturyzowanych, czyli np. zdjęć, filmów, logów, plików eksportów czy różnych „artefaktów” z systemów.

Jest uniwersalne, bo możesz je wykorzystać m.in. do:

  • archiwizacji danych,
  • składowania i analizy dużych zbiorów danych,
  • backupów oraz odtwarzania awaryjnego (DR).

MinIO pozwala też łatwo zbudować klaster z wielu mniejszych instancji storage’u (tzw. podejście „microstorage” – dużo małych elementów zamiast jednego dużego). Dzięki temu rozwiązanie jest proste do skalowania (dokładasz kolejne nody) i potrafi zapewnić wysoką dostępność, bo awaria pojedynczej instancji nie musi zatrzymać całej usługi.

S3 (i MinIO jako „S3-compatible”) nie jest filesystemem, tylko API do obiektów:

  • Bucket = logiczny „kontener”
  • Object = plik + metadane, identyfikowany przez klucz (np. uploads/mors.1.0.0_openshift.tar.gz)

Nie ma „montowania S3 jako PVC” w naturalny sposób. Więcej informacji:

Założenia projektu s3store

Projekt s3store składa się z 2 aplikacji:
1. Aplikacja s3store-manager, która obsługuje funkcjonalność

  • MinIO Server (S3 API)
  • MinIO Console (admin)

i zawiera elementy

  • Secret z root credentials (admin) MinIO (np. MINIO_ROOT_USER, MINIO_ROOT_PASSWORD)
  • Deployment
  • Service port 9000 → S3 API, port 9001 → MinIO Console
  • Route(y): route do Console (żeby adminowo klikać z przeglądarki). route do S3 API (jeśli chcesz upload bezpośrednio z laptopa / presigned URLs)

MinIO ma wbudowaną konsolę web i standardowo wystawia ją na osobnym porcie.

ewentualnie Job „init” do bucket/user/policy

2. Aplikacja s3store-gui, który obsługuje funkcjonalność

  • PHP: upload + listing + download link
  • PHP odbiera upload
  • PHP robi putObject() do MinIO przez S3 API
  • PHP pokazuje link/listę obiektów w bucket

Przygotowanie PV pod S3 MinIO na storage.okdlab.local

Tworzymy nowy wolumen lvm o nazwie lv_pvc_06 o rozmiarze 32 GB w grupie vg_datastore na serwerze 192.168.40.20 (storage.okdlab.local). Więcej informacji o przygotowaniu maszyny wirtualnej storage.okdlab.local https://itadmin.vblog.ovh/klaster-okd-openshift-na-maszynach-wirtualnych-proxmox-przygotowanie-maszyny-storage-nfs/

# na serwerze 192.168.40.20 (storage.okdlab.local) sprawdzamy ilośc wolnego miejsca na  grupie vg_datastore
vgdisplay vg_datastore | egrep -i "VG Size|Free  PE|PE Size"

  VG Size               <64.00 GiB
  PE Size               4.00 MiB
  Free  PE / Size       11007 / <43.00 GiB
  
# przygotowanie volumenu lv o rozmiarze 32GB  
lvcreate -L 32G -n lv_pvc_06 vg_datastore
mkfs.xfs /dev/vg_datastore/lv_pvc_06
mkdir -p /mnt/lv_pvc_06
mount /dev/vg_datastore/lv_pvc_06 /mnt/lv_pvc_06

# Do /etc/fstab zwykle lepiej dodać po UUID
blkid /dev/vg_datastore/lv_pvc_06
/dev/vg_datastore/lv_pvc_06: UUID="9269f680-f5fe-488a-ae8c-17b739bf5c20" BLOCK_SIZE="512" TYPE="xfs"

# dodać taki wpis do /etc/fstab
UUID=9269f680-f5fe-488a-ae8c-17b739bf5c20 /mnt/lv_pvc_06          xfs     defaults        0 0

# dodajemy w /etc/exports
/mnt/lv_pvc_06 192.168.40.0/24(rw,sync,no_root_squash,no_all_squash,no_wdelay)
sudo systemctl restart nfs-server
showmount -e localhost

# dodajemy uprawnienia
sudo chmod 0777 /mnt/lv_pvc_06
sudo chown -R nobody:nobody lv_pvc_06

Struktura aplikacji s3store-manager w projekcie s3store-prod

/repo/pakiety/s3store/1.0.0/s3store-manager
drwxr-xr-x 3 bastuser bastuser   29 Feb 14 10:27 ..
-rw-r--r-- 1 bastuser bastuser  128 Feb 14 11:10 cm-s3store-minio-config.yaml
-rw-r--r-- 1 bastuser bastuser 1260 Feb 14 11:16 deploy-s3store-manager.yaml
-rw-r--r-- 1 bastuser bastuser  461 Feb 14 11:03 Dockerfile
-rw-r--r-- 1 bastuser bastuser  355 Feb 14 11:00 pv-06.yaml
-rw-r--r-- 1 bastuser bastuser  339 Feb 14 11:01 pvc-06.yaml
-rw-r--r-- 1 bastuser bastuser  546 Feb 14 14:04 s3store-gui-policy.json
-rw-r--r-- 1 bastuser bastuser  178 Feb 14 11:12 secret-s3store-minio-root.yaml
-rw-r--r-- 1 bastuser bastuser  254 Feb 14 11:18 svc-s3store-manager.yaml

Definicja PVC na potrzeby aplikacji s3store-manager

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv-s3store
spec:
  capacity:
    storage: 32Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  storageClassName: s3store-nfs-manual
  persistentVolumeReclaimPolicy: Retain
  mountOptions:
    - vers=4.1
  nfs:
    path: /mnt/lv_pvc_06
    server: 192.168.40.20
    readOnly: false

Przygotowanie Dockerfile na potrzeby zbudowania obrazu aplikacji

OpenShift odpali z własnym UID, najważniejsze są uprawnienia na /data.

FROM quay.io/minio/minio:latest
# (root group 0 + mirror perms g=u)
USER 0
RUN mkdir -p /data /tmp/minio && \
    chgrp -R 0 /data /tmp/minio && \
    chmod -R g+rwX /data /tmp/minio && \
    chmod -R g=u /data /tmp/minio

# Wróć do nie-root (OpenShift i tak odpali z własnym UID; ale to nie szkodzi)
USER 1001
EXPOSE 9000 9001
# MinIO image ma entrypoint "minio", więc CMD to parametry dla "minio ..."
CMD ["server", "/data", "--console-address", ":9001"]

ConfigMapa dla aplikacji s3store-manager

apiVersion: v1
kind: ConfigMap
metadata:
  name: s3store-minio-config
  namespace: s3store-prod
data:
  MINIO_REGION_NAME: "homelab"

Secret z root credentials (admin MinIO) dla aplikacji s3store-manager

apiVersion: v1
kind: Secret
metadata:
  name: s3store-minio-root
  namespace: s3store-prod
type: Opaque
stringData:
  MINIO_ROOT_USER: "minioadmin"
  MINIO_ROOT_PASSWORD: "1234abcd."

Przygotowanie aplikacji s3store-manager w projekcie s3store-prod

cd /repo/pakiety/s3store/1.0.0/s3store-manager/

# PV (cluster-scope)
oc apply -f pv-06.yaml
oc get pv | grep nfs-pv-s3store
oc describe pv nfs-pv-s3store

# Projekt / namespace
oc new-project s3store-prod
# albo jeśli już istnieje:
oc project s3store-prod

# PVC
oc apply -f pvc-06.yaml
oc get pvc
oc describe pvc s3store-manager-data

# Secret z root credentials (admin MinIO)
oc apply -f /repo/pakiety/s3store/1.0.0/s3store-manager/secret-s3store-minio-root.yaml
# ConfigMap (opcjonalna, ale przydatna)
oc apply -f /repo/pakiety/s3store/1.0.0/s3store-manager/cm-s3store-minio-config.yaml

# utwórz BuildConfig typu docker, build z lokalnego katalogu
oc new-build --name=s3store-manager --binary --strategy=docker -n s3store-prod

# wystartuj build, biorąc Dockerfile z tego katalogu
oc start-build s3store-manager --from-dir=. --follow -n s3store-prod

# sprawdź image stream
oc get is -n s3store-prod
oc describe is s3store-manager -n s3store-prod

Uruchomienie aplikacji s3store-manager w projekcie s3store-prod

Deployment MinIO (s3store-manager)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: s3store-manager
  namespace: s3store-prod
spec:
  replicas: 1
  selector:
    matchLabels:
      app: s3store-manager
  template:
    metadata:
      labels:
        app: s3store-manager
    spec:
      containers:
        - name: minio
          image: image-registry.openshift-image-registry.svc:5000/s3store-prod/s3store-manager:latest
          imagePullPolicy: Always
          envFrom:
            - secretRef:
                name: s3store-minio-root
            - configMapRef:
                name: s3store-minio-config
          ports:
            - name: s3api
              containerPort: 9000
            - name: console
              containerPort: 9001
          volumeMounts:
            - name: data
              mountPath: /data
          readinessProbe:
            httpGet:
              path: /minio/health/ready
              port: 9000
            initialDelaySeconds: 10
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /minio/health/live
              port: 9000
            initialDelaySeconds: 20
            periodSeconds: 20
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: s3store-manager-data
oc apply -f deploy-s3store-manager.yaml
oc rollout status deployment/s3store-manager
oc get pods -o wide
oc logs deployment/s3store-manager -f

Service (API + Console) aplikacji s3store-manager

apiVersion: v1
kind: Service
metadata:
  name: s3store-manager
  namespace: s3store-prod
spec:
  selector:
    app: s3store-manager
  ports:
    - name: s3api
      port: 9000
      targetPort: 9000
    - name: console
      port: 9001
      targetPort: 9001
oc apply -f svc-s3store-manager.yaml
oc get svc s3store-manager -n s3store-prod
oc describe svc s3store-manager -n s3store-prod

Route edge TLS (oddzielnie dla API i dla Console) aplikacji s3store-manager

# S3 API (9000)
oc create route edge s3store-s3api \
  --service=s3store-manager \
  --port=s3api \
  -n s3store-prod

# Console (9001) - to jest TA właściwa konsola admin
oc create route edge s3store-console \
  --service=s3store-manager \
  --port=console \
  -n s3store-prod

oc get route -n s3store-prod
oc rollout restart deployment/s3store-manager
oc rollout status deployment/s3store-manager
oc get pods -o wide
oc logs deployment/s3store-manager -f

Problem z PersistentVolumeClaims po usunięciu projektu s3store-prod

To jest efekt Retain po skasowaniu starego projektu s3store. Dopóki ten claimRef siedzi w PV, nowy PVC w s3store-prod nie zbinduje się, więc pody będą Pending

# jeśli pv jest statusie Released
[bastuser@bastion s3store-manager]$ oc get pv | egrep "nfs-pv-s3store|s3store"
nfs-pv-s3store    32Gi       RWX            Retain           Released    s3store/s3store-manager-data                s3store-nfs-manual   <unset>                          65m

# to należy zwolnić PV usuwając claimRef
oc project s3store-prod
oc patch pv nfs-pv-s3store --type=merge -p '{"spec":{"claimRef": null}}'

# teraz powinien być status Available
[bastuser@bastion s3store-manager]$ oc get pv nfs-pv-s3store
NAME             CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS         VOLUMEATTRIBUTESCLASS   REASON   AGE
nfs-pv-s3store   32Gi       RWX            Retain           Available           s3store-nfs-manual   <unset>                          67m

Przygotowanie bucket w MinIO GUI

Logujemy się na stronie https://s3store-console-s3store-prod.apps.testcluster.okdlab.local/login

  • użytkownik: minioadmin
  • hasło: 1234abcd.

Powyższe informacje ustawiliśmy w pliku /repo/pakiety/s3store/1.0.0/s3store-manager/secret-s3store-minio-root.yaml

Klikamy na Create Bucket

Bucket Name: uploads

bucket musi być zwykle lowercase, bez spacji, najlepiej bez podkreśleń

web UI w CE zostało ograniczone do przeglądania obiektów i podstawowowych operacji na bucker

Najpewniej z oficjalnej dystrybucji MinIO (opis instalacji jest w dokumentacji/stronie MinIO).
Jeśli wolisz “na szybko” i masz internet na bastionie, zwykle robisz to curl’em (tu podaję jako przykład – dopasuj do swojej dystrybucji):

Przygotowanie bucket,uzytkownika, policy, kluczy w Secret w MinIO przy użyciu narzędzia mc (mcli)

Uwaga występuje klasyczny konflikt nazw: mc = Midnight Commander na Linuksie, a MinIO Client też chce się nazywać mc.
Dlatego w moim przypadku narzędzie zamiast mc będzie nazywało się mcli.

curl -sSL -o /tmp/mcli https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x /tmp/mcli
sudo mv /tmp/mcli /usr/local/bin/mcli
mcli --version
which mc
which mcli

Utworzenie aliasu o wygodnej nazwie mys3store aby nie podawać w kółko całego urla i loginu oraz trybu insecure

mcli alias set mys3store \
  https://s3store-s3api-s3store-prod.apps.testcluster.okdlab.local \
  minioadmin '1234abcd.' \
  --insecure
    
mcli: Configuration written to `/home/bastuser/.mcli/config.json`. Please update your access credentials.
mcli: Successfully created `/home/bastuser/.mcli/share`.
mcli: Initialized share uploads `/home/bastuser/.mcli/share/uploads.json` file.
mcli: Initialized share downloads `/home/bastuser/.mcli/share/downloads.json` file.
Added `mys3store` successfully.

Wyświetlenie listy zapisanych aliasów

mcli alias list
gcs
  URL       : https://storage.googleapis.com
  AccessKey : YOUR-ACCESS-KEY-HERE
  SecretKey : YOUR-SECRET-KEY-HERE
  API       : S3v2
  Path      : dns
  Src       : /home/bastuser/.mcli/config.json

local
  URL       : http://localhost:9000
  AccessKey :
  SecretKey :
  API       :
  Path      : auto
  Src       : /home/bastuser/.mcli/config.json

mys3store
  URL       : https://s3store-s3api-s3store-prod.apps.testcluster.okdlab.local
  AccessKey : minioadmin
  SecretKey : 1234abcd.
  API       : s3v4
  Path      : auto
  Src       : /home/bastuser/.mcli/config.json

play
  URL       : https://play.min.io
  AccessKey : Q3AM3UQ867SPQQA43P2F
  SecretKey : zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
  API       : S3v4
  Path      : auto
  Src       : /home/bastuser/.mcli/config.json

s3
  URL       : https://s3.amazonaws.com
  AccessKey : YOUR-ACCESS-KEY-HERE
  SecretKey : YOUR-SECRET-KEY-HERE
  API       : S3v4
  Path      : dns
  Src       : /home/bastuser/.mcli/config.json

Utworzenie bucketu o nazwie uploads

mcli mb --ignore-existing mys3store/uploads --insecure

# wyświetl wszystkie buckety
mcli ls mys3store --insecure

# wyświetl zawartości bucketu
mcli ls mys3store/uploads --insecure

# wrzucenie lokalnego pliku na bucket
cd /repo/pakiety/s3store/1.0.0/s3store-manager
tar -czf s3storemanager_1.0.0_coo_openshift.tar.gz .
mcli cp s3storemanager_1.0.0_coo_openshift.tar.gz mys3store/uploads/ --insecure

# usunięcie obiektu z bucketa
mcli rm mys3store/uploads/s3storemanager_1.0.0_coo_openshift.tar.gz --insecure

Utworzenie policy dla aplikacji s3store-gui (tylko bucket uploads)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketLocation",
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads"
      ],
      "Resource": [
        "arn:aws:s3:::uploads"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:DeleteObject",
        "s3:GetObject",
        "s3:ListMultipartUploadParts",
        "s3:PutObject",
        "s3:GetObjectTagging",
        "s3:PutObjectTagging"
      ],
      "Resource": [
        "arn:aws:s3:::uploads/*"
      ]
    }
  ]
}
mcli admin policy create mys3store s3store-gui-policy s3store-gui-policy.json --insecure \
  || mcli admin policy update mys3store s3store-gui-policy s3store-gui-policy.json --insecure
  
# weryfikacja
mcli admin policy info mys3store s3store-gui-policy --insecure

Jeśli dokonano jakiś zmian w /repo/pakiety/s3store/1.0.0/s3store-manager/s3store-gui-policy.json to można zaktualizować polisę poleceniem:

mcli admin policy create mys3store s3store-gui-policy ./s3store-gui-policy.json --insecure
# weryfikacja
mcli admin policy info mys3store s3store-gui-policy --insecure

Utworzenie usera aplikacyjnego s3store-gui oraz podpięcie policy

#generujemy sobie losowy secret dla bezpieczeństwa
openssl rand -base64 36 | tr -d '\n' | cut -c1-32
cGW8oKh0mleGaNKvWHecLCApaDYj+w2S

# Dodaj usera i podepnij policy:
mcli admin user add mys3store s3store-gui-user cGW8oKh0mleGaNKvWHecLCApaDYj+w2S --insecure || true
Added user `s3store-gui-user` successfully.

mcli admin policy attach mys3store s3store-gui-policy --user s3store-gui-user --insecure
Attached Policies: [s3store-gui-policy]
To User: s3store-gui-user

# lista użytkowników w buckecie
mcli admin user list mys3store --insecure
enabled    s3store-gui-user      s3store-gui-policy

# lista polis w buckecie
mcli admin policy list mys3store --insecure
readwrite
s3store-gui-policy
writeonly
consoleAdmin
diagnostics
readonly

# lista podpiętych polis do użytkownika w buckecie
mcli admin user info mys3store s3store-gui-user --insecure
AccessKey: s3store-gui-user
Status: enabled
PolicyName: s3store-gui-policy
MemberOf: []

Sprawdzenie zajętości i wolnego miejsca w bucket

# Suma rozmiaru w bucketcie
mcli du mys3store/uploads --insecure

# Lista obiektów z rozmiarami (manualna kontrola)
mcli ls --recursive mys3store/uploads --insecure

oc -n s3store-prod rsh deploy/s3store-manager
df -h /data
exit

Ustawienia quota na bucket

# informacje o quota
mcli quota info mys3store/uploads --insecure
# ustawienia quota
mcli quota set mys3store/uploads --size 10Gi --insecure
# usunięcie quota
mcli quota clear mys3store/uploads --insecure