I have been using Docker for building (compile/packaging) and running web applications for some time. Through this blog, I would like to share how I used docker for building and running a complete (Angular/SpringBoot) webapp in a local and production environments.
Docker with its simplicity and light-weight containers is an excellent choice for developer environment. As Docker is lightweight, one can run multiple containers simultaneously for fine-grained components and tasks. Not to mention the benefit of having a production-like development environment.
The process of building and running the application in a Docker container is a big time saver for daily local builds, Frontend developers can use a Docker container to build and run backend services with the right version of code without having to install backend build and runtime tools locally on their machines.
Below you will find a sample Docker configuration for a complete stack (frontend/backend build and runtime) using existing published docker images. It took me some time to setup Angular CLI build container.
The Frontend Angular code (frontend/src) is built in frontend/dist folder using AngularCLI (ng build) by build container(../frontend/Dockerfile_build). The dist folder and Ngnix configurations are used by Ngnix container to run the Angular app.
Backend code (backend/src) is built using the Gradle container(../backend/Dockerfile_build) with output jar in backend/build/libs folder. The jar is then used by the Java container(../backend/Dockerfile) to run the SpringBoot app.
Java code is compiled and packaged using Gradle/Java8. I took the image file from Offical Docker image library: https://hub.docker.com/_/gradle/
Build this image from backend folder using Dockerfile_build, run using
docker run --rm -v "$PWD":/home/gradle/project -w /home/gradle/project im-build-backend gradle build
It generates the SpringBoot app as an executable jar in the building/libs folder.
This is a regular JDK container with SpringBoot app jar.
Build/run this container from frontend project folder which has package.json and src folders.
Copy the Ngnix confs and dist folder to the Ngnix container to run the Angular app
docker run -it -p 80:80 im-frontend
Run docker-compose up -d to build and run the frontend and backend services.
Output:
In the production configuration, build images are excluded.
Run docker-compose up -d to run the frontend and backend services.
Output:
Docker for development environment
In large enterprise projects with distributed development teams, it becomes very difficult to manage and maintain a consistent development environment across developers. The option of VirtualBox/Vagrant/Chef/Puppet is too complex, bulky and resource intensive to be viable for this use-case.Docker with its simplicity and light-weight containers is an excellent choice for developer environment. As Docker is lightweight, one can run multiple containers simultaneously for fine-grained components and tasks. Not to mention the benefit of having a production-like development environment.
Docker for building artifacts
In typical production usage, Docker is plugged into the CI/CD pipeline, where the artifacts are already generated and docker machines are spun to run them. In a development environment, Docker can itself act as a build machine with the right build configuration to generate the artifacts. Those artifacts can then be deployed to other docker containers to run them.The process of building and running the application in a Docker container is a big time saver for daily local builds, Frontend developers can use a Docker container to build and run backend services with the right version of code without having to install backend build and runtime tools locally on their machines.
Below you will find a sample Docker configuration for a complete stack (frontend/backend build and runtime) using existing published docker images. It took me some time to setup Angular CLI build container.
Project Structure
The sample project is Angular frontend running on Ngnix with SpringBoot(Tomcat) as backend services. Angular is built with Yarn/NPM/Angular CLI and backend with Gradle/Java8
As shown in the figure below, both backend and frontend have two docker files, one to build the component (Dockerfile_Build) and the second one to run it (Dockerfile). Docker-compose is used to tie the application together with the proper order of components and dependencies.
The local docker-compose will include the build steps to build the artifact and run them, whereas the production docker-compose will get QA certified artifacts from CI/CD process and would just run them in the containers.
The local docker-compose will include the build steps to build the artifact and run them, whereas the production docker-compose will get QA certified artifacts from CI/CD process and would just run them in the containers.
sampleProject backend Dockerfile_build Dockerfile src build libs frontend Dockerfile_build Dockerfile src nginx-config dist ops local docker-compose.yml production docker-compose.yml
The Frontend Angular code (frontend/src) is built in frontend/dist folder using AngularCLI (ng build) by build container(../frontend/Dockerfile_build). The dist folder and Ngnix configurations are used by Ngnix container to run the Angular app.
Backend code (backend/src) is built using the Gradle container(../backend/Dockerfile_build) with output jar in backend/build/libs folder. The jar is then used by the Java container(../backend/Dockerfile) to run the SpringBoot app.
Backend Gradle Build Container
Java code is compiled and packaged using Gradle/Java8. I took the image file from Offical Docker image library: https://hub.docker.com/_/gradle/FROM openjdk:8-jdk CMD ["gradle"] ENV GRADLE_HOME /opt/gradle ENV GRADLE_VERSION 4.2.1 ARG GRADLE_DOWNLOAD_SHA256=b551cc04f2ca51c78dd14edb060621f0e5439bdfafa6fd167032a09ac708fbc0 RUN set -o errexit -o nounset \ && echo "Downloading Gradle" \ && wget --no-verbose --output-document=gradle.zip "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" \ \ && echo "Checking download hash" \ && echo "${GRADLE_DOWNLOAD_SHA256} *gradle.zip" | sha256sum --check - \ \ && echo "Installing Gradle" \ && unzip gradle.zip \ && rm gradle.zip \ && mv "gradle-${GRADLE_VERSION}" "${GRADLE_HOME}/" \ && ln --symbolic "${GRADLE_HOME}/bin/gradle" /usr/bin/gradle \ \ && echo "Adding gradle user and group" \ && groupadd --system --gid 1000 gradle \ && useradd --system --gid gradle --uid 1000 --shell /bin/bash --create-home gradle \ && mkdir /home/gradle/.gradle \ && chown --recursive gradle:gradle /home/gradle \ \ && echo "Symlinking root Gradle cache to gradle Gradle cache" \ && ln -s /home/gradle/.gradle /root/.gradle # Create Gradle volume USER gradle VOLUME "/home/gradle/.gradle" WORKDIR /home/gradle RUN set -o errexit -o nounset \ && echo "Testing Gradle installation" \ && gradle --version
docker run --rm -v "$PWD":/home/gradle/project -w /home/gradle/project im-build-backend gradle build
It generates the SpringBoot app as an executable jar in the building/libs folder.
Backend SpringBoot Container
This is a regular JDK container with SpringBoot app jar.FROM frolvlad/alpine-oraclejdk8:slim VOLUME /tmp COPY build/libs/mySample.jar app.jar RUN sh -c 'touch app.jar' ENV JAVA_OPTS="" ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -jar /app.jar" ]
docker run -d -p 8080:8080 im-backend
Frontend Yarn/AngularCLI Build Container
This container has Yarn, Node, Angular CLI and NPM to build Angular app.
FROM/angular- alexsuch cli 1.4.8 :
Build/run this container from frontend project folder which has package.json and src folders.
docker run -it --rm -w /app -v $( pwd ): /app im -frontend -build ng build
FrondEnd Ngnix Container
Copy the Ngnix confs and dist folder to the Ngnix container to run the Angular appFROMCOPY nginx -config/ nginx nginx . /etc/ conf COPY nginx / dist /www/ var im
docker run -it -p 80:80 im-frontend
DockerCompose local
Output:
local romiawasthy$ docker-compose up -d Creating network "local_default" with the default driver Creating local_build_BackEnd_1 ... Creating local_build_BackEnd_1 ... done Creating local_backend_1 ... Creating local_backend_1 ... done Creating local_build_FrontEnd_1 ... Creating local_build_FrontEnd_1 ... done Creating local_frontend_1 ... Creating local_frontend_1 ... done
DockerCompose production
Output:
production romiawasthy$ docker-compose up -d Starting production_backend_1 ... Starting production_backend_1 ... done Creating production_frontend_1 ... Creating production_frontend_1 ... done