Skalieren

Skalieren

Ein oft zitierter Grund für die Gestaltung von Anwendungen mit Hilfe von Cloud-Mustern ist die Fähigkeit, auszugleichen. Das ist: um zusätzliche Ressourcen hinzuzufügen, wie erforderlich. Kontrastieren Sie diese Strategie auf die vorherige der zunehmenden Kapazität durch die Skalierung der Größe der vorhandenen Ressourcen. Um zu skalieren, müssen Sie:

  • Re-Architekt Ihrer Applikation, um zusätzliche Ressourcen zu nutzen.

  • Machen Sie es möglich, neue Ressourcen zu Ihrer Anwendung hinzuzufügen.

Der Abschnitt Einführung in die Architektur der Fraktale beschreibt, wie man modular aufbaut, eine API erstellt und andere Aspekte der Anwendungsarchitektur. Jetzt werden Sie sehen, warum diese Strategien so wichtig sind. Durch die Erstellung einer modularen Anwendung mit entkoppelten Diensten können Sie Komponenten identifizieren, die Anwendungsperformance verursachen und sie ausgleichen. Genauso wichtig, man kann auch Ressourcen entfernen, wenn sie nicht mehr nötig sind. Es ist sehr schwierig, die Kosteneinsparungen zu beziffern, die diese Funktion im Vergleich zur herkömmlichen Infrastruktur bringen kann.

Natürlich ist der Zugang zu zusätzlichen Ressourcen nur ein Teil des Spielplans; während Sie Ressourcen manuell hinzufügen oder löschen können, erhalten Sie mehr Wert und mehr Reaktionsfähigkeit, wenn die Anwendung automatisch zusätzliche Ressourcen anfordert, wenn sie sie benötigen.

Dieser Abschnitt veranschaulicht weiterhin die Trennung von Diensten auf mehrere Instanzen und hebt einige der Entscheidungen hervor, die wir gemacht haben, die Skalierbarkeit in der Anwendungsarchitektur erleichtern.

Sie werden fortschreitend hochfahren, um sechs Instanzen zu verwenden, also stellen Sie sicher, dass Ihr Cloud-Konto die entsprechende Quote hat.

Der vorhergehende Abschnitt verwendet zwei virtuelle Maschinen - einen ‚Steuerdienst‘ und einen ‚Worker‘. Die Geschwindigkeit, mit der Ihre Anwendung Fraktale erzeugen kann, hängt von der Anzahl der Worker ab. Mit nur einem Worker können Sie nur ein Fraktal zu einer Zeit produzieren. Bis dahin benötigen Sie mehr Ressourcen.

Bemerkung

