Language
Search

Introduction to Docker: Ending the “It Works on My Machine” Problem in Development Environments

주황색 컨테이너 측면의 골판 패턴 사진

·

Views 7
What problem does Docker solve?
It eliminates the “it works on my machine” problem. It packages the application code along with the operating system libraries, language versions, and configurations required to run it. This ensures the exact same environment is reproduced regardless of what is installed on the recipient’s machine. In essence, it replaces environment setup documentation with an executable file.

Every developer has experienced a situation where they pulled the exact same code, but it only failed on their machine. The culprit is rarely the code itself. Usually, it’s a mismatched Python version, a missing system library, or misconfigured environment variables.

Until now, the solution was to write installation steps in a README file. However, documentation gets outdated, operating systems vary, and you end up in an endless loop of “I get an error at that step.”

Docker solves this problem differently. Instead of describing the environment in a document, it packages the entire environment and ships it together.

How is it different from a Virtual Machine?

Here is a quick summary:

Virtual Machine Docker Container
What it includes Entire Guest OS + App App + only required libraries
Kernel Separate for each Shared host kernel
Size Several gigabytes Tens to hundreds of megabytes
Startup time Tens of seconds Around 1 second

The key is kernel sharing. While a virtual machine runs an entire extra operating system, a container borrows the host’s kernel and only layers what is necessary on top. This makes it lightweight and fast. However, because they share the kernel, you cannot run something that requires a different type of kernel than the host.

4 Key Concepts You Need to Know

Image — A read-only template containing everything needed for execution. You can think of it like an installation CD or a class in programming.

Container — The actual running instance of an image. You can spin up ten containers from the same image, and each will run independently. The relationship between images and containers is often the most confusing part at first, but thinking of them as a “cookie cutter and cookies” is a good analogy.

Volume — A storage space outside the container. Since deleting a container also deletes the data inside it, persistent data (like database files or uploaded images) should be stored in a volume.

Network — The communication channel between containers. If they are on the same network, they can reference each other by container name. For example, a web container can access a database container using db:5432.

Running Your First Container

First, verify your installation.

docker --version          # 설치 확인
docker run hello-world    # 동작 확인

Now, let’s run something actually useful: an Nginx web server.

# 이미지를 받아 컨테이너로 실행. -d 는 백그라운드, -p 는 포트 연결
docker run -d -p 8080:80 --name web nginx

docker ps                 # 실행 중인 컨테이너 목록
docker logs web           # 로그 확인
docker exec -it web bash  # 컨테이너 안으로 들어가기
docker stop web           # 정지
docker rm web             # 삭제

-p 8080:80 means mapping port 8080 of your local machine to port 80 of the container. If you open localhost:8080 in your browser, you will see the default Nginx page. The host port comes first, and the container port comes second—getting this order mixed up is the very first hurdle for beginners.

Writing a Dockerfile

Now, let’s move from using other people’s images to packaging your own application into an image.

FROM python:3.12-slim

WORKDIR /app

# 1) 의존성 목록만 먼저 복사해서 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 2) 그다음에 소스 복사
COPY . .

CMD ["python", "main.py"]

The order of installing dependencies and copying source code is the key to this file. Docker caches the result of each instruction as a layer. If a layer changes, all subsequent layers must be rebuilt from scratch.

If you copy the source code first and install dependencies later, even a single line of code change will force Docker to reinstall all packages. This makes every build take several minutes. Conversely, if you write it as shown above, the installation layer is reused from the cache as long as requirements.txt remains unchanged, reducing build times to just a few seconds. This is one of the most common pitfalls for beginners, so it is best to memorize this order.

Here is how you build and run it:

docker build -t myapp .
docker run -d -p 8000:8000 myapp

You should also create a .dockerignore file. Excluding directories like .git, node_modules, and local configuration files prevents the image from becoming unnecessarily bloated.

Moving to Docker Compose

The commands above are sufficient if you only have a single container. However, the moment you need multiple containers—such as an app + database + cache—the commands become long and difficult to manage in the correct order. That is when you move to Compose.

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql://user:pass@db:5432/app
    depends_on:
      - db

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

With just a single command, docker compose up -d, both services will spin up. There are two key things to note here: first, the host in DATABASE_URL is not an IP address but the service name db (since they are on the same network, they can resolve each other by name); second, the database data is stored in a volume (ensuring data persists even if the container is deleted).

By keeping this single file in your repository, onboarding a new team member is as simple as running docker compose up. This is precisely why most developers use Docker.

Frequently Asked Questions

Does it work on Windows?

Yes, it does. You can install Docker Desktop, which uses WSL2 under the hood. You need to enable WSL2 during installation. Note that mounting folders from the Windows file system to a container can slow down I/O performance, so it is highly recommended to keep your project files inside the WSL2 file system for better performance.

Does using Docker slow things down?

On Linux, there is virtually no overhead. Since it shares the kernel, it is fundamentally different from a virtual machine. However, on macOS and Windows, Docker runs inside a lightweight Linux virtual machine behind the scenes, which can cause noticeable latency in file I/O. This is where most of the perceived slowness in development environments comes from.

Does data disappear when a container is deleted?

Yes, any data stored inside the container will be lost. That is why persistent data, such as database files or uploaded assets, must be stored in a volume. Volumes have a lifecycle independent of containers, meaning your data remains intact even if you delete and recreate the container.

Is it really necessary for real-world development?

If you are collaborating with multiple people, need to align your local and server environments, or are dealing with a multi-service architecture, it is practically essential. On the other hand, if you are just writing a simple script or building a static site by yourself, you can easily get by without Docker, and the learning curve might not be worth the effort.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *