Цель: обновить инстанс PostgreSQL, который работает на версии 9.4 до 9.5.
RDS не позволяет обновлять PostgreSQL на несколько мажорных версий, поэтому если нужно обновиться например с 9.4 до 10, то итерацию придется повторять несколько раз.
Также важно то, чтобы были доступы для rds_superuser, без этого не получится настроить репликацию.
Пример как это все работает на тестовом стенде я описал ниже.
Создаем наш RDS-инстанс:
export PGUSERNAME=admin_rds
export PGPASSWORD=admin_rds_pass
aws rds create-db-instance \
--db-instance-identifier upgrade-tests \
--allocated-storage 5 \
--db-instance-class db.t2.micro \
--engine postgres \
--master-username ${PGUSERNAME} \
--master-user-password ${PGPASSWORD} \
--engine-version 9.4.15
Заполним нашу базу тестовыми данными:
PGENDPOINT=$(aws rds describe-db-instances --db-instance-identifier upgrade-tests | grep Address | awk '{print $4}')
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} postgres -c "CREATE DATABASE sample;"
wget http://pgfoundry.org/frs/download.php/527/world-1.0.tar.gz
tar xzf world-1.0.tar.gz
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample < dbsamples-0.1/world/world.sql
Снимаем снапшот с базы:
aws rds create-db-snapshot \
--db-snapshot-identifier upgrade-tests-snapshot \
--db-instance-identifier upgrade-tests
Убеждаемся что, снапшот в статусе available:
aws rds describe-db-snapshots --db-snapshot-identifier upgrade-tests-replica-snapshot | grep Status | awk '{print $4}'
available
Создаем второй мастер, на основе ранее сделанного снапшота:
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier upgrade-tests-2 \
--db-snapshot-identifier upgrade-tests-snapshot \
--db-instance-class db.t2.micro \
--engine postgres
Делать это нужно в той же зоне, что и первый инстанс и в той же Security Group, для того чтобы не гонять трафик через интернет.
Проверим статус нашего второго инстанса:
PGENDPOINT2=$(aws rds describe-db-instances --db-instance-identifier upgrade-tests-2 | grep Address | awk '{print $4}')
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} postgres -c "select version();"
PostgreSQL 9.4.15 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9), 64-bit
Обновим версию PostgreSQL, это занимает довольно много времени:
aws rds modify-db-instance \
--db-instance-identifier upgrade-tests-2 \
--allow-major-version-upgrade \
--engine-version 9.5.12 \
--apply-immediately
После обновления проверим версию PostgreSQL:
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} postgres -c "select version();"
PostgreSQL 9.5.12 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9), 64-bit
Далее нужно настроить мастер-мастер репликацию между ними.
Тут описаны различные тулзы для этого.
Для себя я выделил:
Включаем реплику на обеих нодах:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} postgres -c "SET session_replication_role = replica; CREATE EXTENSION plperl;"
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} postgres -c "SET session_replication_role = replica; CREATE EXTENSION plperl;"
Создаем инстанс в том же регионе и той же Security Group, чтобы общаться с RDS по интранету.
На инстансе устанавливаем bucardo:
apt update && apt install bucardo postgresql-plperl-9.5 -y
sed -i 's/ENABLED=0/ENABLED=1/' /etc/default/bucardo
mkdir /var/run/bucardo
Устанавливаем пароль для юзера postgres:
sudo -u postgres psql postgres
postgres=# \password postgres
Создаем базу:
export PGPASSWORD=bucardo
psql -h localhost -U postgres postgres -c "CREATE USER bucardo WITH LOGIN SUPERUSER ENCRYPTED PASSWORD 'bucardo';"
psql -h localhost -U postgres postgres -c "CREATE DATABASE bucardo;"
bucardo install
Добавляем оба инстанса в bucardo:
export SOURCE_HOST=upgrade-tests.cge9boeglav8.eu-west-1.rds.amazonaws.com
export SOURCE_PORT=5432
export SOURCE_DATABASE=sample
export SOURCE_USERNAME=admin_rds
export SOURCE_PASSWORD=admin_rds_pass
export DEST_HOST=upgrade-tests-2.cge9boeglav8.eu-west-1.rds.amazonaws.com
export DEST_PORT=5432
export DEST_DATABASE=sample
export DEST_USERNAME=admin_rds
export DEST_PASSWORD=admin_rds_pass
bucardo add db source_db dbhost=$SOURCE_HOST dbport=$SOURCE_PORT dbname=$SOURCE_DATABASE dbuser=$SOURCE_USERNAME dbpass=$SOURCE_PASSWORD
bucardo add db dest_db dbhost=$DEST_HOST dbport=$DEST_PORT dbname=$DEST_DATABASE dbuser=$DEST_USERNAME dbpass=$DEST_PASSWORD
bucardo list database
Database: dest_db Status: active Conn: psql -p 5432 -U admin_rds -d sample -h upgrade-tests-2.cge9boeglav8.eu-west-1.rds.amazonaws.com
Database: source_db Status: active Conn: psql -p 5432 -U admin_rds -d sample -h upgrade-tests.cge9boeglav8.eu-west-1.rds.amazonaws.com
Добавляем все таблицы и последовательности в группу:
bucardo add table all --db=source_db --herd=sample_herd
Creating relgroup: sample_herd
Added table public.city to relgroup sample_herd
Added table public.country to relgroup sample_herd
Added table public.countrylanguage to relgroup sample_herd
New tables added: 3
bucardo list tables
1. Table: public.city DB: source_db PK: id (integer)
2. Table: public.country DB: source_db PK: code (bpchar)
3. Table: public.countrylanguage DB: source_db PK: countrycode|language (bpchar|text)
bucardo list herd
Relgroup: sample_herd DB: source_db Members: public.city, public.country, public.countrylanguage
bucardo add sequence all --db=source_db --herd=sample_herd
Sorry, no sequences were found
New sequences added: 0
Создаем группу репликации:
bucardo add dbgroup mydb_servers_group
bucardo add dbgroup mydb_servers_group source_db:source
bucardo add dbgroup mydb_servers_group dest_db:source
bucardo add sync mydb_sync herd=sample_herd dbs=mydb_servers_group
bucardo list sync
Sync "mydb_sync" Relgroup "sample_herd" [Active]
DB group "mydb_servers_group" dest_db:source source_db:source
После этого можно запускать bucardo:
bucardo start
Тестируем репликацию (CRUD).
Проверим версии мастеров:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} postgres -c "select version();"
PostgreSQL 9.4.15 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9), 64-bit
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} postgres -c "select version();"
PostgreSQL 9.5.12 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9), 64-bit
Insert
Пишем в мастер1, проверяем на мастер2:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "INSERT INTO city (id, name, countrycode, district, population) VALUES (4080, 'Newtown', 'UKR', 'Kiev', 100);"
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4080;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
4080 | Newtown | UKR | Kiev | 100
Пишем в мастер2, проверяем на мастер1:
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "INSERT INTO city (id, name, countrycode, district, population) VALUES (4081, 'Newcity', 'DEU', 'Berlin', 5);"
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4081;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
4081 | Newcity | DEU | Berlin | 5
Update
Обновляем на мастер1, проверяем на мастер2:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "UPDATE city SET population=101 WHERE id=4080;"
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4080;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
4080 | Newtown | UKR | Kiev | 101
Обновляем на мастер2, проверяем на мастер1:
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "UPDATE city SET district='Hamburg' WHERE id=4081;"
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4081;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
4081 | Newcity | DEU | Hamburg | 5
Delete
Удаляем на мастер1, проверяем на мастер2:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "DELETE FROM city WHERE id=4080;"
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4080;"
id | name | countrycode | district | population
----+------+-------------+----------+------------
Удаляем на мастер2, проверяем на мастер1:
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "DELETE FROM city WHERE id=4081;"
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4081;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
Все тесты пройдены успешны.
Как проверить статус реплики и оставание одного мастера от другого.
С помощью bucardo:
bucardo delta source_db
Total deltas across all targets: 0
Total deltas for database source_db: 0
bucardo delta dest_db
Total deltas across all targets: 0
Total deltas for database dest_db: 0
bucardo status mydb_sync
======================================================================
Last good : Jul 12, 2018 12:47:04 (time to run: 1s)
Rows deleted/inserted : 1 / 1
Sync name : mydb_sync
Current state : Good
Source relgroup/database : sample_herd / source_db
Tables in sync : 3
Status : Active
Check time : None
Overdue time : 00:00:00
Expired time : 00:00:00
Stayalive/Kidsalive : Yes / Yes
Rebuild index : No
Autokick : Yes
Onetimecopy : No
Post-copy analyze : Yes
Last error: :
======================================================================
С помощью SQL:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT pg_last_xlog_receive_location() AS receive, pg_last_xlog_replay_location() AS replay , COALESCE(ROUND(EXTRACT(epoch FROM now() - pg_last_xact_replay_timestamp())),0) AS seconds;"
receive | replay | seconds
---------+--------+---------
| | 0
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT pg_last_xlog_receive_location() AS receive, pg_last_xlog_replay_location() AS replay , COALESCE(ROUND(EXTRACT(epoch FROM now() - pg_last_xact_replay_timestamp())),0) AS seconds;"
receive | replay | seconds
---------+--------+---------
| | 0
Собственно говоря тут все, переключаем приложение на второй мастер, первый мастер тушим, работаем с новой версией PostgreSQL.
RDS не позволяет обновлять PostgreSQL на несколько мажорных версий, поэтому если нужно обновиться например с 9.4 до 10, то итерацию придется повторять несколько раз.
Также важно то, чтобы были доступы для rds_superuser, без этого не получится настроить репликацию.
Пример как это все работает на тестовом стенде я описал ниже.
Создаем наш RDS-инстанс:
export PGUSERNAME=admin_rds
export PGPASSWORD=admin_rds_pass
aws rds create-db-instance \
--db-instance-identifier upgrade-tests \
--allocated-storage 5 \
--db-instance-class db.t2.micro \
--engine postgres \
--master-username ${PGUSERNAME} \
--master-user-password ${PGPASSWORD} \
--engine-version 9.4.15
Заполним нашу базу тестовыми данными:
PGENDPOINT=$(aws rds describe-db-instances --db-instance-identifier upgrade-tests | grep Address | awk '{print $4}')
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} postgres -c "CREATE DATABASE sample;"
wget http://pgfoundry.org/frs/download.php/527/world-1.0.tar.gz
tar xzf world-1.0.tar.gz
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample < dbsamples-0.1/world/world.sql
Снимаем снапшот с базы:
aws rds create-db-snapshot \
--db-snapshot-identifier upgrade-tests-snapshot \
--db-instance-identifier upgrade-tests
Убеждаемся что, снапшот в статусе available:
aws rds describe-db-snapshots --db-snapshot-identifier upgrade-tests-replica-snapshot | grep Status | awk '{print $4}'
available
Создаем второй мастер, на основе ранее сделанного снапшота:
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier upgrade-tests-2 \
--db-snapshot-identifier upgrade-tests-snapshot \
--db-instance-class db.t2.micro \
--engine postgres
Делать это нужно в той же зоне, что и первый инстанс и в той же Security Group, для того чтобы не гонять трафик через интернет.
Проверим статус нашего второго инстанса:
PGENDPOINT2=$(aws rds describe-db-instances --db-instance-identifier upgrade-tests-2 | grep Address | awk '{print $4}')
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} postgres -c "select version();"
PostgreSQL 9.4.15 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9), 64-bit
Обновим версию PostgreSQL, это занимает довольно много времени:
aws rds modify-db-instance \
--db-instance-identifier upgrade-tests-2 \
--allow-major-version-upgrade \
--engine-version 9.5.12 \
--apply-immediately
После обновления проверим версию PostgreSQL:
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} postgres -c "select version();"
PostgreSQL 9.5.12 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9), 64-bit
Далее нужно настроить мастер-мастер репликацию между ними.
Тут описаны различные тулзы для этого.
Для себя я выделил:
- BDR - нужно перекомпиливать PostgreSQL с BDR-плагином
- PgCluster - тоже
- Rubyrep - проект скорее мертв чем жив
- Bucardo - похоже на правду
Включаем реплику на обеих нодах:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} postgres -c "SET session_replication_role = replica; CREATE EXTENSION plperl;"
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} postgres -c "SET session_replication_role = replica; CREATE EXTENSION plperl;"
Создаем инстанс в том же регионе и той же Security Group, чтобы общаться с RDS по интранету.
На инстансе устанавливаем bucardo:
apt update && apt install bucardo postgresql-plperl-9.5 -y
sed -i 's/ENABLED=0/ENABLED=1/' /etc/default/bucardo
mkdir /var/run/bucardo
Устанавливаем пароль для юзера postgres:
sudo -u postgres psql postgres
postgres=# \password postgres
Создаем базу:
export PGPASSWORD=bucardo
psql -h localhost -U postgres postgres -c "CREATE USER bucardo WITH LOGIN SUPERUSER ENCRYPTED PASSWORD 'bucardo';"
psql -h localhost -U postgres postgres -c "CREATE DATABASE bucardo;"
bucardo install
Добавляем оба инстанса в bucardo:
export SOURCE_HOST=upgrade-tests.cge9boeglav8.eu-west-1.rds.amazonaws.com
export SOURCE_PORT=5432
export SOURCE_DATABASE=sample
export SOURCE_USERNAME=admin_rds
export SOURCE_PASSWORD=admin_rds_pass
export DEST_HOST=upgrade-tests-2.cge9boeglav8.eu-west-1.rds.amazonaws.com
export DEST_PORT=5432
export DEST_DATABASE=sample
export DEST_USERNAME=admin_rds
export DEST_PASSWORD=admin_rds_pass
bucardo add db source_db dbhost=$SOURCE_HOST dbport=$SOURCE_PORT dbname=$SOURCE_DATABASE dbuser=$SOURCE_USERNAME dbpass=$SOURCE_PASSWORD
bucardo add db dest_db dbhost=$DEST_HOST dbport=$DEST_PORT dbname=$DEST_DATABASE dbuser=$DEST_USERNAME dbpass=$DEST_PASSWORD
bucardo list database
Database: dest_db Status: active Conn: psql -p 5432 -U admin_rds -d sample -h upgrade-tests-2.cge9boeglav8.eu-west-1.rds.amazonaws.com
Database: source_db Status: active Conn: psql -p 5432 -U admin_rds -d sample -h upgrade-tests.cge9boeglav8.eu-west-1.rds.amazonaws.com
Добавляем все таблицы и последовательности в группу:
bucardo add table all --db=source_db --herd=sample_herd
Creating relgroup: sample_herd
Added table public.city to relgroup sample_herd
Added table public.country to relgroup sample_herd
Added table public.countrylanguage to relgroup sample_herd
New tables added: 3
bucardo list tables
1. Table: public.city DB: source_db PK: id (integer)
2. Table: public.country DB: source_db PK: code (bpchar)
3. Table: public.countrylanguage DB: source_db PK: countrycode|language (bpchar|text)
bucardo list herd
Relgroup: sample_herd DB: source_db Members: public.city, public.country, public.countrylanguage
bucardo add sequence all --db=source_db --herd=sample_herd
Sorry, no sequences were found
New sequences added: 0
Создаем группу репликации:
bucardo add dbgroup mydb_servers_group
bucardo add dbgroup mydb_servers_group source_db:source
bucardo add dbgroup mydb_servers_group dest_db:source
bucardo add sync mydb_sync herd=sample_herd dbs=mydb_servers_group
bucardo list sync
Sync "mydb_sync" Relgroup "sample_herd" [Active]
DB group "mydb_servers_group" dest_db:source source_db:source
После этого можно запускать bucardo:
bucardo start
Тестируем репликацию (CRUD).
Проверим версии мастеров:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} postgres -c "select version();"
PostgreSQL 9.4.15 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9), 64-bit
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} postgres -c "select version();"
PostgreSQL 9.5.12 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9), 64-bit
Insert
Пишем в мастер1, проверяем на мастер2:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "INSERT INTO city (id, name, countrycode, district, population) VALUES (4080, 'Newtown', 'UKR', 'Kiev', 100);"
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4080;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
4080 | Newtown | UKR | Kiev | 100
Пишем в мастер2, проверяем на мастер1:
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "INSERT INTO city (id, name, countrycode, district, population) VALUES (4081, 'Newcity', 'DEU', 'Berlin', 5);"
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4081;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
4081 | Newcity | DEU | Berlin | 5
Update
Обновляем на мастер1, проверяем на мастер2:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "UPDATE city SET population=101 WHERE id=4080;"
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4080;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
4080 | Newtown | UKR | Kiev | 101
Обновляем на мастер2, проверяем на мастер1:
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "UPDATE city SET district='Hamburg' WHERE id=4081;"
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4081;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
4081 | Newcity | DEU | Hamburg | 5
Delete
Удаляем на мастер1, проверяем на мастер2:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "DELETE FROM city WHERE id=4080;"
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4080;"
id | name | countrycode | district | population
----+------+-------------+----------+------------
Удаляем на мастер2, проверяем на мастер1:
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "DELETE FROM city WHERE id=4081;"
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT * FROM city WHERE id=4081;"
id | name | countrycode | district | population
------+------------+-------------+----------+------------
Все тесты пройдены успешны.
Как проверить статус реплики и оставание одного мастера от другого.
С помощью bucardo:
bucardo delta source_db
Total deltas across all targets: 0
Total deltas for database source_db: 0
bucardo delta dest_db
Total deltas across all targets: 0
Total deltas for database dest_db: 0
bucardo status mydb_sync
======================================================================
Last good : Jul 12, 2018 12:47:04 (time to run: 1s)
Rows deleted/inserted : 1 / 1
Sync name : mydb_sync
Current state : Good
Source relgroup/database : sample_herd / source_db
Tables in sync : 3
Status : Active
Check time : None
Overdue time : 00:00:00
Expired time : 00:00:00
Stayalive/Kidsalive : Yes / Yes
Rebuild index : No
Autokick : Yes
Onetimecopy : No
Post-copy analyze : Yes
Last error: :
======================================================================
С помощью SQL:
psql -h ${PGENDPOINT} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT pg_last_xlog_receive_location() AS receive, pg_last_xlog_replay_location() AS replay , COALESCE(ROUND(EXTRACT(epoch FROM now() - pg_last_xact_replay_timestamp())),0) AS seconds;"
receive | replay | seconds
---------+--------+---------
| | 0
psql -h ${PGENDPOINT2} -p 5432 -U ${PGUSERNAME} sample \
-c "SELECT pg_last_xlog_receive_location() AS receive, pg_last_xlog_replay_location() AS replay , COALESCE(ROUND(EXTRACT(epoch FROM now() - pg_last_xact_replay_timestamp())),0) AS seconds;"
receive | replay | seconds
---------+--------+---------
| | 0
Собственно говоря тут все, переключаем приложение на второй мастер, первый мастер тушим, работаем с новой версией PostgreSQL.
Комментариев нет:
Отправить комментарий