Wenn Sie keine Arbeitsanwendung haben, folgen Sie den Schritten in :doc:`introduction‘, um eine zu erstellen.

Last erzeugen

Um zu testen, was passiert, wenn die Fractals-Anwendung unter Last ist, können Sie:

  • Laden Sie den Worker: Erstellen Sie eine Menge von Aufgaben, um die CPU der vorhandenen Worker-Instanzen zu maximieren

  • Laden Sie die API: Erstellen Sie viele API-Dienstanforderungen

Erstellen Sie mehr Aufgaben

Verwenden Sie SSH mit dem vorhandenen SSH-Schlüsselpaar, um sich an dem app-controller Controller-Instanz anzumelden.

$ ssh -i ~/.ssh/id_rsa USERNAME@IP_CONTROLLER

Bemerkung

Ersetzen Sie IP_CONTROLLER mit der IP-Adresse der Controller-Instanz und USERNAME mit dem entsprechenden Benutzernamen.

Rufen Sie den faafo Befehlszeilenschnittstelle an, um die Erzeugung von fünf großen Fraktalen anzufordern.

$ faafo create --height 9999 --width 9999 --tasks 5

Wenn Sie die Belastung des Workers überprüfen, können Sie sehen, dass die Instanz nicht gut läuft. Bei der einzelnen CPU-Varianten-Instanz bedeutet ein Last-Durchschnitt größer als 1, dass der Server an der Kapazitätsgrenze ist.

$ ssh -i ~/.ssh/id_rsa USERNAME@IP_WORKER uptime
10:37:39 up  1:44,  2 users,  load average: 1.24, 1.40, 1.36

Bemerkung

Ersetzen Sie IP_WORKER mit der IP-Adresse der Worker-Instanz und USERNAME mit dem entsprechenden Benutzernamen.

Erstellen Sie weitere API-Dienstanforderungen

API Last ist ein etwas anderes Problem als die vorherige in Bezug auf die Fähigkeit zu arbeiten. Wir können viele Anfragen an die API simulieren, wie folgt:

Verwenden Sie SSH mit dem vorhandenen SSH-Schlüsselpaar, um sich an dem app-controller Controller-Instanz anzumelden.

$ ssh -i ~/.ssh/id_rsa USERNAME@IP_CONTROLLER

Bemerkung

Ersetzen Sie IP_CONTROLLER mit der IP-Adresse der Controller-Instanz und USERNAME mit dem entsprechenden Benutzernamen.

Verwenden Sie eine for-Schleife, um den faafo-Befehlszeilen-Interface anzurufen, um einen zufälligen Satz von Fraktalen 500 mal anzufordern:

$ for i in $(seq 1 500); do faafo --endpoint-url http://IP_CONTROLLER create & done

Bemerkung

Ersetzen Sie IP_CONTROLLER mit der IP-Adresse der Controller-Instanz.

Wenn Sie die Belastung auf dem app-controller API Service Instanz überprüfen, sehen Sie, dass die Instanz nicht gut läuft. Bei Ihrer einzelnen CPU-Varianten-Instanz bedeutet ein Last-Durchschnitt größer als 1, dass der Server an Kapazität ist.

$ uptime
10:37:39 up  1:44,  2 users,  load average: 1.24, 1.40, 1.36

Die schiere Anzahl von Anfragen bedeutet, dass einige Anfragen für Fraktale es nicht zur Nachrichtenwarteschlange zur Verarbeitung machen können. Um sicherzustellen, dass Sie mit der Nachfrage fertig werden können, müssen Sie auch die API-Fähigkeit der Fractals-Anwendung skalieren.

Skalieren

Entfernen Sie die vorhandene App

Gehen Sie vor und löschen Sie die vorhandenen Instanzen und Sicherheitsgruppen, die Sie in früheren Abschnitten erstellt haben. Denken Sie daran, wenn Instanzen in der Cloud nicht mehr arbeiten, entfernen Sie sie und erstellen Sie etwas neu.

for instance in conn.list_servers():
    if instance.name in ['all-in-one','app-worker-1', 'app-worker-2', 'app-controller']:
        print('Destroying Instance: %s' % instance.name)
        conn.delete_server(instance.id, wait=True)

for group in conn.list_security_groups():
    if group['name'] in ['control', 'worker', 'api', 'services']:
        print('Deleting security group: %s' % group['name'])
        conn.delete_security_group(group['name'])

Zusätzliche Sicherheitsgruppen

Wenn Sie die Topologie Ihrer Anwendungen ändern, müssen Sie Sicherheitsgruppen aktualisieren oder erstellen. Hier erstellen Sie die erforderlichen Sicherheitsgruppen neu.

api_group = conn.create_security_group('api', 'for API services only')
conn.create_security_group_rule(api_group['name'], 80, 80, 'TCP')
conn.create_security_group_rule(api_group['name'], 22, 22, 'TCP')

worker_group = conn.create_security_group('worker', 'for services that run on a worker node')
conn.create_security_group_rule(worker_group['name'], 22, 22, 'TCP')

services_group = conn.create_security_group('services', 'for DB and AMQP services only')
conn.create_security_group_rule(services_group['name'], 22, 22, 'TCP')
conn.create_security_group_rule(services_group['name'], 3306, 3306, 'TCP', remote_group_id=api_group['id'])
conn.create_security_group_rule(services_group['name'], 5672, 5672, 'TCP', remote_group_id=worker_group['id'])
conn.create_security_group_rule(services_group['name'], 5672, 5672, 'TCP', remote_group_id=api_group['id'])

Eine Floating-IP-Helferfunktion

Definieren Sie eine kurze Funktion, um unbenutzte oder zugängliche Floating-IPs zu lokalisieren. Das spart ein paar Zeilen Code und verhindert, dass Sie Ihr Floating-IP-Quota zu schnell erreichen.

def get_floating_ip(conn):
    '''A helper function to re-use available Floating IPs'''
    return conn.available_floating_ip()

Teilen Sie die Datenbank und die Nachrichtenwarteschlange

Bevor Sie Ihre Anwendungsdienste ausschalten, wie der API-Dienst oder die Mitarbeiter, müssen Sie eine zentrale Datenbank und einen Code hinzufügen: app-services Messaging-Instanz. Die Datenbank- und Messaging-Warteschlange wird verwendet, um den Zustand der Fraktale zu verfolgen und die Kommunikation zwischen den Diensten zu koordinieren.

userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i database -i messaging
'''

instance_services = conn.create_server(wait=True, auto_ip=False,
    name='app-services',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[services_group['name']],
    userdata=userdata)

services_ip = conn.get_server_private_ip(instance_services)

Skalieren Sie den API-Dienst

Mit mehreren Workern, die Fraktale erzegen so schnell wie sie können, muss das System in der Lage sein, die Anfragen für Fraktale so schnell wie möglich zu erhalten. Wenn unsere Anwendung populär wird, können viele Tausende von Benutzern eine Verbindung zu unserer API herstellen, um Fraktale zu erzeugen.

Bewaffnet mit einer Sicherheitsgruppe, Abbild und Varianten-Größe, können Sie mehrere API-Dienste hinzufügen:

userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i faafo -r api -m 'amqp://guest:guest@%(services_ip)s:5672/' \
    -d 'mysql+pymysql://faafo:password@%(services_ip)s:3306/faafo'
''' % { 'services_ip': services_ip }

instance_api_1 = conn.create_server(wait=True, auto_ip=False,
    name='app-api-1',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[api_group['name']],
    userdata=userdata)
instance_api_2 = conn.create_server(wait=True, auto_ip=False,
    name='app-api-2',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[api_group['name']],
    userdata=userdata)

api_1_ip = conn.get_server_private_ip(instance_api_1)
api_2_ip = conn.get_server_private_ip(instance_api_2)

for instance in [instance_api_1,  instance_api_2]:
    floating_ip = get_floating_ip(conn)
    conn.add_ip_list(instance, [floating_ip['floating_ip_address']])
    print('allocated %(ip)s to %(host)s' % {'ip': floating_ip['floating_ip_address'], 'host': instance['name']})

Diese Dienste sind kundenorientiert, so dass sie im Gegensatz zu den Mitarbeitern keine Nachrichtenwarteschlange verwenden, um Aufgaben zu verteilen. Stattdessen müssen Sie eine Art Lastverteilungsmechanismus einführen, um eingehende Anfragen zwischen den verschiedenen API-Diensten zu teilen.

Eine einfache Lösung ist, die Hälfte Ihrer Freunde eine Adresse und die Hälfte der anderen zu geben, aber diese Lösung ist nicht nachhaltig. Stattdessen können Sie ein ‚DNS Round Robin‘ benutzen <http://en.wikipedia.org/wiki/Round- robin_DNS> _, um das automatisch zu tun. Allerdings kann OpenStack Networking Load Balancing als Service bieten, was :doc:/networking` erklärt.

