DEV Community

Cover image for How to run database, backend and frontend in a single-click with Docker Compose
Wojtek Krzywiec
Wojtek Krzywiec

Posted on • Originally published at wkrzywiec.Medium

How to run database, backend and frontend in a single-click with Docker Compose

In this blog post I would like to show you how easy you can run 3 different Docker containers all at once using single command.

Alt Text

Docker Compose

In order to understand what problem Docker Compose solves you need to understand what Docker, or more general containerization, do.

Basically Docker containers are applications and all dependencies (libraries, artifacts, etc.) which are required to run it. For example all Java applications needs to have JDK installed on the machine where you would like to compile your code. Thanks to Docker you don’t need to install any of it so the software can be ran wherever you want.

So Docker solves the problem ‘Works on my machine’. Great! But what next?

As you might already heard it, nowadays one of the most popular architecture pattern are microservices. Each microservice is a small app designed to do only little part the entire system. They are put into the containers (usually Docker) and interact with each other (usually by HTTP REST API).

So now we can have a problem. If you’re building such complex system with multiple services you might end up with hundreds of such containers. And such is very hard to manage. Imagine the situation when you want to run all of them, and if you try to do so only with ‘vanilla’ Docker you may waist an entire day just typing the commands for each microservice!

But likely there is a hope! Docker Compose.

This tool can help you build Docker images and run containers based on a single file docker-compose.yml!

How it looks like and how you can use it? This is what I would like to show you, but first let me introduce my project which should help you understand the idea of Docker Compose.

Kanban Application

Kanban Board — is a simple implementation of a kanban board, tool that was first introduced in Toyota automotive and now it’s widely used software development for managing tasks. If you want to know more about it go check What is a Kanban Board? video to help you understand it basic concept.

Alt Text

My small application is made of 3 smaller pieces:

  • kanban-postgresPostgreSQL database,

  • kanban-app — Java based application, it connects to database and expose REST endpoints, its written with Spring Boot,

  • kanban-uiAngular, frontend application, it consumes the REST endpoints provided by the backend service.

Prerequisites

In order to follow me you will need to have Docker installed on your machine. The best part is that you don’t need to have installed all the dependencies, like PostgreSQL, Java or Angular to run the app. The only thing that you need to install is Docker!

Instructions how to install Docker on Ubuntu , Windows , Mac.

Docker Compose is already included in installation packs for Windows and Mac, so only Ubuntu users needs to follow this instructions.

Let’s run it!

Now the fun part. First git clone my project from my GitHub repository, it can be done with a command (in the terminal):

> git clone https://github.com/wkrzywiec/kanban-board.git
Enter fullscreen mode Exit fullscreen mode

Next, in the terminal enter the root folder of the project and type the command:

> docker-compose up
Enter fullscreen mode Exit fullscreen mode

Now, you need to wait couple minutes so all 3 Docker containers will be up and running. After that you’ll be able to enter a frontend in a browser with a link: http://localhost:4200.

Alt Text

Awesome! If you want check what REST endpoint the backend is providing you can its Swagger UI page, and which is reachable from the url: http://localhost:8080/api/swagger-ui.html.

Alt Text

And if you’re a database geek you can also connect to it using PgAdmin, Dbeaver or other database client app with following credentials:

  • Host: localhost

  • Database: kanban

  • User: kanban

  • Password: kanban

Alt Text

If you want to stop all these apps all at once you can run the command:

> docker-compose down
Enter fullscreen mode Exit fullscreen mode

docker-compose.yml

Ok, so how this magic really works? The key to is a configuration file docker-compose.yml located in the root folder of my project.

version: '3'
services:

  kanban-postgres:
    image: "postgres:9.6-alpine"
    container_name: kanban-postgres
    volumes:
      - kanban-data:/var/lib/postgresql/data
    ports:
      - 5432:5432
    environment:
      - POSTGRES_DB:kanban
      - POSTGRES_USER:kanban
      - POSTGRES_PASSWORD:kanban

  kanban-app:
    build: ./kanban-app
    container_name: kanban-app
    environment:
      - DB_SERVER=kanban-postgres
    ports:
      - 8080:8080
    links:
      - kanban-postgres

  kanban-ui:
    build: ./kanban-ui
    container_name: kanban-ui
    ports:
      - 4200:80
    links:
      - kanban-app


volumes:
  kanban-data:
Enter fullscreen mode Exit fullscreen mode

Let’s break it piece by piece.

  • version: 3 — this indicates a version of a Compose file, the latest one is the 3 and that’s way I picked it (more info about versioning of Compose file can be found in the official documentation)

  • services: — after this variable you can define all services (applications) that you want to run in one go. In my project there are 3 of them.

kanban-postgres

kanban-postgres:
    image: "postgres:9.6-alpine"
    container_name: kanban-postgres
    volumes:
      - kanban-data:/var/lib/postgresql/data
    ports:
      - 5432:5432
    environment:
      - POSTGRES_DB:kanban
      - POSTGRES_USER:kanban
      - POSTGRES_PASSWORD:kanban
Enter fullscreen mode Exit fullscreen mode
  • image — defines the Docker image that will be the base of the kanban-postgres container. In this case it’s Postgres image taken from Docker Hub. Alpine version is much smaller than the usual one.

  • container_name — specifies the name of the outcome container.

  • volumes — refers to Docker Volumes, which can be treated as a data storage of the database. kanban-data is a name of a volume that will be created during first Compose run (it’s defined at the bottom of the docker-compose.yml). /var/lib/postgresql/data is a place on the host (your PC) where data are stored.

