Fixed docker task.

pull/1/head
Vladimir Protsenko 2 years ago
parent b5212b744e
commit 35644b9a77

@ -6,9 +6,9 @@ docker build -t studX/foodtrucks-web ./
# create the network # create the network
docker network create foodtrucks-network docker network create foodtrucks-network
# start the ES container # start the ES container, specify password beforehand
docker run -d --net foodtrucks-network --name elastic elasticsearch:8.4.3 docker run -d --net foodtrucks-network --name elastic -e ELASTIC_PASSWORD=v7SLsbtXticPLADei5vS elasticsearch:8.4.3
# start the flask app container # start the flask app container
# point to elastic host: https://elastic:9200 user: elastic password: v7SLsbtXticPLADei5vS # point to elastic host: https://elastic:9200 user: elastic password: v7SLsbtXticPLADei5vS
docker run -d -p 80:5000 --net foodtrucks-network --name foodtrucks-web studX/foodtrucks-web https elastic 9200 elastic v7SLsbtXticPLADei5vS docker run -d -p 80:5000 --net foodtrucks-network studX/foodtrucks-web https elastic 9200 elastic v7SLsbtXticPLADei5vS

@ -7,14 +7,15 @@ import requests
es_scheme = sys.argv[1] es_scheme = sys.argv[1]
es_host = sys.argv[2] es_host = sys.argv[2]
es_port =int(sys.argv[3]) es_port = int(sys.argv[3])
es_user = sys.argv[4] es_user = sys.argv[4]
es_password = sys.argv[5] es_password = sys.argv[5]
connection_string = f"{es_scheme}://{es_host}:{es_port}"
print(f"Elastic server: {es_scheme}://{es_host}:{es_port}, auth: {es_user}:{es_password}") print(f"Elastic server: {es_scheme}://{es_host}:{es_port}, auth: {es_user}:{es_password}")
es = Elasticsearch( es = Elasticsearch(
f"{es_scheme}://{es_host}:{es_port}", connection_string,
basic_auth=(es_user, es_password), basic_auth=(es_user, es_password),
verify_certs=False verify_certs=False
) )
@ -40,7 +41,7 @@ def safe_check_index(index, retry=60):
status = es.indices.exists(index=index) status = es.indices.exists(index=index)
return status return status
except exceptions.ConnectionError as e: except exceptions.ConnectionError as e:
print("Unable to connect to ES. Retrying in 5 secs...") print(f"Unable to connect to {connection_string}. Retrying in 5 secs...", flush=True)
time.sleep(5) time.sleep(5)
safe_check_index(index, retry-1) safe_check_index(index, retry-1)

