DockerAlt

Материал из wiki.nntc.nnov.ru
Версия от 10:13, 16 января 2023; Vovan (обсуждение | вклад) (Сборка приложения на языке Java в docker-контейнере)
Перейти к навигации Перейти к поиску

Содержание

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

Подготовка рабочего места

Для выполнения всех работ достаточно установить в виртуальную машину ОС Альт Рабочая Станция 10.

Ссылка на загрузку установочного образа: https://download.basealt.ru/pub/distributions/ALTLinux/p10/images/workstation/x86_64/alt-workstation-10.0-x86_64.iso

После установки нужно обновить систему

su -
apt-get update
apt-get dist-upgrade
update-kernel -f

открыть терминал и установить Docker командой

apt-get install docker-engine

Затем добавить текущего пользователя в группу docker командой

usermod -a -Gdocker student

затем включить сервис и поставить его в автозагрузку

systemctl start docker
systemctl enable docker

перезагрузиться

reboot

[Практическая работа №1] Создание простого контейнера

Вручную

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

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

altlinux/base:p10

Только получить образ на компьютер можно командой:

docker pull altlinux/base:p10

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

docker run -it --name=my_first_container --hostname=my-first-container altlinux/base:p10 /bin/bash


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

root@my-first-container:/#

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

exit

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

Найти:

docker ps -a

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

f0a3cc6a8b63        altlinux/base:p10                 "/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, $USER_NAME"

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

chmod +x /bootstrap.sh

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

USER_NAME=Ivan /bootstrap.sh

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

Hello, Ivan

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

exit

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

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

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

docker images

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

USER_NAME

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

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

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

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

mkdir pr01

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

cd pr01

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

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

Dockerfile

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

FROM altlinux/base:p10
MAINTAINER Vasya I Pupkin <vasya@pupkin.com>
RUN echo '#!/bin/bash' > /bootstrap.sh
RUN echo 'echo "Hello, $USER_NAME"' >> /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 USER_NAME=Ivan -it my_second_image
docker run --rm -e USER_NAME=Maria -it my_second_image
docker run --rm -e USER_NAME=Petr -it my_second_image
docker run --rm -e USER_NAME=Ekaterina -it my_second_image

[Практическая работа №2] Создание контейнера для разработки приложения на языке Java

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

mkdir pr02

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

cd pr02

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

Скачайте в рабочий каталог JDK версии 19.0.1. Это можно сделать командой (рекомендуется использовать в локальной сети колледжа)

wget https://wiki.nntc.nnov.ru/download/openjdk-19.0.1_linux-x64_bin.tar.gz

или командой (при работе вне локальной сети колледжа)

wget https://download.java.net/java/GA/jdk19.0.1/afdd2e245b014143b62ccb916125e3ce/10/GPL/openjdk-19.0.1_linux-x64_bin.tar.gz

Распакуйте загруженный файл командой

tar -xzpf openjdk-19.0.1_linux-x64_bin.tar.gz

В результате будет доступен каталог

jdk-19.0.1

На этом этапе архив можно удалить командой

rm openjdk-19.0.1_linux-x64_bin.tar.gz

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

Dockerfile_dev

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

FROM altlinux/base:p10
MAINTAINER Vasya I Pupkin <vasya@pupkin.com>
RUN apt-get update -q -y && apt-get install nano git libX11 libX11-devel libXxf86vm libXxf86vm-devel libGL xorg-dri-swrast fonts-ttf-dejavu mate-calc -q -y
RUN mkdir /opt/jdk-19.0.1
COPY jdk-19.0.1 /opt/jdk-19.0.1
RUN echo PATH="$PATH:/opt/jdk-19.0.1/bin" > /root/.bashrc
RUN source /root/.bashrc

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

Dockerfile_dev

и каталог

jdk-19.0.1

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

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

vpupkin

а пароль

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


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


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

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


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

docker images

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

vpupkin/java_dev   latest              fa1a63896356        9 seconds ago       2.42GB


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

mkdir /home/student/pr02/src

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

docker run -it --mount source=/home/student/pr02/src,target=/root/src,type=bind --name=java_dev --hostname=java-dev vpupkin/java_dev /bin/bash

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

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

root@java-dev:/#

Проверка версии Java RE и компилятора должны возвращать примерно следующее:

bash-4.4# java -version
openjdk version "19.0.1" 2022-10-18
OpenJDK Runtime Environment (build 19.0.1+10-21)
OpenJDK 64-Bit Server VM (build 19.0.1+10-21, mixed mode, sharing)
bash-4.4# 
bash-4.4# javac -version
javac 19.0.1

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

exit

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

Найти:

docker ps -a

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

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

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

java_dev

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

8e5f4fd0a99b

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


Запустить:

docker start java_dev

или:

docker start 8e5f4fd0a99b

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


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

docker attach java_dev

или

docker attach 8e5f4fd0a99b


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

exit

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


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

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

Ctrl + P

затем

Ctrl + Q


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


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

Сборка приложения на языке Java в docker-контейнере

