注意

本文档适用于 Ceph 开发版本。

librados 简介

The Ceph 存储集群提供了基本存储服务,允许Ceph独特地提供对象、块和文件存储在一个RADOSlibradosAPI 使您能够创建自己的 Ceph 存储集群接口。

The libradosAPI 使您能够与 Ceph 存储集群中的两种类型的守护进程交互:

本指南提供了使用librados的高级介绍。架构获取有关 Ceph安装(快速) for details.

第 1 步:获取 librados

您的客户端应用程序必须与librados绑定以连接到 Cephlibrados以及任何所需的软件包来编写使用librados一起使用。该libradosAPI 是用

获取 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,您需要执行以下步骤:

  1. 在将要成为 RGW 节点的节点上安装jna.jar. 对于 Debian/Ubuntu,执行:

    sudo apt-get install libjna-java
    

    对于 CentOS/RHEL,执行:

    sudo yum install jna
    

    JAR 文件位于/usr/share/java.

  2. 克隆rados-java仓库:

    git clone --recursive https://github.com/ceph/rados-java.git
    
  3. 构建rados-java仓库:

    cd rados-java
    ant
    

    JAR 文件位于rados-java/target.

  4. 将 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

要安装libradosPHP 扩展,您需要执行以下步骤:

  1. 安装 php-dev。对于 Debian/Ubuntu,执行:

    sudo apt-get install php5-dev build-essential
    

    对于 CentOS/RHEL,执行:

    sudo yum install php-devel
    
  2. 克隆phprados仓库:

    git clone https://github.com/ceph/phprados.git
    
  3. 构建phprados:

    cd phprados
    phpize
    ./configure
    make
    sudo make install
    
  4. 启用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 存储集群句柄封装了客户端配置,包括:

  • The 用户 IDforrados_create()或用户名用于rados_create2()(首选)。

  • The cephx身份验证密钥

  • 监视器 ID 和 IP 地址

  • 日志级别

  • 调试级别

因此,使用应用程序连接到集群的第一步是 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 默认使用adminID 和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. 如果您想支持这一点和我们的其他工作,请考虑加入现在加入.