Ceph и подключение клиентов. Ceph Object Gateway и S3

Помимо блочного и файлового типов доступа к данным, Ceph так же поддерживает объектный доступ с помощью привычных API S3, либо Swift.

В данном случае мы посмотрим, какие настройки необходимо выполнить со стороны Ceph, чтобы предоставить клиентам возможность управления данными с помощью S3 API.

Напомню, что ранее процедура установки Ceph 17 с нуля мной уже была описана в этой статье. В данном случае я использую эту же площадку, а также клиента на базе Rocky Linux 8.6.

Про подключение блочных устройств с помощью RBD я ранее писал здесь.

Объектное хранение в Ceph осуществляется с помощью компонента под названием Object Gateway, который ранее назывался RADOS Gateway или RGW. Данный компонент принимает от клиентов HTTP/HTTPS запросы и выполняет соответствующие действия со стороны Ceph.

Процедура предоставления доступа клиентам по S3 достаточно простая:

  1. Необходимо создать Object Gateway;
  2. Создать пользователя S3 и передать клиенту Access Key и Secret Key;
  3. Клиент, используя привычный S3 API размещает данные в Ceph.

Начнем с того, что проверим текущее количество узлов в кластере:

[root@ceph-mon-01 ~]# ceph orch host ls
HOST         ADDR         LABELS  STATUS
ceph-mon-01  10.10.10.13  _admin
ceph-mon-02  10.10.10.14
ceph-mon-03  10.10.10.15
ceph-osd-01  10.10.10.16
ceph-osd-02  10.10.10.17
ceph-osd-03  10.10.10.18
6 hosts in cluster

В моем кластере 6 узлов, 3 из которых используются для системных служб, на 3 оставшихся хранятся данные и используются они только под OSD.

Для организации Object Gateway, я добавляю к текущему кластеру дополнительно два новых узла. Нетрудно догадаться, что делается это для отказоустойчивости. В случае выхода из строя одного узла, нагрузка будет перенесена на второй.

Кстати, в документации рекомендуется использование хотя бы трех узлов, но в лабораторном случае, думаю, это можно опустить.

Новые хосты называются ceph-rgw-01 и ceph-rgw-02. Предварительно их необходимо подготовить и задать короткий Hostname (указывать FQDN не нужно), а также установить Python 3 и Podman (либо Docker):

[root@ceph-rgw-01 ~]# dnf -y install python39 podman
[root@ceph-rgw-02 ~]# dnf -y install python39 podman

Далее все действия выполняются с управляющего узла. В моем случае это ceph-mon-01. Скопируем сертификат для беспарольного SSH-доступа с mon01 на rgw01/02:

[root@ceph-mon-01 ~]# ssh-copy-id -f -i /etc/ceph/ceph.pub root@ceph-rgw-01
[root@ceph-mon-01 ~]# ssh-copy-id -f -i /etc/ceph/ceph.pub root@ceph-rgw-02

Передадим узлы в управление Ceph:

[root@ceph-mon-01 ~]# ceph orch host add ceph-rgw-01
Added host 'ceph-rgw-01' with addr '10.10.10.20'
[root@ceph-mon-01 ~]# ceph orch host add ceph-rgw-02
Added host 'ceph-rgw-02' with addr '10.10.10.21'

Список хостов в кластере теперь выглядит следующим образом:

[root@ceph-mon-01 ~]# ceph orch host ls
HOST         ADDR         LABELS  STATUS
ceph-mon-01  10.10.10.13  _admin
ceph-mon-02  10.10.10.14
ceph-mon-03  10.10.10.15
ceph-osd-01  10.10.10.16
ceph-osd-02  10.10.10.17
ceph-osd-03  10.10.10.18
ceph-rgw-01  10.10.10.20
ceph-rgw-02  10.10.10.21
8 hosts in cluster

Можно заметить новые узлы ceph-rgw-01 и ceph-rgw-02.

Установим для данных узлов метку rgw:

[root@ceph-mon-01 ~]# ceph orch host label add ceph-rgw-01 rgw
Added label rgw to host ceph-rgw-01
[root@ceph-mon-01 ~]# ceph orch host label add ceph-rgw-02 rgw
Added label rgw to host ceph-rgw-02