Перейдём в каталог

cd /root/src/

Склонируем проект приложения на Java

git clone https://gitlab.nntc.nnov.ru/vlad.tancev/simple-counter

Перейдём в проект

cd simple-counter

Исходя из рекомендаци в файле README.md выполним сборку проекта

bash mvnw install
bash mvnw dependency:copy-dependencies

В случае успешной сборки в терминале котейнера долждно быть написано что-то похожее на это:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

На этом этам сборки приложения заврешён.

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

Запуск собранного приложения в docker-контейнере на базе образа с JDK

Запустим новый контейнер командой:

docker run --rm -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix --mount source=/home/student/pr02/src,target=/root/src,type=bind --name=java_test --hostname=java-test vpupkin/java_dev /bin/bash

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

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

DISPLAY

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

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

/tmp/.X11-unix

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

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

--rm

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


Эта опция сюда добавлена, поскольку сама команда старта контейнера содержит в себе цель запускать именно оболочку /bin/bash для ручного тестирования запуска собранного приложения.

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


Когда работа собранного приложения протестирована и есть понимание, что всё работает корректно, можно приступать к сборке образа для production-контейнера, в котором необходимо оставить необходимое и достаточное количество библиотек, необходимых только для запуска уже собранного приложения, а пакеты и утилиты, необходимые для сборки, не включать в результирующий образ.

Важный момент: В некоторых операционных системах может потребоваться выполнить команду

xhost +

для разрешения подключения контейнера к графической оболочке X-сервера.

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

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

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


Не нужны по нескольким причинам:

  1. Исходный чаще всего остаётся интеллектуальной собственностью компании-разработчика и не должен оказаться у заказчика
  2. Наличие на Production-системе компилятора какого-либо языка и/или каких-либо средств разработки является потенциальной уязвимостью системы
  3. Инструменты сборки и тестирования никогда не понадобятся конечному пользователю на Production-системе, но всегда будут занимать место на файловой системе контейнера

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

Как это сделать -- см. в следующей практической работе.

[Практическая работа №3] Создание контейнера для запуска Java-приложения

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

mkdir pr03

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

cd pr03

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

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

Dockerfile_prod

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

FROM altlinux/base:p10
MAINTAINER Vasya I Pupkin <vasya@pupkin.com>
RUN apt-get update -q -y \
    && apt-get install nano su libX11 libX11-devel libXxf86vm libXxf86vm-devel libGL xorg-dri-swrast fonts-ttf-dejavu mate-calc -q -y \
    && apt-get autoclean \
    && apt-get autoremove

RUN adduser app
RUN echo "PATH=$PATH:/opt/jdk-19.0.1/bin" > /home/app/.bashrc
RUN echo "export JAVA_HOME=/opt/jdk-19.0.1" >> /home/app/.bashrc

RUN mkdir /opt/jdk-19.0.1
COPY jdk-19.0.1 /opt/jdk-19.0.1

RUN mkdir /opt/app
COPY app /opt/app
COPY startup.sh /
RUN chmod +x /startup.sh
CMD /bin/su - -c "/startup.sh" app

Создайте каталог app в рабочем каталоге

mkdir app

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

/bin/cp -rf ../pr02/src/simple-counter/target ../pr02/src/simple-counter/.mvn ../pr02/src/simple-counter/mvnw ../pr02/src/simple-counter/pom.xml app/

Скачайте в рабочий каталог JDK версии 19.0.1. Это можно сделать командой (рекомендуется использовать в локальной сети колледжа)

wget https://wiki.nntc.nnov.ru/download/openjdk-19.0.1_linux-x64_bin.tar.gz

или командой (при работе вне локальной сети колледжа)

wget https://download.java.net/java/GA/jdk19.0.1/afdd2e245b014143b62ccb916125e3ce/10/GPL/openjdk-19.0.1_linux-x64_bin.tar.gz

Распакуйте загруженный файл командой

tar -xzpf openjdk-19.0.1_linux-x64_bin.tar.gz

На этом этапе архив можно удалить командой

rm openjdk-19.0.1_linux-x64_bin.tar.gz

Создайте в рабочем каталоге файл

startup.sh

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

#!/bin/bash
cd /opt/app
bash mvnw exec:java -Dexec.mainClass="com.example.demo_up04.DemoApplication"

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

Dockerfile_prod
startup.sh

И два каталога:

jdk-19.0.1
app

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

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

vpupkin

а пароль

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


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


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

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


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

docker images

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

vpupkin/java_prod   latest              20bb2900074d        16 seconds ago      217MB


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

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

Создадим каталог для обмена данными с контейнером

mkdir $HOME/data

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

docker run -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix --mount source="$HOME/data",target=/home/app/data,type=bind --name=java_prod --hostname=java-prod vpupkin/java_prod


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

Dockerfile_prod

строчкой

CMD /bin/su -c "/startup.sh" app

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

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

docker ps -a

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

docker start java_prod

[Практическая работа №4] Взаимодействие двух контейнеров по сети

