Имеется WordPress в Azure App Service как WebApp.
Приложение развёрнуто на Dev окружении, задача — задеплоить его на Stage, который является swap-слотом для Prod, переместить Stage на Prod, после чего — внести изменения на Dev, которые затронут базу — и внести эти изменения в базу Stage, что бы их можно было выкатить на Prod.
Используем dbForge Studio for MySQL. Есть Trial, и требуется полная Professional версия, т.к. Express не имеет необходимых возможностей (сравнение и объединение схем и данных в базах MySQL).
- Подготовка — деплой кода
- Миграция схем баз с помощью dbForge
- Синхронизация данных в таблицах баз MySQL
Содержание
Подготовка — деплой кода
Код приложения деплоится через Travis, с помощью скрипта, через Git-деплой, аналог вот такого>>> решения. Версия для Dev окружения — в develop
бранче, для Prod — в бранче release
.
Файл .travis.yml
выглядит так:
$ cat .travis.yml language: php install: - true branches: only: - develop - release before_script: - git config --global user.email "[email protected]" - git config --global user.name "Travis-CI" script: - if [ $TRAVIS_BRANCH = "develop" ]; then ./ci/deploy.sh jm-wp-dev; fi - if [ $TRAVIS_BRANCH = "release" ]; then ./ci/deploy.sh jm-wp-prod-staging jm-wp-prod; fi
Содержимое репозитория:
$ ls -l total 204 drwxrwxr-x 2 setevoy setevoy 4096 вер 22 17:02 ci -rw-rw-r-- 1 setevoy setevoy 693 вер 22 17:03 getdatabase.php -rw-rw-r-- 1 setevoy setevoy 418 вер 22 17:02 index.php -rw-rw-r-- 1 setevoy setevoy 169 вер 22 17:03 info.php -rw-rw-r-- 1 setevoy setevoy 19550 вер 22 17:03 license.txt -rw-rw-r-- 1 setevoy setevoy 7344 вер 22 17:03 readme.html -rw-rw-r-- 1 setevoy setevoy 692 вер 22 17:03 web.config ... -rw-rw-r-- 1 setevoy setevoy 29890 вер 22 17:03 wp-signup.php -rw-rw-r-- 1 setevoy setevoy 4035 вер 22 17:02 wp-trackback.php -rw-rw-r-- 1 setevoy setevoy 3064 вер 22 17:03 xmlrpc.php
Текущий бранч:
$ git branch * develop
Готовим мердж.
Обновляем бранчи:
$ git pull Already up-to-date.
$ git checkout release && git pull Switched to branch 'release' Your branch is up-to-date with 'origin/release'. Already up-to-date.
Мерджим develop
в release
:
$ git merge develop Auto-merging wp-config.php CONFLICT (add/add): Merge conflict in wp-config.php Automatic merge failed; fix conflicts and then commit the result.
Решаем конфликт (см. тут>>>):
Первым идут старые настройки подключения, до попыток использования MySQL In App, ниже — новые настройки.
Коммитим:
$ git add wp-config.php && git commit -m "Old connections" [release 10166a4] Old connections
Пушим:
$ git push Counting objects: 7960, done. Delta compression using up to 4 threads. ...
Ждём билда:
Тем временем — обновляем параметры подключения.
Раньше для Prod и Stage использовались базы ClearDB, которые показали свою несостоятельность для production-решений (раз>>>, два>>>), поэтому был насетаплен свой бокс с MySQL.
Переходим в WebApp > Application Settins > Connections Strings и обновляем строку:
Устанавливаем её в новое значение:
Database=jm_huber_stage;Data Source=13.**.***.102;User Id=jm_huber_stage;Password=password
И ставим галочку Slot Setting (для Prod будет своя Connection String)
Так как Azure любит удалять Connection Strings из настроек — можно добавить скрипт (только для WordPress на Azure из-за использования переменной %MYSQLCONNSTR_localdb%
для получения данных для подключения) для проверки — какая база данных используется в настоящий момент:
<?php $connectstr_dbhost = ''; $connectstr_dbname = ''; $connectstr_dbusername = ''; $connectstr_dbpassword = ''; foreach ($_SERVER as $key => $value) { if (strpos($key, "MYSQLCONNSTR_") !== 0) { continue; } $connectstr_dbhost = preg_replace("/^.*Data Source=(.+?);.*$/", "\\1", $value); $connectstr_dbname = preg_replace("/^.*Database=(.+?);.*$/", "\\1", $value); $connectstr_dbusername = preg_replace("/^.*User Id=(.+?);.*$/", "\\1", $value); $connectstr_dbpassword = preg_replace("/^.*Password=(.+?)$/", "\\1", $value); } print("DB host: $connectstr_dbhost\n"); print("DB database: $connectstr_dbname\n"); print("DB user: $connectstr_dbusername\n"); ?>
Проверяем:
$ curl http://jm-wp-prod-staging.azurewebsites.net/getdatabase.php DB host: 13.***.***.102 DB database: jm_huber_stage DB user: jm_huber_stage
Повторяем для Prod.
Переходим к базам.
Миграция баз с помощью dbForge
Дальше начинается жесткая «мануальщина», но это лучшее, что удалось придумать за имевшееся время.
Если у кого-то есть опыт в решении подобных задач (деплой с апдейтами баз) — прошу в комменты.
Проверяем текущее состояние Stage базы:
mysql> use jm_huber_stage; Database changed mysql> show tables; Empty set (0.00 sec)
Качаем и устанавливаем dbForge (русс. тут>>>). Увы — только Windows версия, придётся через RDP (под wine
не завелось).
Запускаем:
Создаём два подключения — к Dev и Stage базам:
Первым — надо скопировать схему таблиц из Dev базы в Stage базу, т.к. Stage и Prod базы — пустые.
Выделяем оба подключения, правой кнопкой — New Schema Comparison:
В выпадающих списках Connection — выбираем «откуда» (Source, слева) и «куда» (Target, справа).
Выбираем из Dev — в Stage:
Жмём Compare.
dbForge выведет таблицу сравнения схем обеих баз, и запросы, которые будут выполнены при синхронизации:
Если всё ОК — сверху жмём зеленую стрелку «Synchronize objects to target databse«, выбираем «Execute script directly…» и внизу жмём кнопку Synchronize:
Ещё раз выполняем сравнение — базы одинаковы:
Проверяем:
mysql> show tables; +-------------------------------+ | Tables_in_jm_huber_stage | +-------------------------------+ | wp_bro_images | | wp_cf7dbplugin_st | | wp_cf7dbplugin_submits | | wp_commentmeta | | wp_comments | | wp_ctimelines | | wp_layerslider | | wp_links | | wp_options | | wp_postmeta | | wp_posts | | wp_revslider_css | | wp_revslider_layer_animations | | wp_revslider_navigations | | wp_revslider_sliders | | wp_revslider_slides | | wp_revslider_static_slides | | wp_term_relationships | | wp_term_taxonomy | | wp_termmeta | | wp_terms | | wp_usermeta | | wp_users | +-------------------------------+
Синхронизация данных в таблицах баз MySQL
Данных в Stage базе сейчас нет:
mysql> select * from wp_users; Empty set (0.00 sec)
Возвращаемся к dbForge, выбираем обе базы — и New Data Comparison:
Не забываем поменять местами Dev и Stage базы:
Тут результаты куда интереснее:
wp-options
и пару других таблиц мы потом обновим руками, для URL-ов, в остальном же — сейчас можно синхронизировать всё сразу:
Снова жмём Большую Зелёную Кнопку:
Готово:
Проверяем:
mysql> select * from wp_users; +----+---------------+------------------------------------+---------------+---------------------------------+----------+---------------------+-----------------------------------------------+-------------+---------------+ | ID | user_login | user_pass | user_nicename | user_email | user_url | user_registered | user_activation_key | user_status | display_name | +----+---------------+------------------------------------+---------------+---------------------------------+----------+---------------------+-----------------------------------------------+-------------+---------------+ | 1 | useradmin | $P$B2s9Hnxe.*** . | useradmin | [email protected] | | 2016-06-27 11:45:24 | | 0 | useradmin | ... +----+---------------+------------------------------------+---------------+---------------------------------+----------+---------------------+-----------------------------------------------+-------------+---------------+
Руками — обновляем URL-ы в базе — в будущем эти поля синхронизироваться не будут:
mysql> UPDATE wp_options SET option_value = replace(option_value, 'http://jm-wp-dev.azurewebsites.net', 'http://jm-wp-prod-staging.azurewebsites.net') WHERE option_name = 'home' OR option_name = 'siteurl'; ... mysql> UPDATE wp_posts SET guid = replace(guid, 'http://jm-wp-dev.azurewebsites.net','http://jm-wp-prod-staging.azurewebsites.net'); Query OK, 402 rows affected (0.01 sec) ... mysql> UPDATE wp_posts SET post_content = replace(post_content, 'http://jm-wp-dev.azurewebsites.net', 'http://jm-wp-prod-staging.azurewebsites.net'); Query OK, 5 rows affected (0.01 sec) ... mysql> UPDATE wp_postmeta SET meta_value = replace(meta_value,'http://jm-wp-dev.azurewebsites.net','http://jm-wp-prod-staging.azurewebsites.net'); Query OK, 9 rows affected (0.02 sec) ...
Проверяем:
Следующий шаг — свапнуть (про swap-слоты — см. тут>>>) Stage на Prod.
Для Prod — задаём его Connection Strings:
Не забываем про галочку Slot Setting, жмём Save, свапаем слоты:
Возвращаемся к dbForge, добавляем Prod базу (правой кнопкой на Stage соединениии — Dubplicate connection, потом Modify):
И повторяем то же, что делали с Dev/Stage — сначала копируем схему базы, потом — данные, только сейчас выбираем Stage и Prod базы (хотя на данный момент — без разницы, в качестве Source можно использовать и Dev базу, но в будущем это будет играть роль):
Повторяем для данных:
Сейчас не выбираем никакие таблицы для синхронизации, т.к. базы новые.
Далее — сделаем апдейт на Dev — и задеплоим его на Stage.
Повторяем обновление URL для Prod:
mysql> use jm_huber_prod; Database changed mysql> UPDATE wp_options SET option_value = replace(option_value, 'http://jm-wp-prod-staging.azurewebsites.net', 'http://jm-wp-prod.azurewebsites.net') WHERE option_name = 'home' OR option_name = 'siteurl'; ... mysql> UPDATE wp_posts SET guid = replace(guid, 'http://jm-wp-prod-staging.azurewebsites.net','http://jm-wp-prod.azurewebsites.net'); Query OK, 402 rows affected (0.01 sec) ... mysql> UPDATE wp_posts SET post_content = replace(post_content, 'http://jm-wp-prod-staging.azurewebsites.net', 'http://jm-wp-prod.azurewebsites.net'); ... mysql> UPDATE wp_postmeta SET meta_value = replace(meta_value,'http://jm-wp-prod-staging.azurewebsites.net','http://jm-wp-prod.azurewebsites.net'); Query OK, 9 rows affected (0.01 sec) ...
Проверяем:
Отлично.
А теперь — смоделируем деплой.
На Dev — добаляем пост:
Вовращаемся к dbForge, и выполняем сравнение данных между Dev и Stage (сначала деплоим на стейдж, потом свапаем на прод):
Теперь картина уже куда интереснее:
Во-первых — обращаем внимание на таблицу wp_options
и её поля siteurl
и home
— они должны различаться, т.к. URL-лы разные:
Снимаем с wp_options
галочку для синхронизации.
Во-вторых — в wp_posts
всё посты различаются. По той же причине:
Снимаем отметку с Different records.
Нас интересует пост с ID 3992, во влакдке Only in Source:
Оставляем отметку только для него:
Жмём Next:
Передеплоиваем при необходимости код (Stage сейчас — это бывший Prod, на котором старый код голого WordPress).
Синхронизируем, проверяем:
Готово.