Archival Notice
This guide was written for CentOS 7 and legacy Docker Compose v1 (docker-compose) deployments. Please note that CentOS 7 has reached End of Life (EOL), and modern Docker installations utilize the integrated Compose plugin (docker compose). Please adapt package installations and syntax to current environment standards.
When transitioning from traditional virtual machine administration to modern DevOps engineering, mastering containerization and Infrastructure as Code (IaC) is essential. Understanding how to bundle applications into lightweight containers, orchestrate multi-tier services, and manage cloud infrastructure through code forms the backbone of modern reliability engineering.
In this comprehensive guide, I will walk you through a complete end-to-end deployment workflow: installing Docker CE on CentOS 7, orchestrating a MongoDB and Mongo-Express administration stack via custom Docker bridge networks, containerizing a Node.js Express application, streamlining operations with a Makefile, and establishing a secure Terraform remote state backend on Google Cloud Storage (GCS).
Prerequisites
You will need a clean CentOS 7 VM with root privileges, alongside an active Google Cloud Platform (GCP) account to provision storage buckets and Service Account credentials.
Step 1: Base System Update and Docker CE Installation
First, update your system packages, install required development utilities, and disable SELinux to ensure smooth container network bridging.
# Update system and install core utilities
yum update -y
yum install epel-release yum-utils bash-completion net-tools git -y
# Disable SELinux
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
# Set firewall to trusted mode for container communication
firewall-cmd --set-default=trusted && firewall-cmd --list-all
# Reboot server to bring up clean kernel baseline
reboot
Installing Docker CE and Docker Compose
Once back online, configure the official Docker CE repository and install Docker engines alongside Docker Compose.
# Configure Docker CE repository
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Install Docker engine and Compose
yum install docker-ce docker-ce-cli containerd.io docker-compose -y
# Enable and start Docker daemon
systemctl start docker
systemctl enable docker
systemctl status docker
Verify your installation by pulling and executing the verification container:
docker run hello-world
Step 2: Orchestrating MongoDB and Mongo-Express via Docker Networks
Multi-tier applications require isolated networking. We will construct a custom bridge network (mongo-network) and deploy a MongoDB database alongside the Mongo-Express web administration interface.
# Check default Docker networks
docker network ls
# Create custom isolated bridge network
docker network create mongo-network
# Verify new network allocation
docker network ls
Deploying the Database Tier
Spin up the MongoDB container attached to our custom network, injecting root administrative credentials via environment variables:
docker run -p 27017:27017 -d \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=Randompassword \
--name mongodb \
--net mongo-network \
mongo
Deploying the Web Administration Tier
Similarly, deploy Mongo-Express, linking it directly to the MongoDB instance:
docker run -p 8081:8081 -d \
-e ME_CONFIG_MONGODB_ADMINUSERNAME=admin \
-e ME_CONFIG_MONGODB_ADMINPASSWORD=Randompassword \
-e ME_CONFIG_MONGODB_SERVER=mongodb \
--net mongo-network \
--name mongo-express \
mongo-express
Check the active running status of both containers:
docker ps
Step 3: Application Containerization (Dockerfile and Docker Compose)
To orchestrate the entire application stack seamlessly, we transition from manual CLI execution to declarative orchestration using Docker Compose and a multi-stage Dockerfile.
Installing Node.js and Cloning Application Code
# Install Node.js repository and packages
curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash -
yum install -y nodejs
# Clone demo application repository
cd /root
git clone https://gitlab.com/nanuchi/techworld-js-docker-demo-app.git
cd /root/techworld-js-docker-demo-app/app
# Install dependencies
npm install
npm audit fix
Structuring the Dockerfile
Create a Dockerfile in the root of the application repository using a lightweight Alpine Linux base image:
# /root/techworld-js-docker-demo-app/Dockerfile
FROM node:13-alpine
ENV MONGO_DB_USERNAME=admin \
MONGO_DB_PWD=Randompassword
RUN mkdir -p /home/app
COPY ./app /home/app
WORKDIR /home/app
RUN npm install
CMD ["node", "server.js"]
Build the custom Docker image:
docker build -t my-app:1.0 .
docker images
Structuring docker-compose.yaml
Create the declarative Compose manifest to coordinate the entire multi-container deployment:
# /root/techworld-js-docker-demo-app/docker-compose.yaml
version: '3'
services:
mongodb:
image: mongo
ports:
- "27017:27017"
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=Randompassword
volumes:
- mongo-data:/data/db
mongo-express:
image: mongo-express
restart: always
ports:
- "8080:8081"
environment:
- ME_CONFIG_MONGODB_ADMINUSERNAME=admin
- ME_CONFIG_MONGODB_ADMINPASSWORD=Randompassword
- ME_CONFIG_MONGODB_SERVER=mongodb
volumes:
mongo-data:
driver: local
Execute the orchestration stack in detached mode:
docker-compose -f docker-compose.yaml up -d
Step 4: Operational Streamlining via Makefile
To prevent team members from needing to memorize lengthy Docker and Compose commands, maintain a standard Makefile.
# /root/techworld-js-docker-demo-app/Makefile
.PHONY: up down restart logs build
up:
docker-compose -f docker-compose.yaml up -d
down:
docker-compose -f docker-compose.yaml down
restart: down up
logs:
docker-compose -f docker-compose.yaml logs -f
build:
docker build -t my-app:1.0 .
Step 5: Securing Credentials and Establishing Terraform GCP Remote State
When managing infrastructure through Terraform, storing state files locally risks concurrency conflicts and secrets exposure. We establish a secure remote state backend on Google Cloud Storage (GCS).
1. Securing Credentials (.gitignore and .dockerignore)
Generate a GCP Service Account key (terraform-sa-key.json) with Object Storage Admin privileges. Ensure this sensitive key file is strictly ignored in your repository manifests:
# .gitignore
node_modules
config/config.env
*.key.json
# .dockerignore
node_modules
*.key.json
Export the credentials variable to your shell environment:
export GOOGLE_APPLICATION_CREDENTIALS=$PWD/terraform-sa-key.json
echo $GOOGLE_APPLICATION_CREDENTIALS
2. Provisioning the GCP Storage Bucket
Authenticate with Google Cloud and provision a globally unique GCS bucket:
gcloud auth login
gcloud config set project storybooks-338209
# Create GCS bucket for remote state storage
gsutil mb -p storybooks-338209 gs://storybooks-338209-terraform
Global Bucket Naming
GCS bucket names must be globally unique across all Google Cloud users worldwide. If gsutil mb returns a 409 Conflict error, choose a unique naming prefix for your organization.
3. Initializing Terraform Remote State Backend
Construct your Terraform backend configuration (main.tf) referencing the newly provisioned GCS bucket:
# /root/storybooks/terraform/main.tf
terraform {
backend "gcs" {
bucket = "storybooks-338209-terraform"
prefix = "/state/storybooks"
}
}
Initialize your Terraform environment:
terraform init
Your engineering environment is now fully containerized, orchestrated, operationalized, and backed by robust cloud-native Infrastructure as Code!