Tutorial

Установка Drupal с помощью Docker Compose

NginxMySQLDrupalDockerLEMPCMSOpen SourceUbuntu 18.04

Автор выбрал фонд Организации Объединенных Наций для получения пожертвования в рамках программы Write for DOnations.

Оригинальная версия WordPress этого обучающего руководства была написана Кэйтлин Джуэлл (Kathleen Juell).

Введение

Drupal — это система управления контентом (CMS), написанная на PHP и распространяемая по универсальной общественной лицензии GNU с открытым исходным кодом. Люди и организации по всему миру используют Drupal для управления правительственными сайтами, ведения личных блогов, бизнеса и не только. Уникальные отличия Drupal от других структур CMS: растущее сообщество и набор функций, которые включают безопасные процессы, надежную работу и адаптивность.

Для Drupal необходимо установить стек LAMP (Linux, Apache, MySQL и PHP) или LEMP (Linux, Nginx, MySQL и PHP), но установка компонентов по отдельности занимает много времени. Для упрощения процесса установки Drupal мы можем использовать такие инструменты, как Docker и Docker Compose. В этом обучающем руководстве будут использованы образы Docker для установки отдельных компонентов в Docker-контейнерах. С помощью Docker Compose мы можем определять и управлять несколькими контейнерами для базы данных, приложения, а также взаимодействием/коммуникацией между ними.

В этом обучающем руководстве мы установим Drupal с помощью Docker Compose для возможности контейнеризации и развертывания нашего веб-сайта Drupal на серверах. Мы запустим контейнеры для базы данных MySQL, веб-сервера Nginx и системы Drupal. Также мы защитим нашу установку, получив сертификаты TLS/SSL с Let’s Encrypt​​​ для домена, который мы хотим ассоциировать с нашим сайтом. Наконец, мы настроим задание cron для обновления ваших сертификатов, чтобы домен оставался защищенным.

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

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

  • Сервер на базе Ubuntu 18.04, а также пользователь без прав root с привилегиями sudo и активный брандмауэр. Дополнительную информацию о настройке этих параметров см. в руководстве по первоначальной настройке сервера.
  • Система Docker, установленная на сервере в соответствии с шагами 1 и 2 руководства Установка и использование Docker в Ubuntu 18.04. Это обучающее руководство было протестировано на версии 19.03.8.
  • Docker Compose, установленный на сервере в соответствии с шагом 1 руководства Установка Docker Compose в Ubuntu 18.04. Это обучающее руководство было протестировано на версии 1.21.2.
  • Зарегистрированное доменное имя. В этом обучающем руководстве мы будем использовать your_domain. Вы можете получить бесплатный домен на Freenom или зарегистрировать доменное имя по вашему выбору.
  • На вашем сервере должны быть настроены обе нижеследующие записи DNS. Вы можете воспользоваться введением в работу с DigitalOcean DNS, чтобы получить подробную информацию о добавлении доменов в учетную запись DigitalOcean, если вы используете этот способ:
    • Запись A, где your_domain указывает на публичный IP-адрес вашего сервера.
    • Запись A, где www.your_domain указывает на публичный IP-адрес вашего сервера.

Шаг 1 — Настройка конфигурации веб-сервера

Перед запуском контейнеров нам нужно определить конфигурацию для нашего веб-сервера Nginx. Наш файл конфигурации будет включать несколько специфических для Drupal блоков расположения наряду с блоками расположения, которые будут направлять передаваемые запросы верификации Let’s Encrypt клиенту Certbot для автоматизированного обновления сертификатов.

Вначале создадим директорию проекта для нашей настройки Drupal с именем drupal:

  • mkdir drupal

Перейдите в недавно созданную директорию:

  • cd drupal

Теперь мы можем создать директорию для нашего файла конфигурации:

  • mkdir nginx-conf

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

  • nano nginx-conf/nginx.conf

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

Добавьте в этот файл следующий код. Обязательно замените your_domain на ваше доменное имя:

~/drupal/nginx-conf/nginx.conf
server {
    listen 80;
    listen [::]:80;

    server_name your_domain www.your_domain;

    index index.php index.html index.htm;

    root /var/www/html;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    rewrite ^/core/authorize.php/core/authorize.php(.*)$ /core/authorize.php$1;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass drupal:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ /\.ht {
        deny all;
    }

    location = /favicon.ico {
        log_not_found off; access_log off;
    }
    location = /robots.txt {
        log_not_found off; access_log off; allow all;
    }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }
}

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

Директивы:

  • listen: данный элемент просит Nginx прослушивать порт 80, что позволит нам использовать плагин webroot Certbot для наших запросов сертификатов. Обратите внимание, что мы пока не будем включать порт 443, мы обновим нашу конфигурацию и добавим SSL после успешного получения наших сертификатов.

  • server_name: этот элемент определяет имя нашего сервера и серверный блок, которые должны использоваться для запросов к нашему серверу. Обязательно замените your_domain в этой строке на ваше собственное доменное имя.

  • index: директива index определяет файлы, которые будут использоваться в качестве индексов при обработке запросов к нашему серверу. Здесь мы изменили порядок приоритета по умолчанию, поставив index.php перед index.html, в результате чего Nginx будет давать приоритет файлам с именем index.php при наличии возможности.

  • root: наша директива root назначает имя корневой директории для запросов к нашему серверу. Эта директория, /var/www/html, создается в качестве точки монтирования в момент сборки с помощью инструкций в файле Dockerfile Drupal. Эти инструкции Dockerfile также гарантируют, что файлы релиза Drupal монтируются в этот том.

  • rewrite: если указанное регулярное выражение (^/core/authorize.php/core/authorize.php(.*)$) соответствует запросу URI, URI меняется согласно указаниям в строке замены (/core/authorize.php$1).

Блоки расположения:

  • location ~ /.well-known/acme-challenge: этот блок расположения будет обрабатывать запросы в директории .well-known, где Certbot будет размещать временный файл для подтверждения того, что DNS для нашего домена будет работать с нашим сервером. Настроив данную конфигурацию, мы сможем использовать плагин webroot Certbot для получения сертификатов для нашего домена.

  • location /: в этом блоке расположения мы будем использовать директиву try_files для проверки файлов, соответствующих отдельным запросам URI. Вместо того чтобы возвращать по умолчанию статус 404 Not Found​​​, мы будем передавать контроль файлу index.php Drupal с аргументами запроса.

  • location ~\.php$: этот блок расположения будет обрабатывать PHP-запросы и проксировать эти запросы в наш контейнер drupal. Поскольку наш образ Drupal Docker будет опираться на образ php:fpm, мы также добавим параметры конфигурации, принадлежащие протоколу FastCGI, в этот блок. Nginx требует наличия независимого процессора PHP для запросов PHP: в нашем случае эти запросы будут обрабатываться процессором php-fpm, который будет включать образ php:fpm. Кроме того, этот блок расположения содержит директивы FastCGI, переменные и параметры, которые будут проксировать запросы для приложения Drupal, запущенного в нашем контейнере Drupal, задавать предпочитаемый индекс захваченного URI запроса, а также выполнять парсинг запросов URI.

  • location ~ /\.ht: этот блок будет обрабатывать файлы .htaccess, поскольку Nginx не будет обслуживать их. Директива deny_all гарантирует, что файлы .htaccess никогда не будут отображаться для пользователей.

  • location = /favicon.ico, location = /robots.txt: эти блоки гарантируют, что запросы для /favicon.ico и /robots.txt не будут регистрироваться.

  • location ~* \.(css|gif|ico|jpeg|jpg|js|png)$: этот блок отключает запись в журнал для запросов статичных активов и гарантирует, что эти активы будут иметь высокую кэшируемость, поскольку обычно их трудно обслуживать.

Дополнительную информацию о проксировании FastCGI см. в статье Понимание и реализация проксирования FastCGI в Nginx. Информацию о серверных блоках и блоках расположения см. в статье Знакомство с сервером Nginx и алгоритмы выбора блоков расположения.

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

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

