Skip to content

Latest commit

 

History

22 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Programowanie Aplikacji w Chmurze Obliczeniowej

  dr inż. Sławomir Wojciech Przyłucki

Termin zajęć:
  wtorek, godz. 14:15,

Numer na liście:

  9,

Imię i nazwisko:

  Paweł Pieczykolan,
  III rok studiów inżynierskich, IO 6.7.

Budowanie, uruchamianie i publikowanie obrazu serwera języka C o małej wadze

1. Informacje ogólne

2. Część obowiązkowa

» 2.1 Przygotowanie wymaganych plikówy

» 2.2 Zbudowanie obrazu

» 2.3 Inspekcja obrazu

» 2.4 Uruchomienie kontenera

» 2.5 Weryfikacja działania

3. Część dodatkowa

» 3.1 Modyfikacja pliku Dockerfile

» 3.2 Utworzenie i użycie sterownika Docker Buildx

» 3.3 Zbudowanie obrazu z użyciem Docker Buildx

» 3.4 Inspekcja obrazu

» 3.5 Uruchomienie kontenera

» 3.6 Weryfikacja działania

» 3.7 Porównanie obrazów

4. Podsumowanie


1. Informacje ogólne

Podstawowe informacje dotyczące opracowania


W części obowiązkowej należało stworzyć serwer w dowolnym języku programowania, który działa w kontenerze « Docker » i realizuje określoną funkcjonalność: po uruchomieniu kontenera, serwer zapisuje w logach datę uruchomienia, imię i nazwisko autora (studenta) oraz port « TCP », na którym nasłuchuje na zgłoszenia klienta. Na podstawie adresu « IP » klienta, serwer wyświetla stronę informującą o adresie « IP » klienta oraz o dacie i godzinie w jego strefie czasowej. Należało opracować plik Dockerfile, który umożliwia zbudowanie obrazu kontenera z serwerem, z optymalizacją wieloetapowego budowania obrazu, użycia warstwy scratch, optymalizacji cache, zawartości i ilości warstw. Plik « Dockerfile » miał zawierać informację o autorze.

Aplikacja serwerowa, która wyświetla stronę internetową « HTML », została napisana w języku « C ». Wykorzystano obrazy « Alpine » w wersji 3.19.1 oraz « Scratch » w celu drastycznego zmniejszenia objętości obrazu (> 1 MB).

Część dodatkowa wymagała zbudowania obrazu kontenera z aplikacją opracowaną w części obowiązkowej, które działałyby na architekturach: linux/arm64 oraz linux/amd64, wykorzystując sterownik « Docker Container ». « Dockerfile » powinien wykorzystywać rozszerzony frontend, zawierać deklaracje wykorzystania cache i umożliwiać bezpośrednie wykorzystanie kodów aplikacji umieszczonych w publicznym repozytorium na GitHub. Opracowane obrazy należało przesłać do swojego repozytorium na DockerHub.

W sprawozdaniu należało podać wykorzystane instrukcje wraz z wynikiem ich działania oraz ewentualnymi komentarzami, unikając przekazywania danych wrażliwych. Wszystkie informacje wymagane w sprawozdaniu z części nieobowiązkowej należało opracować w postaci pliku zadanie1_dod.md. W repozytorium należało umieścić opracowane źródła dla serwera, przygotowany plik « Dockerfile » oraz inne niezbędne pliki. Jako sprawozdanie należało przekazać wyłącznie plik tekstowy zawierający linki do użytego repozytorium na GitHub oraz DockerHub.

Note

W realizacji zadania napisania serwera w dowolnym języku, wykorzystano informacje pochodzące ze strony dev.to.

Zadanie wykonano na maszynie wirtualnej VM VirtualBox, na systemie Ubuntu w wersji 20.04 LTS.


2. Część obowiązkowa

Przygotowanie plików serwerowych, zbudowanie obrazu, uruchomienie kontenera oraz weryfikacja działania serwera


