Communication with other Docker containers

Docker
Sponsored links

When we want decide to use Docker container we probably have multiple Docker containers. In that case each container has to communicate with each other and they must be of course in the same network. Docker container is in theory isolated from Host machine and other containers. To enable the communications, we need to create a network for Docker containers.

You can find the complete source code here

This is one of Docker learning series posts.

  1. Start Docker from scratch
  2. Docker volume
  3. Bind host directory to Docker container for dev-env
  4. Communication with other Docker containers
  5. Run multi Docker containers with compose file
  6. Container’s dependency check and health check
  7. Override Docker compose file to have different environments
  8. Creating a cluster with Docker swarm and handling secrets
  9. Update and rollback without downtime in swarm mode
  10. Container optimization
  11. Visualizing log info with Fluentd, Elasticsearch and Kibana
Sponsored links

log-server

To test the network functionality we need to have at least two applications. I created small log server at first. Following code is the all.

import * as restify from "restify";
import * as winston from "winston";

const logger = winston.createLogger({
    level: "info",
    format: winston.format.json(),
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'server.log' }),
    ],
});

const server = restify.createServer();
let calledCount = 0;
server.get('/status', (req, res, next) => {
    res.send(`Log called count: ${calledCount}`);
    next();
});

server.post('/log', (req, res, next) => {
    calledCount++;
    try {
        logger.info(`${req.body.name}: ${req.body.data}`);

        res.send(201, "Created");
    } catch (e) {
        logger.error(`name: ${req.body?.name}, data: ${req.body?.data}`);
        res.send(400, "Bad Request");
    }
    next();
});

server.use(restify.plugins.bodyParser());

const port = 80;
server.listen(port, function () {
    logger.info(`${server.name} listening at ${server.url}`);
});

You can test if the log-server works. But build first.

  1. cd log-server
  2. npm install
  3. npm run build
  4. docker image build -t log-server .
  5. docker container run --rm -p 8001:80 --name log-server log-server
$ curl -X POST -H "Content-Type:application/json" -d '{"name": "curl-test", "data": "log-test-data"}' http://localhost:8001/log
"Created"

log-server’s output is following.

$ docker container run --rm -p 8001:80 --name log-server log-server

{"message":"restify listening at http://[::]:80","level":"info"}
{"message":"curl-test: log-test-data","level":"info"}
Sponsored links

restify-server

Second application is restify server. I copied sample code from restify and modified a little.

// Logger.ts
import * as http from "http";
import { URL } from "url";

export class Logger {
    private readonly logUrl = new URL("/log", process.env.LOGGER_API_URL);
    constructor(private name: string) { }

    public log(message: string) {
        const postData = JSON.stringify({
            name: this.name,
            data: message,
        });
        const options: http.RequestOptions = {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Content-Length': Buffer.byteLength(postData),
            }
        };

        const req = http.request(this.logUrl, options, (res) => {
            console.log(`STATUS: ${res.statusCode}`);
            console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
            res.setEncoding('utf8');
            res.on('data', (chunk) => {
                console.log(`BODY: ${chunk}`);
            });
            res.on('end', () => {
                console.log('No more data in response.');
            });
        });

        req.on('error', (e) => {
            console.error(`problem with request: ${e.message}`);
        });

        // Write data to request body
        req.write(postData);
        req.end();
    }
}

// server.ts
import * as restify from "restify";
import { Logger } from "./Logger";

const server = restify.createServer();
const logger = new Logger("restify-server");

function respond(
    req: restify.Request,
    res: restify.Response,
    next: restify.Next
) {
    logger.log(`GET request with param [${req.params.name}]`);
    res.send('hello ' + req.params.name);
    next();
}

server.get('/hello/:name', respond);
server.head('/hello/:name', respond);

const port = 80;
server.listen(port, function () {
    logger.log(`${server.name} listening at ${server.url}`);
});
  1. cd restify-server
  2. npm install
  3. npm run build
  4. docker image build -t restify-server .

Start the applications without network definition

Since restify-server posts data to log-server at start up log-server must start up first. And then, start restify-server.

$ docker container run --rm -p 8001:80 --name log-server log-server
{"message":"restify listening at http://[::]:80","level":"info"}

Run following command from different console.

$ docker container run --rm -p 8002:80 --name restify-server restify-server
problem with request: getaddrinfo ENOTFOUND log-server

It shows an error because we haven’t created the network for it. The network is completely isolated by default. Let’s create a network named log-test-nat here to place both applications in the same network.

docker network create log-test-nat

Once network is created it needs to be specified by --network option when executing docker run command. Let’s specify the network for both applications.

docker container run --rm -p 8001:80 --name log-server --network log-test-nat log-server

From different console

docker container run --rm -p 8002:80 --name restify-server --network log-test-nat restify-server

You can also execute the same command by npm run dstart because I I defined it in package.json. After restify-server starts up access to the server from browser. You can access to the server by http://localhost:8002/hello/foo-hoge for example and log-server shows following message.

{"message":"restify-server: GET request with param [foo-hoge]","level":"info"}

I specified follwoing code in Dockerfile of restify-server. The host name is log-server which I set with --name option. This container name becomes DNS name of the container, so other container can find the host by the name.

ENV LOGGER_API_URL="http://log-server:80/"

Conclusion

It’s very easy to create network to communicate each other. Just create a named network by docker network create <network name> before running applications and specify it with --network <network name> when executing Docker container run command. However, the command is getting long when we introduce new functionality. We can create batch file or define script in package.json for example but Docker offers another option Docker Compose. Next post is for the functionality!

Comments

Copied title and URL