One place for hosting & domains

      Контейнеризация приложения Node.js для разработки с использованием Docker Compose


      Введение

      Если вы активно занимаетесь разработкой приложений, использование Docker может упростить ваш рабочий процесс и процесс развертывания вашего приложения в продакшен. Работа с контейнерами в процессе разработки предоставляет следующие преимущества:

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

      Из этого руководства вы узнаете, как выполнять настройку среды разработки для приложения Node.js с помощью Docker. Вы создадите два контейнера, один для приложения Node, а другой для базы данных MongoDB с помощью Docker Compose. Поскольку это приложение работает с Node и MongoDB, необходимо выполнить следующие настройки:

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

      После изучения данного руководства у вас будет рабочее приложение с информацией об акулах, работающее на контейнерах Docker:

      Полное собрание акул

      Предварительные требования

      Для данного обучающего модуля вам потребуется следующее:

      Шаг 1 — Клонирование проекта и изменение зависимостей

      Первым, что необходимо сделать при настройке системы, является клонирование кода проекта и изменение файла package.json, который включает зависимости проекта. Мы добавим nodemon в devDependencies проекта, чтобы указать, что мы будем использовать его при разработке. Запуск приложения с nodemon обеспечивает автоматический перезапуск при внесении изменений в ваш код.

      Во-первых, клонируйте репозиторий nodejs-mongo-mongoose из учетной записи DigitalOcean Community на GitHub. Этот репозиторий включает код настройки, описанной в руководстве Интеграция MongoDB с вашим приложением Node, которое содержит описание процесса интеграции базы данных MongoDB с существующим приложением Node с помощью Mongoose.

      Клонируйте репозиторий в директорию с именем node_project:

      • git clone https://github.com/do-community/nodejs-mongo-mongoose.git node_project

      Перейдите в директорию node_project:

      Откройте файл проекта package.json с помощью nano или вашего любимого редактора:

      После зависимостей проекта и над закрывающей фигурной скобкой создайте новый объект devDependencies, который включает nodemon:

      ~/node_project/package.json

      ...
      "dependencies": {
          "ejs": "^2.6.1",
          "express": "^4.16.4",
          "mongoose": "^5.4.10"
        },
        "devDependencies": {
          "nodemon": "^1.18.10"
        }    
      }
      

      Сохраните и закройте файл после завершения редактирования.

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

      Шаг 2 — Настройка вашего приложения для работы с контейнерами

      Изменение нашего приложения для контейнеризованного рабочего процесса подразумевает, что код должен быть более модульным. Контейнеры обеспечивают возможность перемещения между рабочими средами, и наш код должен отражать это, оставаясь как можно менее привязанным к лежащей в основе операционной системе. Для этого мы выполним рефакторинг нашего кода, чтобы добиться более эффективного использования свойства Node process.env, которое возвращает объект с информацией о среде пользователя при запуске. Мы можем использовать этот объект в нашем коде для динамического сохранения информации о конфигурации при запуске в переменных среды.

      Давайте начнем с app.js, главной точки входа нашего приложения. Откройте файл:

      Внутри файла вы увидите определение для константы port, а также функцию listen, которая использует эту константу для указания порта, который будет прослушивать приложение:

      ~/home/node_project/app.js

      ...
      const port = 8080;
      ...
      app.listen(port, function () {
        console.log('Example app listening on port 8080!');
      });
      

      Давайте изменим константу port, чтобы разрешить динамическое присвоение при запуске, с помощью объекта process.env. Внесите следующие изменения в определение константы и функцию listen:

      ~/home/node_project/app.js

      ...
      const port = process.env.PORT || 8080;
      ...
      app.listen(port, function () {
        console.log(`Example app listening on ${port}!`);
      });
      

      Наше новое определение константы присваивает port динамически с помощью значения, передаваемого при запуске, или 8080. Аналогично мы переписали функцию listen для использования шаблонного литерала, который будет интерполировать значение порта при прослушивании соединений. Поскольку мы будем выполнять маппинг портов где-либо еще, эти изменения позволят нам избежать необходимости постоянно пересматривать этот файл при внесении изменений в нашу среду.

      После завершения редактирования сохраните и закройте файл.

      Затем нам нужно изменить информацию о подключении базы данных, чтобы удалить любые учетные данные конфигурации. Откройте файл db.js, содержащий следующую информацию:

      В настоящее время файл выполняет следующие функции:

      • Импортирует Mongoose, Object Document Mapper (ODM), который мы используем для создания схем и моделей данных нашего приложения.
      • Устанавливает учетные данные базы данных в качестве констант, включая имя пользователя и пароль.
      • Подключается к базе данных с помощью метода mongoose.connect.

      Дополнительную информацию о файле см. в шаге 3 руководства Интеграция MongoDB с вашим приложением Node.

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

      ~/node_project/db.js

      ...
      const MONGO_USERNAME = 'sammy';
      const MONGO_PASSWORD = 'your_password';
      const MONGO_HOSTNAME = '127.0.0.1';
      const MONGO_PORT = '27017';
      const MONGO_DB = 'sharkinfo';
      ...
      

      Вместо того, чтобы жестко задавать в коде эту информацию, вы можете использовать объект process.env для захвата значений этих констант в момент запуска. Измените блок следующим образом:

      ~/node_project/db.js

      ...
      const {
        MONGO_USERNAME,
        MONGO_PASSWORD,
        MONGO_HOSTNAME,
        MONGO_PORT,
        MONGO_DB
      } = process.env;
      ...
      

      Сохраните и закройте файл после завершения редактирования.

      На данный момент у вас есть измененный файл db.js для работы с переменными среды вашего приложения, но вам нужно передавать эти переменные в ваше приложение. Давайте создадим файл .env со значениями, которые вы можете передать вашему приложению при запуске.

      Откройте файл:

      Этот файл будет содержать информацию, которую вы удалили из db.js: имя пользователя и пароль для базы данных вашего приложения, а также настройки порта и имя базы данных. Обязательно измените имя пользователя, пароль и имя базы данных, перечисленные здесь, на собственные:

      ~/node_project/.env

      MONGO_USERNAME=sammy
      MONGO_PASSWORD=your_password
      MONGO_PORT=27017
      MONGO_DB=sharkinfo
      

      Обратите внимание, что мы удалили настройки хоста, которые первоначально присутствовали в db.js. Теперь мы определим наш хост на уровне файла Docker Compose вместе с другой информацией о наших сервисах и контейнерах.

      Сохраните и закройте файл после завершения редактирования.

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

      Откройте ваш файл .dockerignore:

      Добавьте следующую строку внизу файла:

      ~/node_project/.dockerignore

      ...
      .gitignore
      .env
      

      Сохраните и закройте файл после завершения редактирования.

      Файл .gitignore в этом репозитории уже включает .env, но вы можете убедиться в его наличии:

      ~~/node_project/.gitignore

      ...
      .env
      ...
      

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

      Шаг 3 — Изменение настроек подключения к базе данных

      Следующим нашим шагом будет повышение надежности работы метода подключения к базе данных с помощью кода, который обрабатывает случаи, когда нашему приложению не удается подключиться к нашей базе данных. Введение данного уровня устойчивости в код приложения является рекомендуемой практикой при работе с контейнерами с помощью Compose.

      Откройте db.js для редактирования:

      Вы увидите добавленный нами ранее код, а также константу url для URI подключения к Mongo и метод подключения к Mongoose:

      ~/node_project/db.js

      ...
      const {
        MONGO_USERNAME,
        MONGO_PASSWORD,
        MONGO_HOSTNAME,
        MONGO_PORT,
        MONGO_DB
      } = process.env;
      
      const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;
      
      mongoose.connect(url, {useNewUrlParser: true});
      

      В настоящий момент метод connect поддерживает параметр, который указывает Mongoose использовать новый парсер URL в Mongo. Давайте добавим дополнительные параметры в этот метод, чтобы определить параметры для попыток восстановления соединения. Мы можем сделать это, создав константу options, которая содержит соответствующую информацию, в дополнение к новой опции парсера URL. Под константами Mongo добавьте следующее определение константы options:

      ~/node_project/db.js

      ...
      const {
        MONGO_USERNAME,
        MONGO_PASSWORD,
        MONGO_HOSTNAME,
        MONGO_PORT,
        MONGO_DB
      } = process.env;
      
      const options = {
        useNewUrlParser: true,
        reconnectTries: Number.MAX_VALUE,
        reconnectInterval: 500,
        connectTimeoutMS: 10000,
      };
      ...
      

      Параметр reconnectTries указывает Mongoose продолжать выполнять попытки подключения неопределенный срок, а reconnectInterval определяет период между попытками переподключения в миллисекундах. connectTimeoutMS указывает 10 секунд в качестве периода, которое драйвер Mongo будет ждать, прежде чем попытаться снова установить соединение.

      Теперь мы можем использовать новую константу options в методе connect Mongoose для тонкой настройки подключения Mongoose. Также мы добавим promise для обработки потенциальных ошибок подключения.

      В настоящее время метод connect Mongoose выглядит следующим образом:

      ~/node_project/db.js

      ...
      mongoose.connect(url, {useNewUrlParser: true});
      

      Удалите существующий метод connect и замените его следующим кодом, который включает константу options и promise:

      ~/node_project/db.js

      ...
      mongoose.connect(url, options).then( function() {
        console.log('MongoDB is connected');
      })
        .catch( function(err) {
        console.log(err);
      });
      

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

      Итоговый файл будет выглядеть примерно так:

      ~/node_project/db.js

      const mongoose = require('mongoose');
      
      const {
        MONGO_USERNAME,
        MONGO_PASSWORD,
        MONGO_HOSTNAME,
        MONGO_PORT,
        MONGO_DB
      } = process.env;
      
      const options = {
        useNewUrlParser: true,
        reconnectTries: Number.MAX_VALUE,
        reconnectInterval: 500,
        connectTimeoutMS: 10000,
      };
      
      const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;
      
      mongoose.connect(url, options).then( function() {
        console.log('MongoDB is connected');
      })
        .catch( function(err) {
        console.log(err);
      });
      

      Сохраните и закройте файл после завершения редактирования.

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

      Шаг 4 — Настройка служб с помощью Docker Compose

      После выполнения рефакторинга кода вы можете начинать добавлять в файл docker-compose.yml определения служб. Служба в Compose — это запущенный контейнер, а определения служб, которые вы будете добавлять в ваш файл docker-compose.yml, содержат информацию о том, как будет запускаться образ каждого контейнера. Compose позволяет вам определять различные службы для создания приложений с несколькими контейнерами.

      Прежде чем определять службы, мы добавим в наш проект инструмент с именем wait-for, чтобы гарантировать, что наше приложение будет пытаться подключиться к нашей базе данных только после завершения всех задач, связанных с запуском базы данных. Этот скрипт обертки использует netcat для опроса, поддерживает или нет конкретный хост и порт подключение по протоколу TCP. Благодаря этому вы можете контролировать попытки подключения вашего приложения к базе данных, проверяя, готова ли база данных к подключению.

      Хотя Compose позволяет вам указывать зависимости между службами с помощью параметра depends_on, данный порядок определяется тем, запущен контейнер или нет, а не его готовностью. Использование depends_on при настройке не является оптимальным решением, поскольку мы хотим, чтобы наше приложение подключилось только после того, как все задачи запуска базы данных, включая добавление пользователя и пароля в базу данных аутентификации admin, будут выполнены. Дополнительную информацию об использовании wait-for и других инструментов для контроля порядка запуска можно найти в соответствующих разделах с рекомендациями в документации Compose.

      Откройте файл с именем wait-for.sh:

      Вставьте в файл следующий код, чтобы создать функцию опроса готовности:

      ~/node_project/app/wait-for.sh

      #!/bin/sh
      
      # original script: https://github.com/eficode/wait-for/blob/master/wait-for
      
      TIMEOUT=15
      QUIET=0
      
      echoerr() {
        if [ "$QUIET" -ne 1 ]; then printf "%sn" "$*" 1>&2; fi
      }
      
      usage() {
        exitcode="$1"
        cat << USAGE >&2
      Usage:
        $cmdname host:port [-t timeout] [-- command args]
        -q | --quiet                        Do not output any status messages
        -t TIMEOUT | --timeout=timeout      Timeout in seconds, zero for no timeout
        -- COMMAND ARGS                     Execute command with args after the test finishes
      USAGE
        exit "$exitcode"
      }
      
      wait_for() {
        for i in `seq $TIMEOUT` ; do
          nc -z "$HOST" "$PORT" > /dev/null 2>&1
      
          result=$?
          if [ $result -eq 0 ] ; then
            if [ $# -gt 0 ] ; then
              exec "$@"
            fi
            exit 0
          fi
          sleep 1
        done
        echo "Operation timed out" >&2
        exit 1
      }
      
      while [ $# -gt 0 ]
      do
        case "$1" in
          *:* )
          HOST=$(printf "%sn" "$1"| cut -d : -f 1)
          PORT=$(printf "%sn" "$1"| cut -d : -f 2)
          shift 1
          ;;
          -q | --quiet)
          QUIET=1
          shift 1
          ;;
          -t)
          TIMEOUT="$2"
          if [ "$TIMEOUT" = "" ]; then break; fi
          shift 2
          ;;
          --timeout=*)
          TIMEOUT="${1#*=}"
          shift 1
          ;;
          --)
          shift
          break
          ;;
          --help)
          usage 0
          ;;
          *)
          echoerr "Unknown argument: $1"
          usage 1
          ;;
        esac
      done
      
      if [ "$HOST" = "" -o "$PORT" = "" ]; then
        echoerr "Error: you need to provide a host and port to test."
        usage 2
      fi
      
      wait_for "$@"
      

      Сохраните и закройте файл после добавления кода.

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

      Затем откройте файл docker-compose.yml:

      Во-первых, определите службу приложения nodejs, добавив в файл следующий код:

      ~/node_project/docker-compose.yml

      version: '3'
      
      services:
        nodejs:
          build:
            context: .
            dockerfile: Dockerfile
          image: nodejs
          container_name: nodejs
          restart: unless-stopped
          env_file: .env
          environment:
            - MONGO_USERNAME=$MONGO_USERNAME
            - MONGO_PASSWORD=$MONGO_PASSWORD
            - MONGO_HOSTNAME=db
            - MONGO_PORT=$MONGO_PORT
            - MONGO_DB=$MONGO_DB
          ports:
            - "80:8080"
          volumes:
            - .:/home/node/app
            - node_modules:/home/node/app/node_modules
          networks:
            - app-network
          command: ./wait-for.sh db:27017 -- /home/node/app/node_modules/.bin/nodemon app.js
      

      Определение службы nodejs включает следующие параметры:

      • build: это определение параметров конфигурации, включая context и dockerfile, которые будут применяться при создании образа приложения Compose. Если вы хотите использовать существующий образ из реестра, например, из Docker Hub, вы можете использовать инструкцию image с информацией об имени пользователя, репозитория и теге образа.
      • context: это определение контекста сборки для сборки образа, в этом случае текущая директория проекта.
      • dockerfile: данный параметр определяет Dockerfile в текущей директории проекта в качестве файла, который Compose будет использоваться для сборки образа приложения. Дополнительную информацию об этом файле см. в руководстве Создание приложения Node.js с помощью Docker.
      • image, container_name: эти параметры присваивают имена для образа и контейнера.
      • restart: данный параметр определяет политику перезапуска. По умолчанию установлено значение no, но мы задали значение, согласно которому контейнер будет перезапускаться, пока не будет закрыт.
      • env_file: этот параметр указывает Compose, что мы хотим добавить переменные среды из файла с именем .env, расположенного в контексте сборки.
      • environment: с помощью этого параметра вы можете добавить настройки подключения к Mongo, которые вы определили в файле .env. Обратите внимание, что мы не задаем значение development для NODE_ENV, так как это поведение Express по умолчанию, если значение NODE_ENV не задано. При переходе в продакшен вы можете установить значение production, чтобы активировать кэширование вида и получать более короткие сообщения об ошибках. Также необходимо отметить, что мы указали в качестве хоста контейнер базы данных db, как описано в шаге 2.
      • ports: данный параметр назначает порт 80 хоста для порта 8080 в контейнере.
      • volumes: мы используем два типа монтирования:
        • Первый тип — это связанное монтирование, которое подразумевает монтирование кода приложения на хост в директорию /home/node/app в контейнере. Это упрощает быструю разработку, поскольку любые изменения, которые вы вносите в код хоста, будут немедленно добавлены в контейнер.
        • Второй тип — это том, node_modules. Когда Docker запускает инструкцию npm install, присутствующую в Dockerfile приложения, npm будет создавать новую директорию node_modules в контейнере, которая включает пакеты, необходимые для запуска приложения. Связанное монтирование, которое мы только что создали, будет скрывать эту созданную нами директорию node_modules. Поскольку директория node_modules в хосте пустая, связывание будет назначать пустой каталог для контейнера, переопределяя новую директорию node_modules и не позволяя запускать наше приложение. Том node_modules решает эту проблему, сохраняя содержимое директории /home/node/app/node_modules и монтируя его в контейнер, скрывая связку.

      При использовании этого подхода следует учитывать следующие моменты:

      • Ваша связка будет монтировать содержимое директории node_modules в контейнере на хост, а эта директория будет принадлежать к root, поскольку том с данным названием был создан Docker.
      • Если на хосте уже существует директория node_modules, она будет перезаписана директорией node_modules, созданной в контейнере. Настройка, которую мы создаем в рамках данного руководства, полагает, что у вас нет существующей директории node_modules и вы не будете работать с npm на хосте. Это соответствует подходу к разработке приложений с учетом 12 факторов, который минимизирует зависимости между средами исполнения.

        • networks: данный параметр указывает, что служба приложения будет подключаться к сети app-network, которую мы определим внизу файла.
        • command: данный параметр позволяет вам задавать команду, которая должна быть выполнена при запуске Compose образа. Обратите внимание, что этот параметр переопределяет инструкцию CMD, заданную нами в Dockerfile нашего приложения. Итак, мы запускаем приложение с помощью скрипта wait-for, который будет опрашивать службу db на порте 27017, чтобы проверить, готова ли служба базы данных. После успешной проверки готовности скрипт будет выполнять заданную нами команду /home/node/app/node_modules/.bin/nodemon app.js, чтобы запустить приложение с nodemon. Это будет гарантировать, что любые будущие изменения, которые мы будем вносить в наш код, будут подгружаться без необходимости перезапуска приложения.

      После этого необходимо создать службу db, добавив следующий код под определением службы приложения:

      ~/node_project/docker-compose.yml

      ...
        db:
          image: mongo:4.1.8-xenial
          container_name: db
          restart: unless-stopped
          env_file: .env
          environment:
            - MONGO_INITDB_ROOT_USERNAME=$MONGO_USERNAME
            - MONGO_INITDB_ROOT_PASSWORD=$MONGO_PASSWORD
          volumes:  
            - dbdata:/data/db   
          networks:
            - app-network  
      

      Некоторые настройки, которые мы определили для службы nodejs, остаются прежними, но мы также должны внести следующие изменения в определения image, environment и volumes:

      • image: для создания этой службы Compose будет запрашивать образ 4.1.8-xenial​​​ Mongo из Docker Hub. Мы закрепляем конкретную версию, чтобы избежать возможных будущих конфликтов при изменении образа Mongo. Дополнительную информацию о закреплении версии см. в документации Docker Рекомендации по работе с Dockerfile.
      • MONGO_INITDB_ROOT_USERNAME, MONGO_INITDB_ROOT_PASSWORD: образ mongo делает эти переменные среды доступными, чтобы вы могли изменять порядок инициализации экземпляра вашей базы данных. MONGO_INITDB_ROOT_USERNAME и MONGO_INITDB_ROOT_PASSWORD вместе создают root-пользователя в базе данных аутентификации admin и гарантируют, что аутентификацию будет активирована при запуске контейнера. Мы задали MONGO_INITDB_ROOT_USERNAME и MONGO_INITDB_ROOT_PASSWORD, используя значения из нашего файла .env, который мы передаем службе db, используя параметр env_file. Подобные действия означают, что пользователь нашего приложения sammy будет root-пользователем в экземпляре базы данных с доступом ко всем административным и оперативным правам этой роли. При работе в продакшене вы можете захотеть создать специального пользователя приложения с соответствующим набором привилегий.
        ​​​​​​[note]
        Примечание: необходимо помнить, что эти переменные не будут применяться, если вы запускаете контейнер, используя существующую директорию данных.
      • dbdata:/data/db: том с именем dbdata будет сохранять данные, которые хранятся в директории данных Mongo по умолчанию, /data/db. Это будет гарантировать, что вы не потеряете данные в случаях остановки или удаления контейнеров.

      Также мы добавили службу db в сеть app-network с параметром networks.

      В качестве завершающего шага добавьте определения тома и сети в конец файла:

      ~/node_project/docker-compose.yml

      ...
      networks:
        app-network:
          driver: bridge
      
      volumes:
        dbdata:
        node_modules:  
      

      Создаваемая пользователем мостовая система app-network позволяет организовать коммуникацию между нашими контейнерами, поскольку они находятся на одном хосте демона Docker. Это позволяет организовать трафик и коммуникации внутри приложения, поскольку она открывает все порты между контейнерами в одной мостовой сети, скрывая все порты от внешнего мира. Таким образом, наши контейнеры db и nodejs могут взаимодействовать друг с другом, и нам нужно будет только открыть порт 80 для внешнего доступа к приложению.

      Наш ключ volumes верхнего уровня определяет тома dbdata и node_modules. Когда Docker создает тома, содержимое тома сохраняется в части файловой системы хоста, /var/lib/docker/volumes/, а данным процессом управляет Docker. Содержимое каждого тома сохраняется в директории /var/lib/docker/volumes/ и монтируются в любой контейнер, который использует том. Таким образом, данные информации об акулах, которые будут добавлять наши пользователи, будут сохраняться в томе dbdata даже при удалении и последующем восстановлении контейнера db.

      Итоговый файл docker-compose.yml будет выглядеть примерно так:

      ~/node_project/docker-compose.yml

      version: '3'
      
      services:
        nodejs:
          build:
            context: .
            dockerfile: Dockerfile
          image: nodejs
          container_name: nodejs
          restart: unless-stopped
          env_file: .env
          environment:
            - MONGO_USERNAME=$MONGO_USERNAME
            - MONGO_PASSWORD=$MONGO_PASSWORD
            - MONGO_HOSTNAME=db
            - MONGO_PORT=$MONGO_PORT
            - MONGO_DB=$MONGO_DB
          ports:
            - "80:8080"
          volumes:
            - .:/home/node/app
            - node_modules:/home/node/app/node_modules
          networks:
            - app-network
          command: ./wait-for.sh db:27017 -- /home/node/app/node_modules/.bin/nodemon app.js
      
        db:
          image: mongo:4.1.8-xenial
          container_name: db
          restart: unless-stopped
          env_file: .env
          environment:
            - MONGO_INITDB_ROOT_USERNAME=$MONGO_USERNAME
            - MONGO_INITDB_ROOT_PASSWORD=$MONGO_PASSWORD
          volumes:     
            - dbdata:/data/db
          networks:
            - app-network  
      
      networks:
        app-network:
          driver: bridge
      
      volumes:
        dbdata:
        node_modules:  
      

      Сохраните и закройте файл после завершения редактирования.

      Когда ваши определения службы будут готовы, вы сможете запустить приложение.

      Шаг 5 — Тестирование приложения

      Имея в распоряжении файл docker-compose.yml, вы можете создать ваши службы с помощью команды docker-compose up. Также вы можете проверить, что ваши данные будут сохраняться, останавливая работу контейнеров и удаляя их с помощью docker-compose down.

      Во-первых, необходимо выполнить сборку образов и создать службы, запустив docker-compose с флагом -d, который будет запускать контейнеры nodejs и db в фоновом режиме:

      Вы увидите вывод, подтверждающий, что ваши службы были успешно созданы:

      Output

      ... Creating db ... done Creating nodejs ... done

      Также вы можете получить более подробную информацию о процессах запуска, отобразив вывод журнала из служб:

      Если запуск был выполнен корректно, вы должны увидеть примерно следующее:

      Output

      ... nodejs | [nodemon] starting `node app.js` nodejs | Example app listening on 8080! nodejs | MongoDB is connected ... db | 2019-02-22T17:26:27.329+0000 I ACCESS [conn2] Successfully authenticated as principal sammy on admin

      Также вы можете проверить состояние ваших контейнеров с помощью docker-compose ps:

      Вы получите вывод, указывающий, что ваши контейнеры запущены:

      Output

      Name Command State Ports ---------------------------------------------------------------------- db docker-entrypoint.sh mongod Up 27017/tcp nodejs ./wait-for.sh db:27017 -- ... Up 0.0.0.0:80->8080/tcp

      После запуска служб вы можете посетить страницу http://your_server_ip​​​ в браузере. Вы увидите стартовую страницу, которая будет выглядеть примерно так:

      Начальная страница приложения

      Нажмите кнопку Get Shark Info (Получить информацию об акулах). Вы увидите страницу с формой входа, где вы можете ввести имя акулы и описание общего типа этой акулы:

      Форма информации об акуле

      В этой форме добавьте акулу по вашему выбору. В демонстрационных целях мы добавим Megalodon Shark в поле Shark Name (Имя акулы) и Ancient в поле Shark Character (Тип акулы):

      Заполненная форма акулы

      Нажмите кнопку Submit (Отправить). Вы увидите страницу с информацией об акуле, отображаемую для вас:

      Вывод акулы

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

      Вернитесь в терминал, введите следующую команду для остановки и удаления контейнеров и сети:

      Обратите внимание, что мы не включаем параметр --volumes, и поэтому наш том dbdata не удаляется.

      Следующая информация подтверждает, что ваши контейнеры и сеть были удалены:

      Output

      Stopping nodejs ... done Stopping db ... done Removing nodejs ... done Removing db ... done Removing network node_project_app-network

      Повторно создайте контейнеры:

      Затем вернитесь к форме информации об акуле:

      Форма информации об акуле

      Введите новую акулу по вашему выбору. Мы будем использовать значение Whale Shark и Large:

      Ввод новой акулы

      После нажатия кнопки *Submit *(Отправить) вы увидите, что новая акула добавлена в коллекцию акул в вашей базе данных без потери ранее введенных данных:

      Полное собрание акул

      Ваше приложение сейчас запущено в контейнерах Docker с сохранением данных и активацией синхронизации кода.

      Заключение

      Выполняя указания данного руководства, вы создали настройку разработки для вашего приложения Node с помощью контейнеров Docker. Вы сделали ваш проект более модульным и портативным с помощью извлечения чувствительной информации и отделив состояние вашего приложения от кода приложения. Вы также настроили шаблонный файл docker-compose.yml, который вы можете изменять в зависимости от потребностей разработки и изменений требований.

      В процессе разработки вы можете захотеть узнать больше о проектировании приложений для контейнеризованных рабочих процессов и процессов Cloud Native. Дополнительную информацию по этим темам вы можете посмотреть в статьях Разработка архитектуры приложений для Kubernetes и Модернизация приложений для Kubernetes.

      Дополнительную информацию о коде, используемом в этом руководстве, см. в статьях Сборка приложения Node.js с помощью Docker и Интеграция MongoDB с вашим приложением Node. Дополнительную информацию о развертывании приложения Node с обратным прокси-сервером Nginx с помощью контейнеров см. в статье Обеспечение безопасности контейнеризованного приложения Node.js с Nginx, Let’s Encrypt и Docker Compose.



      Source link

      Развертывание и управление DNS с использованием DNSControl в Debian 10


      Автор выбрал Electronic Frontier Foundation Inc для получения пожертвований в рамках программы Write for DOnations.

      Введение

      DNSControl — это инструмент, построенный по принципу «инфраструктура как код», который поддерживает развертывание и управление зонами DNS с использованием стандартных принципов разработки программного обеспечения, включая контроль версий, тестирование и автоматизированное развертывание. Инструмент DNSControl разработан Stack Exchange и написан на Go.

      Использование DNSControl помогает избавиться от многих сложностей ручного управления DNS, поскольку файлы зон хранятся в программируемом формате. Инструмент позволяет одновременно развертывать зоны для нескольких поставщиков DNS, определять ошибки синтаксиса и автоматически извлекать конфигурации DNS, за счет чего снижается риск человеческой ошибки. Также DNSControl часто используется для быстрого переноса DNS на другого провайдера; например, в случае DDoS-атаки или выхода системы из строя.

      В этом обучающем руководстве мы научимся устанавливать и настраивать DNSControl, создадим базовую конфигурацию DNS и начнем развертывание записей DNS на рабочем провайдере. Для этого обучающего руководства мы используем DigitalOcean в качестве примера провайдера DNS. Если вы хотите использовать другого провайдера, настройки будут выглядеть очень похоже. После завершения работы вы сможете управлять своей конфигурацией DNS и тестировать ее в безопасной среде, отключенной от сети, а затем автоматически развертывать в производственной среде.

      Предварительные требования

      Для прохождения этого обучающего руководства вам потребуется следующее:

      • Один сервер Debian 10, настроенный в соответствии с указаниями по начальной настройке сервера Debian 10, включая пользователя sudo без привилегий root и активированный брандмауэр для блокировки ненужных портов. your-server-ipv4-address обозначает IP-адрес сервера, где вы размещаете свой сайт или домен. your-server-ipv6-address обозначает адрес IPv6 сервера, где вы размещаете свой сайт или домен.
      • Полностью зарегистрированное доменное имя с хостингом DNS у поддерживаемого провайдера. В этом обучающем руководстве мы используем доменное имя your_domain и DigitalOcean как провайдера услуг.
      • Ключ DigitalOcean API (персональный токен доступа) с разрешениями чтения и записи. Его создание описано в материале Создание персонального токена доступа.

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

      Шаг 1 — Установка DNSControl

      Инструмент DNSControl написан на языке Go, и поэтому вначале вам нужно установить Go на свой сервер и задать GOPATH.

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

      Также вам нужно будет установить Git, поскольку это требуется, чтобы дать Go возможность загрузки и установки программного обеспечения DNSControl из репозитория на GitHub.

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

      Затем мы установим пакеты golang-go и git:

      • sudo apt install golang-go git

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

      Далее вы настроите требуемые переменные среды path для Go. Дополнительную информацию можно получить в обучающем материале по GOPATH. Для начала отредактируем файл ~/.profile:

      Добавим в конец файла следующие строки:

      ~/.profile

      ...
      export GOPATH="$HOME/go"
      export PATH="$PATH:$GOPATH/bin"
      

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

      Мы установили и настроили Go и теперь можем перейти к установке DNSControl.

      Команду go get можно использовать для доставки копии кода, его автоматической компиляции и установки в директорию Go:

      • go get github.com/StackExchange/dnscontrol

      После этого мы можем проверить установленную версию и убедиться, что все работает правильно:

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

      Output

      dnscontrol 2.9-dev

      Если выводится сообщение об ошибке dnscontrol: command not found, необходимо еще раз проверить настройки путей Go.

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

      Шаг 2 — Настройка DNSControl

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

      Вначале нам нужно создать новую директорию для хранения конфигурации DNSControl и перейти в нее:

      • mkdir ~/dnscontrol
      • cd ~/dnscontrol

      Примечание. В этом обучающем модуле мы рассматриваем начальную настройку DNSControl, но для использования в производственной среде рекомендуется хранить конфигурацию DNSControl в системе контроля версий (VCS), такой как Git. Это дает преимущества полного контроля версий, интеграции с CI/CD для тестирования, удобства откатов при развертывании и т. д.

      Если вы планируете использовать DNSControl для записи файлов зоны BIND, вам также следует создать директорию zones:

      Файлы зоны BIND представляют собой чистый стандартизированный метод сохранения зон и записей DNS в простом текстовом формате. Они первоначально использовались для программного обеспечения сервера BIND DNS, однако сейчас они широко используются в качестве стандартного метода хранения зон DNS. Файлы зоны BIND, предоставляемые DNSControl, очень полезны, если их нужно импортировать в персонализированный или собственный сервер DNS, а также для целей аудита.

      Если вы хотите просто использовать DNSControl для передачи изменений DNS управляемому провайдеру, директория zones не потребуется.

      Далее требуется настроить файл creds.json, что позволит DNSControl выполнить аутентификацию вашего провайдера DNS и внести изменения. Формат файла creds.json может немного отличаться в зависимости от используемого провайдера DNS. Ознакомьтесь со списком провайдеров в официальной документации DNSControl, чтобы найти конфигурацию для вашего провайдера.

      Создайте файл creds.json в директории ~/dnscontrol:

      • cd ~/dnscontrol
      • nano creds.json

      Добавьте в файл образец конфигурации creds.json для вашего провайдера DNS. Если вы используете DigitalOcean в качестве своего провайдера DNS, вы можете использовать следующее:

      ~/dnscontrol/creds.json

      {
      "digitalocean": {
        "token": "your-digitalocean-oauth-token"
      }
      }
      

      Этот файл сообщает DNSControl, к каким провайдерам DNS нужно подключаться.

      Необходимо указать форму аутентификации для провайдера DNS. Обычно это ключ API или токен OAuth, однако для некоторых провайдеров требуется дополнительная информация, как описано в списке провайдеров в официальной документации DNSControl.

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

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

      Если вы используете нескольких провайдеров DNS, например для разных доменных имен или делегированных зон DNS, вы можете определить их в том же самом файле creds.json.

      Мы выполнили настройку первоначальных директорий конфигурации DNSControl и настроили файл creds.json так, чтобы DNSControl мог проходить аутентификацию у провайдера DNS и вносить изменения. Теперь мы создадим конфигурацию для наших зон DNS.

      Шаг 3 — Создание файла конфигурации DNS

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

      dnsconfig.js — основной файл конфигурации DNS для DNSControl. В этом файле зоны DNS и соответствующие им записи определяются с использованием синтаксиса JavaScript. Этот синтаксис называется DSL или предметно-ориентированный язык. Дополнительную информацию можно найти на странице JavaScript DSL в официальной документации по DNSControl.

      Вначале создадим файл конфигурации DNS в директории ~/dnscontrol:

      • cd ~/dnscontrol
      • nano dnsconfig.js

      Затем добавим в файл следующий образец конфигурации:

      ~/dnscontrol/dnsconfig.js

      // Providers:
      
      var REG_NONE = NewRegistrar('none', 'NONE');
      var DNS_DIGITALOCEAN = NewDnsProvider('digitalocean', 'DIGITALOCEAN');
      
      // Domains:
      
      D('your_domain', REG_NONE, DnsProvider(DNS_DIGITALOCEAN),
        A('@', 'your-server-ipv4-address')
      );
      

      В файле образца определяется доменное имя или зона DNS определенного провайдера. В данном случае это домен your_domain на хостинге DigitalOcean. Пример записи A также определен для зоны root (@) и указывает на адрес IPv4 сервера, где размещен ваш домен или сайт.

      Базовый файл конфигурации DNSControl состоит из трех основных функций:

      • NewRegistrar(имя, тип, метаданные)​​​​: определяет регистратора домена для доменного имени. DNSControl может использовать эту функцию для внесения требуемых изменений, в частности для модификации авторитетных серверов имен. Если вы хотите использовать DNSControl только для управления зонами DNS, их можно оставить как NONE.

      • NewDnsProvider(имя, тип, метаданные)​​: определяет провайдера DNS для доменного имени или делегированной зоны. Здесь DNSControl применяет вносимые нами изменения DNS.

      • D(имя, регистратор, модификаторы): определяет доменное имя или делегируемую зону DNS для управления DNSControl, а также присутствующие в зоне записи DNS.

      Мы должны настроить NewRegistrar(), NewDnsProvider() и D() соответствующим образом, используя список провайдеров в официальной документации по DNSControl.

      Если в качестве провайдера DNS используется DigitalOcean и нам требуется только вносить изменения DNS (а не авторитетных серверов имен), образцы из предыдущего блока кода подойдут для наших целей.

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

      На этом шаге мы настроили файл конфигурации DNS для DNSControl с определением соответствующих провайдеров. Далее мы заполним файл полезными записями DNS.

      Шаг 4 — Заполнение файла конфигурации DNS

      Далее мы можем заполнить файл конфигурации DNS полезными записями DNS для нашего сайта или сервиса, используя синтаксис DNSControl.

      В отличие от традиционных файлов зоны BIND, где записи DNS имеют формат построчных необработанных данных, в DNSControl записи DNS определяются как параметр функции D() (модификатор домена), как мы вкратце показали на шаге 3.

      Модификатор домена существует для каждого стандартного типа записей DNS, включая A, AAAA, MX, TXT, NS, CAA и т. д. Полный список доступных типов записей можно найти в разделе Модификаторы домена в документации по DNSControl.

      Также доступны модификаторы отдельных записей (модификаторы записей). Они в основном используются для настройки TTL (времени существования) для отдельных записей. Полный список доступных модификаторов записей можно найти в разделе Модификаторы записей документации по DNSControl. Модификаторы записей необязательны для использования, в большинстве простых случаев их можно опускать.

      Синтаксис настройки записей DNS немного отличается для каждого типа записей. Далее приведены несколько примеров наиболее распространенных типов записей:

      • Записи A:

        • Назначение: указывают на адрес IPv4.
        • Синтаксис: A('name', 'address', необязательные модификаторы записи)
        • Пример: A('@', 'your-server-ipv4-address', TTL(30))
      • Записи AAAA:

        • Назначение: указывают на адрес IPv6.
        • Синтаксис: AAAA('name', 'address', необязательные модификаторы записи)
        • Пример: AAAA('@', 'your-server-ipv6-address') (модификатор записи опущен, поэтому используется TTL по умолчанию)
      • Записи CNAME:

        • Назначение: делают домен или субдомен псевдонимом другого домена или субдомена.
        • Синтаксис: CNAME('name', 'target', необязательные модификаторы записи)
        • Пример: CNAME('subdomain1', 'example.org.') (обратите внимание, что если значение содержит точку, завершающий символ . обязательно должен присутствовать в конце)
      • Записи MX:

        • Назначение: направление электронной почты на определенные серверы или адреса.
        • Синтаксис: MX('name', 'priority', 'target', необязательные модификаторы записи)
        • Пример: MX('@', 10, 'mail.example.net') (обратите внимание, что если значение содержит точку, завершающий символ . обязательно должен присутствовать в конце)
      • Записи TXT:

        • Назначение: добавление произвольного обычного текста, часто используется для конфигураций без собственного выделенного типа записи.
        • Синтаксис: TXT('name', 'content', необязательные модификаторы записи)
        • Пример: TXT('@', 'Это запись TXT. ')
      • Записи CAA:

        • Назначение: ограничение и отчет о центрах сертификации (СА), которые могут выпускать сертификаты TLS для доменов или субдоменов.
        • Синтаксис: CAA('name', 'tag', 'value', необязательные модификаторы записи)
        • Пример: CAA('@', 'issue', 'letsencrypt.org')

      Чтобы начать добавлять записи DNS для нашего домена или делегированной зоны DNS, нам нужно отредактировать файл конфигурации DNS:

      Далее мы можем начать указывать параметры для существующей функции D(), используя синтаксис,описанный в предыдущем списке и в разделе Модификаторы доменов официальной документации DNSControl. Для разделения записей должна использоваться запятая (,).

      Приведенный здесь блок кода содержит полный образец базовой конфигурации DNS, предназначенный для справочных целей:

      ~/dnscontrol/dnsconfig.js

      ...
      
      D('your_domain', REG_NONE, DnsProvider(DNS_DIGITALOCEAN),
        A('@', 'your-server-ipv4-address'),
        A('www', 'your-server-ipv4-address'),
        A('mail', 'your-server-ipv4-address'),
        AAAA('@', 'your-server-ipv6-address'),
        AAAA('www', 'your-server-ipv6-address'),
        AAAA('mail', 'your-server-ipv6-address'),
        MX('@', 10, 'mail.your_domain.'),
        TXT('@', 'v=spf1 -all'),
        TXT('_dmarc', 'v=DMARC1; p=reject; rua=mailto:abuse@your_domain; aspf=s; adkim=s;')
      );
      

      После завершения начальной настройки DNS следует сохранить и закрыть файл.

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

      Шаг 5 — Тестирование и развертывание конфигурации DNS

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

      Вначале мы перейдем в директорию dnscontrol:

      Затем мы используем функцию preview в DNSControl для проверки синтаксиса файла и вывода вносимых изменений (без их фактического внесения):

      Если файл конфигурации DNS имеет правильный синтаксис, DNSControl выведет обзор изменений, которые будут произведены. Результат должен выглядеть примерно так:

      Output

      ******************** Domain: your_domain ----- Getting nameservers from: digitalocean ----- DNS Provider: digitalocean...8 corrections #1: CREATE A your_domain your-server-ipv4-address ttl=300 #2: CREATE A www.your_domain your-server-ipv4-address ttl=300 #3: CREATE A mail.your_domain your-server-ipv4-address ttl=300 #4: CREATE AAAA your_domain your-server-ipv6-address ttl=300 #5: CREATE TXT _dmarc.your_domain "v=DMARC1; p=reject; rua=mailto:abuse@your_domain; aspf=s; adkim=s;" ttl=300 #6: CREATE AAAA www.your_domain your-server-ipv6-address ttl=300 #7: CREATE AAAA mail.your_domain your-server-ipv6-address ttl=300 #8: CREATE MX your_domain 10 mail.your_domain. ttl=300 ----- Registrar: none...0 corrections Done. 8 corrections.

      Если вы увидите предупреждение об ошибке в составе результатов, DNSControl даст дополнительные данные о сущности ошибки и ее расположении в файле.

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

      В заключение мы можем передать изменения на рабочий провайдер DNS:

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

      Output

      ******************** Domain: your_domain ----- Getting nameservers from: digitalocean ----- DNS Provider: digitalocean...8 corrections #1: CREATE TXT _dmarc.your_domain "v=DMARC1; p=reject; rua=mailto:abuse@your_domain; aspf=s; adkim=s;" ttl=300 SUCCESS! #2: CREATE A your_domain your-server-ipv4-address ttl=300 SUCCESS! #3: CREATE AAAA your_domain your-server-ipv6-address ttl=300 SUCCESS! #4: CREATE AAAA www.your_domain your-server-ipv6-address ttl=300 SUCCESS! #5: CREATE AAAA mail.your_domain your-server-ipv6-address ttl=300 SUCCESS! #6: CREATE A www.your_domain your-server-ipv4-address ttl=300 SUCCESS! #7: CREATE A mail.your_domain your-server-ipv4-address ttl=300 SUCCESS! #8: CREATE MX your_domain 10 mail.your_domain. ttl=300 SUCCESS! ----- Registrar: none...0 corrections Done. 8 corrections.

      Если теперь мы проверим параметры DNS нашего домена на панели управления DigitalOcean, мы увидим изменения.

      Снимок экрана панели управления DigitalOcean, показывающий некоторые изменения DNS, которые были внесены DNSControl.

      Также мы можем проверить создание записи, отправив запрос DNS для нашего домена или делегированной зоны с помощью команды dig.

      Если команда dig не установлена, необходимо установить пакет dnsutils:

      • sudo apt install dnsutils

      После установки команды dig мы сможем использовать ее для запроса DNS нашего домена. Мы увидим, что записи обновлены соответствующим образом:

      В результатах выводится IP-адрес и запись DNS из вашей зоны, которая была развернута с помощью DNSControl. Распространение записей DNS может занять некоторое время, поэтому можно подождать и запустить команду чуть позже.

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

      Заключение

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

      Если вы хотите узнать об этом больше, DNSControl предусматривает интеграцию с конвейером CI/CD, что позволяет проводить детальные испытания и обеспечивает дополнительный контроль над развертыванием в производственной среде. Также можно интегрировать DNSControl в процессы сборки и развертывания инфраструктуры, что позволит развертывать серверы и добавлять их в службу DNS в полностью автоматическом режиме.

      Если вас интересует дополнительная информация о DNSControl, следующие материалы DigitalOcean содержат описание дальнейших шагов по интеграции DNSControl в рабочие процессы управления изменениями и развертывания инфраструктуры:



      Source link

      Развертывание и управление DNS с использованием OctoDNS в Debian 10


      Автор выбрал фонд Electronic Frontier Foundation для получения пожертвований в рамках программы Write for DOnations.

      Введение

      OctoDNS — это инструмент, созданный по принципу «инфраструктура как код», который поддерживает развертывание и управление зонами DNS с использованием стандартных принципов разработки программного обеспечения, включая контроль версий, тестирование и автоматизированное развертывание. OctoDNS был разработан GitHub и написан на языке Python.

      Использование OctoDNS помогает избавиться от многих сложностей ручного управления DNS, поскольку файлы зон хранятся в структурированном формате (YAML). Инструмент позволяет одновременно развертывать зоны для нескольких поставщиков DNS, определять ошибки синтаксиса и автоматически извлекать конфигурации DNS, за счет чего снижается риск человеческой ошибки. Также OctoDNS часто используется для синхронизации конфигурации DNS разных провайдеров, в частности между тестовыми и производственными системами или между работающими и резервными средами.

      Инструмент OctoDNS аналогичен инструменту DNSControl, созданному Stack Exchange и написанному на Go. В отличие от OctoDNS, в DNSControl для определения зон DNS используется язык конфигурации на базе JavaScript, что позволяет использовать различные функции программирования (например циклы) для определения нескольких похожих записей в одной зоне. Статья Развертывание и управление DNS с помощью DNSControl в Debian 10 рассказывает об основах настройки и конфигурации DNSControl.

      В этом обучающем руководстве мы научимся устанавливать и настраивать OctoDNS, создадим базовую конфигурацию DNS и начнем развертывание записей DNS на рабочем провайдере. Для этого обучающего руководства мы используем DigitalOcean в качестве примера провайдера DNS. Если вы хотите использовать другого провайдера, настройки будут выглядеть очень похоже. После завершения работы вы сможете управлять своей конфигурацией DNS и тестировать ее в безопасной среде, отключенной от сети, а затем автоматически развертывать в производственной среде.

      Предварительные требования

      Для прохождения этого обучающего руководства вам потребуется следующее:

      • Один сервер Debian 10, настроенный в соответствии с указаниями материала Начальная настройка сервера Debian 10, включая пользователя sudo без привилегий root и активированный брандмауэр для блокировки ненужных портов. your-server-ipv4-address и your-server-ipv6-address означают IP-адреса сервера, где будет размещаться ваш сайт или домен.
      • Полностью зарегистрированное доменное имя с хостингом DNS у поддерживаемого провайдера. В этом обучающем модуле мы используем доменное имя your-domain и DigitalOcean как провайдера услуги.
      • Ключ DigitalOcean API (персональный токен доступа) с разрешениями чтения и записи. Его создание описано в материале Создание персонального токена доступа.

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

      Шаг 1 — Установка OctoDNS

      OctoDNS распространяется как пакет Python pip и выполняется в виртуальной среде Python (virtualenv), так что мы начнем этот шаг с установки необходимых пакетов. virtualenv — это изолированная среда Python, которая может иметь собственные библиотеки и конфигурацию, отдельные от основной версии Python в системе. Python и virtualenv доступны в репозиториях программного обеспечения Debian по умолчанию, и поэтому их можно установить с помощью стандартных инструментов управления пакетами.

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

      Затем мы установим пакеты python и virtualenv:

      • sudo apt install python virtualenv

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

      Затем мы создадим необходимые для OctoDNS директории, где будут храниться конфигурации DNS и программы. Для начала создадим директории ~/octodns и ~/octodns/config:

      • mkdir ~/octodns ~/octodns/config

      Теперь перейдем в директорию ~/octodns:

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

      Активируем среду с помощью следующей команды:

      Будет выведен текст следующего вида:

      Output

      Running virtualenv with interpreter /usr/bin/python2 New python executable in /home/user/octodns/env/bin/python2 Also creating executable in /home/user/octodns/env/bin/python Installing setuptools, pkg_resources, pip, wheel...done.

      Строка оболочки Bash будет иметь префикс с именем виртуальной среды. Это показывает, что мы работаем в среде virtualenv:

      (env) user@digitalocean:~/octodns$
      

      Если мы захотим выйти из virtualenv, мы можем использовать команду deactivate в любой момент. Однако нам необходимо оставаться в среде virtualenv, чтобы продолжить выполнение этого обучающего руководства.

      Теперь вы установили и настроили Python и среду virtualenv и можете выполнить установку OctoDNS. OctoDNS распространяется как пакет Python pip (стандартный инструмент управления пакетами и библиотеками Python).

      Мы можем установить пакет OctoDNS pip с помощью команды в среде virtualenv:

      После этого мы можем проверить установленную версию и убедиться, что все работает правильно:

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

      Output

      octoDNS 0.9.9

      Если будет выведено сообщение об ошибке octodns-sync: command not found, необходимо повторно убедиться, что мы находимся в среде virtualenv.

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

      Шаг 2 — Настройка OctoDNS

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

      Примечание. В этом обучающем руководстве мы рассматриваем начальную настройку OctoDNS, но для использования в производственной среде рекомендуется хранить конфигурацию OctoDNS в системе контроля версий (VCS), такой как Git. Это дает преимущества полного контроля версий, интеграции с CI/CD для тестирования, удобства откатов при развертывании и т. д.

      Вначале нам нужно настроить файл config.yaml, который определит зоны DNS для управления OctoDNS и позволит проводить аутентификацию у провайдера DNS и вносить изменения.

      Формат файла config.yaml может немного отличаться в зависимости от используемого провайдера DNS. Ознакомьтесь со списком поддерживаемых провайдеров в официальной документации OctoDNS, чтобы найти конфигурацию для вашего провайдера. При просмотре этой гиперссылки детали конфигурации выводятся как комментарий к коду в фактическом коде Python нашего провайдера, привязанного в столбце Provider в таблице. Когда мы найдем код Python нашего провайдера, например cloudflare.py или route53.py, соответствующий комментарий к коду можно будет найти в классе ProviderNameProvider. Например:

      Excerpt of octodns/provider/route53.py

      class Route53Provider(BaseProvider):
        '''
        AWS Route53 Provider
        route53:
            class: octodns.provider.route53.Route53Provider
            # The AWS access key id
            access_key_id:
            # The AWS secret access key
            secret_access_key:
            # The AWS session token (optional)
            # Only needed if using temporary security credentials
            session_token:
      

      Перейдите в директорию ~/octodns/config:

      Создайте файл config.yaml и откройте его для редактирования:

      Добавьте в файл образец конфигурации config.yaml для вашего провайдера DNS. Если вы используете DigitalOcean в качестве своего провайдера DNS, вы можете использовать следующее:

      ~/octodns/config/config.yaml

      ---
      providers:
        config:
          class: octodns.provider.yaml.YamlProvider
          directory: ./config
          default_ttl: 300
          enforce_order: True
        digitalocean:
          class: octodns.provider.digitalocean.DigitalOceanProvider
          token: your-digitalocean-oauth-token
      
      zones:
        your-domain.:
          sources:
            - config
          targets:
            - digitalocean
      

      Этот файл сообщает OctoDNS, к каким провайдерам DNS требуется подключаться и какими зонами DNS нужно управлять для этих провайдеров.

      Необходимо указать форму аутентификации для провайдера DNS. Обычно это ключ API или токен OAuth.

      Если вы не хотите хранить токен доступа в текстовой форме в файле конфигурации, вы можете передать его в переменную среды при запуске программы. Для этого нужно использовать следующую строку token: в файле config.yaml:

      ~/octodns/config/config.yaml

      token: env/DIGITALOCEAN_OAUTH_TOKEN
      

      Перед запуском OctoDNS необходимо задать следующую переменную среды для токена доступа, и OctoDNS будет считывать ее в месте запуска:

      • export DIGITALOCEAN_OAUTH_TOKEN=your-digitalocean-oauth-token

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

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

      Если вы используете нескольких провайдеров DNS, например для разных доменных имен или делегированных зон DNS, вы можете определить их в том же самом файле config.yaml.

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

      Шаг 3 — Создание файла конфигурации DNS

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

      Для каждой зоны DNS, управляемой с помощью OctoDNS, должен существовать собственный файл, например your-domain.yaml. В этом файле записи DNS для зоны определяются с помощью YAML.

      Для начала перейдем в директорию ~/octodns/config:

      Затем создадим файл your-domain.yaml и откроем его для редактирования:

      Добавим в файл следующий образец конфигурации:

      ~/octodns/config/your-domain.yaml

      ---
      '':
        - type: A
          value: your-server-ipv4-address
      
      www:
        - type: A
          value: your-server-ipv4-address
      

      В этом файле образца определяется зона DNS для your-domain с двумя записями A, указывающими на адрес IPv4, на котором размещен домен или сайт. Одна запись A предназначена для корневого домена (например, your-domain), а другая — для субдомена www (например, www.your-domain).

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

      Мы настроили базовый файл конфигурации зоны DNS для OctoDNS с двумя базовыми записями A, указывающими на адрес IPv4 вашего домена или сайта. Далее мы заполним файл полезными записями DNS.

      Шаг 4 — Заполнение файла конфигурации DNS

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

      В отличие от традиционных файлов зоны BIND, где записи DNS записаны в необработанном построчном формате, записи DNS в OctoDNS определены как ключи и подключи YAML с рядом связанных значений, как кратко показано на шаге 3.

      Ключ 'name' обычно является ключом верхнего уровня, то есть идентификатором записи. Ключи www, subdomain1 и mail являются примерами ключа name службы DNS. В OctoDNS имеется два имени специального назначения: " для корневой записи (обычно @), и '*' для записей с подстановочными символами. Для каждого ключа (записи DNS) требуется значение type. Оно определяет, какой тип записей DNS мы определяем ключом YAML верхнего уровня. Значение type существует для каждого из стандартных типов записей DNS, в том числе A, AAAA, MX, TXT, NS, CNAME и т. д. Полный список доступных типов записей можно найти в разделе Записи в документации по OctoDNS.

      Значения записей DNS определяются напрямую как значения ключей верхнего уровня (если используется только одно значение) или в форме списка (если используется несколько значений, например несколько IP-адресов или адресов MX).

      Например, для определения одного значения можно использовать следующую конфигурацию:

      ~/octodns/config/your-domain.yaml

      'www':
        type: A
        value: 203.0.113.1
      

      Также можно определить несколько значений для одной записи:

      ~/octodns/config/your-domain.yaml

      'www':
        type: A
        values:
        - 203.0.113.1
        - 203.0.113.2
      

      Синтаксис настройки записей DNS немного отличается для каждого типа записей. Далее приведены несколько примеров наиболее распространенных типов записей:

      Записи A:

      Назначение: указывают на адрес IPv4.

      Синтаксис:

      'name':
        type: A
        value: ipv4-address
      

      Пример:

      'www':
        type: A
        value: your-server-ipv4-address
      

      Записи AAAA:

      Назначение: указывают на адрес IPv6.

      Синтаксис:

      'name':
        type: AAAA
        value: ipv6-address
      

      Пример:

      'www':
        type: AAAA
        value: your-server-ipv6-address
      

      Записи CNAME:

      Назначение: делают домен или субдомен псевдонимом другого домена или субдомена.

      Синтаксис:

      'name':
        type: CNAME
        value: fully-qualified-domain-name
      

      Пример:

      'www':
        type: CNAME
        value: www.example.org
      

      Записи MX:

      Назначение: направляют электронную почту на определенные серверы или адреса.

      Синтаксис:

      'name':
        type: MX
        value:
          exchange: mail-server
          preference: priority-value
      

      Следует отметить, что если в значении MX содержатся точки, завершающий символ . обязательно должен быть указан в конце строки.

      Пример:

      '':
        type: MX
        value:
          exchange: mail.your-domain.
          preference: 10
      

      Записи TXT:

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

      Синтаксис:

      'name':
        type: TXT
        value: content
      

      Пример:

      '':
        type: TXT
        value: This is a TXT record.
      

      Чтобы начать добавлять записи DNS для нашего домена или делегированной зоны DNS, нам нужно отредактировать файл конфигурации DNS:

      • cd ~/octodns/config
      • nano your-domain.yaml

      Далее мы можем начать заполнение зоны DNS, используя синтаксис, описанный в предыдущем списке и в разделе Записи официальной документации по OctoDNS.

      Приведенный здесь блок кода содержит полный образец начальной конфигурации DNS, предназначенный для справочных целей:

      ~/octodns/config/your-domain.yaml

      ---
      '':
        - type: A
          value: your-server-ipv4-address
      
        - type: AAAA
          value: your-server-ipv6-address
      
        - type: MX
          value:
            exchange: mail.your-domain.
            preference: 10
      
        - type: TXT
          value: v=spf1 -all
      
      _dmarc:
        type: TXT
        value: v=DMARC1; p=reject; rua=mailto:abuse@your-domain; aspf=s; adkim=s;
      
      mail:
        - type: A
          value: your-server-ipv4-address
      
        - type: AAAA
          value: your-server-ipv6-address
      
      www:
        - type: A
          value: your-server-ipv4-address
      
        - type: AAAA
          value: your-server-ipv6-address
      

      После завершения начальной настройки DNS следует сохранить и закрыть файл.

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

      Шаг 5 — Тестирование и развертывание конфигурации DNS

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

      Вначале мы перейдем в директорию octodns:

      Еще раз проверим, что мы работаем в среде Python virtualenv, посмотрев на имя перед Bash в командной строке:

      (env) user@digitalocean:~/octodns$
      

      Затем используем команду octodns-validate для проверки синтаксиса файла или файлов конфигурации. Также необходимо указать путь к файлу конфигурации:

      • octodns-validate --config=./config/config.yaml

      Если синтаксис YAML конфигурации DNS правильный, OctoDNS не выводит никаких результатов. Если вы увидите предупреждение об ошибке в составе результатов, OctoDNS даст дополнительные данные о сущности ошибки и ее местонахождении в файле YAML.

      Далее можно выполнить пробную установку конфигурации DNS. При этом будет показано, какие будут произведены изменения, но выполняться они не будут:

      • octodns-sync --config=./config/config.yaml

      Результат должен выглядеть примерно так:

      Output

      ******************************************************************************** * your-domain. ******************************************************************************** * digitalocean (DigitalOceanProvider) * Create <ARecord A 300, mail.your-domain., ['your-server-ipv4-address']> (config) * Create <AaaaRecord AAAA 300, mail.your-domain., ['your-server-ipv6-address']> (config) * Create <TxtRecord TXT 300, your-domain., ['v=spf1 -all']> (config) * Create <AaaaRecord AAAA 300, your-domain., ['your-server-ipv6-address']> (config) * Create <ARecord A 300, your-domain., ['your-server-ipv4-address']> (config) * Create <ARecord A 300, www.your-domain., ['your-server-ipv4-address']> (config) * Create <MxRecord MX 300, your-domain., [''10 mail.your-domain.'']> (config) * Create <TxtRecord TXT 300, _dmarc.your-domain., ['v=DMARC1; p=reject; rua=mailto:abuse@your-domain; aspf=s; adkim=s;']> (config) * Create <AaaaRecord AAAA 300, www.your-domain., ['your-server-ipv6-address']> (config) * Summary: Creates=9, Updates=0, Deletes=0, Existing Records=2 ********************************************************************************

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

      В заключение мы можем передать изменения на рабочий провайдер DNS:

      • octodns-sync --config=./config/config.yaml --doit

      Примечание. В некоторых случаях OctoDNS может отказаться применять изменения, если количество модификаций значительно. Это функция автоматической защиты, которая позволит предотвратить случайные ошибки конфигурации. В случае такого отклонения можно повторно запустить octodns-sync с опцией --force, но при этом нужно быть уверенным в правильности этого шага.

      Вы увидите результаты, похожие на показанные при пробном запуске, однако при этом будет добавлено примерно следующее:

      Output

      2019-07-07T23:17:27 INFO DigitalOceanProvider[digitalocean] apply: making changes 2019-07-07T23:17:30 INFO Manager sync: 9 total changes

      Если теперь мы проверим параметры DNS нашего домена на панели управления DigitalOcean, мы увидим изменения.

      Снимок экрана панели управления DigitalOcean, показывающий некоторые изменения DNS, которые были внесены OctoDNS.

      Также мы можем проверить создание записи, отправив запрос DNS для нашего домена или делегированной зоны с помощью команды dig.

      Если команда dig не установлена, необходимо установить пакет dnsutils:

      • sudo apt install dnsutils

      После установки команды dig мы сможем использовать ее для запроса DNS нашего домена. Мы увидим, что записи обновлены соответствующим образом:

      В результатах выводится IP-адрес и запись DNS из вашей зоны, которая была развернута с помощью OctoDNS. Распространение записей DNS может занять некоторое время, поэтому можно подождать и запустить команду чуть позже.

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

      Заключение

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

      Если вы хотите узнать об этом больше, OctoDNS предусматривает интеграцию с конвейером CI/CD, что позволяет проводить детальные испытания и обеспечивает дополнительный контроль над развертыванием в производственной среде. Также можно интегрировать OctoDNS в процессы сборки и развертывания инфраструктуры, что позволит развертывать серверы и добавлять их в службу DNS в полностью автоматическом режиме.

      Если вы хотите узнать больше о возможностях OctoDNS, в следующих статьях DigitalOcean вы найдете интересную информацию о следующих шагах по интеграции OctoDNS в рабочие процессы управления изменениями и развертывания инфраструктуры:



      Source link