Swift/Ring Management

From Wikitech


Swift Ring Management

Swift rings are managed by an automated process; you edit a YAML file that describes the hosts in the cluster, and then the swift_ring_manager process (run by a systemd timer) gradually adjusts the deployed rings to match the state specified in the YAML file. The aim is that the YAML file should be reasonably intuitive; some common tasks are outlined here.

The swift ring manager code is installed on one front-end per Swift cluster (see the ring_manager entry in swift_clusters in hiera); the [code repository] is checked out into /srv/deployment/swift_ring_manager, and the script /usr/local/bin/swift_ring_manager is templated by puppet to ensure the correct version of python is used. That repository contains a test suite, which contains [an extensive set of example changes].

The configuration file is deployed as /etc/swift/hosts.yaml by Puppet; so to change the state of the rings, edit the relevant YAML file (found in modules/swift/files/CLUSTERNAME_hosts.yaml), and put through a CR as usual.

Storage Schemes

Every hosts.yaml file must define one or more schemes, which specify which devices belong to which rings, and with what weight. You typically won't have to edit these, as WMF tends to buy hardware of consistent specification. Each scheme is a mapping of ring name to the members of that ring, with an additional member "weight" which contains a mapping of ring name to the weight of members of that ring. For example:

schemes:
  prod:
    objects: [sdc1, sdd1, sde1]
    accounts: &ap [sda3, sdb3]
    containers: *ap
    ssds: &sp [sda4, sdb4]
    weight:
      objects: 4000
      accounts: &acw 100
      containers: *acw
      ssds: 300

Here, there is a single scheme "prod". There are three devices (/dev/sdc1, /dev/sdd1, /dev/sde1) which hold objects, with weight 4000. In common with the previous Swift ring management code, the rings are given friendly names ("objects" for the "object" ring, "ssds" for the "object-1" ring, etc.); the mapping of friendly name to ring is defined in the regularize_ring_names function.

Adding a host

If a host is the same type as other hosts in the cluster, simply add the host to the relevant list in hosts.yaml. So

hosts:
  prod:
   - hostname

would become

hosts:
  prod:
   - hostname
   - newhostname

To add newhostname, a new host with the prod device scheme. Remember to also add the new node to swift::storagehosts

Removing a host

Simply removing the host entry from hosts.yaml will cause it to be immediately removed from the rings; it is generally better to drain it first (i.e. gradually remove weight from all of its devices). This can be done thus:

hosts:
  prod:
    - hostname:
      - drain

Using failed instead of drain will cause all weights to be set to 0 immediately, rather than gradually.

Removing a device

Do this by setting its weight to zero. By default, this will take effect gradually; if you want to do so straight away, specify immediate as well as weight zero. As a convenience, you can also use drain for "weight 0" and failed for "weight 0, immediate". For example:

hosts:
  prod:
    - hostname:
       - sdc1: 0
       - sdd1: [0, immediate]
       - sde1: failed

Here /dev/sdc1 will have weight on it gradually reduced to 0, whereas /dev/sdd1 and /dev/sde1 will both have their weight set to 0 immediately.

Adjusting a device weight

This is essentially the same as the previous operation, but you can specify any weight you like. You may also specify immediate for a change you want to not be made gradually. For example:

hosts:
  prod:
    - hostname:
       - sdc1: 2000
       - sdd1: [3500, immediate]

Here /dev/sdc1 will have its weight gradually changed to 2000, and /dev/sdd1 will have its weight immediately set to 3500.

Rebalancing the rings without making other changes

Sometimes after a lot of changes have completed you may notice that the rings are somewhat unbalanced ("balance" in swift-ring-builder output is high); you can rebalance the rings by running, on the ring manager host:

sudo /usr/local/bin/swift_ring_manager -o /var/cache/swift_rings --doit --rebalance -v

It's only useful to do this when the rings are otherwise not being changed (as a change to the rings will involve a rebalance anyway).

Invoking swift_ring_manager.py

Typically, you don't need to do this - it should be run automatically on the cluster's ring manager node, which will make changes and distribute the ring files for you. You can run it yourself to see what changes it would make, however - the default operation is to merely say what changes if any would be made. The help message details all the command-line arguments:

usage: swift_ring_manager.py [-h] [--doit] [--immediate-only]
                             [--update-tmp-rings] [-v] [-c CONF_FILE]
                             [--ring-dir RING_DIR] [-o OUT_DIR]
                             [--skip-dispersion-check]
                             [--skip-replication-check] [-s]
                             [--host-file-path HOST_FILE_PATH]
                             [--generate-host-file]

Swift Ring Manager

optional arguments:
  -h, --help            show this help message and exit
  --doit                Update ring files and deploy
  --immediate-only      Only make changes flagged as immediate
  --update-tmp-rings    Only update temporary copies of ring files
  -v, --verbose         emit more debugging information
  -c CONF_FILE, --conf-file CONF_FILE
                        path to configuration file
  --ring-dir RING_DIR   directory containing ring builder files
  -o OUT_DIR, --out-dir OUT_DIR
                        directory to put new rings into
  --skip-dispersion-check
                        don't check dispersion OK before making changes
  --skip-replication-check
                        don't check replication OK before making changes
  -s, --syslog          Log to syslog rather than stdout
  --host-file-path HOST_FILE_PATH
                        fake hosts list file path
  --generate-host-file  generate a hosts file for future testing use