Kontenery wspierane przez system Linux (LXC - Linux Containers) są jedną z najnowocześniejszych metod wirtualizacji aplikacji. LXC pozwala na przydział zasobów CPU, pamięci, dysków i sieci dla odizolowanych aplikacji od systemu operacyjnego. LXC separuje drzewa procesów, dostęp do sieci, ID użytkownika, dostęp do plików. LXC jest techniką funkcjonalnie umiejscawianą pomiędzy rozwiązaniem chroot oraz VM. Kontenery Linux są elastyczne, ponieważ pozwalają administratorowi wirtualizować pojedynczą aplikację, a nie cały system operacyjny z wykorzystaniem VM. Nie ma potrzeby zakupów dodatkowych licencji na systemy operacyjne, tak jak ma to miejsce w przypadku wirtualizacji. LXC oferuje także stosunkowo niewielki narzut systemu operacyjnego, ponieważ aplikacje wykorzystują standardowe wywołania systemowe oraz połączenia do interfejsów z lekkim systemem operacyjnym OS. Przykładowo w takich zastosowaniach warto rozważyć dystrybucję CoreOS.
Docker jest aplikacją pracującą na podbudowie w postaci LXC, która zarządza obrazami oraz asystuje we wdrożeniach wirtualizacji aplikacji. Dostarcza automatyzacji oraz mechanizmów szybkiego tworzenia kontenerów LXC. Dodatkowo udostępnia API, które rozszerza funkcjonalność LXC o możliwość budowania oferty PaaS (Platform as a Service). Docker pierwotnie nazywał się dotCloud, a jego kariera trwa od roku. Aktualnie jest zintegrowany z szeregiem innych narzędzi m.in. Ansible, Chef, OpenStack, Pupper, Salt. Jest też dołączony do RHEL, OpenShift PaaS, Google Compute Engine, Deis, a także Amazon Web Services Elastic Beanstalk. Docker jest aktualnie standardem wirtualizacji aplikacji dla systemu Linux.
Z racji na czasochłonne tworzenie kontenera LXC (ok. 55 minut), proszę przed rozpoczęciem wykonywania ćwiczeń wykonać instrukcje zawarte w punktach:
W trakcie tworzenia kontenera (ostatni podpunkt) można przejść do wykonywania instrukcji od początku.
W przypadku dotarcia do momentu tworzenia kontenera LXC, kontener nie będzie jeszcze gotowy, proszę rozpocząć ćwiczenie dotyczące Docker-a.
cgroups
są zamontowane:mount
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755) cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroups
nie są zamontowane należy wykonać następujące kroki:/etc/fstab
cgroup /sys/fs/cgroup cgroup defaults 0 0
mount /sys/fs/cgroup
cgroup
w odpowiednim kontrolerze.mount
– każdy punkt montowania to jeden kontroler.init
: cat /proc/1/cgroup
cd /sys/fs/cgroup ls -l
cgcreate
(przeczytaj manual):cgcreate [-h] [-t <tuid>:<tgid>] [-a <agid>:<auid>] [-f mode] [-d mode] [-s mode] -g <controllers>:<path> [-g ...]
cpulimited
w ramach controlera cpu
:cgcreate -g cpu:/cpulimited
cgget -g cpu:/cpulimited
ls -l /sys/fs/cgroup/cpu/cpulimited
cat
:cat
ps
odnajdujemy PID uruchomionego procesu programu cat
.cat /proc/PID/cgroup
tasks
. Używając tego pliku sprawdź czy PID powyższego procesu znajduje się w kontrolerach cpu
oraz blkio
należących do grupy, w której pracuje proces np.:cat /sys/fs/cgroup/cpu/CGROUP/tasks | grep PID
cpulimited
. Przeniesienie procesu jest możliwe na dwa sposoby (spróbuj wykonać ćwiczenie używając każdego z nich):cgclassify
(przeczytaj manual):cgclassify -g cpu:/cpulimited PID
tasks
:echo PID > /sys/fs/cgroup/cpu/cpulimited/tasks
# cat /proc/32194/cgroup 8:perf_event:/ 7:blkio:/ 6:net_cls,net_prio:/ 5:freezer:/ 4:devices:/user.slice 3:cpu,cpuacct:/ 2:cpuset:/ 1:name=systemd:/user.slice/user-1000.slice/session-1.scope
Przykład po:
# cat /proc/32194/cgroup 8:perf_event:/ 7:blkio:/ 6:net_cls,net_prio:/ 5:freezer:/ 4:devices:/user.slice 3:cpu,cpuacct:/cpulimited 2:cpuset:/ 1:name=systemd:/user.slice/user-1000.slice/session-1.scope
cgexec
(przeczytaj manual).cat
i uruchamiamy go ponownie, tym razem przy pomocy powyższego polecenia od razu w grupie cpulimited
:cgexec -g cpu:/cpulimited cat
cpulimited
:cgget -r cpu.shares / cgget -r cpu.shares /cpulimited
Obydwie wartości powinny być sobie równe.
echo
:echo 512 > /sys/fs/cgroup/cpu/cpulimited/cpu.shares
cgset
(przeczytaj manual):cgset -r cpu.shares=512 /cpulimited
matho-primes
. Należy samodzielnie znaleźć nazwę pakietu do doinstalowania np. przy pomocy wyszukiwarki na stronie: https://www.debian.org/distrib/packages#search_packageslscpu
matho-primes
uruchamiamy w głównej grupie tyle razy ile mamy procesorów (aby upewnić się, że każdy będzie obciążony).matho-primes 0 9999999999 > /dev/null &
top
.cpulimited
.PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1556 root 20 0 9116 3248 1356 R 88,4 0,0 0:54.89 matho-primes 1557 root 20 0 9116 3220 1316 R 88,1 0,0 0:54.41 matho-primes 1558 root 20 0 9116 3208 1308 R 87,4 0,0 0:54.56 matho-primes 1555 root 20 0 9116 3260 1364 R 86,1 0,0 0:55.41 matho-primes 1554 root 20 0 9116 3220 1316 R 41,6 0,0 0:27.02 matho-primes
W powyższym przykładzie proces 1554 został uruchomiony w grupie cpulimited
. Jak widzimy obciąża on procesor około 50% mniej w porównaniu z innymi procesami.
cpulimited
):
W poniższej instrukcji NAME
oznacza wybraną nazwę maszyny wirtualnej.
apt-get update
apt-get install lxc
apt-get install bridge-utils libvirt-bin debootstrap
lxc-checkconfig
lxc-<tab><tab>
lxc-create
:lxc-create -n NAME -t debian -- -r jessie
root
-a do naszej maszyny wirtualnej./var/lib/lxc/NAME/
.rootfs
, który jest montowany w maszynie wirtualnej jako główny system plików.root
-a do naszej maszyny wirtualnej:rootfs
:chroot rootfs
passwd
.Ctrl-D
- powłoka powinna powrócić do poprzedniego katalogu głównego.lxc-start
służy do uruchamiania kontenera:-d
.lxc-start -n NAME
root
-a, przy pomocy zmienionego hasła.lxc-stop
(przeczytać manual) można zakończyć działania maszyny wirtualnej w sposób czysty lub wymuszony (opcja -k
):lxc-stop -n NAME
init
:ps uax | grep init
init
pojawia się na liście?ls -l /proc/1/ns ls -l /proc/INITPID/ns
net
, czy są one sobie równe?cat
:cat
ps aux | grep cat
init
?kill
./sys/fs/cgroup/
w wyszukaj w nim pliki o nazwie NAME
:find . -name \*NAME\*
lxc-cgroup
(przeczytaj manual) np.:lxc-cgroup -n NAME cpu.shares
tasks
np. w kontrolerze cpu
grupy odnoszącej się do uruchomionej maszyny wirtualnej:cat /sys/fs/cgroup/cpu/lxc/NAME/tasks
apt-get update
less /var/lib/lxc/NAME/config
lxc.network.type
?lxc.container.conf(5)
)none
.lxc-stop
a później lxc-start
.net
, czy są one takie same?lxc-stop
.lxc.network.type
na veth
.lxc.network.flags = up
lxc.network.link = lxcbr0
brctl addbr lxcbr0 brctl addif lxcbr0 eth0
ifconfig eth0 0.0.0.0
dhclient lxcbr0
github
-a:git
-a:apt-get install git
git clone https://github.com/fsuplicy/bubbleSort.git
gcc -o bubbleSort bubbleSort.c
W razie braku kompilatora doinstaluj odpowiednie pakiety.
./bubbleSort test_data/extralarge.txt
bubbleSort
dalej istnieją w kontenerze.lxc-execute
(przeczytać manual) uruchom program echo
. Przykładowa komenda do uruchomienia może wyglądać następująco:echo "test OK"
apt-get install --no-install-recommends NAZWA_PAKIETU
lxc-execute
uruchom program bubbleSort
. Przykładowa komenda do uruchomienia może wyglądać następująco:lxc-execute -n NAME -- /root/bubbleSort/bubbleSort /root/bubbleSort/test_data/large3.txt
root
.uname -r
apt-get purge lxc-docker* docker.io*
/etc/apt/sources.list
:# Docker deb https://apt.dockerproject.org/repo debian-jessie main
apt
, który posłuży do weryfikacji pakietów instalowanych z repozytorium Docker-a:apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
apt-get update
https
, należy go doinstalować:#
.apt-get install apt-transport-https
apt-get install docker-engine
service docker start
debian
:docker run -it debian
docker images
docker ps
docker run -it debian
Ponownie sprawdź listę obrazów i kontenerów.
docker stop CONTAINER
:CONTAINERID
wywołajdocker ps
docker stop CONTAINERID
cat
:cat
ps uax | grep cat
Czy taki proces istnieje?
cat
(CATPID
).ls -l /proc/CATPID/ns
Czy są one takie same jak w przypadku procesu init
?
cat /proc/CATPID/cgroup
tasks
np. w kontrolerze cpu
grupy odnoszącej się do uruchomionej maszyny wirtualnej.cat
wyświetla się jeszcze jakiś identyfikator?cat
przy pomocy kill
. Czy proces w maszynie wirtualnej zakończył się?docker stop CONTAINER
.docker run
.docker diff CONTAINERID
docker commit
:docker commit CONTAINERID debian:newfile
commit
-a wykonujemy następujące polecenie:docker run -ti debian:newfile
Po uruchomieniu się kontenera sprawdź czy stworzony wcześniej plik nadal się w nim znajduje.
apt-get update
git
-a:apt-get install git
git clone https://github.com/fsuplicy/bubbleSort.git
gcc -o bubbleSort bubbleSort.c
W razie braku kompilatora doinstalować odpowiednie pakiety.
./bubbleSort test_data/extralarge.txt
commit
.docker stop
.
Tworzenie swoich obrazów może być zautomatyzowane poprzez stworzenie tzw. Dockerfile
, pliku który zawiera wszystkie instrukcje potrzebne do osiągnięcia zamierzonego celu.
Plik ten definiuje takie kwestie jak:
Dockerfile
:FROM
, ENV
, RUN
, ADD
, CMD
.Dockerfile
, który zautomatyzuje pobieranie oraz uruchamianie programu do sortowania:DDIR
).Dockerfile
:debian
:FROM debian
ENV
:dir
zmienna określająca nazwę folderu w którym znajduje się program bubbleSort
. Ustawiamy jej wartość na bubbleSort
.exe
nazwa pliku wykonywalnego. Ustawiamy jej wartość również na bubbleSort
.tos
nazwa pliku do posortowania. Wybieramy jeden plik z katalogu bubbleSort/test_data
np. large1.txt
.RUN
konfigurujemy środowisko kontenera:# Update repositories and install dependencies RUN apt-get -y update && \ apt-get install -y gcc git
RUN
wywołujemy komendę pobierającą kod z repozytorium.RUN
kompilujemy nasz program:# Build executables from source files RUN cd $dir && gcc *.c -o $exe
# Run sorting CMD cd $dir && ./$exe test_data/$tos
docker build
.-t
w celu zdefiniowania nazwy i tagu naszego obrazu:bubblesort
.1.0
.docker build -t bubblesort:1.0 DDIR
Zakładamy, że wywołanie powyższej komendy odbywa się z poziomu katalogu w którym znajduje się nasz folder DDIR
.
docker images
Przykładowy rezultat:
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE bubblesort 1.0 3677d1f27a16 23 minutes ago 309.3 MB debian newfile 1c11dae8ea4b 47 hours ago 309.3 MB ubuntu latest e9ae3c220b23 9 days ago 187.9 MB debian latest 91bac885982d 9 days ago 125.1 MB
docker run bubblesort:1.0
Czy wyświetlane są rezultaty sortowania?
docker history bubblesort:1.0
Dockerfile
-a o instrukcję pozwalającą na dodanie własnych plików do budowanego obrazu.DDIR
i tworzymy własny plik do posortowania:CNT=10 && echo $CNT > myfile.txt && for I in `seq 1 $CNT` ; do echo $RANDOM ; done | tr '\n' ' ' >> myfile.txt
Wartość zmiennej CNT
możemy zmieniać według upodobań.
Dockerfile
zmieniamy wartość zmiennej tos
na myfile.txt
.ADD
dodajemy nasz plik do obrazu:CMD
:# Clone generated file to bubblesort directory ADD $tos $dir/test_data
2.0
.
Ostatnim zadaniem będzie skopiowanie pliku myfile.txt
z uruchomionego obrazu bubblesort:2.0
do uruchomionego obrazu bubblesort:1.0
i posortowanie go.
W tym celu wykonujemy następujące kroki:
/bin/bash
:docker run -ti bubblesort:1.0 /bin/bash docker run -ti bubblesort:2.0 /bin/bash
myfile.txt
na dysk lokalny a dopiero następnie do docelowego kontenera.docker ps
sprawdzamy identyfikator kontenera stworzonego z obrazu bubblesort:2.0
.docker cp
.docker ps
sprawdzamy identyfikator kontenera stworzonego z obrazu bubblesort:1.0
.docker cp
.