Начало работы с 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) назывался рабочий, который переносил товары на суда и обратно, когда те стояли в портах.

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

Нанять людей для перемещения этих грузов было недешево, но альтернативы не было.

Это должно быть знакомо всем, кто работает в сфере программного обеспечения.

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

doker

На рис показано, как можно сэкономить время и деньги с помощью концепции 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

Ключевые концепции и комманды.

doker

Прежде чем запускать команды Docker, лучше всего разобраться с понятиями образов, контейнеров и слоев. Говоря кратко, контейнеры запускают сис­темы, определенные образами. Эти образы состоят из одного или нескольких слоев (или наборов различий) плюс некоторые метаданные Docker.

Основная функция Docker – создавать, отправлять и запускать программное обеспечение в любом месте, где есть Docker.

Для конечного пользователя Docker – это программа c командной строкой, которую они запускают.

docker build Собрать образ Docker

docker run Запустить образ Docker в качестве контейнера

docker commit Сохранить контейнер Docker в качестве образа

docker tag Присвоить тег образу Docker

Один из способов взглянуть на образы и контейнеры – это рассматривать их как программы и процессы. Точно так же процесс может рассматриваться как «выполняемое приложение», контейнер Docker может рассматриваться как образ, выполняемый Docker.

Если вы знакомы с принципами объектно­ориентированного программирования, еще один способ взглянуть на образы и контейнеры – это рассматривать образы как классы, а контейнеры – как объекты.

Контейнеры запускают один процесс при запуске. Когда процесс завершается, контейнер останавливается.

Например из одного образа можно запустить 3 контейнера:

  • django

  • mysql

  • nginx

Способы создания образов.

Их существует несколько.

  1. Запустить контейнер в командной строке, указав его параметры в команде docker run. Затем можно перенести контейнер в образ с помощью docker commit.

Подойдет если вы хотите проверить работает ли ваш просесс установки.

  1. Выполнить сборку из известного базового образа, указав ее в Dockerfile.

Dockerfile – это текстовый файл, содержащий серию команд.

doker

Основные команды.

FROM : Определяет базовый образ.

COPY : Копирует файлы из локальной папки в образ.

ADD : Похожа на COPY только еще разархивирует в случае архива.

RUN : Запускает команду внутри образа и сохраняет все изменения в образе, которые вносит команда.

ENTRYPOINT [““, ““, …] : Конфигурирует команду по умолчанию внутри контейнера, если не указана то выполняется по умолчанию такая команда /bin/sh -c .

CMD [““, …] : Определение параметров команды запуска, учитывая что по дефолту /bin/sh -c, мы можем к нему добавить параметры через CMD.

WORKDIR

: Устанавливает рабочую директорию для последующих команд таких как RUN , CMD , ENTRYPOINT , COPY , и ADD.

Файл Dockerfile начинается с определения базового образа с помощью команды FROM. В этом примере используется образ Node.js, поэтому у вас есть доступ к двоичным файлам Node.js. Официальный образ Node.js называется node.

git уже установлен в базовом образе, но это не относиттся ко всем образам.

WORKDIR - не только меняет директорию, но и определяет в каком каталоге вы будете находится при запуске приложения.

EXPOSE - сообщает Docker, что контейнеры из собранного образа должны прослушивать этот порт.

Как видим Dockerfile – это простая последовательность ограниченного набора команд, выполняемых в строгом порядке.

Здесь команда RUN влияет на файловую систему, проверяя и устанавливая приложения, а команды EXPOSE, CMD и WORKDIR – на метаданные образа.

Сборка образа.

doker

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

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 помогают справиться с большой проблемой, которая возникает, когда вы используете контейнеры в широком масштабе.

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

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

doker

По умолчанию Docker внутренне использует механизм копирования при записи, чтобы уменьшить объем требуемого дискового пространства.

Вкратце суть метода.

Идея подхода copy-on-write заключается в том, что при чтении области данных используется общая копия, в случае изменения данных — создается новая копия.

При создании образа Doker создает слои файловой системы и их использует при создании новых образов. Например если два образа используют одну ОС Ubuntu то она не будет скопирована в оба, а они будут использовать один и тот же слой, содержащий эту операционную систему.

Архитектура Docker

doker

Исходя из рисунка видно, что докер-образы в реестре мы можем хранить как на своем сервере, так и на официальном сайте 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

doker

Запускаем на сокете.

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, как показано в следующем листинге.

doker

Останов контейнера.

$ 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

doker

Важно отметить, что 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 .

doker

Присваиваем ему тег.

docker tag c8ed7da14734 tb-container

Запускаем контейнер.

doker run -i -t -d  --name tb tb-container
Основы работы с Linux. -> Django и PostgreSQL с Doker.

Джаного и база данных с Docker.

источник

Создаем новый проект.

Файл зависимостей requirements.txt

Django
psycopg2

Установка ВО и зависимостей.

doker

Старт проекта и сервера.

doker

В данном случае мы не применяли миграцию и база данных не была создана.

Теперь задача состоит в том, чтобы не использовать папку 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 .

и наблюдаем процесс

doker

Видим что просит обновить инсталятор pip.

doker

Добавим команду обновления как просит.

...
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

doker

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

Так же нам необходимо сделать ссылку внутри контейнера на наш каталог с проектом снаружи.

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

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