[{"data":1,"prerenderedAt":881},["ShallowReactive",2],{"\u002Fblog\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker":3,"\u002Fblog\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker-surround":870},{"id":4,"title":5,"author":6,"badge":11,"body":13,"date":859,"description":860,"draft":861,"extension":862,"image":863,"meta":865,"navigation":225,"path":866,"seo":867,"stem":868,"__hash__":869},"posts\u002Fblog\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker.md","Load Balancing Applications with HAProxy and Docker",{"name":7,"to":8,"avatar":9},"Nir Galon","https:\u002F\u002Fx.com\u002Fnirgn975",{"src":10},"\u002Favatar.webp",{"label":12},"tutorials",{"type":14,"value":15,"toc":851},"minimark",[16,20,31,34,47,50,55,63,122,128,161,171,174,178,197,551,554,614,624,628,655,660,667,695,702,711,718,727,733,771,807,813,822,833,837,844,847],[17,18,19],"p",{},"A tutorial for a real world docker use case.",[17,21,22,23,30],{},"Recently I read a lot of articles about load balancing applications with ",[24,25,29],"a",{"href":26,"rel":27},"https:\u002F\u002Fwww.docker.com",[28],"nofollow","Docker",", Docker Compose, and Docker Swarm for my work. We have a couple of hundreds of instances and we need to manage them and do load balancing between them.",[17,32,33],{},"There are a lot of articles about this topic, but sadly the use case they present is quite simple and because of that they really don’t help. Here is a couple of examples to what I mean:",[35,36,37,41,44],"ol",{},[38,39,40],"li",{},"Manually create hundreds of containers is not practical.",[38,42,43],{},"Creating hundreds of containers each one on a different port is not practical.",[38,45,46],{},"Write the containers ip and port manually in nginx conf file is not practical.",[17,48,49],{},"For that reason I decided to write this post and present the way we use. It’s not by any mean the “right” way or the only way, but it’s the way that works for us right now. Also, I’m assuming you know Docker, Docker Compose, and Docker Swarm.",[51,52,54],"h2",{"id":53},"_1-our-simple-application","1. Our simple application",[17,56,57,58,62],{},"Let’s start by creating our simple Node.js application. Create a file named ",[59,60,61],"code",{},"index.js"," with the following code:",[64,65,70],"pre",{"className":66,"code":67,"filename":61,"language":68,"meta":69,"style":69},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","var http = require(\"http\");\nvar os = require(\"os\");\nhttp\n  .createServer(function (req, res) {\n    res.writeHead(200, { \"Content-Type\": \"text\u002Fhtml\" });\n    res.end(`\u003Ch1>I'm ${os.hostname()}\u003C\u002Fh1>`);\n  })\n  .listen(8080);\n","javascript","",[59,71,72,80,86,92,98,104,110,116],{"__ignoreMap":69},[73,74,77],"span",{"class":75,"line":76},"line",1,[73,78,79],{},"var http = require(\"http\");\n",[73,81,83],{"class":75,"line":82},2,[73,84,85],{},"var os = require(\"os\");\n",[73,87,89],{"class":75,"line":88},3,[73,90,91],{},"http\n",[73,93,95],{"class":75,"line":94},4,[73,96,97],{},"  .createServer(function (req, res) {\n",[73,99,101],{"class":75,"line":100},5,[73,102,103],{},"    res.writeHead(200, { \"Content-Type\": \"text\u002Fhtml\" });\n",[73,105,107],{"class":75,"line":106},6,[73,108,109],{},"    res.end(`\u003Ch1>I'm ${os.hostname()}\u003C\u002Fh1>`);\n",[73,111,113],{"class":75,"line":112},7,[73,114,115],{},"  })\n",[73,117,119],{"class":75,"line":118},8,[73,120,121],{},"  .listen(8080);\n",[17,123,124,125,62],{},"Now we need to dockerize the app, so we’ll create a file named ",[59,126,127],{},"Dockerfile",[64,129,133],{"className":130,"code":131,"filename":127,"language":132,"meta":69,"style":69},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","FROM node\nRUN mkdir -p \u002Fusr\u002Fsrc\u002Fapp\nCOPY index.js \u002Fusr\u002Fsrc\u002Fapp\nEXPOSE 8080\nCMD [ \"node\", \"\u002Fusr\u002Fsrc\u002Fapp\u002Findex\" ]\n","yaml",[59,134,135,141,146,151,156],{"__ignoreMap":69},[73,136,137],{"class":75,"line":76},[73,138,140],{"class":139},"sfazB","FROM node\n",[73,142,143],{"class":75,"line":82},[73,144,145],{"class":139},"RUN mkdir -p \u002Fusr\u002Fsrc\u002Fapp\n",[73,147,148],{"class":75,"line":88},[73,149,150],{"class":139},"COPY index.js \u002Fusr\u002Fsrc\u002Fapp\n",[73,152,153],{"class":75,"line":94},[73,154,155],{"class":139},"EXPOSE 8080\n",[73,157,158],{"class":75,"line":100},[73,159,160],{"class":139},"CMD [ \"node\", \"\u002Fusr\u002Fsrc\u002Fapp\u002Findex\" ]\n",[17,162,163,164,167,168,170],{},"To build a docker image of our newly awesome Node.js app from this docker file instructions we’ll write ",[59,165,166],{},"docker build -t awesome ."," in the command line, where our ",[59,169,127],{}," file is located.",[17,172,173],{},"Now we have a docker image of our simple (and awesome) Node.js app, and we can create containers from that image. Let’s say we need 20 containers of that app, so we need an automated way to create those containers and manage them. Also we need a container with some HTTP server to route and load balance the requests to our Node.js containers.",[51,175,177],{"id":176},"_2-enter-docker-compose","2. Enter Docker Compose",[17,179,180,181,186,187,192,193,196],{},"For our HTTP server we’ll use ",[24,182,185],{"href":183,"rel":184},"http:\u002F\u002Fwww.haproxy.org",[28],"HAProxy",", that means we need to create a container with HAProxy that will listen to port 80 and load balance the requests to the different Node.js containers on port 8080. To create our containers (Node.js apps and HAProxy) we’ll use ",[24,188,191],{"href":189,"rel":190},"https:\u002F\u002Fdocs.docker.com\u002Fcompose",[28],"Docker Compose",", let’s write our ",[59,194,195],{},"docker-compose.yml"," file:",[64,198,200],{"className":130,"code":199,"filename":195,"language":132,"meta":69,"style":69},"version: \"3\"\n\nservices:\n  awesome:\n    image: awesome\n    ports:\n      - 8080\n    environment:\n      - SERVICE_PORTS=8080\n    deploy:\n      replicas: 20\n      update_config:\n        parallelism: 5\n        delay: 10s\n      restart_policy:\n        condition: on-failure\n        max_attempts: 3\n        window: 120s\n    networks:\n      - web\n\n  proxy:\n    image: dockercloud\u002Fhaproxy\n    depends_on:\n      - awesome\n    environment:\n      - BALANCE=leastconn\n    volumes:\n      - \u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock\n    ports:\n      - 80:80\n    networks:\n      - web\n    deploy:\n      placement:\n        constraints: [node.role == manager]\n\nnetworks:\n  web:\n    driver: overlay\n",[59,201,202,221,227,235,242,252,259,268,275,283,291,302,310,321,332,340,351,362,373,381,389,394,402,412,420,427,434,442,450,458,465,473,480,487,494,502,519,524,532,540],{"__ignoreMap":69},[73,203,204,208,212,215,218],{"class":75,"line":76},[73,205,207],{"class":206},"swJcz","version",[73,209,211],{"class":210},"sMK4o",":",[73,213,214],{"class":210}," \"",[73,216,217],{"class":139},"3",[73,219,220],{"class":210},"\"\n",[73,222,223],{"class":75,"line":82},[73,224,226],{"emptyLinePlaceholder":225},true,"\n",[73,228,229,232],{"class":75,"line":88},[73,230,231],{"class":206},"services",[73,233,234],{"class":210},":\n",[73,236,237,240],{"class":75,"line":94},[73,238,239],{"class":206},"  awesome",[73,241,234],{"class":210},[73,243,244,247,249],{"class":75,"line":100},[73,245,246],{"class":206},"    image",[73,248,211],{"class":210},[73,250,251],{"class":139}," awesome\n",[73,253,254,257],{"class":75,"line":106},[73,255,256],{"class":206},"    ports",[73,258,234],{"class":210},[73,260,261,264],{"class":75,"line":112},[73,262,263],{"class":210},"      -",[73,265,267],{"class":266},"sbssI"," 8080\n",[73,269,270,273],{"class":75,"line":118},[73,271,272],{"class":206},"    environment",[73,274,234],{"class":210},[73,276,278,280],{"class":75,"line":277},9,[73,279,263],{"class":210},[73,281,282],{"class":139}," SERVICE_PORTS=8080\n",[73,284,286,289],{"class":75,"line":285},10,[73,287,288],{"class":206},"    deploy",[73,290,234],{"class":210},[73,292,294,297,299],{"class":75,"line":293},11,[73,295,296],{"class":206},"      replicas",[73,298,211],{"class":210},[73,300,301],{"class":266}," 20\n",[73,303,305,308],{"class":75,"line":304},12,[73,306,307],{"class":206},"      update_config",[73,309,234],{"class":210},[73,311,313,316,318],{"class":75,"line":312},13,[73,314,315],{"class":206},"        parallelism",[73,317,211],{"class":210},[73,319,320],{"class":266}," 5\n",[73,322,324,327,329],{"class":75,"line":323},14,[73,325,326],{"class":206},"        delay",[73,328,211],{"class":210},[73,330,331],{"class":139}," 10s\n",[73,333,335,338],{"class":75,"line":334},15,[73,336,337],{"class":206},"      restart_policy",[73,339,234],{"class":210},[73,341,343,346,348],{"class":75,"line":342},16,[73,344,345],{"class":206},"        condition",[73,347,211],{"class":210},[73,349,350],{"class":139}," on-failure\n",[73,352,354,357,359],{"class":75,"line":353},17,[73,355,356],{"class":206},"        max_attempts",[73,358,211],{"class":210},[73,360,361],{"class":266}," 3\n",[73,363,365,368,370],{"class":75,"line":364},18,[73,366,367],{"class":206},"        window",[73,369,211],{"class":210},[73,371,372],{"class":139}," 120s\n",[73,374,376,379],{"class":75,"line":375},19,[73,377,378],{"class":206},"    networks",[73,380,234],{"class":210},[73,382,384,386],{"class":75,"line":383},20,[73,385,263],{"class":210},[73,387,388],{"class":139}," web\n",[73,390,392],{"class":75,"line":391},21,[73,393,226],{"emptyLinePlaceholder":225},[73,395,397,400],{"class":75,"line":396},22,[73,398,399],{"class":206},"  proxy",[73,401,234],{"class":210},[73,403,405,407,409],{"class":75,"line":404},23,[73,406,246],{"class":206},[73,408,211],{"class":210},[73,410,411],{"class":139}," dockercloud\u002Fhaproxy\n",[73,413,415,418],{"class":75,"line":414},24,[73,416,417],{"class":206},"    depends_on",[73,419,234],{"class":210},[73,421,423,425],{"class":75,"line":422},25,[73,424,263],{"class":210},[73,426,251],{"class":139},[73,428,430,432],{"class":75,"line":429},26,[73,431,272],{"class":206},[73,433,234],{"class":210},[73,435,437,439],{"class":75,"line":436},27,[73,438,263],{"class":210},[73,440,441],{"class":139}," BALANCE=leastconn\n",[73,443,445,448],{"class":75,"line":444},28,[73,446,447],{"class":206},"    volumes",[73,449,234],{"class":210},[73,451,453,455],{"class":75,"line":452},29,[73,454,263],{"class":210},[73,456,457],{"class":139}," \u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock\n",[73,459,461,463],{"class":75,"line":460},30,[73,462,256],{"class":206},[73,464,234],{"class":210},[73,466,468,470],{"class":75,"line":467},31,[73,469,263],{"class":210},[73,471,472],{"class":139}," 80:80\n",[73,474,476,478],{"class":75,"line":475},32,[73,477,378],{"class":206},[73,479,234],{"class":210},[73,481,483,485],{"class":75,"line":482},33,[73,484,263],{"class":210},[73,486,388],{"class":139},[73,488,490,492],{"class":75,"line":489},34,[73,491,288],{"class":206},[73,493,234],{"class":210},[73,495,497,500],{"class":75,"line":496},35,[73,498,499],{"class":206},"      placement",[73,501,234],{"class":210},[73,503,505,508,510,513,516],{"class":75,"line":504},36,[73,506,507],{"class":206},"        constraints",[73,509,211],{"class":210},[73,511,512],{"class":210}," [",[73,514,515],{"class":139},"node.role == manager",[73,517,518],{"class":210},"]\n",[73,520,522],{"class":75,"line":521},37,[73,523,226],{"emptyLinePlaceholder":225},[73,525,527,530],{"class":75,"line":526},38,[73,528,529],{"class":206},"networks",[73,531,234],{"class":210},[73,533,535,538],{"class":75,"line":534},39,[73,536,537],{"class":206},"  web",[73,539,234],{"class":210},[73,541,543,546,548],{"class":75,"line":542},40,[73,544,545],{"class":206},"    driver",[73,547,211],{"class":210},[73,549,550],{"class":139}," overlay\n",[17,552,553],{},"Let’s explain what the hell is happening here. We create 2 services:",[35,555,556,574,608],{},[38,557,558,559,562,563,565,566,569,570,573],{},"The first service is our Node.js app (that call ",[59,560,561],{},"awesome","). It builds that service from an image that call ",[59,564,561],{}," (that we previously built). We expose port 8080, and add an environment variable named ",[59,567,568],{},"SERVICE_PORTS"," with the port we exposed (we do it for the HAProxy, I’ll explain why later on). We also create 20 replicas from that image (20 containers) and have some update and restart settings for them. The important thing to notice is that we put all those containers in a network called ",[59,571,572],{},"web"," (we create the network later, in the end of the file).",[38,575,576,577,579,580,582,583,586,587,589,590,592,593,596,597,600,601,603,604,607],{},"The second service we create is ",[59,578,185],{}," from the haproxy Docker (the company) uses in their cloud (we don’t build it and doesn’t create a ",[59,581,127],{}," for it, we just specify from where to pull the image). It ",[59,584,585],{},"depends_on"," the ",[59,588,561],{}," service, so it won’t boot until the ",[59,591,561],{}," service is finished (i.e. all of the container are up and running). It’s also shared the ",[59,594,595],{},"docker.sock"," file (",[59,598,599],{},"volumes"," field). This is the file the HAProxy container needs to look at to learn about the containers in its network (new and existing containers). We expose port 80, and put this container in the ",[59,602,572],{}," network, In the ",[59,605,606],{},"deploy"," setting, we just tell it to always put this container on the manager node (these settings are related to Docker Swarm, when we have a couple of nodes (i.e. servers).",[38,609,610,611,613],{},"The last thing we do is to create the network (named ",[59,612,572],{},").",[17,615,616],{},[617,618],"img",{"alt":619,"className":620,"src":623},"Our project looks good so far, and we almost at the finish line!",[621,622],"rounded-lg","mx-auto","\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Four-project-looks-good-so-far.webp",[51,625,627],{"id":626},"_3-dockercloud-haproxy","3. DockerCloud HAProxy",[17,629,630,631,636,637,639,640,642,643,646,647,650,651,654],{},"For our HTTP Server we use HAProxy, but not the regular version of HAProxy, we use the version ",[24,632,635],{"href":633,"rel":634},"https:\u002F\u002Fgithub.com\u002Fdocker\u002Fdockercloud-haproxy",[28],"docker uses in their cloud",". That’s why we set an environment variable named ",[59,638,568],{}," in our ",[59,641,561],{}," service, because this is the ports that will be exposed to HAProxy (we can put multiple ports there by separating them with a comma). We also set an environment variable named ",[59,644,645],{},"BALANCE"," to set the load balancing algorithm to be ",[59,648,649],{},"leastconn"," and not ",[59,652,653],{},"roundrobin"," (which is what's set by default).",[656,657,659],"h3",{"id":658},"_31-docker-swarm","3.1 Docker Swarm",[17,661,662,663,666],{},"Now let’s create a swarm (with one computer for now, but you can easily add more to the swarm). To do this we'll write ",[59,664,665],{},"docker swarm init"," and we created a swarm!! It’s also added our computer to the swarm, and because our computer is the first it’s also the manager of the swarm.",[17,668,669,670,673,674,677,678,680,681,684,685,687,688,690,691,694],{},"The network, the services, and all the containers called ",[59,671,672],{},"stack",". To create our stack we need to use ",[59,675,676],{},"docker stack"," command, but we want to point the stack to our ",[59,679,195],{}," file, so it’ll build the stuck according to our plan there. We can do it by writing ",[59,682,683],{},"docker stack deploy --compose-file=docker-compose.yml prod",". we use the ",[59,686,606],{}," command to deploy a new stack (we’ll also use it to update an existing stack), we add a flag to point it to our ",[59,689,195],{}," file. And finally we call our stack ",[59,692,693],{},"prod"," (this is what came up in my head when I wrote those lines, sorry).",[17,696,697,698,701],{},"When we’ll hit ",[59,699,700],{},"http:\u002F\u002Flocalhost"," we’ll get the container id in the response, and we can see it has a different id every request.",[17,703,704],{},[617,705],{"alt":706,"className":707,"src":708,"height":709,"width":710},"Different container id every request",[621,622],"\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Fdifferent-container-id-evert-request.webp",195,171,[17,712,713,714,717],{},"Now let’s look at our services by writing ",[59,715,716],{},"docker service ls"," and we’ll see all of our services and replicas",[17,719,720],{},[617,721],{"alt":722,"className":723,"src":724,"height":725,"width":726},"All of our docker services",[621,622],"\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Fall-of-our-docker-services.webp",81,800,[17,728,729,730,732],{},"We can also create a second version of our ",[59,731,561],{}," app. Let’s change the code a little bit (let’s add some exclamation marks at the end):",[64,734,736],{"className":66,"code":735,"filename":61,"language":68,"meta":69,"style":69},"var http = require(\"http\");\nvar os = require(\"os\");\nhttp\n  .createServer(function (req, res) {\n    res.writeHead(200, { \"Content-Type\": \"text\u002Fhtml\" });\n    res.end(`\u003Ch1>I'm ${os.hostname()}!!!\u003C\u002Fh1>`);\n  })\n  .listen(8080);\n",[59,737,738,742,746,750,754,758,763,767],{"__ignoreMap":69},[73,739,740],{"class":75,"line":76},[73,741,79],{},[73,743,744],{"class":75,"line":82},[73,745,85],{},[73,747,748],{"class":75,"line":88},[73,749,91],{},[73,751,752],{"class":75,"line":94},[73,753,97],{},[73,755,756],{"class":75,"line":100},[73,757,103],{},[73,759,760],{"class":75,"line":106},[73,761,762],{},"    res.end(`\u003Ch1>I'm ${os.hostname()}!!!\u003C\u002Fh1>`);\n",[73,764,765],{"class":75,"line":112},[73,766,115],{},[73,768,769],{"class":75,"line":118},[73,770,121],{},[17,772,773,774,777,778,780,781,784,785,787,788,790,791,794,795,797,798,800,801,639,804,806],{},"So we need to build the image again, but this time it’s the second version of the app so we’ll write ",[59,775,776],{},"docker build -t awesome:v2 ."," and we’ll create an image called ",[59,779,561],{}," but with a ",[59,782,783],{},"v2"," tag. To update our containers in the ",[59,786,561],{}," service to use the ",[59,789,783],{}," version of our app (without stop the service) we’ll write ",[59,792,793],{},"docker service update --image awesome:v2 prod_awesome"," and our service called ",[59,796,561],{},", in ",[59,799,693],{}," stack, will update its containers five by five to use the second version of our app (why 5 containers at a time? because we wrote ",[59,802,803],{},"parallelism: 5",[59,805,195],{}," file.",[17,808,809,810,812],{},"We can see our docker slowly (but surely) kill the old containers and create new ones with the second version of our app. And when we hit ",[59,811,700],{}," we still get a response, there is no downtime.",[17,814,815],{},[617,816],{"alt":817,"className":818,"src":819,"height":820,"width":821},"Some containers are already use the second version. No Downtime (:",[621,622],"\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Fsome-containers-are-already-use-the-second-version.webp",173,218,[17,823,824,825,828,829,832],{},"Also, if we want to scale the service to more than 20 containers, we can do it with only one command: ",[59,826,827],{},"docker service scale prod_awesome=50"," and docker will start 30 more containers from the ",[59,830,831],{},"awesome:v2"," image.",[51,834,836],{"id":835},"_4-summary","4. Summary",[17,838,839,840,843],{},"So, we don’t need to create hundreds of containers manually. We don’t need to place every container of our app in a different port. We don’t need to manually write our containers ip and port in nginx\u002Fhaproxy ",[59,841,842],{},"conf"," file. And we can do it with multiple servers (with docker swarm), with multiple services (with docker compose), update our application without downtime, and scale it up (or down) without downtime.",[17,845,846],{},"Hope it’s been practical, and I would love to hear how you do it in your company!",[848,849,850],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":69,"searchDepth":82,"depth":82,"links":852},[853,854,855,858],{"id":53,"depth":82,"text":54},{"id":176,"depth":82,"text":177},{"id":626,"depth":82,"text":627,"children":856},[857],{"id":658,"depth":88,"text":659},{"id":835,"depth":82,"text":836},"2017-07-11","Load balancing containerized apps with Docker Compose, Docker Swarm, and HAProxy.",false,"md",{"src":864},"\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Flets-start.webp",{},"\u002Fblog\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker",{"title":5,"description":860},"blog\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker","ky06CKpQCPXqRqO7qAtalPXewBR9X-PbvnUvx_KvnpY",[871,876],{"title":872,"path":873,"stem":874,"description":875,"children":-1},"Jekyll Starter Kit generator 2.1.0 is out!","\u002Fblog\u002F2017\u002Fjekyll-starter-kit-generator-2.1.0-is-out","blog\u002F2017\u002Fjekyll-starter-kit-generator-2.1.0-is-out","What's new in version 2.1.0 of the Jekyll Starter Kit generator.",{"title":877,"path":878,"stem":879,"description":880,"children":-1},"Crack The Hash","\u002Fblog\u002F2020\u002Fcrack-the-hash","blog\u002F2020\u002Fcrack-the-hash","Cracking password hashes with Hashcat on a GPU-equipped GCP instance.",1786001638688]