Q DB in Docker

In a production environment, why is it considered a ‘bad practice’ to run databases (like Postgres) inside Docker containers for critical services, and when is it acceptable to do so?”

Think about:

  • Persistence (Data loss)
  • Backup/Recovery (Snapshots vs. file copies)
  • Performance (I/O overhead)

Persistence & I/O: Databases are I/O intensive. Containers introduce a storage driver layer that can bottleneck database performance.

  • Management: Databases require specialized backup/restore, point-in-time recovery, and high-availability (HA) configuration. Managing a production-grade RDS (like AWS Aurora) or a dedicated Postgres cluster is much more reliable than trying to manage HA manually with Docker containers.
  • When is it okay? For local development, CI testing, and ephemeral environments where data can be wiped, it is perfect.

Q Which image to build upon

The Dockerfile FROM directive: You mentioned FROM ubuntu. In a production-grade Java project, why would you prefer FROM eclipse-temurin:21-jre-alpine or distroless images instead of a full ubuntu image?

  • Hint: Think about Security (Attack Surface) and Image Size.

  • A full ubuntu image includes shell utilities (like curl, apt, git, ssh) that are unnecessary for running a Java application. If a hacker exploits your app, these utilities give them a “toolkit” to pivot further into your network. Minimal images have almost nothing for them to use.

  • Size: Smaller images lead to faster CI/CD pipelines (less data to push/pull to the registry) and faster pod startup times in Kubernetes.

Q. Difference between Docker file and docker compose

1. Why you need the Dockerfile

Even if you only have one service, you need the Dockerfile to create the image. Without it, you are relying on whatever Java version is installed on your local machine. The Dockerfile ensures that whether you are building it on your laptop, a colleague’s machine, or a CI/CD pipeline, the environment remains 100% identical.

2. Why you need docker-compose.yml

It’s the orchestrator. It simplifies “plumbing.” It’s like a “manager” that tells Docker: _“Hey, go get these images, connect them to each other, and give them these settings.”

This file is just a \*\*system topology map\*\*. It tells the computer how your services should be connected.

Instead of running long, complex commands like: docker run -d --name my-postgres -e POSTGRES_PASSWORD=... -p 5432:5432 postgres …you just run docker-compose up. It manages the networking, environment variables, and startup order (e.g., waiting for the database to be ready before starting the app).

FeatureDockerfiledocker-compose.yml
PurposeDefines how to build your specific app image (e.g., install Java, copy JARs).Defines how to run a multi-container environment (Postgres + Kafka + Your App).
LevelOne image.The whole system.
ExampleFROM eclipse-temurin:21-jdkservices: postgres: image: postgres:16
  • For Development: Use docker-compose.yml to spin up your local dependencies (like Postgres or Kafka) while running your Java app directly from your IDE. This gives you the best of both worlds: rapid code changes (IDE hot-swap) and reliable local infrastructure (Docker).

  • For Deployment: In a real “production-grade” environment, you often won’t use docker-compose to run the actual app—you’ll use Kubernetes or a cloud platform (like AWS ECS or Google Cloud Run). However, the Dockerfile remains essential, as that is what gets pushed to your container registry.

Q. is Dockerfile name case sensitive ?

Yes, it is case-sensitive.

In the world of Docker, the standard file name is exactly Dockerfile (with a capital “D” and no file extension).

The “Rules” you need to know:

  • The Default: When you run docker build ., Docker automatically looks for a file named Dockerfile.
  • Case Sensitivity: If you name it dockerfile or Dockerfile.txt, Docker will fail with an error saying it cannot find the file.
  • Why? Docker is designed to be cross-platform, and most Linux-based filesystems (which run your containers) treat file names as case-sensitive.

Tip: When to change it

While Dockerfile is the standard, in a complex project with multiple services, you might see names like:

  • Dockerfile.dev
  • Dockerfile.prod

If you ever use a non-standard name, you must explicitly tell Docker where to find it using the -f (file) flag:
docker build -f Dockerfile.prod -t my-app-prod .

Q. is docker-compose also case sensitive

The short answer is yes, it is sensitive, but it follows different naming rules than the Dockerfile.

The Rules for docker-compose.yml

Docker Compose looks for specific filenames by default when you run the docker-compose up command.

  1. The Standard Names:

    • docker-compose.yml (Most common/standard)
    • docker-compose.yaml (Also widely supported)
  2. Case Sensitivity:

    • On Linux/macOS, these filenames are case-sensitive. If you name it Docker-Compose.yml, the command docker-compose up will fail to find it.
    • Always use all lowercase for the filename.
  3. When to deviate:

    • If you have multiple compose files (e.g., docker-compose.dev.yml for local testing and docker-compose.prod.yml for staging), you can use the -f flag to specify which one to use:
      docker-compose -f docker-compose.dev.yml up

Q. If we have a large project with 20+ microservices, why might we choose to split our docker-compose.yml into multiple files (e.g., db.yml, auth.yml, order.yml) rather than putting everything in one giant file?”

Think about:>

  • Cognitive Load: Can a single developer easily read a 2,000-line YAML file?
  • Scope of Impact: If I only want to start the order service and its dependencies, do I need to start all 20 services?
  • Team Autonomy: Can the “Order” team manage their own compose file without stepping on the toes of the “Payment” team?

Reduced Cognitive Load: Developers only need to manage the configuration for their domain.

  • Resource Efficiency: I can run docker-compose -f db.yml -f order.yml up and only start what I need for my local task, saving my laptop’s RAM.
  • Isolation: Changes in the “Payment” service’s configuration cannot accidentally break the “Order” service’s stack.

Q. Why is docker compose rarely used to orchestrate production microservices at scale?”

Think about:

  • Scalability: How do you handle 1,000 instances of a service across 50 servers with Docker Compose?

  • Self-Healing: What happens if a container crashes in Docker Compose vs. in a system like Kubernetes?

  • Scalability (The “Single Host” Problem):
    docker-compose was built for ==single-node orchestration. It has no native mechanism to distribute containers across a cluster of 50 physical servers. To scale to 1,000 instances, you would need to manually coordinate where each instance runs, how it handles service discovery, and how traffic is routed to it. Tools like Kubernetes handle this via a “scheduler” that manages the cluster state as a whole, not just individual containers.

  • Self-Healing (The “State” Problem):

    • In docker-compose, if a container crashes, it is the user’s responsibility to bring it back up, or you rely on simple restart: always flags. It lacks declarative state management. Kubernetes constantly observes the current state vs. the desired state (e.g., “I need 5 replicas running”). If a container crashes, Kubernetes detects the discrepancy immediately and restarts it automatically. Furthermore, Kubernetes monitors container health (liveness and readiness probes), not just whether the process is running.
  • Networking and Service Discovery:

    • At scale, services need to find each other dynamically. docker-compose uses simple DNS within a local network. Kubernetes provides robust service discovery, load balancing, and ingress controllers that handle thousands of requests across distributed clusters seamlessly.

Q. Why is running a container as root considered a security risk in a production Kubernetes environment?”

Think about:

  • Escape Risk: If a process is compromised, what access does it have to the host kernel?

  • Principle of Least Privilege: Why should a Spring Boot app have more permissions than it needs to read its own configuration and write to its own logs?

  • Escape Risk (Kernel-Level Access):

    • Containers share the host’s Linux kernel. If your Spring Boot application runs as root inside the container and is compromised (e.g., via a remote code execution vulnerability), the attacker gains root privileges within that namespace. While Linux namespaces provide isolation, a “container escape” exploit allows the attacker to break out of the container and gain root access on the host kernel. From there, they can compromise every other container running on that same physical server.
  • Principle of Least Privilege:

    • A standard Spring Boot service only needs to read its config, write logs to a specific directory, and bind to a port (usually >1024). It does not need the ability to modify system-level files, install packages, or manage network interfaces. By running as a non-privileged appuser, you ensure that even if the application is fully compromised, the attacker is trapped inside a sandbox with no permissions to perform administrative tasks on the underlying system.
  • Compliance and Auditing:

    • Enterprise environments like Visa require strict adherence to security baselines (like CIS Benchmarks). Running as root is an immediate “Fail” in most security audits because it violates the fundamental requirement of minimizing the blast radius of a potential exploit.

Namespacing and Resource Isolation

Docker Compose uses the project directory as a namespace to prevent naming collisions when multiple developers run different projects on the same host. The instance suffix allows for horizontal scaling, where the orchestrator can manage multiple replicas of the same service definition while maintaining unique identifiers for each container.

Naming Conventions :

The names order-service_postgres_1 and order-service_order-service_1 are populated automatically by Docker Compose based on your project structure and configuration.

Here is how the naming logic works:

  • Prefix (order-service_): By default, Docker Compose uses the name of the \*\*directory\*\* where your \`docker-compose.yml\` file is located as a prefix for all containers it creates. If your project folder is named order-service, every container in that stack will start with that string to ensure they stay grouped together.

  • Service Name (postgres or order-service): This is the label you explicitly assigned to each service in your docker-compose.yml file under the services: section.

  • Instance Number (_1): This is an auto-incrementing suffix added by Docker to identify the specific container instance. Even if you only have one instance, Docker adds the _1 suffix to allow for potential future scaling (e.g., if you decided to run 3 replicas of the order service, they would be named ..._1, ..._2, and ..._3).

Flags

-f : stands for “follow”—it will stream the logs live as they happen, keeps showing logs as new lines are added.
eg : docker-compose logs -f order-service
-i : (interactive) keeps the STDIN open.
-t :(terminal) assigns a TTY, allowing interactive shell usage.
-d (detached mode) runs the container in the background.
—name assigns a name to the container.
—build : ensures your latest code changes are baked into the container

Commands

docker ps -a : Shows all containers, including stopped ones
docker start my_container: estarts a previously stopped container
docker stop my_container : Sends a SIGTERM signal, allowing the container to exit cleanly.

TODO

Network Bridge