openEuler22.03 LTS 是 openEuler 社区于 2022 年 3 月发布的开源操作系统(从系统版本的命名不难发现吧)。openEuler 社区的玩转运营由华为为主导,社区以全球开源贡献者的玩转合作,构建了这个高效、玩转稳定和安全的玩转操作系统。基于 Linux 内核的玩转 openEuler 操作系统,支持 Kunpeng 以及其他处理器,玩转旨在充分发挥计算芯片的玩转潜力。它适用于数据库、大数据、云计算和人工智能等场景。通过社区合作,openEuler 构建了一个创新平台,创建了一个支持多处理器架构的统一开放操作系统,并推动了软件和硬件应用生态系统的繁荣。
openEuler22.03 LTS 带来了一系列关键功能,包括基于 Linux Kernel 5.10 的深度优化、新型媒体文件系统、分层内存扩展、用户模式协议栈、云原生调度增强、QEMU 热补丁、KubeOS、增强的轻量级安全容器、增强的 iSulad、双平面部署、边缘计算支持、嵌入式镜像,以及 secPaver 等。
openEuler22.03 LTS 可以被视为国产操作系统创新项目的首选系统版本之一。
GreatSQL,作为 MySQL 分支 Percona 的延伸,立志成为中国广泛受欢迎的开源数据库。其上一版本基于 Percona Server 8.0.25 构建,而这次的新版本则使用 Percona Server 8.0.32 作为基础,引入了许多重要特性。新发布的 GreatSQL 8.0.32-24 版本增加了并行 load data、逻辑和 CLONE 备份加密、MGR 读写节点可动态绑定 VIP、SQL 兼容扩展、审计日志增强等重大特性。
GreatSQL 8.0.32-24 可以被视为国产开源数据库信创项目,并且解决 MySQL5.7 EOL 问题的重要替代方案之一。
dbops 是一款提供生产级别 MySQL 部署的 playbook 工具,由芬达个人开发。
地址: https://gitee.com/fanderchan/dbops/
GreatSQL 官方并未提供专门针对 openEuler 的编译安装包,而我发现部署 GreatSQL、GreatSQL MGR、GreatSQL HA 等都有许多细节需要注意。本文主要讲述我如何思考并开发 dbops 的新功能,以在 openEuler22 上成功部署 GreatSQL,并运行其"MGR 读写节点可动态绑定 VIP"功能(以下简称"GreatSQL HA 功能")。
dbops 本来就支持 MySQL 和 Percona,所以对 GreatSQL 的支持并不需要大规模的改动。以下大部分是一些针对部署 GreatSQL 与 MySQL 或 Percona 的不同之处的调整。
由于 dbops 的目标是支持大量的国产操作系统,与 MySQL 一样,GreatSQL 提供了针对各种操作系统的预编译二进制包,但有一个是通用的,那就是 Linux - Generic 包。在 dbops 为 MySQL 提供支持以及在实际生产中,都在使用这个包。
同时,为了支持更多的 Linux 系统,我选择了基于 glibc2.17 的包,而非 glibc2.28 的包。前者意味着包是在 glibc 公共库版本为 2.17 的系统下编译出来的,因此,它不会包含大于 2.17 的库函数,兼容性会更强。
我选择支持的是 minimal 包,这个版本剔除了与调试相关的二进制文件和调试符号,体积非常小,仅为常规包的三分之一,我认为非 minimal 包并无优势。
目前,dbops 仅支持 GreatSQL-8.0.32-24-Linux-glibc2.17-x86_64.tar.xz 的部署。
dbops 可以很方便的配置执行 playbook 的变量,变量设置非常集中,一般只需要修改两个文件,一个是全局参数配置文件 common_config.yml,另外一个是当前需要执行的 playbook yaml 文件。
## DB TYPE,suport mysql,percona,greatsql+ db_type: greatsql#Directory of MySQL installation packagemysql_packages_dir: ../downloads/ + greatsql_package: GreatSQL-8.0.32-24-Linux-glibc2.17-x86_64-minimal.tar.xz+ percona_package: Percona-Server-8.0.29-21-Linux.x86_64.glibc2.17.tar.gz## do not modify- mysql_package: "{ { 'mysql-' + mysql_version + '-linux-' + ('glibc2.12' if mysql_version.startswith('5.') else 'glibc2.17') + '-x86_64' + ('.tar.gz' if mysql_version.startswith('5.') else '-minimal.tar.xz') }}"+ mysql_package: "{ % if db_type == 'mysql' %}{ { 'mysql-' + mysql_version + '-linux-' + ('glibc2.12' if mysql_version.startswith('5.') else 'glibc2.17') + '-x86_64' + ('.tar.gz' if mysql_version.startswith('5.') else '-minimal.tar.xz') }}{ % elif db_type == 'percona' %}{ { percona_package }}{ % elif db_type == 'greatsql' %}{ { greatsql_package }}{ % endif %}"## linux mysql run user namemysql_user: mysqlmysql_group: mysqlmysql_user_password: Dbops@9999## mysql install directory- mysql_base_dir: /database/mysql/base/{ { mysql_version }}+ mysql_base_dir: /database/{ { db_type }}/base/{ { mysql_version }}## mysql_data_dir_base define mysql datadir base, real datadir= mysql_data_dir_base + /port- mysql_data_dir_base: /database/mysql+ mysql_data_dir_base: /database/{ { db_type }}+ fcs_use_greatsql_ha: 1
在 mgr.yml 这个 playbook 里新增三个与 GreatSQL HA 相关的参数设置
+ greatsql_vip: 192.168.199.174+ greatsql_net_work_interface: "ens33"+ greatsql_netmast: "255.255.255.255"
新增了 mysql_ansible/roles/mysql_server/templates/8.0/greatsql-my.cnf.j2 模板,此模板是从 percona-my.cnf.j2 模板克隆而来。为了支持 GreatSQL HA 的相关参数设置,我使用 jinja2 语法设置了判断逻辑,只有满足以下三个条件,才会添加这些参数:
+ { % if db_type == 'greatsql' and fcs_use_greatsql_ha == 1 and make_mgr_role_included is defined and make_mgr_role_included %}+ #GreatSQL MGR vip+ plugin-load-add=greatdb_ha.so+ loose-greatdb_ha_enable_mgr_vip=1+ loose-greatdb_ha_mgr_vip_ip={ { greatsql_vip }}+ loose-greatdb_ha_mgr_vip_mask={ { greatsql_netmast }}+ loose-greatdb_ha_mgr_vip_nic={ { greatsql_net_work_interface }}+ + #single-primary mode+ loose-group_replication_single_primary_mode=1+ loose-group_replication_enforce_update_everywhere_checks=0+ { % endif %}
前面设置的三个参数值,greatsql_vip、greatsql_netmast、greatsql_net_work_interface 会对应传入到 my.cnf 里。
+ - name: Download GreatSQL binary tarball if not found locally and auto download is enabled(local)+ ansible.builtin.get_url:+ url: "https://product.greatdb.com/{ { mysql_package[0:18] }}/{ { mysql_package }}"+ dest: "{ { mysql_packages_dir }}/{ { mysql_package }}"+ mode: '0644'+ timeout: 30+ headers:+ User-Agent: "Wget/1.21.1"+ when: not mysql_server__package_file.stat.exists and fcs_auto_download_mysql == 1 and db_type == 'greatsql'+ delegate_to: 127.0.0.1
在正常情况下,您应该手动上传 GreatSQL 的包到 downloads/ 文件夹中。如果在 downloads/ 文件夹中没有找到对应的包,且在 common_config.yml 文件中设置了 fcs_auto_download_mysql == 1(允许从互联网下载安装包),并且 db_type == 'greatsql',那么将会自动从互联网下载安装包。
dbops 本身就支持下载 MySQL 安装包的功能。但由于 GreatSQL 的安装包下载路径不同,因此我额外添加了一个下载链接,以实现相同的下载功能。
为了让 GreatSQL HA 支持 mysqld 执行通常需要较高权限才能操作的挂载和卸载 VIP 操作,我们需要进行一些特殊设置。官方原先提供了两个方案,但我提出了新的解决方案。
在服务设置中,我们加入以下代码:
[Service]+ { % if db_type == 'greatsql' and fcs_use_greatsql_ha == 1 and make_mgr_role_included is defined and make_mgr_role_included %}+ AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW+ { % endif %}
只有当满足条件(需要部署 GreatSQL HA)时,系统会自动在启动服务中配置 "CAP_NET_ADMIN CAP_NET_RAW" 的权限。
我设计的这种方法也得到了官方的采纳,成为首选推荐方法。
GreatSQL 在部署后可能在使用 mysqld 初始化时,或者初始化后使用 mysql 登录时,报告 libcrypto.so 或 libssl.so 找不到。
[root@192-168-199-171 ~]# /usr/local/mysql/bin/mysqld --defaults-file=/database/mysql/etc/3306/my.cnf --initialize-insecure/usr/local/mysql/bin/mysqld: error while loading shared libraries: libcrypto.so: cannot open shared object file: No such file or directory[root@192-168-199-171 lib]# mysql -uroot -p -S /database/mysql/data/3307/mysql.sockmysql: error while loading shared libraries: libssl.so: cannot open shared object file: No such file or directory
针对此问题,我们有两种解决方案。第一种方法是使用 yum install openssl-devel 安装,之后系统库就会包含这两个 so 库:
/usr/lib64/libcrypto.so/usr/lib64/libssl.so
然而,我们也有第二种解决方法。GreatSQL 的 Generic 包其实已经编译了这两个库,只是它们被命名为 libcrypto.so.10 和 libssl.so.10,而非 libcrypto.so 和 libssl.so。我们只需在 /usr/local/mysql/lib/private/ 文件夹内为这两个库创建软链接即可。
并且,我认为采用第二种方法,使用官方包内的 libcrypto.so 和 libssl.so,是最佳的解决方案。
改动代码如下:
mysql_ansible/roles/mysql_server/tasks/fix_greatsql_install.yml
+ - name: Create symbolic links for libssl.so and libcrypto.so+ file:+ src: "/usr/local/mysql/lib/private/{ { link.src }}"+ dest: "/usr/local/mysql/lib/private/{ { link.dest }}"+ state: link+ owner: "{ { mysql_user }}"+ group: "{ { mysql_group }}"+ loop:+ - { src: 'libssl.so.10', dest: 'libssl.so' }+ - { src: 'libcrypto.so.10', dest: 'libcrypto.so' }+ loop_control:+ loop_var: link
mysql_ansible/roles/mysql_server/tasks/main.yml
- name: Install mysql to /usr/local/mysql ansible.builtin.import_tasks: install_mysql.yml+ - name: Fix libcrypto.so and libssl.so not find by create link if db_type is greatsql+ ansible.builtin.import_tasks: fix_greatsql_install.yml+ when: db_type == 'greatsql' - name: Init mysql datadir ansible.builtin.import_tasks: initialize_mysql_datadir.yml
在设定了 VIP 漂移的三台机器中,与 MHA 架构相同,我们可能会遇到一种状况:在一开始设置网卡绑定时,主机管理员可能会没注意,发生以下情况:
192.168.199.171 网卡名 bond1192.168.199.172 网卡名 bond1192.168.199.173 网卡名 bond0
你可能已经发现了,第三台机器的网卡名与前两台不一致。因此,如果发生高可用漂移,MHA 或 MGR 在选择主机时,如果选择了第三台作为主机,那么根据 greatsql_net_work_interface: "bond1" 的设置,漂移操作可能会失败。虽然我还没有进行过测试,但这个问题在 MHA 架构中肯定会发生,而且在 GreatSQL HA 架构中也有很大可能发生!
因此,在运行 ansible-playbook 部署 mgr.yml 时,我已在第一步的 pre_check_and_set(检查和设置系统参数)中,加入了检查网卡名是否一致的步骤。如果网卡名不一致,playbook 将报错并终止运行,给出提示。代码如下:
- name: Add network interface alias to a temporary file ansible.builtin.lineinfile: path: "/tmp/net_aliases.txt" line: "{ { ansible_default_ipv4.alias }}" create: true mode: '0644'- name: Fetch copy ansible.builtin.fetch: src: /tmp/net_aliases.txt dest: /tmp/ssh- name: Append file /tmp/net_aliases.txt (delegate to 127.0.0.1) ansible.builtin.shell: set -o pipefail && find /tmp/ssh/ -name "*.txt" -type f -exec sh -c 'cat { }' \; | sort | uniq | wc -l register: pre_check_and_set__shell_output changed_when: false run_once: true delegate_to: 127.0.0.1- name: Check if shell output is 1 ansible.builtin.assert: that: pre_check_and_set__shell_output.stdout | int == 1 fail_msg: "Network card names are different!" run_once: true delegate_to: 127.0.0.1- name: Delete /tmp/net_aliases.txt ansible.builtin.file: path: /tmp/net_aliases.txt state: absent- name: Delete /tmp/ssh/ (delegate to 127.0.0.1) ansible.builtin.file: path: /tmp/ssh/ state: absent run_once: true delegate_to: 127.0.0.1
GreatSQL 和 Percona 官方都推荐使用 jemalloc 替代默认的 malloc,我至今没有发现 MySQL 官方的类似建议。然而,在实践中,我曾经通过替换 malloc 为 jemalloc 内存管理器来解决内存泄露问题,尽管根本原因仍不清楚。因此,我之前已经在我的 dbops 工具中加入了在部署时选择使用 jemalloc 内存分配器的功能,它是在 systemd 服务中实现的。
以下是涉及的代码:
mysql_ansible/playbooks/common_config.yml
# 配置文件中提供一个开关,决定是否使用 jemalloc 内存分配器,其默认值为 0,即不修改内存分配器fcs_mysql_use_jemalloc: 1
mysql_ansible/roles/mysql_server/tasks/install_mysql_dependents.yml
- name: Install libaio and numactl ansible.builtin.yum: name: "{ { package.name }}" state: present loop: - { name: 'libaio' } - { name: 'numactl' } loop_control: loop_var: package tags: - dependents- name: Install jemalloc when: fcs_mysql_use_jemalloc == 1 tags: - dependents block: - name: Install jemalloc using yum ansible.builtin.yum: name: jemalloc state: present register: mysql_server__jemalloc_install_result ignore_errors: true - name: Set jemalloc rpm file name based on OS ansible.builtin.set_fact: mysql_server__jemalloc_rpm_file: "{ { 'jemalloc-3.6.0-1.el8.x86_64.rpm' if os_type in ['openEuler22', 'openEuler20', 'CentOS8', 'BigCloud8', 'Anolis OS8'] else 'jemalloc-3.6.0-1.el7.x86_64.rpm' }}" - name: Copy jemalloc rpm to target server ansible.builtin.copy: src: "../files/{ { mysql_server__jemalloc_rpm_file }}" dest: "/tmp/{ { mysql_server__jemalloc_rpm_file }}" mode: '0755' when: mysql_server__jemalloc_install_result.failed - name: Install jemalloc from local file ansible.builtin.yum: name: "/tmp/{ { mysql_server__jemalloc_rpm_file }}" state: present disable_gpg_check: true register: mysql_server__jemalloc_local_install_result when: mysql_server__jemalloc_install_result.failed ignore_errors: true - name: Check if jemalloc installation failed ansible.builtin.fail: msg: "Failed to install jemalloc" when: mysql_server__jemalloc_install_result.failed and mysql_server__jemalloc_local_install_result.failed
这段代码主要是用于安装 mysql_server 的依赖包,如果你在配置中设置了 fcs_mysql_use_jemalloc: 1,那么将会安装 jemalloc。默认情况下,它会尝试使用 yum 来安装。如果 yum 安装失败,那么它会尝试使用 dbops 自带的 jemalloc 包。
原因是,预备的 jemalloc 包只有两个版本:一个是针对 EL7,一个是针对 EL8。并没有专门为国产操作系统准备的专用包。在国产操作系统上,你应该优先使用 yum 来安装适合该系统的包。如果使用 yum 安装失败,你可以考虑使用 EL7 或者 EL8 的 jemalloc 包来进行兼容性安装。
mysql_ansible/roles/mysql_server/templates/mysql.service.j2
[Service]...{ % if fcs_mysql_use_jemalloc == 1 %}{ % if os_type in ['openEuler22','openEuler20'] %}Envirnotallow="LD_PRELOAD=/usr/lib64/libjemalloc.so.2"{ % else %}Envirnotallow="LD_PRELOAD=/usr/lib64/libjemalloc.so.1"{ % endif %}{ % endif %}
根据 yum 或者 rpm 包方式安装 libjemalloc 后,不同操作系统的 libjemalloc.so 路径可能会不同,我在服务配置里会做判断和正确加载。
芬达,《芬达的数据库学习笔记》公众号作者,开源爱好者,擅长 MySQL、ansible。
责任编辑:武晓燕 来源: GreatSQL社区 GreatSQLMGREL7(责任编辑:焦点)
《瑞奇与叮当:分离》PC版DirectStorage 1.2演示
《碧蓝幻想:Relink》在澳大利亚通过评级 8月公布新消息
航天科技集团研制大气环境监测卫星大气一号上线 高精度监测能力提升
微软总裁Satya Nadella:AI和微软都需要伟大的目标
力合微(688589.SH)2020年归母净利2782.05万元 基本每股收益0.33元