Хочу разобрать, как создать переносимый образ 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:~$

Web-сервис nginx работает, как Docker образ

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$

Web-сервис nginx работает, как Docker образ

Шаг №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$

Сервис продолжает работать:

How-to-create-a-Docker-on-Ubuntu-18-04-image-001

Останавливаю запущенный 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, плюс размеры установленных пакетов и хронологию действий, вот к примеру:

хронологию действий 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.