[ English | Indonesia | Deutsch | 日本語 ]
Anpassen des OpenStack Compute (nova) Schedulers¶
Viele OpenStack-Projekte ermöglichen die Anpassung spezifischer Funktionen über eine Treiberarchitektur. Sie können einen Treiber schreiben, der einer bestimmten Schnittstelle entspricht, und ihn über die Konfiguration anschließen. So können Sie beispielsweise ganz einfach einen neuen Scheduler für Compute einbinden. Die vorhandenen Scheduler für Compute sind vollständig ausgestattet und gut dokumentiert unter Scheduling. Abhängig von den Anwendungsfällen Ihres Benutzers entsprechen die vorhandenen Planer jedoch möglicherweise nicht Ihren Anforderungen. Möglicherweise müssen Sie einen neuen Scheduler erstellen.
Um einen Scheduler zu erstellen, müssen Sie von der Klasse nova.scheduler.driver.driver.scheduleuler
erben. Von den fünf Methoden, die Sie überschreiben können, müssen Sie die beiden Methoden, die mit einem Sternchen (*) gekennzeichnet sind, überschreiben:
update_service_capabilities
hosts_up
group_hosts
*
schedule_run_instance
*
select_destinations
Um die Anpassung von OpenStack zu demonstrieren, erstellen wir ein Beispiel für einen Compute-Scheduler, der eine Instanz zufällig auf einer Teilmenge von Hosts platziert, abhängig von der ursprünglichen IP-Adresse der Anfrage und dem Präfix des Hostnamens. Ein solches Beispiel könnte nützlich sein, wenn Sie eine Gruppe von Benutzern in einem Subnetz haben und möchten, dass alle ihre Instanzen innerhalb einer Teilmenge Ihrer Hosts starten.
Warnung
Dieses Beispiel dient nur zur Veranschaulichung. Es sollte nicht als Scheduler für Compute ohne weitere Entwicklung und Tests verwendet werden.
Wenn Sie an der Bildschirmsitzung teilnehmen, die stack.sh
mit screen -r stack
beginnt, werden Sie mit vielen Bildschirmfenstern begrüßt:
0$ shell* 1$ key 2$ horizon ... 9$ n-api ... 14$ n-sch ...
shell
Eine Shell, in der Sie etwas Arbeit erledigen können
key
Der Keystone-Service
horizon
Die Webanwendung des Horizon Dashboards
n-{name}
Die nova-Dienste
n-sch
Der nova Scheduler-Dienst
Erstellen Sie den Scheduler und schließen Sie ihn über die Konfiguration an
Der Code für OpenStack befindet sich in
/opt/stack
, also gehen Sie in das Verzeichnisnova
und bearbeiten Sie Ihr Scheduler-Modul. Wechseln Sie in das Verzeichnis, in demnova
installiert ist:$ cd /opt/stack/nova
Erstellen Sie die
ip_scheduler.py
Python-Quellcode-Datei:$ vim nova/scheduler/ip_scheduler.py
Der untenstehende Code ist ein Treiber, der Server auf Hosts basierend auf der IP-Adresse plant, wie am Anfang des Abschnitts beschrieben. Kopieren Sie den Code in die
ip_scheduler.py
. Wenn Sie fertig sind, speichern und schließen Sie die Datei.# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ IP Scheduler implementation """ import random from oslo_config import cfg from nova.compute import rpcapi as compute_rpcapi from nova import exception from nova.openstack.common import log as logging from nova.openstack.common.gettextutils import _ from nova.scheduler import driver CONF = cfg.CONF CONF.import_opt('compute_topic', 'nova.compute.rpcapi') LOG = logging.getLogger(__name__) class IPScheduler(driver.Scheduler): """ Implements Scheduler as a random node selector based on IP address and hostname prefix. """ def __init__(self, *args, **kwargs): super(IPScheduler, self).__init__(*args, **kwargs) self.compute_rpcapi = compute_rpcapi.ComputeAPI() def _filter_hosts(self, request_spec, hosts, filter_properties, hostname_prefix): """Filter a list of hosts based on hostname prefix.""" hosts = [host for host in hosts if host.startswith(hostname_prefix)] return hosts def _schedule(self, context, topic, request_spec, filter_properties): """Picks a host that is up at random.""" elevated = context.elevated() hosts = self.hosts_up(elevated, topic) if not hosts: msg = _("Is the appropriate service running?") raise exception.NoValidHost(reason=msg) remote_ip = context.remote_address if remote_ip.startswith('10.1'): hostname_prefix = 'doc' elif remote_ip.startswith('10.2'): hostname_prefix = 'ops' else: hostname_prefix = 'dev' hosts = self._filter_hosts(request_spec, hosts, filter_properties, hostname_prefix) if not hosts: msg = _("Could not find another compute") raise exception.NoValidHost(reason=msg) host = random.choice(hosts) LOG.debug("Request from %(remote_ip)s scheduled to %(host)s" % locals()) return host def select_destinations(self, context, request_spec, filter_properties): """Selects random destinations.""" num_instances = request_spec['num_instances'] # NOTE(timello): Returns a list of dicts with 'host', 'nodename' and # 'limits' as keys for compatibility with filter_scheduler. dests = [] for i in range(num_instances): host = self._schedule(context, CONF.compute_topic, request_spec, filter_properties) host_state = dict(host=host, nodename=None, limits=None) dests.append(host_state) if len(dests) < num_instances: raise exception.NoValidHost(reason='') return dests def schedule_run_instance(self, context, request_spec, admin_password, injected_files, requested_networks, is_first_time, filter_properties, legacy_bdm_in_spec): """Create and run an instance or instances.""" instance_uuids = request_spec.get('instance_uuids') for num, instance_uuid in enumerate(instance_uuids): request_spec['instance_properties']['launch_index'] = num try: host = self._schedule(context, CONF.compute_topic, request_spec, filter_properties) updated_instance = driver.instance_update_db(context, instance_uuid) self.compute_rpcapi.run_instance(context, instance=updated_instance, host=host, requested_networks=requested_networks, injected_files=injected_files, admin_password=admin_password, is_first_time=is_first_time, request_spec=request_spec, filter_properties=filter_properties, legacy_bdm_in_spec=legacy_bdm_in_spec) except Exception as ex: # NOTE(vish): we don't reraise the exception here to make sure # that all instances in the request get set to # error properly driver.handle_schedule_error(context, ex, instance_uuid, request_spec)
Es gibt viele nützliche Informationen in
context
,`request_spec
undfilter_properties
, die Sie verwenden können, um zu entscheiden, wo Sie die Instanz planen möchten. Um mehr darüber zu erfahren, welche Eigenschaften verfügbar sind, können Sie die folgenden Protokollanweisungen in die Methodeschedule_run_instance
des obigen Schedulers einfügen:LOG.debug("context = %(context)s" % {'context': context.__dict__}) LOG.debug("request_spec = %(request_spec)s" % locals()) LOG.debug("filter_properties = %(filter_properties)s" % locals())
Um diesen Scheduler in nova einzubinden, bearbeiten Sie eine Konfigurationsdatei,
/etc/nova/nova/nova.conf
:$ vim /etc/nova/nova.conf
Suchen Sie die Konfiguration
scheduler_driver
und ändern Sie sie so:scheduler_driver=nova.scheduler.ip_scheduler.IPScheduler
Starten Sie den Dienst nova scheduler neu, damit nova Ihren Scheduler verwenden kann. Wechseln Sie zunächst zum Bildschirm „n-sch“:
Drücken Sie Strg+A gefolgt von 9.
Drücken Sie Strg+A gefolgt von N, bis Sie den Bildschirm
n-sch
erreichen.Drücken Sie Strg+C, um den Dienst zu beenden.
Drücken Sie Pfeil nach oben, um den letzten Befehl aufzurufen.
Drücken Sie Enter, um es auszuführen.
Testen Sie Ihren Scheduler mit der nova CLI. Beginnen Sie mit dem Umschalten auf den Bildschirm
Shell
und gehen Sie anschließend zurück zum Bildschirmn-sch
, um die Protokollausgabe zu überprüfen:Drücken Sie Strg+A gefolgt von 0.
Stellen Sie sicher, dass Sie sich im Verzeichnis
devstack
befinden:$ cd /root/devstack
Quelle
openrc
, um Ihre Umgebungsvariablen für die CLI einzurichten:$ . openrc
Fügen Sie die Image-ID für das einzige installierte Image in eine Umgebungsvariable ein:
$ IMAGE_ID=`openstack image list | egrep cirros | egrep -v "kernel|ramdisk" | awk '{print $2}'`
Starten Sie einen Testserver:
$ openstack server create --flavor 1 --image $IMAGE_ID scheduler-test
Wechseln Sie zurück zum Bildschirm
n-sch
. Unter den Protokollanweisungen sehen Sie die Zeile:2014-01-23 19:57:47.262 DEBUG nova.scheduler.ip_scheduler [req-... demo demo] Request from xx.xx.xx.xx scheduled to devstack-havana _schedule /opt/stack/nova/nova/scheduler/ip_scheduler.py:76
Warnung
Funktionstests wie dieser sind kein Ersatz für richtige Geräte- und Integrationstests, aber sie dienen dazu, Ihnen den Einstieg zu erleichtern.
Ein ähnliches Muster kann in anderen Projekten, die die Treiberarchitektur verwenden, verfolgt werden. Erstellen Sie einfach ein Modul und eine Klasse, die der Treiberoberfläche entsprechen, und schließen Sie es über die Konfiguration an. Ihr Code läuft bei Verwendung dieser Funktion und kann bei Bedarf andere Dienste aufrufen. Es wird kein Projektkern-Code berührt. Suchen Sie nach einem „Treiber“-Wert in den Konfigurationsdateien des Projekts unter /etc/<project>
, um Projekte zu identifizieren, die eine Treiberarchitektur verwenden.
Wenn Ihr Scheduler fertig ist, empfehlen wir Ihnen, ihn Open Source zu verwenden und die Community auf der OpenStack-Mailingliste darüber zu informieren. Vielleicht benötigen andere die gleiche Funktionalität. Sie können Ihren Code verwenden, Feedback geben und möglicherweise einen Beitrag leisten. Wenn es genügend Unterstützung dafür gibt, können Sie vielleicht vorschlagen, dass sie dem offiziellen Compute schedulers hinzugefügt wird.