Úvod

Solaris kontejnery, tzv. zóny je technologie, sloužící ke spuštění odděleného virtualizovaného systému na úrovni hostitelského. Nejedná se o plnohodnotnou virtualizaci, takže v zóně nemůžeme spustit jakýkoliv systém si zamaneme, ale pouze ty, které jsou podporované. V aktuální době je to pouze Solaris, ale existuje ještě projekt brandZ, který umožňuje virtualizaci Linuxu řady 2.4, anebo experimentální 2.6. Projektem brandZ bych se chtěl zabývat až v pokračování článku, takže dneska si ukážeme jen vytváření Solaris zón a resource poolu. Alternativou k zónám jsou na BSD Jaily a na Linuxu například OpenVZ.

Smysl použití

Proč používat zóny a ne například plnohodnotně virtualizované systémy? Především kvůli hardwarovým nárokům. Zóna neprochází celým bootovacím procesem, takže nenačítá vlastní kernel, ale používá ten z hostitelského systému. Přesto je od hostitelského systému oddělena, můžeme jí efektivně přidělit výpočetní výkon, paměť a swap pomocí resource poolů, nastavením capped-cpu a capped-memory, anebo použitím FSS (Fair Share Scheduler) přímo v zóně. Zóny umožňují administrátorovi zajistit levné řešení se stejnou stabilitou a bezpečností jako při využití více fyzických serverů. Typů zón (tzv. brands) existuje několik, v tomto článku se budu zabývat pouze výhozím typem v OpenSolarisu, kterým je ipkg.

Resource pooly

Resource pooly je jedna z možností rozdělení výkonu systému. Ve výchozím nastavení existuje pool_default, který obsahuje celý výkon a je přidělen k zóně global, kterou je sám hostitelský systém. Začneme tím, že aktivujeme resource pooly a necháme si vypsat aktuální nastavení k naší global zóně, tzn. hostitelskému systému.

$ pooladm -e
$ pooladm 
 
system default
	string	system.comment 
	int	system.version 1
	boolean	system.bind-default true
	string	system.poold.objectives wt-load
 
	pool pool_default
		int	pool.sys_id 0
		boolean	pool.active true
		boolean	pool.default true
		int	pool.importance 1
		string	pool.comment 
		pset	pset_default
 
	pset pset_default
		int	pset.sys_id -1
		boolean	pset.default true
		uint	pset.min 1
		uint	pset.max 65536
		string	pset.units population
		uint	pset.load 157
		uint	pset.size 2
		string	pset.comment 
 
		cpu
			int	cpu.sys_id 1
			string	cpu.comment 
			string	cpu.status on-line
 
		cpu
			int	cpu.sys_id 0
			string	cpu.comment 
			string	cpu.status on-line
Graficky bychom aktuální stav zobrazili nějak takto. Máme globální zónu (hostitelský systém), využivající výchozí resource pool. Pomocí resource poolů můžeme rozdělovat výkon jen na počet procesorů. Jelikož mám na laptopu jen dva, tak vytvořím jako příklad jen jeden resource pool s tím, že dostane minimálně a maximálně 1 procesor.
# Vytvoříme pset
$ poolcfg -c 'create pset zone0-pset (uint pset.min=1; uint pset.max=1)' 
 
# Vytvoříme pool
$ poolcfg -c 'create pool zone0'
 
# Přiřadíme pool k psetu
$ poolcfg -c 'associate pool zone0 (pset zone0-pset)'
 
# Uložíme konfiguraci a zobrazíme si výsledek
$ pooladm -c
$ pooladm 
 
system default
	string	system.comment 
	int	system.version 1
	boolean	system.bind-default true
	string	system.poold.objectives wt-load
 
	pool zone0
		int	pool.sys_id 1
		boolean	pool.active true
		boolean	pool.default false
		int	pool.importance 1
		string	pool.comment 
		pset	zone0-pset
 
	pool pool_default
		int	pool.sys_id 0
		boolean	pool.active true
		boolean	pool.default true
		int	pool.importance 1
		string	pool.comment 
		pset	pset_default
 
	pset zone0-pset
		int	pset.sys_id 1
		boolean	pset.default false
		uint	pset.min 1
		uint	pset.max 1
		string	pset.units population
		uint	pset.load 0
		uint	pset.size 0
		string	pset.comment 
 
	pset pset_default
		int	pset.sys_id -1
		boolean	pset.default true
		uint	pset.min 1
		uint	pset.max 65536
		string	pset.units population
		uint	pset.load 91
		uint	pset.size 2
		string	pset.comment 
 
		cpu
			int	cpu.sys_id 1
			string	cpu.comment 
			string	cpu.status on-line
 
		cpu
			int	cpu.sys_id 0
			string	cpu.comment 
			string	cpu.status on-line
