diff --git a/ansible/mysql-deploy.yml b/ansible/mysql-deploy.yml index ceb7fbb..4bbfb2d 100644 --- a/ansible/mysql-deploy.yml +++ b/ansible/mysql-deploy.yml @@ -8,9 +8,18 @@ # --- identity --- mysql_instance: "{{ instance_name }}" mysql_version_value: "{{ mysql_version | default('8.0') }}" + # 版本包映射:8.0 用 Ubuntu 24.04 自带源(精确锁版);8.4 用 MySQL 官方 APT 源的 LTS 组件 + # (noble 自带源无 8.4,官方源组件内即为该系列,不再锁小版本)。 + # 5.6/5.7 已官方 EOL 且 Ubuntu 24.04 无可用 apt 包,明确不纳入白名单。 mysql_package_map: - "8.0": "mysql-server=8.0.46-0ubuntu0.24.04.3" - mysql_package_name: "{{ mysql_package_map[mysql_version_value] }}" + "8.0": + package: "mysql-server=8.0.46-0ubuntu0.24.04.3" + repo_component: "" + "8.4": + package: "mysql-community-server" + repo_component: "mysql-8.4-lts" + mysql_package_name: "{{ (mysql_package_map[mysql_version_value] | default({})).package | default('') }}" + mysql_repo_component: "{{ (mysql_package_map[mysql_version_value] | default({})).repo_component | default('') }}" # --- platform-allocated inputs (safe fallbacks when not passed in) --- # 端口池 13306–13999:平台从池分配并传入;未传时兜底池首端口,占用探测在目标机执行。 @@ -132,13 +141,46 @@ host-wide Σ-quota budgeting is the platform's responsibility, this is a last-resort guard. tasks: - - name: Detect an existing Ubuntu MySQL package - ansible.builtin.command: dpkg-query -W mysql-server + - name: Detect an existing MySQL server package + ansible.builtin.command: dpkg-query -W -f '${Package}=${Version}\n' mysql-server mysql-community-server register: mysql_package_before failed_when: false changed_when: false - - name: Install Ubuntu MySQL package + - name: Resolve the installed MySQL server version + ansible.builtin.set_fact: + mysql_installed_version: >- + {{ (mysql_package_before.stdout_lines | select('search', '=') | list + | first | default('')).split('=') | last }} + + # /usr 下的 mysqld/mysql 二进制全机共享,一台主机只能承载一个 MySQL 版本系列。 + - name: Enforce host-level MySQL series consistency + ansible.builtin.assert: + that: + - mysql_installed_version == '' or mysql_installed_version.startswith(mysql_version_value ~ '.') + fail_msg: >- + Host already runs MySQL {{ mysql_installed_version }} but {{ mysql_version_value }} was requested; + native binaries under /usr are shared host-wide, so one host serves exactly one MySQL series. + + - name: Install the MySQL APT repository signing key + ansible.builtin.get_url: + url: https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 + dest: /etc/apt/keyrings/mysql.asc + owner: root + group: root + mode: '0644' + when: mysql_repo_component != '' + + - name: Configure the MySQL APT repository component + ansible.builtin.apt_repository: + repo: >- + deb [signed-by=/etc/apt/keyrings/mysql.asc] + http://repo.mysql.com/apt/ubuntu {{ ansible_distribution_release }} {{ mysql_repo_component }} + filename: xinfra-mysql-delivery + state: present + when: mysql_repo_component != '' + + - name: Install the MySQL server package ansible.builtin.apt: name: "{{ mysql_package_name }}" state: present @@ -150,7 +192,7 @@ name: mysql.service state: stopped enabled: false - when: mysql_package_before.rc != 0 + when: mysql_installed_version == '' - name: Check for the bundled MySQL AppArmor profile ansible.builtin.stat: diff --git a/ansible/templates/mysql-instance.cnf.j2 b/ansible/templates/mysql-instance.cnf.j2 index c295279..da6e3cf 100644 --- a/ansible/templates/mysql-instance.cnf.j2 +++ b/ansible/templates/mysql-instance.cnf.j2 @@ -66,7 +66,10 @@ loose-mysqlx=0 {% if topology == 'mgr_3' %} # --- Group Replication (config-ready; runtime bootstrap handled out-of-band) --- plugin-load-add=group_replication.so +{% if mysql_version_value != '8.4' %} +{# deprecated since 8.0.26 and removed in 8.3+; only inject for older series #} transaction-write-set-extraction=XXHASH64 +{% endif %} loose-group-replication-group-name={{ mgr_group_name }} loose-group-replication-start-on-boot=OFF loose-group-replication-local-address={{ ansible_host | default(inventory_hostname) }}:{{ mysql_gr_port_value }} diff --git a/server/internal/service/delivery.go b/server/internal/service/delivery.go index 1c6911c..f40a8dd 100644 --- a/server/internal/service/delivery.go +++ b/server/internal/service/delivery.go @@ -286,8 +286,9 @@ func (s *DeliveryService) CreateTask(ctx context.Context, userID uint64, isAdmin return &task, false, nil } -// 版本白名单与 playbook 的 mysql_package_map 保持同步。 -var supportedMySQLVersions = map[string]bool{"8.0": true} +// 版本白名单与 playbook 的 mysql_package_map 保持同步: +// 8.0 走 Ubuntu 自带源,8.4 走 MySQL 官方 APT 源;5.6/5.7 已 EOL 且无 noble 包,不支持。 +var supportedMySQLVersions = map[string]bool{"8.0": true, "8.4": true} // 拓扑白名单:playbook 已支持 primary_replica/mgr_3 的配置渲染, // 但调度器仍是单主机模型且复制编排未自动化,本期仅放开 standalone。 @@ -319,7 +320,7 @@ func validateDeliveryInput(input MySQLDeliveryInput, dataDisks []string) error { return fmt.Errorf("requested resources are outside the supported range (memory: 2048-65536 MiB, storage: 20-2000 GiB)") } if input.MySQLVersion != "" && !supportedMySQLVersions[input.MySQLVersion] { - return fmt.Errorf("unsupported mysql_version %q, supported: 8.0", input.MySQLVersion) + return fmt.Errorf("unsupported mysql_version %q, supported: 8.0, 8.4 (5.6/5.7 are EOL and have no Ubuntu 24.04 packages)", input.MySQLVersion) } if input.Topology != "" && !supportedTopologies[input.Topology] { return fmt.Errorf("unsupported topology %q, supported: standalone (primary_replica/mgr_3 pending scheduler support)", input.Topology) diff --git a/server/internal/service/delivery_test.go b/server/internal/service/delivery_test.go index 508b46f..f929cba 100644 --- a/server/internal/service/delivery_test.go +++ b/server/internal/service/delivery_test.go @@ -37,6 +37,11 @@ func TestValidateDeliveryInput(t *testing.T) { if err := validateDeliveryInput(namedZone, dataDisks); err != nil { t.Fatalf("named timezone rejected: %v", err) } + lts := valid + lts.MySQLVersion = "8.4" + if err := validateDeliveryInput(lts, dataDisks); err != nil { + t.Fatalf("8.4 LTS rejected: %v", err) + } for name, mutate := range map[string]func(*MySQLDeliveryInput){ "uppercase namespace": func(in *MySQLDeliveryInput) { in.Namespace = "Team-A" }, "bad instance": func(in *MySQLDeliveryInput) { in.InstanceName = "mysql_01" }, @@ -45,6 +50,7 @@ func TestValidateDeliveryInput(t *testing.T) { "too little storage": func(in *MySQLDeliveryInput) { in.StorageGi = 10 }, "too much storage": func(in *MySQLDeliveryInput) { in.StorageGi = 4000 }, "unsupported version": func(in *MySQLDeliveryInput) { in.MySQLVersion = "5.7" }, + "eol version": func(in *MySQLDeliveryInput) { in.MySQLVersion = "5.6" }, "unsupported topology": func(in *MySQLDeliveryInput) { in.Topology = "mgr_3" }, "port below pool": func(in *MySQLDeliveryInput) { in.MySQLPort = 3307 }, "port above pool": func(in *MySQLDeliveryInput) { in.MySQLPort = 14000 },