Installing Hadoop: Ubuntu/WSL and a Docker cluster

This is the hands-on lesson: by the end you will have a working Hadoop cluster. Path A installs Hadoop 3.5.0 by hand on Ubuntu or WSL, so you see every file and step. Path B starts one NameNode, three DataNodes and YARN in Docker containers, so you can watch replication and failures on a real multi-node cluster. Both paths were run start to finish for this lesson; the outputs and error messages are the real ones.

Module 4 · Setup⏱ 40 minBeginner → IntermediateTested end to endDocker 3-node clusterError → fix table8 practice problems
1 The story

Two install paths, one working cluster each

Install day in the lab. Riya wants to see every file Hadoop needs, so she installs it by hand inside WSL Ubuntu. Kabir wants three DataNodes on his laptop, which is impossible by hand on one machine, so he reaches for Docker. Both start with a coffee and an error message.

Path A · Riya · native Ubuntu / WSL 1 download + verify tarball2 unpack, set HADOOP_HOME3 edit four *-site.xml files4 SSH key, format NameNode5 start-dfs.sh, start-yarn.sh 1 machine5 daemonsreplication 1 learn what is inside Path B · Kabir · Docker Compose 1 install Docker2 write 2 small files3 docker compose up --scale datanode=3 NameNode RM + NM DN1 DN2 DN3 see a real multi-node cluster Do both: Path A teaches how Hadoop is put together, Path B gives you a cluster to break. myphatshala.in
Riya

Sir, which way is the “correct” install?

Arjun Sir

Both are used at work. Admins who build clusters know the files and steps of Path A by heart, because every installer, Docker image and cluster manager does exactly those steps underneath. And Path B is how many teams spin up a practice or test cluster in minutes.

Kabir

Then why not only Docker? It's faster.

Arjun Sir

Because when Docker hides something and it breaks, you'll need to know what it hid. Today, Riya's machine becomes a one-node cluster; Kabir's laptop gets one NameNode, three DataNodes and YARN. Every command and output you'll see was run for real on Hadoop 3.5.0, including the errors.

2 Definition

Installing Hadoop

Formal definition (write this in exams)

Installing Apache Hadoop from the binary release means: downloading the release tarball and verifying its checksum; unpacking it and setting HADOOP_HOME and PATH; setting JAVA_HOME in etc/hadoop/hadoop-env.sh; writing the site configuration files (core-site.xml, hdfs-site.xml, mapred-site.xml, yarn-site.xml, and workers); setting up passwordless SSH for the start scripts; formatting the NameNode once; and starting HDFS (start-dfs.sh) and YARN (start-yarn.sh). A containerised installation runs the same daemons from a prebuilt image (e.g. apache/hadoop:3.5.0), with configuration supplied through environment variables and one container per daemon, orchestrated by Docker Compose.

In simple words

Download Hadoop, check it isn't damaged, unpack it, tell it where Java is, fill in four settings files, let it log in to itself, prepare an empty HDFS, and start it. Or let Docker start ready-made Hadoop containers for you.

Words you'll meet in this lesson

TarballA .tar.gz archive. Hadoop's binary release is one tarball; unpack it with tar -xzf.
Checksum (SHA-512)A fingerprint of the file. If yours matches the published one, the download is complete and untampered.
HADOOP_HOMEThe folder where Hadoop is unpacked, e.g. ~/hadoop-3.5.0.
hadoop-env.shA script every Hadoop command reads first. Set JAVA_HOME here.
*-site.xmlYour settings. They override Hadoop's built-in defaults (Lesson 4.4).
Docker imageA ready-made file system with Hadoop and Java installed: apache/hadoop:3.5.0.
ContainerA running copy of an image with its own host name and IP. Here: one container per daemon.
Docker ComposeStarts several containers from one docker-compose.yaml file, on a shared network.

What was tested for this lesson

Hadoop3.5.0 binary tarball, 581 MB download, 1.3 GB unpacked; SHA-512 verified
Path AUbuntu 24.04, OpenJDK 17.0.20.1, pseudo-distributed, every step below run in order
Path BDocker Compose with the official apache/hadoop:3.5.0 image (Ubuntu 24.04, OpenJDK 17, about 2.2 GB): 1 NameNode, 3 DataNodes, ResourceManager, NodeManager
RAM usedIdle daemons measured at about 0.2–0.35 GB each (about 1.3 GB for all five in Path A); jobs need more, so give WSL or Docker 6–8 GB if you can

Installing at three levels

Pick your level. New to this? Start at Beginner and climb.

Beginner Setting up a new canteen

  • Download and verify: the equipment arrives; check every box is sealed.
  • Configure: decide where the store room (HDFS) and the kitchen (YARN) are.
  • Format: label the empty shelves of the store room once, on opening day.
  • Start: switch on the store room and the kitchen. Docker is a ready-made food truck: everything installed, just drive it in.

Intermediate The order matters

  • Java and SSH first (Lesson 4.1), then unpack, then hadoop-env.sh, then the XML files.
  • Format once, before the first start-dfs.sh. Formatting again erases HDFS.
  • Start HDFS before YARN: jobs read input from HDFS.
  • Wait until safe mode is off (hdfs dfsadmin -safemode wait) before writing.
  • Check each stage: hadoop version → jps → hdfs dfs -ls → yarn node -list → run an example job.

