Ushbu maqola Docker Swarm`ni sozlash va uni ishga tushirish haqida.

 

Swarm - bu docker konteynerlari uchun standart orkestrator hisoblanadi. Agar sizda docker o'rnatilgan bo'lsa, uning ichida swarm ham bor.

 

Maqola kimlarga mo'ljallangan:

  • Docker va docker compose bilan tajribaga ega bo'lganlarga
  • Docker registry haqida ma'lumotga ega bo'lganlarga
  • Virtual mashinalar haqida ma'lumotga ega bo'lganlarga

 

Darslik shartlari:

  • Docker va docker compose o'rnatilgan bo'lishi
  • Docker registry sozlangan bo'lishi
  • Virtual mashinalar sozlangan bo'lishi shart

 

Terminlar

Swarm`dan foydalanish uchun siz bir nechta turdagi swarm ob'ektlarni eslab qolishingiz kerak:

 

Node - bu docker o'rnatiladigan virtual mashinalarimiz, y'ani klasterda ishtirok etadigan docker namunasi. Node`larning ikki turi, boshqaruvchi (manager) va ishchi (worker) node`lari mavjud. Boshqaruvchi node`lari ishchi node`larini boshqaradi. U ishchi node`larga xizmat ko'rsatishni yaratish/yangilash/o'chirish, shuningdek ularni masshtablash va kerakli holatda saqlash uchun javobgardir. Ishchi node`lar faqat tayinlangan vazifalarni bajarish uchun ishlatiladi va klasterni boshqara olmaydi.

 

Stack - bu mantiqiy bog'liq bo'lgan xizmatlar to'plami. Stack - bu biz docker compose faylida ko'rsatgan xizmatlar to'plami. Stack`ning qismlari (xizmatlar) bir node`da yoki bir nechta node`da ham joylashishi mumkin.

 

Service (xizmat) - bu yuqorida ta`riflagan stack`dagi xizmat. Xizmat - bu konteynerlar tavsifi. Agar siz docker-compose.yaml dan foydalangan bo'lsangiz, unda siz ushbu ob'ekt bilan allaqachon tanishsiz. Standart maydonlarga qo'shimcha ravishda, docker swarm rejimida bir qator qo'shimcha maydonlarni o`z ichiga oladi, ularning aksariyati deploy bo'limida tavsiflangan.

 

Task (vazifa) - bu biz xizmatni tavsiflashda taqdim etgan ma'lumotlar asosida docker yaratgan konteyner hisoblanadi. Swarm konteyner holatini kuzatib boradi va kerak bo'lsa, uni qayta ishga tushiradi yoki boshqa node`ga o'tkazadi.

 

 

 

Klaster yaratish

Klaster to'g'ri ishlashi uchun virtual mashinalarda quyidagi portlar ochiq bo'lishi kerak. 

  • Boshqaruvchi node`lar uchun: 2376/tcp, 2377/tcp, 7946/tcp, 7946/udp, 4789/udp.
  • Ishchi node`lar uchun: 2376/tcp, 7946/tcp, 7946/udp, 4789/udp. 

Agar firewall sozlanmagan bo'lsa, portlar ochiq bo'ladi va alohida ochish shart emas.

Batafsil ma'lumotga ega bo'lish uchun mening olding “Docker klaster yaratish” nomli maqolamni o'qishni tavsiya beraman.

 

Stack yaratish

Stek`ni yaratish uchun misol sifatida 80 portida nginx serverini compose faylini yaratamiz. Buning uchun docker-stack.yml faylini yarating va quyidagini joylashtirib faylni master node`da saqlang:

# docker-stack.yml
version: '3.9'

services:

  mynginx:
    image: nginx:latest
    ports:
      - "80:80"

Endi tayyor nginx xizmatini klasterga joylashtiring. Buning uchun master node`da quyidagi buyruqni bajaring

$ docker stack deploy --with-registry-auth -c ./stack.yml nginx-stack

Javob sifatida consolda quyidagini ko'rasiz:

nginx-stack - bu stack nomi. --with-registry-auth opsiyasi registrdagi bir xil image`ni ishlatish uchun avtorizatsiya ma'lumotlarini ishchi node`ga o'tkazish imkonini beradi.

 

Stack`lar ro'yxatini quyidagi buyruq yordamida ko'rishingiz mumkin:

$ docker stack ls

Javob tariqasida quyidagini ko`rasiz

NAME          SERVICES
nginx-stack   1

 

Stack ichidagi xizmatlar ro'yhati:

Bu yerda nginx-stack_mynginx - xizmat nomi.

 

Xizmat haqida batafsil ma'lumotni ko'rish:

