Scale Docker horizontally with Nginx load balancing
Learn how to scale applications horizontally by distributing requests across multiple service instances with Nginx load balancing.
Lesson Content
Let’s create a nodejs directory for the application files:
mkdir nodejs
cd nodejsCreate a simple Node.js application that responds with Hello World from server, followed by the server name from an environment variable:
cat > index.js <<'EOF'
var http = require('http');
var serverName = process.env.SERVER_NAME || 'default';
var port = process.env.PORT || 8000;
var server = http.createServer(function (request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end(`Hello World from server ${serverName}\n`);
});
server.listen(port);
console.log(`Server running at http://127.0.0.1:${port}/`);
EOFThe application listens on port 8000 inside the container. Create a Dockerfile that starts the Node.js process:
cat > Dockerfile <<'EOF'
FROM mhart/alpine-node
COPY index.js .
EXPOSE 8000
CMD node index.js
EOFBuild the application image and name it app-nodejs:
docker build -t app-nodejs .Start two containers from this image. Set the first server’s name to chicken and the second server’s name to steak:
docker run -d -e "SERVER_NAME=chicken" --name=chicken app-nodejs
docker run -d -e "SERVER_NAME=steak" --name=steak app-nodejsWe don’t publish port 8000 from either container. Nginx handles external requests, so the application containers only need to be reachable by other Docker containers. Keeping the application ports off the host also prevents the Node.js processes from being directly accessible through the public network.
Now return to the project root and create an nginx directory:
cd ..
mkdir nginx
cd nginxCreate nginx.conf with a server block that listens on the default HTTP port. Its root location passes requests to an upstream named app:
cat > nginx.conf <<'EOF'
upstream app {
server chicken:8000;
server steak:8000;
}
server {
listen 80;
location / {
proxy_pass http://app;
}
}
EOFThe upstream block defines the servers that receive requests. Nginx distributes requests between chicken and steak using round-robin balancing. You can add more server lines, and you can assign a server a weight when you want it to receive more or fewer requests than the others.
Create a Dockerfile for Nginx that copies this configuration into the default configuration location:
cat > Dockerfile <<'EOF'
FROM nginx
COPY nginx.conf /etc/nginx/conf.d/default.conf
EOFBuild the image and name it app-nginx:
docker build -t app-nginx .Start the Nginx container and publish port 8080 on the host to port 80 in the container. Link the chicken and steak containers so Nginx can resolve their names:
docker run -d \
--name app-nginx \
-p 8080:80 \
--link chicken \
--link steak \
app-nginxNginx now accepts requests on the host’s port 8080 and forwards them to the two Node.js containers. Send several requests with curl to see the responses alternate between the servers:
curl http://localhost:8080
curl http://localhost:8080
curl http://localhost:8080
curl http://localhost:8080The responses come from chicken and steak in round-robin order. Nginx provides a single public endpoint while distributing the application traffic across multiple Node.js containers.
Join the discussion!
Comments
Want to comment on this lesson?
Log in to leave comments.