Шаг 2 — Настройка переменных среды

Для сохранения информации, связанной с сайтом, нашему приложению Drupal требуется база данных (MySQL, PostgresSQL и др.). Контейнеру Drupal потребуется доступ к определенным переменным среды во время исполнения, чтобы получить доступ к контейнеру базы данных (MySQL). Эти переменные содержат конфиденциальную информацию, например учетные данные базы данных, поэтому мы не можем раскрыть ее непосредственно в файле Docker Compose — главном файле, содержащем информацию о том, как будут работать наши контейнеры.

Рекомендуется всегда помещать конфиденциальные значения в файл .env и ограничить их использование до внутреннего. Это не позволит скопировать эти значения в репозиторий нашего проекта и открыть их для общего доступа.

В главной директории проекта ~/drupal, создайте и откройте файл .env:

  • nano .env

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

~/drupal/.env
MYSQL_ROOT_PASSWORD=root_password
MYSQL_DATABASE=drupal
MYSQL_USER=drupal_database_user
MYSQL_PASSWORD=drupal_database_password

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

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

Если вы планируете использовать Git для контроля версий, инициализируйте текущую рабочую директорию в качестве репозитория с помощью git init:

  • git init

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

  • nano .gitignore

Добавьте следующее:

~/drupal/.gitignore
.env

Сохраните и закройте файл.

Подобным образом откройте файл .dockerignore:

  • nano .dockerignore

Затем добавьте следующее:

~/drupal/.dockerignore
.env
.git

Сохраните и закройте файл.

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

Шаг 3 — Определение служб с помощью Docker Compose

Docker Compose — это инструмент для определения и запуска многоконтейнерных приложений. Мы определяем файл YAML для настройки служб нашего приложения. Служба Docker Compose — это запущенный контейнер, и Compose позволяет нам связывать эти службы с общими томами и сетями.

Мы создадим разные контейнеры для нашего приложения, базы данных и веб-сервера Drupal. Также мы создадим контейнер для запуска Certbot​​​, чтобы получить сертификаты для нашего веб-сервера.

Создайте файл docker-compose.yml:

  • nano docker-compose.yml

Добавьте следующий код для определения версии файла Compose и службы базы данных mysql:

~/drupal/docker-compose.yml
version: "3"

services:
  mysql:
    image: mysql:8.0
    container_name: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: unless-stopped
    env_file: .env
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - internal

Давайте пройдемся по ним отдельно со всеми параметрами настройки службы mysql​​​:

  • image: указывает образ, который будет использоваться/извлекаться для создания контейнера. Чтобы избежать конфликтов в будущем, рекомендуется всегда использовать образ с соответствующей версией тега, кроме тега latest. Подробнее об оптимальных методах использования Dockerfile узнайте в документации Docker.

  • container_name: определяет имя контейнера.

  • command: используется для отмены команды по умолчанию (инструкции CMD) в образе. MySQL поддерживает разные плагины аутентификации, но mysql_native_password — это традиционный способ аутентификации. Поскольку PHP и, следовательно, Drupal не будут поддерживать более новую аутентификацию MySQL, необходимо установить --default-authentication-plugin=mysql_native_password​​​ в качестве механизма аутентификации по умолчанию.

  • restart: используется для определения политики контейнера. Политика unless-stopped​​​ повторно запускает контейнер, пока он не будет остановлен вручную.

  • env_file: добавляет переменные среды из файла. В нашем случае он будет считывать переменные среды из файла .env, определенные в предыдущем шаге.

  • volumes: монтирует пути к хосту или томам с именем, указанным как подпараметры службы. Мы монтируем том с именем db-data в директорию /var/lib/mysql​​​ в контейнере, куда MySQL по умолчанию будет записывать свои файлы данных.

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

Мы дали определение нашей службы mysql​​​, а теперь добавим определение службы приложения drupal в конец файла:

~/drupal/docker-compose.yml
...
  drupal:
    image: drupal:8.7.8-fpm-alpine
    container_name: drupal
    depends_on:
      - mysql
    restart: unless-stopped
    networks:
      - internal
      - external
    volumes:
      - drupal-data:/var/www/html

В этом определении службы мы называем наш контейнер и определяем политику перезапуска, как уже делали это для службы mysql. Также мы добавляем в этот контейнер ряд параметров:

  • image: здесь мы используем образ Drupal 8.7.8-fpm-alpine. Этот образ содержит процессор php-fpm, для которого наш веб-сервер Nginx должен выполнять обработку PHP. Кроме того, мы используем образ alpine, полученный из проекта Alpine Linux, который уменьшит размер всего образа и рекомендуется в оптимальных методах использования Dockerfile. Drupal имеет несколько версий и образов, ознакомьтесь с ними в Dockerhub.

  • depends_on: используется для выражения зависимости между службами. Определение службы mysql как зависимости к нашему контейнеру drupal обеспечит создание контейнера drupal​​​ после контейнера mysql​​​ ​​​и поможет успешно запустить наше приложение.

  • networks: здесь мы добавили этот контейнер в сеть external​​​ вместе с сетью internal​​​. Таким образом доступ к службе mysql​​​ будет возможен только из контейнера drupal по сети internal, но при этом доступ других контейнеров по сети external​​​ к этому контейнеру будет сохранен.

  • volumes: мы монтируем том с именем drupal-data на точку монтирования /var/www/html, созданную образом Drupal. Использование тома с именем таким образом позволит разделить наш код приложения с другими контейнерами.

Далее добавим определение службы Nginx после определения службы drupal:

~/drupal/docker-compose.yml
...
  webserver:
    image: nginx:1.17.4-alpine
    container_name: webserver
    depends_on:
      - drupal
    restart: unless-stopped
    ports:
      - 80:80
    volumes:
      - drupal-data:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - external

Мы снова присвоим имя нашему контейнеру и сделаем его зависимым от контейнера Drupal в отношении порядка запуска. Также мы используем образ alpine — образ Nginx 1.17.4-alpine.

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

  • ports: этот параметр открывает порт 80, чтобы активировать параметры конфигурации, определенные нами в файле nginx.conf в шаге 1.

  • volumes: здесь мы определяем том с именем и путь хоста:

    • drupal-data:/var/www/html​​​: этот параметр будет монтировать код нашего приложения Drupal в директорию /var/www/html​​​, директорию, которую мы задали в качестве корневой директории в нашем серверном блоке Nginx.
    • ./nginx-conf:/etc/nginx/conf.d: этот элемент будет монтировать директорию конфигурации Nginx на хост в соответствующую директорию в контейнере, гарантируя, что любые изменения, которые мы вносим в файлы на хосте, будут отражены в контейнере.
    • certbot-etc:/etc/letsencrypt​​​: этот элемент будет монтировать соответствующие сертификаты и ключи Let’s Encrypt для нашего домена в соответствующую директорию контейнера.
    • networks: мы определили сеть external​​​ только для того, чтобы этот контейнер мог обмениваться данными с контейнером drupal​​​, а не с контейнером mysql​​​.

Наконец, мы добавим наше последнее определение для службы certbot​​​. Обязательно замените sammy@your_domain​​​ и your_domain на свой адрес эл. почты и доменное имя:

~/drupal/docker-compose.yml
...
  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - drupal-data:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain

Это определение попросит Compose извлекать образ certbot/certbot из Docker Hub. Также оно использует тома с именем для обмена ресурсами с контейнером Nginx, включая доменные сертификаты и ключ в certbot-etc и код приложения в drupal-data​​​.

Мы также использовали depends_on, чтобы убедиться, что контейнер certbot​​​ будет запущен после запуска службы webserver​​​.

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