Теперь запустим службу RGW (Ceph Object Gateway) на всех узлах, где ранее была установлена метка rgw и используем порт 8080. На каждом узле будет запущено по одной службе RGW, но можно и больше:

[root@ceph-mon-01 ~]# ceph orch apply rgw s3.vmik.lab '--placement=label:rgw count-per-host:1' --port=8080
Scheduled rgw.s3.vmik.lab update...

Если перейти на любой из узлов rgw, можно увидеть новый контейнер, а также открытый порт 8080:

[root@ceph-rgw-01 ~]# podman ps
CONTAINER ID  IMAGE                                                                                      COMMAND               CREATED         STATUS             PORTS       NAMES
6e83e4590364  quay.io/ceph/ceph@sha256:b4e95ad6b1a4fb02b3d5c3fc8cbdb91431d03fdf5c6d904f8a5cf1efb4053f27  -n client.rgw.s3....  48 seconds ago  Up 48 seconds ago              ceph-2178c624-0901-11ed-ba0a-005056a97edd-rgw-s3-vmik-lab-ceph-rgw-01-lwxrag
[root@ceph-rgw-01 ~]# firewall-cmd --list-ports
8080/tcp 9100/tcp

Статус кластера будет также отображать наличие служб rgw:

[root@ceph-mon-01 ~]# ceph -s
  cluster:
    id:     2178c624-0901-11ed-ba0a-005056a97edd
    health: HEALTH_OK

  services:
    mon: 3 daemons, quorum ceph-mon-01,ceph-mon-02,ceph-mon-03 (age 5w)
    mgr: ceph-mon-01.khfosw(active, since 5w), standbys: ceph-mon-02.cfeaei
    osd: 9 osds: 9 up (since 5w), 9 in (since 2M); 1 remapped pgs
    rgw: 2 daemons active (2 hosts, 1 zones)

Если посмотреть список пулов, можно заметить три новых:

[root@ceph-mon-01 ~]# ceph osd lspools
1 .mgr
3 rbd_images_pool
4 .rgw.root
5 default.rgw.log
6 default.rgw.control
7 default.rgw.meta

Предыдущей командой мы запустили Ceph RGW на двух узлах и, в принципе, к каждому из них можно подключаться и выполнять S3 запросы, если обратиться на порт 8080:

[root@ceph-mon-01 ~]# curl ceph-rgw-01.vmik.lab:8080
<?xml version="1.0" encoding="UTF-8"?><ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Owner><ID>anonymous</ID><DisplayName></DisplayName></Owner><Buckets></Buckets></ListAllMyBucketsResult>
[root@ceph-mon-01 ~]# curl ceph-rgw-02.vmik.lab:8080
<?xml version="1.0" encoding="UTF-8"?><ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Owner><ID>anonymous</ID><DisplayName></DisplayName></Owner><Buckets></Buckets></ListAllMyBucketsResult>

Однако, в случае выхода из строя любого из шлюзов, клиенту придется менять адрес подключения и это плохо.

Данная проблема решается с помощью развертывания служб Ingress на базе HAProxy, а также общего IP-адреса с помощью Keepalived.

Для развертывания Ingress нужно создать соответствующий yaml манифест:

[root@ceph-mon-01 ~]# vi ./rgw_ingress.yaml
service_type: ingress
service_id: rgw.s3.vmik.lab
placement:
  hosts:
    - ceph-rgw-01
    - ceph-rgw-02
spec:
  backend_service: rgw.s3.vmik.lab
  virtual_ip: 10.10.10.22/24
  frontend_port: 80
  monitor_port: 1967
  virtual_interface_networks: 10.10.10.0/24

Где:

service_id – рекомендуется называть по аналогии с сервисом RGW;

hosts – список узлов, на которых будут развернуты службы Ingress. Это не обязательно должны быть те же самые узлы, где работают службы RGW, но в моем случае это так;

backend_service – название сервиса RGW, для которого применяется данный Ingress;

virtual_ip – адрес, который будет использоваться как точка входа для клиентов. В случае выхода из строя одного из узлов, данный адрес перемещается на другой узел;

frontend_port – порт, используемый клиентами для подключения. Важный момент – если используются одни и те же хосты под RGW и Ingress, порты RGW и Ingress должны различаться. В моем случае RGW использует порт 8080, а Ingress 80.

