Klaster OKD (OpenShift) – Projekt OpenShift java-memory-eater

Testowy klaster OKD (OpenShift)

Poniższy wpis bazuje na konfiguracji klastra przeprowadzonej wg. wpisu https://itadmin.vblog.ovh/klaster-okd-openshift-na-maszynach-wirtualnych-proxmox-opis-instalacji/ oraz konfiguracji serwera NFS opisanej na stronie https://itadmin.vblog.ovh/klaster-okd-openshift-na-maszynach-wirtualnych-proxmox-przygotowanie-maszyny-storage-nfs/

Opis projektu OpenShift : java-memory-eater

Projekt ma na celu kontrolowane wywołanie częstego błędu w aplikacjach java: OutOfMemoryError poprzez cykliczne pożeranie pamięci RAM. Bazuje na obrazie Docker-a eclipse-temurin:17-jdk-alpine . Dodatkowo projekt wykorzystuje configmapę w standardowej postaci key – value. POD aplikacji w tym przykładzie może maksymalnie wykorzystać ~1.8 GB pamięci RAM bez ustawionych limitów OpenShift lub ~ 1 GB pamięci RAM z ustawionymi limitami pamięci RAM.

ConfigMap to obiekt w Kubernetes/OpenShift, który przechowuje dane konfiguracyjne w postaci par klucz–wartość.

  • Dane te mogą być następnie udostępniane do podów jako:
    • zmienne środowiskowe (env w kontenerze),
    • albo pliki w wolumenie (volumeMount).

Dzięki temu nie musisz wbudowywać konfiguracji w obraz Dockera — aplikacja może być konfigurowana dynamicznie przez administratorów DevOps bez rekompilacji.

Utworzenie nowego projektu OpenShift o nazwie java-memory-eater

Najpierw należy przygotować kod aplikacji Java , która w pętli zwiększa zużycie pamięci. Aplikacja korzysta z 3 zmiennych środowiskowych przesłanych przez configmapę.
SLEEP_BEFORE_START – ile sekund czekać na starcie zanim ruszy nieskończona pętla zjadająca pamięć
OBJECT_SIZE_MB – ile jednorazowo ma zostać zajęte pamięci
LOOP_TIME – ile ma trwać pauza pomiędzy kolejnym wykonaniem pętli zjadającej pamięć


Jeśli przyjąć, że OBJECT_SIZE_MB= 20 oraz LOOP_TIME = 5 to pętla wykona się 85 razy zjadając 1700 MB pamięci RAM przy założeniu, że rozmiar sterty ustawiony w Dockerfile wynosi odpowiednio
ENV JAVA_XMS=512m , ENV JAVA_XMX=1800m. Można więc przez ponad 7 minut obserwować jak rośnie zużycie pamięci i doprowadza w końcu do wywalenia się poda błędem CrashLoopBackOff

