Oto opis, jak stworzyć z palca bardzo prosty ale poprawny obraz dockerowy. Najpierw stworzę obraz pusty - więc nieuruchamialny, ale poza tym poprawny. Potem stworzę obraz dający się uruchomić.
Najpierw tworzę tar z systemem plików - czyli tak zwaną warstwę. Ja chcę stworzyć obraz pusty (nie mający żadnych plików ani katalogów), więc tworzę pustego tara (sposobem ze stack exchange):
$ head --bytes=10240 /dev/zero > mylayer.tar
Potem sprawdzam, jaka jest suma kontrolna tego tara, bo będę ją zaraz potrzebował:
$ sha256sum mylayer.tar
Mi ta suma wyszła 84ff92691f909a05b224e1c56abb4864f01b4f8e3c854e4bb4c7baf1d3f6d652.
Potem piszę (w tym samym katalogu) dwa pliki konfiguracyjne. Najpierw plik z opisem warstwy, pod nazwą mylayer.json, o takiej treści:
{ "id": "my_layer", "created": "2023-06-12T14:00:00Z", "container_config": {}, "rootfs": { "type": "layers", "diff_ids": [ "sha256:84ff92691f909a05b224e1c56abb4864f01b4f8e3c854e4bb4c7baf1d3f6d652" ] } }
Widzisz, że w tym pliku użyłem tę sumę kontrolną, co przed chwilą sprawdzałem, tak?
Potem tworzę plik konfiguracyjny z opisem całego obrazu - który mógłby mieć wiele warstw, ale u mnie ma tylko jedną. To jest plik o nazwie manifest.json, jego treść to:
[ { "Config": "mylayer.json", "Layers": ["mylayer.tar"] } ]
Razem zawartość mojego katalogu to:
$ ls manifest.json mylayer.json mylayer.tar
Pakuję te trzy pliki tarem:
$ tar -c mylayer.tar manifest.json mylayer.json > ../image.tar
I takim to sposobem dostałem prosty, poprawny obraz dockerowy. Mogę go załadować do dockera poleceniem:
$ docker load < ../image.tar 84ff92691f90: Loading layer [==================================================>] 10.24kB/10.24kB Loaded image ID: sha256:50300b2f83fc25768e1cf832278d6c9026b4d75ed73c87bbc8f5e4f0c2701318
To polecenie kopiuje rzeczy z tego obrazu do katalogu /var/lib/docker/image. Umieszcza je tam w swoim dziwnym formacie, którego nie rozumiem, więc nie licz, że jak tam zajdziesz, to po prostu znajdziesz tam wkopiowany swój plik image.tar.
Z polecenia docker load nie poszły żadne błędy, co dowodzi, że ten obraz jest poprawny. Wyświetlił się identyfikator, który docker nadał temu obrazowi (czyli napis 50300b2f83fc25768e1cf832278d6c9026b4d75ed73c87bbc8f5e4f0c2701318). Zapamiętaj go, będzie ci zaraz potrzebny.
Teraz możemy spróbować uruchomić ten obraz poleceniem:
$ docker run 50300b2f83fc25768e1cf832278d6c9026b4d75ed73c87bbc8f5e4f0c2701318 docker: Error response from daemon: No command specified. See 'docker run --help'.
Nie zadziałało, co nie jest dziwne. Obraz nie ma w swoich plikach konfiguracyjnych podanego, jaki program ma być odpalony na jego starcie, nie podałem tego też przy uruchamianiu, no to docker mówi mi No command specified.
Nie bardzo mam co podawać, bo w mojej warstwie (w tym pustym tarze) nie mam żadnych plików wykonywalnych, ale mogę podać nieistniejącą ścieżkę, żeby chociaż zobaczyć, jak docker nie znajduje programu:
$ docker run 50300b2f83fc25768e1cf832278d6c9026b4d75ed73c87bbc8f5e4f0c2701318 /fiku/miku WARNING: The requested image's platform (unknown) does not match the detected host platform (linux/amd64) and no specific platform was requested docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "/fiku/miku": stat /fiku/miku: no such file or directory: unknown. ERRO[0000] error waiting for container: context canceled
Jak widać, docker nie znalazł programu fiku/miku.
To koniec sekcji o tworzeniu pustego obrazu. Omawiane pliki jakbyś chciał obejrzeć, to są tu.
A teraz zrobię obraz, który będzie się dało nie tylko załadować, ale i uruchomić. Jego warstwa tym razem nie będzie pusta, ale będzie miała w sobie jeden wykonywalny plik - program wypisujący napis "Hello, world!".
Zacznę od zrobienia tego programu. Tworzę taki plik o nazwie hello.c:
#includeint main() { printf("Hello, world!\n"); return 0; }
Kompiluję go statycznie (to ważne że statycznie - nie chcę, żeby ten program do działania potrzebował jakichś bibliotek) poleceniem:
$ gcc -static hello.c -o hello
Sprawdzam, czy program się uruchamia:
$ ./hello Hello, world!
Dobrze. To teraz stworzę tara, w którym będzie tylko ten jeden plik - mój plik wykonywalny hello (ten tar zaraz posłuży mi jako warstwa w moim obrazie):
$ tar cf mylayer.tar hello
Teraz będę tworzył, ładował i uruchamiał obraz tak samo jak poprzednio. Znaczy, najpierw sprawdzam sumę kontrolną warstwy:
$ sha256sum mylayer.tar
Mi ta suma wyszła 47ce307a2e16442b328bb821d69699007351ef6f26b278342c70cd799d272e1a Tobie może wyjść inna, choćby dlatego, że data tego pliku hello u ciebie będzie inna.
Potem piszę (w tym samym katalogu) dwa pliki konfiguracyjne. Najpierw plik z opisem warstwy, pod nazwą mylayer.json, o takiej treści:
{ "id": "my_layer", "created": "2023-06-12T14:00:00Z", "container_config": {}, "rootfs": { "type": "layers", "diff_ids": [ "sha256:47ce307a2e16442b328bb821d69699007351ef6f26b278342c70cd799d272e1a" ] } }
Widzisz, że w tym pliku użyłem tę sumę kontrolną, co przed chwilą sprawdzałem, tak?
Potem tworzę plik konfiguracyjny z opisem całego obrazu - który mógłby mieć wiele warstw, ale u mnie ma tylko jedną. To jest plik o nazwie manifest.json, jego treść to:
[ { "Config": "mylayer.json", "Layers": ["mylayer.tar"] } ]
Razem zawartość mojego katalogu to:
$ ls hello hello.c manifest.json mylayer.json mylayer.tar
Pakuję te trzy pliki - warstwę i dwa pliki konfiguracyjne - tarem:
$ tar -c mylayer.tar manifest.json mylayer.json > ../image.tar
I takim to sposobem dostałem obraz dockerowy. Mogę go załadować do dockera poleceniem:
$ docker load < ../image.tar 47ce307a2e16: Loading layer [==================================================>] 880.6kB/880.6kB Loaded image ID: sha256:840877bd1e3faef46a6ea8699f5c0ef9cdf33fcae49a5e44274a25061841c50f
Z polecenia docker load nie poszły żadne błędy, co dowodzi, że ten obraz jest poprawny. Wyświetlił się identyfikator, który docker nadał temu obrazowi (czyli napis 840877bd1e3faef46a6ea8699f5c0ef9cdf33fcae49a5e44274a25061841c50f). Zapamiętaj go, będzie ci zaraz potrzebny.
Teraz możemy spróbować uruchomić ten obraz poleceniem:
$ docker run 840877bd1e3faef46a6ea8699f5c0ef9cdf33fcae49a5e44274a25061841c50f docker: Error response from daemon: No command specified. See 'docker run --help'.
Nie zadziałało, co nie jest dziwne. Obraz nie ma w swoich plikach konfiguracyjnych podanego, jaki program ma być odpalony na jego starcie, nie podałem tego też przy uruchamianiu, no to docker mówi mi No command specified. No to podam, żeby na starcie uruchomił program /hello (który, jak pamiętamy, jest w naszej warstwie):
$ docker run 840877bd1e3faef46a6ea8699f5c0ef9cdf33fcae49a5e44274a25061841c50f /hello WARNING: The requested image's platform (unknown) does not match the detected host platform (linux/amd64) and no specific platform was requested Hello, world!
Zadziałało.
To koniec sekcji o tworzeniu obrazu, który da się uruchomić. Omawiane pliki jakbyś chciał obejrzeć, to są tu.