Docker: различия между версиями

Материал из wiki.nntc.nnov.ru
Перейти к навигации Перейти к поиску
(Создание образа контейнера)
(Запуск контейнера на базе образа)
Строка 194: Строка 194:
 
===Запуск контейнера на базе образа===
 
===Запуск контейнера на базе образа===
  
Запуск контейнеров на базе созданного образа (за исключением имени образа) ни чем не будет отличаться от запуска контейнеров в ходе создания образа вручную:
+
Запуск контейнеров на базе созданного образа (за исключением имени образа и отсутствием необходимости указывать исполняемый скрипт (он указан непосредственно в Dockerfile)) ни чем не будет отличаться от запуска контейнеров в ходе создания образа вручную:
  
  docker run --rm -e USERNAME=Ivan -it my_second_image /bootstrap.sh
+
  docker run --rm -e USERNAME=Ivan -it my_second_image
  
  docker run --rm -e USERNAME=Maria -it my_second_image /bootstrap.sh
+
  docker run --rm -e USERNAME=Maria -it my_second_image
  
  docker run --rm -e USERNAME=Petr -it my_second_image /bootstrap.sh
+
  docker run --rm -e USERNAME=Petr -it my_second_image
  
  docker run --rm -e USERNAME=Ekaterina -it my_second_image /bootstrap.sh
+
  docker run --rm -e USERNAME=Ekaterina -it my_second_image
  
 
==Создание контейнера для разработки приложения на Lazarus==
 
==Создание контейнера для разработки приложения на Lazarus==

Версия 12:27, 14 октября 2020

Лабораторные работы по Docker

Создание простого контейнера

Вручную

Откройте терминал хостовой машины

На базе официального стандартного Docker-образа

ubuntu

создайте и запустите Docker-контейнер:

docker run -it --name=my_first_container --hostname=my-first-container ubuntu /bin/bash


В случае успешного создания Docker-контейнера в эмуляторе терминала приглашение должно смениться на такое:

root@my-first-container:/#

Внимание! Сейчас вы внутри Docker-контейнера и можете командовать им! Если скомандовать

exit

, то контейнер завершит свою работу и найти и запустить его можно таким образом:

Найти:

docker ps -a

в ответе среди возможных прочих контейнеров должен быть такой:

f0a3cc6a8b63        ubuntu                 "/bin/bash"              15 seconds ago      Exited (0) 5 seconds ago                                my_first_container

Имя его в данном случае:

my_first_container

Идентификатор его в данном случае:

f0a3cc6a8b63

Запустить:

docker start my_first_container

или:

docker start f0a3cc6a8b63

Естественно, в конкретном случае идентификатор будет другой.


Подключиться к терминалу Docker-контейнера:

docker attach my_first_container

или

docker attach f0a3cc6a8b63


Внимание! Сейчас вы внутри контейнера и можете командовать им! Если скомандовать

exit

, то контейнер завершит свою работу придётся опять читать выше по тексту как его запускать...


Однако есть способ отключиться от терминала и не завершить Docker-контейнер:

Для этого нужно последовательно нажать сочетания клавиш:

Ctrl + P

затем

Ctrl + Q


После этого приглашение командной строки контейнера должно заменить приглашение хостовой системы.


На этом этапе мы научились запускать контейнер на базе образа. Однако наша цель -- создать собственный образ, поведение контейнеров, запущенных на базе которого который будет отличаться от поведения только что созданного нами контейнера.

Для создания собственного образа вручную необходимо внести изменения внутри нашего контейнера, а затем выполнить операцию по "превращению" нашего контейнера в образ.

Задача

Создать образ, содержащий стартовый сценарий, который будет считывать переменную окружения с именем пользователя и приветствовать пользователя по имени.

Ход работы

Запустите контейнер my_first_container и подключитесь к его терминалу (как это сделать - описано выше)

Установите внутри контейнера текстовый редактор

apt-get update
apt-get install nano

Создайте с помощью установленного редактора nano файл сценария

bootstrap.sh

