:從概念到常見坑)
這個星期我把Ceph集群上一直缺的那個對象網關補上了——準確說是把RGW的搭建完整趟了一遍流程。RGW這個組件說復雜不算復雜說簡單又比很多人以為的多一些門道。它是Ceph對外提供對象存儲能力的那一層讓你能用S3和Swift的API去操作底層的Ceph存儲池而不是為此再單獨搭一套對象存儲服務。在做對象存儲選型的時候很多人會拿它和MinIO這類輕量服務做對比如果只是一個小項目單機MinIO確實省事但如果你已經有了一套正在運行的Ceph集群又想在不額外引入新組件的前提下提供對象存儲接口RGW就是最順理成章的答案。這篇內容主要適合兩類人一類是已經用Ceph做塊存儲或文件系統(tǒng)、但對RGW還停留在概念層面的朋友另一類是打算在生產環(huán)境或測試環(huán)境里部署RGW想提前避開常見坑的運維和開發(fā)。我會把集群準備、賬號權限、網關注冊、配置參數、S3客戶端驗證這些步驟從頭到尾講清楚重點是每一步背后的原因以及哪些位置容易踩坑。1. 把Ceph對象網關想清楚再做1.1 RGW到底解決什么問題RGW的全稱是RADOS Gateway在Ceph里并不是一個“額外組件”而是一個特殊的守護進程。它把自己偽裝成一塊普通的存儲接口利用librados連接Monitor集群和OSD集群然后把底層的對象存儲能力通過RESTful HTTP API暴露出來。對外它兼容S3協(xié)議和Swift協(xié)議對內它直接讀寫RADOS層的數據看起來就像是一道把本地RADOS翻譯成HTTP的橋。第一次看會比較繞的地方在于數據流??蛻舳嗽L問RGW的時候實際上并不直接跟OSD通信請求流程是這樣的客戶端發(fā)出HTTP請求到RGW的監(jiān)聽端口RGW解析請求、校驗簽名和權限然后通過librados拿到集群運行圖再向對應的OSD發(fā)起讀寫。這個設計的好處是RGW自己不需要維護復雜的數據分布和故障恢復邏輯全都可以甩給Ceph底層代價則是網關節(jié)點的CPU、內存和網絡會直接影響對象存儲的性能所以生產環(huán)境通常建議跑多個RGW實例前面再掛一層負載均衡。還有一個經常被忽略的點RGW和“塊存儲”是兩套完全不同的使用方式。RBD是給你一個塊設備你當普通磁盤去格式化、掛載而RGW給的是一個HTTP接口你通過API去上傳、下載、列出文件和目錄。這兩種場景面對的操作對象、存儲結構、甚至對數據一致性的要求都不一樣所以不能把RGW理解為“把RBD變成HTTP”它有自己的元數據體系、索引池和數據池。1.2 池的規(guī)劃和元數據體系RGW在Ceph里會用到一組專門的池而且數量比直覺上多一些。早期版本可能只需要一個data pool和一個index pool但隨著多站點、生命周期管理等功能的加入默認池的數量已經變得相對固定。手動部署的時候如果池沒建全RGW啟動時雖然會自動嘗試創(chuàng)建但在某些受限環(huán)境里會直接卡住或者報錯這也是我一直建議先理解池規(guī)劃再動手的原因。這些池的邏輯大致是.rgw.root存放區(qū)域和區(qū)域組配置.rgw.control用于控制面消息.rgw.meta里面再拆出用戶、桶等元數據對象.rgw.buckets.index保存桶內對象索引.rgw.buckets.data才是真正存對象數據的池。元數據和數據分開意味著你可以更精細化地管理性能和冗余策略比如讓元數據池跑在更快的盤上數據池用大容量盤。從Ceph的新版本開始RGW還把多站點能力拆成了realm、zonegroup和zone三層模型。如果只有單站點不需要搞得太復雜直接用默認的的zone即可。但如果你計劃做跨地域容災則必須先理清這三層的關系zone是不可再分的存儲區(qū)域zonegroup是一組zone的集合realm則是最大的管理邊界。多區(qū)域之間通過同步日志傳遞對象變更這個過程細節(jié)很多我一般建議第一輪搭建先用單站點把主鏈路跑通再考慮多活。2. 搭建前的準備先把地基打牢2.1 集群健康狀態(tài)檢查不能省搭建RGW看起來只是起一個進程的事但這個進程依賴整個Ceph集群的健康狀態(tài)。如果你拿到一個PG狀態(tài)異常的集群就直接起RGW大概率會出現啟動成功但讀寫失敗、或者桶列表超時這類莫名其妙的問題。所以我在任何一次RGW部署前都會先做三件事。第一件執(zhí)行ceph -s確認集群狀態(tài)不是HEALTH_WARN。如果輸出里出現了PG_DEGRADED、PG_AVAILABILITY或者MON_DOWN之類的告警先把它們處理干凈。第二件查看ceph osd tree確認OSD都在線并且沒有持續(xù)的rebalance或者scrubbing開銷過大。第三件檢查Mon節(jié)點的時鐘同步ntp或者chrony必須正常因為Ceph內部對時間敏感性很強而S3簽名認證對時間更敏感網關節(jié)點和客戶端之間的時間差也會影響API調用。很多人容易忽略的是集群容量。RGW一旦跑起來往上面?zhèn)鲾祿乃俣瓤赡芊浅?烊绻罕旧淼目捎每臻g不多很可能會觸發(fā)full或nearfull狀態(tài)。這種狀態(tài)下RGW的寫入會被拒絕表現成S3返回503或者500錯誤排查起來也很費勁。我的經驗是至少在ceph df里保留20%以上的冗余空間再接入對象網關。2.2 網關運行方式選型現在部署RGW主要有兩條路線一條是用Ceph官方推薦的編排工具來管理比如用cephadm命令直接部署多個RGW實例另一條是手動在節(jié)點上裝radosgw包再通過systemd把服務托管起來。兩條路線沒有絕對的好壞更多取決于你已有集群的部署方式。如果集群本身就是用cephadm建的那就繼續(xù)用編排工具它能自動處理證書、日志收集、服務重啟這些雜事。如果集群是手工搭建的或者要兼容比較舊的發(fā)行版手動部署反而是可控性更高的選擇。手動部署有一個隱含前提你已經在某個節(jié)點上安裝了radosgw二進制。Debian系通常叫radosgwRedHat系通常也在ceph-radosgw或ceph-base包里。裝好之后我不會急著直接跑命令啟動而是先確認它能找到正確的配置文件。默認路徑是/etc/ceph/ceph.conf和/etc/ceph/ceph.client.rgw.keyring但也支持通過--cluster參數指定集群名當多個集群共存時要尤其注意別搞混。另外一個需要考慮的問題是在哪臺機器上跑。RGW是一個有CPU和內存開銷的進程最好別和負載已經很重的Monitor擠在一起。我一般在獨立的應用節(jié)點上起網關至少保證網絡帶寬足夠因為對象存儲最吃網絡大文件上傳下載都會經過這個節(jié)點作為轉發(fā)。3. 核心配置逐項拆解3.1 ceph.conf里與RGW相關的參數Ceph的配置雖然很多但RGW相關的核心參數其實可以收斂到一個表里。我先把我常用的、踩過坑的參數列出來這些是在基本能跑通的基礎上再做性能和安全調整的關鍵。參數名作用我的常用值說明rgw_frontends配置網關前端協(xié)議和端口beast port7480新版本基本都用beastCivetWeb已經不怎么維護rgw_enable_usage_log是否記錄用戶使用量日志false生產環(huán)境如果不需要計量建議關掉省IOrgw_bucket_default_quota_max_objects桶內默認最大對象數1000000防止意外寫爆索引池rgw_bucket_default_quota_max_size桶內默認最大容量10G測試環(huán)境可以設小一點避免占滿磁盤rgw_thread_pool_size網關線程池大小512并發(fā)高的時候需要調大但不要超過CPU核心數的4倍rgw_s3_auth_use_rados_cls_user是否啟用新的用戶認證接口false不開啟也能跑開啟后權限管理更靈活rgw_zone指定當前實例所屬zonedefault單站點默認值不用改第一個參數rgw_frontends是最直觀的。老資料里經常寫civetweb port8080但在較新的Ceph版本中CivetWeb已經被移除了繼續(xù)用舊參數會導致啟動直接報錯。切換到beast之后配置更簡潔性能也更好。端口選擇上我習慣用7480而不是默認的80或443因為以非root方式跑時要綁定千以下的端口需要額外加capabilities不值當。rgw_thread_pool_size這個參數很容易被調壞。官方默認值我記得是512但在一臺只有4核8線程的機器上開512個線程每個線程都會占用棧空間和調度開銷反而會讓吞吐下降。我的原則是先在默認值下把壓力測試跑一遍用top看CPU有沒有到瓶頸確實發(fā)現concurrency不夠了再往上漲。3.2 認證體系的層次要分清RGW涉及兩層認證很多人在這里繞暈。底層是Ceph自身的Cephx認證決定的是RGW進程有沒有權限去操作存儲池上層是S3/Swift API的認證決定的是外部客戶端有沒有權限操作桶和對象。搭建時這兩層都要配置而且任何一個環(huán)節(jié)缺失都會以各種奇怪的現象暴露出來。底層認證做法是給RGW創(chuàng)建一個專屬的用戶入口在Ceph認證體系里被稱為client.rgw.主機名然后把對應權限授予它。我通常會用ceph-authtool生成keyring文件再用ceph auth add注冊到集群。所需的caps一般是對mon有allow rwx對osd有allow rwx。這看起來權限給很大但RGW確實需要具備操作元數據池和數據池的全部權限如果刻意收緊caps很容易出現網關進程起來但讀寫失敗的怪問題。上層認證相對簡單。用radosgw-admin user create創(chuàng)建用戶之后會返回一對AccessKey和SecretKey這就是S3客戶端使用的憑證。這一層和底層Cephx沒有關系你在外部用S3客戶端傳的永遠是這樣的RGW用戶憑證而不是cephx的client.rgw憑證。4. 從零到一的搭建實操4.1 創(chuàng)建網關專用賬號和認證文件我先假定你已經有了一套健康的Ceph集群并且在一臺目標節(jié)點上安裝了radosgw相關包。第一步是先創(chuàng)建keyring文件。實際操作中我會在/etc/ceph/目錄下操作保證文件權限只對ceph用戶可讀。sudo ceph-authtool --create-keyring /etc/ceph/ceph.client.rgw.keyring sudo ceph-authtool --gen-key -n client.rgw.$(hostname) \ --cap osd allow rwx --cap mon allow rwx --cap mgr allow rwx \ /etc/ceph/ceph.client.rgw.keyring sudo chown ceph:ceph /etc/ceph/ceph.client.rgw.keyring這里有個值得注意的細節(jié)--gen-key生成的是隨機密鑰而--cap參數指定了該client在Ceph側擁有的權限。為什么還需要cap mgr因為新版RGW在部分運維操作中會查詢Manager模塊的運行狀態(tài)如果不加這個cap日志里會不斷出現mgr訪問被拒的報錯。接著把生成的keyring注冊到集群認證庫里sudo ceph auth add client.rgw.$(hostname) -i /etc/ceph/ceph.client.rgw.keyring再用ceph auth list確認一下看到client.rgw.主機名已經存在即可。這種做法在集群有多個網關實例時也適用每臺機器可以有自己的client.rgw.hostname賬號互相獨立權限也不需要改。4.2 啟動網關實例手動啟動方式其實非常簡單直接執(zhí)行sudo radosgw -f --cluster ceph --name client.rgw.$(hostname) --setuser ceph --setgroup ceph加-f是為了在前臺運行并打印日志方便第一次調試。日志里如果看到beast listening on :7480這樣的字樣說明端口已經正常監(jiān)聽。此時可以試著直接在瀏覽器里訪問http://節(jié)點IP:7480它應該返回一個簡潔的XML或者版本信息頁面這個頁面里的內容并不是報錯而是證明RGW已經在正常響應HTTP請求。線上環(huán)境我不會用前臺方式而是寫一個systemd unit文件來托管。這里給一個我實際在用的模板直接放到/etc/systemd/system/ceph-radosgwrgw.service[Unit] DescriptionCeph RGW Afternetwork-online.target Wantsnetwork-online.target [Service] Userceph Groupceph ExecStart/usr/bin/radosgw -f --cluster ceph --name client.rgw.%H Restarton-failure RestartSec5 LimitNOFILE1048576 [Install] WantedBymulti-user.target注意這里的%H在systemd里代表主機名可以自動對應到我們在認證庫里注冊的client.rgw.hostname賬號不用每個節(jié)點寫死name。如果主機名帶了域名后綴%h和%H有細微差別我建議先用hostname -s確認短主機名是什么再決定用哪個占位符。如果你的集群是用cephadm管理的可以直接用編排命令來部署多個網關實例本質上是讓cephadm替你完成了上述所有步驟ceph orch apply rgw myrgw --placement3 node1 node2 node3 --port 7480這個命令會創(chuàng)建名為myrgw的RGW服務并把實例調度到三臺節(jié)點上。它的優(yōu)點是自帶多副本和服務發(fā)現缺點是如果你還不熟悉cephadm的service模型出了問題排錯會比手動部署復雜不少。4.3 初始化默認zone并創(chuàng)建S3用戶網關進程起來之后我們還需要確認RGW的zone配置和用戶體系是完整的。單站點模式下默認zone會自動創(chuàng)建通常不需要額外操作。但如果消息日志里提示no zone config found之類的錯誤就需要手動初始化一次sudo ceph auth get-or-create client.rgw.$(hostname) mon allow rwx osd allow rwx -o /etc/ceph/ceph.client.rgw.keyring radosgw-admin realm create --rgw-realmdefault radosgw-admin zonegroup create --rgw-zonegroupdefault --rgw-realmdefault radosgw-admin zone create --rgw-zonegroupdefault --rgw-zonedefault --rgw-realmdefault radosgw-admin period update --commit再強調一下這條命令在不同版本、不同集群初始化狀態(tài)下需要的次數不一樣。我遇到過有環(huán)境在realm create這一步直接報already exists那就跳過它繼續(xù)往下做就好。不要因為報錯就反復執(zhí)行認真看輸出內容再決定下一步。然后創(chuàng)建第一個S3用戶radosgw-admin user create --uidtestuser --display-nameTest User --emailtestexample.com正常輸出會包含一對access_key和secret_key把它們復制保存好后續(xù)所有S3客戶端配置都要用。如果忘記了也不用重新創(chuàng)建用戶直接執(zhí)行radosgw-admin user info --uidtestuser就能把密鑰重新查出來。4.4 S3客戶端冒煙測試網關是否真正工作最終要看S3客戶端能不能完整地走一遍創(chuàng)建桶、上傳對象、下載對象、刪除對象這條鏈路。我平時測試最常用的是s3cmd和boto3因為鞋帶配置起來足夠簡單出現問題時錯誤信息也相對友好。先看s3cmd的配置方式在~/.s3cfg里填入[default] access_key 剛才拿到的AccessKey secret_key 剛才拿到的SecretKey host_base 127.0.0.1:7480 host_bucket 127.0.0.1:7480 use_https False這里最關鍵的配置是host_base和host_bucket。很多初次接觸RGW的人會照搬AWS的默認配置結果發(fā)現一直連到AWS站點去。RGW是獨立網關服務必須指向你自己的節(jié)點和端口兩處地址要完全一致。另外use_https必須為False才能在測試環(huán)境走明文HTTP如果你配置了TLS證書則可以改成True并指定證書路徑。s3cmd測試命令如下s3cmd mb s3://testbucket echo hello ceph rgw hello.txt s3cmd put hello.txt s3://testbucket s3cmd get s3://testbucket/hello.txt /tmp/hello_download.txt s3cmd del s3://testbucket/hello.txt如果能依次執(zhí)行成功說明桶創(chuàng)建、對象上傳、下載、刪除這幾條最基本的路徑都沒有問題。此時可以用radosgw-admin bucket list查看桶是否存在radosgw-admin user info --uidtestuser查看用戶配額和桶數量是否有變化。如果你用Python做自動化測試那么boto3是更常見的選擇。我給一個最小可用的腳本注意endpoint_url必須顯式指定RGW地址import boto3 from botocore.client import Config s3 boto3.client( s3, endpoint_urlhttp://127.0.0.1:7480, aws_access_key_id剛才拿到的AccessKey, aws_secret_access_key剛才拿到的SecretKey, configConfig(signature_versions3v4), region_nameus-east-1, ) s3.create_bucket(Bucketmybucket) s3.put_object(Bucketmybucket, Keyhello.txt, BodybHello Ceph RGW) resp s3.get_object(Bucketmybucket, Keyhello.txt) print(resp[Body].read())有位朋友第一次用boto3連RGW時一直報簽名不一致最后發(fā)現是沒用s3v4簽名版本。RGW對簽名版本的支持比較嚴格我建議在Config里統(tǒng)一加上signature_versions3v4不然老版本的簽名算法會讓服務器驗簽失敗。5. 常見問題與排查技巧實錄5.1 經典坑速查表這一步我把真實環(huán)境里經常出現的問題整理成一個速查表每一個都是我自己踩過或者幫別人排查過的。它的價值在于你可以拿現象去對號入座而不用從零開始猜測問題在哪一層。問題現象真正原因解決方法RGW進程起不來日志報No such file or directorykeyring路徑不對或文件屬主不對檢查/etc/ceph/ceph.client.rgw.keyring是否存在chown ceph:ceph后再啟動報Permission deniedcephx的caps沒配夠確認client.rgw.hostname對mon和osd都有allow rwx端口無法監(jiān)聽端口被占用或者小于1024換高位端口或給進程加網絡capabilitiesS3客戶端報AccessDenied系統(tǒng)時間不同步或簽名算法不匹配同步時鐘在客戶端強制用s3v4簽名create_bucket一直連不上endpoint_url寫錯或者網段不通確認HTTP地址可以被外部訪問curl一下網關端口看響應上傳大文件時收到500錯誤集群空間不足或索引池寫滿ceph df檢查容量擴容或者清理舊數據桶內對象列表為空但數據確實存在索引池和元數據池不一致用radosgw-admin bucket check --fix --bucket 桶名重建索引第一條看起來有點弱智但真的很容易發(fā)生。我會經常同時管理多套環(huán)境配置文件和keyring用scp拷貝過來之后屬主沒有修正結果進程反復啟動失敗。所以我的習慣是在systemd unit里明確寫Userceph并且啟動前手動執(zhí)行一次chown -R ceph:ceph /etc/ceph/把整個配置目錄的屬主統(tǒng)一掉這樣省心很多。第二條和簽名問題是最常見的兩類。簽名問題在上一段里已經提過權限問題則比較隱蔽因為Ceph的日志級別默認較低不會把permission denied這種報錯直接鋪滿屏幕。遇到權限問題時建議臨時在配置里增加一行debug rgw 20或者查看/var/log/ceph/目錄下的RGW日志報錯里會有非常明確的AccessDenied信息。5.2 日志與調試的實用技巧RGW的日志分布在不同的地方。如果用cephadm部署一般可以通過ceph -w看到RGW組件的動態(tài)也可以用cephadm logs ceph-radosgw.hostname來查看某個網關容器的日志。手動部署時日志一般落在/var/log/ceph/ceph-client.rgw.hostname.log如果沒找到需要確認是不是systemd直接接管了標準輸出。調試階段建議把日志級別臨時調高在ceph.conf的[client.rgw.hostname]段里加上debug rgw 20 debug librados 5然后重啟服務日志會非常冗長墨水瓶的朋友可能會被刷屏但這種級別的信息量對定位問題非常有幫助。調完之后記得把debug rgw重新改回默認值否則生產環(huán)境日志量會拖垮磁盤。還有一個小技巧不用S3客戶端也能快速確認網關狀態(tài)。直接用curl發(fā)一個匿名請求curl -I http://127.0.0.1:7480如果返回HTTP狀態(tài)碼200或者405甚至403都說明RGW本身在正常工作如果返回連接超時或拒絕連接問題大概率在網絡層或進程層而不是在API層。5.3 性能與高可用的進一步動作基礎搭建完成后很多人的第一反應是“是不是再調一調性能”。我會建議分兩步走。第一步是把多個RGW實例拉起來讓并發(fā)請求可以分散到多臺機器上這是最直接的吞吐擴展方式??梢园褍蓚€RGW實例跑在同一臺機器上的不同端口也可以分散到不同機器然后在前面用負載均衡器將流量均攤。負載均衡這一層我用的比較順手的方案是直接用Nginx的upstream方式把流媒體請求和普通API請求都代理到后端的RGW端口。關鍵配置如下upstream rgw_backend { least_conn; server 192.168.1.10:7480 max_fails3 fail_timeout10s; server 192.168.1.11:7480 max_fails3 fail_timeout10s; } server { listen 80; server_name rgw.example.local; client_max_body_size 0; location / { proxy_pass http://rgw_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }client_max_body_size一定要設置為0否則上傳大文件時會被Nginx攔下來返回413 Request Entity Too Large。這個坑我踩過不止一次每次新配環(huán)境都要提醒自己。第二個方向是調整RGW本身的數據分片和配額。rgw_bucket_default_quota_max_objects和rgw_bucket_default_quota_max_size要跟實際使用場景匹配。如果是一個備份系統(tǒng)對象數量和單桶容量都要留足余量如果是一個靜態(tài)站那就要考慮對象數量是否會影響列舉速度。6. 寫在搭建之后的話RGW搭建本身不算難每一步都有固定套路真正讓我覺得有學習價值的是過程中對Ceph整個存儲體系的理解。第一點體會是不要把RGW當成一個“裝好就能用”的獨立服務它跟池的規(guī)劃、集群的健康狀態(tài)、客戶端的簽名方式都耦合得很深任何一個環(huán)節(jié)疏忽都會在后續(xù)使用中暴露出來。第二點是我很推薦先用最樸素的s3cmd把完整鏈路跑通再上自動化框架和腳本因為s3cmd的報錯信息更直接排查起來沒有層層包裝的干擾。最后再分享一個日常運維的小習慣每次改完RGW相關配置或者重啟服務之后我都會立刻執(zhí)行一次radosgw-admin bucket list和radosgw-admin user info --uidtestuser確認網關進程的API側還活著。這樣即使稍后出現了異常也能快速判斷問題是在進程層、API層還是底層存儲層。對象存儲的價值是讓應用按需讀寫數據而RGW穩(wěn)定的前提其實是你對它的運行機制有足夠的掌控感。