Pokud bychom pset.min nastavili na 0, měla by globální zóna veškerý výkon (viz. pset.min a pset.max u pset_default) a náš nový pool by měl výkon maximálně jednoho procesoru. Jelikož jsme nastavili pset.min na 1, tak našemu novému poolu garantujeme výkon jednoho procesoru. Grafické znázornění:

Zóny

Začneme tím, že vytvoříme vlastní oddíl pro ukládání zón a hned jeden pro naší cvičnou zónu.

$ zfs create rpool/export/zones
$ zfs create rpool/export/zones/zone0
$ zfs list|grep zone
rpool/export/zones          38K  11,8G    19K  /export/zones
rpool/export/zones/zone0    19K  11,8G    19K  /export/zones/zone0
Ještě upravíme oprávnění, jinak nám zoneadm nedovolí nainstalovat zónu.
$ chmod -R 700 /export/zones
A teď se pustíme do zóny samotné.
$ zonecfg -z zone0
zone0: No such zone configured
Use 'create' to begin configuring a new zone.
zonecfg:zone0> create
# Místo umístění naší zóny
zonecfg:zone0> set zonepath=/export/zones/zone0
# Chceme aby zóna nabootovala po bootu fyzického stroje?
zonecfg:zone0> set autoboot=true
# Přiřadíme zónu k resource poolu zone0
zonecfg:zone0> set pool=zone0
# Přidáme síťové rozhraní e1000g0
zonecfg:zone0> add net
zonecfg:zone0:net> set physical=e1000g0
# Přiřadíme IP adresu
zonecfg:zone0:net> set address=192.168.1.15/24
zonecfg:zone0:net> end
zonecfg:zone0> verify
zonecfg:zone0> commit
zonecfg:zone0> exit
To by bylo, teď samotná instalace zóny, to bude chvíli trvat tak si zatím skočte třeba někam na kafe nebo oběd.
zoneadm -z zone0 install
Hotovo? Tak můžeme nabootovat a lognout se do naší nové zóny.
$ zoneadm -z zone0 ready
$ zoneadm list
global
zone0
$ zoneadm -z zone0 boot
$ zlogin -C zone0
Bude následovat krátký konfigurátor a pak už jen login prompt.
zone0 console login: root
Password: 
Nov  3 13:36:01 zone0 login: ROOT LOGIN /dev/console
Sun Microsystems Inc.   SunOS 5.11      snv_111b        November 2008
root@zone0:~# zoneadm list
zone0
root@zone0:~# pooladm
 
system default
	string	system.comment 
	int	system.version 1
	boolean	system.bind-default true
	string	system.poold.objectives wt-load
 
	pool zone0
		int	pool.sys_id 1
		boolean	pool.active true
		boolean	pool.default false
		int	pool.importance 1
		string	pool.comment 
		pset	zone0-pset
 
	pset zone0-pset
		int	pset.sys_id 1
		boolean	pset.default false
		uint	pset.min 1
		uint	pset.max 1
		string	pset.units population
		uint	pset.load 44
		uint	pset.size 1
		string	pset.comment 
 
		cpu
			int	cpu.sys_id 0
			string	cpu.comment 
			string	cpu.status on-line
 
root@zone0:~# psrinfo 
0	on-line   since 10/27/2009 10:31:46
No není to nádhera? :-) Máme zónu s přiděleným jedním procesorem a systémem, odděleným od hostitelského. Můžeme si s ním dělat co chceme, spustit si na něm odděleně některé služby, cokoliv. Graficky teď máme něco takového:

Připojení adresáře z globálního systému do zóny

Teď vypneme zónu a trošku si s ní pohrajeme. Co třeba připojit svůj domovský adresář do zóny?