Advanced What production installs add

  • Dedicated users (hdfs, yarn, mapred), data on dedicated disks (dfs.datanode.data.dir lists several mount points), logs and PIDs outside /tmp.
  • Configuration pushed identically to all nodes by automation or a cluster manager; a workers file or service manager instead of hand-run scripts.
  • Kerberos security, TLS for web UIs, high availability (two NameNodes, JournalNodes, ZooKeeper), monitoring and log rotation.
  • Container setups mount volumes for the NameNode and DataNode folders, otherwise data disappears with the container (you'll see this happen below).

Exam tip: For “Explain the steps to install Hadoop in pseudo-distributed mode”, list: install Java and SSH → download and extract Hadoop → set JAVA_HOME, HADOOP_HOME, PATH → edit core-site.xml (fs.defaultFS), hdfs-site.xml (dfs.replication=1), mapred-site.xml (mapreduce.framework.name=yarn), yarn-site.xml (yarn.nodemanager.aux-services=mapreduce_shuffle) → passwordless SSH → hdfs namenode -format → start-dfs.sh, start-yarn.sh → verify with jps and the web UIs.

3 Types and ways to use it

Ways to install Hadoop

Path A: tarball (native)Path B: Docker ComposeAt companies
What you getPseudo-distributed, 1 nodeFully distributed in containersFully distributed on servers or cloud VMs
Time30–45 minutes the first time5 minutes plus the image downloadHours to days, automated
ConfigurationXML files you editEnvironment variables turned into XML by the imageCluster manager or automation templates
Data survives restart?Yes, if storage folders are outside /tmpWith stop/start yes; down deletes it (no volumes)Yes, on dedicated disks
Best forUnderstanding every pieceMulti-node behaviour: replication, failuresProduction

How organisations get Hadoop

Apache tarball

The release you install today. Teams that build their own platform start here and automate the steps.

Do it yourself

Packaged distributions

Apache Bigtop builds Linux packages; vendors such as Cloudera ship tested bundles with a manager.

Packages

Containers

The apache/hadoop images on Docker Hub, or Kubernetes operators, for test and dev clusters.

Containers

Managed cloud

Amazon EMR, Google Dataproc and Azure HDInsight create a ready cluster in minutes.

Cloud

Keep a text file of every command you run during an install. When something breaks next week, it is the first thing you'll want, and it is the start of automating the install.

4 Step by step

Path A: install natively on Ubuntu or WSL

Prerequisites from Lesson 4.1 must be done: Java 17, SSH server running, JAVA_HOME in ~/.bashrc. All commands run in the Ubuntu terminal as your normal user; the user here is riya, so replace /home/riya with your own home folder.

  1. Download and verify

    cd ~
    wget https://dlcdn.apache.org/hadoop/common/hadoop-3.5.0/hadoop-3.5.0.tar.gz
    wget https://dlcdn.apache.org/hadoop/common/hadoop-3.5.0/hadoop-3.5.0.tar.gz.sha512
    sha512sum -c hadoop-3.5.0.tar.gz.sha512
    hadoop-3.5.0.tar.gz: OK
    Anything other than OK means a broken or altered download: delete it and download again. If a newer 3.5.x exists when you read this, change the version number everywhere.
  2. Unpack and set the environment

    tar -xzf hadoop-3.5.0.tar.gz
    cat >> ~/.bashrc <<'EOF'
    export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
    export HADOOP_HOME=$HOME/hadoop-3.5.0
    export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
    EOF
    source ~/.bashrc
    hadoop version | head -1
    Hadoop 3.5.0
    If you added the JAVA_HOME line in Lesson 4.1, don't add it twice. bin holds the user commands (hdfs, yarn, hadoop); sbin holds the start and stop scripts.
  3. Tell Hadoop where Java is

    echo "export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64" >> $HADOOP_HOME/etc/hadoop/hadoop-env.sh
    This is the fix for ERROR: JAVA_HOME is not set and could not be found: daemons started over SSH read this file, not your ~/.bashrc.
  4. core-site.xml and hdfs-site.xml

    <configuration>
        <property>
            <name>fs.defaultFS</name>
            <value>hdfs://localhost:9000</value>
        </property>
    </configuration>
    <configuration>
        <property>
            <name>dfs.replication</name>
            <value>1</value>
        </property>
        <property>
            <name>dfs.namenode.name.dir</name>
            <value>file:///home/riya/hadoop-data/namenode</value>
        </property>
        <property>
            <name>dfs.datanode.data.dir</name>
            <value>file:///home/riya/hadoop-data/datanode</value>
        </property>
    </configuration>
    The two dir settings move HDFS out of /tmp, which Linux may empty on reboot. Open each file with nano and replace the empty <configuration> block.
  5. mapred-site.xml and yarn-site.xml

    <configuration>
        <property>
            <name>mapreduce.framework.name</name>
            <value>yarn</value>
        </property>
        <property>
            <name>mapreduce.application.classpath</name>
            <value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value>
        </property>
    </configuration>
    <configuration>
        <property>
            <name>yarn.nodemanager.aux-services</name>
            <value>mapreduce_shuffle</value>
        </property>
        <property>
            <name>yarn.nodemanager.env-whitelist</name>
            <value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_HOME,PATH,LANG,TZ,HADOOP_MAPRED_HOME</value>
        </property>
        <property>
            <name>yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage</name>
            <value>99.5</value>
        </property>
    </configuration>
    The first two properties come from the official single-node guide: they run MapReduce on YARN and let containers find the MapReduce jars. The third is for laptops: by default a NodeManager whose disk is over 90% full marks itself unhealthy and runs nothing (see the errors table). Leave it out on real servers.
  6. Passwordless SSH to localhost

    ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
    cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
    chmod 0600 ~/.ssh/authorized_keys
    ssh localhost "echo connected"
    Warning: Permanently added 'localhost' (ED25519) to the list of known hosts. connected
    The first connection asks to trust the host key (type yes). After that, no questions and no password: exactly what start-dfs.sh needs.
  7. Format the NameNode (once)

    hdfs namenode -format
    … INFO common.Storage: Storage directory /home/riya/hadoop-data/namenode has been successfully formatted. …
    Look for “successfully formatted” in the long output. Never run this again on a cluster with data you need.
  8. Start HDFS and check it

    start-dfs.sh
    jps
    hdfs dfsadmin -safemode wait
    hdfs dfs -mkdir -p /user/riya
    hdfs dfs -mkdir input
    hdfs dfs -put $HADOOP_HOME/etc/hadoop/*.xml input
    Starting namenodes on [localhost] Starting datanodes Starting secondary namenodes [vm] vm: Warning: Permanently added 'vm' (ED25519) to the list of known hosts. 4865 NameNode 4966 DataNode 5304 Jps 5166 SecondaryNameNode Safe mode is OFF
    /user/riya is your HDFS home: relative paths like input mean /user/riya/input. The NameNode web UI is now at http://localhost:9870 (WSL forwards it to Windows).
  9. Start YARN and run a job

    start-yarn.sh
    yarn node -list
    hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.5.0.jar \
        grep input output 'dfs[a-z.]+'
    hdfs dfs -cat output/*
    Starting resourcemanager Starting nodemanagers Total Nodes:1 Node-Id Node-State Node-Http-Address Number-of-Running-Containers localhost:40123 RUNNING localhost:8042 0 … INFO mapreduce.Job: Job job_1792562428511_0001 completed successfully … 1 dfsadmin 1 dfs.replication 1 dfs.namenode.name.dir 1 dfs.datanode.data.dir
    The ResourceManager UI is at http://localhost:8088. The result lists the dfs… property names found in the XML files you uploaded, including the ones you just wrote.
  10. Stop, and start again later

    stop-yarn.sh
    stop-dfs.sh
    # next time:
    start-dfs.sh
    start-yarn.sh
    hdfs dfs -cat output/*
    Stopping nodemanagers Stopping resourcemanager Stopping namenodes on [localhost] Stopping datanodes Stopping secondary namenodes [vm] … 1 dfsadmin 1 dfs.replication 1 dfs.namenode.name.dir 1 dfs.datanode.data.dir
    The output is still there after a full stop and start, because HDFS lives in ~/hadoop-data. On WSL, also run sudo service ssh start after reopening Ubuntu.

Path B: a 3-DataNode cluster with Docker Compose

Install Docker Desktop (Windows/macOS, with the WSL 2 back end on Windows) or Docker Engine (Linux), and give it at least 6 GB of memory. Then make a folder hadoop-cluster with these two files.

  1. docker-compose.yaml

    services:
      namenode:
        image: apache/hadoop:3.5.0
        hostname: namenode
        command: ["hdfs", "namenode"]
        ports:
          - 9870:9870
        env_file:
          - ./config
        environment:
          ENSURE_NAMENODE_DIR: "/tmp/hadoop-hadoop/dfs/name"
      datanode:
        image: apache/hadoop:3.5.0
        command: ["hdfs", "datanode"]
        env_file:
          - ./config
      resourcemanager:
        image: apache/hadoop:3.5.0
        hostname: resourcemanager
        command: ["yarn", "resourcemanager"]
        ports:
          - 8088:8088
        env_file:
          - ./config
      nodemanager:
        image: apache/hadoop:3.5.0
        command: ["yarn", "nodemanager"]
        env_file:
          - ./config
    One service per daemon. ENSURE_NAMENODE_DIR: if this folder does not exist, the image formats the NameNode before starting it. It must be the NameNode's real folder; the image runs as user hadoop, so that is /tmp/hadoop-hadoop/dfs/name. (With /tmp/hadoop-root/…, as in some older examples, the NameNode is re-formatted on every start; this happened during testing.) Only the NameNode and ResourceManager UIs are published to your PC.
  2. config (the settings)

    CORE-SITE.XML_fs.defaultFS=hdfs://namenode
    HDFS-SITE.XML_dfs.namenode.rpc-address=namenode:8020
    HDFS-SITE.XML_dfs.replication=3
    MAPRED-SITE.XML_mapreduce.framework.name=yarn
    MAPRED-SITE.XML_yarn.app.mapreduce.am.env=HADOOP_MAPRED_HOME=/opt/hadoop
    MAPRED-SITE.XML_mapreduce.map.env=HADOOP_MAPRED_HOME=/opt/hadoop
    MAPRED-SITE.XML_mapreduce.reduce.env=HADOOP_MAPRED_HOME=/opt/hadoop
    YARN-SITE.XML_yarn.resourcemanager.hostname=resourcemanager
    YARN-SITE.XML_yarn.nodemanager.pmem-check-enabled=false
    YARN-SITE.XML_yarn.nodemanager.vmem-check-enabled=false
    YARN-SITE.XML_yarn.nodemanager.aux-services=mapreduce_shuffle
    YARN-SITE.XML_yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage=99.5
    CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.maximum-am-resource-percent=0.5
    CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.queues=default
    CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.capacity=100
    CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.maximum-capacity=100
    Each line is FILE_property=value; the image writes them into the matching XML file when a container starts. Write /opt/hadoop literally: this file does not expand variables like $HADOOP_HOME.
  3. Start with three DataNodes

    docker compose up -d --scale datanode=3
    docker compose ps --format "table {{.Name}}\t{{.Status}}"
    NAME STATUS hadoop-cluster-datanode-1 Up 35 seconds hadoop-cluster-datanode-2 Up 35 seconds hadoop-cluster-datanode-3 Up 35 seconds hadoop-cluster-namenode-1 Up 36 seconds hadoop-cluster-nodemanager-1 Up 36 seconds hadoop-cluster-resourcemanager-1 Up 36 seconds
    The first run downloads the image (about 2.2 GB unpacked). Six containers, six “machines”, one network.
  4. Check the cluster

    docker compose exec namenode hdfs dfsadmin -printTopology
    docker compose exec resourcemanager yarn node -list
    Rack: /default-rack 172.18.0.6:9866 (hadoop-cluster-datanode-3.hadoop-cluster_default) In Service 172.18.0.5:9866 (hadoop-cluster-datanode-1.hadoop-cluster_default) In Service 172.18.0.7:9866 (hadoop-cluster-datanode-2.hadoop-cluster_default) In Service Total Nodes:1 Node-Id Node-State Node-Http-Address Number-of-Running-Containers 8f368ea04df2:45495 RUNNING 8f368ea04df2:8042 0
    Three DataNodes in HDFS, one NodeManager in YARN. The NodeManager's name is its container ID.
  5. Store a file and see its three copies

    docker compose exec namenode hdfs dfs -mkdir -p /user/hadoop/data
    docker compose cp canteen_orders.csv namenode:/tmp/canteen_orders.csv
    docker compose exec namenode hdfs dfs -put /tmp/canteen_orders.csv data/
    docker compose exec namenode hdfs fsck /user/hadoop/data/canteen_orders.csv -files -blocks -locations
    /user/hadoop/data/canteen_orders.csv 4692009 bytes, replicated: replication=3, 1 block(s): OK 0. BP-1055690327-172.18.0.2-1792562685335:blk_1073741825_1001 len=4692009 Live_repl=3 [DatanodeInfoWithStorage[172.18.0.6:9866,…], DatanodeInfoWithStorage[172.18.0.7:9866,…], DatanodeInfoWithStorage[172.18.0.5:9866,…]] Status: HEALTHY Number of data-nodes: 3
    canteen_orders.csv is a 3 lakh-row practice file (order_id, item, amount); any CSV works. One 4.5 MB block, stored on all three DataNodes.
  6. Run a job, then see what down does

    docker compose exec namenode yarn jar \
        /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.5.0.jar \
        grep data out "vada pav|samosa|dosa"
    docker compose exec namenode hdfs dfs -cat "out/*"
    docker compose down
    docker compose up -d --scale datanode=3
    docker compose exec namenode hdfs dfs -ls data
    INFO Job:1765 - Job job_1792562696287_0001 completed successfully Rack-local map tasks=1 … 50021 samosa 49986 vada pav 49948 dosa ls: `data': No such file or directory
    down deletes the containers, and with them the HDFS data. The map task is “rack-local”, not “data-local”, because DataNodes and the NodeManager are separate containers here.
  7. Pause and resume without losing data

    docker compose stop
    docker compose start
    docker compose exec namenode hdfs dfsadmin -safemode wait
    docker compose exec namenode hdfs dfs -ls data
    Safe mode is OFF Found 1 items -rw-r--r-- 3 hadoop supergroup 4692009 2026-10-21 06:15 data/canteen_orders.csv
    stop keeps the containers and their disks, so the file is still there (after uploading it again following the down above). Use stop/start at the end of each practice session.

Errors you will meet (all seen during testing)

You seeCauseFix
localhost: ERROR: JAVA_HOME is not set and could not be found.Daemons started over SSH don't read ~/.bashrcSet JAVA_HOME in etc/hadoop/hadoop-env.sh (step 3)
localhost: Permission denied (publickey,password).No passwordless SSH keyStep 6; check sudo service ssh status
Name node is in safe mode.The NameNode has just started and is still collecting block reportsWait: hdfs dfsadmin -safemode wait
FileAlreadyExistsException: Output directory hdfs://localhost:9000/user/riya/output already existsMapReduce never overwrites outputhdfs dfs -rm -r output or use a new folder name
Job stays at Running job; yarn node -list shows Total Nodes:0 or UNHEALTHYNodeManager disk over 90% full; its log says used space above threshold of 90.0%Free disk space, or raise …max-disk-utilization-per-disk-percentage (step 5), restart YARN
Error: Could not find or load main class org.apache.hadoop.mapreduce.v2.app.MRAppMasterContainers can't find the MapReduce jarsSet HADOOP_MAPRED_HOME (Docker config) or mapreduce.application.classpath (step 5)
Docker NameNode container exits at once; UnknownHostException: namenodePort 9870 already used, e.g. by the Path A NameNode on the same PCRun stop-dfs.sh and stop-yarn.sh before docker compose up
DataNode exits: Incompatible clusterIDs in /tmp/hadoop-hadoop/dfs/data: namenode clusterID = CID-6a4b…; datanode clusterID = CID-b43b…The NameNode was formatted again, so it has a new cluster IDNever re-format a cluster with data. On a test cluster: empty the DataNode folder (or recreate the containers)
Files gone after docker compose downNo volumes: data lived inside the containersUse stop/start, or add volumes

Run Path A and Path B one at a time on the same PC. Both use ports 9870 and 8088, and a NameNode that cannot open its port fails in confusing ways.

5 See it happen

What docker compose up builds, and what down destroys

Kabir's cluster from Path B, step by step. Every terminal line is from the real test run.

Life of the Docker practice cluster 6 steps
What you see kabir@laptop : ~/hadoop-cluster $ docker compose up -d \ --scale datanode=3Network hadoop-cluster_default CreatedContainer …-namenode-1 StartedContainer …-resourcemanager-1 Started Container …-datanode-1 StartedContainer …-datanode-2 StartedContainer …-datanode-3 StartedContainer …-nodemanager-1 Started $ … hdfs dfsadmin -printTopologyRack: /default-rack 172.18.0.6:9866 (…datanode-3) In Service 172.18.0.5:9866 (…datanode-1) In Service 172.18.0.7:9866 (…datanode-2) In Service $ … hdfs dfs -put canteen_orders.csv data/$ … hdfs fsck …/canteen_orders.csv …4692009 bytes, replication=3, 1 block(s)blk_1073741825_1001 Live_repl=3Status: HEALTHY $ … yarn jar …examples-3.5.0.jar grep \ data out "vada pav|samosa|dosa"… completed successfully$ … hdfs dfs -cat "out/*"50021 samosa49986 vada pav49948 dosa $ docker compose down$ docker compose up -d --scale datanode=3$ … hdfs dfs -ls datals: `data': No such file or directorydown = containers and data deleted myphatshala.in
Behind the screen network: hadoop-cluster_default namenode:9870 → your PC resourcemanager:8088 → your PC first start formats the NameNode(ENSURE_NAMENODE_DIR) datanode-1172.18.0.5 datanode-2172.18.0.7 datanode-3172.18.0.6 NMtasks heartbeats: 3 live DataNodes blk …825blk …825blk …825 one block × 3 replicas map task reads a replica over the network(rack-local: DN and NM are different containers) new namenodefresh, formatted old containers removedtheir disks, and blk …825, are gone stop / start keeps data · down deletes it myphatshala.in
6 Real-world use

Installs at work

playbooksteps 1–10

1. The manual steps become a script

Platform teams turn exactly the Path A steps into automation (Ansible roles, shell scripts, images), so a new worker is installed in minutes and identical to the others.

Manual once→Automated→Same on every node
compose

2. Throw-away test clusters

Developers start a Compose cluster to test a job, a new configuration or an upgrade, then delete it. Because down removes everything, every test starts clean.

up→Test→down
sha512 OKsigned release

3. Verifying what is installed

Security teams require checksums (and often GPG signatures) for every downloaded binary, and keep an internal copy of approved releases instead of downloading on each server.

Download→Verify→Internal repository
runbookerror → fix

4. Runbooks for known errors

Support teams keep a table like the one in Part 4: the exact error text, the cause and the fix. Searching the log for the exact message is the first step in almost every Hadoop incident.

Error text→Runbook→Fix in minutes

Simplified; organisations differ in tools and policies.

7 Run the code

Turn the Docker config file into Hadoop XML

The apache/hadoop image reads lines like CORE-SITE.XML_fs.defaultFS=hdfs://namenode and writes them into the right XML file. This program does the same, so you can see the XML your containers really use.

config = """CORE-SITE.XML_fs.defaultFS=hdfs://namenode
HDFS-SITE.XML_dfs.replication=3
YARN-SITE.XML_yarn.resourcemanager.hostname=resourcemanager
YARN-SITE.XML_yarn.nodemanager.aux-services=mapreduce_shuffle"""

files = {}
for line in config.splitlines():
    key, value = line.split("=", 1)
    filename, prop = key.split("_", 1)
    files.setdefault(filename.lower(), []).append((prop, value))

for filename, props in files.items():
    print(f"--- {filename}")
    print("<configuration>")
    for name, value in props:
        print(f"  <property><name>{name}</name><value>{value}</value></property>")
    print("</configuration>")
--- core-site.xml <configuration> <property><name>fs.defaultFS</name><value>hdfs://namenode</value></property> </configuration> --- hdfs-site.xml <configuration> <property><name>dfs.replication</name><value>3</value></property> </configuration> --- yarn-site.xml <configuration> <property><name>yarn.resourcemanager.hostname</name><value>resourcemanager</value></property> <property><name>yarn.nodemanager.aux-services</name><value>mapreduce_shuffle</value></property> </configuration>
  • key, value = line.split("=", 1)Split only at the first =: values such as HADOOP_MAPRED_HOME=/opt/hadoop contain another one.
  • filename, prop = key.split("_", 1)The file name is before the first underscore. Property names contain dots and sometimes underscores, so split only once.
  • files.setdefault(filename.lower(), []).append((prop, value))setdefault creates an empty list the first time a file name appears, then keeps adding to it.

Run it on your PC: make the practice file and check Hadoop's answer

Path B uses canteen_orders.csv. This program creates it (the same file on every computer, because the random generator is seeded) and then counts the items with plain Python, so you can check the answer your Hadoop job gives. Save it as make_canteen.py in the hadoop-cluster folder and run it.

Create canteen_orders.csv and count three items

# make_canteen.py: create the practice file canteen_orders.csv (3 lakh orders)
import os
import random

random.seed(7)                       # same file on every computer
prices = {"chai": 15, "vada pav": 20, "samosa": 18, "poha": 25, "idli": 30, "dosa": 45}
items = list(prices)

with open("canteen_orders.csv", "w") as f:
    f.write("order_id,item,amount\n")
    for order_id in range(1001, 1001 + 300_000):
        item = random.choice(items)
        f.write(f"{order_id},{item},{prices[item]}\n")

print(f"Wrote canteen_orders.csv: 300,000 orders, {os.path.getsize('canteen_orders.csv'):,} bytes")

# Check Hadoop's answer with plain Python
counts = {}
with open("canteen_orders.csv") as f:
    next(f)                          # skip the header
    for line in f:
        item = line.split(",")[1]
        counts[item] = counts.get(item, 0) + 1
for item in ["samosa", "vada pav", "dosa"]:
    print(f"{counts[item]:>6}  {item}")

# In a terminal (in the hadoop-cluster folder):
#   python3 make_canteen.py
Wrote canteen_orders.csv: 300,000 orders, 4,692,009 bytes 50021 samosa 49986 vada pav 49948 dosa

These counts match the Hadoop job in Path B exactly (50021 samosa, 49986 vada pav, 49948 dosa). Checking a cluster result against a small independent calculation is a habit worth keeping.

8 Line by line

One config line becomes one XML property

Follow a single line of the Docker config file through the conversion and watch each variable.

Worked examples: real problems, solved and traced in memory

Three things people script during real installs: writing the site files from one table, checking an install stage by stage, and catching configuration mistakes before starting a cluster. Each runs live and is traced in memory.

Example 1BeginnerInstall · automation

Write all four site files from one table

Problem statement

Instead of editing four XML files by hand, keep the pseudo-distributed settings in one Python dictionary and print each file. Use the settings from Path A, steps 4 and 5 (without the long whitelist).

Input
core: fs.defaultFS · hdfs: dfs.replication · mapred: mapreduce.framework.name · yarn: aux-services
Expected output
== core-site.xml
<configuration>
    <property><name>fs.defaultFS</name><value>hdfs://localhost:9000</value></property>
</configuration>
== hdfs-site.xml
<configuration>
    <property><name>dfs.replication</name><value>1</value></property>
</configuration>
== mapred-site.xml
<configuration>
    <property><name>mapreduce.framework.name</name><value>yarn</value></property>
</configuration>
== yarn-site.xml
<configuration>
    <property><name>yarn.nodemanager.aux-services</name><value>mapreduce_shuffle</value></property>
</configuration>

Approach

  1. Map each file name to its properties.
  2. Loop over files, then properties.
  3. Print the XML in Hadoop's format.

Solution: run it live

What happens in memory, step by step

Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.

Example 2IntermediateInstall · checks

Check an install stage by stage

Problem statement

A student sends the results of five checks from their install. Report the first stage that fails and the most likely fix, the way a support engineer would.

Input
version OK · jps has NameNode, DataNode, SecondaryNameNode · safe mode OFF · yarn nodes 0 · job not run
Expected output
PASS  hadoop version
PASS  HDFS daemons in jps
PASS  safe mode OFF
FAIL  yarn node -list > 0
      fix: NodeManager unhealthy? check disk space and NM log

Approach

  1. List the stages in order, each with a test and a fix.
  2. Walk through them; stop at the first failure.
  3. Later stages can't be judged until earlier ones pass.

Solution: run it live

What happens in memory, step by step

Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.

Example 3AdvancedAdmin · config lint

Catch configuration mistakes before starting

Problem statement

Write a small checker for a planned configuration. Flag: replication larger than the number of DataNodes; NameNode or DataNode folders under /tmp; YARN without the mapreduce_shuffle service; MapReduce not set to yarn.

Input
replication 3 · 1 DataNode · name.dir /tmp/… · aux-services missing · framework yarn
Expected output
- replication is higher than the number of DataNodes
- NameNode metadata under /tmp may vanish on reboot
- YARN has no mapreduce_shuffle service

Approach

  1. Write each rule as a condition and a message.
  2. Evaluate all rules, not just the first.
  3. Print every problem found, or “looks good”.

Solution: run it live

What happens in memory, step by step

Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.

9 Quiz and revision

Check yourself

Quick revision

Lesson 4.3 in one screen

  • Path A (native): download → sha512sum -c → unpack → HADOOP_HOME, PATH → JAVA_HOME in hadoop-env.sh.
  • Site files: core (fs.defaultFS), hdfs (dfs.replication=1, storage dirs), mapred (framework=yarn), yarn (mapreduce_shuffle).
  • SSH key for localhost → format once → start-dfs.sh → start-yarn.sh → jps.
  • Check: hadoop version → jps → safemode wait → yarn node -list → example job.
  • UIs: NameNode :9870, ResourceManager :8088.
  • Path B (Docker): docker-compose.yaml + config → docker compose up -d --scale datanode=3.
  • Docker: stop/start keeps data, down deletes it (no volumes).
  • Errors: JAVA_HOME → hadoop-env.sh · publickey → SSH key · safe mode → wait · output exists → rm -r · 0 nodes → NM disk · MRAppMaster → HADOOP_MAPRED_HOME.

Interview and viva questions

What are the steps to install Hadoop on Ubuntu?

Install Java (JDK 17 for Hadoop 3.5) and SSH; download and verify the Hadoop tarball; extract it and set HADOOP_HOME and PATH; set JAVA_HOME in hadoop-env.sh; configure core-site.xml, hdfs-site.xml, mapred-site.xml and yarn-site.xml; set up passwordless SSH to localhost; format the NameNode; start HDFS and YARN with start-dfs.sh and start-yarn.sh; verify with jps, the web UIs and an example job.

Can I install Hadoop on Windows?

For learning, install it inside WSL 2 (Ubuntu) using exactly the Path A steps, or run the Docker cluster with Docker Desktop. The web UIs are reachable from Windows at localhost:9870 and localhost:8088.

How do I check that Hadoop is installed correctly?

hadoop version prints the version; jps lists NameNode, DataNode, SecondaryNameNode, ResourceManager and NodeManager; hdfs dfsadmin -report shows live DataNodes; yarn node -list shows RUNNING NodeManagers; the NameNode UI (9870) and ResourceManager UI (8088) open; and an example job such as grep or wordcount completes.

Why is my NodeManager UNHEALTHY?

Usually a disk-health check: when a local or log directory is more than 90% full (the default threshold), the NodeManager marks it bad and reports itself unhealthy, so YARN schedules nothing there. Free disk space or, on a practice machine, raise yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage.

What is the official Hadoop Docker image?

The Apache Hadoop project publishes apache/hadoop images on Docker Hub (for example apache/hadoop:3.5.0). One image contains everything; the command decides which daemon a container runs, and environment variables become configuration.

How do I stop Hadoop?

Run stop-yarn.sh and then stop-dfs.sh (and mapred --daemon stop historyserver if you started it). With Docker, docker compose stop keeps the containers and data; docker compose down removes them.

Where are Hadoop logs?

In $HADOOP_HOME/logs by default, one file per daemon, for example hadoop-riya-namenode-<host>.log. In Docker, use docker compose logs namenode.

Do I need to format the NameNode every time?

No. Only once, when creating a cluster. Re-formatting erases the namespace and gives HDFS a new cluster ID, so existing DataNodes refuse to join until their data folders are cleaned.

Practice problems

Exercises on the install itself: paths, configuration, checks and errors. All runnable in the browser.

P1Easy

Build the download URL

Print the download URL of the tarball and its checksum for version 3.5.0, using the pattern https://dlcdn.apache.org/hadoop/common/hadoop-V/hadoop-V.tar.gz.

Input
version = "3.5.0"
Expected output
https://dlcdn.apache.org/hadoop/common/hadoop-3.5.0/hadoop-3.5.0.tar.gz
https://dlcdn.apache.org/hadoop/common/hadoop-3.5.0/hadoop-3.5.0.tar.gz.sha512
Show solution and explanation
v = "3.5.0"
url = f"https://dlcdn.apache.org/hadoop/common/hadoop-{v}/hadoop-{v}.tar.gz"
print(url)
print(url + ".sha512")
https://dlcdn.apache.org/hadoop/common/hadoop-3.5.0/hadoop-3.5.0.tar.gz https://dlcdn.apache.org/hadoop/common/hadoop-3.5.0/hadoop-3.5.0.tar.gz.sha512

How it works: The checksum file has the same name plus .sha512. Changing v is all it takes to install another release.

P2Easy

Relative HDFS paths

In HDFS, a relative path is relative to /user/<username>. Print the full paths for user riya of input and output/part-r-00000.

Input
user riya · input · output/part-r-00000
Expected output
input -> /user/riya/input
output/part-r-00000 -> /user/riya/output/part-r-00000
Show solution and explanation
user = "riya"
for p in ["input", "output/part-r-00000"]:
    print(p, "->", f"/user/{user}/{p}")
input -> /user/riya/input output/part-r-00000 -> /user/riya/output/part-r-00000

How it works: /user/riya/input and /user/riya/output/part-r-00000. That is why step 8 creates /user/riya first.

P3Easy

Which UI on which port?

Print the URL of the NameNode and ResourceManager web UIs on localhost.

Input
NameNode 9870 · ResourceManager 8088
Expected output
NameNode         http://localhost:9870
ResourceManager  http://localhost:8088
Show solution and explanation
for name, port in [("NameNode", 9870), ("ResourceManager", 8088)]:
    print(f"{name:16} http://localhost:{port}")
NameNode http://localhost:9870 ResourceManager http://localhost:8088

How it works: http://localhost:9870 and http://localhost:8088, the same for Path A and Path B (which is why they cannot run together).

P4Medium

Parse fsck

From the fsck line "len=4692009 Live_repl=3", print the block size in MB and the number of live replicas.

Input
len=4692009 Live_repl=3
Expected output
4.5 MB, 3 live replicas
Show solution and explanation
line = "len=4692009 Live_repl=3"
fields = dict(part.split("=") for part in line.split())
print(f"{int(fields['len']) / 1024 / 1024:.1f} MB, {fields['Live_repl']} live replicas")
4.5 MB, 3 live replicas

How it works: 4.5 MB and 3 live replicas: the whole file fits in one block, stored on all three DataNodes.

P5Medium

Disk check threshold

A NodeManager disk is 465 GB with 430 GB used. With the default 90% threshold, is it healthy? And with 99.5%?

Input
465 GB disk · 430 GB used · 90% and 99.5%
Expected output
used 92.5% vs limit 90% -> UNHEALTHY
used 92.5% vs limit 99.5% -> healthy
Show solution and explanation
used_pct = 430 / 465 * 100
for limit in [90, 99.5]:
    print(f"used {used_pct:.1f}% vs limit {limit}% -> {'healthy' if used_pct <= limit else 'UNHEALTHY'}")
used 92.5% vs limit 90% -> UNHEALTHY used 92.5% vs limit 99.5% -> healthy

How it works: 92.5% used: unhealthy with the default 90% limit, healthy with 99.5%. Typical of a student laptop full of downloads.

P6Medium

Docker memory budget

Idle Hadoop daemons measured during testing used about 0.2–0.35 GB of RAM each; take 0.3 GB. The Docker cluster runs 6 containers and Docker Desktop is given 4 GB. How much is left for job containers, each needing 1 GB?

Input
6 × 0.3 GB · 4 GB · 1 GB per job container
Expected output
daemons use 1.8 GB, 2.2 GB left -> 2 job container(s)
Show solution and explanation
used = 6 * 0.3
left = 4 - used
print(f"daemons use {used:.1f} GB, {left:.1f} GB left -> {int(left // 1)} job container(s)")
daemons use 1.8 GB, 2.2 GB left -> 2 job container(s)

How it works: About 1.8 GB for the idle daemons and 2.2 GB left: room for only 2 job containers, and a MapReduce job needs one just for its ApplicationMaster. Give Docker 6–8 GB if you can.

P7Business problem

Manual vs scripted install

A team installs 12 practice clusters a year for training batches. By hand: 45 minutes each. Docker Compose: 5 minutes each, plus 3 hours once to prepare the files. Hours saved in a year?

Input
12 installs · 45 min vs 5 min · 3 h setup
Expected output
manual 9 h, compose 4 h, saved 5 h
Show solution and explanation
manual = 12 * 45 / 60
compose = 3 + 12 * 5 / 60
print(f"manual {manual:.0f} h, compose {compose:.0f} h, saved {manual - compose:.0f} h")
manual 9 h, compose 4 h, saved 5 h

How it works: 9 hours by hand against 4 with Compose: 5 hours saved in the first year, and every batch gets an identical cluster.

P8Advanced

Diagnose from error text

Write a function that maps an error message to its fix, using keywords from the errors table, and test it on three real messages.

Input
JAVA_HOME … · Permission denied (publickey,password) · Output directory … already exists
Expected output
set JAVA_HOME in etc/hadoop/hadoop-env.sh
set up passwordless SSH to the host
hdfs dfs -rm -r <output> or choose a new folder
Show solution and explanation
fixes = [("JAVA_HOME is not set", "set JAVA_HOME in etc/hadoop/hadoop-env.sh"),
         ("Permission denied (publickey", "set up passwordless SSH to the host"),
         ("already exists", "hdfs dfs -rm -r <output> or choose a new folder"),
         ("safe mode", "hdfs dfsadmin -safemode wait"),
         ("MRAppMaster", "set HADOOP_MAPRED_HOME / mapreduce.application.classpath")]
def diagnose(msg):
    return next((fix for key, fix in fixes if key in msg), "read the daemon log in $HADOOP_HOME/logs")
for m in ["localhost: ERROR: JAVA_HOME is not set and could not be found.",
          "riya@localhost: Permission denied (publickey,password).",
          "Output directory hdfs://localhost:9000/user/riya/output already exists"]:
    print(diagnose(m))
set JAVA_HOME in etc/hadoop/hadoop-env.sh set up passwordless SSH to the host hdfs dfs -rm -r <output> or choose a new folder

How it works: Each message maps to its fix; anything unknown falls back to “read the log”. Support teams build runbooks on exactly this idea.

Real-world scenario questions (asked in company interviews)

Installation questions test whether you have really done it. Answer with the order of steps, the checks and the errors you met.

Install · troubleshootingFresher

“My first job never finishes”

A student's pseudo-distributed cluster shows all five daemons in jps, but the example job prints “Running job: job_…” and then nothing for ten minutes.

The interviewer is checking: do you know where YARN runs work, and how to check NodeManagers?

Show a strong answer
  • The job is waiting for a container. Run yarn node -list -all: if the NodeManager is missing or UNHEALTHY, nothing can run.
  • Read the NodeManager log: a common message is used space above threshold of 90.0% (disk nearly full).
  • Free disk space (or raise the threshold on a practice machine), restart YARN, and check that the node shows RUNNING.
  • Also check the ResourceManager UI at port 8088 for the application's state and diagnostics.
Install · stepsFresher

“Walk me through installing Hadoop on a single machine”

A common first-round interview question for data engineering roles.

The interviewer is checking: the order of steps and whether you verify each one.

Show a strong answer
  • Java (JDK 17 for 3.5) and SSH; download the tarball and verify the SHA-512.
  • Extract, set HADOOP_HOME/PATH, and JAVA_HOME in hadoop-env.sh.
  • Configure the four site files (default FS, replication 1 and storage folders, MapReduce on YARN, shuffle service).
  • Passwordless SSH, format the NameNode once, start HDFS and YARN, check jps, both web UIs and an example job.
Containers · dataExperienced

“All our test data disappeared overnight”

A team runs a Docker Compose Hadoop cluster for testing. After a laptop restart and docker compose down / up, HDFS is empty.

The interviewer is checking: do you understand container storage?

Show a strong answer
  • Without volumes, NameNode metadata and DataNode blocks live in the container's own file system, which down deletes.
  • Use docker compose stop/start for pauses, or mount volumes for the name and data directories.
  • For test data, keep a script that reloads it (put files, create tables), so a fresh cluster is ready in minutes.
Admin · cluster IDExperienced

DataNode won't start after someone re-formatted the NameNode

After hdfs namenode -format was run again on a test cluster, the DataNode exits at start-up. Its log mentions “Incompatible clusterIDs”.

The interviewer is checking: do you know what formatting changes?

Show a strong answer
  • Formatting created a new namespace with a new cluster ID; the DataNode's storage folder still has the old ID, so it refuses to join.
  • On a test cluster whose data can be discarded: stop the DataNode, empty its dfs.datanode.data.dir, and start it again.
  • On a real cluster: never re-format; restore the NameNode metadata from a backup instead. The old blocks are useless without the old namespace.