[{"data":1,"prerenderedAt":505},["ShallowReactive",2],{"nav-stories":3,"footer-stories":61,"project-service-autoscaler":74},[4,16,25,34,43,52],{"id":5,"color":6,"extension":7,"image":8,"label":9,"link":10,"meta":11,"order":12,"stem":13,"text":14,"__hash__":15},"stories\u002Fstories\u002F01-data-center.yml",null,"yml","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1558494949-ef010cbdcc31?w=1080","DATA_CENTER","https:\u002F\u002Fx.com\u002Fabbeytetteh_",{},1,"stories\u002F01-data-center","Racking new servers. 40gbit backbone online.","0QUZQbaANhdO8WemZxkDdO7vbVopfnynHtH9FxBZb_w",{"id":17,"color":6,"extension":7,"image":18,"label":19,"link":6,"meta":20,"order":21,"stem":22,"text":23,"__hash__":24},"stories\u002Fstories\u002F02-thoughts.yml","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1498050108023-c5249f4df085?w=1080","THOUGHTS",{},2,"stories\u002F02-thoughts","Late night bug hunting. Found the memory leak.","Gd1am954aasY6HRHD7hCtOuessXb6zYZ8iizS501ICg",{"id":26,"color":27,"extension":7,"image":6,"label":28,"link":6,"meta":29,"order":30,"stem":31,"text":32,"__hash__":33},"stories\u002Fstories\u002F03-coding.yml","#3b82f6","CODING",{},3,"stories\u002F03-coding","Just thinking about how much easier life is with Swarm.","vLAyiGUPtlXB2SHa5KM_U2AaK4QkG3Og85UEUE7qzgM",{"id":35,"color":6,"extension":7,"image":36,"label":37,"link":6,"meta":38,"order":39,"stem":40,"text":41,"__hash__":42},"stories\u002Fstories\u002F04-update.yml","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1591799264318-7e6ef8ddb7ea?w=1080","UPDATE",{},4,"stories\u002F04-update","New cluster nodes arrived. Prepping for installation.","kyT60N5C6Re_jMonZbgNy0PbQhzXmUWxDbD0D_v43ts",{"id":44,"color":45,"extension":7,"image":6,"label":46,"link":6,"meta":47,"order":48,"stem":49,"text":50,"__hash__":51},"stories\u002Fstories\u002F05-setup.yml","#86868b","SETUP",{},5,"stories\u002F05-setup","Optimizing the telemetry pipeline for 1M req\u002Fs.","cPOBkzoyXsCmPgRO2d80Hj3vm4MP-6nAejtlQ5iuSzw",{"id":53,"color":6,"extension":7,"image":54,"label":55,"link":6,"meta":56,"order":57,"stem":58,"text":59,"__hash__":60},"stories\u002Fstories\u002F06-travel.yml","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1560969184-10fe8719e047?w=1080","TRAVEL",{},6,"stories\u002F06-travel","Travel log — system architecture workshop in Berlin.","jnOxerdF6usAIHdR35Z-opx0LJAy9kZluXnZhtz62Z0",[62,64,66,68,70,72],{"id":5,"color":6,"extension":7,"image":8,"label":9,"link":10,"meta":63,"order":12,"stem":13,"text":14,"__hash__":15},{},{"id":17,"color":6,"extension":7,"image":18,"label":19,"link":6,"meta":65,"order":21,"stem":22,"text":23,"__hash__":24},{},{"id":26,"color":27,"extension":7,"image":6,"label":28,"link":6,"meta":67,"order":30,"stem":31,"text":32,"__hash__":33},{},{"id":35,"color":6,"extension":7,"image":36,"label":37,"link":6,"meta":69,"order":39,"stem":40,"text":41,"__hash__":42},{},{"id":44,"color":45,"extension":7,"image":6,"label":46,"link":6,"meta":71,"order":48,"stem":49,"text":50,"__hash__":51},{},{"id":53,"color":6,"extension":7,"image":54,"label":55,"link":6,"meta":73,"order":57,"stem":58,"text":59,"__hash__":60},{},{"id":75,"title":76,"body":77,"description":492,"extension":493,"hash":494,"liveUrl":6,"meta":495,"navigation":496,"order":21,"path":497,"rackBay":498,"rackStatus":499,"region":500,"seo":501,"stem":502,"thumbnail":503,"vault":496,"__hash__":504},"projects\u002Fprojects\u002Fservice-autoscaler.md","Service Autoscaler",{"type":78,"value":79,"toc":487},"minimark",[80,84,89,92,465,469,476,480,483],[81,82,83],"p",{},"Service Autoscaler is a lightweight Go daemon that subscribes to a Prometheus metrics stream and drives Docker Swarm service scaling decisions without any manual intervention.",[85,86,88],"h2",{"id":87},"how-it-works","How It Works",[81,90,91],{},"The autoscaler queries a set of configurable PromQL expressions on a 15-second tick. When a metric crosses a defined threshold, it calls the Swarm API to update the target replica count. Scale-down events include a stabilisation window to prevent thrashing.",[93,94,99],"pre",{"className":95,"code":96,"language":97,"meta":98,"style":98},"language-go shiki shiki-themes vitesse-light","func (a *Autoscaler) Tick(ctx context.Context) {\n    for _, rule := range a.rules {\n        val, err := a.prom.QueryInstant(ctx, rule.Query)\n        if err != nil || val == nil {\n            continue\n        }\n        if *val > rule.ScaleUpThreshold {\n            a.swarm.Scale(ctx, rule.Service, rule.CurrentReplicas+rule.Step)\n        } else if *val \u003C rule.ScaleDownThreshold && rule.StabilisedFor(5*time.Minute) {\n            a.swarm.Scale(ctx, rule.Service, max(rule.MinReplicas, rule.CurrentReplicas-rule.Step))\n        }\n    }\n}\n","go","",[100,101,102,153,183,223,249,254,259,282,333,389,448,453,459],"code",{"__ignoreMap":98},[103,104,106,110,114,118,122,126,129,133,136,139,142,145,148,150],"span",{"class":105,"line":12},"line",[103,107,109],{"class":108},"sbBg2","func",[103,111,113],{"class":112},"sYZai"," (",[103,115,117],{"class":116},"svycV","a ",[103,119,121],{"class":120},"si04Y","*",[103,123,125],{"class":124},"sUxyF","Autoscaler",[103,127,128],{"class":112},")",[103,130,132],{"class":131},"sySUi"," Tick",[103,134,135],{"class":112},"(",[103,137,138],{"class":116},"ctx",[103,140,141],{"class":124}," context",[103,143,144],{"class":112},".",[103,146,147],{"class":124},"Context",[103,149,128],{"class":112},[103,151,152],{"class":112}," {\n",[103,154,155,158,161,164,167,170,173,176,178,181],{"class":105,"line":21},[103,156,157],{"class":108},"    for",[103,159,160],{"class":116}," _",[103,162,163],{"class":112},",",[103,165,166],{"class":116}," rule",[103,168,169],{"class":112}," :=",[103,171,172],{"class":108}," range",[103,174,175],{"class":116}," a",[103,177,144],{"class":112},[103,179,180],{"class":116},"rules",[103,182,152],{"class":112},[103,184,185,188,190,193,195,197,199,202,204,207,209,211,213,215,217,220],{"class":105,"line":30},[103,186,187],{"class":116},"        val",[103,189,163],{"class":112},[103,191,192],{"class":116}," err",[103,194,169],{"class":112},[103,196,175],{"class":116},[103,198,144],{"class":112},[103,200,201],{"class":116},"prom",[103,203,144],{"class":112},[103,205,206],{"class":131},"QueryInstant",[103,208,135],{"class":112},[103,210,138],{"class":116},[103,212,163],{"class":112},[103,214,166],{"class":116},[103,216,144],{"class":112},[103,218,219],{"class":116},"Query",[103,221,222],{"class":112},")\n",[103,224,225,228,230,233,236,239,242,245,247],{"class":105,"line":39},[103,226,227],{"class":108},"        if",[103,229,192],{"class":116},[103,231,232],{"class":120}," !=",[103,234,235],{"class":120}," nil",[103,237,238],{"class":120}," ||",[103,240,241],{"class":116}," val",[103,243,244],{"class":120}," ==",[103,246,235],{"class":120},[103,248,152],{"class":112},[103,250,251],{"class":105,"line":48},[103,252,253],{"class":108},"            continue\n",[103,255,256],{"class":105,"line":57},[103,257,258],{"class":112},"        }\n",[103,260,262,264,267,270,273,275,277,280],{"class":105,"line":261},7,[103,263,227],{"class":108},[103,265,266],{"class":120}," *",[103,268,269],{"class":116},"val",[103,271,272],{"class":120}," >",[103,274,166],{"class":116},[103,276,144],{"class":112},[103,278,279],{"class":116},"ScaleUpThreshold",[103,281,152],{"class":112},[103,283,285,288,290,293,295,298,300,302,304,306,308,311,313,315,317,320,323,326,328,331],{"class":105,"line":284},8,[103,286,287],{"class":116},"            a",[103,289,144],{"class":112},[103,291,292],{"class":116},"swarm",[103,294,144],{"class":112},[103,296,297],{"class":131},"Scale",[103,299,135],{"class":112},[103,301,138],{"class":116},[103,303,163],{"class":112},[103,305,166],{"class":116},[103,307,144],{"class":112},[103,309,310],{"class":116},"Service",[103,312,163],{"class":112},[103,314,166],{"class":116},[103,316,144],{"class":112},[103,318,319],{"class":116},"CurrentReplicas",[103,321,322],{"class":120},"+",[103,324,325],{"class":116},"rule",[103,327,144],{"class":112},[103,329,330],{"class":116},"Step",[103,332,222],{"class":112},[103,334,336,339,342,345,347,349,352,354,356,359,362,364,366,369,371,375,377,380,382,385,387],{"class":105,"line":335},9,[103,337,338],{"class":112},"        }",[103,340,341],{"class":108}," else",[103,343,344],{"class":108}," if",[103,346,266],{"class":120},[103,348,269],{"class":116},[103,350,351],{"class":120}," \u003C",[103,353,166],{"class":116},[103,355,144],{"class":112},[103,357,358],{"class":116},"ScaleDownThreshold",[103,360,361],{"class":120}," &&",[103,363,166],{"class":116},[103,365,144],{"class":112},[103,367,368],{"class":131},"StabilisedFor",[103,370,135],{"class":112},[103,372,374],{"class":373},"s-TwI","5",[103,376,121],{"class":120},[103,378,379],{"class":116},"time",[103,381,144],{"class":112},[103,383,384],{"class":116},"Minute",[103,386,128],{"class":112},[103,388,152],{"class":112},[103,390,392,394,396,398,400,402,404,406,408,410,412,414,416,419,421,423,425,428,430,432,434,436,439,441,443,445],{"class":105,"line":391},10,[103,393,287],{"class":116},[103,395,144],{"class":112},[103,397,292],{"class":116},[103,399,144],{"class":112},[103,401,297],{"class":131},[103,403,135],{"class":112},[103,405,138],{"class":116},[103,407,163],{"class":112},[103,409,166],{"class":116},[103,411,144],{"class":112},[103,413,310],{"class":116},[103,415,163],{"class":112},[103,417,418],{"class":131}," max",[103,420,135],{"class":112},[103,422,325],{"class":116},[103,424,144],{"class":112},[103,426,427],{"class":116},"MinReplicas",[103,429,163],{"class":112},[103,431,166],{"class":116},[103,433,144],{"class":112},[103,435,319],{"class":116},[103,437,438],{"class":120},"-",[103,440,325],{"class":116},[103,442,144],{"class":112},[103,444,330],{"class":116},[103,446,447],{"class":112},"))\n",[103,449,451],{"class":105,"line":450},11,[103,452,258],{"class":112},[103,454,456],{"class":105,"line":455},12,[103,457,458],{"class":112},"    }\n",[103,460,462],{"class":105,"line":461},13,[103,463,464],{"class":112},"}\n",[85,466,468],{"id":467},"configuration","Configuration",[81,470,471,472,475],{},"Rules are declared in a YAML file and hot-reloaded on change via ",[100,473,474],{},"inotify",", so threshold adjustments don't require a daemon restart.",[85,477,479],{"id":478},"status","Status",[81,481,482],{},"Running in production. Has handled scale events across 12 services with zero missed scaling opportunities recorded over a 90-day observation window.",[484,485,486],"style",{},"html pre.shiki code .sbBg2, html code.shiki .sbBg2{--shiki-default:#1E754F}html pre.shiki code .sYZai, html code.shiki .sYZai{--shiki-default:#999999}html pre.shiki code .svycV, html code.shiki .svycV{--shiki-default:#B07D48}html pre.shiki code .si04Y, html code.shiki .si04Y{--shiki-default:#AB5959}html pre.shiki code .sUxyF, html code.shiki .sUxyF{--shiki-default:#2E8F82}html pre.shiki code .sySUi, html code.shiki .sySUi{--shiki-default:#59873A}html pre.shiki code .s-TwI, html code.shiki .s-TwI{--shiki-default:#2F798A}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":98,"searchDepth":21,"depth":21,"links":488},[489,490,491],{"id":87,"depth":21,"text":88},{"id":467,"depth":21,"text":468},{"id":478,"depth":21,"text":479},"Go-based daemon that monitors Swarm service load and adjusts replica counts in real time based on Prometheus metrics.","md","D4E5F6",{},true,"\u002Fprojects\u002Fservice-autoscaler","BAY 02","live","EU-WEST-1",{"title":76,"description":492},"projects\u002Fservice-autoscaler","\u002Fimages\u002Fthumbnails\u002Fservice-autoscaler.png","6jraPhE5BqB7rMeWOOshRgbYJmvkH4sWjcymC74tzfk",1779361989070]