$ zoneadm -z zone0 halt
$ zonecfg -z zone0
# Přídáme filesystém
zonecfg:zone0> add fs
# Adresář v globální zóně, který chceme připojit
zonecfg:zone0:fs> set dir=/export/home/kenji
# Místo v zone0, kam ho chceme připojit
zonecfg:zone0:fs> set special=/export/home/kenji
# Typ loopback
zonecfg:zone0:fs> set type=lofs
# Oprávnění k zápisu
zonecfg:zone0:fs> set options=[rw,nodevices]
zonecfg:zone0:fs> end
zonecfg:zone0> commit
zonecfg:zone0> exit
$ zoneadm -z zone0 boot
$ zlogin -C zone0
root@zone0:~# ls /export/home/kenji/
Data                 TODO                 zony_a_crossbow.txt
Downloads            basne.txt
Plocha               packages
Pokud byste v zóně chtěli mít některé věci stejné jako v globální zóně, například abyste nemuseli manuálně instalovat všechny aplikace, můžete před instalací zóny použít parametr inherit-pkg-dir a nastavit jej třeba na /usr /lib /bin, apod.

Capped CPU a memory

Pokud nepotřebujeme nebo nechceme vytvářet celý resource pool, můžeme jednotlivým zónám přidat resource capped-cpu a capped-memory. Parametr ncpu v resource capped-cpu může být i menší než 1, kdy celá čísla udávají celé procesory.

$ zonecfg -z zone0
zonecfg:zone0> add capped-cpu
zonecfg:zone0:capped-cpu> set ncpus=0,5
zonecfg:zone0:capped-cpu> end
zonecfg:zone0> add capped-memory
zonecfg:zone0:capped-memory> set physical=512M
zonecfg:zone0:capped-memory> set swap=1024M
zonecfg:zone0:capped-memory> set locked=10M
zonecfg:zone0:capped-memory> end
zonecfg:zone0> commit
zonecfg:zone0> exit
Tím jsem jednoduchým způsobem přidělil zóně polovinu výkonu jednoho procesoru, nastavil maximální paměť na 512MB, swap na 1024MB a locked memory na 10MB. Příkazem info si mohu prohlédnout všechna nastavení zóny.
nename: zone0
zonepath: /export/zones/zone0
brand: ipkg
autoboot: false
bootargs: 
pool: zone0
limitpriv: 
scheduling-class: 
ip-type: shared
hostid: 
fs:
        dir: /export/home/kenji
        special: /export/home/kenji
        raw not specified
        type: lofs
        options: [rw,nodevices]
net:
        address: 192.168.1.15/24
        physical: e1000g0
        defrouter: not specified
capped-cpu:
        [ncpus: 0,50]
capped-memory:
        physical: 512M
        [swap: 1G]
        [locked: 10M]
rctl:
        name: zone.cpu-cap
        value: (priv=privileged,limit=50,action=deny)
rctl:
        name: zone.max-swap
        value: (priv=privileged,limit=1073741824,action=deny)
rctl:
        name: zone.max-locked-memory
        value: (priv=privileged,limit=10485760,action=deny)i

Fair Share Scheduler

Pokud nám nevyhovuje přidělování výkonu pomocí capped nebo resource poolů, můžeme použít FSS. FSS funguje tak, že přidělíme jednotlivým zónám garantovaný výkon číslem, udávající prioritu dané zóny. Čím vyšší hodnota, tím větší výkon přidělíme. Nejprve nastavíme FSS pro výchozí resource pool, aktualizujeme nastavení a přesuneme všechny procesy pod FSS.

$ poolcfg -c 'modify pool pool_default (string pool.scheduler="FSS")'
$ poolcfg -c
$ priocntl -s -c FSS -i class TS
$ priocntl -s -c FSS -i pid 1
Zbývá přidělit jednotlivým zónám část výkonu (jakoby pomyslnou prioritu). Například takto.
zonecfg:zone0> add rctl
zonecfg:zone0:rctl> set name=zone.cpu-shares
zonecfg:zone0:rctl> add value (priv=privileged,limit=3,action=none)
zonecfg:zone0:rctl> end
zonecfg:zone0> exit
zonecfg:zone1> add rctl
zonecfg:zone1:rctl> set name=zone.cpu-shares
zonecfg:zone1:rctl> add value (priv=privileged,limit=2,action=none)
zonecfg:zone1:rctl> end
zonecfg:zone1> exit
Zóna zone0 má teď přidělenou prioritu 3 a zóna zone1 prioritu 2. Zóna zone0 má tedy garantovaný větší výpočetní výkon nez zóna zone1. Graficky:

Čistka

Tak a to je pro dnešek vše. Nakonec po sobě uděláme pořádek.

$ zoneadm -z zone0 halt
$ zoneadm -z zone0 uninstall
$ zfs destroy -r rpool/export/zones/zone0
$ zfs destroy -r rpool/export/zones
$ poolcfg -c 'destroy pool zone0'
$ poolcfg -c 'destroy pset zone0-pset'
$ pooladm -c
$ pooladm -d