Dışa ölçekleme

Dışa ölçekleme

Bulut kalıpları kullanarak uygulama tasarlamanın çoğu zaman gösterilen sebeplerinden biri dışa ölçeklemedir. Bu kısaca gerektiğinde ek kaynaklar ekleyebilmektir. Kapasite artırmak için mevcut kaynakların boyutlarını artırma stratejisiyle karşılaştırın. Dışa ölçeklemek için:

  • Uygulamanızı ek kaynakların kullanımından faydalanacak şekilde tasarlamalısınız.

  • Uygulamanıza yeni kaynaklar eklemeyi mümkün kılmalısınız.

Fraktallar uygulama mimarisine giriş kısmı modüler şekilde nasıl inşa edileceğini, bir API oluşturulacağını ve uygulama mimarisinin diğer yönlerini tanımlar. Şimdi bu stratejilerin neden bu kadar önemli olduğunu göreceksiniz. Birbirinden bağımsız servislerle modüler uygulama oluşturarak uygulama başarım darboğazlarına sebep olan bileşenleri tanımlayabilir ve bunları ölçekleyebilirsiniz. Aynı önem seviyesinde, artık ihtiyaç duyulmadığında kaynakları kaldırabilirsiniz. Bu özelliğin geleneksel altyapıya göre masrafı ne kadar azaltacağını abartmak mümkün değildir.

Tabi ki ek kaynaklara erişime sahip olmak oyun planının yalnızca parçasıdır; elle kaynak ekleyip silebilseniz de, uygulama otomatik olarak ihtiyaç duyduğunda ek kaynaklar isterse bu daha değerlidir ve yanıt vermeyi artırır.

Bu kısım servislerin birden çok sunucuya ayrımını açıklamaya devam eder ve uygulama mimarisinde ölçeklemeyi kolaylaştıran bazı seçimlerimizi vurgular.

Yavaş yavaş altı sunucu kullanmaya geçeceksiniz, bu yüzden bulut hesabınızın bu kadar kotası olduğundan emin olun.

Önceki kısım iki sanal makine kullanır - biri ‘kontrol’ servisi biri de ‘işçi’ servisidir. Uygulamanızın fraktalları üretme hızı işçi sayısına göre değişir. Tek bir işçiyle bir seferde bir fraktal üretebilirsiniz. Fazla geçmeden daha fazla kaynağa ihtiyacınız olacak.

Not

Çalışan bir uygulamanız yoksa, bir tane oluşturmak için Fraktallar uygulama mimarisine giriş kısmındaki adımları takip edin.

Yük üretin

Fraktal uygulaması yük altındayken neler olacağını denemek için şunu yapabilirsiniz:

  • İşçiye yüklenin: Mevcut işçi sunucuların CPU kullanımını tavana taşımak için bir sürü görev oluşturun

  • API’ye yüklenin: Birçok API servis isteği oluşturun

Daha fazla görev oluşturun

Mevcut SSH anahtar çiftiyle app-controller kontrol sunucusuna giriş için SSH kullanın.

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

Not

IP_CONTROLLER anahtarını kontrol sunucusunun IP adresiyle ve USERNAME anahtarını uygun kullanıcı adıyla değiştirin.

faafo komut satırı arayüzünü çağırarak beş büyük fraktal üretimini isteyin.

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

İşçi üzerindeki yükü kontrol ederseniz, sunucunun pek iyi gitmediğini görebilirsiniz. Tek CPU nitelikli sunucuda, 1’den büyük bir yük ortalaması sunucunun kapasitesinde olduğu anlamına gelir.

$ 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

Not

IP_WORKER anahtarını işçi sunucunun IP adresiyle ve USERNAME anahtarını uygun kullanıcı adıyla değiştirin.

Daha fazla API servis isteği oluşturun

API yükü daha önce bahsedilen çalışma kapasitesinden daha farklı bir problemdir. API’lere şu şekilde birçok istek gönderme denemesi yapabiliriz:

Mevcut SSH anahtar çiftiyle app-controller kontrol sunucusuna giriş için SSH kullanın.

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

Not

IP_CONTROLLER anahtarını kontrol sunucusunun IP adresiyle ve USERNAME anahtarını uygun kullanıcı adıyla değiştirin.