Также мы включили параметр command, указывающий субкоманду для запуска с используемой контейнером по умолчанию командой certbot. Клиент Certbot​​ поддерживает плагины для получения и установки сертификатов. Мы используем плагин webroot для получения сертификата путем включения certonly и --webroot в командную строку. Подробнее о плагине и дополнительных командах читайте в официальной документации Certbot.

Под определением службы certbot добавьте определения сети и тома:

~/drupal/docker-compose.yml
...
networks:
  external:
    driver: bridge
  internal:
    driver: bridge

volumes:
  drupal-data:
  db-data:
  certbot-etc:

Ключ верхнего уровня networks​​​ позволяет нам указывать, какие сети необходимо создать. networks обеспечивает коммуникацию между службами/контейнерами во всех портах, так как они находятся на одном и том же хосте демона Docker. Мы определили две сети, internal​​​ и external​​​, для защиты коммуникации между службами webserver​​​, drupal​​​ и mysql​​​.

Ключ volumes используется для определения томов с именем drupal-data, db-data и certbot-etc​​​. Когда Docker создает тома, содержимое тома сохраняется в директории файловой системы хоста, /var/lib/docker/volumes/, а данным процессом управляет Docker. После этого содержимое каждого тома монтируется из этой директории в любой контейнер, использующий том. Таким образом мы можем делиться кодом и данными между контейнерами.

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

~/drupal/docker-compose.yml
version: "3"

services:
  mysql:
    image: mysql:8.0
    container_name: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: unless-stopped
    env_file: .env
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - internal

  drupal:
    image: drupal:8.7.8-fpm-alpine
    container_name: drupal
    depends_on:
      - mysql
    restart: unless-stopped
    networks:
      - internal
      - external
    volumes:
      - drupal-data:/var/www/html

  webserver:
    image: nginx:1.17.4-alpine
    container_name: webserver
    depends_on:
      - drupal
    restart: unless-stopped
    ports:
      - 80:80
    volumes:
      - drupal-data:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - external

  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - drupal-data:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain

networks:
  external:
    driver: bridge
  internal:
    driver: bridge

volumes:
  drupal-data:
  db-data:
  certbot-etc:

Мы завершили определение наших служб. Далее запустим контейнер и протестируем наши запросы сертификата.

Шаг 4 — Получение сертификатов SSL и учетных данных

Мы можем запустить наши контейнеры с помощью команды docker-compose up, которая будет создавать и запускать наши контейнеры и службы в указанном нами порядке. Если наши запросы доменов будут выполнены успешно, мы увидим корректный статус выхода в нашем выводе и нужные сертификаты, установленные в папке /etc/letsencrypt/live на контейнере веб-сервера.

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

  • docker-compose up -d

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

Output
... Creating mysql ... done Creating drupal ... done Creating webserver ... done Creating certbot ... done

Проверьте статус служб с помощью команды docker-compose ps:

  • docker-compose ps

Мы увидим службы mysql, drupal и webserver с State в значении Up​​​, а выход из certbot​​​ будет выполнен с сообщением о статусе 0:

Output
Name Command State Ports -------------------------------------------------------------------------- certbot certbot certonly --webroot ... Exit 0 drupal docker-php-entrypoint php-fpm Up 9000/tcp mysql docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp

Если вы увидите любое значение, кроме Up в столбце State для служб mysql​​​, drupal​​​ или webserver, или любое сообщение о статусе выхода, отличающееся от 0, для контейнера certbot, проверьте журналы службы с помощью команды docker-compose logs:

  • docker-compose logs service_name

Теперь мы можем проверить, что наши сертификаты установлены на контейнере webserver, с помощью команды docker-compose exec:

  • docker-compose exec webserver ls -la /etc/letsencrypt/live

Результат будет выглядеть следующим образом:

Output
total 16 drwx------ 3 root root 4096 Oct 5 09:15 . drwxr-xr-x 9 root root 4096 Oct 5 09:15 .. -rw-r--r-- 1 root root 740 Oct 5 09:15 README drwxr-xr-x 2 root root 4096 Oct 5 09:15 your_domain