2.1. Przygotowanie wymaganych plików

Utworzono plik « Dockerfile » spełniający wymogi zadania.

#️⃣ Etap 1: Budowanie pliku binarnego serwera C
FROM alpine:3.19.1 AS builder

#️⃣ Ustawienie zmiennej środowiskowej BASE_VERSION, z domyślną wartościa v1
ARG BASE_VERSION
ENV APP_VERSION=${BASE_VERSION:-v1}

#️⃣ Instalacja kompilatora C i musl-dev
RUN apk add --no-cache build-base musl-dev \
    && rm -rf /var/cache/apk/*

#️⃣ Ustawienie katalogu roboczego
WORKDIR /app

#️⃣ Kopiowanie pliku źródłowego
COPY server.c .

#️⃣ Kompilacja statyczna, utworzenie pliku binarnego
RUN gcc -static -o server server.c

#️⃣ Kopiowanie pliku index.html
COPY index.html .

#️⃣ Etap 2: Tworzenie minimalnego obrazu
FROM scratch as final

#️⃣ Ustawienie zmiennej środowiskowej BASE_VERSION, z domyślną wartościa v1
ARG BASE_VERSION
ENV APP_VERSION=${BASE_VERSION:-v1}

#️⃣ Informacje o autorze, dokumentacji i licencji
LABEL org.opencontainers.image.authors="Paweł Pieczykolan"
LABEL org.opencontainers.image.documentation="https://github.com/97703/DockerLabZad1"
LABEL org.opencontainers.image.licenses="GNU v3.0"

#️⃣ Kopiowanie bin z etapu budowania
COPY --from=builder /app/server /server

#️⃣ Kopiowanie pliku index.html
COPY --from=builder /app/index.html /index.html

#️⃣ Ustawienie informacji o porcie
EXPOSE 8080

#️⃣ Komenda startowa
ENTRYPOINT ["/server"]

Rys. 1. Plik Dockerfile


Utworzono prosty plik serwera w języku « C » server.c oraz stronę internetową « HTML » index.html korzystającą ze skryptu « Javascript ».

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Informacje o Kliencie</title>
    <script>
        #️⃣ Wywołanie API do pobrania adresu IP
        fetch("https://api.ipify.org?format=json")
            #️⃣ Konwersja odpowiedzi na format JSON
            .then(response => response.json())
            #️⃣ Obsługa danych
            .then(data => {
                #️⃣ Wyświetlenie adresu IP
                document.getElementById("ip").innerText = data.ip;
                #️⃣ Pobranie danych dotyczących czasu związanych ze strefą czasową ze strony worldtimeapi
                return fetch("https://worldtimeapi.org/api/ip/" + data.ip);
            })
            #️⃣ Konwersja odpowiedzi na format JSON
            .then(response => response.json())
            #️⃣ Obsługa danych
            .then(data => {
                #️⃣ Utworzenie obiektu czasu
                var czas = new Date(data.utc_datetime);
                #️⃣ Wyświetlenie czasu
                document.getElementById("czas").innerText = czas.toLocaleString();
                #️⃣ Wyświetlenie strefy czasowej
                document.getElementById("strefa").innerText = data.timezone;
            })
            #️⃣ Obsługa błędów
            .catch(error => console.error('Błąd:', error));
    #️⃣ Zamknięcie sekcji skryptu
    </script>
</head>
<body>
    <h1>Informacje o Kliencie</h1>
    <p>Adres IP klienta: <span id="ip"></span></p>
    <p>Czas związany ze strefą czasową tego IP: <span id="czas"></span></p>
    <p>Strefa czasowa: <span id="strefa"></span></p>
</body>
</html>

Rys. 2. Plik index.html, analiza skryptu Javascript (komentarze)

2.2. Zbudowanie obrazu

Zbudowano obraz korzystając z pliku « Dockerfile ».

docker build --build-arg BASE_VERSION=v1.0 -t local/dockerzad1:v1

Rys. 3. Zbudowanie obrazu dockerzad1:v1

2.3. Inspekcja obrazu

Wyświetlono liczbę warstw nowoutworzonego obrazu.

docker history local/dockerzad1:v1

Rys. 4. Warstwy obrazu dockerzad1:v1

Sprawdzono obraz pod względem liczebności i jakości potencjalnych luk bezpieczeństwa.

docker scout quickview local/dockerzad1:v1

Rys. 5. Liczba i jakość luk bezpieczeństwa w obrazie dockerzad1:v1

Wyświetlono również jego objętość.

docker images --filter "reference=local/dockerzad1"

Rys. 6. Objętość obrazu części obowiązkowej

2.4. Uruchomienie kontenera

Uruchomiono kontener na podstawie nowoutworzonego obrazu.

docker run -p 8080:8080 --name dockerzad1 local/dockerzad1:v1

Rys. 7. Uruchomienie kontenera dockerzad1

Sprawdzono dziennik zdarzeń za pomocą

docker logs dockerzad1

Rys. 8. Dziennik zdarzeń dla kontenera dockerzad1 (fragment)

2.5. Weryfikacja działania

Uruchomiono przeglądarkę « Firefox » i sprawdzono działanie serwera.

Rys. 9. Efekt działania kontenera


3. Część dodatkowa

Zmodyfikowanie pliku Dockerfile, użycie Docker Buildx oraz Docker Scout


3.1. Modyfikacja pliku Dockerfile

Zmodyfikowano plik « Dockerfile » z części nieobowiązkowej.

Dodano:

  • # syntax=docker/dockerfile:1.4 – odwołanie do oficjalnej i stabilnej wersji obrazu dla rozszerzonych frontend-ów
  • do instrukcji « RUN » apk add --no-cache openssh-client git \ – instalacja klienta « OpenSSH » oraz « Git » w kontenerze « Docker »
  • do instrukcji « RUN » mkdir -p -m 0700 ~/.ssh – utworzenie foldera « .ssh » w katalogu domowym z uprawnieniami 700 (pełne uprawnienia dla właściciela katalogu), o ile taki katalog nie istnieje
  • do instrukcji « RUN » ssh-keyscan github.com >> ~/.ssh/known_hosts && eval $(ssh-agent) – konfiguracja środowiska do korzystania z GitHuba poprzez SSH; uruchomienie agenta SSH
  • RUN --mount=type=ssh git clone git@github.com:97703/DockerZad1.git DockerZad1 – klonowanie prywatnego repozytorium « Git », przy wykorzystaniu bezpiecznego dostępu « SSH »
  • mv /app/DockerZad1/server.c /app/DockerZad1/index.html /app – przeniesienie plików ze sklonowanego repozytorium
  • do pierwszej instrukcji « RUN » etapu pierwszego --mount=type=cache,target=/var/cache/apk – wskazuje folder « apk » dla mechanizmu cache'owania
  • do drugiej instrukcji « RUN » etapu pierwszego --mount=type=cache,target=/var/cache/gcc – wskazuje folder « gcc » dla mechanizmu cache'owania
+# syntax=docker/dockerfile:1.4
#️⃣ Etap 1: Budowanie pliku binarnego serwera C
FROM alpine:3.19.1 AS builder

#️⃣ Ustawienie zmiennej środowiskowej BASE_VERSION, z domyślną wartościa v1
ARG BASE_VERSION
ENV APP_VERSION=${BASE_VERSION:-v1}

#️⃣ Instalacja kompilatora C i musl-dev
+RUN --mount=type=cache,target=/var/cache/apk \
    apk add --no-cache build-base musl-dev \
+    #️⃣ Czyszczenie pamięci podręcznej apk
    && rm -rf /var/cache/apk/* \
+    #️⃣ Instalacja klienta SSH i GITa
+    && apk add --no-cache openssh-client git \
+    #️⃣ Konfiguracja SSH dla dostępu do GitHub
+    && mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts && eval $(ssh-agent)

#️⃣ Ustawienie katalogu roboczego
WORKDIR /app

-#️⃣ Kopiowanie pliku źródłowego
-COPY server.c .

-#️⃣ Kompilacja statyczna, utworzenie pliku binarnego
-RUN gcc -static -o server server.c

-#️⃣ Kopiowanie pliku index.html
-COPY index.html .

+#️⃣ Sklonowanie repozytorium i przeniesienie plików
+RUN --mount=type=ssh git clone git@github.com:97703/DockerZad1.git DockerZad1 \
+    && mv /app/DockerZad1/server.c /app/DockerZad1/index.html /app

+#️⃣ Kompilacja statyczna, utworzenie pliku binarnego
+RUN --mount=type=cache,target=/var/cache/gcc \
+    gcc -static -o server server.c

#️⃣ Etap 2: Tworzenie minimalnego obrazu
FROM scratch as final

#️⃣ Ustawienie zmiennej środowiskowej BASE_VERSION, z domyślną wartościa v1
ARG BASE_VERSION
ENV APP_VERSION=${BASE_VERSION:-v1}

#️⃣ Informacje o autorze, dokumentacji i licencji
LABEL org.opencontainers.image.authors="Paweł Pieczykolan"
LABEL org.opencontainers.image.documentation="https://github.com/97703/DockerLabZad1"
LABEL org.opencontainers.image.licenses="GNU v3.0"

#️⃣ Kopiowanie bin z etapu budowania
COPY --from=builder /app/server /server

#️⃣ Kopiowanie pliku index.html
COPY --from=builder /app/index.html /index.html

#️⃣ Ustawienie informacji o porcie
EXPOSE 8080

#️⃣ Komenda startowa
ENTRYPOINT ["/server"]

Rys. 10. Zmiany w pliku Dockerfile

3.2. Utworzenie i użycie sterownika Docker Buildx

Utworzono nowy sterownik Docker Buildx o nazwie dockerzad1.

docker buildx create --name dockerzad1 --driver docker-container --bootstrap

Nowoutworzony sterownik ustawiono jako domyślny za pomocą

docker buildx use dockerzad1

Rys. 11. Utworzenie i konfiguracja sterownika Docker Container

3.3. Zbudowanie obrazu z użyciem Docker Buildx

Zbudowano obraz w oparciu o polecenie Docker Buildx

docker buildx build -f Dockerfile-2 --sbom=true --provenance=mode=max --ssh default=$SSH_AUTH_SOCK --build-arg BASE_VERSION=v.1.0 --platform linux/arm64,linux/amd64 --cache-from=type=registry,ref=docker.io/ppieczykolan/dockerzad1-2:cache --cache-to=type=registry,ref=docker.io/ppieczykolan/dockerzad1-2:cache -t ppieczykolan/dockerzad1-2 --push .

, gdzie:

  • docker buildx build – polecenie służace do budowania obrazów przy użyciu BuildKit
  • -f Dockerfile-2 – określa nazwę pliku « Dockerfile »
  • --sbom=true – generowanie zestawienia materiałów oprogramowania (spisu komponentów oprogramowania użytych w celu zbudowania danej aplikacji)
  • provenance=mode=max – określa poziom szczegółowości rejestrowania etapów kompilacji
  • ssh default=$SSH_AUTH_SOCK – umożliwia procesowi kompilacji korzystanie z uwierzytelnienia « SSH »
  • --build-arg BASE_VERSION=v.1.0 – przekazuje argument wersji do pliku « Dockerfile »
  • --platform linux/arm64,linux/amd64 – określa platformy docelowe; platformy dla których obraz jest budowany
  • --cache-from=type=registry,ref=docker.io/ppieczykolan/dockerzad1-2:cache – określa źródło z którego można pobrać pamięć podręczną « cache »
  • --cache-to=type=registry,ref=docker.io/ppieczykolan/dockerzad1-2:cache – określa miejsce docelowe pamięci podręcznej « cache »; miejsce udostępnienia pamięci « cache »
  • -t ppieczykolan/dockerzad1-2 – określa miejsce docelowe obrazu (reporyztorium DockerHub lub dysk)
  • --push – nakazuje przesłanie obrazu do repozytorium
  • . – określa kontekst kompliacji

Rys. 12. Zbudowanie obrazu ze zmodyfikowanego pliku Dockerfile – ½

Rys. 13. Zbudowanie obrazu ze zmodyfikowanego pliku Dockerfile – ²⁄₂

Warning

Błąd

ERROR importing cache manifest from docker.io/ppieczykolan/dockerzad1-2:cache

nie jest istotny, gdyż jest to pierwsze budowanie obrazu – nie możemy pobrać « cache » z serwera ponieważ nie został on nigdy wcześniej udostępniony.

3.4. Inspekcja obrazu

Zezwolono na analizowanie obrazu przez usługę Docker Scout

Rys. 14. Podłączenie usługi Docker Scout pod obraz dockerzad1-2

Zweryfikowano zbudowany obraz pod kątem samej budowy.

docker buildx imagetools inspect ppieczykolan/dockerzad1-2

Rys. 15. Sprawdzenie obecności i liczebności luk bezpieczeństwa

Sprawdzono liczbę warstw dla nowego obrazu.

docker history ppieczykolan/dockerzad1-2

Rys. 16. Warstwy obrazu części dodatkowej

Sprawdzono również obecność luk bezpieczeństwa.

docker scout quickview ppieczykolan/dockerzad1-2

Rys. 17. Sprawdzenie obecności i liczebności luk bezpieczeństwa

Obraz jest dostępny pod tym adresem.

3.5. Uruchomienie kontenera

Zaciągnięto obraz ze zdalnego repozytorium za pomocą

docker pull ppieczykolan/dockerzad1-2

Rys. 18. Zaciągnięcie obrazu dockerzad1-2

Uruchomiono kontener na podstawie zaciągniętego obrazu.

docker run -p 8080:8080 --name dockerzad1-2 ppieczykolan/dockerzad1-2:latest

Rys. 19. Uruchomienie kontenera części dodatkowej

3.6. Weryfikacja działania

Zweryfikowano działanie kontenera części dodatkowej. Uruchomiono przeglądarkę i wpisano w pasku adresowym « http://localhost:8080 ».

Rys. 20. Efekt działania kontenera części dodatkowej

3.7. Porównanie obrazów

Porównano utworzone w tym rozwiązaniu obrazy pod względem objętości.

docker images

Rys. 21. Porównanie obrazów


4. Podsumowanie

Wnioski


Zadanie zostało zrealizowane pomyślnie.

W części obowiązkowej opracowano plik « Dockerfile » oraz przygotowano prosty serwer w języku « C », który działa w kontenerze « Docker ». Serwer ten wyświetla informacje o adresie « IP » klienta oraz o dacie i godzinie w jego strefie czasowej.

W części dodatkowej zbudowano obraz kontenera z aplikacją opracowaną wcześniej, który działa na różnych architekturach. Ponadto zastosowano rozszerzony frontend, mechanizm cache'owania oraz udostępniono obraz z cachem na DockerHub.

Wszystkie cele zadania zostały osiągnięte. Dzięki wykorzystaniu narzędzi takich jak Docker Buildx i Docker Scout, możliwe było zautomatyzowanie, ulepszenie procesu budowy obrazów i analiza obrazów pod kątem bezpieczeństwa, a zastosowanie warstwy Scratch w drugim etapie budowy pozwoliło na zmniejszenie objętości pliku do 315kB bez potencjalnych luk bezpieczeństwa.

About

Budowanie, uruchamianie i publikowanie obrazu serwera języka C o małej wadze

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages