X

News, tips, partners, and perspectives for the Oracle Solaris operating system

Ressourcen-Jonglage mit Zonen

Resource Pools fuer Zonen gibt es schon eine ganze Weile.  Lange war es sogar so, dass die Kombination aus Resource Pools und Zonen die einzige Moeglichkeit der Resourcenkontrolle fuer Zonen war.  Mit der Einfuehrung von "capped-cpu" und einigen anderen Parametern wurde die Konfiguration in neueren Solaris-Versionen deutlich einfacher.  Und in den meisten Faellen erreicht man mit ihnen alles, was noetig ist.

Ein Aspekt geht dabei jedoch ein wenig unter: Gemeinsame Resource Pools.  Kuerzlich wurde ich gebeten, das einem Kunden naeher zu erklaeren.  Was liegt da naeher, als es gleich hier aufzuschreiben...

Denken wir uns einmal folgendes Szenario:
  • Wir haben eine typische 2-Tier Anwendung mit Appliaktions- und Datenbank-Schicht.
  • Beide Software-Komponenten werden jeweils nach CPU-Kernen lizenziert, aehnlich wie auch die Oracle Produkte.  Und bei beiden ist es zulaessig, die Anzahl der Lizenzen mit Resource Pools zu begrenzen.
  • Fuer jede Anwendung haben wir Lizenzen fuer jeweils 2 Kerne.
  • Wir brauchen zwei Umgebungen fuer diese Anwendung: Test und Produktion.  Leider benoetigen wir jeweils beide Kerne fuer die Produktion und koennen uns zusaetzliche Lizenzen fuer die Testumgebung nicht leisten.

Die naheligende Loesung ist, dass sich die beiden Umgebungen ihre jeweiligen Resourcen teilen.  Falls notwendig kann man die Testumgebung drosseln, um der Produktion den notwendigen Vorrang einzuraeumen.  Und so geht das:

Als erstes ein kurzer Blick auf die globale Zone und ihre CPU-Ressourcen.  Dann werden die beiden Zonen fuer Anwendung und Datenbank angelegt.  Bevor die Resource Pools ins Spiel kommen, teilen sich beide Zonen die CPUs mit der globalen Zone.  Ich werde hier immer mal wieder das Script "zonecores" von hier verwenden, um die Zuteilungen sichtbar zu machen.

root@mars:~# ./zonecores  -l
#
# Socket, Core, Strand and Zone Overview
#
Socket

Core

Strands

Zones
2

0

0,1,2,3,4,5,6,7 none
2

1

8,9,10,11,12,13,14,15 none
2

2

16,17,18,19,20,21,22,23 none
2

3

24,25,26,27,28,29,30,31 none
2

4

32,33,34,35,36,37,38,39 none
2

5

40,41,42,43,44,45,46,47 none
2

6

48,49,50,51,52,53,54,55 none
2

7

56,57,58,59,60,61,62,63 none
2

8

64,65,66,67,68,69,70,71 none
2

9

72,73,74,75,76,77,78,79 none
2

10

80,81,82,83,84,85,86,87 none
2

11

88,89,90,91,92,93,94,95 none
2

12

96,97,98,99,100,101,102,103 none
2

13

104,105,106,107,108,109,110,111 none
2

14

112,113,114,115,116,117,118,119 none
2

15

120,121,122,123,124,125,126,127 none
2

16

128,129,130,131,132,133,134,135 none
2

17

136,137,138,139,140,141,142,143 none
2

18

144,145,146,147,148,149,150,151 none
2

19

152,153,154,155,156,157,158,159 none
root@mars:~# zoneadm list -ivc
ID NAME STATUS PATH BRAND IP
0 global running / solaris shared
3 db-prod running /system/zones/db-prod solaris excl
4 app-prod running /system/zones/app-prod solaris excl
root@mars:~# for i in db-prod app-prod; do zonecfg -z $i info pool; done
pool:
pool:
root@mars:~# ./zonecores
#
# Checking Whole Core Assignments
#
OK - Zone db-prod using default pool.
OK - Zone app-prod using default pool.
root@mars:~# zlogin app-prod 'psrinfo |wc -l'
160