Теперь, когда все работает успешно, мы можем изменить определение службы certbot, чтобы удалить флаг --staging​​.

Откройте файл docker-compose.yml, найдите определение службы certbot и замените флаг --staging в параметрах команды на флаг --force-renewal, который будет указывать Certbot, что вы хотите запросить новый сертификат с теми же доменами, что и в уже существующем сертификате. Обновленное определение certbot​​​ будет выглядеть примерно следующим образом:

~/drupal/docker-compose.yml
...
  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - drupal-data:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain
...

Нам необходимо снова запустить docker-compose up​​​ для воссоздания контейнера certbot​​​. Также мы будем использовать параметр --no-deps, чтобы сообщить Compose о том, что можно пропустить запуск службы webserver, поскольку она уже запущена:

  • docker-compose up --force-recreate --no-deps certbot

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

Output
Recreating certbot ... done Attaching to certbot certbot | Saving debug log to /var/log/letsencrypt/letsencrypt.log certbot | Plugins selected: Authenticator webroot, Installer None certbot | Renewing an existing certificate certbot | Performing the following challenges: certbot | http-01 challenge for your_domain certbot | http-01 challenge for www.your_domain certbot | Using the webroot path /var/www/html for all unmatched domains. certbot | Waiting for verification... certbot | Cleaning up challenges certbot | IMPORTANT NOTES: certbot | - Congratulations! Your certificate and chain have been saved at: certbot | /etc/letsencrypt/live/your_domain/fullchain.pem certbot | Your key file has been saved at: certbot | /etc/letsencrypt/live/your_domain/privkey.pem certbot | Your cert will expire on 2020-01-03. To obtain a new or tweaked certbot | version of this certificate in the future, simply run certbot certbot | again. To non-interactively renew *all* of your certificates, run certbot | "certbot renew" certbot | - Your account credentials have been saved in your Certbot certbot | configuration directory at /etc/letsencrypt. You should make a certbot | secure backup of this folder now. This configuration directory will certbot | also contain certificates and private keys obtained by Certbot so certbot | making regular backups of this folder is ideal. certbot | - If you like Certbot, please consider supporting our work by: certbot | certbot | Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate certbot | Donating to EFF: https://eff.org/donate-le certbot | certbot exited with code 0

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

Шаг 5 — Изменение конфигурации веб-сервера и определения службы

После установки сертификатов SSL в Nginx нам нужно будет перенаправить все запросы HTTP в HTTPS. Также необходимо указать наш сертификат SSL и места расположения ключей, а также добавить параметры безопасности и заголовки.

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

  • docker-compose stop webserver

Результат будет выглядеть следующим образом:

Output
Stopping webserver ... done

Затем удалим ранее созданный файл конфигурации Nginx:

  • rm nginx-conf/nginx.conf

Откройте другую версию файла:

  • nano nginx-conf/nginx.conf

Добавьте следующий код в файл для перенаправления HTTP на HTTPS и добавления учетных данных, протоколов и заголовков безопасности SSL. Не забудьте заменить your_domain на свой собственный домен:

~/drupal/nginx-conf/nginx.conf
server {
    listen 80;
    listen [::]:80;

    server_name your_domain www.your_domain;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

    location / {
        rewrite ^ https://$host$request_uri? permanent;
    }
}
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name your_domain www.your_domain;

    index index.php index.html index.htm;

    root /var/www/html;

    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    rewrite ^/core/authorize.php/core/authorize.php(.*)$ /core/authorize.php$1;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass drupal:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ /\.ht {
        deny all;
    }

    location = /favicon.ico {
        log_not_found off; access_log off;
    }
    location = /robots.txt {
        log_not_found off; access_log off; allow all;
    }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }
}

Блок сервера HTTP указывает плагин webroot для запросов обновления Certbot в директории .well-known/acme-challenge. Также он содержит директиву перезаписи, которая перенаправляет запросы HTTP в корневую директорию HTTPS.