Применим данный манифест:

[root@ceph-mon-01 ~]# ceph orch apply -i rgw_ingress.yaml
Scheduled ingress.rgw.s3.vmik.lab update...

Если перейти на любой из узлов RGW, можно увидеть два новых контейнера:

[root@ceph-rgw-01 ~]# podman ps
cea8c4bca503  docker.io/arcts/keepalived:latest                                                          ./init.sh             22 seconds ago  Up 22 seconds ago              ceph-2178c624-0901-11ed-ba0a-005056a97edd-keepalived-rgw-s3-vmik-lab-ceph-rgw-01-lkhddg
79b360010d09  docker.io/library/haproxy:2.3                                                              haproxy -f /var/l...  16 seconds ago  Up 17 seconds ago              ceph-2178c624-0901-11ed-ba0a-005056a97edd-haproxy-rgw-s3-vmik-lab-ceph-rgw-01-ksjmla

HAProxy отвечает за прием запросов от клиентов и перенаправление их в RGW, а Keepalived за доступность общего IP-адреса.

В списке служб Ceph у нас так же пополнение. Служба Ingress:

[root@ceph-mon-01 ~]# ceph orch ls
NAME                     PORTS                RUNNING  REFRESHED  AGE  PLACEMENT
alertmanager             ?:9093,9094              1/1  39s ago    2M   count:1
crash                                             8/8  9m ago     2M   *
grafana                  ?:3000                   1/1  39s ago    2M   count:1
ingress.rgw.s3.vmik.lab  10.10.10.22:80,1967      4/4  40s ago    81s  ceph-rgw-01;ceph-rgw-02
mgr                                               2/2  9m ago     2M   count:2
mon                                               3/3  9m ago     2M   ceph-mon-01;ceph-mon-02;ceph-mon-03;count:3
node-exporter            ?:9100                   8/8  9m ago     2M   *
osd                                                 9  9m ago     -    <unmanaged>
prometheus               ?:9095                   1/1  39s ago    2M   count:1
rgw.s3.vmik.lab          ?:8080                   2/2  40s ago    51m  count-per-host:1;label:rgw

Теперь можно обратиться к общему адресу по порту 80:

[root@ceph-mon-01 ~]# curl ceph-rgw.vmik.lab
<?xml version="1.0" encoding="UTF-8"?><ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Owner><ID>anonymous</ID><DisplayName></DisplayName></Owner><Buckets></Buckets></ListAllMyBucketsResult>

В моем случае это DNS имя ceph-rgw.vmik.lab, ссылающееся на адрес 10.10.10.22 (общий адрес Ingress).

Таким образом мы настроили службы RGW и добавили немного отказоустойчивости за счет Ingress. Теперь при подключении клиентов к общему Ingress адресу, потеря любого из узлов не потребует изменение настроек подключения, а перемещение адреса с узла на узел не превышает секунды.

Следующий этап. Мы обеспечили точку входа и теперь необходимо создать учетные записи и передать данные для доступа клиентам.

Все управление производится с помощью radosgw-admin.

Создадим учетную запись:

[root@ceph-mon-01 ~]# radosgw-admin user create --uid=vmik --display-name="VMIK Test" --email=vmik@vmik.lab
{
    "user_id": "vmik",
    "display_name": "VMIK Test",
    "email": "vmik@vmik.lab",
    "suspended": 0,
    "max_buckets": 1000,
    "subusers": [],
    "keys": [
        {
            "user": "vmik",
            "access_key": "O2TY3UOYYF2CBQasda",
            "secret_key": "d08o34L2hrhjsqCoo8vycasdasd"
        }
    ],
    "swift_keys": [],
    "caps": [],
    "op_mask": "read, write, delete",
    "default_placement": "",
    "default_storage_class": "",
    "placement_tags": [],
    "bucket_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "user_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "temp_url_keys": [],
    "type": "rgw",
    "mfa_ids": []
}

Важные поля здесь access_key и secret_key, которые необходимо передать клиенту.

Можно обратить внимание, что изначально никаких квот и ограничений для пользователя нет, что, скорее всего, неправильно.

Установить квоты мы можем как на размер bucket, так и целиком на пользователя. Подробнее про тонкости можно почитать в документации.