Skalieren Sie die Worker

Um die Gesamtkapazität zu erhöhen, fügen Sie drei Worker hinzu:

userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i faafo -r worker -e 'http://%(api_1_ip)s' -m 'amqp://guest:guest@%(services_ip)s:5672/'
''' % {'api_1_ip': api_1_ip, 'services_ip': services_ip}

instance_worker_1 = conn.create_server(wait=True, auto_ip=False,
    name='app-worker-1',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[worker_group['name']],
    userdata=userdata)

instance_worker_2 = conn.create_server(wait=True, auto_ip=False,
    name='app-worker-2',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[worker_group['name']],
    userdata=userdata)

instance_worker_3 = conn.create_server(wait=True, auto_ip=False,
    name='app-worker-3',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[worker_group['name']],
    userdata=userdata)

Wenn Sie diese Kapazität hinzufügen, können Sie mit einer höheren Anzahl von Anträgen auf Fraktale umgehen. Sobald diese Worker-Instanzen beginnen, beginnen sie mit der Überprüfung der Message-Queue für Anfragen, wodurch der Gesamt-Backlog wie eine neue Kasseneröffnung im Supermarkt reduziert wird.

Dieser Vorgang war offensichtlich ein sehr manueller. Herauszufinden, dass wir mehr Worker brauchten und diese dann zu starten, bedarf einiger Anstrengungen. Idealerweise würde das System das selbst tun. Wenn Sie Ihre Anwendung aufbauen, um diese Situationen zu erkennen, können Sie sie automatisch Ressourcen anfordern und entfernen, was Ihnen Mühe macht ist, diese Arbeit selbst zu tun. Stattdessen kann der OpenStack Orchestrierungsdienst die Last überwachen und Instanzen starten. Um herauszufinden, wie man das einrichtet, siehe Orchestrierung.

Vergewissern Sie sich, dass wir einen Einfluss hatten

In den vorherigen Schritten haben Sie mehrere Dienste aufgeteilt und Kapazitäten erweitert. Um die neuen Features der Fractals-Anwendung zu sehen, verbinden Sie sich mit SSH zu einer der App-Instanzen und erstellen Sie ein paar Fraktale.

$ ssh -i ~/.ssh/id_rsa USERNAME@IP_API_1

Bemerkung

Ersetzen Sie IP_API_1 mit der IP-Adresse der ersten API-Instanz und USERNAME mit dem entsprechenden Benutzernamen.

Verwenden Sie den faafo create Befehl, um Fraktale zu erzeugen.

Verwenden Sie den faafo list Befehl, um den Fortschritt der Fraktalgenerierung zu beobachten.

Verwenden Sie den faafo UUID Befehl, um einige der Fraktale zu untersuchen.

Das Feld generated_by zeigt den Worker, der das Fraktal erstellt hat. Weil mehrere Workerinstanzen die Arbeit teilen, werden Fraktale schneller erzeugt und Benutzer können nicht einmal bemerken, wenn ein Arbeiter ausfällt.

root@app-api-1:# faafo list
+--------------------------------------+------------------+-------------+
|                 UUID                 |    Dimensions    |   Filesize  |
+--------------------------------------+------------------+-------------+
| 410bca6e-baa7-4d82-9ec0-78e409db7ade | 295 x 738 pixels | 26283 bytes |
| 66054419-f721-492f-8964-a5c9291d0524 | 904 x 860 pixels | 78666 bytes |
| d123e9c1-3934-4ffd-8b09-0032ca2b6564 | 952 x 382 pixels | 34239 bytes |
| f51af10a-084d-4314-876a-6d0b9ea9e735 | 877 x 708 pixels | 93679 bytes |
+--------------------------------------+------------------+-------------+

root@app-api-1:# faafo show d123e9c1-3934-4ffd-8b09-0032ca2b6564
+--------------+------------------------------------------------------------------+
| Parameter    | Value                                                            |
+--------------+------------------------------------------------------------------+
| uuid         | d123e9c1-3934-4ffd-8b09-0032ca2b6564                             |
| duration     | 1.671410 seconds                                                 |
| dimensions   | 952 x 382 pixels                                                 |
| iterations   | 168                                                              |
| xa           | -2.61217                                                         |
| xb           | 3.98459                                                          |
| ya           | -1.89725                                                         |
| yb           | 2.36849                                                          |
| size         | 34239 bytes                                                      |
| checksum     | d2025a9cf60faca1aada854d4cac900041c6fa762460f86ab39f42ccfe305ffe |
| generated_by | app-worker-2                                                     |
+--------------+------------------------------------------------------------------+
root@app-api-1:# faafo show 66054419-f721-492f-8964-a5c9291d0524
+--------------+------------------------------------------------------------------+
| Parameter    | Value                                                            |
+--------------+------------------------------------------------------------------+
| uuid         | 66054419-f721-492f-8964-a5c9291d0524                             |
| duration     | 5.293870 seconds                                                 |
| dimensions   | 904 x 860 pixels                                                 |
| iterations   | 348                                                              |
| xa           | -2.74108                                                         |
| xb           | 1.85912                                                          |
| ya           | -2.36827                                                         |
| yb           | 2.7832                                                           |
| size         | 78666 bytes                                                      |
| checksum     | 1f313aaa36b0f616b5c91bdf5a9dc54f81ff32488ce3999f87a39a3b23cf1b14 |
| generated_by | app-worker-1                                                     |
+--------------+------------------------------------------------------------------+

Die Fraktale sind jetzt bei jedem der app-api-Hosts erhältlich. Um zu überprüfen, besuchen Sie http://IP_API_1/fraktal/FRACTAL_UUID und http://IP_API_2/fraktal/FRACTAL_UUID. Sie haben jetzt mehrere redundante Web-Services. Wenn einer versagt, können Sie die anderen benutzen.

Bemerkung

Ersetzen Sie IP_API_1 und IP_API_2 mit den entsprechenden Floating IPs. Ersetzen Sie FRACTAL_UUID durch die UUID eines vorhandenen Fraktals.

Machen Sie weiter und testen Sie die Fehlertoleranz. Starten Sie das Löschen von Workern und API-Instanzen. Solange Sie eines von beiden haben, ist Ihre Anwendung in Ordnung. Achten Sie jedoch auf einen Schwachpunkt. Die Datenbank enthält die Fraktale und Fraktal-Metadaten. Wenn Sie diese Instanz verlieren, ist die Anwendung beendet. Zukünftige Abschnitte werden erklären, wie man diesen Schwachpunkt ansprechen kann.

Wenn Sie einen Load Balancer hätten, können Sie diese Last zwischen den beiden verschiedenen API-Diensten verteilen. Sie haben mehrere Möglichkeiten. Der Vernetzung-Bereich zeigt Ihnen eine Option.

In der Theorie können Sie ein einfaches Skript verwenden, um die Belastung Ihrer Worker und API-Dienste zu überwachen und die Erstellung von Instanzen auszulösen, die Sie bereits kennen. Glückwunsch! Sie sind bereit, skalierbare Cloud-Anwendungen zu erstellen.

Natürlich kann das Erstellen eines Überwachungssystems für eine einzelne Anwendung nicht sinnvoll sein. Um zu erlernen, wie man die OpenStack Orchestrierung Monitoring und Auto-Scaling-Funktionen verwendet, um diese Schritte zu automatisieren, siehe Orchestrierung.

Nächste Schritte

Sie sollten sich ziemlich sicher über das Starten von Instanzen und die Verteilung von Diensten aus einer Anwendung unter diesen Instanzen sein.

Wie bereits erwähnt in Einführung in die Architektur der Fraktale, werden die generierten Fraktalbilder auf dem lokalen Dateisystem der API Service Instanzen gespeichert. Da Sie mehrere API-Instanzen auf- und ablaufen lassen, werden die Fraktalbilder über mehrere API-Dienste verteilt, was eine Reihe von :codet: IOError: [Errno 2] No such file or directory Fehler beim Versuch, ein Fraktalbild herunterzuladen eine API-Service-Instanz, die nicht das Fraktalbild auf seinem lokalen Dateisystem hat, verursacht.

Gehen Sie zu Mach es haltbar um zu lernen, wie man Object Storage verwendet, um dieses Problem auf eine elegante Weise zu lösen. Oder Sie können zu einem dieser Abschnitte gehen:

Komplettes Codebeispiel

Diese Datei enthält den ganzen Code aus diesem Tutorial. Mit diesem umfassenden Code-Beispiel können Sie den Code als einzelnes Skript anzeigen und ausführen.

Bevor Sie dieses Skript ausführen, bestätigen Sie, dass Sie Ihre Authentifizierungsinformationen, die Varianten-ID und die Image-ID festgelegt haben.

# step-1
for instance in conn.list_servers():
    if instance.name in ['all-in-one','app-worker-1', 'app-worker-2', 'app-controller']:
        print('Destroying Instance: %s' % instance.name)
        conn.delete_server(instance.id, wait=True)

for group in conn.list_security_groups():
    if group['name'] in ['control', 'worker', 'api', 'services']:
        print('Deleting security group: %s' % group['name'])
        conn.delete_security_group(group['name'])

# step-2
api_group = conn.create_security_group('api', 'for API services only')
conn.create_security_group_rule(api_group['name'], 80, 80, 'TCP')
conn.create_security_group_rule(api_group['name'], 22, 22, 'TCP')

worker_group = conn.create_security_group('worker', 'for services that run on a worker node')
conn.create_security_group_rule(worker_group['name'], 22, 22, 'TCP')

services_group = conn.create_security_group('services', 'for DB and AMQP services only')
conn.create_security_group_rule(services_group['name'], 22, 22, 'TCP')
conn.create_security_group_rule(services_group['name'], 3306, 3306, 'TCP', remote_group_id=api_group['id'])
conn.create_security_group_rule(services_group['name'], 5672, 5672, 'TCP', remote_group_id=worker_group['id'])
conn.create_security_group_rule(services_group['name'], 5672, 5672, 'TCP', remote_group_id=api_group['id'])

# step-3
def get_floating_ip(conn):
    '''A helper function to re-use available Floating IPs'''
    return conn.available_floating_ip()

# step-4
userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i database -i messaging
'''

