Введение

WordPress — бесплатная система управления контентом (CMS) с открытым исходным кодом, которая опирается на базу данных MySQL и обрабатывает запросы с помощью PHP. Благодаря огромному количеству плагинов и системе шаблонов, а также тому факту, что большая часть административных функций может производиться через веб-интерфейс, WordPress завоевала популярность среди создателей самых разных сайтов, от блогов и страниц с описанием продукта и до сайтов электронной торговли.

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

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

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

wordpress illustration for: Предварительные требования

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

  • Зарегистрированное доменное имя. В этом обучающем руководстве мы будем использовать example.com. Вы можете получить бесплатный домен на Freenom или зарегистрировать доменное имя по вашему выбору.
  • На вашем сервере должны быть настроены обе нижеследующие записи DNS. Вы можете воспользоваться введением в работу с DNS hosting, чтобы получить подробную информацию о добавлении доменов в учетную запись the cloud provider, если вы используете этот способ:
  • Запись A, где <^>example.com<^> указывает на публичный IP-адрес вашего сервера.
  • Запись A, где www.<^>example.com<^> указывает на публичный IP-адрес вашего сервера.

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

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

Во-первых, создайте директорию проекта для настройки WordPress с именем <^>wordpress<^> и перейдите в эту директорию:

				
					
mkdir &lt;^&gt;wordpress&lt;^&gt; &amp;&amp; cd &lt;^&gt;wordpress&lt;^&gt;

				
			

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

				
					
mkdir nginx-conf

				
			

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

				
					
nano nginx-conf/nginx.conf

				
			

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

Добавьте в файл следующий код. Обязательно замените <^>example.com<^> на ваше доменное имя.

				
					
[label ~/wordpress/nginx-conf/nginx.conf]