If you want to know more about Docker Volumes and databases in Docker containers you can check my previous entry about this topic.

  • ports — here we map a port of your PC ( 5432 ) to the exposed port of the container ( 5432 ).

  • environment — here you can define environment variables that will be inside Docker container. You can define your own or, like I did, overwrite the already existing one.

In postgres:9.6-alpine Docker image there are several variables that defines the name of the user, database and others. By simple replacing them with your own you can adjust them to your needs. For my project I wanted to have the database, user name and user password to be the same and have value kanban.

kanban-app

 kanban-app:
    build: ./kanban-app
    container_name: kanban-app
    environment:
      - DB_SERVER=kanban-postgres
    ports:
      - 8080:8080
    links:
      - kanban-postgres
Enter fullscreen mode Exit fullscreen mode
  • build — this one is similar to the image from the previous example, but instead of using already prepared Docker image, Compose needs to create one based on Dockerfile. And such is located in the folder kanban-app where is the code of Spring Boot app.
### STAGE 1: Build ###
FROM maven:3.6.1-jdk-8-slim AS build
RUN mkdir -p /workspace
WORKDIR /workspace
COPY pom.xml /workspace
COPY src /workspace/src
RUN mvn -f pom.xml clean package

### STAGE 2: Run ###
FROM openjdk:8-alpine
COPY --from=build /workspace/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
Enter fullscreen mode Exit fullscreen mode

The above Dockerfile consists two build stages. In a first one (2–7 line) the code of an app is compiled to the jar file and then in the second stage (10–13 line) the jar file is copied to it and then run in a new container.

Each stage is a different Docker image, but only the last one is used as the outcome. The reason for this approach is to make a resulting Docker container as thin as it could be — it should contain only the compiled jar file without source code.

  • container_name — express the name of the resulting container.

  • environment — like before, it contains environment variables, but this time it’s my own variable. It’s used in database connection properties file:

spring.datasource.url=jdbc:postgresql://${DB_SERVER}/kanban
spring.datasource.username=kanban
spring.datasource.password=kanban
spring.liquibase.change-log=classpath:/db/changelog/db.changelog-master.xml
Enter fullscreen mode Exit fullscreen mode
  • ports — similar to previous example, it maps the port of your host to the container’s.

  • links — it express the dependency of a kanban-app to a database service kanban-postgres.

kanban-ui

kanban-ui:
    build: ./kanban-ui
    container_name: kanban-ui
    ports:
      - 4200:80
    links:
      - kanban-app
Enter fullscreen mode Exit fullscreen mode

Nothing new is added to definition of this service. Similar to previous ones it defines the container_name , ports (this time the host’s 4200 port is mapped to the container’s 80 ) and links it to the backend service.

The only difference is that a base Docker image needs to be built and it’s located in kanban-ui folder, where Angular code is located with it’s Dockerfile.

### STAGE 1: Build ###
FROM node:12.7-alpine AS build
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY . .
RUN npm run build

### STAGE 2: Run ###
FROM nginx:1.17.1-alpine
COPY default.conf /etc/nginx/conf.d/default.conf
COPY --from=build /usr/src/app/dist/kanban-ui /usr/share/nginx/html
EXPOSE 80
Enter fullscreen mode Exit fullscreen mode

Similar to Java container it has two build stages, first one for compilation and second one for running compiled project. And because the application is running on nginx server there is also some default configuration added to it (line COPY default.conf /etc/nginx/conf.d/default.conf). More information about multi-stage Docker build for Angular project you can find in my previous blog post.

Docker Compose commands

When you’ll be playing around with Docker Compose you might want to use also different CLI commands that it offers. The full list can be found on the official website, but found these the most useful during development of my app:

> docker-compose up -d --force-recreate
Enter fullscreen mode Exit fullscreen mode

The -d flag runs all the containers in the detach mode — all containers run in the background, so you can use a terminal for other purposes.

The --force-recreate flag will stop all old containers that are running and will create them from scratch. It was very useful when I’ve done some changes to several application at once.

> docker-compose down --rmi all
Enter fullscreen mode Exit fullscreen mode

Above command stops and removes all images.

Limitation of Docker Compose

At the end I want to share with you what I didn’t like about Docker Compose.

When I was working on kanban-ui several times I’ve stomped on the problem that some of required functionality wasn’t implemented on the backend side. Therefore I was forced to stop all containers, make a change, run Compose again and then test it. And that takes me a lot of time because building a Java and Angular image takes several minutes, which forced me to stair on the wall during this process.

Nevertheless, it awesome tool to use, especially when you’re working on multi-service project and you develop only single service.

As usual, you can check the entire Kanban Board project in my Github repository:

GitHub logo wkrzywiec / kanban-board

Single-click full-stack application (Postgres, Spring Boot & Angular) using Docker Compose

References

Top comments (4)

Collapse
 
pbose profile image
pBose

Sir big fan of your content, nice article.
I am working on one project on docker for developers, if you give some honest feedback that will be a great help for us
get-deck.com

Collapse
 
wkrzywiec profile image
Wojtek Krzywiec

Sure thing, in upcoming days I'll give it a try 🙂 thanks

Collapse
 
siy profile image
Sergiy Yevtushenko

Microservices are most hyped architecture pattern, but, fortunately, in real life it's much less frequent than in articles.

Collapse
 
wkrzywiec profile image
Wojtek Krzywiec

That's absolutely true. Microservices are now very popular in blog posts like mine, as they're quite new and thrilling. But of course they're not magic, they address some problems that monolith have, but microservices have their own.

I'm aware that only looking on a number of publications about them we could have wrong impression that they are silver bullet for anything. Which is not true. In many cases monolithic application is better than microservies.

And as I observe I think people are staring to see it, and going back to monoliths (with a new flavours, lessons learned from microservices, like modular monolith).