Хочу разобрать, как создать переносимый образ Docker
с сервисом на борту под операционную систему Ubuntu 18.04
. Может кстати я что-то путаю, но действовать необходимо, только так можно понять что и для чего, а после дополнить пробелы или неясность. Это применимо для меня.
Шаг №1: На систему (хостовую) установлен Docker
Шаг №2: Как создается контейнер Docker.
Dockerfile содержит набор инструкций с аргументами. Каждая инструкция пишется заглавными буквами (например FROM). Инструкции обрабатываются сверху вниз. Каждая инструкция добавляет новый слой в образ и коминтит изменения. Docker исполняет инструкции, следуя процессу:
Запуск контейнера из образа
Исполнение инструкции и внесение изменений в контейнер
Запуск эквивалента docker commit для записи изменений в новый слой образа
Запуск нового контейнера из нового образа
Исполнение следующей инструкции в файле и повторение шагов процесса.
Это означает, что если исполнение Dockerfile остановится по какой-то причине (например инструкция не сможет завершиться), вы сможете использовать образ до этой стадии. Это очень полезно при отладке: вы можете запустить контейнер из образа интерактивно и узнать, почему инструкция не выполнилась, используя последний созданный образ.
Также Dockerfile поддерживает комментарии. Любая строчка, начинающаяся с # означает комментарий.
Первая инструкция в Dockerfile всегда должна быть FROM, указывающая, из какого образа нужно построить образ:
Пример
FROM centos:7
FROM ubuntu:18.04
Далее мы указываем инструкцию MAINTAINER, сообщающую Docker автора образа и его email. Это полезно, чтобы пользователи образа могли связаться с автором при необходимости.
FROM ubuntu:18.04
MAINTAINER Ollo Alexander <support@ekzorchik.ru>
Инструкция RUN исполняет команду в конкретном образе. В нашем примере с помощью ее мы обновляем APT репозитории и устанавливаем пакет с NGINX, затем создаем файл /usr/share/nginx/html/index.html. Для уменьшения количества слоем правильнее использовать:
RUN apt-get update && \
apt-get install htop && \
apt-get install mc && \
Т.е. RUN с символом амперсанда лучше чем много RUN и каждый делается что-то.
По-умолчанию инструкция RUN исполняется внутри оболочки с использованием обертки команд /bin/sh -c. Если вы запускаете инструкцию на платформе без оболочки или просто хотите выполнить инструкцию без оболочки, вы можете указать формат исполнения:
RUN [«apt-get», «install», «-y», «nginx»]
Мы используем этот формат для указания массива, содержащего команду для исполнения и параметры команды.
указываем инструкцию EXPOSE, которая говорит Docker, что приложение в контейнере должно использовать определенный порт в контейнере.
EXPOSE 80
Это не означает, что вы можете автоматически получать доступ к сервису, запущенному на порту контейнера (в нашем примере порт 80). По соображениям безопасности Docker не открывает порт автоматически, но ожидает, когда это сделает пользователь в команде docker run. Вы можете указать множество инструкций EXPOSE для указания, какие порты должны быть открыты. Также инструкция EXPOSE полезна для проброса портов между контейнерами.
Созданная директория — билд-окружение, в которой Docker вызывает контекст или строит контекст
ekzorchik@srv-bionic:~$ mkdir project
ekzorchik@srv-bionic:~$ cd project/
ekzorchik@srv-bionic:~/project$
ekzorchik@srv-bionic:~/project$ nano Dockerfile
FROM ubuntu:18.04
MAINTAINER Ollo Alexander <support@ekzorchik.ru>
RUN rm -rf /var/lib/apt/lists && \
apt-get update && \
apt-get install -y nginx && \
echo 'Hi, I am in your container' > /var/www/html/index.nginx-debian.html
EXPOSE 80
После не забываем сохранить внесенные изменения.
Создаю новый образ в текущей директории:
ekzorchik@srv-bionic:~/project$ docker build -t docker/nginx . Sending build context to Docker daemon 2.048kB Step 1/4 : FROM ubuntu:18.04 18.04: Pulling from library/ubuntu 2746a4a261c9: Pull complete 4c1d20cdee96: Pull complete 0d3160e1d0de: Pull complete c8e37668deea: Pull complete Digest: sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4 Status: Downloaded newer image for ubuntu:18.04 ---> 549b9b86cb8d -> в этот контейнер можно подключиться Step 2/4 : MAINTAINER Ollo Alexander <support@ekzorchik.ru> ---> Running in 3ca0bceba9e8 Removing intermediate container 3ca0bceba9e8 ---> 7a70ef80fc49 -> в этот контейнер можно подключиться Step 3/4 : RUN rm -rf /var/lib/apt/lists && apt-get update && apt-get install -y nginx && echo 'Hi, I am in your container' > /var/www/html/index.nginx-debian.html ---> Running in ec84f54f91c0 далее идет обновление информации о пакетах и обновление текуще установленных, а затем слой публикации на 80 порту ---> bd82038c9d16 -> в этот контейнер можно подключиться Step 4/4 : EXPOSE 80 ---> Running in 0a1a36894b95 Removing intermediate container 0a1a36894b95 ---> 89f1840327bb Successfully built 89f1840327bb -> в этот контейнер можно подключиться Successfully tagged docker/nginx:latest ekzorchik@srv-bionic:~/project$
Где конструкция запускаемых параметров:
-t docker → название репозитория, где будет храниться образ
nginx → имя образа.
Последний параметр — путь к папке с Dockerfile (Т.к. папка текущая то указывается символ точки '.')
Если на каком-то этапе произошла ошибка, то можно создать контейнер из предпоследнего шага с ID образа 89f1840327bb
docker run -i -t 89f1840327bb /bin/bash
и отладить исполнение.
Запускаю сформированный контейнер, также его можно запустить на каждом шагу для этого нужно просто указать его ID:
ekzorchik@srv-bionic:~/project$ docker run -i -t 89f1840327bb root@89f1840327bb:/# exit → Если хотите выйти из контейнера, введите exit. Контейнер остановится exit ekzorchik@srv-bionic:~/project$
Чтобы подключиться к данному Web
-сервису развернутому внутри окружения используется ключ «-p
» который перенаправляет порт локальной машины в порт контейнера
ekzorchik@srv-bionic:~/project$ docker build -t docker/nginx . ekzorchik@srv-bionic:~/project$ docker run -it -p 8080:80 docker/nginx root@160ad44ac5ee:/# /etc/init.d/nginx status * nginx is not running root@160ad44ac5ee:/# /etc/init.d/nginx start * Starting nginx nginx [ OK ] root@160ad44ac5ee:/# /etc/init.d/nginx status * nginx is running root@160ad44ac5ee:/# ekzorchik@navy:~$ ssh -l ekzorchik 172.33.33.6 ekzorchik@srv-bionic:~$ sudo netstat -tulpn | grep :8080 tcp6 0 0 :::8080 :::* LISTEN 3275/docker-proxy ekzorchik@srv-bionic:~$
root@160ad44ac5ee:/# exit exit ekzorchik@srv-bionic:~/project$
Заработало, т.е. Получается что я в Dockerfile
описываю окружение, запускаю и получаю что хотел получить. Т.е. я как архитектор продумываю все по порядку:
ekzorchik@srv-bionic:~/project$ nano Dockerfile #FROM -> REPOSITORY:TAG FROM ubuntu:18.04 MAINTAINER Ollo Alexander <support@ekzorchik.ru> RUN rm -rf /var/lib/apt/lists && \ apt-get update && \ apt-get install -y nginx && \ echo 'Hi, I am in your container' > /var/www/html/index.nginx-debian.html EXPOSE 80 ekzorchik@srv-bionic:~/project$ docker build -t docker/nginx . ekzorchik@srv-bionic:~/project$ docker run -it -p 8080:80 docker/nginx root@a85d65bfd0f4:/# /etc/init.d/nginx status * nginx is not running root@a85d65bfd0f4:/# root@a85d65bfd0f4:/# exit exit ekzorchik@srv-bionic:~/project$
но сервис опять не запущен
Почему?
Если при запуске docker-а под именем nginx использовать параметр, то при выходе докер все равно будет запущен:
ekzorchik@srv-bionic:~/project$ docker run -it --restart=always -p 8080:80 docker/nginx root@6e5a5a1a2bae:/#
только сервис внутри не поднят.
ekzorchik@srv-bionic:~$ cd project/ ekzorchik@srv-bionic:~/project$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6e5a5a1a2bae docker/nginx "/bin/bash" 20 seconds ago Up 19 seconds 0.0.0.0:8080->80/tcp beautiful_wu ekzorchik@srv-bionic:~/project$ ekzorchik@srv-bionic:~/project$ docker stop 6e5a5a1a2bae 6e5a5a1a2bae ekzorchik@srv-bionic:~/project$
Разбираюсь далее, выяснил, что нужно изменить Dockerfile
до вида:
ekzorchik@srv-bionic:~/project$ nano Dockerfile #FROM -> REPOSITORY:TAG FROM ubuntu:18.04 MAINTAINER Ollo Alexander <support@ekzorchik.ru> LABEL UPDATE SYSTEM RUN rm -rf /var/lib/apt/lists && \ apt-get update && \ apt-get install -y nginx && \ echo 'Hi, I am in your container' > /var/www/html/index.nginx-debian.html LABEL RUNNING NGINX CMD ["nginx", "-g", "daemon off;"] LABEL Waith connection to 80/tcp EXPOSE 80
После не забываем сохранить внесенные изменения.
ekzorchik@srv-bionic:~/project$ docker build -t docker/nginx . ekzorchik@srv-bionic:~/project$ docker run -it --restart=always -p 8080:80 docker/nginx
^C -> выхожу по сочетанию клавишу Ctrl + C, а сервис все еще работает.
ekzorchik@srv-bionic:~/project$
Смотрю из другой консоли
ekzorchik@srv-bionic:~/project$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0bf12c99d92b docker/nginx "nginx -g 'daemon of…" 14 seconds ago Up 13 seconds 0.0.0.0:8080->80/tcp silly_curie ekzorchik@srv-bionic:~/project$
Кстати если нужно отладить запуск, то убиваем запущенный контейнер:
ekzorchik@srv-bionic:~/project$ docker stop 0bf12c99d92b 0bf12c99d92b ekzorchik@srv-bionic:~/project$
Шаг №3: Чтобы сохранить образ:
ekzorchik@srv-bionic:~/project$ docker save docker/nginx -o docker_nginx.tar ekzorchik@srv-bionic:~/project$ file docker_nginx.tar docker_nginx.tar: POSIX tar archive ekzorchik@srv-bionic:~/project$ ls -lh docker_nginx.tar -rw------- 1 ekzorchik ekzorchik 149M Jan 15 22:11 docker_nginx.tar
Шаг №4: Чтобы удалить все образа:
ekzorchik@srv-bionic:~/project$ docker rmi $(docker images) -f ekzorchik@srv-bionic:~/project$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ekzorchik@srv-bionic:~/project$
Шаг №5: Чтобы загрузить образ который я сохранил в «Шаг №3«:
ekzorchik@srv-bionic:~/project$ docker load -i docker_nginx.tar Loaded image: docker/nginx:latest ekzorchik@srv-bionic:~/project$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker/nginx latest 7e0521aec9d2 12 minutes ago 152MB ekzorchik@srv-bionic:~/project$ ekzorchik@srv-bionic:~/project$ docker run -it --restart=always -p 8080:80 docker/nginx ^C ekzorchik@srv-bionic:~/project$
Сервис продолжает работать:
Останавливаю запущенный Docker
:
ekzorchik@srv-bionic:~/project$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4590b60c804e docker/nginx "nginx -g 'daemon of…" About a minute ago Up About a minute 0.0.0.0:8080->80/tcp wonderful_faraday ekzorchik@srv-bionic:~/project$ docker stop 4590b60c804e 4590b60c804e ekzorchik@srv-bionic:~/project$
Шаг №6: Допустим потребуется перенести сохраненны образ «Шаг №3» в другой каталог и запустить из него:
ekzorchik@srv-bionic:~/project$ cd ~ ekzorchik@srv-bionic:~$ mkdir example ekzorchik@srv-bionic:~$ cp project/docker_nginx.tar example/ ekzorchik@srv-bionic:~$ cd example/ ekzorchik@srv-bionic:~/example$ ekzorchik@srv-bionic:~/example$ docker load -i docker_nginx.tar Loaded image: docker/nginx:latest ekzorchik@srv-bionic:~/example$ ekzorchik@srv-bionic:~/exampledocker run -it --restart=always -p 8080:80 docker/nginx ^C ekzorchik@srv-bionic:~/example$
Сервис успешно работает из другого каталога.
Отлично и не потребовалось создавать Dockerfile
Шаг №7: Что можно узнать по загруженному образу:
ekzorchik@srv-bionic:~/example$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker/nginx latest 7e0521aec9d2 18 minutes ago 152MB ekzorchik@srv-bionic:~/example$ docker inspect docker/nginx ekzorchik@srv-bionic:~/example$ ekzorchik@srv-bionic:~/example$ docker history docker/nginx
[ { "Id": "sha256:7e0521aec9d2683ffbec410dadf872db90a6f9edb204c76578e73c8423bd361e", "RepoTags": [ "docker/nginx:latest" ], "RepoDigests": [], "Parent": "", "Comment": "", "Created": "2020-01-15T19:01:30.854876071Z", "Container": "ac7b81c671a6f1ee9c9176894550becc140c7d86fcd97186eedba7e73d6deab0", "ContainerConfig": { "Hostname": "ac7b81c671a6", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "80/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "EXPOSE 80" ], "Image": "sha256:f8eed64c23d49e6fac8d064693d15f0beedbe9f3da936e8d7fa27d6fb5783010", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": { "RUNNING": "NGINX", "UPDATE": "SYSTEM", "Waith": "connection to 80/tcp" } }, "DockerVersion": "19.03.5", "Author": "Ollo Alexander <support@ekzorchik.ru>", "Config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "80/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "nginx", "-g", "daemon off;" ], "Image": "sha256:f8eed64c23d49e6fac8d064693d15f0beedbe9f3da936e8d7fa27d6fb5783010", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": { "RUNNING": "NGINX", "UPDATE": "SYSTEM", "Waith": "connection to 80/tcp" } }, "Architecture": "amd64", "Os": "linux", "Size": 152191617, "VirtualSize": 152191617, "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/26bb74625a2ec52bbdfbdd21be62a6cbdad16e932e692f142182063c6a7072a4/diff:/var/lib/docker/overlay2/6000d0bf3d3b0d62d612740733d83ba387d3e475a88ff2bcd7f19b60a8be2141/diff:/var/lib/docker/overlay2/47851892ce555e28e4f410c336409d94d7299049889df98728b5ca3ba6949ea6/diff:/var/lib/docker/overlay2/3c030425b3137dc89c03bab357a9d24691b6c1bfe295c8885377893c70590058/diff", "MergedDir": "/var/lib/docker/overlay2/d687a91b719cf357c6d9b019b9b0d1b6b89d62e22e37b6226a0c48919decb17d/merged", "UpperDir": "/var/lib/docker/overlay2/d687a91b719cf357c6d9b019b9b0d1b6b89d62e22e37b6226a0c48919decb17d/diff", "WorkDir": "/var/lib/docker/overlay2/d687a91b719cf357c6d9b019b9b0d1b6b89d62e22e37b6226a0c48919decb17d/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:2dc9f76fb25b31e0ae9d36adce713364c682ba0d2fa70756486e5cedfaf40012", "sha256:9f3bfcc4a1a8a676da07287a1aa6f2dcc8e869ea6f054c337593481a5bb1345e", "sha256:27dd43ea46a831c39d224e7426794145fba953cd7309feccf4d5ea628072f6a2", "sha256:918efb8f161b4cbfa560e00e8e0efb737d7a8b00bf91bb77976257cd0014b765", "sha256:848a70573b3456b8e73ec14dc4d57791daad5c4edd740b0ac2725443438d66dc" ] }, "Metadata": { "LastTagTime": "0001-01-01T00:00:00Z" } } ]
а вот тут указаны команды и по ним можно воссоздать файл Dockerfile
, плюс размеры установленных пакетов и хронологию действий, вот к примеру:
Но лучше я пока буду хранить свои наработки в своем репозитарии Mercurial
так:
- Каталог
-
- Dockerfile (с комментариями «#» и метками «LABEL»)
- Export-image
Шаг №8: Чтобы остановить контейнер нужно знать его ID, а ID (CONTAINER ID) узнается посредством команды:
ekzorchik@srv-bionic:~/example$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8b6356a5e933 docker/nginx "nginx -g 'daemon of…" 5 minutes ago Up 5 minutes 0.0.0.0:8080->80/tcp pedantic_black ekzorchik@srv-bionic:~/example$ ekzorchik@srv-bionic:~/example$ docker stop 8b6356a5e933 8b6356a5e933 ekzorchik@srv-bionic:~/example$ ekzorchik@srv-bionic:~/example$ cd ~/project/ ekzorchik@srv-bionic:~/project$
Шаг №9: По-умолчанию Docker
кэширует каждый шаг и формируя кэш сборок. Чтобы отключить кэш, например для использования последнего apt-get update
, используйте флаг --no-cache.
ekzorchik@srv-bionic:~/project$ docker build --no-cache -t docker/nginx .
Шаг №10: Отобразить сколько репозитариев (FROM) имеется:
ekzorchik@srv-bionic:~/project$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker/nginx latest 5d8066c0ffa6 34 seconds ago 152MB <none> <none> 7e0521aec9d2 24 minutes ago 152MB ubuntu 18.04 549b9b86cb8d 3 weeks ago 64.2MB ekzorchik@srv-bionic:~/project$
Итого, я почерпнул из всего выше разобранного что-то новое при составлении Dockerfile
, как запускать docker и чтобы он продолжал работать, как останавливать контейнер, как сохранить и запускать из того же каталога или другого, как узнать историю и содержимое. Пока стоит завершить данную заметку. Опыт приобретается и главное после всех заметок ставить себе задачи и вот что бы то ни стало их решать. На этом у меня всё, с уважением автор блога Олло Александр aka ekzorchik.