背景
RGW處理的報(bào)文本質(zhì)上是一個(gè)HTTP報(bào)文,通常情況下使用http://<rgw-ip>:<rgw-port>/<bucket-name>
的方式來(lái)訪問(wèn)一個(gè)bucket。實(shí)際應(yīng)用尤其是公有云環(huán)境中,通常要在rgw前架設(shè)Haproxy等負(fù)載均衡設(shè)備,且將Haproxy的ip:port
映射成一個(gè)域名,方便用戶使用,這個(gè)域名也叫Endpoint。
在擁有Endpoint后,訪問(wèn)一個(gè)bucket的url變成了http://<endpoint>/<bucket-name>
形式。不過(guò)這也只是一個(gè)ip到域名映射,和RGW關(guān)系不大,但后面要說(shuō)的功能就和RGW密切相關(guān)了。
使用url方式訪問(wèn)bucket主要用于靜態(tài)網(wǎng)站托管,既然是網(wǎng)站,當(dāng)然就要盡量滿足常規(guī)網(wǎng)站訪問(wèn)形式。為此出現(xiàn)了兩個(gè)需求:
需求1:bucket-domain方式訪問(wèn)
bucket-domain方式是指以http://<bucket-name>.<endpoint>
形式訪問(wèn)bucket。
如果用戶在公有云上托管了一個(gè)網(wǎng)站,以http://<myblog>.aws-s3.com
形式訪問(wèn)肯定要好于http://aws-s3.com/myblog
,前者域名看起來(lái)更像是一個(gè)獨(dú)立網(wǎng)站。
需求2:private-domain
private-domain方式是指使用自己的域名訪問(wèn)特定bucket。
如果用戶自己已經(jīng)有了現(xiàn)成的域名,那直接使用肯定是更接地氣,而且訪問(wèn)者完全不知道這個(gè)網(wǎng)站到底是托管在公有云上還是使用的獨(dú)立主機(jī)。
細(xì)說(shuō)
bucket-domain方式代碼實(shí)現(xiàn)
首先,對(duì)象存儲(chǔ)服務(wù)提供方需要設(shè)置DNS,將<endpoint>
下的子域解析到RGW或其前端的負(fù)載均衡設(shè)備。
當(dāng)HTTP請(qǐng)求到達(dá)RGW后,請(qǐng)求中會(huì)攜帶初始host請(qǐng)求信息,即<bucket-name>.<endpoint>
,RGW會(huì)根據(jù)配置的domain信息,將這個(gè)host信息解析成subdomain和domain兩部分,分別對(duì)應(yīng)bucket-name和endpoint,隨后重新構(gòu)造一個(gè)request url path,格式為/<bucket-name>,至此,整個(gè)邏輯回到了最原始的以http://<endpoint>/<bucket-name>
訪問(wèn)時(shí)的狀態(tài)。
int RGWREST::preprocess(struct req_state *s, RGWClientIO* cio)
{
req_info& info = s->info; //info中存有此次請(qǐng)求相關(guān)的信息
...
if (info.host.size()) { // info.host中存放的就是用戶請(qǐng)求的url domain部分
ldout(s->cct, 10) << "host=" << info.host << dendl;
string domain;
string subdomain;
bool in_hosted_domain_s3website = false;
bool in_hosted_domain = rgw_find_host_in_domains(info.host, &domain, &subdomain, hostnames_set);
...
if (in_hosted_domain && !subdomain.empty()) { //重新構(gòu)建request uri
string encoded_bucket = "/";
encoded_bucket.append(subdomain);
if (s->info.request_uri[0] != '/')
encoded_bucket.append("/");
encoded_bucket.append(s->info.request_uri);
s->info.request_uri = encoded_bucket;
}
...
private-domain方式代碼實(shí)現(xiàn)
首先,用戶需要將自己的域名配置一條CNAME,使對(duì)域名的請(qǐng)求跳轉(zhuǎn)到<bucket-name>.<endpoint>
。
當(dāng)HTTP請(qǐng)求到達(dá)RGW后,請(qǐng)求中攜帶當(dāng)host信息是<private-domain>
,RGW首先查詢自己的domain配置信息,如果沒(méi)有找到和這個(gè)域名相關(guān)的內(nèi)容,則向DNS服務(wù)器請(qǐng)求,期待返回一個(gè)自己能使用的CNAME domain。
int RGWREST::preprocess(struct req_state *s, RGWClientIO* cio)
{
req_info& info = s->info;
...
/* 這一段和bucket-domain一樣,首先嘗試在rgw已配置的domain信息中進(jìn)行解析 */
if (info.host.size()) {
ldout(s->cct, 10) << "host=" << info.host << dendl;
string domain;
string subdomain;
bool in_hosted_domain_s3website = false;
bool in_hosted_domain = rgw_find_host_in_domains(info.host, &domain, &subdomain, hostnames_set);
string s3website_domain;
string s3website_subdomain;
if (s3website_enabled) {
in_hosted_domain_s3website = rgw_find_host_in_domains(info.host, &s3website_domain, &s3website_subdomain, hostnames_s3website_set);
if (in_hosted_domain_s3website) {
in_hosted_domain = true; // TODO: should hostnames be a strict superset of hostnames_s3website?
domain = s3website_domain;
subdomain = s3website_subdomain;
}
}
...
/*解析失敗后嘗試請(qǐng)求DNS,得到CNAME后使用CNAME重新解析*/
if (g_conf->rgw_resolve_cname
&& !in_hosted_domain
&& !in_hosted_domain_s3website) {
string cname;
bool found;
int r = rgw_resolver->resolve_cname(info.host, cname, &found);
if (r < 0) {
ldout(s->cct, 0)
<< "WARNING: rgw_resolver->resolve_cname() returned r=" << r
<< dendl;
}
if (found) {
ldout(s->cct, 5) << "resolved host cname " << info.host << " -> "
<< cname << dendl;
in_hosted_domain =
rgw_find_host_in_domains(cname, &domain, &subdomain, hostnames_set);
...
/* 解析成功后,后面的邏輯就又回到了bucket-domain上,即重新構(gòu)建request uri,然后就進(jìn)入了常規(guī)處理階段。*/
...
配置RGW domain信息
前面提到RGW會(huì)根據(jù)自己配置的domain信息對(duì)用戶的host進(jìn)行解析,這個(gè)domain信息是一個(gè)域名列表,列表包括RGW可以識(shí)別的domain,由于存在常規(guī)s3和s3website兩種訪問(wèn)方式,因此會(huì)有兩個(gè)domain信息配置列表
//file: src/rgw/rgw_rest.cc
static set<string> hostnames_set;
static set<string> hostnames_s3website_set;
列表初始化時(shí)會(huì)加載rgw_dns_name配置項(xiàng),但此配置項(xiàng)只能配置一條domain,因此如果需要增加多條domain(比如使用private-domain方式,但RGW又無(wú)法和解析private-domain的DNS通信的情況下),需要修改zonegroup的hostnames和hostnames_s3website配置。
radosgw-admin zonegroup get > zonegroup.conf
按需修改 zonegroup.conf文件中的hostnames和hostnames_s3website
radosgw-admin zonegroup set --infile=zonegroup.conf
總結(jié)
域名訪問(wèn)分三個(gè)方式
- 初級(jí)方式:
http://<public-cloud-domain>/<bucket-name>
- 中級(jí)方式:
http://<bucket-name>.<public-cloud-domain>
- 高級(jí)方式:
http://<private-domain>
前兩種方式比較簡(jiǎn)單,無(wú)需用戶進(jìn)行額外操作。
第三種方式需要用戶配置DNS CNAME,將請(qǐng)求轉(zhuǎn)發(fā)到http://<bucket-name>.<public-cloud-domain>
上。這種場(chǎng)景需要注意的是,RGW要能夠訪問(wèn)到用戶配置了CNAME的DNS服務(wù)器,否則只能通過(guò)增加RGW domain配置信息的方式來(lái)進(jìn)行彌補(bǔ)。