со следующим содержимым:

#!/bin/bash
echo "Hello, $USERNAME"

Сделайте файл исполняемым

chmod +x /bootstrap.sh

Проверьте работоспособность командой:

USERNAME=Ivan /bootstrap.sh

в результате в терминал должна быть выдана строка

Hello, Ivan

Завершите контейнер, выйдя из него:

exit

Создайте новый образ на базе изменённого контейнера командой:

docker commit -a Vasya -m "My first own image" my_first_container my_first_image

Образ будет доступен в списке образов

docker images

Выполните запуск контейнера с автоматическим удалением его после завершения, передавая в команду запуска различные значения переменной окружения

USERNAME

Примеры команды:

docker run --rm -e USERNAME=Ivan -it my_first_image /bootstrap.sh
docker run --rm -e USERNAME=Maria -it my_first_image /bootstrap.sh
docker run --rm -e USERNAME=Petr -it my_first_image /bootstrap.sh
docker run --rm -e USERNAME=Ekaterina -it my_first_image /bootstrap.sh

Автоматически (посредством файла Dockerfile)

Откройте терминал хостовой машины и создайте рабочий каталог

mkdir docker

Перейдите в рабочий каталог

cd docker

Создание файлов

В рабочем каталоге создайте файл

Dockerfile

по следующему шаблону (в файле нужно изменить имя и емэйл разработчика на собственное):

FROM ubuntu
MAINTAINER Vasya I Pupkin <vasya@pupkin.com>
RUN echo '#!/bin/bash' > /bootstrap.sh
RUN echo 'echo "Hello, $USERNAME"' >> /bootstrap.sh
RUN chmod +x /bootstrap.sh
CMD /bootstrap.sh


В конце этого этапа в рабочем каталоге должен быть один файл:

Dockerfile

Создание образа контейнера

Для создания Docker-образа (image) выполните команду:

docker build -t 'my_second_image:latest' .

Для проверки существования созданного контейнера выполните команду:

docker images

Среди текста ответа должна появиться строчка с новым контейнером:

my_second_image        latest              add5d00c3dc0        7 seconds ago       72.9MB

Запуск контейнера на базе образа

Запуск контейнеров на базе созданного образа (за исключением имени образа и отсутствием необходимости указывать исполняемый скрипт (он указан непосредственно в Dockerfile)) ни чем не будет отличаться от запуска контейнеров в ходе создания образа вручную:

docker run --rm -e USERNAME=Ivan -it my_second_image
docker run --rm -e USERNAME=Maria -it my_second_image
docker run --rm -e USERNAME=Petr -it my_second_image
docker run --rm -e USERNAME=Ekaterina -it my_second_image

Создание контейнера для разработки приложения на Lazarus

Откройте терминал хостовой машины и создайте рабочий каталог

mkdir docker

Перейдите в рабочий каталог

cd docker

Создание файлов

В рабочем каталоге создайте файл

Dockerfile_dev

по следующему шаблону (в файле нужно изменить имя и емэйл разработчика на собственное):

FROM ubuntu:16.04
MAINTAINER Vasya I Pupkin <vasya@pupkin.com>

ENV DEBIAN_FRONTEND noninteractive
ENV USER ubuntu
ENV HOME /home/$USER

# Create new user for vnc login.
RUN adduser $USER --disabled-password

