Developing Docker Multiservices Application Using Git Submodule: A Study Case of Laravel Lumen API and Vue for Covid-19 Worldometers Data
Good day friends!😊
Today we will explore again our very useful friend, Docker, but we will step further! We will use Docker to make multi containers or services for 2 different github projects using GIT submodule.
What is GIT submodule?
Git submodule is one of GIT feature that allow us to keep a GIT repository as a subdirectory of another GIT repository. This lets us clone another repository into our project and keep your commits separate. If you want to know more about GIT submodule, you can read it in GIT Submodule Official Documentation HERE.
Anyway, we will create docker based on our article about Covid-19 application project before:
- API using Laravel Lumen — https://medium.com/remote-worker-indonesia/create-api-covid-19-data-using-laravel-lumen-and-swagger-as-documentation-24edd56ea0d0
- Frontend apps using Vue — https://medium.com/remote-worker-indonesia/create-covid-19-website-using-vue-axios-to-consume-laravel-lumen-api-b91c82434d31
As always let’s start our journey with planning.
The Plan
- For API we will use this project:
https://github.com/haniefhan/covid_19_api
- For frontend apps we will use this project:
https://github.com/haniefhan/covid_19_vue
- Create 2 containers for each project using docker compose
- Both of the containers will use Alpine Linux as its Operating System. Why use this OS? simple, because it’s lightweight and frequently used for production server.
- Use one of GIT feature called git submodule, this feature allow us to keep a Git repository as a subdirectory of another Git repository.
Requirement
- Stable Internet Connection, of course, because Docker will find and download all the files and packages we need from an online repository. — It’s quite a torture to work with docker when there’s a problem with your internet connection, believe me.
- Docker has been installed on your machine. You can download and see how to do it HERE.
Implementation
First what we must do is creating new GIT project (I use github) and name it covid_docker
. Go to project folder in your machine then clone the project using this command:
git clone {your_git_project_url}
If you want to use my github project, you can run this command:
git clone https://github.com/haniefhan/covid_docker.git
After the cloning process is complete, our project folder will look like this:
covid_docker
|- .git
|- README.md // if you create git project with README.md
Our next step is clone our API and frontend projects using git submodule. This is the command:
git submodule add https://github.com/haniefhan/covid_19_api
git submodule add https://github.com/haniefhan/covid_19_vue
It will clone API project into folder covid_19_api
and clone frontend project into folder covid_19_vue
.
This is our project folder looks like now:
covid_docker
|- .git
|- covid_19_api
|- ...
|- covid_19_vue
|- ...
|- .gitmodules
|- README.md // if you create git project with README.md
We know about folder covid_19_api and covid_19_vue come from. But, what is .gitmodules
file? It was file that generated by GIT that contain our project submodules info. This is .gitmodules
file content:
Alright, all set. Let’s move to dockerize our project.
Dockerization
To bundle 2 dockerization process into 1 file, we need docker compose. Of course we still need to create each Dockerfile
per container. This is what we will do.
We will create Dockerfile
for covid_19_api
project, then Dockerfile
for covid_19_vue
project, and the last create docker-compose.yml
to bundle all of Docker instruction before we build it.
Ok, first let’s create new file in folder covid_19_api
with name Dockerfile
.
Covid_19_api Dockerfile
It’s already there? no problem, it’s because I already create it in this article: https://haniefhan.medium.com/create-a-lightweight-but-powerful-docker-for-laravel-lumen-api-58cd0cfc8953
But let’s assume we don’t have Dockerfile
file yet. 😉
Put this content into Dockerfile
file we created:
Explanation:
FROM alpine:3.13
, we use Alpine Linux version 3.13 because it’s really lightweight for our simple server (the image is just around 105.34 Mb include our project) and this version provides us with the PHP version we need (PHP 7.4).RUN apk — no-cache add php7 php7-fpm php7-pdo php7-mbstring php7-openssl
, this command is to install packages we need so that Laravel Lumen can run smoothly.RUN apk — no-chace add php7-json php7-dom curl php7-curl
, because our code using curl, dom-manipulation, and JSON for the response, we need to run the command.RUN apk — no-cache add php7-tokenizer
, we use Swagger to provide API documentation, so we need to add this package.RUN apk — no-cache add php7-phar php7-xml php7-xmlwriter
, we need this command so that composer and itscomposer update
process run smoothly.RUN php7 -r “copy(‘http://getcomposer.org/installer', ‘composer-setup.php’);” && php7 composer-setup.php — install-dir=/usr/bin — filename=composer && php7 -r “unlink(‘composer-setup.php’);”
, composer installation process.COPY . /src WORKDIR /src
, we want to copy the project that we pull before to/src
folder in our server and then make it as ourworkdir
.RUN composer update
, runcomposer update
command in container we created.
Covid_19_vue Dockerfile
Next step is create new file in folder covid_19_vue
with name Dockerfile
. Put this content into Dockerfile
file we created:
Explanation:
FROM node:lts-alpine as build-stage
, we use Node.JS bundle that using Alpine Linux LTS as OS. You can see the detail HERE. We labeled it asbuild-stage
.WORKDIR /app
, change workdir into/app
folder.COPY package*.json ./
, copy all file with namepackage*.json
to workdir (/app
).RUN npm install
, execute commandnpm install
to download our project dependencies that written inpackage.json
file.COPY . .
, copy all of our project files to workdir(/app
).RUN npm run build
, execute commandnpm run build
to build our project.FROM nginx:stable-alpine as production-stage
, second we use nginx bundle that using Alpine Linux Stable as OS. You can see the detail HERE.- And last,
COPY — from=build-stage /app/dist /usr/share/nginx/html
, copy all files in/app/dist
folder inbuild-stage
to/usr/share/nginx/html
folder inproduction-stage
.
Now, our project folder will be like this:
covid_docker
|- .git
|- covid_19_api
|- ...
|- Dockerfile
|- ...
|- covid_19_vue
|- ...
|- Dockerfile
|- ...
|- .gitmodules
|- README.md
Docker-compose.yml
Next step is create docker-compose.yml
file in folder covid_docker
that will execute instruction in each Dockerfile.
The content of docker-compose.yml
file is like this:
Explanation:
version: ‘3.8’
, we use docker-compose version 3.8, because it’s the newest one.services: > api :
, it will create a service with nameapi
.> api > image: covid_19_api
, we will name the image withcovid_19_api
, if this not defined, it will be named with folder name as default.> api > build: > context: ./covid_19_api/
, this code is to point a folder of Dockerfile we will use to build the service.> api > build: > dockerfile: Dockerfile
, this code is to point to the folder of Dockerfile file we will use to build the service. We put its nameDockerfile
, if you change the name of Dockerfile withDockerfile.dev
for example, put its name in docker-compose.yml:Dockerfile.dev
.> api > ports: - 8080:8080
, this code is to point to the port we will use. The left side is the port for our machine to point the container, the right side is for the port inside of the container. We put it like this because we want to access our container using port 8080 (from our browser) and the service that running in the container is using port 8080.> api > command: php -S 0.0.0.0:8080 public/index.php
, this is to activate our service. Important! we need to write its url with 0.0.0.0 not localhost, because0.0.0.0
is url for our machine (so that we can open the url in our browser) andlocalhost
is for url for the container.services: > vue :
, it will create a service with namevue
.> vue > image: covid_19_vue
, we will name the image withcovid_19_vue
, if this not defined, it will be named with folder name as default.> vue > build: > context: ./covid_19_vue/
, this code is to point a folder of Dockerfile we will use to build the service.> vue > build: > dockerfile: Dockerfile
, this code is to point to the folder of Dockerfile file we will use to build the service. We put its nameDockerfile
, if you change the name of Dockerfile withDockerfile.dev
for example, put its name in docker-compose.yml:Dockerfile.dev
.> vue > ports: - 3000:80
, this code is to point to the port we will use. The left side is the port for our machine to point the container, the right side is for the port inside of the container. We put it like this because we want to access our container using port 3000 (from our browser) and the service that running in the container is using port 80.> vue > nginx -g “daemon off;”
, this is to activate our nginx service in vue container.
Now, our project folder will be like this:
covid_docker
|- .git
|- covid_19_api
|- ...
|- Dockerfile
|- ...
|- covid_19_vue
|- ...
|- Dockerfile
|- ...
|- .gitmodules
|- docker-compose.yml
|- README.md
Build the container
All is done, let’s try to build it using command:
docker-compose up --build
The process will started…
=> [covid_19_api internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 1.19kB 0.0s
=> [covid_19_vue internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 379B 0.0s
=> [covid_19_api internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [covid_19_vue internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [covid_19_api internal] load metadata for docker.io/library/alpine:3.13 3.4s
=> [covid_19_vue internal] load metadata for docker.io/library/nginx:stable-alpine 3.3s
=> [covid_19_vue internal] load metadata for docker.io/library/node:lts-alpine 5.4s
=> [covid_19_api 1/9] FROM docker.io/library/alpine:3.13@sha256:2582893dec6f12fd499d3a709477f2c0c0c1dfcd28024c93f1f0626b9e3540c8 0.0s
=> [covid_19_api internal] load build context 0.1s
=> => transferring context: 333.56kB 0.1s
=> CACHED [covid_19_api 2/9] RUN apk — no-cache add php7 php7-fpm php7-pdo php7-mbstring php7-openssl 0.0s
=> CACHED [covid_19_api 3/9] RUN apk — no-cache add php7-json php7-dom curl php7-curl 0.0s
=> CACHED [covid_19_api 4/9] RUN apk — no-cache add php7-tokenizer 0.0s
=> CACHED [covid_19_api 5/9] RUN apk — no-cache add php7-phar php7-xml php7-xmlwriter 0.0s
=> CACHED [covid_19_api 6/9] RUN php7 -r “copy(‘http://getcomposer.org/installer’, ‘composer-setup.php’);” && php7 composer-setup.php — install-dir=/usr/bin — filename=composer && php 0.0s
=> CACHED [covid_19_api 7/9] COPY . /src 0.0s
=> CACHED [covid_19_api 8/9] WORKDIR /src 0.0s
=> CACHED [covid_19_api 9/9] RUN composer update 0.0s
=> [covid_19_vue] exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:51de3c6dda614626f0527dc868b18fad683a3b03462cc3515a1531faa5832a05 0.0s
=> => naming to docker.io/library/covid_19_api 0.0s
=> => writing image sha256:748e43d343ee29dedc3b3d9c15c4f995f50d3c9f845b5640677cad2fb3ed88ae 0.0s
=> => naming to docker.io/library/covid_19_vue 0.0s
=> [covid_19_vue production-stage 1/2] FROM docker.io/library/nginx:stable-alpine@sha256:2012644549052fa07c43b0d19f320c871a25e105d0b23e33645e4f1bcf8fcd97 0.0s
=> [covid_19_vue internal] load build context 0.1s
=> => transferring context: 1.09MB 0.1s
=> [covid_19_vue build-stage 1/6] FROM docker.io/library/node:lts-alpine@sha256:8c94a0291133e16b92be5c667e0bc35930940dfa7be544fb142e25f8e4510a45 0.0s
=> CACHED [covid_19_vue build-stage 2/6] WORKDIR /app 0.0s
=> CACHED [covid_19_vue build-stage 3/6] COPY package*.json ./ 0.0s
=> CACHED [covid_19_vue build-stage 4/6] RUN npm install 0.0s
=> CACHED [covid_19_vue build-stage 5/6] COPY . . 0.0s
=> CACHED [covid_19_vue build-stage 6/6] RUN npm run build 0.0s
=> CACHED [covid_19_vue production-stage 2/2] COPY — from=build-stage /app/dist /usr/share/nginx/html 0.0sUse ‘docker scan’ to run Snyk tests against images to find vulnerabilities and learn how to fix them
[+] Running 3/3
— Network covid_docker_default Created 0.8s
— Container covid_docker_api_1 Created 0.3s
— Container covid_docker_vue_1 Created 0.3s
Attaching to api_1, vue_1
api_1 | [Fri Sep 24 01:12:07 2021] PHP 7.4.21 Development Server (http://0.0.0.0:8080) started
ps: It’s finished so fast in my machine because my machine had a cache (see the CHACED line) for the OS and all the packages our project needs. 😁
Alright, the building process has been complete!
Result
Let’s check it first in our Docker Desktop.
As you can see in images above, our docker compose will create 2 Images with name covid_19_api
and covid_19_vue
. And in menu Containers / Apps, it will create docker group with name covid_docker
with covid_docker_vue_1
container and covid_docker_api_1
container inside.
Next, let’s check frontend apps on our browser
http://localhost:3000/
Next, let’s check API on our browser
http://localhost:8080/latest_covid_data
Last, let’s check is API documentation accessible and run.
http://localhost:8080/api/documentation
Excellent! All works like a charm. 😍
Conclusion
We now know how to use GIT submodule and create multiservice container in Docker using our existed projects. It’s really simple, we just need to create Dockerfile
for each project, then create docker-compose
file to wrap all of Dockerfile
files, and last build it using command docker-compose up--build
.
You can see and clone code in this article in my github:
Thank you for your time to read. Let’s join us again next time as we explore another interesting case! 😉