In diesem Beispiel sehen wir folgendes:

  • Die globale Zone hat 20 Kerne (ist also offenbar irgend eine LDom).  Mit 8 Strands pro Kern gibt das 160 vCPUs.
  • Im Moment sind noch keine CPUs irgend einem Pool zugewiesen.
  • Die beiden Zonen laufen, sind aber ebenfalls noch keinem Resource Pool zugewiesen.
  • Daher werden natuerlich alle 160 vCPUs von den beiden Zonen und der globalen Zone gemeinsam verwendet.  Daher ist natuerlich auch die Anzahl der vCPUs in einer Zone 160.

Als naechstes werden zwei Resource Pools angelegt, einer fuer jede Schicht der Anwendung, und jedem Pool ein Prozessor-Set mit je zwei Kernen zugewiesen.  Dann werden die Zonen daran gebunden und die CPUs neu gezaehlt.

root@mars:~# poolcfg -c 'create pool app-pool'
root@mars:~# poolcfg -c 'create pool db-pool'
root@mars:~# poolcfg -c 'create pset app-pset (uint pset.min = 16 ;
uint pset.max = 16 )'
root@mars:~# poolcfg -c 'create pset db-pset (uint pset.min = 16 ;
uint pset.max = 16 )'
root@mars:~# poolcfg -c 'associate pool app-pool ( pset app-pset) '
root@mars:~# poolcfg -c 'associate pool db-pool ( pset db-pset) '
root@mars:~# pooladm -c
root@mars:~# poolstat
pset
id pool size used load
0 pool_default 128 0.00 0.58
3 db-pool 16 0.00 0.14
2 app-pool 16 0.00 0.15
root@mars:~# psrset
user processor set 1: processors 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
user processor set 2: processors 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
root@mars:~# psrinfo |wc -l
128
root@mars:~# zonecfg -z app-prod set pool=app-pool
root@mars:~# zoneadm -z app-prod apply
zone 'app-prod': Checking: Setting pool=app-pool
zone 'app-prod': Applying the changes
root@mars:~# zonecfg -z app-prod info pool
pool: app-pool
root@mars:~# zonecfg -z db-prod set pool=db-pool
root@mars:~# zoneadm -z db-prod apply
zone 'db-prod': Checking: Setting pool=db-pool
zone 'db-prod': Applying the changes
root@mars:~# zonecfg -z db-prod info pool
pool: db-pool
root@mars:~# zlogin app-prod 'psrinfo |wc -l'
16
root@mars:~# zlogin db-prod 'psrinfo |wc -l'
16
root@mars:~# ./zonecores
#
# Checking Whole Core Assignments
#
OK - Zone db-prod using all 8 strands of core 0.
OK - Zone db-prod using all 8 strands of core 1.
OK - Zone app-prod using all 8 strands of core 2.
OK - Zone app-prod using all 8 strands of core 3.

Bei diesem Beispiel moechte ich auf folgendes hinweisen:

  • Das Resource Pool Subsystem (aka pool facility) war bereits aktiv.  Sollte das nicht der Fall sein, muss man es mit "pooladm -e" aktivieren.
  • Aenderungen an der Poolkonfiguration werden erst wirksam, wenn sie mit "pooladm -c" gespeichert und aktiviert werden.
  • Die Zonen wurden mittels "Zone Live Reconfiguration" an ihre Pools gebunden.  Ein Reboot der Zonen war dazu nicht notwendig.
  • Im Ergebnis hat nun jede Zone zwei Kerne exklusiv zugewiesen.  Die restlichen 16 Kerne verbleiben bei der globalen Zone.

Als naechstes brauchen wir zwei weitere Zonen fuer die Testumgebung.  Unmittelbar nach der Installation teilen sie sich die CPUs mit der globalen Zone.  Da wir uns das lizenztechnisch nicht leisten koennen, binden wir sie wieder an die jeweiligen Pools.

