Как должен выглядеть процесс работы с Docker?
===============================================

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

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

Сейчас у нас есть несколько приватных проектов, состоящие из бэкенда и фронтенда. По масштабу проекты не большие. Бэкенд создан на стеке NodeJS + MongoDB + ElasticSearch + Redis.  
  

Что бы автоматизировать процесс развертывания, начал изучать Docker. Но столкнулся с некоторым непониманием как все должно работать. В основном все материалы в сети отвечают на вопрос: Что такое Docker? И лишь малая часть отвечает на вопрос: Как с ним работать? И даже в них все процессы описываются поверхностно.  
  

Я разобрался с Docker клиентом и поднял docker-machine. Понял как устанавливать образы с Docker Hub и запускать контейнеры. А дальше все... Тупик.  

Возникло много вопросов, что делать дальше?  

Как мне создать свой контейнер состоящий из образов NodeJs, MongoDB, ElasticSearch, Redis?  

Где это все хранить?  

Как мне расшарить папки проектов для Docker?  

Как мне это интегрировать с CI и CD?  

Я запутался. Возникают мысли, а нужно ли мне это? Может все таки по старинке деплоить через FTP?  
  

В общем прошу помощи разобраться в том, как должен быть построен процесс развертывания проектов на Docker?  

Или поделиться материалами, где на практике показано что и как нужно делать. 

 Попробую описать простыми словами без серьезной терминологии (Devops'ы не бейте ногами).  
  

Следующим шагом я бы рекомендовал вам начать использовать [docker-compose](https://docs.docker.com/compose/).  

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

Например у нас такая структура. Я использую php но для nodejs может быть похоже.  

```
project
   - scr/   #Код проета под контролем версий в git
        - Dockerfile
        - phpfile1.php
        - phpfile2.php
        - etc.php
   - db_data/ #папка где будут сохранятся база данных. (иначе каждый запуск контейнера будет ее обнулять)
   - docker-compose.yml
   - site.conf   #конфиг для виртуального хоста nginx
   - nginx.conf #конфиг nginx
```
  
  

Настраивается взаимодействие в специальном [файле](https://docs.docker.com/compose/compose-file/compose-file-v2/).   
**docker-compose.yml**
```
version: '2'
services:
  nginx:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      - ../src/:/app
      - ./site.conf:/etc/nginx/conf.d/site.conf
      - ./nginx.conf:/etc/nginx/nginx.conf
    links:
      - php
  db:
    image: mysql:5.7
    volumes:
      - ./db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: 123
      MYSQL_DATABASE: changeme
      MYSQL_USER: changeme
      MYSQL_PASSWORD: 123
    ports:
      - "33306:3306"
  php:
    build: ../src
    volumes:
      - ../src:/app
    depends_on:
      - db
    environment:
      PHP_DB_HOST: db:3306
      PHP_DB_USER: changeme
      PHP_DB_PASSWORD: 123
```
  
  

Тут я использую контейнер nginx и mysql из DockerHub и свой контейнер для php описанный в   
**src/Dockerfile**
```
FROM php:fpm

RUN apt-get update && \
  apt-get install -y \
    openssl \
    git \
    curl \
    unzip

RUN docker-php-ext-install pdo pdo_mysql

ADD . /app
WORKDIR /app
```
  
  

Теперь использовав docker-compose up мы удобно запустим все контейнеры с нужной конфигурацией.  

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

например из php соединение с БД происходит так:  
  
**db.php**
```
return [
    'class' => 'yii\db\Connection',
    'dsn' => "mysql:host=db:3306;dbname=donor", // db:3306 - это services имя контейнера с mysql в docker-compose.yml 
    'username' => getenv('PHP_DB_USER'), // это переменные окружения для контейнера тоже из docker-compose.yml
    'password' => getenv('PHP_DB_PASSWORD'),
    'charset' => 'utf8',
];
```
  
  
  

Код прокидываем в 2 контейнера php и nginx (раздел volumes). То есть внутри контейнера создается директория /app которая ссылается на директорию на хост машине. Для разработки очень удобно, вы изменяете код и сразу можно обновлять страницу.  
  

На продакшен я обновляю код через git из репозитория и перезапускаю контейнеры (если надо).  
  

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

 как я понял по ответам тут собрались адепты докера. В этой церкви последователь должен появиться атеист. Только сегодня был большой срач насчет какие проблемы можно получить при переходе на докер <https://habrahabr.ru/post/332450/> очень советую почитать.  
  

Теперь к вашему вопросу. Докер НЕЛЬЗЯ использовать для персистентный образов. Т.к. если его правильно готовить никакой уверенности нет на какой ноде будет запускаться ваш контейнер физический. Т.е. все что пишет в память или на диск нельзя оборачивать в докер контейнер. Потом есть системы оркестрации, которые ваш контейнер могут с одной ноды на другую перебросить, что вы думаете будет с памятью контейнера - правильно, она очистится.  
  

Тут мне могут возразить что у нас все работает на "отлично" - это вы господа с проблемами не сталкивались. Читаем статью по ссылке выше и начинаем готовиться.  
  

Давайте разберем ваш стек и посмотрим что можно упаковать в докер контейнер.  

- NodeJS - не знаю как это у вас работает, если нет состояния - то можно  

- MongoDB - база, пишет в файлы, однозначно нельзя  

- ElasticSearch - этого зверя точно нельзя он и в память и на диск пишет  

- Redis - пишет в память, точно нельзя  
  

И что в итоге вы хотите обернуть в контейнер? И зачем вам докер? Чтоб mongodb была запущена от имени root? При этом мы помним о проблемах с безопасностью самой mongodb. Или elasticsearch завернуть в докер, он физический сервер способен утилизировать на 100% по памяти, пропускной способности дисковой подсистемы и 100% процессорного времени, а вы ему хотите еще один уровень абстракции в лице докера? ))   
  

Одно дело на компьютере разработчика использовать docker-composer и совсем другое дело в продакшене. 

> Как мне создать свой контейнер состоящий из образов NodeJs, MongoDB, ElasticSearch, Redis?

  

Используйте docker-compose. Вот посмотрите пример [https://docs.docker.com/compose/wordpress/#bring-u...](https://docs.docker.com/compose/wordpress/#bring-up-wordpress-in-a-web-browser)   

> Где это все хранить?

  

Хранить можно в облаках, но за это нужно платить. Проще таскать с собой docker-compose.yml, и на тестовых серверах собирать.  

> Как мне расшарить папки проектов для Docker?

  

Для этого существуют volumes.   

```
volumes:
       - /home/user/Projects/folder1:/var/www/html
```
  

Теперь папка folder1 на вашем компьютере доступна в контейнере по пути /var/www/html.  

> Как мне это интегрировать с CI и CD?

  

Да все просто. Качаете gitом ваш проект, собираете контейнеры на ваших тестовых серверах и вперед. Можете jenkins еще поставить, он бесплатный. Вот тут есть видосы, как это сделать <https://serversforhackers.com/series/jenkins> 

 Для продакшена удобнее готовое решение, например, легкий вариант (с поддержкой многосерверности) это Flynn.io.  

Или Dokku для одного сервера.  
  

А для девелопероского окружения не Докер, а Вагрант.  
  

Идея Докера, что контейнер, нужный вам создается КАЖДЫЙ РАЗ ЗАНОВО для очередной версии вашего ПО.  

Это факт частенько упускают из виду, пытаясь создать Докер-контейнер навечно.  
  

CI/CD работает так:  
  

Пушите код в Git,   

а, к примеру, Gitlab запускает по гит-хукам воркеры для тестирования.   

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

Если тесты проходят удачно, то тот же самый контейнер отправляется в DockerRegistry продакшн-системы,   

откуда его забирает система оркестрации/управления кластером (тот же Flynn).  
  

Как вводную по разработке архитектуры вашей системы под контейнерами рекомендую прочитать этот кратенький текст:  

<https://12factor.net/ru/>  
  

Какие СУБД в Докер, перекреститесь...  

<https://habrahabr.ru/post/332450/#comment_10299122>  
  

Да и вообще подумайте нужен ли вам этот гемор:  

[soar.name/ru/pro/half-a-year-with-docker-swarm-mod...](http://soar.name/ru/pro/half-a-year-with-docker-swarm-mode-in-production/)  

Польза очевидно большая.  

Но Докер - не серебряная пуля. 

> Как мне создать свой контейнер состоящий из образов NodeJs, MongoDB, ElasticSearch, Redis?

  

Docker спроектирован так, что каждый процесс должен быть развёрнут в отдельном контейнере. Таким образом, для бэкенда вам потребуется создать образ на базе NodeJS. Проще всего создавать образы через Dockerfile. Первой инструкцией в нём будет идти FROM, указывающая, какой образ взять базовым. Для ноды 8 можно взять node:8-alpine [из официального репозитория](https://hub.docker.com/_/node/). Alpine Linux - микродистрибутив, не содержит ничего лишнего, поэтому очень удобен.  
  

Таким образом, минимальный Dockerfile будет примерно таким:  

```
FROM node:8-alpine
MAINTAINER yourcompany
ADD . . # копирует все файлы из рабочей директории проекта в рабочую директорию контейнера за исключением описанных в .dockerignore
# ADD (в отличие от COPY) также умеет автоматически распаковывать архивы и скачивать файлы из интернета
CMD ["node", "./server.js"] # команда, которая выполнится при запуске контейнера; можно не указывать и передавать команду вручную
```
  
  

Для каждой из БД есть свои образы, но некоторые [не рекомендуют](https://habrahabr.ru/post/332450/) разворачивать БД в контейнерах на продакшене.  
  

> Где это все хранить?

  

Есть официальный репозиторий hub.docker.io. Разумеется, приватное размещение образов там платное. Можно поднять свой репозиторий на своём сервере.  
  

> Как мне расшарить папки проектов для Docker?

  

Не совсем понял, что именно вы хотите. В докере есть монтирование volume, но, мне кажется, это не то, что вам в данном случае нужно.  
  

> Как мне это интегрировать с CI и CD?

  

В простейшем случае CI-сервер может скачать проект из Git, установить зависимости, прогнать автотесты, затем запустить `docker build`, а после сборки - `docker push`. Лучше всего при этом взять версию приложения из package.json, чтобы затем указать её как tag у имени пакета. Полное имя образа тогда будет что-то вроде `docker.yourcompany.com/yourapp:1.2.3`, где `docker.yourcompany.com` - доменное имя вашего репозитория. docker push использует его, чтобы понять, куда ему загружать образ. Дальнейшее развёртывание зависит от того, как вы оркестрируете свои контейнеры. У нас для этого CI командует Mesos / Marathon, чтобы они загрузили новую версию.  
  

Это очень вкратце. Задавайте вопросы :) 

 По моему скромному мнению...  

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

Поскольку контейнеров несколько , и управлять этим сложно , применять **Docker compose**, который как раз и описывает это все хозяйство.  

Там же описывать внешние папки монтируемые к контейнеру. Или есть еще какие то Docker Storage ( не пробовал )  

То есть , код всё равно придётся заливать на сервер и после растартовать контейнер/ы. 

[Тут](https://habrahabr.ru/post/310460/) очень много ответов, в том числе и про Docker compose(инструмент для определения и запуска много-контейнерных приложений.)