Decoding Docker πŸ‹ : Breakdown Docker FileπŸ“

πŸ‘‰ Quick Intro πŸ‘€

Yo devs!

  • So If you haven’t yet messed with Docker basics - Images, Containers, Networks and Volumes, check out this cool intro I wrote

  • Else now you must be wondering β€œHow do I build my own image for my project?”

  • Well, the answer is simple: Dockerfile.

  • Let’s break it down fully - all in proper detail, but in our Indian style. No boring gyaan here! 😜

πŸ‘‰ What is Dockerfile πŸ—’οΈ

  • It is basically a recipe (a text file) with step-by-step instructions for Docker on how to build your custom image for your project.

  • Think of it as setting up a new laptop from scratchβ€”installing necessary applications and libraries, pulling your code, running the code, and more. A Dockerfile automates all of that for you.

  • Its like saying β€œOye Docker, yeh steps follow kar aur meri image bana de!” πŸ’ͺ

πŸ‘‰ File Structure & Format 🏒

  • A Dockerfile is packed with multiple instructions, each lined up one after other for different tasks! πŸŽ‰

  • The first and must-have instruction is FROM (we'll dive into this in the sections below).

  • The correct format for writing an instruction is shown below:

      # Comment (describe the purpose)
      COMMAND arguments
    
  • The COMMAND doesn't have to be in uppercase, but it's a common practice to make it stand out from the arguments.

  • Here's a basic Dockerfile example for a Node.js app, just to give you a sneak peek of what it looks like! Get ready to dive deeper into it in the sections below β€” it's going to be awesome! πŸ˜ƒ

      # Use Node.js version 18 as the base image
      FROM node:18
    
      # Set the working directory inside the container to /app
      WORKDIR /app
    
      # Copy all files from current directory on host to working directory in container
      COPY . .
    
      # Install dependencies using npm
      RUN npm install
    
      # Command to run when the container starts
      CMD ["node", "index.js"]
    

πŸ‘‰ Image Building πŸ—οΈ

  1. Build Context

    • To build your Docker image, just run the docker build -t <image_name> <dir> command, and make sure you point it to the directory where your Dockerfile is hanging out.

    • The .dockerignore file comes in handy when you want to skip over some files in your Docker image context directory.

  1. Layers πŸ—‚οΈ

    • Each instruction in a Dockerfile adds a new layer. It's like stacking blocks: each step makes a container, runs the instruction, and saves the result as a new layer.

    • These layers pile up, and the final image is a mix of all these layers.

    • When you run an image and spin up a container, a new writable layer, called the container layer, gets added on top of the existing layers.

    • Container layer is where all the action happens β€” any changes you make, like adding new files, tweaking existing ones, or deleting stuff, all get handled here.

  1. Cache πŸ’Ύ

    • Docker keeps intermediate images to speed up future builds.

    • When you rebuild an image, Docker uses cached layers if nothing's changed.

    • "Using cache" means Docker is reusing layers it already made, instead of creating new ones.

    • Caching makes the build process much faster for the parts of your Dockerfile that stay the same.

πŸ‘‰ Instructions βœ…

  1. FROM

    • Sets the Base Image for the next steps.

        FROM [--platform=<platform>] <image> [AS <name>]
      
        FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
      
        FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
      
    • β€œBhai, Python environment chahiye mujhe.”

        FROM python:3.11
      
  1. COPY

    • Copies files and directories from the host machine to the Docker image. πŸ“Έ

        # syntax
        COPY <source> <destination>
      
        # example
        COPY localfile.txt /app/localfile.txt
        COPY . .
      
  1. ADD

    • It copies files and directories to the Docker image, downloads files from URLs 🌐, and unzips tar files πŸ“¦ directly into the destination folder.

        # syntax
        ADD <source> <destination>
      
        # example
        ADD anotherfile.txt /app/anotherfile.txt
        ADD https://example.com/file.txt /app/file.txt
        ADD archive.tar.gz /app/
      
  1. RUN

    • Executes commands during build (executable or shell).

        # shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows
        RUN <command>
      
        # exec form
        RUN ["executable", "param1", "param2"]
      
    • β€œPackage install karna hai? Run it here.”

        # Install python packages
        RUN pip install -r requirements.txt
      
  1. CMD

    • Tells Docker what to run when container starts.

    • Dockerfile can only contain one CMD instruction. If you enter more than one CMD, only the last one will be executed.

    • Don’t confuse CMD with RUN !!! (RUN at build-time, CMD at run-time)

        # exec form, this is the preferred form
        CMD ["executable","param1","param2"]
      
        # as default parameters to ENTRYPOINT
        CMD ["param1","param2"]
      
        # shell form
        CMD command param1 param2
      
    • "Container start hone par HOME env variable ki value print karna hai ?"

        CMD echo $HOME
        or
        CMD ["sh", "-c", "echo $HOME"]
      
  1. WORKDIR

    • Sets the working directory inside the container and changes the directory for the next commands.

    • So, whatever RUN, CMD, and everything else happens, it executes at the given path.

    • β€œFrom now on, kaam yahin se hoga.”

        # syntax
        WORKDIR <directory>
      
        # example
        WORKDIR /usr/src/app
      
  1. ENTRYPOINT

    • Ensures that a specific command or task always runs when the container starts, regardless of any additional commands you provide.

        # exec form, which is the preferred form
        ENTRYPOINT ["executable", "param1", "param2"]
      
        # shell form
        ENTRYPOINT command param1 param2
      
    • You should include at least one CMD or ENTRYPOINT command in the Dockerfile.

    • If you want to use the container as a program, you need to set ENTRYPOINT.

    • Use CMD to set default arguments for ENTRYPOINT.

Overriding CMD and ENTRYPOINT

    FROM ubuntu:latest

    ENTRYPOINT ["echo", "Default ENTRYPOINT:"]
    CMD ["Hello from CMD"]

    # Default behaviour will be like below
    # Default ENTRYPOINT: Hello from CMD
  • CMD can be overriden by adding more arguments when you run the container

    docker run <container_name> <arguments>

    
      docker run mycontainer "Overridden CMD"
    
      # CMD overriden output will be like below
      # Default ENTRYPOINT: Overridden CMD
    
  • ENTRYPOINT can be overriden by --entrypoint
    docker run --entrypoint <command> <container_name> <arguments>

      docker run --entrypoint /bin/echo mycontainer "Overridden ENTRYPOINT"
    
      # ENTRYPOINT overriden output will be like below
      # Overridden ENTRYPOINT
    
  1. LABEL

    • Adds metadata (information) to the image.

    • β€œYeh image maine banaya hai β€” haan bhai, credit toh banta hai!” 😎

        LABEL maintainer="you@example.com"
      

  1. SHELL

    • The SHELL instruction lets you switch up the default shell for shell-form commands. On Linux, it usually defaults to ["/bin/sh", "-c"], and on Windows, it's ["cmd", "/S", "/C"].

    • This comes in handy on Windows, where cmd, powershell, and sh are pretty common.

    • You can use the SHELL instruction more than once, and each time you do, it replaces the previous one and changes all the commands that come after it.

        SHELL ["executable", "parameters"]
      
    • β€œChalo powershell use karte hain…”

        SHELL ["powershell", "-command"]
        RUN echo Hello
      
  1. HEALTHCHECK

    • Checks if your app inside container is still running properly.

        HEALTHCHECK CMD curl --fail http://localhost:3000 || exit 1
      
    • β€œApp down hai ya nahi? Yeh check karega.”

  1. VOLUME

    • To persist data outside the container.

    • Container delete hone ke baad bhi data safe rahega.

        VOLUME <dir>
      
  1. ENV

    • Sets environment variables.

    • Multiple <key>=<value>… variables can be set at the same time using the ENV instruction.

        # Syntax
        ENV <key>=<value>
      
        # Example
        ENV APP_NAME="MyApp"
      
  1. EXPOSE

    • Opens a port.

    • The EXPOSE instruction doesn't really open the port. It's more like a hint for the image creator and container user about which ports should be opened.

    • To open and map ports, use the -p flag with docker run for specific ports, or use the -P flag to open all exposed ports and map them to higher ports.

    • EXPOSE assumes TCP by default. You can also use UDP.

    • Its like saying β€œOye Docker, listen on this port.” 😏

        # Syntax
        EXPOSE <port>
        EXPOSE <port> [<port>/<protocol>...]
      
        # Example
        EXPOSE 8080
        EXPOSE 80/udp
      

πŸ‘‰ Best Practices & Common Mistakes πŸ˜‰

Best Practices

  • Use Multi-Stage Builds: Keep your final image slim.

  • .dockerignore is your BFF: Skip unnecessary files. 🚫

      # sample .dockerignore file
      *.pyc
      __pycache__/
      *.log
      config/
      README.md
    
  • Avoid using latest: Version everything properly. πŸ”’

  • Chain RUN commands: Reduce layers and improve caching.

  • Don't run as root: Use USER for safety.

Common Gotchas

  • COPY fails? Check your paths and .dockerignore.

  • CMD not working? Switch to ENTRYPOINT. πŸ”„

  • Caching issues? Reorder instructions β€” COPY before RUN if files change often.

πŸ‘‰ Folder Structure πŸ“‚

my-project/
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ .dockerignore
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ main.py
β”‚   β”œβ”€β”€ requirements.txt
β”‚   └── utils.py
β”œβ”€β”€ config/
β”‚   └── config.yaml
└── README.md

πŸ‘‰ Examples πŸ˜€

  1. Python

     # Use the official Python image
     FROM python:3.9
    
     # Set the working directory
     WORKDIR /app
    
     # Copy requirements file and install dependencies
     COPY requirements.txt .
     RUN pip install -r requirements.txt
    
     # Copy the rest of the application code
     COPY . .
    
     # Set the default command to run the application
     CMD ["python", "app.py"]
    
  2. React

     # Use the official Node.js image
     FROM node:14
    
     # Set the working directory
     WORKDIR /app
    
     # Copy package.json and package-lock.json and install dependencies
     COPY package.json package-lock.json ./
     RUN npm install
    
     # Copy the rest of the application code
     COPY . .
    
     # Build the application
     RUN npm run build
    
     # Expose the port the app runs on
     EXPOSE 3000
    
     # Set the default command to run the application
     CMD ["npm", "start"]
    

πŸ‘‰ Conclusion βœ…

You're CRUSHING IT with Docker now! πŸ”₯ Upcoming Part 3 - Docker Compose blog SOON! πŸš€πŸ³ Stay tuned - it's gonna be EPIC! #DockerGang ✨ Till then…

Happy Learning !!! πŸ‘‹

0
Subscribe to my newsletter

Read articles from Sudhanshu Sanjay Motewar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sudhanshu Sanjay Motewar
Sudhanshu Sanjay Motewar