500 kere rasgele fraktal kümesi isteği göndermek için komut satırı arayüzünden bir döngü kullanarak faafo çalıştırın:

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

Not

IP_CONTROLLER anahtarını kontrol sunucusunun IP adresiyle değiştirin.

app-controller API servis sunucusundaki yükü kontrol ederseniz, sunucunun pek iyi çalışmadığını görürsünüz. Tek işlemcili niteliğe sahip sunucunuzda, 1’den büyük bir ortalama yük sunucunun kapasitesinde çalıştığı anlamına gelir.

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

İsteklerin tam sayısı demek fraktallar için bazı isteklerin işleme için ileti kuyruğuna varamayabileceği anlamına gelir. İstekle baş edebileceğinizden emin olmak için ayrıca Fraktal uygulamasının API kapasitesini de ölçeklemelisiniz.

Dışa ölçekleme

Mevcut uygulamayı kaldırın

Önceki kısımlarda oluşturduğunuz mevcut sunucuları ve güvenlik gruplarını silin. Unutmayın, buluttaki sunucular çalışmayı bıraktıklarında onları kaldırmalı ve yeni bir şeyi tekrar oluşturmalısınız.

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'])

Ek güvelik grupları

Uygulamalarınızın topolojilerini değiştirdikçe, güvenlik grupları güncellemeli veya oluşturmalısınız. Burda gerekli güvenlik gruplarını tekrar oluşturursunuz.

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'])

Bir değişken IP yardımcı fonksiyonu

Kullanılmayanları bulmak için kısa bir fonksiyon tanımlayın veya değişken IP’ler ayırın. Bu birkaç satır koddan kar etmenizi sağlar ve değişken IP kotanızın çok çabuk dolmasını önler.

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

Veritabanı ve ileti kuyruğunu ayırın

API servisi veeya işçiler gibi uygulama servislerini dışa ölçeklemeden önce, merkezi bir veritabanı ve bir app-services ileti kuyruğu eklemelisiniz. Veritabanı ve ileti kuyruğu fraktalların durumunu takip etmek ve servisler arasında iletişimi düzenlemek için kullanılacaktır.

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)

API servisini ölçekleyin

Ellerinde geldiğince fraktal üreten birden çok işçiyle, sistem fraktallar için istekleri olabildiğince çabuk alabiliyor olmalıdır. Uygulamamız popülerleşirse, binlerce kullanıcı fraktal üretmek için API’mize bağlanabilir.

Bir güvenlik grubu, imaj ve nitelik boyutuyla birden çok API servisi ekleyebilirsiniz:

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']})

Bu servisler istemciye bakarlar, yani işçilerden farklı olarak görevleri dağıtmak için bir ileti kuyruğu kullanmazlar. Bunun yerine gelen istekleri farklı API servisleri arasında paylaştırmak için bir çeşit yük dengeleme sağlamalısınız.

Basit bir çözüm arkadaşlarınızın yarısına bir adres, diğer yarısına başka bir adres vermektir, ama bu çözüm devam ettirilebilir değildir. Bunun yerine bunu otomatik olarak yapmak için DNS round robin kullanabilirsiniz. Ancak OpenStack ağı belgesinin açıkladığı şekilde Servis olarak Yük Dengeleme sağlayabilir.

İşçileri ölçekleyin

Genel kapasiteyi artırmak için, üç işçi ekleyin:

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)

Bu kapasiteyi eklemek fraktallar için daha yüksek sayıda istekle başa çıkabilmenizi sağlar. Bu işçi sunucular başladığı anda, istekler için ileti kuyruğunu kontrol etmeye başlarlar, markette açılan yeni bir kasa gibi genel birikmeyi azaltırlar.

Bu süreç bariz şekilde elle yapılıyordu. Daha fazla işçiye ihtiyacımız olduğunu anlamak ve yenilerini başlatmak biraz uğraştırdı. İdeal olarak sistem bunu kendi yapar. Uygulamanızı bu gibi durumları algılayacak şekilde inşa ederseniz, otomatik olarak kaynakları istemesini ve kaldırmasını sağlayabilirsiniz, bu da bu işi kendi başınıza yaptığınızda çıkan iş yükünü yok eder. Bunun yerine, OpenStack Orkestrasyon servisi yükü izleyerek uygun şekilde sunucuları başlatabilir. Bunun nasıl ayarlanacağını bulmak için, bkz Orkestrasyon.