server {

        listen 80;

        listen [::]:80;



        server_name &lt;^&gt;example.com&lt;^&gt; www.&lt;^&gt;example.com&lt;^&gt;;



        index &lt;^&gt;index.php&lt;^&gt; 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;

        }



        location ~ \.php$ {

                try_files $uri =404;

                fastcgi_split_path_info ^(.+\.php)(/.+)$;

                fastcgi_pass &lt;^&gt;wordpress&lt;^&gt;: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: этот элемент определяет имя вашего сервера и серверный блок, которые должны использоваться для запросов к вашему серверу. Обязательно замените <^>example.com<^> в этой строке на ваше собственное доменное имя.
  • index: директива index определяет файлы, которые будут использоваться в качестве индексов при обработке запросов к вашему серверу. Здесь мы изменили порядок приоритета по умолчанию, поставив index.php перед index.html, в результате чего Nginx будет давать приоритет файлам с именем index.php при наличии возможности.
  • root: наша директива root назначает имя корневой директории для запросов к нашему серверу. Эта директория, /var/www/html, создается в качестве точки монтирования в момент сборки с помощью инструкций в Dockerfile WordPress. Эти инструкции Dockerfile также гарантируют, что файлы релиза WordPress монтируются в этот том.

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

  • location ~ /.well-known/acme-challenge: этот блок расположения будет обрабатывать запросы в директории .well-known, где Certbot будет размещать временный файл для подтверждения того, что DNS для нашего домена будет работать с нашим сервером. Настроив данную конфигурацию, мы сможем использовать плагин webroot Certbot для получения сертификатов для нашего домена.
  • location /: в этом блоке расположения мы будем использовать директиву try_files для проверки файлов, соответствующих отдельным запросам URI. Вместо того, чтобы возвращать по умолчанию статус 404 не найдено, мы будем передавать контроль файлу index.php WordPress с аргументами запроса.
  • location ~.php$: этот блок расположения будет обрабатывать PHP-запросы и проксировать эти запросы в наш контейнер wordpress. Поскольку наш образ WordPress Docker будет опираться на образ php:fpm, мы также добавим параметры конфигурации, принадлежащие протоколу FastCGI, в этот блок. Nginx требует наличия независимого процессора PHP для запросов PHP: в нашем случае эти запросы будут обрабатываться процессором php-fpm, который будет включать образ php:fpm. Кроме того, этот блок расположения содержит директивы FastCGI, переменные и опции, которые будут проксировать запросы для приложения WordPress, запущенного в нашем контейнере wordpress, задавать предпочитаемый индекс захваченного 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 и алгоритмы выбора блоков расположения.

Сохраните и закройте файл после завершения редактирования. Если вы используете nano, нажмите CTRL+X, Y, затем ENTER.

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

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

Контейнеры базы данных и приложения WordPress должны получить доступ к определенным переменным среды в момент выполнения для сохранения данных приложения и предоставления доступа к этим данным для вашего приложения. Эти переменные включают чувствительные и нечувствительные данные: к чувствительным данным относятся root-пароль MySQL и пароль и пользователь базы данных приложения, а к нечувствительным данным относится информация об имени и хосте базы данных приложения.

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

В главной директории проекта ~/<^>wordpress<^>, откройте файл с именем .env:

				
					
nano .env

				
			

Конфиденциальные значения, которые мы зададим в этом файле, включают пароль для нашего root-пользователя MySQL, имя пользователя и пароль, которые WordPress будет использовать для доступа к базе данных.

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

				
					
[label ~/wordpress/.env]

MYSQL_ROOT_PASSWORD=&lt;^&gt;your_root_password&lt;^&gt;

MYSQL_USER=&lt;^&gt;your_wordpress_database_user&lt;^&gt;

MYSQL_PASSWORD=&lt;^&gt;your_wordpress_database_password&lt;^&gt;

				
			

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

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

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

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

				
					
git init

				
			

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

				
					
nano .gitignore

				
			

Добавьте .env в файл:

				
					
[label ~/wordpress/.gitignore]

.env

				
			

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

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

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

				
					
nano .dockerignore

				
			

Добавьте .env в файл:

				
					
[label ~/wordpress/.dockerignore]

.env

				
			

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

				
					
[label ~/wordpress/.dockerignore]

.env

.git

docker-compose.yml

.dockerignore

				
			

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

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

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

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

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

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

				
					
nano docker-compose.yml

				
			

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

				
					
[label ~/wordpress/docker-compose.yml]

version: '3'



services:

  db:

    image: mysql:&lt;^&gt;8.0&lt;^&gt;

    container_name: db

    restart: unless-stopped

    env_file: .env

    environment:

      - MYSQL_DATABASE=&lt;^&gt;wordpress&lt;^&gt;

    volumes:

      - dbdata:/var/lib/mysql

    command: '--default-authentication-plugin=mysql_native_password'

    networks:

      - app-network

				
			

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

  • image: данный элемент указывает Compose, какой образ будет загружаться для создания контейнера. Мы закрепим здесь образ mysql:<^>8.0<^>, чтобы избежать будущих конфликтов, так как образ mysql:latest продолжит обновляться. Дополнительную информацию о закреплении версии и предотвращении конфликтов зависимостей см. в документации Docker в разделе Рекомендации по работе с Dockerfile.
  • container_name: данный элемент указывает имя контейнера.
  • restart: данный параметр определяет политику перезапуска контейнера. По умолчанию установлено значение no, но мы задали значение, согласно которому контейнер будет перезапускаться, пока не будет остановлен вручную.
  • env_file: этот параметр указывает Compose, что мы хотим добавить переменные среды из файла с именем .env, расположенного в контексте сборки. В этом случае в качестве контекста сборки используется наша текущая директория.
  • environment: этот параметр позволяет добавить дополнительные переменные среды, не определенные в файле .env. Мы настроим переменную MYSQL_DATABASE со значением <^>wordpress<^>, которая будет предоставлять имя нашей базы данных приложения. Поскольку эта информация не является чувствительной, мы можем включить ее напрямую в файл docker-compose.yml.
  • volumes: здесь мы монтируем именованный том с названием dbdata в директорию /var/lib/mysql в контейнере. Это стандартная директория данных в большинстве дистрибутивов.
  • command: данный параметр указывает команду, которая будет переопределять используемое по умолчанию значение инструкции CMD для образа. В нашем случае мы добавим параметр для стандартной команды mysqld образа Docker, которая запускает сервер MySQL в контейнере. Эта опция --default-authentication-plugin=mysql_native_password устанавливает для системной переменной --default-authentication-plugin значение mysql_native_password, которое указывает, какой механизм аутентификации должен управлять новыми запросами аутентификации для сервера. Поскольку PHP и наш образ WordPress не будут поддерживать новое значение аутентификации MySQL по умолчанию, мы должны внести изменения, чтобы выполнить аутентификацию пользователя базы данных приложения.
  • networks: данный параметр указывает, что служба приложения будет подключаться к сети app-network, которую мы определим внизу файла.

Затем под определением службы db добавьте определение для вашей службы приложения wordpress:

				
					
[label ~/wordpress/docker-compose.yml]

...

  wordpress:

    depends_on:

      - db

    image: wordpress:&lt;^&gt;5.1.1&lt;^&gt;-fpm-alpine

    container_name: wordpress

    restart: unless-stopped

    env_file: .env

    environment:

      - WORDPRESS_DB_HOST=db:3306

      - WORDPRESS_DB_USER=$MYSQL_USER

      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD

      - WORDPRESS_DB_NAME=&lt;^&gt;wordpress&lt;^&gt;

    volumes:

      - wordpress:/var/www/html

    networks:

      - app-network

				
			

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

  • depends_on: этот параметр гарантирует, что наши контейнеры будут запускаться в порядке зависимости, и контейнер wordpress запускается после контейнера db. Наше приложение WordPress зависит от наличия базы данных приложения и пользователя, поэтому установка такого порядка зависимостей позволит выполнять запуск приложения корректно.
  • image: для этой настройки мы будем использовать образ WordPress <^>5.11<^>-fpm-alpine. Как было показано в шаге 1, использование этого образа гарантирует, что наше приложение будет иметь процессор php-fpm, который требуется Nginx для обработки PHP. Это еще и образ alpine, полученный из проекта Alpine Linux, который поможет снизить общий размер образа. Дополнительную информацию о преимуществах и недостатках использования образов alpine, а также о том, имеет ли это смысл в случае вашего приложения, см. в полном описании в разделе Варианты образа на странице образа WordPress на Docker Hub.
  • env_file: и снова мы укажем, что хотим загрузить значения из файла .env, поскольку там мы определили пользователя базы данных приложения и пароль.
  • environment: здесь мы будем использовать значения, определенные в файле .env, но мы привяжем их к именам переменных, которые требуются для образа WordPress: WORDPRESS_DB_USER и WORDPRESS_DB_PASSWORD. Также мы определяем значение WORDPRESS_DB_HOST, которое будет указывать сервер MySQL, который будет работать в контейнере db, доступный на используемом по умолчанию порту MySQL 3306. Наше значение WORDPRESS_DB_NAME будет тем же, которое мы указали при определении службы MySQL для MYSQL_DATABASE: <^>wordpress<^>.
  • volumes: мы монтируем том с именем wordpress на точку монтирования /var/www/html, созданную образом WordPress. Использование тома с именем таким образом позволит разделить наш код приложения с другими контейнерами.
  • networks: мы добавляем контейнер wordpress в сеть app-network.

Далее под определением службы приложения wordpress добавьте следующее определение для службы Nginx webserver:

				
					
[label ~/wordpress/docker-compose.yml]

...

  webserver:

    depends_on:

      - wordpress

    image: nginx:&lt;^&gt;1.15.12&lt;^&gt;-alpine

    container_name: webserver

    restart: unless-stopped

    ports:

      - "80:80"

    volumes:

      - wordpress:/var/www/html

      - ./nginx-conf:/etc/nginx/conf.d

      - certbot-etc:/etc/letsencrypt

    networks:

      - app-network

				
			

Мы снова присвоим имя нашему контейнеру и сделаем его зависимым от контейнера wordpress в отношении порядка запуска. Также мы используем образ alpineобраз Nginx <^>1.15.12<^>-alpine.

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

  • ports: этот параметр открывает порт 80, чтобы активировать параметры конфигурации, определенные нами в файле nginx.conf в [шаге 1]().
  • wordpress:/var/www/html: этот параметр будет монтировать код нашего приложения WordPress в директорию /var/www/html, директорию, которую мы задали в качестве root-директории в нашем серверном блоке Nginx.
  • ./nginx-conf:/etc/nginx/conf.d: этот элемент будет монтировать директорию конфигурации Nginx на хост в соответствующую директорию в контейнере, гарантируя, что любые изменения, которые мы вносим в файлы на хосте, будут отражены в контейнере.
  • certbot-etc:/etc/letsencrypt: этот элемент будет монтировать соответствующие сертификаты и ключи Let's Encrypt для нашего домена в соответствующую директорию контейнера.

Здесь мы снова добавили этот контейнер в сеть app-network.

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

				
					
[label ~/wordpress/docker-compose.yml]

  certbot:

    depends_on:

      - webserver

    image: certbot/certbot

    container_name: certbot

    volumes:

      - certbot-etc:/etc/letsencrypt

      - wordpress:/var/www/html

    command: certonly --webroot --webroot-path=/var/www/html --email &lt;^&gt;sammy@example.com&lt;^&gt; --agree-tos --no-eff-email --staging -d &lt;^&gt;example.com&lt;^&gt; -d www.&lt;^&gt;example.com&lt;^&gt;

				
			

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

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

Также мы включили параметр command, указывающий субкоманду для запуска с используемой контейнером по умолчанию командой certbot. Субкоманда certonly будет получать сертификат со следующими параметрами:

  • --webroot: данный элемент говорит Certbot о необходимости использования плагина webroot для размещения файлов в папке webroot для аутентификации. Работа плагина основана на методе валидации HTTP-01, который использует запрос HTTP, чтобы доказать, что Certbot может получить доступ к ресурсам с сервера, который отвечает на заданное доменное имя.
  • --webroot-path: данный элемент указывает путь директории webroot.
  • --email: предпочитаемый адрес электронной почты для регистрации и восстановления.
  • --no-eff-email: данный элемент указывает Certbot, что вы не хотите делиться адресом электронной почты с Electronic Frontier Foundation​​​ (EFF). Вы можете пропустить этот элемент.
  • --staging: данный элемент говорит Certbot, что вы хотите использовать промежуточную среду Let's Encrypt для получения тестовых сертификатов. При использовании этого параметра вы можете протестировать параметры конфигурации и избежать возможных пределов для запросов домена. Дополнительную информацию об этих предельных значениях см. в документации по ограничениям скорости Let's Encrypt.
  • -d: данный элемент позволяет указать доменные имена, которые вы хотите использовать для вашего запроса. В нашем случае мы включили <^>example.com<^> и www.<^>example.com<^>. Обязательно замените эти данные на имя вашего домена.

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

				
					
[label ~/wordpress/docker-compose.yml]

...

volumes:

  certbot-etc:

  wordpress:

  dbdata:



networks:

  app-network:

    driver: bridge  

				
			

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

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

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

				
					
[label ~/wordpress/docker-compose.yml]

version: '3'



services:

  db:

    image: mysql:&lt;^&gt;8.0&lt;^&gt;

    container_name: db

    restart: unless-stopped

    env_file: .env

    environment:

      - MYSQL_DATABASE=&lt;^&gt;wordpress&lt;^&gt;

    volumes:

      - dbdata:/var/lib/mysql

    command: '--default-authentication-plugin=mysql_native_password'

    networks:

      - app-network



  wordpress:

    depends_on:

      - db

    image: wordpress:&lt;^&gt;5.1.1&lt;^&gt;-fpm-alpine

    container_name: wordpress

    restart: unless-stopped

    env_file: .env

    environment:

      - WORDPRESS_DB_HOST=db:3306

      - WORDPRESS_DB_USER=$MYSQL_USER

      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD

      - WORDPRESS_DB_NAME=&lt;^&gt;wordpress&lt;^&gt;

    volumes:

      - wordpress:/var/www/html

    networks:

      - app-network



  webserver:

    depends_on:

      - wordpress

    image: nginx:&lt;^&gt;1.15.12&lt;^&gt;-alpine

    container_name: webserver

    restart: unless-stopped

    ports:

      - "80:80"

    volumes:

      - wordpress:/var/www/html

      - ./nginx-conf:/etc/nginx/conf.d

      - certbot-etc:/etc/letsencrypt

    networks:

      - app-network



  certbot:

    depends_on:

      - webserver

    image: certbot/certbot

    container_name: certbot

    volumes:

      - certbot-etc:/etc/letsencrypt

      - wordpress:/var/www/html

    command: certonly --webroot --webroot-path=/var/www/html --email &lt;^&gt;sammy@example.com&lt;^&gt; --agree-tos --no-eff-email --staging -d &lt;^&gt;example.com&lt;^&gt; -d www.&lt;^&gt;example.com&lt;^&gt;



volumes:

  certbot-etc:

  wordpress:

  dbdata:



networks:

  app-network:

    driver: bridge  

				
			

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

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

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

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

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

				
					
docker-compose up -d

				
			

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

				
					
[secondary_label Output]

Creating db ... done

Creating wordpress ... done

Creating webserver ... done

Creating certbot   ... done

				
			

С помощью docker-compose ps проверьте статус ваших служб:

				
					
docker-compose ps

				
			

Если все будет выполнено успешно, ваши службы db, wordpress и webserver должны иметь статус Up, а работа контейнера certbot будет завершена с сообщением о статусе 0:

				
					
[secondary_label Output]

  Name                 Command               State           Ports       

-------------------------------------------------------------------------

certbot     certbot certonly --webroot ...   Exit 0                      

db          docker-entrypoint.sh --def ...   Up       3306/tcp, 33060/tcp

webserver   nginx -g daemon off;             Up       0.0.0.0:80-&gt;80/tcp

wordpress   docker-entrypoint.sh php-fpm     Up       9000/tcp           

				
			

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

				
					
docker-compose logs &lt;^&gt;service_name&lt;^&gt;

				
			

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

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

				
			

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

				
					
[secondary_label Output]

total 16

drwx------    3 root     root          4096 May 10 15:45 .

drwxr-xr-x    9 root     root          4096 May 10 15:45 ..

-rw-r--r--    1 root     root           740 May 10 15:45 README

drwxr-xr-x    2 root     root          4096 May 10 15:45 &lt;^&gt;example.com&lt;^&gt;

				
			

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

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

				
					
nano docker-compose.yml

				
			

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

				
					
[label ~/wordpress/docker-compose.yml]

...

  certbot:

    depends_on:

      - webserver

    image: certbot/certbot

    container_name: certbot

    volumes:

      - certbot-etc:/etc/letsencrypt

      - certbot-var:/var/lib/letsencrypt

      - wordpress:/var/www/html

    command: certonly --webroot --webroot-path=/var/www/html --email &lt;^&gt;sammy@example.com&lt;^&gt; --agree-tos --no-eff-email &lt;^&gt;--force-renewal&lt;^&gt; -d &lt;^&gt;example.com&lt;^&gt; -d www.&lt;^&gt;example.com&lt;^&gt;

...

				
			

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

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

				
			

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

				
					
[secondary_label 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 &lt;^&gt;example.com&lt;^&gt;

certbot      | http-01 challenge for www.&lt;^&gt;example.com&lt;^&gt;

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/&lt;^&gt;example.com&lt;^&gt;/fullchain.pem

certbot      |    Your key file has been saved at:

certbot      |    /etc/letsencrypt/live/&lt;^&gt;example.com&lt;^&gt;/privkey.pem

certbot      |    Your cert will expire on 2019-08-08. 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

				
			

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

				
					
curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf

				
			

Данная команда будет сохранять эти параметры в файле с именем options-ssl-nginx.conf, расположенном в директории nginx-conf.

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

				
					
rm nginx-conf/nginx.conf

				
			

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

				
					
nano nginx-conf/nginx.conf

				
			

Добавьте следующий код в файл для перенаправления HTTP на HTTPS и добавления учетных данных, протоколов и заголовков безопасности SSL. Обязательно замените <^>example.com<^>​​ на ваше доменное имя:

				
					
[label ~/wordpress/nginx-conf/nginx.conf]

server {

        listen 80;

        listen [::]:80;



        server_name &lt;^&gt;example.com&lt;^&gt; www.&lt;^&gt;example.com&lt;^&gt;;



        location ~ /.well-known/acme-challenge {

                allow all;

                root /var/www/html;

        }



        location / {

                rewrite ^ https://$host$request_uri? permanent;

        }

}



server {

        listen 443 ssl http2;

        listen [::]:443 ssl http2;

        server_name &lt;^&gt;example.com&lt;^&gt; www.&lt;^&gt;example.com&lt;^&gt;;



        index index.php index.html index.htm;



        root /var/www/html;



        server_tokens off;



        ssl_certificate /etc/letsencrypt/live/&lt;^&gt;example.com&lt;^&gt;/fullchain.pem;

        ssl_certificate_key /etc/letsencrypt/live/&lt;^&gt;example.com&lt;^&gt;/privkey.pem;



        include /etc/nginx/conf.d/options-ssl-nginx.conf;



        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;

        # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

        # enable strict transport security only if you understand the implications



        location / {

                try_files $uri $uri/ /index.php$is_args$args;

        }



        location ~ \.php$ {

                try_files $uri =404;

                fastcgi_split_path_info ^(.+\.php)(/.+)$;

                fastcgi_pass &lt;^&gt;wordpress&lt;^&gt;: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 и ключа, а также рекомендованные параметры безопасности Certbot, которые мы сохранили в nginx-conf/options-ssl-nginx.conf.

Кроме того, мы включили ряд заголовков безопасности, которые позволят нам получить оценки A на сайтах серверных тестов SSL Labs и Security Headers. Эти заголовки включают X-Frame-Options, X-Content-Type-Options, Referrer Policy, Content-Security-Policy и X-XSS-Protection. Заголовок HTTP Strict Transport Security (Строгая безопасность передачи информации по протоколу HTTP) закомментирован, активируйте этот элемент, только если вы понимаете возможные последствия и оценили его «предварительно загруженный» функционал.

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

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

Перед повторным созданием службы webserver вам потребуется добавить распределение порта 443 в определение службы webserver.

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

				
					
nano docker-compose.yml

				
			

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

				
					
[label ~/wordpress/docker-compose.yml]

...

  webserver:

    depends_on:

      - wordpress

    image: nginx:&lt;^&gt;1.15.12&lt;^&gt;-alpine

    container_name: webserver

    restart: unless-stopped

    ports:

      - "80:80"

      - &lt;^&gt;"443:443"&lt;^&gt;

    volumes:

      - wordpress:/var/www/html

      - ./nginx-conf:/etc/nginx/conf.d

      - certbot-etc:/etc/letsencrypt

    networks:

      - app-network

				
			

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

				
					
[label ~/wordpress/docker-compose.yml]

version: '3'



services:

  db:

    image: mysql:&lt;^&gt;8.0&lt;^&gt;

    container_name: db

    restart: unless-stopped

    env_file: .env

    environment:

      - MYSQL_DATABASE=wordpress

    volumes:

      - dbdata:/var/lib/mysql

    command: '--default-authentication-plugin=mysql_native_password'

    networks:

      - app-network



  wordpress:

    depends_on:

      - db

    image: wordpress:&lt;^&gt;5.1.1&lt;^&gt;-fpm-alpine

    container_name: wordpress

    restart: unless-stopped

    env_file: .env

    environment:

      - WORDPRESS_DB_HOST=db:3306

      - WORDPRESS_DB_USER=$MYSQL_USER

      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD

      - WORDPRESS_DB_NAME=wordpress

    volumes:

      - wordpress:/var/www/html

    networks:

      - app-network



  webserver:

    depends_on:

      - wordpress

    image: nginx:&lt;^&gt;1.15.12&lt;^&gt;-alpine

    container_name: webserver

    restart: unless-stopped

    ports:

      - "80:80"

      - "443:443"

    volumes:

      - wordpress:/var/www/html

      - ./nginx-conf:/etc/nginx/conf.d

      - certbot-etc:/etc/letsencrypt

    networks:

      - app-network



  certbot:

    depends_on:

      - webserver

    image: certbot/certbot

    container_name: certbot

    volumes:

      - certbot-etc:/etc/letsencrypt

      - wordpress:/var/www/html

    command: certonly --webroot --webroot-path=/var/www/html --email &lt;^&gt;sammy@example.com&lt;^&gt; --agree-tos --no-eff-email --force-renewal -d &lt;^&gt;example.com&lt;^&gt; -d www.&lt;^&gt;example.com&lt;^&gt;



volumes:

  certbot-etc:

  wordpress:

  dbdata:



networks:

  app-network:

    driver: bridge  

				
			

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

Повторно создайте службу webserver:

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

				
			

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

				
					
docker-compose ps

				
			

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

				
					
[secondary_label Output]

  Name                 Command               State                     Ports                  

----------------------------------------------------------------------------------------------

certbot     certbot certonly --webroot ...   Exit 0                                           

db          docker-entrypoint.sh --def ...   Up       3306/tcp, 33060/tcp                     

webserver   nginx -g daemon off;             Up       0.0.0.0:443-&gt;443/tcp, 0.0.0.0:80-&gt;80/tcp

wordpress   docker-entrypoint.sh php-fpm     Up       9000/tcp    

				
			

После запуска ваших контейнеров вы можете завершить процесс установки WordPress через веб-интерфейс.

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

После запуска контейнеров мы можем завершить установку через веб-интерфейс WordPress.

В браузере перейдите на домен вашего сервера. Не забудьте заменить здесь <^>example.com<^> на ваше доменное имя:

				
					
https://&lt;^&gt;example.com&lt;^&gt;

				
			

Выберите язык, который вы хотите использовать:

После нажатия Continue (Продолжить) вы перейдете на главную страницу настройки, где вам нужно будет выбрать имя вашего сайта и пользователя. Рекомендуется выбрать запоминающееся имя пользователя (не просто «admin») и надежный пароль. Вы можете использовать пароль, который генерирует WordPress автоматически, или задать собственный пароль.

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

После нажатия Install WordPress (Установить WordPress) внизу страницы на экране появится запрос выполнения входа:

После входа вы получите доступ к панели управления WordPress:

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

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

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

Откройте скрипт под названием ssl_renew.sh:

				
					
nano ssl_renew.sh

				
			

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

				
					
[label ~/wordpress/ssl_renew.sh]

#!/bin/bash



COMPOSE="/usr/local/bin/docker-compose --no-ansi"

DOCKER="/usr/bin/docker"



cd /home/&lt;^&gt;sammy&lt;^&gt;/wordpress/

$COMPOSE run certbot renew --dry-run &amp;&amp; $COMPOSE kill -s SIGHUP webserver

$DOCKER system prune -af

				
			

Данный скрипт привязывает двоичный код docker-compose для переменной COMPOSE и задает параметр --no-ansi, который запускает команды docker-compose без управляющих символов ANSI. Затем он делает то же самое с двоичным файлом docker. В заключение он меняет директорию проекта на ~/wordpress и запускает следующие команды docker-compose:

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

После этого он выполняет команду docker system prune для удаления всех неиспользуемых контейнеров и образов.

Закройте файл после завершения редактирования. Сделайте его исполняемым:

				
					
chmod +x ssl_renew.sh

				
			

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

				
					
sudo crontab -e

				
			

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

				
					
[secondary_label Output]

no crontab for root - using an empty one



Select an editor.  To change later, run 'select-editor'.

  1. /bin/nano        &lt;---- easiest

  2. /usr/bin/vim.basic

  3. /usr/bin/vim.tiny

  4. /bin/ed



Choose 1-4 [1]:

...

				
			

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

				
					
[label crontab]

...

*/5 * * * * /home/&lt;^&gt;sammy&lt;^&gt;/wordpress/ssl_renew.sh &gt;&gt; /var/log/cron.log 2&gt;&amp;1

				
			

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

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

				
					
tail -f /var/log/cron.log

				
			

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

				
					
[secondary_label 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/&lt;^&gt;example.com&lt;^&gt;/fullchain.pem (success)

** DRY RUN: simulating 'certbot renew' close to cert expiry

**          (The test certificates above have not been saved.)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

				
			

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

				
					
[label crontab]

...

0 12 * * * /home/&lt;^&gt;sammy&lt;^&gt;/wordpress/ssl_renew.sh &gt;&gt; /var/log/cron.log 2&gt;&amp;1

				
			

Также вы можете изменить параметр --dry-run из скрипта ssl_renew.sh:

				
					
[label ~/wordpress/ssl_renew.sh]

#!/bin/bash



COMPOSE="/usr/local/bin/docker-compose --no-ansi"

DOCKER="/usr/bin/docker"



cd /home/&lt;^&gt;sammy&lt;^&gt;/wordpress/

$COMPOSE run certbot renew &amp;&amp; $COMPOSE kill -s SIGHUP webserver

$DOCKER system prune -af

				
			

Ваше задание cron гарантирует, что ваши сертификаты Let's Encrypt не окажутся устаревшими, обновляя их в случае истечения срока действия. Также вы можете настроить замену журнала с помощью утилиты Logrotate, которая будет выполнять ротацию и сжатие файлов журнала.

Заключение

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

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

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