@ -26,7 +26,7 @@ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Проверте, всё ли установлено корректно: Проверте, всё ли установлено корректно:
``` ```
$ docker run hello-world $ sudo docker run hello-world
Hello from Docker. Hello from Docker.
This message shows that your installation appears to be working correctly. This message shows that your installation appears to be working correctly.
@ -38,14 +38,14 @@ This message shows that your installation appears to be working correctly.
Для начала, запустите следующую команду: Для начала, запустите следующую команду:
``` ```
$ docker pull busybox $ sudo docker pull busybox
``` ```
**Внимание.** В зависимости от того, как вы установили Docker, вы можете увидеть сообщение permission denied (доступ запрещён) в ответ на вызов выше приведённой команды. Если вы на Mac, убедитесь, что Docker движок запущен. Если на Линукс, вам может потребоваться повысить права доступа с помощью команды `sudo`. В качестве альтернативного варианта вы можете добавить пользователя в Docker группу для решения этой проблемы. **Внимание.** В зависимости от того, как вы установили Docker, вы можете увидеть сообщение permission denied (доступ запрещён) в ответ на вызов выше приведённой команды. Если вы на Mac, убедитесь, что Docker движок запущен. Если на Линукс, вам может потребоваться повысить права доступа с помощью команды `sudo`. В качестве альтернативного варианта вы можете добавить пользователя в Docker группу для решения этой проблемы.
Команда `pull` скачивает образ `busybox` из Docker реестра и сохраняет его в систему. Вы можете использовать команду `docker images` для вывода в консоль списка образов находящихся в вашей системе. Команда `pull` скачивает образ `busybox` из Docker реестра и сохраняет его в систему. Вы можете использовать команду `docker images` для вывода в консоль списка образов находящихся в вашей системе.
``` ```
# docker images $ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE REPOSITORY TAG IMAGE ID CREATED SIZE
busybox latest 00f017a8c2a6 2 weeks ago 1.11 MB busybox latest 00f017a8c2a6 2 weeks ago 1.11 MB
hello-world latest 48b5124b2768 2 months ago 1.84 kB hello-world latest 48b5124b2768 2 months ago 1.84 kB
@ -56,12 +56,12 @@ hello-world latest 48b5124b2768 2 months ago
Великолепно! Теперь перейдём к запуску контейнера на основе этого образа. Для этого мы воспользуемся всемогущей командой `docker run`. Великолепно! Теперь перейдём к запуску контейнера на основе этого образа. Для этого мы воспользуемся всемогущей командой `docker run`.
``` ```
$ docker run busybox $ sudo docker run busybox
``` ```
Постойте, но ничего не произошло! Это баг? Ну, нет. Под капотом произошло много всего. Когда вы запустили команду `run`, клиент Docker нашёл образ (в нашем случае, `busybox`), загрузил контейнер и запустил команду внутри этого контейнера. Мы не указали никаких аргументов, так что контейнер загрузился, выполнил команду `sh` и процесс контейнера завершился. Ну, да, как-то обидно. Попробуем сделать что-нибудь поинтереснее. Постойте, но ничего не произошло! Это баг? Ну, нет. Под капотом произошло много всего. Когда вы запустили команду `run`, клиент Docker нашёл образ (в нашем случае, `busybox`), загрузил контейнер и запустил команду внутри этого контейнера. Мы не указали никаких аргументов, так что контейнер загрузился, выполнил команду `sh` и процесс контейнера завершился. Ну, да, как-то обидно. Попробуем сделать что-нибудь поинтереснее.
``` ```
$ docker run busybox echo "hello from busybox" $ sudo docker run busybox echo "hello from busybox"
hello from busybox hello from busybox
``` ```
@ -69,13 +69,13 @@ hello from busybox
Давайте взглянем на команду `docker ps`. Она выводит на экран список всех запущенных контейнеров. Давайте взглянем на команду `docker ps`. Она выводит на экран список всех запущенных контейнеров.
``` ```
$ docker ps $ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
``` ```
В силу того, что ни один контейнер не запущен, выводится пустая строка. Попробуем более информативный вариант `docker ps -a`: В силу того, что ни один контейнер не запущен, выводится пустая строка. Попробуем более информативный вариант `docker ps -a`:
``` ```
$ docker ps -a $ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
56fdebcf3df0 busybox "echo hi" About a minute ago Exited (0) About a minute ago jovial_wozniak 56fdebcf3df0 busybox "echo hi" About a minute ago Exited (0) About a minute ago jovial_wozniak
5f585bdd9545 busybox "echo hi" About a minute ago Exited (0) About a minute ago focused_golick 5f585bdd9545 busybox "echo hi" About a minute ago Exited (0) About a minute ago focused_golick
@ -87,7 +87,7 @@ c73ceb428f23 hello-world "/hello" 25 minutes ago
Наверное вы думаете, существует ли способ запустить более одной команды в контейнере. Давайте попробуем: Наверное вы думаете, существует ли способ запустить более одной команды в контейнере. Давайте попробуем:
``` ```
$ docker run -it busybox sh $ sudo docker run -it busybox sh
/ # ls / # ls
bin dev etc home proc root sys tmp usr var bin dev etc home proc root sys tmp usr var
/ # uptime / # uptime
@ -104,14 +104,14 @@ bin dev etc home proc root sys tmp usr var
Давайте вкратце рассмотрим удаление контейнеров. Мы видели выше, что с помощью команды `docker ps -a` всё ещё можно увидеть остатки завершённых контейнеров. На протяжении этого занятия, вы будете запускать `docker run` несколько раз, и оставшиеся, покинутые контейнеры будут съедать дисковое пространство. Так что если они больше вам не понадобятся, вы можете взять за правило удалять контейнеры после завершения работы с ними. Для этого используется команда `docker rm`. Просто скопируйте ID (можно несколько) из вывода выше и передайте параметрами в команду. Давайте вкратце рассмотрим удаление контейнеров. Мы видели выше, что с помощью команды `docker ps -a` всё ещё можно увидеть остатки завершённых контейнеров. На протяжении этого занятия, вы будете запускать `docker run` несколько раз, и оставшиеся, покинутые контейнеры будут съедать дисковое пространство. Так что если они больше вам не понадобятся, вы можете взять за правило удалять контейнеры после завершения работы с ними. Для этого используется команда `docker rm`. Просто скопируйте ID (можно несколько) из вывода выше и передайте параметрами в команду.
``` ```
$ docker rm 305297d7a235 ff0a5c3750b9 $ sudo docker rm 305297d7a235 ff0a5c3750b9
305297d7a235 305297d7a235
ff0a5c3750b9 ff0a5c3750b9
``` ```
При удалении идентификаторы будут снова выведены на экран. Если нужно удалить много контейнеров, то вместо ручного копирования и вставки можно сделать так: При удалении идентификаторы будут снова выведены на экран. Если нужно удалить много контейнеров, то вместо ручного копирования и вставки можно сделать так:
``` ```
$ docker rm $(docker ps -a -q -f status=exited) $ sudo docker rm $(docker ps -a -q -f status=exited)
``` ```
Эта команда удаляет все контейнеры, у которых статус `exited`. Флаг `-q` возвращает только численные ID, а флаг `-f` фильтрует вывод на основе предоставленных условий. Последняя полезная деталь — команде docker run можно передать флаг `--rm`, тогда контейнер будет автоматически удаляться при завершении. Это очень удобно для разовых запусков и экспериментов с Docker. Эта команда удаляет все контейнеры, у которых статус `exited`. Флаг `-q` возвращает только численные ID, а флаг `-f` фильтрует вывод на основе предоставленных условий. Последняя полезная деталь — команде docker run можно передать флаг `--rm`, тогда контейнер будет автоматически удаляться при завершении. Это очень удобно для разовых запусков и экспериментов с Docker.
@ -144,20 +144,20 @@ $ docker rm $(docker ps -a -q -f status=exited)
Поехали. Для одностраничного сайта нам понадобится заранее созданный образ и размещённый в реестре - `nginx:latest`. Можно запустить образ напрямую командой `docker run`. Поехали. Для одностраничного сайта нам понадобится заранее созданный образ и размещённый в реестре - `nginx:latest`. Можно запустить образ напрямую командой `docker run`.
``` ```
$ docker run nginx:latest $ sudo docker run nginx:latest
``` ```
Так как образа не существует локально, клиент сначала скачает образ из реестра, а потом запустит его. Если всё пройдёт без проблем, то вы увидите в терминале сообщения о запуске nginx. Теперь сервер запущен. Как увидеть сайт в действии? На каком порту работает сервер? И, что самое важное, как напрямую достучаться до контейнера из хост системы? Так как образа не существует локально, клиент сначала скачает образ из реестра, а потом запустит его. Если всё пройдёт без проблем, то вы увидите в терминале сообщения о запуске nginx. Теперь сервер запущен. Как увидеть сайт в действии? На каком порту работает сервер? И, что самое важное, как напрямую достучаться до контейнера из хост системы?
В нашем случае клиент не открывает никакие порты, так что нужно будет перезапустить команду `docker run` чтобы сделать порты публичными. Заодно давайте сделаем так, чтобы терминал не был прикреплен к запущенному контейнеру. В таком случае можно будет спокойно закрыть терминал, а контейнер продолжит работу. Этот режим называется `detached`. В нашем случае клиент не открывает никакие порты, так что нужно будет перезапустить команду `docker run` чтобы сделать порты публичными. Заодно давайте сделаем так, чтобы терминал не был прикреплен к запущенному контейнеру. В таком случае можно будет спокойно закрыть терминал, а контейнер продолжит работу. Этот режим называется `detached`.
``` ```
$ docker run -d -P --name static-site nginx:latest $ sudo docker run -d -P --name static-site nginx:latest
e61d12292d69556eabe2a44c16cbd54486b2527e2ce4f95438e504afb7b02810 e61d12292d69556eabe2a44c16cbd54486b2527e2ce4f95438e504afb7b02810
``` ```
Флаг `-d` открепит (`--detach`) терминал, флаг `-P` сделает все открытые порты публичными и случайными, и, наконец, флаг `--name` это имя, которое мы хотим дать контейнеру. Теперь можно увидеть порты с помощью команды `docker port [CONTAINER]`. Флаг `-d` открепит (`--detach`) терминал, флаг `-P` сделает все открытые порты публичными и случайными, и, наконец, флаг `--name` это имя, которое мы хотим дать контейнеру. Теперь можно увидеть порты с помощью команды `docker port [CONTAINER]`.
``` ```
$ docker port static-site $ sudo docker port static-site
80/tcp -> 0.0.0.0:49153 80/tcp -> 0.0.0.0:49153
80/tcp -> :::49153 80/tcp -> :::49153
``` ```
@ -166,7 +166,7 @@ $ docker port static-site
Вы также можете назначить свой порт, на который Docker клиент будет перенаправлять запросы на соединение к контейнеру. Вы также можете назначить свой порт, на который Docker клиент будет перенаправлять запросы на соединение к контейнеру.
``` ```
$ docker run -p 8888:80 --name static-site nginx:latest $ sudo docker run -p 8888:80 --name static-site nginx:latest
``` ```
Ключ `-p` устанавливает соответствие между портом хост операционной системы (8888) с портом контейнера (80). Ключ `-p` устанавливает соответствие между портом хост операционной системы (8888) с портом контейнера (80).
@ -215,17 +215,17 @@ $ docker pull ubuntu:12.04
### 2.3 Наш первый образ ### 2.3 Наш первый образ
Теперь, когда мы лучше понимаем, что такое образы и какие они бывают, самое время создать собственный образ. Цель этого раздела — создать образ с простым приложением на Flask. Для этого задания подготовлено маленькое приложение `flask-app`, которое выводит случайную картинку с кошкой. Склонируйте этот репозиторий к себе на локальную машину `git clone <адрес-репозитория>` и перейдите в папку с приложением. Теперь, когда мы лучше понимаем, что такое образы и какие они бывают, самое время создать собственный образ. Цель этого раздела — создать образ с простым приложением на Flask. Для этого задания подготовлено маленькое приложение `cats-app`, которое выводит случайную картинку с кошкой. Склонируйте этот репозиторий к себе на локальную машину `git clone <адрес-репозитория>` и перейдите в папку с приложением.
Следующим шагом является создание образа с данным веб-приложением. Как говорилось выше, все пользовательские образы базируются на базовых образах. Так как приложение написано на Python, базовый образ следует выбрать с предустановленным Python 3. Точнее мы собираемся использовать `python:3.8` версию python образа. Следующим шагом является создание образа с данным веб-приложением. Как говорилось выше, все пользовательские образы базируются на базовых образах. Так как приложение написано на Python, базовый образ следует выбрать с предустановленным Python 3. Точнее мы собираемся использовать `python:3.8` версию python образа.
Теперь у нас есть все ингредиенты для создания собственных образов - работающее веб-приложение и базовый образ. Как мы будем подходить к этой задаче? Ответ - `Dockerfile`. Теперь у нас есть все ингредиенты для создания собственных образов - работающее веб-приложение и базовый образ. Как мы будем подходить к этой задаче? Ответ `Dockerfile`.
### 2.4 Dockerfile ### 2.4 Dockerfile
**Dockerfile** — это простой текстовый файл, в котором содержится список команд Docker клиента. Это простой способ автоматизировать процесс создания образа. Самое классное, что команды в Dockerfile почти идентичны своим аналогам в Linux. Это значит, что в принципе не нужно изучать новый синтаксис, чтобы начать работать с докер-файлами. **Dockerfile** — это простой текстовый файл, в котором содержится список команд Docker клиента. Это простой способ автоматизировать процесс создания образа. Самое классное, что команды в Dockerfile почти идентичны своим аналогам в Linux. Это значит, что в принципе не нужно изучать новый синтаксис, чтобы начать работать с докер-файлами.
В директории с приложением есть Dockerfile, но так как мы делаем все впервые, нам нужно создать его с нуля. Создайте новый пустой файл в любимом текстовом редакторе, и сохраните его в той же директории, где находится flask-приложение. Назовите файл Dockerfile. В директории с приложением, нам нужно его создать. Создайте пустой файл в любимом текстовом редакторе, и сохраните его в той же директории `cats-app`. Назовите файл Dockerfile.
Для начала укажем базовый образ. Для этого нужно использовать ключевое слово `FROM`. Для начала укажем базовый образ. Для этого нужно использовать ключевое слово `FROM`.
```Dockerfile ```Dockerfile
@ -243,7 +243,7 @@ RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 5000 EXPOSE 5000
``` ```
Последний шаг — указать команду для запуска приложения. Это просто `python ./app.py`. Для этого используем команду `CMD`: Последний шаг — указать команду по-умолчанию для запуска приложения. Это просто `python ./app.py`. Для этого используем команду `CMD`:
```Dockerfile ```Dockerfile
CMD ["python", "./app.py"] CMD ["python", "./app.py"]
``` ```
@ -270,11 +270,15 @@ CMD ["python", "./app.py"]
Теперь можно создать образ. Команда `docker build` занимается сложной задачей создания образа на основе `Dockerfile`. Теперь можно создать образ. Команда `docker build` занимается сложной задачей создания образа на основе `Dockerfile`.
Листинг ниже демонстрирует процесс. Перед тем, как запустите команду сами (не забудьте точку в конце), проверьте, чтобы там был ваш username. Username должен соответствовать тому, что использовался при регистрации на Docker hub. Если вы еще не регистрировались, то сделайте это до выполнения команды. Листинг ниже демонстрирует процесс. Перед тем, как запустите команду сами (не забудьте путь к `cats-app` в конце), проверьте, чтобы там был ваш `имя_пользователя`. Имя пользователя должено соответствовать тому, что использовалось при регистрации на Docker Hub. Если вы используете частный реестр, то к тэгу добавляется в начало доменное имя хоста и опциональный порт, например
```Dockerfile
# -t доменное_имя_реестра:порт/имя_пользователя/имя_образа:тэг_образа
-t myregistryhost:5000/fedora/httpd:version1.0
```
Команда `docker build` довольно проста: она принимает опциональный тег с флагом `-t имя_пользователя/имя_образа` и путь до директории, в которой лежит `Dockerfile`. Команда `docker build` довольно проста: она принимает опциональный тег с флагом `-t имя_пользователя/имя_образа` и путь до директории, в которой лежит `Dockerfile`.
``` ```
$ docker build -t имя_пользователя/имя_образа ./ $ docker build -t studX/catsapp ./
Sending build context to Docker daemon 8.704 kB Sending build context to Docker daemon 8.704 kB
Step 1 : FROM python:3-onbuild Step 1 : FROM python:3-onbuild
# Executing 3 build triggers... # Executing 3 build triggers...
@ -294,13 +298,14 @@ Step 3 : CMD python ./app.py
---> 13e87ed1fbc2 ---> 13e87ed1fbc2
Removing intermediate container f01401a5ace9 Removing intermediate container f01401a5ace9
Successfully built 13e87ed1fbc2 Successfully built 13e87ed1fbc2
Successfully tagged studX/catsapp:latest
``` ```
Если у вас нет образа `python:3.8`, то клиент сначала скачает его, а потом возьмётся за создание вашего образа. Так что, вывод на экран может отличаться от приведённого выше. Если все прошло хорошо, то образ готов! Запустите `docker images` и увидите свой образ в списке. Если у вас нет образа `python:3.8`, то клиент сначала скачает его, а потом возьмётся за создание вашего образа. Так что, вывод на экран может отличаться от приведённого выше. Если всё прошло хорошо, то образ готов! Запустите `docker images` и увидите свой образ в списке.
Последний шаг — запустить образ и проверить его работоспособность: Последний шаг — запустить образ и проверить его работоспособность:
``` ```
$ docker run -p 80:5000 --name catsapp имя_пользователя/имя_образа $ sudo docker run -p 80:5000 --name catsapp studX/catsapp
* Serving Flask app 'app' (lazy loading) * Serving Flask app 'app' (lazy loading)
* Environment: production * Environment: production
WARNING: This is a development server. Do not use it in a production deployment. WARNING: This is a development server. Do not use it in a production deployment.
@ -329,7 +334,7 @@ Press CTRL+C to quit
### 3.1 Приложение для поиска фургонов c едой в Сан-Франциско ### 3.1 Приложение для поиска фургонов c едой в Сан-Франциско
Приложение, которое мы переведём в Docker, называется SF Food Trucks. Приложение создавалось с целью сделать что-то похожее на реально эксплуатируемое приложение и приносещее пользу, но не слишком сложное. Приложение, которое мы переведём в Docker, называется `foodtrucks-app`. Приложение создавалось с целью сделать что-то похожее на реально эксплуатируемое приложение и приносещее пользу, но не слишком сложное.
Серверная часть написана на Python (Flask framework), а для поиска используется Elasticsearch. Как и всё остальное в этом обучении, код проекта находится на Github. Мы используем это приложение, чтобы научиться запускать и разворачивать многоконтейнерное окружение. Серверная часть написана на Python (Flask framework), а для поиска используется Elasticsearch. Как и всё остальное в этом обучении, код проекта находится на Github. Мы используем это приложение, чтобы научиться запускать и разворачивать многоконтейнерное окружение.
@ -341,15 +346,22 @@ Press CTRL+C to quit
Не удивительно, что существует официальный образ для Elasticsearch. Чтобы запустит ES, нужно всего лишь выполнить `docker run`, и вскоре у нас будет локальный, работающий контейнер с одним узлом ES. Некоторые компании считают плохой практикой выкладывать образы с тегом `latest`, в том числе Elastic, поэтому выберем и укажеи тег явно. Не удивительно, что существует официальный образ для Elasticsearch. Чтобы запустит ES, нужно всего лишь выполнить `docker run`, и вскоре у нас будет локальный, работающий контейнер с одним узлом ES. Некоторые компании считают плохой практикой выкладывать образы с тегом `latest`, в том числе Elastic, поэтому выберем и укажеи тег явно.
**Замечание.** Если контейнер не запускается, попробуйте подключиться в интерактивном режиме (с ключом `-it`) и выяснить причину ошибки. Одной из причин может быть нехватка памяти для Elasticsearch. Для версии старше 5 требуется минимум 2 Гб. Проблема также может быть в хост машине. Увеличьте количество доступных кусочков памяти процессу выполнением команды `sysctl -w vm.max_map_count=262144`. Обратите внимание, что это модифицирует свойство системы, в которой может выполняться множество других контейнеров. **Замечание.** Если контейнер не запускается, попробуйте подключиться в интерактивном режиме (с ключом `-it`) и выяснить причину ошибки. Одной из причин может быть нехватка памяти для Elasticsearch. Для версии старше 5 требуется минимум 2 Гб. Если вы увеличили оббъём памяти в виртуальной машине, не забудьте остановить и запустить виртуальную машину, чтобы изменения применились.
Проблема также может быть в максимальное количество кусочков памяти `vm.max_map_count`, которые может иметь процесс . Увеличьте количество доступных кусочков памяти процессу выполнением команды `sysctl -w vm.max_map_count=262144`. Обратите внимание, что это модифицирует свойство системы, в которой может выполняться множество других контейнеров.
``` ```
$ docker run -d -p 9200:9200 --name elastic elasticsearch:8.4.3 $ sudo docker run -d -p 9200:9200 --name elastic elasticsearch:8.4.3
d582e031a005f41eea704cdc6b21e62e7a8a42021297ce7ce123b945ae3d3763 d582e031a005f41eea704cdc6b21e62e7a8a42021297ce7ce123b945ae3d3763
``` ```
Проверьте, что в логах нет ошибок.
```
$ sudo docker logs elastic
```
При запуске в режиме демона сервис не генерирует автоматически пароль для пользователя `elastic`. Мы можем сделать это сами после запуска, чтобы протестировать сервис. При запуске в режиме демона сервис не генерирует автоматически пароль для пользователя `elastic`. Мы можем сделать это сами после запуска, чтобы протестировать сервис.
``` ```
docker exec -ti elastic /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic $ sudo docker exec -ti elastic /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
WARNING: Owner of file [/usr/share/elasticsearch/config/users] used to be [root], but now is [elasticsearch] WARNING: Owner of file [/usr/share/elasticsearch/config/users] used to be [root], but now is [elasticsearch]
WARNING: Owner of file [/usr/share/elasticsearch/config/users_roles] used to be [root], but now is [elasticsearch] WARNING: Owner of file [/usr/share/elasticsearch/config/users_roles] used to be [root], but now is [elasticsearch]
This tool will reset the password of the [elastic] user to an autogenerated value. This tool will reset the password of the [elastic] user to an autogenerated value.
@ -382,11 +394,11 @@ $ curl https://localhost:9200 --insecure --user elastic:36EBVQtPjbiFPMh9Bk7X
} }
``` ```
Заодно давайте запустим контейнер с Flask. Но вначале нужен Dockerfile. В прошлой секции мы использовали образ `python:3.8` в качестве базового. Однако, в этом раз, кроме установки зависимостей через `pip`, нам нужно, чтобы приложение генерировало Javascript файл. Для этого потребуется Nodejs. В связи с появлением дополнительных файлов для работы контейнера нам нужно построить новый образ. Начнем с базового образа debian:buster. Заодно давайте запустим контейнер с Flask. Но вначале нужен Dockerfile. В прошлой раз мы использовали образ `python:3.8` в качестве базового. Однако, в этом раз, кроме установки зависимостей через `pip`, нам нужно, чтобы приложение генерировало Javascript файл. Для этого потребуется Nodejs. В связи с появлением дополнительных файлов для работы контейнера нам нужно построить новый образ. Начнем с базового образа `ubuntu:focal`.
**Замечание.** Если оказывается, что существующий образ не подходит для вашей задачи, то спокойно создавайте свой образ на основе другого базового образа. В большинстве случаев, для образов на Docker Hub можно найти соответствующий Dockerfile на Github. Почитайте существующие докерфайлы — это один из лучших способов научиться делать свои образы. **Замечание.** Если оказывается, что существующий образ не подходит для вашей задачи, то спокойно создавайте свой образ на основе другого базового образа. В большинстве случаев, для образов на Docker Hub можно найти соответствующий Dockerfile на Github. Почитайте существующие докерфайлы — это один из лучших способов научиться делать свои образы.
Наш Dockerfile для Flask-приложения выглядит следующим образом: Наш Dockerfile для приложения `foodtrucks-app` выглядит следующим образом:
``` ```
# start from base # start from base
FROM ubuntu:focal FROM ubuntu:focal
@ -415,16 +427,16 @@ ENTRYPOINT [ "python3", "./app.py" ]
Тут много всего нового. Вначале указан `focal` образ Ubuntu, потом используется пакетный менеджер `apt` для установки зависимостей, в частности: Python и Node. Флаг `y` нужен автоматического выбора "Yes" во всех диалогах. Также создается символическая ссылка для бинарного файла node. Это нужно для решения проблем обратной совместимости. Тут много всего нового. Вначале указан `focal` образ Ubuntu, потом используется пакетный менеджер `apt` для установки зависимостей, в частности: Python и Node. Флаг `y` нужен автоматического выбора "Yes" во всех диалогах. Также создается символическая ссылка для бинарного файла node. Это нужно для решения проблем обратной совместимости.
Потом мы используем команду ADD для копирования приложения в нужную директорию в контейнере — `/opt/flask-app`. Здесь будет находиться весь наш код. Мы также устанавливаем эту директорию в качестве рабочей, так что следующие команды будут выполняться в контексте этой локации. Теперь, когда наши системные зависимости установлены, пора установить зависимости уровня приложения. Начнем с Node, установки пакетов из npm и запуска команды сборки, как указано в нашем `package.json` файле. В конце устанавливаем пакеты Python, открываем порт и определяем запуск приложения с помощь ENTRYPOINT. ENTRYPOINT отличается от CMD тем, что при запуске можно передвать в контейнер параметры, которые добавятся к ENTRYPOINT. Потом мы используем команду ADD для копирования приложения в нужную директорию в контейнере — `/opt/flask-app`. Здесь будет находиться весь наш код. Мы также устанавливаем эту директорию в качестве рабочей, так что следующие команды будут выполняться в контексте этой локации. Теперь, когда наши системные зависимости установлены, пора установить зависимости уровня приложения. Начнем с Node, установки пакетов из npm и запуска команды сборки, как указано в нашем `package.json` файле. В конце устанавливаем пакеты Python, открываем порт и определяем запуск приложения с помощь ENTRYPOINT. ENTRYPOINT отличается от CMD тем, что при запуске можно передвать в контейнер параметры, которые добавятся к ENTRYPOINT. В нашем случае это будет параметры elasticsearch сервера: протокол, доменное имя, порт, имя пользователя, пароль.
Наконец, можно собрать образ и запустить контейнер. Наконец, можно собрать образ и запустить контейнер.
``` ```
$ docker build -t studX/<имя образа>. $ sudo docker build -t studX/foodtrucks-web.
``` ```
При первом запуске нужно будет больше времени, так как клиент Докера будет скачивать образ ubuntu, запускать все команды и готовить образ. Повторный запуск `docker build` после последующих изменений будет практически моментальным. Давайте попробуем запустить приложение. При первом запуске нужно будет больше времени, так как клиент Докера будет скачивать образ ubuntu, запускать все команды и готовить образ. Повторный запуск `docker build` после последующих изменений будет практически моментальным. Давайте попробуем запустить приложение.
``` ```
$ docker run -P studX/<имя образа> https elastic 9200 elastic 36EBVQtPjbiFPMh9Bk7X $ docker run -P studX/foodtrucks-web https localhost 9200 elastic 36EBVQtPjbiFPMh9Bk7X
Unable to connect to ES. Retying in 5 secs... Unable to connect to ES. Retying in 5 secs...
Unable to connect to ES. Retying in 5 secs... Unable to connect to ES. Retying in 5 secs...
Unable to connect to ES. Retying in 5 secs... Unable to connect to ES. Retying in 5 secs...
@ -439,34 +451,44 @@ Out of retries. Bailing out...
Ладно, давайте запустим docker ps. Что тут у нас: Ладно, давайте запустим docker ps. Что тут у нас:
``` ```
$ docker ps $ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c31bf3beb299 elasticsearch "/docker-entrypoin..." 2 hours ago Up 2 hours 0.0.0.0:9200->9200/tcp, 9300/tcp tender_wilson c31bf3beb299 elasticsearch "/docker-entrypoin..." 2 hours ago Up 2 hours 0.0.0.0:9200->9200/tcp, 9300/tcp tender_wilson
``` ```
Итак, у нас есть контейнер ES по любому локальному адресу (0.0.0.0) и порту 9200, и мы можем напрямую обращаться к нему. Если можно было бы сообщить нашему приложению подключаться к этому адресу, то оно сможет общаться с ES, верно? Давайте взглянем на код на Питоне, туда, где описано подключение. Итак, у нас есть контейнер `elastic`, который слушает по любому локальному адресу (0.0.0.0) и порту 9200, и мы можем напрямую обращаться к нему с хоста. Если можно было бы сообщить нашему приложению как подключиться к этому адресу, то оно сможет общаться с `elastic`, верно? Давайте взглянем на код на Python, туда, где описано подключение и вспомним, переданные в контейнер аргументы.
``` ```python
es_scheme = sys.argv[1]
es_host = sys.argv[2]
es_port = int(sys.argv[3])
es_user = sys.argv[4]
es_password = sys.argv[5]
connection_string = f"{es_scheme}://{es_host}:{es_port}"
es = Elasticsearch( es = Elasticsearch(
f"{es_scheme}://{es_host}:{es_port}", connection_string,
basic_auth=(es_user, es_password), basic_auth=(es_user, es_password),
verify_certs=False verify_certs=False
) )
``` ```
```
https localhost 9200 elastic 36EBVQtPjbiFPMh9Bk7X
```
Для того, чтобы это заработало, нужно запустить Flask контейнер на том же хосте, что и контейнер ES, и всё заработает, да? К сожалению, нет, потому что контейнер ES доступен по адресу хост-машины только с хост-машины. Другой контейнер не сможет обратиться по этому адресу. Ладно, если не этот адрес, то какой тогда адрес нужно использовать для работы с контейнером ES? Рады, что вы спросили. Для того, чтобы это заработало, нужно запустить `foodttrucks-web` контейнер на том же хосте, что и контейнер `elastic`, и всё заработает, да? К сожалению, нет, потому что контейнер `elastic` доступен по адресу хост-машины только с хост-машины. Другой контейнер не сможет обратиться по этому адресу. Ладно, если не этот адрес, то какой тогда адрес нужно использовать для работы с контейнером `elastic`? Хорошо, что вы спросили.
Подошло время, чтобы изучить работу сети в Docker. После установки, Docker автоматически создает три сети: Подошло время, чтобы изучить работу сети в Docker. После установки, Docker автоматически создает три сети:
``` ```
$ docker network ls $ sudo docker network ls
NETWORK ID NAME DRIVER NETWORK ID NAME DRIVER
075b9f628ccc none null 075b9f628ccc none null
be0f7178486c host host be0f7178486c host host
8022115322ec bridge bridge 8022115322ec bridge bridge
``` ```
Сеть bridge — это сеть, в которой контейнеры запущены по умолчанию (программный роутер). Это значит, что когда вы запускаете контейнер ES, он работает в bridge сети. Чтобы удостовериться, давайте проверим: Сеть bridge — это сеть, в которой контейнеры запущены по умолчанию (программный роутер). Это значит, что когда вы запускаете контейнер `elastic`, он работает в bridge сети. Чтобы удостовериться, давайте проверим:
``` ```
$ docker network inspect bridge $ sudo docker network inspect bridge
[ [
{ {
"Name": "bridge", "Name": "bridge",
@ -521,9 +543,9 @@ $ docker network inspect bridge
] ]
``` ```
Видно, что контейнер `e931ab24dedc` находится в секции Containers. Также виден IP-адрес, выданный этому контейнеру —` 172.17.0.2`. Именно этот адрес мы и искали? Давайте проверим: запустим Flask-приложение и попробуем обратиться к нему по IP: Видно, что контейнер `e931ab24dedc` находится в секции Containers. Также виден IP-адрес, выданный этому контейнеру — `172.17.0.2`. Именно этот адрес мы и искали? Давайте проверим: запустим `foodtrucks-web` приложение и попробуем обратиться к нему по IP:
``` ```
$ docker run -it --rm studX/<имя образа> --name flaskapp bash $ sudo docker run -it --rm studX/foodtrucks-web --name flaskapp bash
root@35180ccc206a:/opt/flask-app# curl https://172.17.0.2:9200 --insecure --user elastic:36EBVQtPjbiFPMh9Bk7X root@35180ccc206a:/opt/flask-app# curl https://172.17.0.2:9200 --insecure --user elastic:36EBVQtPjbiFPMh9Bk7X
{ {
"name" : "a739a33489d9", "name" : "a739a33489d9",
@ -545,11 +567,11 @@ root@35180ccc206a:/opt/flask-app# curl https://172.17.0.2:9200 --insecure --user
root@35180ccc206a:/opt/flask-app# exit root@35180ccc206a:/opt/flask-app# exit
``` ```
Cейчас всё должно стать понятно. Мы запустили контейнер в интерактивном режиме с процессом bash. Флаг `--rm` нужен для удобства, благодаря нему контейнер автоматически удаляется после выхода. Мы попробуем curl, но нужно сначала установить его. После этого можно удостовериться, что по адресу 172.17.0.2:9200 на самом деле можно обращаться к ES! Супер! Cейчас всё должно стать понятно. Мы запустили контейнер в интерактивном режиме с процессом `bash`. Флаг `--rm` нужен для удобства. Благодаря нему контейнер автоматически удаляется после выхода. Мы попробуем `curl`, но нужно сначала установить его. После этого можно удостовериться, что по адресу `172.17.0.2:9200` на самом деле можно обращаться к `elastic`! Супер!
Не смотря на то, что мы нашли способ наладить связь между контейнерами, существует несколько проблем с этим подходом: Не смотря на то, что мы нашли способ наладить связь между контейнерами, существует несколько проблем с этим подходом.
Придется добавлять записи в файл `/etc/hosts` (локальный DNS) внутри Flask-контейнера, чтобы приложение понимало, что имя хоста es означает 172.17.0.2. Если IP-адрес меняется, то придется вручную менять запись. Придется добавлять записи в файл `/etc/hosts` (локальный DNS) внутри `foodtrucks-web` контейнера, чтобы приложение понимало, что имя хоста `elastic` означает `172.17.0.2`. Если IP-адрес меняется, то придется вручную менять запись.
Так как сеть bridge используется всеми контейнерами по умолчанию, этот метод не безопасен. Так как сеть bridge используется всеми контейнерами по умолчанию, этот метод не безопасен.
@ -557,11 +579,11 @@ Cейчас всё должно стать понятно. Мы запустил
Во-первых, давайте создадим свою сеть. Во-первых, давайте создадим свою сеть.
``` ```
$ docker network create foodtrucks-network $ sudo docker network create foodtrucks-network
1a3386375797001999732cb4c4e97b88172d983b08cd0addfcb161eed0c18d89 1a3386375797001999732cb4c4e97b88172d983b08cd0addfcb161eed0c18d89
``` ```
``` ```
$ docker network ls $ sudo docker network ls
NETWORK ID NAME DRIVER NETWORK ID NAME DRIVER
1a3386375797 foodtrucks-network bridge 1a3386375797 foodtrucks-network bridge
8022115322ec bridge bridge 8022115322ec bridge bridge
@ -573,20 +595,31 @@ be0f7178486c host host
Теперь у нас есть сеть. Можно запустить наши контейнеры внутри сети с помощью флага `--net`. Давайте так и сделаем, но сначала остановим контейнер с ElasticSearch, который был запущен в сети `bridge` по умолчанию. Теперь у нас есть сеть. Можно запустить наши контейнеры внутри сети с помощью флага `--net`. Давайте так и сделаем, но сначала остановим контейнер с ElasticSearch, который был запущен в сети `bridge` по умолчанию.
``` ```
$ docker ps $ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e931ab24dedc elasticsearch "/docker-entrypoint.s" 4 hours ago Up 4 hours 0.0.0.0:9200->9200/tcp, 9300/tcp cocky_spence e931ab24dedc elasticsearch "/docker-entrypoint.s" 4 hours ago Up 4 hours 0.0.0.0:9200->9200/tcp, 9300/tcp cocky_spence
``` ```
``` ```
$ docker stop e931ab24dedc $ sudo docker stop e931ab24dedc
e931ab24dedc
$ sudo docker rm e931ab24dedc
e931ab24dedc e931ab24dedc
``` ```
``` ```
$ docker run -d -p 9200:9200 --net foodtrucks-network --name elastic elasticsearch:8.4.3 $ sudo docker run -d -p 9200:9200 --net foodtrucks-network --name elastic elasticsearch:8.4.3
2c0b96f9b8030f038e40abea44c2d17b0a8edda1354a08166c33e6d351d0c651 2c0b96f9b8030f038e40abea44c2d17b0a8edda1354a08166c33e6d351d0c651
``` ```
Сгенерируем пароль для Elasticsearch, как делали ранее:
``` ```
$ docker network inspect foodtrucks-network $ sudo docker exec -ti elastic /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
...
Password for the [elastic] user successfully reset.
New value: 9gllhwpGufZq2GfkfZ9c
```
Мы сделали то же, что и раньше, но на этот раз присоединили контейнер к сети `foodtrucks-network`.
```
$ sudo docker network inspect foodtrucks-network
[ [
{ {
"Name": "foodtrucks-network", "Name": "foodtrucks-network",
@ -627,10 +660,10 @@ $ docker network inspect foodtrucks-network
] ]
``` ```
Мы сделали то же, что и раньше, но на этот раз присоединили контейнер к сети foodtrucks. Перед тем, как запускать контейнер с приложением, давайте проверим что происходит, когда запуск происходит в сети. Перед тем, как запускать контейнер с приложением, давайте проверим что происходит, когда запуск происходит в сети.
``` ```
$ docker run -it --rm --net foodtrucks-network studX/<имя образа> bash $ sudo docker run -it --rm --net foodtrucks-network --entrypoint /bin/bash studX/foodtrucks-web
root@53af252b771a:/opt/flask-app# curl https://elastic:9200 --insecure --user elastic:v7SLsbtXticPLADei5vS root@53af252b771a:/opt/flask-app# curl https://elastic:9200 --insecure --user elastic:9gllhwpGufZq2GfkfZ9c
{ {
"name" : "fe09955428cc", "name" : "fe09955428cc",
"cluster_name" : "docker-cluster", "cluster_name" : "docker-cluster",
@ -649,44 +682,55 @@ root@53af252b771a:/opt/flask-app# curl https://elastic:9200 --insecure --user el
"tagline" : "You Know, for Search" "tagline" : "You Know, for Search"
} }
``` ```
Файлы приложения скопировались внутрь.
``` ```
root@53af252b771a:/opt/flask-app# ls root@53af252b771a:/opt/flask-app# ls
app.py node_modules package.json requirements.txt static templates webpack.config.js app.py node_modules package.json requirements.txt static templates webpack.config.js
``` ```
Попробуем запустить.
``` ```
root@53af252b771a:/opt/flask-app# python3 app.py https elastic 9200 elastic v7SLsbtXticPLADei5vS root@53af252b771a:/opt/flask-app# python3 app.py https elastic 9200 elastic v7SLsbtXticPLADei5vS
Index not found... ...
Loading data in elasticsearch ... Total trucks loaded: 478
Total trucks loaded: 733 * Serving Flask app 'app' (lazy loading)
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) * Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://172.21.0.3:5000
Press CTRL+C to quit
``` ```
``` ```
root@53af252b771a:/opt/flask-app# exit root@53af252b771a:/opt/flask-app# exit
``` ```
Ура! Работает! Магическим образом Docker теперь разрешает имя es в нужный ip, и поэтому es:9200 можно использовать в приложении — этот адрес корректно направляет запросы в контейнер ES. Отлично! Давайте теперь запустим Flask-контейнер по-настоящему: Ура! Работает! Магическим образом Docker теперь разрешает имя `elastic` в нужный ip, и поэтому `https://elastic:9200` можно использовать в приложении — этот адрес корректно направляет запросы в контейнер `elastic`. Отлично! Давайте теперь запустим `foodtrucks-web` контейнер по-настоящему:
``` ```
$ docker run -d --net foodtrucks -p 80:5000 --name foodtrucks-web <имя пользователя Docker>/<имя образа> $ sudo docker run -d --net foodtrucks-network -p 80:5000 --name foodtrucks-web studX/foodtrucks-web
2a1b77e066e646686f669bab4759ec1611db359362a031667cacbe45c3ddb413 2a1b77e066e646686f669bab4759ec1611db359362a031667cacbe45c3ddb413
``` ```
``` ```
$ docker ps $ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a1b77e066e6 prakhar1989/foodtrucks-web "python ./app.py" 2 seconds ago Up 1 seconds 0.0.0.0:5000->5000/tcp foodtrucks-web 2a1b77e066e6 prakhar1989/foodtrucks-web "python ./app.py" 2 seconds ago Up 1 seconds 0.0.0.0:5000->5000/tcp foodtrucks-web
2c0b96f9b803 elasticsearch "/docker-entrypoint.s" 21 minutes ago Up 21 minutes 0.0.0.0:9200->9200/tcp, 9300/tcp es 2c0b96f9b803 elasticsearch "/docker-entrypoint.s" 21 minutes ago Up 21 minutes 0.0.0.0:9200->9200/tcp, 9300/tcp elastic
``` ```
Зайдите на http://studX.myoffice.ru, и увидите приложение в работе. Опять же, может показаться, что было много работы, но на самом деле мы ввели всего 4 команды чтобы с нуля дойти до работающего приложения. Все команды, которые мы проделали собраны в bash скрипте. Зайдите на http://studX.myoffice.ru, и увидите приложение в работе. Опять же, может показаться, что было много работы, но на самом деле мы ввели всего 4 команды чтобы с нуля дойти до работающего приложения. Все команды, которые мы проделали собраны в bash скрипте.
``` ```
#!/bin/bash #!/bin/bash
# build the flask container # build the flask container
docker build -t studX/foodtrucks-web ./ docker build -t studX/foodtrucks-web ./
# create the network # create the network
docker network create foodtrucks-network docker network create foodtrucks-network
# start the ES container # start the ES container, specify password beforehand
docker run -d --net foodtrucks-network --name elastic elasticsearch:8.4.3 docker run -d --net foodtrucks-network --name elastic -e ELASTIC_PASSWORD=v7SLsbtXticPLADei5vS elasticsearch:8.4.3
# start the flask app container # start the flask app container
# point to elastic host: https://elastic:9200 user: elastic password: v7SLsbtXticPLADei5vS # point to elastic host: https://elastic:9200 user: elastic password: v7SLsbtXticPLADei5vS

Loading…
Cancel
Save