Начало работы с Doker.
Основы работы с Linux. -> Flask с Doker.
Flask с Doker.
Создадим стартовую страницу index.html и положим ее в папку templates.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Docker Flask</title>
</head>
<body>
<h2>Hello from Flask container</h2>
</body>
</html>
Создадим файл с зависимостями requirements.txt.
Flask
Создадим файл Dockerfile
FROM python:3.6
RUN mkdir /app
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN apt update
RUN pip install -r requirements.txt
Создадим сборщик docker-compose.yaml.
version: '3.5'
services:
flask:
build:
context: .
dockerfile: Dockerfile-flask
volumes:
- .:/app
Создадим скрипт для запуска start.py.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0')
Добавим проброс портов.
ports:
- 8082:5000
Добавим команду запуска.
CMD python start.py
Добавим рендеринг шаблона.
from flask import render_template
@app.route('/')
def hello_world():
return render_template('index.html')
Проксирование через nginx.
Cоздаем файл для образа Dockerfile-nginx
FROM nginx:latest
RUN mkdir /app
COPY ./default.conf /etc/nginx/conf.d/default.conf
Файл конфигурации.
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://flask:5000;
}
}
Добавляем контейнер в сборщик.
nginx:
build:
context: .
dockerfile: Dockerfile-nginx
volumes:
- .:/app
ports:
- 8084:80
Проксируем статику.
<img src="/static/test.png" />
Добавляем путь к статике в default.conf.
...
location /static/ {
alias /app/static/;
}
...
Основы работы с Linux. -> Начало работы с Doker.
Начало работы с Doker.
Докером (docker) назывался рабочий, который переносил товары на суда и обратно, когда те стояли в портах.
Там были ящики и грузы разных размеров и форм, и опытные докеры ценились за то, что они в состоянии вручную поместить товары на корабли экономически эффективным способом.
Нанять людей для перемещения этих грузов было недешево, но альтернативы не было.
Это должно быть знакомо всем, кто работает в сфере программного обеспечения.
Много времени и интеллектуальной энергии тратится на перенос программного обеспечения на разные системы и сервера.
На рис показано, как можно сэкономить время и деньги с помощью концепции Docker.
Когда мы покупаем мощный сервер, состоящий из множества процессоров и памяти, то возникает потребность его ‘распилить’ на более мелкие сервера.
И потом на каждый сервер повесить свой процесс, сайт, сервис и т.д.
Т.е. создается гипервизор (основная операционная система) и в него устанавливаются гостевые операционные системы (виртуальные сервера).
Каждому виртуальному серверу выделяются ограниченные ресурсы по памяти процессорам и т.д. Это так же делает безопасным ситуацию взлома виртуального сервера, когда страдает только он.
Docker может использоваться для замены виртуальных машин во многих ситуациях. Если вас волнует только приложение, а не операционная система, Docker может заменить виртуальную машину, а вы можете оставить заботу об операционной системе комуто другому.
Docker не только быстрее, чем виртуальная машина, предназначенная для запуска, он более легок в перемещении, и благодаря его многоуровневой файловой системе вы можете легче и быстрее делиться изменениями с другими. Он также прочно укоренен в команд ной строке и в высшей степени пригоден для написания сценариев.
Если вы хотите быстро поэкспериментировать с программным обеспечением, не нарушая существующую настройку и не проходя через трудности, связанные с подготовкой виртуальной машины, Docker может предоставить «песочницу» за миллисекунды. Освобождающий эффект этого процесса трудно понять, пока вы не испытаете его на себе.
Докер включает в себя 2 понятия - это образ и контейнер.
Образ - это собранный проект на базе операционной системы, содержащий все необходимое для его работы.
Контейнер - это запущенный, работающий образ. Таким образом один раз собранный образ может быть запущен в неограниченном количестве контейнеров.
Создавая образы в структурированном виде, готовые к перемещению в различные среды, Docker заставляет вас явно документировать свои программные зависимости с базовой отправной точки. Даже если вы решите не использовать Docker повсюду, эта документаци
Непрерывная доставка
Это парадигма доставки программного обеспечения, основанная на конвейере, который перестраивает систему при каждом изменении, а затем осуществляет доставку в производство (или «вживую») посредством автоматизированного (или частично автоматизированного) процесса.
Поскольку вы можете более точно контролировать состояние среды сборки, сборки Docker более воспроизводимы, чем традиционные методы сборки программного обеспечения. Это делает реализацию непрерывной доставки намного проще.
Установка на Ubuntu
Ставим необходимые библиотеки.
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
Копируем GPG ключ.
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
Добавляем репозиторий.
sudo add-apt-repository \
“deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable”
Установка.
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
Ключевые концепции и комманды.
Прежде чем запускать команды Docker, лучше всего разобраться с понятиями образов, контейнеров и слоев. Говоря кратко, контейнеры запускают системы, определенные образами. Эти образы состоят из одного или нескольких слоев (или наборов различий) плюс некоторые метаданные Docker.
Основная функция Docker – создавать, отправлять и запускать программное обеспечение в любом месте, где есть Docker.
Для конечного пользователя Docker – это программа c командной строкой, которую они запускают.
docker build Собрать образ Docker
docker run Запустить образ Docker в качестве контейнера
docker commit Сохранить контейнер Docker в качестве образа
docker tag Присвоить тег образу Docker
Один из способов взглянуть на образы и контейнеры – это рассматривать их как программы и процессы. Точно так же процесс может рассматриваться как «выполняемое приложение», контейнер Docker может рассматриваться как образ, выполняемый Docker.
Если вы знакомы с принципами объектноориентированного программирования, еще один способ взглянуть на образы и контейнеры – это рассматривать образы как классы, а контейнеры – как объекты.
Контейнеры запускают один процесс при запуске. Когда процесс завершается, контейнер останавливается.
Например из одного образа можно запустить 3 контейнера:
-
django
-
mysql
-
nginx
Способы создания образов.
Их существует несколько.
- Запустить контейнер в командной строке, указав его параметры в команде docker run. Затем можно перенести контейнер в образ с помощью docker commit.
Подойдет если вы хотите проверить работает ли ваш просесс установки.
- Выполнить сборку из известного базового образа, указав ее в Dockerfile.
Dockerfile – это текстовый файл, содержащий серию команд.
Основные команды.
FROM
COPY
ADD
RUN
ENTRYPOINT [“
CMD [““, …] : Определение параметров команды запуска, учитывая что по дефолту /bin/sh -c, мы можем к нему добавить параметры через CMD.
WORKDIR
Файл Dockerfile начинается с определения базового образа с помощью команды FROM. В этом примере используется образ Node.js, поэтому у вас есть доступ к двоичным файлам Node.js. Официальный образ Node.js называется node.
git уже установлен в базовом образе, но это не относиттся ко всем образам.
WORKDIR - не только меняет директорию, но и определяет в каком каталоге вы будете находится при запуске приложения.
EXPOSE - сообщает Docker, что контейнеры из собранного образа должны прослушивать этот порт.
Как видим Dockerfile – это простая последовательность ограниченного набора команд, выполняемых в строгом порядке.
Здесь команда RUN влияет на файловую систему, проверяя и устанавливая приложения, а команды EXPOSE, CMD и WORKDIR – на метаданные образа.
Сборка образа.
По окончанию сборки нам выдадут окончательный идентификатор образа, готовый для присвоению тега.
Successfully built 66c76cea05bb
Присвоить тег можно командой
docker tag 66c76cea05bb mytag
Запуск контейнера из образа.
doker run -i -t -p 8000:8000 --name example1 mytag
-p - перенаправляет порт из контейнера (первый) на хост компьютера (второй)
Подкоманда docker diff показывает, какие файлы были затронуты с момента создания экземпляра образа как контейнера.
Иногда может возникать ошибка доступа (permisson denite) к сокету докера. Тогда помогает такая установка прав на сокет.
sudo chmod 666 /var/run/docker.sock
Слои Docker
Слои Docker помогают справиться с большой проблемой, которая возникает, когда вы используете контейнеры в широком масштабе.
Представьте себе, что произойдет, если вы запустите сотни или даже тысячи приложений, и каж дому из них потребуется копия файлов для хранения в каком-либо месте.
Как вы можете себе представить, дисковое пространство закончится довольно быстро!
По умолчанию Docker внутренне использует механизм копирования при записи, чтобы уменьшить объем требуемого дискового пространства.
Вкратце суть метода.
Идея подхода copy-on-write заключается в том, что при чтении области данных используется общая копия, в случае изменения данных — создается новая копия.
При создании образа Doker создает слои файловой системы и их использует при создании новых образов. Например если два образа используют одну ОС Ubuntu то она не будет скопирована в оба, а они будут использовать один и тот же слой, содержащий эту операционную систему.
Архитектура Docker
Исходя из рисунка видно, что докер-образы в реестре мы можем хранить как на своем сервере, так и на официальном сайте Docker Hub или на других не официальных.
Когда мы запускаем контейнер, то процесс взаимодействия с ним включает 2 сущности: клиент Doker и демон Doker.
Каждый раз, когда вводим команду, мы ее сообщаем клиенту, тот в свою очередь дергает демона по HTTP и возвращает нам результат.
Демон – это сервер, который получает запросы и возвращает ответы от клиента по протоколу HTTP.
В свою очередь, он будет отправлять запросы в другие службы для отправки и получения образов, также используя протокол HTTP.
Демон также отвечает за заботу о ваших образах и контейнерах за кулисами, тогда как клиент выступает в качестве посредника между вами и интерфейсом RESTful.
Демон Docker
Демон – это процесс, который выполняется в фоновом режиме, а не под непосредственным контролем пользователя. Сервер – процесс, который принимает запросы от клиента и ему отвечает.
Демоны часто также являются серверами, принимающими запросы от клиентов для выполнения действий для них.
Команда docker – это клиент, а демон Docker выступает в качестве сервера, выполняющего обработку ваших контейнеров и образов Docker.
Демон Docker – это центр ваших взаимодействий с Docker, и поэтому он является лучшим местом, где вы можете начать понимать все соответствующие элементы.
Он контролирует доступ к Docker на вашем компьютере, управляет состоянием контейнеров и образов, а также взаимодействует с внешним миром.
Делаем доступным демон из вне.
Хотя по умолчанию ваш демон Docker доступен только на вашем хосте, мо- гут быть причины, чтобы разрешить другим доступ к нему.
Хотя этот метод может быть мощным и полезным, он считается небезопасным. Сокет Docker может быть использован любым, у кого есть доступ (включая контейнеры с подключенным сокетом Docker) для получения привилегий пользователя root.
РЕШЕНИЕ - запустить демон Docker с открытым TCP адресом.
Останов демона.
sudo service docker stop
Проверить запущен ли демон докера можно так:
ps -ef | grep -E 'docker(d| -d| daemon)\b' | grep -v grep
Запускаем на сокете.
sudo docker daemon -H tcp://0.0.0.0:2375
Вы можете подключиться снаружи с помощью следующей команды:
$ docker -H tcp://<your host's ip>:2375 <subcommand>
Запуск контейнеров в качестве демонов
ПРОБЛЕМА
Вы хотите запустить контейнер Docker в фоновом режиме как службу.
РЕШЕНИЕ
Используйте флаг -d в команде docker run и связанные флаги управления контейнерами для определения характеристик службы.
$ docker run -d -i -p 1234:1234 --name daemon ubuntu:14.04 nc -l 1234
-d - запустить контейнер качестве демона.
-i - дает этому контейнеру возможность взаимодействовать с вашим сеансом Telnet.
-p - публикуем порт 1234 из контейнера на хост
–name - задем контейнеру имя. Давать имена контейнерам очень полезно для присваивания запоминаемых имен хостов, к которым вы можете обратиться позже
nc -l 1234 - запускаем простой прослушивающий эхосервер на порту 1234 с помощью netcat (nc).
В простом случае NetCat вызывается как:
nc host port
Это приводит к созданию TCP-подключения с указанными реквизитами и замыканием стандартного ввода на сетевой вывод и наоборот, стандартного вывода на сетевой ввод. Такая функциональность напоминает команду cat, что обусловило выбор имени «netcat». При невозможности подключения программа выводит сообщение об ошибке на stderr.
Теперь можно подключиться к нему и отправлять сообщения через Telnet. Вы увидите, что контейнер получил сообщение с помощью команды docker logs, как показано в следующем листинге.
Останов контейнера.
$ docker stop blog1
Удаление контейнера
$ docker rm blog1
Список всех запущенных контейнеров.
docker ps
Стратегия перезапуска демона.
Флаг docker run–restart позволяет применять набор правил, которым необходимо следовать (так называемая стратегия повторного запуска), когда контейнер завершается
no - Не перезапускать при выходе контейнера
always - Всегда перезапускать при выходе контейнера
unless-stopped - Всегда перезагружать, но помнить о явной остановке
on-failure[:max-retry] - Перезапускать только в случае сбоя
$ docker run -d --restart=always ubuntu echo done
Эта команда запускает контейнер в качестве демона (-d) и всегда перезапускает его по завершении (–restart=always). Она выдает простую команду echo, которая быстро завершается, выходя из контейнера.
Это можно проверить командой
doker ps
Важно отметить, что Docker повторно использует идентификатор контей- нера. Он не изменяется при перезапуске, и для этого вызова Docker всегда будет только одна запись в таблице ps.
docker run -d --restart=on-failure:10 ubuntu /bin/false
Эта команда запускает контейнер как демон (-d) и устанавливает ограничение на количество попыток перезапуска (–restart = on-failure: 10), выходя, если оно превышено. Она запускает простую команду (/ bin / false), которая быстро завершается и непременно потерпит неудачу.
Перемещение Docker в другой раздел
ПРОБЛЕМА
Вы хотите перейти туда, где Docker хранит свои данные.
РЕШЕНИЕ
Остановите и запустите демон Docker, указав новое местоположение с помощью флага -g.
$ dockerd -g /home/dockeruser/mydocker
Можно также отредактировать файл /lib/systemd/system/docker.service и добавить этот параметр.
Разрешение связи между контейнерами
ПРОБЛЕМА
Вы хотите разрешить связь между контейнерами для внутренних целей.
РЕШЕНИЕ
Используйте пользовательские сети, чтобы контейнеры могли взаимодействовать друг с другом.
Создание сети.
$ docker network create my_network
Подключение контейнера к сети.
$ docker network connect my_network blog1
Запуск с явным указанием сети.
$ docker run -it --network my_network ubuntu:16.04 bash
Установка контейнера с игрой под телеграм.
Клонируем репозиторий.
git clone https://github.com/zdimon/telegram-card-bj
cd telegram-card-bj
Делаем Dockerfile
FROM python:3 as telegramm-card
ENV PYTHONUNBUFFERED 1
RUN mkdir /app
WORKDIR /app
COPY . /app
RUN python3 -m pip install --upgrade pip
RUN pip install -r requirements.txt
CMD python3 start.py
Билдим образ
docker build .
Присваиваем ему тег.
docker tag c8ed7da14734 tb-container
Запускаем контейнер.
doker run -i -t -d --name tb tb-container
Основы работы с Linux. -> Django и PostgreSQL с Doker.
Джаного и база данных с Docker.
Создаем новый проект.
Файл зависимостей requirements.txt
Django
psycopg2
Установка ВО и зависимостей.
Старт проекта и сервера.
В данном случае мы не применяли миграцию и база данных не была создана.
Теперь задача состоит в том, чтобы не использовать папку venv а установить зависимости внутри образа Docker.
При этом сделав так, чтобы все файлы проекта оставались в локальной области для разработки и не переносились внутрь контейнера.
Таким образом все что служит для запуска проекта (окружение) будет изолировано внутри контейнера.
Создадим файл Dockerfile.
FROM python:3.6 AS django
ENV PYTHONUNBUFFERED 1
RUN mkdir /app
WORKDIR /app
RUN apt update
RUN apt -y install libpq-dev
COPY requirements.txt /app
RUN pip install -r requirements.txt
ENV PYTHONUNBUFFERED 1 - приказывает python выводить все в терминал
Запускаем сборку образа
docket build .
и наблюдаем процесс
Видим что просит обновить инсталятор pip.
Добавим команду обновления как просит.
...
COPY requirements.txt /app
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip install -r requirements.txt
Теперь наша задача запустить образ в контейнере.
Проще всего это слеать через docker-compose up.
Для этого нужен конфигурационный файл docker-compose.yaml.
version: '3.5'
services:
django:
build: .
restart: always
ports:
- 8000:8080
Запускаем команду сборки и запуска контейнера.
docker-compose up
Теперь нам нужно запустить несколько команд внутри контейнера чтобы поднять веб-сервер с джанго проектом.
Так же нам необходимо сделать ссылку внутри контейнера на наш каталог с проектом снаружи.
version: '3.5'
services:
django:
build: .
restart: always
ports:
- 8000:8000
working_dir: /app
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./blog:/app
Добавим контейнер с PostgreSQL.
db:
restart: always
image: postgres:latest
ports:
- "5432:5432"
volumes:
- ./pg_data:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=1q2w3e
- POSTGRES_DB=blog
Теперь добавим файл настроек окружения для проекта .env.dev
DEBUG=1
SECRET_KEY=foo
DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=blog
SQL_USER=postgres
SQL_PASSWORD=1q2w3e
SQL_HOST=db
SQL_PORT=5432
Включим его в контейнер.
version: '3.5'
services:
django:
...
env_file:
- ./.env.dev
И пропишем коннект в settings.py
DATABASES = {
"default": {
"ENGINE": os.environ.get("SQL_ENGINE", "django.db.backends.sqlite3"),
"NAME": os.environ.get("SQL_DATABASE", os.path.join(BASE_DIR, "db.sqlite3")),
"USER": os.environ.get("SQL_USER", "user"),
"PASSWORD": os.environ.get("SQL_PASSWORD", "password"),
"HOST": os.environ.get("SQL_HOST", "localhost"),
"PORT": os.environ.get("SQL_PORT", "5432"),
}
}
Пересобираем контейнеры.
docker-compose up --build
Проводим миграцию.
docker-compose exec django python manage.py migrate --noinput
Точка входа для образа
Используется для запуска скрипта при каждом запуске образа.
Например если мы хотим применять миграцию каждый раз, когда запускаем образ создадим файл entrypoint.sh.
#!/bin/sh
if [ "$DATABASE" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $SQL_HOST $SQL_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
python manage.py flush --no-input
python manage.py migrate
exec "$@"
И хобавим его в образ.
FROM python:3.6 AS python36
...
RUN mkdir /entry
COPY entrypoint.sh /entry
ENTRYPOINT ["/entry/entrypoint.sh"]
Если нужно использовать разные Dockerfile для контейнеров.
services:
service1:
build:
context: .
args:
- NODE_ENV=local
dockerfile: Dockerfile_X
ports:
- "8765:8765"
Полезные команды
Удаление контейнеров.
docker-compose down -v
Просмотр списка запущенных контейнеров
docker ps
Удалить контейнер
docker-compose rm db
Основы работы с Linux. -> Nginx с Doker.
Nginx с Doker.
Установим прокси-сервер nginx apache c возможностью управлять переменными окружения.
Создадим стартовую страницу index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Docker Nginx</title>
</head>
<body>
<h2>Hello from Nginx container</h2>
</body>
</html>
Создадим файл Dockerfile
FROM nginx:latest
Создадим сборщик docker-compose.yaml
version: '3.5'
services:
nginx:
build: .
restart: always
ports:
- 8081:80
8000:8080 - мы перенаправляем порт 80 изнутри контейнера наружу на 8081
Сборка и запуск контейнера.
docker-compose up --build
Запуск не через сборщик
docker run -it --rm -d -p 8081:80 --name web nginx
Заходим внутрь контейнера.
docker exec -ti b42580b257e2 bash
Выводим конфигурационный файл nginx.
cat /etc/nginx/conf.d/default.conf
b42580b257e2 - идентификатор контейнера, его можно получить командой docker ps
Однако можно и явно задать имя.
version: '3.5'
services:
nginx:
build: .
container_name: my-nginx
И обращаться по имени.
docker exec -ti my-nginx bash
Создаем конфигурационный файл nginx.conf.
server {
listen 80;
server_name localhost;
location / {
root /app;
try_files $uri /index.html;
}
}
Копируем конфигурационный файл внутрь контейнера и создаем папку app.
FROM nginx:latest
RUN mkdir /app
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
Сделаем ссылку из текущей директории внутрь контейнера.
version: '3.5'
services:
nginx:
build: .
restart: always
ports:
- 8081:80
volumes:
- .:/app
Проброс переменных окружения внутрь контейнера.
version: '3.5'
services:
nginx:
...
environment:
- NGINX_PORT=8081
Но проблема в том что эти переменные появляются только на этапе запуска контейнера а не в момент сборки образа.
Поэтому чтобы изменять что то на этапе сборки можно в конфигурационном файле добавить метку.
server {
listen __NGINX_PORT__;
...
}
Потом ее менять при сборке.
FROM nginx:latest
RUN mkdir /app
COPY ./default.conf /app/default.conf
RUN cat /app/default.conf | sed "s/__NGINX_PORT__/8081/" > /etc/nginx/conf.d/default.conf
TODO: можно попробовать флаг -i
sed -i 's/VERSION/8.04/' /etc/nginx/conf.d/default.conf
Или более грамотней, через переменную, которую вначале добавим в процесс сборки в docker-compose.yaml.
nginx:
build:
context: .
args:
- NGINX_PORT=8084
А потом используем.
ARG NGINX_PORT
RUN cat /app/default.conf | sed "s/__NGINX_PORT__/$NGINX_PORT/" > /etc/nginx/conf.d/default.conf