instance_services = conn.create_server(wait=True, auto_ip=False,
    name='app-services',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[services_group['name']],
    userdata=userdata)

services_ip = conn.get_server_private_ip(instance_services)

# step-5
userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i faafo -r api -m 'amqp://guest:guest@%(services_ip)s:5672/' \
    -d 'mysql+pymysql://faafo:password@%(services_ip)s:3306/faafo'
''' % { 'services_ip': services_ip }

instance_api_1 = conn.create_server(wait=True, auto_ip=False,
    name='app-api-1',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[api_group['name']],
    userdata=userdata)
instance_api_2 = conn.create_server(wait=True, auto_ip=False,
    name='app-api-2',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[api_group['name']],
    userdata=userdata)

api_1_ip = conn.get_server_private_ip(instance_api_1)
api_2_ip = conn.get_server_private_ip(instance_api_2)

for instance in [instance_api_1,  instance_api_2]:
    floating_ip = get_floating_ip(conn)
    conn.add_ip_list(instance, [floating_ip['floating_ip_address']])
    print('allocated %(ip)s to %(host)s' % {'ip': floating_ip['floating_ip_address'], 'host': instance['name']})

# step-6
userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i faafo -r worker -e 'http://%(api_1_ip)s' -m 'amqp://guest:guest@%(services_ip)s:5672/'
''' % {'api_1_ip': api_1_ip, 'services_ip': services_ip}

instance_worker_1 = conn.create_server(wait=True, auto_ip=False,
    name='app-worker-1',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[worker_group['name']],
    userdata=userdata)

instance_worker_2 = conn.create_server(wait=True, auto_ip=False,
    name='app-worker-2',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[worker_group['name']],
    userdata=userdata)

instance_worker_3 = conn.create_server(wait=True, auto_ip=False,
    name='app-worker-3',
    image=image_id,
    flavor=flavor_id,
    key_name='demokey',
    security_groups=[worker_group['name']],
    userdata=userdata)
Creative Commons Attribution 3.0 License

Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.