"It runs perfectly fine on my computer."
This is probably the most familiar sentence to anyone who has worked on a project together. You spend an entire night getting the code ready, send it to your team member, and when they open it, the screen is filled with red text. It's not your fault, and it's not his fault either; it's just that your two computers have different installations: the Python version is one number different, he hasn't installed a certain package, or a certain environment variable only exists on your machine. Docker aims to solve this problem, and its solution is very straightforward: it packages and takes away the entire small part of your computer related to this program.
Bento Box Theory
I like to think of Docker images as bento boxes. You're not giving someone a "recipe" and expecting them to buy the ingredients and cook it themselves; you're handing them a fully cooked bento box, complete with rice, vegetables, chopsticks, and even a packet of soy sauce. The recipient can open it and eat without having to ask, "What brand of soy sauce do you use?"
The image is like a bento box containing code, an interpreter, dependencies, and system libraries. The container is like the time you open it to eat. The same bento box can be opened many times, and each time it's the same. Docker Engine is an open-source project of Apache 2.0, and Compose is a tool that uses a YAML file to describe how several bento boxes are served together.
This article won't discuss clusters or scheduling with hundreds of machines. It will only discuss the usage of running several services on a single machine, which is the reality for most personal projects, and also my own experience.
Use the commit tag, not latest.
The image's tag is a string that you decide yourself.latest It's the default value, and also the worst choice. It means "the latest one," but what does "latest" mean relative to? Today's latest might be completely different from last week's latest, and you'd never know. It's like a bento box that only says "Today's Special"—you never know what you'll get when you open it.
My approach is to use the complete hash of the Git commit that builds the image as a tag. Currently, the live version is recorded in one file, and rolling back simply involves changing the target to the previous tag. The Dockerfile can check at the beginning whether the passed-in version string is forty hexadecimal characters; if not, it rejects the build, thus preventing images with placeholder strings from being leaked. I almost used...test When a label creates an image, only to be blocked by your own self-written check, the feeling is subtle, like being tapped on the head by your past self.
The same applies to the base image; it is pinned using a digest, for example...python:3.13-slim-bookworm@sha256:… The same tag might point to different content tomorrow, but digest will not.
The container gets too many things by default.
This is something many tutorials don't cover because it would deviate from the "five-minute tutorial" format. A container is executed by default as root, has root file system writability, and comes with a full set of Linux capabilities. In other words, when you open the lunchbox, it contains all the keys to your house by default.
A few lines in the Compose file can be retracted:user: Specify a non-root UID.read_only: true Make the root file system read-only.cap_drop: [ALL] Discard all abilitiessecurity_opt: [no-new-privileges:true] Blocking and raising power,pids_limit Limit the number of trips.tmpfs Give/tmp A temporary storage space with a size limit that is not executable. Directories that need to be written to are explicitly mounted using bind mount, and read-only directories are added if possible.:ro .
For jobs that only require one trip, I'm used to adding...--network none Importing data and running a conversion script are things that shouldn't be taken lightly. Giving it internet access is like asking the plumber to have your Wi-Fi password—most of the time it's fine, but that's not an excuse.
Compose only recognizes containers it has created.
Compose uses tags to identify which containers belong to its projects, but tags alone are not enough; it also compares them with a config-hash calculated by the system.docker run Even if the name and label are correct, manually created containers...compose up They won't accept it either, and will try to create another one but fail due to name clash.
I learned this skill during emergency care. One of the services was down, and to be quick, I went directly to...docker run I manually started a container, and the service came back up and running; I was quite pleased with myself. The next day, I tried to deploy normally using Compose, but it gave me a completely different look. In the end, I had to delete the manually started container and let Compose rebuild it. (For emergency use...)docker run No problem. Just remember to recreate the scene afterwards, otherwise the next deployment will stop in a strange place, and by then you'll have forgotten what you did.
A small trap in the image
If the imageENTRYPOINT It's already a translator.docker run image python -m foo It will become "to execute a program called using a translator".python The file was not found. The parameters need to be obtained from...-m start.
This kind of mistake isn't noticeable during document review. You look at the instructions, every word is correct, but it becomes apparent after running the program once. I spent twenty minutes suspecting the image was corrupted, only to find out it was just an extra word. What I learned that day wasn't about Docker, but about "running it once before questioning the world."
Mini-drama: Should we learn Docker?
"Do I need to learn Docker for a small project I'm working on alone?"
"Will you get a new computer?"
"I should be able to graduate next year."
"Then you'll be very grateful to yourself next year. Because you'll have to rebuild the entire environment on a new computer, and you'll have forgotten what you installed in the first place."
"So Docker is an instruction manual written for my future self."
"It's an instruction manual, and it's the kind that runs automatically."
"It sounds good, but why does everyone say it's difficult?"
"Because everyone wants to learn the whole set of dishes from the start. You should learn how to make bento boxes first, and we can talk about set meals later."
Prerequisites on Windows
On Windows, regardless of whether you're using WSL2 or Hyper-V as the backend, you need to enable the Hyper-V virtualization layer for Docker Desktop. If there's a VirtualBox virtual machine running important files on the same machine, enabling it will cause VirtualBox to revert to a much slower compatibility mode. You can only choose one to be the primary provider. I chose VirtualBox, so Docker only exists within the virtual machine; there are no containers on the Windows side. This isn't a particularly sophisticated decision; it's just that both of my roommates wanted to share a desk, and one of them had to go to the library.
My positioning
On a single machine, Docker is a packaging and isolation tool, not an orchestration system. Each image answers "Which version is this?", each container answers "What can it handle?", and Compose answers "How do these work together?". Once these three questions are answered clearly, most of the remaining complexity is unnecessary.
As for the statement "It runs perfectly fine on my computer," it won't disappear after using Docker; it will simply become "It runs perfectly fine in my container." The difference is that this time you can give the entire container to the other party and let them see for themselves.
A few tidbits about Docker
Docker was released in 2013 by a company called dotCloud, which was originally in another business; Docker was just an internal tool for them. After the tool became popular, the company changed its name to Docker and sold its original business. This kind of story of "a byproduct becoming more popular than the main business" is common in the software industry; Slack is another example.
The difference between containers and virtual machines can be summed up in one sentence: a virtual machine is like an entire house, with its own foundation; a container is like a room in the same house, sharing the same foundation and utilities. Therefore, container startup takes seconds, while virtual machine startup takes minutes. Also, container isolation is weaker than virtual machine isolation because they share the same core. This determines many security trade-offs, including why so much space was spent discussing "reclaiming capabilities."
Images are stacked layer by layer. Each line of your Dockerfile creates a layer, with each layer stacked on top of the one above it. If two images have the same first few layers, only one copy will be stored on the disk. This is why putting "frequently changing elements" at the end of the Dockerfile and "infrequently changing elements" at the beginning speeds up the build process: earlier layers can be reused, and only later layers need to be rebuilt.
Docker's whale logo, with a bunch of shipping containers on its back, is a very accurate image, but every time I see it, I feel that the whale looks a bit tired.
Three common misunderstandings
The first misconception is that "using Docker makes you secure." By default, containers run as root and can access many things. Security is something you set up, not something Docker provides.
The second misconception is that "the data in the container will always be there." Deleting the container means losing everything written inside, unless you mounted an external directory. Many people make this mistake when they first lose data.docker rm Going down, the database disappeared, and then I posted on the forum asking, "Does Docker have a bug?" No, it just did as instructed.
The third misconception is that "the image is very small".python:3.13 That base image is nearly 1 GB.slim The version is much smaller.alpine Smaller, but some packages won't fit. It's common for hard drives to become unknowingly filled with old images.docker system prune This command is worth remembering; running it once can usually save several GB, although you should make sure you know what it will delete before running it.
A small exercise you can try over the weekend
Find a machine with Docker installed and run it.docker run --rm -it --read-only --user 1000:1000 --cap-drop ALL python:3.13-slim python You will be taken to a Python interactive interface; try...open("/test.txt", "w") It will tell you that it is read-only. Try it.import os; os.getuid() It's 1000, not root.
Then remove those three parameters and run it again; you'll find that it can do everything. The difference between the two is the difference between "a preset container" and "a restricted container." Spending five minutes experiencing it once will be more memorable than reading ten articles. By the way, take a look...docker images Consider how big that image is, and then think about whether you need it to be that big.
Postscript
The statement, "It runs perfectly fine on my computer," is actually quite honest in retrospect. It acknowledges the difference in environment, but the speaker doesn't realize what they're admitting. Docker essentially packages up that "my computer" and delivers it. As for what happens if it still doesn't run after packaging, that's a topic for another article.
Official website: docker.com , docs.docker.com/compose