Video Lesson: Scale Docker horizontally with Nginx load balancing

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 nodejs

Create 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}/`);
EOF

The 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
EOF

Build 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-nodejs

We 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 nginx

Create 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;
    }
}
EOF

The 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
EOF

Build 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-nginx

Nginx 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:8080

The 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.

Maggie

Hey, I'm Maggie! 👋

Ask me anything.

Want to chat with Maggie?

Log in for AI tutor access.

Comments

Join the discussion!

Notes

Keep track of what you learn!