
1. 这不是“装个软件”Glance手动安装的本质是OpenStack服务治理的入门实战很多人看到“Glance手动安装与配置”这个标题第一反应是——不就是yum install一下、改几个配置文件、跑个db_sync吗网上教程一抓一大把照着敲命令就行。我刚入行那会儿也这么想直到在客户现场连续三天没让glance-api起来日志里满屏Connection refused而netstat -tlnp | grep :9292却显示端口压根没监听。后来才发现问题根本不在Glance本身而在它背后那套精密咬合的齿轮Keystone认证链是否闭环、数据库连接池是否被占满、PasteDeploy管道是否漏掉了authtoken filter、甚至MySQL的max_connections参数是否调得太小导致glance-manage初始化时就卡死。Glance从来不是孤立的服务它是OpenStack身份、存储、网络三大支柱交汇处的第一块试金石。它的安装过程本质上是一次对OpenStack整体架构理解深度的现场压力测试。你配错一个admin_tenant_name整个镜像上传流程就会在认证环节静默失败你漏掉glance-registry-paste.ini里[pipeline:glance-registry-keystone]的定义服务进程能启动但所有请求都会直接401返回连日志都懒得记——因为它根本没走到鉴权逻辑里。这篇实训我们不走“复制粘贴式”流程而是以一个真实运维工程师的视角从零开始亲手拧紧每一颗螺丝拆解每一个看似微小的配置项背后的系统级含义。你会看到为什么flavorkeystone必须写在glance-api.conf和glance-registry.conf两个地方为什么sql_connection字符串里的localhost和%权限差异会直接决定服务是“启动即崩”还是“启动即瘫”为什么glance-manage db_sync命令执行成功不代表数据库表结构就真的建好了——它可能只是把migration脚本的版本号写进了alembic_version表而真正的建表操作要等第一个API请求触发SQLAlchemy引擎初始化时才发生。这是一份为真正想搞懂OpenStack底层逻辑的人准备的手册而不是给只想交作业的学生看的速成指南。2. 环境筑基为什么CentOS 7 MySQL 5.7 Python 2.7是当前最稳的组合在动手敲任何一条命令之前我们必须先回答一个关键问题为什么实训环境要锁定在CentOS 7、MySQL 5.7和Python 2.7这绝非历史包袱的无奈妥协而是基于大量生产环境踩坑后得出的工程最优解。先说操作系统。CentOS 7自带的systemd服务管理器与OpenStack各服务的启动脚本如/usr/lib/systemd/system/glance-api.service是深度耦合的。它能精确控制服务依赖关系比如确保glance-api服务只在mysql和keystone服务完全就绪后才启动。我曾在一个Ubuntu 20.04环境里尝试部署结果glance-api总在Keystone的35357端口还没完全bind好时就急着去连导致反复超时重启。systemd的After和Wants指令在CentOS 7上是开箱即用的稳定保障。再看数据库。MySQL 5.7是OpenStack Queens及更早稳定版Glance作为核心组件其兼容性测试主要围绕此版本的黄金搭档。它的utf8mb4字符集支持完美匹配Glance镜像元数据中可能出现的emoji或生僻字它的innodb_file_per_tableON默认设置让每个Glance镜像的元数据表都能独立管理空间避免单个大表撑爆ibdata1文件。而MySQL 8.0引入的caching_sha2_password认证插件会与Glance使用的PyMySQL驱动产生兼容性问题导致sql_connection连接字符串始终报Access denied——这个问题在官方文档里几乎找不到只有翻遍OpenStack的Gerrit代码审查记录才能发现线索。最后是Python环境。Glance的setup.py明确声明了python_requires2.7, !3.0.*, !3.1.*, !3.2.*, !3.3.*。这意味着它原生不支持Python 3.4以下版本更别提3.8了。很多新手在CentOS 8上用dnf install结果装出来的是Python 3.6的Glance包启动时直接ImportError: No module named ConfigParser——因为Python 3里这个模块改名叫configparser了。所以我们的环境准备清单每一条都是血泪教训换来的操作系统CentOS 7.9 Minimal ISO内核3.10.0-1160禁用SELinuxsetenforce 0 sed -i s/SELINUXenforcing/SELINUXdisabled/g /etc/selinux/config关闭firewalldsystemctl stop firewalld systemctl disable firewalld。注意这里禁用SELinux不是偷懒而是因为glance-api进程需要读取/var/lib/glance/images/下的镜像文件而SELinux的httpd_can_network_connect布尔值无法覆盖其全部访问路径强行启用只会带来无尽的avc: denied日志。基础依赖yum install -y epel-release yum install -y python2-pip python2-devel gcc openssl-devel libffi-devel。特别强调python2-devel没有它后续编译pycrypto或cryptography时会因找不到Python.h头文件而失败。MySQL安装必须使用官方Yum仓库而非系统自带的MariaDB。rpm -Uvh https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm yum install -y mysql-community-server。安装后务必修改/etc/my.cnf在[mysqld]段下添加default-storage-engine innodb、character-set-server utf8mb4、collation-server utf8mb4_unicode_ci并重启服务。这是防止后续glance-manage db_sync创建的表出现乱码的唯一保险。时间同步yum install -y chrony systemctl enable chronyd systemctl start chronyd。OpenStack所有服务对时间戳极其敏感Keystone颁发的token有效期、Glance镜像的created_at字段都依赖于节点间毫秒级的时间一致性。我见过最离谱的案例是因NTP服务器漂移导致Glance上传镜像时Keystone认为token已过期返回401 Unauthorized而日志里却只显示Invalid token排查了整整一天才发现是时间不同步。提示所有环境准备命令请务必在root用户下执行并在每一步完成后用echo $?确认返回值为0。一个看似无关紧要的yum install失败比如gcc安装中途断网会导致后续所有Python扩展编译失败而错误信息往往藏在pip install的海量输出里极难定位。3. 数据库与用户glance账号的权限设计是一场精妙的攻防演练Glance的数据库配置远不止是“建个库、建个用户、赋个权限”三板斧那么简单。它是一场在MySQL权限模型框架下针对OpenStack服务特性的精准攻防设计。我们来拆解CREATE DATABASE glance; GRANT ALL ON glance.* TO glancelocalhost IDENTIFIED BY glance; GRANT ALL ON glance.* TO glance% IDENTIFIED BY glance;这条经典命令背后的深意。首先glancelocalhost和glance%这两个用户看似重复实则承担着截然不同的使命。glancelocalhost是为glance-manage db_sync命令服务的。这个命令在本地执行时MySQL客户端默认使用localhost作为host它需要最高权限来创建表、索引、视图甚至执行DROP TABLE IF EXISTS这样的破坏性操作。而glance%则是为glance-api和glance-registry服务进程准备的。这两个服务在运行时其Python进程会通过PyMySQL连接MySQL此时的host是服务所在机器的IP地址比如192.168.1.10而非localhost。如果只创建了glancelocalhost服务进程将永远无法连接数据库日志里只会冰冷地写着pymysql.err.OperationalError: (1045, Access denied for user glance192.168.1.10 (using password: YES))。这就是为什么必须同时授权两个host。其次“GRANT ALL”在这里是必要之恶但绝非最佳实践。ALL权限包含了FILE、SHUTDOWN、PROCESS等危险权限而Glance服务根本用不到它们。一个更安全的方案是精确授予所需权限GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON glance.* TO glancelocalhost;和GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON glance.* TO glance%;。这样即使Glance服务的数据库账号密码泄露攻击者也无法执行LOAD DATA INFILE读取服务器任意文件或SHUTDOWN关闭数据库。第三密码策略。IDENTIFIED BY glance这种明文弱密码在生产环境是绝对禁止的。但在实训环境中我们采用一种折中方案使用openssl rand -base64 12生成一个12位随机密码然后将其硬编码在配置文件中。这比glance强不了多少但至少规避了字典攻击。更重要的是我们要在MySQL层面强制密码复杂度。编辑/etc/my.cnf在[mysqld]段下添加validate_password_policySTRONG和validate_password_length12然后重启MySQL。这样SET PASSWORD FOR glancelocalhost your_random_password;命令才能成功执行。最后也是最容易被忽略的一点glance数据库的字符集。在执行CREATE DATABASE glance;之前必须先确认MySQL的全局字符集。mysql -u root -p -e SHOW VARIABLES LIKE character_set%;。如果character_set_server不是utf8mb4那么新建的glance库默认字符集就是latin1后续插入带中文的镜像名如centos7-中文版时会变成一堆问号。因此正确的建库语句应该是CREATE DATABASE glance CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;。这个细节决定了你的Glance服务未来能否支撑多语言云平台。注意执行完所有GRANT命令后务必运行FLUSH PRIVILEGES;。否则新授权不会立即生效glance-manage db_sync会因权限不足而失败。这是一个老手都可能忘记的“肌肉记忆”陷阱。4. 配置文件的双生子glance-api.conf与glance-registry.conf的协同与分工Glance的配置体系由glance-api.conf和glance-registry.conf这对“双生子”构成它们共享核心逻辑却又各司其职。理解它们的分工是避免配置混乱、排查疑难杂症的关键。很多人误以为只要把所有配置都塞进glance-api.confglance-registry.conf就可以不管了结果服务启动后镜像元数据能查但镜像文件本身却无法下载——因为glance-registry服务根本没起来或者起来了却拒绝处理请求。先看glance-api.conf。它是Glance服务的“门面”负责接收所有来自外部如Horizon Dashboard、nova-compute、或glance命令行工具的HTTP REST API请求。它的核心职责是解析请求、校验权限、调用后端存储驱动如file、swift、rbd进行实际的镜像文件读写并将结果封装成JSON返回。因此它的配置重点在于API暴露与前端集成。enable_v1_apiTrue和enable_v2_apiTrue这两行决定了Glance支持哪个版本的API协议。V1已废弃但某些老旧的nova版本仍会尝试调用所以建议保留。bind_host和bind_port默认0.0.0.0:9292定义了服务监听的网络接口和端口。最关键的是[keystone_authtoken]段auth_url指向Keystone的认证端点http://127.0.0.1:5000/v3/auth_type指定认证方式passwordproject_name、username、password则是glance服务在Keystone中注册的凭据。这些配置确保了每一个进入glance-api的请求都能被正确转发给Keystone进行身份核验。再看glance-registry.conf。它是Glance服务的“中枢神经”负责管理所有镜像的元数据metadata即镜像的ID、名称、大小、状态、属性等。它不直接处理镜像文件的二进制流而是作为一个独立的、可选的服务存在在较新版本中registry功能已被合并进api服务但实训环境仍需单独配置。它的配置重点在于元数据持久化与内部通信。[database]段的connection即sql_connection必须与glance-api.conf中的完全一致指向同一个MySQL数据库这样才能保证API层和Registry层看到的是同一份元数据。[keystone_authtoken]段的配置与glance-api.conf中的内容几乎一样但有一个微妙差别auth_url必须指向Keystone的管理端点http://127.0.0.1:35357/v3/而非公共端点5000端口。这是因为glance-registry作为内部服务需要更高的权限来查询和修改其他项目的镜像元数据而管理端点正是为此设计的。如果这里填错了端口glance-registry服务虽然能启动但所有涉及元数据的操作如glance image-update都会返回401 Unauthorized。这两份配置文件的协同还体现在glance-api-paste.ini和glance-registry-paste.ini这两个“胶水”文件上。它们定义了WSGI应用的中间件管道Pipeline。以glance-api-paste.ini为例[pipeline:glance-api-keystone]段定义了pipeline authtoken context apiapp意思是所有请求进来先经过authtoken中间件联系Keystone做认证再经过context中间件构建请求上下文最后才交给apiapp真正的业务逻辑。而glance-registry-paste.ini中的[pipeline:glance-registry-keystone]其pipeline authtoken context registryapp则确保了Registry的请求也经过同样的认证和上下文构建流程。如果glance-api.conf里写了flavorkeystone但glance-api-paste.ini里却没有对应的[pipeline:glance-api-keystone]定义那么flavorkeystone就只是一个摆设认证流程永远不会被触发。这就是为什么配置文件必须成对修改、成对验证。提示修改完任何配置文件后不要急于重启服务。先用glance-api --print-config和glance-registry --print-config命令检查配置是否被正确加载。这个命令会输出最终生效的完整配置你可以从中快速定位到拼写错误如auth_url写成auth_ur或路径错误如log_file指向了一个不存在的目录。5. PasteDeploy管道authtoken中间件如何成为Glance与Keystone之间的信任桥梁PasteDeploy是Python Web开发中一个古老而强大的约定它定义了如何将多个WSGI中间件Middleware像水管一样串联起来形成一条完整的请求处理流水线。在Glance中authtoken中间件就是这条流水线上最关键的一环它扮演着Glance服务与Keystone认证中心之间“信任桥梁”的角色。理解它的工作原理是诊断401 Unauthorized这类高频错误的不二法门。authtoken中间件的核心任务是拦截每一个HTTP请求提取其中的X-Auth-Token头即Keystone颁发的token然后向Keystone的/v3/auth/tokens端点发起一个HEAD请求验证该token是否有效、是否过期、以及它所代表的用户拥有哪些权限scope。这个过程看似简单但配置稍有不慎整条流水线就会断裂。我们来看glance-api-paste.ini中[filter:authtoken]段的关键配置auth_url http://127.0.0.1:35357/v3/这是Keystone的管理端点URL。必须确保这个地址能被Glance服务所在的机器ping通并且端口35357处于监听状态netstat -tlnp | grep :35357。如果Keystone是高可用部署这里应该是一个VIP地址而非单点IP。auth_type password指明认证方式。Glance服务自身也需要一个Keystone账号来完成“服务到服务”的认证。因此authtoken中间件在向Keystone发起HEAD请求时需要携带自己的凭据这就是auth_typepassword的含义。project_domain_name Default、user_domain_name Default、project_name service、username glance、password glance这五项共同构成了Glance服务在Keystone中的“服务用户”身份。project_name service意味着Glance属于service项目tenant这是OpenStack中为所有基础设施服务nova、neutron、cinder等预设的专用项目。username glance是这个项目下的一个用户。authtoken中间件正是用这个身份去向Keystone证明“我是Glance我有权验证其他用户的token”。include_service_catalog false这是一个极易被忽略的性能开关。当设为true时authtoken会在每次验证token后从Keystone的响应中提取完整的Service Catalog服务目录并将其注入到请求上下文中。这对于需要动态发现其他OpenStack服务如cinder的场景很有用但对于纯粹的镜像管理它完全是冗余开销会显著增加每次API调用的延迟。设为false可以将平均响应时间降低30%以上。delay_auth_decision true这是解决“认证前置”问题的魔法开关。默认情况下authtoken会在请求到达apiapp之前就完成所有认证工作。但如果delay_auth_decision true它会将认证决策延迟到apiapp内部由具体的业务逻辑如image-create来决定是否需要认证。这在某些特殊API如/healthcheck中非常有用可以绕过认证实现服务健康探活。authtoken中间件的调试是运维Glance的必修课。当遇到401错误时第一步不是怀疑Glance配置而是检查authtoken的日志。在glance-api.conf中确保[DEFAULT]段设置了debug True和verbose True并在[oslo_log]段指定了详细的日志路径。然后重现问题查看日志中是否有类似Starting keystone auth_token middleware或Authenticating token token_id的记录。如果没有说明authtoken中间件根本没有被加载问题出在glance-api-paste.ini的[pipeline]定义上。如果有但紧接着是Failed to fetch token data from identity server那就说明auth_url、username或password配置错误或者Keystone服务本身不可用。authtoken的日志就是Glance与Keystone之间信任关系的“心跳监测仪”。注意authtoken中间件的缓存机制。它会将验证过的token缓存在内存中默认TTL为300秒5分钟。这意味着如果你在Keystone中手动撤销了一个tokenGlance在接下来的5分钟内依然会认为它是有效的。这是为了性能而做的妥协。在调试阶段可以将memcached_servers参数留空并设置cache false强制每次请求都实时验证确保调试结果的即时性。6. 启动与验证glance-manage db_sync的真相与glance image-create的全链路剖析glance-manage db_sync和glance image-create是Glance安装过程中最具迷惑性的两个命令。前者常被当作“一键建库”的银弹后者则被当成“上传一个文件”的简单操作。然而它们背后隐藏的复杂性足以让一个未经训练的工程师在深夜的服务器前抓狂。先说glance-manage db_sync。它的名字极具误导性。“sync”同步这个词让人以为它会像rsync一样把本地的数据库模式schema和远程的MySQL实例进行双向同步。事实恰恰相反它是一个单向的、幂等的、基于Alembic迁移框架的“模式升级”schema upgrade命令。它的工作流程是首先读取Glance源码中glance/db/sqlalchemy/migrate_repo/versions/目录下的所有.py迁移脚本然后连接到MySQL查询alembic_version表获取当前数据库的“版本号”最后执行所有版本号高于当前值的迁移脚本将数据库模式升级到最新版。因此glance-manage db_sync成功只意味着“迁移脚本执行完毕”并不保证glance数据库里真的有了images、members等表。要验证这一点必须手动登录MySQLmysql -u glance -pglance -e USE glance; SHOW TABLES;。如果输出为空说明alembic_version表里记录的版本号已经是最新但迁移脚本本身可能因为权限不足或SQL语法错误而静默失败了。此时你需要查看/var/log/glance/api.log搜索alembic关键字找到具体的SQL错误。再说glance image-create。这个命令的执行是一次跨越多个服务、多个进程、多个网络协议的全链路旅程。我们以上传一个名为cirros-0.4.0-x86_64-disk.img的镜像为例追踪它的每一步命令行解析glance客户端工具Python脚本解析--name、--disk-format等参数构造一个HTTP POST请求目标URL为http://127.0.0.1:9292/v2/images。API入口glance-api服务接收到请求authtoken中间件首先介入向Keystone验证客户端提供的token。验证通过后请求被传递给apiapp。元数据创建apiapp调用glance.api.v2.images模块创建一个新的镜像记录Image Record并将其状态设为queued排队中。此时glance数据库的images表里已经有一条新记录但size字段为NULLstatus为queued。文件上传客户端开始通过HTTP分块传输chunked transfer encoding发送镜像文件的二进制数据。glance-api将这些数据流式地写入到/var/lib/glance/images/目录下文件名是镜像的UUID如/var/lib/glance/images/6a5b1c2d-3e4f-5a6b-7c8d-9e0f1a2b3c4d。状态更新当文件传输完成glance-api会更新数据库中该镜像记录的size、checksum、status变为active等字段。Registry同步如果启用在旧版Glance中glance-api还会向glance-registry服务发送一个RPC调用通知其更新元数据缓存。但在现代部署中这一步通常被省略。整个过程任何一个环节出错都会导致失败。最常见的故障点有三个一是/var/lib/glance/images/目录的属主和权限不对必须是glance:glance且chmod 755导致glance-api进程无法写入文件二是glance-api.conf中[glance_store]段的filesystem_store_datadir /var/lib/glance/images/路径配置错误或者该路径不存在三是glance-api服务的ulimit -n文件描述符限制太低无法同时处理大量并发上传请求表现为OSError: [Errno 24] Too many open files。实操心得在执行glance image-create之前务必先用curl -H X-Auth-Token: $TOKEN http://127.0.0.1:9292/v2/images进行一次简单的GET请求。如果这个请求能返回一个空的JSON数组{images: []}说明API服务、认证、数据库连接全部正常问题一定出在上传环节。这是一种快速隔离故障域的有效方法。7. 故障排查从Connection refused到Invalid token的完整排错链路在Glance的安装与配置过程中Connection refused和Invalid token是两大高频、高迷惑性的错误。它们像两座迷雾笼罩的山峰初学者往往在山脚下兜兜转转却找不到通往山顶的路。下面我将分享一条经过千锤百炼的、可复现的完整排错链路它不是教科书式的罗列而是模拟一个真实工程师在凌晨两点面对告警时的思考路径。第一步现象定位——Connection refused当你执行glance image-list得到Connection refused时第一反应不是去查Glance日志而是执行netstat -tlnp | grep :9292。如果没有任何输出说明glance-api进程根本没有监听9292端口。此时立刻检查systemctl status glance-api。如果状态是failed那么journalctl -u glance-api -n 50 --no-pager就是你的第一份情报。日志里最常见的错误是ImportError: No module named keystoneauth1这说明Python环境缺失关键依赖。解决方案是pip2 install keystoneauth1 python-keystoneclient。如果systemctl status显示active (running)但netstat依然看不到端口那问题就出在glance-api.conf的bind_host配置上。检查它是否被错误地设为了127.0.0.1而你的客户端是从另一台机器发起的请求。应改为0.0.0.0或具体的服务IP。第二步认证穿透——Invalid token当netstat能看到端口但glance image-list返回Invalid token时战斗才真正开始。此时glance-api.log里会充斥着Token validation failed。不要慌按顺序检查Keystone是否活着curl -v http://127.0.0.1:5000/v3/。如果返回404或超时说明Keystone没起来或者端口被防火墙拦住了。authtoken配置是否指向了正确的端点检查glance-api-paste.ini中的auth_url。它必须是http://127.0.0.1:5000/v3/公共端点而不是35357管理端点。35357是给glance-registry用的。glance服务用户是否存在openstack user list --domain default | grep glance。如果不存在说明你在Keystone中漏掉了openstack user create --domain default --password-prompt glance这一步。glance用户是否被赋予了admin角色openstack role add --project service --user glance admin。没有这个角色glance服务就无法以管理员身份去验证其他用户的token。第三步元数据黑洞——glance image-list返回空列表一切看起来都正常glance image-list也不报错但返回的JSON里images: []空空如也。这时问题很可能出在glance-registry服务上。执行systemctl status glance-registry如果它处于inactive (dead)状态systemctl start glance-registry。如果启动失败journalctl -u glance-registry会告诉你sql_connection配置的数据库密码错了或者glance数据库里压根没有表。此时回到glance-manage db_sync重新执行并仔细阅读其输出。第四步终极武器——tcpdump当所有日志都沉默所有配置都看似正确时祭出网络层的终极武器tcpdump -i lo port 35357 -w keystone.pcap。在另一个终端执行glance image-list然后用Wireshark打开keystone.pcap。你将清晰地看到glance-api进程是否真的向127.0.0.1:35357发起了TCP连接Keystone是否返回了HTTP/1.1 200 OK以及响应体中是否包含了token字段。网络包是所有抽象配置背后最诚实的真相。最后一个避坑技巧在glance-api.conf中将[DEFAULT]段的log_level DEBUG并确保[oslo_log]段的log_file /var/log/glance/api.log路径可写。然后在执行任何glance命令前先清空日志 /var/log/glance/api.log。这样日志里就只有你本次操作产生的信息排查效率提升十倍。