[Final version] Docker/Kubernetes practical introduction

As a specialist editor for technology blogs and gadget reviews, I revised and proofread the draft you submitted.

In this rewrite, we focused on ``storytelling that creates empathy among readers'' and ``balance of technical accuracy'' **. In particular, we have refined the expressions to give a concrete image of troubles (landmines) that actually occur in the field, enhancing the dignity of a blog written by a professional engineer.


[Final version] Docker/Kubernetes practical introduction

[Debugging news] Numerous “landmines” I noticed after putting Docker and Kubernetes into practice (late night memo)

2am. Only the pale light emitted from the monitor reflected in the silent room.
I kept hitting `kubectl get pods` again, staring at the screen with an expression close to despair.

Status is `CrashLoopBackOff`.
``Why isn't it working?'' - The problem should have been solved in an instant by tracing the logs, but it took several hours to determine the cause.

“It worked with Docker Compose locally, so it should work with Kubernetes too…”

Such naive expectations are brutally crushed in the face of lofty goals such as migrating to microservices and pursuing scalability. The world of Docker and Kubernetes is full of landmines that beginners (or even intermediate users) are bound to step on.

Today, I would like to write about the depths of the ``addictive points'' in containerization and orchestration that I have learned through my hard work at the development site.


1. Docker pitfalls: it's not just "isolation"

The first problem many engineers run into is ``I built a Docker image, but for some reason it doesn't work.'' This is often due to not understanding exactly how container abstractions work.

The curse of image layers and caching

During the Docker build process, each `RUN` and `COPY` directive is managed as a stack of "layers." The things that beginners tend to fall into here are ``improper handling of environment variables'' and ``inclusion of unnecessary files.''

For example, there is a case where you `COPY` a directory containing `node_modules` unprotected. Alternatively, tools that are only needed during builds (such as curl or npm) may be included in the image without being removed. These factors not only increase the final image size but also increase security risks.

What is essential here is the introduction of multi-stage build. This method extracts only the components necessary for runtime by completely separating the build environment and execution environment.

# --- Build Stage ---
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY.
RUN npm run build

# --- Production Stage ---
FROM node:18-alpine
WORKDIR /app
# Copy only the files necessary for execution from the builder
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json.

EXPOSE 3000
CMD ["node", "dist/main.js"]
`

By adopting this configuration, the image size can be reduced by hundreds of MB, and deployment speeds can be dramatically improved. However, what is often overlooked here is the management of the ``build context''. If `.dockerignore` is not configured properly, huge `.git` directories and even temporary files will be sent to the Docker daemon, unnecessarily compressing the execution time of the CI/CD pipeline. Please keep in mind that these "minor differences" can cause operational stress.

Labyrinth of environment variables

Environment variables, which could be managed intuitively using `env_file` in Docker Compose, now require strategic thinking from the moment you migrate to Kubernetes. The proper use of ConfigMap and Secret, and permission management... Many problems such as "It works locally, but the environment variables cannot be read in the Pod" occur due to a conflict between the initialization process by the shell script when starting the container and the priority of the environment variables injected by Docker/K8s. Before rewriting the manifest, it is important to first understand the path from where variables are being injected.

2. Kubernetes despair: Orchestration complexity

The moment we step into Kubernetes (K8s), we are driven by a "declarative" paradigm. However, behind that sophisticated abstraction lies the raw infrastructure struggles of networks and resource management.

CrashLoopBackOff and survival confirmation (Liveness/Readiness)

One of the most frequently encountered errors on K8s is `CrashLoopBackOff`. This is a warning indicating that the container started but immediately terminated abnormally.

The trap that beginners fall into here is confusing ``application startup completion'' and ``process survival''.
For example, when using a heavy framework such as Java or Python, even if the process itself is started, K8s determines that ``this container is dead'' and restarts it repeatedly while internal initialization (such as waiting for a database connection) is not completed.

Proper design of `readinessProbe` and `livenessProbe` prevents this.

  • Readiness Probe: Checks whether it is ready to accept traffic. If it fails, you will be disconnected from the service.
  • Liveness Probe: Determines whether the process itself is alive (restart it if it is dead).
Without understanding this difference, setting the same timeout for both, or setting it too strict, can lead to unnecessary restart loops and unexpected loss of traffic.

Resource limits and OOMKiller

The true cause of the phenomenon that "It worked on my machine but crashes at a specific timing on K8s" is often OOMKiller (Out of Memory Killer).

Kubernetes sets `limits` and `requests`, but this balance design is very strict.

  • requests: The minimum resources (reservation slot) required to run the Pod.

  • limits: Upper limit beyond which K8s will force termination (OOMKill).


Especially when it comes to memory, there are cases where the garbage collection behavior of Python, Node.js, etc. and the container's memory limits are very incompatible. If there is even a temporary spike in the amount of memory required by an application, K8s will mercilessly execute the Pod. If you deploy without being aware of this "resource wall," you will likely suffer from momentary power outages for unknown reasons.

Network and service discovery

`ClusterIP`, `NodePort`, `LoadBalancer`…. It is basic knowledge to know how to use these properly, but what is actually difficult in the field is DNS resolution. CoreDNS runs inside Kubernetes, but problems may occur in communication between containers, such as ``name resolution not being able to be done in time'' or ``receiving only from specific nodes.'' This is due to the CNI (Container Network Interface) settings and the association between Service and Endpoint. Network abstractions are convenient, but we must always be aware of the physical constraints behind them.

3. Practical configuration example: Deployment definition to ensure stability

It is the professional's job to write a manifest that not only "works," but also takes into account settings that can be "operated." Below is a typical example of a production-ready deployment with appropriate resource limits and probes.

`