Выставим квоту в 3GB пользователю:

[root@ceph-mon-01 ~]# radosgw-admin quota set --quota-scope=user --uid=vmik --max-size=3G
[root@ceph-mon-01 ~]# radosgw-admin quota enable --quota-scope=user --uid=vmik

Теперь, если просмотреть информацию о пользователе, можно увидеть наличие квот:

[root@ceph-mon-01 ~]# radosgw-admin user info --uid vmik
    "bucket_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "user_quota": {
        "enabled": true,
        "check_on_raw": false,
        "max_size": 3221225472,
        "max_size_kb": 3145728,
        "max_objects": -1
    }

Кстати, квоты отрабатывают не моментально и рекомендуется почитать внимательно секцию Quota Cache в документации.

Когда пользователь создан, а квоты установлены, можно попробовать подключиться к хранилищу. Здесь кому как удобнее и привычнее работать с S3.

Для себя и тестов я написал небольшую программу на python с использованием библиотеки boto, которая выполняет базовое взаимодействие по S3 API.

Для начала я пробую создать несколько bucket:

What do you want to do? (Print help if you don't know what to do): bucket_create
Enter bucket name: vmik
--------------------------------------------------
What do you want to do? (Print help if you don't know what to do): bucket_create
Enter bucket name: vmik2
--------------------------------------------------
What do you want to do? (Print help if you don't know what to do): bucket_create
Enter bucket name: vmik3
--------------------------------------------------

What do you want to do? (Print help if you don't know what to do): bucket_list
My buckets:
vmik
vmik2
vmik3

В то же время в Ceph:

[root@ceph-mon-01 ~]# radosgw-admin bucket list
[
    "vmik2",
    "vmik3",
    "vmik"
]

Теперь загружу файл в bucket vmik:

What do you want to do? (Print help if you don't know what to do): file_upload
Enter file path: /mnt/d/CentOS-7-x86_64-Minimal-2009.iso
Enter bucket name: vmik
--------------------------------------------------
What do you want to do? (Print help if you don't know what to do): bucket_files
Enter bucket name: vmik
CentOS-7-x86_64-Minimal-2009.iso        2022-10-17T02:58:28.416Z
--------------------------------------------------

Проверим статистику пользователя:

[root@ceph-mon-01 ~]# radosgw-admin user stats --uid vmik --sync-stats
{
    "stats": {
        "size": 1020264448,
        "size_actual": 1020264448,
        "size_kb": 996352,
        "size_kb_actual": 996352,
        "num_objects": 1
    },
    "last_stats_sync": "2022-10-17T03:01:16.739986Z",
    "last_stats_update": "2022-10-17T03:01:16.737704Z"
}

Видно, что некоторый объем пользователем уже занят.

Удалю ранее загруженный файл:

What do you want to do? (Print help if you don't know what to do): file_delete
Enter bucket name: vmik
Enter file name to delete: CentOS-7-x86_64-Minimal-2009.iso

Ceph сообщает о высвобождении пространства:

[root@ceph-mon-01 ~]# radosgw-admin user stats --uid vmik --sync-stats
{
    "stats": {
        "size": 0,
        "size_actual": 0,
        "size_kb": 0,
        "size_kb_actual": 0,
        "num_objects": 0
    },
    "last_stats_sync": "2022-10-17T03:02:29.482104Z",
    "last_stats_update": "2022-10-17T03:02:29.479980Z"
}

Кстати, после создания bucket и загрузки данных, в Ceph появятся новые пулы:

[root@ceph-mon-01 ~]# ceph osd lspools
1 .mgr
3 rbd_images_pool
4 .rgw.root
5 default.rgw.log
6 default.rgw.control
7 default.rgw.meta
8 default.rgw.buckets.index
9 default.rgw.buckets.data

Таким образом мы создали отказоустойчивый вариант RGW, предоставили доступ клиенту, установили квоты, загрузили и удалили файлы.

На этом, про объектный доступ в Ceph у меня все. В качестве факультатива можете подумать на вопрос подключения сертификатов и использования HTTPS, что будет наиболее правильно с точки зрения безопасности.

Leave a Reply

Your email address will not be published. Required fields are marked *

Translate »