$ docker service inspect nginx-stack_mynginx
[
    {
        "ID": "mk3c254xa48x4owc5y9y0g3us",
        "Version": {
            "Index": 51
        },
        "CreatedAt": "2023-02-22T05:48:17.692281252Z",
        "UpdatedAt": "2023-02-23T05:29:42.995813121Z",
        …

        "Endpoint": {
            "Spec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80,
                        "PublishedPort": 80,
                        "PublishMode": "ingress"
                    }
                ]
            },
            "Ports": [
                {
                    "Protocol": "tcp",
                    "TargetPort": 80,
                    "PublishedPort": 80,
                    "PublishMode": "ingress"
                }
            ],
            "VirtualIPs": [
                {
                    "NetworkID": "us62l43ct7053dh205oxn0irh",
                    "Addr": "10.0.0.5/24"
                },
                {
                    "NetworkID": "c6268hjpimtf3q2ymaiwvsvxa",
                    "Addr": "10.0.1.2/24"
                }
            ]
        }
    }
]

Bu yerda xizmat qaysi portlarni tinglayotgani, qaysi tarmoqlarda ishtirok etishi, qanday statusga ega ekanligi va boshqa ko'p ma`lumotlarni ko'rishingiz mumkin.

 

Nginx xizmati ishlayotganiga ishonch hosil qilish uchun, istalgan node`ning IP manazilini browseringizda tering:

 

Stack`ni quyidagi buyruq yordamida o'chirish mumkin:

docker stack rm nginx-stack

 

Label

Swarm xizmatlarni xohlagan mavjud node’larga o'rnatadi, lekin odatda biz ularni ma'lum bir node’larga yoki ma'lum bir guruhga joylashtirishimiz kerak. Va buni label’lar yordamida bajarsak bo'ladi.

 

Misol uchun, bizda dev va prod muhiti mavjud. Dev muhiti ichki dastrulash uchun ishlatiladi (dasturchilar muhiti) va prod foydalanuvchilar muhiti uchun ishlatiladi.

 

Har bir muhit uchun bizda compose fayl mavjud: docker-stack.dev.yaml va docker-stack.prod.yaml. Odatiy holda swarm xizmatlarni node`larga tasodifiy joy`lashtiradi. Va biz dev xizmalarini faqat dev muhiti uchun mo'ljallangan virtual mashinada va xuddi shunday prod xizmatlarini prod muhiti uchun ishlashini xohlaymiz.

 

Misol tariqasida worker1 virtual mashinasini dev muhiti uchun va worker2 mashinasini prod muhiti uchun tag`lar bilan ajratamiz:

$ docker node update --label-add TAG=dev worker1

$ docker node update --label-add TAG=prod worker2

 

Hammasi muvaffaqiyatli o'tganiga ishonch hosil qilish uchun siz quyidagi buyruqni bajarishingiz kerak:

docker node inspect --format="{{json .Spec}}" worker1

Labels bo'limida biz qo'shgan tag`ni ko'rishimiz mumkin:

{"Labels":{"TAG":"dev"},"Role":"worker","Availability":"active"}

 

Endi compose faylimizga xizmatlarni ma`lum node`larga joylashtirish shartini placement direktivasi orqali ko'rsatishimiz kerak bo`ladi.

# docker-stack.dev.yml
version: '3.9'

services:
  mynginx:
    image: nginx:latest
    ports:
      - "80:80"
    # swarm
    deploy:
      placement:
        constraints:
          - "node.labels.TAG==dev"

docker-stack.prod.yml uchun ham shunga o'xshash bo'ladi, faqat prod tegi bilan (ammo, tashqi ramoq uchun siz boshqa portdan foydalanishingiz kerak, masalan, 8080). Stack pbyeklarini joylashtirgandan so'ng, xizmatlar faqat ma'lum bir tegga ega bo'lgan node`larga o'rnatilganligiga ishonch hosil qilasiz.

 

Routing

Ayni paytda bizda 3 node mavjud: master, worker1 va worker2. Agar biz docker-compose.prod.yml uchun stekimizni allaqachon ishlayotgan docker-compose.dev.yml steckiga o'xshab 80 portiga o'rnatishga harakat qilsak, port allaqachon band degan xatoga duch kelamiz. Bundan tashqari, agar biz master node`imizga 172.26.42.120:80 ping bersak, bizning xizmatimiz mavjudligini ko'ramiz, garchi biz hali bu node`ga hech narsa joylashtirmiadik. Xo'sh, nega bunday bo`ldi?

Buning sababi, swarm ingress tarmog'iga ega bo'lib, u node`lar orasidagi trafikni yo'naltirish uchun ishlatiladi. Agar xizmat umumiy portga ega bo'lsa, u holda swarm uni tinglaydi va agar so'rov kelib tushsa, quyidagilarni amalga oshiradi: u ushbu xost mashinasida konteyner bor-yo'qligini aniqlaydi, agar topolmasa, u ushbu xizmat uchun ishga tushirilgan koneyner joylashgan node`ni topadi va so'rovni u yerga yo'naltiradi.

 