public class MemoryEater {
    public static void main(String[] args) {
        int sleepBeforeStart = getEnvAsInt("SLEEP_BEFORE_START", 10);
        int objectSizeMB = getEnvAsInt("OBJECT_SIZE_MB", 10);
        int loopTimeSec = getEnvAsInt("LOOP_TIME", 1); // czas między kolejnymi krokami w sekundach

        System.out.println("Konfiguracja:");
        System.out.println(" - SLEEP_BEFORE_START = " + sleepBeforeStart + "s");
        System.out.println(" - OBJECT_SIZE_MB = " + objectSizeMB + " MB");
        System.out.println(" - LOOP_TIME = " + loopTimeSec + "s");

        try {
            Thread.sleep(sleepBeforeStart * 1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        java.util.List<byte[]> memoryLeak = new java.util.ArrayList<>();

        while (true) {
            try {
                byte[] block = new byte[objectSizeMB * 1024 * 1024];
                memoryLeak.add(block);

                int blocks = memoryLeak.size();
                int totalMB = blocks * objectSizeMB;

                System.out.println(
                    "Dodano blok " + objectSizeMB + "MB, rozmiar listy = " + blocks + ", Suma RAM: " + totalMB + " MB"
                );

                Thread.sleep(loopTimeSec * 1000L);
            } catch (OutOfMemoryError e) {
                System.err.println("💥 OutOfMemoryError złapany!");
                e.printStackTrace();
                break;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("Koniec działania MemoryEater.");
    }

    private static int getEnvAsInt(String name, int defaultValue) {
        String value = System.getenv(name);
        if (value != null) {
            try {
                return Integer.parseInt(value);
            } catch (NumberFormatException e) {
                System.err.println("Niepoprawna wartość dla " + name + ": " + value + " (używam domyślnej " + defaultValue + ")");
            }
        }
        return defaultValue;
    }
}

Następnie należy przygotować odpowiedni Dockerfile, który posłuży do zbudowania obrazu aplikacji. Będą w nim parametry, które kontrolują rozmiar sterty (heap) w Javie czyli tej części pamięci, z której korzystają obiekty aplikacji.

-Xms512m

Xms = initial heap size (rozmiar początkowy sterty).

512m = 512 megabajtów.

JVM przy starcie od razu zarezerwuje 512 MB RAM na heap.

Dzięki temu:

nie musi dynamicznie powiększać sterty od małych wartości (co spowalnia GC),

start aplikacji może być stabilniejszy przy dużych systemach.

-Xmx1800m

Xmx = maximum heap size (maksymalny rozmiar sterty).

1800m = 1800 megabajtów (~1.8 GB).

To jest twardy limit sterty w JVM.

Jeśli aplikacja spróbuje zaalokować więcej obiektów niż się zmieści to wywoła to błąd java.lang.OutOfMemoryError: Java heap space.

FROM eclipse-temurin:17-jdk-alpine
RUN apk add --no-cache tzdata \
    && cp /usr/share/zoneinfo/Europe/Warsaw /etc/localtime \
    && echo "Europe/Warsaw" > /etc/timezone
WORKDIR /app
COPY MemoryEater.java /app
RUN javac MemoryEater.java
ENV JAVA_XMS=512m
ENV JAVA_XMX=1800m
CMD ["sh", "-c", "java -Xms$JAVA_XMS -Xmx$JAVA_XMX MemoryEater"]

Teraz możemy przystąpić do utworzenia projektu o nazwie java-memory-eater oraz build configa i image stream-a o wskazanej dowolnej nazwie np. java-memory-eater-build

oc new-project java-memory-eater
# stworzenie build nowego obrazu bazującego na Dockerfile 
oc new-build --strategy=docker --binary --name=java-memory-eater-build

Następny krokiem jest zbudowanie obrazu czyli builda o nazwie java-memory-eater-build

cd /home/bastuser/repo/java-memory-eater
oc start-build java-memory-eater-build --from-dir=. --follow

Weryfikujemy czy powyższy build wykonał się poprawnie.

# sprawdzenie dostępnych buildów w projekcie
oc get builds
NAME                        TYPE     FROM     STATUS                            STARTED              DURATION
java-memory-eater-build-1   Docker   Binary   Failed (ManageDockerfileFailed)   About a minute ago   3s
java-memory-eater-build-2   Docker   Binary   Failed (GenericBuildFailed)       About a minute ago   6s
java-memory-eater-build-3   Docker   Binary   Complete                          36 seconds ago       18s

# przejrzenie logów z builda
oc logs -f build/java-memory-eater-build-2
Receiving source from STDIN as archive ...
time="2025-10-03T07:03:08Z" level=info msg="Not using native diff for overlay, this may cause degraded performance for building images: kernel has CONFIG_OVERLAY_FS_REDIRECT_DIR enabled"
I1003 07:03:08.215302       1 defaults.go:112] Defaulting to storage driver "overlay" with options [mountopt=metacopy=on].
Caching blobs under "/var/cache/blobs".
error: build error: no FROM image in Dockerfile

# ostatnie zdarzenia w projekcie gdzie widać szczegóły powstawania builda w projekcie
oc get events --sort-by='{.lastTimestamp}' -n java-memory-eater

Dodanie configmapy do projektu

Tworzymy obiekt configmap o nazwie cm-java-memory-eater przechowujący zmienne dla aplikacji java-memory-eater

oc create configmap cm-java-memory-eater \
  --from-literal=SLEEP_BEFORE_START=5 \
  --from-literal=OBJECT_SIZE_MB=20 \
  --from-literal=LOOP_TIME=5

# sprawdzamy czy istnieje taki obiekt cm
oc get cm
NAME                                   DATA   AGE
cm-java-memory-eater                   3      3s

# szczegółu obiektu cm cm-java-memory-eater
 oc describe cm/cm-java-memory-eater
Name:         cm-java-memory-eater
Namespace:    java-memory-eater
Labels:       <none>
Annotations:  <none>
Data
====
LOOP_TIME:
----
5
OBJECT_SIZE_MB:
----
20
SLEEP_BEFORE_START:
----
5
BinaryData
====
Events:  <none>

Utworzenie aplikacji OpenShift

Teraz możemy utworzyć aplikację na bazie istniejącego builda jako deployment-config do którego możemy dodać pvc oraz configmap.

# aplikacja czyli pody mają się zaczynąc od java-memoryeater-app
oc new-app java-memory-eater-build:latest --name=java-memory-eater-app --as-deployment-config

Podłączenie configmap-y do deploymentconfig.

oc set env dc/java-memory-eater-app --from=configmap/cm-java-memory-eater
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
deploymentconfig.apps.openshift.io/java-memory-eater-app updated

#weryfikacja
oc set env dc/java-memory-eater-app --list
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
# deploymentconfigs/java-memory-eater-app, container java-memory-eater-build
# LOOP_TIME from configmap cm-java-memory-eater, key LOOP_TIME
# OBJECT_SIZE_MB from configmap cm-java-memory-eater, key OBJECT_SIZE_MB
# SLEEP_BEFORE_START from configmap cm-java-memory-eater, key SLEEP_BEFORE_START

Restart aplikacji aby dostała nowe ustawienia środowiskowe z configmap.

oc rollout latest dc/java-memory-eater-app
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
Warning: apps.openshift.io/v1 DeploymentRequest is deprecated in v4.14+, unavailable in v4.10000+
deploymentconfig.apps.openshift.io/java-memory-eater-app rolled out

Monitorowanie logów POD-a

Z poziomu konsoli tekstowej oc logs -f <nazwa poda>

oc logs -f java-memory-eater-app-3-sw2vm

Konfiguracja:
 - SLEEP_BEFORE_START = 5s
 - OBJECT_SIZE_MB = 20 MB
 - LEAK_DURATION_SEC = 120s
Dodano blok 20MB, rozmiar listy = 1, Suma RAM: 20 MB
Dodano blok 20MB, rozmiar listy = 2, Suma RAM: 40 MB
Dodano blok 20MB, rozmiar listy = 3, Suma RAM: 60 MB
...
💥 OutOfMemoryError złapany!
java.lang.OutOfMemoryError: Java heap space
        at MemoryEater.main(MemoryEater.java:23)
Koniec działania MemoryEater.

Z poziomu konsoli graficznej jako Developer – projekt – pod – zakładka Logs

Monitorowanie wykorzystanie pamięci przez POD-a

Z poziomu konsoli tekstowej

# sprawdzamy które pody wykorzystują najwięcej zasobów na klastrze
oc adm top pods
NAME                            CPU(cores)   MEMORY(bytes)
java-memory-eater-app-3-8c9xb   54m          2268Mi

# sprawdzamy które pody wykorzystują najwięcej zasobów na klastrze w konkretnym projekcie
oc adm top pods -n java-memory-eater
NAME                            CPU(cores)   MEMORY(bytes)
java-memory-eater-app-3-8c9xb   54m          2268Mi

# sprawdzamy które pody wykorzystują najwięcej zasobów na klastrze w konkretnym projekcie (w pętli)
watch -n 2 oc adm top pods -n java-memory-eater

# sprawdzamy na którego workera na klastrze trafił pod
oc get pods -o wide
NAME                              READY   STATUS      RESTARTS      AGE     IP            NODE        NOMINATED NODE   READINESS GATES
java-memory-eater-app-3-8c9xb     1/1     Running     3 (42s ago)   5m48s   10.129.2.15   compute-3   <none>           <none>
java-memory-eater-app-3-deploy    0/1     Completed   0             5m49s   10.129.2.14   compute-3   <none>           <none>
java-memory-eater-build-3-build   0/1     Completed   0             22m     10.129.2.9    compute-3   <none>           <none>

# sprawdzamy zuzyćie zasobów na workerze w klastrze
oc adm top nodes
NAME              CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
compute-1         202m         5%     3510Mi          23%
compute-2         230m         6%     3350Mi          22%
compute-3         66m          1%     2477Mi          16%
control-plane-1   373m         10%    5533Mi          37%
control-plane-2   316m         9%     4046Mi          27%
control-plane-3   281m         8%     3866Mi          26%

Z poziomu konsoli graficznej jako Developer – projekt – pod

Ustawianie limitów dla pamięci RAM i CPU

Celem tego zadania jest ustawienie niższych limitów dla pamięci np. 1 GB RAM dla POD-a za pomocą mechanizmu limitów zasobów dostępnych w OpenShift.

  1. Requests

Definicja: Minimalna ilość zasobów, którą kontener potrzebuje do działania.

Rola: Scheduler K8s/OS używa tego, żeby zdecydować, na którym node uruchomić poda.

Przykład:
Jeśli pod ma requests.memory=128Mi, scheduler szuka noda, na którym jest co najmniej 128 MiB wolnej pamięci „zagwarantowanej”.

👉 requests = gwarancja, że aplikacja dostanie co najmniej tyle.

  1. Limits

Definicja: Maksymalna ilość zasobów, którą kontener może zużyć.

Rola: To jest twardy sufit – kontener nie może użyć więcej.

Dla pamięci (limits.memory):

Jeśli aplikacja spróbuje użyć więcej niż limit → kubelet ubije kontener (OOMKilled).

Dla CPU (limits.cpu):

Proces nie zostanie ubity, ale będzie dławiony (throttling) przez CFS (Completely Fair Scheduler) w jądrze.

👉 limits = sufit, powyżej którego kontener nie może pójść.

🔹 Różne scenariusze
1️⃣ Brak requests i limits

Pod działa jako BestEffort.

Może używać wszystkiego, co jest wolne, ale nie ma żadnych gwarancji.

Najczęściej pierwszy do zabicia przy presji pamięci na nodzie.

2️⃣ Tylko limits ustawione

Scheduler nie wie, ile „naprawdę” pod potrzebuje.

Może wrzucić kilka podów na jeden node, bo myśli, że każdy jest lekki → w praktyce węzeł się przepełnia i zaczynają się OOM’y.

Klasa QoS: Burstable.

3️⃣ Tylko requests ustawione

Scheduler rezerwuje miejsce, ale brak limitu = kontener może „zajeść” cały node.

Ryzykowne w środowiskach multi-tenantowych.

4️⃣ Ustawione i requests, i limits

Najbardziej przewidywalne.

Scheduler rezerwuje requests.

Kernel/Kubelet pilnuje, żeby nie przekroczyć limits.

Klasa QoS zależy od tego, czy wartości są równe:

requests = limits → QoS Guaranteed (najwyższy priorytet, najmniejsza szansa OOMKill).

requests < limits → QoS Burstable (może używać więcej niż gwarantowane, do limitu).

# ustawiamy parametry, że pod nie moze zużyc w trakcie swojej pracy wiecej niż 1 GB pamięci RAM
oc set resources dc/java-memory-eater-app --limits=memory=1024Mi,cpu=200m --requests=memory=256Mi,cpu=100m -n java-memory-eater

# restart poda
oc rollout latest dc/java-memory-eater-app

Sprawdzenie jakie limity RAM / CPU obowiązują danego POD-a. Sprawdzamy sekcję Limits i Requests.

oc describe pod java-memory-eater-app-5-mbw8b

Name:             java-memory-eater-app-5-mbw8b
Namespace:        java-memory-eater
Priority:         0
Service Account:  default
Node:             compute-3/192.168.40.63
Start Time:       Fri, 03 Oct 2025 12:19:07 +0200
Labels:           deployment=java-memory-eater-app-5
                  deploymentconfig=java-memory-eater-app
Annotations:      k8s.ovn.org/pod-networks:
                    {"default":{"ip_addresses":["10.129.2.45/23"],"mac_address":"0a:58:0a:81:02:2d","gateway_ips":["10.129.2.1"],"routes":[{"dest":"10.128.0.0...
                  k8s.v1.cni.cncf.io/network-status:
                    [{
                        "name": "ovn-kubernetes",
                        "interface": "eth0",
                        "ips": [
                            "10.129.2.45"
                        ],
                        "mac": "0a:58:0a:81:02:2d",
                        "default": true,
                        "dns": {}
                    }]
                  openshift.io/deployment-config.latest-version: 5
                  openshift.io/deployment-config.name: java-memory-eater-app
                  openshift.io/deployment.name: java-memory-eater-app-5
                  openshift.io/generated-by: OpenShiftNewApp
                  openshift.io/scc: restricted-v2
                  seccomp.security.alpha.kubernetes.io/pod: runtime/default
Status:           Running
SeccompProfile:   RuntimeDefault
IP:               10.129.2.45
IPs:
  IP:           10.129.2.45
Controlled By:  ReplicationController/java-memory-eater-app-5
Containers:
  java-memory-eater-build:
    Container ID:   cri-o://a1f1214cdd88d641ea40bd7bf87d2ba5e30726ffcf44f0868e4dc1b0d25d3461
    Image:          image-registry.openshift-image-registry.svc:5000/java-memory-eater/java-memory-eater-build@sha256:b7c529aade50284c8135989a3cdb5150b557d48f980941a9fc60e0ab112d7709
    Image ID:       3b3b978ac3f97932d21d3c0fb9f4f1877ba25bdda40583225f0b929cfd24a08e
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Fri, 03 Oct 2025 12:19:07 +0200
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     200m
      memory:  1Gi
    Requests:
      cpu:     100m
      memory:  256Mi
    Environment:
      LOOP_TIME:           <set to the key 'LOOP_TIME' of config map 'cm-java-memory-eater'>           Optional: false
      OBJECT_SIZE_MB:      <set to the key 'OBJECT_SIZE_MB' of config map 'cm-java-memory-eater'>      Optional: false
      SLEEP_BEFORE_START:  <set to the key 'SLEEP_BEFORE_START' of config map 'cm-java-memory-eater'>  Optional: false
...

Z takimi limitami jak powyżej POD zostanie zrestartowany przy ok. 800 MB zajętości pamięci RAM.

Dlaczego ~800 MB, a nie 1024 MB?
JVM to nie tylko heap (-Xmx).
Mamy też:
Metaspace (klasy, refleksja) – zwykle dziesiątki MB.
Thread stacks (po 1 MB na wątek).
Native memory (alokacje off-heap, biblioteki JNI, GC metadata).
Realnie heap (-Xmx) musi być zapasowo niższy niż limit poda, bo inaczej proces zostanie zabity przez kubelet zanim heap dojdzie do sufitów.

W tym przypadku: kubelet ubija proces przy ~800 MB heap + reszta natywna ~ 1 GB.

 oc logs -f java-memory-eater-app-5-mbw8b
Konfiguracja:
 - SLEEP_BEFORE_START = 5s
 - OBJECT_SIZE_MB = 20 MB
 - LOOP_TIME = 5s
Dodano blok 20MB, rozmiar listy = 1, Suma RAM: 20 MB
Dodano blok 20MB, rozmiar listy = 2, Suma RAM: 40 MB
Dodano blok 20MB, rozmiar listy = 3, Suma RAM: 60 MB
...

Dodano blok 20MB, rozmiar listy = 37, Suma RAM: 740 MB
Dodano blok 20MB, rozmiar listy = 38, Suma RAM: 760 MB
Dodano blok 20MB, rozmiar listy = 39, Suma RAM: 780 MB
Dodano blok 20MB, rozmiar listy = 40, Suma RAM: 800 MB

Poprawiony kod Dockerfile z obsługą SnakeYAML do parsowania plików yml oraz prawidłowym czasem wewnątrz poda.
Dla zapewnienia długiej dostępności warto pobrać plik snakeyaml-2.2.jar na dysk.

cd /home/bastuser/repo/java-hello-application-yml
wget https://repo1.maven.org/maven2/org/yaml/snakeyaml/2.2/snakeyaml-2.2.jar
FROM eclipse-temurin:17-jdk-alpine
RUN apk add --no-cache tzdata \
    && cp /usr/share/zoneinfo/Europe/Warsaw /etc/localtime \
    && echo "Europe/Warsaw" > /etc/timezone
WORKDIR /app
# Skopiuj źródła i bibliotekę
COPY JavaHello.java /app
COPY snakeyaml-2.2.jar /app
# Kompilacja z biblioteką w classpath
RUN javac -cp snakeyaml-2.2.jar JavaHello.java
# Uruchomienie z biblioteką w classpath
CMD ["java", "-cp", ".:snakeyaml-2.2.jar", "JavaHello"]

Wykonanie nowego buildconfiga, nowego imagestream i nowego builda oraz deployment configu

cd /home/bastuser/repo/java-hello-application-yml
oc new-build --strategy=docker --binary --name=java-hello-yaml-build
oc start-build java-hello-yaml-build --from-dir=. --follow
oc new-app java-hello-yaml-build:latest --name=java-hello-app-yaml --as-deployment-config

# zamontowanie configmapy do nowego dc java-hello-app-yaml
oc set volume dc/java-hello-app-yaml \
  --add \
  --name=config-volume \
  --mount-path=/config \
  --configmap-name=cm-java-hello
  
 # zamontowanie volumeny PVC do dc java-hello-app-yaml
 oc set volume dc/java-hello-app-yaml \
  --add \
  --name=downloads \
  --mount-path=/downloads \
  --claim-name=java-hello-pvc
  
# restart poda
oc rollout latest dc/java-hello-app-yaml

Sprawdzenie poprawności konfiguracji i działania poda aplikacji

# sprawdzamy logi poda
oc logs -f java-hello-app-yaml-10-dq7rk
Witaj Zbychu dziś jest 2025-10-02 16:48:27
Witaj Zbychu dziś jest 2025-10-02 16:48:37

# oc get pods, sprawdzamy nazwę poda i wchodzimy do niego 
oc exec -it java-hello-app-yaml-10-dq7rk sh
 
# w środku poda
/app $ cat /config/application.yml
appsettings:
  user_name: "Zbychu"
  loop_time: "10"
  file_path: "/downloads/status.txt"
  
/app $ ls -la /downloads/status.txt
-rw-r--r-- 1 1000710000 root 14256 Oct  2 16:30 /downloads/status.txt

Usunięcie zawartości projektu oraz samego projektu

# usunięcie zawartości bez usuwania projketu
oc delete all --all -n java-hello

pod "java-hello-app-yaml-10-deploy" deleted
pod "java-hello-app-yaml-10-dq7rk" deleted
pod "java-hello-app-yaml-7-deploy" deleted
pod "java-hello-app-yaml-8-deploy" deleted
pod "java-hello-app-yaml-9-deploy" deleted
pod "java-hello-build-1-build" deleted
pod "java-hello-yaml-build-1-build" deleted
pod "java-hello-yaml-build-2-build" deleted
pod "java-hello-yaml-build-3-build" deleted
pod "java-hello-yaml-build-4-build" deleted
replicationcontroller "java-hello-app-yaml-1" deleted
replicationcontroller "java-hello-app-yaml-10" deleted
replicationcontroller "java-hello-app-yaml-2" deleted
replicationcontroller "java-hello-app-yaml-3" deleted
replicationcontroller "java-hello-app-yaml-4" deleted
replicationcontroller "java-hello-app-yaml-5" deleted
replicationcontroller "java-hello-app-yaml-6" deleted
replicationcontroller "java-hello-app-yaml-7" deleted
replicationcontroller "java-hello-app-yaml-8" deleted
replicationcontroller "java-hello-app-yaml-9" deleted
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
deploymentconfig.apps.openshift.io "java-hello-app-yaml" deleted
buildconfig.build.openshift.io "java-hello-build" deleted
buildconfig.build.openshift.io "java-hello-yaml-build" deleted
imagestream.image.openshift.io "java-hello-build" deleted
imagestream.image.openshift.io "java-hello-yaml-build" deleted

# usuniecie wszystkich configmap
oc delete configmap --all -n java-hello

# usuniecie wszystkich secretow
oc delete secret --all -n java-hello

#usuniecie wszystkich pvc
oc delete pvc --all -n java-hello

# usunięcie całęgo projektu
oc delete project java-hello
project.project.openshift.io "java-hello" deleted