Блок сервера HTTPS активирует ssl и http2. Дополнительную информацию о том, как выполняется итерация HTTP/2 в протоколах HTTP и какие преимущества это может дать для повышения производительности веб-сайта, см. во вводной части руководства по настройке Nginx с поддержкой HTTP/2 в Ubuntu 18.04.

Эти блоки активируют SSL, поскольку мы включили наш сертификат SSL и места расположения ключей, а также рекомендуемые заголовки. Эти заголовки позволят нам получить рейтинг А на SSL Labs​​​ и сайтах тестирования сервера Security Headers.

Наши директивы root и index также расположены в этом блоке, равно как и остальные блоки расположения Drupal, описанные в шаге 1.

Сохраните и закройте обновленный файл конфигурации Nginx.

Прежде чем воссоздать контейнер webserver, необходимо добавить сопоставление порта 443 в наше определение службы webserver, поскольку мы активировали сертификаты SSL.

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

  • nano docker-compose.yml

Сделайте следующие изменения в определении службы webserver:

~/drupal/docker-compose.yml
...
  webserver:
    image: nginx:1.17.4-alpine
    container_name: webserver
    depends_on:
      - drupal
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
    volumes:
      - drupal-data:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - external
...

После активации сертификатов SSL наш файл docker-compose.yml​​​ будет выглядеть следующим образом:

~/drupal/docker-compose.yml
version: "3"

services:
  mysql:
    image: mysql:8.0
    container_name: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: unless-stopped
    env_file: .env
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - internal

  drupal:
    image: drupal:8.7.8-fpm-alpine
    container_name: drupal
    depends_on:
      - mysql
    restart: unless-stopped
    networks:
      - internal
      - external
    volumes:
      - drupal-data:/var/www/html

  webserver:
    image: nginx:1.17.4-alpine
    container_name: webserver
    depends_on:
      - drupal
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
    volumes:
      - drupal-data:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - external

  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - drupal-data:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain

networks:
  external:
    driver: bridge
  internal:
    driver: bridge

volumes:
  drupal-data:
  db-data:
  certbot-etc:

Сохраните и закройте файл. Давайте воссоздадим службу webserver с нашей обновленной конфигурацией:

  • docker-compose up -d --force-recreate --no-deps webserver

Результат будет выглядеть следующим образом:

Output
Recreating webserver ... done

Проверьте службы с помощью команды docker-compose ps:

  • docker-compose ps

Мы увидим службы mysql, drupal и webserver со значением Up​​​, а выход из certbot​​​​ будет выполнен с сообщением о статусе 0:

Output
Name Command State Ports -------------------------------------------------------------------------- certbot certbot certonly --webroot ... Exit 0 drupal docker-php-entrypoint php-fpm Up 9000/tcp mysql docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp webserver nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

Теперь все наши службы запущены, и мы можем продолжить установку Drupal через веб-интерфейс.

Шаг 6 — Завершение установки через веб-интерфейс

Давайте выполним установку через веб-интерфейс Drupal.

В браузере перейдите к домену сервера. Не забудьте заменить здесь your_domain на ваше доменное имя:

https://your_domain

Выберите язык:

Страница выбора языка в веб-интерфейсе Drupal

Нажмите Сохранить и продолжить. Мы перейдем на страницу Профиль установки. Drupal имеет несколько профилей, поэтому выберите стандартный профиль и нажмите Сохранить и продолжить.

Страница выбора профиля в веб-интерфейсе Drupal

После выбора профиля мы перейдем к странице Конфигурация базы данных. Выберите тип базы данных MySQL, MySQL, MariaDB, Percona Server или аналогичная и введите значения для Имя базы данных, имя пользователя и пароль из значений, соответствующих MYSQL_DATABASE, MYSQL_USER​​​ и MYSQL_PASSWORD, определенных соответственно в файле .env в шаге 2. Нажмите Расширенные параметры и установите значение Хост, соответствующее контейнеру службы mysql​​​. Нажмите Сохранить и продолжить.

Страница настройки базы данных в веб-интерфейсе Drupal

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