Shu sababli biz docker-compose.prod.yaml uchun docker-compose.dev.yaml`da ko`rsatilgan portlardan farqli port ishlatishimiz kerak bo`ladi.

Swarm avtomatik mashrutizatsiyasni o'chirish uchun mode: host sozlamasidan foydalaniladi.

# docker-stack.prod.yml

version: '3.9'

services:
  mynginx:
    image: nginx:latest
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host

    # swarm
    deploy:
      placement:
        constraints:
          - "node.labels.TAG==prod"

 

Bundan tashqari tashqi load balancer`dan, masalan HAProxy xizmatidan foydalansa ham bo'ladi. Bunday xolatda so'rovlar dastlab HAProxyga tushadi, va keyin tegishli node`larga jo'natiladi.

 

ZeroDownTime Service Upgrade

 

Docker swarm`ning yana bir foydali hususiyatlaridan biri but xizmatlarni uzilishsiz yangilash. Ya'ni swarm xizmatlarni har qanday vaqtda ishlab turishini ta'minlab beradi.

 

Aval healthcheck directivasini qoshishdan boshlaymiz.

healthcheck:
  test: curl -sS http://127.0.0.1:8000/healthz || echo 1
  interval: 30s
  timeout: 3s
  retries: 12

 

Healthcheck conteyner holatini tekshirib turishda xizmat qiladi.

  • test - Docker konteynerning to'g'ri ishlayotganligini aniqlash uchun ushbu buyruqning natijasidan foydalanadi.
  • interval - holatni qanchalik tez-tez tekshirish kerak. Bu holda, har 30 soniyada.
  • timeout - buyruq bajarilishini kutish vaqti.
  • retries - konteyner ichidagi serverimiz holatini tekshirishga urinishlar soni.

Stack`ni joylashtirganingizdan so'ng, konteyner birinchi navbatda starting holatiga ega ekanligini ko'rasiz (konteynerimiz ichidagi server ishga tushmoqda) va bir muncha vaqt o'tgach, u healthy (server ishga tushirildi), aks holda unhealthy (server ishga tushishda xatolik yuz berdi) holatga o`tadi. Swarm rejimida Docker nafaqat konteynerning sog'lig'ini kuzatib boradi, balki nosog'lom holatga o'tgan taqdirda konteynerni qayta yaratishga harakat qiladi.

 

Biz dockerga konteynerning holatini kuzatishni o'rgatganimizdan so'ng, konteynerni yangilash sozlamalarni qo'shishimiz kerak:

deploy:
  replicas: 1
  update_config:
    parallelism: 1
    order: start-first
    failure_action: rollback
    delay: 10s

replicas - bu ishga tushirish kerak bo'lgan konteynerlar soni.

update_config direktivasi xizmatni qanday yangilash kerakligini tavsiflaydi:

  • parallelism - bir vaqtning o'zida yangilanadigan konteynerlar soni. Odatda, bu parametr 1 ga teng bo'ladi - konteynerlar birma bir yangilanadi. 0 - bir vaqtning o'zida barcha konteynerlarni yangilaydi. Ko'p hollarda, bu parametr sizning xizmatingizdagi replikalarning umumiy sonidan kam bo'lishi kerak.
  • order - konteynerlar yangilanish tartibi. Odatda stop-first, u avval joriy konteynerni to'xtatadi va keyin yangisini ishga tushiradi. Uzluksiz yangilanish uchun biz birinchi navbatda yangi konteynerni ishga tushirib va keyin eskisi to'xtatishimiz kerak.
  • failure_action - muvaffaqiyatsizlikka uchragan taqdirdagi strategiya. Bir nechta variant mavjud: davom ettirish, orqaga qaytarish yoki pauza qilish (standart).
  • delay - konteyner guruhini yangilash orasidagi kutish vaqti.

Bundan tashqari, yana bir foydali directiva mavjud - bu rollback_config. U yangilash paytida muvaffaqiyatsizlikka uchragan taqdirdagi harakatlarni tavsiflaydi. Shuningdek, muammolar yuzaga kelganda konteynerlarni qachon va qanday qayta ishga tushirishni tavsiflovchi restart_policy direktivasi ham mavjud.

deploy:
  replicas: 1
  update_config:
    parallelism: 1
    order: start-first
    failure_action: rollback
    delay: 10s
  rollback_config:
    parallelism: 0
    order: stop-first
  restart_policy:
    condition: any
    delay: 5s
    max_attempts: 3
    window: 120s
  • condition - mumkin bo'lgan qiymatlar: noneon-failure yoki any.
  • delay - qayta ishga tushirish urinishlari orasidagi kutish vaqti.
  • max_attempts - qayta ishga tushirishga urinishlarning maksimal soni.
  • window - qayta ishga tushirish muvaffaqiyatli bo'lganligini aniqlashdan oldin qancha kutish kerak.

 

Xulosa

Docker Swarm - bu yengil konteynerlar orkestratori. Va mening fikrimcha, u kichik va o'rta hajmli loyihalardagi ehtiyojni katta qismini qoplaydi. Kubernetesdan farqli ravishda, uni o'rganish ancha oson va ko'p resurs ajratishni talab qilmaydi. Bir necha kun ichida o'rganish mumkin.