apiVersion: apps/v1
Kind: Deployment
metadata:
  name: web-app-deployment
  labels:
    app: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
containers:
      - name: web-app
        image: my-registry.com/web-app:v1.2.0
        ports:
        -containerPort: 8080
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        readinessProbe:
          http1.1:
            path: /healthz/ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          http1.1:
            path: /healthz/live
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 20
        env:
        - name: NODE_ENV
          value: "production"
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: db_host
`

In this setting, it is extremely important to set the `initialDelaySeconds` appropriately. If your application's startup process takes a long time, hitting the probe before it's ready will cause an unnecessary restart loop. In addition, resource `limits` should not be set ``with some margin'', but should be designed based on ``maximum expected values ​​based on actual measurements''.


4. Technology comparison: Docker Compose vs Kubernetes

Why bother introducing complicated Kubernetes? It's not just because I want to use containers. This is to meet operational requirements such as scale, fault tolerance, and declarative management. But in order to reap its benefits, we need to assume a certain level of complexity.

FeaturesDocker ComposeKubernetes (K8s)
Main usesDevelopment environment, small-scale single server operationLarge-scale production, multi-node orchestration
ScalingManually or simply specifying the number of replicas (within the same host)Dynamic resource allocation with automatic scaling (HPA)
Self-healingNone (just restarts even if the process dies)Yes (pod relocation and automatic restart when a node fails)
NetworkMainly uses intra-host bridged networkingDynamic service discovery and load balancing with complex CNI
Learning costLow. Can be learned using the basic concepts of Docker.Expensive. Requires understanding of YAML definition, resource management, and abstraction level.
Declarative configurationDescribe a simple configurationDefine the desired state of the entire infrastructure in detail

In lieu of a conclusion: Mindset to avoid landmines

Docker and Kubernetes are not magic wands. They are sophisticated tools for "managing complex systems in a more predictable manner."
Much of the difficulty we encounter comes from overlooking the realities behind the abstractions provided by tools: network latency, resource constraints, process lifecycles.

  • In Docker, pursue lightweight and clean images.
  • For Kubernetes, ensure stateless design and proper probe configuration.
By knowing the location of these "landmines" in advance, you should be able to reduce late-night debugging time and concentrate on more creative development. After all, the best technology only shows its true value when it is properly understood and controlled as intended. Welcome to the world of containers. The journey from here is tough, but beyond that lies a vast landscape of scalability.

🤖 Developer blog/public content

The developer's personal blog is also available, so please take a look. muttyan.com blog

Amazon great free trial/benefits summary