Bir etkimiz olduğunu onaylayın

Önceki adımlarda, çeşitli servisleri ayırdınız ve kapasiteyi genişlettiniz. Fraktal uygulamasının yeni özelliklerini görmek için, uygulama sunucularından birine SSH çekin ve yeni fraktallar oluşturun.

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

Not

IP_API_1 anahtarını ilk API sunucunun IP adresiyle ve USERNAME anahtarını uygun kullanıcı adıyla değiştirin.

Fraktallar üretmek için faafo create komutunu kullanın.

Fraktal üretim sürecini izlemek için faafo list komutunu kullanın.

Fraktalların bazılarını incelemek için faafo UUID komutunu kullanın.

generated_by alanı fraktalı üreten işçiyi gösterir. Birden çok işçi sunucu işi paylaştığından, fraktallar daha hızlı üretilir ve bir işçi başarısız olduğunda kullanıcı bunu anlamayabilir bile.

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                                                     |
+--------------+------------------------------------------------------------------+

Fraktallar artık app-api sunucularının herhangi birinden kullanılabilirler. Bunu doğrulamak için, http://IP_API_1/fractal/FRACTAL_UUID ve http://IP_API_2/fractal/FRACTAL_UUID adreslerini ziyaret edin. Artık birden fazla yedekli web servisiniz var. Biri başarısız olursa diğerlerini kullanabilirsiniz.

Not

IP_API_1 ve IP_API_2 değerlerini ilişkili değişken IP’lerle değiştirin. FRACTAL_UUID anahtarını mevcut bir fraktalın UUID değeriyle değiştirin.

Şimdi devam edip hata dayanıklılığını deneyin. İşçileri ve API sunucularını silmeye başlayın. İkisinden de birer tane olduğu sürece, uygulamanız çalışır. Ancak bir zayıf noktayı unutmayın. Veritabanı fraktalları ve fraktal metaverisini içerir. Bu sunucuyu kaybederseniz, uygulama durur. İleri kısımlar bu zayıf noktayı nasıl ele alabileceğinizi açıklar.

Bir yük dengeleyiciniz vardıysa, bu yükü iki farklı API servisi arasında dağıtabilirdiniz. Birçok seçeneğiniz vardır. kısmı seçeneklerden birini gösterir.

Teorik olarak, basit bir betik kullanarak işçileriniz ve API servisleriniz üzerindeki yükü izleyerek sunucuların oluşturulmasını tetikleyebilirdiniz, ki bunu yapmayı zaten biliyorsunuz. Tebrikler! Ölçeklenebilir bulut uygulamaları oluşturmaya hazırsınız.

Tabi ki tek bir uygulama için izleme sistemi oluşturmak mantıklı gelmeyebilir. OpenStack Orkestrasyon izlemeyi ve bu adımları otomatikleştirmek için otomatik ölçeklendirmeyi nasıl kullanacağınızı öğrenmek için, bkz Orkestrasyon.

Sonraki adımlar

Sunucular başlatma ve bu sunucular arasındaki bir uygulamadan servisleri dağıtma konusunda kendinize güveniyor olmalısınız.

Fraktallar uygulama mimarisine giriş kısmında bahsedildiği gibi, üretilen fraktal imajları API servisi sunucularının yerel dosya sistemlerinde kaydedilir. Çalışır birden çok API sunucunuz olduğundan, fraktal imajlar birden çok API servisleri arasında dağılmıştır, bu da yerel dosya sisteminde istenen fraktal imajı barındırmayan API servisi sunucularından fraktal imajı indirmeye çalışırken IOError: [Errno 2] Böyle bir dosya ya dizin yok istisnaları almanıza sebep olur.

Bu sorunu Nesne Depolamayı kullanarak zarif bir şekilde nasıl çözeceğinizi öğrenmek için Dayanıklı hale getirin kısmına gidin. Veya şu kısımlardan birine devam edin:

Tam kod örneği

Bu dosya bu öğretici kısmındaki tüm kodu içerir. Bu kapsamlı kod örneği kodu tek bir betik olarak görüntülemenizi ve çalıştırmanızı sağlar.

Bu betiği çalıştırmadan önce, kimlik doğrulama bilgisini, nitelik ve imaj kimliğini ayarladığınızdan emin olun.

# 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.