root@mars:~# zoneadm list -ivc
ID NAME STATUS PATH BRAND IP
0 global running / solaris shared
3 db-prod running /system/zones/db-prod solaris excl
7 app-prod running /system/zones/app-prod solaris excl
8 app-test running /system/zones/app-test solaris excl
9 db-test running /system/zones/db-test solaris excl
root@mars:~# for i in db-test app-test ; do zonecfg -z $i info pool ; done
pool:
pool:
root@mars:~# ./zonecores
#
# Checking Whole Core Assignments
#
OK - Zone db-prod using all 8 strands of core 0.
OK - Zone db-prod using all 8 strands of core 1.
OK - Zone app-prod using all 8 strands of core 2.
OK - Zone app-prod using all 8 strands of core 3.
OK - Zone app-test using default pool.
OK - Zone db-test using default pool.
root@mars:~# zonecfg -z app-test set pool=app-pool
root@mars:~# zoneadm -z app-test apply
zone 'app-test': Checking: Setting pool=app-pool
zone 'app-test': Applying the changes
root@mars:~# zonecfg -z db-test set pool=db-pool
root@mars:~# zoneadm -z db-test apply
zone 'db-test': Checking: Setting pool=db-pool
zone 'db-test': Applying the changes
root@mars:~# for i in db-test app-test ; do zonecfg -z $i info pool ; done
pool: db-pool
pool: app-pool
root@mars:~# zlogin db-test 'psrinfo |wc -l'
16
root@mars:~# ./zonecores
#
# Checking Whole Core Assignments
#
OK - Zone db-prod using all 8 strands of core 0.
OK - Zone db-prod using all 8 strands of core 1.
OK - Zone app-prod using all 8 strands of core 2.
OK - Zone app-prod using all 8 strands of core 3.
OK - Zone app-test using all 8 strands of core 2.
OK - Zone app-test using all 8 strands of core 3.
OK - Zone db-test using all 8 strands of core 0.
OK - Zone db-test using all 8 strands of core 1.
root@mars:~# ./zonecores -s
#
# Checking Core Resource Sharing
#
INFO - Core 0 used by 2 zones!

-> db-prod

-> db-test
INFO - Core 1 used by 2 zones!

-> db-prod

-> db-test
INFO - Core 2 used by 2 zones!

-> app-prod

-> app-test
INFO - Core 3 used by 2 zones!

-> app-prod

-> app-test

Damit haben wir nun zwei Zonen-Paare, die sich jeweils einen gemeinsamen Resource Pool teilen.  Angenommen, die Anwendung stoesst an die Leistungsgrenze und unser freundlicher Lizenzmanager spendiert uns eine Erweiterung auf 3 Kerne.  Dann wollen wir die Freude  natuerlich nicht durch einen Reboot trueben:

root@mars:~# poolcfg -c 'modify pset app-pset (uint pset.min = 24; 
uint pset.max = 24)'
root@mars:~# pooladm -c
root@mars:~# poolstat
pset
id pool size used load
0 pool_default 120 0.00 0.03
3 db-pool 16 0.00 0.00
2 app-pool 24 0.00 0.00
root@mars:~# ./zonecores -sc
#
# Checking Core Resource Sharing
#
INFO - Core 0 used by 2 zones!

-> db-prod

-> db-test
INFO - Core 1 used by 2 zones!

-> db-prod

-> db-test
INFO - Core 2 used by 2 zones!

-> app-prod

-> app-test
INFO - Core 3 used by 2 zones!

-> app-prod

-> app-test
INFO - Core 4 used by 2 zones!

-> app-prod

-> app-test
#
# Checking Whole Core Assignments
#
OK - Zone db-prod using all 8 strands of core 0.
OK - Zone db-prod using all 8 strands of core 1.
OK - Zone app-prod using all 8 strands of core 2.
OK - Zone app-prod using all 8 strands of core 3.
OK - Zone app-prod using all 8 strands of core 4.
OK - Zone app-test using all 8 strands of core 2.
OK - Zone app-test using all 8 strands of core 3.
OK - Zone app-test using all 8 strands of core 4.
OK - Zone db-test using all 8 strands of core 0.
OK - Zone db-test using all 8 strands of core 1.