Страница установки сайта в веб-интерфейсе Drupal

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

Страница настройки сайта в веб-интерфейсе Drupal

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

Страница Добро пожаловать в Drupal в веб-интерфейсе Drupal

Теперь после завершения установки Drupal необходимо убедиться, что наши сертификаты SSL будут обновляться автоматически.

Шаг 7 — Обновление сертификатов

Сертификаты Let’s Encrypt действительны в течение 90 дней, поэтому нам нужно будет настроить процесс автоматического обновления, чтобы гарантировать, что сертификаты не окажутся просроченными. Один из способов — создание задания с помощью утилиты планирования cron. В нашем случае мы настроим задание для cron с помощью скрипта, который будет обновлять наши сертификаты и перезагружать конфигурацию Nginx.

Давайте создадим файл ssl_renew.sh для обновления наших сертификатов:

  • nano ssl_renew.sh

Добавьте следующий код. Не забудьте заменить имя директории на своего собственного пользователя без прав root:

~/drupal/ssl_renew.sh

#!/bin/bash

cd /home/sammy/drupal/
/usr/local/bin/docker-compose -f docker-compose.yml run certbot renew --dry-run && \
/usr/local/bin/docker-compose -f docker-compose.yml kill -s SIGHUP webserver

Этот скрипт меняет директорию проекта ~/drupal и запускает следующие команды docker-compose.

  • docker-compose run: данный параметр запускает контейнер certbot и переопределяет параметр command, указанный в определении службы certbot. Вместо использования субкоманды certonly мы используем здесь субкоманду renew, которая будет обновлять сертификаты, срок действия которых близок к окончанию. Мы включили параметр --dry-run, чтобы протестировать наш скрипт.

  • docker-compose kill: данный параметр отправляет сигнал SIGHUP контейнеру webserver для перезагрузки конфигурации Nginx.

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

  • sudo chmod +x ssl_renew.sh

Далее откройте root-файл crontab для запуска скрипта обновления с заданным интервалом:

  • sudo crontab -e

Если вы впервые редактируете файл, вас попросят выбрать текстовый редактор, чтобы открыть файл с его помощью:

Output
no crontab for root - using an empty one Select an editor. To change later, run 'select-editor'. 1. /bin/nano 2. /usr/bin/vim.basic 3. /usr/bin/vim.tiny 4. /bin/ed Choose 1-4 [1]: ...

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

crontab
...
*/5 * * * * /home/sammy/drupal/ssl_renew.sh >> /var/log/cron.log 2>&1

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

Через пять минут воспользуйтесь командой tail для проверки cron.log, чтобы увидеть, выполнен ли запрос обновления:

  • tail -f /var/log/cron.log

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

Output
** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/your_domain/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Нажмите CTRL+C, чтобы завершить процесс tail.

Теперь мы можем изменить файл crontab для запуска скрипта каждый 2-й день недели в 2:00. Измените последнюю строку crontab на следующее:

crontab
...
* 2 * * 2 /home/sammy/drupal/ssl_renew.sh >> /var/log/cron.log 2>&1

Закройте и сохраните файл.

Теперь удалим параметр --dry-run из скрипта ssl_renew.sh. Сначала откройте его:

  • nano ssl_renew.sh

Затем измените содержимое на следующее:

~/drupal/ssl_renew.sh
#!/bin/bash

cd /home/sammy/drupal/
/usr/local/bin/docker-compose -f docker-compose.yml run certbot renew && \
/usr/local/bin/docker-compose -f docker-compose.yml kill -s SIGHUP webserver

Теперь наше задание cron будет выполнять обновление сертификатов SSL.

Заключение

В этом руководстве мы использовали Docker Compose для создания установки Drupal с веб-сервером Nginx. В рамках этого рабочего процесса мы получили сертификаты TLS/SSL для домена, который мы хотели ассоциировать с сайтом Drupal, и создали задание cron для обновления этих сертификатов при необходимости.

Для получения дополнительной информации посетите тематическую страницу Docker.

Creative Commons License