# Install MATE and dependency component.
RUN apt-get update \
    && apt-get install -y \
        tightvncserver \
        mate-core mate-desktop-environment mate-notification-daemon \
        supervisor \
        net-tools \
        curl \
        git \
        pwgen \
        lazarus \
    && apt-get autoclean \
    && apt-get autoremove \
    && rm -rf /var/lib/apt/lists/*

# Clone noVNC.
RUN git clone https://github.com/novnc/noVNC.git $HOME/noVNC

# Clone websockify for noVNC
Run git clone https://github.com/kanaka/websockify $HOME/noVNC/utils/websockify

# Download ngrok.
ADD https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip $HOME/ngrok/ngrok.zip
RUN unzip -o $HOME/ngrok/ngrok.zip -d $HOME/ngrok && rm $HOME/ngrok/ngrok.zip

# Copy supervisor config.
COPY supervisor.conf /etc/supervisor/conf.d/

# Copy startup script.
COPY startup.sh $HOME

EXPOSE 6080 5901 4040

В рабочем каталоге создайте файл

supervisor.conf

со следующим содержимым:

[program:vncserver]
command=vncserver -geometry 1600x900 :1
user=ubuntu

[program:noVNC]
command=/home/ubuntu/noVNC/utils/launch.sh --vnc localhost:5901
user=ubuntu
stdout_logfile=/var/log/novnc.log
redirect_stderr=true


В рабочем каталоге создайте файл

startup.sh

со следующим содержимым:

#!/bin/bash

if [ ! -f $HOME/.vnc/passwd ] ; then

    if  [ -z "$PASSWORD" ] ; then
        PASSWORD=`pwgen -c -n -1 12`
        echo -e "PASSWORD = $PASSWORD" > $HOME/password.txt
    fi

    echo "$USER:$PASSWORD" | chpasswd

    # Set up vncserver
    su $USER -c "mkdir $HOME/.vnc && echo '$PASSWORD' | vncpasswd -f > $HOME/.vnc/passwd && chmod 600 $HOME/.vnc/passwd && touch $HOME/.Xresources"
    chown -R $USER:$USER $HOME

    if [ ! -z "$SUDO" ]; then
        case "$SUDO" in
            [yY]|[yY][eE][sS])
                adduser $USER sudo
        esac
    fi

else

    VNC_PID=`find $HOME/.vnc -name '*.pid'`
    if [ ! -z "$VNC_PID" ] ; then
        vncserver -kill :1
        rm -rf /tmp/.X1*
    fi

fi

if [ ! -z "$NGROK" ] ; then
        case "$NGROK" in
            [yY]|[yY][eE][sS])
                su ubuntu -c "$HOME/ngrok/ngrok http 6080 --log $HOME/ngrok/ngrok.log --log-format json" &
                sleep 5
                NGROK_URL=`curl -s http://127.0.0.1:4040/status | grep -P "http://.*?ngrok.io" -oh`
                su ubuntu -c "echo -e 'Ngrok URL = $NGROK_URL/vnc.html' > $HOME/ngrok/Ngrok_URL.txt"
        esac
fi

/usr/bin/supervisord -n

В конце этого этапа в рабочем каталоге должно быть три файла:

Dockerfile_dev
supervisor.conf
startup.sh

Создание каталогов

В рабочем каталоге создайте каталог

lazarus

командой

mkdir lazarus

В конце этого этапа в рабочем каталоге должно быть три файла:

Dockerfile_dev
supervisor.conf
startup.sh

и один каталог:

lazarus


Создание образа контейнера

Далее в этом разделе будем исходить из условия, что ваш логин на https://hub.docker.com/ будет

vpupkin

а пароль

ну, вы сами знаете...


! Также стоит помнить, что в эмуляторе терминала находиться нужно в рабочем каталоге !


Для создания Docker-образа (image) выполните команду:

docker build -t 'vpupkin/lazarus_dev:latest' -f Dockerfile_dev .


Для проверки существования созданного контейнера выполните команду:

docker images

Среди текста ответа должна появиться строчка с новым контейнером:

vpupkin/lazarus_dev   latest              fa1a63896356        9 seconds ago       2.42GB


Запуск контейнера на базе образа

На базе полученного образа запустите контейнер, пробросив внутрь контейнера локальный каталог хостовой машины и пробросив порт для взаимодействия с графической оболочкой контейнера посредством клиента noVNC:

docker run -it --mount source="$(pwd)"/lazarus,target=/home/ubuntu/lazarus,type=bind -p 6080:6080 -e PASSWORD=123456 --name=lazarus_dev --hostname=lazarus-dev vpupkin/lazarus_dev /bin/bash

Также в этой команде запуска задаются: имя контейнера, имя хоста, команда, которую выполнит контейнер при запуске и указаны ключи, которые автоматически подключают терминал к контейнеру. Для доступа по протоколу VNC посредством переменной окружения PASSWORD задаётся пароль.

В случае успешного создания контейнера в эмуляторе терминала приглашение должно смениться на такое:

root@lazarus-dev:/#

Внимание! Сейчас вы внутри контейнера и можете командовать им! Если скомандовать

exit

, то контейнер завершит свою работу и найти и запустить его можно таким образом:

Найти:

docker ps -a

в ответе среди возможных прочих контейнеров должен быть такой:

8e5f4fd0a99b        vpupkin/lazarus_dev   "/bin/bash"         3 minutes ago       Exited (0) 6 seconds ago                       lazarus_dev

Имя его в данном случае:

lazarus_dev

Идентификатор его в данном случае:

8e5f4fd0a99b

Остальное пока не важно, однако можно догадаться за что отвечают остальные параметры...


Запустить:

docker start lazarus_dev

или:

docker start 8e5f4fd0a99b

Естественно, в конкретном случае идентификатор будет другой.


Подключиться к терминалу:

docker attach lazarus_dev

или

docker attach 8e5f4fd0a99b


Внимание! Сейчас вы внутри контейнера и можете командовать им! Если скомандовать

exit

, то контейнер завершит свою работу придётся опять читать выше по тексту как его запускать...


Однако есть способ отключиться от терминала и не завершить контейнер:

Для этого нужно последовательно нажать сочетания клавиш:

Ctrl + P

затем

Ctrl + Q


После этого приглашение командной строки контейнера должно заменить приглашение хостовой системы.


На этом этапе мы научились создавать образ контейнера и запускать контейнер на его базе.

Запуск графической подсистемы и вход в неё посредством браузера

Для запуска окружения рабочего стола и доступа в него по протоколу VNC в контейнере предусмотрен специальный сценарий запуска. Чтобы его запустить нужно зайти в терминал контейнера (см. предыдущие пункты)

И выполнить внутри контейнера команду:

/bin/bash /home/ubuntu/startup.sh

Далее можно либо оставить эмулятор терминала и открыть браузер (правда помни, что если случайно закрыть эмулятор терминала, то контейнер завершит выполнение), либо отключиться от терминала контейнера без его завершения (тоже см. выше - про горячие клавиши).


В любом случае, убедиться, что контейнер запущен можно выполнив команду в хостовой машине:

docker ps

и найдя свой контейнер среди списка, который вернёт команда.


Для работы с окружением рабочего стола зайдите браузером по адресу:

http://localhost:6080/vnc.html

мы же там порт забрасывали, при старте контейнера...

В окне, которое загрузится в браузере, ввести пароль. Какой? -- см. выше. Мы его там в переменной окружения задавали, при старте контейнера...


На этом этапе загрузится стандартное окружение рабочего стола MATE, в котором можно использовать файловый менеджер и непосредственно IDE Lazarus.

Внутри окружения в домашнем каталоге пользователя

ubuntu

есть каталог

lazarus

Он связан с хостовой системой. Зря мы чтоли создавали этот каталог и потом биндили его при старте контейнера?

Конечно не зря. Через этот каталог хостовая машина может обмениваться с контейнером файлами. А самое главное, что при уничтожении контейнера все ваши ценные файлы останутся на хостовой машине в каталоге

lazarus

, если вы, конечно, будете хранить ценные файлы именно в каталоге, который из окружения MATE контейнера имеет такой полный путь:

/home/ubuntu/lazarus

(а теперь внимательно смотрим на команду старта контейнера и смотрим где там встречается такая строчка...)


Внутри окружения MATE и в непосредственно IDE Lazarus работаем самостоятельно. Вы это умеете :-)

Порядок работы с IDE Lazarus

После запуска IDE Lazarus необходимо сразу сохранить проект так, чтобы он в итоге лежал в каталоге:

/home/ubuntu/lazarus/project_name

project_name - любое логичное имя вашего проекта.

Также есть вариант наоборот: загружаем в каталог lazarus (тот, который прибиндили в контейнер, см. выше, если непонятно) уже существующий проект, затем открываем его в IDE Lazarus из окружения MATE контейнера.


Работаем над проектом в IDE Lazarus, затем собираем его, сохраняем проект и закрываем IDE Lazarus


На этом этапе в

/home/ubuntu/lazarus/project_name

должен находиться исполняемый файл проекта. Например, если проект вы назвали

mypr.lpr

, то этот файл будет таким:

/home/ubuntu/lazarus/project_name/mypr


Вот примерно такой командой его и можно запустить отдельно от IDE Lazarus. Проверим?


Откройте эмулятор терминала из окружения MATE контейнера и введите там полный путь до исполняемого файла получившейся программы:

/home/ubuntu/lazarus/project_name/mypr

В результате внутри окружения MATE контейнера должно запуститься ваше приложение, написанное на Lazarus.


Получилось? Если да, то теперь нам не нужо все это тяжёлое окружение в виде MATE и сервера с noVNC. Выключите его. Для этого нужно зайти в терминал контейнера:

docker attach lazarus_dev

и завершить выполнение сценария, который до этого стартовали, сочетанием клавиш:

Ctrl + C

вам должно будет вернуться приглашение командной строки контейнера - это признак того что окружение и сервер с noVNC завершены (можете проверить -- в браузере уже ничего не загрузится).


Теперь наша задача - запустить приложение прямо на хостовой машине (но выполняться оно будет в контейнере).

Делаем образ из контейнера с приложением на Lazarus

Для того, чтобы запустить получившееся приложение внутри контейнера, но графическую часть его иметь на хостовой машине, нужно запустить еще один контейнер. Однако, из какого образа?

Вероятно, из такого, где оно (приложение на Lazarus) присутствует. У нас есть такой образ?


Верно, нет. У нас есть лишь контейнер, который мы получили на базе подготовленного чистого образа.


Но мы можем теперь легко получить образ на базе нашего контейнера с приложением на Lazarus. Для этого нужно завершить контейнер и выполнить команду в хостовой систиеме:

docker commit -a Vasya lazarus_dev vpupkin/lazarus_test

Убедиться, что образ создался, можно по прежнему командой:

docker images

А вот теперь уже можно запустить контейнер на базе образа:

vpupkin/lazarus_test

, который будет запускать приложение на Lazarus прямо на хостовой системе. Для этого нужно запустить контейнер такой командой:

docker run --rm -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix --mount source="$(pwd)"/lazarus,target=/home/ubuntu/lazarus,type=bind --name=lazarus_test --hostname=lazarus-test vpupkin/lazarus_test /bin/su -c "/home/ubuntu/lazarus/project_name/mypr" ubuntu

Для успешного запуска графического приложения, работающего в контейнере, с возможностью отображения графического интерфейса, в этой команде сделано две вещи:

Первая: установлена (внутрь контейнера) переменная окружения

DISPLAY

равная значению аналогичной переменной окружения хостовой системы.

Вторая: проброшено устройство

/tmp/.X11-unix

из хостовой системы в контейнер.

Также добавлено монтирование каталога lazarus для обмена файлами межу контейнером и хостовой системой, а также добавлена опция

--rm

которая уничтожает контейнер после его завершения (за данные мы не переживаем, они же лежат локально в каталоге lazarus, который каждый раз монтируется при старте в новый контейнер).


Однако, возможно, не стоит каждый раз содавать контейнер из образа, если изменений в параметрах запуска не требуется?

Достаточно убрать параметр

--rm

из команды запуска контейнера, но следует помнить, что при завершении графического приложения контейнер будет завершён и запускать его уже нужно будет командой старта контейнера (в этом случае контейнер никуда не девается, а просто лежит на хостовой системе в выключенном состоянии).

И включить его можно (т.е. запустить наше lazarus-приложение) так:

docker start lazarus_test

Отвечаем на главный вопрос?

Вопрос звучит так:

Если мы уже разработали приложение, то надо ли конечному пользователю все эти окружения MATE, сервер для запуска окружения noVNC и прочие скрипты инициализации, которые использовались для создания контейнера с инструментами разработки?


Наверное нет. Они сильно загружают систему при том, что больше никогда не понадобятся пользователю.

Поэтому для конечного пользователя целесообразно создать новый контейнер, исключив оттуда всё лишнее и оставив лишь непосредственно готовое приложение.


Как это сделать -- см. в лабораторной работе ниже.

Создание контейнера для запуска приложения на Lazarus

Откройте терминал хостовой машины. Если в домашнем каталоге пользователя присутствует каталог

docker

то переместите его в резервную копию командой:

mv docker docker_backup1

затем заново создайте рабочий каталог командой:

mkdir docker

Перейдите в рабочий каталог

cd docker

Создание файлов

В рабочем каталоге создайте файл

Dockerfile_prod

по следующему шаблону (в файле нужно изменить имя и емэйл разработчика на собственное):

FROM ubuntu:16.04
MAINTAINER Vasya I Pupkin <vasya@pupkin.com>

ENV USER ubuntu
ENV HOME /home/$USER

# Install dependencyes
RUN apt-get update \
    && apt-get install -y libgtk2.0-0 \
    && apt-get autoclean \
    && apt-get autoremove \
    && rm -rf /var/lib/apt/lists/*

# Create new user for run app
RUN adduser $USER --disabled-password

# Copy app to user homedir
COPY mypr /home/$USER/
CMD /bin/su -c "/home/ubuntu/mypr" ubuntu

Скопируйте исполняемый файл, полученный в предыдущей работе в рабочий каталог

cp ../docker_backup1/lazarus/project_name/mypr ./

Мы находимся в новом рабочем каталоге хостовой машины:

/home/user/docker

В начале этой работы каталог предыдущей работы мы переместили сюда:

/home/user/docker_backup1

Следовательно, относительно каталога

/home/user/docker

команда копирования исполняемого файла требует подняться на уровень выше

../

и из каталога

docker_backup1

уровнем выше относительно

/home/user/docker

скопировать необходимый файл в текущий каталог

./

В конце этого этапа в рабочем каталоге должно быть два файла:

Dockerfile_prod
mypr

Создание образа контейнера

Далее в этом разделе будем исходить из условия, что ваш логин на https://hub.docker.com/ будет

vpupkin

а пароль

ну, вы сами знаете...


! Также стоит помнить, что в эмуляторе терминала находиться нужно в рабочем каталоге !


Для создания Docker-образа (image) выполните команду:

docker build -t 'vpupkin/lazarus_prod:latest' -f Dockerfile_prod .


Для проверки существования созданного контейнера выполните команду:

docker images

Среди текста ответа должна появиться строчка с новым контейнером:

vpupkin/lazarus_prod   latest              20bb2900074d        16 seconds ago      217MB


! Обратите внимание на размер контейнера !
Сравните размер контейнера с размером контейнера lazarus_dev из предыдущей работы

Запуск контейнера на базе образа

На базе полученного образа запустите контейнер, пробросив внутрь контейнера локальный каталог хостовой машины и пробросив порт для взаимодействия с графической оболочкой контейнера посредством клиента noVNC:

docker run -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix --name=lazarus_prod --hostname=lazarus-prod vpupkin/lazarus_prod


При запуске контейнера таким образом не указывается команда, т.к. она задаётся в файле

Dockerfile_prod

строчкой

CMD /bin/su -c "/home/ubuntu/mypr" ubuntu

Результатом запуска контейнера должно стать запущенное внутри контейнера графическое приложение с отображением графического интерфейса на хостовой машине.

При закрытии окна графического приложения контейнер будет завершён и будет доступен среди списка контейнеров

docker ps -a

Повторный запуск уже существующего контейнера можно осуществить по имени контейнера командой:

docker start lazarus_prod