Perfekt!  Der zusaetzliche Kern wurde ohne Unterbrechung in Betrieb genommen.

Nun gilt es natuerlich noch sicher zu stellen, dass die Testumgebung die Produktion nicht ausbremsen kann indem zu viel CPU verbraucht wird.  Es gibt zwei einfache Wege, das sicher zu stellen:  Man kann, innerhalb des Pools, die Anzahl der CPUs einer einzelnen Zone weiter limitieren.  Das nennt sich auch CPU capping.  Alternativ kann man mit dem Solaris Fair Share Scheduler der Produktion einen Anteil der CPU garantieren.  Im naechsten Beispiel werden wir daher die Testumgebung der Datenbank auf einen Kern begrenzen und der Produktion der Anwendung 75% der CPUs zusichern:

root@mars:~# zonecfg -z app-prod set scheduling-class=FSS
root@mars:~# zonecfg -z app-prod set cpu-shares=300
root@mars:~# zonecfg -z app-test set scheduling-class=FSS
root@mars:~# zonecfg -z app-test set cpu-shares=100
root@mars:~# zoneadm -z app-prod apply
zone 'app-prod': Checking: Adding rctl name=zone.cpu-shares
zone 'app-prod': Checking: Setting scheduling-class=FSS
zone 'app-prod': Applying the changes
root@mars:~# zoneadm -z app-test apply
zone 'app-test': Checking: Adding rctl name=zone.cpu-shares
zone 'app-test': Checking: Setting scheduling-class=FSS
zone 'app-test': Applying the changes
root@mars:~# zonecfg -z db-test 'add capped-cpu;set ncpus=8;end;commit'
root@mars:~# zoneadm -z db-test apply
zone 'db-test': Checking: Modifying rctl name=zone.cpu-cap
zone 'db-test': Applying the changes

Wieder gibt es ein paar Dinge zu beachten:

  • In den Applikations-Zonen wurde die Default Scheduler Class auf den Fair Share
    Scheduler (FSS) umgestellt.  Das funktioniert zwar bei laufender Zone, ich empfehle aber, diese Zonen zu einem passenden Zeitpunkt neu zu starten, damit der FSS seine volle Wirkung entfalten kann.
  • Theoretisch kann man beide Konzepte auch kombinieren: Capped CPU und CPU Shares.  Allerdings macht das die Konfiguration recht verwirrend.  Ausserdem widerspricht es ein wenig der Philosophie hinter den CPU Shares.  Ich empfehle das ausdruecklich nicht.
  • Der Fair Share Scheduler ist in SuperCluster Datenbank-Umgebungen nicht supported.  Ich empfehle, sich daran auch fuer sonstige Datenbank-Installationen zu halten, zumindest wenn RAC im Spiel ist.  Resource Pools ganz ohne FSS sind dagegen natuerlich ausdruecklich supported.  Sie sind ein Teil der Standard-Installation von Datenbank-Zonen auf SuperCluster.

Abschliessend noch ein Wort zum Thema Lizenzierung.  Im obigen Beispiel verwende ich Lizenzierung als Motivation fuer CPU Pools.  Die gezeigte Implementierung entspricht, nach bestem Wissen und Gewissen, den Anforderungen an hard partitioning bzw. Capped  Container und damit Lizenz-Capping fuer Kern-basierte Oracle Software.  Allerdings ist ausschliesslich der Oracle License Management Service authorisiert, eine solche Konfiguration abzunehmen.  Daher empfehle ich dringend, jede solche Konfiguration vor der Inbetriebnahme entsprechend pruefen und abnehmen zu lassen.  Gleiches gilt natuerlich entsprechend auch fuer die Lizenzierung von Software anderer Software Hersteller. 

Wie immer gibt es hier noch ein paar Links zu Dokumentation und weiterfuehrenden Informationen:

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha