注意
本文档适用于 Ceph 开发版本。
librados 简介
The Ceph 存储集群提供了基本存储服务,允许Ceph独特地提供对象、块和文件存储在一个RADOS,librados
API 使您能够创建自己的 Ceph 存储集群接口。
The librados
API 使您能够与 Ceph 存储集群中的两种类型的守护进程交互:
The Ceph监控器,它维护集群地图的主副本。
The Ceph OSD 守护进程(OSD),它将数据作为对象存储在存储节点上。
本指南提供了使用librados
的高级介绍。架构获取有关 Ceph安装(快速) for details.
第 1 步:获取 librados
您的客户端应用程序必须与librados
绑定以连接到 Cephlibrados
以及任何所需的软件包来编写使用librados
一起使用。该librados
API 是用
获取 C/C++ 的 librados
要安装librados
在 Debian/Ubuntu 发行版上获取 C/C++ 开发支持文件,执行以下操作:
sudo apt-get install librados-dev
要安装librados
在 RHEL/CentOS 发行版上获取 C/C++ 开发支持文件,执行以下操作:
sudo yum install librados2-devel libradospp-devel
安装librados
开发者,您可以在/usr/include/rados
:
ls /usr/include/rados
获取 Python 的 librados
The rados
模块为 Pythonlibrados
支持。您可以为 Debian、Ubuntu、SLE 或python3-rados
或 CentOS/RHEL 的python-rados
软件包。
要安装librados
在 Debian/Ubuntu 发行版上获取 Python 开发支持文件,执行以下操作:
sudo apt-get install python3-rados
要安装librados
在 RHEL/CentOS 发行版上获取 Python 开发支持文件,执行以下操作:
sudo yum install python-rados
要安装librados
在 SLE/openSUSE 发行版上获取 Python 开发支持文件,执行以下操作:
sudo zypper install python3-rados
您可以在/usr/share/pyshared
在 Debian 系统上,或/usr/lib/python*/site-packages
在 CentOS/RHEL 系统上。
获取 Java 的 librados
要安装librados
对于 Java,您需要执行以下步骤:
在将要成为 RGW 节点的节点上安装
jna.jar
. 对于 Debian/Ubuntu,执行:sudo apt-get install libjna-java
对于 CentOS/RHEL,执行:
sudo yum install jna
JAR 文件位于
/usr/share/java
.克隆
rados-java
仓库:git clone --recursive https://github.com/ceph/rados-java.git
构建
rados-java
仓库:cd rados-java ant
JAR 文件位于
rados-java/target
.将 RADOS 的 JAR 复制到公共位置(例如,
/usr/share/java
)并sudo cp target/rados-0.1.3.jar /usr/share/java/rados-0.1.3.jar sudo ln -s /usr/share/java/jna-3.2.7.jar /usr/lib/jvm/default-java/jre/lib/ext/jna-3.2.7.jar sudo ln -s /usr/share/java/rados-0.1.3.jar /usr/lib/jvm/default-java/jre/lib/ext/rados-0.1.3.jar
要构建文档,执行以下操作:
ant docs
获取 PHP 的 librados
要安装librados
PHP 扩展,您需要执行以下步骤:
安装 php-dev。对于 Debian/Ubuntu,执行:
sudo apt-get install php5-dev build-essential
对于 CentOS/RHEL,执行:
sudo yum install php-devel
克隆
phprados
仓库:git clone https://github.com/ceph/phprados.git
构建
phprados
:cd phprados phpize ./configure make sudo make install
启用
phprados
通过向php.ini
:extension=rados.so
第 2 步:配置集群句柄
A Ceph Client,通过librados
,直接与 OSDs 交互以存储和检索数据。要交互 OSDs,客户端应用程序必须调用librados
并连接到 Ceph 监视器。连接后,librados
从 Ceph 监视器检索集群地图。当客户端应用程序想要读取或写入数据时,它创建一个 I/O 上下文并将其绑定到池。池有一个相关的CRUSH 规则它定义了它将如何在存储集群中放置数据。通过 I/O 上下文,客户端提供对象名称给librados
,它将对象名称和集群地图(即集群的拓扑结构)一起计算的OSD以定位数据。然后客户端应用程序可以读取或写入数据。客户端应用程序不需要直接了解集群的拓扑结构。
Ceph 存储集群句柄封装了客户端配置,包括:
因此,使用应用程序连接到集群的第一步是 1) 创建一个应用程序将用于连接到存储集群的集群句柄,然后 2) 使用该句柄连接。要连接到集群,应用程序必须提供一个监视器地址、一个用户名和一个身份验证密钥(cephx 默认启用)。
提示
与不同的 Ceph 存储集群——或使用不同的用户与同一个集群——交谈需要不同的集群句柄。
RADOS 提供了多种方法供您设置所需值。对于监视器和加密密钥设置,处理它们的一种简单方法是为您的 Ceph 配置文件包含一个keyring
密钥环文件路径mon_host
紧随其后时出现在选项值中。例如:
[global]
mon_host = 192.168.1.1
keyring = /etc/ceph/ceph.client.admin.keyring
一旦您创建了句柄,您可以读取 Ceph 配置文件来配置句柄。您还可以将参数传递给您的应用程序,并使用解析命令行参数的函数(例如,rados_conf_parse_argv()
)来解析它们,或者解析 Ceph 环境变量(例如,rados_conf_parse_env()
)。某些包装器可能不会实现便利方法,因此您可能需要实现这些功能。以下图表提供了初始连接的高级流程。
连接后,您的应用程序可以使用集群句柄调用影响整个集群的函数。例如,一旦您有了集群句柄,您就可以:
获取集群统计信息
使用池操作(存在、创建、列出、删除)
获取和设置配置
Ceph 的一项强大功能是能够绑定到不同的池。每个池可能有不同数量的放置组、对象副本和复制策略。例如,一个池可以设置为使用 SSD 存储频繁使用的对象的“热”池,或者使用纠删码的“冷”池。
各种librados
绑定的主要区别在于 C 和面向对象的 C++、Java 和 Python 绑定。面向对象的绑定使用对象来表示集群句柄、IO 上下文、迭代器、异常等。
C 示例
对于 C,使用admin
创建一个简单的集群句柄,配置它并连接到集群可能如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rados/librados.h>
int main (int argc, const char **argv)
{
/* Declare the cluster handle and required arguments. */
rados_t cluster;
char cluster_name[] = "ceph";
char user_name[] = "client.admin";
uint64_t flags = 0;
/* Initialize the cluster handle with the "ceph" cluster name and the "client.admin" user */
int err;
err = rados_create2(&cluster, cluster_name, user_name, flags);
if (err < 0) {
fprintf(stderr, "%s: Couldn't create the cluster handle! %s\n", argv[0], strerror(-err));
exit(EXIT_FAILURE);
} else {
printf("\nCreated a cluster handle.\n");
}
/* Read a Ceph configuration file to configure the cluster handle. */
err = rados_conf_read_file(cluster, "/etc/ceph/ceph.conf");
if (err < 0) {
fprintf(stderr, "%s: cannot read config file: %s\n", argv[0], strerror(-err));
exit(EXIT_FAILURE);
} else {
printf("\nRead the config file.\n");
}
/* Read command line arguments */
err = rados_conf_parse_argv(cluster, argc, argv);
if (err < 0) {
fprintf(stderr, "%s: cannot parse command line arguments: %s\n", argv[0], strerror(-err));
exit(EXIT_FAILURE);
} else {
printf("\nRead the command line arguments.\n");
}
/* Connect to the cluster */
err = rados_connect(cluster);
if (err < 0) {
fprintf(stderr, "%s: cannot connect to cluster: %s\n", argv[0], strerror(-err));
exit(EXIT_FAILURE);
} else {
printf("\nConnected to the cluster.\n");
}
}
编译您的客户端并链接到librados
使用-lrados
从仓库中删除它。例如:
gcc ceph-client.c -lrados -o ceph-client
C++ 示例
Ceph 项目在ceph/examples/librados
目录中提供了一个 C++ 示例。对于 C++,使用admin
用户创建一个简单的集群句柄需要初始化一个librados::Rados
集群句柄对象:
#include <iostream>
#include <string>
#include <rados/librados.hpp>
int main(int argc, const char **argv)
{
int ret = 0;
/* Declare the cluster handle and required variables. */
librados::Rados cluster;
char cluster_name[] = "ceph";
char user_name[] = "client.admin";
uint64_t flags = 0;
/* Initialize the cluster handle with the "ceph" cluster name and "client.admin" user */
{
ret = cluster.init2(user_name, cluster_name, flags);
if (ret < 0) {
std::cerr << "Couldn't initialize the cluster handle! error " << ret << std::endl;
return EXIT_FAILURE;
} else {
std::cout << "Created a cluster handle." << std::endl;
}
}
/* Read a Ceph configuration file to configure the cluster handle. */
{
ret = cluster.conf_read_file("/etc/ceph/ceph.conf");
if (ret < 0) {
std::cerr << "Couldn't read the Ceph configuration file! error " << ret << std::endl;
return EXIT_FAILURE;
} else {
std::cout << "Read the Ceph configuration file." << std::endl;
}
}
/* Read command line arguments */
{
ret = cluster.conf_parse_argv(argc, argv);
if (ret < 0) {
std::cerr << "Couldn't parse command line options! error " << ret << std::endl;
return EXIT_FAILURE;
} else {
std::cout << "Parsed command line options." << std::endl;
}
}
/* Connect to the cluster */
{
ret = cluster.connect();
if (ret < 0) {
std::cerr << "Couldn't connect to cluster! error " << ret << std::endl;
return EXIT_FAILURE;
} else {
std::cout << "Connected to the cluster." << std::endl;
}
}
return 0;
}
编译源代码;然后,链接librados
使用-lrados
生成。
g++ -g -c ceph-client.cc -o ceph-client.o
g++ -g ceph-client.o -lrados -o ceph-client
Python 示例
Python 默认使用admin
ID 和ceph
集群名称,如果 conffile 参数设置为空字符串,则会读取标准ceph.conf
文件。Python 绑定将 C++ 错误转换为异常。
import rados
try:
cluster = rados.Rados(conffile='')
except TypeError as e:
print('Argument validation error: {}'.format(e))
raise e
print("Created cluster handle.")
try:
cluster.connect()
except Exception as e:
print("connection error: {}".format(e))
raise e
finally:
print("Connected to the cluster.")
执行示例以验证它是否连接到您的集群:
python ceph-client.py
Java 示例
Java 要求您指定用户 ID (admin
) 或用户名client.admin
),并默认使用ceph
集群名称。Java 绑定将基于 C++ 的错误转换为异常。
import com.ceph.rados.Rados;
import com.ceph.rados.RadosException;
import java.io.File;
public class CephClient {
public static void main (String args[]){
try {
Rados cluster = new Rados("admin");
System.out.println("Created cluster handle.");
File f = new File("/etc/ceph/ceph.conf");
cluster.confReadFile(f);
System.out.println("Read the configuration file.");
cluster.connect();
System.out.println("Connected to the cluster.");
} catch (RadosException e) {
System.out.println(e.getMessage() + ": " + e.getReturnValue());
}
}
}
编译源代码;然后,运行它。如果您将 JAR 复制到/usr/share/java
并从您的ext
目录符号链接,则您不需要指定类路径。例如:
javac CephClient.java
java CephClient
PHP 示例
在 PHP 中启用 RADOS 扩展后,您可以轻松地开始创建一个新的集群句柄:
<?php
$r = rados_create();
rados_conf_read_file($r, '/etc/ceph/ceph.conf');
if (!rados_connect($r)) {
echo "Failed to connect to Ceph cluster";
} else {
echo "Successfully connected to Ceph cluster";
}
将其保存为 rados.php 并运行代码:
php rados.php
第 3 步:创建 I/O 上下文
一旦您的应用程序有了集群句柄和与 Ceph 存储集群的连接,您可以创建一个 I/O 上下文并开始读取和写入数据。I/O 上下文将连接绑定到特定的池。用户必须具有适当的CAPS权限才能访问指定的池。例如,具有读取权限但没有写入权限的用户将只能读取数据。I/O 上下文功能包括:
写/读数据和扩展属性
列出和迭代对象和扩展属性
快照池、列出快照等。
RADOS 允许您同步和异步交互。一旦您的应用程序有了 I/O 上下文,读/写操作只需要知道对象/xattr 名称。封装在librados
中的 CRUSH 算法使用集群地图来识别适当的 OSD。OSD 守护进程处理复制,如Smart Daemons Enable Hyperscale一起使用。该librados
库也映射对象到放置组,如计算 PG ID.
以下示例使用默认data
池。但是,您也可以使用 API 列出池、确保它们存在,或创建和删除池。对于写操作,示例说明了如何使用同步模式。对于读操作,示例说明了如何使用异步模式。
重要
使用此 API 删除池时要小心。如果您删除一个池,该池和池中的所有数据都将丢失。
C 示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rados/librados.h>
int main (int argc, const char **argv)
{
/*
* Continued from previous C example, where cluster handle and
* connection are established. First declare an I/O Context.
*/
rados_ioctx_t io;
char *poolname = "data";
err = rados_ioctx_create(cluster, poolname, &io);
if (err < 0) {
fprintf(stderr, "%s: cannot open rados pool %s: %s\n", argv[0], poolname, strerror(-err));
rados_shutdown(cluster);
exit(EXIT_FAILURE);
} else {
printf("\nCreated I/O context.\n");
}
/* Write data to the cluster synchronously. */
err = rados_write(io, "hw", "Hello World!", 12, 0);
if (err < 0) {
fprintf(stderr, "%s: Cannot write object \"hw\" to pool %s: %s\n", argv[0], poolname, strerror(-err));
rados_ioctx_destroy(io);
rados_shutdown(cluster);
exit(1);
} else {
printf("\nWrote \"Hello World\" to object \"hw\".\n");
}
char xattr[] = "en_US";
err = rados_setxattr(io, "hw", "lang", xattr, 5);
if (err < 0) {
fprintf(stderr, "%s: Cannot write xattr to pool %s: %s\n", argv[0], poolname, strerror(-err));
rados_ioctx_destroy(io);
rados_shutdown(cluster);
exit(1);
} else {
printf("\nWrote \"en_US\" to xattr \"lang\" for object \"hw\".\n");
}
/*
* Read data from the cluster asynchronously.
* First, set up asynchronous I/O completion.
*/
rados_completion_t comp;
err = rados_aio_create_completion(NULL, NULL, NULL, &comp);
if (err < 0) {
fprintf(stderr, "%s: Could not create aio completion: %s\n", argv[0], strerror(-err));
rados_ioctx_destroy(io);
rados_shutdown(cluster);
exit(1);
} else {
printf("\nCreated AIO completion.\n");
}
/* Next, read data using rados_aio_read. */
char read_res[100];
err = rados_aio_read(io, "hw", comp, read_res, 12, 0);
if (err < 0) {
fprintf(stderr, "%s: Cannot read object. %s %s\n", argv[0], poolname, strerror(-err));
rados_ioctx_destroy(io);
rados_shutdown(cluster);
exit(1);
} else {
printf("\nRead object \"hw\". The contents are:\n %s \n", read_res);
}
/* Wait for the operation to complete */
rados_aio_wait_for_complete(comp);
/* Release the asynchronous I/O complete handle to avoid memory leaks. */
rados_aio_release(comp);
char xattr_res[100];
err = rados_getxattr(io, "hw", "lang", xattr_res, 5);
if (err < 0) {
fprintf(stderr, "%s: Cannot read xattr. %s %s\n", argv[0], poolname, strerror(-err));
rados_ioctx_destroy(io);
rados_shutdown(cluster);
exit(1);
} else {
printf("\nRead xattr \"lang\" for object \"hw\". The contents are:\n %s \n", xattr_res);
}
err = rados_rmxattr(io, "hw", "lang");
if (err < 0) {
fprintf(stderr, "%s: Cannot remove xattr. %s %s\n", argv[0], poolname, strerror(-err));
rados_ioctx_destroy(io);
rados_shutdown(cluster);
exit(1);
} else {
printf("\nRemoved xattr \"lang\" for object \"hw\".\n");
}
err = rados_remove(io, "hw");
if (err < 0) {
fprintf(stderr, "%s: Cannot remove object. %s %s\n", argv[0], poolname, strerror(-err));
rados_ioctx_destroy(io);
rados_shutdown(cluster);
exit(1);
} else {
printf("\nRemoved object \"hw\".\n");
}
}
C++ 示例
#include <iostream>
#include <string>
#include <rados/librados.hpp>
int main(int argc, const char **argv)
{
/* Continued from previous C++ example, where cluster handle and
* connection are established. First declare an I/O Context.
*/
librados::IoCtx io_ctx;
const char *pool_name = "data";
{
ret = cluster.ioctx_create(pool_name, io_ctx);
if (ret < 0) {
std::cerr << "Couldn't set up ioctx! error " << ret << std::endl;
exit(EXIT_FAILURE);
} else {
std::cout << "Created an ioctx for the pool." << std::endl;
}
}
/* Write an object synchronously. */
{
librados::bufferlist bl;
bl.append("Hello World!");
ret = io_ctx.write_full("hw", bl);
if (ret < 0) {
std::cerr << "Couldn't write object! error " << ret << std::endl;
exit(EXIT_FAILURE);
} else {
std::cout << "Wrote new object 'hw' " << std::endl;
}
}
/*
* Add an xattr to the object.
*/
{
librados::bufferlist lang_bl;
lang_bl.append("en_US");
ret = io_ctx.setxattr("hw", "lang", lang_bl);
if (ret < 0) {
std::cerr << "failed to set xattr version entry! error "
<< ret << std::endl;
exit(EXIT_FAILURE);
} else {
std::cout << "Set the xattr 'lang' on our object!" << std::endl;
}
}
/*
* Read the object back asynchronously.
*/
{
librados::bufferlist read_buf;
int read_len = 4194304;
//Create I/O Completion.
librados::AioCompletion *read_completion = librados::Rados::aio_create_completion();
//Send read request.
ret = io_ctx.aio_read("hw", read_completion, &read_buf, read_len, 0);
if (ret < 0) {
std::cerr << "Couldn't start read object! error " << ret << std::endl;
exit(EXIT_FAILURE);
}
// Wait for the request to complete, and check that it succeeded.
read_completion->wait_for_complete();
ret = read_completion->get_return_value();
if (ret < 0) {
std::cerr << "Couldn't read object! error " << ret << std::endl;
exit(EXIT_FAILURE);
} else {
std::cout << "Read object hw asynchronously with contents.\n"
<< read_buf.c_str() << std::endl;
}
}
/*
* Read the xattr.
*/
{
librados::bufferlist lang_res;
ret = io_ctx.getxattr("hw", "lang", lang_res);
if (ret < 0) {
std::cerr << "failed to get xattr version entry! error "
<< ret << std::endl;
exit(EXIT_FAILURE);
} else {
std::cout << "Got the xattr 'lang' from object hw!"
<< lang_res.c_str() << std::endl;
}
}
/*
* Remove the xattr.
*/
{
ret = io_ctx.rmxattr("hw", "lang");
if (ret < 0) {
std::cerr << "Failed to remove xattr! error "
<< ret << std::endl;
exit(EXIT_FAILURE);
} else {
std::cout << "Removed the xattr 'lang' from our object!" << std::endl;
}
}
/*
* Remove the object.
*/
{
ret = io_ctx.remove("hw");
if (ret < 0) {
std::cerr << "Couldn't remove object! error " << ret << std::endl;
exit(EXIT_FAILURE);
} else {
std::cout << "Removed object 'hw'." << std::endl;
}
}
}
Python 示例
print("\n\nI/O Context and Object Operations")
print("=================================")
print("\nCreating a context for the 'data' pool")
if not cluster.pool_exists('data'):
raise RuntimeError('No data pool exists')
ioctx = cluster.open_ioctx('data')
print("\nWriting object 'hw' with contents 'Hello World!' to pool 'data'.")
ioctx.write("hw", b"Hello World!")
print("Writing XATTR 'lang' with value 'en_US' to object 'hw'")
ioctx.set_xattr("hw", "lang", b"en_US")
print("\nWriting object 'bm' with contents 'Bonjour tout le monde!' to pool
'data'.")
ioctx.write("bm", b"Bonjour tout le monde!")
print("Writing XATTR 'lang' with value 'fr_FR' to object 'bm'")
ioctx.set_xattr("bm", "lang", b"fr_FR")
print("\nContents of object 'hw'\n------------------------")
print(ioctx.read("hw"))
print("\n\nGetting XATTR 'lang' from object 'hw'")
print(ioctx.get_xattr("hw", "lang"))
print("\nContents of object 'bm'\n------------------------")
print(ioctx.read("bm"))
print("\n\nGetting XATTR 'lang' from object 'bm'")
print(ioctx.get_xattr("bm", "lang"))
print("\nRemoving object 'hw'")
ioctx.remove_object("hw")
print("Removing object 'bm'")
ioctx.remove_object("bm")
Java 示例
import com.ceph.rados.Rados;
import com.ceph.rados.RadosException;
import java.io.File;
import com.ceph.rados.IoCTX;
public class CephClient {
public static void main (String args[]){
try {
Rados cluster = new Rados("admin");
System.out.println("Created cluster handle.");
File f = new File("/etc/ceph/ceph.conf");
cluster.confReadFile(f);
System.out.println("Read the configuration file.");
cluster.connect();
System.out.println("Connected to the cluster.");
IoCTX io = cluster.ioCtxCreate("data");
String oidone = "hw";
String contentone = "Hello World!";
io.write(oidone, contentone);
String oidtwo = "bm";
String contenttwo = "Bonjour tout le monde!";
io.write(oidtwo, contenttwo);
String[] objects = io.listObjects();
for (String object: objects)
System.out.println(object);
io.remove(oidone);
io.remove(oidtwo);
cluster.ioCtxDestroy(io);
} catch (RadosException e) {
System.out.println(e.getMessage() + ": " + e.getReturnValue());
}
}
}
PHP 示例
<?php
$io = rados_ioctx_create($r, "mypool");
rados_write_full($io, "oidOne", "mycontents");
rados_remove("oidOne");
rados_ioctx_destroy($io);
第 4 步:关闭会话
一旦您的应用程序使用完 I/O 上下文和集群句柄,应用程序应该关闭连接并关闭句柄。对于异步 I/O,应用程序还应该确保完成的异步操作。
C 示例
rados_ioctx_destroy(io);
rados_shutdown(cluster);
C++ 示例
io_ctx.close();
cluster.shutdown();
Java 示例
cluster.ioCtxDestroy(io);
cluster.shutDown();
Python 示例
print("\nClosing the connection.")
ioctx.close()
print("Shutting down the handle.")
cluster.shutdown()
PHP 示例
rados_shutdown($r);
由 Ceph 基金会带给您
Ceph 文档是一个社区资源,由非盈利的 Ceph 基金会资助和托管Ceph Foundation. 如果您想支持这一点和我们的其他工作,请考虑加入现在加入.