Задача: Запустить ПО Moodle в контейнере с именем c2, который взаимодействует с базой данных Mariadb, запущенной в контейнере с именем c1

Загрузить образы контейнеров

docker pull mariadb
docker pull bitnami/moodle


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

Создать контейнер с базой данных Mariadb и задать пароль пользователя root через переменную окружения

docker run --name=c1 --hostname=c1 -e MYSQL_ROOT_PASSWORD=123321 --network bridge -d mariadb

Создать временный контейнер с ПО Moodle и запустить его с доступом в командную оболочку

docker run -it --rm --name c2_tmp --network bridge bitnami/moodle:latest /bin/bash

Установить во временный контейнер утилиты проверки состояния сети

apt update
apt install net-tools iputils-ping

Изнутри контейнера c2_tmp проверить доступ в базу данных (для этого нужно убедиться в корректности ip адреса контейнера c1):

1. Посмотреть ip адрес конейнера c2_tmp (если контейнеры были запущены последовательно, то адрес контейнера c1 будет на единицу меньше)

ifconfig

2. Выполнить

ping <предполагаемый адрес контейнера c1>

в соседней консоли остановить контейнер c1

docker stop c1

в предыдущей консоли убедиться что пинги прекратились. Вернуться в соседнюю консоль. Запустить контейнер c1

docker start c1

в предыдущей консоли убедиться что пинги возобновились.

3. Выполнить проверку подключения к Mariadb посредством клиента mysql в контейнере c2_tmp

mysql -u root -p<пароль, установленный в переменной окружения при запуске контейнера c1> -h <корректный ip адрес контейнера c1>

4. Создать базу пользователя и базу данных Mariadb для работы Moodle (из консоли mysql, предварительно подключившись к ней под пользователем root)

CREATE DATABASE mdatabase;
CREATE USER 'muser'@'%' IDENTIFIED BY '123456';
GRANT ALL PRIVILEGES ON mdatabase.* to 'muser'@'%' WITH GRANT OPTION;

5. Проверить корректность подключения к Mariadb с созданным пользователем к созданной базе данных

mysql -u muser -p123456 -h <корректный ip адрес контейнера c1> mdatabase

6. Выйти из временного контейнера c2_tmp

exit

Запуск контейнера C2 с ПО Moodle

Поскольку в предыдущем пункте были подготовлены параметры доступа в базу данных Mariadb, используем их для запуска контейнера с ПО Moodle

docker run -d --name c2 -p 8080:8080 -p 8443:8443 --env ALLOW_EMPTY_PASSWORD=no --env MOODLE_DATABASE_USER=muser --env MOODLE_DATABASE_PASSWORD=123456 --env MOODLE_DATABASE_NAME=mdatabase --env MOODLE_DATABASE_HOST=<корректный ip адрес контейнера c1> --env MOODLE_USERNAME=user --env MOODLE_PASSWORD=123321 --env MOODLE_EMAIL=user@host.com --env MOODLE_SITE_NAME=user_moodle --network bridge bitnami/moodle:latest

Проверка: Открыть в браузере адрес:

http://localhost:8080

Ввести логин и пароль от ПО Moodle, указанные ранее в переменных окружения при старте контейнера.

Подробное описание по контейнеру: https://hub.docker.com/r/bitnami/moodle

Важно! Первые несколько минут ПО Moodle не будет доступно по указанному адресу http://localhost:8080
т.к. будет выполняться его первое развёртывание.

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

mysql -u muser -p123456 -h <корректный ip адрес контейнера c1> mdatabase

и из консоли mysql проверить наличие таблиц в базе данных

show tables

Самостоятельное задание

Используя справку по утилите docker запустить два аналогичных контейнера, установив статические ip адреса для них.

[Практическая работа №5] Взаимодействие двух контейнеров по сети

Создайте рабочий каталог и перейдите в него

mkdir composemoodle
cd composemoodle

Создайте файл

docker-compose.yml

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

version: "2.2"
services:
  mariadb:
     image: "mariadb:latest"
     environment:
      - MYSQL_ROOT_PASSWORD=123321
      - MYSQL_ALLOW_EMPTY_PASSWORD=no
      - MYSQL_DATABASE=mdatabase
      - MYSQL_USER=muser
      - MYSQL_PASSWORD=123456
  moodle:
    ports:
      - "8080:8080"
      - "8443:8443"
    image: "bitnami/moodle:latest"
    environment:
      - ALLOW_EMPTY_PASSWORD=no
      - MOODLE_DATABASE_USER=muser
      - MOODLE_DATABASE_PASSWORD=123456
      - MOODLE_DATABASE_NAME=mdatabase
      - MOODLE_DATABASE_HOST=mariadb
      - MOODLE_USERNAME=user
      - MOODLE_PASSWORD=123321
      - MOODLE_EMAIL=user@host.com
      - MOODLE_SITE_NAME=user_moodle


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

docker-compose.yml

В рабочем каталоге выполнить команду для запуска проекта

docker-compose up