From b8a86a7e94d8ba235a53a5edced19af1f63059e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:41:38 +0000 Subject: [PATCH] Deployed 6cb6017 with MkDocs version: 1.6.1 --- .nojekyll | 0 404.html | 1343 ++++ apis/application.html | 2018 +++++ apis/cluster.html | 2106 ++++++ apis/index.html | 1562 ++++ apis/logs.html | 1623 ++++ apis/task.html | 1658 ++++ applications/index.html | 1612 ++++ applications/instances.html | 1514 ++++ applications/operations.html | 2074 +++++ applications/outage.html | 1559 ++++ applications/specification.html | 3171 ++++++++ .../twemoji@15.1.0/assets/svg/1f642.svg | 1 + .../fonts.googleapis.com/css.49ea35f2.css | 594 ++ .../v32/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 | Bin 0 -> 10656 bytes .../v32/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 | Bin 0 -> 13360 bytes .../v32/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2 | Bin 0 -> 6144 bytes .../v32/KFOjCnqEu92Fr1Mu51TjASc2CsTKlA.woff2 | Bin 0 -> 1536 bytes .../v32/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 | Bin 0 -> 16756 bytes .../v32/KFOjCnqEu92Fr1Mu51TjASc5CsTKlA.woff2 | Bin 0 -> 7708 bytes .../v32/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 | Bin 0 -> 20216 bytes .../v32/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 | Bin 0 -> 10356 bytes .../v32/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 | Bin 0 -> 13104 bytes .../v32/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2 | Bin 0 -> 6148 bytes .../v32/KFOjCnqEu92Fr1Mu51TzBic2CsTKlA.woff2 | Bin 0 -> 1468 bytes .../v32/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 | Bin 0 -> 16080 bytes .../v32/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2 | Bin 0 -> 7464 bytes .../v32/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 | Bin 0 -> 19780 bytes .../v32/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2 | Bin 0 -> 1516 bytes .../v32/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 | Bin 0 -> 16688 bytes .../v32/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 | Bin 0 -> 13224 bytes .../v32/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2 | Bin 0 -> 6144 bytes .../roboto/v32/KFOkCnqEu92Fr1Mu51xIIzI.woff2 | Bin 0 -> 20144 bytes .../v32/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2 | Bin 0 -> 7724 bytes .../v32/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 | Bin 0 -> 10492 bytes .../v32/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 | Bin 0 -> 9684 bytes .../roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 | Bin 0 -> 18492 bytes .../v32/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2 | Bin 0 -> 7180 bytes .../v32/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2 | Bin 0 -> 1500 bytes .../v32/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 | Bin 0 -> 15028 bytes .../v32/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 | Bin 0 -> 12324 bytes .../v32/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2 | Bin 0 -> 5688 bytes .../v32/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 | Bin 0 -> 9780 bytes .../roboto/v32/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 | Bin 0 -> 18596 bytes .../v32/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2 | Bin 0 -> 6904 bytes .../v32/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2 | Bin 0 -> 1456 bytes .../v32/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 | Bin 0 -> 14740 bytes .../v32/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 | Bin 0 -> 12304 bytes .../v32/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2 | Bin 0 -> 5708 bytes .../roboto/v32/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 | Bin 0 -> 7096 bytes .../s/roboto/v32/KFOmCnqEu92Fr1Mu4mxK.woff2 | Bin 0 -> 18536 bytes .../roboto/v32/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 | Bin 0 -> 9852 bytes .../roboto/v32/KFOmCnqEu92Fr1Mu72xKOzY.woff2 | Bin 0 -> 15336 bytes .../roboto/v32/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 | Bin 0 -> 12456 bytes .../roboto/v32/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 | Bin 0 -> 5796 bytes .../roboto/v32/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 | Bin 0 -> 1496 bytes ...wgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2 | Bin 0 -> 24792 bytes ...wgGEFl0_3vrtSM1J-gEPT5Ese6hmHSZ0mf0h.woff2 | Bin 0 -> 16296 bytes ...wgGEFl0_3vrtSM1J-gEPT5Ese6hmHSd0mf0h.woff2 | Bin 0 -> 7528 bytes ...5mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2 | Bin 0 -> 22736 bytes ...wgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2 | Bin 0 -> 10096 bytes ...wgGEFl0_3vrtSM1J-gEPT5Ese6hmHSx0mf0h.woff2 | Bin 0 -> 13036 bytes ...euFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2 | Bin 0 -> 7972 bytes ...euFoqFrlnAIe2Imhk1T8rbociImtEleUlYIw.woff2 | Bin 0 -> 17428 bytes ...euFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2 | Bin 0 -> 26644 bytes ...q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2 | Bin 0 -> 24652 bytes ...euFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2 | Bin 0 -> 10704 bytes ...euFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2 | Bin 0 -> 14288 bytes .../external/unpkg.com/iframe-worker/shim.js | 1 + .../unpkg.com/mermaid@11/dist/mermaid.min.js | 2314 ++++++ assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.83f73b43.min.js | 16 + assets/javascripts/bundle.83f73b43.min.js.map | 7 + assets/javascripts/glightbox.min.js | 1 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.el.min.js | 1 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.he.min.js | 1 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.hy.min.js | 1 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.kn.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + assets/javascripts/lunr/min/lunr.sa.min.js | 1 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.te.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.6ce7567c.min.js | 42 + .../workers/search.6ce7567c.min.js.map | 7 + assets/stylesheets/glightbox.min.css | 1 + assets/stylesheets/main.0253249f.min.css | 1 + assets/stylesheets/main.0253249f.min.css.map | 1 + assets/stylesheets/palette.06af60db.min.css | 1 + .../stylesheets/palette.06af60db.min.css.map | 1 + cluster/cluster.html | 1692 +++++ cluster/setup/controller.html | 2278 ++++++ cluster/setup/executor-setup.html | 2443 ++++++ cluster/setup/gateway.html | 1920 +++++ cluster/setup/maintenance.html | 1728 +++++ cluster/setup/planning.html | 1620 ++++ cluster/setup/prerequisites.html | 1523 ++++ cluster/setup/units.html | 1556 ++++ cluster/setup/zookeeper.html | 1913 +++++ extra/cli.html | 1414 ++++ extra/epoch.html | 1474 ++++ extra/libraries.html | 1993 +++++ extra/nvidia.html | 1581 ++++ getting-started.html | 1678 +++++ images/app-instance-state-machine.png | Bin 0 -> 79463 bytes images/app-state-machine.png | Bin 0 -> 86932 bytes images/cluster.png | Bin 0 -> 60161 bytes images/cluster.svg | 1 + images/drove-home.png | Bin 0 -> 112127 bytes images/task-state-machine.png | Bin 0 -> 38684 bytes index.html | 1768 +++++ search/search_index.js | 1 + search/search_index.json | 1 + sitemap.xml | 115 + sitemap.xml.gz | Bin 0 -> 412 bytes stylesheets/extra.css | 4 + tasks/index.html | 1631 ++++ tasks/operations.html | 1703 +++++ tasks/specification.html | 2747 +++++++ 144 files changed, 63213 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 apis/application.html create mode 100644 apis/cluster.html create mode 100644 apis/index.html create mode 100644 apis/logs.html create mode 100644 apis/task.html create mode 100644 applications/index.html create mode 100644 applications/instances.html create mode 100644 applications/operations.html create mode 100644 applications/outage.html create mode 100644 applications/specification.html create mode 100644 assets/external/cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/1f642.svg create mode 100644 assets/external/fonts.googleapis.com/css.49ea35f2.css create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc2CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc5CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic2CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xIIzI.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBBc4.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxK.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu72xKOzY.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSZ0mf0h.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSd0mf0h.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSx0mf0h.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEleUlYIw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2 create mode 100644 assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2 create mode 100644 assets/external/unpkg.com/iframe-worker/shim.js create mode 100644 assets/external/unpkg.com/mermaid@11/dist/mermaid.min.js create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.83f73b43.min.js create mode 100644 assets/javascripts/bundle.83f73b43.min.js.map create mode 100644 assets/javascripts/glightbox.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.el.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.he.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.te.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.6ce7567c.min.js create mode 100644 assets/javascripts/workers/search.6ce7567c.min.js.map create mode 100644 assets/stylesheets/glightbox.min.css create mode 100644 assets/stylesheets/main.0253249f.min.css create mode 100644 assets/stylesheets/main.0253249f.min.css.map create mode 100644 assets/stylesheets/palette.06af60db.min.css create mode 100644 assets/stylesheets/palette.06af60db.min.css.map create mode 100644 cluster/cluster.html create mode 100644 cluster/setup/controller.html create mode 100644 cluster/setup/executor-setup.html create mode 100644 cluster/setup/gateway.html create mode 100644 cluster/setup/maintenance.html create mode 100644 cluster/setup/planning.html create mode 100644 cluster/setup/prerequisites.html create mode 100644 cluster/setup/units.html create mode 100644 cluster/setup/zookeeper.html create mode 100644 extra/cli.html create mode 100644 extra/epoch.html create mode 100644 extra/libraries.html create mode 100644 extra/nvidia.html create mode 100644 getting-started.html create mode 100644 images/app-instance-state-machine.png create mode 100644 images/app-state-machine.png create mode 100644 images/cluster.png create mode 100644 images/cluster.svg create mode 100644 images/drove-home.png create mode 100644 images/task-state-machine.png create mode 100644 index.html create mode 100644 search/search_index.js create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz create mode 100644 stylesheets/extra.css create mode 100644 tasks/index.html create mode 100644 tasks/operations.html create mode 100644 tasks/specification.html diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..d25348d --- /dev/null +++ b/404.html @@ -0,0 +1,1343 @@ + + + + + + + + + + + + + + + + + + + Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apis/application.html b/apis/application.html new file mode 100644 index 0000000..d1a531e --- /dev/null +++ b/apis/application.html @@ -0,0 +1,2018 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Application Management - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Application Management

+

Issue application operation command

+

POST /apis/v1/applications/operations

+

Request +

curl --location 'http://drove.local:7000/apis/v1/operations' \
+--header 'Content-Type: application/json' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data '{
+    "type": "SCALE",
+    "appId": "TEST_APP-1",
+    "requiredInstances": 1,
+    "opSpec": {
+        "timeout": "1m",
+        "parallelism": 20,
+        "failureStrategy": "STOP"
+    }
+}'
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "appId": "TEST_APP-1"
+    },
+    "message": "success"
+}
+

+
+

Tip

+

Relevant payloads for application commands can be found in application operations section.

+
+

Cancel currently running operation

+

POST /apis/v1/applications/operations/{appId}/cancel

+

Request +

curl --location --request POST 'http://drove.local:7000/apis/v1/operations/TEST_APP/cancel' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+

+

Response +

{
+    "status": "SUCCESS",
+    "message": "success"
+}
+

+

Get list of applications

+

GET /apis/v1/applications

+

Request +

curl --location 'http://drove.local:7000/apis/v1/applications' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "TEST_APP-1": {
+            "id": "TEST_APP-1",
+            "name": "TEST_APP",
+            "requiredInstances": 0,
+            "healthyInstances": 0,
+            "totalCPUs": 0,
+            "totalMemory": 0,
+            "state": "MONITORING",
+            "created": 1719826995764,
+            "updated": 1719892126096
+        }
+    },
+    "message": "success"
+}
+

+

Get info for an app

+

GET /apis/v1/applications/{id}

+

Request +

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "id": "TEST_APP-1",
+        "name": "TEST_APP",
+        "requiredInstances": 1,
+        "healthyInstances": 1,
+        "totalCPUs": 1,
+        "totalMemory": 128,
+        "state": "RUNNING",
+        "created": 1719826995764,
+        "updated": 1719892279019
+    },
+    "message": "success"
+}
+

+

Get raw JSON specs

+

GET /apis/v1/applications/{id}/spec

+

Request

+
curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/spec' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+
+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "name": "TEST_APP",
+        "version": "1",
+        "executable": {
+            "type": "DOCKER",
+            "url": "ghcr.io/appform-io/perf-test-server-httplib",
+            "dockerPullTimeout": "100 seconds"
+        },
+        "exposedPorts": [
+            {
+                "name": "main",
+                "port": 8000,
+                "type": "HTTP"
+            }
+        ],
+        "volumes": [],
+        "configs": [
+            {
+                "type": "INLINE",
+                "localFilename": "/testfiles/drove.txt",
+                "data": ""
+            }
+        ],
+        "type": "SERVICE",
+        "resources": [
+            {
+                "type": "CPU",
+                "count": 1
+            },
+            {
+                "type": "MEMORY",
+                "sizeInMB": 128
+            }
+        ],
+        "placementPolicy": {
+            "type": "ANY"
+        },
+        "healthcheck": {
+            "mode": {
+                "type": "HTTP",
+                "protocol": "HTTP",
+                "portName": "main",
+                "path": "/",
+                "verb": "GET",
+                "successCodes": [
+                    200
+                ],
+                "payload": "",
+                "connectionTimeout": "1 second",
+                "insecure": false
+            },
+            "timeout": "1 second",
+            "interval": "5 seconds",
+            "attempts": 3,
+            "initialDelay": "0 seconds"
+        },
+        "readiness": {
+            "mode": {
+                "type": "HTTP",
+                "protocol": "HTTP",
+                "portName": "main",
+                "path": "/",
+                "verb": "GET",
+                "successCodes": [
+                    200
+                ],
+                "payload": "",
+                "connectionTimeout": "1 second",
+                "insecure": false
+            },
+            "timeout": "1 second",
+            "interval": "3 seconds",
+            "attempts": 3,
+            "initialDelay": "0 seconds"
+        },
+        "tags": {
+            "superSpecialApp": "yes_i_am",
+            "say_my_name": "heisenberg"
+        },
+        "env": {
+            "CORES": "8"
+        },
+        "exposureSpec": {
+            "vhost": "testapp.local",
+            "portName": "main",
+            "mode": "ALL"
+        },
+        "preShutdown": {
+            "hooks": [
+                {
+                    "type": "HTTP",
+                    "protocol": "HTTP",
+                    "portName": "main",
+                    "path": "/",
+                    "verb": "GET",
+                    "successCodes": [
+                        200
+                    ],
+                    "payload": "",
+                    "connectionTimeout": "1 second",
+                    "insecure": false
+                }
+            ],
+            "waitBeforeKill": "3 seconds"
+        }
+    },
+    "message": "success"
+}
+

+
+

Note

+

configs section data will not be returned by any api calls

+
+

Get list of currently active instances

+

GET /apis/v1/applications/{id}/instances

+

Request +

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/instances' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": [
+        {
+            "appId": "TEST_APP-1",
+            "appName": "TEST_APP",
+            "instanceId": "AI-58eb1111-8c2c-4ea2-a159-8fc68010a146",
+            "executorId": "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d",
+            "localInfo": {
+                "hostname": "ppessdev",
+                "ports": {
+                    "main": {
+                        "containerPort": 8000,
+                        "hostPort": 33857,
+                        "portType": "HTTP"
+                    }
+                }
+            },
+            "resources": [
+                {
+                    "type": "CPU",
+                    "cores": {
+                        "0": [
+                            2
+                        ]
+                    }
+                },
+                {
+                    "type": "MEMORY",
+                    "memoryInMB": {
+                        "0": 128
+                    }
+                }
+            ],
+            "state": "HEALTHY",
+            "metadata": {},
+            "errorMessage": "",
+            "created": 1719892354194,
+            "updated": 1719893180105
+        }
+    ],
+    "message": "success"
+}
+

+

Get list of old instances

+

GET /apis/v1/applications/{id}/instances/old

+

Request +

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/instances/old' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": [
+        {
+            "appId": "TEST_APP-1",
+            "appName": "TEST_APP",
+            "instanceId": "AI-869e34ed-ebf3-4908-bf48-719475ca5640",
+            "executorId": "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d",
+            "resources": [
+                {
+                    "type": "CPU",
+                    "cores": {
+                        "0": [
+                            2
+                        ]
+                    }
+                },
+                {
+                    "type": "MEMORY",
+                    "memoryInMB": {
+                        "0": 128
+                    }
+                }
+            ],
+            "state": "STOPPED",
+            "metadata": {},
+            "errorMessage": "Error while pulling image ghcr.io/appform-io/perf-test-server-httplib: Status 500: {\"message\":\"Get \\\"https://ghcr.io/v2/\\\": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)\"}\n",
+            "created": 1719892279039,
+            "updated": 1719892354099
+        }
+    ],
+    "message": "success"
+}
+

+

Get info for an instance

+

GET /apis/v1/applications/{appId}/instances/{instanceId}

+

Request +

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/instances/AI-58eb1111-8c2c-4ea2-a159-8fc68010a146' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "appId": "TEST_APP-1",
+        "appName": "TEST_APP",
+        "instanceId": "AI-58eb1111-8c2c-4ea2-a159-8fc68010a146",
+        "executorId": "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d",
+        "localInfo": {
+            "hostname": "ppessdev",
+            "ports": {
+                "main": {
+                    "containerPort": 8000,
+                    "hostPort": 33857,
+                    "portType": "HTTP"
+                }
+            }
+        },
+        "resources": [
+            {
+                "type": "CPU",
+                "cores": {
+                    "0": [
+                        2
+                    ]
+                }
+            },
+            {
+                "type": "MEMORY",
+                "memoryInMB": {
+                    "0": 128
+                }
+            }
+        ],
+        "state": "HEALTHY",
+        "metadata": {},
+        "errorMessage": "",
+        "created": 1719892354194,
+        "updated": 1719893440105
+    },
+    "message": "success"
+}
+

+

Application Endpoints

+

GET /apis/v1/endpoints

+
+

Info

+

This API provides up-to-date information about the host and port information about application instances running on the cluster. This information can be used for Service Discovery systems to keep their information in sync with changes in the topology of applications running on the cluster.

+
+
+

Tip

+

Any tag specified in the application specification is also exposed on endpoint. This can be used to implement complicated routing logic if needed in the NGinx template on Drove Gateway.

+
+

Request +

curl --location 'http://drove.local:7000/apis/v1/endpoints' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": [
+        {
+            "appId": "TEST_APP-1",
+            "vhost": "testapp.local",
+            "tags": {
+                "superSpecialApp": "yes_i_am",
+                "say_my_name": "heisenberg"
+            },
+            "hosts": [
+                {
+                    "host": "ppessdev",
+                    "port": 44315,
+                    "portType": "HTTP"
+                }
+            ]
+        },
+        {
+            "appId": "TEST_APP-2",
+            "vhost": "testapp.local",
+            "tags": {
+                "superSpecialApp": "yes_i_am",
+                "say_my_name": "heisenberg"
+            },
+            "hosts": [
+                {
+                    "host": "ppessdev",
+                    "port": 46623,
+                    "portType": "HTTP"
+                }
+            ]
+        }
+    ],
+    "message": "success"
+}
+

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apis/cluster.html b/apis/cluster.html new file mode 100644 index 0000000..dfdabeb --- /dev/null +++ b/apis/cluster.html @@ -0,0 +1,2106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Cluster Management - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Cluster Management

+

Ping API

+

GET /apis/v1/ping

+

Request +

curl --location 'http://drove.local:7000/apis/v1/ping' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": "pong",
+    "message": "success"
+}
+

+
+

Tip

+

Use this api call to determine the leader in a cluster. This api will return a HTTP 200 only for the leader controller. All other controllers in the cluster will return 4xx for this api call.

+
+

Cluster Management

+

Get current cluster state

+

GET /apis/v1/cluster

+

Request +

curl --location 'http://drove.local:7000/apis/v1/cluster' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "leader": "ppessdev:4000",
+        "state": "NORMAL",
+        "numExecutors": 1,
+        "numApplications": 1,
+        "numActiveApplications": 1,
+        "freeCores": 9,
+        "usedCores": 1,
+        "totalCores": 10,
+        "freeMemory": 18898,
+        "usedMemory": 128,
+        "totalMemory": 19026
+    },
+    "message": "success"
+}
+

+

Set maintenance mode on cluster

+

POST /apis/v1/cluster/maintenance/set

+

Request +

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/set' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "state": "MAINTENANCE",
+        "updated": 1719897526772
+    },
+    "message": "success"
+}
+

+

Remove maintenance mode from cluster

+

POST /apis/v1/cluster/maintenance/unset

+

Request +

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/unset' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "state": "NORMAL",
+        "updated": 1719897573226
+    },
+    "message": "success"
+}
+

+
+

Warning

+

Cluster will remain in maintenance mode for some time (about 2 minutes) internally even after maintenance mode is removed.

+
+

Executor Management

+

Get list of executors

+

GET /apis/v1/cluster/executors

+

Request +

curl --location 'http://drove.local:7000/apis/v1/cluster/executors' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": [
+        {
+            "executorId": "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d",
+            "hostname": "ppessdev",
+            "port": 3000,
+            "transportType": "HTTP",
+            "freeCores": 9,
+            "usedCores": 1,
+            "freeMemory": 18898,
+            "usedMemory": 128,
+            "tags": [
+                "ppessdev"
+            ],
+            "state": "ACTIVE"
+        }
+    ],
+    "message": "success"
+}
+

+

Get detailed info for one executor

+

GET /apis/v1/cluster/executors/{id}

+

Request +

curl --location 'http://drove.local:7000/apis/v1/cluster/executors/a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+
{
+    "status": "SUCCESS",
+    "data": {
+        "type": "EXECUTOR",
+        "hostname": "ppessdev",
+        "port": 3000,
+        "transportType": "HTTP",
+        "updated": 1719897100104,
+        "state": {
+            "executorId": "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d",
+            "cpus": {
+                "type": "CPU",
+                "freeCores": {
+                    "0": [
+                        3,
+                        4,
+                        5,
+                        6,
+                        7,
+                        8,
+                        9,
+                        10,
+                        11
+                    ]
+                },
+                "usedCores": {
+                    "0": [
+                        2
+                    ]
+                }
+            },
+            "memory": {
+                "type": "MEMORY",
+                "freeMemory": {
+                    "0": 18898
+                },
+                "usedMemory": {
+                    "0": 128
+                }
+            }
+        },
+        "instances": [
+            {
+                "appId": "TEST_APP-1",
+                "appName": "TEST_APP",
+                "instanceId": "AI-58eb1111-8c2c-4ea2-a159-8fc68010a146",
+                "executorId": "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d",
+                "localInfo": {
+                    "hostname": "ppessdev",
+                    "ports": {
+                        "main": {
+                            "containerPort": 8000,
+                            "hostPort": 33857,
+                            "portType": "HTTP"
+                        }
+                    }
+                },
+                "resources": [
+                    {
+                        "type": "CPU",
+                        "cores": {
+                            "0": [
+                                2
+                            ]
+                        }
+                    },
+                    {
+                        "type": "MEMORY",
+                        "memoryInMB": {
+                            "0": 128
+                        }
+                    }
+                ],
+                "state": "HEALTHY",
+                "metadata": {},
+                "errorMessage": "",
+                "created": 1719892354194,
+                "updated": 1719897100104
+            }
+        ],
+        "tasks": [],
+        "tags": [
+            "ppessdev"
+        ],
+        "blacklisted": false
+    },
+    "message": "success"
+}
+
+

Take executor out of rotation

+

POST /apis/v1/cluster/executors/blacklist

+

Request +

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/blacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+

+
+

Note

+

Unlike other POST apis, the executors to be blacklisted are passed as query parameter id. To blacklist multiple executors, pass .../blacklist?id=<id1>&id=<id2>...

+
+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "successful": [
+            "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d"
+        ],
+        "failed": []
+    },
+    "message": "success"
+}
+

+

Bring executor back into rotation

+

POST /apis/v1/cluster/executors/unblacklist

+

Request +

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/unblacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+

+
+

Note

+

Unlike other POST apis, the executors to be un-blacklisted are passed as query parameter id. To un-blacklist multiple executors, pass .../unblacklist?id=<id1>&id=<id2>...

+
+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "successful": [
+            "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d"
+        ],
+        "failed": []
+    },
+    "message": "success"
+}
+

+

Drove Cluster Events

+

The following APIs can be used to monitor events on Drove. If the data needs to be consumed, the /latest API should be used. For simply knowing if an event of a certain type has occurred or not, the /summary is sufficient.

+

Event List

+

GET /apis/v1/cluster/events/latest

+

Request +

curl --location 'http://drove.local:7000/apis/v1/cluster/events/latest?size=1024&lastSyncTime=0' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "events": [
+            {
+                "metadata": {
+                    "CURRENT_INSTANCES": 0,
+                    "APP_ID": "TEST_APP-1",
+                    "PLACEMENT_POLICY": "ANY",
+                    "APP_VERSION": "1",
+                    "CPU_COUNT": 1,
+                    "CURRENT_STATE": "RUNNING",
+                    "PORTS": "main:8000:http",
+                    "MEMORY": 128,
+                    "EXECUTABLE": "ghcr.io/appform-io/perf-test-server-httplib",
+                    "VHOST": "testapp.local",
+                    "APP_NAME": "TEST_APP"
+                },
+                "type": "APP_STATE_CHANGE",
+                "id": "a2b7d673-2bc2-4084-8415-d8d37cafa63d",
+                "time": 1719977632050
+            },
+            {
+                "metadata": {
+                    "APP_NAME": "TEST_APP",
+                    "APP_ID": "TEST_APP-1",
+                    "PORTS": "main:44315:http",
+                    "EXECUTOR_ID": "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d",
+                    "EXECUTOR_HOST": "ppessdev",
+                    "CREATED": 1719977629042,
+                    "INSTANCE_ID": "AI-5efbb94f-835c-4c62-a073-a68437e60339",
+                    "CURRENT_STATE": "HEALTHY"
+                },
+                "type": "INSTANCE_STATE_CHANGE",
+                "id": "55d5876f-94ac-4c5d-a580-9c3b296add46",
+                "time": 1719977631534
+            }
+        ],
+        "lastSyncTime": 1719977632050//(1)!
+    },
+    "message": "success"
+}
+

+
    +
  1. Pass this as the parameter lastSyncTime in the next call to events api to receive latest events.
  2. +
+ + + + + + + + + + + + + + + + + + + + +
Query ParameterValidationDescription
lastSyncTime+ve long rangeTime when the last sync call happened on the server. Defaults to 0 (initial sync).
size1-1024Number of latest events to return. Defaults to 1024. We recommend leaving this as is.
+

Event Summary

+

GET /apis/v1/cluster/events/summary

+

Request +

curl --location 'http://drove.local:7000/apis/v1/cluster/events/summary?lastSyncTime=0' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+
+Response +
{
+    "status": "SUCCESS",
+    "data": {
+        "eventsCount": {
+            "INSTANCE_STATE_CHANGE": 8,
+            "APP_STATE_CHANGE": 17,
+            "EXECUTOR_BLACKLISTED": 1,
+            "EXECUTOR_UN_BLACKLISTED": 1
+        },
+        "lastSyncTime": 1719977632050//(1)!
+    },
+    "message": "success"
+}
+

+
    +
  1. Pass this as the parameter lastSyncTime in the next call to events api to receive latest events.
  2. +
+

Continuous monitoring for events

+

This is applicable for both the APIs listed above

+
    +
  • In the first call to events api, pass lastSyncTime as zero.
  • +
  • In the response there will be a field lastSyncTime
  • +
  • Pass the last received lastSyncTime as the lastSyncTime param in the next call
  • +
  • This api is cheap enough, you should plan to make calls to it every few seconds
  • +
+
+

Info

+

Model for the events can be found here.

+
+
+

Tip

+

Java programs should definitely look at using the event listener library +to listen to cluster events

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apis/index.html b/apis/index.html new file mode 100644 index 0000000..3eec753 --- /dev/null +++ b/apis/index.html @@ -0,0 +1,1562 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Introduction - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Introduction

+

This section lists all the APIs that a user can communicate with.

+

Making an API call

+

Use a standard HTTP client in the language of your choice to make a call to the leader controller (the cluster virtual host exposed by drove-gateway-nginx).

+
+

Tip

+

In case you are using Java, we recommend using the drove-client library along with the http-transport.

+
+

If multiple controllers endpoints are provided, the client will track the leader automatically. This will reduce your dependency on drove-gateway.

+
+
+

Authentication

+

Drove uses basic auth for authentication. (You can extend to use any other auth format like OAuth). The basic auth credentials need to be sent out in the standard format in the Authorization header.

+

Response format

+

The response format is standard for all API calls:

+
{
+    "status": "SUCCESS",//(1)!
+    "data": {//(2)!
+        "taskId": "T0012"
+    },
+    "message": "success"//(3)!
+}
+
+
    +
  1. SUCCESS or FAILURE as the case may be.
  2. +
  3. Content of this field is contextual to the response.
  4. +
  5. Will contain success if the call was successful or relevant error message.
  6. +
+
+

Warning

+

APIs will return relevant HTTP status codes in case of error (for example 400 for validation errors, 401 for authentication failure). However, you must always ensure that the status field is set to SUCCESS for assuming the api call is succesful, even when HTTP status code is 2xx.

+
+

APIs in Drove belong to the following major classes:

+ +
+

Tip

+

Response models for these apis can be found in drove-models

+
+
+

Note

+

There are no publicly accessible APIs exposed by individual executors.

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apis/logs.html b/apis/logs.html new file mode 100644 index 0000000..bf88063 --- /dev/null +++ b/apis/logs.html @@ -0,0 +1,1623 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Log Related APIs - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Log Related APIs

+

Get list if log files

+

Application +GET /apis/v1/logfiles/applications/{appId}/{instanceId}/list

+

Task +GET /apis/v1/logfiles/tasks/{sourceAppName}/{taskId}/list

+

Request +

curl --location 'http://drove.local:7000/apis/v1/logfiles/applications/TEST_APP-1/AI-5efbb94f-835c-4c62-a073-a68437e60339/list' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "files": [
+        "output.log-2024-07-04",
+        "output.log-2024-07-03",
+        "output.log"
+    ]
+}
+

+

Download Log Files

+

Application +GET /apis/v1/logfiles/applications/{appId}/{instanceId}/download/{fileName}

+

Task +GET /apis/v1/logfiles/tasks/{sourceAppName}/{taskId}/download/{fileName}

+

Request +

curl --location 'http://drove.local:7000/apis/v1/logfiles/applications/TEST_APP-1/AI-5efbb94f-835c-4c62-a073-a68437e60339/download/output.log' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

+
+

Note

+

The Content-Disposition header is set properly to the actual filename. For the above example it would be set to attachment; filename=output.log.

+
+

Read chunks from log

+

Application +GET /apis/v1/logfiles/applications/{appId}/{instanceId}/read/{fileName}

+

Task +GET /apis/v1/logfiles/tasks/{sourceAppName}/{taskId}/read/{fileName}

+ + + + + + + + + + + + + + + + + + + + +
Query ParameterValidationDescription
offsetDefault -1, should be positive numberThe offset of the file to read from.
lengthShould be a positive numberNumber of bytes to read.
+

Request +

curl --location 'http://drove.local:7000/apis/v1/logfiles/applications/TEST_APP-1/AI-5efbb94f-835c-4c62-a073-a68437e60339/read/output.log' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "data": "", //(1)!
+    "offset": 43318 //(2)!
+}
+

+
    +
  1. Will contain raw data or empty string (in case of first call)
  2. +
  3. Offset to be passed in the next call
  4. +
+

How to tail logs

+
    +
  1. Have a fixed buffer size in ming 1024/4096 etc
  2. +
  3. Make a call to /read api with offset=-1, length = buffer size
  4. +
  5. The call will return no data, but will have a valid offset
  6. +
  7. Pass this offset in the next call, data will be returned if available (or empty). The response will also return the offset to pass in the .ext call.
  8. +
  9. The data returned might be empty or less than length depending on availability.
  10. +
  11. Keep repeating (4) to keep tailing log
  12. +
+
+

Warning

+
    +
  • Offset = 0 means start of the file
  • +
  • First call must be -1 for tail type functionality
  • +
+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/apis/task.html b/apis/task.html new file mode 100644 index 0000000..2ece595 --- /dev/null +++ b/apis/task.html @@ -0,0 +1,1658 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Task Management - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Task Management

+

Issue task operation

+

POST /apis/v1/tasks/operations

+

Request +

curl --location 'http://drove.local:7000/apis/v1/tasks/operations' \
+--header 'Content-Type: application/json' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data '{
+    "type": "KILL",
+    "sourceAppName" : "TEST_APP",
+    "taskId" : "T0012",
+    "opSpec": {
+        "timeout": "5m",
+        "parallelism": 1,
+        "failureStrategy": "STOP"
+    }
+}'
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "taskId": "T0012"
+    },
+    "message": "success"
+}
+

+
+

Tip

+

Relevant payloads for task commands can be found in task operations section.

+
+

Search for task

+

POST /apis/v1/tasks/search

+

List all tasks

+

GET /apis/v1/tasks

+

Request +

curl --location 'http://drove.local:7000/apis/v1/tasks' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": [
+        {
+            "sourceAppName": "TEST_APP",
+            "taskId": "T0013",
+            "instanceId": "TI-c2140806-2bb5-4ed3-9bb9-0c0c5fd0d8d6",
+            "executorId": "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d",
+            "hostname": "ppessdev",
+            "executable": {
+                "type": "DOCKER",
+                "url": "ghcr.io/appform-io/test-task",
+                "dockerPullTimeout": "100 seconds"
+            },
+            "resources": [
+                {
+                    "type": "CPU",
+                    "cores": {
+                        "0": [
+                            2
+                        ]
+                    }
+                },
+                {
+                    "type": "MEMORY",
+                    "memoryInMB": {
+                        "0": 512
+                    }
+                }
+            ],
+            "volumes": [],
+            "env": {
+                "ITERATIONS": "10"
+            },
+            "state": "RUNNING",
+            "metadata": {},
+            "errorMessage": "",
+            "created": 1719827035480,
+            "updated": 1719827038414
+        }
+    ],
+    "message": "success"
+}
+

+

Get Task Instance Details

+

GET /apis/v1/tasks/{sourceAppName}/instances/{taskId}

+

Request +

curl --location 'http://drove.local:7000/apis/v1/tasks/TEST_APP/instances/T0012' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4='
+

+

Response +

{
+    "status": "SUCCESS",
+    "data": {
+        "sourceAppName": "TEST_APP",
+        "taskId": "T0012",
+        "instanceId": "TI-6cf36f5c-6480-4ed5-9e2d-f79d9648529a",
+        "executorId": "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d",
+        "hostname": "ppessdev",
+        "executable": {
+            "type": "DOCKER",
+            "url": "ghcr.io/appform-io/test-task",
+            "dockerPullTimeout": "100 seconds"
+        },
+        "resources": [
+            {
+                "type": "CPU",
+                "cores": {
+                    "0": [
+                        3
+                    ]
+                }
+            },
+            {
+                "type": "MEMORY",
+                "memoryInMB": {
+                    "0": 512
+                }
+            }
+        ],
+        "volumes": [],
+        "env": {
+            "ITERATIONS": "10"
+        },
+        "state": "STOPPED",
+        "metadata": {},
+        "taskResult": {
+            "status": "SUCCESSFUL",
+            "exitCode": 0
+        },
+        "errorMessage": "",
+        "created": 1719823470267,
+        "updated": 1719823483836
+    },
+    "message": "success"
+}
+

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/applications/index.html b/applications/index.html new file mode 100644 index 0000000..f0eb5cf --- /dev/null +++ b/applications/index.html @@ -0,0 +1,1612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Introduction - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Introduction

+

An application is a virtual representation of a running service in the system.

+

Running containers for an application are called application instances.

+

An application specification contains the following details about the application:

+
    +
  • Name - Name of the application
  • +
  • Version - Version of this specification
  • +
  • Executable - The container to deploy on the cluster
  • +
  • Ports - Ports to be exposed from the container
  • +
  • Resources - CPU and Memory required for the container
  • +
  • Placement Policy - How containers are to be placed in the cluster
  • +
  • Healthchecks - Healthcheck details
  • +
  • Readiness Checks - Readiness checks to pass before container is considered to be healthy
  • +
  • Pre Shutdown Hooks - Pre shutdown hooks to run on container before it is killed
  • +
  • Environment Variables - Environment variables and values
  • +
  • Exposure Information - Virtual host information
  • +
  • Volumes - Volumes to be mounted into the container
  • +
  • Configs - Configs/files to be mounted into the container
  • +
  • Logging details - Logging spec (for example rsyslog server)
  • +
  • Tags - A map of strings for additional metadata
  • +
+
+

Info

+

Once a spec is registered to the cluster, it can not be changed

+
+

Application ID

+

Once an application is created on the cluster, an Application id is generated. The format of this id currently is: {name}-{version}. All further operations to be done on the application will need to refer to it by this ID.

+

Application States and Operations

+

An application on a Drove cluster follows a fixed lifecycle modelled as a state machine. State transitions are triggered by operations. Operations can be issued externally using API calls or may be generated internally by the application monitoring system.

+

States

+

Applications on a Drove cluster can be one of the following states:

+
    +
  • INIT - This is an intermediate state during which the application is being initialized and the spec is being validated. This is the origination state of the application.
  • +
  • MONITORING - A stable state in which application is created or suspended and does not have any running instances
  • +
  • RUNNING - A stable state in which application has the expected non-zero number of healthy application instances running on the cluster
  • +
  • OUTAGE_DETECTED - An intermediate state when Drove has detected that the current number of application instances is not matching the expected number of instances.
  • +
  • SCALING_REQUESTED - An intermediate state that signifies that application instances are being spun up or shut down to get the number of running instances to match the expected instances.
  • +
  • STOP_INSTANCES_REQUESTED - An intermediate state that signifies that specific instances of the application are being killed as requested by the user/system.
  • +
  • REPLACE_INSTANCES_REQUESTED - An _intermediate state _that signifies that instances of the application are being replaced with newer instances as requested by the user. This signifies that the app is effectively being restarted.
  • +
  • DESTROY_REQUESTED - An intermediate state that signifies that the user has requested to destroy the application and remove it from the cluster.
  • +
  • DESTROYED - An intermediate state that signifies that the app has been destroyed and metadata cleanup is underway. This is the terminal state of an application.
  • +
+

Operations

+

The following application operations are recognized by Drove:

+
    +
  • CREATE - Create an application. Take the Application Specification. Fails if an app with the same application id (name + version) already exists on the cluster
  • +
  • DESTROY - Destroy an application. Takes app id as parameter. Deletes all metadata about the application from the cluster. Allowed only if the application is in Monitoring state (i.e. has zero running instances).
  • +
  • START_INSTANCES - Create new application instances. Takes the app id as well as the number of new instances to deploy. Allowed only if the application is in Monitoring or Running state.
  • +
  • STOP_INSTANCES - Stop running application instances. Takes the app id, list of instance ids to be stopped as well as flag to denote if replacement instances are to be started by Drove or not. Allowed only if the application is in Monitoring or Running state.
  • +
  • SCALE - Scale the application up and down to the specified number of instances. Drove will internally calculate whether to spin new containers up or spin old containers down as needed. Allowed if the app is in Monitoring or Running state. It is better to use either START or STOP instances command above to be more explicit in behavior. The SCALE operation is mostly for internal use by Drove, but can be issued externally as well.
  • +
  • REPLACE_INSTANCES - Replace application instances with newer ones. Can be used to do rolling restarts on the cluster. Specific instances can be targeted as well by passing an optional list of instance ids to be replaced. Allowed only when the application is in Running state.
  • +
  • SUSPEND - A shortcut to set expected instances for an application to zero. This will get translated into a SCALE operation and any running instances will be gracefully shut down. Allowed only when the application is in running state.
  • +
  • RECOVER - Internal command used to restore application state on controller failover.
  • +
+
+

Tip

+

All operations can take an optional Cluster Operation Spec which can be used to control the timeout and parallelism of tasks generated by the operation.

+
+

Application State Machine

+

The following state machine signifies the states and transitions as affected by cluster state and operations issued.

+

Application State Machine

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/applications/instances.html b/applications/instances.html new file mode 100644 index 0000000..d923a72 --- /dev/null +++ b/applications/instances.html @@ -0,0 +1,1514 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Application Instances - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Application Instances

+

Application instances are running containers for an application. The state machine for instances are managed in a decentralised manner on the cluster nodes locally and not by the controllers. This includes running health checks, readiness checks and shutdown hooks on the container, container loss detection and container state recovery on executor service restart.

+

Regular updates about the instance state are provided by executors to the controllers and are used to keep the application state up-to-date or trigger application operations to bring the applications to stable states.

+

Application Instance States

+

An application instance can be in one of the following states at one point in time:

+
    +
  • PENDING - Container state machine start has been triggered.
  • +
  • PROVISIONING - Docker image is being downloaded
  • +
  • PROVISIONING_FAILED - Docker image download failed
  • +
  • STARTING - Docker run is being executed
  • +
  • START_FAILED - Docker run failed
  • +
  • UNREADY - Docker started, readiness check not yet started.
  • +
  • READINESS_CHECK_FAILED - Readiness check was run and has failed terminally
  • +
  • READY - Readiness checks have passed
  • +
  • HEALTHY - Health check has passed. Container is running properly and passing regular health checks
  • +
  • UNHEALTHY - Regular health check has failed. Container will stop.
  • +
  • STOPPING - Shutdown hooks are being called and docker kill be be issued
  • +
  • DEPROVISIONING - Docker image is being cleaned up
  • +
  • STOPPED - Docker stop has completed
  • +
  • LOST - Container has exited unexpectedly while executor service was down
  • +
  • UNKNOWN - All running containers are in this state when executor service is getting restarted and before startup recovery has kicked in
  • +
+

Application Instance State Machine

+

Instance state machine transitions might be triggered on receipt of commands issued by the controller or due to internal changes in the container (might have died or started failing health checks) as well as external factors like executor service restarts.

+

Application Instance State Machine

+
+

Note

+

No operations are allowed to be performed on application instances directly through the executor

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/applications/operations.html b/applications/operations.html new file mode 100644 index 0000000..8deea87 --- /dev/null +++ b/applications/operations.html @@ -0,0 +1,2074 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Application Operations - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Application Operations

+

This page discusses operations relevant to Application management. Please go over the Application State Machine and Application Instance State Machine to understand the different states an application (and it's instances) can be in and how operations applied move an application from one state to another.

+
+

Note

+

Please go through Cluster Op Spec to understand the operation parameters being sent.

+
+
+

Note

+

Only one operation can be active on a particular {appName,version} combination.

+
+
+

Warning

+

Only the leader controller will accept and process operations. To avoid confusion, use the controller endpoint exposed by Drove Gateway to issue commands.

+
+

How to initiate an operation

+
+

Tip

+

Use the Drove CLI to perform all manual operations.

+
+

All operations for application lifecycle management need to be issued via a POST HTTP call to the leader controller endpoint on the path /apis/v1/applications/operations. API will return HTTP OK/200 and relevant json response as payload.

+

Sample api call:

+
curl --location 'http://drove.local:7000/apis/v1/applications/operations' \
+--header 'Content-Type: application/json' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data '{
+    "type": "START_INSTANCES",
+    "appId": "TEST_APP-3",
+    "instances": 1,
+    "opSpec": {
+        "timeout": "5m",
+        "parallelism": 32,
+        "failureStrategy": "STOP"
+    }
+}'
+
+
+

Note

+

In the above examples, http://drove.local:7000 is the endpoint of the leader. TEST_APP-3 is the Application ID. Authorization is basic auth.

+
+

Cluster Operation Specification

+

When an operation is submitted to the cluster, a cluster op spec needs to be specified. This is needed to control different aspects of the operation, including parallelism of an operation or increase the timeout for the operation and so on.

+

The following aspects of an operation can be configured:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TimeouttimeoutThe duration after which Drove considers the operation to have timed out.
ParallelismparallelismParallelism of the task. (Range: 1-32)
Failure StrategyfailureStrategySet this to STOP.
+
+

Note

+

For internal recovery operations, Drove generates it's own operations. For that, Drove applies the following cluster operation spec:

+
    +
  • timeout - 300 seconds
  • +
  • parallelism - 1
  • +
  • failureStrategy - STOP
  • +
+
+

The default operation spec can be configured in the controller configuration file. It is recommended to set this to a something like 8 for faster recovery.

+
+
+

How to cancel an operation

+

Operations can be requested to be cancelled asynchronously. A POST call needs to be made to leader controller endpoint on the api /apis/v1/operations/{applicationId}/cancel (1) to achieve this.

+
    +
  1. applicationId is the Application ID for the application
  2. +
+
curl --location --request POST 'http://drove.local:7000/apis/v1/operations/TEST_APP-3/cancel' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+
+
+

Warning

+

Operation cancellation is not instantaneous. Cancellation will be affected only after current execution of the active operation is complete.

+
+

Create an application

+

Before deploying containers on the cluster, an application needs to be created.

+

Preconditions:

+
    +
  • App should not exist in the cluster
  • +
+

State Transition:

+
    +
  • none → MONITORING
  • +
+

To create an application, an Application Spec needs to be created first.

+

Once ready, CLI command needs to be issued or the following payload needs to be sent:

+
+
+
+
drove -c local apps create sample/test_app.json
+
+
+
+

Sample Request Payload +

{
+    "type": "CREATE",
+    "spec": {...}, //(1)!
+    "opSpec": { //(2)!
+        "timeout": "5m",
+        "parallelism": 1,
+        "failureStrategy": "STOP"
+    }
+}
+

+
    +
  1. Spec as mentioned in Application Specification
  2. +
  3. Operation spec as mentioned in Cluster Op Spec
  4. +
+

Sample response +

{
+    "data" : {
+        "appId" : "TEST_APP-1"
+    },
+    "message" : "success",
+    "status" : "SUCCESS"
+}
+

+
+
+
+

Starting new instances of an application

+

New instances can be started by issuing the START_INSTANCES command.

+

Preconditions +- Application must be in one of the following states: MONITORING, RUNNING

+

State Transition:

+
    +
  • {RUNNING, MONITORING} → RUNNING
  • +
+

The following command/payload will start 2 new instances of the application.

+
+
+
+
drove -c local apps deploy TEST_APP-1 2
+
+
+
+

Sample Request Payload +

{
+    "type": "START_INSTANCES",
+    "appId": "TEST_APP-1",//(1)!
+    "instances": 2,//(2)!
+    "opSpec": {//(3)!
+        "timeout": "5m",
+        "parallelism": 32,
+        "failureStrategy": "STOP"
+    }
+}
+

+
    +
  1. Application ID
  2. +
  3. Number of instances to be started
  4. +
  5. Operation spec as mentioned in Cluster Op Spec
  6. +
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "appId": "TEST_APP-1"
+    },
+    "message": "success"
+}
+

+
+
+
+

Suspending an application

+

All instances of an application can be shut down by issuing the SUSPEND command.

+

Preconditions +- Application must be in one of the following states: MONITORING, RUNNING

+

State Transition:

+
    +
  • {RUNNING, MONITORING} → MONITORING
  • +
+

The following command/payload will suspend all instances of the application.

+
+
+
+
drove -c local apps suspend TEST_APP-1
+
+
+
+

Sample Request Payload +

{
+    "type": "SUSPEND",
+    "appId": "TEST_APP-1",//(1)!
+    "opSpec": {//(2)!
+        "timeout": "5m",
+        "parallelism": 32,
+        "failureStrategy": "STOP"
+    }
+}
+

+
    +
  1. Application ID
  2. +
  3. Operation spec as mentioned in Cluster Op Spec
  4. +
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "appId": "TEST_APP-1"
+    },
+    "message": "success"
+}
+

+
+
+
+

Scaling the application up or down

+

Scaling the application to required number of containers can be achieved using the SCALE command. Application can be either scaled up or down using this command.

+

Preconditions +- Application must be in one of the following states: MONITORING, RUNNING

+

State Transition:

+
    +
  • {RUNNING, MONITORING} → MONITORING if requiredInstances is set to 0
  • +
  • {RUNNING, MONITORING} → RUNNING if requiredInstances is non 0
  • +
+
+
+
+
drove -c local apps scale TEST_APP-1 2
+
+
+
+

Sample Request Payload +

{
+    "type": "SCALE",
+    "appId": "TEST_APP-1", //(3)!
+    "requiredInstances": 2, //(1)!
+    "opSpec": { //(2)!
+        "timeout": "1m",
+        "parallelism": 20,
+        "failureStrategy": "STOP"
+    }
+}
+

+
    +
  1. Absolute number of instances to be maintained on the cluster for the application
  2. +
  3. Operation spec as mentioned in Cluster Op Spec
  4. +
  5. Application ID
  6. +
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "appId": "TEST_APP-1"
+    },
+    "message": "success"
+}
+

+
+
+
+
+

Note

+

During scale down, older instances are stopped first

+
+
+

Tip

+

If implementing automation on top of Drove APIs, just use the SCALE command to scale up or down instead of using START_INSTANCES or SUSPEND separately.

+
+

Restarting an application

+

Application can be restarted by issuing the REPLACE_INSTANCES operation. In this case, first clusterOpSpec.parallelism number of containers are spun up first and then an equivalent number of them are spun down. This ensures that cluster maintains enough capacity is maintained in the cluster to handle incoming traffic as the restart is underway.

+
+

Warning

+

If the cluster does not have sufficient capacity to spin up new containers, this operation will get stuck. So adjust your parallelism accordingly.

+
+

Preconditions +- Application must be in RUNNING state.

+

State Transition:

+
    +
  • RUNNINGREPLACE_INSTANCES_REQUESTEDRUNNING
  • +
+
+
+
+
drove -c local apps restart TEST_APP-1
+
+
+
+

Sample Request Payload +

{
+    "type": "REPLACE_INSTANCES",
+    "appId": "TEST_APP-1", //(1)!
+    "instanceIds": [], //(2)!
+    "opSpec": { //(3)!
+        "timeout": "1m",
+        "parallelism": 20,
+        "failureStrategy": "STOP"
+    }
+}
+

+
    +
  1. Application ID
  2. +
  3. Instances that need to be restarted. This is optional. If nothing is passed, all instances will be replaced.
  4. +
  5. Operation spec as mentioned in Cluster Op Spec
  6. +
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "appId": "TEST_APP-1"
+    },
+    "message": "success"
+}
+

+
+
+
+
+

Tip

+

To replace specific instances, pass their application instance ids (starts with AI-...) in the instanceIds parameter in the JSON payload.

+
+

Stop or replace specific instances of an application

+

Application instances can be killed by issuing the STOP_INSTANCES operation. Default behaviour of Drove is to replace killed instances by new instances. Such new instances are always spun up before the specified(old) instances are stopped. If skipRespawn parameter is set to true, the application instance is killed but no new instances are spun up to replace it.

+
+

Warning

+

If the cluster does not have sufficient capacity to spin up new containers, and skipRespawn is not set or set to false, this operation will get stuck.

+
+

Preconditions +- Application must be in RUNNING state.

+

State Transition:

+
    +
  • RUNNINGSTOP_INSTANCES_REQUESTEDRUNNING if final number of instances is non zero
  • +
  • RUNNINGSTOP_INSTANCES_REQUESTEDMONITORING if final number of instances is zero
  • +
+
+
+
+
drove -c local apps appinstances kill TEST_APP-1 AI-601d160e-c692-4ddd-8b7f-4c09b30ed02e
+
+
+
+

Sample Request Payload +

{
+    "type": "STOP_INSTANCES",
+    "appId" : "TEST_APP-1",//(1)!
+    "instanceIds" : [ "AI-601d160e-c692-4ddd-8b7f-4c09b30ed02e" ],//(2)!
+    "skipRespawn" : true,//(3)!
+    "opSpec": {//(4)!
+        "timeout": "5m",
+        "parallelism": 1,
+        "failureStrategy": "STOP"
+    }
+}
+

+
    +
  1. Application ID
  2. +
  3. Instance ids to be stopped
  4. +
  5. Do not spin up new containers to replace the stopped ones. This is set ot false by default.
  6. +
  7. Operation spec as mentioned in Cluster Op Spec
  8. +
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "appId": "TEST_APP-1"
+    },
+    "message": "success"
+}
+

+
+
+
+

Destroy an application

+

To remove an application deployment (appName-version combo) the DESTROY command can be issued.

+

Preconditions:

+
    +
  • App should not exist in the cluster
  • +
+

State Transition:

+
    +
  • MONITORINGDESTROY_REQUESTEDDESTROYED → none
  • +
+

To create an application, an Application Spec needs to be created first.

+

Once ready, CLI command needs to be issued or the following payload needs to be sent:

+
+
+
+
drove -c local apps destroy TEST_APP_1
+
+
+
+

Sample Request Payload +

{
+    "type": "DESTROY",
+    "appId" : "TEST_APP-1",//(1)!
+    "opSpec": {//(2)!
+        "timeout": "5m",
+        "parallelism": 2,
+        "failureStrategy": "STOP"
+    }
+}
+

+
    +
  1. Spec as mentioned in Application Specification
  2. +
  3. Operation spec as mentioned in Cluster Op Spec
  4. +
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "appId": "TEST_APP-1"
+    },
+    "message": "success"
+}
+

+
+
+
+
+

Warning

+

All metadata for an app and it's instances are completely obliterated from Drove's storage once an app is destroyed

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/applications/outage.html b/applications/outage.html new file mode 100644 index 0000000..3953954 --- /dev/null +++ b/applications/outage.html @@ -0,0 +1,1559 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Outage Detection and Recovery - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Outage Detection and Recovery

+

Drove tracks all instances for an app deployment in the cluster. It will ensure the required number of containers is always running on the cluster.

+

Instance health detection and tracking

+

Executor runs periodic health checks on the container according to check spec configuration. +- Runs readiness checks to ensure container is started properly before declaring it healthy +- Runs health checks on the container at regular intervals to ensure it is in operating condition

+

Behavior for both is configured by setting the appropriate options in the application specification.

+

Result of such health checks (both success and failure) are reported to the controller. Appropriate action is taken to shut down containers that fail readiness or health checks.

+

Container crash

+

If container for an application crashes, Drove will automatically spin up a container in it's place.

+

Executor node hardware failure

+

If an executor node fails, instances running on that node will be lost. This is detected by the outage detector and new containers are spun up on other parts of the cluster.

+

Executor service temporary unavailability

+

On restart, executor service reads the metadata embedded in the container and registers them. It performs a reconciliation with the leader controller to kill any local containers if the unavailability was too long and controller has already spun up new alternatives.

+

Zombie (container) detection and cleanup

+

Executor service keeps track of all containers it is supposed to run by running periodic reconciliation with the leader controller. Any mismatch gets handled:

+
    +
  • if a container is found that is not supposed to be running, it is killed
  • +
  • If a container that is supposed to be running is not found, it is marked as lost and reported to the controller. This triggers the controller to spin up an alternative container on the cluster.
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/applications/specification.html b/applications/specification.html new file mode 100644 index 0000000..d5934cd --- /dev/null +++ b/applications/specification.html @@ -0,0 +1,3171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Application Specification - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Application Specification

+

An application is defined using JSON. We use a sample configuration below to explain the options.

+

Sample Application Definition

+
{
+    "name": "TEST_APP", // (1)!
+    "version": "1", // (2)!
+    "type": "SERVICE", // (3)!
+    "executable": { //(4)!
+        "type": "DOCKER", // (5)!
+        "url": "ghcr.io/appform-io/perf-test-server-httplib",// (6)!
+        "dockerPullTimeout": "100 seconds"// (7)!
+    },
+    "resources": [//(20)!
+        {
+            "type": "CPU",
+            "count": 1//(21)!
+        },
+        {
+            "type": "MEMORY",
+            "sizeInMB": 128//(22)!
+        }
+    ],
+    "volumes": [//(12)!
+        {
+            "pathInContainer": "/data",//(13)!
+            "pathOnHost": "/mnt/datavol",//(14)!
+            "mode" : "READ_WRITE"//(15)!
+        }
+    ],
+    "configs" : [//(16)!
+        {
+            "type" : "INLINE",//(17)!
+            "localFilename": "/testfiles/drove.txt",//(18)!
+            "data" : "RHJvdmUgdGVzdA=="//(19)!
+        }
+    ],
+    "placementPolicy": {//(23)!
+        "type": "ANY"//(24)!
+    },
+    "exposedPorts": [//(8)!
+        {
+            "name": "main",//(9)!
+            "port": 8000,//(10)!
+            "type": "HTTP"//(11)!
+        }
+    ],
+    "healthcheck": {//(25)!
+        "mode": {//(26)!
+            "type": "HTTP", //(27)!
+            "protocol": "HTTP",//(28)!
+            "portName": "main",//(29)!
+            "path": "/",//(30)!
+            "verb": "GET",//(31)!
+            "successCodes": [//(32)!
+                200
+            ],
+            "payload": "", //(33)!
+            "connectionTimeout": "1 second" //(34)!
+        },
+        "timeout": "1 second",//(35)!
+        "interval": "5 seconds",//(36)!
+        "attempts": 3,//(37)!
+        "initialDelay": "0 seconds"//(38)!
+    },
+    "readiness": {//(39)!
+        "mode": {
+            "type": "HTTP",
+            "protocol": "HTTP",
+            "portName": "main",
+            "path": "/",
+            "verb": "GET",
+            "successCodes": [
+                200
+            ],
+            "payload": "",
+            "connectionTimeout": "1 second"
+        },
+        "timeout": "1 second",
+        "interval": "3 seconds",
+        "attempts": 3,
+        "initialDelay": "0 seconds"
+    },
+    "exposureSpec": {//(42)!
+        "vhost": "testapp.local", //(43)!
+        "portName": "main", //(44)!
+        "mode": "ALL"//(45)!
+    },
+    "env": {//(41)!
+        "CORES": "8"
+    },
+    "args" : [//(54)!
+        "./entrypoint.sh",
+        "arg1",
+        "arg2"
+    ],
+    "tags": { //(40)!
+        "superSpecialApp": "yes_i_am",
+        "say_my_name": "heisenberg"
+    },
+    "preShutdown": {//(46)!
+        "hooks": [ //(47)!
+            {
+                "type": "HTTP",
+                "protocol": "HTTP",
+                "portName": "main",
+                "path": "/",
+                "verb": "GET",
+                "successCodes": [
+                    200
+                ],
+                "payload": "",
+                "connectionTimeout": "1 second"
+            }
+        ],
+        "waitBeforeKill": "3 seconds"//(48)!
+    },
+    "logging": {//(49)!
+        "type": "LOCAL",//(50)!
+        "maxSize": "100m",//(51)!
+        "maxFiles": 3,//(52)!
+        "compress": true//(53)!
+    }
+}
+
+
    +
  1. A human readable name for the application. This will remain constant for different versions of the app.
  2. +
  3. A version number. Drove does not enforce any format for this, but it is recommended to increment this for changes in spec.
  4. +
  5. This should be fixed to SERVICE for an application/service.
  6. +
  7. Coordinates for the executable. Refer to Executable Specification for details.
  8. +
  9. Right now the only type supported is DOCKER.
  10. +
  11. Docker container address
  12. +
  13. Timeout for container pull.
  14. +
  15. The ports to be exposed from the container.
  16. +
  17. A logical name for the port. This will be used to reference this port in other sections.
  18. +
  19. Actual port number as mentioned in Dockerfile.
  20. +
  21. Type of port. Can be: HTTP, HTTPS, TCP, UDP.
  22. +
  23. Volumes to be mounted. Refer to Volume Specification for details.
  24. +
  25. Path that will be visible inside the container for this mount.
  26. +
  27. Actual path on the host machine for the mount.
  28. +
  29. Mount mode can be READ_WRITE and READ_ONLY
  30. +
  31. Configuration to be injected as file inside the container. Please refer to Config Specification for details.
  32. +
  33. Type of config. Can be INLINE, EXECUTOR_LOCAL_FILE, CONTROLLER_HTTP_FETCH and EXECUTOR_HTTP_FETCH. Specifies how drove will get the contents to be injected..
  34. +
  35. File name for the config inside the container.
  36. +
  37. Serialized form of the data, this and other parameters will vary according to the type specified above.
  38. +
  39. List of resources required to run this application. Check Resource Requirements Specification for more details.
  40. +
  41. Number of CPU cores to be allocated.
  42. +
  43. Amount of memory to be allocated expressed in Megabytes
  44. +
  45. Specifies how the container will be placed on the cluster. Check Placement Policy for details.
  46. +
  47. Type of placement can be ANY, ONE_PER_HOST, MATCH_TAG, NO_TAG, RULE_BASED, ANY and COMPOSITE. Rest of the parameters in this section will depend on the type.
  48. +
  49. Health check to ensure service is running fine. Refer to Check Specification for details.
  50. +
  51. Mode of health check, can be api call or command.
  52. +
  53. Type of this check spec. Type can be HTTP or CMD. Rest of the options in this example are HTTP specific.
  54. +
  55. API call protocol. Can be HTTP/HTTPS
  56. +
  57. Port name as mentioned in the exposedPorts section.
  58. +
  59. HTTP path. Include query params here.
  60. +
  61. HTTP method. Can be GET,PUT or POST.
  62. +
  63. Set of HTTP status codes which can be considered as success.
  64. +
  65. Payload to be sent for POST and PUT calls.
  66. +
  67. Connection timeout for the port.
  68. +
  69. Timeout for the check run.
  70. +
  71. Interval between check runs.
  72. +
  73. Max attempts after which the overall check is considered to be a failure.
  74. +
  75. Time to wait before starting check runs.
  76. +
  77. Readiness check to pass for the container to be considered as ready. Refer to Check Specification for details.
  78. +
  79. Key value metadata that can be used in external systems.
  80. +
  81. Custom environment variables. Additional variables are injected by Drove as well. See Environment Variables section for details.
  82. +
  83. Specifies the virtual host on which this container is exposed.
  84. +
  85. FQDN for the virtual host.
  86. +
  87. Port name as specified in exposedPorts section.
  88. +
  89. Mode for exposure. Set this to ALL for now.
  90. +
  91. Things to do before a container is shutdown. Check Pre Shutdown Behavior for more details.
  92. +
  93. Hooks (HTTP api call or shell command) to run before shutting down the container. Format is same as health/readiness checks. Refer to HTTP Check Actions and Command Check Options for details.
  94. +
  95. Time to wait before killing the container. The container will be in UNREADY state during this time and hence won't have api calls routed to it via Drove Gateway.
  96. +
  97. Specify how docker log files are configured. Refer to Logging Specification
  98. +
  99. Log to local file
  100. +
  101. Maximum File Size
  102. +
  103. Number of latest log files to retain
  104. +
  105. Log files will be compressed
  106. +
  107. List of command line arguments. See Command Line Arguments for details.
  108. +
+

Executable Specification

+

Right now Drove supports only docker containers. However as engines, both docker and podman are supported. Drove executors will fetch the executable directly from the registry based on the configuration provided.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet type to DOCKER.
URLurlDocker container URL`.
TimeoutdockerPullTimeoutTimeout for docker image pull.
+
+

Note

+

Drove supports docker registry authentication. This can be configured in the executor configuration file.

+
+

Resource Requirements Specification

+

This section specifies the hardware resources required to run the container. Right now only CPU and MEMORY are supported as resource types that can be reserved for a container.

+

CPU Requirements

+

Specifies number of cores to be assigned to the container.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet type to CPU for this.
CountcountNumber of cores to be assigned.
+

Memory Requirements

+

Specifies amount of memory to be allocated to a container.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet type to MEMORY for this.
CountsizeInMBAmount of memory (in Mega Bytes) to be allocated.
+

Sample +

[
+    {
+        "type": "CPU",
+        "count": 1
+    },
+    {
+        "type": "MEMORY",
+        "sizeInMB": 128
+    }
+]
+

+
+

Note

+

Both CPU and MEMORY configurations are mandatory.

+
+

Volume Specification

+

Files and directories can be mounted from the executor host into the container. The volumes section contains a list of volumes that need to be mounted.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Path In ContainerpathInContainerPath that will be visible inside the container for this mount.
Path On HostpathOnHostActual path on the host machine for the mount.
Mount ModemodeMount mode can be READ_WRITE and READ_ONLY to allow the containerized process to write or read to the volume.
+
+

Info

+

We do not support mounting remote volumes as of now.

+
+

Config Specification

+

Drove supports injection of configuration files into containers. The specifications for the same are discussed below.

+

Inline config

+

Inline configuration can be added in the Application Specification itself. This will manifest as a file inside the container.

+

The following details are needed for this:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to INLINE
Local FilenamelocalFilenameFile name for the config inside the container.
DatadataBase64 encoded string for the data. The value for this will be masked on UI.
+

Config file: +

port: 8080
+logLevel: DEBUG
+
+Corresponding config specification: +
{
+    "type" : "INLINE",
+    "localFilename" : "/config/service.yml",
+    "data" : "cG9ydDogODA4MApsb2dMZXZlbDogREVCVUcK"
+}
+

+
+

Warning

+

The full base 64 encoded config data will get stored in Drove ZK and will be pushed to executors inline. It is not recommended to stream large config files to containers using this method. This will probably need additional configuration on your ZK cluster.

+
+

Locally loaded config

+

Config file from a path on the executor directly. Such files can be distributed to the executor host using existing configuration management systems such as OpenTofu, Salt etc.

+

The following details are needed for this:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to EXECUTOR_LOCAL_FILE
Local FilenamelocalFilenameFile name for the config inside the container.
File pathfilePathOnHostPath to the config file on executor host.
+

Sample config specification: +

{
+    "type" : "EXECUTOR_LOCAL_FILE",
+    "localFilename" : "/config/service.yml",
+    "data" : "/mnt/configs/myservice/config.yml"
+}
+

+

Controller fetched Config

+

Config file can be fetched from a remote server by the controller. Once fetched, these will be streamed to the executor as part of the instance specification for starting a container.

+

The following details are needed for this:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to CONTROLLER_HTTP_FETCH
Local FilenamelocalFilenameFile name for the config inside the container.
HTTP Call DetailshttpHTTP Call related details. Please refer to HTTP Call Specification for details.
+

Sample config specification: +

{
+    "type" : "CONTROLLER_HTTP_FETCH",
+    "localFilename" : "/config/service.yml",
+    "http" : {
+        "protocol" : "HTTP",
+        "hostname" : "configserver.internal.yourdomain.net",
+        "port" : 8080,
+        "path" : "/configs/myapp",
+        "username" : "appuser",
+        "password" : "secretpassword"
+    }
+}
+

+
+

Note

+

The controller will make an API call for every single time it asks an executor to spin up a container. Please make sure to account for this in your configuration management system.

+
+

Executor fetched Config

+

Config file can be fetched from a remote server by the executor before spinning up a container. Once fetched, the payload will be injected as a config file into the container.

+

The following details are needed for this:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to EXECUTOR_HTTP_FETCH
Local FilenamelocalFilenameFile name for the config inside the container.
HTTP Call DetailshttpHTTP Call related details. Please refer to HTTP Call Specification for details.
+

Sample config specification: +

{
+    "type" : "EXECUTOR_HTTP_FETCH",
+    "localFilename" : "/config/service.yml",
+    "http" : {
+        "protocol" : "HTTP",
+        "hostname" : "configserver.internal.yourdomain.net",
+        "port" : 8080,
+        "path" : "/configs/myapp",
+        "username" : "appuser",
+        "password" : "secretpassword"
+    }
+}
+

+
+

Note

+

All executors will make an API call for every single time they spin up a container for this application. Please make sure to account for this in your configuration management system.

+
+

HTTP Call Specification

+

This section details the options that can set when making http calls to a configuration management system from controllers or executors.

+

The following options are available for HTTP call:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
ProtocolprotocolProtocol to use for upstream call. Can be HTTP or HTTPS.
HostnamehostnameHost to call.
PortportProvide custom port. Defaults to 80 for http and 443 for https.
API PathpathPath component of the URL. Include query parameters here. Defaults to /
HTTP MethodverbType of call, use GET, POST or PUT. Defaults to GET.
Success CodesuccessCodesList of HTTP status codes which is considered as success. Defaults to [200]
PayloadpayloadData to be used for POST and PUT calls
Connection TimeoutconnectionTimeoutTimeout for upstream connection.
Operation timeoutoperationTimeoutTimeout for actual operation.
UsernameusernameUsername to be used basic auth. This field is masked out on the UI.
PasswordpasswordPassword to be used for basic auth. This field is masked on the UI.
Authorization HeaderauthHeaderData to be passed in HTTP Authorization header. This field is masked on the UI.
Additional HeadersheadersAny other headers to be passed to the upstream in the HTTP calls. This is a map of
Skip SSL ChecksinsecureSkip hostname and certification checks during SSL handshake with the upstream.
+

Placement Policy Specification

+

Placement policy governs how Drove deploys containers on the cluster. The following sections discuss the different placement policies available and how they can be configured to achieve optimal placement of containers.

+
+

Warning

+

All policies will work only at a {appName, version} combination level. They will not ensure constraints at an appName level. This means that for somethinge like a one per node placement, for the same appName, multiple containers can run on the same host if multiple deployments with different versions are active in a cluster. Same applies for all policies like N per host and so on.

+
+
+

Important details about executor tagging

+
    +
  • All hosts have at-least one tag, it's own hostname.
  • +
  • The TAG policy will consider them as valid tags. This can be used to place containers on specific hosts if needed.
  • +
  • This is handled specially in all other policy types and they will consider executors having only the hostname tag as untagged.
  • +
  • A host with a tag (other than host) will not have any containers running if not placed on them specifically using the MATCH_TAG policy
  • +
+
+

Any Placement

+

Containers for a {appName, version} combination can run on any un-tagged executor host.

+ + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut ANY as policy.
+

Sample: +

{
+    "type" : "ANY"
+}
+

+
+

Tip

+

For most use-cases this is the placement policy to use.

+
+

One Per Host Placement

+

Ensures that only one container for a particular {appName, version} combination is running on an executor host at a time.

+ + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut ONE_PER_HOST as policy.
+

Sample: +

{
+    "type" : "ONE_PER_HOST"
+}
+

+

Max N Per Host Placement

+

Ensures that at most N containers for a {appName, version} combination is running on an executor host at a time.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut MAX_N_PER_HOST as policy.
Max countmaxThe maximum num of containers that can run on an executor. Range: 1-64
+

Sample: +

{
+    "type" : "MAX_N_PER_HOST",
+    "max": 3
+}
+

+

Match Tag Placement

+

Ensures that containers for a {appName, version} combination are running on an executor host that has the tags as mentioned in the policy.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut MATCH_TAG as policy.
Max counttagThe tag to match.
+

Sample: +

{
+    "type" : "MATCH_TAG",
+    "tag": "gpu_enabled"
+}
+

+

No Tag Placement

+

Ensures that containers for a {appName, version} combination are running on an executor host that has no tags.

+ + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut NO_TAG as policy.
+

Sample: +

{
+    "type" : "NO_TAG"
+}
+

+
+

Info

+

The NO_TAG policy is mostly for internal use, and does not need to be specified when deploying containers that do not need any special placement logic.

+
+

Composite Policy Based Placement

+

Composite policy can be used to combine policies together to create complicated placement requirements.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut COMPOSITE as policy.
PolicespoliciesList of policies to combine
CombinercombinerCan be AND and OR and signify all-match and any-match logic on the policies mentioned.
+

Sample: +

{
+    "type" : "COMPOSITE",
+    "policies": [
+        {
+            "type": "ONE_PER_HOST"
+        },
+        {
+            "type": "MATH_TAG",
+            "tag": "gpu_enabled"
+        }
+    ],
+    "combiner" : "AND"
+}
+
+The above policy will ensure that only one container of the relevant {appName,version} will run on GPU enabled machines.

+
+

Tip

+

It is easy to go into situations where no executors match complicated placement policies. Internally, we tend to keep things rather simple and use the ANY placement for most cases and maybe tags in a few places with over-provisioning or for hosts having special hardware 🙂

+
+

Environment variables

+

This config can be used to inject custom environment variables to containers. The values are defined as part of deployment specification, are same across the cluster and immutable to modifications from inside the container (ie any overrides from inside the container will not be visible across the cluster).

+

Sample: +

{
+    "MY_VARIABLE_1": "fizz",
+    "MY_VARIABLE_2": "buzz"
+}
+

+

The following environment variables are injected by Drove to all containers:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Variable NameValue
HOSTHostname where the container is running. This is for marathon compatibility.
PORT_PORT_NUMBERA variable for every port specified in exposedPorts section. The value is the actual port on the host, the specified port is mapped to. For example if ports 8080 and 8081 are specified, two variables called PORT_8080 and PORT_8081 will be injected.
DROVE_EXECUTOR_HOSTHostname where container is running.
DROVE_CONTAINER_IDContainer that is deployed
DROVE_APP_NAMEApp name as specified in the Application Specification
DROVE_INSTANCE_IDActual instance ID generated by Drove
DROVE_APP_IDApplication ID as generated by Drove
DROVE_APP_INSTANCE_AUTH_TOKENA JWT string generated by Drove that can be used by this container to call /apis/v1/internal/... apis.
+
+

Warning

+

Do not pass secrets using environment variables. These variables are all visible on the UI as is. Please use Configs to inject secrets files and so on.

+
+

Command line arguments

+

A list of command line arguments that are sent to the container engine to execute inside the container. This is provides ways for you to configure your container behaviour based off such arguments. Please refer to docker documentation for details.

+
+

Danger

+

This might have security implications from a system point of view. As such Drove provides administrators a way to disable passing arguments at the cluster level by setting disableCmdlArgs to true in the controller configuration.

+
+

Check Specification

+

One of the cornerstones of managing applications on the cluster is to ensure we keep track of instance health and manage their life cycle depending on their health state. We need to define how to monitor health for containers accordingly. The checks will be executed on Applications and a Check result is generated. The result consists of the following:

+
    +
  • Status - Healthy, Unhealthy or Stopped if the container is already in stopping state
  • +
  • Message - Any error message as generated by a specific checker
  • +
+

Common Options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
ModemodeThe definition of a HTTP call or a Command to be executed in the container. See following sections for details.
TimeouttimeoutDuration for which we wait before declaring a check as failed
IntervalintervalInterval at which check will be retried
AttemptsattemptsNumber of times a check is retried before it is declared as a failure
Initial DelayinitialDelayDelay before executing the check for the first time.
+
+

Note

+

initialDelay is ignored when readiness checks and health checks are run in the recovery path as the container is already running at that point in time.

+
+

HTTP Check Options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeFixed to HTTP for HTTP checker
ProtocolprotocolHTTP or HTTPS call to be made
Port NameportNameThe name of the container port to make the http call on as specified in the Exposed Ports section in Application Spec
PathpathThe api path to call
HTTP methodverbThe HTTP Verb/Method to invoke. GET/PUT and POST are supported here
Success CodessuccessCodesA set of HTTP status codes that we should consider as a success from this API.
PayloadpayloadA string payload that we can pass if the Verb is POST or PUT
Connection TimeoutconnectionTimeoutMaximum time for which the checker will wait for the connection to be set up with the container.
InsecureinsecureSkip hostname and certificate checks for HTTPS ports during checks.
+

Command Check Options

+ + + + + + + + + + + + + + + + + + + + +
FieldOptionDescription
TypetypeFixed to CMD for command checker
CommandcommandCommand to execute in the container. (Equivalent to docker exec -it <container> command>)
+

Exposure Specification

+

Exposure spec is used to specify the virtual host Drove Gateway exposes to outside world for communication with the containers.

+

The following information needs to be specified:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Virtual HostvhostThe virtual host to be exposed on NGinx. This should be a fully qualified domain name.
Port NameportNameThe portname to be exposed on the vhost. Port names are defined in exposedPorts section.
Exposure ModemodeUse ALL here for now. Signifies that all healthy instances of the app are exposed to traffic.
+

Sample: +

{
+    "vhost": "teastapp.mydomain",
+    "port": "main",
+    "mode": "ALL"
+}
+

+
+

Note

+

Application instances in any state other than HEALTHY are not considered for exposure. Please check Application Instance State Machine for an understanding of states of instances.

+
+

Configuring Pre Shutdown Behaviour

+

Before a container is shut down, it is desirable to ensure things are spun down properly. This behaviour can be configured in the preShutdown section of the configuration.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
HookshooksList of api calls and commands to be run on the container before it is killed. Each hook is either a HTTP Call Spec or Command Spec
Wait TimewaitBeforeKillTime to wait before killing the container.
+

Sample +

{
+    "hooks": [
+        {
+            "type": "HTTP",
+            "protocol": "HTTP",
+            "portName": "main",
+            "path": "/",
+            "verb": "GET",
+            "successCodes": [
+                200
+            ],
+            "payload": "",
+            "connectionTimeout": "1 second"
+        }
+    ],
+    "waitBeforeKill": "3 seconds"//(48)!
+}
+

+
+

Note

+

The waitBeforeKill timed wait kicks in after all the hooks have been executed.

+
+

Logging Specification

+

Can be used to configure how container logs are managed on the system.

+
+

Note

+

This section affects the docker log driver. Drove will continue to stream logs to it's own logger which can be configured at executor level through the executor configuration file.

+
+

Local Logger configuration

+

This is used to configure the json-file log driver.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to LOCAL
Max SizemaxSizeMaximum file size. Anything bigger than this will lead to rotation.
Max FilesmaxFilesMaximum number of logs files to keep. Range: 1-100
CompresscompressEnable log file compression.
+
+

Tip

+

If logging section is omitted, the following configuration is applied by default: +- File size: 10m +- Number of files: 3 +- Compression: on

+
+

Rsyslog configuration

+

In case suers want to stream logs to an rsyslog server, the logging configuration needs to be set to RSYSLOG mode.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to RSYSLOG
ServerserverURL for the rsyslog server.
Tag PrefixtagPrefixPrefix to add at the start of a tag
Tag SuffixtagSuffixSuffix to add at the en of a tag.
+
+

Note

+

The default tag is the DROVE_INSTANCE_ID. The tagPrefix and tagSuffix will to before and after this

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/assets/external/cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/1f642.svg b/assets/external/cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/1f642.svg new file mode 100644 index 0000000..ff9f989 --- /dev/null +++ b/assets/external/cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/1f642.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/external/fonts.googleapis.com/css.49ea35f2.css b/assets/external/fonts.googleapis.com/css.49ea35f2.css new file mode 100644 index 0000000..68986a1 --- /dev/null +++ b/assets/external/fonts.googleapis.com/css.49ea35f2.css @@ -0,0 +1,594 @@ +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc2CsTKlA.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc5CsTKlA.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xIIzI.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic2CsTKlA.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7mxKOzY.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4WxKOzY.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7WxKOzY.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEleUlYIw.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEleUlYIw.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: italic; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSx0mf0h.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSd0mf0h.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSZ0mf0h.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSx0mf0h.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2) format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSd0mf0h.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSZ0mf0h.woff2) format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 700; + font-display: fallback; + src: url(../fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..d88dd2b865729b9e220b2f6644a4e42ca9757525 GIT binary patch literal 10656 zcmV;RDPPuiPew8T0RR9104bmV5&!@I0A0WU04Y2G0RR9100000000000000000000 z0000Qb{m#>9DyDNU;u(P2v`Y&JP`~Efr(uEN(+J<01|;10X7081B5gLAO(Xg2OtcB zKO0bQK$%T0&7% z5rVhba2gs_BAoNM_RR zC;A7E{tva?QJX>-e5guqT#yc9=*O==p8kL4eA{=Ipt$EV^Bvk0y3~6>KbIAHNE%>K zT9A*7oFa1W{42j5&|wG3$GZ+O_c8YF-PFG+{eL>kcD}}5xR4cMAA7?^w@FiARgJ!6 zdHzT;Xr08e7Azm?*hlOmZUfY(OVL=n)!v4PyWAB`+AF#)z+GNmL+!vQ9aCz!##loQ z&9Axar+rZ?_x3HjENTbEh!kT?alHL^$7)NxY+ifO46p@5mHrL_Mh*aQFmILb6oKW+ z2saMGuMpunsA{#UUXyCkqS|z*E?sKah#E7kX053W4=i?o%o0Ph^54|JX+W-e>*x*? ze&(da0fpaFB4UBUKSI(HfI=hy)6zALeN9OKguwp;2?lWC5i!``1p)N;NMNM#9JLrL zLAz1&TT_*vpHSSjcbO7)ul3gXb}qGkK=gfo{_kbi6#;Xxy6aU4-P9M*C)($e;~%oO z_mb8#!;Gqt`l0(&?5n`H;ZCxd!`7+o0pl~Y8~43zr#JZK+rta1-O={t1hANPyE@-{ z9N&9|%zkN6=fkY6-D>dPOjOYf5@lroo9t#;z%qc5tgXHxP}0Ukzd5jV2lpdzeC&Pp zbO5U>ev8=;#3ieD+VvjA6Al{eoi&S`JAPThuG5-p&zP~|R)V;#`lC^X#$DHWEQ(c% zExVX$N;;N7DQIY$#B5B}5nWtlw1YP_yRf&aPz%-FJ+=5o^Kz;DBjBKxwyuGpk+F&C z-hKNI96V-b=j80->E-Je5*i*skBo|riH(a-OiE5oV=&q09lQ1&ICSLLiBkm%6)Oc| zHcJRb29T|I?dkQXPNA_q?!`NGateVQLI=cC<^TYIBbRsh*=yn9-c;ndY@rYd0$?B) zB50&aN$iMD10W=FvDGx)ISpR@Hr)w8`(EwVx9=w;R_wINHWg(E9Y$%+QuIkVV#F9= zGv?LG%m~(uK7kZk5)hCv>*w{yAwz$CN!Ls^R~*24j9L zq13bH{&Ts%Z8yaO_?~NZ^)AoRZ^ll`;pEGD=!ElSE-J4gT`(GyC2KcludO5{nZEp& zmTHKK$O*|PF(OR(b$t*KBxW2XJ3V+O7dH=ApKi4pwd&Ms5Rs6@%V}b7sa;yaPDQZO zwp0WwRmIk$uE8mHhHY;l{v-J_Jfywjs@Fkmt!r0(ou)WVtenuwM=MiX&br0X7Z213 zGSh&;xv`&lOI6#5UYc-K_Eb>U0|;i}n#{F=s**1{nw`kwC-cM`tx;McwB>9^qJ58N zpd)A^eG=_6%f9jCIJ57vN-7X^IcW}-h#E=8B1wQJCEuSsFx$;&H+x9R3EYvu2wG6m zXVV#V zzyV@IIRIDk-XY(j-mx=m#SeXhuUcQQw#zQDs~zr4UKew{5dlh?s2gdnq^&k>*JuC& z5P|?+fRGD?8Q!Mff*=rj0MN3Rz2xj=4^0nw4|zm_-#}H`^RMo^NF}1L?&wS0Q)%7NLK(#Dso2A#RldQpfxLPGHt=a zhqlPPMvvEZ_my!U`5Wlzy3xL{ZOvS5dS$dI3+tu53ZaNjgmY=kU7{oGblnbAFc!G$-1TtCxDO7tZfa;<)JAck1VakakcqQhmjh}9Xo6N~hn^MZ`e3WO z$PlNb16=p0myY}76yOT3>0Xi^+CX6LAv?m>uKkt!l6{}!lR^ixP@w|CTrrpt!z*IW zB(1X1kktKPTLMR z|8U^A5yEVo#l){Y)<~e@>A{SZ9(&A+^yi1C={tHpasa#s@rRmA-ii(Nb zlaQ2>wlDMR5@m^aqC28a`VIGR=CoXSJoV^xr9=TAPP6)r>h{FRvPai68s zy|qB<5Lxvv%2q|93(!qDq{Bnvl0kPZP~QBg?+8@#o&bQlK%apF(9WEK;72m>*Ab8rodb1ax=de-AsQCltKNm!CD>eGIERyGb652WU+8t#zI&_ ziD-zpXUO2~TyT_$mPPbBSIk)UgEZ@A13r%ur4cFQgn8~mUA|q%!X0KHu)daA1F)v0 zft=1snR8KYEP6p@abpLgfi|f3*_u(0lKB_|=vl|GpuZN*Al1$#^k}`xubDI3Obkc_ ze*r!CcNG`veTGUhlXuGKVFPafv+dgCOgPndjH@zzX@VK-i$Mv&X1`RzWgY4HsV@T3 z5}SEfY?em*4$N^UbtV;;IDRnpR3I_{`;rmDK~GK;CD)O*ThzMSJG=`*K-=pT~0 z(OB6ki@7q28nr*D3$-qSlDswgb>iyWO1aLxZ7wr=sZ~2IKd6gSjhlGNw4~l^cYR#i z(ID{HPkCr$MWXzX{={Q12F4D|`x$aQ&xJ7}8|Md{R`o?owul;aOSMLWCuWL@<*1;b z_dA?Xi!K(${6Db}rW0$?UY9-PiwXxql2?JQC}329MifCRy;Xz(G2j8WOg)I>(;RPT3m&rW0?q;!(yg+tRV24(5g0sUg zG6V}F$WTT9#x^p>wzQ`ThBlEiMgaQIQHfhSRyT4>6m=&elh+GNeVgr_5q?N`>wSwt zV|YP%Y~4brWW4b?#0Q|Ept_awHX@~F13sau>N_A7!$z zRrXTu306@>>3PA^~b0z6H zu0_!#=-pH?`XUUr<|uDaW%fU96ePp^>1JxJ@ zDEWtwq~8guLUTC54#7tDp(R#mqr@1)q?8B`5=`2ezqkGF55hQ!hHTcjMOfDmE#?i8 zoJdsmX;uKzTbF&A^TCs!mfT=Mm=#Kln{3?LUY4t76oYJbu>T^5lb^n|uF_VBJ-xe; z#uA{Lg6;i!N*WUx2Nkyy%@Q|F@6vBvYnjUeC6|qFA)o0dqcQ5cN$xONvJ?u zJ;I1JhM7IagbaOr8W2wKT`>-vY&>i^AFDco;c>h8xyoyK}>*5rz!ag9w_Ig!x1T>0zGq-=< z`6y+sHqzxUa%2ke9xt8}^)*PN&NDQfGpiAayKiul%QYa(aD8_4rYRo&!Ar+{`!(Ib z{=5p$!^_*`1umf~x4!@`fv;YaJ8O(t`*6z#zVeBSrHZ{?kKWgPrCdp%R^q5+#iP_g zhWSwMnWH6SF&T#LKLRPczYgZdv#jUwT#4% z$Qj53>mt>pcKd=UzENhwEDJE>I$UjxiG!{pvN{I;sow%ZZtw)1@FpU4R12}yb*4K5 zzFv=jo8j3m>9Y7u)mLu&$J}KGH`PFIgeHC)G#7$-Mb@;e+f%+XQNilnS=LNq+um~I)5X%;gIo=vBAD<2LZlV)b@imA@1mvvb0bz2x_94Sy&yk4Y(Ez zg#A2)++H*tPX!Fg<%PE*CfV3cD$C;_V&+VuLpF7;Ud~t(sU9kBcV4`HusaX#G#AH* zCa8|V8uxL#=Tr}pK3Rd6DXMsq%9w+_7P++rd0kit<%1W_c>aN}qbUAj_iZ54&?6Lh zUx$Tb9=VpE90tXL{N&U-q)Zj^Li6`(r~`Qo$ZlGFlkf+7>`iE4+Dwww;{^G}GX4gt z0Umq=T_waEmKiJle>LNW6)F(w;7qgNC~zo-8sROb>{k*L3{@i6UZ~wjdL&CMK7VxB z;<8)U__c5Zx{8L-Z)_oVaW1r?$2 z31dKkIWQG`ZuXph7DIOj%E74PWuBB?LP1AsIowWT*eHc4TrwF3pTjrb$^3qzCH+LJ zcu&zt58MIW-J7w%CG^@1QKBq@BpAGO!nCa-Bh(AJawIt8Xz;q7?LM2Qz65jNF2eVu zbL0gj!OLgJK8Jn$ooKo{(6c>EswEV*HdVkOHpWr)v}@dlnAXoH*ZG<^_-T&4uE@!> zAlZ7@n~)%U-j+(tJB5^#F)u6r^jY>4mp#?yHPu!9BWAY5zG@`7ikn>3X;{@9xl-`9 zvPdgTZuU_lp$LC^u(=&<0=K$l3s?Y=S#|zhL@u<6yuC~0qmw$v%1lzV<0dd{G`D66 zj%ft*pqnv~moXd;p>TYv5uQZ;93@s03fgO@5k5ePKINpz%O(nWTzuNbam$S%bB~vv zy=&!R#lx=n^>NO#{7rB|SMb*eVu+AQf} zt{qm;xszhJ50g9V{K$wX|3G?i?ms^9^Onr*TfkC~$8^sUpPOKPB_w(2gkD>P{tz;% zb&1!tI);V1C#uVgkYs?yD?wN6@~2B7B$IXoNrAG`<1gn{cB z(-{PifV6MBGq_#fp}=!F{zzb;%K5Q8#Cmyd@hl5q9m3}?D!mXs+cxbV3%Q?H833P- z5UZlwOP)B$40w7+-0baSVB+=wz1t}g%bH&k*Vn83feYR66@+s5Hw8CJlq10pu;e(b z)<#7_eaP#%0~a`^JNth=MqPp562x{OC-g~~E~5iJi9e7H_~ZZJLyppcKMqipKJQ-@oKgs%R->bL8T_$N@=;#_)}!8DCo#b#61 z^gg|u?XLeiw8Fj;^U8(`QGq~GnGrCxJsYN7^EDlu&hUJK$yde3`7YX6*H*8iPJ@>U zz26c`abX5FD%FDD(j_OnrsnNK&`Ow zjns?^vAb*d z$TD0BEQ30t#r!FgG5`*+KX-$I>E2*rSEwZh8liEKXK;E~y_ym+-S^^byO;-#QVtwV zvFlm~EkJZ?M1i%Fq_=~T7j^Ob8P9Jl&u_VpzjewQtBVz&9-nc2%yNBPIP|!ASAwQ- zrs_}M{AM~j810)Ll>#P$!zq%%406fm(E)G(Ip2XU!1u1XyDBZrI2y@4r+SMV750o)-lP7l zu6~y3gszEI3ybw23d+$DF<8IR$BXUx$<*W1QG?~aYl?MNoL60*DN4(z9y+qTnR(N* z$@<}MCXplPCP8?~(Nm;Ld0a3Av^DhHX8P>9n1+J2VABU^jo_(4{-HdDgoBQT&a+IH z88<^eupDgNTsF4qj1ct^;m6L$gC3BbK3d&2-y1vwU3@LSeoQ4yq%(Fj7NmhEb_>r#t)WR^Zf z^aNdscasaYn*`ulc2c!eqj$%EZX(`G;7kH`;*Perd(G*N{VbPyedo^eAFQLOJ9Xtm zks8nr&rV}%ah;zp&kSO^H)*Ge+=UuI=6C^)fuS=pgA&$*gxD>ltRh`YeaZVbBCA=1rw~V;c_&X70Ktis~w4 z)M0P-zSPADYjUtPx@J5duDy(_#DtLBR~9Sa0=V`Pt^yNGK6`bMbp@T^lj8#h!Ffe^ z1~vj6j1Z|PcH zfBURFP@H-yUTS{J>yqU|dA7tmvDB*ZkAeA64s;_ad>O+{$Jg{tG{9}(b}canN3X7& z-uKFG>Nd6#!>Zza3n+z3faj+YmN3~|Lc;=|2Jf~6iJv~=H5vn+Dp6!4D|Y@|>4Hx~ z6U9#7@QqY#l+$W%<2mmXs25&-fa*;Gz>NH+R5%^J*bpQMwLqQ4Bi7=p*JuA%xexxb z+yL2+>+`x^z$SI7Nb^!^+f97t3 ztC7#WLtemjUa5b&Mc*BL*-Ln)d-N8HSW^7}Da`SRiLD(=4)7WV3Y$FUO#OMvX$#M3F3 zwS>p~E^QM4-6RCS1rs8#LXn*;hdmY))#SS94ZBxl_h!DI>yX|yU3j)`Mla{Zx#}&l zv}zmM1T=fQ&#Q~Lz)cFd=iV9?C3-c8ZcO+H_Akc0frqt!KUBK~{97=^M{(<16$Jj= zd57)&?oem7K<mwu_q$TikC>S)44dI|gCR!0maSHx zKwi}5x^WHxXI=_rkbtMNg2}%Y6Vp^uy8As5NWZF2HX<4u>xa$k+04^!&wjGR1g`8r z0;0f?E+cs_T(lO7NY!#oX1hWKG@#0GdWJBC#h{bHDbaB}Bz_PH2<_1elE_Od4p2~j z4CY1yADzo$4JZQ`Sk~+UD($>W2OuX)M8E{509)kOE{1l5mUfjddDylE(+IR92y_Em zy1l%i_N7kq`n^_18`VJI*-tcXdkE4c3sNRP5t8*hK~l*?4pPh2u2Bw;2#MyOuE0wz za4!u+B-VEh(Iuh@Bum&}dQEhk4(J~PW{prLpa?R96c1#*#Hagr(A_^Fe+!rM#gI2- z>u&6@ZErbhGHfD8z89`|aX;nwgkk#DNas2icV(`|j?K)tVC9`^e*txp%6zTY3f^E{ z%uCT|dtCe=r4(&d0pcZ)F^IhD@%6cSmNR-qG^|surbs~)$3?9sxh&~QpOZan#ied zryUeYGkAzf^hQoJ83&2_s(ci=s29{df{Sy2!lym5+mX_Y=Q*ZDdW|YZ)W>0qRI^QS zTfW)Fg#nw@;_>MoU{Dq(VB2cxNqTa^3uI+{7!gr+B)>@t#0*isf>>|MmT=|O&1(7+ z!i2s&Djv`}8jU-Yvz{FUJeNBTv00}vV$MZ*kCaZXtmM%KZ8pVxLD7`-K?^cXLo{0n z6Qe#}%oh4=9j}6Xm%}HVYT$J#-)KEPZ~M;!UCt+e8P2hcYR}e4mW+}{=;Rm&6)KOq zU5`j%$XQt)m?qWZ&9FHg(%5&h%MO=E6WjC;Rkm6M&0A-OxbgV$XGVkML{$2xEJs ztHF;1Gmj7#u&E04KKN+7P+0y|T`{lw?X_5~-i!r}#@?MyLQEF(1_QgD6kMQ~2X`*5 zTYv|Wy+kAs7=-1YkS>n{Eub<&^hWL6A)vUEHvd=o_8~_0CH(TeS2IjOD(sUzq94q3 zCJo5lqG$A5^D=LkQ>-pB-e;_PSpsQ3Zi@-Ha#b^wi%ZUw=Nynb2vZw~pGeVyvLGhv zviX8nWzS&X7IupTH7{ZtPq2fmme9U~H}8=+@S0>qw-6?c@%-{+-E7k6)BOyR4Ob&2 z-T)N{2Tds5aMP&m33(enmi8WkMKR<}5HYdFT3E?zsruuJHRj`U{HmNmMHP#4MzRrZ zW4#{C^{icg+_|h+=n}#DyAjK0J3(!QZd!;RE&GZs>+{t*DA?;?tT&8TJ07Y$n@rhr zRltOc$!{Ww-6p0bsy6Um)Wy)XX2^KWVt2+l(^y)gJE_|+;Em8FF1Oos&a<9U-mCRry%uQuS?qj#Zw##6Ug){v3IVxgDs<9AY3vX*ciE zz|U~XZIDABg2*QZ!MBDxTczAQ%+-g^2o)v8xOY|gC~{E*!XaNA;x zH?s@4=09yJ9GB+wQZ(YB{ob_U&+_=0beGgh6LgvRyBGy z1Uov+@Y}YUSx5O(pGtzt`P$WZm;1Zsk+FRouB*cAZTZS}@p<|E23Ix#%=kJH(m=cqD; z*^~o&*ol~DXj9Otc}&s9H5}TWAt1;+GyxT}BIr5@3TEHQpFA=b|1(?eOT~Gzan8ib zp*--n{GJt}a+udY-roD{pZP1vv9ZbwcjwPjvzVD2pF|1oN?TfZl)BBDglEj_JX<3S zr$aP$>+qdJcCgCTjnv?#A)#s~J852Hw}-=Y_*7EKK@ysFjF2E9>9L+pF_u{>iF&+7 zP)lRfyXT{lOZx71^>*BfXe0Zy!$)gQa7jK>vLw|!%sz<~4`4FWnwmRjwOCtY3=p46W2skEtNpO3u$ zBko8Y9l+PgKi)$7bnVNBT7q>H2ye}rh>>=^mb>fSTR;GCG8FMv z{%Lp*q=~4amSjrThleA;w$T>z0bPVH`9z_vM57;95P)k+QNUr&m}iN-l>N+%5ymhk zy6Z$RrVzN`O0j^9WSoQqQiz$1MU)BJ6JHxF6v?TV2}hm@6vxkkb>U42EB3fWX+Bpd zCA{p=vP&I$Z{5DBpa1@=+`rTv9RmP(@Gp4^fY;0QpMMTl?&bTlR2UH$00c;nKQaJv zZS+>@_XALo=PO*^^@OX+zqfJpXA^T1ey3h~_PE@Ps98?uW z!k#1Y4B|ZuczpWYTqcArOWb@gex9qHJrEmHfBD#3&HFHpSZwXQM(pA>3lsnd^F-{$ zcOvfojm7+8-CBPL{YR`3U-Z2>-Ta2JjWg;X*65#22gWGScas9Qhz7(BsX)4HI_wB- z+cY!>O@;!D@ibz9)F55x3Q~p`;APuJ!TiaMGq&&RfdDS6B&8Rnj0FvV1J@yt50^2G z0BH`e)90_#5Jn%SVWd1yBd~hep9CtsJ57LDC&@z$9JYDRuBzeXJ27j_D$zvanp0RS z*3HC==1f$%%Qj5ecs`AI2`SAwq2EC(PA zf6E8=#5LJ`-2qbTL99Y;~O2dA#!|Gy=0BE!;YV5{B>4aS{|kZdY-b~emB z&#r^j9;X3lDA(J}t-Y`DeSUs^Mjp#FUo%AzOEltQ;k5MFe`_kyh>c9-E9&$t?e%zs zyai&evC?d1HMz@2cWAd#6W0+M`y|)@Z$I-sn)yG##ibM|n*u?g%*#SmM)y=W1Xo24 zP#8jsvZ}i?JU_SI|9_*lv1CLI0gGsr(K4z>VBo9*hvgM5R18(dmx455M%^7NM}t3Cc!v@Qy~LC`RWD$%;E5Nnrf5()g^u*7-g1^ z;|n+`;@dk+9_A*9N63A;UQMAH>A(Qc0lq#6vpdR3wvKE*1MX?U)rT$sPyyp=_x@M& zKbI-GJRQp!T4W5rsQ;>(Bqi9QD&gG0UU7p7QIthiDcrx?hW_i>NxH_{cmW0hl9*n< z{VM~)G5`Qr0p=}X2(p|7$a=OQyLdqihy)pO6y$OwKnMd$08$9s#|tE~i#rbyqPvh){yibUz_p{vw8_ z@1Vern_HnE>6FF05>{OU%fqX1?Ya-1`!_Z4#Sfo@V%q(d@8o-uSW0%@vj2ks@Ap_| zD7*=(z6&cG)#MzVa%z}c_%up3Q#YGic&Rx1hR@t|y>7FI$J$kwU8UmomY;aiF<;vX z5vitKsg#q~cL(8^Yu3o&_L>P?b(<)og7;LYbOuXw?nW-hgZCo@l@oS#3$> zb#VoxJ=t7#dG&qBbowlB+?yiiLz4F_us!H{;8IeC{?9-{IR?a{=8@I@4jm<|-w6%A3_ntcaPyc{maA)5ZV( zUs`^)vbwhU>dm{{`FniCCw#^i{2?()Qi{@)p)AOH^I!xpo$EXP^@$lhBIU=#MI=zm$jJtQ#}En1(EMlYrn!CNJ?4k7#yNf_13 zP-Evj!kRgH56wxO3`tU3mWu6(l5yv7<7YrN*4rcJaD4?D!}ZDgIU5Y(06zlp`oy^b zoU@M52Og5o7>to-ybGF;v(I&;(ay2(qERn!anef1Y~+6ah|{tB?&X?m;t~Pv)&y28 zkO?XjEdj+cd0m$A!D0hV*jX#0Gn2`hLdc<#{RRP>^u9* zz6yBhyskA+Cde2E7OX^oTBihSoBoV@eRDqVjz8G3F#XmA}r@%36b` zd{na|+S!|ZF^@ys5}b$}2tSAFNVS!q-se}gCfRB4*1Z+ed}J zceU&48g~Kc!wO$(&FdxtLOdYE8Y8lGGa!V^4!+)ZWelC*+OJ;d@4aIa7|CtC+i>4- z+i={_`|k6~%PUbo{;cfueJ{YbmzFu-{#oi;>RfUK1URBo+4IVP+o+Zi^cWrTD7L7x zp)>b|Vi+?y*8dA&%FMJGH{3L5UaUBirp&q?A|Ja&@)N>KD`)X4&2kpP+9ZI|2tk=z zAe14v@gxmQ5+%Y$rv+LCf(bxzQnbKLfXM!!^eBK{o&XLg1*kU=@9;@P2M@LsiohsR zEWtqzRAmlt4?3X%u7WD!AXRQ4Q<|;T#<3i0w^(lYb-qETgvG!j_jqu?fQP=f^D0h|e4_t5tUCL|W$IctiWVco8gH2h2RwHD!DGfy%p6>< z#eL29-On2QfUB?<8QHsHui4q_V9S#mhf1xW*9Oh)M{&o!fq{Y z#^!3)0n5f+yhB`_g$IHcJH$36s;Gn&lGuH|R5eZOm6qe#C)lkaiq%Fa-J>0L%ZtR{ z43l<>^6w#m#;DlgbQNp9&rhS8rf#}2I{-DCQGV&5Ls*2XWC5U^0Dun!a2<$$1{D4X zK=gZnodtA%dalRMNFsVN<`nagBUH~Jq!(csGz$CRij`GQ!isb}k~B{t`aW1quMTNt zg&>fAn}L`2n}%#dzEx%>$tkf`Us4(~9(23%3EuTZb*%zG$!_C6jDM~yM~l9 z#)^EbGd9geT|1|eQmbW%BRA;<-z4)Px~$9G;*+wG!$&VamXwdqJXVrY1cF_-P|n1T zXg91KvoFR~o|C@cX*UrO**sUdXOzZZK19@;2r|*=FC2=eoe3Dn{+@yv#tF z4rWQnp0_$C70pT~G)!hKrv?_YiZwnxG=!Joxdqw z>+8qZ4;?CBP2e8}TP!riwxX5Yrcp0}DU$=9C&o^U($1H%UM7bWyWJm08OSsZ;zig= zHig}}N)?t$aXRH90yQijU&ULXt*=4FF9LRC-;~hvYvwm|&#VV_$89yC40~17dKP-B z(`&WV7b6WSMFXr}rC(gPd#ZWW*d_297Br!VwNzUC=Z+|ACAPV<$UAZBjHc}3Bv@zJ zK2ultgsdm|jzap<+F@!~sg<9Zn&iO587K71ruvi>F?m7E2VP5ppf#X)L*9e3;ka+Dy2s06JTbp2h)9DV3w06fE}O_u>%}B&+XJuQ2^|^))F4#`vVqwxyUnzs7>(?wPF_ zuFtOBn&lQ*VAa)p6>s4Txr_j(wBD^e_a}b%`SQpA8o1PYM6j3|iA)mJCM1PkH+#R% z>#JERajDHI`uAJPLg_e7z>a-Uufu=^iivXX(MvKHowK=Rle)2a5b^)Lk@;E^Eoz}K&u>F*k&sR z;;?j@yrr9B(!i;Us1?UZVVN}07`#J^y~(GSGm#ArJ+-bxoi|UMeEdTD2KhKiJy*p1 zX`mzX;V6gJaEZ(J$1bF~Dr9>UxWpHmXEMWFeIgDu`stV@9}3beZL{M`)0ce4*9Gg2o$aMgwX@Z$Ij2o%^iGjR6Fy?;M`p!ZnOwq0+XuRiit z_rV!YJ6nRT8U11V$e#a4^3Ck2z+bh3Hn-?;IvD7lIEqZ6 zsM@=*@-v7plxlmuI*J4TgWf#1=e(;O{(&G9^nN=XuT32#rfX=HB+0|6Hn`d)1T_7s z==iH^4rioOPfVRZirAWv&ecY^eJ4gF;~xm(NRc0dRqDKqul5|_b!Vz}ApV=}Qlru>NcO{-NmVMSXS@G5_!*No`Xa~@`?Mu@1M_!A&Lh{B zi4&RP)dk(fNI5cdQKn5>Y~jtEFEZ&}6G;(yaVN^4eY#3vbz8aPgNSp)2!p^rvvw(1 z2}pIYW)a+}e{=B%c&VDqFF~ZWt-hkTbc;oNBYCRKWnArYmS~m^_Oox#XBm#>J zgomoV@4OopMHtFkUg75SVlRj2g{s4;Tq|fNav}7#!;VSxD&bd2;G?opzNZi z6eJ6|*bpoMx4^Asr=0>7sl63KH~2YlFm#Nm;TadiBaDIsnM0Mm3+Q^JZw0w5AC&=w z_d^ayDeq_;5)XIB82Wnskqaq|4~H92H$KWe3V%w@gDzJ3e8g5N;p*zd*AM2y=};Hn zVHH;_gn3=4V(=DtiO4v`73$@DL|80RWip>sdWi~Pk3s0 ztF$Cw*gHriMYk8?`3MIJ&<@nu=VNCT-rR}QAk*ihTcvXjb=*KVAU(^-74%R+;7b3h z4LSgBPaEPQEaV@KDU11CCZsSi0&Yajekmr`6tvvBhqNG7cPL}2mwBmIeDn!}0#qb) zCYO=NU*{1<0ndIo9dnQYV(quGk{;HbAkT4Q3*Cm(38NrgUV=+k z8mOLGM3SKTIAW+Bdon%+YJzJgvHwtc6KSbe{TO7y88`!)J1L!!bu%sPrX2aXP%<=C zqZG&ANW)Ch(k_b7P?a*T8CY5D$cxrKUsa-Lc{F5-wVlCxp-^X?Nt~Sn#riFekIK{vv64=I;YMtI3%o(v)XV3r4*hPs(9A1cBkq+=%O zT&CxD&+P$H!-nBP!$xS%&TuM=&R1BYhYP3pVPd6MeZz(I=?TEMVL{KYAQu>WhvV<>;{91mp}Ow@|YC zc8ST3?91c&NP23wFMB&4MiszM_)Q{)e6(An^)fjFIv=w;$TjLkcs^VMwRee>VA`Rv z+kXMZ2bagHjzKaZK{hoZ8AGo9Hav*pLC2FQ!5xqOLq?FZK`nb@&Jm z?m^npxBJu=n&w7$K_hO~_WJ7Yz~R{nOo~WQQk0oq6Ca))H9Q-E3#bwg$&9yT5c$Q5~CRfRX%ErlJ| zQNhr0FLkD|%^o7hKvBR`5cFCgj0NLDsYn=dI@FxKpyi9=-jy$0*R`-JLU0vE1|LqnJ1>6e(mj^4z*4g=<(NuE z7-d!8D3}mP4~dB#Cr0S=PMy@o2cFbP?Z^tGwuhW!5JS!~3jd~O&!1xum|4(B0uMd_ zS0BL^2sw+orN5$fpE-L7O0pqKb3zQ5PzsNnl=VGER_x(L)PF*m8zLUU`;$L^EJN(t zWr?=&v(*s6mm(8{3uW_*#A9%%D|&2l5A>#;MUxsLWudW_glG(#eDcPHcK8l*H&ZUa z!+e8L1__|gr=xVFFHkDv&rRCMpKgoJho9uP!aF(-7U9FheI=`|wny&Nd;D6Ap+o5& zK3f5)S&=1JOCLO;lp;J=jHxpq9y)+OK$T z$}=%;luG&Q7fp$+op35N8)gv}FPc>(c_)%Yi!5mLG1({1#V2h*>(xr?2gaZSpc`QjT9m}ne z>>C{|gWQHFEbzet;$~BFu-sa06%{8wGE(9GQM}4j@y9NsDbyMCg{?F#&RKI3wGy_+ z^ULbuka2Wv_AZa=3N#lw0MDM^HxT1QE-yYGsXO@v69aNbt_;Tk9PW>uu9B+u^-IwJ zi$T0DqOg%*o4)EjTt;_AI+6-iRicyG0OjoL=L#`Fgul4U4k9OHFlaOr&=AO920l35qv4<_$OfS)XHJyd$w5_8&U>CVavRu8> zE?RRZZQiwy5ZOylP2+FFcE{_BZrE6<9{M97#+?=S8I|lA#QXM3 z&B)yS_4$3I9T|B@=*`4tCLiS?IdK0NrB!Aw0PpK7*~4sPB1h(loVjgNujlvKO8dOE zAZ#M_HCB_Q!&zWdBUX+ome2I8RSF?O+lOv}Y%RS&yHj z-;qaRL*Yb*2oe@z4uwM@hp@$I3k_ORnCo-PfMd(UKM>e}G34HLq3$-IO-a+DJaPO` zaS!TvFh)8`tf@GC1icKpOpr;H(O0<_U)2*ieV&U?iR`zxWHZHOC2q=QkY(Qy*-u0k z`-q%6;;RhYs~zL7(o{Zab$BfPjah7WTm0p`_!_lXBQc?MAR8${EsU=3PRv4bz*BKD z6b*TSo3^*NNS(1S9VsioWC+=bxae8(^sJ(&LYIb6-au^l{;9{83WuWemw9nn_D})T zQcpdZa6B#XsO-Xx+ozE@Ki6>AEKy~~RU-R}{HZHM_QdWl*U|Nmzl|&YfoT!-O8puye4JBx97vVlG+Vb(f&7w$k6+V?Y}6R| zbLp;7nl-NCU@Xq97AgnV%QW+ibtB1{P1St#zd$e2EX#<6rWS9%d(W(y!PPH-b?h)n z9m{^ytpELFT9*7IGXM9t;AQSL{~a!o`72zu<&84tguK{RS_v-koja zo?+xZLyDY|shuH3Oci{c#=Xr|;w0sk(#A$2kgnzI`^^HUmIS00LS@T9Es_ZM_T+3- zLlZVn*cIav6Qf#=;1V;SY|RiOT%!9}hNmMO0*CjCRXR#d{Rq7fMb1Z?@<_A7E@P$XH9NnlaYd}Mxt&na2zU5~$5mN&E%zE* z`vqy~7ljJhzj%bj_7y^YUF1!al0`*#lfvHD0fNv#7{rNU1r@uFsR6k}h%H^f-{(&y zPYk~)H&Dq85c1#hE#O4@5iafL%Ch-O_F5+EYGbwxaz`fmTM=&(mpKr@+y4JR08^0x z^+c7X1#rNKe$m{YV%5=a zu->VEhpM(o3$MrC))W>Cp8`5PwE?iLF)1^G8JD?QDmAp(QHDNZ@q(3qIaDgN!;7-? zc<6cJBgLvv$th!eyfPdgP<5s*!{MNwDW?OJ@F<5czTUxZFs5w9eQLqFhfQ|}lAfDAagBD0W#KWAkKRVCVs9NIDu>zZNOjq) z?C=;&&eq?}9d{)bi45wvYe$XU7bGWMM!Uo^_Kzo~82jf*dAArG0L`|Vk0T5Ck`IB_ z{B-)eL}1oWHa#&LE3F=H$8wp^>K0@Vi`q3T5v?@Gb5iqs(XpAIT0m>Qb)Ymp{W^3Q zbb)g24M1Jl9&Nu@>f(8aiE?pn)QWRem`3O^H4V)Wvo&pHZng%3_3lV<8QBKH21w?$ z34lgki&-Uw9hPGdX^MrFn<4eoy^X1@e*IFU(x;hO zd6D5gGeaE)uz{!;;p5)(v8%13ifxnIY+K?O|Tx0wJ~M5AS^v` zU9b061ZG@Z;u^gM0eZz+ifX<58{;hJH!N%Ur{0@ivJ$tll80p1Mz5?Fn|l7|2CUFA z%U8nFIN87L6FWdZUIU)~=|nFOKK9bQWPiQ<|E6b^tYq_M9%S=P5>|d)R z(r|28`?n%>dP8{eNDlS1U!Qh8wqIpeu6t<$^6Bou0%ZSH4j%Z;+=iarPxL!p%&~*` zud5`(FL^vSY`Ta%jlhIs^V9dFhMy8$z<0p*uVKs63Ja(ft zT3vwVsQlj-fEplAtP~@FKiaZugpPh3bCKTjuojbkX5rlWb@A+kd7L zyXzdHqc+m0%|5y|H0$+m8z6&8<3zs7VIwmmhgH2@NeywXRFoki}s)JC$1KxYI9;1 zfEm@Fw3W}KxJ^ipM$a;U7|xGo&|qeO{~A@fc>-|H;2=8zch~=B@T%Lr3s4=^f2`f` z>-i|JCd}%2Zw_D@A}&hFJe6ZsF7k3*eoyz4RU5-uE$a9tQSPsRquCOSa=8q1Lf~1)wjVb?{ha1M8yQh-<^Nf79+~ zaDtl;&`Mc0-moqM_y8Q#Cmj{w|1w|<1+Tw<2P2SBpCongf>|;~0rCp;K>G{2_hEq_ zZaSoeeG-)QRy+n$h4O^y8o9z%8Soo{gn)&9{LCK@Kf6p1-0sNk7M3Zxj6;HClu!jZ ziTLw&a3Zzc0En!c8*V!E)86_MUVw8<Bmz*eK}@K!Ka4&bS&_U(RGwOf@mRhcMt? zphMs%pwsbV;1W4|wDqb~z%2M53cac0+|c#wVFeu89_<3KA{A7+sm^n0(OYtcWW!M> zYXF0gXetI&m`U)=kvOFFbTcUtGJp(OtJw9cy7Ey#qLfQ4qEQTkD*0{m?wCcu5QI1k z2nmgVK3H=HkqR0z)DyR$t?=(gxj`mpMj-%%5ktcfxZ)elsn(a>%NDg@qH^GkXpu4W z3ofHg7^=oFWVJw(+QN*FF|)#`SLQWmqw5h}Nd)sSaWU0(v7o}_8lO$_yoUO;fCJX(Sr{<+x*r<@R|)Cd_s?C-AZJBREHI+N2PR( zIRos+fX7xqaU5d$xT1*97t=1!GhEVufDo}d)SX%|DQOKciKgikD;y=v+l2HNK;hQq zM(r6T&>9^m7?qdx;fY;e_YhaHrl5T0OSf&HS}ZX| z$u{Fo{*7KTSOT$R@osb7=i@U)^viY@VdU4bfqE6AgtO|nq4Wq0{DPtRZqa_Bw-y2 zw{(}@BS#()2RSym3MH0xw&o#s+)_aa&F8pOn`%?-%^*k#3i`Nrp(}c3>)I@$e?bA_ zxmEOTrywK(cGwfDL!GVBg#)`ihJaUs-fUxV=!JH^4cSa7|8r*Yqh>hRV{Rmc4BJKP< z{R{7Ad*>z&K>;nBOxr5hvP|x)P?sw2+7;?(CzFhw1iiI%;{ZAn&WR@KsPTRrLTtqr=ztL^5yl`_ z#G($%$HjOAt0w{&VUL^N(op>hQSzWAhIV0{8zxw%0pK-pR7h&@L8i{oh-O@xJk^!1 z+=mRH_8&o5k*o*Nb{m9*vTXLG!6N-IQjI&hIEVj#_%-8nVg_oQ1DLZp>|Q%_tnSh z@Md@aV{|!y{0xxVfu3tlT6J&S{J<#n$%+?x6`MeXa=MZN;gJVhcv9116y+Hl+6?P< zkZ6?}W{O-a(H=59uX;6U40%in@`-xd3)I=CdzTY>2yyGkP1Pr@usHP%1pTG;+d1^C zimach)2xwT0rXXWqfkPZX%eP5T&FsSKsspd3!roZN1AdkKxX>Q_8T=QZk8MV4h->K9CLRxw zm$o3{Ah}A6L=TiCF;{MAMDre_PWQEoHAhkF}Qc44>qHmU@} zR}qXiflXxTd~?4tS-XOL_%NEb%z-Oq320OFm7ghHkiTK_R~j3Sdq%>mztMZzeVTSX zvcc3#r=vdZW<-IEqkPOJ!QB_uP=^;5&_-;Gr*+(5#*u&RB?hxU1NitOrD;EWeZmj^ z!aH_m5BAF5rQcpa1+9%yiJEk_BDqP>0qA*L=0*C&)=8ArBGiJnr~>>C7zO|_Jrj#4 zTgAC!S9WJF@N~8IRetJ258<2C_W-jLL$uTg(=>MRsNHj;X-EukRT;#@Joiyx9vYK2U z6gzh>yoI1wn#+P<>HvkBf!oK2O4*!oZ-mFKeAmAE4jyK%XO5#}1D;OM zCfh5=TwBhI7Rg?;zOS^o^j(la4bu{w zRV4E3Cs$2FT+!WVuaQrckP+i`P`n_xa1F_EOgRreGO|MgS=SQvt1|!JyQ})6{t1?V zPo%FxAgZOrK@1%t&7q6G9K&&#KDKgys)2DE*!7)A6uj_Khe@+nC){9*l8J4m#V@0;%$GJ#_V;#%-&cRe z=D>CySLbadHhcrxfbN?C%(CQ1eDHHWaRD!QGTi<&2pilCE(TA77lZqfDjO&n47XaQ5RV5L%eCp{R#={r z+=UHxgEz_duiz{6Qe4Dt;R@)6tNR4e87QuSh8{dhn6QBe>p2cXj^!lvdX_|F3S$xo z@USFJ&#m3NPraT;vsWI=Bu3YXGnXI9mbw zsy@?X+Dx~78j$&`y=d$N01ZpPA(g7xZhu%ID8O@7Y~3{WI9W?a{r`gS2+BiUxXhDJ zo79$8Jn!9EiydB#u9>S=?3?lGTR4d%iI54&XCXa?e9&ewj{-#aE9xYbut8-IsE^JU z!LOzy{}wRGcYK#@@kCMsBB{wCX&5VMdWnXuufU_Ej7X9~1<8#|lF#)>?!pGU!Oh^q z;A-%NkFfovn2{0mv&abEaF$Gqiew>!Q8FhsSlHT!<~^XkJdmlqao?((@l1oF3O@=; z1b~2c*;G^ko}6=L&apX*Bs-crROo4hSy^@@4|*L2gijsMz=Q0@)#7Y*?BFA3VHE)Y zSoc2m-l;jKtisIBY)Y_aCf>Q+Xdh#QdRJ(vDM~QSVB}qEEmNXl>OH5X z97WE`b|ic=7zn`oZ+GLtxyCdY|3gH26953u|4cj>0N_pBfBtE-@nF+Eo1}on7yuCX zTL%^(Zb#V>emg)f0r)($(_9Z*^#1VqCiHs;Ly_GT*){G35${6xQQk|jn`E(?3z_!L z2c?1K3bMWOyT@L7`^LJpqtQ+?+*M>PTb{H5`dhzmKdXwLg&(R*wTWVL^W)jDSl4`z znj_83V3gbUdVVCGXnDoX=JYRZW3imN(44>1oJ#R-PNMo6HS;-p{(`T5sK4Up8yk9> zH=bG9YLC5LOEXd}fJw{Z)mij1iD5IR-&9CX7YX!Fx|ixY;4vM|3ae&vYYP{-_V%amFsUuhri8Tm}JFov#XM+xoEk{PntBN z-T>@qS}mGmJI(M^3Zbn!Skq=gY9rOVIieQV-}7uUwPPwwKA@I0MdT4`C;4O|li)e6 z4~7lZR5K>X7h0mR=6a0$M71VQQTxeP~P4baN zE9{rauX{UxC#&vpZ-9V&PM+K&M00}tOY?#}+&e$W%b_8Gd`x~O>b2lum?hPc<|EA= z?t`+YrxJ3{PYFE#P@Fp*zV+8i`d;6B4M0FUIgH8&8UcU|Fx~D$&SXp{Q#Jz(;7&&+qSIn&n>B?>oE_HDSvfQ%NEWd$DwF&tfcmjVfhsDUvk&evL3UYaxp zABb?>_plb>g^V48DquU)SEFs{cL^sGs!j`_E8D`aNLigs#BIO6s^rN;4YgEc2>oLE zV=%lBhdn33wiQO$t6vNZiHi%X3BbQ*>^dYMK`BG7i^d(wg~XB6Au}1z4Ti!8EsZKQNc!m3e2?~nBci#~xYTTh<<&bh)*>cVFSuL8IfDWylrdQhu Gr2qidA)JB$ literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..317f651efe77aac3beaa1de48b60c15d5154f833 GIT binary patch literal 6144 zcmV+b82{&YPew8T0RR9102lxO5&!@I0626202iD90RR9100000000000000000000 z0000QQX7V19D+y&U;u$i2v`Y&JP`~E&UDBI3xXm55`i26HUcCAgg^u!1%oUHAPj;( z8>}(~-0|Q%AcgDf9~ngjr&sX*;eeY1*U>Byk5gb27>*;LKp5UAfG0M6_ENSeHcHWn z9>tI5`)x}>F`^C#i#WGWJC^2+$9}Hu=o;Gk=KP*b!Ko3F%o0><{g>4+vwNS&9g{*t zbsV125lR+@K%xRv_urKNwWjayZUA79gE0Oku1KydWNQh9A)_;uXataQ&G7uW-shxR zwX3y=gE}pGEn!=EYf$z<-q9VQb~&ci$pAp8u~4f}pL~gIZ?5!1+X}m}(3>L5?vu}m zEt<0nZ~^&J*{6$ev5!jit(ctx6t$LzFb@F1HY&pVspwtQsSG589N@LifcU!Ne@<6~ zLvq*kbJc8zPRd;Q%%ui&4aYAL=h2J zT#6;hecmTpV1fX)1rri1K~i`i3P*^Beh|XIYQSQIF*t&_DfsN!6A*CV00J9mn+!eZ zkQfo*U1(I51Ky=7SsCD6m9LQl9u!!A_PO$6l^hU3`OOd*DCJvZ%sUjy5G#>+Jyfi; zQ6Zmc&bLM7dUt-$Z)7TtypSJr*`X$}&+@&{W2VY`fv|pbmG`wsa&GM_=Nnb~p?rFM zyti{%?#g$$1Q@WvJR1A~yUH0+eK|1eXY^Mq@1-GA$U=FdkS2R)wg@F@sv>ES|d=nu1Ku;i&&hCE_R#F6W9N}N1l1_7(5JQig> zW{yXjW@A?el(q-U){EIqECy?q!(q&li9Mnm!*_6C{ZOBPXI2M99}a1BChfq(r;|9S zPNen{v>pemgQ_A9UMC;C;J38L^w1t-tB2;(ZoYyj6pb6Vvwo~430Tnk_s)x2z-F^7 zd(E7SL$t?)p3+M*9a>@sgOB%*bJWX^jTalbsc|4p*pf{lNa1Pa<4JZB%yBrW8uP`c zeT+1?d}o!@Zc9#E9Gm;G_PA1ZdjbbCWIjw*CwW>iBy8*0-QWdP8xI;9&cPr}sekn9 zv)NCRad_(G+rE-&so-I69aS}g4{N*l>p>}UqetwvzP6EBhHl&ztzA*&zayJ4{hgrQ z9-~GTdD|VR!#;K;W1xeYA;S^Ju}sSV_W{JlfPI0w4j8ro`eQ)d9$0=4 zz;Y`?s9-8mPqvy-4xvb6=tW;e}i(QU%T~#_8+ssrabWu0F zgkC53t$BwEra4_{Y{;8i z*i9;-JNGA%Iqai!zBZHNty%`9=XP_Dg2U;)!t*w?&E+V&tUMpHOjg$vHg2=Wvolo~ z(Z-~2L*_e9Pd2!O1LzpydYcV1^_B<459OW`f~ObbyXT8OYa)k8_dnsRHq;>}dldv& z>t|b5r(o8gVKu0tZ@aQRi{h$7JT{Rj!6E$%Mg{vkJ=uoss3CP_V6umfW`{g~>-!>F zzzn0>8-8d4-?wJLxl!8> z>glNrn>t1cC@JLXYnxak-W`!fyEX|WuSkjj@~o89u{WKTw<-i|)s(43rtiG5(|J%7 z#@9>f-gAAA+s#x5Qrs@R(KmGRDc56?+`}2m#bT$&BC(7t9n-RZgX~N3#ouxLx;d4S zOYY6@zJ5Cb-PBxami2#~n>X*?bi8pxT#!X`9|V|b5Rqj8jKFFN)qSMspDsn9><%a! zQH;Ahr|#D5*5RW~TGmXHaT*v&LM%m-*n4LbCH`U+sTkKy73|_Kxz01i_p2D!O;vV6 z&ROIGoAexsC&zI*z5O>G-a3e*t?;QD#Rs$xPN5`n=Q^I>Kaw`b`l_%F2&G^olBcumDSDp2c*+rgy*GO)@XR&7G zr6t&E?DO$;Gx^xNNvX4)nyqJea;9b5RpohFK5gwB{!p61l4jTnuDz0OwrX>_7-~t3 zib^<~sen45b!&+Agwm!3pT)E80ai6du5Z;KT4Wfym+2W#iMu|84VHTN$K z+h(9#3(%*GLxl;pdpq0;?e7!+pv`34nG>V0qC4Q*7<9v3 zPt{F4)vA=hWzbfo`XaMdz&bqd+cbDF`U==w3;o;Pu=EKw2cCJu;-GrFPPHo~1kcQs zB*S|2tidjyC}A2Xg8>@R?9H}cyIMU$UPVk@W|TmQ3n}?KSL1o-qG!=8JZnRS{Q$E7 z#Zm#BVx3>FrLB0a;PrQ}xrEI`);~p#*rmJh-`C7n!TeV05vLRn6Ww4^PBKWS^;(Nbc80Spl{mqLgi55L}rw>eKEm;X`p;HBc z1#(h#t5ts~;BHv7y_?I;L2}?CdX8nshzZ#$j{4{+)03r0Gkm9M4rx>lDs-udJvFDK z3_gomxO)G0I+x?NJ?=Q7?aicQ7jvp5fHhDzN6EE{MdL^py3@#qmm{sn?(XDYi~$zk zoSpC>j&oqzrx|D?a&=v?KWibqX<)tzS&6=<%IbBy$YFn)5%K-1#u03nXy0s3E z1E^21Zup!r>prtLyB_uy^~}Ucqvo^c^v{}3B2CZN*tRz=eT2=8Os#sC z_&-&8t}F#sBj>byDN)Td*tBI3va`62k`aU7DP^Ex`;l!GfJXZtIbxK{Rhn^iw*k>Y zy*-#p3edQ?j08voiZbc(Zey>Xr%5VR$@b3qA!GQyL6dH#!L(eJE(vnIEcCwYlI3I^ zZyEARvd*fF&v*f51D0ha-+t5;PWAITCLW7N7%5lj(RFaU7>R;|r6MtDlh^P|F9l{5 zA7VRDnSulCI~zqTnTS1lw23BNQBeySpgskhD+Q=9AufK!x#<@!_B> zh#7v9QOar}5hBtRWFfpCCSbK#l8DXAAk$Ws;n;FcYhGnFOTEvt`x+U=QWJNmDW|nNTdQKHs`*VZbpSzP~@bPU| z+Vz_}Wf36;0k{9RUYh2Gu<GSZYK5Fq$6S49+YIVk@lNriueAX^zg0B zHjTC$L=*N+N?LCZBh6s9jGc}y1T}Wk1#@yp7yX5%=%RM$aLJH)X-StZ`w7{Pmo?|} zvWy(Y4DA`TtCe0iFi2ll3_*dqq6>c0(wS=MYQS-zuIfY7wLs#?^O}B4iq+U%jcN@0 zVR5E;0NFB>TnM%}_;lo026mI#>P$%Aps4eKyq&n4DEI+Qu87@+{4dUno=2#0ws_RA$tC%dNIVD-8(b~PEn{&Sv028>+W5lO5lFn<+I%ZZ{c%&)@1p`n;vC{}F#_4J zZ$uG~r{_1a**uc}Q3Iy}MCL9Sarjb7kgVF_NFt8#YGPA*oAJQO$x}$6OOL7V6u;mJ zzRzV!uW>W;Y2eOke)z{Mc%q-pPyI@60oo}ei%Y(#Px=iKpl&wWVnF$zUsRnhJ8$GF z$rp{$f3OMYn~$S&%@nQg1~A$(UALre;xe78yIP`M?}X80?C_w?_v^R(9#If=`{!dmAer*26_t~xZ zT|ot(MGjq&+b|Od8agA+qKS48fFNO~DF!iI1Ge5@>`uOC5*8h|=XkcBU1-+Lg!CYM zNN>Y8!mX_f_-waP!q^=414u$;$6t;IswCpYz+vbdyMvj`Kc|1TA3F{VL#O07>@)}- z8k8uS2n^=ec95_?Xnx7K1{C@-!iw$Jc($PBkSQw(-_pDAjr5>5+f9@(X%G7W>izk_ ziMqz_eE?`HY0x@|-7zo-;&#EILpgL4sJ@_hm2xRT zE$)orxo8{2P6UUJFW^Ks2ERb(gH1!{l{02CubI4%cW6M~(mJaJGGD;%0>+RTeU1a( zTNTeP{i-miTCH6$ZUDRi4erNi1^%>&(-(gH_$qZE2*W;Q0)JSdl4!u500YAtjL&f) zg*3`G?1Z$~i6cQa9Ma%uSF_FdC$I>_z^~6~=-&k#O3$j1IQH4DSnzxyM|pVPT3b7h zAtk(AD9{>9*5}7SRyxP_wYd(o!ohG5P~*;8rOSf(1METPNmhIJT8 zM3KcAn*3%o${|iQL?K11N#OA@NHN<0-T*pag7A6AcS?(6Z|NN4&uQ=wW9K+sb=ro_ zD#b%Q?4#KD>0rKoUPSKqy(!uoCP^Jswd=bb=m?1B$Vb$3&&E!j@5|4CS&&vNKBszw zDV`7&OqeVsLYA%}Nv5RGtQ6?;-WFD5@@^-CT56R+Ax3t*HEk1z+PN+%!f}bTzYByA zjKEjXdH)jqWd?mrh8e(sp@?)o*4?RT(K>Yc#I{%+w%SffY?P(~Cabw5+TIANBFkk# z38#t>R2cjsg5xsAAtkA3@U%NcMcJz4`8d)g=ujem?$_k&7{NjgUUku0(I|J<*FEeSc7xbqT~!}(E;b)?tCy^sI|HV*5ORC#8{5MOV^!RuEoO&B zidL1Z!JMkL3z#&bk2Rzc?iD2(~BBPsp;~zTv8@&TSY?)C~)M7q}W2KKG+J`N^!ArJEK6IKd~%f z*b?f{gn5W!Gb>Qn=}9iCdiN>?h(m~j97|`a#mi< zE#lkU8Yg!VH4va&)j~rSP0C@djB*nokOS|fOyMfD4w^IuoZ-g2DNDA)CddOU4-U~o zH$pa$Hh&<@(K3ls*T>hz*S_%s@^tos*cK&WvS`2pO6>CVSYjQUIFA?NhAyC_x}R7T zPkY9z$^{rOPC{0KwoL0)4HZ6H&)`MyC7!hBK32;LVG)-_%FiV?R>bvjKNcIvAf7aq zvMxItaD%F$VltnrD*%_z<+A%u%`f;6AOM{y0>TX0YE9!qBa(s99r~;u=CQ?(c2F0U z7m|pkZ=?L5SbyvAL$o|;K5poS^M^`Z6hZ5$Q|a3M9nHe(H11CmPUZf`b*>*l1O-$X zY55c4oW5g9L1N3G15Xw4f!&2OFQ?+b{=4X$f!%~nfcP!{nsW#z{11N5xhU!n!TEus!f*BqKZ0{z{5l-+pL%!b zfDzIVsAz4@J?E5j$2s9Vvh(m?H1{YeElc5FHg5+Ulz~&&LHHM~z+RiP6xccD?AbcV zz*rJ5@m5)8lAhKd?*~JD%pnd4@X#D@P|DEDF!WY?FS2h8#}K_!#2eM7My0*Y<0(>N zq3Q*Sir(Bp@VzaYtsKA7_fPJ!iT@F8W&rr~PnItLd=*K*{+aOq`h3hx4d%fB0pnE@ z6=2>%WkZFl0KM(u6!+Cxq?+QlbBXYD#^AI=r=loOUsbb zs0MM;geDQ(VwKdrxu%SFr&SZjpz&R;z7?3#lT^1^N@`xv>+!*;S2zzWECMF$89jma zxYs|f3EFFK@M@eRRVuQ617Fr+2&E^=p+}@6&ECE@KdMBBuq6gDhzijqnbP}B7Kta~ zVM&BVUyR47L!8W?d=sCGD=e-T28$;fR%{jmo7ZUEz5ydX&7T6?cNybKgDGDwOQB$k zIT4uIg`hSE=XkX{hNN9OhVgqlhAUd673hc=j&ZVxL{LV6%V_9`N&)^g ziqpeJ*lDxghwCm)7`MqFy7Q`zLYps&3kb0AJ(}t`5kB?StNuFv`X1 zM5OUMIrEqvS^p@C?!4$RC!j99_tGfueQaGyAGdo0{Fin5R?|vI{Z_(|(7e?El|91lV_I-pZ9qzj9GA%PIP0 SegdgLsh_*2EC(PAfJp#$BSChJJiav*_-j=AqKQMA|}A3xz0}3WQiphd9@T)0v?cdjtgF1pow2 z1OfrL8OJz8QAt%3PBZ}92CzrZViuYyXXEZIeT?ng(%w5q^sV1E5DXA?ysB9ujt=^z z`fL!n^9=PRIvO|%ARVg$JQSs?wiE%m)dv65F)mdw5my0dDP3Y>q9#GLcQ`7dwKXcf znja?$2B6@>(Tp%2B&e(;?9kASw=e)C@k;5cYa0^cOzTcz zAD2brhg|B~H3=!=rdixwB*nUfRN_+1h$f^OH%p{Cxul_OqGg_Ho?5=?RI|kFTE9tZ zlOauaYCqLVnv%wJo06tPW!j3YBik)2m5^E@p|Lx+#49aunP>rn8)P{9Vm769; zE*-W#P*A|OHMP0-h_K-f`S%8P z?d|Gu_O1%_h+cpH>ul^D|+sw_cH8s8Z=7)Q)Yd!q>>lXE?SloPQ`N6+f zxp$>!bX(I3Pt(<}Evb6__10;RqRU6s_EqSP(2?RDKX`V2a*ZlhpDb3Zne?z{)bGQt z&(vCX&9GDA`8Pe7ebms7D^Kpre6O9UG<)htUNYgiduRJD9{o!C0O?)%#Kk>*$`jg{ zY4Lw2KFj(iKe<|cRU5fvidGhBI`{S^2Znz=yNvtFHwUb}BQ{*0w|q+Ku%mJ^Q_xGm zf${$=;1Jkh`zt-AznJv~L!t0Oc~K*Ik%S>!NBUkwQXHuyBST8gQVU2Z{^qQv5L=ej zVL61ePrBdDdR3NhKP4#r@8-|U4xc)WVR-(y^ym)gW_?p|C?d{7Vr|hkBfkQ0O*G}t zM0%4_dr@k|!@yMZuGNJHr2`yFpjaf`UM2{@>oPP@6hl=E5P&ie|5K-<)Hd z06h9bc>u3f!xw)q`tLQ@Jm*#vTqpntaP9S$YM# z`g%T;9_MU3i~wAm%}Cnl2mt(`pm;G5fRhd?LURFYeA&bbM)P8hUBV&tBN&0l#sNI0A+HIAjO1VPCYBVaA zU>14g(;#DmjBPR!RO!~K%Qoe$%WZBq>r!cL zF}e_ggLd1Z%eo&~ZIe=+vDkgu6PFvXQJ!87>?>@D%iSv1w%hs)zSR!xHpxQ;`&ZaK zY_c6Fy)j9Qtnex^k@bo51@7>r@~7v7amKhq^jjsf#tWnFP!4p~EueaJ$2x7ac63us zs#Y&e1#wAYoU?OPYNB#PTg6G7^#yY#k4oZNn4B~=Sm^eQ_Uu?!IWLj-nq!?6@i}Du zen(95?=Ung!(wZ;?&NIC9)iK7e&R3%Sm=>{>dDULad9^R%-`Q)dbTrm?nWn%D|vKhwkj literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e0934d94e95b8f6fa1d541448b9f646e05c877c5 GIT binary patch literal 16756 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 z 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

xNsgs2dmYC5`1PTpk@S_3cRaIRb_hK4 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 SYkI*7T8CziX@4%b5&!^*pZ631 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..efbe79a294d2a5816017ca654b2c0071e9de9d5b GIT binary patch literal 13104 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 KuVs;F0RRAvJ12Dj literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ea329ab8262310d36eed5cb3aab74c8548b0dd57 GIT binary patch literal 6148 zcmV+f82jgUPew8T0RR9102l-S5&!@I06H)L02iVF0RR9100000000000000000000 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 W6iz@`zsu3k2CI=u4f-W1L2ml5i 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 Wzk{-4rd()5mu{-TrML#n1pok0$Ovx$ literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1283c45d72b3fb4f4c96927ca56c33b35958c230 GIT binary patch literal 7464 zcmV+@9oOP_Pew8T0RR91039d*5&!@I06L@q0368x0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..851fedb92bd64b3ae2878f6f7a90841cff0d24a0 GIT binary patch literal 19780 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∾^te@a;W{kKTTH#0o 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,iiBZ4r}M?=1_X-76&vmPP-mBe z4Qx`GDoQz-p zrDv+Tq|wj3NPrpeAJqc8FGB9kk#zDakzLT>MQ_lJQM;t_Wn8d_p>D9;?}`=Udkvms zLn7stPckQ**u|x=#Pi?|qov4Lg6zqJTJ|23e(icAT8(-yGL^0C*f&)tZKYfw62VoR z2O<-7_}GN(L9FHraG80CBF*>X_g#-7uOT$?P*fxa3~jub-EAAPr57P8!&s1ls7*vF zO(vn8C#Y#;SrpEkkS`%j{arXftl0xywkQ=UD&mA|u3hvyB~t({*iF;excGoilZ5?GQc`FbMOp%sS}9B^~>|1^GJpV=>; zA?nEzYvu839O;m0K24)tPps2k3P~VG!(imry#l+@qnICSSM3Vzo zMM3vG@7sro`wNZ}@dDm}enY0s-{GEAS4xp1rx;0crVYJzg5FA9SbbF`JqapaS}$-0 zzLqceV|M6A3Rjv|i8WP>2~T2q$^V9`KUlUXs~gX~z!&O~`5uDMz`t|W?+&x;%UxV6 zt4MF4OwfUJK?MiYS2MEeVDn~w6<=0MEB721)ltx+wz0JOS>8bIfszU<%#cBr$sE2YTow*@!GDVSxh?kmo^P77N#;JPqvU)o zzK|*J*#1D6#cvV(S2N7tvR`23VXzb&1s4IKnb`oWUQ;Yd=89)o6<_4&o8K#t^<&oh zNOP%q)buRYc&cAvt&WrzuOHPf>QVKqI>~Zj4ueiU7#WZLWE0WaARs*_IOe!{P5O)aW;K{ozjbw>V4bYHO1w(?oVGE+6&MU(KEkjc_hNU+IV#Bd{2FxV`hT3qcGz@Kp zG2}rsWVlsOhPW{qYylavP#acs4QGfL%qa3n94Gk;QXhIZ@eL`$l)~=Ema5lDy*O2Y zRL~iHdk1zY)J^<`BnEklDKsb%kTM;h0)TD~LQrBQ6HY@V)x_JdsnH8%8dT$BSveuD%oNflzPU5bhi(cTNQw`G zGB;-L!eH(?gB+~sMp)08M3PRc8(})Xvv!F_yQ!n=5O)=w(>0sh_i>RM z2HIprFRChm2Kf6e-I*5G>dsw*&L4A(mJBs{3tI}~wuEW9%DXMAnb`WRM5ZwR{)3x9 z+}~t64z;)j$Y9u#dzW;^9*5peP;4LYrj@9z znnf#O-O}(|PVd`*(yWcoj0$XvZKKgsuh*FQdeS}M|GfnMe+T&QJK+Bp1pd1#|Hu(W+RpZEBjH(YDe!XNIz*@=nxmr63Q0Q1EnU*El0lBeqa-TI{#@Quyj2u{BD)~%scA8OG4YggSGZf-8WXgMPM)3NnjV*2lkL%Q1#-#Uik~1+)sAI z73bEE_LNHi`d^p7Br1s&vx5kLS-0R^@!A_!PSA_O)H08aIU+XKYJ)U=<;;470Vqol6=cS-nG?vgjv0vF5y5gnc^6SP7Tthgxygd%l_Fz zSZI|WTRdY37@q}3s(7cfl=B&T%5X{`Rov1Ga(Q;7RVI1+fdCejUJil7bm+0of7k4ZDvrO&OYw1YPISSTsdwT z{*?U2_&d=zc=wfji;w6x&mEp{$KHcGM^}<>@gw&u_l5h5`;I2Qle@Y6%p1WgBfT*&}6i literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8f20a2cc1b0b8dbd9f75174bdf248cbe800b50a8 GIT binary patch literal 1516 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 LrJ*q=4PyWR#vvox literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e1f558cd3f7c412f3e42ea6533e2855ca8ae5830 GIT binary patch literal 13224 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..688c713dc6860f28df2bedd0a642c868f17b94e7 GIT binary patch literal 6144 zcmV+b82{&YPew8T0RR9102lxO5&!@I05{M802iGA0RR9100000000000000000000 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 Si literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xIIzI.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xIIzI.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..9dc0be8362dd3302d12382547e46cb7ae2c8b6be GIT binary patch literal 20144 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..3e5facb890d04bc7fcf288ba7cd1c9403369fb30 GIT binary patch literal 7724 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1125cc0127468d17764e277be8ed3ddb99969f4b GIT binary patch literal 10492 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 }h677(3&d+QGjo2Bjr`Ny}< zY%TL}wir6--6!oALyYI}RevGLyFk?gW|Ohdn4O$8Z?u2>tpZm(kN-H@(~~p#tq(aw z7rve_oU8`ik)<4yzY9^GB5>gerCwTil8c%lgjAj+5skQqcgTB@xcc6H zyvKd%XqpmKaknHo#?cdMaBEjpi3!Z}^Iov)1?ck(~s5`NO)+z4h)C$96WL+4o| zZv~ys1O}%Q&Z!rFe$;jG;OM!6%ZcEAH4N8xE3W4k4fh)NdBgI!lXL%uJO2ou5Im*; z_V!`l%xUcWqrLqPI#UB{>|jl;{+~P8Qw>WrV6TWdXYiddSmR33<-xjQ^yh2*OM^Ww zqAfXeqq*EyAHzcnm;2vS`nhvi$|Cf!yFMJ=W$1sf;PAl%xAn=NS|72{KA;+D=${FU z=+07(aHJh$&2od%5z3J9Q56q9P$C5BK%S6079YU0*0k3kDk;N8^=V=SEvMLEeH;iW?({YJ9O)`O#nL#9;D9INWhdY14CV8I& zEU*R16eo}q?ZdS}EC%r_9H-~TG9f{nLk5Z_Oq8F!nTiQ(X|Ah6 zHQ}-&Jr1au@Ev)_Z1qfp(nOw(s)-olZfPiQLUUt$EHws6RYHHbiTvesqrl!ICr=|2 zaVCoW0V8$#ZB(91NGpM9Itgzk0sq-zozpx8C}-M91rkk1KcvbSYkJOfKq@oUmb#ew y#I$^|WlGG%D4J09DyDNU;u(P2v`Y&JP`~Efq{7QN(+J<01|;10X7081B5gLAO(Va2OtcB zIvXcY73|ncxE+8LMfX68BG@<(Vm#+W5o{bl2>rHvd5imuJD@?rPUv@?EH2MKEtbc1mIY8>|mRJ4Z5exE$B^ZmxOY=v=9%Q zUUh`VKFQ_z|F`?+o2P1@s{200OkxZobVDQW(YeIChNrpxy9E^+ZP6l3KtPjRobgVs>-k;#zYW#s?VvQDF8WyL}b@m#W$B!xGAz)2?%u*q)E~KV#(Ya`Q7j0?(X}^B;UBrCx{f*FmiDW2c zc2>`uUDm!l!FJKX-xo88CRsF;K~$niNR&u}MyT{`9ejV8_P*qxE~J}SngF3PR(!b4 z?Dpw9I+xvYh~#YQlMsS$$?@MMx{@v)J`b1-e7o)EXb^+|aMhzI7X^~i4Iwz1Ald?Q z#)CLZPZ+QljvR$ESK-cGT;nN-$ii2s2$xB^0xU8SHhI6yDl{2F9e0AA8<4p?Dl7oV zTpi})4`gm2Mg##FD1g7En_YipSPIi^<_-H<&X3y#ZxWm85Y8o-j3y@qt=UJNoV{T6wtLPMVs?6* z69v@#gg$|MqxpAzP^Fpnb-iie_&3Vfyps8>^V>W#k*LND_hb=0c~f;-vc=?1Ca86%mz={aVOxY)1XO6up1bdqQ!_6Cq=4snIMXM z@jws+EC8@7fYpftSkta;6s~r;2uTQ104)-E002PP;-j|FI(1l!HqqcV@!-dW4AdqF zHvw+}Um=1UKr8a{24!U8-!_O=zt|f>wEj_L*V9}wI^D#2wwBMQHX9`Mgre?1wSo^H zNeI}vJL+rG0BaEY$E#+r?D?5j8G zGWF`fgoWUw)`SJ$L{HD2$_PYuZ)KegQ1_qeJ%UjA7PZ`kC#Qav7^KoABbX#EI5`{> z&AGW;6Xl}3oMH$!9yr8{+w8OOnS<;LYY(vwnfjTA*+yWgxx}F-br*x!Fk1f4B-n-1 zmt%xDHB2sSV{+%lf*~_d#>SoT!jcDv}3Xf-kcAa=NX$px`)jpfbIhfc_UMp)fJ6jETvF7 zxSh1e0klV(=AKwaItx@zX`OAhYCB{>2K6W;>--R%pHi?^oMCoL;B;ZyV8&!YmShEM z8|R4ZU=MO47dnq95QB4rI8ZV&*omT;O)5$>nVIhdC%Hg5O9rb_E%R^<8Y(rj&ImAf z!Gk~@L0cno<_WY&%d|)fwA_7}xoM;41}&P)GC^Y6N?$_SN*~xhg!⋘iv5Y-g@w} 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 WvBs=}zzEW=AzKWF*C#4aH~;{?1*U}n literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fBBc4.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..72226f5df01b19b5db8e7fe8df16d93bd2a646d3 GIT binary patch literal 18492 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 ODwAE}GN`$r3kCvP?5KJG literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a26ba157a533ab9ed979d512dfb4beaa890547d1 GIT binary patch literal 1500 zcmV<21ta=*Pew8T0RR9100rCt5&!@I016NQ00n*k0RR9100000000000000000000 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 KFPNT{g8~6O0t27` literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..14af54ae68c055ac99aae4c52872fb2a1a3338ee GIT binary patch literal 12324 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�TIFZa7msJCQ7lD6Q zfp4IY*T6sTqA;u?ybooHM>cn_ymTLEiifny6-RM?Lc0!ATlxZ*0>yb3_k1LJdW#!P zuroq=#@x#`;D(W}WPrVb&L~2_2J?}&2@oTUyG;ZU4_e$zj~U`<4?_+hvNjQ91mc`z z$oI&MSxI;icjF&uqzvUux<7gUk;&|=p)^l0YRDACKS`a!&Lnjf_c4^MhL0Pl1@Z>j z$Vw8TJsmw{CM!cWAxA24URFw8H;kVNP{ux|1kgRmIb^a!2xbEE3v&S(uAC3Za@G<6 zIfh<mIF1@Z7nl1-!5eM&&2oz1Xm(b?JIM09Uzb*@`)oto_}Gp*i&94w!G;ItWMo~EfXV1W2!*FX7)dN2 zq0{gy?VbdSCLARhEP!89_Ob{h5s#b|bB2uBaAZuK7A#E;XnfJo(WtW$fYR=ooRNf! z5P;?~OjSCLwo~*(teHH;0qL5I6wHS*EccAD4FAmrN2?o}k%4PTQ#7=k4@N3$?lM1E zL%-qRkVCZ{;=RIjw%G=?<(&LK26uZ-RCGaS3%abLD6n`6hJ<=5aET9~!kl}$>3~^bMo3}1aJ#X@Z=z!4DOQe}fbnIDtG7J(u-cB&_xlZZkGL~<@jXXYg^ z2}q+Sony<;n0%(4IY4r9B@>Av0TDsj=OZ-PfJrdF5t8dk=Uig)ndi)bc|o72fiY;1 K{Md0Q0ssJ$#ip15 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a7026d4c3afd9c83ffe3acd3bc4dc11283dab930 GIT binary patch literal 5688 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..41637e58ca4a6076924871c469c506fa182efa19 GIT binary patch literal 9780 zcmV-4Cd=7(Pew8T0RR91046j55&!@I09yC}042}>0RR9100000000000000000000 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 KxLqU10{{SCezoZU literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..660850eed0f9b98671f9c00256697e81f9431993 GIT binary patch literal 14740 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 qg61z_0?k?8&OZ^-_sE%yl}3Ckfloi literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..c17545332e1f32ae3b1f321d94b51fc54756f906 GIT binary patch literal 5708 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a7f32b6f05ab0e473a38ca47efaf58724dff8315 GIT binary patch literal 7096 zcmV;p8%N}KPew8T0RR9102{af5&!@I06Dk-02@~T0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxK.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu4mxK.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2d7b215136a471c9a1644966be6e5af672806eea GIT binary patch literal 18536 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�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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a4962e9b425730ba136b544273ff2bf03c0503ea GIT binary patch literal 9852 zcmV-?CWF~`Pew8T0RR910496@5&!@I09srC045j!0RR9100000000000000000000 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 ip5Q3{p1bO#^| zf=L^deih@GrFa}bQ14`#jj&kL4`hSNEs8SV*&oex4 zxA*UyL)MCnRojTxsSq+|GWIx}og5BXcZa*nj1xI$-bwC4r9tM3G?0eI_IuZ=Bu!KU zRNI|F_*jNe+@OzzUEY0D7YGtDkRwt>-R9mpVn4?u+Wj^S@^QUZ%lJX2P4r`M}_S ziZZ^c!r}1|vbHW2Zk2pOK5%X?r)2;Ytn887_s%TKlbMA*BI2bLmGyz;$%#Mv;(0I) z+;wo*#`+XxQk1F0;1QFnH4TwbDF_3o5f&zqtx-vYuq`j#-G{)c%Xardisc9Xsr-Rj zV1{XoKZ;!c#l~RQ`sMH4{n`p>5C)XTiX^i`3w#Is|395sJ0q>%S^)&*u;@Zvn&z5v z=4)0XuwzT|-kFx)t4bMQ~ht?py~kQ5d& zwaoBH8dR|)nmKaVQMGbiOUhU{ruNfZ@k*2`0;6gr;P&~ULew*6-p>CrHB0|%(6}i9 zEtjekL{gCTfKDz76okW1h&}8E)(zzbgbcBg?r`4zX;P)`SqI3P=!}o9F1p(2;tM0a z_U#+MjWQMy!FT<2eXMC6lG02=f(RoJY!g5+yZgQUu{}M#>K{p(fFlOOxV8cqeurN} zFN2YWq77oq9K@0(hz(m1dk)}SxqF0~-oZB^wBc);p)IDj;0KO{j| z!FVH-jcyVG^kxW1ea)#;wu|iRPWSyXX&vBF=&Muq%Y~y&Yc0UZg-h*7i~Nv#e1g8< z%13ucmR;ujZ4p&F*iUqiuPPfZpU;!>Ma%*G2FI3J#oflP_g%jpyUYQJb@732SSZWY zwx>IN*od$EF-lw^OYUY%>8jgX)Lw!Ffzr`uP343!CL?b~EwWA)_9ldXf8(CujS|A} z4zp|Th7RZNkC|nvUlJ4TDi8K6ba$jBb3=J9)*R{ncbweYqza z4G9U9;FeT?WAGN#n0VJpN;R~290cJ^dD^}bM(3)63og=O%8WVB^zr8L6Cg;47_n}+ z2@9t{p(a%Kw4rH7*P&CFZasQ2+}Cda3)@2@o*MPixCxV{Oq;P}&ALrn-gpOs(ga4= zl?p2y_#vh1DiB+=+_UD@b#Xv|S|I!a5FiKQbLvSDtgahAB_DL4-ZMCWy77w-#7Dpb z<~rNgaYMS9N;n@`5ei0_%#N;4g5gkD761|CBlDxK26@F4J4JvJ#lv$S>Ux6pf|NfiM_O+2t2Vh*j@J?9nzY5 z62msJu#I97kd+@kvmS9 zc7<5>BAd4GteC*^3vRH;nn)@yOm(C@pJ2;U^z13EhVpu-C=U_@a@A2fO$0~zo_5n% zmdK_HJewh~DT3P}velA`foY|bhQfABQA3orM0s0PG#QBia*I*=88^$0^6av}u8HiL zq`s5V1x4*w(N!cuIBEA2n|=}8PfGhmdEX&-5+tR&AeQ|ALh2e$iQSac*D&2w(hX&O zh15+vv`c5%2{wHq*kOMd)-WyYbLHhBxzA9#hufst)k&DnNRc+bQG!nyUd`|sthFO{ zcHB7p1VOtW*$9?C-&WeMGA$nQncEV3O;VF#`cz7nVf$H0U+RH0CwJ$7mx=%4-l6OjXZg9U8+0%l9fZ@2nSnpY<&*G`&aUj zR~_#}Br!LWpQ79jHEm($VV-6*vsusAfl{v#alfwm8_)P8GD*P(my%ScIrQ{pFwZiU zxxC7^fu-L4PXpy!Hz9xr^QC$6!34mA5wX>$Eo~TuNkOwjQ#8%Y61sp5+N}LLtTVdV zsn+G2TkdFNvYa{#=DE9?z2?q?=Nj2QQ(nBo{;FEE^5ZW+pm*Lctf?i-i);T)7%5)a z2>(l7vFfHASOXp`vW0_&CE%O%Sr;V@FZ99rA+I3sCslCvD1>}g#W z-A7kw2+;5G9MP+a{8n%vE+Lu%fe(WP`Ncq(!Dj>I^g zr!wBRNJ-94JYymI!$MQ-V{ z@0Gu`U^4xBFxt60cm}9`2NwUoG6R4x^uT$HGG<5qUT}$$4@0?d&{744 zBZ%`#n9{f~SjuPyLE*AdC4D?78Q&-N0r-LTWw@n1Ep6`e3(8$lhZ{Jzt~lDyg*Y-quS zj$|NPvLQF}MkdY!;~|w&(tVM4MXtpp`o4(5m~{uiv5tP>CFyPPbyHQ|JJ)vCZm&yd zn>31EG)>*KVpwR|ij_GYH|yPITyCeXyCIg?EVFd3s#1zh(~Dipd^uQc+a<2TvFjKa z3XyO`!WrEQcBPrp#oEI%`=9GoLK~wh02GmHPbJhAwj=_ltdJjS64ut|fZLJ*<@tMS z%6Ig>QiOk~n*Zhi^Jc$-J5Q4sqbzPRDlZT3mle+!w4Bpw$sb7%_*#>nbM!e$qcSWP z%vc�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 G0002&1Enzl literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..20c87e676ea8cca21679bc8886fd9566172771de GIT binary patch literal 12456 zcmV;ZFjvoaPew8T0RR9105GTk5&!@I0D0^H05C!T0RR9100000000000000000000 z0000QWE+nf9D_y%U;u+y2viA!JP`~EfxjSutauBAE&vjOAOSW4Bm;<81Rw>1bO#^| zf=L@*Z6$1*HXY~=Lhu!TNk>sRn(0VHuyGKC&8xxxe@c*Ygh|;&@n$H?R;%0Bu2-2% zClyA8fF^cwmM;35*J|}eSt!1f-}&b66xXO8B}{{noNmk5a>h3ib5GtqQMYwJy<FB+Hg;%dw-xkg#uFpGrVec?aD11Hncx0dIiO_@Ojx?XbP8wX&yS=on_7f=|NT zssLN+piq04D3)Jw3do-EA)E5+cS%<%85ShN?S-*ufJ)%%$1}(D!0T0GC%k^D1B5-Y z;ou3Xd^!#Q``4C!-LChfV>BUGPPhZ^(>C+x$yA5XHUp%)DXaQc&1r0A@NQzsSO2F- ze?=0$jzH;+i#NqmE_+})AnO_cYXkR{soLKC-OCS#v0n*-93KiG;as^^s2r7w&Q0@J z{AV0VJs1Fx96W;ps0ctQ;Yn(s7%QhrcIDEUl2eLROw+5AE42=(DDAd#Yq`_7a#2>X zFZ{o%=K4Qya%nP=x@ODElwqXAbd8JrE!~$%%cmoirYpSy@Z||$nyA#PWzCkcwsFj5 z%N`wL*w8HB`-5qUmhXJiJ*n4`&JmgFc*YnZ)cfOS>=@JP*;-OA5~)HTss{IG6~p;# zKKQo*v%3XtkXti?+}RTHAXm6&gh8HF3;AFwNDN#Aa2_!{%oTFtK~B!jE-pkOgmbw8 zeTci5+qwYBCnv{}faKF-gCl|DbNu7TKoSC;NQ*o>IF<}J5%-O0V64;87>Aj<1H8d< zAR1Hz`knWkqprWE%^$3;t2Z=g&*|Ny2l}2(nVLY(v#RSQtG9$+-@DI!^|!dqlzlX_ z%b9DsRebe!CZSCCi)~(iQOx?$8yh~@w&{MNO-y(v5xVK8nv7y=h!Awod25t;!Fo1m zy7$WErW^GuHWuIl1iR3TRy=5#kAV3-HxNvMohk-^03=h^t zoURM1-VlO=K@?!;=fev>H!${7$jX9%bK~?2;FACby@0L;TGeJl5yE`0OFs|JUnz)@ z`ydR2&$HuzqR|8hsg16b1D)~eOv0!5Tr0Z}`I#XE2)w|`P6x&3p^-Vo7gi|Ej}ID( zE)3l(Yuez&Bei)^BNk$6c5mhk;T!B1F}t`PjYsu3 zvtnxbp0_7IIVuKVpYtb>1SF4`FfJerSEyVQGM5T3Q!02tYjbuc`G{O7m@IyL8mN+!t>|k7i1VaoQj8nM4xCR2_JcBHPATYu? zTu@@a#NQIXOB|fKpX49;Lw=F}$qyTrJGVCnl$>^~?*?_&h9fLpjLvJ46C!Z;AXmZX zYf}OV)O$)D$mNgQwx0;mwtj~`gdbmZuJaH6?)SI10EWO7FN^~J15ev)2Ec+o2rzF| z3=Z!N2cpM=E&>h`5&3%ZaSVeki}_>D2dg0hxnPuXII7XgnHa_&Ec|7x0gl7@KVh~>9 zMAAlRP2yj_fdY6K!YaZ&lNs}q+*DUw+$_+17Qq}6>7#;q8UYVsE^!1pq!e_Jc1$4j z%3jwuuPmj*asxg%j&$^}ecew8&^1SzyJr}=*r#&0B83P{j<7r=OC6mEky9jA_(zb# z>g}U*Ez>smplswyY-ZBf-qm26=3Xh!#+3*S9V|=|@5b)w0&hCGBg?;eGmK)iR^>tdSYm5E>!YI<=7NHRe{RUuuO+l({Tq7LjIi+9NXS)~art z2+Z|*XlJImPBl1A%!lnxZ$9%noAmGNcN_UW8(|_dsTp_El7wU0;g3jun0B4CFx z*@=kClDf+Pt#DQu7C$?(5<#+jSalklts$nXS^_789%(^fg%UV)1_;)4kGhih;HhC* zYF52S``mT5hdzhgIa%z4F8jP&;YN~Ew?s|~o~fP6!phmn+hUsJlVXWw&2mTE)ER!k z5NkE0pW0+emU${D>&Ob|2Ul03a@NdF&!Ja2oitbTKs)@bny=K}&a%XG&@Bp}x{OQ? zNpYlTB~&$mfZDNb3jASg7Ni1JRyR25=b5@1H-acxwwkXDg-FyxUbyZaq- z***wWBVnJKJdpmLJxm^SA4%f@uX_)^{9!hmF@CgXot9rSvph9Vc6FnQGp!OODV&gF ztEy2GtB_l~IVyeI34oTbEG||Vt5!aljX9BBmD;k7S`b%W4=BeaSLBGrKn}I|onZVt zmym5Sfa|i8AkWmfL1IIAGUAFkn|HfR*K52ztBmPz_|1tDU<Qj`bit<83_7fVbT5&g5j{^kN9bxR%A;165Eq>=Ct*M^-R=3SU4G*$0EG&Dh zaZUzqhe8_zHDGErwdb-G+I^`IgMdiRl0}Ba3-H`L$s~*=fdyTK#$_?!YUP|2J0)?C z`FO8Ij973!EUBo50Un~**lwy&qdo~K_}3>Qr-e{nP~D}ksd#puuyT)~cgOTByp|fI zlwgF>j80`^3ep02<7DIq#)`gzv@+ zLQeMY%ONS&-5UjtZLkyHm7kc{FToT4wy-<|YsUchQFKWK%X}vJF`IzW&t@_mn&D1w zs-r6gwn1s7mV|g{UXAJ=`Zim>cu}H4)r4-x|j}z%pV}oWfw9~?#*5v zK&S%nvn^pso=_37IxH$C^J%Us6PPehF)e`{>Hq-~b!D3^0}A$$Q?1!3#R&@t%T?`= zn=dOMTsviux9Z3W3)Kk@D-Zk`VSP6*C(Mjn3o-yLtG$^=sz5zPMY=KutV&wiQIWE? z@oO#i`Fl#DoPES)xlEU-=gCpBR?b8!I~B4rG{`xg!C}|Tm1TQ)26z)ymZO)=wc4W2 z!_{v%| zy}*7j)428v;J){TJv{_&z4d_Ejz4+lvFG79{1^_~n{WL!ZQ?Qc>hf7ehr%jj_5+E{ zpB?J{^r!Pb%KkZhuit$AuG{<6xn6qr^Et4ykF(D0ZM#Yxdl3F#^v9Ry3^a6=NDW%N z4)flVw-np!G{HCe(=(dNFHfU?UGMhq4*rD^^#SzjGkO)$(T6oABG&12fc#Wmx4e?* zs5^Bb@WLcOe!5gQw;gC~H#uPy&KbrSz_d}-p`}bKwQ$k*BA;bfCpEDOtT}!FcJ95x zPLLW;Sz0*O3X&YM0ITt3Bca^M*q5=a(8RlkIw_+%%wTam{PXqU)2;P|$@IK}?8LH4 zaOsp}OaB;q>H~Rj>HcTkY~|*YALGJn2j~=l{KPscDZ-GMX}Maz(eAZ`0%hJ`EtgrT zLi=f5ZF#WmIJpuKz?${qp4<%$ov?K%>s%|==V)j2aCMZOf$oMsDmCKn>gnU|C^lTZ z1;OtNUmtlO4BLzR`$(2Kew!cmPv@4cs~x4^=(j&OU#;bpEoS40;k@@sHx3tfl&9r3 z6w`omATjTU`RjXCJ?Pgjs9z{qx06{H+o{!8>e8Y;s3{S_cTxhHqx&%{pQ!NI!ud?r z#i^?1s>al{%D5n_c=QTDb|(O`yIdD?%DR>D65IvIUjp)*QV{g!^{r=hz1JF>dwT1h z>%Co)nmE#fhW~03D*tu3Pj;GL@jXjagwcndQ4%GKk}+{geWFOlq({(7>a%BZfu(Yj za`dOg3*4DnBl@8d-1}^yx}Qbn+F7&;T7|Ss%d>h&>6PMQS?V(HM)6P6%ML70+MHGT{Z(b+7Wupe6QH1rlS0m& zyTMlfHHI3a-M_?epl>-<_2khWhS*|WV6fW?jVb?LTsgnIAp*Bshu06xkE##JzDu7x6iTeKbM-WT-w7+!p@^9)LA!J_HE`J5Em95?V0PVWCv#g6fmucOgwX5 z_GA<(-7}RGX&^j=A3YJsHd>t)&f~FfDjQVnV^vJI7v4yB^qg*)`#_$>R=h;fZ56Q6_` z!XtB2uP#V-u!nc?RIX3ke|fohT@d#_UtQ9l*9B!S1abeEoI`C-3yWG`%nx+DD7@VM z)a)pwf{Gh4tN~2?5G>8Jr2X_w3sreJn;0i?ofc;T#UbbQXy4fTn-$}>Sy2JO=iDqz z!cBzrx`|FnZl#~DF|+5rVp{x|2=9DSYds-bmBX2%Ac^#F(X}SLWBx z7SE#m)v_WZGamL1Er6Lx=Jg*7D`s>I6VGrlZ8-3;4ihtkrwURu1mPM~ObdRD&-s|Z z^s!36Z~Ow&$LFLp-URWzP%!1CV$O!y$Oj#X5**{H;1g+2hoP&1_{g@c+I0!sVFm8C0XWQA5)Bquy<8Ib>g=_r(BM4d<>xQ z`)Spe93 z+urq8#K@?Wu<(K?$5C|g*3flIpA*hWpU~?X{^%3fpgckK_1>ziNV56QAeWwG5JR=3 zTZw#o9|sjgIopMMYr-`s6r?){&MzkW9JMS@3%)Z|ZCIDO|0=H} zjHqB$krudgr(*2%lpoo3_?2aT`_tf|5m&NvUzl-0+cRdc^~}Q?4c9uP_Jr}}shsY)5LnHD7yC<%&1m?XyZ? zlD*69RYQ^CHhSkeg8vy#;n|~iXO-dIw_)w=BQ<(Ld}S|8jLbv55#7bzNl@Fh>y0|j z!XjlJy4~=pl-qIk*`cyOaJ5(HfqOxZN)5TYd%oAy_@cbCFSgsEbp5))-c2~{p2*GG zF@0i50wUV)X7A@-Hs>%1%%UpZQ?tC>DeA(Gm;7pf$70wjvR6iMxZ`Po?EOURR>o>9 zw_jd){UgQ$7Ed;q+krn4v4q!yPsh$-w`8cStR8O0fJemQUAbTanJ7qGko-km@a0{d z{Z%@+B_oRuNcmB@UHcb&$A2HHO#S(|Fd01fyuT557uDV{pEsToX_;1#Blzxi0JT{- zk54i^uQE(E=s)i2%Fc?;YTiA87!7F=Vr6Fo_rB1%KSJDe?8%(R$t`8Pt>sBHa~pz{ zIl<22Fz2dU%pN!-6ymeoQscb5Q{sD4lE+e#t%;|MO>n9vMghjAI5i`)V;QwEp01Vz zRc+oRv*Vr~A)&4wIn^mzYU&?ekWzBIj}H3j^++GB!=<7klL~wkDZbkBa>vh@oHSHg zJvb_rPLj&(Qx&RC`j&a1pS6X*uZ6jbGcWtj_Wl)*p zQOX{fc?znm5oR(3Ph}facx816QP0lamK&I>%|rJ~dY22`brgS=TWPbPC!mah?{)xzp)adZ@)#=C)?{qhK zu5%5M((|h7RfX>H^OXLly3doJpH@AsSROuN(g9wKn9$x))$uEThAF>Oabl2`m@upY zm#7aL^nKTze_cf#P`MKG=erm)^6|zTw5Z7h$WO^j>uN&d(H}ao?F#%g!Qm$EBMVA@y!P}di#aEVOQNb!fLL{4thT6`FxA`d{FAb z=@gwUqgLrT4OssRiq1%dP8#oBYEuN%T3H9#{K4bsddCZRvE1F);n0bk)J zv>?FcUUF*Z+(#@TR(X`U0KKFm%H-}am#$#zxS_{$%vqQ;iu6)`k?HdnZI7A2+^@TT z`9A&rQ;9D^-)^s>K5hZMY)??~yxLe=nAu)?1^C6>7M^;z@zr07%s6*e;SlJroy|$2 z_LM8J?#6UQ1k^+n#yksfcL~`|J?wPKMmZGrAatf7%)){MWq9}7dJzV&ssjaSv^2Do zXzb1Bqp|1L%me)_O#^(aEd0F8EdzaAOcmu1DSkhs?8$p;9C}12!U-P$Bj+)*?VNjf z=W8ZulbJpIF-NNZ?Ju?ofnPq4m;Uvef8k2+AN`f;b`p;kXzHFaX@5!hX018qP*`OsLwQow6XGgf2A;$-lX}7rwdBPjyZZ@Fx6~aG!mKfQp>y?#T5tx`?6V|+ix#TJO4mZuC zVl^S`%Yx+Z@f6OD-IQ-V*Vk+4I;{-$@FHIii)%!_aKdpxhXiCnOiZU3T5F2o<6E~f zUV-wyd&!y{zbpNUUI>H`57jh!b$I|Gyvr zt_A~C`y87@j9+*me4AibgH&Wf;u!@{>2?PP-;O9jocZWSfL6nEEC+4uMoWcQ4I(H> zEpu_>Wzy;Z`>i}M7K(1me%tf{iX^j7#M+0ri`EZ<7zYigiwRp6QqZooQ(N7}7CDcxT}o*9GWhNQ&74C|@9H(WDlER_`Z_zhklD_{|Q9 zqR{ct7J#HiIXI>SQ9M*8kRg(eu9y;p{;TJoASkPIN+>9@bkJoRtt{pgqxVL>M}n;zkVV5$P$) zJz9YT)l(0sNc;3J4h1FtXaEB-jS9Exi8cV-3z&%B4#}p+<7P!BQYT#kXoBiC0kv>j zW3(U|0kN|3W1{y(z+~|F#EixMF$hHnXDa%B+~aJ}#%8osF#tFqH)CN$D z=k5^GC>0-z&lh%D6jMM{GmwpB@mj@NU5zR=PHZog0*zm7Tn8$agkLp#fMORj{8D}c z#6o~rNY-uXX-x1~I;yF1>F7mBq|q^%pwgx0nMp6=dT`7X#lf0Ur9v20%36q6yt!kD zI4cMavG*>T#zO705eg*eT3mpFz`WW&nOO_=iv7%-(t*v^j4BngQKhVehy~AsLd03t z|AO|O#awB~_ss9T%%b-wGY88xUlGZ5sxqmhJJ6XRagN4ZMcSI6CTQYz4b)}#3Q^5|UzV?2w%(OhZ~4xjcp{Or`eb+Gjd5Ww zKqWR_Q@WFW8kpbQjrq3C@dZQXZflbdns%xe<+~*Rgh%jG)c7_R0IzCxU+ixD4%cn_ zi=Ha4C%goj63%{<)r_;s%lhi%h6{o1oZYBL1IDX%m8P@wg!{N(ch|5TB2N(2E?$d$lSKIvg1(OM*`9llzLvQ9RP{xNm zL4~qxbP{y&gB$71BP29?xYloZHM<(sOXd3o_$U0$XFUL)0_Jzh3FD_SgoDd12Invd z_RdMr;XwQY^8q0hyPxrWlkd7L9Ckv@2#1+431w#oC1*dp^UNFiCew<6i(Qy&8HTdM zPKGn8AppXDdY)!q_DWWW3hmkz~`Kq?$4s2hIQrY^o(VEhI=?}Jn{o%VYLbJcL1xe`U zk=e6&bxJiBsNXrsP_WUOZ7&)>&OnB5JqQ)})+^?{%n?~K#Sq1N+`deL;ARhf$N6rI zOIKumn7>G6gklRvld!aequQAa8wY+eyYU4Xf%-k@T={wdRRLdWjv5FHCLB%fd^-WY z#$GQ5(E|zp(!}@6kF$$J73M28-{bD=fpj5YUcXuTuNgf{h{?ja*Qg&zO=EGzpK`ijD#6So7m=-g1 zfXkKB^_T}nG=dNWw9sT&wLn7Y$~lk;#@$g5Dj`-TTnI>iG!D$iXSN$eP$J^$BE;Sc zztv8ayuZ6QA_HIxCu?QaOr_rs|_25tpE zE06&{tNiZe*BY_22PIEJ7y+b6>CT~+l2AaKuC;0Yvh*ck{Q3rwA$X;T!NH z_$_edC16aCQN;UyFJ2g3iCm+QC?UGUzQVr8e#wMdSF@>IC03qEl?vob{3I$=TZn+; zvvG)$08_somo<|Xn3~mX96VbXQ6T+s3gE-kpUQWe)Myo=74yxcP2J1xjT%gn0j45d zVXBxQ0VYzb!M)$5|LY1AFay#!C_i47 zY*t3$6~Q}1GoFa^vF$K-^C&D8s1xlQ6F4riLz&vMAIAPTpKSodS1u=NbPFj02ys8X z7zpZgwQOw)=JUf-Xvh& zI6en#ozWIjT!%LuQWYPjWkzRiig#V$IAMZbXQse$_!r^4|C2j5P-|bvbXm^va`*+y z&2Wlkl;y~>kPR@GU%_7F#)gVw6zDo>s#Y1RNn}A!t9a!ykVWw(f$^T|x=3rD#H^jA z_-4Q2N{RtXo0K#h)D~59a}UfyhFiQcN=BKw2Brk4gklqc)w%#`!8%pA-i22~TLU?; zGnZevCOBTUe5pLY`*%s~&1n|Y`Evxl^j04-lJGJ*+wNz&?MnsPC3_NQ$t+(@@LEC- z$Xfm4~RQ}cF1?3AU+7>|0y0jZn@By07F!dA_&CsK%q zqqQQDYTXt!jB7;6;XIxzqww6tqFFD1(3J-l;nv&>3z)lWk)O#XRYW5QODy#}%ESOE z_>MS?A@%T7kU=JPa{; zKAbLeH^!FQcqRiXvL!i2FP5*GdpPzQ(UP`ga6wmKzsr|wFG3|^ASv;w$M|Ar zF@e>RvGjzqiGoqQbx$Vhbq{Sg`uH{!Wpe{F;Y`NWAt1x7AbZ+A57s~`wwoDUxpOT< zaC(k%FKIcjeSPb6xKsZz(TY-Z^lvHe|D=woDeBUir~XIYpgLtqS8AL+}6 z;bGbur9bGExKC@a^ttQ^6i8U39hgXMt2|)>%ri?uIYYCIx>6J8%yOCcSWjcmP>W#f z%&21n4is!CrhYS#%^akeHi>SzO@UstUm2Gn8pC`W^ZYJD>i@)!k@${h#^9L&MI(^c zB8y5Y);5(qsS>ncK$Q_`)k9J^@GT+$wnwYvQc)Jl0bEy)NMusaNlX$1@;i~)@+!z4 zy*}Hck^hB7kY??5EV8t4t?8`Q7NDG#78}`2Gy%vwT}nA4v~dAqS&dy0Bfd6(p>HUf z9EBhtphkM2-XlFC()*j!K;0Ra!1le37pw-VL9f;v#pSDmRy@GNBP&^&g)tFSuH(Iq zY)6vGuw@tmu#YI6a-!Q=Pa3;k(y^~A*F!MRb-4-4xFoI^=>Y=;WffPs=vRS(gJ?C)#wxN&D#Y2IL?c8+R!OA?F zbDm1U7p8vo_EZ7ZQ>D`?O-ek7zd80^$0?3VN?h#KLAT_sz=&e9HP47KnsWgm3Q#fu z5JQGdx$kWoKspK7Q)8o8ML0K-Uka5jR3sudKx_hqs|sLD+gQ*&yec;OPwJ2w4QPoD z8V~;sN-^E<+I0f$Zf+*GlM7cnUF_vO!fT!^5{4vAyun_hG2pVr6zJVL^j*tpxhSrH zzD<3SCngJV7sL$_erGocs2ya%X^;m<9gS|>b*fH(n}hRn2qg%k(42c9v1|ph4@jCq zu|-^i2MP>8`W%cb~*paaOQE1a?F;1&&(G*T>X6fZ`sXkr{dAO|ca8^_j)V z1Wx_aN;Wx84~$+Rx-%9qeu1nt5}M{pdgGI>x_G!8s1{_BY5V9-dx#8GCm0t!B`M3@ zo;`u+qZt%JtTSrVUw}Q`darG_6jSo)+BZ?{##^Yz2$#?H0jmVR!hGZ0V;~rS0C7!) zBL9rX61#@mdJwdBC#J=4adl%1R;3&#?nI4}!_Mt&XWp2*9U@2O(OOYI5)013h5wr+ z?hIYevmO(7m&w#6muf3aOUh6!$b1Xg6-}F1Zk}`PR%PDv_@zf3P zT8|Q_QOcfN@`QVDo8HnOI4>cy@e6i?>aAV?UGd3V*bylnY(tDck@9@M1DnAL(D z^2NH!LhY2pm+?tSm%*Miup8-cOh;Sbo7%i*Mr5=r2IFhi7>MWeUu2>4j74a>_Po4= zX56^3L74HQSOluEB2nua3l>@;5=hQY;|IyC+4sPs1L$8|_+$Fl#e#D^@uO(rt|ssu zqrTF5H1w2@&B6ERz@vjv_7i;oRNTLX9igi)x>~Bw?}Tt;^*XHFYX8G;2cwNGmFv=E z0jirhEnR+%#Jv(0po( zYWwi6)%>o}P2hGB%H_4VeG5BcS-sz=TV7673-$Z4rKr+jSKxT4(lubrFEv^=q(Fu! zj~s;%!$yIkSOkhXL>*+>qjtKc1H5Y`HWs27G9lqtwrJ(&^=qU16&)$D3a@0~%cBji zLnODxST4+AYb-8=Z%d{)7!w)n%`bUdAsn@f+C-gS<6Kl@B%vKT;mR$?Qk3Pn;9DBz z<_*luWuwL#do-jYoY&q6dBUqab7K`KGR6LF5&G^S`gcSCGIo&x23l)HG+qF@hzQpdUO-J1%T89| zAmv!DH!&bmplvOpLq#wZ*2(owScKttKMqI<<6&9o3*{Q9^V2GVYe0Z;~1OH*PQ=$wTMBHe+aNU0&6RdK={#tvED7_Qjyb_$&cT(c z!d-&iyhmVum+MvwFw<}A+Vj;{tUUQyr)sV>_S0_cy zw5KqRQm6t=Gf!#$X{cO2gW2yVL+~u)RV51Nj=TZyp=t@(W^`^vina*n+x<_`=#@N< zQQPLEj(^S$PZ_}fKN)cLeBl3Y3OIWlaCQqsIP=-YYZ1w<6744mI_fLAbzEURBR3Ts zn{lvZ$*1heZTEx?IPZwS(gZ+0DjIlPWI^e({#ai@Bn6`N0o?YtN$+8xoAeWXNuwkRzsw~ z|M@0CW44qt?&RDKAGvDL-DoWo;R#tgeZzZ*j3+}hAx0|k&Q@StAzIxc#r6$x0nI|p zA(G35;GTf^!hHb|*6t68vS$i_7(*{WS_wV%DhXD?{RsDt9s@Pf-AVPxcME#^o|vYKJO^rri&+rl&gsu zvRsCXdA^hZo)M>{7`#&?F{8piW-|j_Cl6K;9UGbnOJbVRJW60KulS=;1afN*juJhTfvJA;E| z;WDG!A}P4C6*+Jm6?3nFN!9mkQ$*+n+A}7dIH*cKoE0yw|qlZsd_lq!m@4@1x5=I`m+C0?5=!KL0^=ue>_QUb~ zEG+)~%kS4$Vf>XZw`(p;+<&yZErrRKK3^=i?a7-@UF*ZtfBA_gH^Wk!O9dO2T|Ph3 z%7^Kz7sqy~$=toy+#}<`k9rv;k9GjukvoH~l3_g!*AHe>N%f))0000Vo+R)9 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..cfd043dbaaefbe81712f30912f9403405061703d GIT binary patch literal 5796 zcmV;V7F+3ePew8T0RR9102ZVG5&!@I05~`R02V_40RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 b/assets/external/fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..47ce460fa9a827bd143a2cc1d70f6dd4b97352cf GIT binary patch literal 1496 zcmV;}1t1bO#^|f=L^k2vJm! zG68-Z0c>{d3teP$ehh!!R+zFi z-JPU2hiMtv&=% zoF#>G^mfu~?)EXT^#44%xYAf+PnzyRmrfXD`z+1P>BAPo{4DhHA0USWz_LS89mQb? zABjS9#>o$>WHQzW8;R`BAe^NUHzF)3jbtMQNT!1(r-dG4kAs(wv4FO-CDQ4kIzP2~ zoSY#@;vE6a3VVtBUB42^YNV-nntzi@nTPr4(7b z59jed?NA~bd7Y}>KJF3~Ly^xHX1dH0Z&7@&IhOvo9qFePuBDm#m=uoj{)JKSiP}pj z`Dx3gYwO=W+Q&7)OucTu? zKY$Z%c(~@@)6o3`{dGU3wq;7;q*k#MzDcI?Ydq)Gt=;QT+yA6}9BR$ZXuHSB4+E3Y0P{Ga%dcgsuHfaSoh{j~ScdZM zypF1!y;U|kWMy(gm+@FCZgEay8QRxcdXoKpt1l4nBG$_Ad7G+>>zh~*0QK9dIvv2P zXV)#BJhk%JZwCnIR7FaRc^V4>;^gT2r~_Ye``*8aR+RuBe^8TvZ&2cwU+&*Km9eY? zP>CQ=T=q{!b?`5%f030DBK)f4PUk6yfBJ#!T300m)?Uu1{~Q6Ka*yR`d;#=`;uL{^ z(gnCeTHy@)Y)8=Xq9f{gcro!Urp=L=VRGcwqVqkp0xnW7Ehe5Q-t?VF`AqT^D#akN zYL%c=DpQD2tq!9OC0c|aAz~#7v7954Q6g3WRz22oi5MjsRLWN?1*2A-GOTJu)J6wR z+;}0(c#a#N&G~o`mPw0RWlEH)XK{rS!mwB+*E0wasudy;0#wQ#A+e|G5cwvurXSUa zQG~<=noFRFxVG*rtu4Yq6_*A)MA|cs3_Z6_uNe?6IugRY$nmA-hPPu{xcj0>lEfhA z`O8U~6XU&f70FyTPOR#aye2!;(TWr^S7&+7C1qzxyoV*J=n&ThOG&FSFUr+s-0nLs z_AKcfZ0ApggUE9pgI7>)Xd2M literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSV0mf0h.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..022274d4c4af877ac322df360cb0c1856d2931a1 GIT binary patch literal 24792 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∾9# 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1d988a3f4c7e169088a83234773dd3eb346c2196 GIT binary patch literal 22736 zcmV({K+?Z=Pew8T0RR9109eof5&!@I0F*QU09a`N0RR9100000000000000000000 z0000SHa|#KK~gFPU_Vn-K~!WX8UTb=FG3Lr3W1wI`YH>9OaL(N9sxE2Bm;v?1Rw>5 zI0uAl3el^jU`!HS|}lX}_z|0%gKrq%%JpHP5cA&GK38q*Cqz!)Zh z3apH95}Rv5J)>?_K;Z-==Ek~#+>EWbiDH2v&s-r;1M>{N7l_j@J0t7T6f#TVno%yw z4J=Y4{ihNYcGi=>)4lDP>VMu}AEB{Na*wCW`l@=*t?3>FTpS?uO!k`x;gidQ76%9G zOp;Oe8=jwA4Zd{Ygqh6G8V9n8nH1lL{M66LJ>g)1d}kPJ&e5atm-?J zhkwTL-uLM&HM21(53X?kOH?Y%U|QT+W`o}R=d_d)nPm&`3%`IYG)OYV3hqo1AkRfS zwx>On7_+mOVMzX0r+rhBzPLw#Q*pg@v8t9QK(-wjp-AiThCHOx%2@^{hd|X8)!jo= zU0&6CuBv`El;6q_l)!k4c)H_Q;+; zV~`ZmEYSySL-dj*o&M_#O)J@fk2n$!Bdvx&2_AwSExxr~Q$)ZR-*Ah&4&*Z zxRv-7Q4~cQe{ZVR{tplyfN~&Ad$`V&Y>wd!=i0bfnsY^Sb4|5)*yaA+<%)|1BrX8* zxD1*B2x+(k!!2H1@YrKb83^Pc>vI2fqWdcmnyP>4 zFQ{K0Me?FW*aNnlxFcfI*mAv>y@R|05o7>o-3?q6JPaQ8hJ!W~BET^{K^wBm2=qsx z$YI3l;ZYO-J}JyRs?}*uBTcYP>0H()FDj^+B);k;W#!DeWVcX6(=rs{WfagOZD#;$ zu|8AlXnYICq1Ye|E0aHi5rc^GrWv^o2&lvhia`Lb!!`x`x*v|imM^ds`baGrWQ_C=XSFB2=LP9XNzjxUH+vZ4SC%PzHP8(m$Kuy}&Rc z21vRbvyDVFswLeNTv&txlpoLlPUL?J$)t0Ws<-NUt3Ur1kLKd%6cD%ySKvHsk+mno zA!OkP_ujwv-o5Acp4fYO@0rQ=?|YB@|C2jo@!wYWOJA5Q-#_7Jq+knx)Gy$83#smZ;F8QnzgXsM4-lj|a3=)RHBctJ4`BcrI%4 z&^)ItFbjn1q)WPFI*)_zG9dwx6w58O%nH5wi1k}-z)Gu(7&2^)L8CTUYn@HjtGCgl zG2F^4rc>QBd=@Q1%7`rChU*3h9; z>&mESz-6IGV{x6s5>(*4Q&@(EWne`ynMsu6FT;S(qt^oErvMBUFJ?Ocs2|Efa`z_% z4+3-`nsg~u)%>w>kU>I#3DY9O@oV8+5|@}#Q_WJMOA-h0q!0^ZwMq$C2&yp!P?B}P z@=2j4X15s27udgjvj`{xRk)Vg42}kc$VYK}XeQ(MdWFMU+dd|DYIw_WNZfZ={Qg>P z(pa;VGZq{PjM>$55w<85>$GN26xDH@yT7H>HzK!0lB@?0YNLG*)Kfw5L8fQSk-yW? zdK*RC=z}=1`q-Y04rUutp-kgD*M~neG7>ET#ZYW87Frl4qJe6@&I+E_Xaq&Qj-lYY zakSa2)nw5z8X5^!7wa@`8^dL^V8B!O7wBYjz%UNeHM?Z1>}-h;1WeXi5po+HqoFtI zD#-~-qem&XsDR8YYgYy#DX~Tcam>zfHfBlO!D9I4F2+|;7eH(Ua)z!_&V?5df}w>h z;|te>K$xO>e?AGsX9~{gFl-;*V*66(23qLteM )8x2wTy4sm|$UBVF7Wz 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 bGAR@Yh2TTsqnk*C_-*A}-af0}9g0mS;50#L literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSt0mf0h.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..11e6a46a40c7afdee2981511cf98f78a62ef9f2f GIT binary patch literal 10096 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

-KfCKkVgUqz8QxqfO}8iQ0)j$QDq& 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtElOUlYIw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1f1c97fecdbde2d7c29e8dd92b152e5b4f30f17c GIT binary patch literal 7972 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 XMQ{4hxA|M=Cwa5=H!EjyH2?qr_e6{Z literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEluUlYIw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..6f232c38a17ecea6607432d096a52b3c0ce4fb3a GIT binary patch literal 26644 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 literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEm-Ul.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a3e5aef7c93b6334a03923067da54adb1e025619 GIT binary patch literal 24652 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 TS>*;jf81#>o`iG#8mbKdV$}U= literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEmOUlYIw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f73f27d6e1a1f8ae9f9da42274083f7543d85513 GIT binary patch literal 10704 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 CEy7m- literal 0 HcmV?d00001 diff --git a/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2 b/assets/external/fonts.gstatic.com/s/robotomono/v23/L0xdDF4xlVMF-BfR8bXMIjhOsXG-q2oeuFoqFrlnAIe2Imhk1T8rbociImtEn-UlYIw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..135d06e00d05b590861cd226b3bbe781f4940952 GIT binary patch literal 14288 zcmV;>H!sL{Pew8T0RR9105{M85&!@I09+^l05@p>0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/assets/external/unpkg.com/iframe-worker/shim.js b/assets/external/unpkg.com/iframe-worker/shim.js new file mode 100644 index 0000000..5f1e232 --- /dev/null +++ b/assets/external/unpkg.com/iframe-worker/shim.js @@ -0,0 +1 @@ +"use strict";(()=>{function c(s,n){parent.postMessage(s,n||"*")}function d(...s){return s.reduce((n,e)=>n.then(()=>new Promise(r=>{let t=document.createElement("script");t.src=e,t.onload=r,document.body.appendChild(t)})),Promise.resolve())}var o=class extends EventTarget{constructor(e){super();this.url=e;this.m=e=>{e.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:e.data})),this.onmessage&&this.onmessage(e))};this.e=(e,r,t,i,m)=>{if(r===`${this.url}`){let a=new ErrorEvent("error",{message:e,filename:r,lineno:t,colno:i,error:m});this.dispatchEvent(a),this.onerror&&this.onerror(a)}};let r=document.createElement("iframe");r.hidden=!0,document.body.appendChild(this.iframe=r),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Anatomy of a Drove Cluster

+

The following diagram provides a high level overview of a typical Drove cluster. +Drove Cluster +The overall topology consists of the following components:

+
    +
  • An Apache ZooKeeper cluster for state persistence and coordination
  • +
  • A set of controller nodes one of which (the leader) manages the cluster
  • +
  • A set of executor nodes on which the containers actually execute
  • +
  • NGinx + drove-gateway nodes that expose virtual hosts for the leader controller as well as for the vhosts defined for the various applications running on the cluster
  • +
+

Apache ZooKeeper

+

Zookeeper is a central component in a Drove cluster. It is used in the following manner:

+
    +
  • As store for discovery of cluster components like Controller and Executor to each other
  • +
  • For electing the leader controller in the cluster
  • +
  • As storage for Application and Task Specifications
  • +
  • Asynchronous communication channel/transient store for real-time information about controller and executor state in the cluster
  • +
+

Controller

+

The controller service is the brains of a Drove cluster. The role of the controller consists of the following:

+
    +
  • Ensure it has a reasonably up-to-date information about the cluster topology and free/used resources
  • +
  • Track executor status (blacklisted/online/offline etc) and tagging. - Take corrective actions in case some of them become inaccessible for whatever reason
  • +
  • Manages container placement to ensure that application and task containers get placed according to provided placement configuration/spec
  • +
  • Manage NUMA node and core affinity ensuring that instances get deployed optimally on cores and NUMA nodes without stepping on each other
  • +
  • Provide a UI for users to consume data about cluster, applications and tasks
  • +
  • Provide APIs for systems to provision apps, tasks and manage the cluster
  • +
  • Provide event stream for other tools and services to follow what is happening on the cluster
  • +
  • Provide APIs to list container level logs and provide real-time offset based polling of log contents for application and task instances
  • +
  • Implement leader election based HA so that only one controller is active(leader) at a time.
  • +
  • All decisions regarding scheduling, state machine management and recovery are taken only by the leader
  • +
  • Manage the lifecycle of all applications deployed on the cluster.
      +
    • Maintain the required number of application instances as specified during deployment. This means that the controller has to monitor all applications running on all nodes and replace any instances or kill any spurious ones to ensure a constant number of instances on the cluster. The required number of instances is maintained as expected count, the current number of instances is maintained as running count.
    • +
    • Provide a way to adjust the number of instances for this application. This would help in users being able to scale applications up and down as needed.
    • +
    • Provide a way to restart all instances of the application. This would mean the controller would have to orchestrate a continuous string of start-kill operations across instances running on the cluster.
    • +
    • Graceful shutdown/suspension of application across the cluster. This comes as a natural extension of the above and is mostly a scale down operation with the expected count set as zero.
    • +
    +
  • +
  • Manage task lifecycle
      +
    • Maintain task state-machine by scheduling it on executors and ensuring it reaches terminal state
    • +
    • Provide mechanisms to cancel tasks
    • +
    +
  • +
  • Reconcile stale and dead instances for applications and tasks and take corrective measures to ensure steady state if necessary
  • +
  • Application instance migration from blacklisted executors
  • +
  • Send command messages to executors to start and stop instances with retries and failure recovery
  • +
+

Executors

+

Executors are the agents running on the nodes where the containers are deployed. Role of the executors is the following:

+
    +
  • Publish hardware topology of the machine to the controller on startup.
  • +
  • Manage container lifecycle including:
      +
    • Pulling containers from docker repository with optional authentication
    • +
    • Start containers with proper options for pinning containers to specific NUMA nodes and cores as specified by controller
        +
      • Data for an instance is stored as specific docker label values on the containers themselves
      • +
      +
    • +
    • Run HTTP call or shell command based readiness checks to ensure application container is ready to serve traffic based on readiness checks specification in start message
    • +
    • Monitor application container health by running periodic HTTP call or shell command based health checks as specified by controller in start message
    • +
    • Track normal (for tasks) or abnormal (for application instances) container exits.
        +
      • For tasks, the exit code is collected and used to deduce if task succeeded (exit code is 0) or failed (exit code is non-zero)
      • +
      • For application containers, the expectation is for the container to stop only when explicitly requested and hence all exits are considered as failures and handled accordingly
      • +
      +
    • +
    • Stop application containers on request from controller
    • +
    • Run any pre-shutdown hook calls as specified in the application specification before killing container
    • +
    • Cleanup container volumes etc
    • +
    • Cleanup docker images (if local image caching is turned off which is the default behaviour)
    • +
    +
  • +
  • Send regular local node status updates to ZooKeeper every 20 seconds
  • +
  • Send instant updates by making direct HTTP calls to the leader controller when anything changes for any running containers and for every step of the container state machine execution for both task and application instances to allow for faster updates
  • +
  • Recover containers on process restart based on the metadata stored as labels on the running container. This data is reconciled with a snapshot of the expected instances on the node as received from the leader controller at that point in time.
  • +
  • Find and kill any zombie containers that are not supposed to exist on that node. The check is done every 30 seconds.
  • +
  • Provide container log-file listing and offset based content delivery APIs to container
  • +
+

NGinx and Drove-Gateway

+

Almost all of the traffic between service containers is routed via the internal Ranger based service discovery system at PhonePe. However, traffic from the edge as well and between different protected environments are routed using the well-established virtual host (and additionally, in some unusual cases, header) based routing.

+
    +
  • All applications on Drove can specify a Vhost and a port name as endpoint for such routing.
  • +
  • Upstream information for such VHosts or endpoints is available from an API from the leading Drove controller.
  • +
  • This information can be used to configure any load-balancer or tourer or reverse proxy to expose applications running on Drove to the outside world.
  • +
  • +

    We modified an existing project called Nixy so that it gets the upstream information from Drove instead of Marathon. +Nixy plays the following role in a cluster:

    +
  • +
  • +

    Track the leader controller for a Drove cluster by making ping calls to all specified controllers

    +
  • +
  • Provide a special data structure that can be used by a template to expose a vhost that points to the leader controller in a Drove cluster. This can be used for any tools that need to interact with a Drove cluster for deployments, monitoring as well as callback endpoints for OAuth etc etc
  • +
  • Listen to relevant events from the Drove cluster to trigger upstream refresh as necessary
  • +
  • Provide data structures that include the vhost, upstream endpoints (host:port) and metadata (application level tags) that can be used to build templates that generate NGinx configurations to enable progressively complicated routing of calls from downstream to upstreams hosted on Drove clusters. Data structure exposed to templates, groups all upstream host:port tuples by using the vhost. This allows for multiple deployments for the same VHost to exist. This is needed for a variety of situations including online-updates of services.
  • +
  • Supports username/password based authentication and header based (used internally) to Drove clusters.
  • +
  • Support for both NGinx Plus and OSS products. Drove-Nixy can make appropriate api calls to corresponding NGinx plus server to only refresh existing VHost on topology change, as well as affect a full reload when new Vhosts are detected. This ensures that there are no connection drops for critical path applications where NGinx Plus might be used. This also solves the issue of NGinx workers going into a hung state due to frequent reloads on busy clusters like our dev testing environment.
  • +
+
+

Tip

+

The NGinx deployment is standard across all Drove clusters. However, for clusters that receive a lot of traffic using Nginx, the cluster exposing the VHost for Drove itself might be separated from the one exposing the application virtual hosts to allow for easy scalability of the latter. The template for these are configured differently as needed respectively.

+
+

Other components

+

There are a few more components that are used for operational management and observability.

+

Telegraf

+

PhonePe’s internal metric management system uses a HTTP based metric collector. Telegraf is installed on all Drove nodes to collect metric from the metric port (Admin connector on Dropwizard) and push that information to our metric ingestion system. This information is then used to build dashboards as well as by our Anomaly detection and alerting systems.

+

Log Management

+

Drove provides a special logger called drove that can be configured to handle compression rotation and archival of container logs. Such container logs are stored on specialised partitions by application/application-instance-id or by source app name/ task id for application and task instances respectively. PhonePe’s standardised log rotation tools are used to monitor and ship out such logs to our central log management system. The same can be replaced or enhanced by running something like promtail on Drove logs to ship out logs to tools like Grafana Loki.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/cluster/setup/controller.html b/cluster/setup/controller.html new file mode 100644 index 0000000..ee3d902 --- /dev/null +++ b/cluster/setup/controller.html @@ -0,0 +1,2278 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Setting up Controllers - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Setting up Controllers

+

Controllers are the brains of Drove cluster. For HA, at least 2 controllers should be set up.

+

Please note the following behaviour about controllers:

+
    +
  • Only one controller is leader at a time. A leader controller does not relinquish control till the process is stopped or dies
  • +
  • A controller process will die when it loses connectivity to Zookeeper
  • +
  • The process/container for controller should keep restarting till it gets connectivity
  • +
  • All decisions for the cluster are taken by the leader controller only
  • +
  • During maintenance and package upgrades etc, it is better to roll changes out on non-leaders first and then do the leader at the end
  • +
  • The controller process holds all metadata about the cluster, the current states and other information in memory.
      +
    • Some of this information is backed by Zookeeper based storage layer
    • +
    • The other information is recreated dynamically based on updates from executors
    • +
    +
  • +
  • Controllers being down does not affect running containers on executors
  • +
+

Controller Configuration File Reference

+

The Drove Controller is written on the Dropwizard framework. The configuration to the service is set using a YAML file which needs to be injected into the container. A typical controller configuration file will look like the following:

+
server: #(1)!
+  applicationConnectors: #(2)!
+    - type: http
+      port: 4000
+  adminConnectors: #(3)!
+    - type: http
+      port: 4001
+  applicationContextPath: / #(4)!
+  requestLog: #(5)!
+    appenders:
+      - type: console
+        timeZone: ${DROVE_TIMEZONE}
+      - type: file
+        timeZone: ${DROVE_TIMEZONE}
+        currentLogFilename: /logs/drove-controller-access.log
+        archivedLogFilenamePattern: /logs/drove-controller-access.log-%d-%i
+        archivedFileCount: 3
+        maxFileSize: 100MiB
+
+
+logging: #(6)!
+  level: INFO
+  loggers:
+    com.phonepe.drove: ${DROVE_LOG_LEVEL}
+
+  appenders:
+    - type: console #(7)!
+      threshold: ALL
+      timeZone: ${DROVE_TIMEZONE}
+      logFormat: "%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n"
+    - type: file #(8)!
+      threshold: ALL
+      timeZone: ${DROVE_TIMEZONE}
+      currentLogFilename: /logs/drove-controller.log
+      archivedLogFilenamePattern: /logs/drove-controller.log-%d-%i
+      archivedFileCount: 3
+      maxFileSize: 100MiB
+      logFormat: "%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n"
+      archive: true
+
+
+zookeeper: #(9)!
+  connectionString: ${ZK_CONNECTION_STRING}
+
+clusterAuth: #(10)!
+  secrets:
+  - nodeType: CONTROLLER
+    secret: ${DROVE_CONTROLLER_SECRET}
+  - nodeType: EXECUTOR
+    secret: ${DROVE_EXECUTOR_SECRET}
+
+userAuth: #(11)!
+  enabled: true
+  users:
+    - username: admin
+      password: ${DROVE_ADMIN_PASSWORD}
+      role: EXTERNAL_READ_WRITE
+    - username: guest
+      password: ${DROVE_GUEST_PASSWORD}
+      role: EXTERNAL_READ_ONLY
+
+instanceAuth: #(12)!
+  secret: ${DROVE_INSTANCE_AUTH_SECRET}
+
+options: #(13)!
+  maxStaleInstancesCount: 3
+  staleCheckInterval: 1m
+  staleAppAge: 1d
+  staleInstanceAge: 18h
+  staleTaskAge: 1d
+  clusterOpParallelism: 4
+
+
    +
  1. Server listener configuration. See Dropwizard Server Configuration for the different options.
  2. +
  3. Main port configuration. This is where the UI and APIs will be exposed. Check connector configuration docs for details.
  4. +
  5. Admin port. You can take thread dumps, metrics, run healthchecks on the Drove controller on this port.
  6. +
  7. Base path for UI. Keep this as is.
  8. +
  9. Access logs configuration. See requestLog docs.
  10. +
  11. Main logging configuration. See logging docs.
  12. +
  13. Log to console. Useful in docker-compose.
  14. +
  15. Log to rotating files. Useful for running servers.
  16. +
  17. Configure how to connect to Zookeeper See Zookeeper Config for details.
  18. +
  19. Configuration for authentication between nodes in the cluster. Please check intra node auth config for details.
  20. +
  21. Configure user authentication to access the cluster. Please check User auth config for details.
  22. +
  23. Signing secret for JWT to be embedded in application and task instances. Check Instance auth config for details.
  24. +
  25. Special options to configure controller behaviour. See Controller Options for details.
  26. +
+
+

Tip

+

In case you do not want to expose admin apis to outside the host, please set bindHost in the admin connectors section.

+
adminConnectors:
+  - type: http
+    port: 10001
+    bindHost: 127.0.0.1
+
+
+

Zookeeper Connection Configuration

+

The following details can be configured.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Connection StringconnectionStringThe connection string of the form: zkserver:2181,zkserver2:2181...
Data namespacenamespaceThe top level node inside which all Drove data will be scoped. Defaults to drove if not set.
+

Sample

+
zookeeper:
+  connectionString: "192.168.3.10:2181,192.168.3.11:2181,192.168.3.12:2181"
+  namespace: drovetest
+
+

Intra Node Authentication Configuration

+

Communication between controller and executor is protected by a shared-secret based authentication. The following configuration is meant to configure this. This section consists of a list of 2 members:

+
    +
  • Config for controller to talk to executors
  • +
  • Config for executors to talk to controller
  • +
+

Each section consists of the following:

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Node TypenodeTypeType of node in the cluster. Can be CONTROLLER or EXECUTOR
SecretsecretThe actual secret to be passed.
+

Sample +

clusterAuth:
+  secrets:
+  - nodeType: CONTROLLER
+    secret: ControllerSecretValue
+  - nodeType: EXECUTOR
+    secret: ExecutorSecret
+

+
+

Danger

+

The values are passed in the header as is. Please manage the config file ownership to ensure that the files are not world readable.

+
+
+

Tip

+

You can use pwgen -s 32 to generate secure random strings for usage as secrets.

+
+

User Authentication Configuration

+

This section is used to configure user details for human and other systems that need to call Drove APIs or access the Drove UI. This is implemented using basic auth.

+

The configuration consists of:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
EnabledenabledEnable basic auth for the cluster
EncodingencodingThe actual encoding of the password. Can be PLAIN or CRYPT
CachingcachingPolicyCaching policy for the authentication and authorization of the user. Please check CaffeineSpec docs for more details. Set to maximumSize=500, expireAfterAccess=30m by default
List of usersusersA list of users recognized by the system
+

Each entry in the user list consists of:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
User NameusernameThe actual login username
PasswordpasswordThe password for the user. Needs to be set to bcrypt string of the actual password if encoding is set to CRYPT in the parent section.
User RoleroleThe role of the user in the cluster. Can be EXTERNAL_READ_WRITE for users who have both read and write permissions or EXTERNAL_READ_ONLY for users with read-only permissions.
+

Sample +

userAuth:
+  enabled: true
+  encoding: CRYPT
+  users:
+    - username: admin
+      password: "$2y$10$pfGnPkYrJEGzasvVNPjRu.IJldV9TDa0Vh.u1UdimILWDuhvapc2O"
+      role: EXTERNAL_READ_WRITE
+    - username: guest
+      password: "$2y$10$uCJ7WxIvd13C.1oOTs28p.xpJShGiTWuDLY/sGH9JE8nrkSGBFkc6"
+      role: EXTERNAL_READ_ONLY
+    - username: noread
+      password: "$2y$10$8mr/zXL5rMW/s/jlBcgXHu0UvyzfdDDvyc.etfuoR.991sn9UOX/K"
+

+
+

No authentication

+

To configure a cluster without authentication, remove this section entirely.

+
+
+

Operator role

+

If role is not set, the user will be able to access the UI, but will not have access to application logs. This comes in handy to provide access to other teams to explore your deployment topology, but not get access to your logs that might contain sensitive information.

+
+
+

Password Hashing

+

We strongly recommend using bcrypt passwords for authentication. You can use the following command to generate hashed password strings:

+
htpasswd -nbBC 10 <username> <password>|cut -d ':' -f2
+
+
+

Instance Authentication Configuration

+

All application and task instances, get access to an unique JWT that is injected into it by Drove as the environment variable DROVE_APP_INSTANCE_AUTH_TOKEN. This token is signed using a secret. This secret can be configured by setting the secret parameter in the instanceAuth section.

+

Sample +

instanceAuth:
+  secret: RandomSecret
+

+

Controller Options

+

The following options can be set to influence the behavior of the Drove cluster and the controller.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Stale Check IntervalstaleCheckIntervalInterval at which Drove checks for stale application and task metadata for cleanup. Defaults to 1 hour. Expressed in duration.
Stale App AgestaleAppAgeApps in MONITORING state are cleaned up after some time by Drove. This variable can be used to control the max time for which such apps are maintained in the cluster. Defaults to 7 days. Expressed in duration.
Stale App Instances CountmaxStaleInstancesCountMaximum number of application instances metadata for stopped or lost instances to be maintained in the cluster. Defaults to 100.
Stale Instance AgestaleInstanceAgeMaximum age for a stale application instance to be retained. Defaults to 7 days. Expressed in duration.
Stale Task AgestaleTaskAgeMaximum time for which metadata for a finished task is retained on the cluster. Defaults to 2 days. Expressed in duration.
Event Storage DurationmaxEventsStorageDurationMaximum time for which cluster events are retained on the cluster. Defaults to 1 hour. Expressed in duration.
Default Operation TimeoutclusterOpTimeoutTimeout for operations that are initiated by drove itself. For example, instance spin up in case of executor failure, instance migrations etc. Defaults to 5 minutes. Expressed in duration.
Operation threadsclusterOpParallelismSignified the parallelism for operations internal to the cluster. Defaults to: 1. Range: 1-32.
Audited MethodsauditedHttpMethodsDrove prints an audit log with user details when an api is called by an user. Defaults to ["POST", "PUT"].
Allowed mount directoriesallowedMountDirsIf provided, Drove will ensure that application and task spec can mount only the directories mentioned in this set on executor host.
Disable read-only authdisableReadAuthWhen userAuth is enabled, setting this option, will enforce authorization only on write operations.
Disable command line argumentsdisableCmdlArgsWhen set to true, passing command line arguments will be disabled. Default: false (users can pass arguments.
+

Sample +

options:
+  staleCheckInterval: 5m
+  staleAppAge: 2d
+  maxStaleInstancesCount: 20
+  staleInstanceAge: 1d
+  staleTaskAge: 2d
+  maxEventsStorageDuration: 30m
+  clusterOpParallelism: 32
+  allowedMountDirs:
+   - /mnt/scratch
+

+

Stale data cleanup

+

In order to keep internal memory footprint low, reduce the amount of data stored on Zookeeper, and provide a faster experience on the UI,Drove keeps cleaning up data for stale applications, application instances, task instances and cluster events.

+

The retention for such metadata can be controlled using the following config options:

+
    +
  • staleAppAge
  • +
  • maxStaleInstancesCount
  • +
  • staleInstanceAge
  • +
  • staleTaskAge
  • +
  • maxEventsStorageDuration
  • +
+
+

Warning

+

Configuration changes done to these parameters will have direct impact on memory usage by the controller and memory and disk utilization on the Zookeeper cluster.

+
+

Internal Operations

+

Drove may need to create and issue operations on applications and tasks to manage cluster stability, for maintenance and other reasons. The following parameters can be used to control the speed and parallelism of such operations:

+
    +
  • clusterOpTimeout
  • +
  • clusterOpParallelism
  • +
+
+

Tip

+

The default value of 1 for the clusterOpParallelism parameter is generally too low for most clusters. Unless there is a specific problem, it would be advisable to set this to at least 4. If number of instances is quite high for applications (order of tens or hundreds), feel free to set this to 32.

+
+

Increasing clusterOpParallelism will make recovery faster in case of executor failures, but it will increase cpu utilization on the controller by a little bit.

+
+
+ +

The auditedHttpMethods parameter contains a list of all HTTP methods that need to be audited. This means that if the auditedHttpMethods contains POST and PUT, any drove HTTP POST or PUT apis being called will lead to a audit in the controller logs with the details of the user that made the call.

+
+

Warning

+

It would be advisable to not add GET to the list. This is because the UI keeps making calls to GET apis on drove to fetch data to render. These calls are automated and happen every few seconds from the browser. This will blow up controller logs size.

+
+

The allowedMountDirs option whitelists only some directories to be mounted on containers. If this is not provided, containers will be able to mount any directory on the executors.

+
+

Danger

+

It is highly recommended to set allowedMountDirs to a designated directory that containers might want to use as scratch space if needed. Keeping this empty will almost definitely cause security issues in the long run.

+
+

Relevant directories

+

Location for data and logs are as follows:

+
    +
  • /etc/drove/controller/ - Configuration files
  • +
  • /var/log/drove/controller/ - Logs
  • +
+

We shall be volume mounting the config and log directories with the same name.

+
+

Prerequisite Setup

+

If not done already, please complete the prerequisite setup on all machines earmarked for the cluster.

+
+

Setup the config file

+

Create a relevant configuration file in /etc/drove/controller/controller.yml.

+

Sample +

server:
+  applicationConnectors:
+    - type: http
+      port: 10000
+  adminConnectors:
+    - type: http
+      port: 10001
+  requestLog:
+    appenders:
+      - type: file
+        timeZone: IST
+        currentLogFilename: /var/log/drove/controller/drove-controller-access.log
+        archivedLogFilenamePattern: /var/log/drove/controller/drove-controller-access.log-%d-%i
+        archivedFileCount: 3
+        maxFileSize: 100MiB
+
+logging:
+  level: INFO
+  loggers:
+    com.phonepe.drove: INFO
+
+
+  appenders:
+    - type: file
+      threshold: ALL
+      timeZone: IST
+      currentLogFilename: /var/log/drove/controller/drove-controller.log
+      archivedLogFilenamePattern: /var/log/drove/controller/drove-controller.log-%d-%i
+      archivedFileCount: 3
+      maxFileSize: 100MiB
+      logFormat: "%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n"
+
+zookeeper:
+  connectionString: "192.168.56.10:2181"
+
+clusterAuth:
+  secrets:
+  - nodeType: CONTROLLER
+    secret: "0v8XvJrDc7r86ZY1QCByPTDPninI4Xii"
+  - nodeType: EXECUTOR
+    secret: "pOd9sIEXhv0wrGOVc7ebwNvR7twZqyTN"
+
+userAuth:
+  enabled: true
+  encoding: CRYPT
+  users:
+    - username: admin
+      password: "$2y$10$pfGnPkYrJEGzasvVNPjRu.IJldV9TDa0Vh.u1UdimILWDuhvapc2O"
+      role: EXTERNAL_READ_WRITE
+    - username: guest
+      password: "$2y$10$uCJ7WxIvd13C.1oOTs28p.xpJShGiTWuDLY/sGH9JE8nrkSGBFkc6"
+      role: EXTERNAL_READ_ONLY
+
+
+instanceAuth:
+  secret: "bd2SIgz9OMPG2L8wA6zxj21oLVLbuLFC"
+
+options:
+  maxStaleInstancesCount: 3
+  staleCheckInterval: 1m
+  staleAppAge: 2d
+  staleInstanceAge: 1d
+  staleTaskAge: 1d
+  clusterOpParallelism: 4
+  allowedMountDirs:
+   - /dev/null
+

+

Setup required environment variables

+

Environment variables need to run the drove controller are setup in /etc/drove/controller/controller.env.

+
CONFIG_FILE_PATH=/etc/drove/controller/controller.yml
+JAVA_PROCESS_MIN_HEAP=2g
+JAVA_PROCESS_MAX_HEAP=2g
+ZK_CONNECTION_STRING="192.168.3.10:2181"
+JAVA_OPTS="-Xlog:gc:/var/log/drove/controller/gc.log -Xlog:gc:::filecount=3,filesize=10M -Xlog:gc::time,level,tags -XX:+UseNUMA -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -Dfile.encoding=utf-8 -Djute.maxbuffer=0x9fffff"
+
+

Create systemd file

+

Create a systemd file. Put the following in /etc/systemd/system/drove.controller.service:

+
[Unit]
+Description=Drove Controller Service
+After=docker.service
+Requires=docker.service
+
+[Service]
+User=drove
+TimeoutStartSec=0
+Restart=always
+ExecStartPre=-/usr/bin/docker pull ghcr.io/phonepe/drove-controller:latest
+ExecStart=/usr/bin/docker run  \
+    --env-file /etc/drove/controller/controller.env \
+    --volume /etc/drove/controller:/etc/drove/controller:ro \
+    --volume /var/log/drove/controller:/var/log/drove/controller \
+    --publish 10000:10000  \
+    --publish 10001:10001 \
+    --hostname %H \
+    --rm \
+    --name drove.controller \
+    ghcr.io/phonepe/drove-controller:latest
+
+[Install]
+WantedBy=multi-user.target
+
+

Verify the file with the following command: +

systemd-analyze verify drove.controller.service
+

+

Set permissions +

chmod 664 /etc/systemd/system/drove.controller.service
+

+

Start the service on all servers

+

Use the following to start the service:

+
systemctl daemon-reload
+systemctl enable drove.controller
+systemctl start drove.controller
+
+

You can tail the logs at /var/logs/drove/controller/drove-controller.log.

+

The console would be available at http://<ip>:10000 and admin functionality will be available on http://<ip>:10001 according to the above config.

+

Health checks can be performed by running a curl as follows:

+
curl http://localhost:10001/healthcheck
+
+
+

Note

+
    +
  • The healthcheck check api is available on admin port.
  • +
  • HTTP status is 200/OK if things are fine.
  • +
+
+

Once controllers are up, one of them will become the leader. You can check the leader by running the following command:

+
curl http://<ip>:10000/apis/v1/ping
+
+

Only on the leader you should get the following response along with a HTTP status 200/OK: +

{
+    "status":"SUCCESS",
+    "data":"pong",
+    "message":"success"
+}
+

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/cluster/setup/executor-setup.html b/cluster/setup/executor-setup.html new file mode 100644 index 0000000..8543e10 --- /dev/null +++ b/cluster/setup/executor-setup.html @@ -0,0 +1,2443 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Setting up Executor Nodes - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Setting up Executor Nodes

+

We shall setup the executor nodes by setting up the hardware, operating system first and then the executor service itself.

+

Considerations and tuning for hardware and operating system

+

In the following sections we discus some aspects of scheduling, hardware and settings on the OS to ensure good performance.

+

CPU and Memory considerations

+

The executor nodes are the servers that host and run the actual docker containers. Drove will take into consideration the NUMA topology of these machines to optimize the placement for containers to extract the maximum performance. Along with this, Drove will cpuset the containers to the allocated cores in a non overlapping manner, so that the cores allocated to a container are dedicated to it. Memory allocated to a container is pinned as well and selected from the same NUMA node.

+

Needless to say the minimum amount of CPU that can be given to an application or task is 1. Fractional cpu allocation can be achieved in a predictable manner by configuring over provisioning on executor nodes.

+

Over Provisioning of CPU and Memory

+

Drove does not do any kind of burst scaling or overcommitment to ensure application performance remains predictable even under load. Instead, in Drove, there is a feature to make executors appear to have more cores (and memory) than it actually has. This can be used to get more utilization out of executor nodes in clusters that do not need guaranteed performance (for example staging or dev testing clusters). This is achieved by enabling over provisioning.

+

Over provisioning needs to be configured in the executor configuration. It primarily consists of two configs:

+
    +
  • CPU Multiplier - an integral multiplier which will be used to multiply the number of available cores
  • +
  • Memory Multiplier - an integral multiplier which will be used to multiply available memory
  • +
+

VCores (virtual cores) are internal representation of a CPU core on the executor. If over provisioning is disabled, a vcore will correspond to a physical core. If over provisioning is enabled, 1 CPU core will generate cpu multiplier number of v cores. Drove does do cpuset even on containers running on nodes that have over provisioning enabled, however the physical cores that the containers get bound to are chosen at random, albeit from the same NUMA node. cpuset-mem is always done on the same NUMA node as well.

+
+

Mixed clusters

+

In some production clusters you might have applications that are non critical in terms of performance and are unable to utilize a full core. These can be tagged to be spun up on some nodes where over provisioning is enabled. Adopting such a cluster topology will ensure that containers that need high performance run on nodes without over provisioning and the smaller apps (like for example operations consoles etc) are run on separate nodes with over provisioning enabled. Just ensure the latter are tagged properly and during app deployment specify this tag in application spec or task spec.

+
+

Disable NUMA Pinning

+

There is an option to disable memory and core pinning. In this situation, all cores from all NUM nodes show up as being part of one node. cpuset-mems is not called if numa pinning is disabled and therefore you will be leaving some memory performance on the table. We recommend not to dabble with this unless you have tasks and containers that need more than the number of cores available on a single NUMA node. This setting is enabled at executor level by setting disableNUMAPinning: true.

+

Hyper-threading

+

Whether Hyper Threading needs to be enabled or not is a bit dependent on applications deployed and how effectively they can utilize individual CPU cores. For mixed workloads, we recommend Hyper Threading to be enabled on the executor nodes.

+

Isolating container and OS processes

+

Typically we would not want containers to share CPU resources with processes for the operating system, Drove Executor Service as well as Docker engine (if using docker) and so on. While complete isolation would need creating a full scheduler (and passing isolcpus to GRUB parameters), we can get a good middle ground by ensuring such processes utilize only a few CPU cores on the system, and let the Drove executors deploy and pin containers to the rest.

+

This is achieved in two steps:

+
    +
  • Make changes to systemd to use only specific cores
  • +
  • Exclude these cores in the drove executor configuration
  • +
+

Let's say our server has 2 NUMA nodes, each with 40 hyper-threaded cores. We want to reserve the first 2 cores from each CPU to the OS processes. So we reserve cores [0,1,2,3] for the OS processes.

+

The following line in /etc/systemd/system.conf

+
#CPUAffinity=
+
+

needs to be changed to

+
CPUAffinity=0 1 2 3
+
+
+

Tip

+

Reboot the machine for this to take effect.

+
+

The changes can be validated post reboot by running the following command:

+
grep Cpus_allowed_list /proc/1/status
+
+

The expected output should be: +

Cpus_allowed_list:  0-3
+

+
+

Note

+

Refer to this for more details.

+
+

GPU Computation

+

Nvidia based GPU compute can be enabled at executor level by installing relevant drivers. Please follow the setup guide to enable this. Remember to tag these nodes to isolate them from the primary cluster and use tags to deploy apps and tasks that need GPU.

+

Storage consideration

+

On executor nodes the disk might be under pressure if container (re)deployments are frequent or the containers log very heavily. As such, we recommend the logging directory for Drove be mounted on hardware that will be able to handle this load. Similar considerations need to be given to the log and package directory for docker or podman.

+

Executor Configuration Reference

+

The Drove Executor is written on the Dropwizard framework. The configuration to the service is set using a YAML file which needs to be injected into the container. A typical controller configuration file will look like the following:

+
server: #(1)!
+  applicationConnectors: #(2)!
+    - type: http
+      port: 3000
+  adminConnectors: #(3)!
+    - type: http
+      port: 3001
+  applicationContextPath: /
+  requestLog:
+    appenders:
+      - type: console
+        timeZone: ${DROVE_TIMEZONE}
+      - type: file
+        timeZone: ${DROVE_TIMEZONE}
+        currentLogFilename: /logs/drove-executor-access.log
+        archivedLogFilenamePattern: /logs/drove-executor-access.log-%d-%i
+        archivedFileCount: 3
+        maxFileSize: 100MiB
+
+logging:
+  level: INFO
+  loggers:
+    com.phonepe.drove: ${DROVE_LOG_LEVEL}
+
+  appenders: #(4)!
+    - type: console #(5)!
+      threshold: ALL
+      timeZone: ${DROVE_TIMEZONE}
+      logFormat: "%(%-5level) [%date] [%logger{0} - %X{instanceLogId}] %message%n"
+    - type: file #(6)!
+      threshold: ALL
+      timeZone: ${DROVE_TIMEZONE}
+      currentLogFilename: /logs/drove-executor.log
+      archivedLogFilenamePattern: /logs/drove-executor.log-%d-%i
+      archivedFileCount: 3
+      maxFileSize: 100MiB
+      logFormat: "%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n"
+      archive: true
+
+    - type: drove #(7)!
+      logPath: "/logs/applogs/"
+      archivedLogFileSuffix: "%d"
+      archivedFileCount: 3
+      threshold: TRACE
+      timeZone: ${DROVE_TIMEZONE}
+      logFormat: "%(%-5level) | %-23date | %-30logger{0} | %message%n"
+      archive: true
+
+zookeeper: #(8)!
+  connectionString: ${ZK_CONNECTION_STRING}
+
+clusterAuth: #(9)!
+  secrets:
+  - nodeType: CONTROLLER
+    secret: ${DROVE_CONTROLLER_SECRET}
+  - nodeType: EXECUTOR
+    secret: ${DROVE_EXECUTOR_SECRET}
+
+resources: #(10)!
+  osCores: [ 0, 1 ]
+  exposedMemPercentage: 60
+  disableNUMAPinning: ${DROVE_DISABLE_NUMA_PINNING}
+  enableNvidiaGpu: ${DROVE_ENABLE_NVIDIA_GPU}
+
+options: #(11)!
+  cacheImages: true
+  maxOpenFiles: 10_000
+  logBufferSize: 5m
+  cacheFileSize: 10m
+  cacheFileCount: 3
+
+
    +
  1. Server listener configuration. See Dropwizard Server Configuration for the different options.
  2. +
  3. Main port configuration. This is where the UI and APIs will be exposed. Check connector configuration docs for details.
  4. +
  5. Admin port. You can take thread dumps, metrics, run healthchecks on the Drove controller on this port.
  6. +
  7. Logging configuration. See logging docs.
  8. +
  9. Log to console. Useful in docker-compose.
  10. +
  11. Log to rotating files. Useful for running servers.
  12. +
  13. Drove application logger configuration. See drove logger config for details.
  14. +
  15. Configure how to connect to Zookeeper See Zookeeper Config for details.
  16. +
  17. Configuration for authentication between nodes in the cluster. Please check intra node auth config for details.
  18. +
  19. Resource configuration for this node.
  20. +
  21. Options to configure executor behaviour. Check executor options section for details.
  22. +
+
+

Tip

+

In case you do not want to expose admin apis to outside the host, please set bindHost in the admin connectors section.

+
adminConnectors:
+  - type: http
+    port: 10001
+    bindHost: 127.0.0.1
+
+
+

Zookeeper Connection Configuration

+

The following details can be configured.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Connection StringconnectionStringThe connection string of the form: zkserver:2181,zkserver2:2181...
Data namespacenamespaceThe top level node inside which all Drove data will be scoped. Defaults to drove if not set.
+

Sample

+
zookeeper:
+  connectionString: "192.168.3.10:2181,192.168.3.11:2181,192.168.3.12:2181"
+  namespace: drovetest
+
+
+

Note

+

This section is same across the cluster including both controller and executor.

+
+

Intra Node Authentication Configuration

+

Communication between controller and executor is protected by a shared-secret based authentication. The following configuration is meant to configure this. This section consists of a list of 2 members:

+
    +
  • Config for controller to talk to executors
  • +
  • Config for executors to talk to controller
  • +
+

Each section consists of the following:

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Node TypenodeTypeType of node in the cluster. Can be CONTROLLER or EXECUTOR
SecretsecretThe actual secret to be passed.
+

Sample +

clusterAuth:
+  secrets:
+  - nodeType: CONTROLLER
+    secret: ControllerSecretValue
+  - nodeType: EXECUTOR
+    secret: ExecutorSecret
+

+
+

Note

+

This section is same across the cluster including both controller and executor.

+
+

Drove Application Logger Configuration

+

Drove will segregate application and task instance logs in a directory of your choice. The path for such files is set as: +- <application id>/<instance id> for Application Instances +- <sourceAppName>/<task id> for Task Instances

+

The Drove Log Appender is based of LogBack's Sifting Appender.

+

The following configuration options are supported:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
PathlogPathDirectory to host the logs
Archive old logsarchiveWhether to enable log rotation
Archived File SuffixarchivedLogFileSuffixSuffix for archived log files.
Archived File CountarchivedFileCountCount of archived log files. Older files are deleted.
File SizemaxFileSizeSize of current log file after which it is archived and a new file is created. Unit: DataSize.
Total SizetotalSizeCaptotal size after which deletion takes place. Unit: DataSize.
Buffer SizebufferSizeBuffer size for the logger. (Set to 8KB by default). Used if immediateFlush is turned off.
Immediate FlushimmediateFlushFlush logs immediately. Set to true by default (recommended)
+

Sample +

logging:
+  level: INFO
+  ...
+
+  appenders:
+    # Setup appenders for the executor process itself first
+    ...
+
+    - type: drove
+      logPath: "/logs/applogs/"
+      archivedLogFileSuffix: "%d"
+      archivedFileCount: 3
+      threshold: TRACE
+      timeZone: ${DROVE_TIMEZONE}
+      logFormat: "%(%-5level) | %-23date | %-30logger{0} | %message%n"
+      archive: true
+

+

Resource Configuration

+

This section can be used to configure how resources are exposed from an executor to the cluster. We have discussed a few of the considerations that will drive the configuration that is being setup.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
OS CoresosCoresA list of cores reserved for use by operating system processes. See the relevant section for details on the pre-steps needed to achieve this.
Exposed MemoryexposedMemPercentageWhat percentage of the system memory can be used by the containers running on the host collectively. Range: 50-100 integer
NUMA PinningdisableNUMAPinningDisable NUMA and CPU core pinning for containers. Pinning is on by default. (default: false)
Nvidia GPUenableNvidiaGpuEnable GPU support on containers. This setting makes all available Nvidia GPUs on the current executor machine available for any container running on this executor. GPU resources are not discovered on the executor, managed and rationed between containers. Needs to be used in conjunction with tagging (see tags below) to ensure only the applications which require a GPU end up on the executor with GPUs.
TagstagsA set of strings that can be used in TAG placement policy to route application and task instances to this executor.
Over ProvisioningoverProvisioningSetup over provisioning configuration.
+
+

Tagging

+

The current hostname is always added as a tag by default and is handled specially to allow for non-tagged deployments to be routed to this executor. If any tag is specified in the tags config, this node will receive containers only when MATCH_TAG placement is used. Please check relevant sections to specify correct placement policies for applications and tasks.

+
+

Sample +

resources:
+  osCores: [0,1,2,3]
+  exposedMemPercentage: 90
+

+

Over provisioning configuration

+

Drove strives to ensure that containers can run unencumbered on CPU cores allocated to them. This means that the minimum allocation unit possible is 1 for cores. It does not support fractional CPU.

+

However, there are situations where we would want some non-critical applications to run the cluster but not waste CPU. The overProvisioning configuration aims to provide user a way to turn off NUMA pinning on the executor and run more containers than it normally would.

+

To ensure predictability, we do not want pinned and non-pinned containers running on the same host. Hence, an executor host can either be running in pinned mode or in non-pinned mode.

+

To enable more containers than we could usually deploy and to still retain some level of control on how small you want a container to go, we specify multipliers on CPU and memory.

+

Example: +- Let's say your executor server has 40 cores available. If you set cpuMultiplier as 4, this node will now show up as having 160 cores to the controller. +- Let's say your server had 512GB of memory, setting memoryMultiplier to 2 will make drove see it as 1TB.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
EnabledenabledSet this to true to enable over provisioning. Default: false
CPU MultipliercpuMultiplierMultiplier to be applied to enable cpu over provisioning. Default: 1. Range: 1-20
Memory MultipliermemoryMultiplierMultiplier to be applied to enable memory over provisioning. Default: 1. Range: 1-20
+

Sample +

resources:
+  exposedMemPercentage: 90
+  overProvisioning:
+    enabled: true
+    memoryMultiplier: 1
+    cpuMultiplier: 3
+

+
+

Tip

+

This feature was developed to allow us to run our development environments cheaper. In such environments there is not much pressure on CPU or memory, but a large number of containers run as developers can spin up containers for features they are working on. There was no point is wasting a full core on containers that get hit twice a minute or less. On production we tend to err on the side of caution and allocate at least one core even to the most trivial applications as of the time of writing this.

+
+

Executor Options

+

The following options can be set to influence the behavior for the Drove executors.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
HostnamehostnameOverride the hostname that gets exposed to the controller. Make sure this is resolvable.
Cache ImagescacheImagesCache container images. If this is not passed, a container image is removed when a container dies and no other instance is using the image.
Command TimeoutcontainerCommandTimeoutTimeout used by the container engine client when issuing container commands to docker or podman
Container Socket PathdockerSocketPathThe path of socket for docker socket. Comes in handy to configure path for socket when using podman etc.
Max Open FilesmaxOpenFilesOverride the maximum number of file descriptors a container can open. Default: 470,000
Log Buffer SizelogBufferSizeThe size of the buffer the executor uses to read logs from container. Unit DataSize. Range: 1-128MB. Default: 10MB
Cache File SizecacheFileSizeTo limit disk usage, configure fixed size log file cache for containers. Unit: DataSize. Range: 10MB-100GB. Default: 20MB. Compression is always enabled.
Cache File CountcacheFileSizeTo limit disk usage, configure fixed count of log file cache for containers. Unit: integer. Max: 1024. Default: 3
+

Sample +

options:
+  logBufferSize: 20m
+  cacheFileSize: 30m
+  cacheFileCount: 3
+  cacheImages: true
+

+

Relevant directories

+

Location for data and logs are as follows:

+
    +
  • /etc/drove/executor/ - Configuration files
  • +
  • /var/log/drove/executor/ - Executor Logs
  • +
  • /var/log/drove/executor/instance-logs - Application/Task Instance Logs
  • +
+

We shall be volume mounting the config and log directories with the same name.

+
+

Prerequisite Setup

+

If not done already, please complete the prerequisite setup on all machines earmarked for the cluster.

+
+

Setup the config file

+

Create a relevant configuration file in /etc/drove/controller/executor.yml.

+

Sample +

server:
+  applicationConnectors:
+    - type: http
+      port: 11000
+  adminConnectors:
+    - type: http
+      port: 11001
+  requestLog:
+    appenders:
+      - type: file
+        timeZone: IST
+        currentLogFilename: /var/log/drove/executor/drove-executor-access.log
+        archivedLogFilenamePattern: /var/log/drove/executor/drove-executor-access.log-%d-%i
+        archivedFileCount: 3
+        maxFileSize: 100MiB
+
+logging:
+  level: INFO
+  loggers:
+    com.phonepe.drove: INFO
+
+
+  appenders:
+    - type: file
+      threshold: ALL
+      timeZone: IST
+      currentLogFilename: /var/log/drove/executor/drove-executor.log
+      archivedLogFilenamePattern: /var/log/drove/executor/drove-executor.log-%d-%i
+      archivedFileCount: 3
+      maxFileSize: 100MiB
+      logFormat: "%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n"
+    - type: drove
+      logPath: "/var/log/drove/executor/instance-logs"
+      archivedLogFileSuffix: "%d-%i"
+      archivedFileCount: 0
+      maxFileSize: 1GiB
+      threshold: INFO
+      timeZone: IST
+      logFormat: "%(%-5level) | %-23date | %-30logger{0} | %message%n"
+      archive: true
+
+zookeeper:
+  connectionString: "192.168.56.10:2181"
+
+clusterAuth:
+  secrets:
+  - nodeType: CONTROLLER
+    secret: "0v8XvJrDc7r86ZY1QCByPTDPninI4Xii"
+  - nodeType: EXECUTOR
+    secret: "pOd9sIEXhv0wrGOVc7ebwNvR7twZqyTN"
+
+resources:
+  osCores: []
+  exposedMemPercentage: 90
+  disableNUMAPinning: true
+  overProvisioning:
+    enabled: true
+    memoryMultiplier: 10
+    cpuMultiplier: 10
+
+options:
+  cacheImages: true
+  logBufferSize: 20m
+  cacheFileSize: 30m
+  cacheFileCount: 3
+  cacheImages: true
+

+

Setup required environment variables

+

Environment variables need to run the drove controller are setup in /etc/drove/executor/executor.env.

+
CONFIG_FILE_PATH=/etc/drove/executor/executor.yml
+JAVA_PROCESS_MIN_HEAP=1g
+JAVA_PROCESS_MAX_HEAP=1g
+ZK_CONNECTION_STRING="192.168.56.10:2181"
+JAVA_OPTS="-Xlog:gc:/var/log/drove/executor/gc.log -Xlog:gc:::filecount=3,filesize=10M -Xlog:gc::time,level,tags -XX:+UseNUMA -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -Dfile.encoding=utf-8 -Djute.maxbuffer=0x9fffff"
+
+

Create systemd file

+

Create a systemd file. Put the following in /etc/systemd/system/drove.executor.service:

+

[Unit]
+Description=Drove Executor Service
+After=docker.service
+Requires=docker.service
+
+[Service]
+User=drove
+TimeoutStartSec=0
+Restart=always
+ExecStartPre=-/usr/bin/docker pull ghcr.io/phonepe/drove-executor:latest
+ExecStart=/usr/bin/docker run  \
+    --env-file /etc/drove/executor/executor.env \
+    --volume /etc/drove/executor:/etc/drove/executor:ro \
+    --volume /var/log/drove/executor:/var/log/drove/executor \
+    --volume /var/run/docker.sock:/var/run/docker.sock \
+    --publish 11000:11000  \
+    --publish 11001:11001 \
+    --hostname %H \
+    --rm \
+    --name drove.executor \
+    ghcr.io/phonepe/drove-executor:latest
+
+[Install]
+WantedBy=multi-user.target
+
+Verify the file with the following command: +
systemd-analyze verify drove.executor.service
+

+

Set permissions +

chmod 664 /etc/systemd/system/drove.executor.service
+

+

Start the service on all servers

+

Use the following to start the service:

+
systemctl daemon-reload
+systemctl enable drove.executor
+systemctl start drove.executor
+
+

You can tail the logs at /var/logs/drove/executor/drove-executor.log.

+

The executor should now show up on the Drove Console.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/cluster/setup/gateway.html b/cluster/setup/gateway.html new file mode 100644 index 0000000..25def10 --- /dev/null +++ b/cluster/setup/gateway.html @@ -0,0 +1,1920 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Setting up Drove Gateway - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Setting up Drove Gateway

+

The Drove Gateway works as a gateway to expose apps running on a drove cluster to rest of the world.

+

Drove Gateway container uses NGinx and a modified version of Nixy to track drove endpoints. More details about this can be found in the drove-gateway project.

+

Drove Gateway Nixy Configuration Reference

+

The nixy running inside the gateway container is configured using a custom TOML file. This section looks into this file:

+
address = "127.0.0.1"# (1)!
+port = "6000"
+
+
+# Drove Options
+drove = [#(2)!
+  "http://controller1.mydomain:10000",
+   "http://controller1.mydomain:10000"
+   ]
+
+leader_vhost = "drove-staging.mydomain"#(3)!
+event_refresh_interval_sec = 5#(5)!
+user = ""#(6)!
+pass = ""
+access_token = ""#(7)!
+
+# Parameters to control which apps are exposed as VHost
+routing_tag = "externally_exposed"#(4)!
+realm = "api.mydomain,support.mydomain"#(8)!
+realm_suffix = "-external.mydomain"#(9)!
+
+# Nginx related config
+
+nginx_config = "/etc/nginx/nginx.conf"#(10)!
+nginx_template = "/etc/drove/gateway/nginx.tmpl"#(11)!
+nginx_cmd = "nginx"#(12)!
+nginx_ignore_check = true#(13)!
+
+# NGinx plus specific options
+nginxplusapiaddr="127.0.0.1"#(14)!
+nginx_reload_disabled=true#(15)!
+maxfailsupstream = 0#(16)!
+failtimeoutupstream = "1s"
+slowstartupstream = "0s"
+
+
    +
  1. +

    Nixy listener configuration. Endpoint for nixy itself.

    +
  2. +
  3. +

    List of Drove controllers. Add all controller nodes here. Nixy will automatically determine and track the current leader.

    +
    +

    Auto detection is disabled if a single endpoint is specified.

    +
    +
  4. +
  5. +

    Helps create a vhost entry that tracks the leader on the cluster. Use this to expose the Drove endpoint to users. The value for this will be available to the template engine as the LeaderVHost variable.

    +
  6. +
  7. +

    If some special routing behaviour needs to be implemented in the template based on some tag metadata of the deployed apps, set the routing_tag option to set the tag name to be used. The actual value is derived from app instances and exposed to the template engine as the variable: RoutingTag. Optional.

    +
    +

    In this example, the RoutingTag variable will be set to the value specified in the routing_tag tag key specified when deploying the Drove Application. For example, if we want to expose the app we can set it to yes, and filter the VHost to be exposed in NGinx template when RoutingTag == "yes".

    +
    +
  8. +
  9. +

    Drove Gateway/Nixy works on event polling on controller. This is the polling interval. Especially if number of NGinx nodes is high. Default is 2 seconds. Unless cluster is really busy with a high rate of change of containers, this strikes a good balance between apps becoming discoverable vs putting the leader controller under heavy load.

    +
  10. +
  11. +

    user and pass are optional params can be used to set basic auth credentials to the calls made to Drove controllers if basic auth is enabled on the cluster. Leave empty if no basic auth is required.

    +
  12. +
  13. +

    If cluster has some custom header based auth, the following can be used. The contents on this parameter are passed verbatim to the Authorization HTTP header. Leave empty if no token auth is enabled on the cluster.

    +
  14. +
  15. +

    By default drove-gateway will expose all vhost declared in the spec for all drove apps on a cluster (caveat: filtering can be done using RoutingTag as well). If specific vhosts need to be exposed, set the realms parameter to a comma separated list of realms. Optional.

    +
  16. +
  17. +

    Beside perfect vhost matching, Drove Gateway supports suffix based matches as well. A single suffix is supported. Optional.

    +
  18. +
  19. +

    Path to NGinx config.

    +
  20. +
  21. +

    Path to the template file, based on which the template will be generated.

    +
  22. +
  23. +

    NGinx command to use to reload the config. Set this to openresty optionally to use openresty.

    +
  24. +
  25. +

    Ignore calling NGinx command to test the config. Set this to false or delete this line on production. Default: false.

    +
  26. +
  27. +

    If using NGinx plus, set the endpoint to the local server here. If left empty, NGinx plus api based vhost update will be disabled.

    +
  28. +
  29. +

    If specific vhosts are exposed, auto-discovery and updation of config (and NGinx reloads) might not be desired as it will cause connection drops. Set the following parameter to true to disable reloads. Nixy will only update upstreams using the nplus APIs. Default: false.

    +
  30. +
  31. +

    Connection parameters for NGinx plus.

    +
  32. +
+
+

NGinx plus

+

NGinx plus is not shipped with this docker. If you want to use NGinx plus, please build nixy from the source tree here and build your own container.

+
+

Relevant directories

+

Location for data and logs are as follows:

+
    +
  • /etc/drove/gateway/ - Configuration files
  • +
  • /var/log/drove/gateway/ - NGinx Logs
  • +
+

We shall be volume mounting the config and log directories with the same name.

+
+

Prerequisite Setup

+

If not done already, please complete the prerequisite setup on all machines earmarked for the cluster.

+
+

Go through the following steps to run drove-gateway as a service.

+

Create the TOML config for Nixy

+

Sample config file /etc/drove/gateway/gateway.toml:

+
address = "127.0.0.1"
+port = "6000"
+
+
+# Drove Options
+drove = [
+  "http://controller1.mydomain:10000",
+   "http://controller1.mydomain:10000"
+   ]
+
+leader_vhost = "drove-staging.mydomain"
+event_refresh_interval_sec = 5
+user = "guest"
+pass = "guest"
+
+
+# Nginx related config
+nginx_config = "/etc/nginx/nginx.conf"
+nginx_template = "/etc/drove/gateway/nginx.tmpl"
+nginx_cmd = "nginx"
+nginx_ignore_check = true
+
+
+

Replace domain names

+

Please remember to update mydomain to a valid domain you want to use.

+
+

Create template for NGinx

+

Create a NGinx template with the following config in /etc/drove/gateway/nginx.tmpl

+
# Generated by drove-gateway {{datetime}}
+
+user www-data;
+worker_processes auto;
+pid /run/nginx.pid;
+
+events {
+    use epoll;
+    worker_connections 2048;
+    multi_accept on;
+}
+http {
+    server_names_hash_bucket_size  128;
+    add_header X-Proxy {{ .Xproxy }} always;
+    access_log /var/log/nginx/access.log;
+    error_log /var/log/nginx/error.log warn;
+    server_tokens off;
+    client_max_body_size 128m;
+    proxy_buffer_size 128k;
+    proxy_buffers 4 256k;
+    proxy_busy_buffers_size 256k;
+    proxy_redirect off;
+    map $http_upgrade $connection_upgrade {
+        default upgrade;
+        ''      close;
+    }
+    # time out settings
+    proxy_send_timeout 120;
+    proxy_read_timeout 120;
+    send_timeout 120;
+    keepalive_timeout 10;
+
+    server {
+        listen       7000 default_server;
+        server_name  _;
+        # Everything is a 503
+        location / {
+            return 503;
+        }
+    }
+    {{if and .LeaderVHost .Leader.Endpoint}}
+    upstream {{.LeaderVHost}} {
+        server {{.Leader.Host}}:{{.Leader.Port}};
+    }
+    server {
+        listen 7000;
+        server_name {{.LeaderVHost}};
+        location / {
+            proxy_set_header HOST {{.Leader.Host}};
+            proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+            proxy_connect_timeout 30;
+            proxy_http_version 1.1;
+            proxy_set_header Upgrade $http_upgrade;
+            proxy_set_header Connection $connection_upgrade;
+            proxy_pass http://{{.LeaderVHost}};
+        }
+    }
+    {{end}}
+    {{- range $id, $app := .Apps}}
+    upstream {{$app.Vhost}} {
+        {{- range $app.Hosts}}
+        server {{ .Host }}:{{ .Port }};
+        {{- end}}
+    }
+    server {
+        listen 7000;
+        server_name {{$app.Vhost}};
+        location / {
+            proxy_set_header HOST $host;
+            proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+            proxy_connect_timeout 30;
+            proxy_http_version 1.1;
+            proxy_set_header Upgrade $http_upgrade;
+            proxy_set_header Connection $connection_upgrade;
+            proxy_pass http://{{$app.Vhost}};
+        }
+    }
+    {{- end}}
+}
+
+

The above template will do the following:

+
    +
  • Set NGinx port to 7000. This is the port exposed on the Docker container for the gateway. Do not change this.
  • +
  • Sets up error and access logs to /var/log/nginx. Log rotation is setup for this path already.
  • +
  • Set up a Vhost drove-staging.mydomain that will get auto-updated with the current leader of the Drove cluster
  • +
  • Setup automatically updated virtual hosts for all apps on the cluster.
  • +
+

Create environment file

+

We want to configure the drove gateway container using the required environment variables. To do that, put the following in /etc/drove/gateway/gateway.env:

+
CONFIG_FILE_PATH=/etc/drove/gateway/gateway.toml
+TEMPLATE_FILE_PATH=/etc/drove/gateway/nginx.tmpl
+
+

Create systemd file

+

Create a systemd file. Put the following in /etc/systemd/system/drove.gateway.service:

+
[Unit]
+Description=Drove Gateway Service
+After=docker.service
+Requires=docker.service
+
+[Service]
+User=drove
+TimeoutStartSec=0
+Restart=always
+ExecStartPre=-/usr/bin/docker pull ghcr.io/phonepe/drove-gateway:latest
+ExecStart=/usr/bin/docker run  \
+    --env-file /etc/drove/gateway/gateway.env \
+    --volume /etc/drove/gateway:/etc/drove/gateway:ro \
+    --volume /var/log/drove/gateway:/var/log/nginx \
+    --network host \
+    --hostname %H \
+    --rm \
+    --name drove.gateway \
+    ghcr.io/phonepe/drove-gateway:latest
+
+[Install]
+WantedBy=multi-user.target
+
+

Verify the file with the following command: +

systemd-analyze verify drove.gateway.service
+

+

Set permissions +

chmod 664 /etc/systemd/system/drove.gateway.service
+

+

Start the service on all servers

+

Use the following to start the service:

+
systemctl daemon-reload
+systemctl enable drove.gateway
+systemctl start drove.gateway
+
+

Checking Logs

+

You can check logs using: +

journalctl -u drove.gateway -f
+

+

NGinx logs would be available at /var/log/drove/gateway.

+

Log rotation for NGinx

+

The gateway sets up log rotation for the access and errors logs with the following config: +

/var/log/nginx/*.log {
+    rotate 5
+    size 10M
+    dateext
+    dateformat -%Y-%m-%d
+    missingok
+    compress
+    delaycompress
+    sharedscripts
+    notifempty
+    postrotate
+        test -r /var/run/nginx.pid && kill -USR1 `cat /var/run/nginx.pid`
+    endscript
+}
+

+
+

This will rotate both error and access logs when they hit 10MB and keep 5 logs.

+
+

Configure the above if you want and volume mount your config to /etc/logrotate.d/nginx to use different scheme as per your requirements.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/cluster/setup/maintenance.html b/cluster/setup/maintenance.html new file mode 100644 index 0000000..a1543a0 --- /dev/null +++ b/cluster/setup/maintenance.html @@ -0,0 +1,1728 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Maintaining a Drove Cluster - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Maintaining a Drove Cluster

+

There are a couple of constructs built into Drove to allow for easy maintenance.

+
    +
  • Cluster Maintenance Mode
  • +
  • Executor node blacklisting
  • +
+

Maintenance mode

+

Drove supports a maintenance mode to allow for software updates without affecting the containers running on the cluster.

+
+

Danger

+

In maintenance mode, outage detection is turned off and container failure for applications are not acted upon even if detected.

+
+

Engaging maintenance mode

+

Set cluster to maintenance mode.

+

Preconditions +- Cluster must be in the following state: MAINTENANCE

+
+
+
+
drove -c local cluster maintenance-on
+
+
+
+

Sample Request +

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/set' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+

+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "state": "MAINTENANCE",
+        "updated": 1721630351178
+    },
+    "message": "success"
+}
+

+
+
+
+

Disengaging maintenance mode

+

Set cluster to normal mode.

+

Preconditions +- Cluster must be in the following state: MAINTENANCE

+
+
+
+
drove -c local cluster maintenance-off
+
+
+
+

Sample Request

+
curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/unset' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "state": "NORMAL",
+        "updated": 1721630491296
+    },
+    "message": "success"
+}
+

+
+
+
+

Updating drove version across the cluster quickly

+

We recommend the following sequence of steps:

+
    +
  1. Find the leader controller for the cluster using drove ... cluster leader.
  2. +
  3. +

    Update the controller container on the nodes that are not the leader.

    +
    +

    If you are using the systemd file given here, you just need to restart the controller service using systemctl restart drove.controller

    +
    +
  4. +
  5. +

    Set cluster to maintenance mode using drove ... cluster maintenance-on.

    +
  6. +
  7. +

    Update the leader controller.

    +
    +

    If you are using the systemd file given here, you just need to restart the leader controller service: systemctl restart drove.controller

    +
    +
  8. +
  9. +

    Update the executors.

    +
    +

    If you are using the systemd file given here, you just need to restart all executors: systemctl restart drove.executor

    +
    +
  10. +
  11. +

    Take cluster out of maintenance mode: drove ... cluster maintenance-off

    +
  12. +
+

Executor blacklisting

+

In cases where we want to take an executor node out of the cluster for planned maintenance, we need to ensure application instances running on the node are replaced by containers on other nodes and the ones running here are shut down cleanly.

+

This is achieved by blacklisting the node.

+
+

Tip

+

Whenever blacklisting is done, it causes some flux in the application topology due to new container migration from blacklisted to normal nodes. To reduce the number of times this happens, plan to perform multiple operations togeter and blacklist and un-blacklist executors together.

+

Drove will optimize bulk blacklisting related app migrations and will migrate containers together for an app only once rather than once for every node.

+
+
+

Danger

+

Task instances are not migrated out. This is because it is impossible for Drove to know if a task can be migrated or not (i.e. killed and spun up on a new node in any order).

+
+

To blacklist executors do the following:

+
+
+
+
drove -c local executor blacklist dd2cbe76-9f60-3607-b7c1-bfee91c15623 ex1 ex2 
+
+
+
+

Sample Request

+
curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/blacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d&id=ex1&id=ex2' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "failed": [
+            "ex2",
+            "ex1"
+        ],
+        "successful": [
+            "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d"
+        ]
+    },
+    "message": "success"
+}
+

+
+
+
+

To un-blacklist executors do the following:

+
+
+
+
drove -c local executor unblacklist dd2cbe76-9f60-3607-b7c1-bfee91c15623 ex1 ex2 
+
+
+
+

Sample Request

+
curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/unblacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d&id=ex1&id=ex2' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data ''
+
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "failed": [
+            "ex2",
+            "ex1"
+        ],
+        "successful": [
+            "a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d"
+        ]
+    },
+    "message": "success"
+}
+

+
+
+
+
+

Note

+

Drove will not re-evaluate placement of existing Applications in RUNNING state once executors are brought back into rotation.

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/cluster/setup/planning.html b/cluster/setup/planning.html new file mode 100644 index 0000000..0e8b589 --- /dev/null +++ b/cluster/setup/planning.html @@ -0,0 +1,1620 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Planning your cluster - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Planning your cluster

+

Running a drove cluster in production for critical workloads involves planning and preparation on factors like Availability, Scale, Security and Access management. +The following issues should be considered while planning your drove cluster.

+

Criteria for planning

+

The simplest form of a drove cluster would run controller, zookeeper, executor and gateway services all on the same machine while a highly available would separate out all components according to following considerations:

+
    +
  • Availability: Use of a single node in either executor, controller, gateway or zookeeper has a single point of failure. A typical highly available cluster consists of:
      +
    • Separated controller and executor nodes
    • +
    • Multi controller setup - typically minimum 3 controller nodes to provide adequate availability and majority for leader election
    • +
    • Multiple gateway nodes to load balance traffic and provide fault tolerance
    • +
    • Availability of sufficient executor nodes to follow placement policies suitable for high availability for application instances across executors
    • +
    • Multiple node zookeeper cluster to provide adequate availability and quorum. Even number of nodes is not useful. Atleast three zookeeper servers are required.
    • +
    +
  • +
  • Scale: You should size your components as per expected amount of traffic to your instances but also have a plan for expected demand growth and ways to scale your cluster accordingly
  • +
  • Security and Access management: You can use authentication for intra-cluster communication and add encryption for secure communications. Access management can be devolved by creating multiple users with either read or write roles.
  • +
+

Cluster configuration

+

Controllers

+

Controllers will manage the cluster with application instances spread across multiple executors as per different placement policies. Controllers use leader-election to coordinate and will act as a single entity while each executor acts as a single entity that runs many different application instances.

+
    +
  • Multiple controllers: For high availability, there should be atleast three controllers. Drove uses leader election to coordinate across the controllers. If the leader fails, the other controllers would elect a leader and takeover.
  • +
  • Separate Zookeeper service and snapshots: The zookeeper service used for state coordination by controller can either be run on same machines as controller or on separate machines. Zookeeper stores the configuration data for the whole cluster and zookeeper snapshots can be used to back up the data.
  • +
  • Availability zones: You can split the controllers and/or zookeeper nodes across availability zones/data centers to improve resilience.
  • +
  • Transit encryption and certificate Management: Controllers and executors can communicate via secure communications. TLS settings and certificates can be added by modifying applicationConnectors and adminConnectors parameters as per Dropwizard HTTPS connector configuration
  • +
  • Separate Gateways: The drove gateways will route and load balance application traffic across application instances. The gateway service can either be run on same machines as controller or on separate machines.
  • +
  • Resources: The required JVM maximum heap size for drove controller service will increase with the increase in number of executors,applications and application instances in the cluster. The JVM parameters should be reviewed as per your scale.
  • +
+

Zookeeper

+
    +
  • Quorum: For replicated zookeeper, a minimum of three servers are required, and it is recommended that you have an odd number of servers. If you only have two servers, if one of them fails, there are not enough machines to form a majority quorum. Two servers are inherently less stable than a single server, because there are two single points of failure. Refer Running replicated ZooKeeper in ZooKeeper Getting Started Guide
  • +
  • zxid rollover: zxid is the ZooKeeper transaction id and is 64 bit number. The zxid has two parts epoch and a counter which use the high order 32-bits for the epoch and the low order 32-bits for the counter. A zookeeper election is forced when the 32-bit counter rolls over. This will be more frequent as scale increases in your cluster.
  • +
  • JVM parameters and resources: The required JVM maximum heap size for zookeeper will increase with the increase in number of executors,applications and application instances in the cluster. The JVM parameters should be reviewed as per your scale. Refer ZooKeeper Administrator's Guide
  • +
+

Executors

+
    +
  • Containerisation Engine: Drove supports the Docker engine and has experimental support for Podman. Choose your engine as per your security, networking and performance considerations.
  • +
  • Container Networking: The container engine and container networking should be configured as per your requirements. It is recommended to use Port forwarding based container networking if you choose to use Drove Gateway to route application traffic. Container engine settings can be modified to manage DNS and proxy parameters for containers.
  • +
  • Placement policies and availability: Drove supports placement policies to set criteria for replication of instances across executors and avoiding single points of failure. Drove tags can be assigned to executors and placement policy can be used to pin certain applications to specific selected executors if you have any hardware or other considerations.
  • +
  • Scaling: As your cluster scale increases, you can continue adding executors to the cluster. Placement policies should be used to manage availability criteria. Controller and ZooKeeper resource requirements will increase as your executor count increases and should be reviewed accordingly.
  • +
+

Gateways

+
    +
  • Container Networking: It is recommended to use Port forwarding based container networking if you choose to use Drove Gateways to route application traffic
  • +
  • Load balancing: Gateways use Nginx as a web server and can use many different approaches to load balancing among multiple gateway nodes. Some examples include:
      +
    • DNS Load balancing: Multiple gateway IP's can be added as A records to the virtual host domain to let clients use round-robin DNS and split load across gateway nodes
    • +
    • Anycast/Network Load balancing: If any sort of anycast/network load balancing functionality is available in your network, it can be used to split traffic across gateway nodes
    • +
    +
  • +
  • High Availability and Scaling: Many different methods are available to achieve high availability and scale NGINX. Any method can be used by adequately modifying the template used by Gateway to render Nginx configuration.
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/cluster/setup/prerequisites.html b/cluster/setup/prerequisites.html new file mode 100644 index 0000000..90f463a --- /dev/null +++ b/cluster/setup/prerequisites.html @@ -0,0 +1,1523 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Setting up the prerequisites - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Setting up the prerequisites

+

On all machines on the drove cluster, we would want to use the same user and have a consistent storage structure for configuration, logs etc.

+
+

Note

+

All commands o be issues as root. To get to admin/root mode issue the following command:

+
sudo su
+
+
+

Setting up user

+

We shall create an user called drove to be used to run all services and containers and assign the file ownership to this user.

+

adduser --system --group "drove" --home /var/lib/misc --no-create-home > /dev/null
+
+We want to user to be able to run docker containers, so we add the user to the docker group:

+
groupadd docker
+usermod -aG docker drove
+
+

Create directories

+

We shall use the following locations to store configurations, logs etc:

+
    +
  • /etc/drove/... - for configuration
  • +
  • /var/log/drove/.. - for all logs
  • +
+

We go ahead and create these locations and setup the correct permissions:

+
mkdir -p /etc/drove
+chown -R drove.drove /etc/drove
+chmod 700 /etc/drove
+chmod g+s /etc/drove
+
+mkdir -p /var/lib/drove
+chown -R drove.drove /var/lib/drove
+chmod 700 /var/lib/drove
+
+mkdir -p /var/log/drove
+
+
+

Danger

+

Ensure you run the chmod commands to remove read access everyone other than the owner.

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/cluster/setup/units.html b/cluster/setup/units.html new file mode 100644 index 0000000..5621acb --- /dev/null +++ b/cluster/setup/units.html @@ -0,0 +1,1556 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Units Reference - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Units Reference

+

In the configuration files for Drove, we use the Duration and DataSize units to make configuration easier.

+

Data Size

+

Use the following shortcuts to express sizes in human readable form such as 2GB etc:

+
    +
  • B - Bytes
  • +
  • byte - Bytes
  • +
  • Bytes - Bytes
  • +
  • K - Kilobytes
  • +
  • KB - Kilobytes
  • +
  • KiB - Kibibytes
  • +
  • Kilobyte - Kilobytes
  • +
  • kibibyte - Kibibytes
  • +
  • KiloBytes - Kilobytes
  • +
  • kibiBytes - Kibibytes
  • +
  • M - Megabytes
  • +
  • MB - Megabytes
  • +
  • MiB - Mebibytes
  • +
  • megabyte - Megabytes
  • +
  • mebibyte - Mebibytes
  • +
  • megaBytes - Megabytes
  • +
  • mebiBytes - Mebibytes
  • +
  • G - Gigabytes
  • +
  • GB - Gigabytes
  • +
  • GiB - Gibibytes
  • +
  • gigabyte - Gigabytes
  • +
  • gibibyte - Gibibytes
  • +
  • gigaBytes - Gigabytes
  • +
  • gibiBytes - Gibibytes
  • +
  • T - Terabytes
  • +
  • TB - Terabytes
  • +
  • TiB - Tebibytes
  • +
  • terabyte - Terabytes
  • +
  • tebibyte - Tebibytes
  • +
  • teraBytes - Terabytes
  • +
  • tebiBytes - Tebibytes
  • +
  • P - Petabytes
  • +
  • PB - Petabytes
  • +
  • PiB - Pebibytes
  • +
  • petabyte - Petabytes
  • +
  • pebibyte - Pebibytes
  • +
  • petaBytes - Petabytes
  • +
  • pebiBytes - Pebibytes
  • +
+

Duration

+

Time durations in Drove can be expressed in human readable form, for example: 3d can be used to signify 3 days and so on. The list of valid duration unit suffixes are:

+
    +
  • ns - nanoseconds
  • +
  • nanosecond - nanoseconds
  • +
  • nanoseconds - nanoseconds
  • +
  • us - microseconds
  • +
  • microsecond - microseconds
  • +
  • microseconds - microseconds
  • +
  • ms - milliseconds
  • +
  • millisecond - milliseconds
  • +
  • milliseconds - milliseconds
  • +
  • s - seconds
  • +
  • second - seconds
  • +
  • seconds - seconds
  • +
  • m - minutes
  • +
  • min - minutes
  • +
  • mins - minutes
  • +
  • minute - minutes
  • +
  • minutes - minutes
  • +
  • h - hours
  • +
  • hour - hours
  • +
  • hours - hours
  • +
  • d - days
  • +
  • day - days
  • +
  • days - days
  • +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/cluster/setup/zookeeper.html b/cluster/setup/zookeeper.html new file mode 100644 index 0000000..2dceff9 --- /dev/null +++ b/cluster/setup/zookeeper.html @@ -0,0 +1,1913 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Setting Up Zookeeper - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Setting Up Zookeeper

+

We shall be running Zookeeper using the official Docker images. All data volumes etc will be mounted on the host machines.

+

The following ports will be exposed:

+
    +
  • 2181 - This is the main port for ZK clients to connect to the server
  • +
  • 2888 - The port used by Zookeeper for in-cluster communications between peers
  • +
  • 3888 - Port used for internal leader election
  • +
  • 8080 - Admin server port. We are going to turn this off.
  • +
+
+

Danger

+

The ZK admin server does not shut down cleanly from time to time. And is not needed for anything related to Drove. If not needed, you should turn it off.

+
+

We assume the following to be the IP for the 3 zookeeper nodes:

+
    +
  • 192.168.3.10
  • +
  • 192.168.3.11
  • +
  • 192.168.3.12
  • +
+

Relevant directories

+

Location for data and logs are as follows:

+
    +
  • /etc/drove/zk - Configuration files
  • +
  • /var/lib/drove/zk/ - Data and data logs
  • +
  • /var/log/drove/zk - Logs
  • +
+

Important files

+

The zookeeper container stores snapshots, transaction logs and application logs on /data, /datalog and /logs directories respectively. We shall be volume mounting the following:

+
    +
  • /var/lib/drove/zk/data to /data on the container
  • +
  • /var/lib/drove/zk/datalog to /datalog on the container
  • +
  • /var/logs/drove/zk to /logs on the container
  • +
+

Docker will create these directories when container comes up for the first time.

+
+

Tip

+

The zk server id (as set above using the ZOO_MY_ID) can also be set by putting the server number in a file named myid in the /data directory.

+
+
+

Prerequisite Setup

+

If not done already, lease complete the prerequisite setup on all machines earmarked for the cluster.

+
+

Setup configuration files

+

Let's create the config directory:

+
mkdir -p /etc/drove/zk
+
+

We shall be creating 3 different configuration files to configure zookeeper:

+
    +
  • zk.env - Environment variables to be used by zookeeper container
  • +
  • java.env - Setup JVM related options
  • +
  • logbaxk.xml - Logging configuration
  • +
+

Setup environment variables

+

Let us prepare the configuration. Put the following in a file: /etc/drove/zk/zk.env:

+
#(1)!
+ZOO_TICK_TIME=2000
+ZOO_INIT_LIMIT=10
+ZOO_SYNC_LIMIT=5
+ZOO_STANDALONE_ENABLED=false
+ZOO_ADMINSERVER_ENABLED=false
+
+#(2)!
+ZOO_AUTOPURGE_PURGEINTERVAL=12
+ZOO_AUTOPURGE_SNAPRETAINCOUNT=5
+
+#(3)!
+ZOO_MY_ID=1
+ZOO_SERVERS=server.1=192.168.3.10:2888:3888;2181 server.2=192.168.3.11:2888:3888;2181 server.3=192.168.3.12:2888:3888;2181
+
+
    +
  1. This is cluster level configuration to ensure the cluster topology remains stable through minor flaps
  2. +
  3. This will control how much data we retain
  4. +
  5. This section needs to change per server. Each server should have a different ZOO_MY_ID set. And the same numbers get referred to in ZOO_SERVERS section.
  6. +
+
+

Warning

+
    +
  • +

    The ZOO_MY_ID value needs to be different on every server.So it would be:

    +
      +
    • 192.168.3.10 - 1
    • +
    • 192.168.3.11 - 2
    • +
    • 192.168.3.12 - 3
    • +
    +
  • +
  • +

    The format for ZOO_SERVERS is server.id=<address1>:<port1>:<port2>[:role];[<client port address>:]<client port>.

    +
  • +
+
+
+

Info

+

Exhaustive set of options can be found on the Official Docker Page.

+
+

Setup JVM parameters

+

Put the following in /etc/drove/zk/java.env

+
export SERVER_JVMFLAGS='-Djute.maxbuffer=0x9fffff -Xmx4g -Xms4g -Dfile.encoding=utf-8 -XX:+UseG1GC -XX:+UseNUMA -XX:+ExitOnOutOfMemoryError'
+
+
+

Configuring Max Data Size

+

Drove data per node can get a bit on the larger side from time to time depending on your application configuration. To be on the safe side, we need to increase the maximum data size per node. This is achieved by setting the JVM option -Djute.maxbuffer=0x9fffff on all cluster nodes in Drove. This is 10MB (approx). The actual payload doesn't reach anywhere close. However we shall be picking up payload compression in a future version to stop this variable from needing to be set.

+

For the Zookeeper Docker, the environment variable SERVER_JVMFLAGS needs to be set to -Djute.maxbuffer=0x9fffff.

+

Please refer to Zookeeper Advanced Configuration for further properties that can be tuned.

+
+
+

JVM Size

+

We set 4GB JVM heap size for ZK by adding appropriate options in SERVER_JVMFLAGS. Please make sure you have sized your machines to have 10-16GB of RAM at the very least. Tune the JVM size and machine size according to your needs.

+
+

q

+
+

JVMFLAGS environment variable

+

Do not set this variable in zk.env. Couple of reasons:

+
    +
  • This will affect both the zk server as well as the client.
  • +
  • There is an issue and the flag (nor the SERVER_JVMFLAGS) are not used properly by the startup scripts.
  • +
+
+

Configure logging

+

We want to have physical log files on disk for debugging and audits and want the container to be ephemeral to allow for easy updates etc. To achieve this, put the following in /etc/drove/zk/logback.xml:

+
<!--
+ Copyright 2022 The Apache Software Foundation
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Define some default values that can be overridden by system properties
+-->
+<configuration>
+  <!-- Uncomment this if you would like to expose Logback JMX beans -->
+  <!--jmxConfigurator /-->
+
+  <property name="zookeeper.console.threshold" value="INFO" />
+
+  <property name="zookeeper.log.dir" value="/logs" />
+  <property name="zookeeper.log.file" value="zookeeper.log" />
+  <property name="zookeeper.log.threshold" value="INFO" />
+  <property name="zookeeper.log.maxfilesize" value="256MB" />
+  <property name="zookeeper.log.maxbackupindex" value="20" />
+
+  <!--
+    console
+    Add "console" to root logger if you want to use this
+  -->
+  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>
+    </encoder>
+    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+      <level>${zookeeper.console.threshold}</level>
+    </filter>
+  </appender>
+
+  <!--
+    Add ROLLINGFILE to root logger to get log file output
+  -->
+  <appender name="ROLLINGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <File>${zookeeper.log.dir}/${zookeeper.log.file}</File>
+    <encoder>
+      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>
+    </encoder>
+    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+      <level>${zookeeper.log.threshold}</level>
+    </filter>
+    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <maxIndex>${zookeeper.log.maxbackupindex}</maxIndex>
+      <FileNamePattern>${zookeeper.log.dir}/${zookeeper.log.file}.%i</FileNamePattern>
+    </rollingPolicy>
+    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <MaxFileSize>${zookeeper.log.maxfilesize}</MaxFileSize>
+    </triggeringPolicy>
+  </appender>
+
+  <!--
+    Add TRACEFILE to root logger to get log file output
+    Log TRACE level and above messages to a log file
+  -->
+  <!--property name="zookeeper.tracelog.dir" value="${zookeeper.log.dir}" />
+  <property name="zookeeper.tracelog.file" value="zookeeper_trace.log" />
+  <appender name="TRACEFILE" class="ch.qos.logback.core.FileAppender">
+    <File>${zookeeper.tracelog.dir}/${zookeeper.tracelog.file}</File>
+    <encoder>
+      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>
+    </encoder>
+    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+      <level>TRACE</level>
+    </filter>
+  </appender-->
+
+  <!--
+    zk audit logging
+  -->
+  <property name="zookeeper.auditlog.file" value="zookeeper_audit.log" />
+  <property name="zookeeper.auditlog.threshold" value="INFO" />
+  <property name="audit.logger" value="INFO, RFAAUDIT" />
+
+  <appender name="RFAAUDIT" class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <File>${zookeeper.log.dir}/${zookeeper.auditlog.file}</File>
+    <encoder>
+      <pattern>%d{ISO8601} %p %c{2}: %m%n</pattern>
+    </encoder>
+    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+      <level>${zookeeper.auditlog.threshold}</level>
+    </filter>
+    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <maxIndex>10</maxIndex>
+      <FileNamePattern>${zookeeper.log.dir}/${zookeeper.auditlog.file}.%i</FileNamePattern>
+    </rollingPolicy>
+    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <MaxFileSize>10MB</MaxFileSize>
+    </triggeringPolicy>
+  </appender>
+
+  <logger name="org.apache.zookeeper.audit.Slf4jAuditLogger" additivity="false" level="${audit.logger}">
+    <appender-ref ref="RFAAUDIT" />
+  </logger>
+
+  <root level="INFO">
+    <appender-ref ref="CONSOLE" />
+    <appender-ref ref="ROLLINGFILE" />
+  </root>
+</configuration>
+
+
+

Tip

+

This is a customization of the original file from Zookeeper source tree. Please refer to documentation to configure logging.

+
+

Create Systemd File

+

Create a systemd file. Put the following in /etc/systemd/system/drove.zookeeper.service:

+
[Unit]
+Description=Drove Zookeeper Service
+After=docker.service
+Requires=docker.service
+
+[Service]
+User=drove
+TimeoutStartSec=0
+Restart=always
+ExecStartPre=-/usr/bin/docker pull zookeeper:3.8
+ExecStart=/usr/bin/docker run \
+    --env-file /etc/drove/zk/zk.env \
+    --volume /var/lib/drove/zk/data:/data \
+    --volume /var/lib/drove/zk/datalog:/datalog \
+    --volume /var/log/drove/zk:/logs \
+    --volume /etc/drove/zk/logback.xml:/conf/logback.xml \
+    --volume /etc/drove/zk/java.env:/conf/java.env \
+    --publish 2181:2181 \
+    --publish 2888:2888 \
+    --publish 3888:3888 \
+    --rm \
+    --name drove.zookeeper \
+    zookeeper:3.8
+
+[Install]
+WantedBy=multi-user.target
+
+

Verify the file with the following command: +

systemd-analyze verify drove.zookeeper.service
+

+

Set permissions +

chmod 664 /etc/systemd/system/drove.zookeeper.service
+

+

Start the service on all servers

+

Use the following to start the service:

+
systemctl daemon-reload
+systemctl enable drove.zookeeper
+systemctl start drove.zookeeper
+
+

You can check server status using the following:

+
echo srvr | nc localhost 2181
+
+
+

Tip

+

Replace localhost on the above command with the actual ZK server IPs to test remote connectivity.

+
+
+

Note

+

You can access the ZK client from the container using the following command:

+
docker exec -it drove.zookeeper bin/zkCli.sh
+
+

To connect to remote host you can use the following: +

docker exec -it drove.zookeeper bin/zkCli.sh -server <server name or ip>:2181
+

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/extra/cli.html b/extra/cli.html new file mode 100644 index 0000000..df34bfe --- /dev/null +++ b/extra/cli.html @@ -0,0 +1,1414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Drove CLI - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Drove CLI

+

Details for the Drove CLI, including installation and usage can be found in the cli repo.

+

Repo link: https://github.com/PhonePe/drove-cli.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/extra/epoch.html b/extra/epoch.html new file mode 100644 index 0000000..420b4bb --- /dev/null +++ b/extra/epoch.html @@ -0,0 +1,1474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Epoch - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Epoch

+

Epoch is a cron type scheduler that spins up container jobs on Drove.

+

Details for using epoch can be found in the epoch repo.

+

Link for Epoch repo: https://github.com/PhonePe/epoch.

+

Epoch CLI

+

There is a cli client for interaction with epoch. Details for installation and usage can be found in the epoch CLI repo.

+

Link for Epoch CLI repo: https://github.com/phonepe/epoch-cli.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/extra/libraries.html b/extra/libraries.html new file mode 100644 index 0000000..991126f --- /dev/null +++ b/extra/libraries.html @@ -0,0 +1,1993 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Libraries - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Libraries

+

Drove is written in Java. We provide a few libraries that can be used to integrate with a Drove cluster.

+

Setup

+

Setup the drove version +

<properties>
+    <!--other properties-->
+    <drove.version>1.31</drove.version>
+</properties>
+

+
+

Checking the latest version

+

Latest version can be checked at the github packages page here

+
+

All libraries are located in sub packages of the top level package com.phonepe.drove.

+
+

Java Version Compatibility

+

Using Drove libraries would need Java versions 17+.

+
+

Drove Model

+

The model library for the classes used in request and response. It has dependency on jackson and dropwizard-validation.

+

Dependency

+
<dependency>
+    <groupId>com.phonepe.drove</groupId>
+    <artifactId>drove-models</artifactId>
+    <version>${drove.version}</version>
+</dependency>
+
+

Drove Client

+

We provide a client library that can be used to connect to a Drove cluster. The cluster accepts controller endpoints as parameter (among other things) and automatically tracks the leader controller. If a single controller endpoint is provided, this functionality is turned off.

+

Please note that the client does not provide specific functions corresponding to different api calls from the controller, it acts as a simple endpoint discovery mechanism for drove cluster. Please refer to API section for details on individual apis.

+

Transport

+

The transport layer in the client is used to actually make HTTP calls to the Drove server. A new transport can be used by implementing the get(), post(), put() and delete() methods in the DroveHttpTransport interface.

+

By default Drove client uses Java internal HTTP client as a trivial transport implementation. We also provide an Apache Http Components based implementation.

+
+

Tip

+

Do not use the default transport in production. Please use the HTTP Components based transport or your custom ones.

+
+

Dependencies

+
 <dependency>
+    <groupId>com.phonepe.drove</groupId>
+    <artifactId>drove-client</artifactId>
+    <version>${drove.version}</version>
+</dependency>
+<dependency>
+    <groupId>com.phonepe.drove</groupId>
+    <artifactId>drove-client-httpcomponent-transport</artifactId>
+    <version>${drove.version}</version>
+</dependency>
+
+

Sample code

+
public class DroveCluster implements AutoCloseable {
+
+    @Getter
+    private final DroveClient droveClient;
+
+    public DroveCluster() {
+        final var config = new DroveConfig()
+            .setEndpoints(List.of("http://controller1:4000,http://controller2:4000"));
+
+        this.droveClient = new DroveClient(config,
+                                      List.of(new BasicAuthDecorator("guest", "guest")),
+                                           new DroveHttpComponentsTransport(config.getCluster()));
+    }
+
+    @Override
+    public void close() throws Exception {
+        this.droveClient.close();
+    }
+}
+
+
+

RequestDecorator

+

This interface can be implemented to augment requests with special headers like for example Authorization, as well as for other stuff like adding content type etc etc.

+
+

Drove Event Listener

+

This library provides callbacks that can be used to listen and react to events happening on the Drove cluster.

+

Dependencies

+
<!--Include Drove client-->
+<dependency>
+    <groupId>com.phonepe.drove</groupId>
+    <artifactId>drove-events-client</artifactId>
+    <version>${drove.version}</version>
+</dependency>
+
+

Sample Code

+
final var droveClient = ... //build your java transport, client here
+
+//Create and setup your object mapper
+final var mapper = new ObjectMapper();
+mapper.registerModule(new ParameterNamesModule());
+mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
+mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
+
+final var listener = new DroveRemoteEventListener(droveClient, //Create listener
+                                                    mapper,
+                                                    new DroveEventPollingOffsetInMemoryStore(),
+                                                    Duration.ofSeconds(1));
+
+listener.onEventReceived() //Connect signal handlers
+    .connect(events -> {
+        log.info("Remote Events: {}", events);
+    });
+
+listener.start(); //Start listening
+
+
+//Once done close the listener
+listener.close();
+
+
+

Event Types

+

Please check the com.phonepe.drove.models.events package for the different event types and classes.

+
+
+

Event Polling Offset Store

+

The event poller library uses polling to find new events based on an offset. The event polling offset store is used to store and retrieve this offset. The DroveEventPollingOffsetInMemoryStore default store stores this information in-memory. Implement DroveEventPollingOffsetStore to a more permanent storage if you want this to be more permanent.

+
+

Drove Hazelcast Cluster Discovery

+

Drove provides an implementation of the Hazelcast discovery SPI so that containers deployed on a drove cluster can discover each other. This client uses the token injected by drove in the DROVE_APP_INSTANCE_AUTH_TOKEN environment variable to get sibling information from the controller.

+

Dependencies

+
<!--Include Drove client-->
+<!--Include Hazelcast-->
+<dependency>
+    <groupId>com.phonepe.drove</groupId>
+    <artifactId>drove-events-client</artifactId>
+    <version>${drove.version}</version>
+</dependency>
+
+

Sample Code

+
//Setup hazelcast
+Config config = new Config();
+
+// Enable discovery
+config.setProperty("hazelcast.discovery.enabled", "true");
+config.setProperty("hazelcast.discovery.public.ip.enabled", "true");
+config.setProperty("hazelcast.socket.client.bind.any", "true");
+config.setProperty("hazelcast.socket.bind.any", "false");
+
+//Setup networking
+NetworkConfig networkConfig = config.getNetworkConfig();
+networkConfig.getInterfaces().addInterface("0.0.0.0").setEnabled(true);
+networkConfig.setPort(port); //Port is the port exposed on the container for hazelcast clustering
+
+// Setup Drove discovery
+JoinConfig joinConfig = networkConfig.getJoin();
+
+DiscoveryConfig discoveryConfig = joinConfig.getDiscoveryConfig();
+DiscoveryStrategyConfig discoveryStrategyConfig =
+        new DiscoveryStrategyConfig(new DroveDiscoveryStrategyFactory());
+discoveryStrategyConfig.addProperty("drove-endpoint", "http://controller1:4000,http://controller2:4000"); //Controller endpoints
+discoveryStrategyConfig.addProperty("port-name", "hazelcast"); // Name of the hazelcast port defined in Application spec
+discoveryStrategyConfig.addProperty("transport", "com.phonepe.drove.client.transport.httpcomponent.DroveHttpComponentsTransport");
+discoveryStrategyConfig.addProperty("cluster-by-app-name", true); //Cluster container across multiple app versions
+discoveryConfig.addDiscoveryStrategyConfig(discoveryStrategyConfig);
+
+//Create hazelcast node
+val node = Hazelcast.newHazelcastInstance(config);
+
+//Once connected, node.getCluster() will be non null
+
+
+

Peer discovery modes

+

By default the containers will only discover and connect to containers from the same application id. If you need to connect to containers from all versions of the same application please set the cluster-by-app-name property to true as in the above example.

+
+

Drove Apache Ignite Discovery

+

Drove provides an implementation of the apache ignite discovery so that containers deployed on a drove cluster can discover each other. This client uses the token injected by drove in the DROVE_APP_INSTANCE_AUTH_TOKEN environment variable to get sibling information from the controller.

+

Dependencies

+
<!--Include Drove client-->
+<!--Include apache ignite-->
+<dependency>
+    <groupId>com.phonepe.drove</groupId>
+    <artifactId>drove-ignite-discovery</artifactId>
+    <version>${drove.version}</version>
+</dependency>
+
+

Sample Code

+
//Setup ignite
+IgniteConfigProvider igniteConfigProvider = new IgniteConfigProvider();
+
+IgniteConfiguration igniteConfiguration = igniteConfigProvider.provideIgniteConfiguration(DroveIgniteConfig.builder()
+        .communicationPortName("igniteComm") // Communication port name
+        .droveEndpoint("http://controller1:4000,http://controller2:4000") //Controller endpoints
+        .useAppNameForDiscovery(true) //Cluster container across multiple app versions
+        .discoveryPortName("igniteDiscovery") // Discovery port name
+        .build());
+
+// Start ignite
+Ignite ignite = Ignition.start(configuration);
+
+
+

Peer discovery modes

+
+

By default the containers will only discover and connect to containers from the same application id. If you need to connect to containers from all versions of the same application please set the cluster-by-app-name property to true as in the above example.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/extra/nvidia.html b/extra/nvidia.html new file mode 100644 index 0000000..5cda4ad --- /dev/null +++ b/extra/nvidia.html @@ -0,0 +1,1581 @@ + + + + + + + + + + + + + + + + + + + + + + + Setting up Nvidia GPU computation on executor - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Setting up Nvidia GPU computation on executor

+

Prerequisite: Docker version 19.0.3+. Check Docker versions and nvidia for details.

+

Below steps are for ubuntu primarily for other distros check the associated links.

+

Install nvidia drivers on hosts

+

Ubuntu provides packaged drivers for nvidia. +Driver installation Guide

+

Recommended +

ubuntu-drivers list --gpgpu
+ubuntu-drivers install --gpgpu nvidia:535-server
+

+

Alternatively apt can be used, but may require additional steps Manual install +

# Check for the latest stable version 
+apt search nvidia-driver.*server
+apt install -y nvidia-driver-535-server  nvidia-utils-535-server 
+

+

For other distros check Guide

+

Install Nvidia-container-toolkit

+

Add nvidia repo

+

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg   && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list |     sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' |     sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
+
+apt install -y nvidia-container-toolkit
+
+For other distros check guide here

+

Configure docker with nvidia toolkit

+
nvidia-ctk runtime configure --runtime=docker
+
+systemctl restart docker #Restart Docker
+
+

Verify installation

+

On Host +nvidia-smi -l +In docker container +docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi

+

+-----------------------------------------------------------------------------+
+| NVIDIA-SMI 535.86.10    Driver Version: 535.86.10    CUDA Version: 12.2     |
+|-------------------------------+----------------------+----------------------+
+| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
+| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
+|                               |                      |               MIG M. |
+|===============================+======================+======================|
+|   0  Tesla T4            On   | 00000000:00:1E.0 Off |                    0 |
+| N/A   34C    P8     9W /  70W |      0MiB / 15109MiB |      0%      Default |
+|                               |                      |                  N/A |
++-------------------------------+----------------------+----------------------+
+
++-----------------------------------------------------------------------------+
+| Processes:                                                                  |
+|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
+|        ID   ID                                                   Usage      |
+|=============================================================================|
+|  No running processes found                                                 |
++-----------------------------------------------------------------------------+
+
+Verification guide

+

Enable nvidia support on drove

+

Enable Nvidia support in drove-executor.yml and restart drove-executor +

...
+resources:
+  ...
+  enableNvidiaGpu: true
+...
+

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/getting-started.html b/getting-started.html new file mode 100644 index 0000000..b5aa4f5 --- /dev/null +++ b/getting-started.html @@ -0,0 +1,1678 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + +

Getting Started

+

To get a trivial cluster up and running on a machine, the compose file can be used.

+

Update etc hosts to interact wih nginx

+

Add the following lines to /etc/hosts +

127.0.0.1   drove.local
+127.0.0.1   testapp.local
+

+

Download the compose file

+
wget https://raw.githubusercontent.com/PhonePe/drove-orchestrator/master/compose/compose.yaml
+
+

Bringing up a demo cluster

+

cd compose
+docker-compose up
+
+This will start zookeeper,drove controller, executor and nginx/drove-gateway. +The following ports are used:

+
    +
  • Zookeeper - 2181
  • +
  • Executor - 3000
  • +
  • Controller - 4000
  • +
  • Gateway - 7000
  • +
+

Drove credentials would be admin/admin and guest/guest for read-write and read-only permissions respectively.

+

You should be able to access the UI at http://drove.local:7000

+

Install drove-cli

+

Install the CLI for drove +

pip install drove-cli
+

+

Create Client Configuration

+

Put the following in ${HOME}/.drove

+
[local]
+endpoint = http://drove.local:4000
+username = admin
+password = admin
+
+

Deploy an app

+

Get the sample app spec: +

wget https://raw.githubusercontent.com/PhonePe/drove-cli/master/sample/test_app.json
+

+

Now deploy the app. +

drove -c local apps create test_app.json
+

+

Scale the app

+

drove -c local apps scale TEST_APP-1 1 -w
+
+This would expose the app as testapp.local. Endpoint would be: http://testapp.local:7000.

+

You can test the app by running the following commands:

+
curl http://testapp.local:7000/
+curl http://testapp.local:7000/files/drove.txt
+
+

Suspend and destroy the app

+
drove -c local apps scale TEST_APP-1 0 -w
+drove -c local apps destroy TEST_APP-1
+
+

Accessing the code

+

Code is hosted on github.

+

Cloning everything:

+
git clone git@github.com:PhonePe/drove-orchestrator.git
+git submodule init
+git submodule update
+
+ + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/images/app-instance-state-machine.png b/images/app-instance-state-machine.png new file mode 100644 index 0000000000000000000000000000000000000000..28c303926774248848d713898dee576cb68a3581 GIT binary patch literal 79463 zcmeFYbyS>BlRr9x2Y0t1!QEX#a0~7}NC-N(3~mV$+$Bh`;O-U(79hC026u-$mo_f84VVhr`faU0wa@s_LrhXChRUWzdj`kpTbznw+eZ8UO%?2><}^kPx6P z?SUON00050mxhjunyEXbqqBph%|{TWi>D)q669fH2>^J^7o^#^k#M+$JkKyz!zp6) zIJ0NPlUrn73!s8zr&n_3ITks#GdYjpL>(H^s+4qwv%c$nCd1acO+bo zwL8Sx6s~u6V(yd4AI=`YcRQx5?wjsD#znW_wVCs56v(3L%$y2e$@zKr&1J|{^uFYy zTi(gjr?gn|iqWX~9chkMwwqD3yB6kTeHJk`(}U~lojX3%2fqvIDdWP&g_V$97V`|k z?r2d#NZ(DW_~i4&kCC9^QOqd5yObmI$BQaoduj0}+(HUtrLJj>35t~q1ucm1T~gWE zt6%3L`V2nH*d6c!&o_@hcPJcIjKanGFvMaq{={(JZrv^|UkD%RUtB(CXpS0kmF_a& z{CUuwoWVY(Ugjf|d^;0X4AQ=FSj=5K?sG3Fp)+UUTUzzL+lJi}6usSQ5reF(OsITI z>4r$INg#3kxVMO+t!3Xx7i)ifMO)u!Mw@*8O{Dk4ob?HPVbSNvIx#kRr>qOiHA?IE z`1o)LzeB&W%#@VB+;NL0(Pc?Im;pKCbzU8%D{_Nmf0|G{TT)vY(1O1mTl4<>W? z{$;~)M}OM01+MPzJOt#9Qv?>#t zOf3WEvSbdUs0@NFDQTB0T1EIMf~h{T=U1Nb z@vj;8Z8LF?I#k(sPD*0^)LnxY1LvYiGZ`HR(2S?H$;4TPq^+D9=^s`(QK(-%M%Q34 z@gaXk*FhzG-HCm5vNT;QO&e$MR4MnxJ)&x7Nd=@ts-vvy6`B-Iwp9{y_*!%|B-nYqVVQ@h$FtbJg7w%womN;D_5E zkyOs$X^BAI5N$(2qA#c1a3boQ)Np#{sCokp=52QDCT=IDqhVMNa8rRbWZ#aIYKYnz z&^y%_@|X08M9Q|p%5b&5NzoEoy}`1EPmG58>6(7vIQ-~}g&DO`boNB# za-BCxv*|Zq#sG{Eo>98sca*^Oy25uWdX(QeRK+YljDB~{@#V9Ni`l117|iET32()- zsPjRg;4yPmhba;gv{J8tG2N$a&?JsYK~g<#s=YBYEfh~=X~FDrHVZ_+VL2OhEvi{b ztUPJKSNn*sFVIxKA!|(P+G|G7SBvyZ;r&z%MteUw5ZL3jth~S;F_B>pM7H<)a7yos z!>5z~q>XmY_|S~v>y$EL2D8Zum`<|2M44Z6Lz(yfSK?Z(X@GOs!FxyFM(O)+qWXpk$;~)j zT2q)Fi_yj!$4Hr&@Kh-j9kxaUNaDuV-EaDlPMQ<=1{h%-br7ZPw2)6S*Ne&F0=(Qd z1IKWH9$pVUwnx62OLrVgQ}9G2)n3BR#oe)ELoS_Elb*$_m+hWwQjI1$1_4r>pMhmXG-@o?Vap#f zvDHkjENNR8713H_$GwUBqroWm~w=1h0VSXpn^b77i`@=HoQ+K zrPwkcTq;W)bHHU_UeAd^pv@iIjX3okAwbzKB+TI>S(o~;hj5_HMiN;VMqX0q9)p}J zHT*lSl3kG?-OweZUq9c6wPQGGq_Iu;2`F;|hGP^BT)=qtd@{Rv-nf2aas*btTZyQf zPsGANZL6%3CSja~Z_Hi`4_BSBBV~&u6R6o`yyTvLr`{GT z1@+8P3cW{#ck&OfIab4|Ouxz;RqP;q515*a+Of1o6qZnQ3&Jm1R=1(?d-CAI8p>&23i;$h<3pcCWUOWyCO|AQkuUfQ-?qee zsf74Eo!D2iq+FNG`2I<@=)neP)GaQrT`+ls^W~><(}|p~U_?K@@#ASc`izlYjbw^7 zuNL4FiH(kZ7aHjF!{u>a%3Y4~?y_SzhOs2ERs95e!FBaLp^%t?J}hUYCqd=5=!oP@ z-3{!R(PgN?{$mN>5GCUu`YteUj9j%Y!t3j%e6(eshm@A`dcz9;ui3ZF-&`d8wRO#{ zMAa2PFL>E$xsUcTGfLq`!YbEu)BqXk9Jw4R5wgtGZU=M*OlM8=si|pQ}ma51u2>T_*-5baP-eJlM!R_(8 zIVsZZiG<&v+lR~O+Ot!uo=4BITf2Lh=&p!*YIi{$oX9tO|LPsb#WburE^JPvhClkc zR8qa%jYoo|U|S@KXXEgPsXfVD@MbjTOjAMt1pb-}_th&PYj~e4OdP_sXoDMfj>AC^ z@C4B|47DPU$J$=S@9M1^qE4l5LJXrkE?h*}Zmu~JYXW!z{Z(=LJj*^!Sx$yo&m@4e z;VWuzKA~ixS&caYy*{x!fg@bmQoXYl$$Ppf>|syo#;%AjM3`Plvt6^foT?vrz8r@W z;Hl@=SbGF_@<&L^z=l@>0|d24Q?n4i?K=sGe>lPYh*tcb93#uhL5>7IB8~2=V>_{S zf=x9>X6#9kH80F(>Ti(P{CNl*_rSPR#dg@1gF2;M6n17u3vkvZr8cYHuuBmzB}FeY zJAzWvi1=Np3B3*@3pepBAs4Mant3LP4OZSTp1L`Mp6XUSN~%c0h90?oKw@>|oeN_9 z1gBy*j|xyK)B==5x((yO{K^9JN8FI^*FY2R$@1>f>F+S_V%doC`9JpG!b+~sI$C3r z>z%knh);yZM7xe*jgvDPh8cdgKof8Li9zdO$3zxRK*OGr1TYwuVs_mliA#wE>0}`= z*a;2>GF?C>j}ms~X>8?$c6Dzb#%t&v`y(r13e>S~Ni=f&v~csRB7eUplLC&tqS-N{ zjH;t=61%Xt9`Mq5)%CluQBdW~uXn zRodUF2lU|JNgC;U^TJbyx~w7-(}gMG-+r%)wuJgJQ(4igasm>Qal>zadi+)Sb^-e1NA*sHu+aRkPXqFjiWVy|WSxU3-r=t*^cz__@MuNh)HQ74q9p z3)-@SKCIA~HxTa|P;a!{a&EfLdftTlIU=E9zCrOs62&2iqyDm%v~vPc`_ug6+Tkr1 z$tjDqq-6AgV`_oRbW+tVrpeL#0KdH`5r{5D-(TEYmpfd1>~nZPq-iOUh?IfGNh9W4 z49)GvGRov)l;hKq$AS5>kCZHFj#RNII?Kp$A4`I3BWT=)b^I{!SL~(>c>1;Fcm8xn zC#BZPCbEb_Z0?U(p5(~g8c8ZHlyGSk-rSQc$x^J4lq;N5WRWCSmty*?9;;FcdP&j; z>#ZWDXcQ$Nen`_#l6+L;TBX|7{2hP;gww!k3e7|W1A3k93_vzn z%)|Y07vy*$9H%u`W(yhpsfohUKrhAco7IZ(hSv*WW|{PR!4kodn8g>nJESTRxi|7@ zWR2xI*|YsA!j3cTw=e}m1tyacl5Mf?#H7O|T@rdJA!+kh7>#QE@26ieH)sd3!#OKB zCm!`onR~zk5j3oVNuu3@M8+4bNv*Y=ZN!#tQvi2BgWM0-BI%tRv$>jih$JWKH#U^s zwqPBhhFwHL>SCQU7)2=#jQwkR27GrHsaPp~+gk*Ub;WxZSu95NFrz-aVb>8Km3*sd z-VbqTW=4);YGI40zPmXxh>OZUKMzgvBk}AKh6NT*4rqaa5z<{fGBg~p1UKAp4?eZ# z7V}*Uj&w`PWC)(0bGdL4ZDiZe02j#7V=R+md-2m!Tk!+hc6*)L7n7(6U92lLu&9_% z4X@*a$3s!q<^tbtOjlarGIbd(54>5f!4Z3)E(pax_`T`Yg`Ck@GS~R~15T&`UiOU^ zkuHgbsk6<}hYN=(_+q?Wr0zl^-vham1s}^Nc6hKHY{VF`WaYcWfq+NlcVx{Ju8&O( zSJLhF$43AhyFR_T-=qQExls^Z#ub=qRWBY^O3ifmn;LkAUpS5tr@EVaa9(itpS%FH zIQ*d8>CZ7$1BhtuBxB~O4E1$&kI}QBEuwyEqbq+f8lP?~gR`z`t}LNneTmDGkajIH z=I0VHL4*gIsqM8>?}$XE3GX^+*c~= z2+uX@op^R|Khm>%Eox$Z2w~cB3i{T2HrgB#qo~wdq2yC-}T9Y7= zWS)wRGA@i##Oww<-_L{sx^{GQQvLf9g))Fx;*m0>)disS_$+u0c%YjmP?j0yPhflX zNziqCgb(S4d6A3QR;im~?4Ilw)kQC?-0&GJUf74+5>RB|V4_}FC=le8UEB9b*9*ZU z%wNEP+a(Ofc?vl(uF`{umDgdW{s0M<6pR@2C(mTYE zZpPX2q^~(~O+57@3EG%V)JUc19=kY;Fs;VrT>P8Pp|_4 z@83VlCK@&H9+;R#vbcfeO~oH=}dy_GY zYVUeupwE0p#ROO6r$`Rc3KR86xY{5VekIwL+UH%2&p?%q@zz#+Z^a!L151lKZOV|e z%Jq5MH@qbwYjbM_sck|TD^B~K__h95g0Vu_oE7KBQbI=2^*5meamzRepEV2nw^r|K zcm+X;b#?NdZwpqnv79JH{lzkCUe^(8Xi$TYJ-H7Me)s(G-*9&xpBpA6SiwT`px`TY z(DWV>YdiAcy}Kr&=pg5dW5-;xBJKPucDwZM!2t~#`O;q$2%%)9*0h< zNSH_Y_hU`~qD_KpmV!(tzpSKc_)il$>_n^C$3K7qeB~dPf!>DqlQNmAN`?!uy95mq z#ju|bT^oQVbn;jkps(}cKAdJjE<6-nBCibg>B?sKCvBy^zH4fHRqj_;_g#(U0&_t8 zz2M>xc0jq%lG&yVo*!E@B;R+soRi zSUy2;YM)o(YPu$VzrM$;!Fd+^8jAk7itRYEm0Bc~PgbP#Jy7|f>NI4J?@UF;VbE7L zllHKe%`;Btuf4FLHrfk2Y#RAA+S~TH`pO2?OtIqGz26F8l9qo z*&CmSX`&dnbiQT;Et-&vP9N)!1Wh1RYYGCydH*WF`vU_i^&9tmMA{|E8m}1I2*aOj zPxIQN9S1);ZXUB&WJ#9q3=W?GuQZ+;1gE1Ephfdwo40RO<=(#ik2*QDI-cPhD-A&c0Up13CT;6Gt5%;TP5uTnad(#C<8aE9KYjmJ(S657LNzmt-a`f^* z${XPN!D{|473~unLv8r!Y9qz*k;u_9P%>|ryVMqD?4l%9{ztE(n&F5+7xDg+73 zBn~<`IgjWH@0%Mhf-T+UohMS?4)>^#cvjhm ze%IbDz7hB^_$(sciDJ)n_9~rSlY$`K-;?2m5!5B$)Z4!}>29_5>N3sf>}NfMe9dUD z9(GNmmNmgr_(CZufkvC~{#}ArCN2uYe2n)^7+HrDYls0YyEAhR3|jRD;&af-lHP5x z93KNNVzcqbdvjc*1DIdUc77BKZs%|(VzAm?3&u78uI&7o1-(tA-4z7Z@`bc&M1vzh zkV@#s!RXCGhRyTNdm>+Te8NU}ubIVVy?cIN!1$!*83I2)Tpw~m+tKNfiIu~1md)ig ztm%n!j^+M68uUJc*amuUp`)ZIXzpOgYHHzN24eNFbA;Y&006>b9*(BwwjdWuGmw>y zy$IEDO9vIDjfDu6Hjfg!lH*&DwT-NoGf3S_S;O4R)?C1XN=y`4*h3J?zz*bMO6g(u z(H<=5Awu<+uORgEOEVi4-|xi>bXi=!F%OIjaqnkEIzO7cYpNpT(SC zfSZMz56Z`kkDH%`!<^mBoSk2QUBHa{-&m+P+d#F_^y9xv^}@;m%8H8<1mfW5GGj5b zfb!$EV7Fi~1)1}+@Nk=Q^IP(BaPV?-{bglgE-3BbY-bAHP8&N@D-fHbz18227r+H2 zROLjdI9b{Mt3~yrsf#6afC!bMjlHYKe|2cs*n!ktOkdFC;NuYBV&~!I5#V9x;bQ0i zFCs0FGZ?CgFFZNeS-Jk<`?4*9(8WN3HGR=jD1*NvplcC)>kKk=ad6gfaQG-f^&%4G z%gDd#O)31(rpVfWp%k7k8vieyR|h%$^WQ%v@X_XP7bWFiwG}iq|7Q}gsT;`R??ljX z|8$vKo7!7}p!WSQh5C7e{%!SasRYI-3!!PvHhpJ`WI#|F#f;z z{A(}%Uz`ET`hO?+AL0AIaQ!b_|04wcN5ucDUH=Q${|JHq5%K?O*Z*U1A^%sx1G0xk zLGI9W#y0=A5;PS;FjJ6`0zAL`{0H6fONl9pU%pWYdduV7r{XD%+RYaowiaai%k@hK&D)7@R2rpzRcJXVT z?Tn%8R5qERtD)ggd1*O)c(6sF`OidGT|8VSR>mAP7F^&St$5r(#tOwwXzHu4VfdmI zJE>ONS3*0>3FmDqVD#|+zdrq{YZ6p~$@v5?@jcDA>t=dwqN{hps} z<>lo+{Q09{zqhvspC=PDq9OMe3ylmD&@I)eN2`{RQC9gbgoYG-W_e|#I6puC+fjVb z`f}-4HB2n%Q>-I_7Z5JHl|TGF=q)9tPJDX0zCP`1Dk>`PG#Df*s>B4&8g%qEM;!?& zDD@l@3?t0TFz7Q!4v#7I&tuVoEB==o7FO(`8bIP-&GiaExPNG|=2-tfAr5?zZtmlQ zhsW*Kq^GX#mrS&!=BB+?;S~1Qu4Lvp>U4yha33aRhmG4wv%HW+p9tmUSI6GICJjYl za~WFk#^{R-U^QGVDOJ1nyk4qwe7y*c5h_gOT5G@0@dVQ|yWD5813!Jru~gb!m^mad z8NhCQbhDlsJS?0He#P!pyHLS#)Hq{=qLlt-nurq)2I#T=s=?Ub^L#Y`arJvmj&X;S zfT0hEfg@1(k=y5%7L^!I#Ea(2bwUXkQ9NsJJgEW&TMDleBNPH?)lr znVyeC<$Ej?Yue(nYTjct#J=}A>H6WxYxlvf&Fcu9DL4&zYSO18SNRTpyo|6DyQOh8 zvJnuu$w_a&G2>e*NvtE`rKz_a|1mEeg*{PYMK_qQ#iISDD<>U2#HHhNcz8cVNr_NO z%0j{K_H(H;0YOXQ6D{pggim#{k$f;ZKC^!)+FB3B+JeK<*$`{cyU(l5`zCXoH>WZw zT!_*9Fi*EC9$R?HugB_`9`2ZYP9)4$D%!TQ7OkutZZ#lNj17msWm*)2Dmp?(MZrYy ze)syfD?3AMYvnCD%49be>hA4B$t}mVAMiZLf0z|72%n=WW;TPju6i&|#@^~1i@Fps z#VsYmO!BF_kt<{n@REMiq)aX7z{}dz}D<3LWnag;bbg?jP zsTX#KqLpv_@IDS9ad08Sw4dL6PGr~_RorV`H@k-Pzji!oI=Nanl6&Y5Iok8{oI@A z^FD4Gc@vL^*W_-FeNGOUh5rx-i@ERt=Ec|L0=l1U&Nc^H&qst9&P%i_Mk464`sb{& zqc}p{mp#J<^lfV9^JFka1a?Tz&d*bM?TD>1UGc3locgx6Vnpxg-@SXc=(&@Li-*VY zaketykc*R(e?6SKaWxR7NV60lK;d%&#CfBbV>OzU;q|9Dl3XM$mCNcZedQrkU0DEh zUXLMqL#bJ!v6iH7KPT*PwAd6$!WZ2H-ogIdTV&KGm?`L*tCB5hVQKj%(BW5wao00B z!+E?q8F3cUVVJlcUdprajoXi;h3Sv4?7AKMIU+pw@jD}TOa};5)sXg5$7+R0d2man zgQmc%6W%$})Vog}f1cWX;F5gd$({jB?IE`~tBw0#-}BM4`rC|l21Z~m0J=ULk;b$v z9}<}O%>EJ++B5L;K-q3buzjSUU_c^QDMRFh-2Lz*?Ke~$FmKPdkJ>JZy{pGPS@dYO zLwZ>K+1;3#jSd*+^ie384R-HDtbfCVVz{5TKQBjxp7av1dQ&kmxu$QP3*2rVW~=O7 zh+H>XVl=F@Z-A~3YFJS=w$lyAD5-mXiLE1Di`wa#Ee)}`&yhSoR@hp;s75t~I!>tF zY^@Crlh*f6!nbW+mqmsxyq^i!wx_g>x7x0kVRLeF{PCEyR14(s{!Eo7pOvgVQ@24L zy|ljTbdfl;c9m$B@*ew5m1ur9Xu<~AvOpP`9M0FJH>^~e!cstPUi;k+8Pn6#157sh zqQ2`?1u8vF)Vl6ZSDE!98~I*pPM7I7c&;#Mmn#ZP*V;^Y98~>G5%K1=Tc|J4Z?Lzp zuweH*v&ix}lUo;>=({#iS3`nV$jL`4;d7}7>}6-7X!jf}!&z9Jt~&i8*hO4nEQf^O2g_g93R*@^K@_ zx=hG&*sogf(y1YPKJ0C&k!lS*a|Rs6O5&_c7xru=H;2fhK*}B5#~Zw~D}T$vo={p= zRgR62A6_KMEp{DVFzXN*Fz?YeUb#F=VJ;8E-?S|~3nwIzD;`p*bqCsVC+)bh@;LsW z!_QHyNZhloVzPdz+NgKE+CEm2ND^Hs6LS`XSWu$nstYL7Z~2Nw;nDf)c&v+uB}Ih9 z$cN*WpmYcq(72oXvf+i0?J^EUj9aduujtN(% zj)M%W0PybD<{fB^W6@EF8cgQ+r?}90*>eYYayesseo!-dwv}w`JgsYGXH*C^cNc~V zJG;2lN);U)F;G`rgnGC3`*{8*yTd#FpVhj@t47#XCdP7y@0 z9O@v`eaiQ?hRak74&MhLb35a5YE>S$yiO`RG<)`tq#STT>^L890#lwVc8Z|gwbBSv zGNoYXx8##dt4fP9azkgqs|-i9+<|Pl)dVh2z0h!5lH}`H>Lg?1vo&RP)z7ckD4mX?-bf3igLb4B~z zr@e{7WT;6>0If7@@WYP*cL zHbi6fiXYtWU;76bKWwxAdR(3CzQ);S&PYL0OQ5Sswbf$ZlO~9qZA2aMK=6{TMcmXzWtbE=_7)34mRK=+_0R(!1*Rgxc#$hk7fe0CuvE(_kY>_o}g38#_Tb`Gqao zLrD?egT*`1&^%Xsk6OXtwXz0iXg=e)fu~Iqo!Cm&_y%Lf_;`D!OdldJRdM`H;oiMS z3oQ#W|I2ooxvJuP1Wbg#leNDr6Xgs{TzQ(lY^aO^)Ihhazx^B3B2ON#SJYKiJDXki zd9^oV1h!KWTyuZZ|F-4^EmxZMSSMVl?9wJ1boa+n4~&i$Lj9 zdm{5Y9b4O}azk&C#>hx<69O}<(w*koCnYA6TNmvv`^h@R+HEwQ)wBlaaTj8n^Hk+% zZ8f7vNgkA@)G$n0q(@L<(d_a#`zCqNxD@aEZI@;{S9;SEuw+`{LPB-=l&>W+Y-?f> z#Q+?RJ|i9!tzX_@8expZNav9-Mdg|~4(R64?tCT>s(3=6TW&Jna+}1w*|wRHk(!vO z7e*ur8~zq?5WF>ExZSl4wmC}EItcI{CFEMs(_c29uqb)(DcvR8&oubM}2N)~oyT=Y5{^VO>2z)(#nQgjAAN|44ZwRUM~cmiLiUMK!qx zC^ve4+tz6p^0$0QNYO zyPnq*5V=1{TFCIWzr7dSqnTNJ`;tEbg9oj)hf{|y!T>}4{W&r*%n2r6W zq%Pz=#5fTF)g%^y!`g`yekZEuyO|ER-PviCLY{#(x8yvfbY7Re@sLr``^Z-~Z}N>S zo+EVLtF^2as~2+8+O%F6wRs)J0wig$IquifXf?^p>gwq+k%UzZ3?8J|2@R?gx`Lpf z{%MLBbeSL@+xs@@T0((LRA)Lj!`caTW!DxbpLGV5y&&Jl=z@D(3Y4mVbsc9lY3BwE<^#j zYe2DNzH|IJSa$by3{q*^e)@~6cnIdHA}20?`pr;bk9!{Wgp%@70iZj2(R*%g?w5b9 zGvtUfieq2{I4zr2j+}sRiD+x5nN!a3?Hk88bk)+z%F6m3&$E5_oW<` z?Hra$!sNz8NO1VQ8>j1WadBI}tz8~jm(R{cMu!mX%PPgY1M*vA@&2apz+m6CMeyiW zB-coe+u^*vVWIiY=!HXAV2=CovSOxSA{1CmR0KZlqXx?X>}$A2$m89@()EjNUF=Oj z-GqDPbllqYgCX&33ZDb{v|c2~#oW*wOFO#X03OX?=^hgTP(zmK=l zY!_FS65FioJoa@$>0~*P-S;Lsc`25lXY|>r>kA`Ke92anVe0qC0UoQOxUG?bIigB4 zL7ytCu$rOKQR7Ow>y-|_XX)PF-nuA`Fk*9JpsW9#xVEjGow=f~tsRcJq8B^i_hV0^ zD6-i@Y4c$P8yg$H;ei2$lO0VU0?ecE=si9DXG4-G>5d5phkp_{Q?^p0wmaS+M4>#r zx(^0kdj+j0-6%iIs_f9SbyQU=1=QDm;HWGfwbxWv?@knAm=B`DPd7T5>(yF!jfy=F z?^kpvA1}8J3=Wz>qf2JJ+9EZh8Xe|odg7?^@3YxS9~RKb^KtJO9Hksm?gbS+kw!eG zMeH2h-aOo}@eN8Ow-~M$Fwcy{jt!92`07piYS8}T zE>`-e5Nui&x>%I^TXDB1u^Fs`26@&_uRnYzg6$zjUyA4S{4+dEK+O5s8ASC}1l0e- zAl=8tQp<2UZ}um6{v`Kio-qLb!rym0T1<@*drCPTBEtXJ`w7p$b1RYIe7??B=K5&K zeOFjnU<+6D@j`u@_WA)08hQoOWqAa;ulOcIQ@ilU$jD!hj2fj{QPlE3=_+U#g*_^n zqMOE}C@Vkk4twJ1g)O!wuC&}b7_SRrEH-xY5b@{3)-p^qnVr`>Y-2iEyG>8WXx5RF zE|WmBC!Ek`Wh}lj7nRGJG`l%%*v5|`8x8?%6rrQXVGd7g+YWkr+BeU;(+nyt60BTi zJ<1kCGj_+-ln}yX!H%A(@<@E&&B1~XWDUl)hGSb}I2R~yF5Q0#t~x(!>UlCFY!jL< zS5CKUtE!&tH9BqZ9%%JNk*b2aAWqOCgG98GaC5y z>rtnG7MkoJQyu`Lh&&*j%GPu1g; zvWobr;WG96{vWNisJlKw4>!CYbx`YnP;j0SX-62{71-9R*3fbb&e>7WpHt6W)JlN^ ztMjlX?T+uzi`LvHJr7)2g;tXX%6t0sWW?L9#_qve6M!?V4}&trj;W0`u8jIx^d?-nY$w2{B&h<;QH>$t*^-J5BIV- zcq`F;POc;$^j;E!!cUjcZg~0Dv)DdJ~gYj93MJd zXB}PBtoUKX2&+9+i#=h}uJ+9_{cok+M1R>B)>sNNNeEw+&SW9 zLd3KjUB2U(DR%`1S9GxYWp+H_8F|99Hm>@!-O18$zDk*Fa9A;i4e0EF>d<}Aj;Hp` z`8((|^$a;wm{_sC>*ETBHL063b5<>&kPvoD)XDNgPRuOtautd&`Q+08MKbetC6Ke1wD`+-(W{vaQ7`DwQhPYpQzF&6sM~}S-+H|V_}E(D|$ZR zIo@tLj?10w6|(!@EsKO*xDX0X!t*4tgyxb$WuCtoWw;|tB}*#>p3;*OpR?ry&n z)m}>07SEhcR2dMj^~+A!2oSY&<8=TmjuJsA#>%6)=p#3&o6ev8CHIClCBzP85i8|_ zR#fPD>I;E9CHAG<`@e#o)V{9ZPk&t#bc3Ji>HV`I@~ug1?&p3FzeNs@XR)FM5bmx* zN;D1QLznENRaGW4OtD0^3Cys+)=q!DQa)cRt0qjvuE`sFMYeJVErX+XN8t5{+xYNdQC^i@wge!Isr8Kqro@4 zQE56>xh4T7a&~rxkO{h2$3&6vwTpOP9dy9X>}}&hcSb}LDr#Hl2hC(n)sCm6p}rXR zp-^;|^BG3KjM08l+a9#0^bGW`yX;ON0uiCxl%^n>ejfLemef#00A%%4MgsT`; zjRYOHMJHBjs3*c`9^!_c8m?732a>jS?oga3qL`ql`~{`0)K+`uy_sj0*I?Tqc)#pO zlhKocZ*kQ~f1()2W4V5GzmH@|f%mg?o^5qa%_v`=4>o$(&d%0Wlxi__>q%P^0-*I{ zD*eRl><$3`ir#(Yj%Q&p6fh}tBV@{#yoL82RjiFTVgKmJi2XNg=n4h+8D476R10_! zQf~k;#O7y`UscoOp+h!T^8iqk!w6A;4}UL#Vh!V< z=Jfi+2r8D!i&$#6|E#2iSw!Sfi!viKbLsux_NITef4tcJ&E)@#0tYjSM#8K3tPK@c z!>nuY>|G>BY4@5Vnc?{3p!^bDH5(jEz!W2VY#SYR#EY$uK})FU2+}1lKr$A}0 z)K(Jo7<2REkAd168#FlMCPaM#-qe&whKI}2K0K?vqoh(3*xKBj)L`gePDn~}vyzh= zD9qXsp@1errGEt=aCM`Bf{AAa4QRk+b+?^{jA&4d2W9?3Sgm(5v8lknZh>A1GTqQnc}J$P=L=%pkP{;MY<0|>B|jqU*5cfkpcu47 zZ9HIjjQUYvkU9erM*aLp5a&eRBN8TD{6RbTFnU$y&w9s!6C3m+zAr`L?s9)d;~jLf za{-c|3pmF-3VLgJW`O;9GTP#RJcMNR!Fihx!3eheG-d+fhLo^;eT9t&_6er;!niHq z*x%gE)R+zktC3IT%pNX9H5A1`T~^mn#Qn0bh&-mUpa8eVVSlq#a!MS2SpFsBdH_Fh zBP{({@-=Kb!W$S`NC6n=Nb1OUQkZXkf%vqAh$jC?TP!(`bwk2>ZYshKmxA^{kWA); zUx`lm$^hO1n7qzOBzh6E@T+1IvP;3XipT|l2?Y|sX!?W1DRO`(>_-E4Se@Lf4NvZ# z$h*MreIij}ge>q!3TaPdCm5^Te$9A}pNy|4Xv$`~I~k zV**N80)vu6w(KGb6)b@bAdiQvP79@|KXgYPC`G3O1z~nvN z{_-D?lZaG?_^-b#6@y~MHCJs6C<`^z4ZvUiJYAGNT^0wS z;F4_^cen>EJlejda_)?f&Qtha=h> zPE(&f-;WD+-0U-V@=P?6gaZuYM;?V16N>=p!=Duuzn>t%gr~gB=poW3KW!D4*-BI5 z5U7n6UtC=L3>boX*KaQd_)>Ss6=g-~1PlfEP@-n&TehV^Xnh4v_*oS?Y2Y!}7X(IR zI#WU0JUu*Y^l~L*u=zj^12FQ#C=J zdaylW4RZ&x*3FE)o0`cQqL}{}Dm_{Aft5iYuyGsuwMXj5y(Dq4!Rj8YK}~|@Q?;?% zDg+yAuHv?bUl|o4=&c4ZRBMnaHSmXIFIXIzS$m&F7bnd8B@NC4`XIeeN{i}SL$Ve~ zG)!>B>skDC-e}jGcUT?Ro>b*?-SlGg92k6FZ_!qj4T!zpiAdNVAu>K4Y6r3{4mKrw zkzHG~5|tZ=Ot_{497XImBdJV8$Ck6Rj}YOuH#o)7&vzQGD)_`%S-I9k4Ys>{OkPDm zcOPx)i?ubG;`2k)_*J?>5j+1kwo#eOO+BL_u)h<#R?z zQN=@J)Dq~qVw5xf@nhGD~Z~UglNLtTc)6e$nL2OtLHCW7xzWSDsl`@7xl1JO@p1V@0Hi zkf;{4{PzzkFD?xQ_8Z=UjR zG!Q^nL{-Gj2AFcnBY^XdZZv&83dgHC#?iMHnzBfC3?mW{l9)4GLHqmnD&wj*Wg*Ov zkgnLO%xe77;^<@JZy~LN)jO5@6?Lh6weX)C&)j{&F(ikwkF`*#NyQTaRI9S!LIFYk z=#7)7D>N()p(MFg-;)}%9^$9^;9qmbZC}ES%Uw(wd!ePjQKFE`oq1N1cy3Hh{NCz) zlX#cx@8DnK|6FYaV@?MXrril)T^!iQoQ_Eh*LZV#drJYVa^zop`!_x^d5_SYrA}59 z;?0SE(rR2~j86Io5uX=Ou@-vv`F=k%psQ1 z6rgtlYD@T0`N`Fh2v9Rfdd|Mene&Dn`({3S zz}V}EK76wI@w~I^l7lw3WPD}SVJ3t@4;;DN5ewcIS{oG?5)*ff7JHWT%QRcXTg86% zcno;oh2BlO(M^RtoghRl&}6{vY>PTNvaR8*t~eKiR=Hx4DNX9}Yh9NlY{2hF~G|5aT6 z!w5~9NC_u{znU2yY4aYR!Xi;W+>$x|!WBI+wp!x^&chnh*!dx!=ncpW*A4lly7?*>>I$mo*;PQT<>?+kpVzco4I4vvm?@yyn;6A?mOe$jRfR zODwYrW3vP6aCc9*?*|E;+@@NE;ky-|WVB1HO(=?yzAN@Plt7*t#wrQ4_ zp1a|{;NR?L2cQ6!LWYtWBSa>$gIhLTzRIGAXRXZx=T2yHFfenzip2-e?D6 zltI7i_R+PtvX+f?!t>k-mZYpMpnhTZg^!YU07PZj+`$> zGb}|Z0$tbj@g@N|sSKj$qc|ufU*klHQ6A;So)0WOYyLr_(_36pkf4>|8RcpM%!60R z{KCMj5#hH;WQIC@9Z^_Ro(TG|XGk5*`-vf=}13Sw@5hr|8#x=(NUIZ}TP5 zFv6a|?|}d9cke&wf>U%}td?)ea{fzCMDaI@ZvT}`iuTPajMvZ0gLCw8o|3}I!lmA9 z>K2K*-z5Y78AROs0#aUafAx~IGG4j$sssO&?%))}RDH7yWgNIRLc%tPYBJ0YOX%zV z8admJ1U!-ZbdryZq43YCA)Utrz>tm;AF76&4n}1*zq$(><6qP{?6{*I~ zi}H*EI5xj4U}8dL_L1Q`ma!CE!*hplAKCy#fk>T*5msqvvkNoxfGpKNCMn zHMvbM@B%Pj#~-J~MBzalm`dhzk93ztXdkyKhxsw=Uvbv0+RUWH{^cehd6PPF(d%Uv%|K4>pr!XS7tEQGM z+HFsoyU)w zdFG7n(m+)=PQPaS;lY@?hsr%d)e%J~1CZukD>GkrIR5$)FKRoK1&AOOf~CnTUKVnt z2xUBK8$2i;^K(99uir$YhlwTbBR;5c=e(>Y=Q{!?2z!d$bI7ni}|@2UV@52l}@P3X#!F>SQ!8Aq@0A15y4U!ykGzhuJL;LAXe*Qe$Z>tIgB;(kyNvOap`-(j0ii=^!gXaxL z3RX#?B}om9avnSj|KVJ!yj6v+fZ!<|ptUid-=tVqB;By>oUN*QBM5uJt&-qtz8v3&kYPY=i#^D{B6DLfI0v7+Qn`LhNT>zdv+~D@S*_Z>(`*rQ%z4sTb%}E zB@icEsrTNz1&CYQJ^@fiBv^?|F~@CgEjv0o7FZ?9FiVsD#xBP#VYF2g?^`h_(_2LY zir$^CZ4QcseOc6pcaPy;isT84>>4 zg>B8kH3BMpx$;7Jww`#cb0dS@XXxnobv}{%S$E+3r5@&`uUh5?-M=22;j2!xdfhMR zIL-q;FPR!1%IhC7n7j3lR-2%dRNzwbrY!=BMfNfw!IMuwpnaC{z(Af&$$xXIrY`Uo%M4Zva%LVB zCT%)63z*7jXY1%dUnDJ;g|_u zGdS@x^gV-v@x$a6>KQV>_jlYLt;-E4snv?-`*S}0P^@}*HY$$Y^(lT8<|n;;W3tP3 zU!8h_8BvzfT5_YfD`v<+w<15n=m7`=ub;!#gZZJY1alN2BF#2%cl|a3Mq7i`)zyg! zR$(8j8&V##FTAAB8yk-F7R!i<<@)klD|^jvdP07w^>k0>52x5!x!}GM6k{Fg3RsnFN#WPc!dMS zjfO_0fv%(ECrpG>VH?i6@0T*rPruECsHrrlHnJ8{UPpxwKR@MUW(MkpFTHs~{qyHf z@=rS~##xOvTW9s@D`^n-y0LlLhZ@VrcV25BU zt_aPrR{Csej=&TDR+&2qBtn;j0*Uu;32$fubvihKwF@#+z&-j{nYQ}*2f7?kswj$Tkcah80 z-O=3sKicg3c2bXWrA60*=lMa_&9*ywgalx};sm4b9WO={MPmL^^x+pom7Cs(0k&Kb`FU(&UJk6HaoiH?2>Oa~lQh7ceEbf5VUp1{XeUI;_Gr(3Lcg+c92+r|O zOD!NiJ@e`&di9owgkD+WTLwBkTdX&{VBc?(u)(EkFNOym)lG0Hg(xo>)T19 zYfl!3b8~aC&CUO_1$>#pozVFX>%CuJ+FSjtH;tsx0ztoTGK!a+yNiok&&eL2ZZ$16 z!Tk$BC&;igXU#l?65>y3COl=n<0duCd91NB`Lpvq>0H^J%C^PtQcY$}=5i<7ZD0QO zGPGR;?vST)6<3BHeBTa(pHn9*&EsJilzU-8_s{4+FG%M_&cRN+p3uE5E;+zQ-zMP=MT$9ixk9l($w_pxEh z81Oh6=uRJiCxQ>q^7sD6Aa}pc=gPt=JDD;BebZ6HdFJ+KFaj$CLt1T74voE)$g(!7X;c*x$8_%dKC06u$p)HJ>fsdvvg z$<4qAMCndTSPMqp5Ac}Sjs+DMAq@fgPz6TP57n4$zhRcleI+Z% zT{l975alPYwZ?>bydKeN=J1{`vkO7J_Ik^BFG(1>+jd$@D6}%W0To;Y_n2M*9Y5Bi zg>V5`zYh;v*d^ZZtVp7#r44IEVZ|Jb#GZ$NmLiFp z((5eEM<$aFkq?uYR%?mz$)fIPEnZ^g+D$zlnR||L0Y>vUc!DoZZEoE*dAcL&hSL0@ z@2m4Xa+xo9%KHaJPdHE3mCVHxe!O2AiG1R4qveN&%h|6R^_}!Hi1bm)s9Pae8%H6WOQ>p)M{*sf!d zGKz43rVB;%kN5rTTspaosJ!K%{7I~@IGsee*!>26mIU_pj*iEYeQpDTgQ14-nrqSk zoWAMz@*{Fz7=3Nz**rZH|KPIV)e}SkC!BNs7tPgW<%OqfSJ41dUbJ`J5(LRZ*?MqW z#BRBO;dJD_#^f;qezo=U5=hz=zAYFm;4GeLgPy0!%g9-2sVZ;VOsd^WjMv)Rcz@HT z7bp+kaQ3j1A`&*4y&z@c&hsf$_PHg@GKv0gl0 z$*c6a;Xoc_KD8c749T|$#AhBIygA6-5`&J z6U+3wGE1AfT?FHe=!7kMr>I%1E)LT=g;f@{J-) zH4agyi=4#mpqs^Huf&FPuz@5vE7A|U@*IPdHD`O{3< zx-W5yfKrqHkGP|UOgCb8sSGPxmBOEu=m<>!`u=_}Y)trKLFazdEN6ss4CVfLk&ZjL zqg!gG`^Y~(-aiCd;TPOYwmKBJ z*pZ~T*tQBI7~-$M2!wb3aPJqtAY-P_lJRv=TI>vxzXYdd==}WLv#CY{cQ-&3Y6XM< z>>_=CllqrSlUU`BFdciWn4_&w@>lr<1xh;b>HkwBn5~y4Et_VAIC~`C{vIa+s%ig$srL_i=}+RQDua8&kGl{b4A>qkZDq5Q zvTsM-HWhc)b9n%Fcof)KtaysMF|ILJ;_sRnj>XPDE@3$4y^<|Hp}lmb?9+wgS)xKlUeorKG<|lN78_@3AZ%q$ z=o+*IRQg1s+$$-fJv(81&-m6tftw$u9{V}VLy^REC&N6DRsJ&Qop^7B#&9k~>dPvF z$<+e?!#KX)+KN-2aO)I!yy!n z&SPuoz*u_ucO$M6MAx2zBMwq~;hmuiR`S@=52dA+ko$plIh&iCeSKL?N`Gh~t6acQM9@2rxGt%M z=yZN_%sHFF?5jlkxzMUXSsr&xaN{biAP6mkL-L{-RIf< zWsj47hwroJV>-TQPfx!W%pOq1@Wrp3`s-EZBp?rIa4E*8fSA;K{r$K+K|cyV6qs@>n{$7V=3Z7ugkJ!*k1m69@eZBSOB_bX;%Wo>nqgT3qZ7OBWKoOJ75v< z*CLGV4H=M(!P!Xh3rJ(`HN5-rJG-J{y}X#yhRWVbFfyH>7&1(Vup3s4fWm{WB<*F(;j*=oc0GXQ>&0P3v-@DRan4NgXf0REz~FW4k^OhpuBl!gUw;MQgoX z4!eqrPId`1R|TP>oS}$~NPFd`Tl8Epc&iX(>e`a^ADz0%3v@ zRZ@zCX{zz10$rjrNLpAE5)v%k#y|d80mpMwnPari1QR971#fO{UdU(KvpPT7i^*T_ zFwoO`Lv}tv!S=TfsYp$TI}FI;v19vbpwFI1LP? zudkARnYz71$lu>|2ymS_o5Z(NDgBdJ7NC-8W*{$cO$??!`Me&iTHbJsk@c=xV1#!ZM4MOM1@B&yNoFPv#*Y89AuL-EKn8Q^w!#3&{_p-3r{?YyB0KK9m2q(*Jk{KOz6C%~gO1ttLlW!DP(UTj1eKvHE^g54Xy-YS+DXK! zncoz|F&KeaDo&rtumvcVXKx{0XK(;q66|?z$t95d8M^iGSFeaTXk(0`*T}2smu&Sl zlA-zaK(_;i$(JxSD2X%~!S}$QVYX!h+U7Zg_i~^6D47t!bD&n%-8!6>*3ELJ7mW#^FOXTa}SZ-J{$3wbX@kCAyc#Ph(j_mX! zK)dbW0r}M6ADh16GF;vOmKd1z&a>m z9fyawd^->5E&iT{ImX;8@}xMPG7Fft*HETtLd_X&)2dM7s^H?PP!8zW$qAeiev*jE zugYmU`Rs@|zJi_hBtpIV#2xg=cUQl!ZCyOD{ocwy{I-6*YoKbV)f_U&Maw{Ga~Dm- zJ!=rOSx_KFkN3G#6tz)c*JzJx^~bTqqb|RsT(e`Yp#No6;lv%ayFBs8-3xe9(CD_T zbY)e|o3FW~2lzR~f2Ju7593ltAbLrdYUl!^0JG#Ro!ZjY&;j7Jl2JTPW z^CrZ#nYY5Le`#?N`n53s`kZ<4z>v4ov&Unt`7|=eJ)f^cUh?hZk1gn$fUZAdvxZAi zRBayf(vwEK<2%@SJ3InPYs7I{V*NhPHkBlXbH1mh>y+9>?swP8=`j_9X9AhkjDDz5oROc@!b5(9rpJE3o z(tjLp+=A-4Bu}C*Cc8A5TiE|%mwem0MHs~q2jw8_hEW-QZ*Q@b#Oe&CmDkX{y6@!8 zc6X;?XGL@zSEWa}UIl!^0mIRYSPuRewMeK#yB(y*rAhxE2ae*PBL!R2=#gKjjj+VN{ux z%}$70yP`&is|t^}FnM_E_2UL)LX$fxK*tz62CzEt4hv7ulz#ECo#QG{sq_r~%mnR2 zWcr#Fpk^s6)}9?p)8mcRSN;t;mJUH0={E~N&tso;uqMJ=9E4F`R`&yx=NJ0?i=KJL zZAKp|b>$jr_eE_oKdSClcr8OUTJ0+y<_qW5_gI#!{v-8qvZdNX0RNsElRzFMpk4&k z1AC=ASdSUJvYKb^ZMq}j7oGep#vb6*YID|G6t5b7SCe*pIH8UP??yYwqmRF@i1z}h zkhrCxjQC4lLFNmc;liK;V!?zj3FX&vRQU02=V1n91(LA$5W&8h$}cWumPD#@Vx8C>U01PZFWUks}xgG zgpIL5N5{lt9i44#(gt_TfG$sc{}hR4nYRfVO|d+B=+icLZ4duig)aG*XV1m2Tz^Ve zo=~rKkiShWG!d6xhWFjFek_2Q4a*=^W3-(6pq9ACpz~8puYt-hKu>m<)lE-@jYqw5p0q7jB{Lwe7QSz|i2pM(J&w2%0vkhZutAy7W=S z2JC-Vi3fLc5X(v>jFq{_gyy3j{BNsanwN(30ldxgc$|BPdU;0ABC4TfR^X1zl4M=l z%1Z2a>gDbynC2=mUM|#~o<<3-Y8yRO+whx&_hP=F4_oIvjzt!;)NlKQNV8mGtk{Hs zBpg*+o-*rk?25>{(?kJ)A|Y<>b=BhWT`Q53uFiUZnjBn2M1CwRTnw=qEnMih|+}tg|~8U|1uG8F%uZ?WD!=~ zi$k#G)8e74nYVs$dVWU=WQLuE4fBkIxcPGhYQWXu%|eMP1Ans>npIYspk;idi2k#6 zU=MY9_d+_Jfg-BUwZa?({QcSw&I0^8jkr~M8w~)hA+PZ%7zs>wM;UT)am{~?!1n}& zjBmPLBqpqrEjEa!t;MX;awS>zT`*ymfn6{O&u6(Il(P5@Jr&CP${=)8;uh7$5lqOl zjodCCUvs(}VlK@xtTY}D+$bC?rP-{K0`RGZ6FbLEPc%Dl%%oh#KUUZ~>+?!z^7@Yz z$x{?Rl|nW4VlVCLS~##P zOWYgr1CPSO&XdDwDZn(W{CKcvP;tWLBqSyj&BDR4-Wlp5 zY(P3ABF~O@EW^LVQOOXrU0GB~S-8$Aii++Tw%||*mX($9adN(w&zZ%wlU=aHYlU3E z7+^H8eioG_nnkk2AaDHLYEH&>k}#tW9k&-{tO%=dn@O6PzIK24bHcid)d2epErqhL z3=l_gV1q4)@-n(1<74+OF$!X3Y@oVGVx`GL8`6C1FE3Nl{PJFq0jlvL6nt|4!Ha*C zVgG0WxHNyn?(?~0P;t=IBeejE)}X&#soAex_>k%c$BnO$d&*e5SR&-j_(t(HMp-+X zrg$ggWo=)mZiUGCqp;XzuzSBD{veih6jUgqIq|)%uXiX~GjU|ym3(N_Bo>rYJ#@vE zY$@wv#p4FWPfe>PQNNW@`kG{8CJP}vXC9~kL*-cpK5nY@(`Ifip>7IBG>kzs16Ki7 z1#4kM$E32rusF!3NJ)K>Rb9;w%&`0*jdI;s%CfsjV=usp39r3I6bFmNbsvBUuY@Y^ zp#fN7Z=CB6cYYqPtn@99nblLBOzfdY<&EwdGdF6{7W(coCxenJRxi(wie70x&xigDvTY$ zNq|k}ohiw@>(X{Cz)m#nW=J;LmDeRZ`OeF}G(LFkQB)|guHy@3bWhU4PSw_4Q0KTw zEU4TE>B>C6efwq(3nd{YKGVNVX9+SPo8s|E;J})Js3k)N;S0({zKX+RU>U8?{mFH% zOlBG{GH2am$)4$K$z#_CWHdq0X4j)}%?+n`0{@?zPGlO=|s>+7^4{2v&w8Uhe)Wh5M`j~0Vg(i&_JH4da zTUU;hId&V7A+{i`&X+}~b_9ydF=m6#Qzj!C;7eDNX~4Sv<8YSZdeDak(>1pJj^N+} z9KGlz5)J?nATW&r=sLy38Go_I;b|Cm=ArZtr@y|%PqhYXW!XOH%h22Q39+!%lfwSD zd1>bIamPXO>{xHXZdJZSD!~^Pw1B2obFQT{1YAu)M3+~h8-3W9xoQMzxL z4pr33HMa`w2>*9i)>pkaO~7qqwE!>cz&FKgQZ;F+@2a53>#qfHjTzJM{(IJM`#~qQ@%odBXbfX3zD}`Nwf^JT=kN*EeTNalA`u*%9I&Db12> zP_NiJx6ROiqyZ>t+Qrm*z+wPcSLSLKKzU3_!mLq8WUJwdnq?WvC9~n_Q7;g8wHLn> zi1IR}_&56F3Mr9>GZD)30*8uAb!{cMDm;b&>bWGi`$%2AIpFtb5uFgcePmtEly?HP z6`<4#$A;={P+8qKt`h@({3gOY4c-!JYn60;@qSjUBC=7oRR1M^M^pBP_pdMb@AgD< zG7r{nV+m=3ivnl=M2_F&3fOwP-iG<&Jg6x)FXog0s*NoP7?);kteay$e>z~JQd3if zdDit^L1)(F0ILzNuCjRH%Q{8kpaI@CvSz$y?9?@HVi5}4;!(kX9BftC+u@(zcnb*I zm&cQcXd$Jx0<4!1MVt-U)o(h}Zi}~C1pci-fHS(+vUG4-iOpu9X)bmheCTRYJen|~ zR!>A2SuBx~pa615T!7QTI#3HohQtl7wzjnoI|IFjxAImptDMKHwraiIySfBZ5N*6| ze0s=dXduiQ!Hw`*$HX%-Jg+_=og*~D$|aIt+Qb>fU8euR%7{&hFQ2^V#o1SDjlFwI z7j?S%XFL<~`hT}{`s&S_pXR)W$;h*PWsd6hTSq@?%a_yi0zqLGO8y}aoQQj_7ioGf z_g!2zB9!U?dCxK0VF{0@7+h`#cn>ocGcXVL78-uM9H?s^(G@zGa@U?W^~3`~Fnnwb zC9C&BE(9u;;`HNq*Ptd(&xf3w@;D9TVGK6}-2AI{y5#yw1?7kA#6{ zJm%i2wJg^w8}eOVFETIEKE6U8$-XF#7X(vFVJZCk+N}N=?^fBYN;%2haPegZ-y@2Z z6?GfbQA9q*z;gcMws5*@%R>MmM!ow~gfZ9b3FgnisR!1tWV!9;rol1|;I$#eZ!;sIAf>yIi zU0^K+FwC?%?_s$+3TI$!ND1^X3NOlJ4oc)+{G|f4wQD`Hz@@@dIJWYxikQe#E;cO1 zej{8`oNU`#VQ7uI6TTTKDWaJ@3@aDQ?AeDTLB#EcxXl&n!E7QoY|VEm3-cOTYi4NBD@{tvCw&ePM{ZTMXn#r-5`#cpDhS)Knn90m;BwH!=!7Z za;>H}P?(_Agfg|v82Kouw4ps+#crl++MBkU!(?29@@AT+y4qH~@4g67Xo|5nn+V1J zQxe?6vjruOL`tJ?qktB{bCviF0m6qoU=CV@2Jf@$i51`IS_*i1dFR|w+pBv)$#RTa zr6naZVB;=;#}5P;OOABF=9E3A)e{7wu)*53iE?9cJVtNG+($CIujahcLp@&}^L%ql zv*R%%6e6v=9y6LkPhuuE)I3aK2{9>3cB}3h)-6(-Yg?2A=jQ-xq9yr*yMwg>V1}yM z8^G2TCB{}2HP8byPQC@IE|8DTo1lYkBn>3#@mcap0K|4E^~bf9C(poR#Big$R$c=K zFx0qJ?T(THU@Wn_*!2*R4Y`2Lpy9?Xei_`T;^hIa)WOxWKJ=ju03Z2}{6PkB(CDs2 zlxD1~d|f^`(NIKS|4MuF@Y8#&ibD?gLgc47!ZyHp{PV1^mw@R`HC~8{NQ0-M_h4rS zPy+>!62L%Rg?&tZo1foF83YEukj@oc+$$Q5Rem;DDpW)Z^f*M}?A3{s>E(ohAK#bum@}NpJ*nbr|Z_I$4VckY`PbKb)$BUmC!ydmpGf5(89D&HNNXQ zRFvd6Am>c(Up6MY9i{PvbQ5r#kH7-xZ`@qc1%d*C%9b^z>)VNCkKn=nCoiT2I42B9 zF$GYKuHV#Iu&%Nc@}Fhcr#mkIJNgXQGd1r#?#Sh!vUT^CWg%XFmpu|^parQoPO z#xBFuJ_f|c{K(g@K?6I-1po{x*&b8`-SB^%DwdTnn{``0nQS55;2z)$?D#s9CQW$z zu#^Y#*RdJcUbK%5NEAK+yjzSCyU~4;7#AD7N%%Mlt*O$iSD>bJo` z{#SBG)t9WHk9qyspvVw{-NwY1pe8SR5XD1$?@kFw_^Izdq2!a^X+I$6&h_fXN+5`R z2{O*o=I8`&{r`t0<33?l>6%GBXJ{?@|Zvq>-&d65PJv=I} zudQO->~)idTW6&cv)!{SBPLG_KsUNY4G@^S4m%iP<}-gmLys;YdHfsgs~5{q}&3o z508_dKjEh83okH0Nv~8VkF#U}{KT}D8b2UoM|-IkKhdrf*uA*W=c#s+4pY+ zI07k^&Nll$Xpe!rVMT^IV%ElaS32$f;2rxgOv!toJ?ofci5l;&VU3t1o&&e6c`1__9B`D&Zk<1dB8prZVjF{y^r#*CR>9p>h>9#vk7U z(wZT8OrRsUFSmvB7bjcVo@n@T9ik98Sp$Z*@wUiE>DB<~;r5+7-o12&EKe>P-7!=Z zhy9>R7~!-^ydrQL0K*R!I>%G)-+Ihjj1OFbK`0gF^)~!>rw5!8B~+-rSa+#(S%WBp zddkKq7_n|+dtyJ}{3wCb>2*YKLb+=*d#41OTN=BEc2V@nX52{wu#jGwdavYR*Mbsi)xj-MiOKhA0LntgUGPV4f1YwR$d1Sd4QXO}J0M z(m@XW>Lxg;-0V~MA+XSra3&C0rS0d~P2vL@GV|Y@qP&tTHWxl97Qp2Fb!3&L-gk zYX9*D6ZFmNy!Xfe9&)%LjMHzF;lIj;i%ay52Rw>v1pi)(9;>Z!^$i+MYi}xR>oQJ` zy960BtAnES1XibOn?iao4eh_3O*;508#SOg|e40?;9vSlPmYt*YaQNwFX$ zL(r7%NqCdM=X~T@3DDreIo)SL1tVWPuBut~xWi(H?fo;^8tQFa3qUO_2oa~aqwtj5 zXTM+X2sm*ZY23$&WysrcW*m%GVH|u;hmc(~(E%bK~Dg@L?i7@%Z2LJ zP!>2oiAC5aJMBvELiO3c;Ofl&4vUB;dL=zIeM!C>$!Hk&Iq$H6kw4l~^ewx`e81vL zW^j(Ukd@|xS)#Vh>-nMT+S>BekC!YF{lNHoUx%@RGGdGG*{2Uq6(jWm=uU+R5Dqr+ zi^rEennLRVuhVbe-YFE^^yJfV~Rz8r?Y8dnb>>NFd;il!Da! zbz(Zf2O0krv26?`iB(2yO~4cP z@9CfXz$>Co&C250Ch0D+KKpAvI8zU!7bKpXtN>HPH5cF(3ctt8f~Iq2zzld+xqBC9 zy-gPAdG~kK+E<6k)VmTq%BDqP4h_E!MAePp zH!(YWk2*j9i3#2)i|L=LC+fUF{;e?wWi8Zi+OFrZ#obZ9OhTg1Vb@kcq`A2deck$(<_;4F%#AG|Jh;7jFo ziRxa*bGvFplm2k_3?aebo*wzhsJfL8g_Peyjd~o?wds}&ln{J1a>+I9P^3b7x-7aRHL1b z3%WyURxPjiEVwCoa&uEyO~a@y(wY_|F-rS{x4E)MyqAw=M+zdMid2sy6gky4XGTgM zS)(L@lKG!sZP=PGvAWHU^#s zc8h%RGe|dk)AtO(eE$P1aMl11^SKp0SS?VgweeIWL=)Zr);{$qPb9}eP`V$}Z zCy%zv)O%VM2`_^<{0lGv+n~z-IS|$c%A0Q)_H!T8&c@dd%;|_@9MTPhf#jnGjKdqL z6+quC9tV4seOoC26kkhD54h+7L)cF2*JY>8NOI2nd)&gIbaAb;%hm9KY9@dPk^G}%Dg4uGxAob>Nr{jwhpn}qofi&kO@LAbu|lF+@ZFfH;&|FRE1LquM+eTItoI_%Xh zr$~AJ?$Yk-`XVH~cVFE2YKeemvsx>2j@^=%Mq5-4MTpzB+(lr05wgrw)0RbQ#f5qB zXNC@+RHXm(+_!m0k*3M|Jcv;#zeTtmajX!!A6eD$LQ^xUO#e|HuRaH@*kJgviV2fh zfN@MmqGlNkVf%2Od>Y#UT|5$ZsU@IBZ{X>HaBbi_^c;{}n$FTysqUqyA5ssXcyUjd_aJ9?6 z9kGwEM!8`M61eZu(4F38T>N1crTo+9z%?n=M1;?n1+uzVcF;o~>~!?MQ7$yf`%v=8 zIk1Q9ZrdU@WbN`8UY;-8Fn;ORj`1+D^d+m?Zc7n_)4PlhEEjoX3i-98g+675B~$XT z*nqm9pid6L&1e|)S;U!T0b3HNdcz(*W!xde_rx(zkk3`Z`iqr;1;;W*up{0gLKy?& zVh~R`sMNcfwG;7#0tRr4Y$y|8Udu@i7k|gBhZsNVmj-p9Jj6X94>GlcL{R~oA~bFy z8HCUART^9z!Y#`wfzS|BEB9;p(I&1464ExMj8ZW_GeVX6At-`#KAc5{9- zI{{k*tYall_?aa9XPTs7oKhaq;0*)lO7RQy0{gAk#KA;>ehP+i0}n5g?J=j!A@YcK z;vw&0#>jeGpp^d_Z*eC!8=&O`R|tdwVd?OPfPFW8q<<~=;N$!!y2@s0`ji_zrH*+I zA_64wT3S}@9Nwh9{Z`WAYb+ivYq~aaRVt93*?-jFdU}$kf3j7aIn@#@K4IE$i(5Rk zfGsp}B;+qgtvfRWWl-ht{qG;QY^jvoA?-#Xi=m%hS!EW&H#pTGuR8pkYi#WVjSz3@ z?{a}Rk??cpe2_FIVW@RD^Q)y7Ixl6fDMp;KcLdMNBee^L7a}h;bR4+QAQCKCM7*VF z3$P#Dfe*$ofXb3jLKWr`p_sY#_BPI4gZBqcbCEX z0sW#c$3}e~UM`6lg*?#^EffhW>;zu0gjvN+pNhFx{Ui(8?3(G%s)vyhiU zHREyY5l#X(AQnMbdio?4bU%M;Z^GtkoT%KUZGPU#pcT~o`98MB%cvS*=gO0mA((Jn z<87pz^#h#Q(2-=AJ{2gK#Pt#MvQ37P+2&nhMPk)fyTLoz4 z3%W9;$e4Q&Gcf7hOxB%g{`9~(;Ku_u(~j(P8t$KKGp;omf;}dUiOZgFR_K^T&{9Ed zD|nv-1%;wIc$!-t?NnF$tJnYYl8)cW`%kf3u0oE?h@$Cc7WZr*JF?dPuX#r{Te4U6 zVqW0O37Nywb#TG=3ulM9@=la9jmCfHLwh&=GKsGhM108zs9_)_4=>T~0za5@d70x; zR<`0fH!#$;lfnE1XskZgKgIGOpvM`poh25+u@ETUevi1VJ%r6r_|grfr+Lwu{B^X) zxCHw%Y$lll>(xl-`Yl7BmT1+1S2ww=787r@l~|e8V3#}=?mYh;gw;FCpm_H21a+k) zEa6N)HkQzII2Y(}_2Yfly14!ET$5+``NGxEO6Laz)pCv}P;B-k^;LlP7zCZ#GfE{t zewcJC^3bI+M|Q$4$_a)1P&G7k7yIkUUUiTtDVy7um=|P45xqH~CK(e?T<7e;tVr1V z!$vZIgaJXoTZA{DM)baYi*`pb!-|y=%X?vSRhDr4eOrcT&0gZc+7|}2H;Xh?$CB9D z!d?~cz#m%}(06d_*g=sjG?;=Wf_K5ecFF81{EGSF7NKN@W-O3=zGu?Q4U>=nZw(&J zp6$8sIF8y=!ZG-z1?0-}Y#W=0Hz&4A(5a8#7*(;k!Y?xNawNay8NpsN0#G1eUsQ!g z9O|rNZ1$R{GvG_q8sd&JXXz%dcEF1_Q?xHb;BpJLI#ZsIX-28oy4Es7 ziAQ|>^Bv%=B`;hnd1vVp2z1jV-L3ZR#GO4Sh%4KFeqoC9#%%S)EY1Zc^b5wdC_=x@ z>{dl~nFnr}vZE)Z49J^i_0EobN9GzfFOHKWy6Q%{Qy=fIUmT0?<@yE%4A9P=o~j9y z=@SeqE&N^nF7@bDFQbOW?yy`*6C0j->w_Qs}wDlv?JU;PA(g;~U3pe%^%H-gRE86)pj`1dVs4r!z)4S(X*HWRXLT%dF?x_;Y3agrlY0Z`F9r z&AlX)m}#_MFL|^gV5(-L5d4Mi+8?n$(bkT7f>7hY9`93B@%61MZ8b8^Fj95Wjd!aI?NawSRthp{*u)%pSM#4dwacu!mVH1!$$_h zD)Af(+&9wI8eICQtya{RqY5<{zng`5FLj=tay_#Z7>m*yeTV1w+iHHhAB6k55z+pv zL)S`CA>10PB&#oRU+&YS7HZPzADyAAeWm7AN~PnU0Z+XJ=3~Dyv@DLCYaItK6XKMv zcX?(M_?5Ex)WNppRi3*5&9EE=lf);l%u)2ABz7eL8T$vw$N&*ZeBQrv{CLtNW$!f1 z7VuiHv-9#gp71_n=U^o5bTu}h7wG{(pA|IdzJ5&;-E$;W9@D?<{3*?)q`?Dxx}X9S zG6yGgUGs43aX(>WY%YtzE1rdb#>7{8LQ0ITPRXemtM-i9eNA`_SIklsQ=eI&oDqvS7;@#-2{dwQ<~2cQUy39zACs7nG|5mAa^?+rKq z48{lcQ{NdsM@c7|ss<)9tHQ?c5q#Q|eGUkwM7roW0&GY}=&=qYHDLP{`}qrIO=0-? z{x3A399r^@wzPx;+Y;E{e|*rh?KIzhqG;ZhU3@Y|{NV$wu$InzsXm)zxUBiGV$Rr6 z+P9?-sY7~Q@d*iSB!oakC%CW0cbk$>8;Fn0f%517G4&QuQLXPE?+o4D9ZGjdDxsht zAQBRigM@^1cPJ^SbP1>BA_~&dbss$c-@SLO<67segPWPX-~GN% ze7~P37&m2>0g-Oe)sI_`tJ4Ar(?d@GmVwQH;dYWu&b_;LMQc(77-O6wC zWh=4F<@k4=ZHg=lkkZ8cT=-<$f%rBR!27Lk|GaFv!bjzI1@k>C#;PUX%9zXd0;*3B z5|944&biPz6b)Kc$3*3^Jz|YPrGr zghdwK8%vt&p8d;5Gtn~5>z&7ni+MU*OqQ7YU8Vot9u;9f!?vdPy91&76K1GTV?ke@ z!*PcH#4(rgVxGq+`YxSw!iaAP$J|8lndS4T??fg`fI%MhE z3!uXjn=d1wbGqJ4%G=j(xXXSjTfLKE=GG1La0u_tH z_rQ(kmGX+xzX081S$|F06m7h` zy35Etz?PxP!!c#Nt2j#HTS*?f5cA(r{NBXAsHN$1)%Q~9$FIbpMk zMt5|A>LMZM9c=6FrrF1*G8a!ncqZzqb|&0!-rE&pCim-Qu4wIf0F};9m9}LkOTw?} z?+s2fGoM91<|`8;qO#kCe~)8hIe0csPby5!60eIDZADX_)Lc4#nX9i|b_T+JdeF2Ya(}4mDrZ)(4%@C=(He0S|z*ur140o)G?e~{y=5`Z1(;Y$RQAN!5Et^(YQ-Bz#-?-*c zu=jW5hPOz+`RYc%0V5sVIb3F_*Ti5C&~OZLG)z{PK?_0UJU{>LjT<)#eBLWHhLE`C znKW>iu)n$x^>a~O9n-mKZH3dCn^~8?hL_3uGyQ#`0s7LYE3WQGQZqy5!$UkGpL@o|$LnI@a1XG1yG-eW_-&17mDhkN?v`&Zgn5wYe3d7$9PyWkC z3e+r5$p+cCR?0LUDd1OUMi-NeDj|Ucp&ajU*jZKSM()#uPs9H4?Gc;l1!SE_Ow@}0 z^Yd?7sGJ!+pPOGmUO2V=u*~SGk#ovH=Ng^58vWi+S})$Wa0FB7ODwWa^{PH*N8{O# zil<1I_E^6@{pZFT-nX~df0Rktnd}BQc`J08RHTJp24PiIM`=f?`LguZ)|Qe0S}Nje zhX^dkvppeY$EI9Tamt(WY1b8omP0X@m%6;==CUjWp%?gJT-w7Sp?hpMTXk9CP18!J z5hMI!UiV|^Yfbjyq&H-X>4V;jPevOg-HyZbf{r}rT(a9qjdL%cwLU&I5@+cz#*@vn z>nh{Ye(o1b5nQWR57{ngG0;aco+ngMI%w)wKLSSGyQ&Y|GVSoCWiL!;3c#evIvCNeC# zAjI@P2x)VSjE&UDun#S-oc1XC= zCrX;nS2q_V_MS_?@-7X&a@q=35?u&Q1 z4*zT?D#>!y)6pp-b$wJMKX*TxG~EC?MJ#tQ(-0yASH6kRhsLj(0G*I^{eA1O$o(zd zx)`fgomZVq?I+GsqIn_JUn%&WcP24EdW3-FAutez5TWZ={Bp_2HOv}@*`&oNh7?H? z+E%Qq@or(FpxR16PSPKKKwRe@`>nRYBittZ zLAzB%c-e+5_FcGL8P$$f+OMxQ#l@g<7zDM$b0?W^2qz+$Pv!+hwf9QWobOEd@%C5e zpyv9^M$9XG%b0HoqHo!iv0`zTDQ`JzphvA$S+DAw<4!K_9unTg915WcZf6*G*R9yX z8UGCQzcsz{`|qmD&ZsyZTHM?$4~ObRmXmEHebTwcm>ga3G@A74 zdWvlhKXLg{%irTIHkS<+(b>+M4$oQwFAapSW$4Bv%Yl^SMifUy)*Ajqk}oXKM+$u{ z+t&NFzb}8$^ysP4-(oU!UwvIUC!D3k)O1Q{p6AYYAlxoSM@3Ti?x0|hbzgJp;dymQ z1xID43AXkVEvm1?BdZr~5wreOeQxM~v7YnPr7OD5g!OGlzRi#uHg)|_-|nq`{k6Ib zHmy2)j%;Pv^ZL#F3jK(pYHK%RSA3hfxJ8-EG~Enya*_T4=#As zdN)tyN6^+{^V!}aChEfVwFf_$x4%wXvSyyj{ZDKqM6X?H4BBK-4OMwB&+jzt$kAKc z7bnUsn|BSN8*@=-^1}_~_X9D@<n!=I_u)d6$_E0!; zRZOf`f|44&b#uBu@6C}m%;0Q=PRci|T-Z8ZEO;n@bH~wfYxVu($&0o)##@^6?DISC@%Q!dx*}=v7@7R{%~5<{?g#X>)LXvoo9M8 zZwErD*}w|rjq&A$JmZ?n)O%`{CSml_MBS9C;FtH+Wk0i$F62szDZiGGWWON)>@r1V z&w`g*y<6~Q8J5BCjB+HHZ3CU2ym08)nRT#sutMf(gxMAaTF*-Z=TfXb;>H^w`-lpN>;ubW6S8}4~!Y$ElRRX5R>~|blXb;Wp-(!C##}Zf$ zUXgQ={4?I-(qt^*zO|fhgBSCjVsR~QzVFk57%xZHl5a2(_JrtlyJYI(&(@hSIbG{! z3K=ZY#gX}XyaQwUC2D^K&ubas#KgWjr+S=tpOj0?6S94Gq4@(bUQ z_dG;>UF^ok%OqqdW;J=RbjZa1eL=GKU@LmAoJ=w!%;8e=MVkXM&2ztZ5O*vM>Rf!N zAf3~N_mUJmO-`8G0%&t#^fwnwioZxAgA0by8M6qlGaR(mS3m;vFKoj3WG?Q}Xwi$S z((lA(7SQ@#2owSzT`;M&*c8w_JZAgwCND5>xLHob9M6B!makRi_zvG@{j^n(c~|SF zY8X6hghCl~jqSsD-6bRMvfJ+W+fSlSj|iWJVogabf>+Aj9;kb)wd%Jj>g2pxyp$g# z)FncM@)41`$#kh@{cyO*W3$f*b2j4X*Qb}{>=)A1Vw?Ydwmh_y()hAH#u{li!bcSQ zaBpV!-H$%PHDt$pBS%)qxl2EkkR&Fn@7zoD)Ya9Gubulm-bZOoTSA!k!>jR?3nteI zlbU8g4kspgfAz^t*E9p+H`oh5eM9@7xPdK5wGRaBBolQeLg-sKysi^F@}B2&aE&8C zgf|8(zGwF|?x9D!p?TK7eRxa>F0PHo@71o&={M)Y>2!-uPw%;sjREoQOGWnCH~gey zyh{05BrF$B2yUOsxc_-A7j41+X?VxCM+0Xd_xQ_n78CK{N71}DxT6<~y4+Qe&YYyI zKXVkf6?4Z4mcwFvRhTpH8&Au(XOn8HRP0=$z0KwQ#O&2*Y)|oJrHc4i{U`02^dto5 z@gD{8(^b$upYp8~un{3aWiSLk-#*{-!DjYXr>DRNZ2JIHcT9p)o2MBE!g|*54jTVw z+}oKWe>_XH8Db+MfN>+)Qr>)xjg1yC8}DlmXr-n&r8Rd~@B6N{O*A;_WTOs78@Jvz zb^1GD++Pp0^Y%WoA$?TUeh87y&xnZyuiwvBsX&%s2?zW*K<*mT;>}?PF}@-Ag+ZQy zR=15?pK+X^Yi;(Cw#%`$49kkIJ^?R7nnaHEn_)a<5p5Ll>&%s9-IfR@u@qvjUs^(- zwqjsN60&d~CRh9`dP#x6u3Mib(~u#tcX$qmm#gfE1+00?;Zo(4+j;`WEzazuj7bsb z0w!+m;6~3((~a|ahkmmY$4oc*FBCMVkqJq<5Gb_=1SXQ&ZmEyUkHz5}s-xKFHzoWl ziK|V^IKL@85NZ*-eG=hLzyKINMzQ_Efz0MKR}lp!7q$^xT9-S;{R?77BrChJ@Am|5`G2+V z?q6%{6*%6H#p)_1tO zGxtY^77fz#DRt$nB~cj~H1t{GD+m__dDny;J=DBrmgd)y8SMVm^uOnUXX}sCSpZgz zJLi&_Hac*uJR2=cUZBy(9;Qk)cT@K#O;nEjr>BDRYh8-&4E;vpCPh?w&Y!TmuiMbx zp-#HrwNjtEM;1I+TrglDpLzjD-Rfz4xPEgTwZyl&^WpEQ9t`AZO&_imt3?yobN0Ho z%U-{!=>NSnLf?n1nj(2N9(zE69-Y`soCpWlNV&aPW9Z?In=lzcRWd%iB;>t=~w^<;={zw z&MH0|8&{mRBT~CoeN1-tm1wQj@gE{~Y7*fkf)n|NI2}oZN$)*8bF%mNv9d@elMrL3 z=pq4MrEKqSN6nu3PBSkYlYU;-d>`U&lyKf~o1w=NHEZf<@nmO#Q^JRp$a?CYg-XKb zzari(_n*&>^6Rubn0JqulwAOM+yAMIvO(yHt8cCOY;GR(-X_`?U#2NmOd2+)-(S+> z*u>nvuA}Lf<%-m>V~p6CNa>~>Z>;%C>{YA>qlD0${5xh|pDpwepGrIp1+>+1LjFDu zG(7$4$akwVQG9hjFK9<*8ztkjIU4@=tYIWV32meZ&*G=r{k1L%!rZeRJsUu9drO#@ zxuW|ht})gKW4@YM3sQS>D0lD2$H&X{SCkJN0na-H`QhBz+P?sso4*U@wE&t20y+4P zv2+(oyHZNhI_3?o9Kw8e@k<-ETedr-&8riuiq}dk>*k+IbTqKLTB2|$IWke7`O9+% zTpT(_k$lqXt3oy979Mj`bTtxAo^8}rnW)4@j`OGi^hHHpBcS>hRb^+6ClTk^gEF37o!f2+ z6--Hth(=h4nYA1>E)L+yQwdmoZ>Wlw9DensTn)#rtCtZQFY3$g{cA)|y1?-I9upys ze#Lhc+QaaGlfYjigjS(n;)pBSNNOC{_VZX|BzV+a=G!KOmp458D2v z5?tpwC{N{#o!rh#-Pzpn!isDKeh^#;GXsI70jTMdm?U|wh6UO?M5eyF*Zvz+o3|m& zv&l7h#ro)ZTGN|JX$jXn((KfS0s6J2eF4K_jREXRsHHRuKUc9s&naS$&F7j-rt(-* zrY@6MtILJmftvE~i8`0u2k#vJ$t?VIw|l~AoSY_~`0vv)N>r4_#OsISmGbN-2VD)S zKI{!H&5~#NvSE?Aw{)a+9)(h!YHt5bo(ZWP_aCo&8y=sJ4^{Zs4;lS`BjzbSyAZu; z8gJ0MgQq(gE3}<-UOi>(hY24zNY5g*q9>_2InK8;&ZBJcdqCw-ZLrh=ya_W~MQlX@ zHEB;$Rn0=SyuJTPnV?@BcABr0Yf9ulWsjo1b0@2QccPB|V+P_r z7Pptp&OqyA`ZPP#;iH(3+c;x}X!P9?9VNr!C+Ycv>vGd~W{gd4+JB%+U4V1hzy0*d z`R1pFpLD4DGuN6f3YO{@B28PyNBjCBH|BM<2Ue$cIq0g%1km^JR@1G&BLRYi+19fE z3Xb7RxGmH~cqhw8Cm+R3K2Tzu_cst&gvuTrPCFo_6*#53 z8hHPRCo*2HFo+sB-Oi*I>`93gE|-ubM`d%SMP22P^rZ3Yoljvcny;TuvR5ywID~Don+~Vw+GD=f+f+t>a11QE#<7nY zX`jWBG_Nit*l#;Nbo(;(rM9g684rt;w5O2~Ri#K2J{{d74K5?KCa&{u9`#*pR~aJD z?uy_NO#K1lM?)P}8yD+#C$ob;?zXK3zk06wA#Bwezk%7SlvD<<9Fe@Es7xI|)3DBz z@*8iaU6pX(k@{ciQKAl&?_B7P%Qn1w@U_meGI)2DU}2R{nC&{NEC`bEPMApUw}%konpASbOIe6V|aK9vT|J>dd`(@Q^ik|MJZ=+QQJ_ES2dP zNf8UDO%5_PQ>g;=pr*igoX37`Ru==-E}Fi1AETA_GT6W7^p~B;+@G4k>bKF^%7Iem zE@I7pq-y?ry86%q@(`b;J4?Pk-cPK8eivPqRrJ4L%NVP;c(6UVNN>V4d+r+r#@u*( z_+j#VQl-eUuWG;edVLL;7w!YEX!%oxwaOawXn3w34KHke2k?SK*wG$4N~0)20PmUc zq)PBjknSdPRgL=IC+C;bhp*Auqk?{TqR;ZDXx z28WPOI%a-Rt!`dHuS!LnD^#k)kK+(UUm9kRCR^B8-Jl~rZy9Odajy~j^#5=uTx=BX zP-V2m_$o$V4}z&FQL#|7=?3+6D}D2vCN9*du^oz?GnpqfWKJZHc%fZWlwtG`g zwqoDSOHQ=>qRO5={44nT#JcNladxRlj-1NhlUNFT8%T@JM)qFi2*fj%NxG9M=AgJ& z!e0CE>BT`Gft(AXjrYv*f?j0{MS}ADoB%omlvBlh;_KsC;K#I@s2h9Z^ywL4<@z-& zB&#ck3D3NzC^hZ}0k{EO^n^QzwZ{ByijKvO71dxHDGAPBNM+KH#qx@XOpBr!hYrdk z&UJDRzDKL&yXW~P-y(+;x~h^0#Qc|iJ;(0ip2L}l6plo8v`4r{8b;ihvSY(jrX27} zsc#f!vG|n{j-Q8|BnS~06#K`k&?kUTRv+AIRe%kuw>L}e;;Fi9W%XSp0@@@JhGjRm z|4g?Rgr6QxLl#@h=5H}LpzsaKgV$@Zq~MY;zq{dO%Q3us4Ight0kbU)#E4W=8h45|;BPZ>HGSKA7345l{KI@+gJE>c?DC;sF_8uEjL z6x=DA=c;nJMs48J@Y!S-yo#KcmxeD92|6L1Yg%BSB=9_+Lfn(W`7s_uBh2&?OKJmu zY2I)YrLl-(Ebk5Ywl%i7I~(CEI5oHj>hI>i_!APucLSN5m#AI8WhT zc4vSNU7$wDLKq(+-%@hc&;$|xj(p|+OuZj~f<8(1=Ua{AN7pJ%ojAgkG=o!5wqAbE z;VLLGg?4{P7w&uizNBx=^7>3!R8*Azi|&3x^e8nk5i}AvlIM?T8|;Cnb=d8tp5!Ss zOf>JdF5R=;9``a!?o=s~?`9~WzdZR*R43hPR{)7C;iiNw!Eq#=jZRvG$GNCR+U?e| z12vsJ^>Cel7_a4HI^QIXMGr*k){IpBiK4Ur4bS`7%FIQcI&~sygT?FLq9Jq^xzaa8WFzy zLnvCY%V%RI!IUN;#m#p@5kWY)))zs3#GHDxPOnoEb-zyy-g?yKi!<&W$64zU&m_fn zZIx*_5Iwe=x0ji zHb>hC%yI@|B4%dhz{l5KJe9H3+3=QNace8J?L?hLrm@b}&p}SSI?zQs z{&0W3u;pDEHsB8UNx55$2lHmE{{o_6m zpf}_G;%A^m&AL+tyg%l6`(rI2W9}yP_uc?%vYr6mycDhEy$hA^T9Zwh)jK^qmREx+ znzvRg)aF={7(0Vi-($A+xRdWH>HFL`qTziXsr5qs(yQ;OSsnze4cLo4JZikmsqZ(! znQp`pTB;#+!tf?8K3%)+W=IwDjf)EkG(nN7s^(%3)(?-rlwJeRXYR8$D8t(P#}%`|CS zmY(I=_HrkQDbca4E04fwK>Oqj&{7n&ad`YN&A?qgSz3yX;`AIWlMFFSD0hZ^02sZ? z$;0o}A=;&EcegjM3d*Kl4q$xo^5q4@$+NVeYa-`~=!dQqD)qcWk$YrjU7->p=sR

%Wyx-byf7!a$~$(#2v7=7ZpX+53!47lUNZ$iW+-78u_ggC(!K`w7%kZ?HFRYI5L# zVTV*gfr?0k@j9{k{Tk$a;Qv`7h`umFfD;X9oO3i)-7Hma0eq*hTvj8$|0C7qsPScS zalvo-p*9@gxa%7$9r$RRO?=B;MEH0{G(hjV_uFH=t2_hjO34rLP z(k=JrVD4Cgw^g01nYGRHt{IcP=$|?m-L>RiBpl-*cW4Ln zlY%2DBXH{VOlCxE{F08^ik&f>cym9hOE0F7X_uCw2>uDjg~;LYrDKCs^A&xA5@k`* zfBv|FgH8hoNG^j4O3A#>#f^q)YljxM764ed`yL)(S#29}fSw4i5$;^1(ZFzAb0kN{ zm6`J^+}nR&p%@T@Ir44R%`5kCIlS-VL}Yg`ukQLsgd?V# zd{3g7Lti*K%^gN0PP(-}VAkG{Ok#Y8RwY_*?(7uJT90v@^quY{i&Z3Pc$Xi}KF&tk zA3*!wOcLLo2|DI+P{wX+!WRT zB1DPf1-?!5eR~+nkmC3RdyoM{TiSF3@u4h9UY=XjsH~mU#R3TqyxhrsKUMJrT2V#1 zU?kla!MLSymxNexx77qfn@;vHpZ*>@WtxnpWsL8xG*ujj)ddcxc@j#S7P`7?hQ#WB zxV7l;0dp4*Y?sV51JcVy!&|6_uZp46V1@Ce3V3!Qo)D2dZ&6xH(QmA;z$Wi8({8>D z#I91{Nf=kf>FWk?m{QO!tecW1b;q3h+Z(|DLo*&f)(&_0>+w^fHw0KaFXlJMdz}G% zbSsYPqhUREnv(lST1_OMwYv0O9<{iOUH^7BHrRB-vE~IZN??fErHeq+{7nGeo&sbZ zuP@~LJ2<>d&&|z+z09;c>H{|$#TuAoA!6zS!%(W}l@iU#KYrh9BEwNncrFkm69(a4 z%^}+3o^jLp^|m|fh(Kw|o;IRcf`igVQ(&Lz8qQJ^PiJli?dl?N2RH6ihBGCcOaJ_BQTjDZmqG%@;CWZh%bPgb+hZ6KyRPKE5fBjx z(G^?(?(L_?=;{@rY461P~Fkn{(E{)Y!`^a2-y zuK#UyT%X@ zWy;Uy)|7kF($e|n-yXM;5nGoSft&P61S!;OzSoE>UmTF%)fV|5A}@+7|NaZWvS^tv z`&u^nG@6Ofu);xE*LmkukCGrd*QH8O%tX7D-{i`|ORu+ zpPd%6)DXgfHc0&_ESwj!9o#VqT2!T4k>>ES&yb{5j4hKRKf*hnUHgRs;!3wSSPb0O zOfO?2jtqNpyi4&y9}KiD$p;vg|6Oghjh_!Lx959?xOKs_8)=7vq2HZ$gUb(6n(pZc z%eOY`8{b?B;pA12P++5A{9t^ulnj2%LI#9K)wNg?a%QP>YC8Y#chv8PDKD%v@mCxK z(N94$dm~N*1#&=tK?xC>_&fUj8tf2u?gI`^dAJE+U=z=(!H!`iC|d!gUO^Q1S+wzz zo0|;2YOKxsN8H3P`yu3-;DVvHWqN+TX!&f_d5$-R+7D|*mN#MYinm)`_xs1ZskQQA*)D{o0ND^-=z%~Q`c2jrb0bBItsbqI^OY8Hy2o@C@QrQ z93ZV#0G@gp$^Hgg93Mb+`>e)xQ;;4y&_Gr`Hg>D;g^D170J`lAzGKy>C`8%oN#Kl8 z5wwKyNOIKjasbH6Hq|}Sr`GwAyaTqsm@r>(Wdhxn&E}6jog5h`*<3w6IYyc`dY!+a zax4R>oki&Omptzwg-PErOV{bPd;qbJH2bg79RB%Q!!P$efEPhX!N$eUA6wCWk^|O4 z1Pu)hc)1badp6DJ-ukHW5m*2qwIqXSJ-9HXfztv0#b@PP`fc}C`Up6+q{EH^2*CHU zIFugOV+ar}9{Zzn)wX#K_|F!p-$X;k^ zW}5XyqGbowcS8~$u6_2TPowGA(nPK+Qz%*xOvUD|42BcjY9nAf{Iq-OcZI@O-D#BU{#h5 zY-d8~|4buM{$y{y5mt2Lk0;j~{(ioG_V0JcE4d*9{(HHLwAy&<>bh?edF;rVjA486L(@tZa<=!v<%Y#t zp83%u*4OVju3Vj=V_GAKFt)dT?$a<@HobUn9zCNQMelz3gd*;Gl%YF;j7R{34|m9} zvOF~}$2pleEom^|Ujjr3c>ke04*1YhIA7TTm7oIt{2cMqh^ng~4h0#h4(xCC5VUht zy;rMXPESp(xl(pB5X>z=k7|rH=Kw39GlsPo(g+bO&DqK!d2(e#LdN}j9?%varKUpj zcZ$+XTwFW_a-tlt48vR`1OXY-dLl@JN4dGVBS8i14LV#yn9)Mh4;`ss#E{aOG7U|_ zVVmzVb^-48k9U+RcQ)p_Vai6t-rim`2hsJ6ti^5^Ln&q3i3cZ_CNK-kU~$2(d;tVE z!ul`IG)N(Yyv9XkGms@I+qA-R!8OZqV&@Wi)B>}82R*t79Rh13QHr*(+q`%K;RHjc zy^k?9zSLKC)IzF%4=k=pE#NoX4#u8e#<~&fK6~nDazk>KK6Ld#N<}nhajsst#pj}3 zlVU-(@Eqdi@j4>&r-YcH*o;LM_Xl4oJ6)34=GyWQ)VW^V?k%}V4w?b7e& z%Wy<5N5q@EghNwQ^JE*<_B^BoiU{TzzQ-}dw723k$iQr`Ex2*1~s44`uF9hM1=oRlt&t4>-dsOc{+(ilz%ceBU?fPm+@p3M>eJ}$* znVud6^s9VUCz*RawPVB)AhC`;e?8-D)&3mFHvCSxQzai_o`(%W4Bv2Bfz>_Bf#~(D zEMxL(yDR-nLzxKfhetT4Jg?zH{-<0n$9r z?n)%BSWNob+mr&Pl<0SloyxE8xQmM^Z99xhO~(;w^cvQ(>tW6uiTAMwSJ9)C$@3x* zKqz!DLam}-#d5pwEG(YUMUOTBiMA z(SC5U=Zi-@9T%$pgMp}@pWnzDO2>8|Pd!`HF0~$QpGy$VQVG9Q5VA=eO+YT>otr%G z(RV(x5|rv~usUDrWOt%7PpY@RH2b^jDgHjkAZp%~X`P|-%j%qbS~_o0iO8kz=e{hD zmeO9&6tjo12509HDR>$h8suTkcO}pZo7TB9cZlQRA=;-u@}?-O%d@>Ssjymb7_*Tm zL6znw;*n!~+YFqG4VF~kZ2Jg=Q#WHC4gtzv1}uqwllJJ8t7>bK>wV;v>wzPU;)y$q zmuHctht~6f!@JgektiA8+}z6OYQ+j4Amg!qT=@6QX|Rhp8pBl^CEca(odq#_kvxE>YLP_#FdAP@-zn1q|9F?wt>5tT> z?khCsUOjprH6d1xUewdrv>=)2uB^h z1Wa?F0s;;SZoh0}=`OBengK2ZryBr`tW=*%e1X@ zpAf%HmEhzr`L~f!uZe@=Pn6ki&ZGNI00ypC`c*o;CdFO!S`V^@{Sg+S! z;P2UwcVm~;z86~dZ8jYIlx{;t>2r+NU9@nyC<7|f07#Q~8R8EbySujue?Eka_y%mC zpa}5Any(SXzEe3ix5R9db`)5O=R9||0{%4TF9IJNF+{@X)Wnn`+(W?1sA~XDjT8Z4 zMi0bt3LZ0Ol@>ysq3CLPu&lz^nclZW^w$mPz74kodIPrf#yJ{puxHztUX}qr`RrVx z_(^x&!QbDP4Gd_Y7-$EFE|foN!cW7_N;+pdC~OZP_T)zZ92EvSoSknX{m-Dwfgp@U zP1d?F!koG1YSGn79goL5Tb581;zMEXT@>p+CZxgmrW<5USdqNsW=u2|fjdPJC>(mSsJ z3R2z#d{96a;7*v=xdwLMT-8K!6#n9?;;f9^&&neajoh7dZ1kC7wTTy2?804vvP)^l zSO@9q4xCqK8!E3oOmrBnur3JP`sq30R__S~rj4(T1(0j_yr;sKC_nY|Y!~}$?bUbw z?57}xoqfbX8cj%fN#G9MO&C+CnZluqLh-D%?s7m}BptrrZ@u^HdCE0Cy`Plfd+**J z^Pf96{PJPoQzw{w`1VTtr|JI9O9DJ%Vib^|J)daQZYBRP*X8n-mhyUy?cAEpXZlKe zj&t>we4eP?h*GM4TX7L3MEi0^H-__NwtfuHCibn-Vdlu{HeVo*s!4laqP|SN@`rQA zN1j1dnRp#hivvxZ4q4o6%QCVYRKOvEbg%xFb}Ocq1EvX}|zh2zjKE=kv%! zloO1fj+#$?eCSpS3JMpna)vS&V2Y&mT zr?4zNL`Iv~haJ9YXk>AKm-Nx0X z<+BhFmKb3n?2Ul3V#_0>-Sn999jgMj=NqgOh8gB@5W7P;^Bp8FXZ$1}yvHr3W*oW$DpP~iW$4yXU~gU1PwtW^0f-_ldw=!#x|zo!hr{rM~R zX>TY$hN=>1Uo^nN_#;#QlZ*mBK`V)iN@VM*M9#w|mW__(IyW!YUy{#$5D|+GMOoEn z)t_pW$Li!pdRX|Ux);U;iOvoX+60$t<&Br*e#LT!@B0=8aykW4$S9QlsE&Cs>_YbE zka)*kc6)9)v&^tPU!ktLQ=DaYZy^v8WSEou{Frde7rqFau)Qnb7c4D5JF1XI14eU*D6jpZu(Gf*9(_Ny-*oabNoY^Ybd>y&? zVjJF(7`wMLv}e0{Rm}8SxT?z(3Zm%e-7K+a6^YA`NF=ey3Q;(X6skqjU#+o%Dqerb z47fy-uQq(x?a-gOrGwLL)$#DATNq-VSQ40*d1Rx6JqQgg*CCuCTozkRw}7kW`u}3> ze_w|hHcuUwP+bd)$#lbFy+OuAwv4b0`}hd`KFH>Zr7OV)R&_N!ULGn=cqhOa3x!`K zR#+iS(?X{FVB^wWO7oF*$K`viWYA%`Vk5;jisWK!U?6H<4iyqT0~WE7T1lSOfT>6u zc*TyODX9kTliOW9v3#V^6T@a2f*HX(W06PJ$p58)KFpoDr0W)YBhjXWxa-RcIh2 zM@2Nr&!7Mm5UFpt8?<~n@9ABPh%plGyW!Fxce;TS>Nas{!Q@q)uLVsEr&5q8;MSzal^zy>4k~+3u}=2%xzI zm4JbyeRo!!e75Wjk7^*3=wy*9MhL4W2Hc*z(8W)p$STKL|Uy0B} zf5^T0`8EKudUykFxe|_w8(T3t37g!9kNxjPMCsdtAC8r+ml;#rCayCkslr`nUuRm!_i+0p`zu3*;!c11|44G+c#R2| zG0~oa-_QZZJT(z7pMUism>8mc5-rr>nRlz>VDp<57Znli?X#IPm#)w+B*1T*#!=|- z;KG^2Zy`P6kY~MytB6u(d+NI=P7s81IRe{(-*LXfoQv04McT5eP=T*ljb#0$FxAl@ zJ@-&gjM6CX3GzkrLdyUDlcd!Lc$69}X#x5XXn?P~c-rG@B;E6Cvz~3<)K1SOR8JF- zBxvy8Tb~vEU|J^We(a85K-LHh$qkb>;HUE2;`0s6-0_DIJYQ!>$Xg?%rQ6nIWxzMAgy`B$%-6GDAcys{* z)4LeQ#PhFy74=n{3!<@^ix<4Tya1V@4yRkV%9V6Zi^!Rv86TUZ+ikRaw8P)?hj1Mb z%>qO5Hc0;l-Mgms*lBwt=ml0bDhb9PZs=sqX0+lcuiRVy*pZmUMpj|7 zc`lD!S?K@w2|NZMV%+v)TXaLGsVFJkw#ZLG%6+n0gELeC^-$<}q=BV?ryuR}A8?l=9BS6nxK2WP~+4!R`5&U97ALh+tMF1#b!d96xjCUV!Vq&W{B4?#W= z)CE47*};+(6~v^LXWT&IY8^;VbUtek2E3nY=iH-Qe`0YqGou!_BI_SupKD`=tiSp@ zxXMWgq6^|Qk{<%Gaob2617$mlr>4)Hhs{mmA`nzCh6KhA0Achrfq6SZ3J=dBvM?@t z)hMTrZc$qRb)N4nO6K)sX`Gq8>+(f^AzKoi_wSZhkHQz0sGNoVGjdR2d|OPPJ3LPV zBS#Jk5gtRF?0;K<5H*xf;X$xtwtWG~jazlCQF;8@p8A)1HQZiLzgsgq6=UY%%Uk;| zVHey-d8)+`Z`e>!6{pcekK#bb2i(P%czD=Y{uPd?KHOIRRh*VXbSExAzdSBNe_N*G z977oubi7#5qf^?Y#JrkG*XX=S)zkg?ViBIGcyo=siPcVSD$0Lb_T*W0w=4~c$Y`ic zf9&E}5ayCkP8D=0F-N|wF3YEHjE4Ff+zb;K0IibD{8I8Is2<+KytVBgpG&@W_4clX z9&e8=W7Uz|R?3vloHXS;XE@0yed6)De4G|nGps#~SF?`S_yB;_(Vwbb5HWaFX?`Ig zg0smHf9>+^4B5|~dHeq{!s31(af2uTwuVQ-&mYY19;SAY3_rh0+2p?__MpU8#H>1| zJJCMo?!P8u%!iN$oiFApuhUq74RDMxeGvi%HYG!sW7+Pn8~0xoodD-a*c?WyDFx>P zht>&W4XvJHJek>stvv}m9WuO%9sK$i+XqMP9tk92I!e^%&4;S0{w>J8U;hj|$D!R_|f#AU`sl6mkM-s|w5~M+te zw_-X5hA;wUHj?`sbcseRt#7bfU1YT;st6z#q(Hv|*5%uQ0iD3G|Ize|I^U0&hojS2 zUe`WVgpSVx6wT~4E!bt?l)M_b2;+jFA>v!5y+A?~5x`|6X;m@yN*jsfQcwtdV<6P0 zYjxH$`cI2>B7hDv(yoQMu^*i<-Y}ZH9rC-CE~!rHI6_@0)5xGlnLB<*s@f!*qCfwJ z;RIc{MG2qGO|&RtoPc6_n~IY&oPHQsa))T+vv-f0N{EAVUA6Q#WqRc_ZE{t03UYv-v4m7S2CISRGj(F%g+NX-|o`oPxSN7`YoB9q~v&C37j!z!{Bg zvilSQ1Et=1=0W{A1@@xw6edaaZzcku={iiEmtRvz_O^q`nBt&vpnh$nRa~2i4JsN} zA>haoSw>KClJ?R7Fb@dDksYG&&ZoUe{=d(%DYzaq`nPSOubr`E4a-bI=Fz^3cS0~Z zn?R|NhpU{xoZBomjMpIMyuYA2Ch%!4}6Gi zqGsm8j~^llS9ly@?iSP;#6g_Mn23&nZR=K@TX zJ51;cYp>@mM1Fq&Vv3!NpaR-$qvogY>Q>@md%~EO`9V3|qhd_yhRem^GoZr1Ht}mC zQSH*y$ouUEgSZxkJ!vu2ohd2_mKoa*T4_D*l~$KCQ6I}$I^Ud5?3_R7IcVh*7k3xe zEJe~_;$HTtKME_PTo?1et)yon*FkPZEUYfx z?;sjrJuX${TOYOEseixH@)7?5t~icD)}t6Ut|^Qh?VeIQnLkkzrHKl%zA68Bwxiqt zr}O#h8+_AQ5?T#qyd&Jb0%5L!-T>|ayfDA|8j|@ziB8OEQL{>XaQRQX=(mHaVX&zk zR%bxF{o|9=)EoFeoiOcHZin%&E83c>Ov|xEguO6Ng}PzNGw&@N%8#L`cBM;7QjHR` zJ;(d&Z#&5wVcEdRL*p9W$+5eu$E7lhHx1atn^KbZ9xnckI#eK|eL~2`_(`j3l4{nY zfl5F}SNCBGdLDvKFzhiROrUX5d?)VeS*C~=v$?2HzhxoQ`a6RY(T2x@)Sz_LmDD0R zY#Qm!$|D|}Oje8_a9}SAWyQ^X_H)ek`+;{{a+4S5XRpYsld*5xnA~GNzrgXH)C-sP zx1^|9c?sg${0R$_0H?VG5{<>3)FUHjyc!ZxQp)`v4@f%kVT+T5L3cd*P z?LkP{#@t&88wkK|0&V;(b)SwmMkTC5f|Qt2_%xdMYU0yP37o&?yHAHXXfv?%TR%?? zbWpQ=H=n7peptVyh;v|rw=afXjf|e!l)nDuhR>Q3yWo!UwT^SddY;`WrgyBN6`6`! zDTM9};8q&}{{oeHcc`qK>$?GjzOywk_d7Ol#7mCv4#MP`Df_l6Lkh9KDZJ>cI^Sa& zv(}@34c!_mAylHTZT+P;`Sm7B!^~+$Y&UXg1~(~~Oru|P_wBM?l3e#TQw8BN6=n9V z(JI$?#rhDN_U2Wyd-eXL}EbPDEh?&-_|fl>D9%W`q^hC;j)@#!ahOt}`KF=U+h`w-tM@kUqqO8K zt|KCKGT4-L_ar@O1L1T_8&BYw!uiXm&z2`262Eqd*(htC6fO7_R*|^zD2~FiWIlm# zB?5czt6dgVXKqAzw^PlZsrK-29ls%f1@8cV{IP)$bd!Rlr9EOeP`1zRaZ;CRd<(-Z{O}y zmD6tIs&$8o<*k8& z_v?b%OB|$QZXSyNi@(QS&NE35;>GVF(kUFD^c1_}J<$J4Xv4|xVXdf$T)@&k=4?@_ZzoN{DfM#M_RnWf!#Iq7wi&)2y;BMb zKsw9sL5$^r0Cl}o^pA7on3nSLu{&MomfBE{?g&qqamP8$H2160(Jzmue#%&OwhJi}4 znSE>kyltogok%T5u#fLOHky+!b&ULE;1}o???ZWl+C#?sAUb|nP<7~9613;w`>V+{ z2u56>c$MgaNw7AL@_lyy$my4#hLZ9ci`~G@k<`5T=i0kF69f&H6IH?qlxgb5$JU&+ zI@n4U!0Ee6(Lqj3NXYrC%L4^qqB4=S_K-0xH3i+kIKN>LvDQ^BNOr5ntr?r z1J-bXpfdH{h=T07(r|sa_;UsrKwo1aRC(&9KrM>B)t%mZN7lDplLKEm645a>Nfd0>R9BZ#4b+1> z&{8%_OHHU$Wc3z(L>yVL?b71;Is>8ce|)OfMl1b}<7W7g1-c*mWFT!)$0RVIHum=k z*4>#tifs-uX4%$ zVr1)~rJEHs3-T1P49N|9cidHU<}3a!tNlfa_EAk=PFo5oXNu^ zI^fXv{6%lZQw2@)N?@#FP%~5cAkwAwq1Ff@osOV^Td1$E5Bupcw9xYG!2QProUA=nFF3`0IbJKbO`vFdDCuX8x6|I%b(tnrQ8;|U?XaeXj>)+Gzy*S?< zS(|DcM;lG&$B)fP^;v@}9?y+W_j;jF7YxC10d1%%j=Xh?H%p1_Z}T>c-!`#IwFRLf zuXamlyB}EckSFlZI=qTwrz>Nbi@MiVy}n4V-D!#zWpY&xGkjQ%u6y9w@Zr3(o*{0Y zR=DSI0!}#~woU<^v(`L4H=%fha=&~J1?Xa~Wcoi6FLgwYO1lO>*OKpgb?>DqzrmaD zrR=@^GBIZR6RmFCSplU}#R>c=9>D!c6^}S}8h`XXm&8;h`uFeVW4!p;j2~LuLB^xi zpQr=(W%@AvLGN#z0|+^(V%iQ!8((6=ftp_=4;1y!%YDf1wdk=8slJFRr~Xz>On?G# z#uGkBc>%sS4zz~?kSRkxL;Izmj~3v}o0nCqhrGb^ZdV!5Ij1 zC^G--*w$seF#JmsbBkS$2P8w&m z{Ol*SJbbayGtuE8i7VZxyG7Uu(h{3X#@;i#J}C!5<^!CyhQg`Uk0pZqc%w)(8(wl3 z2iCTE4Q4TZ{nXpjJ<*xJhS_Xc5?P%1nfUuU*#S?v58FH8q~oOVPV=7FDFgQ(-!YqV zqgj&}udTtT4OgACDa7hjbp-6a+6+&vMK-!!=C1m4rTNyc(p8hDbTTp9?`uaZJtoD> zVHCp6>8u|$ujFWR7@7L8`%5@l*l3bFaNV`~8qZs?G@jIQ9Kc1vfXZYIdjB&Ri&MhB zC5AQd>#C@=D>3nsZzuDR%rD&8xHz}VR1(N7ys7t`sk?u06s>-I7-YD7-a+H|=<@Db z`1$#ryxJplRq^*>_CbOJV>qz4mPpKJ>9BhVqu!3Yr;ebD46rzkg_{J{tNOZ)dah=cj?#&e4jhab3I<30udL(FA?qOq5ID01 zNp&wKJ1MTi2f62~XeTr=(lKelTc9Kk1WQQ>wBc@9nY>SxQg|Q<;G8R90A*J!WD%-d z1y^cgV)6X4gyr{@X6%VCr6u@tQ-8+CfzitI8dWzCj`HKrz0IH7RPN;eTxVI%LJbH{ zbhfs(*KfWcRn1%PqCa!*I=SZXeKxkoz{RZZt@yON&iIe7rq!|g3%FmGf;bY7cH=GI z3D?cd?C0-Hv5OXy?|+c@E!9}zh~T>9cO>$m&p*t!z;@>}olS0h_t)B}i)%n@;xYSU zR+u&x2KKyHUPhjs5VYe2_g%aw5U|W!C1{93AMWIjf1>T4v+TueJV76t{2v!!=ciNk zz``5#M}h0Lk%JeQdC&2*Nsjy(wfxHHxdt;sLmz+ec~vd`of?6z>zZ$F~}zP{+gH=`AG=khhX#^lR0(2Adt#gMCyRL67(48 zq+O{Tg~yUgG>+r=)Q8H`+q~kJlAD2a_NUBs^-^_-_0*?Pg2kP^b%FA! zzt36kDdhk9au`~33qRex>&u~P@&1jmT8_hqzc_7$QajRZvfT8C1LwU8pKDr%TbtI} z+yB9cd7B+dzED6?32S0x)?zj^-ix$2CCO>%D9Igq&~!gHE%>&y7UjQp-2Qn^Q$FSV zU38dNOU(?tZg+aJ@vU}8lTLn@h9t|6*k!Wr$!Dh{*;A?Ts7n!|=elV*5`g#Gj#M!x zWo8CCdwK08=^;UTV9mt)$PQ=_l~4W*W~$|vC+OJm=AJq}&?gwLqQ~~AKzmf8r-$=( zxrcWh^Qg`-IIySR1tRNZ3m{vsg`}V`YWZg#aoI$b&&U&Piez6M1+|kV9EjM`JiBE2 zYesF~LtUDX@R>t>@QhR+UG{^!@+y)ImUS|s=QtI(11T8ric>xU4YxbI;)V`R;$AYq zBC>d(Cs{g7Fdce0leSrc_P5=>d1c`ex1<~o8x0Mei072dkh;dgQ)&Wg)+E$7Ww z{>LsW93-FMepXFf4^Cq01RTPqkU5>7=bJY-s_jH3+T+ullpRm5kha>1xPn4+fz!AG zAZa1FYWW-B1xrFgVo-ZXZUq^-D6vVhDd_bLHWdy&yKVptkf~8zyd`MS^HqJKp+Nm+UGkq*CRi-DYcjM|YE=nqzSCOQzCPxbdGKsn;p~*d zYjw2a4|Biez?d&*$4~aDG!%V=g5^qa@3nk$`Dkv>*|?gMZ58r9;qgs5Y4e7o2)~Ob zXKiWbO&xuWLSL#TAP^L6lA+JCC-f~FCe9|`o{a_M4ka0_U|=PMUVC0IklfU0Z)Wz#E8#Bt zooQZOMXCI|B{vroeS(}RgI)CdX44YN)dbS6NrpKPOqj@1Ol8(w=p19q*G|D!O6QKg zPNcdzQJ;aY{Z@t69CkS~7_hc+W?0Xs*XO%dx@spkcz4sC#Z_Zj+%usQ!_dtpZhQCTd;#A>nN6MJD95Q{$!FO&!Z^x{`hnK zZxJv$0Z=YV6|sEE`IWl+7Vl3iDMsxQ@=XV!wvt;Xw_lmrof7$6bPX1sI7~zlT4ejL z1nc=|*Ib5PUBaLAavd#hl<|SMrjdGyPn7Xy;1_ubXJP4k%I+EX z5Zj8Q`Y&LDDJj*UQ|tO%NYG%m!7QJ(Q4DGmSI(C7dJ09YlYx(arCZ1LTeh_k7o{}+ zL@M?MzrT9jcPIEb@31n_W{qD!VB)@^VT~O6CQs-{``v7xYljqxSf)KZXB;B!CoD+0 z>+5#Z*(Bd5GJ*z7(YQR@eWPRoUhU8HleO;;hWI~r!xVlu^8OUvxentNzTDGK=c}*c zcns8pxY3UqDwl;=5EGln^UXZ7vql9Su0c^2o=SlQ-PR3il+r80>w%EkJxi}`T%TydbvshKkT);hj(UX;Yw?(VVK zzyty&cCLGBp5KjFqk=_PZ12bii;3hAh4l#R%W;)ik;lHgm|(+QiYrByjQ1DI zxieHUfHcx=e_x2}kG<3TuQ)kl@B)rskM{Ve!*`cgT+?Avkdn}`c%976DowXG@H>N_ zr&PhMLue`n$|o;g^W32`@kP5<%{v_i?e)pXzIuEzmsMWR9{-&tK2Xr-2JVgar?ieQ zUm<(@IBzJnPX+DKHSQId?Y=V5#(KW$XhKCp%il&$PfJ2iNJsg81VMz&UhfeYlulhv zlta+PwXx{C(6yc-W@kH{GP-)$XDlJr;jO;tI^|~IHi%_cQhhoY=WNxnue(+G!Ko)Y z-FrgsXLR|7bK_Y6a$VFQma4nZ&)frkdVuZz=zP8P%B1v0O~KNH?Z$3LBvT+u0lK&_ zzJQ(iWxEvwmqpgA-s=-R7sA_y08-Qwpo{3P#+a+@_XvN)%qO`lXOWq{AkFECWxuqB zt}&H~_HqBg>vA41WZKju-<;==s=B!?E!;2VJ%7L6Cjo+zIgp={Ef zhrZn3s2{Q;M+V))8RPzL;?1~QNSDUY(nusO&iPp|acz7tOQsAHM^z@v5@f*^n@K2< z6M}l(gGgkoxWHRl1{&yUP|W`0S_nwFHV;kR0|z7)m#=LSsJl6sO4CoutZD6QVoHYP zEzZRF8Wq-N((gu>v+qs6qrhwod{knR+WT~uW%#>wNnurEos&%EnDsMjTh`2~f#>sG zv8=ryk_@o>bEsIq=c*?;IMuTe{y7f1r@=-0X3ECO$yA;-NKT~{X zoqHTuZ|aTn-B=^uYxPUwa}h*<1NUD$Y2<)342{EUnsm*6>E28t}`K zVD%=9C)ckpMQkzm%QFIXG?>_fnt$jMyTk2y3f^45x?=xALyonPADWwqIO-x=G0mv^ zxl-iRsh?RtL{8(NXfTpJ&OyRlKThkb8ebCF1^@M>*kf;OF1i<^!*X)!`gXG-{v zZ${}!fu_Ll*xt6laY4Sxyk*Xs?gpH%REi2#2}i@*PO?q#`>S1?6-E_wS_p z$Tr<~=C@Q4_4~U-3s$%CH+U=SDk`2dygZ*3LJMNw_g*zl4K~CaidcaRSrRIdOYe;n z^T2Y3E?au@vIy%Ar>`uR^=U+M;gQ`~*Swe)Ml6^G70#gb0@L24zw_P2#Zfl?7OEX*!At(DzqmDiRq#_68G-kkG}&Fz^HAocm(TI z*L#JR|C_|dcEEi#2*!g3qw#*>YyAd-Reaf(U6~3*b8njH48wgtPax)AZ}OCqe(h{) zOo~w6x3U^NBTDDvnsmGN=zF%@!k?yl5m4posd?t#4f`$CSJ||og%FCl{mvkUMb57% zoi|5& zlt_z5E6)d8gj@L*!=tlIESm5K-ZKjGirI~5KXtrN{A9|+PwE9xjyqDDlCUGH9@u71 z_Lmy#M$m`&v0vCg-*?Ws!zqs&2tA}~v!MXj6u(l@`GhEU17n#xgoJTTM*HReJ$nr( z2!Tyf1+_7i!+X3@lr~DI-h@cXYZ8;pM8s{ab`*v9fKzRGfjcpdB*2#Y&G1WA@1?d1 zPymEWVRKEE`syE79JZ7s3aIre6vDyP7}!uwvg0|5F-;1h&XK!}n-0R^6v1;@|Oi zp7>Om?q4k>+2j+U)m_+7)Ua=Qz-ygu$9+=;C!+nm%x0E5Td6p<(sE^N9cPFa!>>og zT(51t$GsJ@RTDrn!c9XtddP(5MDz-~oFdF=;8-9CL4e*%#_A0Al#_wjNK^w+Rh7t$ zT`;`eN(-Pf{Nj~5E3$uQ`Pv64Ke;xAbBD^m#$nw!==p9`y^joXzzs#X`gadbS1FJO zgi<9=ec}9utLynrxqI&cia+^Q8d#Yl77@%L+`&D;^})~P1un+x$Z$rob8{~~%)NgN zUXWkDQ@GO9N`67}fA}lV`(WgNZ$V=UQuM=44 zK5oIWOmULIIbdYKWx}nPKvk(Af)M^jSe7hY3N2d<6p1duXIOK;1uh0mpZzVNqcj;N z!!(^$ppV`|!xQscfbdWBqHh+W9Qc%NB>(t~>=_`#sfq%HgXFk4kC%qGD615VR$zUe zwc1vmL^kQOM)lhBw6B!1Vg3&sFUoyaFcgnLgs6<_+5( zNsLT;G#c*TxP!@)2eW>PlEqEW2=Xhg692`OM}^+JR(82{%l*S?`rl)la2nHLIc?tE zmm8Q9*ye4bTOsibhm1r1T~zTWWW|9%@U50B5!{gX1ad@w@{IPA+q%0{ThO5yoX zK$h?(+nDQNWSF}v!%Hin2%;qf0=@nH`_-o;t+BTdAH*o8Q1B0m}E zEY+?x;RHP8B{-ttI<~B7?06+Yblsq56mIQPR;+2y$z~_Jt(iMTyF9r=`4S3IY(vT& zyt#ceVuVU-+?=q>YpKf_*EJ4W9laSc`x0#C0y+gF?V1SzGfY@dV@4U8huk-=@YibT{Q~r92};r5rxYSb&&2 zrIl4Z>ANf(qcd(a~ zFViB1AhkPxRLW?@>(0gACo*<%qO|;vVGfo~u9lxviKYjgd0s209HBUfNUiZn3Ef)% zj=CpS|4yJ9SKLPgbW>Ek^ZNBQ#5=f8XgcuZ!nf{>5Y%Alwv-TTdb3@U!hMMd!wR#y z`geo0A((e~7-z20HH*2Ky2I&2hOZlE!O~<-3!#Zk-WGq*(^1n*QTDdA2_>j_N&0Tu z`vje31JRpCqN7vh;-GWXw~Y(}w5`lgK*H>sz>w$c*1=T^Js`XLm%aFB9LtI6WNLukf@h;d@^dGf{(;a>!5pJ0m||5gHh= z&+?2*CJZPFQMPd|`eW_<6(@-+J5$)()%NloVCg^0OOYk^WSrjj#=oU_hr_CDB*5Y> zKvGlU;HS;KG5R-R{f%7`K2 zrjCdG3iJoeJ{19)uT%rQ!%SaTHDCqsEpm)dJi=W+D9QW*tMbvXq8hZNag80WZ0FSA+ zRVc|>139QR`Cov@iKdFWeTLIuW@L0U2(FA*+b>tXiU<)Cb^hssqcW~KuM^4LqR20i z9z@2P@z^~f45bR2SGlT{s3Rp?n%o~t zsPY%>x)|54X(r>o^a$}5{dqxi^qUhaf^KZZie+YG?*&5)jU@+9F4y?2SK}IBbEfXV ztjT#{R9RV>sI2&2|056=GkAFsH^+A`HR`Rq zudm~4R9h)|XcR}N{2p>l1|@mv*LO(!`}-*zm5KTkKr8sq0_K!jEq~6sY~*TL;XR5` zh)`J$J6L_0a%sRq?@IY({Vh|@IP$+bZ9EMESF_7>lP?rV%%F`>Uj}wco%#-xr$)}7 zJ_uGaO^0a%LPBaoj|+saPaz|2*mhkY{lpHC1gA+2GLDJsMkBoq#F zC0G~1N+wy{Au(Mf$Bg4#IKWXH9$YvrzkWJd1u1YM+2)gQEel|NI{~X3MO%tX(W*N6 z1r=JI_lJ1sFJ^YcFKZyxPFY0o4@l>Fw<$LXxM~-QP?D&h$*%FHt9y;2IadiRyFJ$L zD!ccs6eeqz+*51JkH7BbP+gr#5C^xIp_V%I=njl@DF{yM1I+gR*D^Ktvw_x_Uz>PmOn<~Ev2ssobX4PlME_J{aXPTP^M zNOs5?Ib7#99zVn#&<#bEnJf|QLUtz~bKqXWS;T)>^=M$$yMKq#xqZP&l!Y)uG(ohx zwzs&RFmC2^-7@L0^(U7|O?!Xr zfm+hhJMVOs!6^OcWWhNe;8^JtjlfmR=>XJiV)L4JD4042V1Ibv(OS>#1A ziYS?hAUAoLe67&O6%V>hhZ$0hl>RxO&|S=?b>*S$l3bx?OrF7Tx5RBfeU`z9SM3KR7@`r`1?A;i^~l?3cIO)2K@$>o+YZU8^RY z^N{=x#EZ>75;QxsYj4V0hrSMjv7xZ;JOCK#DGg#m*%EcZbB z^UIy!{T0FT&!5fc#D>1^PoLQrUHMh~FE=3{&bEaYP=~kW2X}w{noLO^b}3vB*D@}y zKVxK%Va^SeuLvB3mbdYqNsbTeyS!$n`-zF>ZDcAZivz}~P;SYem~?fjvaI+089O3! z%sxYa-w$pC@UH&uuBYj~Aqp7a415MbMn5FMT;{Uvh#&l$MC@p#!*I?2>ZqK|3o)I; zS1yD@an}!gx~TsWQUPJh3t(UYfURfmvlJm(OSvp}9+ZoA3kuB_DlPyXPzf-%&T={^ zy`4&ynHar`*VzZ5I<(a*L2hB;l5m~nz}%sql|@Hz3eT~M&EW__)&89qh%De72hxP! z@}e#vR02J%@_3y;JZu_>%mlH6MMw8IOH#OsixwK!7&9)@sXI)ejaFbS)$3*FT=Prm zs&oJZR6@GoHAVg}!9PW0%k|geGZe=kef6At*2ektUSlw#@Phh#N$BETa4=3w;T3aI zi--(ZJ@>0OR>7#{!S&vj6?$(P-(qkAf`S+ac0Zf z!BE-8z3ZdomQMg6-C+Ow7@#{#tJi`TDE#b;SPj%_w|q=Ob^q%Ma4nH<2bNaU@=uLc zfb;hC3`*ymr$Fm@6s`Kur{455DnIsKoYvkE_PF1)mtE^mw&e>PqCW-reqm0|AudLdGX|6;_3&6RVLBC_7|@C+jtoTyhtVyPxCQ8G9+B{WZiMKQLPmRljI zez81cQblhg%$A|#rC-RRFDo_~^n?@wE*_@d4BxN5zB9p{#epe)z@tAR*ahl;9w{w}{pAnkDnW5q5X$bXwbt*tf@Dvq}YSd3=c>Ou6Q*@qn zAFm0qj*#z&f8OT2pFg>;yE`AimwOw;d2$et0wjxeO`A3-qMZ zwn{p~iove+nq)>%2jegin-H(UG-Lw%H^L4ZgP_W$S)xcsa0dU-+1t79O4w15P2`HZ z$+uZb%e5W+E;y`^VN1mI&19$b?n0*?+hzm$x~%IAszWUw$V&t;Ik)RcF!OdE{X~|z z*oZIz6~!rc)9U8ryr;090H#CxfYiN%GZ9$!@}C4#sor_yfnH5x<`002B(&@3r}w5u z(3CF}M1GObBg%vK3zHGwNu>Ilf~XC{h>VxWH7b!!Uwjz;V*7o3No5)w&5Pm1=>P`w zM;zhtEk7*Dai2~9451Y*G|!M#Z2;RfB*E% z8ShGBP%7H*KIes6wL+Lar_;eT<2i%#R7}MGfRj+Cw~$)i0a6pI^Qn1Gssv!nH4r&l zeivLC#X=}A!kSD%+7e2h?l4@0aBd4_{9C?aht-TDGb&Fe%7V)u+!Eh3s!w^x=2}M@ zI{`z&=NNXVh)D$6UOv6+n1$Z`6Uvv%g@+vi?f{JGSy>x!aSEHV8`MR+IcoVZ_bqYC zdv9mT%|uQB9Hk7ke3b+pZokaMuAl!40Yik=ozbQ+wSNkw;d7D zHGjD5JDXIsWSb9uzrQsa4kxql zV@b~QlteS{;`d2{vqDHUqMvSvs$Pgo-j2SK{QE@#=;3N;Bs@1Sdi0%@dcn1TmJ|{A z#Yf7bGmZ3H`G&&hmUcw_v_nzwX7tHcU_34LLheg)&%`RHaWlLpXrmZVF$?QYm);?( z?OwqL;{d(plfCgZF}uD7Ymd{9p1^$K+w2EiFMR_1G-K@g(re!qk3ke8@ZVkb4Bx=? z11r1}qJ02Jgc$Qj%7<^gNsSwnAU@!o_Xi6&I_fELthfVDs?woAdR0yQ8zV}bQ-+}P zdBE}TBSaU{?_K-IYCwI84VCkHKm{|Q5pDNMd@yZdVnV3v;r26FeE|}u)76w?Bi>5z zJu?+T=U-~hVD41)%d8$XYk*58gWS2!ShQP-dim|!Hv-tqH~knI3TG@&XW0#A?Fal zSEfz*51(KsoYLyQhqnU zt((e?$-aZ+G;gs9QK{ zUFI(MEM;?`MR$&b|Iq+6e?rK#jwz=yP}xE5r(1dtr3-Ejd7m70cqm+D6Pq{BCJ-@!?$4{&^W8e$uq zn-yqAhD>-z0Ub$YesV`Ww=D zj0`3ebPQFkM02{&s;(pte)O&{_HRHRG0g;ay2%rm4H;=gwp)>$bz67$^o%za78W+^ z@LkfCcY~jq1#t0(pvdSgjpxZSA_=C>xbA>B5Wottf`yhw*XYCUs-6&jQA_fFVuMPi zoqim$jN!M;@L2?Tc@M{6+exlJO#n?bczEWr)9pc(tnECST5;Wg?KCl<2mLG7wf}@NAx6g`4*NooL;((K@45q0$3td-& zK03H{d4R!(#7FA&ora50IiIvw`7q@z+Z+gIGw23ZQMls6SS`G21=4X#=e%!xvIyn6u$hDI= zA&S02!BdJJ79Y?@9y0{FLzm&t^gcb}8VH_NzvfU$L>P*`B|Aq%-&L-I->9iWW17xm zVVNl~Ia^fAj|q`$QqV1qm)jG9=??%(Is@_7CvH8vh<$f|K( zs+7``zH!5%-EKnxudRi@-lpEM8yNa_t1@{jIV#Gk=jZ(dkD%aMe&u>TY{ykptE-IL za7SX~QAUyefJFnR=y~#E+?|jO2xmcafZs`%L;Fj9p#`$nm^HQZA*-cMbCG7`q-72}(;yKAwv>Vv<@bmtHv#!3rKJ4H*KVe$D*1yB`lJNT=sFuDW&$o=(1YpY=`n)nxmri$#UBEb(e>~+b8cPMMobvPENwo~ zc^c-REkI3i0QSPRNG>bhjl9gnLhK+1D6t?5fK+6 zRr&6Cju&u~)!tPFhB(wNcWvSM+M*=~Df9J;{FS;h5UDcgbX$j-G1oBHYfdVfZdpEh zZs}U1{Dv!&-G%X36}}7MOu)lFJ_jATnX@vdwXL47^*JYZ6Ca{PlRro81bZpkS@@By zS|4(uvj~s-Uom{d+CWeYUBJjb%U3F^h0z{=Qo=pz^9@x(&(!lb4U&*CypX~c^hLW9 z5Pg8k=q!W=KtSCUdefPPTbD&coqUKdMzl}qsr5b{{XGBNO7BKqA|^P<80(km3@Zxi zj;MJik_mFA#6dJ;4Py=6QtaT=mnzd&TaI_G;Qq!g`c#n>yyeb=;6ezM7MOaAl&W;^ z{fb|UuITjSqgArzu0K;3p}mIk5hulS#?t9QN#G0NI%Dst+PheHGCbwgdZ5Wa!&hHy zt%jLiV*ooW&i@oHN}tov&>S!MPKo3WeL*<`5(0B0Noa2c%3ujH4HSi+3>n1?7%f8Y zk~@Cg>}pDbrUSASP%baJ72*~@CX&dKK`#Z7zNRSxsf)U*v&t4O8jo5DP}c$ZaEk0y zBFHA3bEB+ydh4)Fp72(P+?qswB!M$73-%FCL9oYa-2{#3JNC^|BvQy#auZL45GebX zxQ>5Gj_*5-@7sW;ch4_-Ov*D*_V-FIC8Lm{d_>&f5@X3dBOr5|1d|en>PH#x?W~6o zJxHctdW=VLBae#n91q)l1rbk9q|I{@7ILe>lh1{@)Atd;D6QO@=`}_IoPj~QB?;SLY&Z-{PBatmM~H!w zzp%`}euyYTbPV+bu(2SNec`+j*0uh%M5WD=?g)r*?DBwZYV)v0+pUET76B(j18 zWq`s;7Kw^*9OnQrpcWPb-)TF?EN>A@!n3k9g3)G6m|duK+x3-aZSe9(-x9TCdwTC5 z-n6TC=b&S!!DM4fIZRY|MK?VhTpj#!-EW`rTzLWC6AcH&^2KPJ&asBC~Wmc(jNo z>6TIloAFAsQ%@s)zq#D1rZii(W4(uJr|352eGf8JKNd8umQn(aK(uiUy3<6V4I`dQ>P9uUI{$09P~z z_mVoU6#d+1g*G8&6or1|Qs&@>o`%pfPlk=G7*SQm0(TZJ(wy|H5b@+=ZX$lfJ>{hX z2^(|M)x|rL%`KsH0Dm2QVmA%0A>?&a&>7?5m(YhnY$D&s;&Ld`ifOlEb?HZ*LqR*pvsP#-P=Ks2bp6zJI1{b_Sj{6| zr?(Pa02N>6Wj06e>lWP-LT$2L1aYlJ;-zg((?ry7Hp7rX!5Jd`h;E%KX#@|J?P!%| z^2dD(CE{xxVr&TruE^~CNg;88Y!0p)vrp=gzR61Jkzptez6)ZB8$r3WxZQsuiEaWc z9BZiQtMm0D?<}tVZ$lfr*ui*D%sBG?$v1+rIiqJp`8;vzYUk7zTuYg&7O3EFpf-3* zq$?_wH}?f_@o8yj3Taj#CJx=j0s-!7nESInE^>)(w;E_(s%s{b8rf-x`@Nj^b9k-Q zXr(7aJ99BZn2riJr|F-P@Fh#oe$CBXlw0&Q+koEljsp_j{vFd{B+PRCUCLtFh6F+N zw!#B&A$1423nbf5C z`&fan<{t+i*y+Y(hYH3@0VU%*E^u7*A3PXcLG_56x>@Q%nD#VU0akrjIgl6P%e7^o zU{_DjQBhqw4~n$^33QAcb=`gz85s%0hD9r@OLDnGUm;jNH?&)MP5I*Jh7*Y;ECoiv zY@O?<>C6tP=ncJr3gwEylgSS-3@FI*U$5n|BDz7T zWQ7goM-c~0OiAlQ|7iF^(Q5hZMk_>MKk_Nwud1+6fDs5c=k5w?(5%@3nCLm^5`b@dN>a>YADZ zl}nSBFX#=AM1z+w>jvhO0Wj%-M-|ys zEOsytx(ngRg`S@NjJoD_U;6oBe@CTkOo{tT*cN*D>9GJJxdfdstahW7G~grm7q8IO z6kmd6l{Y_4D64Q_rQ#wX?rr-bt&(1tG8DDA(z(7^siW^2J(slby7r$9L@7@A?RV2S z6j&7CmbCUpgNp$B8T696uu4kY@CS3caz1`(H+ounx8(*5OL@*~B};gSf!CR;7k!(7 z8Tko)1n>3&v~(}h@BY7A=?9Jg)`9q$c$vMxQ-V{1hBf}TI*&o>H7Z!L673=m8=}3S zQe8qBLDaJFt8Tr_XhmBszf3(*ptdqEkLR^!=Z5c;0l=ZLI((_X4Y_~g#+PAteUR$< zXI&8LF8YgNM!Y^2)XMDGD3?m&yZHqLnSV+3InE$K{~Y?2XF!ceuoAzKry<2O#mm7l z=L{xoC}|)|X%6>afG6f=x{nVd188rR}oGLOnRpeOF z*u{U53V>0V%*Yk(!b-+IbiD3msrO>Ei&w~h8ci2f27>0y-}6zxM@-nO+wVA-I_FfIYGwh(@SxUx z-T)-~_-I#%JefP)qH#`tVFHyLCYymV&{OyKP`1Bb?n3w~4LDrEkZx&e-Y%kBIa$Lq)z*%1s^+g3w0VnTe zl;AIL@8ZnvMQyjf?%|I?@?t3%qLiX(3#w$uh%l1?9t*0$#N%by|h6%&uc*W)^g5 zn-jj>zYz$bapa!t2LBAdhR$t#3YH1iwXsUMkY`ZQn-}fcfTEo#bLWG+%FNvORI+b~ z1<+-DQbl_uluzJb*I)kZl-lUpXv%Fe#+-qG(-UrlhM>@FS40Pv$Q;I=^Nr2w*xN}z zYffbIAZ7`pC5q`RneqQBe{8b%czmtjQ=!Q4+v0SP(vRD{U5`iNg%|F&4$BK8w`C5y#2E9~r*BRQ6++rm&oL7?{O@tlmi;&`qh|6dY>IvEhXM!IC%B=`GXUS1%W zTO(N-gT7As1ki;)%Tj%^xkJE-b%BKW|fr%r<&3;PuaiQPO``FS6-l_ z;0Fn>*|oTv^(^Q2C0}=V`#(SfbZ&1Id1e;f-v%?ER7ho-a`i=(P_CH_Bkwqzm;pI5 zNVSmX|9_jSiS9rz>j=ALI^AvZ$tfwLz;1X#j*Fd9iT1ZIBH+or4#9GRD*RTHf!Yb7 z0D=xDB0(o2UMB)NgK8=G-Xn!UiaFmebh_Dsb0d&6T|N|0rHMPM&V3U|EIOk7n<#e> zXyeC@Fv#*FponY^m$Zvqd_(Q>R{L4UrHO^_#VS9lF^H+tSsVEjvFBI>>0K9*_*)&3 zTLF8Mof^sdsF&3&@3o(CnfQ-Q+6TQp=hXo?!Ipbpp~$ltsHan?k81ggJ>A{g0Casi z3ZQ^uyTEj~krzhY$!Tf+v!E0aF<)@^|2^#kb%-UZ*&{*;z$J0X1BJ1AOw+U(@2+Ha zhR!EgxdY6{l!xXq>2mvG8iqv3Y*Vc>sqQ9rPw@?RZ-;F%lyQ&91*kFu=$w4E<3t_a9 z0VQO-PCZT#!QST{*m;9VEiVcO?gmcyVF}Dm8@Vrp3*6<3V+Dz@Ip3*qE871*@`mXE zUl-9-)^qzJkZk)L(1pBZdK~ChHzu;|cq5VnR6C?z%HRzQ5GIWr!h98wt-yjWb>Ags z482!zZud7Vd0+*W`nP8eGL)kfI?m3k^Lf$iDm`ZjR(yGri~cxyCl1 z(<}H!!{7D(y$2-CGimK-)+JsTN7wOOemWTQ?sTg%?*ADf33g`o&sCni1)PlT3>^=s zW%fZAk^C;_g*oUiWnbS(2c#&7{y)c%%TQ4O8M_y3kTb>Y2j35VT8o~0->LB3;$>Q& zU$AsUlkJft>(%8+73s4B2MLF9<>nLAdDttkt#{v?j?u6;N73(m5DhfK8qNsRsvJKx z+7?q5C64D?I)k7zm!9;wNQ*(*MK2pT<_*=Vp0- zDdi$r?e1z=&5VNGcEEsZ^dITX{$t$S@Jf^I`+I)lChoIQ4g1ZK14bUUSvN@Kk3Or- zqU`sXT8zy#Webpmj`ZuL4RmiNNda)8veO}GKmPe4)o6iCJoj-xYVKHiybT> zC7<-Vf8I=4tzD4nt4vJ*GdZwhD|@tp=AM69vq_ zeyZe`&fdwH_;Xxu2jh2B@|ev&gNQp5_)b*SV=hLt9I%5OqRpU z6tbmJUaL~DPf!ER1HeMguv`eU`c$a)zMhYmE0-hBj`(@-{MYnqPe$$TVxlBd_gll# z_K`=yXxfwQZ4FGi^ukFXCS`wT(ctglmdX$GyD;CgcKQXRr+R1f3hSJ6m6oM}1@K@onA$5W*TDSl+Wh-t!LvUzRzv^qR>P~k&sPr@ z;6CBPnGf|-YS_}Q{#74+O&=9QpqCTiQ^f5w&?vcjxeJqr?qIovK zCb#cpuszNLZm~;?I6HoWoQ5^7I~~qHKUsa_D3ll~GFCR$`||K+x_9R2X+cTkOYF@~R?x4eBm}Rz z*w^gSOaP73zd!dyQd*l)|3>{a8YRU~8IPTWb@oM;3NIPKM0WhN%Vm?Sl)$wtBin?` zQfqA zFw_(}j;5ki*V*$XrG!o?3HuD%fqh6z7Lb1yiyxFPuy?wBz@iSA4*Cd~y|a-Ck>nKN z$1L(9W0!HM$+|Cj+LtzVR?9o)e!b*~jNM!Fx|7t&cw zN0$drOU_1?-?^FjGL_G&=$xM0sEzC9m+f8%PUo~-LeKO}6tM>3mo?tF2u-i6PadmH zlcET2TV(ps8E+|$o{~5==b~7DuC9EHA9h%MIOoFP{fFbz-$sg^hkucu!=w=iiYnDx zH|~?M+2g%Z1@W(X(b`LyculdoJdP@LSo!63K{#3Cs@G9wdt($hy}{ILb$h613}r=# z12>%6FTgBJci*cTJ}zc>y~^NkPHWNPD_>=$sNip|fgk<$^46a;{Soj-Lstn_?e}o# z2h<4K1ZAu?8t!WEUD|VT@z)?24=fK}TytX%ClY=fzIAJvAlf)A~aIMB3IFTa+;VqgHNId3Lzb%zts74D$^?eBOMWY6n`c>cfguEZUx zwhxa%h8D&oyP;%{naJ2lTD*;=NVXV+gm@`T8jL3U%iB9-lC`%iDf?2^v6qyJ%#5*= z2`z>sGt4mao$2fQE537`>vvt}I?Hv=xt`~o=lR|D@4of*XI9S-&BXUD(H6wF1~vF& z<>v^5`DBXx(D*37e^hUFI#W@o@R_6urP;i<`nHZ1hQ&|VV6Dy8Ur#r^5$hB zDdI;U82^fP8q=(z=ar>Kk;&ygOc(=(ja{387b6u8KqxaS+FhZOi6EQ|In;vgu*h zACFqyOxV_GosJZqim%0Pf35yL<`l`>4C+mIXOHj$cd!%+qyBh>E03GfQ)HCiE3K3A zNu2J0TPYP7=SBQ3={Q;$?4643jnnxqrxAPa%*1ey`NF8Dd3<;dhg7_6D0E1YIIeQoid-whD!iyl7In+bF!hxEtSFwL|D8)UT zUqiNdV!gve{&_2L>#tSaywioe+ds@h#J?Bg_{E((Nw5Uqz5zp;?le9(SF`2wv)ZGT z+!9;f8z7XGKPt;AMt9~nPy0d`@_%2yF(4OlbA0<}9ZS0}?XMi+2%&0Y%bdRq1_+K0 zl~+0$CT$CeMUy2JZNqQ6wol=8zNjBI{AkU`2V~7)>D*#{0IHUoj!34wfa!wBtv9@E(Dk zOiEo+4^xhG8Sc6=@bp@39oJN7&2G(+OX+)>v653Ig`B<;s|r{~x5AnTg(u4HM5YzR zv4;rz3X@jf*<(6jdu6bq#sgwxYbMSF4l_1-Idc*Y>H|RQRDVK?t2F76=4C?`BcM0= z#N-P;7$AaJr3x>dRbIjL$}IP!rI%p(yR}J$k}CoIcB2{ErxEGyzP$vbx3}&nhNy8u zT}!)a%7@qWCu6+2+HZukdl71~hNgz2);L4(YFR1I79-;7SNA5!Ox>A-6klKL zv|wp7^s4lIvO|IpFD&AFLiQC(duzIvt1|I_Wg_W79t_a z?Lh|*nnt6{Um31Se{&Y5`x=eyEcmLcvzLA_FHp_^7{Scw1^3M>yj&@YK%6^GR)=pr zcPz#Mhz}UOoE^;UMAQtgcQy?_ZAAK+?L>;7v_#k397U>GIv`}6W?BRIio|;tj}Kd} z-c9x{3rtVJj3t_%^Z4c zgLtpvB0@C(XxP53%ra!+UxYZfKYxyJ+E{#j#A(K$?fECYv=gs5`WGI}F87EzX!SG% zdUdY=V#E3IdKE63L|?VLB9vQ^%-za>#{T|ye=y7-Lcp&ZY6E^i&=)OT03aSgQXWaoC1Z+avvWs6Z) zRp;ScO(o!G&J$LOE7dNcHUx{(Wx$ZYP>?~uBqe z(O}@|xsS56kT`gE4&BP=DK7M^Topd$~hBzppILR{f z=8J*$10;fczgGh@tfk^{dWmu6=xjMjM>@H#GvtbFeG~CUn1X{z0*ljQL#JV-&;A9jcP_`nV z;eJJwRC(5<;kil4df2)zQvN-W>=-k>PFiBl(@{OH-j{A^w@)af1so>pVx-E$7)DvB zWB*7Pq`hak%TiafdoNz}-D8pQ!YC))e4m`}yor;TWADYwiMxoKmT520vzZy3wd6X> z#OkbO_FE@%vD`{g@a3NRqD-h#?eqwbO^Xj96uJRzPE8pq(Ek3ah+DF=ZeA*rEKJqT zhy0zYoyoG4nTHuP5f>`L`9D}*NiJdB9=Vm2s68!AVzp`ZP?9`JI$9X9KBY;$hH{0f za%lEkbGOJeA->Rc%y9jBE7w%%DU=G2Q*rs{PcseleLxI$4p|Cv*}tq@iZ2q`-3)6W zy6{kilHUGw%9%5Bt16|a@*rG`_u1ZN9S{9KNoJ2ufqs2Cc%VLT-C@^p5PmxJ4fErJa5!KKGkBq&e z;2SHuUULq~QTTZ=@A$%zpHF0mSYzbNcWj@Gp~IJW294bAT`qJSB-R2EZBEUf6siTd4WF9lI^Vc2cbuP17SMn#h-z2kS zM;Y3}v88@vCCcF=bBo4TQdhwRm-x$}Tos`kfa;B?8J%T;F?P0_ z_tu2A*P;+}Q;ynfjx4Lo@`PiBpsfmMX0{we>epx`Z+v=fiiDF$(vOiba~d`Qop)B^4Cxu^jp^)qjpLXq&fSuhIooKihfo55+EHJldkFL_;e7!ep_6CS zuAUnjU=>)u@oBHX*uqo%I)bm$L%XVFr1ksbP~J)62F@|oe)I(0!FWx;FaiNwl>u3B#lcniavn|uB`(ij_!CSeT#KmrAq30nj-Q zTug@CI5`;_^-re?$}ZxA1DAtBSH2RG8@>6%ek+fa@Y(M;UYqnLZALB!U_&^UX_FpQ ziwSW-(N$7wGj)mV#<||i7QQN-Z__?{Hr}`UP=(+TPX()|-v_Y2+l_dw!TH1^Z}1uY z$%=EHx_b&Uu>Z(66O;LU7FAnQj&`sA-M{3wI}*AezL-Gvr1N$zh<`CBIVVS*`Jl&T z%V$w+uycJt2@Byi3Gjo8X^48lG*n>tcNz>nU{YM zUW2vbsk6904P%i~@SthE2NIeH8Vayb$nEUr*UgG;ht2NN)w<{>@+_)gvb!4T@86xv z(ujdqZ8uh)^8O``E@-I=Z_NT5x=pp{-5z_$3IND@XdDNW3f;rn|Lh^?@9K-A6=uHRzg1zF z8p2>Y2&#mbGSbx63dGBsC3?fQN2esb{kN3AVSO zucPpt$lj0E%uZ`rBHS-FiyW6Zy=Z~HMVygSi*&!hk|Wl97A{tpsq1b%?mxX_(4zs5On6*=sQz|9RSoV*QNMUEYXOGdb2EC$ z?gn(wE9mwGp71AOc!2G09yxB@sGl$kROcV`OkXX3*6X-rAew>vh^AnAWxM*+gZd>T z0bvFYl7+Z}i_DxE;No#W&gsEnbaH<+BGActP(Ai%I6oAjwrkV%)32N|0G#oLE5{nhQwplM4PU@ygSMLX{Ie6r z^vM{n^QJG1laXWkk-{z1slIl tdGZ+8Z7}_ZwpXY-FSbQ+&i~OuDnePLG+AV3wM__~+WxqsU5za!`9Ia`z4ZV9 literal 0 HcmV?d00001 diff --git a/images/app-state-machine.png b/images/app-state-machine.png new file mode 100644 index 0000000000000000000000000000000000000000..eb11fb667fc0c277f5fae391dfbce9c159290d2f GIT binary patch literal 86932 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 literal 0 HcmV?d00001 diff --git a/images/cluster.png b/images/cluster.png new file mode 100644 index 0000000000000000000000000000000000000000..82a98d70449a6165a13bb14d3aa693c7a73e7755 GIT binary patch literal 60161 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

p9zE%E^Cr)`Kv8EO-u$0jRTNS$y|m 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%0A+s$ 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 F{{nKeXT<;j literal 0 HcmV?d00001 diff --git a/images/cluster.svg b/images/cluster.svg new file mode 100644 index 0000000..52867d5 --- /dev/null +++ b/images/cluster.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/drove-home.png b/images/drove-home.png new file mode 100644 index 0000000000000000000000000000000000000000..468ef691766cae62d8c6d7c37e2762bc18ad089c GIT binary patch literal 112127 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ද=B6>{+h7f@ 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 +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + + + + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Introduction

+

Drove is a container orchestrator built at PhonePe. It is focused on simplicity, container performance, and easy operations.

+

Drove Home

+

Features

+

The following sections go over the features.

+

Functional

+
    +
  • Application (service) and application container lifecycle management including mandated readiness checks, health checks, and pre-shutdown hooks to enable operators to take containers out of rotation easily and shut them down gracefully if needed.
  • +
  • Ensures the required (specified) number of containers will always be present in the cluster. It will detect failures across the cluster and bring containers up/down to maintain the required instance count.
  • +
  • Provides endpoint information to be consumed by routers like drove-gateway+nginx/traefik, etc., to expose containers over vhost.
  • +
  • Supports short-lived container-based tasks. This helps folks build newer systems that can spin up containers as needed on the cluster. (See epoch).
  • +
  • Provides functionality for real-time log streaming and log download for all instances.
  • +
  • Log generation is handled by Drove in a file layout suitable for existing log shipping mechanisms as well as for streaming to rsyslog servers (if needed).
  • +
  • Provides a functional read-only web-based console for checking cluster, application, task, and instance states, log streaming, etc.
  • +
  • Provides APIs for both read and write operations.
  • +
  • Supports discovery for sibling containers to support dynamic cluster reconfiguration in frameworks like Hazelcast.
  • +
  • Support extra metadata in the form of tags on instances. This can be used in external systems for routing or other use-cases, as this information is available at the endpoint as well.
  • +
  • CLI system for easy deployments and app/task lifecycle management.
  • +
  • NGinx based router called drove-gateway for efficient communication with the cluster itself and containers deployed on it.
  • +
+

Operations

+
    +
  • Only two components (controller and executor) to make a cluster (plus Zookeeper for coordination, drove-gateway for routing if needed).
  • +
  • All components dockerised to allow for easy deployment as well as upgrades.
  • +
  • Simple single file YAML based configuration for the controller and executor.
  • +
  • Cluster can be set to maintenance mode, where it pauses making changes to the cluster and turns off safeguards around ensuring the required number of containers get reported from the executor nodes. This will allow the SRE team to do seamless software updates across the whole cluster in a few minutes, irrespective of the size.
  • +
  • Blacklisting of the executor nodes will automatically move all the running application containers to other nodes and prevent any further allocations to this node. This allows the node to be taken down for maintenance that needs longer periods of time to complete the OS/patch application, hardware maintenance, etc.
  • +
  • Detect and kill any Zombie container nodes. On mesos, SRE team needs to be involved to manually kill such containers.
  • +
+

Performance

+
    +
  • Scheduler needs to be aware of NUMA hardware topology of the node and prevent containers from being split across nodes.
  • +
  • Scheduler will pin containers to specific cores on a NUMA node so as to stop containers from stepping on each other’s toes during peak hours and allow them to fully utilize the multi-level caches associated with the allocated CPU cores. Some balance is anyways gained by enabling hyper-threading on the executor nodes. This should be sufficient to provide a significant boost to the application performance.
  • +
  • Allows for specialised nodes in the cluster. For example, there might be nodes with GPU available. We would want to run ML models that can utilise such hardware rather than allocate generic service containers on such nodes. To this end, the scheduler supports tagging and allows for containers to be explicitly mapped to tagged nodes.
  • +
  • Allows for different placement policies to provide some flexibility to users in where they want to place their container nodes. This sometimes helps developers deploy specific apps to specific nodes where they might have been granted special privileges to perform deeper than usual investigations of running service containers (for example, take heap-dumps to specific mounted volumes, etc.).
  • +
  • Allows for configuration injection at container startup. Such configuration can be streamed in as part of the deployment specification, mounted in from executor hosts, or fetched via API calls by the controllers or executors.
  • +
  • Provides provisions to allow for extension of the scheduler to implement different scheduling algorithms in the code later on.
  • +
  • Sometimes, NUMA localization and CPU pinning are overkill for clusters that don't need to extract the last bit of performance. For example, testing/staging clusters. To this end, Drove supports the following features:
      +
    • Allows turning off NUMA and core pinning at executor level.
    • +
    • Allows to specify multipliers for available CPU/memory to accommodate overprovisioning on the cluster.
    • +
    +
  • +
  • Because the above are set at an executor level, the cluster can have different types of nodes with different required performance characteristics appropriately tagged. Relevant apps can be deployed based on performance requirements.
  • +
+

Resilience

+
    +
  • Small number of moving pieces. Keeps the minimal amount of dependencies in the system. This reduces the exposure to failures by effectively reducing the number of external dependencies and possible failure points.
  • +
  • Controller stores state on external system (Zookeeper) for now.
  • +
  • Executor stores all container specific states in the container metadata itself. No other state is maintained/needed by executor.
  • +
  • Containers keep running even when most of the system is down. This means that even when the cluster coordinators, executors, state storage, etc. are down, the already deployed containers keep on running as is. If a service discovery mechanism is implemented properly, this effectively protects the system against service disruptions, even in the face of failure of critical cluster components. At PhonePe, we use Ranger for service discovery.
  • +
  • Container state reconciliation is part of the executor system, so that executor service can be restarted easily without affecting application or task deployments. In other words, the executor needs to recognise the containers started by itself on restarts and report their state as usual to the controller.
  • +
  • Keeps things as simple as possible. Drove uses a few simple constructs (scale up/scale down) and implements all application/task features using that.
  • +
  • Multi-mode cluster messaging ensures that faster updates will be sent to controller via sync channels, while the controller(s) keep refreshing the cluster state periodically, irrespective of the last synced data. Drove assumes that communication failures would happen. Even if new changes can’t be propagated from executor to controller, it tries to keep existing topology as updated as possible.
  • +
  • Built in safeguards to detect and kill any rogue (Zombie) container instances that have remained back for some reason (maybe some bug in the orchestrator, etc.).
  • +
  • Controller is highly available with one leader active at a time. Any communication issues with Zookeeper will lead to quick death of the controller so that another controller can take up its place as quickly as possible.
  • +
  • Leader can be tracked using the ping api and is used by components such as drove-gateway to provide a Virtual Host that can be used to interact with the cluster via the UI or the CLI, and other tools.
  • +
+

Security

+
    +
  • Clearly designate roles for read and write operations. Write operations include cluster maintenance and app and task lifecycle maintenance.
  • +
  • Authentication system is easily extensible.
  • +
  • Supports basic auth as the minimal auth requirement. User credentials are stored in bcrypt format in controller config files.
  • +
  • Support a no-auth mode for starter clusters.
  • +
  • Provides audit logs for events in the system. Such logs can get aggregated and/or shipped out independently by existing log aggregation systems like logrotate-+rsync or (r)syslog, etc., by configuring the appropriate loggers in the controller configuration file.
  • +
  • Separate authentication system for intra-cluster authentication and for edge. This will mean that even if external auth is compromised (or vice versa), the system will keep working as is.
  • +
  • Shared secret is used for intra-cluster authentication.
  • +
  • Dynamically generated tokens are injected into container instances for seamless sibling discovery. This provides a way for developers to implement clustering mechanisms for frameworks like Hazelcast (provided already).
  • +
+

Observability

+
    +
  • Real-time event stream from the controller can be used for any other event driven system like drove-gateway, etc., to refresh upstream topology.
  • +
  • Metrics are available on admin ports for both the controllers and executors. Something like Telegraf can be used to collect and send them to the centralised metrics management system of your choice. (At PhonePe, we use Telegraf, which pushes the metrics to our custom metrics collection service, backed by a modified version of OpenTSDB. We use Grafana to visualize the same metrics.).
  • +
  • Published metrics from controllers include system health metrics around themselves.
  • +
  • Published metrics from executors contain system health metrics as well as other metrics around the containers running on them. This includes, but is not limited to, CPU, Memory and network usage.
  • +
+

Unsupported Features

+
    +
  • Auto-scaling of containers: In PhonePe, we have an extensive metrics ingestion system and an auto-scaler that works on a proprietary algorithm to scale containers up and down based on the same. This works independent of the orchestration system in play (we were on Drove and Mesos both at the same time during the transition period) and calls APIs on the deployment system that handles scaling operations independently. Any implementation at the orchestration level will not work as the contributors to the metrics might be running on different clusters, and scaling them independently will bring in more complexities rather than solving for simplicity.
  • +
  • Network level traffic control: At PhonePe, network security is handled at VRF level, and container-level access control is not needed. All services are already integrated with the OAuth2 compliant internal authentication and authorization system and perform security checks for the same at the application layer. As a matter of fact, we want containers to be as close to the raw network level as possible to ensure we can extract the highest level of network performance possible, other things being constant.
  • +
  • End-to-end configuration management: At this point of time, app/task configuration is maintained independently at PhonePe, subject to our approval workflows based on the compliance domain for the application, which can be static or dynamic and may be tied to deployments.
  • +
  • Multi-DC clusters: We have not tested a single Drove cluster spanning across multiple data centers.
  • +
+

Terminology

+

Before we delve into the details, let's get acquainted with the required terminology:

+
    +
  • Application - A service running on the cluster. Such a service can have an exposed port and will have an automatically configured virtual host on Drove Gateway.
  • +
  • Task - A transient container-based task.
  • +
  • Controller Nodes - The brains of the cluster. Only one cluster is the leader and hence the decision maker in the system.
  • +
  • Executor Nodes - The workhorse nodes of the cluster where the actual containers are run.
  • +
  • Drove CLI - A command line client to interact with the cluster.
  • +
  • Drove Gateway - Used to provide ingress to the leader and containers running on the cluster.
  • +
  • Epoch - A cron-type scheduler to spin up tasks on a Drove cluster based on pre-defined schedules.
  • +
+

Github Repositories

+ +

License

+

Apache 2

+ + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/search/search_index.js b/search/search_index.js new file mode 100644 index 0000000..eaefce1 --- /dev/null +++ b/search/search_index.js @@ -0,0 +1 @@ +var __index = {"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"index.html","title":"Introduction","text":"

Drove is a container orchestrator built at PhonePe. It is focused on simplicity, container performance, and easy operations.

"},{"location":"index.html#features","title":"Features","text":"

The following sections go over the features.

"},{"location":"index.html#functional","title":"Functional","text":"
  • Application (service) and application container lifecycle management including mandated readiness checks, health checks, and pre-shutdown hooks to enable operators to take containers out of rotation easily and shut them down gracefully if needed.
  • Ensures the required (specified) number of containers will always be present in the cluster. It will detect failures across the cluster and bring containers up/down to maintain the required instance count.
  • Provides endpoint information to be consumed by routers like drove-gateway+nginx/traefik, etc., to expose containers over vhost.
  • Supports short-lived container-based tasks. This helps folks build newer systems that can spin up containers as needed on the cluster. (See epoch).
  • Provides functionality for real-time log streaming and log download for all instances.
  • Log generation is handled by Drove in a file layout suitable for existing log shipping mechanisms as well as for streaming to rsyslog servers (if needed).
  • Provides a functional read-only web-based console for checking cluster, application, task, and instance states, log streaming, etc.
  • Provides APIs for both read and write operations.
  • Supports discovery for sibling containers to support dynamic cluster reconfiguration in frameworks like Hazelcast.
  • Support extra metadata in the form of tags on instances. This can be used in external systems for routing or other use-cases, as this information is available at the endpoint as well.
  • CLI system for easy deployments and app/task lifecycle management.
  • NGinx based router called drove-gateway for efficient communication with the cluster itself and containers deployed on it.
"},{"location":"index.html#operations","title":"Operations","text":"
  • Only two components (controller and executor) to make a cluster (plus Zookeeper for coordination, drove-gateway for routing if needed).
  • All components dockerised to allow for easy deployment as well as upgrades.
  • Simple single file YAML based configuration for the controller and executor.
  • Cluster can be set to maintenance mode, where it pauses making changes to the cluster and turns off safeguards around ensuring the required number of containers get reported from the executor nodes. This will allow the SRE team to do seamless software updates across the whole cluster in a few minutes, irrespective of the size.
  • Blacklisting of the executor nodes will automatically move all the running application containers to other nodes and prevent any further allocations to this node. This allows the node to be taken down for maintenance that needs longer periods of time to complete the OS/patch application, hardware maintenance, etc.
  • Detect and kill any Zombie container nodes. On mesos, SRE team needs to be involved to manually kill such containers.
"},{"location":"index.html#performance","title":"Performance","text":"
  • Scheduler needs to be aware of NUMA hardware topology of the node and prevent containers from being split across nodes.
  • Scheduler will pin containers to specific cores on a NUMA node so as to stop containers from stepping on each other\u2019s toes during peak hours and allow them to fully utilize the multi-level caches associated with the allocated CPU cores. Some balance is anyways gained by enabling hyper-threading on the executor nodes. This should be sufficient to provide a significant boost to the application performance.
  • Allows for specialised nodes in the cluster. For example, there might be nodes with GPU available. We would want to run ML models that can utilise such hardware rather than allocate generic service containers on such nodes. To this end, the scheduler supports tagging and allows for containers to be explicitly mapped to tagged nodes.
  • Allows for different placement policies to provide some flexibility to users in where they want to place their container nodes. This sometimes helps developers deploy specific apps to specific nodes where they might have been granted special privileges to perform deeper than usual investigations of running service containers (for example, take heap-dumps to specific mounted volumes, etc.).
  • Allows for configuration injection at container startup. Such configuration can be streamed in as part of the deployment specification, mounted in from executor hosts, or fetched via API calls by the controllers or executors.
  • Provides provisions to allow for extension of the scheduler to implement different scheduling algorithms in the code later on.
  • Sometimes, NUMA localization and CPU pinning are overkill for clusters that don't need to extract the last bit of performance. For example, testing/staging clusters. To this end, Drove supports the following features:
    • Allows turning off NUMA and core pinning at executor level.
    • Allows to specify multipliers for available CPU/memory to accommodate overprovisioning on the cluster.
  • Because the above are set at an executor level, the cluster can have different types of nodes with different required performance characteristics appropriately tagged. Relevant apps can be deployed based on performance requirements.
"},{"location":"index.html#resilience","title":"Resilience","text":"
  • Small number of moving pieces. Keeps the minimal amount of dependencies in the system. This reduces the exposure to failures by effectively reducing the number of external dependencies and possible failure points.
  • Controller stores state on external system (Zookeeper) for now.
  • Executor stores all container specific states in the container metadata itself. No other state is maintained/needed by executor.
  • Containers keep running even when most of the system is down. This means that even when the cluster coordinators, executors, state storage, etc. are down, the already deployed containers keep on running as is. If a service discovery mechanism is implemented properly, this effectively protects the system against service disruptions, even in the face of failure of critical cluster components. At PhonePe, we use Ranger for service discovery.
  • Container state reconciliation is part of the executor system, so that executor service can be restarted easily without affecting application or task deployments. In other words, the executor needs to recognise the containers started by itself on restarts and report their state as usual to the controller.
  • Keeps things as simple as possible. Drove uses a few simple constructs (scale up/scale down) and implements all application/task features using that.
  • Multi-mode cluster messaging ensures that faster updates will be sent to controller via sync channels, while the controller(s) keep refreshing the cluster state periodically, irrespective of the last synced data. Drove assumes that communication failures would happen. Even if new changes can\u2019t be propagated from executor to controller, it tries to keep existing topology as updated as possible.
  • Built in safeguards to detect and kill any rogue (Zombie) container instances that have remained back for some reason (maybe some bug in the orchestrator, etc.).
  • Controller is highly available with one leader active at a time. Any communication issues with Zookeeper will lead to quick death of the controller so that another controller can take up its place as quickly as possible.
  • Leader can be tracked using the ping api and is used by components such as drove-gateway to provide a Virtual Host that can be used to interact with the cluster via the UI or the CLI, and other tools.
"},{"location":"index.html#security","title":"Security","text":"
  • Clearly designate roles for read and write operations. Write operations include cluster maintenance and app and task lifecycle maintenance.
  • Authentication system is easily extensible.
  • Supports basic auth as the minimal auth requirement. User credentials are stored in bcrypt format in controller config files.
  • Support a no-auth mode for starter clusters.
  • Provides audit logs for events in the system. Such logs can get aggregated and/or shipped out independently by existing log aggregation systems like logrotate-+rsync or (r)syslog, etc., by configuring the appropriate loggers in the controller configuration file.
  • Separate authentication system for intra-cluster authentication and for edge. This will mean that even if external auth is compromised (or vice versa), the system will keep working as is.
  • Shared secret is used for intra-cluster authentication.
  • Dynamically generated tokens are injected into container instances for seamless sibling discovery. This provides a way for developers to implement clustering mechanisms for frameworks like Hazelcast (provided already).
"},{"location":"index.html#observability","title":"Observability","text":"
  • Real-time event stream from the controller can be used for any other event driven system like drove-gateway, etc., to refresh upstream topology.
  • Metrics are available on admin ports for both the controllers and executors. Something like Telegraf can be used to collect and send them to the centralised metrics management system of your choice. (At PhonePe, we use Telegraf, which pushes the metrics to our custom metrics collection service, backed by a modified version of OpenTSDB. We use Grafana to visualize the same metrics.).
  • Published metrics from controllers include system health metrics around themselves.
  • Published metrics from executors contain system health metrics as well as other metrics around the containers running on them. This includes, but is not limited to, CPU, Memory and network usage.
"},{"location":"index.html#unsupported-features","title":"Unsupported Features","text":"
  • Auto-scaling of containers: In PhonePe, we have an extensive metrics ingestion system and an auto-scaler that works on a proprietary algorithm to scale containers up and down based on the same. This works independent of the orchestration system in play (we were on Drove and Mesos both at the same time during the transition period) and calls APIs on the deployment system that handles scaling operations independently. Any implementation at the orchestration level will not work as the contributors to the metrics might be running on different clusters, and scaling them independently will bring in more complexities rather than solving for simplicity.
  • Network level traffic control: At PhonePe, network security is handled at VRF level, and container-level access control is not needed. All services are already integrated with the OAuth2 compliant internal authentication and authorization system and perform security checks for the same at the application layer. As a matter of fact, we want containers to be as close to the raw network level as possible to ensure we can extract the highest level of network performance possible, other things being constant.
  • End-to-end configuration management: At this point of time, app/task configuration is maintained independently at PhonePe, subject to our approval workflows based on the compliance domain for the application, which can be static or dynamic and may be tied to deployments.
  • Multi-DC clusters: We have not tested a single Drove cluster spanning across multiple data centers.
"},{"location":"index.html#terminology","title":"Terminology","text":"

Before we delve into the details, let's get acquainted with the required terminology:

  • Application - A service running on the cluster. Such a service can have an exposed port and will have an automatically configured virtual host on Drove Gateway.
  • Task - A transient container-based task.
  • Controller Nodes - The brains of the cluster. Only one cluster is the leader and hence the decision maker in the system.
  • Executor Nodes - The workhorse nodes of the cluster where the actual containers are run.
  • Drove CLI - A command line client to interact with the cluster.
  • Drove Gateway - Used to provide ingress to the leader and containers running on the cluster.
  • Epoch - A cron-type scheduler to spin up tasks on a Drove cluster based on pre-defined schedules.
"},{"location":"index.html#github-repositories","title":"Github Repositories","text":"
  • Uber Repo - https://github.com/PhonePe/drove-orchestrator
  • Drove Orchestrator Code - https://github.com/PhonePe/drove
  • Drove CLI - https://github.com/PhonePe/drove-cli
  • Drove Gateway - https://github.com/PhonePe/drove-gateway
  • Epoch - https://github.com/PhonePe/epoch
  • Epoch CLI - https://github.com/PhonePe/epoch-cli
  • CoreDNS plugin for Drove - https://github.com/PhonePe/coredns-drove
"},{"location":"index.html#license","title":"License","text":"

Apache 2

"},{"location":"getting-started.html","title":"Getting Started","text":"

To get a trivial cluster up and running on a machine, the compose file can be used.

"},{"location":"getting-started.html#update-etc-hosts-to-interact-wih-nginx","title":"Update etc hosts to interact wih nginx","text":"

Add the following lines to /etc/hosts

127.0.0.1   drove.local\n127.0.0.1   testapp.local\n

"},{"location":"getting-started.html#download-the-compose-file","title":"Download the compose file","text":"
wget https://raw.githubusercontent.com/PhonePe/drove-orchestrator/master/compose/compose.yaml\n
"},{"location":"getting-started.html#bringing-up-a-demo-cluster","title":"Bringing up a demo cluster","text":"

cd compose\ndocker-compose up\n
This will start zookeeper,drove controller, executor and nginx/drove-gateway. The following ports are used:

  • Zookeeper - 2181
  • Executor - 3000
  • Controller - 4000
  • Gateway - 7000

Drove credentials would be admin/admin and guest/guest for read-write and read-only permissions respectively.

You should be able to access the UI at http://drove.local:7000

"},{"location":"getting-started.html#install-drove-cli","title":"Install drove-cli","text":"

Install the CLI for drove

pip install drove-cli\n

"},{"location":"getting-started.html#create-client-configuration","title":"Create Client Configuration","text":"

Put the following in ${HOME}/.drove

[local]\nendpoint = http://drove.local:4000\nusername = admin\npassword = admin\n
"},{"location":"getting-started.html#deploy-an-app","title":"Deploy an app","text":"

Get the sample app spec:

wget https://raw.githubusercontent.com/PhonePe/drove-cli/master/sample/test_app.json\n

Now deploy the app.

drove -c local apps create test_app.json\n

"},{"location":"getting-started.html#scale-the-app","title":"Scale the app","text":"

drove -c local apps scale TEST_APP-1 1 -w\n
This would expose the app as testapp.local. Endpoint would be: http://testapp.local:7000.

You can test the app by running the following commands:

curl http://testapp.local:7000/\ncurl http://testapp.local:7000/files/drove.txt\n
"},{"location":"getting-started.html#suspend-and-destroy-the-app","title":"Suspend and destroy the app","text":"
drove -c local apps scale TEST_APP-1 0 -w\ndrove -c local apps destroy TEST_APP-1\n
"},{"location":"getting-started.html#accessing-the-code","title":"Accessing the code","text":"

Code is hosted on github.

Cloning everything:

git clone git@github.com:PhonePe/drove-orchestrator.git\ngit submodule init\ngit submodule update\n
"},{"location":"apis/index.html","title":"Introduction","text":"

This section lists all the APIs that a user can communicate with.

"},{"location":"apis/index.html#making-an-api-call","title":"Making an API call","text":"

Use a standard HTTP client in the language of your choice to make a call to the leader controller (the cluster virtual host exposed by drove-gateway-nginx).

Tip

In case you are using Java, we recommend using the drove-client library along with the http-transport.

If multiple controllers endpoints are provided, the client will track the leader automatically. This will reduce your dependency on drove-gateway.

"},{"location":"apis/index.html#authentication","title":"Authentication","text":"

Drove uses basic auth for authentication. (You can extend to use any other auth format like OAuth). The basic auth credentials need to be sent out in the standard format in the Authorization header.

"},{"location":"apis/index.html#response-format","title":"Response format","text":"

The response format is standard for all API calls:

{\n    \"status\": \"SUCCESS\",//(1)!\n    \"data\": {//(2)!\n        \"taskId\": \"T0012\"\n    },\n    \"message\": \"success\"//(3)!\n}\n
  1. SUCCESS or FAILURE as the case may be.
  2. Content of this field is contextual to the response.
  3. Will contain success if the call was successful or relevant error message.

Warning

APIs will return relevant HTTP status codes in case of error (for example 400 for validation errors, 401 for authentication failure). However, you must always ensure that the status field is set to SUCCESS for assuming the api call is succesful, even when HTTP status code is 2xx.

APIs in Drove belong to the following major classes:

  • Application Management
  • Task Management
  • Cluster Management
  • Log Access

Tip

Response models for these apis can be found in drove-models

Note

There are no publicly accessible APIs exposed by individual executors.

"},{"location":"apis/application.html","title":"Application Management","text":""},{"location":"apis/application.html#issue-application-operation-command","title":"Issue application operation command","text":"

POST /apis/v1/applications/operations

Request

curl --location 'http://drove.local:7000/apis/v1/operations' \\\n--header 'Content-Type: application/json' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data '{\n    \"type\": \"SCALE\",\n    \"appId\": \"TEST_APP-1\",\n    \"requiredInstances\": 1,\n    \"opSpec\": {\n        \"timeout\": \"1m\",\n        \"parallelism\": 20,\n        \"failureStrategy\": \"STOP\"\n    }\n}'\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

Tip

Relevant payloads for application commands can be found in application operations section.

"},{"location":"apis/application.html#cancel-currently-running-operation","title":"Cancel currently running operation","text":"

POST /apis/v1/applications/operations/{appId}/cancel

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/operations/TEST_APP/cancel' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-list-of-applications","title":"Get list of applications","text":"

GET /apis/v1/applications

Request

curl --location 'http://drove.local:7000/apis/v1/applications' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"TEST_APP-1\": {\n            \"id\": \"TEST_APP-1\",\n            \"name\": \"TEST_APP\",\n            \"requiredInstances\": 0,\n            \"healthyInstances\": 0,\n            \"totalCPUs\": 0,\n            \"totalMemory\": 0,\n            \"state\": \"MONITORING\",\n            \"created\": 1719826995764,\n            \"updated\": 1719892126096\n        }\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-info-for-an-app","title":"Get info for an app","text":"

GET /apis/v1/applications/{id}

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"id\": \"TEST_APP-1\",\n        \"name\": \"TEST_APP\",\n        \"requiredInstances\": 1,\n        \"healthyInstances\": 1,\n        \"totalCPUs\": 1,\n        \"totalMemory\": 128,\n        \"state\": \"RUNNING\",\n        \"created\": 1719826995764,\n        \"updated\": 1719892279019\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-raw-json-specs","title":"Get raw JSON specs","text":"

GET /apis/v1/applications/{id}/spec

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/spec' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"name\": \"TEST_APP\",\n        \"version\": \"1\",\n        \"executable\": {\n            \"type\": \"DOCKER\",\n            \"url\": \"ghcr.io/appform-io/perf-test-server-httplib\",\n            \"dockerPullTimeout\": \"100 seconds\"\n        },\n        \"exposedPorts\": [\n            {\n                \"name\": \"main\",\n                \"port\": 8000,\n                \"type\": \"HTTP\"\n            }\n        ],\n        \"volumes\": [],\n        \"configs\": [\n            {\n                \"type\": \"INLINE\",\n                \"localFilename\": \"/testfiles/drove.txt\",\n                \"data\": \"\"\n            }\n        ],\n        \"type\": \"SERVICE\",\n        \"resources\": [\n            {\n                \"type\": \"CPU\",\n                \"count\": 1\n            },\n            {\n                \"type\": \"MEMORY\",\n                \"sizeInMB\": 128\n            }\n        ],\n        \"placementPolicy\": {\n            \"type\": \"ANY\"\n        },\n        \"healthcheck\": {\n            \"mode\": {\n                \"type\": \"HTTP\",\n                \"protocol\": \"HTTP\",\n                \"portName\": \"main\",\n                \"path\": \"/\",\n                \"verb\": \"GET\",\n                \"successCodes\": [\n                    200\n                ],\n                \"payload\": \"\",\n                \"connectionTimeout\": \"1 second\",\n                \"insecure\": false\n            },\n            \"timeout\": \"1 second\",\n            \"interval\": \"5 seconds\",\n            \"attempts\": 3,\n            \"initialDelay\": \"0 seconds\"\n        },\n        \"readiness\": {\n            \"mode\": {\n                \"type\": \"HTTP\",\n                \"protocol\": \"HTTP\",\n                \"portName\": \"main\",\n                \"path\": \"/\",\n                \"verb\": \"GET\",\n                \"successCodes\": [\n                    200\n                ],\n                \"payload\": \"\",\n                \"connectionTimeout\": \"1 second\",\n                \"insecure\": false\n            },\n            \"timeout\": \"1 second\",\n            \"interval\": \"3 seconds\",\n            \"attempts\": 3,\n            \"initialDelay\": \"0 seconds\"\n        },\n        \"tags\": {\n            \"superSpecialApp\": \"yes_i_am\",\n            \"say_my_name\": \"heisenberg\"\n        },\n        \"env\": {\n            \"CORES\": \"8\"\n        },\n        \"exposureSpec\": {\n            \"vhost\": \"testapp.local\",\n            \"portName\": \"main\",\n            \"mode\": \"ALL\"\n        },\n        \"preShutdown\": {\n            \"hooks\": [\n                {\n                    \"type\": \"HTTP\",\n                    \"protocol\": \"HTTP\",\n                    \"portName\": \"main\",\n                    \"path\": \"/\",\n                    \"verb\": \"GET\",\n                    \"successCodes\": [\n                        200\n                    ],\n                    \"payload\": \"\",\n                    \"connectionTimeout\": \"1 second\",\n                    \"insecure\": false\n                }\n            ],\n            \"waitBeforeKill\": \"3 seconds\"\n        }\n    },\n    \"message\": \"success\"\n}\n

Note

configs section data will not be returned by any api calls

"},{"location":"apis/application.html#get-list-of-currently-active-instances","title":"Get list of currently active instances","text":"

GET /apis/v1/applications/{id}/instances

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/instances' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"appId\": \"TEST_APP-1\",\n            \"appName\": \"TEST_APP\",\n            \"instanceId\": \"AI-58eb1111-8c2c-4ea2-a159-8fc68010a146\",\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"localInfo\": {\n                \"hostname\": \"ppessdev\",\n                \"ports\": {\n                    \"main\": {\n                        \"containerPort\": 8000,\n                        \"hostPort\": 33857,\n                        \"portType\": \"HTTP\"\n                    }\n                }\n            },\n            \"resources\": [\n                {\n                    \"type\": \"CPU\",\n                    \"cores\": {\n                        \"0\": [\n                            2\n                        ]\n                    }\n                },\n                {\n                    \"type\": \"MEMORY\",\n                    \"memoryInMB\": {\n                        \"0\": 128\n                    }\n                }\n            ],\n            \"state\": \"HEALTHY\",\n            \"metadata\": {},\n            \"errorMessage\": \"\",\n            \"created\": 1719892354194,\n            \"updated\": 1719893180105\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-list-of-old-instances","title":"Get list of old instances","text":"

GET /apis/v1/applications/{id}/instances/old

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/instances/old' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"appId\": \"TEST_APP-1\",\n            \"appName\": \"TEST_APP\",\n            \"instanceId\": \"AI-869e34ed-ebf3-4908-bf48-719475ca5640\",\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"resources\": [\n                {\n                    \"type\": \"CPU\",\n                    \"cores\": {\n                        \"0\": [\n                            2\n                        ]\n                    }\n                },\n                {\n                    \"type\": \"MEMORY\",\n                    \"memoryInMB\": {\n                        \"0\": 128\n                    }\n                }\n            ],\n            \"state\": \"STOPPED\",\n            \"metadata\": {},\n            \"errorMessage\": \"Error while pulling image ghcr.io/appform-io/perf-test-server-httplib: Status 500: {\\\"message\\\":\\\"Get \\\\\\\"https://ghcr.io/v2/\\\\\\\": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)\\\"}\\n\",\n            \"created\": 1719892279039,\n            \"updated\": 1719892354099\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-info-for-an-instance","title":"Get info for an instance","text":"

GET /apis/v1/applications/{appId}/instances/{instanceId}

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/instances/AI-58eb1111-8c2c-4ea2-a159-8fc68010a146' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\",\n        \"appName\": \"TEST_APP\",\n        \"instanceId\": \"AI-58eb1111-8c2c-4ea2-a159-8fc68010a146\",\n        \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n        \"localInfo\": {\n            \"hostname\": \"ppessdev\",\n            \"ports\": {\n                \"main\": {\n                    \"containerPort\": 8000,\n                    \"hostPort\": 33857,\n                    \"portType\": \"HTTP\"\n                }\n            }\n        },\n        \"resources\": [\n            {\n                \"type\": \"CPU\",\n                \"cores\": {\n                    \"0\": [\n                        2\n                    ]\n                }\n            },\n            {\n                \"type\": \"MEMORY\",\n                \"memoryInMB\": {\n                    \"0\": 128\n                }\n            }\n        ],\n        \"state\": \"HEALTHY\",\n        \"metadata\": {},\n        \"errorMessage\": \"\",\n        \"created\": 1719892354194,\n        \"updated\": 1719893440105\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#application-endpoints","title":"Application Endpoints","text":"

GET /apis/v1/endpoints

Info

This API provides up-to-date information about the host and port information about application instances running on the cluster. This information can be used for Service Discovery systems to keep their information in sync with changes in the topology of applications running on the cluster.

Tip

Any tag specified in the application specification is also exposed on endpoint. This can be used to implement complicated routing logic if needed in the NGinx template on Drove Gateway.

Request

curl --location 'http://drove.local:7000/apis/v1/endpoints' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"appId\": \"TEST_APP-1\",\n            \"vhost\": \"testapp.local\",\n            \"tags\": {\n                \"superSpecialApp\": \"yes_i_am\",\n                \"say_my_name\": \"heisenberg\"\n            },\n            \"hosts\": [\n                {\n                    \"host\": \"ppessdev\",\n                    \"port\": 44315,\n                    \"portType\": \"HTTP\"\n                }\n            ]\n        },\n        {\n            \"appId\": \"TEST_APP-2\",\n            \"vhost\": \"testapp.local\",\n            \"tags\": {\n                \"superSpecialApp\": \"yes_i_am\",\n                \"say_my_name\": \"heisenberg\"\n            },\n            \"hosts\": [\n                {\n                    \"host\": \"ppessdev\",\n                    \"port\": 46623,\n                    \"portType\": \"HTTP\"\n                }\n            ]\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html","title":"Cluster Management","text":""},{"location":"apis/cluster.html#ping-api","title":"Ping API","text":"

GET /apis/v1/ping

Request

curl --location 'http://drove.local:7000/apis/v1/ping' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": \"pong\",\n    \"message\": \"success\"\n}\n

Tip

Use this api call to determine the leader in a cluster. This api will return a HTTP 200 only for the leader controller. All other controllers in the cluster will return 4xx for this api call.

"},{"location":"apis/cluster.html#cluster-management_1","title":"Cluster Management","text":""},{"location":"apis/cluster.html#get-current-cluster-state","title":"Get current cluster state","text":"

GET /apis/v1/cluster

Request

curl --location 'http://drove.local:7000/apis/v1/cluster' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"leader\": \"ppessdev:4000\",\n        \"state\": \"NORMAL\",\n        \"numExecutors\": 1,\n        \"numApplications\": 1,\n        \"numActiveApplications\": 1,\n        \"freeCores\": 9,\n        \"usedCores\": 1,\n        \"totalCores\": 10,\n        \"freeMemory\": 18898,\n        \"usedMemory\": 128,\n        \"totalMemory\": 19026\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#set-maintenance-mode-on-cluster","title":"Set maintenance mode on cluster","text":"

POST /apis/v1/cluster/maintenance/set

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/set' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"state\": \"MAINTENANCE\",\n        \"updated\": 1719897526772\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#remove-maintenance-mode-from-cluster","title":"Remove maintenance mode from cluster","text":"

POST /apis/v1/cluster/maintenance/unset

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/unset' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"state\": \"NORMAL\",\n        \"updated\": 1719897573226\n    },\n    \"message\": \"success\"\n}\n

Warning

Cluster will remain in maintenance mode for some time (about 2 minutes) internally even after maintenance mode is removed.

"},{"location":"apis/cluster.html#executor-management","title":"Executor Management","text":""},{"location":"apis/cluster.html#get-list-of-executors","title":"Get list of executors","text":"

GET /apis/v1/cluster/executors

Request

curl --location 'http://drove.local:7000/apis/v1/cluster/executors' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"hostname\": \"ppessdev\",\n            \"port\": 3000,\n            \"transportType\": \"HTTP\",\n            \"freeCores\": 9,\n            \"usedCores\": 1,\n            \"freeMemory\": 18898,\n            \"usedMemory\": 128,\n            \"tags\": [\n                \"ppessdev\"\n            ],\n            \"state\": \"ACTIVE\"\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#get-detailed-info-for-one-executor","title":"Get detailed info for one executor","text":"

GET /apis/v1/cluster/executors/{id}

Request

curl --location 'http://drove.local:7000/apis/v1/cluster/executors/a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"type\": \"EXECUTOR\",\n        \"hostname\": \"ppessdev\",\n        \"port\": 3000,\n        \"transportType\": \"HTTP\",\n        \"updated\": 1719897100104,\n        \"state\": {\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"cpus\": {\n                \"type\": \"CPU\",\n                \"freeCores\": {\n                    \"0\": [\n                        3,\n                        4,\n                        5,\n                        6,\n                        7,\n                        8,\n                        9,\n                        10,\n                        11\n                    ]\n                },\n                \"usedCores\": {\n                    \"0\": [\n                        2\n                    ]\n                }\n            },\n            \"memory\": {\n                \"type\": \"MEMORY\",\n                \"freeMemory\": {\n                    \"0\": 18898\n                },\n                \"usedMemory\": {\n                    \"0\": 128\n                }\n            }\n        },\n        \"instances\": [\n            {\n                \"appId\": \"TEST_APP-1\",\n                \"appName\": \"TEST_APP\",\n                \"instanceId\": \"AI-58eb1111-8c2c-4ea2-a159-8fc68010a146\",\n                \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n                \"localInfo\": {\n                    \"hostname\": \"ppessdev\",\n                    \"ports\": {\n                        \"main\": {\n                            \"containerPort\": 8000,\n                            \"hostPort\": 33857,\n                            \"portType\": \"HTTP\"\n                        }\n                    }\n                },\n                \"resources\": [\n                    {\n                        \"type\": \"CPU\",\n                        \"cores\": {\n                            \"0\": [\n                                2\n                            ]\n                        }\n                    },\n                    {\n                        \"type\": \"MEMORY\",\n                        \"memoryInMB\": {\n                            \"0\": 128\n                        }\n                    }\n                ],\n                \"state\": \"HEALTHY\",\n                \"metadata\": {},\n                \"errorMessage\": \"\",\n                \"created\": 1719892354194,\n                \"updated\": 1719897100104\n            }\n        ],\n        \"tasks\": [],\n        \"tags\": [\n            \"ppessdev\"\n        ],\n        \"blacklisted\": false\n    },\n    \"message\": \"success\"\n}\n
"},{"location":"apis/cluster.html#take-executor-out-of-rotation","title":"Take executor out of rotation","text":"

POST /apis/v1/cluster/executors/blacklist

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/blacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Note

Unlike other POST apis, the executors to be blacklisted are passed as query parameter id. To blacklist multiple executors, pass .../blacklist?id=<id1>&id=<id2>...

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"successful\": [\n            \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\"\n        ],\n        \"failed\": []\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#bring-executor-back-into-rotation","title":"Bring executor back into rotation","text":"

POST /apis/v1/cluster/executors/unblacklist

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/unblacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Note

Unlike other POST apis, the executors to be un-blacklisted are passed as query parameter id. To un-blacklist multiple executors, pass .../unblacklist?id=<id1>&id=<id2>...

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"successful\": [\n            \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\"\n        ],\n        \"failed\": []\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#drove-cluster-events","title":"Drove Cluster Events","text":"

The following APIs can be used to monitor events on Drove. If the data needs to be consumed, the /latest API should be used. For simply knowing if an event of a certain type has occurred or not, the /summary is sufficient.

"},{"location":"apis/cluster.html#event-list","title":"Event List","text":"

GET /apis/v1/cluster/events/latest

Request

curl --location 'http://drove.local:7000/apis/v1/cluster/events/latest?size=1024&lastSyncTime=0' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"events\": [\n            {\n                \"metadata\": {\n                    \"CURRENT_INSTANCES\": 0,\n                    \"APP_ID\": \"TEST_APP-1\",\n                    \"PLACEMENT_POLICY\": \"ANY\",\n                    \"APP_VERSION\": \"1\",\n                    \"CPU_COUNT\": 1,\n                    \"CURRENT_STATE\": \"RUNNING\",\n                    \"PORTS\": \"main:8000:http\",\n                    \"MEMORY\": 128,\n                    \"EXECUTABLE\": \"ghcr.io/appform-io/perf-test-server-httplib\",\n                    \"VHOST\": \"testapp.local\",\n                    \"APP_NAME\": \"TEST_APP\"\n                },\n                \"type\": \"APP_STATE_CHANGE\",\n                \"id\": \"a2b7d673-2bc2-4084-8415-d8d37cafa63d\",\n                \"time\": 1719977632050\n            },\n            {\n                \"metadata\": {\n                    \"APP_NAME\": \"TEST_APP\",\n                    \"APP_ID\": \"TEST_APP-1\",\n                    \"PORTS\": \"main:44315:http\",\n                    \"EXECUTOR_ID\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n                    \"EXECUTOR_HOST\": \"ppessdev\",\n                    \"CREATED\": 1719977629042,\n                    \"INSTANCE_ID\": \"AI-5efbb94f-835c-4c62-a073-a68437e60339\",\n                    \"CURRENT_STATE\": \"HEALTHY\"\n                },\n                \"type\": \"INSTANCE_STATE_CHANGE\",\n                \"id\": \"55d5876f-94ac-4c5d-a580-9c3b296add46\",\n                \"time\": 1719977631534\n            }\n        ],\n        \"lastSyncTime\": 1719977632050//(1)!\n    },\n    \"message\": \"success\"\n}\n

  1. Pass this as the parameter lastSyncTime in the next call to events api to receive latest events.
Query Parameter Validation Description lastSyncTime +ve long range Time when the last sync call happened on the server. Defaults to 0 (initial sync). size 1-1024 Number of latest events to return. Defaults to 1024. We recommend leaving this as is."},{"location":"apis/cluster.html#event-summary","title":"Event Summary","text":"

GET /apis/v1/cluster/events/summary

Request

curl --location 'http://drove.local:7000/apis/v1/cluster/events/summary?lastSyncTime=0' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n
Response
{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"eventsCount\": {\n            \"INSTANCE_STATE_CHANGE\": 8,\n            \"APP_STATE_CHANGE\": 17,\n            \"EXECUTOR_BLACKLISTED\": 1,\n            \"EXECUTOR_UN_BLACKLISTED\": 1\n        },\n        \"lastSyncTime\": 1719977632050//(1)!\n    },\n    \"message\": \"success\"\n}\n

  1. Pass this as the parameter lastSyncTime in the next call to events api to receive latest events.
"},{"location":"apis/cluster.html#continuous-monitoring-for-events","title":"Continuous monitoring for events","text":"

This is applicable for both the APIs listed above

  • In the first call to events api, pass lastSyncTime as zero.
  • In the response there will be a field lastSyncTime
  • Pass the last received lastSyncTime as the lastSyncTime param in the next call
  • This api is cheap enough, you should plan to make calls to it every few seconds

Info

Model for the events can be found here.

Tip

Java programs should definitely look at using the event listener library to listen to cluster events

"},{"location":"apis/logs.html","title":"Log Related APIs","text":""},{"location":"apis/logs.html#get-list-if-log-files","title":"Get list if log files","text":"

Application GET /apis/v1/logfiles/applications/{appId}/{instanceId}/list

Task GET /apis/v1/logfiles/tasks/{sourceAppName}/{taskId}/list

Request

curl --location 'http://drove.local:7000/apis/v1/logfiles/applications/TEST_APP-1/AI-5efbb94f-835c-4c62-a073-a68437e60339/list' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"files\": [\n        \"output.log-2024-07-04\",\n        \"output.log-2024-07-03\",\n        \"output.log\"\n    ]\n}\n

"},{"location":"apis/logs.html#download-log-files","title":"Download Log Files","text":"

Application GET /apis/v1/logfiles/applications/{appId}/{instanceId}/download/{fileName}

Task GET /apis/v1/logfiles/tasks/{sourceAppName}/{taskId}/download/{fileName}

Request

curl --location 'http://drove.local:7000/apis/v1/logfiles/applications/TEST_APP-1/AI-5efbb94f-835c-4c62-a073-a68437e60339/download/output.log' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

Note

The Content-Disposition header is set properly to the actual filename. For the above example it would be set to attachment; filename=output.log.

"},{"location":"apis/logs.html#read-chunks-from-log","title":"Read chunks from log","text":"

Application GET /apis/v1/logfiles/applications/{appId}/{instanceId}/read/{fileName}

Task GET /apis/v1/logfiles/tasks/{sourceAppName}/{taskId}/read/{fileName}

Query Parameter Validation Description offset Default -1, should be positive number The offset of the file to read from. length Should be a positive number Number of bytes to read.

Request

curl --location 'http://drove.local:7000/apis/v1/logfiles/applications/TEST_APP-1/AI-5efbb94f-835c-4c62-a073-a68437e60339/read/output.log' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"data\": \"\", //(1)!\n    \"offset\": 43318 //(2)!\n}\n

  1. Will contain raw data or empty string (in case of first call)
  2. Offset to be passed in the next call
"},{"location":"apis/logs.html#how-to-tail-logs","title":"How to tail logs","text":"
  1. Have a fixed buffer size in ming 1024/4096 etc
  2. Make a call to /read api with offset=-1, length = buffer size
  3. The call will return no data, but will have a valid offset
  4. Pass this offset in the next call, data will be returned if available (or empty). The response will also return the offset to pass in the .ext call.
  5. The data returned might be empty or less than length depending on availability.
  6. Keep repeating (4) to keep tailing log

Warning

  • Offset = 0 means start of the file
  • First call must be -1 for tail type functionality
"},{"location":"apis/task.html","title":"Task Management","text":""},{"location":"apis/task.html#issue-task-operation","title":"Issue task operation","text":"

POST /apis/v1/tasks/operations

Request

curl --location 'http://drove.local:7000/apis/v1/tasks/operations' \\\n--header 'Content-Type: application/json' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data '{\n    \"type\": \"KILL\",\n    \"sourceAppName\" : \"TEST_APP\",\n    \"taskId\" : \"T0012\",\n    \"opSpec\": {\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}'\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"taskId\": \"T0012\"\n    },\n    \"message\": \"success\"\n}\n

Tip

Relevant payloads for task commands can be found in task operations section.

"},{"location":"apis/task.html#search-for-task","title":"Search for task","text":"

POST /apis/v1/tasks/search

"},{"location":"apis/task.html#list-all-tasks","title":"List all tasks","text":"

GET /apis/v1/tasks

Request

curl --location 'http://drove.local:7000/apis/v1/tasks' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"sourceAppName\": \"TEST_APP\",\n            \"taskId\": \"T0013\",\n            \"instanceId\": \"TI-c2140806-2bb5-4ed3-9bb9-0c0c5fd0d8d6\",\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"hostname\": \"ppessdev\",\n            \"executable\": {\n                \"type\": \"DOCKER\",\n                \"url\": \"ghcr.io/appform-io/test-task\",\n                \"dockerPullTimeout\": \"100 seconds\"\n            },\n            \"resources\": [\n                {\n                    \"type\": \"CPU\",\n                    \"cores\": {\n                        \"0\": [\n                            2\n                        ]\n                    }\n                },\n                {\n                    \"type\": \"MEMORY\",\n                    \"memoryInMB\": {\n                        \"0\": 512\n                    }\n                }\n            ],\n            \"volumes\": [],\n            \"env\": {\n                \"ITERATIONS\": \"10\"\n            },\n            \"state\": \"RUNNING\",\n            \"metadata\": {},\n            \"errorMessage\": \"\",\n            \"created\": 1719827035480,\n            \"updated\": 1719827038414\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/task.html#get-task-instance-details","title":"Get Task Instance Details","text":"

GET /apis/v1/tasks/{sourceAppName}/instances/{taskId}

Request

curl --location 'http://drove.local:7000/apis/v1/tasks/TEST_APP/instances/T0012' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"sourceAppName\": \"TEST_APP\",\n        \"taskId\": \"T0012\",\n        \"instanceId\": \"TI-6cf36f5c-6480-4ed5-9e2d-f79d9648529a\",\n        \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n        \"hostname\": \"ppessdev\",\n        \"executable\": {\n            \"type\": \"DOCKER\",\n            \"url\": \"ghcr.io/appform-io/test-task\",\n            \"dockerPullTimeout\": \"100 seconds\"\n        },\n        \"resources\": [\n            {\n                \"type\": \"CPU\",\n                \"cores\": {\n                    \"0\": [\n                        3\n                    ]\n                }\n            },\n            {\n                \"type\": \"MEMORY\",\n                \"memoryInMB\": {\n                    \"0\": 512\n                }\n            }\n        ],\n        \"volumes\": [],\n        \"env\": {\n            \"ITERATIONS\": \"10\"\n        },\n        \"state\": \"STOPPED\",\n        \"metadata\": {},\n        \"taskResult\": {\n            \"status\": \"SUCCESSFUL\",\n            \"exitCode\": 0\n        },\n        \"errorMessage\": \"\",\n        \"created\": 1719823470267,\n        \"updated\": 1719823483836\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"applications/index.html","title":"Introduction","text":"

An application is a virtual representation of a running service in the system.

Running containers for an application are called application instances.

An application specification contains the following details about the application:

  • Name - Name of the application
  • Version - Version of this specification
  • Executable - The container to deploy on the cluster
  • Ports - Ports to be exposed from the container
  • Resources - CPU and Memory required for the container
  • Placement Policy - How containers are to be placed in the cluster
  • Healthchecks - Healthcheck details
  • Readiness Checks - Readiness checks to pass before container is considered to be healthy
  • Pre Shutdown Hooks - Pre shutdown hooks to run on container before it is killed
  • Environment Variables - Environment variables and values
  • Exposure Information - Virtual host information
  • Volumes - Volumes to be mounted into the container
  • Configs - Configs/files to be mounted into the container
  • Logging details - Logging spec (for example rsyslog server)
  • Tags - A map of strings for additional metadata

Info

Once a spec is registered to the cluster, it can not be changed

"},{"location":"applications/index.html#application-id","title":"Application ID","text":"

Once an application is created on the cluster, an Application id is generated. The format of this id currently is: {name}-{version}. All further operations to be done on the application will need to refer to it by this ID.

"},{"location":"applications/index.html#application-states-and-operations","title":"Application States and Operations","text":"

An application on a Drove cluster follows a fixed lifecycle modelled as a state machine. State transitions are triggered by operations. Operations can be issued externally using API calls or may be generated internally by the application monitoring system.

"},{"location":"applications/index.html#states","title":"States","text":"

Applications on a Drove cluster can be one of the following states:

  • INIT - This is an intermediate state during which the application is being initialized and the spec is being validated. This is the origination state of the application.
  • MONITORING - A stable state in which application is created or suspended and does not have any running instances
  • RUNNING - A stable state in which application has the expected non-zero number of healthy application instances running on the cluster
  • OUTAGE_DETECTED - An intermediate state when Drove has detected that the current number of application instances is not matching the expected number of instances.
  • SCALING_REQUESTED - An intermediate state that signifies that application instances are being spun up or shut down to get the number of running instances to match the expected instances.
  • STOP_INSTANCES_REQUESTED - An intermediate state that signifies that specific instances of the application are being killed as requested by the user/system.
  • REPLACE_INSTANCES_REQUESTED - An _intermediate state _that signifies that instances of the application are being replaced with newer instances as requested by the user. This signifies that the app is effectively being restarted.
  • DESTROY_REQUESTED - An intermediate state that signifies that the user has requested to destroy the application and remove it from the cluster.
  • DESTROYED - An intermediate state that signifies that the app has been destroyed and metadata cleanup is underway. This is the terminal state of an application.
"},{"location":"applications/index.html#operations","title":"Operations","text":"

The following application operations are recognized by Drove:

  • CREATE - Create an application. Take the Application Specification. Fails if an app with the same application id (name + version) already exists on the cluster
  • DESTROY - Destroy an application. Takes app id as parameter. Deletes all metadata about the application from the cluster. Allowed only if the application is in Monitoring state (i.e. has zero running instances).
  • START_INSTANCES - Create new application instances. Takes the app id as well as the number of new instances to deploy. Allowed only if the application is in Monitoring or Running state.
  • STOP_INSTANCES - Stop running application instances. Takes the app id, list of instance ids to be stopped as well as flag to denote if replacement instances are to be started by Drove or not. Allowed only if the application is in Monitoring or Running state.
  • SCALE - Scale the application up and down to the specified number of instances. Drove will internally calculate whether to spin new containers up or spin old containers down as needed. Allowed if the app is in Monitoring or Running state. It is better to use either START or STOP instances command above to be more explicit in behavior. The SCALE operation is mostly for internal use by Drove, but can be issued externally as well.
  • REPLACE_INSTANCES - Replace application instances with newer ones. Can be used to do rolling restarts on the cluster. Specific instances can be targeted as well by passing an optional list of instance ids to be replaced. Allowed only when the application is in Running state.
  • SUSPEND - A shortcut to set expected instances for an application to zero. This will get translated into a SCALE operation and any running instances will be gracefully shut down. Allowed only when the application is in running state.
  • RECOVER - Internal command used to restore application state on controller failover.

Tip

All operations can take an optional Cluster Operation Spec which can be used to control the timeout and parallelism of tasks generated by the operation.

"},{"location":"applications/index.html#application-state-machine","title":"Application State Machine","text":"

The following state machine signifies the states and transitions as affected by cluster state and operations issued.

"},{"location":"applications/instances.html","title":"Application Instances","text":"

Application instances are running containers for an application. The state machine for instances are managed in a decentralised manner on the cluster nodes locally and not by the controllers. This includes running health checks, readiness checks and shutdown hooks on the container, container loss detection and container state recovery on executor service restart.

Regular updates about the instance state are provided by executors to the controllers and are used to keep the application state up-to-date or trigger application operations to bring the applications to stable states.

"},{"location":"applications/instances.html#application-instance-states","title":"Application Instance States","text":"

An application instance can be in one of the following states at one point in time:

  • PENDING - Container state machine start has been triggered.
  • PROVISIONING - Docker image is being downloaded
  • PROVISIONING_FAILED - Docker image download failed
  • STARTING - Docker run is being executed
  • START_FAILED - Docker run failed
  • UNREADY - Docker started, readiness check not yet started.
  • READINESS_CHECK_FAILED - Readiness check was run and has failed terminally
  • READY - Readiness checks have passed
  • HEALTHY - Health check has passed. Container is running properly and passing regular health checks
  • UNHEALTHY - Regular health check has failed. Container will stop.
  • STOPPING - Shutdown hooks are being called and docker kill be be issued
  • DEPROVISIONING - Docker image is being cleaned up
  • STOPPED - Docker stop has completed
  • LOST - Container has exited unexpectedly while executor service was down
  • UNKNOWN - All running containers are in this state when executor service is getting restarted and before startup recovery has kicked in
"},{"location":"applications/instances.html#application-instance-state-machine","title":"Application Instance State Machine","text":"

Instance state machine transitions might be triggered on receipt of commands issued by the controller or due to internal changes in the container (might have died or started failing health checks) as well as external factors like executor service restarts.

Note

No operations are allowed to be performed on application instances directly through the executor

"},{"location":"applications/operations.html","title":"Application Operations","text":"

This page discusses operations relevant to Application management. Please go over the Application State Machine and Application Instance State Machine to understand the different states an application (and it's instances) can be in and how operations applied move an application from one state to another.

Note

Please go through Cluster Op Spec to understand the operation parameters being sent.

Note

Only one operation can be active on a particular {appName,version} combination.

Warning

Only the leader controller will accept and process operations. To avoid confusion, use the controller endpoint exposed by Drove Gateway to issue commands.

"},{"location":"applications/operations.html#how-to-initiate-an-operation","title":"How to initiate an operation","text":"

Tip

Use the Drove CLI to perform all manual operations.

All operations for application lifecycle management need to be issued via a POST HTTP call to the leader controller endpoint on the path /apis/v1/applications/operations. API will return HTTP OK/200 and relevant json response as payload.

Sample api call:

curl --location 'http://drove.local:7000/apis/v1/applications/operations' \\\n--header 'Content-Type: application/json' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data '{\n    \"type\": \"START_INSTANCES\",\n    \"appId\": \"TEST_APP-3\",\n    \"instances\": 1,\n    \"opSpec\": {\n        \"timeout\": \"5m\",\n        \"parallelism\": 32,\n        \"failureStrategy\": \"STOP\"\n    }\n}'\n

Note

In the above examples, http://drove.local:7000 is the endpoint of the leader. TEST_APP-3 is the Application ID. Authorization is basic auth.

"},{"location":"applications/operations.html#cluster-operation-specification","title":"Cluster Operation Specification","text":"

When an operation is submitted to the cluster, a cluster op spec needs to be specified. This is needed to control different aspects of the operation, including parallelism of an operation or increase the timeout for the operation and so on.

The following aspects of an operation can be configured:

Name Option Description Timeout timeout The duration after which Drove considers the operation to have timed out. Parallelism parallelism Parallelism of the task. (Range: 1-32) Failure Strategy failureStrategy Set this to STOP.

Note

For internal recovery operations, Drove generates it's own operations. For that, Drove applies the following cluster operation spec:

  • timeout - 300 seconds
  • parallelism - 1
  • failureStrategy - STOP

The default operation spec can be configured in the controller configuration file. It is recommended to set this to a something like 8 for faster recovery.

"},{"location":"applications/operations.html#how-to-cancel-an-operation","title":"How to cancel an operation","text":"

Operations can be requested to be cancelled asynchronously. A POST call needs to be made to leader controller endpoint on the api /apis/v1/operations/{applicationId}/cancel (1) to achieve this.

  1. applicationId is the Application ID for the application
curl --location --request POST 'http://drove.local:7000/apis/v1/operations/TEST_APP-3/cancel' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Warning

Operation cancellation is not instantaneous. Cancellation will be affected only after current execution of the active operation is complete.

"},{"location":"applications/operations.html#create-an-application","title":"Create an application","text":"

Before deploying containers on the cluster, an application needs to be created.

Preconditions:

  • App should not exist in the cluster

State Transition:

  • none \u2192 MONITORING

To create an application, an Application Spec needs to be created first.

Once ready, CLI command needs to be issued or the following payload needs to be sent:

Drove CLIJSON
drove -c local apps create sample/test_app.json\n

Sample Request Payload

{\n    \"type\": \"CREATE\",\n    \"spec\": {...}, //(1)!\n    \"opSpec\": { //(2)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Spec as mentioned in Application Specification
  2. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"data\" : {\n        \"appId\" : \"TEST_APP-1\"\n    },\n    \"message\" : \"success\",\n    \"status\" : \"SUCCESS\"\n}\n

"},{"location":"applications/operations.html#starting-new-instances-of-an-application","title":"Starting new instances of an application","text":"

New instances can be started by issuing the START_INSTANCES command.

Preconditions - Application must be in one of the following states: MONITORING, RUNNING

State Transition:

  • {RUNNING, MONITORING} \u2192 RUNNING

The following command/payload will start 2 new instances of the application.

Drove CLIJSON
drove -c local apps deploy TEST_APP-1 2\n

Sample Request Payload

{\n    \"type\": \"START_INSTANCES\",\n    \"appId\": \"TEST_APP-1\",//(1)!\n    \"instances\": 2,//(2)!\n    \"opSpec\": {//(3)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 32,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Application ID
  2. Number of instances to be started
  3. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"applications/operations.html#suspending-an-application","title":"Suspending an application","text":"

All instances of an application can be shut down by issuing the SUSPEND command.

Preconditions - Application must be in one of the following states: MONITORING, RUNNING

State Transition:

  • {RUNNING, MONITORING} \u2192 MONITORING

The following command/payload will suspend all instances of the application.

Drove CLIJSON
drove -c local apps suspend TEST_APP-1\n

Sample Request Payload

{\n    \"type\": \"SUSPEND\",\n    \"appId\": \"TEST_APP-1\",//(1)!\n    \"opSpec\": {//(2)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 32,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Application ID
  2. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"applications/operations.html#scaling-the-application-up-or-down","title":"Scaling the application up or down","text":"

Scaling the application to required number of containers can be achieved using the SCALE command. Application can be either scaled up or down using this command.

Preconditions - Application must be in one of the following states: MONITORING, RUNNING

State Transition:

  • {RUNNING, MONITORING} \u2192 MONITORING if requiredInstances is set to 0
  • {RUNNING, MONITORING} \u2192 RUNNING if requiredInstances is non 0
Drove CLIJSON
drove -c local apps scale TEST_APP-1 2\n

Sample Request Payload

{\n    \"type\": \"SCALE\",\n    \"appId\": \"TEST_APP-1\", //(3)!\n    \"requiredInstances\": 2, //(1)!\n    \"opSpec\": { //(2)!\n        \"timeout\": \"1m\",\n        \"parallelism\": 20,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Absolute number of instances to be maintained on the cluster for the application
  2. Operation spec as mentioned in Cluster Op Spec
  3. Application ID

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

Note

During scale down, older instances are stopped first

Tip

If implementing automation on top of Drove APIs, just use the SCALE command to scale up or down instead of using START_INSTANCES or SUSPEND separately.

"},{"location":"applications/operations.html#restarting-an-application","title":"Restarting an application","text":"

Application can be restarted by issuing the REPLACE_INSTANCES operation. In this case, first clusterOpSpec.parallelism number of containers are spun up first and then an equivalent number of them are spun down. This ensures that cluster maintains enough capacity is maintained in the cluster to handle incoming traffic as the restart is underway.

Warning

If the cluster does not have sufficient capacity to spin up new containers, this operation will get stuck. So adjust your parallelism accordingly.

Preconditions - Application must be in RUNNING state.

State Transition:

  • RUNNING \u2192 REPLACE_INSTANCES_REQUESTED \u2192 RUNNING
Drove CLIJSON
drove -c local apps restart TEST_APP-1\n

Sample Request Payload

{\n    \"type\": \"REPLACE_INSTANCES\",\n    \"appId\": \"TEST_APP-1\", //(1)!\n    \"instanceIds\": [], //(2)!\n    \"opSpec\": { //(3)!\n        \"timeout\": \"1m\",\n        \"parallelism\": 20,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Application ID
  2. Instances that need to be restarted. This is optional. If nothing is passed, all instances will be replaced.
  3. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

Tip

To replace specific instances, pass their application instance ids (starts with AI-...) in the instanceIds parameter in the JSON payload.

"},{"location":"applications/operations.html#stop-or-replace-specific-instances-of-an-application","title":"Stop or replace specific instances of an application","text":"

Application instances can be killed by issuing the STOP_INSTANCES operation. Default behaviour of Drove is to replace killed instances by new instances. Such new instances are always spun up before the specified(old) instances are stopped. If skipRespawn parameter is set to true, the application instance is killed but no new instances are spun up to replace it.

Warning

If the cluster does not have sufficient capacity to spin up new containers, and skipRespawn is not set or set to false, this operation will get stuck.

Preconditions - Application must be in RUNNING state.

State Transition:

  • RUNNING \u2192 STOP_INSTANCES_REQUESTED \u2192 RUNNING if final number of instances is non zero
  • RUNNING \u2192 STOP_INSTANCES_REQUESTED \u2192 MONITORING if final number of instances is zero
Drove CLIJSON
drove -c local apps appinstances kill TEST_APP-1 AI-601d160e-c692-4ddd-8b7f-4c09b30ed02e\n

Sample Request Payload

{\n    \"type\": \"STOP_INSTANCES\",\n    \"appId\" : \"TEST_APP-1\",//(1)!\n    \"instanceIds\" : [ \"AI-601d160e-c692-4ddd-8b7f-4c09b30ed02e\" ],//(2)!\n    \"skipRespawn\" : true,//(3)!\n    \"opSpec\": {//(4)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Application ID
  2. Instance ids to be stopped
  3. Do not spin up new containers to replace the stopped ones. This is set ot false by default.
  4. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"applications/operations.html#destroy-an-application","title":"Destroy an application","text":"

To remove an application deployment (appName-version combo) the DESTROY command can be issued.

Preconditions:

  • App should not exist in the cluster

State Transition:

  • MONITORING \u2192 DESTROY_REQUESTED \u2192 DESTROYED \u2192 none

To create an application, an Application Spec needs to be created first.

Once ready, CLI command needs to be issued or the following payload needs to be sent:

Drove CLIJSON
drove -c local apps destroy TEST_APP_1\n

Sample Request Payload

{\n    \"type\": \"DESTROY\",\n    \"appId\" : \"TEST_APP-1\",//(1)!\n    \"opSpec\": {//(2)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 2,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Spec as mentioned in Application Specification
  2. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

Warning

All metadata for an app and it's instances are completely obliterated from Drove's storage once an app is destroyed

"},{"location":"applications/outage.html","title":"Outage Detection and Recovery","text":"

Drove tracks all instances for an app deployment in the cluster. It will ensure the required number of containers is always running on the cluster.

"},{"location":"applications/outage.html#instance-health-detection-and-tracking","title":"Instance health detection and tracking","text":"

Executor runs periodic health checks on the container according to check spec configuration. - Runs readiness checks to ensure container is started properly before declaring it healthy - Runs health checks on the container at regular intervals to ensure it is in operating condition

Behavior for both is configured by setting the appropriate options in the application specification.

Result of such health checks (both success and failure) are reported to the controller. Appropriate action is taken to shut down containers that fail readiness or health checks.

"},{"location":"applications/outage.html#container-crash","title":"Container crash","text":"

If container for an application crashes, Drove will automatically spin up a container in it's place.

"},{"location":"applications/outage.html#executor-node-hardware-failure","title":"Executor node hardware failure","text":"

If an executor node fails, instances running on that node will be lost. This is detected by the outage detector and new containers are spun up on other parts of the cluster.

"},{"location":"applications/outage.html#executor-service-temporary-unavailability","title":"Executor service temporary unavailability","text":"

On restart, executor service reads the metadata embedded in the container and registers them. It performs a reconciliation with the leader controller to kill any local containers if the unavailability was too long and controller has already spun up new alternatives.

"},{"location":"applications/outage.html#zombie-container-detection-and-cleanup","title":"Zombie (container) detection and cleanup","text":"

Executor service keeps track of all containers it is supposed to run by running periodic reconciliation with the leader controller. Any mismatch gets handled:

  • if a container is found that is not supposed to be running, it is killed
  • If a container that is supposed to be running is not found, it is marked as lost and reported to the controller. This triggers the controller to spin up an alternative container on the cluster.
"},{"location":"applications/specification.html","title":"Application Specification","text":"

An application is defined using JSON. We use a sample configuration below to explain the options.

"},{"location":"applications/specification.html#sample-application-definition","title":"Sample Application Definition","text":"
{\n    \"name\": \"TEST_APP\", // (1)!\n    \"version\": \"1\", // (2)!\n    \"type\": \"SERVICE\", // (3)!\n    \"executable\": { //(4)!\n        \"type\": \"DOCKER\", // (5)!\n        \"url\": \"ghcr.io/appform-io/perf-test-server-httplib\",// (6)!\n        \"dockerPullTimeout\": \"100 seconds\"// (7)!\n    },\n    \"resources\": [//(20)!\n        {\n            \"type\": \"CPU\",\n            \"count\": 1//(21)!\n        },\n        {\n            \"type\": \"MEMORY\",\n            \"sizeInMB\": 128//(22)!\n        }\n    ],\n    \"volumes\": [//(12)!\n        {\n            \"pathInContainer\": \"/data\",//(13)!\n            \"pathOnHost\": \"/mnt/datavol\",//(14)!\n            \"mode\" : \"READ_WRITE\"//(15)!\n        }\n    ],\n    \"configs\" : [//(16)!\n        {\n            \"type\" : \"INLINE\",//(17)!\n            \"localFilename\": \"/testfiles/drove.txt\",//(18)!\n            \"data\" : \"RHJvdmUgdGVzdA==\"//(19)!\n        }\n    ],\n    \"placementPolicy\": {//(23)!\n        \"type\": \"ANY\"//(24)!\n    },\n    \"exposedPorts\": [//(8)!\n        {\n            \"name\": \"main\",//(9)!\n            \"port\": 8000,//(10)!\n            \"type\": \"HTTP\"//(11)!\n        }\n    ],\n    \"healthcheck\": {//(25)!\n        \"mode\": {//(26)!\n            \"type\": \"HTTP\", //(27)!\n            \"protocol\": \"HTTP\",//(28)!\n            \"portName\": \"main\",//(29)!\n            \"path\": \"/\",//(30)!\n            \"verb\": \"GET\",//(31)!\n            \"successCodes\": [//(32)!\n                200\n            ],\n            \"payload\": \"\", //(33)!\n            \"connectionTimeout\": \"1 second\" //(34)!\n        },\n        \"timeout\": \"1 second\",//(35)!\n        \"interval\": \"5 seconds\",//(36)!\n        \"attempts\": 3,//(37)!\n        \"initialDelay\": \"0 seconds\"//(38)!\n    },\n    \"readiness\": {//(39)!\n        \"mode\": {\n            \"type\": \"HTTP\",\n            \"protocol\": \"HTTP\",\n            \"portName\": \"main\",\n            \"path\": \"/\",\n            \"verb\": \"GET\",\n            \"successCodes\": [\n                200\n            ],\n            \"payload\": \"\",\n            \"connectionTimeout\": \"1 second\"\n        },\n        \"timeout\": \"1 second\",\n        \"interval\": \"3 seconds\",\n        \"attempts\": 3,\n        \"initialDelay\": \"0 seconds\"\n    },\n    \"exposureSpec\": {//(42)!\n        \"vhost\": \"testapp.local\", //(43)!\n        \"portName\": \"main\", //(44)!\n        \"mode\": \"ALL\"//(45)!\n    },\n    \"env\": {//(41)!\n        \"CORES\": \"8\"\n    },\n    \"args\" : [//(54)!\n        \"./entrypoint.sh\",\n        \"arg1\",\n        \"arg2\"\n    ],\n    \"tags\": { //(40)!\n        \"superSpecialApp\": \"yes_i_am\",\n        \"say_my_name\": \"heisenberg\"\n    },\n    \"preShutdown\": {//(46)!\n        \"hooks\": [ //(47)!\n            {\n                \"type\": \"HTTP\",\n                \"protocol\": \"HTTP\",\n                \"portName\": \"main\",\n                \"path\": \"/\",\n                \"verb\": \"GET\",\n                \"successCodes\": [\n                    200\n                ],\n                \"payload\": \"\",\n                \"connectionTimeout\": \"1 second\"\n            }\n        ],\n        \"waitBeforeKill\": \"3 seconds\"//(48)!\n    },\n    \"logging\": {//(49)!\n        \"type\": \"LOCAL\",//(50)!\n        \"maxSize\": \"100m\",//(51)!\n        \"maxFiles\": 3,//(52)!\n        \"compress\": true//(53)!\n    }\n}\n
  1. A human readable name for the application. This will remain constant for different versions of the app.
  2. A version number. Drove does not enforce any format for this, but it is recommended to increment this for changes in spec.
  3. This should be fixed to SERVICE for an application/service.
  4. Coordinates for the executable. Refer to Executable Specification for details.
  5. Right now the only type supported is DOCKER.
  6. Docker container address
  7. Timeout for container pull.
  8. The ports to be exposed from the container.
  9. A logical name for the port. This will be used to reference this port in other sections.
  10. Actual port number as mentioned in Dockerfile.
  11. Type of port. Can be: HTTP, HTTPS, TCP, UDP.
  12. Volumes to be mounted. Refer to Volume Specification for details.
  13. Path that will be visible inside the container for this mount.
  14. Actual path on the host machine for the mount.
  15. Mount mode can be READ_WRITE and READ_ONLY
  16. Configuration to be injected as file inside the container. Please refer to Config Specification for details.
  17. Type of config. Can be INLINE, EXECUTOR_LOCAL_FILE, CONTROLLER_HTTP_FETCH and EXECUTOR_HTTP_FETCH. Specifies how drove will get the contents to be injected..
  18. File name for the config inside the container.
  19. Serialized form of the data, this and other parameters will vary according to the type specified above.
  20. List of resources required to run this application. Check Resource Requirements Specification for more details.
  21. Number of CPU cores to be allocated.
  22. Amount of memory to be allocated expressed in Megabytes
  23. Specifies how the container will be placed on the cluster. Check Placement Policy for details.
  24. Type of placement can be ANY, ONE_PER_HOST, MATCH_TAG, NO_TAG, RULE_BASED, ANY and COMPOSITE. Rest of the parameters in this section will depend on the type.
  25. Health check to ensure service is running fine. Refer to Check Specification for details.
  26. Mode of health check, can be api call or command.
  27. Type of this check spec. Type can be HTTP or CMD. Rest of the options in this example are HTTP specific.
  28. API call protocol. Can be HTTP/HTTPS
  29. Port name as mentioned in the exposedPorts section.
  30. HTTP path. Include query params here.
  31. HTTP method. Can be GET,PUT or POST.
  32. Set of HTTP status codes which can be considered as success.
  33. Payload to be sent for POST and PUT calls.
  34. Connection timeout for the port.
  35. Timeout for the check run.
  36. Interval between check runs.
  37. Max attempts after which the overall check is considered to be a failure.
  38. Time to wait before starting check runs.
  39. Readiness check to pass for the container to be considered as ready. Refer to Check Specification for details.
  40. Key value metadata that can be used in external systems.
  41. Custom environment variables. Additional variables are injected by Drove as well. See Environment Variables section for details.
  42. Specifies the virtual host on which this container is exposed.
  43. FQDN for the virtual host.
  44. Port name as specified in exposedPorts section.
  45. Mode for exposure. Set this to ALL for now.
  46. Things to do before a container is shutdown. Check Pre Shutdown Behavior for more details.
  47. Hooks (HTTP api call or shell command) to run before shutting down the container. Format is same as health/readiness checks. Refer to HTTP Check Actions and Command Check Options for details.
  48. Time to wait before killing the container. The container will be in UNREADY state during this time and hence won't have api calls routed to it via Drove Gateway.
  49. Specify how docker log files are configured. Refer to Logging Specification
  50. Log to local file
  51. Maximum File Size
  52. Number of latest log files to retain
  53. Log files will be compressed
  54. List of command line arguments. See Command Line Arguments for details.
"},{"location":"applications/specification.html#executable-specification","title":"Executable Specification","text":"

Right now Drove supports only docker containers. However as engines, both docker and podman are supported. Drove executors will fetch the executable directly from the registry based on the configuration provided.

Name Option Description Type type Set type to DOCKER. URL url Docker container URL`. Timeout dockerPullTimeout Timeout for docker image pull.

Note

Drove supports docker registry authentication. This can be configured in the executor configuration file.

"},{"location":"applications/specification.html#resource-requirements-specification","title":"Resource Requirements Specification","text":"

This section specifies the hardware resources required to run the container. Right now only CPU and MEMORY are supported as resource types that can be reserved for a container.

"},{"location":"applications/specification.html#cpu-requirements","title":"CPU Requirements","text":"

Specifies number of cores to be assigned to the container.

Name Option Description Type type Set type to CPU for this. Count count Number of cores to be assigned."},{"location":"applications/specification.html#memory-requirements","title":"Memory Requirements","text":"

Specifies amount of memory to be allocated to a container.

Name Option Description Type type Set type to MEMORY for this. Count sizeInMB Amount of memory (in Mega Bytes) to be allocated.

Sample

[\n    {\n        \"type\": \"CPU\",\n        \"count\": 1\n    },\n    {\n        \"type\": \"MEMORY\",\n        \"sizeInMB\": 128\n    }\n]\n

Note

Both CPU and MEMORY configurations are mandatory.

"},{"location":"applications/specification.html#volume-specification","title":"Volume Specification","text":"

Files and directories can be mounted from the executor host into the container. The volumes section contains a list of volumes that need to be mounted.

Name Option Description Path In Container pathInContainer Path that will be visible inside the container for this mount. Path On Host pathOnHost Actual path on the host machine for the mount. Mount Mode mode Mount mode can be READ_WRITE and READ_ONLY to allow the containerized process to write or read to the volume.

Info

We do not support mounting remote volumes as of now.

"},{"location":"applications/specification.html#config-specification","title":"Config Specification","text":"

Drove supports injection of configuration files into containers. The specifications for the same are discussed below.

"},{"location":"applications/specification.html#inline-config","title":"Inline config","text":"

Inline configuration can be added in the Application Specification itself. This will manifest as a file inside the container.

The following details are needed for this:

Name Option Description Type type Set the value to INLINE Local Filename localFilename File name for the config inside the container. Data data Base64 encoded string for the data. The value for this will be masked on UI.

Config file:

port: 8080\nlogLevel: DEBUG\n
Corresponding config specification:
{\n    \"type\" : \"INLINE\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"data\" : \"cG9ydDogODA4MApsb2dMZXZlbDogREVCVUcK\"\n}\n

Warning

The full base 64 encoded config data will get stored in Drove ZK and will be pushed to executors inline. It is not recommended to stream large config files to containers using this method. This will probably need additional configuration on your ZK cluster.

"},{"location":"applications/specification.html#locally-loaded-config","title":"Locally loaded config","text":"

Config file from a path on the executor directly. Such files can be distributed to the executor host using existing configuration management systems such as OpenTofu, Salt etc.

The following details are needed for this:

Name Option Description Type type Set the value to EXECUTOR_LOCAL_FILE Local Filename localFilename File name for the config inside the container. File path filePathOnHost Path to the config file on executor host.

Sample config specification:

{\n    \"type\" : \"EXECUTOR_LOCAL_FILE\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"data\" : \"/mnt/configs/myservice/config.yml\"\n}\n

"},{"location":"applications/specification.html#controller-fetched-config","title":"Controller fetched Config","text":"

Config file can be fetched from a remote server by the controller. Once fetched, these will be streamed to the executor as part of the instance specification for starting a container.

The following details are needed for this:

Name Option Description Type type Set the value to CONTROLLER_HTTP_FETCH Local Filename localFilename File name for the config inside the container. HTTP Call Details http HTTP Call related details. Please refer to HTTP Call Specification for details.

Sample config specification:

{\n    \"type\" : \"CONTROLLER_HTTP_FETCH\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"http\" : {\n        \"protocol\" : \"HTTP\",\n        \"hostname\" : \"configserver.internal.yourdomain.net\",\n        \"port\" : 8080,\n        \"path\" : \"/configs/myapp\",\n        \"username\" : \"appuser\",\n        \"password\" : \"secretpassword\"\n    }\n}\n

Note

The controller will make an API call for every single time it asks an executor to spin up a container. Please make sure to account for this in your configuration management system.

"},{"location":"applications/specification.html#executor-fetched-config","title":"Executor fetched Config","text":"

Config file can be fetched from a remote server by the executor before spinning up a container. Once fetched, the payload will be injected as a config file into the container.

The following details are needed for this:

Name Option Description Type type Set the value to EXECUTOR_HTTP_FETCH Local Filename localFilename File name for the config inside the container. HTTP Call Details http HTTP Call related details. Please refer to HTTP Call Specification for details.

Sample config specification:

{\n    \"type\" : \"EXECUTOR_HTTP_FETCH\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"http\" : {\n        \"protocol\" : \"HTTP\",\n        \"hostname\" : \"configserver.internal.yourdomain.net\",\n        \"port\" : 8080,\n        \"path\" : \"/configs/myapp\",\n        \"username\" : \"appuser\",\n        \"password\" : \"secretpassword\"\n    }\n}\n

Note

All executors will make an API call for every single time they spin up a container for this application. Please make sure to account for this in your configuration management system.

"},{"location":"applications/specification.html#http-call-specification","title":"HTTP Call Specification","text":"

This section details the options that can set when making http calls to a configuration management system from controllers or executors.

The following options are available for HTTP call:

Name Option Description Protocol protocol Protocol to use for upstream call. Can be HTTP or HTTPS. Hostname hostname Host to call. Port port Provide custom port. Defaults to 80 for http and 443 for https. API Path path Path component of the URL. Include query parameters here. Defaults to / HTTP Method verb Type of call, use GET, POST or PUT. Defaults to GET. Success Code successCodes List of HTTP status codes which is considered as success. Defaults to [200] Payload payload Data to be used for POST and PUT calls Connection Timeout connectionTimeout Timeout for upstream connection. Operation timeout operationTimeout Timeout for actual operation. Username username Username to be used basic auth. This field is masked out on the UI. Password password Password to be used for basic auth. This field is masked on the UI. Authorization Header authHeader Data to be passed in HTTP Authorization header. This field is masked on the UI. Additional Headers headers Any other headers to be passed to the upstream in the HTTP calls. This is a map of Skip SSL Checks insecure Skip hostname and certification checks during SSL handshake with the upstream."},{"location":"applications/specification.html#placement-policy-specification","title":"Placement Policy Specification","text":"

Placement policy governs how Drove deploys containers on the cluster. The following sections discuss the different placement policies available and how they can be configured to achieve optimal placement of containers.

Warning

All policies will work only at a {appName, version} combination level. They will not ensure constraints at an appName level. This means that for somethinge like a one per node placement, for the same appName, multiple containers can run on the same host if multiple deployments with different versions are active in a cluster. Same applies for all policies like N per host and so on.

Important details about executor tagging

  • All hosts have at-least one tag, it's own hostname.
  • The TAG policy will consider them as valid tags. This can be used to place containers on specific hosts if needed.
  • This is handled specially in all other policy types and they will consider executors having only the hostname tag as untagged.
  • A host with a tag (other than host) will not have any containers running if not placed on them specifically using the MATCH_TAG policy
"},{"location":"applications/specification.html#any-placement","title":"Any Placement","text":"

Containers for a {appName, version} combination can run on any un-tagged executor host.

Name Option Description Policy Type type Put ANY as policy.

Sample:

{\n    \"type\" : \"ANY\"\n}\n

Tip

For most use-cases this is the placement policy to use.

"},{"location":"applications/specification.html#one-per-host-placement","title":"One Per Host Placement","text":"

Ensures that only one container for a particular {appName, version} combination is running on an executor host at a time.

Name Option Description Policy Type type Put ONE_PER_HOST as policy.

Sample:

{\n    \"type\" : \"ONE_PER_HOST\"\n}\n

"},{"location":"applications/specification.html#max-n-per-host-placement","title":"Max N Per Host Placement","text":"

Ensures that at most N containers for a {appName, version} combination is running on an executor host at a time.

Name Option Description Policy Type type Put MAX_N_PER_HOST as policy. Max count max The maximum num of containers that can run on an executor. Range: 1-64

Sample:

{\n    \"type\" : \"MAX_N_PER_HOST\",\n    \"max\": 3\n}\n

"},{"location":"applications/specification.html#match-tag-placement","title":"Match Tag Placement","text":"

Ensures that containers for a {appName, version} combination are running on an executor host that has the tags as mentioned in the policy.

Name Option Description Policy Type type Put MATCH_TAG as policy. Max count tag The tag to match.

Sample:

{\n    \"type\" : \"MATCH_TAG\",\n    \"tag\": \"gpu_enabled\"\n}\n

"},{"location":"applications/specification.html#no-tag-placement","title":"No Tag Placement","text":"

Ensures that containers for a {appName, version} combination are running on an executor host that has no tags.

Name Option Description Policy Type type Put NO_TAG as policy.

Sample:

{\n    \"type\" : \"NO_TAG\"\n}\n

Info

The NO_TAG policy is mostly for internal use, and does not need to be specified when deploying containers that do not need any special placement logic.

"},{"location":"applications/specification.html#composite-policy-based-placement","title":"Composite Policy Based Placement","text":"

Composite policy can be used to combine policies together to create complicated placement requirements.

Name Option Description Policy Type type Put COMPOSITE as policy. Polices policies List of policies to combine Combiner combiner Can be AND and OR and signify all-match and any-match logic on the policies mentioned.

Sample:

{\n    \"type\" : \"COMPOSITE\",\n    \"policies\": [\n        {\n            \"type\": \"ONE_PER_HOST\"\n        },\n        {\n            \"type\": \"MATH_TAG\",\n            \"tag\": \"gpu_enabled\"\n        }\n    ],\n    \"combiner\" : \"AND\"\n}\n
The above policy will ensure that only one container of the relevant {appName,version} will run on GPU enabled machines.

Tip

It is easy to go into situations where no executors match complicated placement policies. Internally, we tend to keep things rather simple and use the ANY placement for most cases and maybe tags in a few places with over-provisioning or for hosts having special hardware

"},{"location":"applications/specification.html#environment-variables","title":"Environment variables","text":"

This config can be used to inject custom environment variables to containers. The values are defined as part of deployment specification, are same across the cluster and immutable to modifications from inside the container (ie any overrides from inside the container will not be visible across the cluster).

Sample:

{\n    \"MY_VARIABLE_1\": \"fizz\",\n    \"MY_VARIABLE_2\": \"buzz\"\n}\n

The following environment variables are injected by Drove to all containers:

Variable Name Value HOST Hostname where the container is running. This is for marathon compatibility. PORT_PORT_NUMBER A variable for every port specified in exposedPorts section. The value is the actual port on the host, the specified port is mapped to. For example if ports 8080 and 8081 are specified, two variables called PORT_8080 and PORT_8081 will be injected. DROVE_EXECUTOR_HOST Hostname where container is running. DROVE_CONTAINER_ID Container that is deployed DROVE_APP_NAME App name as specified in the Application Specification DROVE_INSTANCE_ID Actual instance ID generated by Drove DROVE_APP_ID Application ID as generated by Drove DROVE_APP_INSTANCE_AUTH_TOKEN A JWT string generated by Drove that can be used by this container to call /apis/v1/internal/... apis.

Warning

Do not pass secrets using environment variables. These variables are all visible on the UI as is. Please use Configs to inject secrets files and so on.

"},{"location":"applications/specification.html#command-line-arguments","title":"Command line arguments","text":"

A list of command line arguments that are sent to the container engine to execute inside the container. This is provides ways for you to configure your container behaviour based off such arguments. Please refer to docker documentation for details.

Danger

This might have security implications from a system point of view. As such Drove provides administrators a way to disable passing arguments at the cluster level by setting disableCmdlArgs to true in the controller configuration.

"},{"location":"applications/specification.html#check-specification","title":"Check Specification","text":"

One of the cornerstones of managing applications on the cluster is to ensure we keep track of instance health and manage their life cycle depending on their health state. We need to define how to monitor health for containers accordingly. The checks will be executed on Applications and a Check result is generated. The result consists of the following:

  • Status - Healthy, Unhealthy or Stopped if the container is already in stopping state
  • Message - Any error message as generated by a specific checker
"},{"location":"applications/specification.html#common-options","title":"Common Options","text":"Name Option Description Mode mode The definition of a HTTP call or a Command to be executed in the container. See following sections for details. Timeout timeout Duration for which we wait before declaring a check as failed Interval interval Interval at which check will be retried Attempts attempts Number of times a check is retried before it is declared as a failure Initial Delay initialDelay Delay before executing the check for the first time.

Note

initialDelay is ignored when readiness checks and health checks are run in the recovery path as the container is already running at that point in time.

"},{"location":"applications/specification.html#http-check-options","title":"HTTP Check Options","text":"Name Option Description Type type Fixed to HTTP for HTTP checker Protocol protocol HTTP or HTTPS call to be made Port Name portName The name of the container port to make the http call on as specified in the Exposed Ports section in Application Spec Path path The api path to call HTTP method verb The HTTP Verb/Method to invoke. GET/PUT and POST are supported here Success Codes successCodes A set of HTTP status codes that we should consider as a success from this API. Payload payload A string payload that we can pass if the Verb is POST or PUT Connection Timeout connectionTimeout Maximum time for which the checker will wait for the connection to be set up with the container. Insecure insecure Skip hostname and certificate checks for HTTPS ports during checks."},{"location":"applications/specification.html#command-check-options","title":"Command Check Options","text":"Field Option Description Type type Fixed to CMD for command checker Command command Command to execute in the container. (Equivalent to docker exec -it <container> command>)"},{"location":"applications/specification.html#exposure-specification","title":"Exposure Specification","text":"

Exposure spec is used to specify the virtual host Drove Gateway exposes to outside world for communication with the containers.

The following information needs to be specified:

Name Option Description Virtual Host vhost The virtual host to be exposed on NGinx. This should be a fully qualified domain name. Port Name portName The portname to be exposed on the vhost. Port names are defined in exposedPorts section. Exposure Mode mode Use ALL here for now. Signifies that all healthy instances of the app are exposed to traffic.

Sample:

{\n    \"vhost\": \"teastapp.mydomain\",\n    \"port\": \"main\",\n    \"mode\": \"ALL\"\n}\n

Note

Application instances in any state other than HEALTHY are not considered for exposure. Please check Application Instance State Machine for an understanding of states of instances.

"},{"location":"applications/specification.html#configuring-pre-shutdown-behaviour","title":"Configuring Pre Shutdown Behaviour","text":"

Before a container is shut down, it is desirable to ensure things are spun down properly. This behaviour can be configured in the preShutdown section of the configuration.

Name Option Description Hooks hooks List of api calls and commands to be run on the container before it is killed. Each hook is either a HTTP Call Spec or Command Spec Wait Time waitBeforeKill Time to wait before killing the container.

Sample

{\n    \"hooks\": [\n        {\n            \"type\": \"HTTP\",\n            \"protocol\": \"HTTP\",\n            \"portName\": \"main\",\n            \"path\": \"/\",\n            \"verb\": \"GET\",\n            \"successCodes\": [\n                200\n            ],\n            \"payload\": \"\",\n            \"connectionTimeout\": \"1 second\"\n        }\n    ],\n    \"waitBeforeKill\": \"3 seconds\"//(48)!\n}\n

Note

The waitBeforeKill timed wait kicks in after all the hooks have been executed.

"},{"location":"applications/specification.html#logging-specification","title":"Logging Specification","text":"

Can be used to configure how container logs are managed on the system.

Note

This section affects the docker log driver. Drove will continue to stream logs to it's own logger which can be configured at executor level through the executor configuration file.

"},{"location":"applications/specification.html#local-logger-configuration","title":"Local Logger configuration","text":"

This is used to configure the json-file log driver.

Name Option Description Type type Set the value to LOCAL Max Size maxSize Maximum file size. Anything bigger than this will lead to rotation. Max Files maxFiles Maximum number of logs files to keep. Range: 1-100 Compress compress Enable log file compression.

Tip

If logging section is omitted, the following configuration is applied by default: - File size: 10m - Number of files: 3 - Compression: on

"},{"location":"applications/specification.html#rsyslog-configuration","title":"Rsyslog configuration","text":"

In case suers want to stream logs to an rsyslog server, the logging configuration needs to be set to RSYSLOG mode.

Name Option Description Type type Set the value to RSYSLOG Server server URL for the rsyslog server. Tag Prefix tagPrefix Prefix to add at the start of a tag Tag Suffix tagSuffix Suffix to add at the en of a tag.

Note

The default tag is the DROVE_INSTANCE_ID. The tagPrefix and tagSuffix will to before and after this

"},{"location":"cluster/cluster.html","title":"Anatomy of a Drove Cluster","text":"

The following diagram provides a high level overview of a typical Drove cluster. The overall topology consists of the following components:

  • An Apache ZooKeeper cluster for state persistence and coordination
  • A set of controller nodes one of which (the leader) manages the cluster
  • A set of executor nodes on which the containers actually execute
  • NGinx + drove-gateway nodes that expose virtual hosts for the leader controller as well as for the vhosts defined for the various applications running on the cluster
"},{"location":"cluster/cluster.html#apache-zookeeper","title":"Apache ZooKeeper","text":"

Zookeeper is a central component in a Drove cluster. It is used in the following manner:

  • As store for discovery of cluster components like Controller and Executor to each other
  • For electing the leader controller in the cluster
  • As storage for Application and Task Specifications
  • Asynchronous communication channel/transient store for real-time information about controller and executor state in the cluster
"},{"location":"cluster/cluster.html#controller","title":"Controller","text":"

The controller service is the brains of a Drove cluster. The role of the controller consists of the following:

  • Ensure it has a reasonably up-to-date information about the cluster topology and free/used resources
  • Track executor status (blacklisted/online/offline etc) and tagging. - Take corrective actions in case some of them become inaccessible for whatever reason
  • Manages container placement to ensure that application and task containers get placed according to provided placement configuration/spec
  • Manage NUMA node and core affinity ensuring that instances get deployed optimally on cores and NUMA nodes without stepping on each other
  • Provide a UI for users to consume data about cluster, applications and tasks
  • Provide APIs for systems to provision apps, tasks and manage the cluster
  • Provide event stream for other tools and services to follow what is happening on the cluster
  • Provide APIs to list container level logs and provide real-time offset based polling of log contents for application and task instances
  • Implement leader election based HA so that only one controller is active(leader) at a time.
  • All decisions regarding scheduling, state machine management and recovery are taken only by the leader
  • Manage the lifecycle of all applications deployed on the cluster.
    • Maintain the required number of application instances as specified during deployment. This means that the controller has to monitor all applications running on all nodes and replace any instances or kill any spurious ones to ensure a constant number of instances on the cluster. The required number of instances is maintained as expected count, the current number of instances is maintained as running count.
    • Provide a way to adjust the number of instances for this application. This would help in users being able to scale applications up and down as needed.
    • Provide a way to restart all instances of the application. This would mean the controller would have to orchestrate a continuous string of start-kill operations across instances running on the cluster.
    • Graceful shutdown/suspension of application across the cluster. This comes as a natural extension of the above and is mostly a scale down operation with the expected count set as zero.
  • Manage task lifecycle
    • Maintain task state-machine by scheduling it on executors and ensuring it reaches terminal state
    • Provide mechanisms to cancel tasks
  • Reconcile stale and dead instances for applications and tasks and take corrective measures to ensure steady state if necessary
  • Application instance migration from blacklisted executors
  • Send command messages to executors to start and stop instances with retries and failure recovery
"},{"location":"cluster/cluster.html#executors","title":"Executors","text":"

Executors are the agents running on the nodes where the containers are deployed. Role of the executors is the following:

  • Publish hardware topology of the machine to the controller on startup.
  • Manage container lifecycle including:
    • Pulling containers from docker repository with optional authentication
    • Start containers with proper options for pinning containers to specific NUMA nodes and cores as specified by controller
      • Data for an instance is stored as specific docker label values on the containers themselves
    • Run HTTP call or shell command based readiness checks to ensure application container is ready to serve traffic based on readiness checks specification in start message
    • Monitor application container health by running periodic HTTP call or shell command based health checks as specified by controller in start message
    • Track normal (for tasks) or abnormal (for application instances) container exits.
      • For tasks, the exit code is collected and used to deduce if task succeeded (exit code is 0) or failed (exit code is non-zero)
      • For application containers, the expectation is for the container to stop only when explicitly requested and hence all exits are considered as failures and handled accordingly
    • Stop application containers on request from controller
    • Run any pre-shutdown hook calls as specified in the application specification before killing container
    • Cleanup container volumes etc
    • Cleanup docker images (if local image caching is turned off which is the default behaviour)
  • Send regular local node status updates to ZooKeeper every 20 seconds
  • Send instant updates by making direct HTTP calls to the leader controller when anything changes for any running containers and for every step of the container state machine execution for both task and application instances to allow for faster updates
  • Recover containers on process restart based on the metadata stored as labels on the running container. This data is reconciled with a snapshot of the expected instances on the node as received from the leader controller at that point in time.
  • Find and kill any zombie containers that are not supposed to exist on that node. The check is done every 30 seconds.
  • Provide container log-file listing and offset based content delivery APIs to container
"},{"location":"cluster/cluster.html#nginx-and-drove-gateway","title":"NGinx and Drove-Gateway","text":"

Almost all of the traffic between service containers is routed via the internal Ranger based service discovery system at PhonePe. However, traffic from the edge as well and between different protected environments are routed using the well-established virtual host (and additionally, in some unusual cases, header) based routing.

  • All applications on Drove can specify a Vhost and a port name as endpoint for such routing.
  • Upstream information for such VHosts or endpoints is available from an API from the leading Drove controller.
  • This information can be used to configure any load-balancer or tourer or reverse proxy to expose applications running on Drove to the outside world.
  • We modified an existing project called Nixy so that it gets the upstream information from Drove instead of Marathon. Nixy plays the following role in a cluster:

  • Track the leader controller for a Drove cluster by making ping calls to all specified controllers

  • Provide a special data structure that can be used by a template to expose a vhost that points to the leader controller in a Drove cluster. This can be used for any tools that need to interact with a Drove cluster for deployments, monitoring as well as callback endpoints for OAuth etc etc
  • Listen to relevant events from the Drove cluster to trigger upstream refresh as necessary
  • Provide data structures that include the vhost, upstream endpoints (host:port) and metadata (application level tags) that can be used to build templates that generate NGinx configurations to enable progressively complicated routing of calls from downstream to upstreams hosted on Drove clusters. Data structure exposed to templates, groups all upstream host:port tuples by using the vhost. This allows for multiple deployments for the same VHost to exist. This is needed for a variety of situations including online-updates of services.
  • Supports username/password based authentication and header based (used internally) to Drove clusters.
  • Support for both NGinx Plus and OSS products. Drove-Nixy can make appropriate api calls to corresponding NGinx plus server to only refresh existing VHost on topology change, as well as affect a full reload when new Vhosts are detected. This ensures that there are no connection drops for critical path applications where NGinx Plus might be used. This also solves the issue of NGinx workers going into a hung state due to frequent reloads on busy clusters like our dev testing environment.

Tip

The NGinx deployment is standard across all Drove clusters. However, for clusters that receive a lot of traffic using Nginx, the cluster exposing the VHost for Drove itself might be separated from the one exposing the application virtual hosts to allow for easy scalability of the latter. The template for these are configured differently as needed respectively.

"},{"location":"cluster/cluster.html#other-components","title":"Other components","text":"

There are a few more components that are used for operational management and observability.

"},{"location":"cluster/cluster.html#telegraf","title":"Telegraf","text":"

PhonePe\u2019s internal metric management system uses a HTTP based metric collector. Telegraf is installed on all Drove nodes to collect metric from the metric port (Admin connector on Dropwizard) and push that information to our metric ingestion system. This information is then used to build dashboards as well as by our Anomaly detection and alerting systems.

"},{"location":"cluster/cluster.html#log-management","title":"Log Management","text":"

Drove provides a special logger called drove that can be configured to handle compression rotation and archival of container logs. Such container logs are stored on specialised partitions by application/application-instance-id or by source app name/ task id for application and task instances respectively. PhonePe\u2019s standardised log rotation tools are used to monitor and ship out such logs to our central log management system. The same can be replaced or enhanced by running something like promtail on Drove logs to ship out logs to tools like Grafana Loki.

"},{"location":"cluster/setup/controller.html","title":"Setting up Controllers","text":"

Controllers are the brains of Drove cluster. For HA, at least 2 controllers should be set up.

Please note the following behaviour about controllers:

  • Only one controller is leader at a time. A leader controller does not relinquish control till the process is stopped or dies
  • A controller process will die when it loses connectivity to Zookeeper
  • The process/container for controller should keep restarting till it gets connectivity
  • All decisions for the cluster are taken by the leader controller only
  • During maintenance and package upgrades etc, it is better to roll changes out on non-leaders first and then do the leader at the end
  • The controller process holds all metadata about the cluster, the current states and other information in memory.
    • Some of this information is backed by Zookeeper based storage layer
    • The other information is recreated dynamically based on updates from executors
  • Controllers being down does not affect running containers on executors
"},{"location":"cluster/setup/controller.html#controller-configuration-file-reference","title":"Controller Configuration File Reference","text":"

The Drove Controller is written on the Dropwizard framework. The configuration to the service is set using a YAML file which needs to be injected into the container. A typical controller configuration file will look like the following:

server: #(1)!\n  applicationConnectors: #(2)!\n    - type: http\n      port: 4000\n  adminConnectors: #(3)!\n    - type: http\n      port: 4001\n  applicationContextPath: / #(4)!\n  requestLog: #(5)!\n    appenders:\n      - type: console\n        timeZone: ${DROVE_TIMEZONE}\n      - type: file\n        timeZone: ${DROVE_TIMEZONE}\n        currentLogFilename: /logs/drove-controller-access.log\n        archivedLogFilenamePattern: /logs/drove-controller-access.log-%d-%i\n        archivedFileCount: 3\n        maxFileSize: 100MiB\n\n\nlogging: #(6)!\n  level: INFO\n  loggers:\n    com.phonepe.drove: ${DROVE_LOG_LEVEL}\n\n  appenders:\n    - type: console #(7)!\n      threshold: ALL\n      timeZone: ${DROVE_TIMEZONE}\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n    - type: file #(8)!\n      threshold: ALL\n      timeZone: ${DROVE_TIMEZONE}\n      currentLogFilename: /logs/drove-controller.log\n      archivedLogFilenamePattern: /logs/drove-controller.log-%d-%i\n      archivedFileCount: 3\n      maxFileSize: 100MiB\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n      archive: true\n\n\nzookeeper: #(9)!\n  connectionString: ${ZK_CONNECTION_STRING}\n\nclusterAuth: #(10)!\n  secrets:\n  - nodeType: CONTROLLER\n    secret: ${DROVE_CONTROLLER_SECRET}\n  - nodeType: EXECUTOR\n    secret: ${DROVE_EXECUTOR_SECRET}\n\nuserAuth: #(11)!\n  enabled: true\n  users:\n    - username: admin\n      password: ${DROVE_ADMIN_PASSWORD}\n      role: EXTERNAL_READ_WRITE\n    - username: guest\n      password: ${DROVE_GUEST_PASSWORD}\n      role: EXTERNAL_READ_ONLY\n\ninstanceAuth: #(12)!\n  secret: ${DROVE_INSTANCE_AUTH_SECRET}\n\noptions: #(13)!\n  maxStaleInstancesCount: 3\n  staleCheckInterval: 1m\n  staleAppAge: 1d\n  staleInstanceAge: 18h\n  staleTaskAge: 1d\n  clusterOpParallelism: 4\n
  1. Server listener configuration. See Dropwizard Server Configuration for the different options.
  2. Main port configuration. This is where the UI and APIs will be exposed. Check connector configuration docs for details.
  3. Admin port. You can take thread dumps, metrics, run healthchecks on the Drove controller on this port.
  4. Base path for UI. Keep this as is.
  5. Access logs configuration. See requestLog docs.
  6. Main logging configuration. See logging docs.
  7. Log to console. Useful in docker-compose.
  8. Log to rotating files. Useful for running servers.
  9. Configure how to connect to Zookeeper See Zookeeper Config for details.
  10. Configuration for authentication between nodes in the cluster. Please check intra node auth config for details.
  11. Configure user authentication to access the cluster. Please check User auth config for details.
  12. Signing secret for JWT to be embedded in application and task instances. Check Instance auth config for details.
  13. Special options to configure controller behaviour. See Controller Options for details.

Tip

In case you do not want to expose admin apis to outside the host, please set bindHost in the admin connectors section.

adminConnectors:\n  - type: http\n    port: 10001\n    bindHost: 127.0.0.1\n
"},{"location":"cluster/setup/controller.html#zookeeper-connection-configuration","title":"Zookeeper Connection Configuration","text":"

The following details can be configured.

Name Option Description Connection String connectionString The connection string of the form: zkserver:2181,zkserver2:2181... Data namespace namespace The top level node inside which all Drove data will be scoped. Defaults to drove if not set.

Sample

zookeeper:\n  connectionString: \"192.168.3.10:2181,192.168.3.11:2181,192.168.3.12:2181\"\n  namespace: drovetest\n
"},{"location":"cluster/setup/controller.html#intra-node-authentication-configuration","title":"Intra Node Authentication Configuration","text":"

Communication between controller and executor is protected by a shared-secret based authentication. The following configuration is meant to configure this. This section consists of a list of 2 members:

  • Config for controller to talk to executors
  • Config for executors to talk to controller

Each section consists of the following:

Name Option Description Node Type nodeType Type of node in the cluster. Can be CONTROLLER or EXECUTOR Secret secret The actual secret to be passed.

Sample

clusterAuth:\n  secrets:\n  - nodeType: CONTROLLER\n    secret: ControllerSecretValue\n  - nodeType: EXECUTOR\n    secret: ExecutorSecret\n

Danger

The values are passed in the header as is. Please manage the config file ownership to ensure that the files are not world readable.

Tip

You can use pwgen -s 32 to generate secure random strings for usage as secrets.

"},{"location":"cluster/setup/controller.html#user-authentication-configuration","title":"User Authentication Configuration","text":"

This section is used to configure user details for human and other systems that need to call Drove APIs or access the Drove UI. This is implemented using basic auth.

The configuration consists of:

Name Option Description Enabled enabled Enable basic auth for the cluster Encoding encoding The actual encoding of the password. Can be PLAIN or CRYPT Caching cachingPolicy Caching policy for the authentication and authorization of the user. Please check CaffeineSpec docs for more details. Set to maximumSize=500, expireAfterAccess=30m by default List of users users A list of users recognized by the system

Each entry in the user list consists of:

Name Option Description User Name username The actual login username Password password The password for the user. Needs to be set to bcrypt string of the actual password if encoding is set to CRYPT in the parent section. User Role role The role of the user in the cluster. Can be EXTERNAL_READ_WRITE for users who have both read and write permissions or EXTERNAL_READ_ONLY for users with read-only permissions.

Sample

userAuth:\n  enabled: true\n  encoding: CRYPT\n  users:\n    - username: admin\n      password: \"$2y$10$pfGnPkYrJEGzasvVNPjRu.IJldV9TDa0Vh.u1UdimILWDuhvapc2O\"\n      role: EXTERNAL_READ_WRITE\n    - username: guest\n      password: \"$2y$10$uCJ7WxIvd13C.1oOTs28p.xpJShGiTWuDLY/sGH9JE8nrkSGBFkc6\"\n      role: EXTERNAL_READ_ONLY\n    - username: noread\n      password: \"$2y$10$8mr/zXL5rMW/s/jlBcgXHu0UvyzfdDDvyc.etfuoR.991sn9UOX/K\"\n

No authentication

To configure a cluster without authentication, remove this section entirely.

Operator role

If role is not set, the user will be able to access the UI, but will not have access to application logs. This comes in handy to provide access to other teams to explore your deployment topology, but not get access to your logs that might contain sensitive information.

Password Hashing

We strongly recommend using bcrypt passwords for authentication. You can use the following command to generate hashed password strings:

htpasswd -nbBC 10 <username> <password>|cut -d ':' -f2\n
"},{"location":"cluster/setup/controller.html#instance-authentication-configuration","title":"Instance Authentication Configuration","text":"

All application and task instances, get access to an unique JWT that is injected into it by Drove as the environment variable DROVE_APP_INSTANCE_AUTH_TOKEN. This token is signed using a secret. This secret can be configured by setting the secret parameter in the instanceAuth section.

Sample

instanceAuth:\n  secret: RandomSecret\n

"},{"location":"cluster/setup/controller.html#controller-options","title":"Controller Options","text":"

The following options can be set to influence the behavior of the Drove cluster and the controller.

Name Option Description Stale Check Interval staleCheckInterval Interval at which Drove checks for stale application and task metadata for cleanup. Defaults to 1 hour. Expressed in duration. Stale App Age staleAppAge Apps in MONITORING state are cleaned up after some time by Drove. This variable can be used to control the max time for which such apps are maintained in the cluster. Defaults to 7 days. Expressed in duration. Stale App Instances Count maxStaleInstancesCount Maximum number of application instances metadata for stopped or lost instances to be maintained in the cluster. Defaults to 100. Stale Instance Age staleInstanceAge Maximum age for a stale application instance to be retained. Defaults to 7 days. Expressed in duration. Stale Task Age staleTaskAge Maximum time for which metadata for a finished task is retained on the cluster. Defaults to 2 days. Expressed in duration. Event Storage Duration maxEventsStorageDuration Maximum time for which cluster events are retained on the cluster. Defaults to 1 hour. Expressed in duration. Default Operation Timeout clusterOpTimeout Timeout for operations that are initiated by drove itself. For example, instance spin up in case of executor failure, instance migrations etc. Defaults to 5 minutes. Expressed in duration. Operation threads clusterOpParallelism Signified the parallelism for operations internal to the cluster. Defaults to: 1. Range: 1-32. Audited Methods auditedHttpMethods Drove prints an audit log with user details when an api is called by an user. Defaults to [\"POST\", \"PUT\"]. Allowed mount directories allowedMountDirs If provided, Drove will ensure that application and task spec can mount only the directories mentioned in this set on executor host. Disable read-only auth disableReadAuth When userAuth is enabled, setting this option, will enforce authorization only on write operations. Disable command line arguments disableCmdlArgs When set to true, passing command line arguments will be disabled. Default: false (users can pass arguments.

Sample

options:\n  staleCheckInterval: 5m\n  staleAppAge: 2d\n  maxStaleInstancesCount: 20\n  staleInstanceAge: 1d\n  staleTaskAge: 2d\n  maxEventsStorageDuration: 30m\n  clusterOpParallelism: 32\n  allowedMountDirs:\n   - /mnt/scratch\n

"},{"location":"cluster/setup/controller.html#stale-data-cleanup","title":"Stale data cleanup","text":"

In order to keep internal memory footprint low, reduce the amount of data stored on Zookeeper, and provide a faster experience on the UI,Drove keeps cleaning up data for stale applications, application instances, task instances and cluster events.

The retention for such metadata can be controlled using the following config options:

  • staleAppAge
  • maxStaleInstancesCount
  • staleInstanceAge
  • staleTaskAge
  • maxEventsStorageDuration

Warning

Configuration changes done to these parameters will have direct impact on memory usage by the controller and memory and disk utilization on the Zookeeper cluster.

"},{"location":"cluster/setup/controller.html#internal-operations","title":"Internal Operations","text":"

Drove may need to create and issue operations on applications and tasks to manage cluster stability, for maintenance and other reasons. The following parameters can be used to control the speed and parallelism of such operations:

  • clusterOpTimeout
  • clusterOpParallelism

Tip

The default value of 1 for the clusterOpParallelism parameter is generally too low for most clusters. Unless there is a specific problem, it would be advisable to set this to at least 4. If number of instances is quite high for applications (order of tens or hundreds), feel free to set this to 32.

Increasing clusterOpParallelism will make recovery faster in case of executor failures, but it will increase cpu utilization on the controller by a little bit.

"},{"location":"cluster/setup/controller.html#security-related-options","title":"Security related options","text":"

The auditedHttpMethods parameter contains a list of all HTTP methods that need to be audited. This means that if the auditedHttpMethods contains POST and PUT, any drove HTTP POST or PUT apis being called will lead to a audit in the controller logs with the details of the user that made the call.

Warning

It would be advisable to not add GET to the list. This is because the UI keeps making calls to GET apis on drove to fetch data to render. These calls are automated and happen every few seconds from the browser. This will blow up controller logs size.

The allowedMountDirs option whitelists only some directories to be mounted on containers. If this is not provided, containers will be able to mount any directory on the executors.

Danger

It is highly recommended to set allowedMountDirs to a designated directory that containers might want to use as scratch space if needed. Keeping this empty will almost definitely cause security issues in the long run.

"},{"location":"cluster/setup/controller.html#relevant-directories","title":"Relevant directories","text":"

Location for data and logs are as follows:

  • /etc/drove/controller/ - Configuration files
  • /var/log/drove/controller/ - Logs

We shall be volume mounting the config and log directories with the same name.

Prerequisite Setup

If not done already, please complete the prerequisite setup on all machines earmarked for the cluster.

"},{"location":"cluster/setup/controller.html#setup-the-config-file","title":"Setup the config file","text":"

Create a relevant configuration file in /etc/drove/controller/controller.yml.

Sample

server:\n  applicationConnectors:\n    - type: http\n      port: 10000\n  adminConnectors:\n    - type: http\n      port: 10001\n  requestLog:\n    appenders:\n      - type: file\n        timeZone: IST\n        currentLogFilename: /var/log/drove/controller/drove-controller-access.log\n        archivedLogFilenamePattern: /var/log/drove/controller/drove-controller-access.log-%d-%i\n        archivedFileCount: 3\n        maxFileSize: 100MiB\n\nlogging:\n  level: INFO\n  loggers:\n    com.phonepe.drove: INFO\n\n\n  appenders:\n    - type: file\n      threshold: ALL\n      timeZone: IST\n      currentLogFilename: /var/log/drove/controller/drove-controller.log\n      archivedLogFilenamePattern: /var/log/drove/controller/drove-controller.log-%d-%i\n      archivedFileCount: 3\n      maxFileSize: 100MiB\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n\nzookeeper:\n  connectionString: \"192.168.56.10:2181\"\n\nclusterAuth:\n  secrets:\n  - nodeType: CONTROLLER\n    secret: \"0v8XvJrDc7r86ZY1QCByPTDPninI4Xii\"\n  - nodeType: EXECUTOR\n    secret: \"pOd9sIEXhv0wrGOVc7ebwNvR7twZqyTN\"\n\nuserAuth:\n  enabled: true\n  encoding: CRYPT\n  users:\n    - username: admin\n      password: \"$2y$10$pfGnPkYrJEGzasvVNPjRu.IJldV9TDa0Vh.u1UdimILWDuhvapc2O\"\n      role: EXTERNAL_READ_WRITE\n    - username: guest\n      password: \"$2y$10$uCJ7WxIvd13C.1oOTs28p.xpJShGiTWuDLY/sGH9JE8nrkSGBFkc6\"\n      role: EXTERNAL_READ_ONLY\n\n\ninstanceAuth:\n  secret: \"bd2SIgz9OMPG2L8wA6zxj21oLVLbuLFC\"\n\noptions:\n  maxStaleInstancesCount: 3\n  staleCheckInterval: 1m\n  staleAppAge: 2d\n  staleInstanceAge: 1d\n  staleTaskAge: 1d\n  clusterOpParallelism: 4\n  allowedMountDirs:\n   - /dev/null\n

"},{"location":"cluster/setup/controller.html#setup-required-environment-variables","title":"Setup required environment variables","text":"

Environment variables need to run the drove controller are setup in /etc/drove/controller/controller.env.

CONFIG_FILE_PATH=/etc/drove/controller/controller.yml\nJAVA_PROCESS_MIN_HEAP=2g\nJAVA_PROCESS_MAX_HEAP=2g\nZK_CONNECTION_STRING=\"192.168.3.10:2181\"\nJAVA_OPTS=\"-Xlog:gc:/var/log/drove/controller/gc.log -Xlog:gc:::filecount=3,filesize=10M -Xlog:gc::time,level,tags -XX:+UseNUMA -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -Dfile.encoding=utf-8 -Djute.maxbuffer=0x9fffff\"\n
"},{"location":"cluster/setup/controller.html#create-systemd-file","title":"Create systemd file","text":"

Create a systemd file. Put the following in /etc/systemd/system/drove.controller.service:

[Unit]\nDescription=Drove Controller Service\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nUser=drove\nTimeoutStartSec=0\nRestart=always\nExecStartPre=-/usr/bin/docker pull ghcr.io/phonepe/drove-controller:latest\nExecStart=/usr/bin/docker run  \\\n    --env-file /etc/drove/controller/controller.env \\\n    --volume /etc/drove/controller:/etc/drove/controller:ro \\\n    --volume /var/log/drove/controller:/var/log/drove/controller \\\n    --publish 10000:10000  \\\n    --publish 10001:10001 \\\n    --hostname %H \\\n    --rm \\\n    --name drove.controller \\\n    ghcr.io/phonepe/drove-controller:latest\n\n[Install]\nWantedBy=multi-user.target\n

Verify the file with the following command:

systemd-analyze verify drove.controller.service\n

Set permissions

chmod 664 /etc/systemd/system/drove.controller.service\n

"},{"location":"cluster/setup/controller.html#start-the-service-on-all-servers","title":"Start the service on all servers","text":"

Use the following to start the service:

systemctl daemon-reload\nsystemctl enable drove.controller\nsystemctl start drove.controller\n

You can tail the logs at /var/logs/drove/controller/drove-controller.log.

The console would be available at http://<ip>:10000 and admin functionality will be available on http://<ip>:10001 according to the above config.

Health checks can be performed by running a curl as follows:

curl http://localhost:10001/healthcheck\n

Note

  • The healthcheck check api is available on admin port.
  • HTTP status is 200/OK if things are fine.

Once controllers are up, one of them will become the leader. You can check the leader by running the following command:

curl http://<ip>:10000/apis/v1/ping\n

Only on the leader you should get the following response along with a HTTP status 200/OK:

{\n    \"status\":\"SUCCESS\",\n    \"data\":\"pong\",\n    \"message\":\"success\"\n}\n

"},{"location":"cluster/setup/executor-setup.html","title":"Setting up Executor Nodes","text":"

We shall setup the executor nodes by setting up the hardware, operating system first and then the executor service itself.

"},{"location":"cluster/setup/executor-setup.html#considerations-and-tuning-for-hardware-and-operating-system","title":"Considerations and tuning for hardware and operating system","text":"

In the following sections we discus some aspects of scheduling, hardware and settings on the OS to ensure good performance.

"},{"location":"cluster/setup/executor-setup.html#cpu-and-memory-considerations","title":"CPU and Memory considerations","text":"

The executor nodes are the servers that host and run the actual docker containers. Drove will take into consideration the NUMA topology of these machines to optimize the placement for containers to extract the maximum performance. Along with this, Drove will cpuset the containers to the allocated cores in a non overlapping manner, so that the cores allocated to a container are dedicated to it. Memory allocated to a container is pinned as well and selected from the same NUMA node.

Needless to say the minimum amount of CPU that can be given to an application or task is 1. Fractional cpu allocation can be achieved in a predictable manner by configuring over provisioning on executor nodes.

"},{"location":"cluster/setup/executor-setup.html#over-provisioning-of-cpu-and-memory","title":"Over Provisioning of CPU and Memory","text":"

Drove does not do any kind of burst scaling or overcommitment to ensure application performance remains predictable even under load. Instead, in Drove, there is a feature to make executors appear to have more cores (and memory) than it actually has. This can be used to get more utilization out of executor nodes in clusters that do not need guaranteed performance (for example staging or dev testing clusters). This is achieved by enabling over provisioning.

Over provisioning needs to be configured in the executor configuration. It primarily consists of two configs:

  • CPU Multiplier - an integral multiplier which will be used to multiply the number of available cores
  • Memory Multiplier - an integral multiplier which will be used to multiply available memory

VCores (virtual cores) are internal representation of a CPU core on the executor. If over provisioning is disabled, a vcore will correspond to a physical core. If over provisioning is enabled, 1 CPU core will generate cpu multiplier number of v cores. Drove does do cpuset even on containers running on nodes that have over provisioning enabled, however the physical cores that the containers get bound to are chosen at random, albeit from the same NUMA node. cpuset-mem is always done on the same NUMA node as well.

Mixed clusters

In some production clusters you might have applications that are non critical in terms of performance and are unable to utilize a full core. These can be tagged to be spun up on some nodes where over provisioning is enabled. Adopting such a cluster topology will ensure that containers that need high performance run on nodes without over provisioning and the smaller apps (like for example operations consoles etc) are run on separate nodes with over provisioning enabled. Just ensure the latter are tagged properly and during app deployment specify this tag in application spec or task spec.

"},{"location":"cluster/setup/executor-setup.html#disable-numa-pinning","title":"Disable NUMA Pinning","text":"

There is an option to disable memory and core pinning. In this situation, all cores from all NUM nodes show up as being part of one node. cpuset-mems is not called if numa pinning is disabled and therefore you will be leaving some memory performance on the table. We recommend not to dabble with this unless you have tasks and containers that need more than the number of cores available on a single NUMA node. This setting is enabled at executor level by setting disableNUMAPinning: true.

"},{"location":"cluster/setup/executor-setup.html#hyper-threading","title":"Hyper-threading","text":"

Whether Hyper Threading needs to be enabled or not is a bit dependent on applications deployed and how effectively they can utilize individual CPU cores. For mixed workloads, we recommend Hyper Threading to be enabled on the executor nodes.

"},{"location":"cluster/setup/executor-setup.html#isolating-container-and-os-processes","title":"Isolating container and OS processes","text":"

Typically we would not want containers to share CPU resources with processes for the operating system, Drove Executor Service as well as Docker engine (if using docker) and so on. While complete isolation would need creating a full scheduler (and passing isolcpus to GRUB parameters), we can get a good middle ground by ensuring such processes utilize only a few CPU cores on the system, and let the Drove executors deploy and pin containers to the rest.

This is achieved in two steps:

  • Make changes to systemd to use only specific cores
  • Exclude these cores in the drove executor configuration

Let's say our server has 2 NUMA nodes, each with 40 hyper-threaded cores. We want to reserve the first 2 cores from each CPU to the OS processes. So we reserve cores [0,1,2,3] for the OS processes.

The following line in /etc/systemd/system.conf

#CPUAffinity=\n

needs to be changed to

CPUAffinity=0 1 2 3\n

Tip

Reboot the machine for this to take effect.

The changes can be validated post reboot by running the following command:

grep Cpus_allowed_list /proc/1/status\n

The expected output should be:

Cpus_allowed_list:  0-3\n

Note

Refer to this for more details.

"},{"location":"cluster/setup/executor-setup.html#gpu-computation","title":"GPU Computation","text":"

Nvidia based GPU compute can be enabled at executor level by installing relevant drivers. Please follow the setup guide to enable this. Remember to tag these nodes to isolate them from the primary cluster and use tags to deploy apps and tasks that need GPU.

"},{"location":"cluster/setup/executor-setup.html#storage-consideration","title":"Storage consideration","text":"

On executor nodes the disk might be under pressure if container (re)deployments are frequent or the containers log very heavily. As such, we recommend the logging directory for Drove be mounted on hardware that will be able to handle this load. Similar considerations need to be given to the log and package directory for docker or podman.

"},{"location":"cluster/setup/executor-setup.html#executor-configuration-reference","title":"Executor Configuration Reference","text":"

The Drove Executor is written on the Dropwizard framework. The configuration to the service is set using a YAML file which needs to be injected into the container. A typical controller configuration file will look like the following:

server: #(1)!\n  applicationConnectors: #(2)!\n    - type: http\n      port: 3000\n  adminConnectors: #(3)!\n    - type: http\n      port: 3001\n  applicationContextPath: /\n  requestLog:\n    appenders:\n      - type: console\n        timeZone: ${DROVE_TIMEZONE}\n      - type: file\n        timeZone: ${DROVE_TIMEZONE}\n        currentLogFilename: /logs/drove-executor-access.log\n        archivedLogFilenamePattern: /logs/drove-executor-access.log-%d-%i\n        archivedFileCount: 3\n        maxFileSize: 100MiB\n\nlogging:\n  level: INFO\n  loggers:\n    com.phonepe.drove: ${DROVE_LOG_LEVEL}\n\n  appenders: #(4)!\n    - type: console #(5)!\n      threshold: ALL\n      timeZone: ${DROVE_TIMEZONE}\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{instanceLogId}] %message%n\"\n    - type: file #(6)!\n      threshold: ALL\n      timeZone: ${DROVE_TIMEZONE}\n      currentLogFilename: /logs/drove-executor.log\n      archivedLogFilenamePattern: /logs/drove-executor.log-%d-%i\n      archivedFileCount: 3\n      maxFileSize: 100MiB\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n      archive: true\n\n    - type: drove #(7)!\n      logPath: \"/logs/applogs/\"\n      archivedLogFileSuffix: \"%d\"\n      archivedFileCount: 3\n      threshold: TRACE\n      timeZone: ${DROVE_TIMEZONE}\n      logFormat: \"%(%-5level) | %-23date | %-30logger{0} | %message%n\"\n      archive: true\n\nzookeeper: #(8)!\n  connectionString: ${ZK_CONNECTION_STRING}\n\nclusterAuth: #(9)!\n  secrets:\n  - nodeType: CONTROLLER\n    secret: ${DROVE_CONTROLLER_SECRET}\n  - nodeType: EXECUTOR\n    secret: ${DROVE_EXECUTOR_SECRET}\n\nresources: #(10)!\n  osCores: [ 0, 1 ]\n  exposedMemPercentage: 60\n  disableNUMAPinning: ${DROVE_DISABLE_NUMA_PINNING}\n  enableNvidiaGpu: ${DROVE_ENABLE_NVIDIA_GPU}\n\noptions: #(11)!\n  cacheImages: true\n  maxOpenFiles: 10_000\n  logBufferSize: 5m\n  cacheFileSize: 10m\n  cacheFileCount: 3\n
  1. Server listener configuration. See Dropwizard Server Configuration for the different options.
  2. Main port configuration. This is where the UI and APIs will be exposed. Check connector configuration docs for details.
  3. Admin port. You can take thread dumps, metrics, run healthchecks on the Drove controller on this port.
  4. Logging configuration. See logging docs.
  5. Log to console. Useful in docker-compose.
  6. Log to rotating files. Useful for running servers.
  7. Drove application logger configuration. See drove logger config for details.
  8. Configure how to connect to Zookeeper See Zookeeper Config for details.
  9. Configuration for authentication between nodes in the cluster. Please check intra node auth config for details.
  10. Resource configuration for this node.
  11. Options to configure executor behaviour. Check executor options section for details.

Tip

In case you do not want to expose admin apis to outside the host, please set bindHost in the admin connectors section.

adminConnectors:\n  - type: http\n    port: 10001\n    bindHost: 127.0.0.1\n
"},{"location":"cluster/setup/executor-setup.html#zookeeper-connection-configuration","title":"Zookeeper Connection Configuration","text":"

The following details can be configured.

Name Option Description Connection String connectionString The connection string of the form: zkserver:2181,zkserver2:2181... Data namespace namespace The top level node inside which all Drove data will be scoped. Defaults to drove if not set.

Sample

zookeeper:\n  connectionString: \"192.168.3.10:2181,192.168.3.11:2181,192.168.3.12:2181\"\n  namespace: drovetest\n

Note

This section is same across the cluster including both controller and executor.

"},{"location":"cluster/setup/executor-setup.html#intra-node-authentication-configuration","title":"Intra Node Authentication Configuration","text":"

Communication between controller and executor is protected by a shared-secret based authentication. The following configuration is meant to configure this. This section consists of a list of 2 members:

  • Config for controller to talk to executors
  • Config for executors to talk to controller

Each section consists of the following:

Name Option Description Node Type nodeType Type of node in the cluster. Can be CONTROLLER or EXECUTOR Secret secret The actual secret to be passed.

Sample

clusterAuth:\n  secrets:\n  - nodeType: CONTROLLER\n    secret: ControllerSecretValue\n  - nodeType: EXECUTOR\n    secret: ExecutorSecret\n

Note

This section is same across the cluster including both controller and executor.

"},{"location":"cluster/setup/executor-setup.html#drove-application-logger-configuration","title":"Drove Application Logger Configuration","text":"

Drove will segregate application and task instance logs in a directory of your choice. The path for such files is set as: - <application id>/<instance id> for Application Instances - <sourceAppName>/<task id> for Task Instances

The Drove Log Appender is based of LogBack's Sifting Appender.

The following configuration options are supported:

Name Option Description Path logPath Directory to host the logs Archive old logs archive Whether to enable log rotation Archived File Suffix archivedLogFileSuffix Suffix for archived log files. Archived File Count archivedFileCount Count of archived log files. Older files are deleted. File Size maxFileSize Size of current log file after which it is archived and a new file is created. Unit: DataSize. Total Size totalSizeCap total size after which deletion takes place. Unit: DataSize. Buffer Size bufferSize Buffer size for the logger. (Set to 8KB by default). Used if immediateFlush is turned off. Immediate Flush immediateFlush Flush logs immediately. Set to true by default (recommended)

Sample

logging:\n  level: INFO\n  ...\n\n  appenders:\n    # Setup appenders for the executor process itself first\n    ...\n\n    - type: drove\n      logPath: \"/logs/applogs/\"\n      archivedLogFileSuffix: \"%d\"\n      archivedFileCount: 3\n      threshold: TRACE\n      timeZone: ${DROVE_TIMEZONE}\n      logFormat: \"%(%-5level) | %-23date | %-30logger{0} | %message%n\"\n      archive: true\n

"},{"location":"cluster/setup/executor-setup.html#resource-configuration","title":"Resource Configuration","text":"

This section can be used to configure how resources are exposed from an executor to the cluster. We have discussed a few of the considerations that will drive the configuration that is being setup.

Name Option Description OS Cores osCores A list of cores reserved for use by operating system processes. See the relevant section for details on the pre-steps needed to achieve this. Exposed Memory exposedMemPercentage What percentage of the system memory can be used by the containers running on the host collectively. Range: 50-100 integer NUMA Pinning disableNUMAPinning Disable NUMA and CPU core pinning for containers. Pinning is on by default. (default: false) Nvidia GPU enableNvidiaGpu Enable GPU support on containers. This setting makes all available Nvidia GPUs on the current executor machine available for any container running on this executor. GPU resources are not discovered on the executor, managed and rationed between containers. Needs to be used in conjunction with tagging (see tags below) to ensure only the applications which require a GPU end up on the executor with GPUs. Tags tags A set of strings that can be used in TAG placement policy to route application and task instances to this executor. Over Provisioning overProvisioning Setup over provisioning configuration.

Tagging

The current hostname is always added as a tag by default and is handled specially to allow for non-tagged deployments to be routed to this executor. If any tag is specified in the tags config, this node will receive containers only when MATCH_TAG placement is used. Please check relevant sections to specify correct placement policies for applications and tasks.

Sample

resources:\n  osCores: [0,1,2,3]\n  exposedMemPercentage: 90\n

"},{"location":"cluster/setup/executor-setup.html#over-provisioning-configuration","title":"Over provisioning configuration","text":"

Drove strives to ensure that containers can run unencumbered on CPU cores allocated to them. This means that the minimum allocation unit possible is 1 for cores. It does not support fractional CPU.

However, there are situations where we would want some non-critical applications to run the cluster but not waste CPU. The overProvisioning configuration aims to provide user a way to turn off NUMA pinning on the executor and run more containers than it normally would.

To ensure predictability, we do not want pinned and non-pinned containers running on the same host. Hence, an executor host can either be running in pinned mode or in non-pinned mode.

To enable more containers than we could usually deploy and to still retain some level of control on how small you want a container to go, we specify multipliers on CPU and memory.

Example: - Let's say your executor server has 40 cores available. If you set cpuMultiplier as 4, this node will now show up as having 160 cores to the controller. - Let's say your server had 512GB of memory, setting memoryMultiplier to 2 will make drove see it as 1TB.

Name Option Description Enabled enabled Set this to true to enable over provisioning. Default: false CPU Multiplier cpuMultiplier Multiplier to be applied to enable cpu over provisioning. Default: 1. Range: 1-20 Memory Multiplier memoryMultiplier Multiplier to be applied to enable memory over provisioning. Default: 1. Range: 1-20

Sample

resources:\n  exposedMemPercentage: 90\n  overProvisioning:\n    enabled: true\n    memoryMultiplier: 1\n    cpuMultiplier: 3\n

Tip

This feature was developed to allow us to run our development environments cheaper. In such environments there is not much pressure on CPU or memory, but a large number of containers run as developers can spin up containers for features they are working on. There was no point is wasting a full core on containers that get hit twice a minute or less. On production we tend to err on the side of caution and allocate at least one core even to the most trivial applications as of the time of writing this.

"},{"location":"cluster/setup/executor-setup.html#executor-options","title":"Executor Options","text":"

The following options can be set to influence the behavior for the Drove executors.

Name Option Description Hostname hostname Override the hostname that gets exposed to the controller. Make sure this is resolvable. Cache Images cacheImages Cache container images. If this is not passed, a container image is removed when a container dies and no other instance is using the image. Command Timeout containerCommandTimeout Timeout used by the container engine client when issuing container commands to docker or podman Container Socket Path dockerSocketPath The path of socket for docker socket. Comes in handy to configure path for socket when using podman etc. Max Open Files maxOpenFiles Override the maximum number of file descriptors a container can open. Default: 470,000 Log Buffer Size logBufferSize The size of the buffer the executor uses to read logs from container. Unit DataSize. Range: 1-128MB. Default: 10MB Cache File Size cacheFileSize To limit disk usage, configure fixed size log file cache for containers. Unit: DataSize. Range: 10MB-100GB. Default: 20MB. Compression is always enabled. Cache File Count cacheFileSize To limit disk usage, configure fixed count of log file cache for containers. Unit: integer. Max: 1024. Default: 3

Sample

options:\n  logBufferSize: 20m\n  cacheFileSize: 30m\n  cacheFileCount: 3\n  cacheImages: true\n

"},{"location":"cluster/setup/executor-setup.html#relevant-directories","title":"Relevant directories","text":"

Location for data and logs are as follows:

  • /etc/drove/executor/ - Configuration files
  • /var/log/drove/executor/ - Executor Logs
  • /var/log/drove/executor/instance-logs - Application/Task Instance Logs

We shall be volume mounting the config and log directories with the same name.

Prerequisite Setup

If not done already, please complete the prerequisite setup on all machines earmarked for the cluster.

"},{"location":"cluster/setup/executor-setup.html#setup-the-config-file","title":"Setup the config file","text":"

Create a relevant configuration file in /etc/drove/controller/executor.yml.

Sample

server:\n  applicationConnectors:\n    - type: http\n      port: 11000\n  adminConnectors:\n    - type: http\n      port: 11001\n  requestLog:\n    appenders:\n      - type: file\n        timeZone: IST\n        currentLogFilename: /var/log/drove/executor/drove-executor-access.log\n        archivedLogFilenamePattern: /var/log/drove/executor/drove-executor-access.log-%d-%i\n        archivedFileCount: 3\n        maxFileSize: 100MiB\n\nlogging:\n  level: INFO\n  loggers:\n    com.phonepe.drove: INFO\n\n\n  appenders:\n    - type: file\n      threshold: ALL\n      timeZone: IST\n      currentLogFilename: /var/log/drove/executor/drove-executor.log\n      archivedLogFilenamePattern: /var/log/drove/executor/drove-executor.log-%d-%i\n      archivedFileCount: 3\n      maxFileSize: 100MiB\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n    - type: drove\n      logPath: \"/var/log/drove/executor/instance-logs\"\n      archivedLogFileSuffix: \"%d-%i\"\n      archivedFileCount: 0\n      maxFileSize: 1GiB\n      threshold: INFO\n      timeZone: IST\n      logFormat: \"%(%-5level) | %-23date | %-30logger{0} | %message%n\"\n      archive: true\n\nzookeeper:\n  connectionString: \"192.168.56.10:2181\"\n\nclusterAuth:\n  secrets:\n  - nodeType: CONTROLLER\n    secret: \"0v8XvJrDc7r86ZY1QCByPTDPninI4Xii\"\n  - nodeType: EXECUTOR\n    secret: \"pOd9sIEXhv0wrGOVc7ebwNvR7twZqyTN\"\n\nresources:\n  osCores: []\n  exposedMemPercentage: 90\n  disableNUMAPinning: true\n  overProvisioning:\n    enabled: true\n    memoryMultiplier: 10\n    cpuMultiplier: 10\n\noptions:\n  cacheImages: true\n  logBufferSize: 20m\n  cacheFileSize: 30m\n  cacheFileCount: 3\n  cacheImages: true\n

"},{"location":"cluster/setup/executor-setup.html#setup-required-environment-variables","title":"Setup required environment variables","text":"

Environment variables need to run the drove controller are setup in /etc/drove/executor/executor.env.

CONFIG_FILE_PATH=/etc/drove/executor/executor.yml\nJAVA_PROCESS_MIN_HEAP=1g\nJAVA_PROCESS_MAX_HEAP=1g\nZK_CONNECTION_STRING=\"192.168.56.10:2181\"\nJAVA_OPTS=\"-Xlog:gc:/var/log/drove/executor/gc.log -Xlog:gc:::filecount=3,filesize=10M -Xlog:gc::time,level,tags -XX:+UseNUMA -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -Dfile.encoding=utf-8 -Djute.maxbuffer=0x9fffff\"\n
"},{"location":"cluster/setup/executor-setup.html#create-systemd-file","title":"Create systemd file","text":"

Create a systemd file. Put the following in /etc/systemd/system/drove.executor.service:

[Unit]\nDescription=Drove Executor Service\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nUser=drove\nTimeoutStartSec=0\nRestart=always\nExecStartPre=-/usr/bin/docker pull ghcr.io/phonepe/drove-executor:latest\nExecStart=/usr/bin/docker run  \\\n    --env-file /etc/drove/executor/executor.env \\\n    --volume /etc/drove/executor:/etc/drove/executor:ro \\\n    --volume /var/log/drove/executor:/var/log/drove/executor \\\n    --volume /var/run/docker.sock:/var/run/docker.sock \\\n    --publish 11000:11000  \\\n    --publish 11001:11001 \\\n    --hostname %H \\\n    --rm \\\n    --name drove.executor \\\n    ghcr.io/phonepe/drove-executor:latest\n\n[Install]\nWantedBy=multi-user.target\n
Verify the file with the following command:
systemd-analyze verify drove.executor.service\n

Set permissions

chmod 664 /etc/systemd/system/drove.executor.service\n

"},{"location":"cluster/setup/executor-setup.html#start-the-service-on-all-servers","title":"Start the service on all servers","text":"

Use the following to start the service:

systemctl daemon-reload\nsystemctl enable drove.executor\nsystemctl start drove.executor\n

You can tail the logs at /var/logs/drove/executor/drove-executor.log.

The executor should now show up on the Drove Console.

"},{"location":"cluster/setup/gateway.html","title":"Setting up Drove Gateway","text":"

The Drove Gateway works as a gateway to expose apps running on a drove cluster to rest of the world.

Drove Gateway container uses NGinx and a modified version of Nixy to track drove endpoints. More details about this can be found in the drove-gateway project.

"},{"location":"cluster/setup/gateway.html#drove-gateway-nixy-configuration-reference","title":"Drove Gateway Nixy Configuration Reference","text":"

The nixy running inside the gateway container is configured using a custom TOML file. This section looks into this file:

address = \"127.0.0.1\"# (1)!\nport = \"6000\"\n\n\n# Drove Options\ndrove = [#(2)!\n  \"http://controller1.mydomain:10000\",\n   \"http://controller1.mydomain:10000\"\n   ]\n\nleader_vhost = \"drove-staging.mydomain\"#(3)!\nevent_refresh_interval_sec = 5#(5)!\nuser = \"\"#(6)!\npass = \"\"\naccess_token = \"\"#(7)!\n\n# Parameters to control which apps are exposed as VHost\nrouting_tag = \"externally_exposed\"#(4)!\nrealm = \"api.mydomain,support.mydomain\"#(8)!\nrealm_suffix = \"-external.mydomain\"#(9)!\n\n# Nginx related config\n\nnginx_config = \"/etc/nginx/nginx.conf\"#(10)!\nnginx_template = \"/etc/drove/gateway/nginx.tmpl\"#(11)!\nnginx_cmd = \"nginx\"#(12)!\nnginx_ignore_check = true#(13)!\n\n# NGinx plus specific options\nnginxplusapiaddr=\"127.0.0.1\"#(14)!\nnginx_reload_disabled=true#(15)!\nmaxfailsupstream = 0#(16)!\nfailtimeoutupstream = \"1s\"\nslowstartupstream = \"0s\"\n
  1. Nixy listener configuration. Endpoint for nixy itself.

  2. List of Drove controllers. Add all controller nodes here. Nixy will automatically determine and track the current leader.

    Auto detection is disabled if a single endpoint is specified.

  3. Helps create a vhost entry that tracks the leader on the cluster. Use this to expose the Drove endpoint to users. The value for this will be available to the template engine as the LeaderVHost variable.

  4. If some special routing behaviour needs to be implemented in the template based on some tag metadata of the deployed apps, set the routing_tag option to set the tag name to be used. The actual value is derived from app instances and exposed to the template engine as the variable: RoutingTag. Optional.

    In this example, the RoutingTag variable will be set to the value specified in the routing_tag tag key specified when deploying the Drove Application. For example, if we want to expose the app we can set it to yes, and filter the VHost to be exposed in NGinx template when RoutingTag == \"yes\".

  5. Drove Gateway/Nixy works on event polling on controller. This is the polling interval. Especially if number of NGinx nodes is high. Default is 2 seconds. Unless cluster is really busy with a high rate of change of containers, this strikes a good balance between apps becoming discoverable vs putting the leader controller under heavy load.

  6. user and pass are optional params can be used to set basic auth credentials to the calls made to Drove controllers if basic auth is enabled on the cluster. Leave empty if no basic auth is required.

  7. If cluster has some custom header based auth, the following can be used. The contents on this parameter are passed verbatim to the Authorization HTTP header. Leave empty if no token auth is enabled on the cluster.

  8. By default drove-gateway will expose all vhost declared in the spec for all drove apps on a cluster (caveat: filtering can be done using RoutingTag as well). If specific vhosts need to be exposed, set the realms parameter to a comma separated list of realms. Optional.

  9. Beside perfect vhost matching, Drove Gateway supports suffix based matches as well. A single suffix is supported. Optional.

  10. Path to NGinx config.

  11. Path to the template file, based on which the template will be generated.

  12. NGinx command to use to reload the config. Set this to openresty optionally to use openresty.

  13. Ignore calling NGinx command to test the config. Set this to false or delete this line on production. Default: false.

  14. If using NGinx plus, set the endpoint to the local server here. If left empty, NGinx plus api based vhost update will be disabled.

  15. If specific vhosts are exposed, auto-discovery and updation of config (and NGinx reloads) might not be desired as it will cause connection drops. Set the following parameter to true to disable reloads. Nixy will only update upstreams using the nplus APIs. Default: false.

  16. Connection parameters for NGinx plus.

NGinx plus

NGinx plus is not shipped with this docker. If you want to use NGinx plus, please build nixy from the source tree here and build your own container.

"},{"location":"cluster/setup/gateway.html#relevant-directories","title":"Relevant directories","text":"

Location for data and logs are as follows:

  • /etc/drove/gateway/ - Configuration files
  • /var/log/drove/gateway/ - NGinx Logs

We shall be volume mounting the config and log directories with the same name.

Prerequisite Setup

If not done already, please complete the prerequisite setup on all machines earmarked for the cluster.

Go through the following steps to run drove-gateway as a service.

"},{"location":"cluster/setup/gateway.html#create-the-toml-config-for-nixy","title":"Create the TOML config for Nixy","text":"

Sample config file /etc/drove/gateway/gateway.toml:

address = \"127.0.0.1\"\nport = \"6000\"\n\n\n# Drove Options\ndrove = [\n  \"http://controller1.mydomain:10000\",\n   \"http://controller1.mydomain:10000\"\n   ]\n\nleader_vhost = \"drove-staging.mydomain\"\nevent_refresh_interval_sec = 5\nuser = \"guest\"\npass = \"guest\"\n\n\n# Nginx related config\nnginx_config = \"/etc/nginx/nginx.conf\"\nnginx_template = \"/etc/drove/gateway/nginx.tmpl\"\nnginx_cmd = \"nginx\"\nnginx_ignore_check = true\n

Replace domain names

Please remember to update mydomain to a valid domain you want to use.

"},{"location":"cluster/setup/gateway.html#create-template-for-nginx","title":"Create template for NGinx","text":"

Create a NGinx template with the following config in /etc/drove/gateway/nginx.tmpl

# Generated by drove-gateway {{datetime}}\n\nuser www-data;\nworker_processes auto;\npid /run/nginx.pid;\n\nevents {\n    use epoll;\n    worker_connections 2048;\n    multi_accept on;\n}\nhttp {\n    server_names_hash_bucket_size  128;\n    add_header X-Proxy {{ .Xproxy }} always;\n    access_log /var/log/nginx/access.log;\n    error_log /var/log/nginx/error.log warn;\n    server_tokens off;\n    client_max_body_size 128m;\n    proxy_buffer_size 128k;\n    proxy_buffers 4 256k;\n    proxy_busy_buffers_size 256k;\n    proxy_redirect off;\n    map $http_upgrade $connection_upgrade {\n        default upgrade;\n        ''      close;\n    }\n    # time out settings\n    proxy_send_timeout 120;\n    proxy_read_timeout 120;\n    send_timeout 120;\n    keepalive_timeout 10;\n\n    server {\n        listen       7000 default_server;\n        server_name  _;\n        # Everything is a 503\n        location / {\n            return 503;\n        }\n    }\n    {{if and .LeaderVHost .Leader.Endpoint}}\n    upstream {{.LeaderVHost}} {\n        server {{.Leader.Host}}:{{.Leader.Port}};\n    }\n    server {\n        listen 7000;\n        server_name {{.LeaderVHost}};\n        location / {\n            proxy_set_header HOST {{.Leader.Host}};\n            proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;\n            proxy_connect_timeout 30;\n            proxy_http_version 1.1;\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection $connection_upgrade;\n            proxy_pass http://{{.LeaderVHost}};\n        }\n    }\n    {{end}}\n    {{- range $id, $app := .Apps}}\n    upstream {{$app.Vhost}} {\n        {{- range $app.Hosts}}\n        server {{ .Host }}:{{ .Port }};\n        {{- end}}\n    }\n    server {\n        listen 7000;\n        server_name {{$app.Vhost}};\n        location / {\n            proxy_set_header HOST $host;\n            proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;\n            proxy_connect_timeout 30;\n            proxy_http_version 1.1;\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection $connection_upgrade;\n            proxy_pass http://{{$app.Vhost}};\n        }\n    }\n    {{- end}}\n}\n

The above template will do the following:

  • Set NGinx port to 7000. This is the port exposed on the Docker container for the gateway. Do not change this.
  • Sets up error and access logs to /var/log/nginx. Log rotation is setup for this path already.
  • Set up a Vhost drove-staging.mydomain that will get auto-updated with the current leader of the Drove cluster
  • Setup automatically updated virtual hosts for all apps on the cluster.
"},{"location":"cluster/setup/gateway.html#create-environment-file","title":"Create environment file","text":"

We want to configure the drove gateway container using the required environment variables. To do that, put the following in /etc/drove/gateway/gateway.env:

CONFIG_FILE_PATH=/etc/drove/gateway/gateway.toml\nTEMPLATE_FILE_PATH=/etc/drove/gateway/nginx.tmpl\n
"},{"location":"cluster/setup/gateway.html#create-systemd-file","title":"Create systemd file","text":"

Create a systemd file. Put the following in /etc/systemd/system/drove.gateway.service:

[Unit]\nDescription=Drove Gateway Service\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nUser=drove\nTimeoutStartSec=0\nRestart=always\nExecStartPre=-/usr/bin/docker pull ghcr.io/phonepe/drove-gateway:latest\nExecStart=/usr/bin/docker run  \\\n    --env-file /etc/drove/gateway/gateway.env \\\n    --volume /etc/drove/gateway:/etc/drove/gateway:ro \\\n    --volume /var/log/drove/gateway:/var/log/nginx \\\n    --network host \\\n    --hostname %H \\\n    --rm \\\n    --name drove.gateway \\\n    ghcr.io/phonepe/drove-gateway:latest\n\n[Install]\nWantedBy=multi-user.target\n

Verify the file with the following command:

systemd-analyze verify drove.gateway.service\n

Set permissions

chmod 664 /etc/systemd/system/drove.gateway.service\n

"},{"location":"cluster/setup/gateway.html#start-the-service-on-all-servers","title":"Start the service on all servers","text":"

Use the following to start the service:

systemctl daemon-reload\nsystemctl enable drove.gateway\nsystemctl start drove.gateway\n
"},{"location":"cluster/setup/gateway.html#checking-logs","title":"Checking Logs","text":"

You can check logs using:

journalctl -u drove.gateway -f\n

NGinx logs would be available at /var/log/drove/gateway.

"},{"location":"cluster/setup/gateway.html#log-rotation-for-nginx","title":"Log rotation for NGinx","text":"

The gateway sets up log rotation for the access and errors logs with the following config:

/var/log/nginx/*.log {\n    rotate 5\n    size 10M\n    dateext\n    dateformat -%Y-%m-%d\n    missingok\n    compress\n    delaycompress\n    sharedscripts\n    notifempty\n    postrotate\n        test -r /var/run/nginx.pid && kill -USR1 `cat /var/run/nginx.pid`\n    endscript\n}\n

This will rotate both error and access logs when they hit 10MB and keep 5 logs.

Configure the above if you want and volume mount your config to /etc/logrotate.d/nginx to use different scheme as per your requirements.

"},{"location":"cluster/setup/maintenance.html","title":"Maintaining a Drove Cluster","text":"

There are a couple of constructs built into Drove to allow for easy maintenance.

  • Cluster Maintenance Mode
  • Executor node blacklisting
"},{"location":"cluster/setup/maintenance.html#maintenance-mode","title":"Maintenance mode","text":"

Drove supports a maintenance mode to allow for software updates without affecting the containers running on the cluster.

Danger

In maintenance mode, outage detection is turned off and container failure for applications are not acted upon even if detected.

"},{"location":"cluster/setup/maintenance.html#engaging-maintenance-mode","title":"Engaging maintenance mode","text":"

Set cluster to maintenance mode.

Preconditions - Cluster must be in the following state: MAINTENANCE

Drove CLIJSON
drove -c local cluster maintenance-on\n

Sample Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/set' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"state\": \"MAINTENANCE\",\n        \"updated\": 1721630351178\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"cluster/setup/maintenance.html#disengaging-maintenance-mode","title":"Disengaging maintenance mode","text":"

Set cluster to normal mode.

Preconditions - Cluster must be in the following state: MAINTENANCE

Drove CLIJSON
drove -c local cluster maintenance-off\n

Sample Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/unset' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"state\": \"NORMAL\",\n        \"updated\": 1721630491296\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"cluster/setup/maintenance.html#updating-drove-version-across-the-cluster-quickly","title":"Updating drove version across the cluster quickly","text":"

We recommend the following sequence of steps:

  1. Find the leader controller for the cluster using drove ... cluster leader.
  2. Update the controller container on the nodes that are not the leader.

    If you are using the systemd file given here, you just need to restart the controller service using systemctl restart drove.controller

  3. Set cluster to maintenance mode using drove ... cluster maintenance-on.

  4. Update the leader controller.

    If you are using the systemd file given here, you just need to restart the leader controller service: systemctl restart drove.controller

  5. Update the executors.

    If you are using the systemd file given here, you just need to restart all executors: systemctl restart drove.executor

  6. Take cluster out of maintenance mode: drove ... cluster maintenance-off

"},{"location":"cluster/setup/maintenance.html#executor-blacklisting","title":"Executor blacklisting","text":"

In cases where we want to take an executor node out of the cluster for planned maintenance, we need to ensure application instances running on the node are replaced by containers on other nodes and the ones running here are shut down cleanly.

This is achieved by blacklisting the node.

Tip

Whenever blacklisting is done, it causes some flux in the application topology due to new container migration from blacklisted to normal nodes. To reduce the number of times this happens, plan to perform multiple operations togeter and blacklist and un-blacklist executors together.

Drove will optimize bulk blacklisting related app migrations and will migrate containers together for an app only once rather than once for every node.

Danger

Task instances are not migrated out. This is because it is impossible for Drove to know if a task can be migrated or not (i.e. killed and spun up on a new node in any order).

To blacklist executors do the following:

Drove CLIJSON
drove -c local executor blacklist dd2cbe76-9f60-3607-b7c1-bfee91c15623 ex1 ex2 \n

Sample Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/blacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d&id=ex1&id=ex2' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"failed\": [\n            \"ex2\",\n            \"ex1\"\n        ],\n        \"successful\": [\n            \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\"\n        ]\n    },\n    \"message\": \"success\"\n}\n

To un-blacklist executors do the following:

Drove CLIJSON
drove -c local executor unblacklist dd2cbe76-9f60-3607-b7c1-bfee91c15623 ex1 ex2 \n

Sample Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/unblacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d&id=ex1&id=ex2' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"failed\": [\n            \"ex2\",\n            \"ex1\"\n        ],\n        \"successful\": [\n            \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\"\n        ]\n    },\n    \"message\": \"success\"\n}\n

Note

Drove will not re-evaluate placement of existing Applications in RUNNING state once executors are brought back into rotation.

"},{"location":"cluster/setup/planning.html","title":"Planning your cluster","text":"

Running a drove cluster in production for critical workloads involves planning and preparation on factors like Availability, Scale, Security and Access management. The following issues should be considered while planning your drove cluster.

"},{"location":"cluster/setup/planning.html#criteria-for-planning","title":"Criteria for planning","text":"

The simplest form of a drove cluster would run controller, zookeeper, executor and gateway services all on the same machine while a highly available would separate out all components according to following considerations:

  • Availability: Use of a single node in either executor, controller, gateway or zookeeper has a single point of failure. A typical highly available cluster consists of:
    • Separated controller and executor nodes
    • Multi controller setup - typically minimum 3 controller nodes to provide adequate availability and majority for leader election
    • Multiple gateway nodes to load balance traffic and provide fault tolerance
    • Availability of sufficient executor nodes to follow placement policies suitable for high availability for application instances across executors
    • Multiple node zookeeper cluster to provide adequate availability and quorum. Even number of nodes is not useful. Atleast three zookeeper servers are required.
  • Scale: You should size your components as per expected amount of traffic to your instances but also have a plan for expected demand growth and ways to scale your cluster accordingly
  • Security and Access management: You can use authentication for intra-cluster communication and add encryption for secure communications. Access management can be devolved by creating multiple users with either read or write roles.
"},{"location":"cluster/setup/planning.html#cluster-configuration","title":"Cluster configuration","text":""},{"location":"cluster/setup/planning.html#controllers","title":"Controllers","text":"

Controllers will manage the cluster with application instances spread across multiple executors as per different placement policies. Controllers use leader-election to coordinate and will act as a single entity while each executor acts as a single entity that runs many different application instances.

  • Multiple controllers: For high availability, there should be atleast three controllers. Drove uses leader election to coordinate across the controllers. If the leader fails, the other controllers would elect a leader and takeover.
  • Separate Zookeeper service and snapshots: The zookeeper service used for state coordination by controller can either be run on same machines as controller or on separate machines. Zookeeper stores the configuration data for the whole cluster and zookeeper snapshots can be used to back up the data.
  • Availability zones: You can split the controllers and/or zookeeper nodes across availability zones/data centers to improve resilience.
  • Transit encryption and certificate Management: Controllers and executors can communicate via secure communications. TLS settings and certificates can be added by modifying applicationConnectors and adminConnectors parameters as per Dropwizard HTTPS connector configuration
  • Separate Gateways: The drove gateways will route and load balance application traffic across application instances. The gateway service can either be run on same machines as controller or on separate machines.
  • Resources: The required JVM maximum heap size for drove controller service will increase with the increase in number of executors,applications and application instances in the cluster. The JVM parameters should be reviewed as per your scale.
"},{"location":"cluster/setup/planning.html#zookeeper","title":"Zookeeper","text":"
  • Quorum: For replicated zookeeper, a minimum of three servers are required, and it is recommended that you have an odd number of servers. If you only have two servers, if one of them fails, there are not enough machines to form a majority quorum. Two servers are inherently less stable than a single server, because there are two single points of failure. Refer Running replicated ZooKeeper in ZooKeeper Getting Started Guide
  • zxid rollover: zxid is the ZooKeeper transaction id and is 64 bit number. The zxid has two parts epoch and a counter which use the high order 32-bits for the epoch and the low order 32-bits for the counter. A zookeeper election is forced when the 32-bit counter rolls over. This will be more frequent as scale increases in your cluster.
  • JVM parameters and resources: The required JVM maximum heap size for zookeeper will increase with the increase in number of executors,applications and application instances in the cluster. The JVM parameters should be reviewed as per your scale. Refer ZooKeeper Administrator's Guide
"},{"location":"cluster/setup/planning.html#executors","title":"Executors","text":"
  • Containerisation Engine: Drove supports the Docker engine and has experimental support for Podman. Choose your engine as per your security, networking and performance considerations.
  • Container Networking: The container engine and container networking should be configured as per your requirements. It is recommended to use Port forwarding based container networking if you choose to use Drove Gateway to route application traffic. Container engine settings can be modified to manage DNS and proxy parameters for containers.
  • Placement policies and availability: Drove supports placement policies to set criteria for replication of instances across executors and avoiding single points of failure. Drove tags can be assigned to executors and placement policy can be used to pin certain applications to specific selected executors if you have any hardware or other considerations.
  • Scaling: As your cluster scale increases, you can continue adding executors to the cluster. Placement policies should be used to manage availability criteria. Controller and ZooKeeper resource requirements will increase as your executor count increases and should be reviewed accordingly.
"},{"location":"cluster/setup/planning.html#gateways","title":"Gateways","text":"
  • Container Networking: It is recommended to use Port forwarding based container networking if you choose to use Drove Gateways to route application traffic
  • Load balancing: Gateways use Nginx as a web server and can use many different approaches to load balancing among multiple gateway nodes. Some examples include:
    • DNS Load balancing: Multiple gateway IP's can be added as A records to the virtual host domain to let clients use round-robin DNS and split load across gateway nodes
    • Anycast/Network Load balancing: If any sort of anycast/network load balancing functionality is available in your network, it can be used to split traffic across gateway nodes
  • High Availability and Scaling: Many different methods are available to achieve high availability and scale NGINX. Any method can be used by adequately modifying the template used by Gateway to render Nginx configuration.
"},{"location":"cluster/setup/prerequisites.html","title":"Setting up the prerequisites","text":"

On all machines on the drove cluster, we would want to use the same user and have a consistent storage structure for configuration, logs etc.

Note

All commands o be issues as root. To get to admin/root mode issue the following command:

sudo su\n
"},{"location":"cluster/setup/prerequisites.html#setting-up-user","title":"Setting up user","text":"

We shall create an user called drove to be used to run all services and containers and assign the file ownership to this user.

adduser --system --group \"drove\" --home /var/lib/misc --no-create-home > /dev/null\n
We want to user to be able to run docker containers, so we add the user to the docker group:

groupadd docker\nusermod -aG docker drove\n
"},{"location":"cluster/setup/prerequisites.html#create-directories","title":"Create directories","text":"

We shall use the following locations to store configurations, logs etc:

  • /etc/drove/... - for configuration
  • /var/log/drove/.. - for all logs

We go ahead and create these locations and setup the correct permissions:

mkdir -p /etc/drove\nchown -R drove.drove /etc/drove\nchmod 700 /etc/drove\nchmod g+s /etc/drove\n\nmkdir -p /var/lib/drove\nchown -R drove.drove /var/lib/drove\nchmod 700 /var/lib/drove\n\nmkdir -p /var/log/drove\n

Danger

Ensure you run the chmod commands to remove read access everyone other than the owner.

"},{"location":"cluster/setup/units.html","title":"Units Reference","text":"

In the configuration files for Drove, we use the Duration and DataSize units to make configuration easier.

"},{"location":"cluster/setup/units.html#data-size","title":"Data Size","text":"

Use the following shortcuts to express sizes in human readable form such as 2GB etc:

  • B - Bytes
  • byte - Bytes
  • Bytes - Bytes
  • K - Kilobytes
  • KB - Kilobytes
  • KiB - Kibibytes
  • Kilobyte - Kilobytes
  • kibibyte - Kibibytes
  • KiloBytes - Kilobytes
  • kibiBytes - Kibibytes
  • M - Megabytes
  • MB - Megabytes
  • MiB - Mebibytes
  • megabyte - Megabytes
  • mebibyte - Mebibytes
  • megaBytes - Megabytes
  • mebiBytes - Mebibytes
  • G - Gigabytes
  • GB - Gigabytes
  • GiB - Gibibytes
  • gigabyte - Gigabytes
  • gibibyte - Gibibytes
  • gigaBytes - Gigabytes
  • gibiBytes - Gibibytes
  • T - Terabytes
  • TB - Terabytes
  • TiB - Tebibytes
  • terabyte - Terabytes
  • tebibyte - Tebibytes
  • teraBytes - Terabytes
  • tebiBytes - Tebibytes
  • P - Petabytes
  • PB - Petabytes
  • PiB - Pebibytes
  • petabyte - Petabytes
  • pebibyte - Pebibytes
  • petaBytes - Petabytes
  • pebiBytes - Pebibytes
"},{"location":"cluster/setup/units.html#duration","title":"Duration","text":"

Time durations in Drove can be expressed in human readable form, for example: 3d can be used to signify 3 days and so on. The list of valid duration unit suffixes are:

  • ns - nanoseconds
  • nanosecond - nanoseconds
  • nanoseconds - nanoseconds
  • us - microseconds
  • microsecond - microseconds
  • microseconds - microseconds
  • ms - milliseconds
  • millisecond - milliseconds
  • milliseconds - milliseconds
  • s - seconds
  • second - seconds
  • seconds - seconds
  • m - minutes
  • min - minutes
  • mins - minutes
  • minute - minutes
  • minutes - minutes
  • h - hours
  • hour - hours
  • hours - hours
  • d - days
  • day - days
  • days - days
"},{"location":"cluster/setup/zookeeper.html","title":"Setting Up Zookeeper","text":"

We shall be running Zookeeper using the official Docker images. All data volumes etc will be mounted on the host machines.

The following ports will be exposed:

  • 2181 - This is the main port for ZK clients to connect to the server
  • 2888 - The port used by Zookeeper for in-cluster communications between peers
  • 3888 - Port used for internal leader election
  • 8080 - Admin server port. We are going to turn this off.

Danger

The ZK admin server does not shut down cleanly from time to time. And is not needed for anything related to Drove. If not needed, you should turn it off.

We assume the following to be the IP for the 3 zookeeper nodes:

  • 192.168.3.10
  • 192.168.3.11
  • 192.168.3.12
"},{"location":"cluster/setup/zookeeper.html#relevant-directories","title":"Relevant directories","text":"

Location for data and logs are as follows:

  • /etc/drove/zk - Configuration files
  • /var/lib/drove/zk/ - Data and data logs
  • /var/log/drove/zk - Logs
"},{"location":"cluster/setup/zookeeper.html#important-files","title":"Important files","text":"

The zookeeper container stores snapshots, transaction logs and application logs on /data, /datalog and /logs directories respectively. We shall be volume mounting the following:

  • /var/lib/drove/zk/data to /data on the container
  • /var/lib/drove/zk/datalog to /datalog on the container
  • /var/logs/drove/zk to /logs on the container

Docker will create these directories when container comes up for the first time.

Tip

The zk server id (as set above using the ZOO_MY_ID) can also be set by putting the server number in a file named myid in the /data directory.

Prerequisite Setup

If not done already, lease complete the prerequisite setup on all machines earmarked for the cluster.

"},{"location":"cluster/setup/zookeeper.html#setup-configuration-files","title":"Setup configuration files","text":"

Let's create the config directory:

mkdir -p /etc/drove/zk\n

We shall be creating 3 different configuration files to configure zookeeper:

  • zk.env - Environment variables to be used by zookeeper container
  • java.env - Setup JVM related options
  • logbaxk.xml - Logging configuration
"},{"location":"cluster/setup/zookeeper.html#setup-environment-variables","title":"Setup environment variables","text":"

Let us prepare the configuration. Put the following in a file: /etc/drove/zk/zk.env:

#(1)!\nZOO_TICK_TIME=2000\nZOO_INIT_LIMIT=10\nZOO_SYNC_LIMIT=5\nZOO_STANDALONE_ENABLED=false\nZOO_ADMINSERVER_ENABLED=false\n\n#(2)!\nZOO_AUTOPURGE_PURGEINTERVAL=12\nZOO_AUTOPURGE_SNAPRETAINCOUNT=5\n\n#(3)!\nZOO_MY_ID=1\nZOO_SERVERS=server.1=192.168.3.10:2888:3888;2181 server.2=192.168.3.11:2888:3888;2181 server.3=192.168.3.12:2888:3888;2181\n
  1. This is cluster level configuration to ensure the cluster topology remains stable through minor flaps
  2. This will control how much data we retain
  3. This section needs to change per server. Each server should have a different ZOO_MY_ID set. And the same numbers get referred to in ZOO_SERVERS section.

Warning

  • The ZOO_MY_ID value needs to be different on every server.So it would be:

    • 192.168.3.10 - 1
    • 192.168.3.11 - 2
    • 192.168.3.12 - 3
  • The format for ZOO_SERVERS is server.id=<address1>:<port1>:<port2>[:role];[<client port address>:]<client port>.

Info

Exhaustive set of options can be found on the Official Docker Page.

"},{"location":"cluster/setup/zookeeper.html#setup-jvm-parameters","title":"Setup JVM parameters","text":"

Put the following in /etc/drove/zk/java.env

export SERVER_JVMFLAGS='-Djute.maxbuffer=0x9fffff -Xmx4g -Xms4g -Dfile.encoding=utf-8 -XX:+UseG1GC -XX:+UseNUMA -XX:+ExitOnOutOfMemoryError'\n

Configuring Max Data Size

Drove data per node can get a bit on the larger side from time to time depending on your application configuration. To be on the safe side, we need to increase the maximum data size per node. This is achieved by setting the JVM option -Djute.maxbuffer=0x9fffff on all cluster nodes in Drove. This is 10MB (approx). The actual payload doesn't reach anywhere close. However we shall be picking up payload compression in a future version to stop this variable from needing to be set.

For the Zookeeper Docker, the environment variable SERVER_JVMFLAGS needs to be set to -Djute.maxbuffer=0x9fffff.

Please refer to Zookeeper Advanced Configuration for further properties that can be tuned.

JVM Size

We set 4GB JVM heap size for ZK by adding appropriate options in SERVER_JVMFLAGS. Please make sure you have sized your machines to have 10-16GB of RAM at the very least. Tune the JVM size and machine size according to your needs.

q

JVMFLAGS environment variable

Do not set this variable in zk.env. Couple of reasons:

  • This will affect both the zk server as well as the client.
  • There is an issue and the flag (nor the SERVER_JVMFLAGS) are not used properly by the startup scripts.
"},{"location":"cluster/setup/zookeeper.html#configure-logging","title":"Configure logging","text":"

We want to have physical log files on disk for debugging and audits and want the container to be ephemeral to allow for easy updates etc. To achieve this, put the following in /etc/drove/zk/logback.xml:

<!--\n Copyright 2022 The Apache Software Foundation\n\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n Define some default values that can be overridden by system properties\n-->\n<configuration>\n  <!-- Uncomment this if you would like to expose Logback JMX beans -->\n  <!--jmxConfigurator /-->\n\n  <property name=\"zookeeper.console.threshold\" value=\"INFO\" />\n\n  <property name=\"zookeeper.log.dir\" value=\"/logs\" />\n  <property name=\"zookeeper.log.file\" value=\"zookeeper.log\" />\n  <property name=\"zookeeper.log.threshold\" value=\"INFO\" />\n  <property name=\"zookeeper.log.maxfilesize\" value=\"256MB\" />\n  <property name=\"zookeeper.log.maxbackupindex\" value=\"20\" />\n\n  <!--\n    console\n    Add \"console\" to root logger if you want to use this\n  -->\n  <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n    <encoder>\n      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>\n    </encoder>\n    <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n      <level>${zookeeper.console.threshold}</level>\n    </filter>\n  </appender>\n\n  <!--\n    Add ROLLINGFILE to root logger to get log file output\n  -->\n  <appender name=\"ROLLINGFILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n    <File>${zookeeper.log.dir}/${zookeeper.log.file}</File>\n    <encoder>\n      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>\n    </encoder>\n    <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n      <level>${zookeeper.log.threshold}</level>\n    </filter>\n    <rollingPolicy class=\"ch.qos.logback.core.rolling.FixedWindowRollingPolicy\">\n      <maxIndex>${zookeeper.log.maxbackupindex}</maxIndex>\n      <FileNamePattern>${zookeeper.log.dir}/${zookeeper.log.file}.%i</FileNamePattern>\n    </rollingPolicy>\n    <triggeringPolicy class=\"ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy\">\n      <MaxFileSize>${zookeeper.log.maxfilesize}</MaxFileSize>\n    </triggeringPolicy>\n  </appender>\n\n  <!--\n    Add TRACEFILE to root logger to get log file output\n    Log TRACE level and above messages to a log file\n  -->\n  <!--property name=\"zookeeper.tracelog.dir\" value=\"${zookeeper.log.dir}\" />\n  <property name=\"zookeeper.tracelog.file\" value=\"zookeeper_trace.log\" />\n  <appender name=\"TRACEFILE\" class=\"ch.qos.logback.core.FileAppender\">\n    <File>${zookeeper.tracelog.dir}/${zookeeper.tracelog.file}</File>\n    <encoder>\n      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>\n    </encoder>\n    <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n      <level>TRACE</level>\n    </filter>\n  </appender-->\n\n  <!--\n    zk audit logging\n  -->\n  <property name=\"zookeeper.auditlog.file\" value=\"zookeeper_audit.log\" />\n  <property name=\"zookeeper.auditlog.threshold\" value=\"INFO\" />\n  <property name=\"audit.logger\" value=\"INFO, RFAAUDIT\" />\n\n  <appender name=\"RFAAUDIT\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n    <File>${zookeeper.log.dir}/${zookeeper.auditlog.file}</File>\n    <encoder>\n      <pattern>%d{ISO8601} %p %c{2}: %m%n</pattern>\n    </encoder>\n    <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n      <level>${zookeeper.auditlog.threshold}</level>\n    </filter>\n    <rollingPolicy class=\"ch.qos.logback.core.rolling.FixedWindowRollingPolicy\">\n      <maxIndex>10</maxIndex>\n      <FileNamePattern>${zookeeper.log.dir}/${zookeeper.auditlog.file}.%i</FileNamePattern>\n    </rollingPolicy>\n    <triggeringPolicy class=\"ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy\">\n      <MaxFileSize>10MB</MaxFileSize>\n    </triggeringPolicy>\n  </appender>\n\n  <logger name=\"org.apache.zookeeper.audit.Slf4jAuditLogger\" additivity=\"false\" level=\"${audit.logger}\">\n    <appender-ref ref=\"RFAAUDIT\" />\n  </logger>\n\n  <root level=\"INFO\">\n    <appender-ref ref=\"CONSOLE\" />\n    <appender-ref ref=\"ROLLINGFILE\" />\n  </root>\n</configuration>\n

Tip

This is a customization of the original file from Zookeeper source tree. Please refer to documentation to configure logging.

"},{"location":"cluster/setup/zookeeper.html#create-systemd-file","title":"Create Systemd File","text":"

Create a systemd file. Put the following in /etc/systemd/system/drove.zookeeper.service:

[Unit]\nDescription=Drove Zookeeper Service\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nUser=drove\nTimeoutStartSec=0\nRestart=always\nExecStartPre=-/usr/bin/docker pull zookeeper:3.8\nExecStart=/usr/bin/docker run \\\n    --env-file /etc/drove/zk/zk.env \\\n    --volume /var/lib/drove/zk/data:/data \\\n    --volume /var/lib/drove/zk/datalog:/datalog \\\n    --volume /var/log/drove/zk:/logs \\\n    --volume /etc/drove/zk/logback.xml:/conf/logback.xml \\\n    --volume /etc/drove/zk/java.env:/conf/java.env \\\n    --publish 2181:2181 \\\n    --publish 2888:2888 \\\n    --publish 3888:3888 \\\n    --rm \\\n    --name drove.zookeeper \\\n    zookeeper:3.8\n\n[Install]\nWantedBy=multi-user.target\n

Verify the file with the following command:

systemd-analyze verify drove.zookeeper.service\n

Set permissions

chmod 664 /etc/systemd/system/drove.zookeeper.service\n

"},{"location":"cluster/setup/zookeeper.html#start-the-service-on-all-servers","title":"Start the service on all servers","text":"

Use the following to start the service:

systemctl daemon-reload\nsystemctl enable drove.zookeeper\nsystemctl start drove.zookeeper\n

You can check server status using the following:

echo srvr | nc localhost 2181\n

Tip

Replace localhost on the above command with the actual ZK server IPs to test remote connectivity.

Note

You can access the ZK client from the container using the following command:

docker exec -it drove.zookeeper bin/zkCli.sh\n

To connect to remote host you can use the following:

docker exec -it drove.zookeeper bin/zkCli.sh -server <server name or ip>:2181\n

"},{"location":"extra/cli.html","title":"Drove CLI","text":"

Details for the Drove CLI, including installation and usage can be found in the cli repo.

Repo link: https://github.com/PhonePe/drove-cli.

"},{"location":"extra/epoch.html","title":"Epoch","text":"

Epoch is a cron type scheduler that spins up container jobs on Drove.

Details for using epoch can be found in the epoch repo.

Link for Epoch repo: https://github.com/PhonePe/epoch.

"},{"location":"extra/epoch.html#epoch-cli","title":"Epoch CLI","text":"

There is a cli client for interaction with epoch. Details for installation and usage can be found in the epoch CLI repo.

Link for Epoch CLI repo: https://github.com/phonepe/epoch-cli.

"},{"location":"extra/libraries.html","title":"Libraries","text":"

Drove is written in Java. We provide a few libraries that can be used to integrate with a Drove cluster.

"},{"location":"extra/libraries.html#setup","title":"Setup","text":"

Setup the drove version

<properties>\n    <!--other properties-->\n    <drove.version>1.31</drove.version>\n</properties>\n

Checking the latest version

Latest version can be checked at the github packages page here

All libraries are located in sub packages of the top level package com.phonepe.drove.

Java Version Compatibility

Using Drove libraries would need Java versions 17+.

"},{"location":"extra/libraries.html#drove-model","title":"Drove Model","text":"

The model library for the classes used in request and response. It has dependency on jackson and dropwizard-validation.

"},{"location":"extra/libraries.html#dependency","title":"Dependency","text":"
<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-models</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#drove-client","title":"Drove Client","text":"

We provide a client library that can be used to connect to a Drove cluster. The cluster accepts controller endpoints as parameter (among other things) and automatically tracks the leader controller. If a single controller endpoint is provided, this functionality is turned off.

Please note that the client does not provide specific functions corresponding to different api calls from the controller, it acts as a simple endpoint discovery mechanism for drove cluster. Please refer to API section for details on individual apis.

"},{"location":"extra/libraries.html#transport","title":"Transport","text":"

The transport layer in the client is used to actually make HTTP calls to the Drove server. A new transport can be used by implementing the get(), post(), put() and delete() methods in the DroveHttpTransport interface.

By default Drove client uses Java internal HTTP client as a trivial transport implementation. We also provide an Apache Http Components based implementation.

Tip

Do not use the default transport in production. Please use the HTTP Components based transport or your custom ones.

"},{"location":"extra/libraries.html#dependencies","title":"Dependencies","text":"
 <dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-client</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-client-httpcomponent-transport</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#sample-code","title":"Sample code","text":"
public class DroveCluster implements AutoCloseable {\n\n    @Getter\n    private final DroveClient droveClient;\n\n    public DroveCluster() {\n        final var config = new DroveConfig()\n            .setEndpoints(List.of(\"http://controller1:4000,http://controller2:4000\"));\n\n        this.droveClient = new DroveClient(config,\n                                      List.of(new BasicAuthDecorator(\"guest\", \"guest\")),\n                                           new DroveHttpComponentsTransport(config.getCluster()));\n    }\n\n    @Override\n    public void close() throws Exception {\n        this.droveClient.close();\n    }\n}\n

RequestDecorator

This interface can be implemented to augment requests with special headers like for example Authorization, as well as for other stuff like adding content type etc etc.

"},{"location":"extra/libraries.html#drove-event-listener","title":"Drove Event Listener","text":"

This library provides callbacks that can be used to listen and react to events happening on the Drove cluster.

"},{"location":"extra/libraries.html#dependencies_1","title":"Dependencies","text":"
<!--Include Drove client-->\n<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-events-client</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#sample-code_1","title":"Sample Code","text":"
final var droveClient = ... //build your java transport, client here\n\n//Create and setup your object mapper\nfinal var mapper = new ObjectMapper();\nmapper.registerModule(new ParameterNamesModule());\nmapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);\nmapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);\nmapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);\nmapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\nmapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);\n\nfinal var listener = new DroveRemoteEventListener(droveClient, //Create listener\n                                                    mapper,\n                                                    new DroveEventPollingOffsetInMemoryStore(),\n                                                    Duration.ofSeconds(1));\n\nlistener.onEventReceived() //Connect signal handlers\n    .connect(events -> {\n        log.info(\"Remote Events: {}\", events);\n    });\n\nlistener.start(); //Start listening\n\n\n//Once done close the listener\nlistener.close();\n

Event Types

Please check the com.phonepe.drove.models.events package for the different event types and classes.

Event Polling Offset Store

The event poller library uses polling to find new events based on an offset. The event polling offset store is used to store and retrieve this offset. The DroveEventPollingOffsetInMemoryStore default store stores this information in-memory. Implement DroveEventPollingOffsetStore to a more permanent storage if you want this to be more permanent.

"},{"location":"extra/libraries.html#drove-hazelcast-cluster-discovery","title":"Drove Hazelcast Cluster Discovery","text":"

Drove provides an implementation of the Hazelcast discovery SPI so that containers deployed on a drove cluster can discover each other. This client uses the token injected by drove in the DROVE_APP_INSTANCE_AUTH_TOKEN environment variable to get sibling information from the controller.

"},{"location":"extra/libraries.html#dependencies_2","title":"Dependencies","text":"
<!--Include Drove client-->\n<!--Include Hazelcast-->\n<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-events-client</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#sample-code_2","title":"Sample Code","text":"
//Setup hazelcast\nConfig config = new Config();\n\n// Enable discovery\nconfig.setProperty(\"hazelcast.discovery.enabled\", \"true\");\nconfig.setProperty(\"hazelcast.discovery.public.ip.enabled\", \"true\");\nconfig.setProperty(\"hazelcast.socket.client.bind.any\", \"true\");\nconfig.setProperty(\"hazelcast.socket.bind.any\", \"false\");\n\n//Setup networking\nNetworkConfig networkConfig = config.getNetworkConfig();\nnetworkConfig.getInterfaces().addInterface(\"0.0.0.0\").setEnabled(true);\nnetworkConfig.setPort(port); //Port is the port exposed on the container for hazelcast clustering\n\n// Setup Drove discovery\nJoinConfig joinConfig = networkConfig.getJoin();\n\nDiscoveryConfig discoveryConfig = joinConfig.getDiscoveryConfig();\nDiscoveryStrategyConfig discoveryStrategyConfig =\n        new DiscoveryStrategyConfig(new DroveDiscoveryStrategyFactory());\ndiscoveryStrategyConfig.addProperty(\"drove-endpoint\", \"http://controller1:4000,http://controller2:4000\"); //Controller endpoints\ndiscoveryStrategyConfig.addProperty(\"port-name\", \"hazelcast\"); // Name of the hazelcast port defined in Application spec\ndiscoveryStrategyConfig.addProperty(\"transport\", \"com.phonepe.drove.client.transport.httpcomponent.DroveHttpComponentsTransport\");\ndiscoveryStrategyConfig.addProperty(\"cluster-by-app-name\", true); //Cluster container across multiple app versions\ndiscoveryConfig.addDiscoveryStrategyConfig(discoveryStrategyConfig);\n\n//Create hazelcast node\nval node = Hazelcast.newHazelcastInstance(config);\n\n//Once connected, node.getCluster() will be non null\n

Peer discovery modes

By default the containers will only discover and connect to containers from the same application id. If you need to connect to containers from all versions of the same application please set the cluster-by-app-name property to true as in the above example.

"},{"location":"extra/libraries.html#drove-apache-ignite-discovery","title":"Drove Apache Ignite Discovery","text":"

Drove provides an implementation of the apache ignite discovery so that containers deployed on a drove cluster can discover each other. This client uses the token injected by drove in the DROVE_APP_INSTANCE_AUTH_TOKEN environment variable to get sibling information from the controller.

"},{"location":"extra/libraries.html#dependencies_3","title":"Dependencies","text":"
<!--Include Drove client-->\n<!--Include apache ignite-->\n<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-ignite-discovery</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#sample-code_3","title":"Sample Code","text":"
//Setup ignite\nIgniteConfigProvider igniteConfigProvider = new IgniteConfigProvider();\n\nIgniteConfiguration igniteConfiguration = igniteConfigProvider.provideIgniteConfiguration(DroveIgniteConfig.builder()\n        .communicationPortName(\"igniteComm\") // Communication port name\n        .droveEndpoint(\"http://controller1:4000,http://controller2:4000\") //Controller endpoints\n        .useAppNameForDiscovery(true) //Cluster container across multiple app versions\n        .discoveryPortName(\"igniteDiscovery\") // Discovery port name\n        .build());\n\n// Start ignite\nIgnite ignite = Ignition.start(configuration);\n

Peer discovery modes

By default the containers will only discover and connect to containers from the same application id. If you need to connect to containers from all versions of the same application please set the cluster-by-app-name property to true as in the above example.

"},{"location":"extra/nvidia.html","title":"Setting up Nvidia GPU computation on executor","text":"

Prerequisite: Docker version 19.0.3+. Check Docker versions and nvidia for details.

Below steps are for ubuntu primarily for other distros check the associated links.

"},{"location":"extra/nvidia.html#install-nvidia-drivers-on-hosts","title":"Install nvidia drivers on hosts","text":"

Ubuntu provides packaged drivers for nvidia. Driver installation Guide

Recommended

ubuntu-drivers list --gpgpu\nubuntu-drivers install --gpgpu nvidia:535-server\n

Alternatively apt can be used, but may require additional steps Manual install

# Check for the latest stable version \napt search nvidia-driver.*server\napt install -y nvidia-driver-535-server  nvidia-utils-535-server \n

For other distros check Guide

"},{"location":"extra/nvidia.html#install-nvidia-container-toolkit","title":"Install Nvidia-container-toolkit","text":"

Add nvidia repo

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg   && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list |     sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' |     sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list\n\napt install -y nvidia-container-toolkit\n
For other distros check guide here

Configure docker with nvidia toolkit

nvidia-ctk runtime configure --runtime=docker\n\nsystemctl restart docker #Restart Docker\n
"},{"location":"extra/nvidia.html#verify-installation","title":"Verify installation","text":"

On Host nvidia-smi -l In docker container docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi

+-----------------------------------------------------------------------------+\n| NVIDIA-SMI 535.86.10    Driver Version: 535.86.10    CUDA Version: 12.2     |\n|-------------------------------+----------------------+----------------------+\n| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |\n| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |\n|                               |                      |               MIG M. |\n|===============================+======================+======================|\n|   0  Tesla T4            On   | 00000000:00:1E.0 Off |                    0 |\n| N/A   34C    P8     9W /  70W |      0MiB / 15109MiB |      0%      Default |\n|                               |                      |                  N/A |\n+-------------------------------+----------------------+----------------------+\n\n+-----------------------------------------------------------------------------+\n| Processes:                                                                  |\n|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |\n|        ID   ID                                                   Usage      |\n|=============================================================================|\n|  No running processes found                                                 |\n+-----------------------------------------------------------------------------+\n
Verification guide

"},{"location":"extra/nvidia.html#enable-nvidia-support-on-drove","title":"Enable nvidia support on drove","text":"

Enable Nvidia support in drove-executor.yml and restart drove-executor

...\nresources:\n  ...\n  enableNvidiaGpu: true\n...\n

"},{"location":"tasks/index.html","title":"Introduction","text":"

A task is a representation for transient containerized workloads on the cluster. A task instance is supposed to have a much shorter life-time than an application instance. Use tasks to spin up things like automation scripts etc.

"},{"location":"tasks/index.html#primary-differences-with-an-application","title":"Primary differences with an application","text":"

Please note the following important differences between a task instance and application instances

  • Tasks cannot expose ports and virtual hosts for incoming traffic
  • There are no readiness checks, health checks or shutdown hooks for a task
  • Task instances cannot be scaled up or down
  • Tasks cannot be restarted
  • A task is typically owned by an application running on the cluster.
  • Task instances are not replaced if the corresponding executor node goes down during execution
  • Unlike applications there is no task + task instance, the only representation of task on a Drove cluster is a task instance.

Tip

Use epoch to spin up tasks in a periodic manner

A task specification contains the following sections:

  • Source App Name - Name of the application that created this task
  • Task ID - User supplied task ID unique in the same sourceAppName scope
  • Executable - The container to deploy on the cluster
  • Resources - CPU and Memory required for the container
  • Placement Policy - How containers are to be placed in the cluster
  • Environment Variables - Environment variables and values
  • Volumes - Volumes to be mounted into the container
  • Configs - Configs/files to be mounted into the container
  • Logging details - Logging spec (for example rsyslog server)
  • Tags - A map of strings for additional metadata
"},{"location":"tasks/index.html#task-id","title":"Task ID","text":"

Identification of a task is a bit more complicated on Drove. There is a Task ID ({sourceAppName}-{taskId}) which is used internally in drove. This is returned to the client when task is created.

However, clients are supposed to use the {sourceAppName,taskId} combo they have sent in the task spec to address and send commands to their tasks.

"},{"location":"tasks/index.html#task-states-and-operations","title":"Task States and operations","text":"

Tasks on Drove have their own life cycle modelled as a state machine. State transitions can be triggered by issuing operations using the APIs.

"},{"location":"tasks/index.html#states","title":"States","text":"

Tasks on a Drove cluster can be one of the following states:

  • PENDING - Task has been submitted, yet to be provisioned
  • PROVISIONING - Task is assigned to an executor and docker image i ng -ownloaded
  • PROVISIONING_FAILED - Docker image download failed
  • STARTING - Docker run is starting
  • RUNNING - Task is running currently
  • RUN_COMPLETED - Task run has completed. Whether passed or failed n -o be checked from the task result.
  • DEPROVISIONING - Docker image cleanup underway
  • STOPPED - Task cleanup completed. This is a terminal state.
  • LOST - Task disappeared while executor was down.
  • UNKNOWN - All tasks that are running are put in this state when executor has been restarted and startup recovery has not kicked in yet
"},{"location":"tasks/index.html#operations","title":"Operations","text":"

The following task operations are recognized by Drove:

  • CREATE - Create a task. The Task definition/spec is provided as an argument to this.
  • KILL - Kill a task. The task ID is taken as a parameter.

Tip

All operations need Cluster Operation Spec which can be used to control the timeout and parallelism of tasks generated by the operation.

"},{"location":"tasks/index.html#task-state-machine","title":"Task State Machine","text":"

The following state machine signifies the states and transitions as affected by cluster state and operations issued.

"},{"location":"tasks/operations.html","title":"Task Operations","text":"

This page discusses operations relevant to Task management. Please go over the Task State Machine to understand the different states a task can be in and how operations applied (and external changes) move a task from one state to another.

Note

Please go through Cluster Op Spec to understand the operation parameters being sent.

For tasks only the timeout parameter is relevant.

Note

Only one operation can be active on a particular task identified by a {sourceAppName,taskId} at a time.

Warning

Only the leader controller will accept and process operations. To avoid confusion, use the controller endpoint exposed by Drove Gateway to issue commands.

"},{"location":"tasks/operations.html#cluster-operation-specification","title":"Cluster Operation Specification","text":"

When an operation is submitted to the cluster, a cluster op spec needs to be specified. This is needed to control different aspects of the operation, including parallelism of an operation or increase the timeout for the operation and so on.

The following aspects of an operation can be configured:

Name Option Description Timeout timeout The duration after which Drove considers the operation to have timed out. Parallelism parallelism Parallelism of the task. (Range: 1-32) Failure Strategy failureStrategy Set this to STOP.

Note

For internal recovery operations, Drove generates it's own operations. For that, Drove applies the following cluster operation spec:

  • timeout - 300 seconds
  • parallelism - 1
  • failureStrategy - STOP

The default operation spec can be configured in the controller configuration file. It is recommended to set this to a something like 8 for faster recovery.

"},{"location":"tasks/operations.html#how-to-initiate-an-operation","title":"How to initiate an operation","text":"

Tip

Use the Drove CLI to perform all manual operations.

All operations for task lifecycle management need to be issued via a POST HTTP call to the leader controller endpoint on the path /apis/v1/tasks/operations. API will return HTTP OK/200 and relevant json response as payload.

Sample api call:

curl --location 'http://drove.local:7000/apis/v1/tasks/operations' \\\n--header 'Content-Type: application/json' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data '{\n    \"type\": \"KILL\",\n    \"sourceAppName\" : \"TEST_APP\",\n    \"taskId\" : \"T0012\",\n    \"opSpec\": {\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}'\n

Note

In the above examples, http://drove.local:7000 is the endpoint of the leader. TEST_APP is the name of the application that started this task and taskId is a unique client generated id. Authorization is basic auth.

Warning

Task operations are not cancellable.

"},{"location":"tasks/operations.html#create-a-task","title":"Create a task","text":"

A task can be created issuing the following command.

Preconditions: - Task with same {sourceAppName,taskId} should not exist on the cluster.

State Transition:

  • none \u2192 PENDING \u2192 PROVISIONING \u2192 STARTING \u2192 RUNNING \u2192 RUN_COMPLETED \u2192 DEPROVISIONING \u2192 STOPPED

To create a task a Task Spec needs to be created first.

Once ready, CLI command needs to be issued or the following payload needs to be sent:

Drove CLIJSON
drove -c local tasks create sample/test_task.json\n

Sample Request Payload

{\n    \"type\": \"CREATE\",\n    \"spec\": {...}, //(1)!\n    \"opSpec\": { //(2)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Spec as mentioned in Task Specification
  2. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"taskId\": \"TEST_APP-T0012\"\n    },\n    \"message\": \"success\"\n}\n

Warning

There are no separate create/run steps in a task. Creation will start execution automatically and immediately.

"},{"location":"tasks/operations.html#kill-a-task","title":"Kill a task","text":"

A task can be created issuing the following command.

Preconditions: - Task with same {sourceAppName,taskId} needs to exist on the cluster.

State Transition:

  • RUNNING \u2192 RUN_COMPLETED \u2192 DEPROVISIONING \u2192 STOPPED

CLI command needs to be issued or the following payload needs to be sent:

Drove CLIJSON
drove -c local tasks kill TEST_APP T0012\n

Sample Request Payload

{\n    \"type\": \"KILL\",\n    \"sourceAppName\" : \"TEST_APP\",//(1)!\n    \"taskId\" : \"T0012\",//(2)!\n    \"opSpec\": {//(3)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Source app name as mentioned in spec during task creation
  2. Task ID as mentioned in the spec
  3. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"taskId\": \"T0012\"\n    },\n    \"message\": \"success\"\n}\n

Note

Task metadata will remain on the cluster for some time. Metadata cleanup for tasks is automatic and can be configured in the controller configuration.

"},{"location":"tasks/specification.html","title":"Task Specification","text":"

A task is defined using JSON. We use a sample configuration below to explain the options.

"},{"location":"tasks/specification.html#sample-task-definition","title":"Sample Task Definition","text":"
{\n    \"sourceAppName\": \"TEST_APP\",//(1)!\n    \"taskId\": \"T0012\",//(2)!\n    \"executable\": {//(3)!\n        \"type\": \"DOCKER\", // (4)!\n        \"url\": \"ghcr.io/appform-io/test-task\",//(5)!\n        \"dockerPullTimeout\": \"100 seconds\"//(6)!\n    },\n     \"resources\": [//(7)!\n        {\n            \"type\": \"CPU\",\n            \"count\": 1//(8)!\n        },\n        {\n            \"type\": \"MEMORY\",\n            \"sizeInMB\": 128//(9)!\n        }\n    ],\n    \"volumes\": [//(10)!\n        {\n            \"pathInContainer\": \"/data\",//(11)!\n            \"pathOnHost\": \"/mnt/datavol\",//(12)!\n            \"mode\" : \"READ_WRITE\"//(13)!\n        }\n    ],\n    \"configs\" : [//(14)!\n        {\n            \"type\" : \"INLINE\",//(15)!\n            \"localFilename\": \"/testfiles/drove.txt\",//(16)!\n            \"data\" : \"RHJvdmUgdGVzdA==\"//(17)!\n        }\n    ],\n    \"placementPolicy\": {//(18)!\n        \"type\": \"ANY\"//(19)!\n    },\n    \"env\": {//(20)!\n        \"CORES\": \"8\"\n    },\n    \"args\" : [] //(27)!\n    \"tags\": { //(21)!\n        \"superSpecialApp\": \"yes_i_am\",\n        \"say_my_name\": \"heisenberg\"\n    },\n    \"logging\": {//(22)!\n        \"type\": \"LOCAL\",//(23)!\n        \"maxSize\": \"100m\",//(24)!\n        \"maxFiles\": 3,//(25)!\n        \"compress\": true//(26)!\n    }\n}\n
  1. Name of the application that has started the task. Make sure this is a valid application on the cluster.
  2. An unique ID for this task. Uniqueness is up to the user, Drove will scope it in the sourceAppName namespace.
  3. Coordinates for the executable. Refer to Executable Specification for details.
  4. Right now the only type supported is DOCKER.
  5. Docker container address
  6. Timeout for container pull.
  7. Volumes to be mounted. Refer to Volume Specification for details.
  8. Path that will be visible inside the container for this mount.
  9. Actual path on the host machine for the mount.
  10. Mount mode can be READ_WRITE and READ_ONLY
  11. Configuration to be injected as file inside the container. Please refer Config Specification for details.
  12. Type of config. Can be INLINE, EXECUTOR_LOCAL_FILE, ONTROLLER_HTTP_FETCHandEXECUTOR_HTTP_FETCH`. Specifies how drove will t the contents to be injected..
  13. File name for the config inside the container.
  14. Serialized form of the data, this and other parameters will vary cording to the type specified above.
  15. List of resources required to run this application. Check Resource Requirements Specification for more tails.
  16. Number of CPU cores to be allocated.
  17. Amount of memory to be allocated expressed in Megabytes
  18. Specifies how the container will be placed on the cluster. Check Placement Policy for details.
  19. Type of placement can be ANY, ONE_PER_HOST, MATCH_TAG, NO_TAG, RULE_BASED, ANY and COMPOSITE. Rest of the parameters in this section will depend on the type.
  20. Custom environment variables. Additional variables are injected by Drove as well. See Environment Variables section for tails.
  21. Key value metadata that can be used in external systems.
  22. Specify how docker log files are configured. Refer to Logging Specification
  23. Log to local file
  24. Maximum File Size
  25. Number of latest log files to retain
  26. Log files will be compressed
  27. List of command line arguments. See Command Line Arguments for details.

Warning

Please make sure sourceAppName is set to a correct application name as specified in the name parameter of a running application on the cluster.

If this is not done, stale task metadata will not be cleaned up and your metadata store performance will get affected over time.

"},{"location":"tasks/specification.html#executable-specification","title":"Executable Specification","text":"

Right now Drove supports only docker containers. However as engines, both docker and podman are supported. Drove executors will fetch the executable directly from the registry based on the configuration provided.

Name Option Description Type type Set type to DOCKER. URL url Docker container URL`. Timeout dockerPullTimeout Timeout for docker image pull.

Note

Drove supports docker registry authentication. This can be configured in the executor configuration file.

"},{"location":"tasks/specification.html#resource-requirements-specification","title":"Resource Requirements Specification","text":"

This section specifies the hardware resources required to run the container. Right now only CPU and MEMORY are supported as resource types that can be reserved for a container.

"},{"location":"tasks/specification.html#cpu-requirements","title":"CPU Requirements","text":"

Specifies number of cores to be assigned to the container.

Name Option Description Type type Set type to CPU for this. Count count Number of cores to be assigned."},{"location":"tasks/specification.html#memory-requirements","title":"Memory Requirements","text":"

Specifies amount of memory to be allocated to a container.

Name Option Description Type type Set type to MEMORY for this. Count sizeInMB Amount of memory (in Mega Bytes) to be allocated.

Sample

[\n    {\n        \"type\": \"CPU\",\n        \"count\": 1\n    },\n    {\n        \"type\": \"MEMORY\",\n        \"sizeInMB\": 128\n    }\n]\n

Note

Both CPU and MEMORY configurations are mandatory.

"},{"location":"tasks/specification.html#volume-specification","title":"Volume Specification","text":"

Files and directories can be mounted from the executor host into the container. The volumes section contains a list of volumes that need to be mounted.

Name Option Description Path In Container pathInContainer Path that will be visible inside the container for this mount. Path On Host pathOnHost Actual path on the host machine for the mount. Mount Mode mode Mount mode can be READ_WRITE and READ_ONLY to allow the containerized process to write or read to the volume.

Info

We do not support mounting remote volumes as of now.

"},{"location":"tasks/specification.html#config-specification","title":"Config Specification","text":"

Drove supports injection of configuration files into containers. The specifications for the same are discussed below.

"},{"location":"tasks/specification.html#inline-config","title":"Inline config","text":"

Inline configuration can be added in the Application Specification itself. This will manifest as a file inside the container.

The following details are needed for this:

Name Option Description Type type Set the value to INLINE Local Filename localFilename File name for the config inside the container. Data data Base64 encoded string for the data. The value for this will be masked on UI.

Config file:

port: 8080\nlogLevel: DEBUG\n
Corresponding config specification:
{\n    \"type\" : \"INLINE\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"data\" : \"cG9ydDogODA4MApsb2dMZXZlbDogREVCVUcK\"\n}\n

Warning

The full base 64 encoded config data will get stored in Drove ZK and will be pushed to executors inline. It is not recommended to stream large config files to containers using this method. This will probably need additional configuration on your ZK cluster.

"},{"location":"tasks/specification.html#locally-loaded-config","title":"Locally loaded config","text":"

Config file from a path on the executor directly. Such files can be distributed to the executor host using existing configuration management systems such as OpenTofu, Salt etc.

The following details are needed for this:

Name Option Description Type type Set the value to EXECUTOR_LOCAL_FILE Local Filename localFilename File name for the config inside the container. File path filePathOnHost Path to the config file on executor host.

Sample config specification:

{\n    \"type\" : \"EXECUTOR_LOCAL_FILE\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"data\" : \"/mnt/configs/myservice/config.yml\"\n}\n

"},{"location":"tasks/specification.html#controller-fetched-config","title":"Controller fetched Config","text":"

Config file can be fetched from a remote server by the controller. Once fetched, these will be streamed to the executor as part of the instance specification for starting a container.

The following details are needed for this:

Name Option Description Type type Set the value to CONTROLLER_HTTP_FETCH Local Filename localFilename File name for the config inside the container. HTTP Call Details http HTTP Call related details. Please refer to HTTP Call Specification for details.

Sample config specification:

{\n    \"type\" : \"CONTROLLER_HTTP_FETCH\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"http\" : {\n        \"protocol\" : \"HTTP\",\n        \"hostname\" : \"configserver.internal.yourdomain.net\",\n        \"port\" : 8080,\n        \"path\" : \"/configs/myapp\",\n        \"username\" : \"appuser\",\n        \"password\" : \"secretpassword\"\n    }\n}\n

Note

The controller will make an API call for every single time it asks an executor to spin up a container. Please make sure to account for this in your configuration management system.

"},{"location":"tasks/specification.html#executor-fetched-config","title":"Executor fetched Config","text":"

Config file can be fetched from a remote server by the executor before spinning up a container. Once fetched, the payload will be injected as a config file into the container.

The following details are needed for this:

Name Option Description Type type Set the value to EXECUTOR_HTTP_FETCH Local Filename localFilename File name for the config inside the container. HTTP Call Details http HTTP Call related details. Please refer to HTTP Call Specification for details.

Sample config specification:

{\n    \"type\" : \"EXECUTOR_HTTP_FETCH\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"http\" : {\n        \"protocol\" : \"HTTP\",\n        \"hostname\" : \"configserver.internal.yourdomain.net\",\n        \"port\" : 8080,\n        \"path\" : \"/configs/myapp\",\n        \"username\" : \"appuser\",\n        \"password\" : \"secretpassword\"\n    }\n}\n

Note

All executors will make an API call for every single time they spin up a container for this application. Please make sure to account for this in your configuration management system.

"},{"location":"tasks/specification.html#http-call-specification","title":"HTTP Call Specification","text":"

This section details the options that can set when making http calls to a configuration management system from controllers or executors.

The following options are available for HTTP call:

Name Option Description Protocol protocol Protocol to use for upstream call. Can be HTTP or HTTPS. Hostname hostname Host to call. Port port Provide custom port. Defaults to 80 for http and 443 for https. API Path path Path component of the URL. Include query parameters here. Defaults to / HTTP Method verb Type of call, use GET, POST or PUT. Defaults to GET. Success Code successCodes List of HTTP status codes which is considered as success. Defaults to [200] Payload payload Data to be used for POST and PUT calls Connection Timeout connectionTimeout Timeout for upstream connection. Operation timeout operationTimeout Timeout for actual operation. Username username Username to be used basic auth. This field is masked out on the UI. Password password Password to be used for basic auth. This field is masked on the UI. Authorization Header authHeader Data to be passed in HTTP Authorization header. This field is masked on the UI. Additional Headers headers Any other headers to be passed to the upstream in the HTTP calls. This is a map of Skip SSL Checks insecure Skip hostname and certification checks during SSL handshake with the upstream."},{"location":"tasks/specification.html#placement-policy-specification","title":"Placement Policy Specification","text":"

Placement policy governs how Drove deploys containers on the cluster. The following sections discuss the different placement policies available and how they can be configured to achieve optimal placement of containers.

Warning

All policies will work only at a {appName, version} combination level. They will not ensure constraints at an appName level. This means that for somethinge like a one per node placement, for the same appName, multiple containers can run on the same host if multiple deployments with different versions are active in a cluster. Same applies for all policies like N per host and so on.

Important details about executor tagging

  • All hosts have at-least one tag, it's own hostname.
  • The TAG policy will consider them as valid tags. This can be used to place containers on specific hosts if needed.
  • This is handled specially in all other policy types and they will consider executors having only the hostname tag as untagged.
  • A host with a tag (other than host) will not have any containers running if not placed on them specifically using the MATCH_TAG policy
"},{"location":"tasks/specification.html#any-placement","title":"Any Placement","text":"

Containers for a {appName, version} combination can run on any un-tagged executor host.

Name Option Description Policy Type type Put ANY as policy.

Sample:

{\n    \"type\" : \"ANY\"\n}\n

Tip

For most use-cases this is the placement policy to use.

"},{"location":"tasks/specification.html#one-per-host-placement","title":"One Per Host Placement","text":"

Ensures that only one container for a particular {appName, version} combination is running on an executor host at a time.

Name Option Description Policy Type type Put ONE_PER_HOST as policy.

Sample:

{\n    \"type\" : \"ONE_PER_HOST\"\n}\n

"},{"location":"tasks/specification.html#max-n-per-host-placement","title":"Max N Per Host Placement","text":"

Ensures that at most N containers for a {appName, version} combination is running on an executor host at a time.

Name Option Description Policy Type type Put MAX_N_PER_HOST as policy. Max count max The maximum num of containers that can run on an executor. Range: 1-64

Sample:

{\n    \"type\" : \"MAX_N_PER_HOST\",\n    \"max\": 3\n}\n

"},{"location":"tasks/specification.html#match-tag-placement","title":"Match Tag Placement","text":"

Ensures that containers for a {appName, version} combination are running on an executor host that has the tags as mentioned in the policy.

Name Option Description Policy Type type Put MATCH_TAG as policy. Max count tag The tag to match.

Sample:

{\n    \"type\" : \"MATCH_TAG\",\n    \"tag\": \"gpu_enabled\"\n}\n

"},{"location":"tasks/specification.html#no-tag-placement","title":"No Tag Placement","text":"

Ensures that containers for a {appName, version} combination are running on an executor host that has no tags.

Name Option Description Policy Type type Put NO_TAG as policy.

Sample:

{\n    \"type\" : \"NO_TAG\"\n}\n

Info

The NO_TAG policy is mostly for internal use, and does not need to be specified when deploying containers that do not need any special placement logic.

"},{"location":"tasks/specification.html#composite-policy-based-placement","title":"Composite Policy Based Placement","text":"

Composite policy can be used to combine policies together to create complicated placement requirements.

Name Option Description Policy Type type Put COMPOSITE as policy. Polices policies List of policies to combine Combiner combiner Can be AND and OR and signify all-match and any-match logic on the policies mentioned.

Sample:

{\n    \"type\" : \"COMPOSITE\",\n    \"policies\": [\n        {\n            \"type\": \"ONE_PER_HOST\"\n        },\n        {\n            \"type\": \"MATH_TAG\",\n            \"tag\": \"gpu_enabled\"\n        }\n    ],\n    \"combiner\" : \"AND\"\n}\n
The above policy will ensure that only one container of the relevant {appName,version} will run on GPU enabled machines.

Tip

It is easy to go into situations where no executors match complicated placement policies. Internally, we tend to keep things rather simple and use the ANY placement for most cases and maybe tags in a few places with over-provisioning or for hosts having special hardware

"},{"location":"tasks/specification.html#environment-variables","title":"Environment variables","text":"

This config can be used to inject custom environment variables to containers. The values are defined as part of deployment specification, are same across the cluster and immutable to modifications from inside the container (ie any overrides from inside the container will not be visible across the cluster).

Sample:

{\n    \"MY_VARIABLE_1\": \"fizz\",\n    \"MY_VARIABLE_2\": \"buzz\"\n}\n

The following environment variables are injected by Drove to all containers:

Variable Name Value HOST Hostname where the container is running. This is for marathon compatibility. PORT_PORT_NUMBER A variable for every port specified in exposedPorts section. The value is the actual port on the host, the specified port is mapped to. For example if ports 8080 and 8081 are specified, two variables called PORT_8080 and PORT_8081 will be injected. DROVE_EXECUTOR_HOST Hostname where container is running. DROVE_CONTAINER_ID Container that is deployed DROVE_APP_NAME App name as specified in the Application Specification DROVE_INSTANCE_ID Actual instance ID generated by Drove DROVE_APP_ID Application ID as generated by Drove DROVE_APP_INSTANCE_AUTH_TOKEN A JWT string generated by Drove that can be used by this container to call /apis/v1/internal/... apis.

Warning

Do not pass secrets using environment variables. These variables are all visible on the UI as is. Please use Configs to inject secrets files and so on.

"},{"location":"tasks/specification.html#command-line-arguments","title":"Command line arguments","text":"

A list of command line arguments that are sent to the container engine to execute inside the container. This is provides ways for you to configure your container behaviour based off such arguments. Please refer to docker documentation for details.

Danger

This might have security implications from a system point of view. As such Drove provides administrators a way to disable passing arguments at the cluster level by setting disableCmdlArgs to true in the controller configuration.

"},{"location":"tasks/specification.html#logging-specification","title":"Logging Specification","text":"

Can be used to configure how container logs are managed on the system.

Note

This section affects the docker log driver. Drove will continue to stream logs to it's own logger which can be configured at executor level through the executor configuration file.

"},{"location":"tasks/specification.html#local-logger-configuration","title":"Local Logger configuration","text":"

This is used to configure the json-file log driver.

Name Option Description Type type Set the value to LOCAL Max Size maxSize Maximum file size. Anything bigger than this will lead to rotation. Max Files maxFiles Maximum number of logs files to keep. Range: 1-100 Compress compress Enable log file compression.

Tip

If logging section is omitted, the following configuration is applied by default: - File size: 10m - Number of files: 3 - Compression: on

"},{"location":"tasks/specification.html#rsyslog-configuration","title":"Rsyslog configuration","text":"

In case suers want to stream logs to an rsyslog server, the logging configuration needs to be set to RSYSLOG mode.

Name Option Description Type type Set the value to RSYSLOG Server server URL for the rsyslog server. Tag Prefix tagPrefix Prefix to add at the start of a tag Tag Suffix tagSuffix Suffix to add at the en of a tag.

Note

The default tag is the DROVE_INSTANCE_ID. The tagPrefix and tagSuffix will to before and after this

"}]} \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 0000000..c5c65f0 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"index.html","title":"Introduction","text":"

Drove is a container orchestrator built at PhonePe. It is focused on simplicity, container performance, and easy operations.

"},{"location":"index.html#features","title":"Features","text":"

The following sections go over the features.

"},{"location":"index.html#functional","title":"Functional","text":"
  • Application (service) and application container lifecycle management including mandated readiness checks, health checks, and pre-shutdown hooks to enable operators to take containers out of rotation easily and shut them down gracefully if needed.
  • Ensures the required (specified) number of containers will always be present in the cluster. It will detect failures across the cluster and bring containers up/down to maintain the required instance count.
  • Provides endpoint information to be consumed by routers like drove-gateway+nginx/traefik, etc., to expose containers over vhost.
  • Supports short-lived container-based tasks. This helps folks build newer systems that can spin up containers as needed on the cluster. (See epoch).
  • Provides functionality for real-time log streaming and log download for all instances.
  • Log generation is handled by Drove in a file layout suitable for existing log shipping mechanisms as well as for streaming to rsyslog servers (if needed).
  • Provides a functional read-only web-based console for checking cluster, application, task, and instance states, log streaming, etc.
  • Provides APIs for both read and write operations.
  • Supports discovery for sibling containers to support dynamic cluster reconfiguration in frameworks like Hazelcast.
  • Support extra metadata in the form of tags on instances. This can be used in external systems for routing or other use-cases, as this information is available at the endpoint as well.
  • CLI system for easy deployments and app/task lifecycle management.
  • NGinx based router called drove-gateway for efficient communication with the cluster itself and containers deployed on it.
"},{"location":"index.html#operations","title":"Operations","text":"
  • Only two components (controller and executor) to make a cluster (plus Zookeeper for coordination, drove-gateway for routing if needed).
  • All components dockerised to allow for easy deployment as well as upgrades.
  • Simple single file YAML based configuration for the controller and executor.
  • Cluster can be set to maintenance mode, where it pauses making changes to the cluster and turns off safeguards around ensuring the required number of containers get reported from the executor nodes. This will allow the SRE team to do seamless software updates across the whole cluster in a few minutes, irrespective of the size.
  • Blacklisting of the executor nodes will automatically move all the running application containers to other nodes and prevent any further allocations to this node. This allows the node to be taken down for maintenance that needs longer periods of time to complete the OS/patch application, hardware maintenance, etc.
  • Detect and kill any Zombie container nodes. On mesos, SRE team needs to be involved to manually kill such containers.
"},{"location":"index.html#performance","title":"Performance","text":"
  • Scheduler needs to be aware of NUMA hardware topology of the node and prevent containers from being split across nodes.
  • Scheduler will pin containers to specific cores on a NUMA node so as to stop containers from stepping on each other\u2019s toes during peak hours and allow them to fully utilize the multi-level caches associated with the allocated CPU cores. Some balance is anyways gained by enabling hyper-threading on the executor nodes. This should be sufficient to provide a significant boost to the application performance.
  • Allows for specialised nodes in the cluster. For example, there might be nodes with GPU available. We would want to run ML models that can utilise such hardware rather than allocate generic service containers on such nodes. To this end, the scheduler supports tagging and allows for containers to be explicitly mapped to tagged nodes.
  • Allows for different placement policies to provide some flexibility to users in where they want to place their container nodes. This sometimes helps developers deploy specific apps to specific nodes where they might have been granted special privileges to perform deeper than usual investigations of running service containers (for example, take heap-dumps to specific mounted volumes, etc.).
  • Allows for configuration injection at container startup. Such configuration can be streamed in as part of the deployment specification, mounted in from executor hosts, or fetched via API calls by the controllers or executors.
  • Provides provisions to allow for extension of the scheduler to implement different scheduling algorithms in the code later on.
  • Sometimes, NUMA localization and CPU pinning are overkill for clusters that don't need to extract the last bit of performance. For example, testing/staging clusters. To this end, Drove supports the following features:
    • Allows turning off NUMA and core pinning at executor level.
    • Allows to specify multipliers for available CPU/memory to accommodate overprovisioning on the cluster.
  • Because the above are set at an executor level, the cluster can have different types of nodes with different required performance characteristics appropriately tagged. Relevant apps can be deployed based on performance requirements.
"},{"location":"index.html#resilience","title":"Resilience","text":"
  • Small number of moving pieces. Keeps the minimal amount of dependencies in the system. This reduces the exposure to failures by effectively reducing the number of external dependencies and possible failure points.
  • Controller stores state on external system (Zookeeper) for now.
  • Executor stores all container specific states in the container metadata itself. No other state is maintained/needed by executor.
  • Containers keep running even when most of the system is down. This means that even when the cluster coordinators, executors, state storage, etc. are down, the already deployed containers keep on running as is. If a service discovery mechanism is implemented properly, this effectively protects the system against service disruptions, even in the face of failure of critical cluster components. At PhonePe, we use Ranger for service discovery.
  • Container state reconciliation is part of the executor system, so that executor service can be restarted easily without affecting application or task deployments. In other words, the executor needs to recognise the containers started by itself on restarts and report their state as usual to the controller.
  • Keeps things as simple as possible. Drove uses a few simple constructs (scale up/scale down) and implements all application/task features using that.
  • Multi-mode cluster messaging ensures that faster updates will be sent to controller via sync channels, while the controller(s) keep refreshing the cluster state periodically, irrespective of the last synced data. Drove assumes that communication failures would happen. Even if new changes can\u2019t be propagated from executor to controller, it tries to keep existing topology as updated as possible.
  • Built in safeguards to detect and kill any rogue (Zombie) container instances that have remained back for some reason (maybe some bug in the orchestrator, etc.).
  • Controller is highly available with one leader active at a time. Any communication issues with Zookeeper will lead to quick death of the controller so that another controller can take up its place as quickly as possible.
  • Leader can be tracked using the ping api and is used by components such as drove-gateway to provide a Virtual Host that can be used to interact with the cluster via the UI or the CLI, and other tools.
"},{"location":"index.html#security","title":"Security","text":"
  • Clearly designate roles for read and write operations. Write operations include cluster maintenance and app and task lifecycle maintenance.
  • Authentication system is easily extensible.
  • Supports basic auth as the minimal auth requirement. User credentials are stored in bcrypt format in controller config files.
  • Support a no-auth mode for starter clusters.
  • Provides audit logs for events in the system. Such logs can get aggregated and/or shipped out independently by existing log aggregation systems like logrotate-+rsync or (r)syslog, etc., by configuring the appropriate loggers in the controller configuration file.
  • Separate authentication system for intra-cluster authentication and for edge. This will mean that even if external auth is compromised (or vice versa), the system will keep working as is.
  • Shared secret is used for intra-cluster authentication.
  • Dynamically generated tokens are injected into container instances for seamless sibling discovery. This provides a way for developers to implement clustering mechanisms for frameworks like Hazelcast (provided already).
"},{"location":"index.html#observability","title":"Observability","text":"
  • Real-time event stream from the controller can be used for any other event driven system like drove-gateway, etc., to refresh upstream topology.
  • Metrics are available on admin ports for both the controllers and executors. Something like Telegraf can be used to collect and send them to the centralised metrics management system of your choice. (At PhonePe, we use Telegraf, which pushes the metrics to our custom metrics collection service, backed by a modified version of OpenTSDB. We use Grafana to visualize the same metrics.).
  • Published metrics from controllers include system health metrics around themselves.
  • Published metrics from executors contain system health metrics as well as other metrics around the containers running on them. This includes, but is not limited to, CPU, Memory and network usage.
"},{"location":"index.html#unsupported-features","title":"Unsupported Features","text":"
  • Auto-scaling of containers: In PhonePe, we have an extensive metrics ingestion system and an auto-scaler that works on a proprietary algorithm to scale containers up and down based on the same. This works independent of the orchestration system in play (we were on Drove and Mesos both at the same time during the transition period) and calls APIs on the deployment system that handles scaling operations independently. Any implementation at the orchestration level will not work as the contributors to the metrics might be running on different clusters, and scaling them independently will bring in more complexities rather than solving for simplicity.
  • Network level traffic control: At PhonePe, network security is handled at VRF level, and container-level access control is not needed. All services are already integrated with the OAuth2 compliant internal authentication and authorization system and perform security checks for the same at the application layer. As a matter of fact, we want containers to be as close to the raw network level as possible to ensure we can extract the highest level of network performance possible, other things being constant.
  • End-to-end configuration management: At this point of time, app/task configuration is maintained independently at PhonePe, subject to our approval workflows based on the compliance domain for the application, which can be static or dynamic and may be tied to deployments.
  • Multi-DC clusters: We have not tested a single Drove cluster spanning across multiple data centers.
"},{"location":"index.html#terminology","title":"Terminology","text":"

Before we delve into the details, let's get acquainted with the required terminology:

  • Application - A service running on the cluster. Such a service can have an exposed port and will have an automatically configured virtual host on Drove Gateway.
  • Task - A transient container-based task.
  • Controller Nodes - The brains of the cluster. Only one cluster is the leader and hence the decision maker in the system.
  • Executor Nodes - The workhorse nodes of the cluster where the actual containers are run.
  • Drove CLI - A command line client to interact with the cluster.
  • Drove Gateway - Used to provide ingress to the leader and containers running on the cluster.
  • Epoch - A cron-type scheduler to spin up tasks on a Drove cluster based on pre-defined schedules.
"},{"location":"index.html#github-repositories","title":"Github Repositories","text":"
  • Uber Repo - https://github.com/PhonePe/drove-orchestrator
  • Drove Orchestrator Code - https://github.com/PhonePe/drove
  • Drove CLI - https://github.com/PhonePe/drove-cli
  • Drove Gateway - https://github.com/PhonePe/drove-gateway
  • Epoch - https://github.com/PhonePe/epoch
  • Epoch CLI - https://github.com/PhonePe/epoch-cli
  • CoreDNS plugin for Drove - https://github.com/PhonePe/coredns-drove
"},{"location":"index.html#license","title":"License","text":"

Apache 2

"},{"location":"getting-started.html","title":"Getting Started","text":"

To get a trivial cluster up and running on a machine, the compose file can be used.

"},{"location":"getting-started.html#update-etc-hosts-to-interact-wih-nginx","title":"Update etc hosts to interact wih nginx","text":"

Add the following lines to /etc/hosts

127.0.0.1   drove.local\n127.0.0.1   testapp.local\n

"},{"location":"getting-started.html#download-the-compose-file","title":"Download the compose file","text":"
wget https://raw.githubusercontent.com/PhonePe/drove-orchestrator/master/compose/compose.yaml\n
"},{"location":"getting-started.html#bringing-up-a-demo-cluster","title":"Bringing up a demo cluster","text":"

cd compose\ndocker-compose up\n
This will start zookeeper,drove controller, executor and nginx/drove-gateway. The following ports are used:

  • Zookeeper - 2181
  • Executor - 3000
  • Controller - 4000
  • Gateway - 7000

Drove credentials would be admin/admin and guest/guest for read-write and read-only permissions respectively.

You should be able to access the UI at http://drove.local:7000

"},{"location":"getting-started.html#install-drove-cli","title":"Install drove-cli","text":"

Install the CLI for drove

pip install drove-cli\n

"},{"location":"getting-started.html#create-client-configuration","title":"Create Client Configuration","text":"

Put the following in ${HOME}/.drove

[local]\nendpoint = http://drove.local:4000\nusername = admin\npassword = admin\n
"},{"location":"getting-started.html#deploy-an-app","title":"Deploy an app","text":"

Get the sample app spec:

wget https://raw.githubusercontent.com/PhonePe/drove-cli/master/sample/test_app.json\n

Now deploy the app.

drove -c local apps create test_app.json\n

"},{"location":"getting-started.html#scale-the-app","title":"Scale the app","text":"

drove -c local apps scale TEST_APP-1 1 -w\n
This would expose the app as testapp.local. Endpoint would be: http://testapp.local:7000.

You can test the app by running the following commands:

curl http://testapp.local:7000/\ncurl http://testapp.local:7000/files/drove.txt\n
"},{"location":"getting-started.html#suspend-and-destroy-the-app","title":"Suspend and destroy the app","text":"
drove -c local apps scale TEST_APP-1 0 -w\ndrove -c local apps destroy TEST_APP-1\n
"},{"location":"getting-started.html#accessing-the-code","title":"Accessing the code","text":"

Code is hosted on github.

Cloning everything:

git clone git@github.com:PhonePe/drove-orchestrator.git\ngit submodule init\ngit submodule update\n
"},{"location":"apis/index.html","title":"Introduction","text":"

This section lists all the APIs that a user can communicate with.

"},{"location":"apis/index.html#making-an-api-call","title":"Making an API call","text":"

Use a standard HTTP client in the language of your choice to make a call to the leader controller (the cluster virtual host exposed by drove-gateway-nginx).

Tip

In case you are using Java, we recommend using the drove-client library along with the http-transport.

If multiple controllers endpoints are provided, the client will track the leader automatically. This will reduce your dependency on drove-gateway.

"},{"location":"apis/index.html#authentication","title":"Authentication","text":"

Drove uses basic auth for authentication. (You can extend to use any other auth format like OAuth). The basic auth credentials need to be sent out in the standard format in the Authorization header.

"},{"location":"apis/index.html#response-format","title":"Response format","text":"

The response format is standard for all API calls:

{\n    \"status\": \"SUCCESS\",//(1)!\n    \"data\": {//(2)!\n        \"taskId\": \"T0012\"\n    },\n    \"message\": \"success\"//(3)!\n}\n
  1. SUCCESS or FAILURE as the case may be.
  2. Content of this field is contextual to the response.
  3. Will contain success if the call was successful or relevant error message.

Warning

APIs will return relevant HTTP status codes in case of error (for example 400 for validation errors, 401 for authentication failure). However, you must always ensure that the status field is set to SUCCESS for assuming the api call is succesful, even when HTTP status code is 2xx.

APIs in Drove belong to the following major classes:

  • Application Management
  • Task Management
  • Cluster Management
  • Log Access

Tip

Response models for these apis can be found in drove-models

Note

There are no publicly accessible APIs exposed by individual executors.

"},{"location":"apis/application.html","title":"Application Management","text":""},{"location":"apis/application.html#issue-application-operation-command","title":"Issue application operation command","text":"

POST /apis/v1/applications/operations

Request

curl --location 'http://drove.local:7000/apis/v1/operations' \\\n--header 'Content-Type: application/json' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data '{\n    \"type\": \"SCALE\",\n    \"appId\": \"TEST_APP-1\",\n    \"requiredInstances\": 1,\n    \"opSpec\": {\n        \"timeout\": \"1m\",\n        \"parallelism\": 20,\n        \"failureStrategy\": \"STOP\"\n    }\n}'\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

Tip

Relevant payloads for application commands can be found in application operations section.

"},{"location":"apis/application.html#cancel-currently-running-operation","title":"Cancel currently running operation","text":"

POST /apis/v1/applications/operations/{appId}/cancel

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/operations/TEST_APP/cancel' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-list-of-applications","title":"Get list of applications","text":"

GET /apis/v1/applications

Request

curl --location 'http://drove.local:7000/apis/v1/applications' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"TEST_APP-1\": {\n            \"id\": \"TEST_APP-1\",\n            \"name\": \"TEST_APP\",\n            \"requiredInstances\": 0,\n            \"healthyInstances\": 0,\n            \"totalCPUs\": 0,\n            \"totalMemory\": 0,\n            \"state\": \"MONITORING\",\n            \"created\": 1719826995764,\n            \"updated\": 1719892126096\n        }\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-info-for-an-app","title":"Get info for an app","text":"

GET /apis/v1/applications/{id}

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"id\": \"TEST_APP-1\",\n        \"name\": \"TEST_APP\",\n        \"requiredInstances\": 1,\n        \"healthyInstances\": 1,\n        \"totalCPUs\": 1,\n        \"totalMemory\": 128,\n        \"state\": \"RUNNING\",\n        \"created\": 1719826995764,\n        \"updated\": 1719892279019\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-raw-json-specs","title":"Get raw JSON specs","text":"

GET /apis/v1/applications/{id}/spec

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/spec' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"name\": \"TEST_APP\",\n        \"version\": \"1\",\n        \"executable\": {\n            \"type\": \"DOCKER\",\n            \"url\": \"ghcr.io/appform-io/perf-test-server-httplib\",\n            \"dockerPullTimeout\": \"100 seconds\"\n        },\n        \"exposedPorts\": [\n            {\n                \"name\": \"main\",\n                \"port\": 8000,\n                \"type\": \"HTTP\"\n            }\n        ],\n        \"volumes\": [],\n        \"configs\": [\n            {\n                \"type\": \"INLINE\",\n                \"localFilename\": \"/testfiles/drove.txt\",\n                \"data\": \"\"\n            }\n        ],\n        \"type\": \"SERVICE\",\n        \"resources\": [\n            {\n                \"type\": \"CPU\",\n                \"count\": 1\n            },\n            {\n                \"type\": \"MEMORY\",\n                \"sizeInMB\": 128\n            }\n        ],\n        \"placementPolicy\": {\n            \"type\": \"ANY\"\n        },\n        \"healthcheck\": {\n            \"mode\": {\n                \"type\": \"HTTP\",\n                \"protocol\": \"HTTP\",\n                \"portName\": \"main\",\n                \"path\": \"/\",\n                \"verb\": \"GET\",\n                \"successCodes\": [\n                    200\n                ],\n                \"payload\": \"\",\n                \"connectionTimeout\": \"1 second\",\n                \"insecure\": false\n            },\n            \"timeout\": \"1 second\",\n            \"interval\": \"5 seconds\",\n            \"attempts\": 3,\n            \"initialDelay\": \"0 seconds\"\n        },\n        \"readiness\": {\n            \"mode\": {\n                \"type\": \"HTTP\",\n                \"protocol\": \"HTTP\",\n                \"portName\": \"main\",\n                \"path\": \"/\",\n                \"verb\": \"GET\",\n                \"successCodes\": [\n                    200\n                ],\n                \"payload\": \"\",\n                \"connectionTimeout\": \"1 second\",\n                \"insecure\": false\n            },\n            \"timeout\": \"1 second\",\n            \"interval\": \"3 seconds\",\n            \"attempts\": 3,\n            \"initialDelay\": \"0 seconds\"\n        },\n        \"tags\": {\n            \"superSpecialApp\": \"yes_i_am\",\n            \"say_my_name\": \"heisenberg\"\n        },\n        \"env\": {\n            \"CORES\": \"8\"\n        },\n        \"exposureSpec\": {\n            \"vhost\": \"testapp.local\",\n            \"portName\": \"main\",\n            \"mode\": \"ALL\"\n        },\n        \"preShutdown\": {\n            \"hooks\": [\n                {\n                    \"type\": \"HTTP\",\n                    \"protocol\": \"HTTP\",\n                    \"portName\": \"main\",\n                    \"path\": \"/\",\n                    \"verb\": \"GET\",\n                    \"successCodes\": [\n                        200\n                    ],\n                    \"payload\": \"\",\n                    \"connectionTimeout\": \"1 second\",\n                    \"insecure\": false\n                }\n            ],\n            \"waitBeforeKill\": \"3 seconds\"\n        }\n    },\n    \"message\": \"success\"\n}\n

Note

configs section data will not be returned by any api calls

"},{"location":"apis/application.html#get-list-of-currently-active-instances","title":"Get list of currently active instances","text":"

GET /apis/v1/applications/{id}/instances

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/instances' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"appId\": \"TEST_APP-1\",\n            \"appName\": \"TEST_APP\",\n            \"instanceId\": \"AI-58eb1111-8c2c-4ea2-a159-8fc68010a146\",\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"localInfo\": {\n                \"hostname\": \"ppessdev\",\n                \"ports\": {\n                    \"main\": {\n                        \"containerPort\": 8000,\n                        \"hostPort\": 33857,\n                        \"portType\": \"HTTP\"\n                    }\n                }\n            },\n            \"resources\": [\n                {\n                    \"type\": \"CPU\",\n                    \"cores\": {\n                        \"0\": [\n                            2\n                        ]\n                    }\n                },\n                {\n                    \"type\": \"MEMORY\",\n                    \"memoryInMB\": {\n                        \"0\": 128\n                    }\n                }\n            ],\n            \"state\": \"HEALTHY\",\n            \"metadata\": {},\n            \"errorMessage\": \"\",\n            \"created\": 1719892354194,\n            \"updated\": 1719893180105\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-list-of-old-instances","title":"Get list of old instances","text":"

GET /apis/v1/applications/{id}/instances/old

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/instances/old' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"appId\": \"TEST_APP-1\",\n            \"appName\": \"TEST_APP\",\n            \"instanceId\": \"AI-869e34ed-ebf3-4908-bf48-719475ca5640\",\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"resources\": [\n                {\n                    \"type\": \"CPU\",\n                    \"cores\": {\n                        \"0\": [\n                            2\n                        ]\n                    }\n                },\n                {\n                    \"type\": \"MEMORY\",\n                    \"memoryInMB\": {\n                        \"0\": 128\n                    }\n                }\n            ],\n            \"state\": \"STOPPED\",\n            \"metadata\": {},\n            \"errorMessage\": \"Error while pulling image ghcr.io/appform-io/perf-test-server-httplib: Status 500: {\\\"message\\\":\\\"Get \\\\\\\"https://ghcr.io/v2/\\\\\\\": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)\\\"}\\n\",\n            \"created\": 1719892279039,\n            \"updated\": 1719892354099\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#get-info-for-an-instance","title":"Get info for an instance","text":"

GET /apis/v1/applications/{appId}/instances/{instanceId}

Request

curl --location 'http://drove.local:7000/apis/v1/applications/TEST_APP-1/instances/AI-58eb1111-8c2c-4ea2-a159-8fc68010a146' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\",\n        \"appName\": \"TEST_APP\",\n        \"instanceId\": \"AI-58eb1111-8c2c-4ea2-a159-8fc68010a146\",\n        \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n        \"localInfo\": {\n            \"hostname\": \"ppessdev\",\n            \"ports\": {\n                \"main\": {\n                    \"containerPort\": 8000,\n                    \"hostPort\": 33857,\n                    \"portType\": \"HTTP\"\n                }\n            }\n        },\n        \"resources\": [\n            {\n                \"type\": \"CPU\",\n                \"cores\": {\n                    \"0\": [\n                        2\n                    ]\n                }\n            },\n            {\n                \"type\": \"MEMORY\",\n                \"memoryInMB\": {\n                    \"0\": 128\n                }\n            }\n        ],\n        \"state\": \"HEALTHY\",\n        \"metadata\": {},\n        \"errorMessage\": \"\",\n        \"created\": 1719892354194,\n        \"updated\": 1719893440105\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/application.html#application-endpoints","title":"Application Endpoints","text":"

GET /apis/v1/endpoints

Info

This API provides up-to-date information about the host and port information about application instances running on the cluster. This information can be used for Service Discovery systems to keep their information in sync with changes in the topology of applications running on the cluster.

Tip

Any tag specified in the application specification is also exposed on endpoint. This can be used to implement complicated routing logic if needed in the NGinx template on Drove Gateway.

Request

curl --location 'http://drove.local:7000/apis/v1/endpoints' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"appId\": \"TEST_APP-1\",\n            \"vhost\": \"testapp.local\",\n            \"tags\": {\n                \"superSpecialApp\": \"yes_i_am\",\n                \"say_my_name\": \"heisenberg\"\n            },\n            \"hosts\": [\n                {\n                    \"host\": \"ppessdev\",\n                    \"port\": 44315,\n                    \"portType\": \"HTTP\"\n                }\n            ]\n        },\n        {\n            \"appId\": \"TEST_APP-2\",\n            \"vhost\": \"testapp.local\",\n            \"tags\": {\n                \"superSpecialApp\": \"yes_i_am\",\n                \"say_my_name\": \"heisenberg\"\n            },\n            \"hosts\": [\n                {\n                    \"host\": \"ppessdev\",\n                    \"port\": 46623,\n                    \"portType\": \"HTTP\"\n                }\n            ]\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html","title":"Cluster Management","text":""},{"location":"apis/cluster.html#ping-api","title":"Ping API","text":"

GET /apis/v1/ping

Request

curl --location 'http://drove.local:7000/apis/v1/ping' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": \"pong\",\n    \"message\": \"success\"\n}\n

Tip

Use this api call to determine the leader in a cluster. This api will return a HTTP 200 only for the leader controller. All other controllers in the cluster will return 4xx for this api call.

"},{"location":"apis/cluster.html#cluster-management_1","title":"Cluster Management","text":""},{"location":"apis/cluster.html#get-current-cluster-state","title":"Get current cluster state","text":"

GET /apis/v1/cluster

Request

curl --location 'http://drove.local:7000/apis/v1/cluster' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"leader\": \"ppessdev:4000\",\n        \"state\": \"NORMAL\",\n        \"numExecutors\": 1,\n        \"numApplications\": 1,\n        \"numActiveApplications\": 1,\n        \"freeCores\": 9,\n        \"usedCores\": 1,\n        \"totalCores\": 10,\n        \"freeMemory\": 18898,\n        \"usedMemory\": 128,\n        \"totalMemory\": 19026\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#set-maintenance-mode-on-cluster","title":"Set maintenance mode on cluster","text":"

POST /apis/v1/cluster/maintenance/set

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/set' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"state\": \"MAINTENANCE\",\n        \"updated\": 1719897526772\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#remove-maintenance-mode-from-cluster","title":"Remove maintenance mode from cluster","text":"

POST /apis/v1/cluster/maintenance/unset

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/unset' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"state\": \"NORMAL\",\n        \"updated\": 1719897573226\n    },\n    \"message\": \"success\"\n}\n

Warning

Cluster will remain in maintenance mode for some time (about 2 minutes) internally even after maintenance mode is removed.

"},{"location":"apis/cluster.html#executor-management","title":"Executor Management","text":""},{"location":"apis/cluster.html#get-list-of-executors","title":"Get list of executors","text":"

GET /apis/v1/cluster/executors

Request

curl --location 'http://drove.local:7000/apis/v1/cluster/executors' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"hostname\": \"ppessdev\",\n            \"port\": 3000,\n            \"transportType\": \"HTTP\",\n            \"freeCores\": 9,\n            \"usedCores\": 1,\n            \"freeMemory\": 18898,\n            \"usedMemory\": 128,\n            \"tags\": [\n                \"ppessdev\"\n            ],\n            \"state\": \"ACTIVE\"\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#get-detailed-info-for-one-executor","title":"Get detailed info for one executor","text":"

GET /apis/v1/cluster/executors/{id}

Request

curl --location 'http://drove.local:7000/apis/v1/cluster/executors/a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"type\": \"EXECUTOR\",\n        \"hostname\": \"ppessdev\",\n        \"port\": 3000,\n        \"transportType\": \"HTTP\",\n        \"updated\": 1719897100104,\n        \"state\": {\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"cpus\": {\n                \"type\": \"CPU\",\n                \"freeCores\": {\n                    \"0\": [\n                        3,\n                        4,\n                        5,\n                        6,\n                        7,\n                        8,\n                        9,\n                        10,\n                        11\n                    ]\n                },\n                \"usedCores\": {\n                    \"0\": [\n                        2\n                    ]\n                }\n            },\n            \"memory\": {\n                \"type\": \"MEMORY\",\n                \"freeMemory\": {\n                    \"0\": 18898\n                },\n                \"usedMemory\": {\n                    \"0\": 128\n                }\n            }\n        },\n        \"instances\": [\n            {\n                \"appId\": \"TEST_APP-1\",\n                \"appName\": \"TEST_APP\",\n                \"instanceId\": \"AI-58eb1111-8c2c-4ea2-a159-8fc68010a146\",\n                \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n                \"localInfo\": {\n                    \"hostname\": \"ppessdev\",\n                    \"ports\": {\n                        \"main\": {\n                            \"containerPort\": 8000,\n                            \"hostPort\": 33857,\n                            \"portType\": \"HTTP\"\n                        }\n                    }\n                },\n                \"resources\": [\n                    {\n                        \"type\": \"CPU\",\n                        \"cores\": {\n                            \"0\": [\n                                2\n                            ]\n                        }\n                    },\n                    {\n                        \"type\": \"MEMORY\",\n                        \"memoryInMB\": {\n                            \"0\": 128\n                        }\n                    }\n                ],\n                \"state\": \"HEALTHY\",\n                \"metadata\": {},\n                \"errorMessage\": \"\",\n                \"created\": 1719892354194,\n                \"updated\": 1719897100104\n            }\n        ],\n        \"tasks\": [],\n        \"tags\": [\n            \"ppessdev\"\n        ],\n        \"blacklisted\": false\n    },\n    \"message\": \"success\"\n}\n
"},{"location":"apis/cluster.html#take-executor-out-of-rotation","title":"Take executor out of rotation","text":"

POST /apis/v1/cluster/executors/blacklist

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/blacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Note

Unlike other POST apis, the executors to be blacklisted are passed as query parameter id. To blacklist multiple executors, pass .../blacklist?id=<id1>&id=<id2>...

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"successful\": [\n            \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\"\n        ],\n        \"failed\": []\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#bring-executor-back-into-rotation","title":"Bring executor back into rotation","text":"

POST /apis/v1/cluster/executors/unblacklist

Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/unblacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Note

Unlike other POST apis, the executors to be un-blacklisted are passed as query parameter id. To un-blacklist multiple executors, pass .../unblacklist?id=<id1>&id=<id2>...

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"successful\": [\n            \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\"\n        ],\n        \"failed\": []\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"apis/cluster.html#drove-cluster-events","title":"Drove Cluster Events","text":"

The following APIs can be used to monitor events on Drove. If the data needs to be consumed, the /latest API should be used. For simply knowing if an event of a certain type has occurred or not, the /summary is sufficient.

"},{"location":"apis/cluster.html#event-list","title":"Event List","text":"

GET /apis/v1/cluster/events/latest

Request

curl --location 'http://drove.local:7000/apis/v1/cluster/events/latest?size=1024&lastSyncTime=0' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"events\": [\n            {\n                \"metadata\": {\n                    \"CURRENT_INSTANCES\": 0,\n                    \"APP_ID\": \"TEST_APP-1\",\n                    \"PLACEMENT_POLICY\": \"ANY\",\n                    \"APP_VERSION\": \"1\",\n                    \"CPU_COUNT\": 1,\n                    \"CURRENT_STATE\": \"RUNNING\",\n                    \"PORTS\": \"main:8000:http\",\n                    \"MEMORY\": 128,\n                    \"EXECUTABLE\": \"ghcr.io/appform-io/perf-test-server-httplib\",\n                    \"VHOST\": \"testapp.local\",\n                    \"APP_NAME\": \"TEST_APP\"\n                },\n                \"type\": \"APP_STATE_CHANGE\",\n                \"id\": \"a2b7d673-2bc2-4084-8415-d8d37cafa63d\",\n                \"time\": 1719977632050\n            },\n            {\n                \"metadata\": {\n                    \"APP_NAME\": \"TEST_APP\",\n                    \"APP_ID\": \"TEST_APP-1\",\n                    \"PORTS\": \"main:44315:http\",\n                    \"EXECUTOR_ID\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n                    \"EXECUTOR_HOST\": \"ppessdev\",\n                    \"CREATED\": 1719977629042,\n                    \"INSTANCE_ID\": \"AI-5efbb94f-835c-4c62-a073-a68437e60339\",\n                    \"CURRENT_STATE\": \"HEALTHY\"\n                },\n                \"type\": \"INSTANCE_STATE_CHANGE\",\n                \"id\": \"55d5876f-94ac-4c5d-a580-9c3b296add46\",\n                \"time\": 1719977631534\n            }\n        ],\n        \"lastSyncTime\": 1719977632050//(1)!\n    },\n    \"message\": \"success\"\n}\n

  1. Pass this as the parameter lastSyncTime in the next call to events api to receive latest events.
Query Parameter Validation Description lastSyncTime +ve long range Time when the last sync call happened on the server. Defaults to 0 (initial sync). size 1-1024 Number of latest events to return. Defaults to 1024. We recommend leaving this as is."},{"location":"apis/cluster.html#event-summary","title":"Event Summary","text":"

GET /apis/v1/cluster/events/summary

Request

curl --location 'http://drove.local:7000/apis/v1/cluster/events/summary?lastSyncTime=0' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n
Response
{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"eventsCount\": {\n            \"INSTANCE_STATE_CHANGE\": 8,\n            \"APP_STATE_CHANGE\": 17,\n            \"EXECUTOR_BLACKLISTED\": 1,\n            \"EXECUTOR_UN_BLACKLISTED\": 1\n        },\n        \"lastSyncTime\": 1719977632050//(1)!\n    },\n    \"message\": \"success\"\n}\n

  1. Pass this as the parameter lastSyncTime in the next call to events api to receive latest events.
"},{"location":"apis/cluster.html#continuous-monitoring-for-events","title":"Continuous monitoring for events","text":"

This is applicable for both the APIs listed above

  • In the first call to events api, pass lastSyncTime as zero.
  • In the response there will be a field lastSyncTime
  • Pass the last received lastSyncTime as the lastSyncTime param in the next call
  • This api is cheap enough, you should plan to make calls to it every few seconds

Info

Model for the events can be found here.

Tip

Java programs should definitely look at using the event listener library to listen to cluster events

"},{"location":"apis/logs.html","title":"Log Related APIs","text":""},{"location":"apis/logs.html#get-list-if-log-files","title":"Get list if log files","text":"

Application GET /apis/v1/logfiles/applications/{appId}/{instanceId}/list

Task GET /apis/v1/logfiles/tasks/{sourceAppName}/{taskId}/list

Request

curl --location 'http://drove.local:7000/apis/v1/logfiles/applications/TEST_APP-1/AI-5efbb94f-835c-4c62-a073-a68437e60339/list' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"files\": [\n        \"output.log-2024-07-04\",\n        \"output.log-2024-07-03\",\n        \"output.log\"\n    ]\n}\n

"},{"location":"apis/logs.html#download-log-files","title":"Download Log Files","text":"

Application GET /apis/v1/logfiles/applications/{appId}/{instanceId}/download/{fileName}

Task GET /apis/v1/logfiles/tasks/{sourceAppName}/{taskId}/download/{fileName}

Request

curl --location 'http://drove.local:7000/apis/v1/logfiles/applications/TEST_APP-1/AI-5efbb94f-835c-4c62-a073-a68437e60339/download/output.log' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

Note

The Content-Disposition header is set properly to the actual filename. For the above example it would be set to attachment; filename=output.log.

"},{"location":"apis/logs.html#read-chunks-from-log","title":"Read chunks from log","text":"

Application GET /apis/v1/logfiles/applications/{appId}/{instanceId}/read/{fileName}

Task GET /apis/v1/logfiles/tasks/{sourceAppName}/{taskId}/read/{fileName}

Query Parameter Validation Description offset Default -1, should be positive number The offset of the file to read from. length Should be a positive number Number of bytes to read.

Request

curl --location 'http://drove.local:7000/apis/v1/logfiles/applications/TEST_APP-1/AI-5efbb94f-835c-4c62-a073-a68437e60339/read/output.log' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"data\": \"\", //(1)!\n    \"offset\": 43318 //(2)!\n}\n

  1. Will contain raw data or empty string (in case of first call)
  2. Offset to be passed in the next call
"},{"location":"apis/logs.html#how-to-tail-logs","title":"How to tail logs","text":"
  1. Have a fixed buffer size in ming 1024/4096 etc
  2. Make a call to /read api with offset=-1, length = buffer size
  3. The call will return no data, but will have a valid offset
  4. Pass this offset in the next call, data will be returned if available (or empty). The response will also return the offset to pass in the .ext call.
  5. The data returned might be empty or less than length depending on availability.
  6. Keep repeating (4) to keep tailing log

Warning

  • Offset = 0 means start of the file
  • First call must be -1 for tail type functionality
"},{"location":"apis/task.html","title":"Task Management","text":""},{"location":"apis/task.html#issue-task-operation","title":"Issue task operation","text":"

POST /apis/v1/tasks/operations

Request

curl --location 'http://drove.local:7000/apis/v1/tasks/operations' \\\n--header 'Content-Type: application/json' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data '{\n    \"type\": \"KILL\",\n    \"sourceAppName\" : \"TEST_APP\",\n    \"taskId\" : \"T0012\",\n    \"opSpec\": {\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}'\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"taskId\": \"T0012\"\n    },\n    \"message\": \"success\"\n}\n

Tip

Relevant payloads for task commands can be found in task operations section.

"},{"location":"apis/task.html#search-for-task","title":"Search for task","text":"

POST /apis/v1/tasks/search

"},{"location":"apis/task.html#list-all-tasks","title":"List all tasks","text":"

GET /apis/v1/tasks

Request

curl --location 'http://drove.local:7000/apis/v1/tasks' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": [\n        {\n            \"sourceAppName\": \"TEST_APP\",\n            \"taskId\": \"T0013\",\n            \"instanceId\": \"TI-c2140806-2bb5-4ed3-9bb9-0c0c5fd0d8d6\",\n            \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n            \"hostname\": \"ppessdev\",\n            \"executable\": {\n                \"type\": \"DOCKER\",\n                \"url\": \"ghcr.io/appform-io/test-task\",\n                \"dockerPullTimeout\": \"100 seconds\"\n            },\n            \"resources\": [\n                {\n                    \"type\": \"CPU\",\n                    \"cores\": {\n                        \"0\": [\n                            2\n                        ]\n                    }\n                },\n                {\n                    \"type\": \"MEMORY\",\n                    \"memoryInMB\": {\n                        \"0\": 512\n                    }\n                }\n            ],\n            \"volumes\": [],\n            \"env\": {\n                \"ITERATIONS\": \"10\"\n            },\n            \"state\": \"RUNNING\",\n            \"metadata\": {},\n            \"errorMessage\": \"\",\n            \"created\": 1719827035480,\n            \"updated\": 1719827038414\n        }\n    ],\n    \"message\": \"success\"\n}\n

"},{"location":"apis/task.html#get-task-instance-details","title":"Get Task Instance Details","text":"

GET /apis/v1/tasks/{sourceAppName}/instances/{taskId}

Request

curl --location 'http://drove.local:7000/apis/v1/tasks/TEST_APP/instances/T0012' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4='\n

Response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"sourceAppName\": \"TEST_APP\",\n        \"taskId\": \"T0012\",\n        \"instanceId\": \"TI-6cf36f5c-6480-4ed5-9e2d-f79d9648529a\",\n        \"executorId\": \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\",\n        \"hostname\": \"ppessdev\",\n        \"executable\": {\n            \"type\": \"DOCKER\",\n            \"url\": \"ghcr.io/appform-io/test-task\",\n            \"dockerPullTimeout\": \"100 seconds\"\n        },\n        \"resources\": [\n            {\n                \"type\": \"CPU\",\n                \"cores\": {\n                    \"0\": [\n                        3\n                    ]\n                }\n            },\n            {\n                \"type\": \"MEMORY\",\n                \"memoryInMB\": {\n                    \"0\": 512\n                }\n            }\n        ],\n        \"volumes\": [],\n        \"env\": {\n            \"ITERATIONS\": \"10\"\n        },\n        \"state\": \"STOPPED\",\n        \"metadata\": {},\n        \"taskResult\": {\n            \"status\": \"SUCCESSFUL\",\n            \"exitCode\": 0\n        },\n        \"errorMessage\": \"\",\n        \"created\": 1719823470267,\n        \"updated\": 1719823483836\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"applications/index.html","title":"Introduction","text":"

An application is a virtual representation of a running service in the system.

Running containers for an application are called application instances.

An application specification contains the following details about the application:

  • Name - Name of the application
  • Version - Version of this specification
  • Executable - The container to deploy on the cluster
  • Ports - Ports to be exposed from the container
  • Resources - CPU and Memory required for the container
  • Placement Policy - How containers are to be placed in the cluster
  • Healthchecks - Healthcheck details
  • Readiness Checks - Readiness checks to pass before container is considered to be healthy
  • Pre Shutdown Hooks - Pre shutdown hooks to run on container before it is killed
  • Environment Variables - Environment variables and values
  • Exposure Information - Virtual host information
  • Volumes - Volumes to be mounted into the container
  • Configs - Configs/files to be mounted into the container
  • Logging details - Logging spec (for example rsyslog server)
  • Tags - A map of strings for additional metadata

Info

Once a spec is registered to the cluster, it can not be changed

"},{"location":"applications/index.html#application-id","title":"Application ID","text":"

Once an application is created on the cluster, an Application id is generated. The format of this id currently is: {name}-{version}. All further operations to be done on the application will need to refer to it by this ID.

"},{"location":"applications/index.html#application-states-and-operations","title":"Application States and Operations","text":"

An application on a Drove cluster follows a fixed lifecycle modelled as a state machine. State transitions are triggered by operations. Operations can be issued externally using API calls or may be generated internally by the application monitoring system.

"},{"location":"applications/index.html#states","title":"States","text":"

Applications on a Drove cluster can be one of the following states:

  • INIT - This is an intermediate state during which the application is being initialized and the spec is being validated. This is the origination state of the application.
  • MONITORING - A stable state in which application is created or suspended and does not have any running instances
  • RUNNING - A stable state in which application has the expected non-zero number of healthy application instances running on the cluster
  • OUTAGE_DETECTED - An intermediate state when Drove has detected that the current number of application instances is not matching the expected number of instances.
  • SCALING_REQUESTED - An intermediate state that signifies that application instances are being spun up or shut down to get the number of running instances to match the expected instances.
  • STOP_INSTANCES_REQUESTED - An intermediate state that signifies that specific instances of the application are being killed as requested by the user/system.
  • REPLACE_INSTANCES_REQUESTED - An _intermediate state _that signifies that instances of the application are being replaced with newer instances as requested by the user. This signifies that the app is effectively being restarted.
  • DESTROY_REQUESTED - An intermediate state that signifies that the user has requested to destroy the application and remove it from the cluster.
  • DESTROYED - An intermediate state that signifies that the app has been destroyed and metadata cleanup is underway. This is the terminal state of an application.
"},{"location":"applications/index.html#operations","title":"Operations","text":"

The following application operations are recognized by Drove:

  • CREATE - Create an application. Take the Application Specification. Fails if an app with the same application id (name + version) already exists on the cluster
  • DESTROY - Destroy an application. Takes app id as parameter. Deletes all metadata about the application from the cluster. Allowed only if the application is in Monitoring state (i.e. has zero running instances).
  • START_INSTANCES - Create new application instances. Takes the app id as well as the number of new instances to deploy. Allowed only if the application is in Monitoring or Running state.
  • STOP_INSTANCES - Stop running application instances. Takes the app id, list of instance ids to be stopped as well as flag to denote if replacement instances are to be started by Drove or not. Allowed only if the application is in Monitoring or Running state.
  • SCALE - Scale the application up and down to the specified number of instances. Drove will internally calculate whether to spin new containers up or spin old containers down as needed. Allowed if the app is in Monitoring or Running state. It is better to use either START or STOP instances command above to be more explicit in behavior. The SCALE operation is mostly for internal use by Drove, but can be issued externally as well.
  • REPLACE_INSTANCES - Replace application instances with newer ones. Can be used to do rolling restarts on the cluster. Specific instances can be targeted as well by passing an optional list of instance ids to be replaced. Allowed only when the application is in Running state.
  • SUSPEND - A shortcut to set expected instances for an application to zero. This will get translated into a SCALE operation and any running instances will be gracefully shut down. Allowed only when the application is in running state.
  • RECOVER - Internal command used to restore application state on controller failover.

Tip

All operations can take an optional Cluster Operation Spec which can be used to control the timeout and parallelism of tasks generated by the operation.

"},{"location":"applications/index.html#application-state-machine","title":"Application State Machine","text":"

The following state machine signifies the states and transitions as affected by cluster state and operations issued.

"},{"location":"applications/instances.html","title":"Application Instances","text":"

Application instances are running containers for an application. The state machine for instances are managed in a decentralised manner on the cluster nodes locally and not by the controllers. This includes running health checks, readiness checks and shutdown hooks on the container, container loss detection and container state recovery on executor service restart.

Regular updates about the instance state are provided by executors to the controllers and are used to keep the application state up-to-date or trigger application operations to bring the applications to stable states.

"},{"location":"applications/instances.html#application-instance-states","title":"Application Instance States","text":"

An application instance can be in one of the following states at one point in time:

  • PENDING - Container state machine start has been triggered.
  • PROVISIONING - Docker image is being downloaded
  • PROVISIONING_FAILED - Docker image download failed
  • STARTING - Docker run is being executed
  • START_FAILED - Docker run failed
  • UNREADY - Docker started, readiness check not yet started.
  • READINESS_CHECK_FAILED - Readiness check was run and has failed terminally
  • READY - Readiness checks have passed
  • HEALTHY - Health check has passed. Container is running properly and passing regular health checks
  • UNHEALTHY - Regular health check has failed. Container will stop.
  • STOPPING - Shutdown hooks are being called and docker kill be be issued
  • DEPROVISIONING - Docker image is being cleaned up
  • STOPPED - Docker stop has completed
  • LOST - Container has exited unexpectedly while executor service was down
  • UNKNOWN - All running containers are in this state when executor service is getting restarted and before startup recovery has kicked in
"},{"location":"applications/instances.html#application-instance-state-machine","title":"Application Instance State Machine","text":"

Instance state machine transitions might be triggered on receipt of commands issued by the controller or due to internal changes in the container (might have died or started failing health checks) as well as external factors like executor service restarts.

Note

No operations are allowed to be performed on application instances directly through the executor

"},{"location":"applications/operations.html","title":"Application Operations","text":"

This page discusses operations relevant to Application management. Please go over the Application State Machine and Application Instance State Machine to understand the different states an application (and it's instances) can be in and how operations applied move an application from one state to another.

Note

Please go through Cluster Op Spec to understand the operation parameters being sent.

Note

Only one operation can be active on a particular {appName,version} combination.

Warning

Only the leader controller will accept and process operations. To avoid confusion, use the controller endpoint exposed by Drove Gateway to issue commands.

"},{"location":"applications/operations.html#how-to-initiate-an-operation","title":"How to initiate an operation","text":"

Tip

Use the Drove CLI to perform all manual operations.

All operations for application lifecycle management need to be issued via a POST HTTP call to the leader controller endpoint on the path /apis/v1/applications/operations. API will return HTTP OK/200 and relevant json response as payload.

Sample api call:

curl --location 'http://drove.local:7000/apis/v1/applications/operations' \\\n--header 'Content-Type: application/json' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data '{\n    \"type\": \"START_INSTANCES\",\n    \"appId\": \"TEST_APP-3\",\n    \"instances\": 1,\n    \"opSpec\": {\n        \"timeout\": \"5m\",\n        \"parallelism\": 32,\n        \"failureStrategy\": \"STOP\"\n    }\n}'\n

Note

In the above examples, http://drove.local:7000 is the endpoint of the leader. TEST_APP-3 is the Application ID. Authorization is basic auth.

"},{"location":"applications/operations.html#cluster-operation-specification","title":"Cluster Operation Specification","text":"

When an operation is submitted to the cluster, a cluster op spec needs to be specified. This is needed to control different aspects of the operation, including parallelism of an operation or increase the timeout for the operation and so on.

The following aspects of an operation can be configured:

Name Option Description Timeout timeout The duration after which Drove considers the operation to have timed out. Parallelism parallelism Parallelism of the task. (Range: 1-32) Failure Strategy failureStrategy Set this to STOP.

Note

For internal recovery operations, Drove generates it's own operations. For that, Drove applies the following cluster operation spec:

  • timeout - 300 seconds
  • parallelism - 1
  • failureStrategy - STOP

The default operation spec can be configured in the controller configuration file. It is recommended to set this to a something like 8 for faster recovery.

"},{"location":"applications/operations.html#how-to-cancel-an-operation","title":"How to cancel an operation","text":"

Operations can be requested to be cancelled asynchronously. A POST call needs to be made to leader controller endpoint on the api /apis/v1/operations/{applicationId}/cancel (1) to achieve this.

  1. applicationId is the Application ID for the application
curl --location --request POST 'http://drove.local:7000/apis/v1/operations/TEST_APP-3/cancel' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Warning

Operation cancellation is not instantaneous. Cancellation will be affected only after current execution of the active operation is complete.

"},{"location":"applications/operations.html#create-an-application","title":"Create an application","text":"

Before deploying containers on the cluster, an application needs to be created.

Preconditions:

  • App should not exist in the cluster

State Transition:

  • none \u2192 MONITORING

To create an application, an Application Spec needs to be created first.

Once ready, CLI command needs to be issued or the following payload needs to be sent:

Drove CLIJSON
drove -c local apps create sample/test_app.json\n

Sample Request Payload

{\n    \"type\": \"CREATE\",\n    \"spec\": {...}, //(1)!\n    \"opSpec\": { //(2)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Spec as mentioned in Application Specification
  2. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"data\" : {\n        \"appId\" : \"TEST_APP-1\"\n    },\n    \"message\" : \"success\",\n    \"status\" : \"SUCCESS\"\n}\n

"},{"location":"applications/operations.html#starting-new-instances-of-an-application","title":"Starting new instances of an application","text":"

New instances can be started by issuing the START_INSTANCES command.

Preconditions - Application must be in one of the following states: MONITORING, RUNNING

State Transition:

  • {RUNNING, MONITORING} \u2192 RUNNING

The following command/payload will start 2 new instances of the application.

Drove CLIJSON
drove -c local apps deploy TEST_APP-1 2\n

Sample Request Payload

{\n    \"type\": \"START_INSTANCES\",\n    \"appId\": \"TEST_APP-1\",//(1)!\n    \"instances\": 2,//(2)!\n    \"opSpec\": {//(3)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 32,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Application ID
  2. Number of instances to be started
  3. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"applications/operations.html#suspending-an-application","title":"Suspending an application","text":"

All instances of an application can be shut down by issuing the SUSPEND command.

Preconditions - Application must be in one of the following states: MONITORING, RUNNING

State Transition:

  • {RUNNING, MONITORING} \u2192 MONITORING

The following command/payload will suspend all instances of the application.

Drove CLIJSON
drove -c local apps suspend TEST_APP-1\n

Sample Request Payload

{\n    \"type\": \"SUSPEND\",\n    \"appId\": \"TEST_APP-1\",//(1)!\n    \"opSpec\": {//(2)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 32,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Application ID
  2. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"applications/operations.html#scaling-the-application-up-or-down","title":"Scaling the application up or down","text":"

Scaling the application to required number of containers can be achieved using the SCALE command. Application can be either scaled up or down using this command.

Preconditions - Application must be in one of the following states: MONITORING, RUNNING

State Transition:

  • {RUNNING, MONITORING} \u2192 MONITORING if requiredInstances is set to 0
  • {RUNNING, MONITORING} \u2192 RUNNING if requiredInstances is non 0
Drove CLIJSON
drove -c local apps scale TEST_APP-1 2\n

Sample Request Payload

{\n    \"type\": \"SCALE\",\n    \"appId\": \"TEST_APP-1\", //(3)!\n    \"requiredInstances\": 2, //(1)!\n    \"opSpec\": { //(2)!\n        \"timeout\": \"1m\",\n        \"parallelism\": 20,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Absolute number of instances to be maintained on the cluster for the application
  2. Operation spec as mentioned in Cluster Op Spec
  3. Application ID

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

Note

During scale down, older instances are stopped first

Tip

If implementing automation on top of Drove APIs, just use the SCALE command to scale up or down instead of using START_INSTANCES or SUSPEND separately.

"},{"location":"applications/operations.html#restarting-an-application","title":"Restarting an application","text":"

Application can be restarted by issuing the REPLACE_INSTANCES operation. In this case, first clusterOpSpec.parallelism number of containers are spun up first and then an equivalent number of them are spun down. This ensures that cluster maintains enough capacity is maintained in the cluster to handle incoming traffic as the restart is underway.

Warning

If the cluster does not have sufficient capacity to spin up new containers, this operation will get stuck. So adjust your parallelism accordingly.

Preconditions - Application must be in RUNNING state.

State Transition:

  • RUNNING \u2192 REPLACE_INSTANCES_REQUESTED \u2192 RUNNING
Drove CLIJSON
drove -c local apps restart TEST_APP-1\n

Sample Request Payload

{\n    \"type\": \"REPLACE_INSTANCES\",\n    \"appId\": \"TEST_APP-1\", //(1)!\n    \"instanceIds\": [], //(2)!\n    \"opSpec\": { //(3)!\n        \"timeout\": \"1m\",\n        \"parallelism\": 20,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Application ID
  2. Instances that need to be restarted. This is optional. If nothing is passed, all instances will be replaced.
  3. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

Tip

To replace specific instances, pass their application instance ids (starts with AI-...) in the instanceIds parameter in the JSON payload.

"},{"location":"applications/operations.html#stop-or-replace-specific-instances-of-an-application","title":"Stop or replace specific instances of an application","text":"

Application instances can be killed by issuing the STOP_INSTANCES operation. Default behaviour of Drove is to replace killed instances by new instances. Such new instances are always spun up before the specified(old) instances are stopped. If skipRespawn parameter is set to true, the application instance is killed but no new instances are spun up to replace it.

Warning

If the cluster does not have sufficient capacity to spin up new containers, and skipRespawn is not set or set to false, this operation will get stuck.

Preconditions - Application must be in RUNNING state.

State Transition:

  • RUNNING \u2192 STOP_INSTANCES_REQUESTED \u2192 RUNNING if final number of instances is non zero
  • RUNNING \u2192 STOP_INSTANCES_REQUESTED \u2192 MONITORING if final number of instances is zero
Drove CLIJSON
drove -c local apps appinstances kill TEST_APP-1 AI-601d160e-c692-4ddd-8b7f-4c09b30ed02e\n

Sample Request Payload

{\n    \"type\": \"STOP_INSTANCES\",\n    \"appId\" : \"TEST_APP-1\",//(1)!\n    \"instanceIds\" : [ \"AI-601d160e-c692-4ddd-8b7f-4c09b30ed02e\" ],//(2)!\n    \"skipRespawn\" : true,//(3)!\n    \"opSpec\": {//(4)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Application ID
  2. Instance ids to be stopped
  3. Do not spin up new containers to replace the stopped ones. This is set ot false by default.
  4. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"applications/operations.html#destroy-an-application","title":"Destroy an application","text":"

To remove an application deployment (appName-version combo) the DESTROY command can be issued.

Preconditions:

  • App should not exist in the cluster

State Transition:

  • MONITORING \u2192 DESTROY_REQUESTED \u2192 DESTROYED \u2192 none

To create an application, an Application Spec needs to be created first.

Once ready, CLI command needs to be issued or the following payload needs to be sent:

Drove CLIJSON
drove -c local apps destroy TEST_APP_1\n

Sample Request Payload

{\n    \"type\": \"DESTROY\",\n    \"appId\" : \"TEST_APP-1\",//(1)!\n    \"opSpec\": {//(2)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 2,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Spec as mentioned in Application Specification
  2. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"appId\": \"TEST_APP-1\"\n    },\n    \"message\": \"success\"\n}\n

Warning

All metadata for an app and it's instances are completely obliterated from Drove's storage once an app is destroyed

"},{"location":"applications/outage.html","title":"Outage Detection and Recovery","text":"

Drove tracks all instances for an app deployment in the cluster. It will ensure the required number of containers is always running on the cluster.

"},{"location":"applications/outage.html#instance-health-detection-and-tracking","title":"Instance health detection and tracking","text":"

Executor runs periodic health checks on the container according to check spec configuration. - Runs readiness checks to ensure container is started properly before declaring it healthy - Runs health checks on the container at regular intervals to ensure it is in operating condition

Behavior for both is configured by setting the appropriate options in the application specification.

Result of such health checks (both success and failure) are reported to the controller. Appropriate action is taken to shut down containers that fail readiness or health checks.

"},{"location":"applications/outage.html#container-crash","title":"Container crash","text":"

If container for an application crashes, Drove will automatically spin up a container in it's place.

"},{"location":"applications/outage.html#executor-node-hardware-failure","title":"Executor node hardware failure","text":"

If an executor node fails, instances running on that node will be lost. This is detected by the outage detector and new containers are spun up on other parts of the cluster.

"},{"location":"applications/outage.html#executor-service-temporary-unavailability","title":"Executor service temporary unavailability","text":"

On restart, executor service reads the metadata embedded in the container and registers them. It performs a reconciliation with the leader controller to kill any local containers if the unavailability was too long and controller has already spun up new alternatives.

"},{"location":"applications/outage.html#zombie-container-detection-and-cleanup","title":"Zombie (container) detection and cleanup","text":"

Executor service keeps track of all containers it is supposed to run by running periodic reconciliation with the leader controller. Any mismatch gets handled:

  • if a container is found that is not supposed to be running, it is killed
  • If a container that is supposed to be running is not found, it is marked as lost and reported to the controller. This triggers the controller to spin up an alternative container on the cluster.
"},{"location":"applications/specification.html","title":"Application Specification","text":"

An application is defined using JSON. We use a sample configuration below to explain the options.

"},{"location":"applications/specification.html#sample-application-definition","title":"Sample Application Definition","text":"
{\n    \"name\": \"TEST_APP\", // (1)!\n    \"version\": \"1\", // (2)!\n    \"type\": \"SERVICE\", // (3)!\n    \"executable\": { //(4)!\n        \"type\": \"DOCKER\", // (5)!\n        \"url\": \"ghcr.io/appform-io/perf-test-server-httplib\",// (6)!\n        \"dockerPullTimeout\": \"100 seconds\"// (7)!\n    },\n    \"resources\": [//(20)!\n        {\n            \"type\": \"CPU\",\n            \"count\": 1//(21)!\n        },\n        {\n            \"type\": \"MEMORY\",\n            \"sizeInMB\": 128//(22)!\n        }\n    ],\n    \"volumes\": [//(12)!\n        {\n            \"pathInContainer\": \"/data\",//(13)!\n            \"pathOnHost\": \"/mnt/datavol\",//(14)!\n            \"mode\" : \"READ_WRITE\"//(15)!\n        }\n    ],\n    \"configs\" : [//(16)!\n        {\n            \"type\" : \"INLINE\",//(17)!\n            \"localFilename\": \"/testfiles/drove.txt\",//(18)!\n            \"data\" : \"RHJvdmUgdGVzdA==\"//(19)!\n        }\n    ],\n    \"placementPolicy\": {//(23)!\n        \"type\": \"ANY\"//(24)!\n    },\n    \"exposedPorts\": [//(8)!\n        {\n            \"name\": \"main\",//(9)!\n            \"port\": 8000,//(10)!\n            \"type\": \"HTTP\"//(11)!\n        }\n    ],\n    \"healthcheck\": {//(25)!\n        \"mode\": {//(26)!\n            \"type\": \"HTTP\", //(27)!\n            \"protocol\": \"HTTP\",//(28)!\n            \"portName\": \"main\",//(29)!\n            \"path\": \"/\",//(30)!\n            \"verb\": \"GET\",//(31)!\n            \"successCodes\": [//(32)!\n                200\n            ],\n            \"payload\": \"\", //(33)!\n            \"connectionTimeout\": \"1 second\" //(34)!\n        },\n        \"timeout\": \"1 second\",//(35)!\n        \"interval\": \"5 seconds\",//(36)!\n        \"attempts\": 3,//(37)!\n        \"initialDelay\": \"0 seconds\"//(38)!\n    },\n    \"readiness\": {//(39)!\n        \"mode\": {\n            \"type\": \"HTTP\",\n            \"protocol\": \"HTTP\",\n            \"portName\": \"main\",\n            \"path\": \"/\",\n            \"verb\": \"GET\",\n            \"successCodes\": [\n                200\n            ],\n            \"payload\": \"\",\n            \"connectionTimeout\": \"1 second\"\n        },\n        \"timeout\": \"1 second\",\n        \"interval\": \"3 seconds\",\n        \"attempts\": 3,\n        \"initialDelay\": \"0 seconds\"\n    },\n    \"exposureSpec\": {//(42)!\n        \"vhost\": \"testapp.local\", //(43)!\n        \"portName\": \"main\", //(44)!\n        \"mode\": \"ALL\"//(45)!\n    },\n    \"env\": {//(41)!\n        \"CORES\": \"8\"\n    },\n    \"args\" : [//(54)!\n        \"./entrypoint.sh\",\n        \"arg1\",\n        \"arg2\"\n    ],\n    \"tags\": { //(40)!\n        \"superSpecialApp\": \"yes_i_am\",\n        \"say_my_name\": \"heisenberg\"\n    },\n    \"preShutdown\": {//(46)!\n        \"hooks\": [ //(47)!\n            {\n                \"type\": \"HTTP\",\n                \"protocol\": \"HTTP\",\n                \"portName\": \"main\",\n                \"path\": \"/\",\n                \"verb\": \"GET\",\n                \"successCodes\": [\n                    200\n                ],\n                \"payload\": \"\",\n                \"connectionTimeout\": \"1 second\"\n            }\n        ],\n        \"waitBeforeKill\": \"3 seconds\"//(48)!\n    },\n    \"logging\": {//(49)!\n        \"type\": \"LOCAL\",//(50)!\n        \"maxSize\": \"100m\",//(51)!\n        \"maxFiles\": 3,//(52)!\n        \"compress\": true//(53)!\n    }\n}\n
  1. A human readable name for the application. This will remain constant for different versions of the app.
  2. A version number. Drove does not enforce any format for this, but it is recommended to increment this for changes in spec.
  3. This should be fixed to SERVICE for an application/service.
  4. Coordinates for the executable. Refer to Executable Specification for details.
  5. Right now the only type supported is DOCKER.
  6. Docker container address
  7. Timeout for container pull.
  8. The ports to be exposed from the container.
  9. A logical name for the port. This will be used to reference this port in other sections.
  10. Actual port number as mentioned in Dockerfile.
  11. Type of port. Can be: HTTP, HTTPS, TCP, UDP.
  12. Volumes to be mounted. Refer to Volume Specification for details.
  13. Path that will be visible inside the container for this mount.
  14. Actual path on the host machine for the mount.
  15. Mount mode can be READ_WRITE and READ_ONLY
  16. Configuration to be injected as file inside the container. Please refer to Config Specification for details.
  17. Type of config. Can be INLINE, EXECUTOR_LOCAL_FILE, CONTROLLER_HTTP_FETCH and EXECUTOR_HTTP_FETCH. Specifies how drove will get the contents to be injected..
  18. File name for the config inside the container.
  19. Serialized form of the data, this and other parameters will vary according to the type specified above.
  20. List of resources required to run this application. Check Resource Requirements Specification for more details.
  21. Number of CPU cores to be allocated.
  22. Amount of memory to be allocated expressed in Megabytes
  23. Specifies how the container will be placed on the cluster. Check Placement Policy for details.
  24. Type of placement can be ANY, ONE_PER_HOST, MATCH_TAG, NO_TAG, RULE_BASED, ANY and COMPOSITE. Rest of the parameters in this section will depend on the type.
  25. Health check to ensure service is running fine. Refer to Check Specification for details.
  26. Mode of health check, can be api call or command.
  27. Type of this check spec. Type can be HTTP or CMD. Rest of the options in this example are HTTP specific.
  28. API call protocol. Can be HTTP/HTTPS
  29. Port name as mentioned in the exposedPorts section.
  30. HTTP path. Include query params here.
  31. HTTP method. Can be GET,PUT or POST.
  32. Set of HTTP status codes which can be considered as success.
  33. Payload to be sent for POST and PUT calls.
  34. Connection timeout for the port.
  35. Timeout for the check run.
  36. Interval between check runs.
  37. Max attempts after which the overall check is considered to be a failure.
  38. Time to wait before starting check runs.
  39. Readiness check to pass for the container to be considered as ready. Refer to Check Specification for details.
  40. Key value metadata that can be used in external systems.
  41. Custom environment variables. Additional variables are injected by Drove as well. See Environment Variables section for details.
  42. Specifies the virtual host on which this container is exposed.
  43. FQDN for the virtual host.
  44. Port name as specified in exposedPorts section.
  45. Mode for exposure. Set this to ALL for now.
  46. Things to do before a container is shutdown. Check Pre Shutdown Behavior for more details.
  47. Hooks (HTTP api call or shell command) to run before shutting down the container. Format is same as health/readiness checks. Refer to HTTP Check Actions and Command Check Options for details.
  48. Time to wait before killing the container. The container will be in UNREADY state during this time and hence won't have api calls routed to it via Drove Gateway.
  49. Specify how docker log files are configured. Refer to Logging Specification
  50. Log to local file
  51. Maximum File Size
  52. Number of latest log files to retain
  53. Log files will be compressed
  54. List of command line arguments. See Command Line Arguments for details.
"},{"location":"applications/specification.html#executable-specification","title":"Executable Specification","text":"

Right now Drove supports only docker containers. However as engines, both docker and podman are supported. Drove executors will fetch the executable directly from the registry based on the configuration provided.

Name Option Description Type type Set type to DOCKER. URL url Docker container URL`. Timeout dockerPullTimeout Timeout for docker image pull.

Note

Drove supports docker registry authentication. This can be configured in the executor configuration file.

"},{"location":"applications/specification.html#resource-requirements-specification","title":"Resource Requirements Specification","text":"

This section specifies the hardware resources required to run the container. Right now only CPU and MEMORY are supported as resource types that can be reserved for a container.

"},{"location":"applications/specification.html#cpu-requirements","title":"CPU Requirements","text":"

Specifies number of cores to be assigned to the container.

Name Option Description Type type Set type to CPU for this. Count count Number of cores to be assigned."},{"location":"applications/specification.html#memory-requirements","title":"Memory Requirements","text":"

Specifies amount of memory to be allocated to a container.

Name Option Description Type type Set type to MEMORY for this. Count sizeInMB Amount of memory (in Mega Bytes) to be allocated.

Sample

[\n    {\n        \"type\": \"CPU\",\n        \"count\": 1\n    },\n    {\n        \"type\": \"MEMORY\",\n        \"sizeInMB\": 128\n    }\n]\n

Note

Both CPU and MEMORY configurations are mandatory.

"},{"location":"applications/specification.html#volume-specification","title":"Volume Specification","text":"

Files and directories can be mounted from the executor host into the container. The volumes section contains a list of volumes that need to be mounted.

Name Option Description Path In Container pathInContainer Path that will be visible inside the container for this mount. Path On Host pathOnHost Actual path on the host machine for the mount. Mount Mode mode Mount mode can be READ_WRITE and READ_ONLY to allow the containerized process to write or read to the volume.

Info

We do not support mounting remote volumes as of now.

"},{"location":"applications/specification.html#config-specification","title":"Config Specification","text":"

Drove supports injection of configuration files into containers. The specifications for the same are discussed below.

"},{"location":"applications/specification.html#inline-config","title":"Inline config","text":"

Inline configuration can be added in the Application Specification itself. This will manifest as a file inside the container.

The following details are needed for this:

Name Option Description Type type Set the value to INLINE Local Filename localFilename File name for the config inside the container. Data data Base64 encoded string for the data. The value for this will be masked on UI.

Config file:

port: 8080\nlogLevel: DEBUG\n
Corresponding config specification:
{\n    \"type\" : \"INLINE\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"data\" : \"cG9ydDogODA4MApsb2dMZXZlbDogREVCVUcK\"\n}\n

Warning

The full base 64 encoded config data will get stored in Drove ZK and will be pushed to executors inline. It is not recommended to stream large config files to containers using this method. This will probably need additional configuration on your ZK cluster.

"},{"location":"applications/specification.html#locally-loaded-config","title":"Locally loaded config","text":"

Config file from a path on the executor directly. Such files can be distributed to the executor host using existing configuration management systems such as OpenTofu, Salt etc.

The following details are needed for this:

Name Option Description Type type Set the value to EXECUTOR_LOCAL_FILE Local Filename localFilename File name for the config inside the container. File path filePathOnHost Path to the config file on executor host.

Sample config specification:

{\n    \"type\" : \"EXECUTOR_LOCAL_FILE\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"data\" : \"/mnt/configs/myservice/config.yml\"\n}\n

"},{"location":"applications/specification.html#controller-fetched-config","title":"Controller fetched Config","text":"

Config file can be fetched from a remote server by the controller. Once fetched, these will be streamed to the executor as part of the instance specification for starting a container.

The following details are needed for this:

Name Option Description Type type Set the value to CONTROLLER_HTTP_FETCH Local Filename localFilename File name for the config inside the container. HTTP Call Details http HTTP Call related details. Please refer to HTTP Call Specification for details.

Sample config specification:

{\n    \"type\" : \"CONTROLLER_HTTP_FETCH\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"http\" : {\n        \"protocol\" : \"HTTP\",\n        \"hostname\" : \"configserver.internal.yourdomain.net\",\n        \"port\" : 8080,\n        \"path\" : \"/configs/myapp\",\n        \"username\" : \"appuser\",\n        \"password\" : \"secretpassword\"\n    }\n}\n

Note

The controller will make an API call for every single time it asks an executor to spin up a container. Please make sure to account for this in your configuration management system.

"},{"location":"applications/specification.html#executor-fetched-config","title":"Executor fetched Config","text":"

Config file can be fetched from a remote server by the executor before spinning up a container. Once fetched, the payload will be injected as a config file into the container.

The following details are needed for this:

Name Option Description Type type Set the value to EXECUTOR_HTTP_FETCH Local Filename localFilename File name for the config inside the container. HTTP Call Details http HTTP Call related details. Please refer to HTTP Call Specification for details.

Sample config specification:

{\n    \"type\" : \"EXECUTOR_HTTP_FETCH\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"http\" : {\n        \"protocol\" : \"HTTP\",\n        \"hostname\" : \"configserver.internal.yourdomain.net\",\n        \"port\" : 8080,\n        \"path\" : \"/configs/myapp\",\n        \"username\" : \"appuser\",\n        \"password\" : \"secretpassword\"\n    }\n}\n

Note

All executors will make an API call for every single time they spin up a container for this application. Please make sure to account for this in your configuration management system.

"},{"location":"applications/specification.html#http-call-specification","title":"HTTP Call Specification","text":"

This section details the options that can set when making http calls to a configuration management system from controllers or executors.

The following options are available for HTTP call:

Name Option Description Protocol protocol Protocol to use for upstream call. Can be HTTP or HTTPS. Hostname hostname Host to call. Port port Provide custom port. Defaults to 80 for http and 443 for https. API Path path Path component of the URL. Include query parameters here. Defaults to / HTTP Method verb Type of call, use GET, POST or PUT. Defaults to GET. Success Code successCodes List of HTTP status codes which is considered as success. Defaults to [200] Payload payload Data to be used for POST and PUT calls Connection Timeout connectionTimeout Timeout for upstream connection. Operation timeout operationTimeout Timeout for actual operation. Username username Username to be used basic auth. This field is masked out on the UI. Password password Password to be used for basic auth. This field is masked on the UI. Authorization Header authHeader Data to be passed in HTTP Authorization header. This field is masked on the UI. Additional Headers headers Any other headers to be passed to the upstream in the HTTP calls. This is a map of Skip SSL Checks insecure Skip hostname and certification checks during SSL handshake with the upstream."},{"location":"applications/specification.html#placement-policy-specification","title":"Placement Policy Specification","text":"

Placement policy governs how Drove deploys containers on the cluster. The following sections discuss the different placement policies available and how they can be configured to achieve optimal placement of containers.

Warning

All policies will work only at a {appName, version} combination level. They will not ensure constraints at an appName level. This means that for somethinge like a one per node placement, for the same appName, multiple containers can run on the same host if multiple deployments with different versions are active in a cluster. Same applies for all policies like N per host and so on.

Important details about executor tagging

  • All hosts have at-least one tag, it's own hostname.
  • The TAG policy will consider them as valid tags. This can be used to place containers on specific hosts if needed.
  • This is handled specially in all other policy types and they will consider executors having only the hostname tag as untagged.
  • A host with a tag (other than host) will not have any containers running if not placed on them specifically using the MATCH_TAG policy
"},{"location":"applications/specification.html#any-placement","title":"Any Placement","text":"

Containers for a {appName, version} combination can run on any un-tagged executor host.

Name Option Description Policy Type type Put ANY as policy.

Sample:

{\n    \"type\" : \"ANY\"\n}\n

Tip

For most use-cases this is the placement policy to use.

"},{"location":"applications/specification.html#one-per-host-placement","title":"One Per Host Placement","text":"

Ensures that only one container for a particular {appName, version} combination is running on an executor host at a time.

Name Option Description Policy Type type Put ONE_PER_HOST as policy.

Sample:

{\n    \"type\" : \"ONE_PER_HOST\"\n}\n

"},{"location":"applications/specification.html#max-n-per-host-placement","title":"Max N Per Host Placement","text":"

Ensures that at most N containers for a {appName, version} combination is running on an executor host at a time.

Name Option Description Policy Type type Put MAX_N_PER_HOST as policy. Max count max The maximum num of containers that can run on an executor. Range: 1-64

Sample:

{\n    \"type\" : \"MAX_N_PER_HOST\",\n    \"max\": 3\n}\n

"},{"location":"applications/specification.html#match-tag-placement","title":"Match Tag Placement","text":"

Ensures that containers for a {appName, version} combination are running on an executor host that has the tags as mentioned in the policy.

Name Option Description Policy Type type Put MATCH_TAG as policy. Max count tag The tag to match.

Sample:

{\n    \"type\" : \"MATCH_TAG\",\n    \"tag\": \"gpu_enabled\"\n}\n

"},{"location":"applications/specification.html#no-tag-placement","title":"No Tag Placement","text":"

Ensures that containers for a {appName, version} combination are running on an executor host that has no tags.

Name Option Description Policy Type type Put NO_TAG as policy.

Sample:

{\n    \"type\" : \"NO_TAG\"\n}\n

Info

The NO_TAG policy is mostly for internal use, and does not need to be specified when deploying containers that do not need any special placement logic.

"},{"location":"applications/specification.html#composite-policy-based-placement","title":"Composite Policy Based Placement","text":"

Composite policy can be used to combine policies together to create complicated placement requirements.

Name Option Description Policy Type type Put COMPOSITE as policy. Polices policies List of policies to combine Combiner combiner Can be AND and OR and signify all-match and any-match logic on the policies mentioned.

Sample:

{\n    \"type\" : \"COMPOSITE\",\n    \"policies\": [\n        {\n            \"type\": \"ONE_PER_HOST\"\n        },\n        {\n            \"type\": \"MATH_TAG\",\n            \"tag\": \"gpu_enabled\"\n        }\n    ],\n    \"combiner\" : \"AND\"\n}\n
The above policy will ensure that only one container of the relevant {appName,version} will run on GPU enabled machines.

Tip

It is easy to go into situations where no executors match complicated placement policies. Internally, we tend to keep things rather simple and use the ANY placement for most cases and maybe tags in a few places with over-provisioning or for hosts having special hardware

"},{"location":"applications/specification.html#environment-variables","title":"Environment variables","text":"

This config can be used to inject custom environment variables to containers. The values are defined as part of deployment specification, are same across the cluster and immutable to modifications from inside the container (ie any overrides from inside the container will not be visible across the cluster).

Sample:

{\n    \"MY_VARIABLE_1\": \"fizz\",\n    \"MY_VARIABLE_2\": \"buzz\"\n}\n

The following environment variables are injected by Drove to all containers:

Variable Name Value HOST Hostname where the container is running. This is for marathon compatibility. PORT_PORT_NUMBER A variable for every port specified in exposedPorts section. The value is the actual port on the host, the specified port is mapped to. For example if ports 8080 and 8081 are specified, two variables called PORT_8080 and PORT_8081 will be injected. DROVE_EXECUTOR_HOST Hostname where container is running. DROVE_CONTAINER_ID Container that is deployed DROVE_APP_NAME App name as specified in the Application Specification DROVE_INSTANCE_ID Actual instance ID generated by Drove DROVE_APP_ID Application ID as generated by Drove DROVE_APP_INSTANCE_AUTH_TOKEN A JWT string generated by Drove that can be used by this container to call /apis/v1/internal/... apis.

Warning

Do not pass secrets using environment variables. These variables are all visible on the UI as is. Please use Configs to inject secrets files and so on.

"},{"location":"applications/specification.html#command-line-arguments","title":"Command line arguments","text":"

A list of command line arguments that are sent to the container engine to execute inside the container. This is provides ways for you to configure your container behaviour based off such arguments. Please refer to docker documentation for details.

Danger

This might have security implications from a system point of view. As such Drove provides administrators a way to disable passing arguments at the cluster level by setting disableCmdlArgs to true in the controller configuration.

"},{"location":"applications/specification.html#check-specification","title":"Check Specification","text":"

One of the cornerstones of managing applications on the cluster is to ensure we keep track of instance health and manage their life cycle depending on their health state. We need to define how to monitor health for containers accordingly. The checks will be executed on Applications and a Check result is generated. The result consists of the following:

  • Status - Healthy, Unhealthy or Stopped if the container is already in stopping state
  • Message - Any error message as generated by a specific checker
"},{"location":"applications/specification.html#common-options","title":"Common Options","text":"Name Option Description Mode mode The definition of a HTTP call or a Command to be executed in the container. See following sections for details. Timeout timeout Duration for which we wait before declaring a check as failed Interval interval Interval at which check will be retried Attempts attempts Number of times a check is retried before it is declared as a failure Initial Delay initialDelay Delay before executing the check for the first time.

Note

initialDelay is ignored when readiness checks and health checks are run in the recovery path as the container is already running at that point in time.

"},{"location":"applications/specification.html#http-check-options","title":"HTTP Check Options","text":"Name Option Description Type type Fixed to HTTP for HTTP checker Protocol protocol HTTP or HTTPS call to be made Port Name portName The name of the container port to make the http call on as specified in the Exposed Ports section in Application Spec Path path The api path to call HTTP method verb The HTTP Verb/Method to invoke. GET/PUT and POST are supported here Success Codes successCodes A set of HTTP status codes that we should consider as a success from this API. Payload payload A string payload that we can pass if the Verb is POST or PUT Connection Timeout connectionTimeout Maximum time for which the checker will wait for the connection to be set up with the container. Insecure insecure Skip hostname and certificate checks for HTTPS ports during checks."},{"location":"applications/specification.html#command-check-options","title":"Command Check Options","text":"Field Option Description Type type Fixed to CMD for command checker Command command Command to execute in the container. (Equivalent to docker exec -it <container> command>)"},{"location":"applications/specification.html#exposure-specification","title":"Exposure Specification","text":"

Exposure spec is used to specify the virtual host Drove Gateway exposes to outside world for communication with the containers.

The following information needs to be specified:

Name Option Description Virtual Host vhost The virtual host to be exposed on NGinx. This should be a fully qualified domain name. Port Name portName The portname to be exposed on the vhost. Port names are defined in exposedPorts section. Exposure Mode mode Use ALL here for now. Signifies that all healthy instances of the app are exposed to traffic.

Sample:

{\n    \"vhost\": \"teastapp.mydomain\",\n    \"port\": \"main\",\n    \"mode\": \"ALL\"\n}\n

Note

Application instances in any state other than HEALTHY are not considered for exposure. Please check Application Instance State Machine for an understanding of states of instances.

"},{"location":"applications/specification.html#configuring-pre-shutdown-behaviour","title":"Configuring Pre Shutdown Behaviour","text":"

Before a container is shut down, it is desirable to ensure things are spun down properly. This behaviour can be configured in the preShutdown section of the configuration.

Name Option Description Hooks hooks List of api calls and commands to be run on the container before it is killed. Each hook is either a HTTP Call Spec or Command Spec Wait Time waitBeforeKill Time to wait before killing the container.

Sample

{\n    \"hooks\": [\n        {\n            \"type\": \"HTTP\",\n            \"protocol\": \"HTTP\",\n            \"portName\": \"main\",\n            \"path\": \"/\",\n            \"verb\": \"GET\",\n            \"successCodes\": [\n                200\n            ],\n            \"payload\": \"\",\n            \"connectionTimeout\": \"1 second\"\n        }\n    ],\n    \"waitBeforeKill\": \"3 seconds\"//(48)!\n}\n

Note

The waitBeforeKill timed wait kicks in after all the hooks have been executed.

"},{"location":"applications/specification.html#logging-specification","title":"Logging Specification","text":"

Can be used to configure how container logs are managed on the system.

Note

This section affects the docker log driver. Drove will continue to stream logs to it's own logger which can be configured at executor level through the executor configuration file.

"},{"location":"applications/specification.html#local-logger-configuration","title":"Local Logger configuration","text":"

This is used to configure the json-file log driver.

Name Option Description Type type Set the value to LOCAL Max Size maxSize Maximum file size. Anything bigger than this will lead to rotation. Max Files maxFiles Maximum number of logs files to keep. Range: 1-100 Compress compress Enable log file compression.

Tip

If logging section is omitted, the following configuration is applied by default: - File size: 10m - Number of files: 3 - Compression: on

"},{"location":"applications/specification.html#rsyslog-configuration","title":"Rsyslog configuration","text":"

In case suers want to stream logs to an rsyslog server, the logging configuration needs to be set to RSYSLOG mode.

Name Option Description Type type Set the value to RSYSLOG Server server URL for the rsyslog server. Tag Prefix tagPrefix Prefix to add at the start of a tag Tag Suffix tagSuffix Suffix to add at the en of a tag.

Note

The default tag is the DROVE_INSTANCE_ID. The tagPrefix and tagSuffix will to before and after this

"},{"location":"cluster/cluster.html","title":"Anatomy of a Drove Cluster","text":"

The following diagram provides a high level overview of a typical Drove cluster. The overall topology consists of the following components:

  • An Apache ZooKeeper cluster for state persistence and coordination
  • A set of controller nodes one of which (the leader) manages the cluster
  • A set of executor nodes on which the containers actually execute
  • NGinx + drove-gateway nodes that expose virtual hosts for the leader controller as well as for the vhosts defined for the various applications running on the cluster
"},{"location":"cluster/cluster.html#apache-zookeeper","title":"Apache ZooKeeper","text":"

Zookeeper is a central component in a Drove cluster. It is used in the following manner:

  • As store for discovery of cluster components like Controller and Executor to each other
  • For electing the leader controller in the cluster
  • As storage for Application and Task Specifications
  • Asynchronous communication channel/transient store for real-time information about controller and executor state in the cluster
"},{"location":"cluster/cluster.html#controller","title":"Controller","text":"

The controller service is the brains of a Drove cluster. The role of the controller consists of the following:

  • Ensure it has a reasonably up-to-date information about the cluster topology and free/used resources
  • Track executor status (blacklisted/online/offline etc) and tagging. - Take corrective actions in case some of them become inaccessible for whatever reason
  • Manages container placement to ensure that application and task containers get placed according to provided placement configuration/spec
  • Manage NUMA node and core affinity ensuring that instances get deployed optimally on cores and NUMA nodes without stepping on each other
  • Provide a UI for users to consume data about cluster, applications and tasks
  • Provide APIs for systems to provision apps, tasks and manage the cluster
  • Provide event stream for other tools and services to follow what is happening on the cluster
  • Provide APIs to list container level logs and provide real-time offset based polling of log contents for application and task instances
  • Implement leader election based HA so that only one controller is active(leader) at a time.
  • All decisions regarding scheduling, state machine management and recovery are taken only by the leader
  • Manage the lifecycle of all applications deployed on the cluster.
    • Maintain the required number of application instances as specified during deployment. This means that the controller has to monitor all applications running on all nodes and replace any instances or kill any spurious ones to ensure a constant number of instances on the cluster. The required number of instances is maintained as expected count, the current number of instances is maintained as running count.
    • Provide a way to adjust the number of instances for this application. This would help in users being able to scale applications up and down as needed.
    • Provide a way to restart all instances of the application. This would mean the controller would have to orchestrate a continuous string of start-kill operations across instances running on the cluster.
    • Graceful shutdown/suspension of application across the cluster. This comes as a natural extension of the above and is mostly a scale down operation with the expected count set as zero.
  • Manage task lifecycle
    • Maintain task state-machine by scheduling it on executors and ensuring it reaches terminal state
    • Provide mechanisms to cancel tasks
  • Reconcile stale and dead instances for applications and tasks and take corrective measures to ensure steady state if necessary
  • Application instance migration from blacklisted executors
  • Send command messages to executors to start and stop instances with retries and failure recovery
"},{"location":"cluster/cluster.html#executors","title":"Executors","text":"

Executors are the agents running on the nodes where the containers are deployed. Role of the executors is the following:

  • Publish hardware topology of the machine to the controller on startup.
  • Manage container lifecycle including:
    • Pulling containers from docker repository with optional authentication
    • Start containers with proper options for pinning containers to specific NUMA nodes and cores as specified by controller
      • Data for an instance is stored as specific docker label values on the containers themselves
    • Run HTTP call or shell command based readiness checks to ensure application container is ready to serve traffic based on readiness checks specification in start message
    • Monitor application container health by running periodic HTTP call or shell command based health checks as specified by controller in start message
    • Track normal (for tasks) or abnormal (for application instances) container exits.
      • For tasks, the exit code is collected and used to deduce if task succeeded (exit code is 0) or failed (exit code is non-zero)
      • For application containers, the expectation is for the container to stop only when explicitly requested and hence all exits are considered as failures and handled accordingly
    • Stop application containers on request from controller
    • Run any pre-shutdown hook calls as specified in the application specification before killing container
    • Cleanup container volumes etc
    • Cleanup docker images (if local image caching is turned off which is the default behaviour)
  • Send regular local node status updates to ZooKeeper every 20 seconds
  • Send instant updates by making direct HTTP calls to the leader controller when anything changes for any running containers and for every step of the container state machine execution for both task and application instances to allow for faster updates
  • Recover containers on process restart based on the metadata stored as labels on the running container. This data is reconciled with a snapshot of the expected instances on the node as received from the leader controller at that point in time.
  • Find and kill any zombie containers that are not supposed to exist on that node. The check is done every 30 seconds.
  • Provide container log-file listing and offset based content delivery APIs to container
"},{"location":"cluster/cluster.html#nginx-and-drove-gateway","title":"NGinx and Drove-Gateway","text":"

Almost all of the traffic between service containers is routed via the internal Ranger based service discovery system at PhonePe. However, traffic from the edge as well and between different protected environments are routed using the well-established virtual host (and additionally, in some unusual cases, header) based routing.

  • All applications on Drove can specify a Vhost and a port name as endpoint for such routing.
  • Upstream information for such VHosts or endpoints is available from an API from the leading Drove controller.
  • This information can be used to configure any load-balancer or tourer or reverse proxy to expose applications running on Drove to the outside world.
  • We modified an existing project called Nixy so that it gets the upstream information from Drove instead of Marathon. Nixy plays the following role in a cluster:

  • Track the leader controller for a Drove cluster by making ping calls to all specified controllers

  • Provide a special data structure that can be used by a template to expose a vhost that points to the leader controller in a Drove cluster. This can be used for any tools that need to interact with a Drove cluster for deployments, monitoring as well as callback endpoints for OAuth etc etc
  • Listen to relevant events from the Drove cluster to trigger upstream refresh as necessary
  • Provide data structures that include the vhost, upstream endpoints (host:port) and metadata (application level tags) that can be used to build templates that generate NGinx configurations to enable progressively complicated routing of calls from downstream to upstreams hosted on Drove clusters. Data structure exposed to templates, groups all upstream host:port tuples by using the vhost. This allows for multiple deployments for the same VHost to exist. This is needed for a variety of situations including online-updates of services.
  • Supports username/password based authentication and header based (used internally) to Drove clusters.
  • Support for both NGinx Plus and OSS products. Drove-Nixy can make appropriate api calls to corresponding NGinx plus server to only refresh existing VHost on topology change, as well as affect a full reload when new Vhosts are detected. This ensures that there are no connection drops for critical path applications where NGinx Plus might be used. This also solves the issue of NGinx workers going into a hung state due to frequent reloads on busy clusters like our dev testing environment.

Tip

The NGinx deployment is standard across all Drove clusters. However, for clusters that receive a lot of traffic using Nginx, the cluster exposing the VHost for Drove itself might be separated from the one exposing the application virtual hosts to allow for easy scalability of the latter. The template for these are configured differently as needed respectively.

"},{"location":"cluster/cluster.html#other-components","title":"Other components","text":"

There are a few more components that are used for operational management and observability.

"},{"location":"cluster/cluster.html#telegraf","title":"Telegraf","text":"

PhonePe\u2019s internal metric management system uses a HTTP based metric collector. Telegraf is installed on all Drove nodes to collect metric from the metric port (Admin connector on Dropwizard) and push that information to our metric ingestion system. This information is then used to build dashboards as well as by our Anomaly detection and alerting systems.

"},{"location":"cluster/cluster.html#log-management","title":"Log Management","text":"

Drove provides a special logger called drove that can be configured to handle compression rotation and archival of container logs. Such container logs are stored on specialised partitions by application/application-instance-id or by source app name/ task id for application and task instances respectively. PhonePe\u2019s standardised log rotation tools are used to monitor and ship out such logs to our central log management system. The same can be replaced or enhanced by running something like promtail on Drove logs to ship out logs to tools like Grafana Loki.

"},{"location":"cluster/setup/controller.html","title":"Setting up Controllers","text":"

Controllers are the brains of Drove cluster. For HA, at least 2 controllers should be set up.

Please note the following behaviour about controllers:

  • Only one controller is leader at a time. A leader controller does not relinquish control till the process is stopped or dies
  • A controller process will die when it loses connectivity to Zookeeper
  • The process/container for controller should keep restarting till it gets connectivity
  • All decisions for the cluster are taken by the leader controller only
  • During maintenance and package upgrades etc, it is better to roll changes out on non-leaders first and then do the leader at the end
  • The controller process holds all metadata about the cluster, the current states and other information in memory.
    • Some of this information is backed by Zookeeper based storage layer
    • The other information is recreated dynamically based on updates from executors
  • Controllers being down does not affect running containers on executors
"},{"location":"cluster/setup/controller.html#controller-configuration-file-reference","title":"Controller Configuration File Reference","text":"

The Drove Controller is written on the Dropwizard framework. The configuration to the service is set using a YAML file which needs to be injected into the container. A typical controller configuration file will look like the following:

server: #(1)!\n  applicationConnectors: #(2)!\n    - type: http\n      port: 4000\n  adminConnectors: #(3)!\n    - type: http\n      port: 4001\n  applicationContextPath: / #(4)!\n  requestLog: #(5)!\n    appenders:\n      - type: console\n        timeZone: ${DROVE_TIMEZONE}\n      - type: file\n        timeZone: ${DROVE_TIMEZONE}\n        currentLogFilename: /logs/drove-controller-access.log\n        archivedLogFilenamePattern: /logs/drove-controller-access.log-%d-%i\n        archivedFileCount: 3\n        maxFileSize: 100MiB\n\n\nlogging: #(6)!\n  level: INFO\n  loggers:\n    com.phonepe.drove: ${DROVE_LOG_LEVEL}\n\n  appenders:\n    - type: console #(7)!\n      threshold: ALL\n      timeZone: ${DROVE_TIMEZONE}\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n    - type: file #(8)!\n      threshold: ALL\n      timeZone: ${DROVE_TIMEZONE}\n      currentLogFilename: /logs/drove-controller.log\n      archivedLogFilenamePattern: /logs/drove-controller.log-%d-%i\n      archivedFileCount: 3\n      maxFileSize: 100MiB\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n      archive: true\n\n\nzookeeper: #(9)!\n  connectionString: ${ZK_CONNECTION_STRING}\n\nclusterAuth: #(10)!\n  secrets:\n  - nodeType: CONTROLLER\n    secret: ${DROVE_CONTROLLER_SECRET}\n  - nodeType: EXECUTOR\n    secret: ${DROVE_EXECUTOR_SECRET}\n\nuserAuth: #(11)!\n  enabled: true\n  users:\n    - username: admin\n      password: ${DROVE_ADMIN_PASSWORD}\n      role: EXTERNAL_READ_WRITE\n    - username: guest\n      password: ${DROVE_GUEST_PASSWORD}\n      role: EXTERNAL_READ_ONLY\n\ninstanceAuth: #(12)!\n  secret: ${DROVE_INSTANCE_AUTH_SECRET}\n\noptions: #(13)!\n  maxStaleInstancesCount: 3\n  staleCheckInterval: 1m\n  staleAppAge: 1d\n  staleInstanceAge: 18h\n  staleTaskAge: 1d\n  clusterOpParallelism: 4\n
  1. Server listener configuration. See Dropwizard Server Configuration for the different options.
  2. Main port configuration. This is where the UI and APIs will be exposed. Check connector configuration docs for details.
  3. Admin port. You can take thread dumps, metrics, run healthchecks on the Drove controller on this port.
  4. Base path for UI. Keep this as is.
  5. Access logs configuration. See requestLog docs.
  6. Main logging configuration. See logging docs.
  7. Log to console. Useful in docker-compose.
  8. Log to rotating files. Useful for running servers.
  9. Configure how to connect to Zookeeper See Zookeeper Config for details.
  10. Configuration for authentication between nodes in the cluster. Please check intra node auth config for details.
  11. Configure user authentication to access the cluster. Please check User auth config for details.
  12. Signing secret for JWT to be embedded in application and task instances. Check Instance auth config for details.
  13. Special options to configure controller behaviour. See Controller Options for details.

Tip

In case you do not want to expose admin apis to outside the host, please set bindHost in the admin connectors section.

adminConnectors:\n  - type: http\n    port: 10001\n    bindHost: 127.0.0.1\n
"},{"location":"cluster/setup/controller.html#zookeeper-connection-configuration","title":"Zookeeper Connection Configuration","text":"

The following details can be configured.

Name Option Description Connection String connectionString The connection string of the form: zkserver:2181,zkserver2:2181... Data namespace namespace The top level node inside which all Drove data will be scoped. Defaults to drove if not set.

Sample

zookeeper:\n  connectionString: \"192.168.3.10:2181,192.168.3.11:2181,192.168.3.12:2181\"\n  namespace: drovetest\n
"},{"location":"cluster/setup/controller.html#intra-node-authentication-configuration","title":"Intra Node Authentication Configuration","text":"

Communication between controller and executor is protected by a shared-secret based authentication. The following configuration is meant to configure this. This section consists of a list of 2 members:

  • Config for controller to talk to executors
  • Config for executors to talk to controller

Each section consists of the following:

Name Option Description Node Type nodeType Type of node in the cluster. Can be CONTROLLER or EXECUTOR Secret secret The actual secret to be passed.

Sample

clusterAuth:\n  secrets:\n  - nodeType: CONTROLLER\n    secret: ControllerSecretValue\n  - nodeType: EXECUTOR\n    secret: ExecutorSecret\n

Danger

The values are passed in the header as is. Please manage the config file ownership to ensure that the files are not world readable.

Tip

You can use pwgen -s 32 to generate secure random strings for usage as secrets.

"},{"location":"cluster/setup/controller.html#user-authentication-configuration","title":"User Authentication Configuration","text":"

This section is used to configure user details for human and other systems that need to call Drove APIs or access the Drove UI. This is implemented using basic auth.

The configuration consists of:

Name Option Description Enabled enabled Enable basic auth for the cluster Encoding encoding The actual encoding of the password. Can be PLAIN or CRYPT Caching cachingPolicy Caching policy for the authentication and authorization of the user. Please check CaffeineSpec docs for more details. Set to maximumSize=500, expireAfterAccess=30m by default List of users users A list of users recognized by the system

Each entry in the user list consists of:

Name Option Description User Name username The actual login username Password password The password for the user. Needs to be set to bcrypt string of the actual password if encoding is set to CRYPT in the parent section. User Role role The role of the user in the cluster. Can be EXTERNAL_READ_WRITE for users who have both read and write permissions or EXTERNAL_READ_ONLY for users with read-only permissions.

Sample

userAuth:\n  enabled: true\n  encoding: CRYPT\n  users:\n    - username: admin\n      password: \"$2y$10$pfGnPkYrJEGzasvVNPjRu.IJldV9TDa0Vh.u1UdimILWDuhvapc2O\"\n      role: EXTERNAL_READ_WRITE\n    - username: guest\n      password: \"$2y$10$uCJ7WxIvd13C.1oOTs28p.xpJShGiTWuDLY/sGH9JE8nrkSGBFkc6\"\n      role: EXTERNAL_READ_ONLY\n    - username: noread\n      password: \"$2y$10$8mr/zXL5rMW/s/jlBcgXHu0UvyzfdDDvyc.etfuoR.991sn9UOX/K\"\n

No authentication

To configure a cluster without authentication, remove this section entirely.

Operator role

If role is not set, the user will be able to access the UI, but will not have access to application logs. This comes in handy to provide access to other teams to explore your deployment topology, but not get access to your logs that might contain sensitive information.

Password Hashing

We strongly recommend using bcrypt passwords for authentication. You can use the following command to generate hashed password strings:

htpasswd -nbBC 10 <username> <password>|cut -d ':' -f2\n
"},{"location":"cluster/setup/controller.html#instance-authentication-configuration","title":"Instance Authentication Configuration","text":"

All application and task instances, get access to an unique JWT that is injected into it by Drove as the environment variable DROVE_APP_INSTANCE_AUTH_TOKEN. This token is signed using a secret. This secret can be configured by setting the secret parameter in the instanceAuth section.

Sample

instanceAuth:\n  secret: RandomSecret\n

"},{"location":"cluster/setup/controller.html#controller-options","title":"Controller Options","text":"

The following options can be set to influence the behavior of the Drove cluster and the controller.

Name Option Description Stale Check Interval staleCheckInterval Interval at which Drove checks for stale application and task metadata for cleanup. Defaults to 1 hour. Expressed in duration. Stale App Age staleAppAge Apps in MONITORING state are cleaned up after some time by Drove. This variable can be used to control the max time for which such apps are maintained in the cluster. Defaults to 7 days. Expressed in duration. Stale App Instances Count maxStaleInstancesCount Maximum number of application instances metadata for stopped or lost instances to be maintained in the cluster. Defaults to 100. Stale Instance Age staleInstanceAge Maximum age for a stale application instance to be retained. Defaults to 7 days. Expressed in duration. Stale Task Age staleTaskAge Maximum time for which metadata for a finished task is retained on the cluster. Defaults to 2 days. Expressed in duration. Event Storage Duration maxEventsStorageDuration Maximum time for which cluster events are retained on the cluster. Defaults to 1 hour. Expressed in duration. Default Operation Timeout clusterOpTimeout Timeout for operations that are initiated by drove itself. For example, instance spin up in case of executor failure, instance migrations etc. Defaults to 5 minutes. Expressed in duration. Operation threads clusterOpParallelism Signified the parallelism for operations internal to the cluster. Defaults to: 1. Range: 1-32. Audited Methods auditedHttpMethods Drove prints an audit log with user details when an api is called by an user. Defaults to [\"POST\", \"PUT\"]. Allowed mount directories allowedMountDirs If provided, Drove will ensure that application and task spec can mount only the directories mentioned in this set on executor host. Disable read-only auth disableReadAuth When userAuth is enabled, setting this option, will enforce authorization only on write operations. Disable command line arguments disableCmdlArgs When set to true, passing command line arguments will be disabled. Default: false (users can pass arguments.

Sample

options:\n  staleCheckInterval: 5m\n  staleAppAge: 2d\n  maxStaleInstancesCount: 20\n  staleInstanceAge: 1d\n  staleTaskAge: 2d\n  maxEventsStorageDuration: 30m\n  clusterOpParallelism: 32\n  allowedMountDirs:\n   - /mnt/scratch\n

"},{"location":"cluster/setup/controller.html#stale-data-cleanup","title":"Stale data cleanup","text":"

In order to keep internal memory footprint low, reduce the amount of data stored on Zookeeper, and provide a faster experience on the UI,Drove keeps cleaning up data for stale applications, application instances, task instances and cluster events.

The retention for such metadata can be controlled using the following config options:

  • staleAppAge
  • maxStaleInstancesCount
  • staleInstanceAge
  • staleTaskAge
  • maxEventsStorageDuration

Warning

Configuration changes done to these parameters will have direct impact on memory usage by the controller and memory and disk utilization on the Zookeeper cluster.

"},{"location":"cluster/setup/controller.html#internal-operations","title":"Internal Operations","text":"

Drove may need to create and issue operations on applications and tasks to manage cluster stability, for maintenance and other reasons. The following parameters can be used to control the speed and parallelism of such operations:

  • clusterOpTimeout
  • clusterOpParallelism

Tip

The default value of 1 for the clusterOpParallelism parameter is generally too low for most clusters. Unless there is a specific problem, it would be advisable to set this to at least 4. If number of instances is quite high for applications (order of tens or hundreds), feel free to set this to 32.

Increasing clusterOpParallelism will make recovery faster in case of executor failures, but it will increase cpu utilization on the controller by a little bit.

"},{"location":"cluster/setup/controller.html#security-related-options","title":"Security related options","text":"

The auditedHttpMethods parameter contains a list of all HTTP methods that need to be audited. This means that if the auditedHttpMethods contains POST and PUT, any drove HTTP POST or PUT apis being called will lead to a audit in the controller logs with the details of the user that made the call.

Warning

It would be advisable to not add GET to the list. This is because the UI keeps making calls to GET apis on drove to fetch data to render. These calls are automated and happen every few seconds from the browser. This will blow up controller logs size.

The allowedMountDirs option whitelists only some directories to be mounted on containers. If this is not provided, containers will be able to mount any directory on the executors.

Danger

It is highly recommended to set allowedMountDirs to a designated directory that containers might want to use as scratch space if needed. Keeping this empty will almost definitely cause security issues in the long run.

"},{"location":"cluster/setup/controller.html#relevant-directories","title":"Relevant directories","text":"

Location for data and logs are as follows:

  • /etc/drove/controller/ - Configuration files
  • /var/log/drove/controller/ - Logs

We shall be volume mounting the config and log directories with the same name.

Prerequisite Setup

If not done already, please complete the prerequisite setup on all machines earmarked for the cluster.

"},{"location":"cluster/setup/controller.html#setup-the-config-file","title":"Setup the config file","text":"

Create a relevant configuration file in /etc/drove/controller/controller.yml.

Sample

server:\n  applicationConnectors:\n    - type: http\n      port: 10000\n  adminConnectors:\n    - type: http\n      port: 10001\n  requestLog:\n    appenders:\n      - type: file\n        timeZone: IST\n        currentLogFilename: /var/log/drove/controller/drove-controller-access.log\n        archivedLogFilenamePattern: /var/log/drove/controller/drove-controller-access.log-%d-%i\n        archivedFileCount: 3\n        maxFileSize: 100MiB\n\nlogging:\n  level: INFO\n  loggers:\n    com.phonepe.drove: INFO\n\n\n  appenders:\n    - type: file\n      threshold: ALL\n      timeZone: IST\n      currentLogFilename: /var/log/drove/controller/drove-controller.log\n      archivedLogFilenamePattern: /var/log/drove/controller/drove-controller.log-%d-%i\n      archivedFileCount: 3\n      maxFileSize: 100MiB\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n\nzookeeper:\n  connectionString: \"192.168.56.10:2181\"\n\nclusterAuth:\n  secrets:\n  - nodeType: CONTROLLER\n    secret: \"0v8XvJrDc7r86ZY1QCByPTDPninI4Xii\"\n  - nodeType: EXECUTOR\n    secret: \"pOd9sIEXhv0wrGOVc7ebwNvR7twZqyTN\"\n\nuserAuth:\n  enabled: true\n  encoding: CRYPT\n  users:\n    - username: admin\n      password: \"$2y$10$pfGnPkYrJEGzasvVNPjRu.IJldV9TDa0Vh.u1UdimILWDuhvapc2O\"\n      role: EXTERNAL_READ_WRITE\n    - username: guest\n      password: \"$2y$10$uCJ7WxIvd13C.1oOTs28p.xpJShGiTWuDLY/sGH9JE8nrkSGBFkc6\"\n      role: EXTERNAL_READ_ONLY\n\n\ninstanceAuth:\n  secret: \"bd2SIgz9OMPG2L8wA6zxj21oLVLbuLFC\"\n\noptions:\n  maxStaleInstancesCount: 3\n  staleCheckInterval: 1m\n  staleAppAge: 2d\n  staleInstanceAge: 1d\n  staleTaskAge: 1d\n  clusterOpParallelism: 4\n  allowedMountDirs:\n   - /dev/null\n

"},{"location":"cluster/setup/controller.html#setup-required-environment-variables","title":"Setup required environment variables","text":"

Environment variables need to run the drove controller are setup in /etc/drove/controller/controller.env.

CONFIG_FILE_PATH=/etc/drove/controller/controller.yml\nJAVA_PROCESS_MIN_HEAP=2g\nJAVA_PROCESS_MAX_HEAP=2g\nZK_CONNECTION_STRING=\"192.168.3.10:2181\"\nJAVA_OPTS=\"-Xlog:gc:/var/log/drove/controller/gc.log -Xlog:gc:::filecount=3,filesize=10M -Xlog:gc::time,level,tags -XX:+UseNUMA -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -Dfile.encoding=utf-8 -Djute.maxbuffer=0x9fffff\"\n
"},{"location":"cluster/setup/controller.html#create-systemd-file","title":"Create systemd file","text":"

Create a systemd file. Put the following in /etc/systemd/system/drove.controller.service:

[Unit]\nDescription=Drove Controller Service\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nUser=drove\nTimeoutStartSec=0\nRestart=always\nExecStartPre=-/usr/bin/docker pull ghcr.io/phonepe/drove-controller:latest\nExecStart=/usr/bin/docker run  \\\n    --env-file /etc/drove/controller/controller.env \\\n    --volume /etc/drove/controller:/etc/drove/controller:ro \\\n    --volume /var/log/drove/controller:/var/log/drove/controller \\\n    --publish 10000:10000  \\\n    --publish 10001:10001 \\\n    --hostname %H \\\n    --rm \\\n    --name drove.controller \\\n    ghcr.io/phonepe/drove-controller:latest\n\n[Install]\nWantedBy=multi-user.target\n

Verify the file with the following command:

systemd-analyze verify drove.controller.service\n

Set permissions

chmod 664 /etc/systemd/system/drove.controller.service\n

"},{"location":"cluster/setup/controller.html#start-the-service-on-all-servers","title":"Start the service on all servers","text":"

Use the following to start the service:

systemctl daemon-reload\nsystemctl enable drove.controller\nsystemctl start drove.controller\n

You can tail the logs at /var/logs/drove/controller/drove-controller.log.

The console would be available at http://<ip>:10000 and admin functionality will be available on http://<ip>:10001 according to the above config.

Health checks can be performed by running a curl as follows:

curl http://localhost:10001/healthcheck\n

Note

  • The healthcheck check api is available on admin port.
  • HTTP status is 200/OK if things are fine.

Once controllers are up, one of them will become the leader. You can check the leader by running the following command:

curl http://<ip>:10000/apis/v1/ping\n

Only on the leader you should get the following response along with a HTTP status 200/OK:

{\n    \"status\":\"SUCCESS\",\n    \"data\":\"pong\",\n    \"message\":\"success\"\n}\n

"},{"location":"cluster/setup/executor-setup.html","title":"Setting up Executor Nodes","text":"

We shall setup the executor nodes by setting up the hardware, operating system first and then the executor service itself.

"},{"location":"cluster/setup/executor-setup.html#considerations-and-tuning-for-hardware-and-operating-system","title":"Considerations and tuning for hardware and operating system","text":"

In the following sections we discus some aspects of scheduling, hardware and settings on the OS to ensure good performance.

"},{"location":"cluster/setup/executor-setup.html#cpu-and-memory-considerations","title":"CPU and Memory considerations","text":"

The executor nodes are the servers that host and run the actual docker containers. Drove will take into consideration the NUMA topology of these machines to optimize the placement for containers to extract the maximum performance. Along with this, Drove will cpuset the containers to the allocated cores in a non overlapping manner, so that the cores allocated to a container are dedicated to it. Memory allocated to a container is pinned as well and selected from the same NUMA node.

Needless to say the minimum amount of CPU that can be given to an application or task is 1. Fractional cpu allocation can be achieved in a predictable manner by configuring over provisioning on executor nodes.

"},{"location":"cluster/setup/executor-setup.html#over-provisioning-of-cpu-and-memory","title":"Over Provisioning of CPU and Memory","text":"

Drove does not do any kind of burst scaling or overcommitment to ensure application performance remains predictable even under load. Instead, in Drove, there is a feature to make executors appear to have more cores (and memory) than it actually has. This can be used to get more utilization out of executor nodes in clusters that do not need guaranteed performance (for example staging or dev testing clusters). This is achieved by enabling over provisioning.

Over provisioning needs to be configured in the executor configuration. It primarily consists of two configs:

  • CPU Multiplier - an integral multiplier which will be used to multiply the number of available cores
  • Memory Multiplier - an integral multiplier which will be used to multiply available memory

VCores (virtual cores) are internal representation of a CPU core on the executor. If over provisioning is disabled, a vcore will correspond to a physical core. If over provisioning is enabled, 1 CPU core will generate cpu multiplier number of v cores. Drove does do cpuset even on containers running on nodes that have over provisioning enabled, however the physical cores that the containers get bound to are chosen at random, albeit from the same NUMA node. cpuset-mem is always done on the same NUMA node as well.

Mixed clusters

In some production clusters you might have applications that are non critical in terms of performance and are unable to utilize a full core. These can be tagged to be spun up on some nodes where over provisioning is enabled. Adopting such a cluster topology will ensure that containers that need high performance run on nodes without over provisioning and the smaller apps (like for example operations consoles etc) are run on separate nodes with over provisioning enabled. Just ensure the latter are tagged properly and during app deployment specify this tag in application spec or task spec.

"},{"location":"cluster/setup/executor-setup.html#disable-numa-pinning","title":"Disable NUMA Pinning","text":"

There is an option to disable memory and core pinning. In this situation, all cores from all NUM nodes show up as being part of one node. cpuset-mems is not called if numa pinning is disabled and therefore you will be leaving some memory performance on the table. We recommend not to dabble with this unless you have tasks and containers that need more than the number of cores available on a single NUMA node. This setting is enabled at executor level by setting disableNUMAPinning: true.

"},{"location":"cluster/setup/executor-setup.html#hyper-threading","title":"Hyper-threading","text":"

Whether Hyper Threading needs to be enabled or not is a bit dependent on applications deployed and how effectively they can utilize individual CPU cores. For mixed workloads, we recommend Hyper Threading to be enabled on the executor nodes.

"},{"location":"cluster/setup/executor-setup.html#isolating-container-and-os-processes","title":"Isolating container and OS processes","text":"

Typically we would not want containers to share CPU resources with processes for the operating system, Drove Executor Service as well as Docker engine (if using docker) and so on. While complete isolation would need creating a full scheduler (and passing isolcpus to GRUB parameters), we can get a good middle ground by ensuring such processes utilize only a few CPU cores on the system, and let the Drove executors deploy and pin containers to the rest.

This is achieved in two steps:

  • Make changes to systemd to use only specific cores
  • Exclude these cores in the drove executor configuration

Let's say our server has 2 NUMA nodes, each with 40 hyper-threaded cores. We want to reserve the first 2 cores from each CPU to the OS processes. So we reserve cores [0,1,2,3] for the OS processes.

The following line in /etc/systemd/system.conf

#CPUAffinity=\n

needs to be changed to

CPUAffinity=0 1 2 3\n

Tip

Reboot the machine for this to take effect.

The changes can be validated post reboot by running the following command:

grep Cpus_allowed_list /proc/1/status\n

The expected output should be:

Cpus_allowed_list:  0-3\n

Note

Refer to this for more details.

"},{"location":"cluster/setup/executor-setup.html#gpu-computation","title":"GPU Computation","text":"

Nvidia based GPU compute can be enabled at executor level by installing relevant drivers. Please follow the setup guide to enable this. Remember to tag these nodes to isolate them from the primary cluster and use tags to deploy apps and tasks that need GPU.

"},{"location":"cluster/setup/executor-setup.html#storage-consideration","title":"Storage consideration","text":"

On executor nodes the disk might be under pressure if container (re)deployments are frequent or the containers log very heavily. As such, we recommend the logging directory for Drove be mounted on hardware that will be able to handle this load. Similar considerations need to be given to the log and package directory for docker or podman.

"},{"location":"cluster/setup/executor-setup.html#executor-configuration-reference","title":"Executor Configuration Reference","text":"

The Drove Executor is written on the Dropwizard framework. The configuration to the service is set using a YAML file which needs to be injected into the container. A typical controller configuration file will look like the following:

server: #(1)!\n  applicationConnectors: #(2)!\n    - type: http\n      port: 3000\n  adminConnectors: #(3)!\n    - type: http\n      port: 3001\n  applicationContextPath: /\n  requestLog:\n    appenders:\n      - type: console\n        timeZone: ${DROVE_TIMEZONE}\n      - type: file\n        timeZone: ${DROVE_TIMEZONE}\n        currentLogFilename: /logs/drove-executor-access.log\n        archivedLogFilenamePattern: /logs/drove-executor-access.log-%d-%i\n        archivedFileCount: 3\n        maxFileSize: 100MiB\n\nlogging:\n  level: INFO\n  loggers:\n    com.phonepe.drove: ${DROVE_LOG_LEVEL}\n\n  appenders: #(4)!\n    - type: console #(5)!\n      threshold: ALL\n      timeZone: ${DROVE_TIMEZONE}\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{instanceLogId}] %message%n\"\n    - type: file #(6)!\n      threshold: ALL\n      timeZone: ${DROVE_TIMEZONE}\n      currentLogFilename: /logs/drove-executor.log\n      archivedLogFilenamePattern: /logs/drove-executor.log-%d-%i\n      archivedFileCount: 3\n      maxFileSize: 100MiB\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n      archive: true\n\n    - type: drove #(7)!\n      logPath: \"/logs/applogs/\"\n      archivedLogFileSuffix: \"%d\"\n      archivedFileCount: 3\n      threshold: TRACE\n      timeZone: ${DROVE_TIMEZONE}\n      logFormat: \"%(%-5level) | %-23date | %-30logger{0} | %message%n\"\n      archive: true\n\nzookeeper: #(8)!\n  connectionString: ${ZK_CONNECTION_STRING}\n\nclusterAuth: #(9)!\n  secrets:\n  - nodeType: CONTROLLER\n    secret: ${DROVE_CONTROLLER_SECRET}\n  - nodeType: EXECUTOR\n    secret: ${DROVE_EXECUTOR_SECRET}\n\nresources: #(10)!\n  osCores: [ 0, 1 ]\n  exposedMemPercentage: 60\n  disableNUMAPinning: ${DROVE_DISABLE_NUMA_PINNING}\n  enableNvidiaGpu: ${DROVE_ENABLE_NVIDIA_GPU}\n\noptions: #(11)!\n  cacheImages: true\n  maxOpenFiles: 10_000\n  logBufferSize: 5m\n  cacheFileSize: 10m\n  cacheFileCount: 3\n
  1. Server listener configuration. See Dropwizard Server Configuration for the different options.
  2. Main port configuration. This is where the UI and APIs will be exposed. Check connector configuration docs for details.
  3. Admin port. You can take thread dumps, metrics, run healthchecks on the Drove controller on this port.
  4. Logging configuration. See logging docs.
  5. Log to console. Useful in docker-compose.
  6. Log to rotating files. Useful for running servers.
  7. Drove application logger configuration. See drove logger config for details.
  8. Configure how to connect to Zookeeper See Zookeeper Config for details.
  9. Configuration for authentication between nodes in the cluster. Please check intra node auth config for details.
  10. Resource configuration for this node.
  11. Options to configure executor behaviour. Check executor options section for details.

Tip

In case you do not want to expose admin apis to outside the host, please set bindHost in the admin connectors section.

adminConnectors:\n  - type: http\n    port: 10001\n    bindHost: 127.0.0.1\n
"},{"location":"cluster/setup/executor-setup.html#zookeeper-connection-configuration","title":"Zookeeper Connection Configuration","text":"

The following details can be configured.

Name Option Description Connection String connectionString The connection string of the form: zkserver:2181,zkserver2:2181... Data namespace namespace The top level node inside which all Drove data will be scoped. Defaults to drove if not set.

Sample

zookeeper:\n  connectionString: \"192.168.3.10:2181,192.168.3.11:2181,192.168.3.12:2181\"\n  namespace: drovetest\n

Note

This section is same across the cluster including both controller and executor.

"},{"location":"cluster/setup/executor-setup.html#intra-node-authentication-configuration","title":"Intra Node Authentication Configuration","text":"

Communication between controller and executor is protected by a shared-secret based authentication. The following configuration is meant to configure this. This section consists of a list of 2 members:

  • Config for controller to talk to executors
  • Config for executors to talk to controller

Each section consists of the following:

Name Option Description Node Type nodeType Type of node in the cluster. Can be CONTROLLER or EXECUTOR Secret secret The actual secret to be passed.

Sample

clusterAuth:\n  secrets:\n  - nodeType: CONTROLLER\n    secret: ControllerSecretValue\n  - nodeType: EXECUTOR\n    secret: ExecutorSecret\n

Note

This section is same across the cluster including both controller and executor.

"},{"location":"cluster/setup/executor-setup.html#drove-application-logger-configuration","title":"Drove Application Logger Configuration","text":"

Drove will segregate application and task instance logs in a directory of your choice. The path for such files is set as: - <application id>/<instance id> for Application Instances - <sourceAppName>/<task id> for Task Instances

The Drove Log Appender is based of LogBack's Sifting Appender.

The following configuration options are supported:

Name Option Description Path logPath Directory to host the logs Archive old logs archive Whether to enable log rotation Archived File Suffix archivedLogFileSuffix Suffix for archived log files. Archived File Count archivedFileCount Count of archived log files. Older files are deleted. File Size maxFileSize Size of current log file after which it is archived and a new file is created. Unit: DataSize. Total Size totalSizeCap total size after which deletion takes place. Unit: DataSize. Buffer Size bufferSize Buffer size for the logger. (Set to 8KB by default). Used if immediateFlush is turned off. Immediate Flush immediateFlush Flush logs immediately. Set to true by default (recommended)

Sample

logging:\n  level: INFO\n  ...\n\n  appenders:\n    # Setup appenders for the executor process itself first\n    ...\n\n    - type: drove\n      logPath: \"/logs/applogs/\"\n      archivedLogFileSuffix: \"%d\"\n      archivedFileCount: 3\n      threshold: TRACE\n      timeZone: ${DROVE_TIMEZONE}\n      logFormat: \"%(%-5level) | %-23date | %-30logger{0} | %message%n\"\n      archive: true\n

"},{"location":"cluster/setup/executor-setup.html#resource-configuration","title":"Resource Configuration","text":"

This section can be used to configure how resources are exposed from an executor to the cluster. We have discussed a few of the considerations that will drive the configuration that is being setup.

Name Option Description OS Cores osCores A list of cores reserved for use by operating system processes. See the relevant section for details on the pre-steps needed to achieve this. Exposed Memory exposedMemPercentage What percentage of the system memory can be used by the containers running on the host collectively. Range: 50-100 integer NUMA Pinning disableNUMAPinning Disable NUMA and CPU core pinning for containers. Pinning is on by default. (default: false) Nvidia GPU enableNvidiaGpu Enable GPU support on containers. This setting makes all available Nvidia GPUs on the current executor machine available for any container running on this executor. GPU resources are not discovered on the executor, managed and rationed between containers. Needs to be used in conjunction with tagging (see tags below) to ensure only the applications which require a GPU end up on the executor with GPUs. Tags tags A set of strings that can be used in TAG placement policy to route application and task instances to this executor. Over Provisioning overProvisioning Setup over provisioning configuration.

Tagging

The current hostname is always added as a tag by default and is handled specially to allow for non-tagged deployments to be routed to this executor. If any tag is specified in the tags config, this node will receive containers only when MATCH_TAG placement is used. Please check relevant sections to specify correct placement policies for applications and tasks.

Sample

resources:\n  osCores: [0,1,2,3]\n  exposedMemPercentage: 90\n

"},{"location":"cluster/setup/executor-setup.html#over-provisioning-configuration","title":"Over provisioning configuration","text":"

Drove strives to ensure that containers can run unencumbered on CPU cores allocated to them. This means that the minimum allocation unit possible is 1 for cores. It does not support fractional CPU.

However, there are situations where we would want some non-critical applications to run the cluster but not waste CPU. The overProvisioning configuration aims to provide user a way to turn off NUMA pinning on the executor and run more containers than it normally would.

To ensure predictability, we do not want pinned and non-pinned containers running on the same host. Hence, an executor host can either be running in pinned mode or in non-pinned mode.

To enable more containers than we could usually deploy and to still retain some level of control on how small you want a container to go, we specify multipliers on CPU and memory.

Example: - Let's say your executor server has 40 cores available. If you set cpuMultiplier as 4, this node will now show up as having 160 cores to the controller. - Let's say your server had 512GB of memory, setting memoryMultiplier to 2 will make drove see it as 1TB.

Name Option Description Enabled enabled Set this to true to enable over provisioning. Default: false CPU Multiplier cpuMultiplier Multiplier to be applied to enable cpu over provisioning. Default: 1. Range: 1-20 Memory Multiplier memoryMultiplier Multiplier to be applied to enable memory over provisioning. Default: 1. Range: 1-20

Sample

resources:\n  exposedMemPercentage: 90\n  overProvisioning:\n    enabled: true\n    memoryMultiplier: 1\n    cpuMultiplier: 3\n

Tip

This feature was developed to allow us to run our development environments cheaper. In such environments there is not much pressure on CPU or memory, but a large number of containers run as developers can spin up containers for features they are working on. There was no point is wasting a full core on containers that get hit twice a minute or less. On production we tend to err on the side of caution and allocate at least one core even to the most trivial applications as of the time of writing this.

"},{"location":"cluster/setup/executor-setup.html#executor-options","title":"Executor Options","text":"

The following options can be set to influence the behavior for the Drove executors.

Name Option Description Hostname hostname Override the hostname that gets exposed to the controller. Make sure this is resolvable. Cache Images cacheImages Cache container images. If this is not passed, a container image is removed when a container dies and no other instance is using the image. Command Timeout containerCommandTimeout Timeout used by the container engine client when issuing container commands to docker or podman Container Socket Path dockerSocketPath The path of socket for docker socket. Comes in handy to configure path for socket when using podman etc. Max Open Files maxOpenFiles Override the maximum number of file descriptors a container can open. Default: 470,000 Log Buffer Size logBufferSize The size of the buffer the executor uses to read logs from container. Unit DataSize. Range: 1-128MB. Default: 10MB Cache File Size cacheFileSize To limit disk usage, configure fixed size log file cache for containers. Unit: DataSize. Range: 10MB-100GB. Default: 20MB. Compression is always enabled. Cache File Count cacheFileSize To limit disk usage, configure fixed count of log file cache for containers. Unit: integer. Max: 1024. Default: 3

Sample

options:\n  logBufferSize: 20m\n  cacheFileSize: 30m\n  cacheFileCount: 3\n  cacheImages: true\n

"},{"location":"cluster/setup/executor-setup.html#relevant-directories","title":"Relevant directories","text":"

Location for data and logs are as follows:

  • /etc/drove/executor/ - Configuration files
  • /var/log/drove/executor/ - Executor Logs
  • /var/log/drove/executor/instance-logs - Application/Task Instance Logs

We shall be volume mounting the config and log directories with the same name.

Prerequisite Setup

If not done already, please complete the prerequisite setup on all machines earmarked for the cluster.

"},{"location":"cluster/setup/executor-setup.html#setup-the-config-file","title":"Setup the config file","text":"

Create a relevant configuration file in /etc/drove/controller/executor.yml.

Sample

server:\n  applicationConnectors:\n    - type: http\n      port: 11000\n  adminConnectors:\n    - type: http\n      port: 11001\n  requestLog:\n    appenders:\n      - type: file\n        timeZone: IST\n        currentLogFilename: /var/log/drove/executor/drove-executor-access.log\n        archivedLogFilenamePattern: /var/log/drove/executor/drove-executor-access.log-%d-%i\n        archivedFileCount: 3\n        maxFileSize: 100MiB\n\nlogging:\n  level: INFO\n  loggers:\n    com.phonepe.drove: INFO\n\n\n  appenders:\n    - type: file\n      threshold: ALL\n      timeZone: IST\n      currentLogFilename: /var/log/drove/executor/drove-executor.log\n      archivedLogFilenamePattern: /var/log/drove/executor/drove-executor.log-%d-%i\n      archivedFileCount: 3\n      maxFileSize: 100MiB\n      logFormat: \"%(%-5level) [%date] [%logger{0} - %X{appId}] %message%n\"\n    - type: drove\n      logPath: \"/var/log/drove/executor/instance-logs\"\n      archivedLogFileSuffix: \"%d-%i\"\n      archivedFileCount: 0\n      maxFileSize: 1GiB\n      threshold: INFO\n      timeZone: IST\n      logFormat: \"%(%-5level) | %-23date | %-30logger{0} | %message%n\"\n      archive: true\n\nzookeeper:\n  connectionString: \"192.168.56.10:2181\"\n\nclusterAuth:\n  secrets:\n  - nodeType: CONTROLLER\n    secret: \"0v8XvJrDc7r86ZY1QCByPTDPninI4Xii\"\n  - nodeType: EXECUTOR\n    secret: \"pOd9sIEXhv0wrGOVc7ebwNvR7twZqyTN\"\n\nresources:\n  osCores: []\n  exposedMemPercentage: 90\n  disableNUMAPinning: true\n  overProvisioning:\n    enabled: true\n    memoryMultiplier: 10\n    cpuMultiplier: 10\n\noptions:\n  cacheImages: true\n  logBufferSize: 20m\n  cacheFileSize: 30m\n  cacheFileCount: 3\n  cacheImages: true\n

"},{"location":"cluster/setup/executor-setup.html#setup-required-environment-variables","title":"Setup required environment variables","text":"

Environment variables need to run the drove controller are setup in /etc/drove/executor/executor.env.

CONFIG_FILE_PATH=/etc/drove/executor/executor.yml\nJAVA_PROCESS_MIN_HEAP=1g\nJAVA_PROCESS_MAX_HEAP=1g\nZK_CONNECTION_STRING=\"192.168.56.10:2181\"\nJAVA_OPTS=\"-Xlog:gc:/var/log/drove/executor/gc.log -Xlog:gc:::filecount=3,filesize=10M -Xlog:gc::time,level,tags -XX:+UseNUMA -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -Dfile.encoding=utf-8 -Djute.maxbuffer=0x9fffff\"\n
"},{"location":"cluster/setup/executor-setup.html#create-systemd-file","title":"Create systemd file","text":"

Create a systemd file. Put the following in /etc/systemd/system/drove.executor.service:

[Unit]\nDescription=Drove Executor Service\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nUser=drove\nTimeoutStartSec=0\nRestart=always\nExecStartPre=-/usr/bin/docker pull ghcr.io/phonepe/drove-executor:latest\nExecStart=/usr/bin/docker run  \\\n    --env-file /etc/drove/executor/executor.env \\\n    --volume /etc/drove/executor:/etc/drove/executor:ro \\\n    --volume /var/log/drove/executor:/var/log/drove/executor \\\n    --volume /var/run/docker.sock:/var/run/docker.sock \\\n    --publish 11000:11000  \\\n    --publish 11001:11001 \\\n    --hostname %H \\\n    --rm \\\n    --name drove.executor \\\n    ghcr.io/phonepe/drove-executor:latest\n\n[Install]\nWantedBy=multi-user.target\n
Verify the file with the following command:
systemd-analyze verify drove.executor.service\n

Set permissions

chmod 664 /etc/systemd/system/drove.executor.service\n

"},{"location":"cluster/setup/executor-setup.html#start-the-service-on-all-servers","title":"Start the service on all servers","text":"

Use the following to start the service:

systemctl daemon-reload\nsystemctl enable drove.executor\nsystemctl start drove.executor\n

You can tail the logs at /var/logs/drove/executor/drove-executor.log.

The executor should now show up on the Drove Console.

"},{"location":"cluster/setup/gateway.html","title":"Setting up Drove Gateway","text":"

The Drove Gateway works as a gateway to expose apps running on a drove cluster to rest of the world.

Drove Gateway container uses NGinx and a modified version of Nixy to track drove endpoints. More details about this can be found in the drove-gateway project.

"},{"location":"cluster/setup/gateway.html#drove-gateway-nixy-configuration-reference","title":"Drove Gateway Nixy Configuration Reference","text":"

The nixy running inside the gateway container is configured using a custom TOML file. This section looks into this file:

address = \"127.0.0.1\"# (1)!\nport = \"6000\"\n\n\n# Drove Options\ndrove = [#(2)!\n  \"http://controller1.mydomain:10000\",\n   \"http://controller1.mydomain:10000\"\n   ]\n\nleader_vhost = \"drove-staging.mydomain\"#(3)!\nevent_refresh_interval_sec = 5#(5)!\nuser = \"\"#(6)!\npass = \"\"\naccess_token = \"\"#(7)!\n\n# Parameters to control which apps are exposed as VHost\nrouting_tag = \"externally_exposed\"#(4)!\nrealm = \"api.mydomain,support.mydomain\"#(8)!\nrealm_suffix = \"-external.mydomain\"#(9)!\n\n# Nginx related config\n\nnginx_config = \"/etc/nginx/nginx.conf\"#(10)!\nnginx_template = \"/etc/drove/gateway/nginx.tmpl\"#(11)!\nnginx_cmd = \"nginx\"#(12)!\nnginx_ignore_check = true#(13)!\n\n# NGinx plus specific options\nnginxplusapiaddr=\"127.0.0.1\"#(14)!\nnginx_reload_disabled=true#(15)!\nmaxfailsupstream = 0#(16)!\nfailtimeoutupstream = \"1s\"\nslowstartupstream = \"0s\"\n
  1. Nixy listener configuration. Endpoint for nixy itself.

  2. List of Drove controllers. Add all controller nodes here. Nixy will automatically determine and track the current leader.

    Auto detection is disabled if a single endpoint is specified.

  3. Helps create a vhost entry that tracks the leader on the cluster. Use this to expose the Drove endpoint to users. The value for this will be available to the template engine as the LeaderVHost variable.

  4. If some special routing behaviour needs to be implemented in the template based on some tag metadata of the deployed apps, set the routing_tag option to set the tag name to be used. The actual value is derived from app instances and exposed to the template engine as the variable: RoutingTag. Optional.

    In this example, the RoutingTag variable will be set to the value specified in the routing_tag tag key specified when deploying the Drove Application. For example, if we want to expose the app we can set it to yes, and filter the VHost to be exposed in NGinx template when RoutingTag == \"yes\".

  5. Drove Gateway/Nixy works on event polling on controller. This is the polling interval. Especially if number of NGinx nodes is high. Default is 2 seconds. Unless cluster is really busy with a high rate of change of containers, this strikes a good balance between apps becoming discoverable vs putting the leader controller under heavy load.

  6. user and pass are optional params can be used to set basic auth credentials to the calls made to Drove controllers if basic auth is enabled on the cluster. Leave empty if no basic auth is required.

  7. If cluster has some custom header based auth, the following can be used. The contents on this parameter are passed verbatim to the Authorization HTTP header. Leave empty if no token auth is enabled on the cluster.

  8. By default drove-gateway will expose all vhost declared in the spec for all drove apps on a cluster (caveat: filtering can be done using RoutingTag as well). If specific vhosts need to be exposed, set the realms parameter to a comma separated list of realms. Optional.

  9. Beside perfect vhost matching, Drove Gateway supports suffix based matches as well. A single suffix is supported. Optional.

  10. Path to NGinx config.

  11. Path to the template file, based on which the template will be generated.

  12. NGinx command to use to reload the config. Set this to openresty optionally to use openresty.

  13. Ignore calling NGinx command to test the config. Set this to false or delete this line on production. Default: false.

  14. If using NGinx plus, set the endpoint to the local server here. If left empty, NGinx plus api based vhost update will be disabled.

  15. If specific vhosts are exposed, auto-discovery and updation of config (and NGinx reloads) might not be desired as it will cause connection drops. Set the following parameter to true to disable reloads. Nixy will only update upstreams using the nplus APIs. Default: false.

  16. Connection parameters for NGinx plus.

NGinx plus

NGinx plus is not shipped with this docker. If you want to use NGinx plus, please build nixy from the source tree here and build your own container.

"},{"location":"cluster/setup/gateway.html#relevant-directories","title":"Relevant directories","text":"

Location for data and logs are as follows:

  • /etc/drove/gateway/ - Configuration files
  • /var/log/drove/gateway/ - NGinx Logs

We shall be volume mounting the config and log directories with the same name.

Prerequisite Setup

If not done already, please complete the prerequisite setup on all machines earmarked for the cluster.

Go through the following steps to run drove-gateway as a service.

"},{"location":"cluster/setup/gateway.html#create-the-toml-config-for-nixy","title":"Create the TOML config for Nixy","text":"

Sample config file /etc/drove/gateway/gateway.toml:

address = \"127.0.0.1\"\nport = \"6000\"\n\n\n# Drove Options\ndrove = [\n  \"http://controller1.mydomain:10000\",\n   \"http://controller1.mydomain:10000\"\n   ]\n\nleader_vhost = \"drove-staging.mydomain\"\nevent_refresh_interval_sec = 5\nuser = \"guest\"\npass = \"guest\"\n\n\n# Nginx related config\nnginx_config = \"/etc/nginx/nginx.conf\"\nnginx_template = \"/etc/drove/gateway/nginx.tmpl\"\nnginx_cmd = \"nginx\"\nnginx_ignore_check = true\n

Replace domain names

Please remember to update mydomain to a valid domain you want to use.

"},{"location":"cluster/setup/gateway.html#create-template-for-nginx","title":"Create template for NGinx","text":"

Create a NGinx template with the following config in /etc/drove/gateway/nginx.tmpl

# Generated by drove-gateway {{datetime}}\n\nuser www-data;\nworker_processes auto;\npid /run/nginx.pid;\n\nevents {\n    use epoll;\n    worker_connections 2048;\n    multi_accept on;\n}\nhttp {\n    server_names_hash_bucket_size  128;\n    add_header X-Proxy {{ .Xproxy }} always;\n    access_log /var/log/nginx/access.log;\n    error_log /var/log/nginx/error.log warn;\n    server_tokens off;\n    client_max_body_size 128m;\n    proxy_buffer_size 128k;\n    proxy_buffers 4 256k;\n    proxy_busy_buffers_size 256k;\n    proxy_redirect off;\n    map $http_upgrade $connection_upgrade {\n        default upgrade;\n        ''      close;\n    }\n    # time out settings\n    proxy_send_timeout 120;\n    proxy_read_timeout 120;\n    send_timeout 120;\n    keepalive_timeout 10;\n\n    server {\n        listen       7000 default_server;\n        server_name  _;\n        # Everything is a 503\n        location / {\n            return 503;\n        }\n    }\n    {{if and .LeaderVHost .Leader.Endpoint}}\n    upstream {{.LeaderVHost}} {\n        server {{.Leader.Host}}:{{.Leader.Port}};\n    }\n    server {\n        listen 7000;\n        server_name {{.LeaderVHost}};\n        location / {\n            proxy_set_header HOST {{.Leader.Host}};\n            proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;\n            proxy_connect_timeout 30;\n            proxy_http_version 1.1;\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection $connection_upgrade;\n            proxy_pass http://{{.LeaderVHost}};\n        }\n    }\n    {{end}}\n    {{- range $id, $app := .Apps}}\n    upstream {{$app.Vhost}} {\n        {{- range $app.Hosts}}\n        server {{ .Host }}:{{ .Port }};\n        {{- end}}\n    }\n    server {\n        listen 7000;\n        server_name {{$app.Vhost}};\n        location / {\n            proxy_set_header HOST $host;\n            proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;\n            proxy_connect_timeout 30;\n            proxy_http_version 1.1;\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection $connection_upgrade;\n            proxy_pass http://{{$app.Vhost}};\n        }\n    }\n    {{- end}}\n}\n

The above template will do the following:

  • Set NGinx port to 7000. This is the port exposed on the Docker container for the gateway. Do not change this.
  • Sets up error and access logs to /var/log/nginx. Log rotation is setup for this path already.
  • Set up a Vhost drove-staging.mydomain that will get auto-updated with the current leader of the Drove cluster
  • Setup automatically updated virtual hosts for all apps on the cluster.
"},{"location":"cluster/setup/gateway.html#create-environment-file","title":"Create environment file","text":"

We want to configure the drove gateway container using the required environment variables. To do that, put the following in /etc/drove/gateway/gateway.env:

CONFIG_FILE_PATH=/etc/drove/gateway/gateway.toml\nTEMPLATE_FILE_PATH=/etc/drove/gateway/nginx.tmpl\n
"},{"location":"cluster/setup/gateway.html#create-systemd-file","title":"Create systemd file","text":"

Create a systemd file. Put the following in /etc/systemd/system/drove.gateway.service:

[Unit]\nDescription=Drove Gateway Service\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nUser=drove\nTimeoutStartSec=0\nRestart=always\nExecStartPre=-/usr/bin/docker pull ghcr.io/phonepe/drove-gateway:latest\nExecStart=/usr/bin/docker run  \\\n    --env-file /etc/drove/gateway/gateway.env \\\n    --volume /etc/drove/gateway:/etc/drove/gateway:ro \\\n    --volume /var/log/drove/gateway:/var/log/nginx \\\n    --network host \\\n    --hostname %H \\\n    --rm \\\n    --name drove.gateway \\\n    ghcr.io/phonepe/drove-gateway:latest\n\n[Install]\nWantedBy=multi-user.target\n

Verify the file with the following command:

systemd-analyze verify drove.gateway.service\n

Set permissions

chmod 664 /etc/systemd/system/drove.gateway.service\n

"},{"location":"cluster/setup/gateway.html#start-the-service-on-all-servers","title":"Start the service on all servers","text":"

Use the following to start the service:

systemctl daemon-reload\nsystemctl enable drove.gateway\nsystemctl start drove.gateway\n
"},{"location":"cluster/setup/gateway.html#checking-logs","title":"Checking Logs","text":"

You can check logs using:

journalctl -u drove.gateway -f\n

NGinx logs would be available at /var/log/drove/gateway.

"},{"location":"cluster/setup/gateway.html#log-rotation-for-nginx","title":"Log rotation for NGinx","text":"

The gateway sets up log rotation for the access and errors logs with the following config:

/var/log/nginx/*.log {\n    rotate 5\n    size 10M\n    dateext\n    dateformat -%Y-%m-%d\n    missingok\n    compress\n    delaycompress\n    sharedscripts\n    notifempty\n    postrotate\n        test -r /var/run/nginx.pid && kill -USR1 `cat /var/run/nginx.pid`\n    endscript\n}\n

This will rotate both error and access logs when they hit 10MB and keep 5 logs.

Configure the above if you want and volume mount your config to /etc/logrotate.d/nginx to use different scheme as per your requirements.

"},{"location":"cluster/setup/maintenance.html","title":"Maintaining a Drove Cluster","text":"

There are a couple of constructs built into Drove to allow for easy maintenance.

  • Cluster Maintenance Mode
  • Executor node blacklisting
"},{"location":"cluster/setup/maintenance.html#maintenance-mode","title":"Maintenance mode","text":"

Drove supports a maintenance mode to allow for software updates without affecting the containers running on the cluster.

Danger

In maintenance mode, outage detection is turned off and container failure for applications are not acted upon even if detected.

"},{"location":"cluster/setup/maintenance.html#engaging-maintenance-mode","title":"Engaging maintenance mode","text":"

Set cluster to maintenance mode.

Preconditions - Cluster must be in the following state: MAINTENANCE

Drove CLIJSON
drove -c local cluster maintenance-on\n

Sample Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/set' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"state\": \"MAINTENANCE\",\n        \"updated\": 1721630351178\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"cluster/setup/maintenance.html#disengaging-maintenance-mode","title":"Disengaging maintenance mode","text":"

Set cluster to normal mode.

Preconditions - Cluster must be in the following state: MAINTENANCE

Drove CLIJSON
drove -c local cluster maintenance-off\n

Sample Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/maintenance/unset' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"state\": \"NORMAL\",\n        \"updated\": 1721630491296\n    },\n    \"message\": \"success\"\n}\n

"},{"location":"cluster/setup/maintenance.html#updating-drove-version-across-the-cluster-quickly","title":"Updating drove version across the cluster quickly","text":"

We recommend the following sequence of steps:

  1. Find the leader controller for the cluster using drove ... cluster leader.
  2. Update the controller container on the nodes that are not the leader.

    If you are using the systemd file given here, you just need to restart the controller service using systemctl restart drove.controller

  3. Set cluster to maintenance mode using drove ... cluster maintenance-on.

  4. Update the leader controller.

    If you are using the systemd file given here, you just need to restart the leader controller service: systemctl restart drove.controller

  5. Update the executors.

    If you are using the systemd file given here, you just need to restart all executors: systemctl restart drove.executor

  6. Take cluster out of maintenance mode: drove ... cluster maintenance-off

"},{"location":"cluster/setup/maintenance.html#executor-blacklisting","title":"Executor blacklisting","text":"

In cases where we want to take an executor node out of the cluster for planned maintenance, we need to ensure application instances running on the node are replaced by containers on other nodes and the ones running here are shut down cleanly.

This is achieved by blacklisting the node.

Tip

Whenever blacklisting is done, it causes some flux in the application topology due to new container migration from blacklisted to normal nodes. To reduce the number of times this happens, plan to perform multiple operations togeter and blacklist and un-blacklist executors together.

Drove will optimize bulk blacklisting related app migrations and will migrate containers together for an app only once rather than once for every node.

Danger

Task instances are not migrated out. This is because it is impossible for Drove to know if a task can be migrated or not (i.e. killed and spun up on a new node in any order).

To blacklist executors do the following:

Drove CLIJSON
drove -c local executor blacklist dd2cbe76-9f60-3607-b7c1-bfee91c15623 ex1 ex2 \n

Sample Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/blacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d&id=ex1&id=ex2' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"failed\": [\n            \"ex2\",\n            \"ex1\"\n        ],\n        \"successful\": [\n            \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\"\n        ]\n    },\n    \"message\": \"success\"\n}\n

To un-blacklist executors do the following:

Drove CLIJSON
drove -c local executor unblacklist dd2cbe76-9f60-3607-b7c1-bfee91c15623 ex1 ex2 \n

Sample Request

curl --location --request POST 'http://drove.local:7000/apis/v1/cluster/executors/unblacklist?id=a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d&id=ex1&id=ex2' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data ''\n

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"failed\": [\n            \"ex2\",\n            \"ex1\"\n        ],\n        \"successful\": [\n            \"a45442a1-d4d0-3479-ab9e-3ed0aa5f7d2d\"\n        ]\n    },\n    \"message\": \"success\"\n}\n

Note

Drove will not re-evaluate placement of existing Applications in RUNNING state once executors are brought back into rotation.

"},{"location":"cluster/setup/planning.html","title":"Planning your cluster","text":"

Running a drove cluster in production for critical workloads involves planning and preparation on factors like Availability, Scale, Security and Access management. The following issues should be considered while planning your drove cluster.

"},{"location":"cluster/setup/planning.html#criteria-for-planning","title":"Criteria for planning","text":"

The simplest form of a drove cluster would run controller, zookeeper, executor and gateway services all on the same machine while a highly available would separate out all components according to following considerations:

  • Availability: Use of a single node in either executor, controller, gateway or zookeeper has a single point of failure. A typical highly available cluster consists of:
    • Separated controller and executor nodes
    • Multi controller setup - typically minimum 3 controller nodes to provide adequate availability and majority for leader election
    • Multiple gateway nodes to load balance traffic and provide fault tolerance
    • Availability of sufficient executor nodes to follow placement policies suitable for high availability for application instances across executors
    • Multiple node zookeeper cluster to provide adequate availability and quorum. Even number of nodes is not useful. Atleast three zookeeper servers are required.
  • Scale: You should size your components as per expected amount of traffic to your instances but also have a plan for expected demand growth and ways to scale your cluster accordingly
  • Security and Access management: You can use authentication for intra-cluster communication and add encryption for secure communications. Access management can be devolved by creating multiple users with either read or write roles.
"},{"location":"cluster/setup/planning.html#cluster-configuration","title":"Cluster configuration","text":""},{"location":"cluster/setup/planning.html#controllers","title":"Controllers","text":"

Controllers will manage the cluster with application instances spread across multiple executors as per different placement policies. Controllers use leader-election to coordinate and will act as a single entity while each executor acts as a single entity that runs many different application instances.

  • Multiple controllers: For high availability, there should be atleast three controllers. Drove uses leader election to coordinate across the controllers. If the leader fails, the other controllers would elect a leader and takeover.
  • Separate Zookeeper service and snapshots: The zookeeper service used for state coordination by controller can either be run on same machines as controller or on separate machines. Zookeeper stores the configuration data for the whole cluster and zookeeper snapshots can be used to back up the data.
  • Availability zones: You can split the controllers and/or zookeeper nodes across availability zones/data centers to improve resilience.
  • Transit encryption and certificate Management: Controllers and executors can communicate via secure communications. TLS settings and certificates can be added by modifying applicationConnectors and adminConnectors parameters as per Dropwizard HTTPS connector configuration
  • Separate Gateways: The drove gateways will route and load balance application traffic across application instances. The gateway service can either be run on same machines as controller or on separate machines.
  • Resources: The required JVM maximum heap size for drove controller service will increase with the increase in number of executors,applications and application instances in the cluster. The JVM parameters should be reviewed as per your scale.
"},{"location":"cluster/setup/planning.html#zookeeper","title":"Zookeeper","text":"
  • Quorum: For replicated zookeeper, a minimum of three servers are required, and it is recommended that you have an odd number of servers. If you only have two servers, if one of them fails, there are not enough machines to form a majority quorum. Two servers are inherently less stable than a single server, because there are two single points of failure. Refer Running replicated ZooKeeper in ZooKeeper Getting Started Guide
  • zxid rollover: zxid is the ZooKeeper transaction id and is 64 bit number. The zxid has two parts epoch and a counter which use the high order 32-bits for the epoch and the low order 32-bits for the counter. A zookeeper election is forced when the 32-bit counter rolls over. This will be more frequent as scale increases in your cluster.
  • JVM parameters and resources: The required JVM maximum heap size for zookeeper will increase with the increase in number of executors,applications and application instances in the cluster. The JVM parameters should be reviewed as per your scale. Refer ZooKeeper Administrator's Guide
"},{"location":"cluster/setup/planning.html#executors","title":"Executors","text":"
  • Containerisation Engine: Drove supports the Docker engine and has experimental support for Podman. Choose your engine as per your security, networking and performance considerations.
  • Container Networking: The container engine and container networking should be configured as per your requirements. It is recommended to use Port forwarding based container networking if you choose to use Drove Gateway to route application traffic. Container engine settings can be modified to manage DNS and proxy parameters for containers.
  • Placement policies and availability: Drove supports placement policies to set criteria for replication of instances across executors and avoiding single points of failure. Drove tags can be assigned to executors and placement policy can be used to pin certain applications to specific selected executors if you have any hardware or other considerations.
  • Scaling: As your cluster scale increases, you can continue adding executors to the cluster. Placement policies should be used to manage availability criteria. Controller and ZooKeeper resource requirements will increase as your executor count increases and should be reviewed accordingly.
"},{"location":"cluster/setup/planning.html#gateways","title":"Gateways","text":"
  • Container Networking: It is recommended to use Port forwarding based container networking if you choose to use Drove Gateways to route application traffic
  • Load balancing: Gateways use Nginx as a web server and can use many different approaches to load balancing among multiple gateway nodes. Some examples include:
    • DNS Load balancing: Multiple gateway IP's can be added as A records to the virtual host domain to let clients use round-robin DNS and split load across gateway nodes
    • Anycast/Network Load balancing: If any sort of anycast/network load balancing functionality is available in your network, it can be used to split traffic across gateway nodes
  • High Availability and Scaling: Many different methods are available to achieve high availability and scale NGINX. Any method can be used by adequately modifying the template used by Gateway to render Nginx configuration.
"},{"location":"cluster/setup/prerequisites.html","title":"Setting up the prerequisites","text":"

On all machines on the drove cluster, we would want to use the same user and have a consistent storage structure for configuration, logs etc.

Note

All commands o be issues as root. To get to admin/root mode issue the following command:

sudo su\n
"},{"location":"cluster/setup/prerequisites.html#setting-up-user","title":"Setting up user","text":"

We shall create an user called drove to be used to run all services and containers and assign the file ownership to this user.

adduser --system --group \"drove\" --home /var/lib/misc --no-create-home > /dev/null\n
We want to user to be able to run docker containers, so we add the user to the docker group:

groupadd docker\nusermod -aG docker drove\n
"},{"location":"cluster/setup/prerequisites.html#create-directories","title":"Create directories","text":"

We shall use the following locations to store configurations, logs etc:

  • /etc/drove/... - for configuration
  • /var/log/drove/.. - for all logs

We go ahead and create these locations and setup the correct permissions:

mkdir -p /etc/drove\nchown -R drove.drove /etc/drove\nchmod 700 /etc/drove\nchmod g+s /etc/drove\n\nmkdir -p /var/lib/drove\nchown -R drove.drove /var/lib/drove\nchmod 700 /var/lib/drove\n\nmkdir -p /var/log/drove\n

Danger

Ensure you run the chmod commands to remove read access everyone other than the owner.

"},{"location":"cluster/setup/units.html","title":"Units Reference","text":"

In the configuration files for Drove, we use the Duration and DataSize units to make configuration easier.

"},{"location":"cluster/setup/units.html#data-size","title":"Data Size","text":"

Use the following shortcuts to express sizes in human readable form such as 2GB etc:

  • B - Bytes
  • byte - Bytes
  • Bytes - Bytes
  • K - Kilobytes
  • KB - Kilobytes
  • KiB - Kibibytes
  • Kilobyte - Kilobytes
  • kibibyte - Kibibytes
  • KiloBytes - Kilobytes
  • kibiBytes - Kibibytes
  • M - Megabytes
  • MB - Megabytes
  • MiB - Mebibytes
  • megabyte - Megabytes
  • mebibyte - Mebibytes
  • megaBytes - Megabytes
  • mebiBytes - Mebibytes
  • G - Gigabytes
  • GB - Gigabytes
  • GiB - Gibibytes
  • gigabyte - Gigabytes
  • gibibyte - Gibibytes
  • gigaBytes - Gigabytes
  • gibiBytes - Gibibytes
  • T - Terabytes
  • TB - Terabytes
  • TiB - Tebibytes
  • terabyte - Terabytes
  • tebibyte - Tebibytes
  • teraBytes - Terabytes
  • tebiBytes - Tebibytes
  • P - Petabytes
  • PB - Petabytes
  • PiB - Pebibytes
  • petabyte - Petabytes
  • pebibyte - Pebibytes
  • petaBytes - Petabytes
  • pebiBytes - Pebibytes
"},{"location":"cluster/setup/units.html#duration","title":"Duration","text":"

Time durations in Drove can be expressed in human readable form, for example: 3d can be used to signify 3 days and so on. The list of valid duration unit suffixes are:

  • ns - nanoseconds
  • nanosecond - nanoseconds
  • nanoseconds - nanoseconds
  • us - microseconds
  • microsecond - microseconds
  • microseconds - microseconds
  • ms - milliseconds
  • millisecond - milliseconds
  • milliseconds - milliseconds
  • s - seconds
  • second - seconds
  • seconds - seconds
  • m - minutes
  • min - minutes
  • mins - minutes
  • minute - minutes
  • minutes - minutes
  • h - hours
  • hour - hours
  • hours - hours
  • d - days
  • day - days
  • days - days
"},{"location":"cluster/setup/zookeeper.html","title":"Setting Up Zookeeper","text":"

We shall be running Zookeeper using the official Docker images. All data volumes etc will be mounted on the host machines.

The following ports will be exposed:

  • 2181 - This is the main port for ZK clients to connect to the server
  • 2888 - The port used by Zookeeper for in-cluster communications between peers
  • 3888 - Port used for internal leader election
  • 8080 - Admin server port. We are going to turn this off.

Danger

The ZK admin server does not shut down cleanly from time to time. And is not needed for anything related to Drove. If not needed, you should turn it off.

We assume the following to be the IP for the 3 zookeeper nodes:

  • 192.168.3.10
  • 192.168.3.11
  • 192.168.3.12
"},{"location":"cluster/setup/zookeeper.html#relevant-directories","title":"Relevant directories","text":"

Location for data and logs are as follows:

  • /etc/drove/zk - Configuration files
  • /var/lib/drove/zk/ - Data and data logs
  • /var/log/drove/zk - Logs
"},{"location":"cluster/setup/zookeeper.html#important-files","title":"Important files","text":"

The zookeeper container stores snapshots, transaction logs and application logs on /data, /datalog and /logs directories respectively. We shall be volume mounting the following:

  • /var/lib/drove/zk/data to /data on the container
  • /var/lib/drove/zk/datalog to /datalog on the container
  • /var/logs/drove/zk to /logs on the container

Docker will create these directories when container comes up for the first time.

Tip

The zk server id (as set above using the ZOO_MY_ID) can also be set by putting the server number in a file named myid in the /data directory.

Prerequisite Setup

If not done already, lease complete the prerequisite setup on all machines earmarked for the cluster.

"},{"location":"cluster/setup/zookeeper.html#setup-configuration-files","title":"Setup configuration files","text":"

Let's create the config directory:

mkdir -p /etc/drove/zk\n

We shall be creating 3 different configuration files to configure zookeeper:

  • zk.env - Environment variables to be used by zookeeper container
  • java.env - Setup JVM related options
  • logbaxk.xml - Logging configuration
"},{"location":"cluster/setup/zookeeper.html#setup-environment-variables","title":"Setup environment variables","text":"

Let us prepare the configuration. Put the following in a file: /etc/drove/zk/zk.env:

#(1)!\nZOO_TICK_TIME=2000\nZOO_INIT_LIMIT=10\nZOO_SYNC_LIMIT=5\nZOO_STANDALONE_ENABLED=false\nZOO_ADMINSERVER_ENABLED=false\n\n#(2)!\nZOO_AUTOPURGE_PURGEINTERVAL=12\nZOO_AUTOPURGE_SNAPRETAINCOUNT=5\n\n#(3)!\nZOO_MY_ID=1\nZOO_SERVERS=server.1=192.168.3.10:2888:3888;2181 server.2=192.168.3.11:2888:3888;2181 server.3=192.168.3.12:2888:3888;2181\n
  1. This is cluster level configuration to ensure the cluster topology remains stable through minor flaps
  2. This will control how much data we retain
  3. This section needs to change per server. Each server should have a different ZOO_MY_ID set. And the same numbers get referred to in ZOO_SERVERS section.

Warning

  • The ZOO_MY_ID value needs to be different on every server.So it would be:

    • 192.168.3.10 - 1
    • 192.168.3.11 - 2
    • 192.168.3.12 - 3
  • The format for ZOO_SERVERS is server.id=<address1>:<port1>:<port2>[:role];[<client port address>:]<client port>.

Info

Exhaustive set of options can be found on the Official Docker Page.

"},{"location":"cluster/setup/zookeeper.html#setup-jvm-parameters","title":"Setup JVM parameters","text":"

Put the following in /etc/drove/zk/java.env

export SERVER_JVMFLAGS='-Djute.maxbuffer=0x9fffff -Xmx4g -Xms4g -Dfile.encoding=utf-8 -XX:+UseG1GC -XX:+UseNUMA -XX:+ExitOnOutOfMemoryError'\n

Configuring Max Data Size

Drove data per node can get a bit on the larger side from time to time depending on your application configuration. To be on the safe side, we need to increase the maximum data size per node. This is achieved by setting the JVM option -Djute.maxbuffer=0x9fffff on all cluster nodes in Drove. This is 10MB (approx). The actual payload doesn't reach anywhere close. However we shall be picking up payload compression in a future version to stop this variable from needing to be set.

For the Zookeeper Docker, the environment variable SERVER_JVMFLAGS needs to be set to -Djute.maxbuffer=0x9fffff.

Please refer to Zookeeper Advanced Configuration for further properties that can be tuned.

JVM Size

We set 4GB JVM heap size for ZK by adding appropriate options in SERVER_JVMFLAGS. Please make sure you have sized your machines to have 10-16GB of RAM at the very least. Tune the JVM size and machine size according to your needs.

q

JVMFLAGS environment variable

Do not set this variable in zk.env. Couple of reasons:

  • This will affect both the zk server as well as the client.
  • There is an issue and the flag (nor the SERVER_JVMFLAGS) are not used properly by the startup scripts.
"},{"location":"cluster/setup/zookeeper.html#configure-logging","title":"Configure logging","text":"

We want to have physical log files on disk for debugging and audits and want the container to be ephemeral to allow for easy updates etc. To achieve this, put the following in /etc/drove/zk/logback.xml:

<!--\n Copyright 2022 The Apache Software Foundation\n\n Licensed to the Apache Software Foundation (ASF) under one\n or more contributor license agreements.  See the NOTICE file\n distributed with this work for additional information\n regarding copyright ownership.  The ASF licenses this file\n to you under the Apache License, Version 2.0 (the\n \"License\"); you may not use this file except in compliance\n with the License.  You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n Define some default values that can be overridden by system properties\n-->\n<configuration>\n  <!-- Uncomment this if you would like to expose Logback JMX beans -->\n  <!--jmxConfigurator /-->\n\n  <property name=\"zookeeper.console.threshold\" value=\"INFO\" />\n\n  <property name=\"zookeeper.log.dir\" value=\"/logs\" />\n  <property name=\"zookeeper.log.file\" value=\"zookeeper.log\" />\n  <property name=\"zookeeper.log.threshold\" value=\"INFO\" />\n  <property name=\"zookeeper.log.maxfilesize\" value=\"256MB\" />\n  <property name=\"zookeeper.log.maxbackupindex\" value=\"20\" />\n\n  <!--\n    console\n    Add \"console\" to root logger if you want to use this\n  -->\n  <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n    <encoder>\n      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>\n    </encoder>\n    <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n      <level>${zookeeper.console.threshold}</level>\n    </filter>\n  </appender>\n\n  <!--\n    Add ROLLINGFILE to root logger to get log file output\n  -->\n  <appender name=\"ROLLINGFILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n    <File>${zookeeper.log.dir}/${zookeeper.log.file}</File>\n    <encoder>\n      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>\n    </encoder>\n    <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n      <level>${zookeeper.log.threshold}</level>\n    </filter>\n    <rollingPolicy class=\"ch.qos.logback.core.rolling.FixedWindowRollingPolicy\">\n      <maxIndex>${zookeeper.log.maxbackupindex}</maxIndex>\n      <FileNamePattern>${zookeeper.log.dir}/${zookeeper.log.file}.%i</FileNamePattern>\n    </rollingPolicy>\n    <triggeringPolicy class=\"ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy\">\n      <MaxFileSize>${zookeeper.log.maxfilesize}</MaxFileSize>\n    </triggeringPolicy>\n  </appender>\n\n  <!--\n    Add TRACEFILE to root logger to get log file output\n    Log TRACE level and above messages to a log file\n  -->\n  <!--property name=\"zookeeper.tracelog.dir\" value=\"${zookeeper.log.dir}\" />\n  <property name=\"zookeeper.tracelog.file\" value=\"zookeeper_trace.log\" />\n  <appender name=\"TRACEFILE\" class=\"ch.qos.logback.core.FileAppender\">\n    <File>${zookeeper.tracelog.dir}/${zookeeper.tracelog.file}</File>\n    <encoder>\n      <pattern>%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n</pattern>\n    </encoder>\n    <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n      <level>TRACE</level>\n    </filter>\n  </appender-->\n\n  <!--\n    zk audit logging\n  -->\n  <property name=\"zookeeper.auditlog.file\" value=\"zookeeper_audit.log\" />\n  <property name=\"zookeeper.auditlog.threshold\" value=\"INFO\" />\n  <property name=\"audit.logger\" value=\"INFO, RFAAUDIT\" />\n\n  <appender name=\"RFAAUDIT\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n    <File>${zookeeper.log.dir}/${zookeeper.auditlog.file}</File>\n    <encoder>\n      <pattern>%d{ISO8601} %p %c{2}: %m%n</pattern>\n    </encoder>\n    <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n      <level>${zookeeper.auditlog.threshold}</level>\n    </filter>\n    <rollingPolicy class=\"ch.qos.logback.core.rolling.FixedWindowRollingPolicy\">\n      <maxIndex>10</maxIndex>\n      <FileNamePattern>${zookeeper.log.dir}/${zookeeper.auditlog.file}.%i</FileNamePattern>\n    </rollingPolicy>\n    <triggeringPolicy class=\"ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy\">\n      <MaxFileSize>10MB</MaxFileSize>\n    </triggeringPolicy>\n  </appender>\n\n  <logger name=\"org.apache.zookeeper.audit.Slf4jAuditLogger\" additivity=\"false\" level=\"${audit.logger}\">\n    <appender-ref ref=\"RFAAUDIT\" />\n  </logger>\n\n  <root level=\"INFO\">\n    <appender-ref ref=\"CONSOLE\" />\n    <appender-ref ref=\"ROLLINGFILE\" />\n  </root>\n</configuration>\n

Tip

This is a customization of the original file from Zookeeper source tree. Please refer to documentation to configure logging.

"},{"location":"cluster/setup/zookeeper.html#create-systemd-file","title":"Create Systemd File","text":"

Create a systemd file. Put the following in /etc/systemd/system/drove.zookeeper.service:

[Unit]\nDescription=Drove Zookeeper Service\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nUser=drove\nTimeoutStartSec=0\nRestart=always\nExecStartPre=-/usr/bin/docker pull zookeeper:3.8\nExecStart=/usr/bin/docker run \\\n    --env-file /etc/drove/zk/zk.env \\\n    --volume /var/lib/drove/zk/data:/data \\\n    --volume /var/lib/drove/zk/datalog:/datalog \\\n    --volume /var/log/drove/zk:/logs \\\n    --volume /etc/drove/zk/logback.xml:/conf/logback.xml \\\n    --volume /etc/drove/zk/java.env:/conf/java.env \\\n    --publish 2181:2181 \\\n    --publish 2888:2888 \\\n    --publish 3888:3888 \\\n    --rm \\\n    --name drove.zookeeper \\\n    zookeeper:3.8\n\n[Install]\nWantedBy=multi-user.target\n

Verify the file with the following command:

systemd-analyze verify drove.zookeeper.service\n

Set permissions

chmod 664 /etc/systemd/system/drove.zookeeper.service\n

"},{"location":"cluster/setup/zookeeper.html#start-the-service-on-all-servers","title":"Start the service on all servers","text":"

Use the following to start the service:

systemctl daemon-reload\nsystemctl enable drove.zookeeper\nsystemctl start drove.zookeeper\n

You can check server status using the following:

echo srvr | nc localhost 2181\n

Tip

Replace localhost on the above command with the actual ZK server IPs to test remote connectivity.

Note

You can access the ZK client from the container using the following command:

docker exec -it drove.zookeeper bin/zkCli.sh\n

To connect to remote host you can use the following:

docker exec -it drove.zookeeper bin/zkCli.sh -server <server name or ip>:2181\n

"},{"location":"extra/cli.html","title":"Drove CLI","text":"

Details for the Drove CLI, including installation and usage can be found in the cli repo.

Repo link: https://github.com/PhonePe/drove-cli.

"},{"location":"extra/epoch.html","title":"Epoch","text":"

Epoch is a cron type scheduler that spins up container jobs on Drove.

Details for using epoch can be found in the epoch repo.

Link for Epoch repo: https://github.com/PhonePe/epoch.

"},{"location":"extra/epoch.html#epoch-cli","title":"Epoch CLI","text":"

There is a cli client for interaction with epoch. Details for installation and usage can be found in the epoch CLI repo.

Link for Epoch CLI repo: https://github.com/phonepe/epoch-cli.

"},{"location":"extra/libraries.html","title":"Libraries","text":"

Drove is written in Java. We provide a few libraries that can be used to integrate with a Drove cluster.

"},{"location":"extra/libraries.html#setup","title":"Setup","text":"

Setup the drove version

<properties>\n    <!--other properties-->\n    <drove.version>1.31</drove.version>\n</properties>\n

Checking the latest version

Latest version can be checked at the github packages page here

All libraries are located in sub packages of the top level package com.phonepe.drove.

Java Version Compatibility

Using Drove libraries would need Java versions 17+.

"},{"location":"extra/libraries.html#drove-model","title":"Drove Model","text":"

The model library for the classes used in request and response. It has dependency on jackson and dropwizard-validation.

"},{"location":"extra/libraries.html#dependency","title":"Dependency","text":"
<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-models</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#drove-client","title":"Drove Client","text":"

We provide a client library that can be used to connect to a Drove cluster. The cluster accepts controller endpoints as parameter (among other things) and automatically tracks the leader controller. If a single controller endpoint is provided, this functionality is turned off.

Please note that the client does not provide specific functions corresponding to different api calls from the controller, it acts as a simple endpoint discovery mechanism for drove cluster. Please refer to API section for details on individual apis.

"},{"location":"extra/libraries.html#transport","title":"Transport","text":"

The transport layer in the client is used to actually make HTTP calls to the Drove server. A new transport can be used by implementing the get(), post(), put() and delete() methods in the DroveHttpTransport interface.

By default Drove client uses Java internal HTTP client as a trivial transport implementation. We also provide an Apache Http Components based implementation.

Tip

Do not use the default transport in production. Please use the HTTP Components based transport or your custom ones.

"},{"location":"extra/libraries.html#dependencies","title":"Dependencies","text":"
 <dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-client</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-client-httpcomponent-transport</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#sample-code","title":"Sample code","text":"
public class DroveCluster implements AutoCloseable {\n\n    @Getter\n    private final DroveClient droveClient;\n\n    public DroveCluster() {\n        final var config = new DroveConfig()\n            .setEndpoints(List.of(\"http://controller1:4000,http://controller2:4000\"));\n\n        this.droveClient = new DroveClient(config,\n                                      List.of(new BasicAuthDecorator(\"guest\", \"guest\")),\n                                           new DroveHttpComponentsTransport(config.getCluster()));\n    }\n\n    @Override\n    public void close() throws Exception {\n        this.droveClient.close();\n    }\n}\n

RequestDecorator

This interface can be implemented to augment requests with special headers like for example Authorization, as well as for other stuff like adding content type etc etc.

"},{"location":"extra/libraries.html#drove-event-listener","title":"Drove Event Listener","text":"

This library provides callbacks that can be used to listen and react to events happening on the Drove cluster.

"},{"location":"extra/libraries.html#dependencies_1","title":"Dependencies","text":"
<!--Include Drove client-->\n<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-events-client</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#sample-code_1","title":"Sample Code","text":"
final var droveClient = ... //build your java transport, client here\n\n//Create and setup your object mapper\nfinal var mapper = new ObjectMapper();\nmapper.registerModule(new ParameterNamesModule());\nmapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);\nmapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);\nmapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);\nmapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);\nmapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);\n\nfinal var listener = new DroveRemoteEventListener(droveClient, //Create listener\n                                                    mapper,\n                                                    new DroveEventPollingOffsetInMemoryStore(),\n                                                    Duration.ofSeconds(1));\n\nlistener.onEventReceived() //Connect signal handlers\n    .connect(events -> {\n        log.info(\"Remote Events: {}\", events);\n    });\n\nlistener.start(); //Start listening\n\n\n//Once done close the listener\nlistener.close();\n

Event Types

Please check the com.phonepe.drove.models.events package for the different event types and classes.

Event Polling Offset Store

The event poller library uses polling to find new events based on an offset. The event polling offset store is used to store and retrieve this offset. The DroveEventPollingOffsetInMemoryStore default store stores this information in-memory. Implement DroveEventPollingOffsetStore to a more permanent storage if you want this to be more permanent.

"},{"location":"extra/libraries.html#drove-hazelcast-cluster-discovery","title":"Drove Hazelcast Cluster Discovery","text":"

Drove provides an implementation of the Hazelcast discovery SPI so that containers deployed on a drove cluster can discover each other. This client uses the token injected by drove in the DROVE_APP_INSTANCE_AUTH_TOKEN environment variable to get sibling information from the controller.

"},{"location":"extra/libraries.html#dependencies_2","title":"Dependencies","text":"
<!--Include Drove client-->\n<!--Include Hazelcast-->\n<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-events-client</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#sample-code_2","title":"Sample Code","text":"
//Setup hazelcast\nConfig config = new Config();\n\n// Enable discovery\nconfig.setProperty(\"hazelcast.discovery.enabled\", \"true\");\nconfig.setProperty(\"hazelcast.discovery.public.ip.enabled\", \"true\");\nconfig.setProperty(\"hazelcast.socket.client.bind.any\", \"true\");\nconfig.setProperty(\"hazelcast.socket.bind.any\", \"false\");\n\n//Setup networking\nNetworkConfig networkConfig = config.getNetworkConfig();\nnetworkConfig.getInterfaces().addInterface(\"0.0.0.0\").setEnabled(true);\nnetworkConfig.setPort(port); //Port is the port exposed on the container for hazelcast clustering\n\n// Setup Drove discovery\nJoinConfig joinConfig = networkConfig.getJoin();\n\nDiscoveryConfig discoveryConfig = joinConfig.getDiscoveryConfig();\nDiscoveryStrategyConfig discoveryStrategyConfig =\n        new DiscoveryStrategyConfig(new DroveDiscoveryStrategyFactory());\ndiscoveryStrategyConfig.addProperty(\"drove-endpoint\", \"http://controller1:4000,http://controller2:4000\"); //Controller endpoints\ndiscoveryStrategyConfig.addProperty(\"port-name\", \"hazelcast\"); // Name of the hazelcast port defined in Application spec\ndiscoveryStrategyConfig.addProperty(\"transport\", \"com.phonepe.drove.client.transport.httpcomponent.DroveHttpComponentsTransport\");\ndiscoveryStrategyConfig.addProperty(\"cluster-by-app-name\", true); //Cluster container across multiple app versions\ndiscoveryConfig.addDiscoveryStrategyConfig(discoveryStrategyConfig);\n\n//Create hazelcast node\nval node = Hazelcast.newHazelcastInstance(config);\n\n//Once connected, node.getCluster() will be non null\n

Peer discovery modes

By default the containers will only discover and connect to containers from the same application id. If you need to connect to containers from all versions of the same application please set the cluster-by-app-name property to true as in the above example.

"},{"location":"extra/libraries.html#drove-apache-ignite-discovery","title":"Drove Apache Ignite Discovery","text":"

Drove provides an implementation of the apache ignite discovery so that containers deployed on a drove cluster can discover each other. This client uses the token injected by drove in the DROVE_APP_INSTANCE_AUTH_TOKEN environment variable to get sibling information from the controller.

"},{"location":"extra/libraries.html#dependencies_3","title":"Dependencies","text":"
<!--Include Drove client-->\n<!--Include apache ignite-->\n<dependency>\n    <groupId>com.phonepe.drove</groupId>\n    <artifactId>drove-ignite-discovery</artifactId>\n    <version>${drove.version}</version>\n</dependency>\n
"},{"location":"extra/libraries.html#sample-code_3","title":"Sample Code","text":"
//Setup ignite\nIgniteConfigProvider igniteConfigProvider = new IgniteConfigProvider();\n\nIgniteConfiguration igniteConfiguration = igniteConfigProvider.provideIgniteConfiguration(DroveIgniteConfig.builder()\n        .communicationPortName(\"igniteComm\") // Communication port name\n        .droveEndpoint(\"http://controller1:4000,http://controller2:4000\") //Controller endpoints\n        .useAppNameForDiscovery(true) //Cluster container across multiple app versions\n        .discoveryPortName(\"igniteDiscovery\") // Discovery port name\n        .build());\n\n// Start ignite\nIgnite ignite = Ignition.start(configuration);\n

Peer discovery modes

By default the containers will only discover and connect to containers from the same application id. If you need to connect to containers from all versions of the same application please set the cluster-by-app-name property to true as in the above example.

"},{"location":"extra/nvidia.html","title":"Setting up Nvidia GPU computation on executor","text":"

Prerequisite: Docker version 19.0.3+. Check Docker versions and nvidia for details.

Below steps are for ubuntu primarily for other distros check the associated links.

"},{"location":"extra/nvidia.html#install-nvidia-drivers-on-hosts","title":"Install nvidia drivers on hosts","text":"

Ubuntu provides packaged drivers for nvidia. Driver installation Guide

Recommended

ubuntu-drivers list --gpgpu\nubuntu-drivers install --gpgpu nvidia:535-server\n

Alternatively apt can be used, but may require additional steps Manual install

# Check for the latest stable version \napt search nvidia-driver.*server\napt install -y nvidia-driver-535-server  nvidia-utils-535-server \n

For other distros check Guide

"},{"location":"extra/nvidia.html#install-nvidia-container-toolkit","title":"Install Nvidia-container-toolkit","text":"

Add nvidia repo

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg   && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list |     sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' |     sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list\n\napt install -y nvidia-container-toolkit\n
For other distros check guide here

Configure docker with nvidia toolkit

nvidia-ctk runtime configure --runtime=docker\n\nsystemctl restart docker #Restart Docker\n
"},{"location":"extra/nvidia.html#verify-installation","title":"Verify installation","text":"

On Host nvidia-smi -l In docker container docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi

+-----------------------------------------------------------------------------+\n| NVIDIA-SMI 535.86.10    Driver Version: 535.86.10    CUDA Version: 12.2     |\n|-------------------------------+----------------------+----------------------+\n| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |\n| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |\n|                               |                      |               MIG M. |\n|===============================+======================+======================|\n|   0  Tesla T4            On   | 00000000:00:1E.0 Off |                    0 |\n| N/A   34C    P8     9W /  70W |      0MiB / 15109MiB |      0%      Default |\n|                               |                      |                  N/A |\n+-------------------------------+----------------------+----------------------+\n\n+-----------------------------------------------------------------------------+\n| Processes:                                                                  |\n|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |\n|        ID   ID                                                   Usage      |\n|=============================================================================|\n|  No running processes found                                                 |\n+-----------------------------------------------------------------------------+\n
Verification guide

"},{"location":"extra/nvidia.html#enable-nvidia-support-on-drove","title":"Enable nvidia support on drove","text":"

Enable Nvidia support in drove-executor.yml and restart drove-executor

...\nresources:\n  ...\n  enableNvidiaGpu: true\n...\n

"},{"location":"tasks/index.html","title":"Introduction","text":"

A task is a representation for transient containerized workloads on the cluster. A task instance is supposed to have a much shorter life-time than an application instance. Use tasks to spin up things like automation scripts etc.

"},{"location":"tasks/index.html#primary-differences-with-an-application","title":"Primary differences with an application","text":"

Please note the following important differences between a task instance and application instances

  • Tasks cannot expose ports and virtual hosts for incoming traffic
  • There are no readiness checks, health checks or shutdown hooks for a task
  • Task instances cannot be scaled up or down
  • Tasks cannot be restarted
  • A task is typically owned by an application running on the cluster.
  • Task instances are not replaced if the corresponding executor node goes down during execution
  • Unlike applications there is no task + task instance, the only representation of task on a Drove cluster is a task instance.

Tip

Use epoch to spin up tasks in a periodic manner

A task specification contains the following sections:

  • Source App Name - Name of the application that created this task
  • Task ID - User supplied task ID unique in the same sourceAppName scope
  • Executable - The container to deploy on the cluster
  • Resources - CPU and Memory required for the container
  • Placement Policy - How containers are to be placed in the cluster
  • Environment Variables - Environment variables and values
  • Volumes - Volumes to be mounted into the container
  • Configs - Configs/files to be mounted into the container
  • Logging details - Logging spec (for example rsyslog server)
  • Tags - A map of strings for additional metadata
"},{"location":"tasks/index.html#task-id","title":"Task ID","text":"

Identification of a task is a bit more complicated on Drove. There is a Task ID ({sourceAppName}-{taskId}) which is used internally in drove. This is returned to the client when task is created.

However, clients are supposed to use the {sourceAppName,taskId} combo they have sent in the task spec to address and send commands to their tasks.

"},{"location":"tasks/index.html#task-states-and-operations","title":"Task States and operations","text":"

Tasks on Drove have their own life cycle modelled as a state machine. State transitions can be triggered by issuing operations using the APIs.

"},{"location":"tasks/index.html#states","title":"States","text":"

Tasks on a Drove cluster can be one of the following states:

  • PENDING - Task has been submitted, yet to be provisioned
  • PROVISIONING - Task is assigned to an executor and docker image i ng -ownloaded
  • PROVISIONING_FAILED - Docker image download failed
  • STARTING - Docker run is starting
  • RUNNING - Task is running currently
  • RUN_COMPLETED - Task run has completed. Whether passed or failed n -o be checked from the task result.
  • DEPROVISIONING - Docker image cleanup underway
  • STOPPED - Task cleanup completed. This is a terminal state.
  • LOST - Task disappeared while executor was down.
  • UNKNOWN - All tasks that are running are put in this state when executor has been restarted and startup recovery has not kicked in yet
"},{"location":"tasks/index.html#operations","title":"Operations","text":"

The following task operations are recognized by Drove:

  • CREATE - Create a task. The Task definition/spec is provided as an argument to this.
  • KILL - Kill a task. The task ID is taken as a parameter.

Tip

All operations need Cluster Operation Spec which can be used to control the timeout and parallelism of tasks generated by the operation.

"},{"location":"tasks/index.html#task-state-machine","title":"Task State Machine","text":"

The following state machine signifies the states and transitions as affected by cluster state and operations issued.

"},{"location":"tasks/operations.html","title":"Task Operations","text":"

This page discusses operations relevant to Task management. Please go over the Task State Machine to understand the different states a task can be in and how operations applied (and external changes) move a task from one state to another.

Note

Please go through Cluster Op Spec to understand the operation parameters being sent.

For tasks only the timeout parameter is relevant.

Note

Only one operation can be active on a particular task identified by a {sourceAppName,taskId} at a time.

Warning

Only the leader controller will accept and process operations. To avoid confusion, use the controller endpoint exposed by Drove Gateway to issue commands.

"},{"location":"tasks/operations.html#cluster-operation-specification","title":"Cluster Operation Specification","text":"

When an operation is submitted to the cluster, a cluster op spec needs to be specified. This is needed to control different aspects of the operation, including parallelism of an operation or increase the timeout for the operation and so on.

The following aspects of an operation can be configured:

Name Option Description Timeout timeout The duration after which Drove considers the operation to have timed out. Parallelism parallelism Parallelism of the task. (Range: 1-32) Failure Strategy failureStrategy Set this to STOP.

Note

For internal recovery operations, Drove generates it's own operations. For that, Drove applies the following cluster operation spec:

  • timeout - 300 seconds
  • parallelism - 1
  • failureStrategy - STOP

The default operation spec can be configured in the controller configuration file. It is recommended to set this to a something like 8 for faster recovery.

"},{"location":"tasks/operations.html#how-to-initiate-an-operation","title":"How to initiate an operation","text":"

Tip

Use the Drove CLI to perform all manual operations.

All operations for task lifecycle management need to be issued via a POST HTTP call to the leader controller endpoint on the path /apis/v1/tasks/operations. API will return HTTP OK/200 and relevant json response as payload.

Sample api call:

curl --location 'http://drove.local:7000/apis/v1/tasks/operations' \\\n--header 'Content-Type: application/json' \\\n--header 'Authorization: Basic YWRtaW46YWRtaW4=' \\\n--data '{\n    \"type\": \"KILL\",\n    \"sourceAppName\" : \"TEST_APP\",\n    \"taskId\" : \"T0012\",\n    \"opSpec\": {\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}'\n

Note

In the above examples, http://drove.local:7000 is the endpoint of the leader. TEST_APP is the name of the application that started this task and taskId is a unique client generated id. Authorization is basic auth.

Warning

Task operations are not cancellable.

"},{"location":"tasks/operations.html#create-a-task","title":"Create a task","text":"

A task can be created issuing the following command.

Preconditions: - Task with same {sourceAppName,taskId} should not exist on the cluster.

State Transition:

  • none \u2192 PENDING \u2192 PROVISIONING \u2192 STARTING \u2192 RUNNING \u2192 RUN_COMPLETED \u2192 DEPROVISIONING \u2192 STOPPED

To create a task a Task Spec needs to be created first.

Once ready, CLI command needs to be issued or the following payload needs to be sent:

Drove CLIJSON
drove -c local tasks create sample/test_task.json\n

Sample Request Payload

{\n    \"type\": \"CREATE\",\n    \"spec\": {...}, //(1)!\n    \"opSpec\": { //(2)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Spec as mentioned in Task Specification
  2. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"taskId\": \"TEST_APP-T0012\"\n    },\n    \"message\": \"success\"\n}\n

Warning

There are no separate create/run steps in a task. Creation will start execution automatically and immediately.

"},{"location":"tasks/operations.html#kill-a-task","title":"Kill a task","text":"

A task can be created issuing the following command.

Preconditions: - Task with same {sourceAppName,taskId} needs to exist on the cluster.

State Transition:

  • RUNNING \u2192 RUN_COMPLETED \u2192 DEPROVISIONING \u2192 STOPPED

CLI command needs to be issued or the following payload needs to be sent:

Drove CLIJSON
drove -c local tasks kill TEST_APP T0012\n

Sample Request Payload

{\n    \"type\": \"KILL\",\n    \"sourceAppName\" : \"TEST_APP\",//(1)!\n    \"taskId\" : \"T0012\",//(2)!\n    \"opSpec\": {//(3)!\n        \"timeout\": \"5m\",\n        \"parallelism\": 1,\n        \"failureStrategy\": \"STOP\"\n    }\n}\n

  1. Source app name as mentioned in spec during task creation
  2. Task ID as mentioned in the spec
  3. Operation spec as mentioned in Cluster Op Spec

Sample response

{\n    \"status\": \"SUCCESS\",\n    \"data\": {\n        \"taskId\": \"T0012\"\n    },\n    \"message\": \"success\"\n}\n

Note

Task metadata will remain on the cluster for some time. Metadata cleanup for tasks is automatic and can be configured in the controller configuration.

"},{"location":"tasks/specification.html","title":"Task Specification","text":"

A task is defined using JSON. We use a sample configuration below to explain the options.

"},{"location":"tasks/specification.html#sample-task-definition","title":"Sample Task Definition","text":"
{\n    \"sourceAppName\": \"TEST_APP\",//(1)!\n    \"taskId\": \"T0012\",//(2)!\n    \"executable\": {//(3)!\n        \"type\": \"DOCKER\", // (4)!\n        \"url\": \"ghcr.io/appform-io/test-task\",//(5)!\n        \"dockerPullTimeout\": \"100 seconds\"//(6)!\n    },\n     \"resources\": [//(7)!\n        {\n            \"type\": \"CPU\",\n            \"count\": 1//(8)!\n        },\n        {\n            \"type\": \"MEMORY\",\n            \"sizeInMB\": 128//(9)!\n        }\n    ],\n    \"volumes\": [//(10)!\n        {\n            \"pathInContainer\": \"/data\",//(11)!\n            \"pathOnHost\": \"/mnt/datavol\",//(12)!\n            \"mode\" : \"READ_WRITE\"//(13)!\n        }\n    ],\n    \"configs\" : [//(14)!\n        {\n            \"type\" : \"INLINE\",//(15)!\n            \"localFilename\": \"/testfiles/drove.txt\",//(16)!\n            \"data\" : \"RHJvdmUgdGVzdA==\"//(17)!\n        }\n    ],\n    \"placementPolicy\": {//(18)!\n        \"type\": \"ANY\"//(19)!\n    },\n    \"env\": {//(20)!\n        \"CORES\": \"8\"\n    },\n    \"args\" : [] //(27)!\n    \"tags\": { //(21)!\n        \"superSpecialApp\": \"yes_i_am\",\n        \"say_my_name\": \"heisenberg\"\n    },\n    \"logging\": {//(22)!\n        \"type\": \"LOCAL\",//(23)!\n        \"maxSize\": \"100m\",//(24)!\n        \"maxFiles\": 3,//(25)!\n        \"compress\": true//(26)!\n    }\n}\n
  1. Name of the application that has started the task. Make sure this is a valid application on the cluster.
  2. An unique ID for this task. Uniqueness is up to the user, Drove will scope it in the sourceAppName namespace.
  3. Coordinates for the executable. Refer to Executable Specification for details.
  4. Right now the only type supported is DOCKER.
  5. Docker container address
  6. Timeout for container pull.
  7. Volumes to be mounted. Refer to Volume Specification for details.
  8. Path that will be visible inside the container for this mount.
  9. Actual path on the host machine for the mount.
  10. Mount mode can be READ_WRITE and READ_ONLY
  11. Configuration to be injected as file inside the container. Please refer Config Specification for details.
  12. Type of config. Can be INLINE, EXECUTOR_LOCAL_FILE, ONTROLLER_HTTP_FETCHandEXECUTOR_HTTP_FETCH`. Specifies how drove will t the contents to be injected..
  13. File name for the config inside the container.
  14. Serialized form of the data, this and other parameters will vary cording to the type specified above.
  15. List of resources required to run this application. Check Resource Requirements Specification for more tails.
  16. Number of CPU cores to be allocated.
  17. Amount of memory to be allocated expressed in Megabytes
  18. Specifies how the container will be placed on the cluster. Check Placement Policy for details.
  19. Type of placement can be ANY, ONE_PER_HOST, MATCH_TAG, NO_TAG, RULE_BASED, ANY and COMPOSITE. Rest of the parameters in this section will depend on the type.
  20. Custom environment variables. Additional variables are injected by Drove as well. See Environment Variables section for tails.
  21. Key value metadata that can be used in external systems.
  22. Specify how docker log files are configured. Refer to Logging Specification
  23. Log to local file
  24. Maximum File Size
  25. Number of latest log files to retain
  26. Log files will be compressed
  27. List of command line arguments. See Command Line Arguments for details.

Warning

Please make sure sourceAppName is set to a correct application name as specified in the name parameter of a running application on the cluster.

If this is not done, stale task metadata will not be cleaned up and your metadata store performance will get affected over time.

"},{"location":"tasks/specification.html#executable-specification","title":"Executable Specification","text":"

Right now Drove supports only docker containers. However as engines, both docker and podman are supported. Drove executors will fetch the executable directly from the registry based on the configuration provided.

Name Option Description Type type Set type to DOCKER. URL url Docker container URL`. Timeout dockerPullTimeout Timeout for docker image pull.

Note

Drove supports docker registry authentication. This can be configured in the executor configuration file.

"},{"location":"tasks/specification.html#resource-requirements-specification","title":"Resource Requirements Specification","text":"

This section specifies the hardware resources required to run the container. Right now only CPU and MEMORY are supported as resource types that can be reserved for a container.

"},{"location":"tasks/specification.html#cpu-requirements","title":"CPU Requirements","text":"

Specifies number of cores to be assigned to the container.

Name Option Description Type type Set type to CPU for this. Count count Number of cores to be assigned."},{"location":"tasks/specification.html#memory-requirements","title":"Memory Requirements","text":"

Specifies amount of memory to be allocated to a container.

Name Option Description Type type Set type to MEMORY for this. Count sizeInMB Amount of memory (in Mega Bytes) to be allocated.

Sample

[\n    {\n        \"type\": \"CPU\",\n        \"count\": 1\n    },\n    {\n        \"type\": \"MEMORY\",\n        \"sizeInMB\": 128\n    }\n]\n

Note

Both CPU and MEMORY configurations are mandatory.

"},{"location":"tasks/specification.html#volume-specification","title":"Volume Specification","text":"

Files and directories can be mounted from the executor host into the container. The volumes section contains a list of volumes that need to be mounted.

Name Option Description Path In Container pathInContainer Path that will be visible inside the container for this mount. Path On Host pathOnHost Actual path on the host machine for the mount. Mount Mode mode Mount mode can be READ_WRITE and READ_ONLY to allow the containerized process to write or read to the volume.

Info

We do not support mounting remote volumes as of now.

"},{"location":"tasks/specification.html#config-specification","title":"Config Specification","text":"

Drove supports injection of configuration files into containers. The specifications for the same are discussed below.

"},{"location":"tasks/specification.html#inline-config","title":"Inline config","text":"

Inline configuration can be added in the Application Specification itself. This will manifest as a file inside the container.

The following details are needed for this:

Name Option Description Type type Set the value to INLINE Local Filename localFilename File name for the config inside the container. Data data Base64 encoded string for the data. The value for this will be masked on UI.

Config file:

port: 8080\nlogLevel: DEBUG\n
Corresponding config specification:
{\n    \"type\" : \"INLINE\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"data\" : \"cG9ydDogODA4MApsb2dMZXZlbDogREVCVUcK\"\n}\n

Warning

The full base 64 encoded config data will get stored in Drove ZK and will be pushed to executors inline. It is not recommended to stream large config files to containers using this method. This will probably need additional configuration on your ZK cluster.

"},{"location":"tasks/specification.html#locally-loaded-config","title":"Locally loaded config","text":"

Config file from a path on the executor directly. Such files can be distributed to the executor host using existing configuration management systems such as OpenTofu, Salt etc.

The following details are needed for this:

Name Option Description Type type Set the value to EXECUTOR_LOCAL_FILE Local Filename localFilename File name for the config inside the container. File path filePathOnHost Path to the config file on executor host.

Sample config specification:

{\n    \"type\" : \"EXECUTOR_LOCAL_FILE\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"data\" : \"/mnt/configs/myservice/config.yml\"\n}\n

"},{"location":"tasks/specification.html#controller-fetched-config","title":"Controller fetched Config","text":"

Config file can be fetched from a remote server by the controller. Once fetched, these will be streamed to the executor as part of the instance specification for starting a container.

The following details are needed for this:

Name Option Description Type type Set the value to CONTROLLER_HTTP_FETCH Local Filename localFilename File name for the config inside the container. HTTP Call Details http HTTP Call related details. Please refer to HTTP Call Specification for details.

Sample config specification:

{\n    \"type\" : \"CONTROLLER_HTTP_FETCH\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"http\" : {\n        \"protocol\" : \"HTTP\",\n        \"hostname\" : \"configserver.internal.yourdomain.net\",\n        \"port\" : 8080,\n        \"path\" : \"/configs/myapp\",\n        \"username\" : \"appuser\",\n        \"password\" : \"secretpassword\"\n    }\n}\n

Note

The controller will make an API call for every single time it asks an executor to spin up a container. Please make sure to account for this in your configuration management system.

"},{"location":"tasks/specification.html#executor-fetched-config","title":"Executor fetched Config","text":"

Config file can be fetched from a remote server by the executor before spinning up a container. Once fetched, the payload will be injected as a config file into the container.

The following details are needed for this:

Name Option Description Type type Set the value to EXECUTOR_HTTP_FETCH Local Filename localFilename File name for the config inside the container. HTTP Call Details http HTTP Call related details. Please refer to HTTP Call Specification for details.

Sample config specification:

{\n    \"type\" : \"EXECUTOR_HTTP_FETCH\",\n    \"localFilename\" : \"/config/service.yml\",\n    \"http\" : {\n        \"protocol\" : \"HTTP\",\n        \"hostname\" : \"configserver.internal.yourdomain.net\",\n        \"port\" : 8080,\n        \"path\" : \"/configs/myapp\",\n        \"username\" : \"appuser\",\n        \"password\" : \"secretpassword\"\n    }\n}\n

Note

All executors will make an API call for every single time they spin up a container for this application. Please make sure to account for this in your configuration management system.

"},{"location":"tasks/specification.html#http-call-specification","title":"HTTP Call Specification","text":"

This section details the options that can set when making http calls to a configuration management system from controllers or executors.

The following options are available for HTTP call:

Name Option Description Protocol protocol Protocol to use for upstream call. Can be HTTP or HTTPS. Hostname hostname Host to call. Port port Provide custom port. Defaults to 80 for http and 443 for https. API Path path Path component of the URL. Include query parameters here. Defaults to / HTTP Method verb Type of call, use GET, POST or PUT. Defaults to GET. Success Code successCodes List of HTTP status codes which is considered as success. Defaults to [200] Payload payload Data to be used for POST and PUT calls Connection Timeout connectionTimeout Timeout for upstream connection. Operation timeout operationTimeout Timeout for actual operation. Username username Username to be used basic auth. This field is masked out on the UI. Password password Password to be used for basic auth. This field is masked on the UI. Authorization Header authHeader Data to be passed in HTTP Authorization header. This field is masked on the UI. Additional Headers headers Any other headers to be passed to the upstream in the HTTP calls. This is a map of Skip SSL Checks insecure Skip hostname and certification checks during SSL handshake with the upstream."},{"location":"tasks/specification.html#placement-policy-specification","title":"Placement Policy Specification","text":"

Placement policy governs how Drove deploys containers on the cluster. The following sections discuss the different placement policies available and how they can be configured to achieve optimal placement of containers.

Warning

All policies will work only at a {appName, version} combination level. They will not ensure constraints at an appName level. This means that for somethinge like a one per node placement, for the same appName, multiple containers can run on the same host if multiple deployments with different versions are active in a cluster. Same applies for all policies like N per host and so on.

Important details about executor tagging

  • All hosts have at-least one tag, it's own hostname.
  • The TAG policy will consider them as valid tags. This can be used to place containers on specific hosts if needed.
  • This is handled specially in all other policy types and they will consider executors having only the hostname tag as untagged.
  • A host with a tag (other than host) will not have any containers running if not placed on them specifically using the MATCH_TAG policy
"},{"location":"tasks/specification.html#any-placement","title":"Any Placement","text":"

Containers for a {appName, version} combination can run on any un-tagged executor host.

Name Option Description Policy Type type Put ANY as policy.

Sample:

{\n    \"type\" : \"ANY\"\n}\n

Tip

For most use-cases this is the placement policy to use.

"},{"location":"tasks/specification.html#one-per-host-placement","title":"One Per Host Placement","text":"

Ensures that only one container for a particular {appName, version} combination is running on an executor host at a time.

Name Option Description Policy Type type Put ONE_PER_HOST as policy.

Sample:

{\n    \"type\" : \"ONE_PER_HOST\"\n}\n

"},{"location":"tasks/specification.html#max-n-per-host-placement","title":"Max N Per Host Placement","text":"

Ensures that at most N containers for a {appName, version} combination is running on an executor host at a time.

Name Option Description Policy Type type Put MAX_N_PER_HOST as policy. Max count max The maximum num of containers that can run on an executor. Range: 1-64

Sample:

{\n    \"type\" : \"MAX_N_PER_HOST\",\n    \"max\": 3\n}\n

"},{"location":"tasks/specification.html#match-tag-placement","title":"Match Tag Placement","text":"

Ensures that containers for a {appName, version} combination are running on an executor host that has the tags as mentioned in the policy.

Name Option Description Policy Type type Put MATCH_TAG as policy. Max count tag The tag to match.

Sample:

{\n    \"type\" : \"MATCH_TAG\",\n    \"tag\": \"gpu_enabled\"\n}\n

"},{"location":"tasks/specification.html#no-tag-placement","title":"No Tag Placement","text":"

Ensures that containers for a {appName, version} combination are running on an executor host that has no tags.

Name Option Description Policy Type type Put NO_TAG as policy.

Sample:

{\n    \"type\" : \"NO_TAG\"\n}\n

Info

The NO_TAG policy is mostly for internal use, and does not need to be specified when deploying containers that do not need any special placement logic.

"},{"location":"tasks/specification.html#composite-policy-based-placement","title":"Composite Policy Based Placement","text":"

Composite policy can be used to combine policies together to create complicated placement requirements.

Name Option Description Policy Type type Put COMPOSITE as policy. Polices policies List of policies to combine Combiner combiner Can be AND and OR and signify all-match and any-match logic on the policies mentioned.

Sample:

{\n    \"type\" : \"COMPOSITE\",\n    \"policies\": [\n        {\n            \"type\": \"ONE_PER_HOST\"\n        },\n        {\n            \"type\": \"MATH_TAG\",\n            \"tag\": \"gpu_enabled\"\n        }\n    ],\n    \"combiner\" : \"AND\"\n}\n
The above policy will ensure that only one container of the relevant {appName,version} will run on GPU enabled machines.

Tip

It is easy to go into situations where no executors match complicated placement policies. Internally, we tend to keep things rather simple and use the ANY placement for most cases and maybe tags in a few places with over-provisioning or for hosts having special hardware

"},{"location":"tasks/specification.html#environment-variables","title":"Environment variables","text":"

This config can be used to inject custom environment variables to containers. The values are defined as part of deployment specification, are same across the cluster and immutable to modifications from inside the container (ie any overrides from inside the container will not be visible across the cluster).

Sample:

{\n    \"MY_VARIABLE_1\": \"fizz\",\n    \"MY_VARIABLE_2\": \"buzz\"\n}\n

The following environment variables are injected by Drove to all containers:

Variable Name Value HOST Hostname where the container is running. This is for marathon compatibility. PORT_PORT_NUMBER A variable for every port specified in exposedPorts section. The value is the actual port on the host, the specified port is mapped to. For example if ports 8080 and 8081 are specified, two variables called PORT_8080 and PORT_8081 will be injected. DROVE_EXECUTOR_HOST Hostname where container is running. DROVE_CONTAINER_ID Container that is deployed DROVE_APP_NAME App name as specified in the Application Specification DROVE_INSTANCE_ID Actual instance ID generated by Drove DROVE_APP_ID Application ID as generated by Drove DROVE_APP_INSTANCE_AUTH_TOKEN A JWT string generated by Drove that can be used by this container to call /apis/v1/internal/... apis.

Warning

Do not pass secrets using environment variables. These variables are all visible on the UI as is. Please use Configs to inject secrets files and so on.

"},{"location":"tasks/specification.html#command-line-arguments","title":"Command line arguments","text":"

A list of command line arguments that are sent to the container engine to execute inside the container. This is provides ways for you to configure your container behaviour based off such arguments. Please refer to docker documentation for details.

Danger

This might have security implications from a system point of view. As such Drove provides administrators a way to disable passing arguments at the cluster level by setting disableCmdlArgs to true in the controller configuration.

"},{"location":"tasks/specification.html#logging-specification","title":"Logging Specification","text":"

Can be used to configure how container logs are managed on the system.

Note

This section affects the docker log driver. Drove will continue to stream logs to it's own logger which can be configured at executor level through the executor configuration file.

"},{"location":"tasks/specification.html#local-logger-configuration","title":"Local Logger configuration","text":"

This is used to configure the json-file log driver.

Name Option Description Type type Set the value to LOCAL Max Size maxSize Maximum file size. Anything bigger than this will lead to rotation. Max Files maxFiles Maximum number of logs files to keep. Range: 1-100 Compress compress Enable log file compression.

Tip

If logging section is omitted, the following configuration is applied by default: - File size: 10m - Number of files: 3 - Compression: on

"},{"location":"tasks/specification.html#rsyslog-configuration","title":"Rsyslog configuration","text":"

In case suers want to stream logs to an rsyslog server, the logging configuration needs to be set to RSYSLOG mode.

Name Option Description Type type Set the value to RSYSLOG Server server URL for the rsyslog server. Tag Prefix tagPrefix Prefix to add at the start of a tag Tag Suffix tagSuffix Suffix to add at the en of a tag.

Note

The default tag is the DROVE_INSTANCE_ID. The tagPrefix and tagSuffix will to before and after this

"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..b589098 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,115 @@ + + + + https://phonepe.github.io/drove-orchestrator/index.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/getting-started.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/apis/index.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/apis/application.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/apis/cluster.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/apis/logs.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/apis/task.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/applications/index.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/applications/instances.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/applications/operations.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/applications/outage.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/applications/specification.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/cluster/cluster.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/cluster/setup/controller.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/cluster/setup/executor-setup.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/cluster/setup/gateway.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/cluster/setup/maintenance.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/cluster/setup/planning.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/cluster/setup/prerequisites.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/cluster/setup/units.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/cluster/setup/zookeeper.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/extra/cli.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/extra/epoch.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/extra/libraries.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/extra/nvidia.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/tasks/index.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/tasks/operations.html + 2024-11-21 + + + https://phonepe.github.io/drove-orchestrator/tasks/specification.html + 2024-11-21 + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..772118b0a4270d436c15a412efbed7aac6422151 GIT binary patch literal 412 zcmV;N0b~9jiwFn+cs^$W|8r?{Wo=<_E_iKh0L|9HPQx$^0MI*MQT5(tn>Ya4x*J~r zACRS{wU)*qcDKUUldfZeGaQ^tlPGnbqSWxn>j?Y~-3!XV~?5z~(w>R`p}q+!eXa0hZpSZ9TbW zj>0)i>?uHmxXbAm3*bNkenY`AZg)v>R5|KEFV=^ zZkI;Wb^e9QAANAZ+F%qREnRi8QEUg=R2;$Ko1P4HO-_oE)VC=A^T%i$1xe(+LK;lq z#ynkJhU$2o6*$`ub*{l;5D0TNQ)i3bhLB4veXs-x-bb9PTUMtV1ByP?C(#k|I~p#PnSx;5EXjD~w3+W#VtIH$jkmd{VOaB|9 G5C8x?K*Z1h literal 0 HcmV?d00001 diff --git a/stylesheets/extra.css b/stylesheets/extra.css new file mode 100644 index 0000000..ee2c783 --- /dev/null +++ b/stylesheets/extra.css @@ -0,0 +1,4 @@ +/* Dark mode */ +[data-md-color-scheme="slate"] img { + background-color: lightgray; +} \ No newline at end of file diff --git a/tasks/index.html b/tasks/index.html new file mode 100644 index 0000000..ac98e1d --- /dev/null +++ b/tasks/index.html @@ -0,0 +1,1631 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Introduction - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Introduction

+

A task is a representation for transient containerized workloads on the cluster. A task instance is supposed to have a much shorter life-time than an application instance. Use tasks to spin up things like automation scripts etc.

+

Primary differences with an application

+

Please note the following important differences between a task instance and application instances

+
    +
  • Tasks cannot expose ports and virtual hosts for incoming traffic
  • +
  • There are no readiness checks, health checks or shutdown hooks for a task
  • +
  • Task instances cannot be scaled up or down
  • +
  • Tasks cannot be restarted
  • +
  • A task is typically owned by an application running on the cluster.
  • +
  • Task instances are not replaced if the corresponding executor node goes down during execution
  • +
  • Unlike applications there is no task + task instance, the only representation of task on a Drove cluster is a task instance.
  • +
+
+

Tip

+

Use epoch to spin up tasks in a periodic manner

+
+

A task specification contains the following sections:

+
    +
  • Source App Name - Name of the application that created this task
  • +
  • Task ID - User supplied task ID unique in the same sourceAppName scope
  • +
  • Executable - The container to deploy on the cluster
  • +
  • Resources - CPU and Memory required for the container
  • +
  • Placement Policy - How containers are to be placed in the cluster
  • +
  • Environment Variables - Environment variables and values
  • +
  • Volumes - Volumes to be mounted into the container
  • +
  • Configs - Configs/files to be mounted into the container
  • +
  • Logging details - Logging spec (for example rsyslog server)
  • +
  • Tags - A map of strings for additional metadata
  • +
+

Task ID

+

Identification of a task is a bit more complicated on Drove. There is a Task ID ({sourceAppName}-{taskId}) which is used internally in drove. This is returned to the client when task is created.

+

However, clients are supposed to use the {sourceAppName,taskId} combo they have sent in the task spec to address and send commands to their tasks.

+

Task States and operations

+

Tasks on Drove have their own life cycle modelled as a state machine. State transitions can be triggered by issuing operations using the APIs.

+

States

+

Tasks on a Drove cluster can be one of the following states:

+
    +
  • PENDING - Task has been submitted, yet to be provisioned
  • +
  • PROVISIONING - Task is assigned to an executor and docker image i ng -ownloaded
  • +
  • PROVISIONING_FAILED - Docker image download failed
  • +
  • STARTING - Docker run is starting
  • +
  • RUNNING - Task is running currently
  • +
  • RUN_COMPLETED - Task run has completed. Whether passed or failed n -o be checked from the task result.
  • +
  • DEPROVISIONING - Docker image cleanup underway
  • +
  • STOPPED - Task cleanup completed. This is a terminal state.
  • +
  • LOST - Task disappeared while executor was down.
  • +
  • UNKNOWN - All tasks that are running are put in this state when executor has been restarted and startup recovery has not kicked in yet
  • +
+

Operations

+

The following task operations are recognized by Drove:

+
    +
  • CREATE - Create a task. The Task definition/spec is provided as an argument to this.
  • +
  • KILL - Kill a task. The task ID is taken as a parameter.
  • +
+
+

Tip

+

All operations need Cluster Operation Spec which can be used to control the timeout and parallelism of tasks generated by the operation.

+
+

Task State Machine

+

The following state machine signifies the states and transitions as affected by cluster state and operations issued.

+

Task State Machine

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/tasks/operations.html b/tasks/operations.html new file mode 100644 index 0000000..50ef299 --- /dev/null +++ b/tasks/operations.html @@ -0,0 +1,1703 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Task Operations - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Task Operations

+

This page discusses operations relevant to Task management. Please go over the Task State Machine to understand the different states a task can be in and how operations applied (and external changes) move a task from one state to another.

+
+

Note

+

Please go through Cluster Op Spec to understand the operation parameters being sent.

+

For tasks only the timeout parameter is relevant.

+
+
+

Note

+

Only one operation can be active on a particular task identified by a {sourceAppName,taskId} at a time.

+
+
+

Warning

+

Only the leader controller will accept and process operations. To avoid confusion, use the controller endpoint exposed by Drove Gateway to issue commands.

+
+

Cluster Operation Specification

+

When an operation is submitted to the cluster, a cluster op spec needs to be specified. This is needed to control different aspects of the operation, including parallelism of an operation or increase the timeout for the operation and so on.

+

The following aspects of an operation can be configured:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TimeouttimeoutThe duration after which Drove considers the operation to have timed out.
ParallelismparallelismParallelism of the task. (Range: 1-32)
Failure StrategyfailureStrategySet this to STOP.
+
+

Note

+

For internal recovery operations, Drove generates it's own operations. For that, Drove applies the following cluster operation spec:

+
    +
  • timeout - 300 seconds
  • +
  • parallelism - 1
  • +
  • failureStrategy - STOP
  • +
+
+

The default operation spec can be configured in the controller configuration file. It is recommended to set this to a something like 8 for faster recovery.

+
+
+

How to initiate an operation

+
+

Tip

+

Use the Drove CLI to perform all manual operations.

+
+

All operations for task lifecycle management need to be issued via a POST HTTP call to the leader controller endpoint on the path /apis/v1/tasks/operations. API will return HTTP OK/200 and relevant json response as payload.

+

Sample api call:

+
curl --location 'http://drove.local:7000/apis/v1/tasks/operations' \
+--header 'Content-Type: application/json' \
+--header 'Authorization: Basic YWRtaW46YWRtaW4=' \
+--data '{
+    "type": "KILL",
+    "sourceAppName" : "TEST_APP",
+    "taskId" : "T0012",
+    "opSpec": {
+        "timeout": "5m",
+        "parallelism": 1,
+        "failureStrategy": "STOP"
+    }
+}'
+
+
+

Note

+

In the above examples, http://drove.local:7000 is the endpoint of the leader. TEST_APP is the name of the application that started this task and taskId is a unique client generated id. Authorization is basic auth.

+
+
+

Warning

+

Task operations are not cancellable.

+
+

Create a task

+

A task can be created issuing the following command.

+

Preconditions: +- Task with same {sourceAppName,taskId} should not exist on the cluster.

+

State Transition:

+
    +
  • none → PENDINGPROVISIONINGSTARTINGRUNNINGRUN_COMPLETEDDEPROVISIONINGSTOPPED
  • +
+

To create a task a Task Spec needs to be created first.

+

Once ready, CLI command needs to be issued or the following payload needs to be sent:

+
+
+
+
drove -c local tasks create sample/test_task.json
+
+
+
+

Sample Request Payload +

{
+    "type": "CREATE",
+    "spec": {...}, //(1)!
+    "opSpec": { //(2)!
+        "timeout": "5m",
+        "parallelism": 1,
+        "failureStrategy": "STOP"
+    }
+}
+

+
    +
  1. Spec as mentioned in Task Specification
  2. +
  3. Operation spec as mentioned in Cluster Op Spec
  4. +
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "taskId": "TEST_APP-T0012"
+    },
+    "message": "success"
+}
+

+
+
+
+
+

Warning

+

There are no separate create/run steps in a task. Creation will start execution automatically and immediately.

+
+

Kill a task

+

A task can be created issuing the following command.

+

Preconditions: +- Task with same {sourceAppName,taskId} needs to exist on the cluster.

+

State Transition:

+
    +
  • RUNNINGRUN_COMPLETEDDEPROVISIONINGSTOPPED
  • +
+

CLI command needs to be issued or the following payload needs to be sent:

+
+
+
+
drove -c local tasks kill TEST_APP T0012
+
+
+
+

Sample Request Payload +

{
+    "type": "KILL",
+    "sourceAppName" : "TEST_APP",//(1)!
+    "taskId" : "T0012",//(2)!
+    "opSpec": {//(3)!
+        "timeout": "5m",
+        "parallelism": 1,
+        "failureStrategy": "STOP"
+    }
+}
+

+
    +
  1. Source app name as mentioned in spec during task creation
  2. +
  3. Task ID as mentioned in the spec
  4. +
  5. Operation spec as mentioned in Cluster Op Spec
  6. +
+

Sample response +

{
+    "status": "SUCCESS",
+    "data": {
+        "taskId": "T0012"
+    },
+    "message": "success"
+}
+

+
+
+
+
+

Note

+

Task metadata will remain on the cluster for some time. Metadata cleanup for tasks is automatic and can be configured in the controller configuration.

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/tasks/specification.html b/tasks/specification.html new file mode 100644 index 0000000..71b7013 --- /dev/null +++ b/tasks/specification.html @@ -0,0 +1,2747 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Task Specification - Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Task Specification

+

A task is defined using JSON. We use a sample configuration below to explain the options.

+

Sample Task Definition

+
{
+    "sourceAppName": "TEST_APP",//(1)!
+    "taskId": "T0012",//(2)!
+    "executable": {//(3)!
+        "type": "DOCKER", // (4)!
+        "url": "ghcr.io/appform-io/test-task",//(5)!
+        "dockerPullTimeout": "100 seconds"//(6)!
+    },
+     "resources": [//(7)!
+        {
+            "type": "CPU",
+            "count": 1//(8)!
+        },
+        {
+            "type": "MEMORY",
+            "sizeInMB": 128//(9)!
+        }
+    ],
+    "volumes": [//(10)!
+        {
+            "pathInContainer": "/data",//(11)!
+            "pathOnHost": "/mnt/datavol",//(12)!
+            "mode" : "READ_WRITE"//(13)!
+        }
+    ],
+    "configs" : [//(14)!
+        {
+            "type" : "INLINE",//(15)!
+            "localFilename": "/testfiles/drove.txt",//(16)!
+            "data" : "RHJvdmUgdGVzdA=="//(17)!
+        }
+    ],
+    "placementPolicy": {//(18)!
+        "type": "ANY"//(19)!
+    },
+    "env": {//(20)!
+        "CORES": "8"
+    },
+    "args" : [] //(27)!
+    "tags": { //(21)!
+        "superSpecialApp": "yes_i_am",
+        "say_my_name": "heisenberg"
+    },
+    "logging": {//(22)!
+        "type": "LOCAL",//(23)!
+        "maxSize": "100m",//(24)!
+        "maxFiles": 3,//(25)!
+        "compress": true//(26)!
+    }
+}
+
+
    +
  1. Name of the application that has started the task. Make sure this is a valid application on the cluster.
  2. +
  3. An unique ID for this task. Uniqueness is up to the user, Drove will scope it in the sourceAppName namespace.
  4. +
  5. Coordinates for the executable. Refer to Executable Specification for details.
  6. +
  7. Right now the only type supported is DOCKER.
  8. +
  9. Docker container address
  10. +
  11. Timeout for container pull.
  12. +
  13. Volumes to be mounted. Refer to Volume Specification for details.
  14. +
  15. Path that will be visible inside the container for this mount.
  16. +
  17. Actual path on the host machine for the mount.
  18. +
  19. Mount mode can be READ_WRITE and READ_ONLY
  20. +
  21. Configuration to be injected as file inside the container. Please refer Config Specification for details.
  22. +
  23. Type of config. Can be INLINE, EXECUTOR_LOCAL_FILE, ONTROLLER_HTTP_FETCHandEXECUTOR_HTTP_FETCH`. Specifies how drove will t the contents to be injected..
  24. +
  25. File name for the config inside the container.
  26. +
  27. Serialized form of the data, this and other parameters will vary cording to the type specified above.
  28. +
  29. List of resources required to run this application. Check Resource Requirements Specification for more tails.
  30. +
  31. Number of CPU cores to be allocated.
  32. +
  33. Amount of memory to be allocated expressed in Megabytes
  34. +
  35. Specifies how the container will be placed on the cluster. Check Placement Policy for details.
  36. +
  37. Type of placement can be ANY, ONE_PER_HOST, MATCH_TAG, NO_TAG, RULE_BASED, ANY and COMPOSITE. Rest of the parameters in this section will depend on the type.
  38. +
  39. Custom environment variables. Additional variables are injected by Drove as well. See Environment Variables section for tails.
  40. +
  41. Key value metadata that can be used in external systems.
  42. +
  43. Specify how docker log files are configured. Refer to Logging Specification
  44. +
  45. Log to local file
  46. +
  47. Maximum File Size
  48. +
  49. Number of latest log files to retain
  50. +
  51. Log files will be compressed
  52. +
  53. List of command line arguments. See Command Line Arguments for details.
  54. +
+
+

Warning

+

Please make sure sourceAppName is set to a correct application name as specified in the name parameter of a running application on the cluster.

+

If this is not done, stale task metadata will not be cleaned up and your metadata store performance will get affected over time.

+
+

Executable Specification

+

Right now Drove supports only docker containers. However as engines, both docker and podman are supported. Drove executors will fetch the executable directly from the registry based on the configuration provided.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet type to DOCKER.
URLurlDocker container URL`.
TimeoutdockerPullTimeoutTimeout for docker image pull.
+
+

Note

+

Drove supports docker registry authentication. This can be configured in the executor configuration file.

+
+

Resource Requirements Specification

+

This section specifies the hardware resources required to run the container. Right now only CPU and MEMORY are supported as resource types that can be reserved for a container.

+

CPU Requirements

+

Specifies number of cores to be assigned to the container.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet type to CPU for this.
CountcountNumber of cores to be assigned.
+

Memory Requirements

+

Specifies amount of memory to be allocated to a container.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet type to MEMORY for this.
CountsizeInMBAmount of memory (in Mega Bytes) to be allocated.
+

Sample +

[
+    {
+        "type": "CPU",
+        "count": 1
+    },
+    {
+        "type": "MEMORY",
+        "sizeInMB": 128
+    }
+]
+

+
+

Note

+

Both CPU and MEMORY configurations are mandatory.

+
+

Volume Specification

+

Files and directories can be mounted from the executor host into the container. The volumes section contains a list of volumes that need to be mounted.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Path In ContainerpathInContainerPath that will be visible inside the container for this mount.
Path On HostpathOnHostActual path on the host machine for the mount.
Mount ModemodeMount mode can be READ_WRITE and READ_ONLY to allow the containerized process to write or read to the volume.
+
+

Info

+

We do not support mounting remote volumes as of now.

+
+

Config Specification

+

Drove supports injection of configuration files into containers. The specifications for the same are discussed below.

+

Inline config

+

Inline configuration can be added in the Application Specification itself. This will manifest as a file inside the container.

+

The following details are needed for this:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to INLINE
Local FilenamelocalFilenameFile name for the config inside the container.
DatadataBase64 encoded string for the data. The value for this will be masked on UI.
+

Config file: +

port: 8080
+logLevel: DEBUG
+
+Corresponding config specification: +
{
+    "type" : "INLINE",
+    "localFilename" : "/config/service.yml",
+    "data" : "cG9ydDogODA4MApsb2dMZXZlbDogREVCVUcK"
+}
+

+
+

Warning

+

The full base 64 encoded config data will get stored in Drove ZK and will be pushed to executors inline. It is not recommended to stream large config files to containers using this method. This will probably need additional configuration on your ZK cluster.

+
+

Locally loaded config

+

Config file from a path on the executor directly. Such files can be distributed to the executor host using existing configuration management systems such as OpenTofu, Salt etc.

+

The following details are needed for this:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to EXECUTOR_LOCAL_FILE
Local FilenamelocalFilenameFile name for the config inside the container.
File pathfilePathOnHostPath to the config file on executor host.
+

Sample config specification: +

{
+    "type" : "EXECUTOR_LOCAL_FILE",
+    "localFilename" : "/config/service.yml",
+    "data" : "/mnt/configs/myservice/config.yml"
+}
+

+

Controller fetched Config

+

Config file can be fetched from a remote server by the controller. Once fetched, these will be streamed to the executor as part of the instance specification for starting a container.

+

The following details are needed for this:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to CONTROLLER_HTTP_FETCH
Local FilenamelocalFilenameFile name for the config inside the container.
HTTP Call DetailshttpHTTP Call related details. Please refer to HTTP Call Specification for details.
+

Sample config specification: +

{
+    "type" : "CONTROLLER_HTTP_FETCH",
+    "localFilename" : "/config/service.yml",
+    "http" : {
+        "protocol" : "HTTP",
+        "hostname" : "configserver.internal.yourdomain.net",
+        "port" : 8080,
+        "path" : "/configs/myapp",
+        "username" : "appuser",
+        "password" : "secretpassword"
+    }
+}
+

+
+

Note

+

The controller will make an API call for every single time it asks an executor to spin up a container. Please make sure to account for this in your configuration management system.

+
+

Executor fetched Config

+

Config file can be fetched from a remote server by the executor before spinning up a container. Once fetched, the payload will be injected as a config file into the container.

+

The following details are needed for this:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to EXECUTOR_HTTP_FETCH
Local FilenamelocalFilenameFile name for the config inside the container.
HTTP Call DetailshttpHTTP Call related details. Please refer to HTTP Call Specification for details.
+

Sample config specification: +

{
+    "type" : "EXECUTOR_HTTP_FETCH",
+    "localFilename" : "/config/service.yml",
+    "http" : {
+        "protocol" : "HTTP",
+        "hostname" : "configserver.internal.yourdomain.net",
+        "port" : 8080,
+        "path" : "/configs/myapp",
+        "username" : "appuser",
+        "password" : "secretpassword"
+    }
+}
+

+
+

Note

+

All executors will make an API call for every single time they spin up a container for this application. Please make sure to account for this in your configuration management system.

+
+

HTTP Call Specification

+

This section details the options that can set when making http calls to a configuration management system from controllers or executors.

+

The following options are available for HTTP call:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
ProtocolprotocolProtocol to use for upstream call. Can be HTTP or HTTPS.
HostnamehostnameHost to call.
PortportProvide custom port. Defaults to 80 for http and 443 for https.
API PathpathPath component of the URL. Include query parameters here. Defaults to /
HTTP MethodverbType of call, use GET, POST or PUT. Defaults to GET.
Success CodesuccessCodesList of HTTP status codes which is considered as success. Defaults to [200]
PayloadpayloadData to be used for POST and PUT calls
Connection TimeoutconnectionTimeoutTimeout for upstream connection.
Operation timeoutoperationTimeoutTimeout for actual operation.
UsernameusernameUsername to be used basic auth. This field is masked out on the UI.
PasswordpasswordPassword to be used for basic auth. This field is masked on the UI.
Authorization HeaderauthHeaderData to be passed in HTTP Authorization header. This field is masked on the UI.
Additional HeadersheadersAny other headers to be passed to the upstream in the HTTP calls. This is a map of
Skip SSL ChecksinsecureSkip hostname and certification checks during SSL handshake with the upstream.
+

Placement Policy Specification

+

Placement policy governs how Drove deploys containers on the cluster. The following sections discuss the different placement policies available and how they can be configured to achieve optimal placement of containers.

+
+

Warning

+

All policies will work only at a {appName, version} combination level. They will not ensure constraints at an appName level. This means that for somethinge like a one per node placement, for the same appName, multiple containers can run on the same host if multiple deployments with different versions are active in a cluster. Same applies for all policies like N per host and so on.

+
+
+

Important details about executor tagging

+
    +
  • All hosts have at-least one tag, it's own hostname.
  • +
  • The TAG policy will consider them as valid tags. This can be used to place containers on specific hosts if needed.
  • +
  • This is handled specially in all other policy types and they will consider executors having only the hostname tag as untagged.
  • +
  • A host with a tag (other than host) will not have any containers running if not placed on them specifically using the MATCH_TAG policy
  • +
+
+

Any Placement

+

Containers for a {appName, version} combination can run on any un-tagged executor host.

+ + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut ANY as policy.
+

Sample: +

{
+    "type" : "ANY"
+}
+

+
+

Tip

+

For most use-cases this is the placement policy to use.

+
+

One Per Host Placement

+

Ensures that only one container for a particular {appName, version} combination is running on an executor host at a time.

+ + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut ONE_PER_HOST as policy.
+

Sample: +

{
+    "type" : "ONE_PER_HOST"
+}
+

+

Max N Per Host Placement

+

Ensures that at most N containers for a {appName, version} combination is running on an executor host at a time.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut MAX_N_PER_HOST as policy.
Max countmaxThe maximum num of containers that can run on an executor. Range: 1-64
+

Sample: +

{
+    "type" : "MAX_N_PER_HOST",
+    "max": 3
+}
+

+

Match Tag Placement

+

Ensures that containers for a {appName, version} combination are running on an executor host that has the tags as mentioned in the policy.

+ + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut MATCH_TAG as policy.
Max counttagThe tag to match.
+

Sample: +

{
+    "type" : "MATCH_TAG",
+    "tag": "gpu_enabled"
+}
+

+

No Tag Placement

+

Ensures that containers for a {appName, version} combination are running on an executor host that has no tags.

+ + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut NO_TAG as policy.
+

Sample: +

{
+    "type" : "NO_TAG"
+}
+

+
+

Info

+

The NO_TAG policy is mostly for internal use, and does not need to be specified when deploying containers that do not need any special placement logic.

+
+

Composite Policy Based Placement

+

Composite policy can be used to combine policies together to create complicated placement requirements.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
Policy TypetypePut COMPOSITE as policy.
PolicespoliciesList of policies to combine
CombinercombinerCan be AND and OR and signify all-match and any-match logic on the policies mentioned.
+

Sample: +

{
+    "type" : "COMPOSITE",
+    "policies": [
+        {
+            "type": "ONE_PER_HOST"
+        },
+        {
+            "type": "MATH_TAG",
+            "tag": "gpu_enabled"
+        }
+    ],
+    "combiner" : "AND"
+}
+
+The above policy will ensure that only one container of the relevant {appName,version} will run on GPU enabled machines.

+
+

Tip

+

It is easy to go into situations where no executors match complicated placement policies. Internally, we tend to keep things rather simple and use the ANY placement for most cases and maybe tags in a few places with over-provisioning or for hosts having special hardware 🙂

+
+

Environment variables

+

This config can be used to inject custom environment variables to containers. The values are defined as part of deployment specification, are same across the cluster and immutable to modifications from inside the container (ie any overrides from inside the container will not be visible across the cluster).

+

Sample: +

{
+    "MY_VARIABLE_1": "fizz",
+    "MY_VARIABLE_2": "buzz"
+}
+

+

The following environment variables are injected by Drove to all containers:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Variable NameValue
HOSTHostname where the container is running. This is for marathon compatibility.
PORT_PORT_NUMBERA variable for every port specified in exposedPorts section. The value is the actual port on the host, the specified port is mapped to. For example if ports 8080 and 8081 are specified, two variables called PORT_8080 and PORT_8081 will be injected.
DROVE_EXECUTOR_HOSTHostname where container is running.
DROVE_CONTAINER_IDContainer that is deployed
DROVE_APP_NAMEApp name as specified in the Application Specification
DROVE_INSTANCE_IDActual instance ID generated by Drove
DROVE_APP_IDApplication ID as generated by Drove
DROVE_APP_INSTANCE_AUTH_TOKENA JWT string generated by Drove that can be used by this container to call /apis/v1/internal/... apis.
+
+

Warning

+

Do not pass secrets using environment variables. These variables are all visible on the UI as is. Please use Configs to inject secrets files and so on.

+
+

Command line arguments

+

A list of command line arguments that are sent to the container engine to execute inside the container. This is provides ways for you to configure your container behaviour based off such arguments. Please refer to docker documentation for details.

+
+

Danger

+

This might have security implications from a system point of view. As such Drove provides administrators a way to disable passing arguments at the cluster level by setting disableCmdlArgs to true in the controller configuration.

+
+

Logging Specification

+

Can be used to configure how container logs are managed on the system.

+
+

Note

+

This section affects the docker log driver. Drove will continue to stream logs to it's own logger which can be configured at executor level through the executor configuration file.

+
+

Local Logger configuration

+

This is used to configure the json-file log driver.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to LOCAL
Max SizemaxSizeMaximum file size. Anything bigger than this will lead to rotation.
Max FilesmaxFilesMaximum number of logs files to keep. Range: 1-100
CompresscompressEnable log file compression.
+
+

Tip

+

If logging section is omitted, the following configuration is applied by default: +- File size: 10m +- Number of files: 3 +- Compression: on

+
+

Rsyslog configuration

+

In case suers want to stream logs to an rsyslog server, the logging configuration needs to be set to RSYSLOG mode.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOptionDescription
TypetypeSet the value to RSYSLOG
ServerserverURL for the rsyslog server.
Tag PrefixtagPrefixPrefix to add at the start of a tag
Tag SuffixtagSuffixSuffix to add at the en of a tag.
+
+

Note

+

The default tag is the DROVE_INSTANCE_ID. The tagPrefix and tagSuffix will to before and after this

+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file

G3+~gi)%bGXFJ{KM(Tx{ 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 EFQ8P!;{X5v literal 0 HcmV?d00001 diff --git a/images/task-state-machine.png b/images/task-state-machine.png new file mode 100644 index 0000000000000000000000000000000000000000..f00d387e560ef41538ecdf621bc4f343de0c3eb9 GIT binary patch literal 38684 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
cGumY6)OU;p1crUa^; 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 literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 0000000..dacbf19 --- /dev/null +++ b/index.html @@ -0,0 +1,1768 @@ + + + + + + + + + + + + + + + + + + + + + + + Drove Container Orchestrator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +