diff --git a/2026/day-01/mk-learning-plan.md b/2026/day-01/mk-learning-plan.md new file mode 100644 index 0000000000..9c84da44c6 --- /dev/null +++ b/2026/day-01/mk-learning-plan.md @@ -0,0 +1,106 @@ +## Day 01 – Introduction to DevOps and Cloud +### Task +The goal of Day 01 is to set a strong foundation for my DevOps journey by creating a clear and realistic 90-day execution plan. +This plan defines my understanding, motivation, goals, and consistency strategy for becoming a DevOps Engineer. +
+Current Level + +
+My Understanding of DevOps & Cloud +DevOps is a cultural and set of practices that focuses on collaboration between development and operations teams to deliverable a scalable, reliable and optimum sofware as product. +Mainly DevOps plays major role in sofware industry as follows: + +Goal: The main goal of DevOps engineer is to deliver applications faster, more reliably, with automation and collaboration. +Keys areas focuses on: + +
+Why I Am Learning DevOps & Cloud + +
+Where I Want to Reach in 90 Days +By the end of 90 days, I want to: + +Core DevOps Skills I Want to Build +1. Linux & networking trobleshooting +2. Containerization, CI/CD Pipelining, Monitoriing +3. Orchestarization, Kubernetes deployment and debugging + +⏱ Weekly Time Commitment +Weekdays- 2-3hrs per day +Weekends- 6-8 hrs per day + +Focus will be on: + +Daily task completion +Hands-on practice +Consistent GitHub commits +at least 3-posts per week +
+##90DaysOfDevOps #TrainwithShubham +HappyLearning +Manish Kumar Vishwakarma diff --git a/2026/day-02/Linux-system-command-prac.pdf b/2026/day-02/Linux-system-command-prac.pdf new file mode 100644 index 0000000000..d84cf2694f Binary files /dev/null and b/2026/day-02/Linux-system-command-prac.pdf differ diff --git a/2026/day-02/linux-architecture-notes.md b/2026/day-02/linux-architecture-notes.md new file mode 100644 index 0000000000..4a52afb342 --- /dev/null +++ b/2026/day-02/linux-architecture-notes.md @@ -0,0 +1,58 @@ +## Linux Internals – DevOps Foundation +This document covers the core Linux concepts required for troubleshooting and system management in production environments. +1. Core Components of Linux + Kernel +The Linux kernel is the main component of a Linux operating system (OS) and is the core interface between a computer’s hardware and its processes. It communicates between the 2, managing resources as efficiently as possible. +The kernel is so named because—like a seed inside a hard shell—it exists within the OS and controls all the major functions of the hardware, whether it’s a phone, laptop, server, or any other kind of computer. +What the kernel does +The kernel has 4 jobs: +1. Memory management: Keep track of how much memory is used to store what, and where +2. Process management: Determine which processes can use the central processing unit (CPU), when, and for how long +3. Device drivers: Act as mediator/interpreter between the hardware and processes +4. System calls and security: Receive requests for service from the processes + +| **State** | **Full Form** | **Meaning** | **Real Scenario Example** | +| --------- | ------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------ | +| **R** | Running | Process is actively executing on CPU or ready to run | `nginx` handling a web request | +| **S** | Sleeping (Interruptible Sleep) | Waiting for a resource (disk, network, input) | App waiting for DB response | +| **T** | Stopped | Process is paused/stopped manually (e.g., via signal) | Pressed `Ctrl+Z` in terminal | +| **Z** | Zombie | Process finished execution but parent hasn’t collected exit status | Child process ended, parent didn’t call `wait()` | + +Why Process States Matter +| **Issue** | **Related Process State(s)** | **What It Indicates** | **What You Check as DevOps Engineer** | | +| ---------------------------- | ------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------- | ------------------------- | +| **High CPU Usage** | `R` (Running) | Process is actively consuming CPU | `top`, `htop`, `ps -eo %cpu --sort=-%cpu` | | +| **Memory Leaks** | Usually `S` or `R` | Process memory keeps increasing without release | `top`, `ps aux --sort=-%mem`, `free -m`, check OOM logs | | +| **Stuck / Unresponsive App** | `S` (Sleeping), `D` (Uninterruptible Sleep) | Waiting for I/O, disk, network, or DB | `ps -ef`, `strace -p `, check disk/network latency | | +| **Zombie Process** | `Z` (Zombie) | Process finished but parent hasn’t cleaned it | `ps -el | grep Z`, check parent PID | +| **Orphan Process** | Any state (adopted by PID 1) | Parent died before child | `ps -ef`, verify PPID = 1 | | + + +3. systemd – Service Management + + | **Component** | **Description** | + | ---------------------------- | ------------------------------------------------------------------------------------------- | + | **systemd** | Modern init system used in most Linux distributions to manage system processes and services | + | **Starts services at boot** | Automatically initializes and starts required services during system startup | + | **Restarts failed services** | Can automatically restart services if they crash (based on configuration) | + | **Manages dependencies** | Ensures services start in the correct order based on dependency relationships | + +4. Essential Daily Commands + | **Command** | **Purpose** | **Common Usage Example** | + | ------------ | ------------------------------------ | ------------------------ | + | `ps` | View running processes | `ps -ef` | + | `top` | Monitor real-time CPU & memory usage | `top` | + | `systemctl` | Manage system services | `systemctl status nginx` | + | `journalctl` | View system and service logs | `journalctl -u nginx` | + | `kill` | Terminate processes | `kill -9 ` | + +📌 Conclusion +Linux is the base operating system for most production systems. +Understanding kernel behavior, process lifecycle, and systemd service management forms the foundation for effective DevOps troubleshooting and incident response. + +Note: use ps and top for process monitoring, systemctl for service management, journalctl for log analysis, and kill for process control. + +#90DaysOfDevOps #TrainWithShubham + +Happy Learning +Manish Kumar Vishwakarma diff --git a/2026/day-03/linux advanced.pdf b/2026/day-03/linux advanced.pdf new file mode 100644 index 0000000000..2da0a21398 Binary files /dev/null and b/2026/day-03/linux advanced.pdf differ diff --git a/2026/day-03/linux-commands-cheatsheet.md b/2026/day-03/linux-commands-cheatsheet.md new file mode 100644 index 0000000000..2c9074fac5 --- /dev/null +++ b/2026/day-03/linux-commands-cheatsheet.md @@ -0,0 +1,83 @@ +# 🐧 Linux Command Toolkit – Long-Term Cheat Sheet + +This is a practical command reference focused on: +- Process Management +- File System Operations +- Networking Troubleshooting + +These are foundational commands used in DevOps, Cloud, and Linux administration. + +--- + +# 🔹 Process Management + +| Command | Usage | +|----------|--------| +| `ps aux` | List all running processes with CPU and memory usage. | +| `ps -ef` | Show processes in full-format listing. | +| `top` | Real-time process and resource monitor. | +| `htop` | Interactive process viewer (enhanced top). | +| `pgrep ` | Find process ID by name. | +| `pidof ` | Get PID of a specific process. | +| `kill ` | Send termination signal to a process. | +| `kill -9 ` | Force kill a process immediately. | +| `pkill ` | Kill processes by name. | +| `nice -n 10 ` | Start a process with adjusted priority. | +| `renice -n 5 -p ` | Change priority of running process. | +| `jobs` | Show background jobs in current shell. | +| `bg` | Resume a job in background. | +| `fg` | Bring a background job to foreground. | +| `uptime` | Show system uptime and load average. | + +--- + +# 🔹 File System Management + +| Command | Usage | +|----------|--------| +| `pwd` | Show current working directory. | +| `ls -lah` | List files with permissions and sizes. | +| `cd ` | Change directory. | +| `mkdir ` | Create a directory. | +| `mkdir -p path/dir` | Create nested directories. | +| `touch file.txt` | Create an empty file. | +| `cp src dest` | Copy files or directories. | +| `mv src dest` | Move or rename files/directories. | +| `rm file` | Delete a file. | +| `rm -rf dir` | Remove directory recursively (use carefully). | +| `find /path -name "file"` | Search files by name. | +| `du -sh ` | Show disk usage of files/directories. | +| `df -h` | Show disk space usage. | +| `stat file` | Display detailed file metadata. | +| `chmod 755 file` | Change file permissions. | +| `chown user:group file` | Change file ownership. | +| `less file.log` | View large file with scrolling. | +| `tail -f file.log` | Monitor file in real time. | + +--- + +# 🔹 Networking Troubleshooting + +| Command | Usage | +|----------|--------| +| `ip addr` | Display IP addresses of network interfaces. | +| `ping google.com` | Test connectivity and latency to host. | +| `curl -I https://example.com` | Check HTTP response headers. | +| `dig example.com` | Query DNS records. | +| `ss -tulnp` | Show listening ports and services. | +| `netstat -tulnp` | Display open ports and connections. | + + +--- + +# 🔹 Log & Service Debugging Essentials + +| Command | Usage | +|----------|--------| +| `journalctl -u nginx` | View logs for specific systemd service. | +| `journalctl -f` | Follow system logs in real time. | +| `systemctl status nginx` | Check service status and recent logs. | + +--- + + diff --git a/2026/day-04/linux-practice.md b/2026/day-04/linux-practice.md new file mode 100644 index 0000000000..bae6808af0 --- /dev/null +++ b/2026/day-04/linux-practice.md @@ -0,0 +1,76 @@ +### Linux Practice: Processes and Services +1️⃣ Check Running Processes + + +image
+ +image
+top command: +image + +📊 System Health Checks (Quick Snapshot) +| Check Item | Command Used | What It Shows | What to Look For | +| -------------------- | -------------- | -------------------------- | ------------------------------------ | +| CPU Usage | `top` / `htop` | Real-time CPU consumption | High `%us` or `%sy` values | +| Memory Usage | `free -m` | RAM & swap usage | Low free memory, high swap usage | +| Load Average | `uptime` | System load (1, 5, 15 min) | Load > CPU cores = possible overload | +| Running Processes | `ps aux` | All running processes | High `%CPU` or `%MEM` values | +| Live Process Monitor | `top` | Dynamic process view | Identify abnormal resource usage | + +usage +🔎 Identifying High CPU Process (Inside top) +| Action | Key Press | Purpose | +| -------------- | ----------- | ----------------------------------------- | +| Sort by CPU | `Shift + P` | Shows highest CPU-consuming process first | +| Sort by Memory | `Shift + M` | Shows highest memory-consuming process | +| Kill a Process | `k` | Terminate selected process | +| Refresh Faster | `d` | Change refresh interval | + +2️⃣ Inspect One systemd Service + +Check Status of SSH Service +image +
+) +🔹 View Logs for That Service + +image +✅ Step 1: Check CPU & Load +top +uptime + +image +Step 2: Check Memory +free -h +image +Step 3: Check Disk +df -h +image +If disk > 90% full → possible issue. + +Service Practice Note – SSH Service (sshd) +1️⃣ Check if Service is Running +systemctl status ssh + +image +
+2️⃣ Check if SSH is Enabled at Boot +image +Step 2️⃣ Enable SSH to start at boot +image +
+Step 3️⃣ If SSH is not running, start it +sudo systemctl start ssh +Then verify: +systemctl status ssh +so use +systemctl enable sshd +systemctl start sshd +systemctl status sshd +image +
+Use hashtags: #90DaysOfDevOps #TrainWithShubham + +Happy Learning +Manish Kumar Vishwakarma + diff --git a/2026/day-05/linux-troubleshooting-runbook.md b/2026/day-05/linux-troubleshooting-runbook.md new file mode 100644 index 0000000000..2a69ded84d --- /dev/null +++ b/2026/day-05/linux-troubleshooting-runbook.md @@ -0,0 +1,57 @@ +## Day 05 – Linux Troubleshooting Drill: CPU, Memory, and Logs +Focused Troubleshooting Runbook + +1️⃣ Environment Basics +uname -a +image +Command 2: OS Version + +cat /etc/os-release +image +2️⃣ Filesystem Sanity +Command 3: Create Test Directory + +image +cp /etc/hosts /tmp/runbook-demo/hosts-copy +ls -l /tmp/runbook-demo + +image +3️⃣ CPU / Memory (2) +Command 5: Check Process Resource Usage +ps -eo pid,pcpu,pmem,comm --sort=-pcpu | head +image +Command 6: Memory Snapshot +free -h + +image +4️⃣ Disk / IO (2) +Command 7: Disk Usage +df -h + +image +Command 8: Log Directory Size +du -sh /var/log + +image +5️⃣ Network (2) +Command 9: Check Listening Ports +ss -tulpn | grep nginx + +image +Command 10: Test Service Endpoint + +image + +6️⃣ Logs (2) +Command 11: Service Logs +connect() failed (111: Connection refused) while connecting to upstream +Command 12: Application Log +OSError: [Errno 98] Address already in use +
+Use hashtags: +#90DaysOfDevOps #DevOpsKaJosh #TrainWithShubham + +Happy Learning +Manish Kumar Vishwakarma + + diff --git a/2026/day-06/file-io-practice.md b/2026/day-06/file-io-practice.md new file mode 100644 index 0000000000..c12936838c --- /dev/null +++ b/2026/day-06/file-io-practice.md @@ -0,0 +1,61 @@ +### Day 06 – Linux Fundamentals: Read and Write Text Files +Today’s goal is to practice basic file read/write using only fundamental commands. + +You will create a small text file and practice: + +Creating a file +Writing text to a file +Appending new lines +Reading the file back +Keep it basic and repeatable. +
+Step 1: Create a File +touch mynotes.txt + +



+image +



+Step 2: Write Text to the File (Overwrite Mode) +



+echo "This is my testing file." > mynotes.txt +echo "This is my testing file content." >> mynotes.txt +echo "This is another content for testing line 3" | tee -a mynotes.txt +



+image +



+image +Command 3: +head -n 2 mynotes.txt +



+image +tail -n 2 mynotes.txt +

+image +



+
+1. Logs = Text Files +COMMAND USED TO VERIFY LOGS +/var/log/syslog + +/var/log/auth.log + +/var/log/nginx/access.log +2. Configuration Files = Text Files +/etc/ssh/sshd_config + +/etc/nginx/nginx.conf + +/etc/fstab + +

+
+Use hashtags: +#90DaysOfDevOps #DevOpsKaJosh #TrainWithShubham + +Happy Learning +Manish Kumar Vishwakarma + + + + + diff --git a/2026/day-07/linux-fs-and-scenarios.md b/2026/day-07/linux-fs-and-scenarios.md new file mode 100644 index 0000000000..625fa86ef4 --- /dev/null +++ b/2026/day-07/linux-fs-and-scenarios.md @@ -0,0 +1,39 @@ +### Day 07 – Linux File System Hierarchy & Scenario-Based Practice + +Task +Today's goal is to understand where things live in Linux and practice troubleshooting like a DevOps engineer. + +You will create notes covering: + +Linux File System Hierarchy (the most important directories) +Practice solving real-world scenarios step by step + +
+Root Directory) + +The starting point of everything in Linux. + +All files and directories branch from /. + +Similar to C:\ in Windows (but Linux has one unified tree). + +CORE COMMANDS: +1. ls / +

+ +image +2. /home +

+ +image +3. cd /root +

+image +4. cd /etc +

+image +

+cd /var/log +image +

+ diff --git a/2026/day-08/cloud-deployment.md b/2026/day-08/cloud-deployment.md new file mode 100644 index 0000000000..8a7adf41d4 --- /dev/null +++ b/2026/day-08/cloud-deployment.md @@ -0,0 +1,41 @@ +## Day 08 – Cloud Server Setup: Docker, Nginx & Web Deployment +Task +Today's goal is to deploy a real web server on the cloud and learn practical server management. + +Launch a cloud instance (AWS EC2 or Utho) +Connect via SSH +Install Nginx +Configure security groups for web access (port 80 by default for nginx) +Extract and save logs to a file +Verify your webpage is accessible from the internet +This is real DevOps work - exactly what you'll do in production. +
+ +1. Objective + Deploy an Nginx web server on a cloud virtual machine and verify: +
    +
  • + SSH connectivity +
  • +
  • + Nginx installation +
  • +
  • + Web server accessibility +
  • +
  • + Log file verification +
  • +
+2. Step 1 – SSH into Server + Command Used: +ssh ubuntu@ec2-34-215-147-145.us-west-2.compute.amazonaws.com + image +

+3. Step 2 – Install Nginx +COMMAND: +sudo apt update +sudo apt install nginx -y + image +Check status +
diff --git a/2026/day-13/day-13-lvm.md b/2026/day-13/day-13-lvm.md new file mode 100644 index 0000000000..afef3e5f79 --- /dev/null +++ b/2026/day-13/day-13-lvm.md @@ -0,0 +1,99 @@ +## Day 13 – Linux Volume Management (LVM) +Task +Learn LVM to manage storage flexibly – create, extend, and mount volumes. + +Watch First: Linux LVM Tutorial + +Expected Output +A markdown file: day-13-lvm.md +Screenshots of command outputs + +
+ +### Commands Used +### Task 1: Check Current Storage +| Task | Command | Purpose | Expected Result | +| ----------------------------- | ------- | --------------------------------------------- | --------------------------------------------------- | +| **Check Block Devices** | `lsblk` | Lists all disks, partitions, and mount points | Shows devices like `/dev/nvme1n1`, `/dev/sda`, etc. | +| **Check Physical Volumes** | `pvs` | Displays existing LVM physical volumes | Shows PV name, VG name, size | +| **Check Volume Groups** | `vgs` | Displays existing volume groups | Shows VG name, size, free space | +| **Check Logical Volumes** | `lvs` | Displays logical volumes inside VGs | Shows LV name, VG name, size | +| **Check Mounted Filesystems** | `df -h` | Shows mounted storage usage | Displays filesystem size, used space | + + + +### Task 2: Create Physical Volume +| Command | Purpose | Output | +| ----------------------- | --------------------------------------- | ---------------------------- | +| `pvcreate /dev/nvme1n1` | Initializes disk as LVM Physical Volume | PV created successfully | +| `pvs` | Verify PV creation | Shows `/dev/nvme1n1` in list | + +### Task 3: Create Volume Group +| Command | Purpose | Output | +| --------------------------------- | ----------------------------- | --------------------------- | +| `vgcreate devops-vg /dev/nvme1n1` | Creates Volume Group using PV | VG created | +| `vgs` | Verify VG | Shows `devops-vg` with size | + + +### Task 4: Create Logical Volume +| Command | Purpose | Output | +| ---------------------------------------- | ---------------------------- | ------------------- | +| `lvcreate -L 500M -n app-data devops-vg` | Creates 500MB Logical Volume | LV created | +| `lvs` | Verify LV | Shows `app-data` LV | + +### Task 5: Format and Mount Logical Volume +| Command | Purpose | Output | +| --------------------------------------------- | ------------------------------- | -------------------- | +| `mkfs.ext4 /dev/devops-vg/app-data` | Formats LV with ext4 filesystem | Filesystem created | +| `mkdir -p /mnt/app-data` | Creates mount directory | Directory created | +| `mount /dev/devops-vg/app-data /mnt/app-data` | Mounts logical volume | Storage mounted | +| `df -h /mnt/app-data` | Verify mount and size | Shows ~500MB mounted | + + +### Command Used for Check Usuage +#### 1. lsblk (List of ESB block storage) +| NAME | MAJ:MIN | RM | SIZE | RO | TYPE | MOUNTPOINT | +| ---------------------- | ------- | -- | ---- | -- | ---- | ------------- | +| nvme0n1 | 259:0 | 0 | 20G | 0 | disk | | +| ├─nvme0n1p1 | 259:1 | 0 | 1G | 0 | part | /boot | +| └─nvme0n1p2 | 259:2 | 0 | 19G | 0 | part | / | +| nvme1n1 | 259:3 | 0 | 10G | 0 | disk | | +| └─devops--vg-app--data | 253:0 | 0 | 500M | 0 | lvm | /mnt/app-data | + +image +
+ +image + + +#### 2. pvc (Physical Volumes) +| PV | VG | Fmt | Attr | PSize | PFree | +| ------------ | --------- | ---- | ---- | ------ | ----- | +| /dev/nvme1n1 | devops-vg | lvm2 | a-- | 10.00g | 9.50g | + +#### 3. vgs (Volume Groups) +| VG | #PV | #LV | #SN | Attr | VSize | VFree | +| --------- | --- | --- | --- | ------ | ------ | ----- | +| devops-vg | 1 | 1 | 0 | wz--n- | 10.00g | 9.50g | +#### 4. lvs (Logical Volumes) +| LV | VG | Attr | LSize | Pool | Origin | Data% | Meta% | +| -------- | --------- | ---------- | ------- | ---- | ------ | ----- | ----- | +| app-data | devops-vg | -wi-a----- | 500.00m | | | | | + +#### 5. df -h (Disk Usage) +| Filesystem | Size | Used | Avail | Use% | Mounted on | +| -------------------------------- | ---- | ---- | ----- | ---- | ------------- | +| /dev/nvme0n1p2 | 19G | 3.2G | 15G | 18% | / | +| /dev/nvme0n1p1 | 1G | 150M | 850M | 15% | /boot | +| /dev/mapper/devops--vg-app--data | 496M | 24K | 462M | 1% | /mnt/app-data | +#### 6. Format and Mount Logical Volume +| Filesystem | Size | Used | Avail | Use% | Mounted on | +| -------------------------------- | ---- | ---- | ----- | ---- | ------------- | +| /dev/mapper/devops--vg-app--data | 496M | 24K | 462M | 1% | /mnt/app-data | + +#### 7. Extend Logical Volume +| Filesystem | Size | Used | Avail | Use% | Mounted on | +| -------------------------------- | ---- | ---- | ----- | ---- | ------------- | +| /dev/mapper/devops--vg-app--data | 696M | 30K | 640M | 1% | /mnt/app-data | + + diff --git a/2026/day-16/day-16-shell-scripting.md b/2026/day-16/day-16-shell-scripting.md new file mode 100644 index 0000000000..48894d2c15 --- /dev/null +++ b/2026/day-16/day-16-shell-scripting.md @@ -0,0 +1,126 @@ +# Shell Scripting Basics +
+ +### Task 1: First Script +1. Create a file hello.sh +2. Add the shebang line #!/bin/bash at the top +3. Print Hello, DevOps! using echo +4. Make it executable and run it +
+Scripts +What happens if you remove the shebang line? +./hello.sh + +The kernel checks for a shebang to determine which interpreter should run the script. + +If no shebang is found, the system executes the script using the current shell (usually the shell you are logged into). + +bash hello.sh + +The script is explicitly executed by the Bash shell. + +It does not depend on the shebang line. + +sh hello.sh + +The script is executed using the sh shell. + +Behavior may differ from Bash because sh may not support all Bash-specific features. + +image +
+Task 2: Variables +1. Create variables.sh with: +
    +
  • A variable for your NAME
  • +
  • A variable for your ROLE (e.g., "DevOps Engineer")
  • +
  • Print: Hello, I am and I am a
  • + +2. Try using single quotes vs double quotes — what's the difference? +
      +
    • Using double quote " " - Allow variable expansion +
    • +
    • Using single quote ' ' - Treat every character exactly as written +
    • +
    +
    + +image +
    +Task 3: User Input with read +
      + +Create greet.sh that: +
    • Asks the user for their name using read
    • +
    • Asks for their favourite tool
    • +
    • Prints: Hello , your favourite tool is +
    • +
    + +### Scripts: + +image +
    +### Task 4: If-Else Conditions +1. Create check_number.sh that: +
      +
    • Takes a number using read +
    • +
    • Prints whether it is positive, negative, or zero +
    • + +
    +

    +### Script: + +image +
    + +2. Create file_check.sh that: +
      + +Asks for a filename + +
    • Checks if the file exists using -f
    • +
    • Prints appropriate message
    • +
    +### Script: +image +
    +Task 5: Combine It All +Create server_check.sh that: +
      +
    • Stores a service name in a variable (e.g., nginx, sshd) +
    • +
    • Asks the user: "Do you want to check the status? (y/n)" +
    • +
    • If y — runs systemctl status and prints whether it's active or not +
    • +
    • If n — prints "Skipped." +
    • +
    +
    + image +

    + OUTPUT: + + image + +
    + +| **Topic** | **What I Learned** | **Example / Command** | +| ------------------------------ | --------------------------------------------------------------------------------------------------------- | ---------------------------------------- | +| **Shebang & Script Execution** | Learned how to write and execute Bash scripts using the shebang `#!/bin/bash` to specify the interpreter. | `#!/bin/bash` and run with `./script.sh` | +| **Variables in Bash** | Learned how to assign variables and access them using `$`. | `name="Manish"` → `echo $name` | +| **Single vs Double Quotes** | Double quotes allow variable expansion, while single quotes treat text literally. | `"Hello $name"` vs `'Hello $name'` | +| **User Input** | Learned how to take input from users using the `read` command. | `read username` | +| **Conditional Statements** | Used `if`, `elif`, and `else` to control script flow based on conditions. | `if [ $num -gt 0 ]; then` | +| **Numeric Comparisons** | Learned to check numbers using operators like `-gt`, `-lt`, and `-eq`. | `[ $num -gt 0 ]` | +| **File Existence Check** | | | + + + + + + diff --git a/2026/day-17/ scripts/countdown.sh b/2026/day-17/ scripts/countdown.sh new file mode 100644 index 0000000000..5e417a031c --- /dev/null +++ b/2026/day-17/ scripts/countdown.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Ask the user for a number +echo "Enter a number to start countdown:" +read num + +# Countdown using while loop +while [ $num -ge 0 ] +do + echo $num + num=$((num-1)) +done + +# Final message +echo "Done!" diff --git a/2026/day-17/ scripts/for_loop.sh b/2026/day-17/ scripts/for_loop.sh new file mode 100644 index 0000000000..be83a97262 --- /dev/null +++ b/2026/day-17/ scripts/for_loop.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# List of fruits +fruits=("Apple" "Banana" "Mango" "Orange" "Grapes") + +# Loop through the list +for fruit in "${fruits[@]}" +do + echo "$fruit" +done diff --git a/2026/day-17/ scripts/greet.sh b/2026/day-17/ scripts/greet.sh new file mode 100644 index 0000000000..1b56832dbf --- /dev/null +++ b/2026/day-17/ scripts/greet.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Check if argument is provided +if [ -z "$1" ]; then + echo "Usage: ./greet.sh " +else + echo "Hello, $1!" +fi diff --git a/2026/day-17/day-17-scripting.md b/2026/day-17/day-17-scripting.md new file mode 100644 index 0000000000..45e0ce59ab --- /dev/null +++ b/2026/day-17/day-17-scripting.md @@ -0,0 +1,137 @@ +# Day 17 – Shell Scripting: Loops, Arguments & Error Handling +## Task 1: For Loop +1. Create `for_loop.sh` that: + - Loops through a list of 5 fruits and prints each one +Script: + Screenshot 2026-03-08 005057 +Output: + +Screenshot 2026-03-08 005141 + + +2. Create `count.sh` that: + - Prints numbers 1 to 10 using a for loop + + - [Script] + + image + + - Output: + Screenshot 2026-03-08 005425 + +--- + +## Task 2: While Loop +1. Create `countdown.sh` that: + - Takes a number from the user + - Counts down to 0 using a while loop + - Prints "Done!" at the end + + - [Script] + Screenshot 2026-03-08 005908 + + -[Countdown] + Screenshot 2026-03-08 010045 + +--- + +## Task 3: Command-Line Arguments +1. Create `greet.sh` that: + - Accepts a name as `$1` + - Prints `Hello, !` + - If no argument is passed, prints "Usage: ./greet.sh " + + - [Script] + + image + + - OUTPUT + Screenshot 2026-03-08 010915 + +3. Create `args_demo.sh` that: + - Prints total number of arguments (`$#`) + - Prints all arguments (`$@`) + - Prints the script name (`$0`) + + - [Script] + image + + - OUTPUT + image + +--- + +## Task 4: Install Packages via Script +1. Create `install_packages.sh` that: + - Defines a list of packages: `nginx`, `curl`, `wget` + - Loops through the list + - Checks if each package is installed (use `dpkg -s` or `rpm -q`) + - Installs it if missing, skips if already present + - Prints status for each package + + - [Script] + image + + - OUTPUT: + + image + +--- + +## Task 5: Error Handling +1. Create `safe_script.sh` that: + - Uses `set -e` at the top (exit on error) + - Tries to create a directory `/tmp/devops-test` + - Tries to navigate into it + - Creates a file inside + - Uses `||` operator to print an error if any step fails + + - [Script] + image + + - OUTPUT + + image + + +2. Modify your `install_packages.sh` to check if the script is being run as root — exit with a message if not. + + - [Script] + image + + - OUTPUT + + +| **Command (Terminal Prompt)** | **Output in Terminal** | +| ------------------------------ | ----------------------------------------------- | +| `$ ./install_packages.sh` | `Error: Please run this script as root.` | +| `$ sudo ./install_packages.sh` | `Running as root. Starting package check...` | +| | `Checking nginx...` | +| | `Status - nginx is already installed.` | +| | `-----------------------------` | +| | `Checking curl...` | +| | `Status - curl is not installed. Installing...` | +| | `Status - curl installed successfully.` | +| | `-----------------------------` | +| | `Checking wget...` | +| | `Status - wget is already installed.` | +| | `-----------------------------` | + + + + ------------------------ +--- + + +## What I Learned + +* Used for loops to iterate over lists and number ranges +* Used while loops for countdown logic with user input +* Handled command-line arguments using $1, $#, $@, $0 +* Added usage messages for missing arguments +* Took user input using read +* Automated package installation (nginx, curl, wget) +* Checked package status using dpkg -s +* Added root user validation using $EUID +* Implemented error handling with set -e and || +* Created safe scripts to avoid failures and overwrites diff --git a/2026/day-18/day-18-scripting.md b/2026/day-18/day-18-scripting.md new file mode 100644 index 0000000000..ffb541d6eb --- /dev/null +++ b/2026/day-18/day-18-scripting.md @@ -0,0 +1,85 @@ +# Day 18 – Shell Scripting: Functions & Slightly Advanced Concepts + +## Task 1: Basic Functions +1. Create `functions.sh` with: + - A function `greet` that takes a name as argument and prints `Hello, !` + - A function `add` that takes two numbers and prints their sum + - Call both functions from the script + + [Script](scripts/functions.sh) + + ![task1](images/task1.png) + +--- + +## Task 2: Functions with Return Values +1. Create `disk_check.sh` with: + - A function `check_disk` that checks disk usage of `/` using `df -h` + - A function `check_memory` that checks free memory using `free -h` + - A main section that calls both and prints the results + + [Script](scripts/disk_check.sh) + + ![task2](images/task2.png) + +--- + +## Task 3: Strict Mode — `set -euo pipefail` +1. Create `strict_demo.sh` with `set -euo pipefail` at the top +2. Try using an **undefined variable** — what happens with `set -u`? +3. Try a command that **fails** — what happens with `set -e`? +4. Try a **piped command** where one part fails — what happens with `set -o pipefail`? + +**Document:** What does each flag do? +- `set -e` → Exit the script immediately if any command fails. +- `set -u` → Exit the script if an undefined (unset) variable is used. +- `set -o pipefail` → Pipeline fails if any command fails. + + [Script](scripts/strict_demo.sh) + + ![task3](images/task3withstrictmode.png) + +--- + +## Task 4: Local Variables +1. Create `local_demo.sh` with: + - A function that uses `local` keyword for variables + - Show that `local` variables don't leak outside the function + - Compare with a function that uses regular variables + + [Script](scripts/local_demo.sh) + + ![task4](images/task4.png) + +--- + +## Task 5: Build a Script — System Info Reporter +Create `system_info.sh` that uses functions for everything: +1. A function to print **hostname and OS info** +2. A function to print **uptime** +3. A function to print **disk usage** (top 5 by size) +4. A function to print **memory usage** +5. A function to print **top 5 CPU-consuming processes** +6. A `main` function that calls all of the above with section headers +7. Use `set -euo pipefail` at the top + + [Script](scripts/system_info.sh) + + ![task5](images/task5.png) + +--- + +## What I Learned + +**Functions & Modularity** – Learned to create reusable, organized code blocks.This makes scripts cleaner, easier to read, and simpler to maintain. + +**System Monitoring Scripts** – Explored fetching system info like memory,disk usage,and CPU processes.Useful for building quick automation for system health checks. + +**Error Handling & Safety** – Using `set -euo pipefail` to catch undefined variables,failing commands,and pipeline errors early,making scripts more reliable. + +**Variable Scope** – Understood the difference between local and global variables. Local variables stay inside functions, while global variables affect the wider script. + +**Practical Automation** – Using a main function to orchestrate tasks helps make scripts modular,maintainable,and automation-friendly. + +**Function Naming Pitfall** – Faced an issue where naming a function the same as a system command (uptime) caused an infinite loop. +Learned to avoid using system command names for functions. diff --git a/2026/day-18/images/task1.png b/2026/day-18/images/task1.png new file mode 100644 index 0000000000..280ad678fa Binary files /dev/null and b/2026/day-18/images/task1.png differ diff --git a/2026/day-18/images/task1.tf b/2026/day-18/images/task1.tf new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-18/images/task1.tf @@ -0,0 +1 @@ + diff --git a/2026/day-18/images/task2.png b/2026/day-18/images/task2.png new file mode 100644 index 0000000000..843a0791cd Binary files /dev/null and b/2026/day-18/images/task2.png differ diff --git a/2026/day-18/images/task3withstrictmode.png b/2026/day-18/images/task3withstrictmode.png new file mode 100644 index 0000000000..507f2dbab1 Binary files /dev/null and b/2026/day-18/images/task3withstrictmode.png differ diff --git a/2026/day-18/scripts/a.tt b/2026/day-18/scripts/a.tt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-18/scripts/a.tt @@ -0,0 +1 @@ + diff --git a/2026/day-18/scripts/disk_check.sh b/2026/day-18/scripts/disk_check.sh new file mode 100644 index 0000000000..60c1622e13 --- /dev/null +++ b/2026/day-18/scripts/disk_check.sh @@ -0,0 +1,21 @@ +#!/bin/bash +<< readme +This script checks disk usage of / and free memory +Usage: +./disk_check.sh +readme + +function check_disk { + echo "=== Disk Usage (/) ===" + df -h / +} + +function check_memory { + echo "=== Memory Usage ===" + free -h +} + +# Main section +check_disk +echo "" +check_memory \ No newline at end of file diff --git a/2026/day-18/scripts/functions.sh b/2026/day-18/scripts/functions.sh new file mode 100644 index 0000000000..1744b67a69 --- /dev/null +++ b/2026/day-18/scripts/functions.sh @@ -0,0 +1,16 @@ +#!/bin/bash +function greet { + name=$1 + echo "Hello ,$name" +} +function add { + num1=$1 + num2=$2 + sum=$(( num1 + num2 )) + echo "The sum of $num1 and $num2 is: $sum" +} + +# Call both functions +greet "Manish" +add 2 4 + diff --git a/2026/day-18/scripts/strict_demo.sh b/2026/day-18/scripts/strict_demo.sh new file mode 100644 index 0000000000..c1874bab6c --- /dev/null +++ b/2026/day-18/scripts/strict_demo.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -euo pipefail + +echo "Script started" + +echo "1) Testing undefined variable (set -u)" +echo "Value: $UNDEFINED_VAR" + +echo "2) Testing failing command (set -e)" +ls /directory-that-does-not-exist + +echo "3) Testing pipe failure (set -o pipefail)" +echo "hello" | grep "world" + +echo "Script completed" + + + +# Scenario 1 — Test set -u (undefined variable) +# Comment these: +# echo "2) Testing failing command (set -e)" +# ls /directory-that-does-not-exist" + +# echo "3) Testing pipe failure (set -o pipefail)" +# echo "hello" | grep "world" + +# echo "Script completed" + + +# Scenario 2 — Test set -e (failing command) +# Comment these: +# echo "1) Testing undefined variable (set -u)" +# echo "Value: $UNDEFINED_VAR" +# echo "3) Testing pipe failure (set -o pipefail)" +# echo "hello" | grep "world" +# echo "Script completed" + +# Scenario 3 — Test pipefail +# Now comment everything except pipe test. +# echo "3) Testing pipe failure (set -o pipefail)" +# echo "hello" | grep "world" \ No newline at end of file diff --git a/2026/day-22/day-22-notes.md b/2026/day-22/day-22-notes.md new file mode 100644 index 0000000000..9fc759081e --- /dev/null +++ b/2026/day-22/day-22-notes.md @@ -0,0 +1,141 @@ +# Day 22 – Introduction to Git: Your First Repository + +## Challenge Tasks + +### Task 1: Install and Configure Git +1. Verify Git is installed on your machine +2. Set up your Git identity — name and email +3. Verify your configuration + +![git](https://github.com/manishvishwakarma89/90DaysOfDevOps-tws/blob/master/2026/day-22/images/git_config.png) + +### Task 2: Create Your Git Project +1. Create a new folder called `devops-git-practice` +2. Initialize it as a Git repository +3. Check the status — read and understand what Git is telling you +4. Explore the hidden `.git/` directory — look at what's inside + +![git](https://github.com/manishvishwakarma89/90DaysOfDevOps-tws/blob/master/2026/day-22/images/git21.png) + +--- + +### Task 3: Create Your Git Commands Reference +1. Create a file called `git-commands.md` inside the repo +2. Add the Git commands you've used so far, organized by category: + - **Setup & Config** + - **Basic Workflow** + - **Viewing Changes** +3. For each command, write: + - What it does (1 line) + - An example of how to use it + + +## Setup & Config + + ### git init + - Initializes a new Git repository. + - **Example**: + ```bash + git init + ``` + ### git config + - Configures Git username or email. + - **Example**: + ```bash + git config --global user.name "Your Name" + git config --global user.email "Your Email" + ``` + - View Config Values + - **Example**: + ```bash + git config --global --list + ``` + +## Basic Workflow + + ### git add + - Stages files for commit. + - **Example**: + ```bash + git add git-commands.md + ``` + + ### git commit + - Save staged changes with a message explaining what you changed. + - **Example**: + ```bash + git commit -m "Add git commands reference" + ``` + + ## Viewing Changes + + ### git status + - Lists which files are modified and not yet stage + - **Example**: + ```bash + git status + ``` + + ### git log + - Shows the history of commits in your repository who changed what,when,and why. + - Its also shows Commit hash,Author name & email,Date,Commit message + - **Example**: + ```bash + git log + ``` + +### Task 4: Stage and Commit +1. Stage your file +2. Check what's staged +3. Commit with a meaningful message +4. View your commit history + +```bash + git add git-command.md + git status + + On branch master + No commits yet + Changes to be committed: + (use "git rm --cached ..." to unstage) + new file: git-command.md + + git commmit -m "Initial git command reference" + +``` + + +--- + +### Task 5: Make More Changes and Build History +1. Edit `git-commands.md` — add more commands as you discover them +2. Check what changed since your last commit +3. Stage and commit again with a different, descriptive message +4. Repeat this process at least **3 times** so you have multiple commits in your history +5. View the full history in a compact format + +![git](https://github.com/manishvishwakarma89/90DaysOfDevOps-tws/blob/master/2026/day-22/images/git22.png) +--- + +### Task 6: Understand the Git Workflow +Answer these questions in your own words (add them to a `day-22-notes.md` file): +1. What is the difference between `git add` and `git commit`? +- `git add` tells Git which changes you want to include in the next commit. It moves changes to the staging area. +- `git commit` saves staged changes with a message explaining what you changed. + +2. What does the **staging area** do? Why doesn't Git just commit directly? +- The staging area is like a waiting room for changes. You choose what to include in your next commit. +- Git doesn't commit directly so you can decide exactly which changes to save and organize your commits better. + +3. What information does `git log` show you? +- `git log` shows a history of commits in your repository. +- It includes the commit ID, author, date, and commit message for each change. + +4. What is the `.git/` folder and what happens if you delete it? +- The `.git/` folder stores all Git information for your project: commits, branches, tags, and configuration. +- If you delete it, Git will no longer track your project, and you will lose all version history + +5. What is the difference between a **working directory**, **staging area**, and **repository**? +- **Working Directory:** Where you make and see changes to your files. +- **Staging Area:** Where you put changes you want to commit. +- **Repository:** Where Git stores all committed changes permanently. diff --git a/2026/day-22/images/a b/2026/day-22/images/a new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-22/images/a @@ -0,0 +1 @@ + diff --git a/2026/day-22/images/git21.png b/2026/day-22/images/git21.png new file mode 100644 index 0000000000..13fef50c37 Binary files /dev/null and b/2026/day-22/images/git21.png differ diff --git a/2026/day-22/images/git22.png b/2026/day-22/images/git22.png new file mode 100644 index 0000000000..3eb3a74113 Binary files /dev/null and b/2026/day-22/images/git22.png differ diff --git a/2026/day-22/images/git_config.png b/2026/day-22/images/git_config.png new file mode 100644 index 0000000000..5634d03d4e Binary files /dev/null and b/2026/day-22/images/git_config.png differ diff --git a/2026/day-23/day-23-notes.md b/2026/day-23/day-23-notes.md new file mode 100644 index 0000000000..a69d0b0b40 --- /dev/null +++ b/2026/day-23/day-23-notes.md @@ -0,0 +1,150 @@ +# Day 23 – Git Branching & Working with GitHub + +### Task 1: Understanding Branches +1. What is a branch in Git? +- A branch is a separate line of development in a project. +- Think of it as a copy of the code where you can work on new features. + +2. Why do we use branches instead of committing everything to `main`? +- Branches let us work on new features or fixes safely without breaking `main` branch, which always holds the stable,production-ready code. + +3. What is `HEAD` in Git? +- `HEAD` is a reference to your current location in the repository.I +- It always points to the latest commit on the branch you are working on. + +4. What happens to your files when you switch branches? +- Git updates your project files to look like the branch you switched to. +- Files that exist in the current branch but not in the new branch will disappear temporarily. +- Files that are different in the new branch will be replaced with the new branch version. + +--- + +### Task 2: Branching Commands — Hands-On + +1. List all branches in your repo +- `git branch` + + ![gitb](images/git_branch.png) + +2. Create a new branch called `feature-1` +- `git branch feature-1` + + ![gitbn](images/feature1.png) + +3. Switch to `feature-1` +- `git switch feature-1` + + ![gitsb](images/ft2_ft1.png) + +4. Create a new branch and switch to it in a single command — call it `feature-2` +- `git checkout -b feature-2` + + ![gitcb](images/ft2.png) + + +5. Try using `git switch` to move between branches — how is it different from `git checkout`? +- `git switch ` :only switches branches. +- `git checkout ` :switches branches and can also restore files. + + ![images](images/checkout_ft1.png) + +6. Make a commit on `feature-1` that does **not** exist on `main` +- `git commit -m "Add git branch command section to git-commands.md"` + + ![images](images/6.png) + +7. Switch back to `main` — verify that the commit from `feature-1` is not there + + ![images](images/7.png) + +8. Delete a branch you no longer need +- `git branch -d feature-2` + + ![images](images/8.png) + +9. Add all branching commands to your `git-commands.md` + +--- + +### Task 3: Push to GitHub +1. Create a **new repository** on GitHub (do NOT initialize it with a README) + + ![images](images/repo_create.png) + +2. Connect your local `devops-git-practice` repo to the GitHub remote +3. Push your `main` branch to GitHub + + ![images](images/pushmain.png) + +4. Push `feature-1` branch to GitHub + + ![images](images/pushft1.png) + + +5. Verify both branches are visible on GitHub + + ![images](images/branchverify.png) + +6. What is the difference between `origin` and `upstream`? +- `origin`: origin is the default name for the repo you cloned,points to your own GitHub repository where you push and pull changes. +`example`: https://github.com/srdangat/devops-git-practice.git +- `upstream`: upstream refers to the original repository you forked from.You use it to pull updates from the original project into your fork. +`example`: https://github.com/srdangat/90DaysOfDevOps + +--- + +### Task 4: Pull from GitHub + +1. Make a change to a file **directly on GitHub** (use the GitHub editor) + + ![images](images/changesgithub.png) + +2. Pull that change to your local repo + + ![images](images/pullft1.png) + +3. What is the difference between `git fetch` and `git pull`? +- `git fetch`: Downloads changes from remote only; does not change your branch,just updates remote info. +- `git pull` : Downloads changes from remote and merges them into your current branch, updating your local branch immediately. + + +### Task 5: Clone vs Fork +1. **Clone** any public repository from GitHub to your local machine + + ![clone](images/clone.png) + + +2. **Fork** the same repository on GitHub, then clone your fork + + ![fork](images/fork.png) + +3. 1. What is the difference between clone and fork? + + - `clone` : Download the project from GitHub to my computer. + - `fork` : Make my own copy of someone else’s project on GitHub. + + 2. When would you clone vs fork? + + - `clone when`: + - You are working on your own project. + - You already have write access. + - You just want the code locally. + - Example: Working in your company repo where you’re a team member. + - `fork when` + - You don’t have write access. + - You want to contribute to open source. + - You want your own safe copy. + - Example: Contributing to aws-containers repository retail-store-sample-app + + 3. After forking, how do you keep your fork in sync with the original repo? + + - After forking and cloning my fork, I add the original repository as an upstream remote.Then I fetch changes from upstream, merge the upstream default branch into my current branch,and push the updates to my fork. + - Example: + ```bash + git remote add upstream git@github.com:aws-containers/retail-store-sample-app.git + git checkout main + git fetch upstream + git merge upstream/main + git push origin main + ``` +--- diff --git a/2026/day-23/images/6.png b/2026/day-23/images/6.png new file mode 100644 index 0000000000..d657b1fd2c Binary files /dev/null and b/2026/day-23/images/6.png differ diff --git a/2026/day-23/images/7.png b/2026/day-23/images/7.png new file mode 100644 index 0000000000..036adef64b Binary files /dev/null and b/2026/day-23/images/7.png differ diff --git a/2026/day-23/images/8.png b/2026/day-23/images/8.png new file mode 100644 index 0000000000..45eca20e5d Binary files /dev/null and b/2026/day-23/images/8.png differ diff --git a/2026/day-23/images/branchverify.png b/2026/day-23/images/branchverify.png new file mode 100644 index 0000000000..557ea037c4 Binary files /dev/null and b/2026/day-23/images/branchverify.png differ diff --git a/2026/day-23/images/changesgithub.png b/2026/day-23/images/changesgithub.png new file mode 100644 index 0000000000..2bf1c4b341 Binary files /dev/null and b/2026/day-23/images/changesgithub.png differ diff --git a/2026/day-23/images/checkout_ft1.png b/2026/day-23/images/checkout_ft1.png new file mode 100644 index 0000000000..5a343ff10a Binary files /dev/null and b/2026/day-23/images/checkout_ft1.png differ diff --git a/2026/day-23/images/clone.png b/2026/day-23/images/clone.png new file mode 100644 index 0000000000..f91d43e3b1 Binary files /dev/null and b/2026/day-23/images/clone.png differ diff --git a/2026/day-23/images/feature1.png b/2026/day-23/images/feature1.png new file mode 100644 index 0000000000..3ac7f2e951 Binary files /dev/null and b/2026/day-23/images/feature1.png differ diff --git a/2026/day-23/images/fork.png b/2026/day-23/images/fork.png new file mode 100644 index 0000000000..966369eced Binary files /dev/null and b/2026/day-23/images/fork.png differ diff --git a/2026/day-23/images/ft2.png b/2026/day-23/images/ft2.png new file mode 100644 index 0000000000..c917dd518c Binary files /dev/null and b/2026/day-23/images/ft2.png differ diff --git a/2026/day-23/images/ft2_ft1.png b/2026/day-23/images/ft2_ft1.png new file mode 100644 index 0000000000..b2d4f4bafc Binary files /dev/null and b/2026/day-23/images/ft2_ft1.png differ diff --git a/2026/day-23/images/git_branch.png b/2026/day-23/images/git_branch.png new file mode 100644 index 0000000000..5af393aadb Binary files /dev/null and b/2026/day-23/images/git_branch.png differ diff --git a/2026/day-23/images/pullft1.png b/2026/day-23/images/pullft1.png new file mode 100644 index 0000000000..3b87e48cca Binary files /dev/null and b/2026/day-23/images/pullft1.png differ diff --git a/2026/day-23/images/pushmain.png b/2026/day-23/images/pushmain.png new file mode 100644 index 0000000000..db11ea00a3 Binary files /dev/null and b/2026/day-23/images/pushmain.png differ diff --git a/2026/day-23/images/repo_create.png b/2026/day-23/images/repo_create.png new file mode 100644 index 0000000000..f448117c8e Binary files /dev/null and b/2026/day-23/images/repo_create.png differ diff --git a/2026/day-23/images/sd b/2026/day-23/images/sd new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-23/images/sd @@ -0,0 +1 @@ + diff --git a/2026/day-29/docker-basics.md b/2026/day-29/docker-basics.md new file mode 100644 index 0000000000..2a9fc2104e --- /dev/null +++ b/2026/day-29/docker-basics.md @@ -0,0 +1,146 @@ +Task 1: What is Docker? +Docker is an open source containerization platform that allows you to package any application along with dependencies (libraries, environment, configs, runtime) into a container so it runs consistently across environments (dev, test, production). +Screenshot 2026-04-23 163856### + +Simple words: build app; run anywhere +A container is a lightweight, standalone unit that includes: +- Application code +- Runtime (e.g., Python, Node) +- Libraries & dependencies + It runs on top of the host OS but stays isolated from other containers. +Why do we need containers? +Before containers: + +“It works on my machine” problem +Dependency conflicts +Difficult deployments + +- We need for consistency, fast deployment, lightweight, easly scaling + +Q. Containers vs Virtual Machines — what's the real difference? +COntainers vs Virtual can be differntiate for the following features are: + +Virtual Containers- Works on OS-Level doesn't use hardware whereas Virtual machines works on Hardware and software. +Size: Containers size is in MB whereas VM size is in GB +Time: Container starts time is fast in seconds whereas VM starting time is in Minutes. +Resources: Resource usuage take less whereas VM takes much time. +Application Uses: COntainer used for micro-services app but VMs used as a legacy application with running different OS (Linux, Window OS etc) + +Q. What is the Docker architecture? (daemon, client, images, containers, registry) + Docker uses a client–server architecture to build, manage, and run containers. +ChatGPT Image Apr 23, 2026, 04_11_01 PM + + Docker Core Components are: + 1. Docker Client + The tool you interact with (CLI/API) + Example commands: + - docker build + - docker pull + - docker run + Sends requests to the Docker Daemon + 2. Docker Daemon (dockerd) + - The main engine running in the background + It is Responsible for building images, running containers, managing volumes & netowks + 3. Docker Images: + Its blueprint tempalte of container + contains application-code, runtime, install dependencies & libraries + 4. Docker Containers + Docker container has follwing lifecycle + - start + - stop + - Delete + Docker container is running instance of images, and its lightweight and isolated. + 5. Docker Registry + It contains running container both on public and private network + - storing images for locally or docker hub as public repo + 1. Developer runs a command (docker run nginx) + 2. Docker Client sends request to Docker Daemon + 3. Daemon checks local system for image + 4. If not found → pulls image from Registry + 5. Daemon creates and starts Container + 6. Application runs inside container +
    +### Task 2: Install Docker +1. Install Docker on your machine (or use a cloud instance) +I install Ubuntu server +command for installing docker: +sudo apt update -y +sudo apt install -y docker.io +- Start and enable Docker: +sudo systemctl start docker +sudo systemctl enable docker +Run Docker without sudo: +sudo usermod -aG docker $USER docker +Log out and log back in after this +Screenshot 2026-04-23 163054 +Screenshot 2026-04-23 163348 + +2. Step 2: Verify Installation +docker --version +Expected output: +Screenshot 2026-04-23 163506 + +docker info +Screenshot 2026-04-23 163856 + +Step 3: Run Hello World Container +docker run hell-world +image + +Step 4: Understand the Output +What actually happens behind the scenes: +1. Client → Daemon + - Your command goes from Docker Client → Docker Daemon +2. Check for Image Locally + Docker looks for hello-world image on your system + Not found (first time) +3. Pull from Registry + - Docker pulls the image from Docker Hub +4. Create Container + Docker creates a container from the image +5. Run the Container + - The container runs and prints a message + +### Task 3: Run Real Containers +1. Run an Nginx Container + docker run -d -p 8080:80 --name my-nginx nginx + image +2. Run an Ubuntu container in interactive mode — explore it like a mini Linux machine + Screenshot 2026-04-23 165347 +3. List all running containers + Screenshot 2026-04-23 165609 +4. List all containers (including stopped ones) + Screenshot 2026-04-23 165725 +5. Stop and remove a container +Screenshot 2026-04-23 165924 +Screenshot 2026-04-23 170034 +
    +### Task 4: Explore +1. Run a container in detached mode — what's different? +What’s different? +-d = detached mode +Runs in background (no terminal attached) +You get container ID instead of shell +2. Give a container a custom name +docker run -d --name my-nginx nginx +3. Map a port from the container to your host +docker run -d -p 8080:80 --name web nginx +4. Check logs of a running container +docker logs web +Screenshot 2026-04-23 170639 + +5. Run a command inside a running container +docker exec -it web bash +Screenshot 2026-04-23 170720 +
    +Key Concepts (Important) +-d → background execution +--name → easy management +-p → expose container to outside world +logs → debugging +exec → access running container +“In Docker, I can run containers in detached mode using -d, assign custom names using --name, map ports using -p, check logs using docker logs, and execute commands inside a running container using docker exec -it +
    +#90DaysOfDevOps #DevOpsKaJosh #TrainWithShubham + +Happy Learning! TrainWithShubham diff --git a/2026/day-30/day-30-images.md b/2026/day-30/day-30-images.md new file mode 100644 index 0000000000..b194a4f2c2 --- /dev/null +++ b/2026/day-30/day-30-images.md @@ -0,0 +1,5 @@ +### Day 30 – Docker Images & Container Lifecycle +
    +Challenge Tasks + +### Task 1: Docker Images diff --git a/2026/day-31/day-31-dockerfile.md b/2026/day-31/day-31-dockerfile.md new file mode 100644 index 0000000000..ec2f73cf91 --- /dev/null +++ b/2026/day-31/day-31-dockerfile.md @@ -0,0 +1,153 @@ +### Task 1: Your First Dockerfile +1. Create a folder called `my-first-image` +2. Inside it, create a `Dockerfile` that: + - Uses `ubuntu` as the base image + - Installs `curl` + - Sets a default command to print `"Hello from my custom image!"` +3. Build the image and tag it `my-ubuntu:v1` +task-1 + + +4. Run a container from your image + + t2 + +**Verify:** The message prints on `docker run` + +--- + +### Task 2: Dockerfile Instructions + +- `FROM` `python:3.12-alpine` +Uses lightweight Python image based on Alpine Linux. + +- `WORKDIR` `/app` +Sets /app as working directory inside container. + +- `COPY . .` +Copies everything from your my-first-image folder into /app inside container. + +- `RUN` `pip install -r requirements.txt` +Installs all Python dependencies. + +- `EXPOSE 5000` +Documents that container uses port 5000. +t3 + +- `CMD ["python","app.py"]` +Runs Python app when container starts. + +t2 2 + +--- +### Task 3: CMD vs ENTRYPOINT +1. Create an image with `CMD ["echo", "hello"]` — run it, then run it with a custom command. What happens? + + ![image](images/t3.1.png) + +* **Run without arguments:** + The container runs the default command `echo hello` and outputs: + + ``` + hello + ``` + +* **Run with a custom command:** + When you run the container with a custom command (e.g., `echo "custom command"`), the custom command **completely overrides** the `CMD`, so the output is: + + ``` + custom command + ``` +3. When would you use CMD vs ENTRYPOINT? + +- Use `CMD` when you want to provide a default command that can be changed easily when you run the container. + +- Use `ENTRYPOINT` when you want to set a fixed command that always runs. + +--- +### Task 4: Build a Simple Web App Image +1. Create a small static HTML file (`index.html`) with any content +2. Write a Dockerfile that: + - Uses `nginx:alpine` as base + - Copies your `index.html` to the Nginx web directory +3. Build and tag it `my-website:v1` +4. Run it with port mapping and access it in your browser + + t4 1 + t4 3 + + +--- + +### Task 5: .dockerignore +1. Create a `.dockerignore` file in one of your project folders +2. Add entries for: `node_modules`, `.git`, `*.md`, `.env` +3. Build the image — verify that ignored files are not included + + t5 + +--- + +### Task 6: Build Optimization +1. Build an image, then change one line and rebuild — notice how Docker uses **cache** + +```bash +FROM python:3.11-slim +WORKDIR /app +COPY . . +RUN pip install -r requirements.txt +CMD ["python","app.py"] +``` +Observation: The image is built successfully and all layers are created. + +Change one line and rebuild: change in app.py + +```bash +FROM python:3.11-slim +WORKDIR /app +COPY . . +RUN pip install -r requirements.txt +CMD ["python","app.py"] +``` + +Observation: +Even though only the application code changed +Docker re-ran pip install -r requirements.txt +Any change in source code invalidated the cache for all following layers. + + + +2. Reorder your Dockerfile so that frequently changing lines come **last** + +```bash +FROM python:3.11-slim +WORKDIR /app +COPY requirements.txt . +RUN pip install -r requirements.txt +COPY . . +CMD ["python","app.py"] +``` + +final + + +Observation: +Docker reused cached layers for: Base image,Working directory,Dependency installation + +3. Why does layer order matter for build speed? + +- Docker builds images in layers and caches each layer. +- If a layer changes,Docker rebuilds that layer and all layers after it. +- By placing: + - Rarely changing files (dependencies) first + - Frequently changing files (source code) last +- Docker can reuse cached layers,resulting in faster rebuilds. + +--- + + + +There should be no test.md, .env, .git, or node_modules listed +--- + + diff --git a/2026/day-31/images/final.png b/2026/day-31/images/final.png new file mode 100644 index 0000000000..a92202b22d Binary files /dev/null and b/2026/day-31/images/final.png differ diff --git a/2026/day-31/images/t1 b/2026/day-31/images/t1 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-31/images/t1 @@ -0,0 +1 @@ + diff --git a/2026/day-31/images/t2.2.png b/2026/day-31/images/t2.2.png new file mode 100644 index 0000000000..57085d16d0 Binary files /dev/null and b/2026/day-31/images/t2.2.png differ diff --git a/2026/day-31/images/t3.1.png b/2026/day-31/images/t3.1.png new file mode 100644 index 0000000000..7aaac481fe Binary files /dev/null and b/2026/day-31/images/t3.1.png differ diff --git a/2026/day-31/images/t3.png b/2026/day-31/images/t3.png new file mode 100644 index 0000000000..e0c9125f33 Binary files /dev/null and b/2026/day-31/images/t3.png differ diff --git a/2026/day-31/images/t4.1.png b/2026/day-31/images/t4.1.png new file mode 100644 index 0000000000..397e15fe50 Binary files /dev/null and b/2026/day-31/images/t4.1.png differ diff --git a/2026/day-31/images/t4.2.png b/2026/day-31/images/t4.2.png new file mode 100644 index 0000000000..e9f9a26152 Binary files /dev/null and b/2026/day-31/images/t4.2.png differ diff --git a/2026/day-31/images/t4.3.png b/2026/day-31/images/t4.3.png new file mode 100644 index 0000000000..c7187067d4 Binary files /dev/null and b/2026/day-31/images/t4.3.png differ diff --git a/2026/day-32/day-32-volumes-networking.md b/2026/day-32/day-32-volumes-networking.md new file mode 100644 index 0000000000..6ec045410f --- /dev/null +++ b/2026/day-32/day-32-volumes-networking.md @@ -0,0 +1,142 @@ +# Day 32 – Docker Volumes & Networking + +## Challenge Tasks + +### Task 1: The Problem +1. Run a Postgres or MySQL container + + ![image](images/task1.1.png) + +2. Create some data inside it (a table, a few rows — anything) + + ![image](images/task1.2.png) + +3. Stop and remove the container + + ![image](images/task1.3.png) + +4. Run a new one — is your data still there? + + ![image](images/task1.4.png) + + - No, Data is lost when a container is removed because containers are ephemeral and do not persist data by default. + +--- + +### Task 2: Named Volumes +1. Create a named volume + + ![image](images/task2.1.jpg) + +2. Run the same database container, but this time **attach the volume** to it + + task2 2 + + +3. Add some data, stop and remove the container + +task2 3 + + +4. Run a brand new container with the **same volume** + + ![image](images/task2.4.jpg) + +5. Is the data still there? + - Yes,all previous data ,tables and rows are still there. + + Verify: `docker volume ls`, `docker volume inspect` + + ![image](images/final.jpg) + +--- +--- + +### Task 3: Bind Mounts +1. Create a folder on your host machine with an `index.html` file + + ![image](images/task3.1.png) + +2. Run an Nginx container and **bind mount** your folder to the Nginx web directory +3. Access the page in your browser + + ![image](images/task3.2.png) + +4. Edit the `index.html` on your host — refresh the browser + + ![image](images/task3.3.png) + ![image](images/task3.3.1.png) + ![image](images/task3.3.2.png) +**Volumes vs Bind Mounts** + +**Volumes:** +- Managed by Docker. +- Stored in a part of the host filesystem which is managed by Docker. +- Preferred method for data persistence. + +**Bind Mounts:** +- Maps a file or directory on the host to a file or directory in the container. +- More complex but provides flexibility to interact with the host system. + +--- + +### Task 4: Docker Networking Basics +1. List all Docker networks on your machine + + ![image](images/task4.1.png) + +2. Inspect the default `bridge` network + + ![image](images/task4.2.png) + +- `docker network inspect` is the command used to retrieve detailed configuration and status information about a specific Docker network. +- The `bridge network` is indeed the default network in Docker. + +3. Run two containers on the default bridge — can they ping each other by **name**? + +- No + + ![image](images/task4.3.png) + + +4. Run two containers on the default bridge — can they ping each other by **IP**? + +- Yes + + ![image](images/task4.4.png) + +--- + +### Task 5: Custom Networks +1. Create a custom bridge network called `my-app-net` + + ![image](images/task523.png) + +2. Run two containers on `my-app-net` +3. Can they ping each other by **name** now? + +- `yes they can ping each other by name` + + ![image](images/task6234.png) + + +4. Why does custom networking allow name-based communication but the default bridge doesn't? + +- Default Docker `bridge network` `does not have built-in DNS`,so containers cannot resolve each other by name.they need IPs. +- `User-defined networks` have `embedded DNS`, so containers can communicate using their names. + +--- + +### Task 6: Put It Together +1. Create a custom network + + ![image](images/task6.1.png) + +2. Run a **database container** (MySQL/Postgres) on that network with a volume for data +3. Run an **app container** (use any image) on the same network +4. Verify the app container can reach the database by container name + + ![image](images/task6234.png) + + +--- diff --git a/2026/day-32/images/final.jpg b/2026/day-32/images/final.jpg new file mode 100644 index 0000000000..262d165ab6 Binary files /dev/null and b/2026/day-32/images/final.jpg differ diff --git a/2026/day-32/images/s b/2026/day-32/images/s new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-32/images/s @@ -0,0 +1 @@ + diff --git a/2026/day-32/images/task 1.2.png b/2026/day-32/images/task 1.2.png new file mode 100644 index 0000000000..c05ff1d9a4 Binary files /dev/null and b/2026/day-32/images/task 1.2.png differ diff --git a/2026/day-32/images/task 1.3.png b/2026/day-32/images/task 1.3.png new file mode 100644 index 0000000000..ce0b587f39 Binary files /dev/null and b/2026/day-32/images/task 1.3.png differ diff --git a/2026/day-32/images/task 5.2.png b/2026/day-32/images/task 5.2.png new file mode 100644 index 0000000000..9234646e49 Binary files /dev/null and b/2026/day-32/images/task 5.2.png differ diff --git a/2026/day-32/images/task1.1.png b/2026/day-32/images/task1.1.png new file mode 100644 index 0000000000..69ed6be165 Binary files /dev/null and b/2026/day-32/images/task1.1.png differ diff --git a/2026/day-32/images/task1.2.png b/2026/day-32/images/task1.2.png new file mode 100644 index 0000000000..c05ff1d9a4 Binary files /dev/null and b/2026/day-32/images/task1.2.png differ diff --git a/2026/day-32/images/task1.3.png b/2026/day-32/images/task1.3.png new file mode 100644 index 0000000000..ce0b587f39 Binary files /dev/null and b/2026/day-32/images/task1.3.png differ diff --git a/2026/day-32/images/task1.4.png b/2026/day-32/images/task1.4.png new file mode 100644 index 0000000000..7681257cd0 Binary files /dev/null and b/2026/day-32/images/task1.4.png differ diff --git a/2026/day-32/images/task1.png b/2026/day-32/images/task1.png new file mode 100644 index 0000000000..69ed6be165 Binary files /dev/null and b/2026/day-32/images/task1.png differ diff --git a/2026/day-32/images/task2.1.jpg b/2026/day-32/images/task2.1.jpg new file mode 100644 index 0000000000..6f28c5ea69 Binary files /dev/null and b/2026/day-32/images/task2.1.jpg differ diff --git a/2026/day-32/images/task2.2.png b/2026/day-32/images/task2.2.png new file mode 100644 index 0000000000..ed3ae00d87 Binary files /dev/null and b/2026/day-32/images/task2.2.png differ diff --git a/2026/day-32/images/task2.4.jpg b/2026/day-32/images/task2.4.jpg new file mode 100644 index 0000000000..700581b16a Binary files /dev/null and b/2026/day-32/images/task2.4.jpg differ diff --git a/2026/day-32/images/task3.1.png b/2026/day-32/images/task3.1.png new file mode 100644 index 0000000000..6d2f5d0b9b Binary files /dev/null and b/2026/day-32/images/task3.1.png differ diff --git a/2026/day-32/images/task3.2.png b/2026/day-32/images/task3.2.png new file mode 100644 index 0000000000..e562eeecd7 Binary files /dev/null and b/2026/day-32/images/task3.2.png differ diff --git a/2026/day-32/images/task3.3.1.png b/2026/day-32/images/task3.3.1.png new file mode 100644 index 0000000000..8880f5af6b Binary files /dev/null and b/2026/day-32/images/task3.3.1.png differ diff --git a/2026/day-32/images/task3.3.2.png b/2026/day-32/images/task3.3.2.png new file mode 100644 index 0000000000..fd07d98e9b Binary files /dev/null and b/2026/day-32/images/task3.3.2.png differ diff --git a/2026/day-32/images/task3.3.png b/2026/day-32/images/task3.3.png new file mode 100644 index 0000000000..dbaf19afbe Binary files /dev/null and b/2026/day-32/images/task3.3.png differ diff --git a/2026/day-32/images/task4.1.png b/2026/day-32/images/task4.1.png new file mode 100644 index 0000000000..1b6973513b Binary files /dev/null and b/2026/day-32/images/task4.1.png differ diff --git a/2026/day-32/images/task4.2.png b/2026/day-32/images/task4.2.png new file mode 100644 index 0000000000..7de9b99698 Binary files /dev/null and b/2026/day-32/images/task4.2.png differ diff --git a/2026/day-32/images/task4.3.png b/2026/day-32/images/task4.3.png new file mode 100644 index 0000000000..4101f6d05d Binary files /dev/null and b/2026/day-32/images/task4.3.png differ diff --git a/2026/day-32/images/task4.4.png b/2026/day-32/images/task4.4.png new file mode 100644 index 0000000000..f8a917f972 Binary files /dev/null and b/2026/day-32/images/task4.4.png differ diff --git a/2026/day-32/images/task5.1.png b/2026/day-32/images/task5.1.png new file mode 100644 index 0000000000..5e45b3d574 Binary files /dev/null and b/2026/day-32/images/task5.1.png differ diff --git a/2026/day-32/images/task523.png.png b/2026/day-32/images/task523.png.png new file mode 100644 index 0000000000..58a4b93435 Binary files /dev/null and b/2026/day-32/images/task523.png.png differ diff --git a/2026/day-32/images/task6234.png b/2026/day-32/images/task6234.png new file mode 100644 index 0000000000..8e29b30227 Binary files /dev/null and b/2026/day-32/images/task6234.png differ diff --git a/2026/day-32/images/test2.3.jpg b/2026/day-32/images/test2.3.jpg new file mode 100644 index 0000000000..097e0c1fae Binary files /dev/null and b/2026/day-32/images/test2.3.jpg differ diff --git a/2026/day-32/images/test2.4.jpg b/2026/day-32/images/test2.4.jpg new file mode 100644 index 0000000000..700581b16a Binary files /dev/null and b/2026/day-32/images/test2.4.jpg differ diff --git a/2026/day-33/day-33-compose.md b/2026/day-33/day-33-compose.md new file mode 100644 index 0000000000..c319654dbb --- /dev/null +++ b/2026/day-33/day-33-compose.md @@ -0,0 +1,115 @@ +# Day 33 – Docker Compose: Multi-Container Basics + +## Challenge Tasks + +### Task 1: Install & Verify +1. Check if Docker Compose is available on your machine +2. Verify the version + + ![image](images/compose-version.png) + +--- + +### Task 2: Your First Compose File +1. Create a folder `compose-basics` +2. Write a `docker-compose.yml` that runs a single **Nginx** container with port mapping +3. Start it with `docker compose up` +4. Access it in your browser +5. Stop it with `docker compose down` + + [Dockerfile](compose-basics/Dockerfile) + + [Compose file](compose-basics/docker-compose.yml) + + ![image](images/task2.png) + + ![image](images/task2.5.png) + +--- + +### Task 3: Two-Container Setup +Write a `docker-compose.yml` that runs: +- A **WordPress** container +- A **MySQL** container + +They should: +- Be on the same network (Compose does this automatically) +- MySQL should have a named volume for data persistence +- WordPress should connect to MySQL using the service name + +Start it, access WordPress in your browser, and set it up. + +![image](images/wrd1.png) + + +![image](images/wrd2install.png) + + +![image](images/wrdbefore.png) + + +**Verify:** Stop and restart with `docker compose down` and `docker compose up` — is your WordPress data still there? + +- Yes,wordpress data is there. + + +![image](images/down.png) + + +![image](images/wrdafter.png) + + + +[Compose file](wordpress-mysql/docker-compose.yml) + + +--- + +### Task 4: Compose Commands +Practice and document these: +1. Start services in **detached mode** + + `docker compose up -d` + ![image](images/task4.1.png) + +2. View running services + + `docker compose ps` + ![image](images/task4.2.png) + +3. View **logs** of all services + + `docker compose logs db` && `docker compose logs wordpress` + ![image](images/task4.3.png) + +4. View logs of a **specific** service + + ![image](images/task4.4.png) + +5. **Stop** services without removing + + `docker compose stop` + ![image](images/task4.5.png) + +6. **Remove** everything (containers, networks) + + `docker compose down` + ![image](images/task4.6.png) + +7. **Rebuild** images if you make a change + + `docker compose up --build.` + +--- + +### Task 5: Environment Variables +1. Add environment variables directly in your `docker-compose.yml` +2. Create a `.env` file and reference variables from it in your compose file +3. Verify the variables are being picked up + + ![image](images/task5.png) + + [Compose file](worpress-mysql/docker-compose.yml) + + [Env](worpress-mysql-env/.env) + diff --git a/2026/day-33/images/compose-version.png b/2026/day-33/images/compose-version.png new file mode 100644 index 0000000000..5b2f53921e Binary files /dev/null and b/2026/day-33/images/compose-version.png differ diff --git a/2026/day-33/images/down.png b/2026/day-33/images/down.png new file mode 100644 index 0000000000..db7d20dcc3 Binary files /dev/null and b/2026/day-33/images/down.png differ diff --git a/2026/day-33/images/s b/2026/day-33/images/s new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-33/images/s @@ -0,0 +1 @@ + diff --git a/2026/day-33/images/task2.5.png b/2026/day-33/images/task2.5.png new file mode 100644 index 0000000000..a53ffa38e9 Binary files /dev/null and b/2026/day-33/images/task2.5.png differ diff --git a/2026/day-33/images/task2.png b/2026/day-33/images/task2.png new file mode 100644 index 0000000000..7b5265019e Binary files /dev/null and b/2026/day-33/images/task2.png differ diff --git a/2026/day-33/images/task4.1.png b/2026/day-33/images/task4.1.png new file mode 100644 index 0000000000..0d3448414e Binary files /dev/null and b/2026/day-33/images/task4.1.png differ diff --git a/2026/day-33/images/task4.2.png b/2026/day-33/images/task4.2.png new file mode 100644 index 0000000000..4ded84f168 Binary files /dev/null and b/2026/day-33/images/task4.2.png differ diff --git a/2026/day-33/images/task4.3.png b/2026/day-33/images/task4.3.png new file mode 100644 index 0000000000..b8e10cd30e Binary files /dev/null and b/2026/day-33/images/task4.3.png differ diff --git a/2026/day-33/images/task4.4.png b/2026/day-33/images/task4.4.png new file mode 100644 index 0000000000..53797a3f7e Binary files /dev/null and b/2026/day-33/images/task4.4.png differ diff --git a/2026/day-33/images/task4.5.png b/2026/day-33/images/task4.5.png new file mode 100644 index 0000000000..1fc5cf6f67 Binary files /dev/null and b/2026/day-33/images/task4.5.png differ diff --git a/2026/day-33/images/task4.6.png b/2026/day-33/images/task4.6.png new file mode 100644 index 0000000000..7fcc01a599 Binary files /dev/null and b/2026/day-33/images/task4.6.png differ diff --git a/2026/day-33/images/task5.png b/2026/day-33/images/task5.png new file mode 100644 index 0000000000..76d4944be1 Binary files /dev/null and b/2026/day-33/images/task5.png differ diff --git a/2026/day-33/images/task_env.png b/2026/day-33/images/task_env.png new file mode 100644 index 0000000000..c76ad30fa7 Binary files /dev/null and b/2026/day-33/images/task_env.png differ diff --git a/2026/day-33/images/wrd1.png b/2026/day-33/images/wrd1.png new file mode 100644 index 0000000000..bae42c9ae2 Binary files /dev/null and b/2026/day-33/images/wrd1.png differ diff --git a/2026/day-33/images/wrd2install.png b/2026/day-33/images/wrd2install.png new file mode 100644 index 0000000000..05ed5d331c Binary files /dev/null and b/2026/day-33/images/wrd2install.png differ diff --git a/2026/day-33/images/wrdafter.png b/2026/day-33/images/wrdafter.png new file mode 100644 index 0000000000..9bcbb96794 Binary files /dev/null and b/2026/day-33/images/wrdafter.png differ diff --git a/2026/day-33/images/wrdbefore.png b/2026/day-33/images/wrdbefore.png new file mode 100644 index 0000000000..94e838b8c4 Binary files /dev/null and b/2026/day-33/images/wrdbefore.png differ diff --git a/2026/day-33/wordpress-mysql-env/.env b/2026/day-33/wordpress-mysql-env/.env new file mode 100644 index 0000000000..9abf21c26e --- /dev/null +++ b/2026/day-33/wordpress-mysql-env/.env @@ -0,0 +1,7 @@ +MYSQL_ROOT_PASSWORD=root +MYSQL_DATABASE=wordpress +MYSQL_USER=admin +MYSQL_PASSWORD=admin123 +WORDPRESS_DB_USER=admin +WORDPRESS_DB_PASSWORD=admin123 +WORDPRESS_DB_NAME=wordpress diff --git a/2026/day-33/wordpress-mysql/docker-compose.yml b/2026/day-33/wordpress-mysql/docker-compose.yml new file mode 100644 index 0000000000..e1786b407a --- /dev/null +++ b/2026/day-33/wordpress-mysql/docker-compose.yml @@ -0,0 +1,48 @@ +version: "3.8" + +services: + mysql: + image: mysql:8.0 + container_name: mysql-db + restart: unless-stopped + + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: wordpress + MYSQL_USER: admin + MYSQL_PASSWORD: admin@123 + + volumes: + - mysql_data:/var/lib/mysql + + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 30s + + wordpress: + image: wordpress:latest + container_name: wordpress-app + restart: unless-stopped + + depends_on: + mysql: + condition: service_healthy + + ports: + - "8080:80" + + environment: + WORDPRESS_DB_HOST: mysql:3306 + WORDPRESS_DB_USER: admin + WORDPRESS_DB_PASSWORD: admin@123 + WORDPRESS_DB_NAME: wordpress + + volumes: + - wp_data:/var/www/html + +volumes: + mysql_data: + wp_data: diff --git a/2026/day-34/day-34-compose-advanced.md b/2026/day-34/day-34-compose-advanced.md new file mode 100644 index 0000000000..457a919045 --- /dev/null +++ b/2026/day-34/day-34-compose-advanced.md @@ -0,0 +1,116 @@ +# Day 34 – Docker Compose: Real-World Multi-Container Apps + +## Challenge Tasks + +### Task 1: Build Your Own App Stack +Create a `docker-compose.yml` for a 3-service stack: +- A **web app** (use Python Flask, Node.js, or any language you know) +- A **database** (Postgres or MySQL) +- A **cache** (Redis) + + [Code](web_db_cache/) + + + +--- + +### Task 2: depends_on & Healthchecks +1. Add `depends_on` to your compose file so the app starts **after** the database +2. Add a **healthcheck** on the database service +3. Use `depends_on` with `condition: service_healthy` so the app waits for the database to be truly ready, not just started + + + **Test:** Bring everything down and up — does the app wait for the DB? + + - Yes + + ![image](images/task2down.jpg) + + ![image](images/task2log.jpg) + +- Postgres container starts first. +- Healthcheck waits until DB is ready. +- App container starts only after DB is healthy. + +--- + +### Task 3: Restart Policies +1. Add `restart: always` to your database service +2. Manually kill the database container — does it come back? + - yes its back + + ![image](images/task3.1.jpg) + + + +3. Try `restart: on-failure` — how is it different? + - no restart + + ![image](images/task3.3.jpg) + + +4. When would you use each restart policy? + + - `restart:always` `Use When:` + Databases, + Backend APIs, + Production services, + Anything that must always run + + - `restart:on-failure` `Use When`: + Data processing jobs + One-time migration scripts +--- + +### Task 4: Custom Dockerfiles in Compose +1. Instead of using a pre-built image for your app, use `build:` in your compose file to build from a Dockerfile +2. Make a code change in your app +3. Rebuild and restart with one command + + [Dockerfile](web_db_cache/app/Dockerfile) + + + ![image](images/before.jpg) + + + + ![image](images/aftercodechange.jpg) + + + + [Compose](web_db_cache/docker-compose.yml) + +--- + +### Task 5: Named Networks & Volumes +1. Define **explicit networks** in your compose file instead of relying on the default +2. Define **named volumes** for database data +3. Add **labels** to your services for better organization + + [Compose](web_db_cache/docker-compose.yml) + + +--- + +### Task 6: Scaling +1. Try scaling your web app to 3 replicas using `docker compose up --scale` +2. What happens? What breaks? +3. Why doesn't simple scaling work with port mapping? + + + ![image](images/task5.1.jpg) + + + ![image](images/task5.2.jpg) + + - First container started + + - It binds host port 3000 = container port 3000. + + - Second and third containers failed + + - Status Created means Docker couldn’t start them,port 3000 is already in use on the host. + + - Docker can’t bind multiple containers to the same host port. + +--- diff --git a/2026/day-34/images/aftercodechange.png b/2026/day-34/images/aftercodechange.png new file mode 100644 index 0000000000..18dd56b7bd Binary files /dev/null and b/2026/day-34/images/aftercodechange.png differ diff --git a/2026/day-34/images/before.jpg b/2026/day-34/images/before.jpg new file mode 100644 index 0000000000..7a382d4669 Binary files /dev/null and b/2026/day-34/images/before.jpg differ diff --git a/2026/day-34/images/s b/2026/day-34/images/s new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-34/images/s @@ -0,0 +1 @@ + diff --git a/2026/day-34/images/task2.3.jpg b/2026/day-34/images/task2.3.jpg new file mode 100644 index 0000000000..565afb8444 Binary files /dev/null and b/2026/day-34/images/task2.3.jpg differ diff --git a/2026/day-34/images/task2down.jpg b/2026/day-34/images/task2down.jpg new file mode 100644 index 0000000000..565afb8444 Binary files /dev/null and b/2026/day-34/images/task2down.jpg differ diff --git a/2026/day-34/images/task2log.jpg b/2026/day-34/images/task2log.jpg new file mode 100644 index 0000000000..7577ed4cd2 Binary files /dev/null and b/2026/day-34/images/task2log.jpg differ diff --git a/2026/day-34/images/task3.1.jpg b/2026/day-34/images/task3.1.jpg new file mode 100644 index 0000000000..4da70a7fad Binary files /dev/null and b/2026/day-34/images/task3.1.jpg differ diff --git a/2026/day-34/images/task3.3.jpg b/2026/day-34/images/task3.3.jpg new file mode 100644 index 0000000000..3225e854c9 Binary files /dev/null and b/2026/day-34/images/task3.3.jpg differ diff --git a/2026/day-34/images/task5.1.jpg b/2026/day-34/images/task5.1.jpg new file mode 100644 index 0000000000..061173d2ba Binary files /dev/null and b/2026/day-34/images/task5.1.jpg differ diff --git a/2026/day-34/web_db_cache/app/Dockerfile b/2026/day-34/web_db_cache/app/Dockerfile new file mode 100644 index 0000000000..c5e4c88f6d --- /dev/null +++ b/2026/day-34/web_db_cache/app/Dockerfile @@ -0,0 +1,20 @@ +# Node.js lightweight image +FROM node:20-alpine + +# Set working directory +WORKDIR /app + +# Copy package.json +COPY package.json ./ + +# Install dependencies +RUN npm install + +# Copy rest of the app files +COPY . . + +# Expose app port +EXPOSE 3030 + +# Start the app +CMD ["node", "index.js"] \ No newline at end of file diff --git a/2026/day-34/web_db_cache/app/index.js b/2026/day-34/web_db_cache/app/index.js new file mode 100644 index 0000000000..8b7b49dc42 --- /dev/null +++ b/2026/day-34/web_db_cache/app/index.js @@ -0,0 +1,247 @@ +const express = require("express"); +const { Client } = require("pg"); +const { createClient } = require("redis"); +const os = require("os"); + +const app = express(); +const port = 3000; + +const DATABASE_URL = process.env.DATABASE_URL; +const REDIS_HOST = process.env.REDIS_HOST; + +// Initialize DB table +async function initDb() { + const client = new Client({ connectionString: DATABASE_URL }); + await client.connect(); + + await client.query(` + CREATE TABLE IF NOT EXISTS visits ( + id SERIAL PRIMARY KEY, + count INTEGER NOT NULL + ); + `); + + await client.end(); +} + +app.get("/", async (req, res) => { + const db = new Client({ connectionString: DATABASE_URL }); + const redis = createClient({ url: `redis://${REDIS_HOST}:6379` }); + + try { + await db.connect(); + if (!redis.isOpen) await redis.connect(); + + const result = await db.query("SELECT count FROM visits WHERE id=1"); + + let count; + if (result.rows.length > 0) { + count = result.rows[0].count + 1; + await db.query("UPDATE visits SET count=$1 WHERE id=1", [count]); + } else { + count = 1; + await db.query("INSERT INTO visits (id, count) VALUES (1, $1)", [count]); + } + + await redis.set("last_visit", count); + const cached = await redis.get("last_visit"); + + // Test Redis connection status + const redisStatus = redis.isOpen ? "green" : "red"; + + await db.end(); + await redis.quit(); + + const hostname = os.hostname(); // Container hostname + + res.send(` + + + +Docker 3-Tier Demo + + + + + + + +
    + +
    + +
    +

    🚀 Docker 3-Tier Demo

    + +
    0
    +
    Page Visits
    + +
    + PostgreSQL Connected + Redis Active +
    + +
    + Cached Value: ${cached} +
    + +
    + + + + +
    + +
    + Container: ${hostname} +
    + + +
    + + + + + + `); + + } catch(err){ + res.status(500).send("Error: "+err.message); + } +}); + +app.listen(port,"0.0.0.0",async()=>{ + console.log("Starting app..."); + await initDb(); + console.log(`Server running on port ${port}`); +}); \ No newline at end of file diff --git a/2026/day-34/web_db_cache/app/package.json b/2026/day-34/web_db_cache/app/package.json new file mode 100644 index 0000000000..6890f6fb83 --- /dev/null +++ b/2026/day-34/web_db_cache/app/package.json @@ -0,0 +1,17 @@ +{ + "name": "3tier-demo", + "version": "1.0.0", + "description": "Docker 3-tier demo redis status,and hostname display", + "main": "index.js", + "engines": { + "node": ">=18" + }, + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "express": "^4.18.2", + "pg": "^8.11.3", + "redis": "^4.6.7" + } +} \ No newline at end of file diff --git a/2026/day-34/web_db_cache/docker-compose.yml b/2026/day-34/web_db_cache/docker-compose.yml new file mode 100644 index 0000000000..5d6c362d00 --- /dev/null +++ b/2026/day-34/web_db_cache/docker-compose.yml @@ -0,0 +1,59 @@ +services: + db: + image: postgres:15 + restart: always + # restart: on-failure + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} + ports: + - "5432:5432" + volumes: + - db_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - 3-tier + labels: + tier: "database" + + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + networks: + - 3-tier + labels: + tier: "cache" + + app: + build: + context: ./app + dockerfile: Dockerfile + ports: + - "${APP_PORT}:3000" + networks: + - 3-tier + environment: + #postgres://:@:/ + DATABASE_URL: "postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}" + REDIS_HOST: redis + depends_on: + db: + condition: service_healthy + labels: + tier: "web" + + +volumes: + db_data: + +networks: + 3-tier: + driver: bridge \ No newline at end of file diff --git a/2026/day-35-multistage-hub.md b/2026/day-35-multistage-hub.md new file mode 100644 index 0000000000..e46aadf1a4 --- /dev/null +++ b/2026/day-35-multistage-hub.md @@ -0,0 +1,89 @@ +# Day 35 – Multi-Stage Builds & Docker Hub + +## Challenge Tasks + +### Task 1: The Problem with Large Images +1. Write a simple Go, Java, or Node.js app (even a "Hello World" is fine) +2. Create a Dockerfile that builds and runs it in a **single stage** +3. Build the image and check its **size** + + - Image Size is 638 MB + + ![image](images/task1.png) + + + [Dockerfile](java-app/Dockerfile) + + +--- + +### Task 2: Multi-Stage Build +1. Rewrite the Dockerfile using **multi-stage build**: + - Stage 1: Build the app (install dependencies, compile) + - Stage 2: Copy only the built artifact into a minimal base image (`alpine`, `distroless`, or `scratch`) +2. Build the image and check its size again +3. Compare the two sizes + + - first image size is 638 MB + - multi-stage image size is 255 MB + + ![images](images/task2.png) + + + [Dockerfile](hello-java/Dockerfile.multistage) + +Why is the multi-stage image so much smaller? + +- Multi-stage builds smaller images because they separate “build” from “runtime”, copying only what’s necessary into the final image. + +--- + +### Task 3: Push to Docker Hub +1. Create a free account on [Docker Hub](https://hub.docker.com) (if you don't have one) +2. Log in from your terminal +3. Tag your image properly: `yourusername/image-name:tag` +4. Push it to Docker Hub +5. Pull it on a different machine (or after removing locally) to verify + + + ![image](images/task3.1.png) + + + ![image](images/task3.2.png) + + ![image](images/task3.3.png) + +--- + +### Task 4: Docker Hub Repository +1. Go to Docker Hub and check your pushed image +2. Add a **description** to the repository +3. Explore the **tags** tab — understand how versioning works +4. Pull a specific tag vs `latest` — what happens? + + - Specific tag (e.g., 1.0) = pulls that exact version of the image. + - latest = pulls whatever image is currently marked latest, which can change + + + + + ![image](images/task4.png) + + + ![image](images/task4.1.png) + +--- + +### Task 5: Image Best Practices +Apply these to one of your images and rebuild: +1. Use a **minimal base image** (alpine vs ubuntu — compare sizes) +2. **Don't run as root** — add a non-root USER in your Dockerfile +3. Combine `RUN` commands to **reduce layers** +4. Use **specific tags** for base images (not `latest`) + + + [Dockerfile](hello-java/Dockerfile.final) + +--- + +Dockehub link : https://hub.docker.com/repository/docker/sanketdangat11/java-multi-stage/ diff --git a/2026/day-35/day-35-multistage-hub.md b/2026/day-35/day-35-multistage-hub.md new file mode 100644 index 0000000000..8d0f10f93c --- /dev/null +++ b/2026/day-35/day-35-multistage-hub.md @@ -0,0 +1,90 @@ + +# Day 35 – Multi-Stage Builds & Docker Hub + +## Challenge Tasks + +### Task 1: The Problem with Large Images +1. Write a simple Go, Java, or Node.js app (even a "Hello World" is fine) +2. Create a Dockerfile that builds and runs it in a **single stage** +3. Build the image and check its **size** + + - Image Size is 638 MB + + ![image](images/task1.png) + + + [Dockerfile](hello-java/Dockerfile) + + +--- + +### Task 2: Multi-Stage Build +1. Rewrite the Dockerfile using **multi-stage build**: + - Stage 1: Build the app (install dependencies, compile) + - Stage 2: Copy only the built artifact into a minimal base image (`alpine`, `distroless`, or `scratch`) +2. Build the image and check its size again +3. Compare the two sizes + + - first image size is 638 MB + - multi-stage image size is 255 MB + + ![images](images/task2.1.png) + + + [Dockerfile](hello-java/Dockerfile.multistage) + +Why is the multi-stage image so much smaller? + +- Multi-stage builds smaller images because they separate “build” from “runtime”, copying only what’s necessary into the final image. + +--- + +### Task 3: Push to Docker Hub +1. Create a free account on [Docker Hub](https://hub.docker.com) (if you don't have one) +2. Log in from your terminal +3. Tag your image properly: `yourusername/image-name:tag` +4. Push it to Docker Hub +5. Pull it on a different machine (or after removing locally) to verify + + + ![image](images/task3.1.png) + + + ![image](images/task3.2.png) + + ![image](images/task3.3.png) + +--- + +### Task 4: Docker Hub Repository +1. Go to Docker Hub and check your pushed image +2. Add a **description** to the repository +3. Explore the **tags** tab — understand how versioning works +4. Pull a specific tag vs `latest` — what happens? + + - Specific tag (e.g., 1.0) = pulls that exact version of the image. + - latest = pulls whatever image is currently marked latest, which can change + + + + + ![image](images/task4.png) + + + ![image](images/task4.1.png) + +--- + +### Task 5: Image Best Practices +Apply these to one of your images and rebuild: +1. Use a **minimal base image** (alpine vs ubuntu — compare sizes) +2. **Don't run as root** — add a non-root USER in your Dockerfile +3. Combine `RUN` commands to **reduce layers** +4. Use **specific tags** for base images (not `latest`) + + + [Dockerfile](hello-java/Dockerfile.final) + +--- + +Dockehub link :[https://hub.docker.com/repository/docker/manishvishwa801/java-multistage/] diff --git a/2026/day-35/hello-java/Dockerfile b/2026/day-35/hello-java/Dockerfile new file mode 100644 index 0000000000..6efed8a737 --- /dev/null +++ b/2026/day-35/hello-java/Dockerfile @@ -0,0 +1,14 @@ +# Use Java 17 JDK image +FROM eclipse-temurin:17-jdk + +# Set working directory +WORKDIR /app + +# Copy Java file into container +COPY src/Main.java . + +# Compile Java file +RUN javac Main.java + +# Run the program +CMD ["java", "Main"] diff --git a/2026/day-35/hello-java/Dockerfile.final b/2026/day-35/hello-java/Dockerfile.final new file mode 100644 index 0000000000..089d952892 --- /dev/null +++ b/2026/day-35/hello-java/Dockerfile.final @@ -0,0 +1,29 @@ +# Stage 1: Build +FROM eclipse-temurin:17-jdk-alpine AS builder + +WORKDIR /app + +COPY src/Main.java . + +# Compile the application +RUN javac Main.java + +# Stage 2: Runtime +FROM eclipse-temurin:17-jre-alpine + +# Create non-root user and app directory in one layer +RUN addgroup -S appgroup && \ + adduser -S appuser -G appgroup && \ + mkdir -p /app && \ + chown -R appuser:appgroup /app + +WORKDIR /app + +# Copy compiled artifact +COPY --from=builder /app/Main.class . + +# Use non-root user +USER appuser + +# Run application +CMD ["java", "Main"] diff --git a/2026/day-35/hello-java/Dockerfile.multistage b/2026/day-35/hello-java/Dockerfile.multistage new file mode 100644 index 0000000000..ac59e61b5c --- /dev/null +++ b/2026/day-35/hello-java/Dockerfile.multistage @@ -0,0 +1,20 @@ +# Stage 1: Build the Java app +FROM eclipse-temurin:17-jdk AS builder + +WORKDIR /app + +COPY src/Main.java . + +# Compile the Java file +RUN javac Main.java + +# Stage 2: Minimal runtime image +FROM eclipse-temurin:17-jre-alpine + +WORKDIR /app + +# Copy only compiled class file +COPY --from=builder /app/Main.class . + +# Run the application +CMD ["java", "Main"] diff --git a/2026/day-35/hello-java/src/Main.java b/2026/day-35/hello-java/src/Main.java new file mode 100644 index 0000000000..24f79fa596 --- /dev/null +++ b/2026/day-35/hello-java/src/Main.java @@ -0,0 +1,5 @@ +public class Main { + public static void main(String[] args) { + System.out.println("Hello, Docker World!"); + } +} diff --git a/2026/day-35/images/s b/2026/day-35/images/s new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-35/images/s @@ -0,0 +1 @@ + diff --git a/2026/day-35/images/task1.png b/2026/day-35/images/task1.png new file mode 100644 index 0000000000..5d35304b2d Binary files /dev/null and b/2026/day-35/images/task1.png differ diff --git a/2026/day-35/images/task2.1.png b/2026/day-35/images/task2.1.png new file mode 100644 index 0000000000..0ba9c2e5f4 Binary files /dev/null and b/2026/day-35/images/task2.1.png differ diff --git a/2026/day-35/images/task3.1.png b/2026/day-35/images/task3.1.png new file mode 100644 index 0000000000..9f15548a81 Binary files /dev/null and b/2026/day-35/images/task3.1.png differ diff --git a/2026/day-35/images/task3.2.png b/2026/day-35/images/task3.2.png new file mode 100644 index 0000000000..92e0993a06 Binary files /dev/null and b/2026/day-35/images/task3.2.png differ diff --git a/2026/day-35/images/task3.3.png b/2026/day-35/images/task3.3.png new file mode 100644 index 0000000000..5564c943f8 Binary files /dev/null and b/2026/day-35/images/task3.3.png differ diff --git a/2026/day-35/images/task4.1.png b/2026/day-35/images/task4.1.png new file mode 100644 index 0000000000..b613b6b239 Binary files /dev/null and b/2026/day-35/images/task4.1.png differ diff --git a/2026/day-35/images/task4.2.png b/2026/day-35/images/task4.2.png new file mode 100644 index 0000000000..be1c7e5a77 Binary files /dev/null and b/2026/day-35/images/task4.2.png differ diff --git a/2026/day-50/day-50-k8s-setup.md b/2026/day-50/day-50-k8s-setup.md new file mode 100644 index 0000000000..29cf821e18 --- /dev/null +++ b/2026/day-50/day-50-k8s-setup.md @@ -0,0 +1,100 @@ +## Day 50 – Kubernetes Architecture and Cluster Setup +Challenge Tasks +Task 1: Recall the Kubernetes Story +

    +Kubernetes was created to manage containers at scale. Docker can run containers on a single machine, but when applications grow and need to run across multiple servers, Docker alone is not enough. Kubernetes solves this by automating deployment, scaling, networking, and management of containers.

    + +
    +## Task2: Kubernetes Architecture +Kubernetes consists of two main components: +
      +
    • Control Plane
    • +
    • API Server – Handles all requests
    • +
    • Scheduler – Assigns pods to nodes
    • +
    • Controller Manager – Maintains desired state +
    • +
    • etcd – Stores cluster data
    • +
    +
    +image + +
  • +Worker Node +| Component | Description | Key Responsibility | +| --------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| **Kubelet** | Agent running on each worker node | Ensures containers (pods) are running as expected and communicates with the API server | +| **Container Runtime** | Software that runs containers (e.g., containerd, Docker) | Pulls images and runs containers inside pods | +| **Kube Proxy** | Network component on each node | Manages networking rules and enables communication between services and pods | + +
    + ## Cluster Setup + +
      Tool Used +
    • Kind (Kubernetes IN Docker)
    • +
    • Steps for Setting up
    • +
    + +### Task 3: Install kubectl +# macOS +brew install kubectl +brew install docker --cask docker + +Screenshot 2026-03-27 at 10 50 29 PM +
    +### Step 3: Create Kubernetes Cluster + + +[Kind-config](./kind-config.yml) + +📄 View Screenshot: [Cluster Output](./screenshot/cluster.pdf) + +
    + + +
    +### Step 4: Verify Cluster +kubectl get nodes +### Task 5: Explore Your Cluster +# See cluster info +kubectl cluster-info +📄 View Screenshot: [Cluster Output](./screenshot/cluster-info.pdf) +# List all nodes +kubectl get nodes + +# Get detailed info about your node +kubectl describe node + +# List all namespaces +kubectl get namespaces + +### See ALL pods running in the cluster (across all namespaces) + +kubectl get pods -A + +Look at the pods running in the kube-system namespace: + +kubectl get pods -n kube-system + +📄 View Screenshot: [Cluster Output](./screenshot/kube-system.pdf) + +
    +#90DaysOfDevOps #DevOpsKaJosh #TrainWithShubham + + + + + + + + + + + + + + + + + + + diff --git a/2026/day-50/kind-config.yml b/2026/day-50/kind-config.yml new file mode 100644 index 0000000000..e2c39f2805 --- /dev/null +++ b/2026/day-50/kind-config.yml @@ -0,0 +1,20 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + image: kindest/node:v1.33.1 + - role: worker + image: kindest/node:v1.33.1 + - role: worker + image: kindest/node:v1.33.1 + + - role: worker + image: kindest/node:v1.33.1 + + extraPortMappings: + - containerPort: 8080 + hostPort: 8080 + protocol: TCP + - containerPort: 8443 + hostPort: 8443 + protocol: TCP \ No newline at end of file diff --git a/2026/day-50/screenshot . b/2026/day-50/screenshot . new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-50/screenshot . @@ -0,0 +1 @@ + diff --git a/2026/day-50/screenshot/Cluster-info.pdf b/2026/day-50/screenshot/Cluster-info.pdf new file mode 100644 index 0000000000..8da135e809 Binary files /dev/null and b/2026/day-50/screenshot/Cluster-info.pdf differ diff --git a/2026/day-50/screenshot/cluster.pdf b/2026/day-50/screenshot/cluster.pdf new file mode 100644 index 0000000000..1ccd81cbcc Binary files /dev/null and b/2026/day-50/screenshot/cluster.pdf differ diff --git a/2026/day-50/screenshot/cluster.png b/2026/day-50/screenshot/cluster.png new file mode 100644 index 0000000000..4b6b0f68c6 Binary files /dev/null and b/2026/day-50/screenshot/cluster.png differ diff --git a/2026/day-50/screenshot/kube-system.pdf b/2026/day-50/screenshot/kube-system.pdf new file mode 100644 index 0000000000..b93beea3e7 Binary files /dev/null and b/2026/day-50/screenshot/kube-system.pdf differ diff --git a/2026/day-51/day-51-pods.md b/2026/day-51/day-51-pods.md new file mode 100644 index 0000000000..e3f6c0e60c --- /dev/null +++ b/2026/day-51/day-51-pods.md @@ -0,0 +1,276 @@ +# Day 51 – Kubernetes Manifests and Your First Pods + +## The Anatomy of a Kubernetes Manifest + +Every Kubernetes resource is defined using a YAML manifest with four required top-level fields: + +```yaml +apiVersion: v1 # Which API version to use +kind: Pod # What type of resource +metadata: # Name, labels, namespace + name: my-pod + labels: + app: my-app +spec: # The actual specification (what you want) + containers: + - name: my-container + image: nginx:latest + ports: + - containerPort: 80 +``` + +- `apiVersion` — tells Kubernetes which API group to use. For Pods, it is `v1`. +- `kind` — the resource type. Today it is `Pod`. Later you will use `Deployment`, `Service`, etc. +- `metadata` — the identity of your resource. `name` is required. `labels` are key-value pairs used for organization and selection. +- `spec` — the desired state. For a Pod, this means which containers to run, which images, which ports, etc. + +--- + +## Challenge Tasks + +### Task 1: Create Your First Pod (Nginx) +Create a file called `nginx-pod.yaml`: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-pod + labels: + app: nginx +spec: + containers: + - name: nginx + image: nginx:latest + ports: + - containerPort: 80 +``` + +Apply it: +```bash +kubectl apply -f nginx-pod.yaml +``` + +Verify: +```bash +kubectl get pods +kubectl get pods -o wide +``` + +![image](images/1.png) + +Wait until the STATUS shows `Running`. Then explore: + +# Detailed info about the pod +```bash +kubectl describe pod nginx-pod +``` +- It shows pod metadata, node & network info,container details,readiness/status,mounted volumes, scheduling constraints, and lifecycle events. + + +# Read the logs +```bash +kubectl logs nginx-pod +``` +- It shows the container’s initialization,configuration steps and Nginx startup logs. + + +# Get a shell inside the container +```bash +kubectl exec -it nginx-pod -- /bin/bash +``` + +# Inside the container, run: +```bash +curl localhost:80 +exit +``` +**Verify:** Can you see the Nginx welcome page when you curl from inside the pod? + + - Yes i can see Nginx welcome page inside pod + +![image](images/nginx-curl.png) + +--- + +### Task 2: Create a Custom Pod (BusyBox) +Write a new manifest `busybox-pod.yaml` from scratch (do not copy-paste the nginx one): + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: busybox-pod + labels: + app: busybox + environment: dev +spec: + containers: + - name: busybox + image: busybox:latest + command: ["sh", "-c", "echo Hello from BusyBox && sleep 3600"] +``` + +Apply and verify: +```bash +kubectl apply -f busybox-pod.yaml +kubectl get pods +kubectl logs busybox-pod +``` + +Notice the `command` field — BusyBox does not run a long-lived server like Nginx. Without a command that keeps it running, the container would exit immediately and the pod would go into `CrashLoopBackOff`. + +**Verify:** Can you see "Hello from BusyBox" in the logs? + +- yes + + ![imaage](images/busybox-pod.png) + +--- + +### Task 3: Imperative vs Declarative +You have been using the declarative approach (writing YAML, then `kubectl apply`). Kubernetes also supports imperative commands: + +```bash +# Create a pod without a YAML file +kubectl run redis-pod --image=redis:latest + +# Check it +kubectl get pods +``` + +Now extract the YAML that Kubernetes generated: +```bash +kubectl get pod redis-pod -o yaml +``` + +Compare this output with your hand-written manifests. Notice how much extra metadata Kubernetes adds automatically (status, timestamps, uid, resource version). + +![image](images/redis.png) + +You can also use dry-run to generate YAML without creating anything: +```bash +kubectl run test-pod --image=nginx --dry-run=client -o yaml +``` + +![image](images/nginx-dry.png) + +This is a powerful trick — use it to quickly scaffold a manifest, then customize it. + +**Verify:** Save the dry-run output to a file and compare its structure with your nginx-pod.yaml. What fields are the same? What is different? + +**Same fields:** + +- apiVersion: v1 +- kind: Pod +- metadata.name: nginx-pod +- metadata.labels.app: nginx +- spec.containers[0].name: nginx +- spec.containers[0].image: nginx:latest +- spec.containers[0].ports[0].containerPort: 80 + +**Different fields:** +- metadata.annotations +- creationTimestamp +- uid +- resourceVersion +- namespace +- spec.containers[0].imagePullPolicy +- resources +- terminationMessagePath/Policy +- volumeMounts +- spec.dnsPolicy +- restartPolicy +- enableServiceLinks +- nodeName +- schedulerName +- serviceAccount +- terminationGracePeriodSeconds +- tolerations +- volumesstatus + + +**Imperative (`kubectl run`)** + +1. Creates resources immediately with a command. +2. Quick and good for testing;not stored as a file. + +**Declarative (`kubectl apply -f`)** + +1. Uses a YAML file to define desired state. +2. Versionable,repeatable,and preferred for production. + + +--- + +### Task 4: Validate Before Applying +Before applying a manifest, you can validate it: + +```bash +# Check if the YAML is valid without actually creating the resource +kubectl apply -f nginx-pod.yaml --dry-run=client + +# Validate against the cluster's API (server-side validation) +kubectl apply -f nginx-pod.yaml --dry-run=server +``` + +Now intentionally break your YAML (remove the `image` field or add an invalid field) and run dry-run again. See what error you get. + +**Verify:** What error does Kubernetes give when the image field is missing? + +- error get `The Pod "nginx-pod" is invalid: spec.containers[0].image: Required value` +--- + +### Task 5: Pod Labels and Filtering +Labels are how Kubernetes organizes and selects resources. You added labels in your manifests — now use them: + + +# List all pods with their labels +kubectl get pods --show-labels + +![image](images/all-labels.png) + +# Filter pods by label +kubectl get pods -l app=nginx +kubectl get pods -l environment=dev + +# Add a label to an existing pod +kubectl label pod nginx-pod environment=production + +# Verify +kubectl get pods --show-labels + +# Remove a label +kubectl label pod nginx-pod environment- + + +![image](images/labels.png) + +Write a manifest for a third pod with at least 3 labels (app, environment, team). Apply it and practice filtering. + +--- + +### Task 6: Clean Up +Delete all the pods you created: + +```bash +# Delete by name +kubectl delete pod nginx-pod +kubectl delete pod busybox-pod +kubectl delete pod redis-pod + +# Or delete using the manifest file +kubectl delete -f nginx-pod.yaml + +# Verify everything is gone +kubectl get pods +``` + +![image](images/task6.png) + +**What happens when you delete a standalone Pod?** +- when you delete a standalone Pod, it is gone forever. There is no controller to recreate it. +- This is why in production you use Deployments instead of bare Pods. + +--- + diff --git a/2026/day-51/images/1.png b/2026/day-51/images/1.png new file mode 100644 index 0000000000..8cadf14606 Binary files /dev/null and b/2026/day-51/images/1.png differ diff --git a/2026/day-51/images/abc.txt b/2026/day-51/images/abc.txt new file mode 100644 index 0000000000..edd5ed838e --- /dev/null +++ b/2026/day-51/images/abc.txt @@ -0,0 +1 @@ +abc.txt diff --git a/2026/day-51/images/all-labels.png b/2026/day-51/images/all-labels.png new file mode 100644 index 0000000000..c8898155d0 Binary files /dev/null and b/2026/day-51/images/all-labels.png differ diff --git a/2026/day-51/images/busybox-pod.png b/2026/day-51/images/busybox-pod.png new file mode 100644 index 0000000000..072f237a9e Binary files /dev/null and b/2026/day-51/images/busybox-pod.png differ diff --git a/2026/day-51/images/labels.png b/2026/day-51/images/labels.png new file mode 100644 index 0000000000..c5c5d18821 Binary files /dev/null and b/2026/day-51/images/labels.png differ diff --git a/2026/day-51/images/nginx-curl.png b/2026/day-51/images/nginx-curl.png new file mode 100644 index 0000000000..cd0dee509f Binary files /dev/null and b/2026/day-51/images/nginx-curl.png differ diff --git a/2026/day-51/images/nginx-dry.png b/2026/day-51/images/nginx-dry.png new file mode 100644 index 0000000000..426a7cbd85 Binary files /dev/null and b/2026/day-51/images/nginx-dry.png differ diff --git a/2026/day-51/images/redis.png b/2026/day-51/images/redis.png new file mode 100644 index 0000000000..de4c4696a8 Binary files /dev/null and b/2026/day-51/images/redis.png differ diff --git a/2026/day-51/images/task4.png b/2026/day-51/images/task4.png new file mode 100644 index 0000000000..a945ee94c5 Binary files /dev/null and b/2026/day-51/images/task4.png differ diff --git a/2026/day-51/manifests/mani.txt b/2026/day-51/manifests/mani.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-51/manifests/mani.txt @@ -0,0 +1 @@ + diff --git a/2026/day-52/day-52-namespaces-deployments.md b/2026/day-52/day-52-namespaces-deployments.md new file mode 100644 index 0000000000..6674599f61 --- /dev/null +++ b/2026/day-52/day-52-namespaces-deployments.md @@ -0,0 +1,308 @@ +# Day 52 – Kubernetes Namespaces and Deployments + +## Challenge Tasks + +### Task 1: Explore Default Namespaces +Kubernetes comes with built-in namespaces. List them: + +```bash +kubectl get namespaces +``` + +You should see at least: +- `default` — where your resources go if you do not specify a namespace +- `kube-system` — Kubernetes internal components (API server, scheduler, etc.) +- `kube-public` — publicly readable resources +- `kube-node-lease` — node heartbeat tracking + +Check what is running inside `kube-system`: +```bash +kubectl get pods -n kube-system +``` + +These are the control plane components keeping your cluster alive. Do not touch them. + +**Verify:** How many pods are running in `kube-system`? `12 Pods are running` + +![images](images/task1.png) + +--- + +### Task 2: Create and Use Custom Namespaces +Create two namespaces — one for a development environment and one for staging: + +```bash +kubectl create namespace dev +kubectl create namespace staging +``` + +Verify they exist: +```bash +kubectl get namespaces +``` + +![image](images/ns.png) + +You can also create a namespace from a manifest: +```yaml +# namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: production +``` + +```bash +kubectl apply -f namespace.yaml +``` +![image](images/ns-prd.png) + + +Now run a pod in a specific namespace: +```bash +kubectl run nginx-dev --image=nginx:latest -n dev +kubectl run nginx-staging --image=nginx:latest -n staging +``` + +![image](images/pod-ns.png) + +List pods across all namespaces: +```bash +kubectl get pods -A +``` + +![image](images/allpods.png) + +Notice that `kubectl get pods` without `-n` only shows the `default` namespace. You must specify `-n ` or use `-A` to see everything. + +**Verify:** Does `kubectl get pods` show these pods? What about `kubectl get pods -A`? +- When I run `kubectl get pods`,it does not show any pods. +- When I run `kubectl get pods -A` it shows the pods. + +--- + +### Task 3: Create Your First Deployment +A Deployment tells Kubernetes: "I want X replicas of this Pod running at all times." If a Pod crashes, the Deployment controller recreates it automatically. + +Create a file `nginx-deployment.yaml`: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + namespace: dev + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.24 + ports: + - containerPort: 80 +``` + +Key differences from a standalone Pod: +- `kind: Deployment` instead of `kind: Pod` +- `apiVersion: apps/v1` instead of `v1` +- `replicas: 3` tells Kubernetes to maintain 3 identical pods +- `selector.matchLabels` connects the Deployment to its Pods +- `template` is the Pod template — the Deployment creates Pods using this blueprint + +Apply it: +```bash +kubectl apply -f nginx-deployment.yaml +``` + +Check the result: +```bash +kubectl get deployments -n dev +kubectl get pods -n dev +``` + +![image](images/task3.png) + +You should see 3 pods with names like `nginx-deployment-xxxxx-yyyyy`. + +**Verify:** What do the READY, UP-TO-DATE, and AVAILABLE columns mean in the deployment output? + +**READY:** Pods ready to serve traffic (ready/desired) + +**UP-TO-DATE:** Pods using the latest deployment spec + +**AVAILABLE:** Pods ready and stable + +--- + +### Task 4: Self-Healing — Delete a Pod and Watch It Come Back +This is the key difference between a Deployment and a standalone Pod. + +```bash +# List pods +kubectl get pods -n dev + +# Delete one of the deployment's pods (use an actual pod name from your output) +kubectl delete pod -n dev + +# Immediately check again +kubectl get pods -n dev +``` + +The Deployment controller detects that only 2 of 3 desired replicas exist and immediately creates a new one. The deleted pod is replaced within seconds. + +**Verify:** Is the replacement pod's name the same as the one you deleted, or different? + +- Yes,different name but same prefix + +![image](images/task4.png) + +--- + +### Task 5: Scale the Deployment +Change the number of replicas: + +```bash +# Scale up to 5 +kubectl scale deployment nginx-deployment --replicas=5 -n dev +kubectl get pods -n dev + +# Scale down to 2 +kubectl scale deployment nginx-deployment --replicas=2 -n dev +kubectl get pods -n dev +``` + +![image](images/scale.png) + + +![image](images/down.png) + +Watch how Kubernetes creates or terminates pods to match the desired count. + +You can also scale by editing the manifest — change `replicas: 4` in your YAML file and run `kubectl apply -f nginx-deployment.yaml` again. + +![image](images/scale-declarative.png) + +**Verify:** When you scaled down from 5 to 2, what happened to the extra pods? + +- The extra pods are terminated automatically. +- Kubernetes keeps only the desired number of replicas (2) and removes the remaining 3 pods to match that state. + +--- + +### Task 6: Rolling Update +Update the Nginx image version to trigger a rolling update: + +```bash +kubectl set image deployment/nginx-deployment nginx=nginx:1.25 -n dev +``` + +Watch the rollout in real time: +```bash +kubectl rollout status deployment/nginx-deployment -n dev +``` + +Kubernetes replaces pods one by one — old pods are terminated only after new ones are healthy. This means zero downtime. + +Check the rollout history: +```bash +kubectl rollout history deployment/nginx-deployment -n dev +``` + +![image](images/rolling_update.png) + +Now roll back to the previous version: +```bash +kubectl rollout undo deployment/nginx-deployment -n dev +kubectl rollout status deployment/nginx-deployment -n dev +``` + +Verify the image is back to the previous version: +```bash +kubectl describe deployment nginx-deployment -n dev | grep Image +``` + +![image](images/rollback_undo.png) + +**Verify:** What image version is running after the rollback? +- After rollback Image Version is `nginx:1.24` +--- + +### Task 7: Clean Up +```bash +kubectl delete deployment nginx-deployment -n dev +kubectl delete pod nginx-dev -n dev +kubectl delete pod nginx-staging -n staging +kubectl delete namespace dev staging production +``` + +Deleting a namespace removes everything inside it. Be very careful with this in production. + +```bash +kubectl get namespaces +kubectl get pods -A +``` + +![image](images/cleanup-deplo.png) + +**Verify:** Are all your resources gone? +- Yes, all resources gone + + + +--- + +**What namespaces are and why you would use them** +- Namespaces are like folders in Kubernetes that separate resources inside one cluster +- They are used to keep things organized and isolated (logical isolation) (e.g., stage,dev and prod don’t mix) + + +**Explaination of Deployment manifest** + +- `apiVersion` & `kind` Defines the resource as a Deployment +- `metadata`Contains Deployment identity (name, namespace, labels) +- `spec` Main configuration of the Deployment +- `spec.replicas` Ensures 3 Pods are always running +- `spec.selector` Matches Pods with label app: nginx +- `spec.template` Blueprint used to create Pods +- `template.metadata` Labels assigned to Pods +- `template.spec` Pod-level configuration +- `containers` Defines container details (name, image) +- `ports` Exposes container port 80 + +**What happens when you delete a Pod managed by a Deployment vs a standalone Pod** + +1. Pod managed by a Deployment: + - Kubernetes automatically recreates a new pod to maintain the desired number of replicas. + - The new pod gets a different name but keeps the same Deployment/ReplicaSet prefix. + - Ensures the desired state is always met. + +2. Standalone Pod (not managed by Deployment): + - Kubernetes does NOT recreate it. + - Once deleted, the pod is gone permanently. + + +**How scaling works (both imperative and declarative)** + +1. `Imperative`: you directly tell Kubernetes how many replicas you want using a command. +2. `Declarative`: you update the Deployment manifest (YAML) with the desired replicas. + +**How rolling updates and rollbacks work** + +1. `Rolling Updates` +- Deployment updates its pod template (e.g., new container image). +- Kubernetes creates new pods with the updated spec. +- Old pods are terminated gradually as new pods become ready. + +2. `Rollbacks` +- Deployment keeps a history of previous ReplicaSets. +- You trigger rollback to a previous revision. +- Kubernetes recreates pods from the old ReplicaSet while removing the current ones. diff --git a/2026/day-52/images/abc.txt b/2026/day-52/images/abc.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-52/images/abc.txt @@ -0,0 +1 @@ + diff --git a/2026/day-52/images/allpods.png b/2026/day-52/images/allpods.png new file mode 100644 index 0000000000..a22c648a41 Binary files /dev/null and b/2026/day-52/images/allpods.png differ diff --git a/2026/day-52/images/cleanup-deplo.png b/2026/day-52/images/cleanup-deplo.png new file mode 100644 index 0000000000..4e6724a725 Binary files /dev/null and b/2026/day-52/images/cleanup-deplo.png differ diff --git a/2026/day-52/images/ns-prd.png b/2026/day-52/images/ns-prd.png new file mode 100644 index 0000000000..85343067a4 Binary files /dev/null and b/2026/day-52/images/ns-prd.png differ diff --git a/2026/day-52/images/ns.png b/2026/day-52/images/ns.png new file mode 100644 index 0000000000..7a856b9248 Binary files /dev/null and b/2026/day-52/images/ns.png differ diff --git a/2026/day-52/images/rollback_undo.png b/2026/day-52/images/rollback_undo.png new file mode 100644 index 0000000000..e05d244e57 Binary files /dev/null and b/2026/day-52/images/rollback_undo.png differ diff --git a/2026/day-52/images/rolling_update.png b/2026/day-52/images/rolling_update.png new file mode 100644 index 0000000000..1e81ef7f77 Binary files /dev/null and b/2026/day-52/images/rolling_update.png differ diff --git a/2026/day-52/images/scale.png b/2026/day-52/images/scale.png new file mode 100644 index 0000000000..dcb4d00442 Binary files /dev/null and b/2026/day-52/images/scale.png differ diff --git a/2026/day-52/images/task1.png b/2026/day-52/images/task1.png new file mode 100644 index 0000000000..e448cfc509 Binary files /dev/null and b/2026/day-52/images/task1.png differ diff --git a/2026/day-52/images/task3.png b/2026/day-52/images/task3.png new file mode 100644 index 0000000000..4bfa8b79a4 Binary files /dev/null and b/2026/day-52/images/task3.png differ diff --git a/2026/day-52/images/task4-pod delete.png b/2026/day-52/images/task4-pod delete.png new file mode 100644 index 0000000000..7c2cbaf73a Binary files /dev/null and b/2026/day-52/images/task4-pod delete.png differ diff --git a/2026/day-52/images/task4.png b/2026/day-52/images/task4.png new file mode 100644 index 0000000000..a945ee94c5 Binary files /dev/null and b/2026/day-52/images/task4.png differ diff --git a/2026/day-53/day-53-services.md b/2026/day-53/day-53-services.md new file mode 100644 index 0000000000..68d514980f --- /dev/null +++ b/2026/day-53/day-53-services.md @@ -0,0 +1,430 @@ +# Day 53 – Kubernetes Services + +## Challenge Tasks + +### Task 1: Deploy the Application +First, create a Deployment that you will expose with Services. Create `app-deployment.yaml`: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web-app + labels: + app: web-app +spec: + replicas: 3 + selector: + matchLabels: + app: web-app + template: + metadata: + labels: + app: web-app + spec: + containers: + - name: nginx + image: nginx:1.25 + ports: + - containerPort: 80 +``` + +```bash +kubectl apply -f app-deployment.yaml +kubectl get pods -o wide +``` + +Note the individual Pod IPs. These will change if pods restart — that is the problem Services fix. + +**Verify:** Are all 3 pods running? Note down their IP addresses. + + +![image](images/day53-task1.png) + +--- + +### Task 2: ClusterIP Service (Internal Access) +ClusterIP is the default Service type. It gives your Pods a stable internal IP that is only reachable from within the cluster. + +Create `clusterip-service.yaml`: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: web-app-clusterip +spec: + type: ClusterIP + selector: + app: web-app + ports: + - port: 80 + targetPort: 80 +``` + +Key fields: +- `selector.app: web-app` — this Service routes traffic to all Pods with the label `app: web-app` +- `port: 80` — the port the Service listens on +- `targetPort: 80` — the port on the Pod to forward traffic to + +```bash +kubectl apply -f clusterip-service.yaml +kubectl get services +``` + +![image](images/day-53-task2.1.png) + +You should see `web-app-clusterip` with a CLUSTER-IP address. This IP is stable — it will not change even if Pods restart. + +Now test it from inside the cluster: +```bash +# Run a temporary pod to test connectivity +kubectl run test-client --image=busybox:latest --rm -it --restart=Never -- sh + +# Inside the test pod, run: +wget -qO- http://web-app-clusterip +exit +``` + +You should see the Nginx welcome page. The Service load-balanced your request to one of the 3 Pods. + +![image](images/task2.2.png) + +**Verify:** Does the Service respond? Try running the wget command multiple times — the Service distributes traffic across all healthy Pods. +- Yes,service respond +--- + +### Task 3: Discover Services with DNS +Kubernetes has a built-in DNS server. Every Service gets a DNS entry automatically: + +``` +..svc.cluster.local +``` + +Test this: +```bash +kubectl run dns-test --image=busybox:latest --rm -it --restart=Never -- sh + +# Inside the pod: +# Short name (works within the same namespace) +wget -qO- http://web-app-clusterip + +# Full DNS name +wget -qO- http://web-app-clusterip.default.svc.cluster.local + +# Look up the DNS entry +nslookup web-app-clusterip +exit +``` + +Both the short name and the full DNS name resolve to the same ClusterIP. In practice, you use the short name when communicating within the same namespace and the full name when reaching across namespaces. + +**Verify:** What IP does `nslookup` return? Does it match the CLUSTER-IP from `kubectl get services`? + +- Yes — the IPs match perfectly.`nslookup` is correctly resolving the service to the `same ClusterIP` shown by Kubernetes. + +![image](images/task2.3.png) +--- + +### Task 4: NodePort Service (External Access via Node) +A NodePort Service exposes your application on a port on every node in the cluster. This lets you access the Service from outside the cluster. + +Create `nodeport-service.yaml`: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: web-app-nodeport +spec: + type: NodePort + selector: + app: web-app + ports: + - port: 80 + targetPort: 80 + nodePort: 30080 +``` + +- `nodePort: 30080` — the port opened on every node (must be in range 30000-32767) +- Traffic flow: `:30080` -> Service -> Pod:80 + +```bash +kubectl apply -f nodeport-service.yaml +kubectl get services +``` + +![image](images/node-port.png) + +Access the service: +```bash +# If using Minikube +minikube service web-app-nodeport --url + +# If using Kind, get the node IP first +kubectl get nodes -o wide +# Then curl :30080 + +# If using Docker Desktop +curl http://localhost:30080 +``` + +**Verify:** Can you see the Nginx welcome page from your browser or terminal using the NodePort? +- Yes,I see the Nginx welcome page from terminal using the NodePort + +![image](images/node-response.png) + +--- + +### Task 5: LoadBalancer Service (Cloud External Access) +In a cloud environment (AWS, GCP, Azure), a LoadBalancer Service provisions a real external load balancer that routes traffic to your nodes. + +Create `loadbalancer-service.yaml`: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: web-app-loadbalancer +spec: + type: LoadBalancer + selector: + app: web-app + ports: + - port: 80 + targetPort: 80 +``` + +```bash +kubectl apply -f loadbalancer-service.yaml +kubectl get services +``` + +On a local cluster (Minikube, Kind, Docker Desktop), the EXTERNAL-IP will show `` because there is no cloud provider to create a real load balancer. This is expected. + +If you are using Minikube: +```bash +# Minikube can simulate a LoadBalancer +minikube tunnel +# In another terminal, check again: +kubectl get services +``` + +In a real cloud cluster, the EXTERNAL-IP would be a public IP address or hostname provisioned by the cloud provider. + +**Verify:** What does the EXTERNAL-IP column show? Why is it `` on a local cluster? + +- In a local cluster,the EXTERNAL-IP staying `` is expected because there’s no cloud provider to assign an external address. +- In a cloud environment,the same Service type would automatically provision a public load balancer and receive an external IP. + + +![image](images/task5.png) +--- + +### Task 6: Understand the Service Types Side by Side +Check all three services: + +```bash +kubectl get services -o wide +``` + +Verify this: +```bash +kubectl describe service web-app-loadbalancer +``` + +You should see all three: a ClusterIP, a NodePort, and the LoadBalancer configuration. + +**Verify:** Does the LoadBalancer service also have a ClusterIP and NodePort assigned? + +- Yes — a LoadBalancer service always has both ClusterIP and NodePort. + +![image](images/task6.png) + +--- + +### Task 7: Clean Up +```bash +kubectl delete -f app-deployment.yaml +kubectl delete -f clusterip-service.yaml +kubectl delete -f nodeport-service.yaml +kubectl delete -f loadbalancer-service.yaml + +kubectl get pods +kubectl get services +``` + +Only the built-in `kubernetes` service in the default namespace should remain. + +**Verify:** Is everything cleaned up? + +- Yes,everything cleaned up + +![image](images/task7.png) + + + +--- + +**What problem Services solve and how they relate to Pods and Deployments** + +**The Problem** + +Pods in Kubernetes are ephemeral: +- They get new IP addresses when restarted +- They are created/destroyed dynamically by a Deployment + +**The Solution:** `Service` + +A Service provides: +- A stable IP address (ClusterIP) +- A stable DNS name +- Load balancing across Pods + +**Relationship** + +`Deployment:` Manages Pods (creates 3 replicas) + +`Pods:` Run your application (nginx) + +`Service`: Sits in front of Pods, Uses labels (selector) to find them.Routes traffic to them + +`Client → Service → Pods (via label selector)` + + +**Your three Service manifests with an explanation of each type** + +`ClusterIP Service` + +```bash +apiVersion: v1 +kind: Service +metadata: + name: web-app-clusterip +spec: + type: ClusterIP + selector: + app: web-app + ports: + - port: 80 + targetPort: 80 +``` +- Default Service type +- Exposes Pods inside the cluster only +- Provides a stable internal IP + DNS name +- Used for internal communication between services + + +`NodePort Service` + +```bash +apiVersion: v1 +kind: Service +metadata: + name: web-app-nodeport +spec: + type: NodePort + selector: + app: web-app + ports: + - port: 80 + targetPort: 80 + nodePort: 30080 +``` +- Exposes Service on each node’s IP at a fixed port (30000–32767) +- Access using: `:NodePort` +- Used for external access in development/testing + + +`LoadBalancer Service` + +```bash +apiVersion: v1 +kind: Service +metadata: + name: web-app-loadbalancer +spec: + type: LoadBalancer + selector: + app: web-app + ports: + - port: 80 + targetPort: 80 +``` +- Creates an external load balancer (in cloud environments) +- Provides a public IP to access the app +- Used for production external traffic +- Internally also includes ClusterIP + NodePort + + +**The difference between ClusterIP, NodePort, and LoadBalancer** + +| Type | Accessible From | Use Case | +|------|----------------|----------| +| ClusterIP | Inside the cluster only | Internal communication between services | +| NodePort | Outside via `:` | Development, testing, direct node access | +| LoadBalancer | Outside via cloud load balancer | Production traffic in cloud environments | + +Each type builds on the previous one: +- LoadBalancer creates a NodePort, which creates a ClusterIP +- So a LoadBalancer service also has a ClusterIP and a NodePort + + + +**How Kubernetes DNS works for service discovery** + +1. `Service is created` +- Kubernetes automatically creates a DNS entry for the Service. +- Example: web-app-clusterip + +2. `Pod makes a request using Service name` +- The Pod accesses the Service using its DNS name:`wget http://web-app-clusterip` + +3. `DNS query sent to CoreDNS` +- The request is sent to Kubernetes DNS (CoreDNS) for resolution. + +4. `DNS resolves Service name → ClusterIP` +- The Service name resolves to its ClusterIP. +- Example: web-app-clusterip → ClusterIP +- This matches the output of: kubectl get svc + +5. `Request reaches the Service` +- The request is routed to the Service using the ClusterIP. + +6. `Service forwards request to Pods (via Endpoints)` +- The Service selects Pods using labels (app: web-app) +- Traffic is load-balanced across all healthy Pods + +7. `Response sent back to Pod` +- One of the Pods processes the request and returns a response +(e.g., Nginx welcome page) + + +`Pod → CoreDNS → Service (ClusterIP) → Endpoints → Pod` + +**What Endpoints are and how to inspect them** + +- `Endpoints` = actual Pod IPs behind a Service +- A Service does NOT directly store Pods — it uses Endpoints. + +`Example:` +- `Service: web-app-clusterip` +- `Endpoints look like:` + +```bash + 10.244.0.5:80 + 10.244.0.6:80 + 10.244.0.7:80 +``` + +`Why Endpoints matter:` +- They show real backend Pods +- They update automatically when Pods: `Start ,Stop ,Restart` + +`How to inspect Endpoints:` +```bash +kubectl get endpoints +``` +```bash +kubectl describe endpoints web-app-clusterip +``` diff --git a/2026/day-53/images/abc.txt b/2026/day-53/images/abc.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-53/images/abc.txt @@ -0,0 +1 @@ + diff --git a/2026/day-53/images/day53-task1.png b/2026/day-53/images/day53-task1.png new file mode 100644 index 0000000000..a10f115335 Binary files /dev/null and b/2026/day-53/images/day53-task1.png differ diff --git a/2026/day-53/images/day53-task2.png b/2026/day-53/images/day53-task2.png new file mode 100644 index 0000000000..a0ce11658a Binary files /dev/null and b/2026/day-53/images/day53-task2.png differ diff --git a/2026/day-53/images/node-port.png b/2026/day-53/images/node-port.png new file mode 100644 index 0000000000..4b885e16f7 Binary files /dev/null and b/2026/day-53/images/node-port.png differ diff --git a/2026/day-53/images/node-response.png b/2026/day-53/images/node-response.png new file mode 100644 index 0000000000..0a161748b0 Binary files /dev/null and b/2026/day-53/images/node-response.png differ diff --git a/2026/day-53/images/task2.2.png b/2026/day-53/images/task2.2.png new file mode 100644 index 0000000000..f89c0cb7d7 Binary files /dev/null and b/2026/day-53/images/task2.2.png differ diff --git a/2026/day-53/images/task2.3.png b/2026/day-53/images/task2.3.png new file mode 100644 index 0000000000..4e7f5426bb Binary files /dev/null and b/2026/day-53/images/task2.3.png differ diff --git a/2026/day-53/images/task5.png b/2026/day-53/images/task5.png new file mode 100644 index 0000000000..9d0c052aac Binary files /dev/null and b/2026/day-53/images/task5.png differ diff --git a/2026/day-53/images/task6.png b/2026/day-53/images/task6.png new file mode 100644 index 0000000000..9058ad820e Binary files /dev/null and b/2026/day-53/images/task6.png differ diff --git a/2026/day-53/images/task7.png b/2026/day-53/images/task7.png new file mode 100644 index 0000000000..d0f80a2dbc Binary files /dev/null and b/2026/day-53/images/task7.png differ diff --git a/2026/day-54/day-54-configmaps-secrets.md b/2026/day-54/day-54-configmaps-secrets.md new file mode 100644 index 0000000000..55aeba2308 --- /dev/null +++ b/2026/day-54/day-54-configmaps-secrets.md @@ -0,0 +1,127 @@ +# Day 54 – Kubernetes ConfigMaps and Secrets + +## Challenge Tasks + +### Task 1: Create a ConfigMap from Literals +1. Use `kubectl create configmap` with `--from-literal` to create a ConfigMap called `app-config` with keys `APP_ENV=production`, `APP_DEBUG=false`, and `APP_PORT=8080` +2. Inspect it with `kubectl describe configmap app-config` and `kubectl get configmap app-config -o yaml` +3. Notice the data is stored as plain text — no encoding, no encryption + +**Verify:** Can you see all three key-value pairs? + +- Yes,all 3 key-value pairs are visible in plain text +- No encoding, no encryption + + +![image](images/task1.png) + +--- + +### Task 2: Create a ConfigMap from a File +1. Write a custom Nginx config file that adds a `/health` endpoint returning "healthy" +2. Create a ConfigMap from this file using `kubectl create configmap nginx-config --from-file=default.conf=` +3. The key name (`default.conf`) becomes the filename when mounted into a Pod + +**Verify:** Does `kubectl get configmap nginx-config -o yaml` show the file contents? + +- Yes file contents are fully visible in YAML + +![image](images/task2.png) + +--- + +### Task 3: Use ConfigMaps in a Pod +1. Write a Pod manifest that uses `envFrom` with `configMapRef` to inject all keys from `app-config` as environment variables. Use a busybox container that prints the values. +2. Write a second Pod manifest that mounts `nginx-config` as a volume at `/etc/nginx/conf.d`. Use the nginx image. +3. Test that the mounted config works: `kubectl exec -- curl -s http://localhost/health` + +Use environment variables for simple key-value settings. Use volume mounts for full config files. + +**Verify:** Does the `/health` endpoint respond? + +- Yes,/health endpoint respond + +![image](images/task3.1.png) + + +![image](images/task3.2.png) + + +--- + +### Task 4: Create a Secret +1. Use `kubectl create secret generic db-credentials` with `--from-literal` to store `DB_USER=admin` and `DB_PASSWORD=s3cureP@ssw0rd` +2. Inspect with `kubectl get secret db-credentials -o yaml` — the values are base64-encoded +3. Decode a value: `echo '' | base64 --decode` + +**base64 is encoding, not encryption.** Anyone with cluster access can decode Secrets. The real advantages are RBAC separation, tmpfs storage on nodes, and optional encryption at rest. + +**Verify:** Can you decode the password back to plaintext? + +- Yes, decode the password back to plaintext + +![image](images/task4.png) + +--- + +### Task 5: Use Secrets in a Pod +1. Write a Pod manifest that injects `DB_USER` as an environment variable using `secretKeyRef` +2. In the same Pod, mount the entire `db-credentials` Secret as a volume at `/etc/db-credentials` with `readOnly: true` +3. Verify: each Secret key becomes a file, and the content is the decoded plaintext value + +**Verify:** Are the mounted file values plaintext or base64? +- Mounted file values planintext + +![image](images/task5.png) +--- + +### Task 6: Update a ConfigMap and Observe Propagation +1. Create a ConfigMap `live-config` with a key `message=hello` +2. Write a Pod that mounts this ConfigMap as a volume and reads the file in a loop every 5 seconds +3. Update the ConfigMap: `kubectl patch configmap live-config --type merge -p '{"data":{"message":"world"}}'` +4. Wait 30-60 seconds — the volume-mounted value updates automatically +5. Environment variables from earlier tasks do NOT update — they are set at pod startup only + +**Verify:** Did the volume-mounted value change without a pod restart? + +- Yes, the volume-mounted value does change without restarting the Pod. + +![image](images/task6.png) + +--- + +### Task 7: Clean Up +Delete all pods, ConfigMaps, and Secrets you created. + +![image](images/task7.png) + +--- + +**What ConfigMaps and Secrets are and when to use each** + +- `ConfigMap` stores non-sensitive data (e.g., config, URLs) +- `Secret` stores sensitive data (e.g., passwords, tokens) +- Secrets use base64 (not secure by itself) + +**The difference between environment variables and volume mounts** + +`Environment Variables:` +- Injected at Pod startup +- Do NOT update if ConfigMap/Secret changes + +`Volume Mounts:` +- Data is available as files inside container +- Auto-updates (after ~30–60 seconds) + +**Why base64 is encoding, not encryption** + +- Base64 is just encoding, not secure +- It can be easily decoded by anyone (no key needed) +- In `Kubernetes Secrets:` + - Data is base64 only for safe storage in YAML + - Anyone with access can decode it + +**How ConfigMap updates propagate to volumes but not env vars** + +- `ConfigMap as volume` updates automatically without restart +- `ConfigMap as env var` stays same until Pod restart diff --git a/2026/day-54/images/abc.txt b/2026/day-54/images/abc.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-54/images/abc.txt @@ -0,0 +1 @@ + diff --git a/2026/day-54/manifest/abc.txt b/2026/day-54/manifest/abc.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-54/manifest/abc.txt @@ -0,0 +1 @@ + diff --git a/2026/day-68/day-68-ansible-intro.md b/2026/day-68/day-68-ansible-intro.md new file mode 100644 index 0000000000..a039f50c1a --- /dev/null +++ b/2026/day-68/day-68-ansible-intro.md @@ -0,0 +1,175 @@ +### Day 68 -- Introduction to Ansible and Inventory Setup +##### Challenge Tasks +#### Task 1: Understand Ansible +Research and write short notes on: + +Q1. What is configuration management? Why do we need it? +Configuration Management is a systematic DevOps tools is to manage the server configuration, installing required packages for web servers to run for applications. Configuration Management is a way to ensure that your systems(servers,software and other os peripherical devices) always match a "desired state" or baseline. + +Instead of installing & configuring packages manually in each system or nodes, we define the desired setup and apply it automatically using tools like Ansible, chef, puppet. + +Why do we need it? +Without configuration management, complex IT environments quickly experience "configuration drift," where systems gradually deviate from their intended settings, leading to unpredictable failures. Key reasons for its importance. +- Reliability & Uptime: By ensuring consistency between development, testing, and production environments, CM prevents "it worked on my machine" errors and reduces costly outages. +- Security & Compliance: It helps close security holes by ensuring critical patches are applied and default insecure settings are changed. +- Scalability: Automated tools (like Ansible, Puppet, or Terraform) allow teams to manage hundreds or thousands of servers with the same effort it takes to manage one. +- Efficiency: Automation reduces repetitive manual tasks, freeing up staff to focus on innovation rather than "firefighting" configuration errors + +Q2. How is Ansible different from Chef, Puppet, and Salt? +Ansible is different because of following reasons: +- Ansible: If you want to get started quickly without managing agent software. It's excellent for "orchestration"—running tasks in a specific order across multiple machines. +- Chef: Ideal for "development-centric" teams who are comfortable writing actual code (Ruby) to manage their infrastructure. +- Puppet: The most "mature" choice for massive, heterogeneous environments where you need strict, declarative state management and detailed reporting. +- SaltStack: Best for speed. It uses a high-performance messaging system (ZeroMQ) that can push changes to thousands of servers almost instantly. + +Q3. What does "agentless" mean? How does Ansible connect to managed nodes? + +In the context of configuration management, agentless means you do not need to install or maintain any proprietary software (agents) on the servers you want to manage. Instead of having a background service constantly running on the target machine, the management tool connect it only when needed, performs its tasks, and then disconnects. + +- How Ansible Connects: +Ansible uses existing, standard communication protocols that are already built into most operating systems to manage nodes: + +- For Linux/Unix Nodes (SSH): Ansible connects primarily via Secure Shell (SSH). By default, it assumes you are using SSH keys for passwordless authentication, though it can also use standard passwords with the --ask-pass flag. +- For Windows Nodes (WinRM or SSH): +WinRM: Traditionally, Ansible uses Windows Remote Management (WinRM), which communicates over HTTP/HTTPS. +SSH: On modern Windows versions (Server 2019+ and Windows 10+), Ansible officially supports SSH as a faster and more secure alternative to WinRM. + +- For Network Devices: Ansible can connect to routers and switches using standard protocols like SSH, NETCONF, or specific APIs provided by the manufacturer. + The Execution Process: + + When you run a command or playbook, Ansible follows this "push-based" flow: +- Connects: The control node initiates an SSH or WinRM connection to the target. +- Transfers: It pushes small, temporary programs called modules (usually Python-based) to the remote machine. +- Executes: The remote machine runs these modules locally. +- Cleans Up: Once the task is finished, Ansible removes the temporary modules and closes the connection. + +
    + + +#### Task 2: Set Up Your Lab Environment +You need 2-3 EC2 instances to practice on. Choose one approach: + +Option A: Use Terraform (recommended -- you just learned this) Use your TerraWeek skills to provision 3 EC2 instances with: + +Amazon Linux 2 or Ubuntu 22.04 +t2.micro instance type iam taking t3.micro because on my account t2.micro N/A +A security group allowing SSH (port 22) +A key pair for SSH access +Option B: Launch manually from AWS Console Create 3 instances with the same specs above. +ssh -i ~/your-key.pem ec2-user@ +ssh -i ~/your-key.pem ec2-user@ +ssh -i ~/your-key.pem ec2-user@ + +Screenshot (57)


    + + + + +- Instance 1: web server +- Instance 2: app server + +- Instance 3: db server + +Verify you can SSH into each one from your control node: +ssh -i ~/your-key.pem ec2-user@ +ssh -i ~/your-key.pem ec2-user@ +ssh -i ~/your-key.pem ec2-user@ +Screenshot 2026-04-08 at 10 21 02 PM + Screenshot 2026-04-08 at 8 26 42 PM + + + +Screenshot 2026-04-08 at 10 13 27 PM + + + + +#### Task 3: Install Ansible +Install Ansible on your control node (your laptop or one dedicated EC2 instance): +- macOS +brew install ansible + +- Ubuntu/Debian +sudo apt update +sudo apt install ansible -y + +- Amazon Linux / RHEL +sudo yum install ansible -y +- or +pip3 install ansible + +- Verify +ansible --version +task4 +
    +### Task 4: Create Your Inventory File +The inventory tells Ansible which servers to manage. Create a project directory and your first inventory: + + +Screenshot 2026-04-08 at 10 56 39 PM + +Troubleshoot: If ping fails: + +Check the SSH key path and permissions (chmod 400 your-key.pem) +Check the security group allows SSH from your IP +Check the ansible_user matches your AMI (ec2-user for Amazon Linux, ubuntu for Ubuntu) + + +#### Task 5: Run Ad-Hoc Commands +Ad-hoc commands let you run quick one-off tasks without writing a playbook. + +- Check uptime on all servers: +ansible all -i inventory.ini -m command -a "uptime" + +Screenshot 2026-04-08 at 10 59 54 PM + +- Check free memory on web servers only: +ansible web -i inventory.ini -m command -a "free -h" + +ansible all -i inventory.ini -m command -a "df -h" + +Screenshot 2026-04-08 at 11 04 12 PM + +- Install a package on the web group: +ansible web -i inventory.ini -m yum -a "name=git state=present" --become + +Screenshot 2026-04-08 at 11 07 34 PM + +- Copy a file to all servers: +echo "Hello from Ansible" > hello.txt +ansible all -i inventory.ini -m copy -a "src=hello.txt dest=/tmp/hello.txt" + +Screenshot 2026-04-08 at 11 11 10 PM 1 + + +- Verify the file was copied: +ansible all -i inventory.ini -m command -a "cat /tmp/hello.txt" +Screenshot 2026-04-08 at 11 12 53 PM +
    +#### Task 6: Explore Inventory Groups and Patterns + +Create a group of groups -- add this to your inventory.ini: +[application:children] +web +app + +[all_servers:children] +application +db + +- Run commands against different groups: +ansible application -i inventory.ini -m ping # web + app servers +ansible db -i inventory.ini -m ping # only db server +ansible all_servers -i inventory.ini -m ping # everything + +Screenshot 2026-04-08 at 11 18 48 PM + + + + + + + + + + diff --git a/2026/day-68/terraform/.gitignore b/2026/day-68/terraform/.gitignore new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-68/terraform/.gitignore @@ -0,0 +1 @@ + diff --git a/2026/day-68/terraform/ansible-terra-key b/2026/day-68/terraform/ansible-terra-key new file mode 100644 index 0000000000..930802b1f5 --- /dev/null +++ b/2026/day-68/terraform/ansible-terra-key @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBzs6nJoiMOEmTMb3QKNVDCKdVLKA3HzKfnOMegYguJNwAAAKi1oU0StaFN +EgAAAAtzc2gtZWQyNTUxOQAAACBzs6nJoiMOEmTMb3QKNVDCKdVLKA3HzKfnOMegYguJNw +AAAEDLku94UVCDoXEVAhJjSqLTgDnZ9PhL5v2YY9vDKq8pBXOzqcmiIw4SZMxvdAo1UMIp +1UsoDcfMp+c4x6BiC4k3AAAAJHByaXlhbmthQFByaXlhbmthcy1NYWNCb29rLUFpci5sb2 +NhbAE= +-----END OPENSSH PRIVATE KEY----- diff --git a/2026/day-68/terraform/ansible-terra-key.pub b/2026/day-68/terraform/ansible-terra-key.pub new file mode 100644 index 0000000000..94da0e1073 --- /dev/null +++ b/2026/day-68/terraform/ansible-terra-key.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHOzqcmiIw4SZMxvdAo1UMIp1UsoDcfMp+c4x6BiC4k3 priyanka@Priyankas-MacBook-Air.local diff --git a/2026/day-68/terraform/ec2.tf b/2026/day-68/terraform/ec2.tf new file mode 100644 index 0000000000..95dc3f2665 --- /dev/null +++ b/2026/day-68/terraform/ec2.tf @@ -0,0 +1,86 @@ +resource aws_key_pair my_key_pair { + +key_name="ansible-terra-key" +public_key=file("ansible-terra-key.pub") +} + +# VPC Default + +resource aws_default_vpc default { +} + +# Security Group + +resource aws_security_group my_security_group { +name="terra-security-group" +vpc_id= aws_default_vpc.default.id # interpolation +description = "this is Inbound and outbound rules for your instance Security group" + +} + +# Inbound & Outbount port rules +resource aws_vpc_security_group_ingress_rule allow_http { + security_group_id = aws_security_group.my_security_group.id + cidr_ipv4 = "0.0.0.0/0" + from_port = 80 + ip_protocol = "tcp" + to_port = 80 +} + +resource aws_vpc_security_group_ingress_rule allow_ssh { + security_group_id = aws_security_group.my_security_group.id + cidr_ipv4 = "0.0.0.0/0" + from_port = 22 + ip_protocol = "tcp" + to_port = 22 +} + + +resource aws_vpc_security_group_egress_rule allow_all_traffic { + security_group_id = aws_security_group.my_security_group.id + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "-1" # semantically equivalent to all ports +} + + +# EC2 instance + + resource aws_instance my_instance { + ## getting dynamic instances with key & values + for_each = var.instances + ami=each.value.ami + instance_type = each.value.instance_type + key_name = aws_key_pair.my_key_pair.key_name + + vpc_security_group_ids = [ + aws_security_group.my_security_group.id + ] + root_block_device { + volume_size = 10 + volume_type = "gp3" + } + +/* + count = 3 + ami = "ami-0d76b909de1a0595d" # OS AMI ID + + instance_type = "t3.micro" # Instance Type + + key_name = aws_key_pair.my_key_pair.key_name # Key pair + + vpc_security_group_ids = [aws_security_group.my_security_group.id] # VPC & Security Group + + # root storage (EBS) + root_block_device { + volume_size = 10 + volume_type = "gp3" + } + + tags = { + Name = "terra-automate-server" + } */ + tags={ + Name= each.key + OS_family=each.value.os_family + } +} \ No newline at end of file diff --git a/2026/day-68/terraform/generate_hosts.tf b/2026/day-68/terraform/generate_hosts.tf new file mode 100644 index 0000000000..a240a15812 --- /dev/null +++ b/2026/day-68/terraform/generate_hosts.tf @@ -0,0 +1,11 @@ +#TODO +/* resource "local_file" "my_host_file" { +filename= "hosts" + content={ + for name, instance in aws_instance.my_instance: name=> { + public_ip=instance.public_ip + user=var.instances[name].user + + } +} +} */ diff --git a/2026/day-68/terraform/outputs.tf b/2026/day-68/terraform/outputs.tf new file mode 100644 index 0000000000..0f05d7b3f9 --- /dev/null +++ b/2026/day-68/terraform/outputs.tf @@ -0,0 +1,11 @@ +output "instance_details" { + description = "Public IPs and SSH users for each instance" + value = { + for name, instance in aws_instance.my_instance : name => { + public_ip = instance.public_ip + public_dns = instance.public_dns + ssh_user = var.instances[name].user + os_family = var.instances[name].os_family + } + } +} \ No newline at end of file diff --git a/2026/day-68/terraform/providers.tf b/2026/day-68/terraform/providers.tf new file mode 100644 index 0000000000..c0583a7860 --- /dev/null +++ b/2026/day-68/terraform/providers.tf @@ -0,0 +1,5 @@ +provider "aws" { + + region="us-west-2" + +} \ No newline at end of file diff --git a/2026/day-68/terraform/terraform.tf b/2026/day-68/terraform/terraform.tf new file mode 100644 index 0000000000..0cedae8f9b --- /dev/null +++ b/2026/day-68/terraform/terraform.tf @@ -0,0 +1,11 @@ +terraform { + + required_providers { + aws = { + source = "hashicorp/aws" + version = "6.39.0" + } + } +} + + diff --git a/2026/day-68/terraform/terraform.tfstate b/2026/day-68/terraform/terraform.tfstate new file mode 100644 index 0000000000..f37751c9ad --- /dev/null +++ b/2026/day-68/terraform/terraform.tfstate @@ -0,0 +1,960 @@ +{ + "version": 4, + "terraform_version": "1.14.8", + "serial": 102, + "lineage": "81ca0427-a01c-ce4d-8a82-11a1de99a847", + "outputs": { + "instance_details": { + "value": { + "app-server": { + "os_family": "redhat", + "public_dns": "ec2-34-221-61-140.us-west-2.compute.amazonaws.com", + "public_ip": "34.221.61.140", + "ssh_user": "ec2-user" + }, + "control-node": { + "os_family": "ubuntu", + "public_dns": "ec2-35-93-211-243.us-west-2.compute.amazonaws.com", + "public_ip": "35.93.211.243", + "ssh_user": "ubuntu" + }, + "db-server": { + "os_family": "redhat", + "public_dns": "ec2-34-210-79-56.us-west-2.compute.amazonaws.com", + "public_ip": "34.210.79.56", + "ssh_user": "ec2-user" + }, + "web-server": { + "os_family": "amazon", + "public_dns": "ec2-44-252-105-156.us-west-2.compute.amazonaws.com", + "public_ip": "44.252.105.156", + "ssh_user": "ec2-user" + } + }, + "type": [ + "object", + { + "app-server": [ + "object", + { + "os_family": "string", + "public_dns": "string", + "public_ip": "string", + "ssh_user": "string" + } + ], + "control-node": [ + "object", + { + "os_family": "string", + "public_dns": "string", + "public_ip": "string", + "ssh_user": "string" + } + ], + "db-server": [ + "object", + { + "os_family": "string", + "public_dns": "string", + "public_ip": "string", + "ssh_user": "string" + } + ], + "web-server": [ + "object", + { + "os_family": "string", + "public_dns": "string", + "public_ip": "string", + "ssh_user": "string" + } + ] + } + ] + } + }, + "resources": [ + { + "mode": "managed", + "type": "aws_default_vpc", + "name": "default", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:vpc/vpc-0cbf157632debbde9", + "assign_generated_ipv6_cidr_block": false, + "cidr_block": "172.31.0.0/16", + "default_network_acl_id": "acl-050afbafd1743c46b", + "default_route_table_id": "rtb-08901d1c15584c3bb", + "default_security_group_id": "sg-0589da029675a1a17", + "dhcp_options_id": "dopt-036434adc6558e467", + "enable_dns_hostnames": true, + "enable_dns_support": true, + "enable_network_address_usage_metrics": false, + "existing_default_vpc": true, + "force_destroy": false, + "id": "vpc-0cbf157632debbde9", + "instance_tenancy": "default", + "ipv6_association_id": "", + "ipv6_cidr_block": "", + "ipv6_cidr_block_network_border_group": "", + "ipv6_ipam_pool_id": "", + "ipv6_netmask_length": 0, + "main_route_table_id": "rtb-08901d1c15584c3bb", + "owner_id": "822923364368", + "region": "us-west-2", + "tags": {}, + "tags_all": {} + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + } + ] + }, + { + "mode": "managed", + "type": "aws_instance", + "name": "my_instance", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "index_key": "app-server", + "schema_version": 2, + "attributes": { + "ami": "ami-04c7815cd1d6c8fa4", + "arn": "arn:aws:ec2:us-west-2:822923364368:instance/i-0727ad3c7a4ba2e27", + "associate_public_ip_address": true, + "availability_zone": "us-west-2a", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 2 + } + ], + "credit_specification": [ + { + "cpu_credits": "unlimited" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0727ad3c7a4ba2e27", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t3.micro", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "ansible-terra-key", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 1, + "http_tokens": "optional", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0f4e54d12cc57aff9" + } + ], + "primary_network_interface_id": "eni-0f4e54d12cc57aff9", + "private_dns": "ip-172-31-42-176.us-west-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "172.31.42.176", + "public_dns": "ec2-34-221-61-140.us-west-2.compute.amazonaws.com", + "public_ip": "34.221.61.140", + "region": "us-west-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/sda1", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": null, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-0a9df64e9db7fe61b", + "volume_size": 10, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [ + "terra-security-group" + ], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-011d9992f679688e5", + "tags": { + "Name": "app-server", + "OS_family": "redhat" + }, + "tags_all": { + "Name": "app-server", + "OS_family": "redhat" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0a2386ce249a576a1" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "i-0727ad3c7a4ba2e27", + "region": "us-west-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_default_vpc.default", + "aws_key_pair.my_key_pair", + "aws_security_group.my_security_group" + ] + }, + { + "index_key": "control-node", + "schema_version": 2, + "attributes": { + "ami": "ami-0d76b909de1a0595d", + "arn": "arn:aws:ec2:us-west-2:822923364368:instance/i-0e10bdd616df7b0d0", + "associate_public_ip_address": true, + "availability_zone": "us-west-2a", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 2 + } + ], + "credit_specification": [ + { + "cpu_credits": "unlimited" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0e10bdd616df7b0d0", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t3.micro", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "ansible-terra-key", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-048dcec87cecd0073" + } + ], + "primary_network_interface_id": "eni-048dcec87cecd0073", + "private_dns": "ip-172-31-35-114.us-west-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "172.31.35.114", + "public_dns": "ec2-35-93-211-243.us-west-2.compute.amazonaws.com", + "public_ip": "35.93.211.243", + "region": "us-west-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/sda1", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": null, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-0f85d82afc590d4fe", + "volume_size": 10, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [ + "terra-security-group" + ], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-011d9992f679688e5", + "tags": { + "Name": "control-node", + "OS_family": "ubuntu" + }, + "tags_all": { + "Name": "control-node", + "OS_family": "ubuntu" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0a2386ce249a576a1" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "i-0e10bdd616df7b0d0", + "region": "us-west-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_default_vpc.default", + "aws_key_pair.my_key_pair", + "aws_security_group.my_security_group" + ] + }, + { + "index_key": "db-server", + "schema_version": 2, + "attributes": { + "ami": "ami-04c7815cd1d6c8fa4", + "arn": "arn:aws:ec2:us-west-2:822923364368:instance/i-0c51bd43bb629d990", + "associate_public_ip_address": true, + "availability_zone": "us-west-2a", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 2 + } + ], + "credit_specification": [ + { + "cpu_credits": "unlimited" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0c51bd43bb629d990", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t3.micro", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "ansible-terra-key", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 1, + "http_tokens": "optional", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-08b271a35565af505" + } + ], + "primary_network_interface_id": "eni-08b271a35565af505", + "private_dns": "ip-172-31-47-84.us-west-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "172.31.47.84", + "public_dns": "ec2-34-210-79-56.us-west-2.compute.amazonaws.com", + "public_ip": "34.210.79.56", + "region": "us-west-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/sda1", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": null, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-0d0321ce84f04edd0", + "volume_size": 10, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [ + "terra-security-group" + ], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-011d9992f679688e5", + "tags": { + "Name": "db-server", + "OS_family": "redhat" + }, + "tags_all": { + "Name": "db-server", + "OS_family": "redhat" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0a2386ce249a576a1" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "i-0c51bd43bb629d990", + "region": "us-west-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_default_vpc.default", + "aws_key_pair.my_key_pair", + "aws_security_group.my_security_group" + ] + }, + { + "index_key": "web-server", + "schema_version": 2, + "attributes": { + "ami": "ami-043ab4148b7bb33e9", + "arn": "arn:aws:ec2:us-west-2:822923364368:instance/i-0cb11e3966c5313a6", + "associate_public_ip_address": true, + "availability_zone": "us-west-2a", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 2 + } + ], + "credit_specification": [ + { + "cpu_credits": "unlimited" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0cb11e3966c5313a6", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t3.micro", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "ansible-terra-key", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0ec4f1c8839c2626e" + } + ], + "primary_network_interface_id": "eni-0ec4f1c8839c2626e", + "private_dns": "ip-172-31-35-209.us-west-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "172.31.35.209", + "public_dns": "ec2-44-252-105-156.us-west-2.compute.amazonaws.com", + "public_ip": "44.252.105.156", + "region": "us-west-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": null, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-071d2fa3251d30159", + "volume_size": 10, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [ + "terra-security-group" + ], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-011d9992f679688e5", + "tags": { + "Name": "web-server", + "OS_family": "amazon" + }, + "tags_all": { + "Name": "web-server", + "OS_family": "amazon" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0a2386ce249a576a1" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "i-0cb11e3966c5313a6", + "region": "us-west-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_default_vpc.default", + "aws_key_pair.my_key_pair", + "aws_security_group.my_security_group" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_key_pair", + "name": "my_key_pair", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:key-pair/ansible-terra-key", + "fingerprint": "OhCgiKyqLkRx8Fr5sqmlfuiSv1rsCrBygrM4jt6yNkA=", + "id": "ansible-terra-key", + "key_name": "ansible-terra-key", + "key_name_prefix": "", + "key_pair_id": "key-0d4354db8c1de8d59", + "key_type": "ed25519", + "public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHOzqcmiIw4SZMxvdAo1UMIp1UsoDcfMp+c4x6BiC4k3 priyanka@Priyankas-MacBook-Air.local", + "region": "us-west-2", + "tags": null, + "tags_all": {} + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + } + ] + }, + { + "mode": "managed", + "type": "aws_security_group", + "name": "my_security_group", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:security-group/sg-0a2386ce249a576a1", + "description": "this is Inbound and outbound rules for your instance Security group", + "egress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "", + "from_port": 0, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "-1", + "security_groups": [], + "self": false, + "to_port": 0 + } + ], + "id": "sg-0a2386ce249a576a1", + "ingress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "", + "from_port": 22, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 22 + }, + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "", + "from_port": 443, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 443 + }, + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "", + "from_port": 80, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 80 + } + ], + "name": "terra-security-group", + "name_prefix": "", + "owner_id": "822923364368", + "region": "us-west-2", + "revoke_rules_on_delete": false, + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0cbf157632debbde9" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "sg-0a2386ce249a576a1", + "region": "us-west-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6OTAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "aws_default_vpc.default" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_vpc_security_group_egress_rule", + "name": "allow_all_traffic", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:security-group-rule/sgr-01c07a4e05ff9dd98", + "cidr_ipv4": "0.0.0.0/0", + "cidr_ipv6": null, + "description": null, + "from_port": null, + "id": "sgr-01c07a4e05ff9dd98", + "ip_protocol": "-1", + "prefix_list_id": null, + "referenced_security_group_id": null, + "region": "us-west-2", + "security_group_id": "sg-0a2386ce249a576a1", + "security_group_rule_id": "sgr-01c07a4e05ff9dd98", + "tags": null, + "tags_all": {}, + "to_port": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "sgr-01c07a4e05ff9dd98", + "region": "us-west-2" + }, + "dependencies": [ + "aws_default_vpc.default", + "aws_security_group.my_security_group" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_vpc_security_group_ingress_rule", + "name": "allow_http", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:security-group-rule/sgr-05a12ccaa52e03a1c", + "cidr_ipv4": "0.0.0.0/0", + "cidr_ipv6": null, + "description": null, + "from_port": 80, + "id": "sgr-05a12ccaa52e03a1c", + "ip_protocol": "tcp", + "prefix_list_id": null, + "referenced_security_group_id": null, + "region": "us-west-2", + "security_group_id": "sg-0a2386ce249a576a1", + "security_group_rule_id": "sgr-05a12ccaa52e03a1c", + "tags": null, + "tags_all": {}, + "to_port": 80 + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "sgr-05a12ccaa52e03a1c", + "region": "us-west-2" + }, + "dependencies": [ + "aws_default_vpc.default", + "aws_security_group.my_security_group" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_vpc_security_group_ingress_rule", + "name": "allow_ssh", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:security-group-rule/sgr-0d12aad10c53974a3", + "cidr_ipv4": "0.0.0.0/0", + "cidr_ipv6": null, + "description": null, + "from_port": 22, + "id": "sgr-0d12aad10c53974a3", + "ip_protocol": "tcp", + "prefix_list_id": null, + "referenced_security_group_id": null, + "region": "us-west-2", + "security_group_id": "sg-0a2386ce249a576a1", + "security_group_rule_id": "sgr-0d12aad10c53974a3", + "tags": null, + "tags_all": {}, + "to_port": 22 + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "sgr-0d12aad10c53974a3", + "region": "us-west-2" + }, + "dependencies": [ + "aws_default_vpc.default", + "aws_security_group.my_security_group" + ] + } + ] + } + ], + "check_results": null +} diff --git a/2026/day-68/terraform/terraform.tfstate.backup b/2026/day-68/terraform/terraform.tfstate.backup new file mode 100644 index 0000000000..8767104ce8 --- /dev/null +++ b/2026/day-68/terraform/terraform.tfstate.backup @@ -0,0 +1,730 @@ +{ + "version": 4, + "terraform_version": "1.14.8", + "serial": 95, + "lineage": "81ca0427-a01c-ce4d-8a82-11a1de99a847", + "outputs": { + "public_ip": { + "value": { + "control-node": { + "public_ip": "34.210.72.48", + "user": "ubuntu" + }, + "worker-amazon": { + "public_ip": "35.91.50.51", + "user": "ec2-user" + }, + "worker-redhat": { + "public_ip": "44.250.68.8", + "user": "ec2-user" + } + }, + "type": [ + "object", + { + "control-node": [ + "object", + { + "public_ip": "string", + "user": "string" + } + ], + "worker-amazon": [ + "object", + { + "public_ip": "string", + "user": "string" + } + ], + "worker-redhat": [ + "object", + { + "public_ip": "string", + "user": "string" + } + ] + } + ] + } + }, + "resources": [ + { + "mode": "managed", + "type": "aws_default_vpc", + "name": "default", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:vpc/vpc-0cbf157632debbde9", + "assign_generated_ipv6_cidr_block": false, + "cidr_block": "172.31.0.0/16", + "default_network_acl_id": "acl-050afbafd1743c46b", + "default_route_table_id": "rtb-08901d1c15584c3bb", + "default_security_group_id": "sg-0589da029675a1a17", + "dhcp_options_id": "dopt-036434adc6558e467", + "enable_dns_hostnames": true, + "enable_dns_support": true, + "enable_network_address_usage_metrics": false, + "existing_default_vpc": true, + "force_destroy": false, + "id": "vpc-0cbf157632debbde9", + "instance_tenancy": "default", + "ipv6_association_id": "", + "ipv6_cidr_block": "", + "ipv6_cidr_block_network_border_group": "", + "ipv6_ipam_pool_id": "", + "ipv6_netmask_length": 0, + "main_route_table_id": "rtb-08901d1c15584c3bb", + "owner_id": "822923364368", + "region": "us-west-2", + "tags": null, + "tags_all": {} + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + } + ] + }, + { + "mode": "managed", + "type": "aws_instance", + "name": "my_instance", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "index_key": "control-node", + "schema_version": 2, + "attributes": { + "ami": "ami-0d76b909de1a0595d", + "arn": "arn:aws:ec2:us-west-2:822923364368:instance/i-0bb6ffcfcd3058c3f", + "associate_public_ip_address": true, + "availability_zone": "us-west-2a", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 2 + } + ], + "credit_specification": [ + { + "cpu_credits": "unlimited" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0bb6ffcfcd3058c3f", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t3.micro", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "terra-automate-key", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0043de5de9ce522b5" + } + ], + "primary_network_interface_id": "eni-0043de5de9ce522b5", + "private_dns": "ip-172-31-36-10.us-west-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "172.31.36.10", + "public_dns": "ec2-34-210-72-48.us-west-2.compute.amazonaws.com", + "public_ip": "34.210.72.48", + "region": "us-west-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/sda1", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": null, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-08690b2d328c03247", + "volume_size": 10, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [ + "terra-security-group" + ], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-011d9992f679688e5", + "tags": { + "Name": "control-node", + "OS_family": "ubuntu" + }, + "tags_all": { + "Name": "control-node", + "OS_family": "ubuntu" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0a2386ce249a576a1" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "i-0bb6ffcfcd3058c3f", + "region": "us-west-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_default_vpc.default", + "aws_key_pair.my_key_pair", + "aws_security_group.my_security_group" + ] + }, + { + "index_key": "worker-amazon", + "schema_version": 2, + "attributes": { + "ami": "ami-043ab4148b7bb33e9", + "arn": "arn:aws:ec2:us-west-2:822923364368:instance/i-0908934e26a3145d8", + "associate_public_ip_address": true, + "availability_zone": "us-west-2a", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 2 + } + ], + "credit_specification": [ + { + "cpu_credits": "unlimited" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0908934e26a3145d8", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t3.micro", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "terra-automate-key", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0387122a4fb139053" + } + ], + "primary_network_interface_id": "eni-0387122a4fb139053", + "private_dns": "ip-172-31-44-92.us-west-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "172.31.44.92", + "public_dns": "ec2-35-91-50-51.us-west-2.compute.amazonaws.com", + "public_ip": "35.91.50.51", + "region": "us-west-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": null, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-01535695e4ba08081", + "volume_size": 10, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [ + "terra-security-group" + ], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-011d9992f679688e5", + "tags": { + "Name": "worker-amazon", + "OS_family": "amazon" + }, + "tags_all": { + "Name": "worker-amazon", + "OS_family": "amazon" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0a2386ce249a576a1" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "i-0908934e26a3145d8", + "region": "us-west-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_default_vpc.default", + "aws_key_pair.my_key_pair", + "aws_security_group.my_security_group" + ] + }, + { + "index_key": "worker-redhat", + "schema_version": 2, + "attributes": { + "ami": "ami-04c7815cd1d6c8fa4", + "arn": "arn:aws:ec2:us-west-2:822923364368:instance/i-07ee16e48e3002075", + "associate_public_ip_address": true, + "availability_zone": "us-west-2a", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 2 + } + ], + "credit_specification": [ + { + "cpu_credits": "unlimited" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-07ee16e48e3002075", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t3.micro", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "terra-automate-key", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 1, + "http_tokens": "optional", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-04c08f15c9efe0390" + } + ], + "primary_network_interface_id": "eni-04c08f15c9efe0390", + "private_dns": "ip-172-31-44-100.us-west-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "172.31.44.100", + "public_dns": "ec2-44-250-68-8.us-west-2.compute.amazonaws.com", + "public_ip": "44.250.68.8", + "region": "us-west-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/sda1", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": null, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-08388cab68c9f5c07", + "volume_size": 10, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [ + "terra-security-group" + ], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-011d9992f679688e5", + "tags": { + "Name": "worker-redhat", + "OS_family": "redhat" + }, + "tags_all": { + "Name": "worker-redhat", + "OS_family": "redhat" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0a2386ce249a576a1" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "i-07ee16e48e3002075", + "region": "us-west-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_default_vpc.default", + "aws_key_pair.my_key_pair", + "aws_security_group.my_security_group" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_key_pair", + "name": "my_key_pair", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:key-pair/terra-automate-key", + "fingerprint": "ZqRpP1AKvybE60rJJjVYupG0JR9WQ95fFgHYUUD2jSI=", + "id": "terra-automate-key", + "key_name": "terra-automate-key", + "key_name_prefix": "", + "key_pair_id": "key-0bc8c3618dd15240d", + "key_type": "ed25519", + "public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFT2C2YpF5D3+oI6x77CUfc3xSUZY/2ol9mKWPBHjLN2 priyanka@Priyankas-MacBook-Air.local", + "region": "us-west-2", + "tags": null, + "tags_all": {} + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + } + ] + }, + { + "mode": "managed", + "type": "aws_security_group", + "name": "my_security_group", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:security-group/sg-0a2386ce249a576a1", + "description": "this is Inbound and outbound rules for your instance Security group", + "egress": [], + "id": "sg-0a2386ce249a576a1", + "ingress": [], + "name": "terra-security-group", + "name_prefix": "", + "owner_id": "822923364368", + "region": "us-west-2", + "revoke_rules_on_delete": false, + "tags": null, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0cbf157632debbde9" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "sg-0a2386ce249a576a1", + "region": "us-west-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6OTAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "aws_default_vpc.default" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_vpc_security_group_egress_rule", + "name": "allow_all_traffic", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:security-group-rule/sgr-01c07a4e05ff9dd98", + "cidr_ipv4": "0.0.0.0/0", + "cidr_ipv6": null, + "description": null, + "from_port": null, + "id": "sgr-01c07a4e05ff9dd98", + "ip_protocol": "-1", + "prefix_list_id": null, + "referenced_security_group_id": null, + "region": "us-west-2", + "security_group_id": "sg-0a2386ce249a576a1", + "security_group_rule_id": "sgr-01c07a4e05ff9dd98", + "tags": null, + "tags_all": {}, + "to_port": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "sgr-01c07a4e05ff9dd98", + "region": "us-west-2" + }, + "dependencies": [ + "aws_default_vpc.default", + "aws_security_group.my_security_group" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_vpc_security_group_ingress_rule", + "name": "allow_http", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:security-group-rule/sgr-05a12ccaa52e03a1c", + "cidr_ipv4": "0.0.0.0/0", + "cidr_ipv6": null, + "description": null, + "from_port": 80, + "id": "sgr-05a12ccaa52e03a1c", + "ip_protocol": "tcp", + "prefix_list_id": null, + "referenced_security_group_id": null, + "region": "us-west-2", + "security_group_id": "sg-0a2386ce249a576a1", + "security_group_rule_id": "sgr-05a12ccaa52e03a1c", + "tags": null, + "tags_all": {}, + "to_port": 80 + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "sgr-05a12ccaa52e03a1c", + "region": "us-west-2" + }, + "dependencies": [ + "aws_default_vpc.default", + "aws_security_group.my_security_group" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_vpc_security_group_ingress_rule", + "name": "allow_ssh", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-west-2:822923364368:security-group-rule/sgr-0d12aad10c53974a3", + "cidr_ipv4": "0.0.0.0/0", + "cidr_ipv6": null, + "description": null, + "from_port": 22, + "id": "sgr-0d12aad10c53974a3", + "ip_protocol": "tcp", + "prefix_list_id": null, + "referenced_security_group_id": null, + "region": "us-west-2", + "security_group_id": "sg-0a2386ce249a576a1", + "security_group_rule_id": "sgr-0d12aad10c53974a3", + "tags": null, + "tags_all": {}, + "to_port": 22 + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "822923364368", + "id": "sgr-0d12aad10c53974a3", + "region": "us-west-2" + }, + "dependencies": [ + "aws_default_vpc.default", + "aws_security_group.my_security_group" + ] + } + ] + } + ], + "check_results": null +} diff --git a/2026/day-68/terraform/variables.tf b/2026/day-68/terraform/variables.tf new file mode 100644 index 0000000000..d5a0cddd69 --- /dev/null +++ b/2026/day-68/terraform/variables.tf @@ -0,0 +1,47 @@ +variable "aws_region" { + description = "AWS region where resources will be provisioned" + type = string + default = "us-west-2" +} + +variable "instances" { + description = "Map of instance names to AMI IDs, SSH users, and OS family" + + type = map(object({ + ami = string + user = string + os_family = string + instance_type = string + })) + + # default value for instances + default = { + + "control-node" = { + ami = "ami-0d76b909de1a0595d" # Ubuntu Server 24.04 LTS + user = "ubuntu" + os_family = "ubuntu" + instance_type = "t3.micro" + } + + "web-server" = { + ami = "ami-043ab4148b7bb33e9" # Amazon Linux + user = "ec2-user" + os_family = "amazon" + instance_type = "t3.micro" + } + + "app-server" = { + ami = "ami-04c7815cd1d6c8fa4" # RHEL 9 + user = "ec2-user" + os_family = "redhat" + instance_type = "t3.micro" + } + "db-server" = { + ami = "ami-04c7815cd1d6c8fa4" # RHEL 9 + user = "ec2-user" + os_family = "redhat" + instance_type = "t3.micro" + } + } +} diff --git a/2026/day-69/2026/day-69/day-69-playbooks.md b/2026/day-69/2026/day-69/day-69-playbooks.md new file mode 100644 index 0000000000..3a25f2f2f8 --- /dev/null +++ b/2026/day-69/2026/day-69/day-69-playbooks.md @@ -0,0 +1,65 @@ +### Day 69 -- Ansible Playbooks and Modules +#### Challenge Tasks +##### Task 1: Your First Playbook + +Create install-nginx.yml: +image + +(Use apt instead of yum if your instances run Ubuntu) + +ansible-playbook -i ../inventory.ini install-nginx.yml + +image + +Verify: Curl the web server's public IP. Do you see your custom page + +image +
    + +#### Task 2: Understand the Playbook Structure +Open your playbook and annotate each part in your notes: +--- # YAML document start +- name: Play name # PLAY -- targets a group of hosts + hosts: web # Which inventory group to run on + become: true # Run tasks as root (sudo) + tasks: # List of TASKS in this play + +Answer: + +What is the difference between a play and a task? + +A play define: + +Which hosts to target +What roles/tasks to apply +A task define + +Single unit of work +Calls one module (like apt, copy, service) +It’s a high-level mapping between hosts and work +Can you have multiple plays in one playbook? + +Yes, Each play: Targets different host groups and Runs independently in sequence +What does become: true do at the play level vs the task level? + +play level Applies to ALL tasks in the play + +task level Applies only to that task + +What happens if a task fails -- do remaining tasks still run? + +Default behavior: +Execution stops for that host + +tasks: + - name: Task 1 (fails) + - name: Task 2 (won’t run) + +
    +### Task 3: Learn the Essential Modules +Practice each of these modules by writing a playbook called essential-modules.yml with multiple tasks: +image +image + + + diff --git a/2026/day-87/1st_response.png b/2026/day-87/1st_response.png new file mode 100644 index 0000000000..944fef7fc7 Binary files /dev/null and b/2026/day-87/1st_response.png differ diff --git a/2026/day-87/2nd_response.png b/2026/day-87/2nd_response.png new file mode 100644 index 0000000000..904ac87f03 Binary files /dev/null and b/2026/day-87/2nd_response.png differ diff --git a/2026/day-87/3rd_response.png b/2026/day-87/3rd_response.png new file mode 100644 index 0000000000..a38be82644 Binary files /dev/null and b/2026/day-87/3rd_response.png differ diff --git a/2026/day-87/day-87-agentic-ai-intro.md b/2026/day-87/day-87-agentic-ai-intro.md new file mode 100644 index 0000000000..ce286161f3 --- /dev/null +++ b/2026/day-87/day-87-agentic-ai-intro.md @@ -0,0 +1,410 @@ +# Day 87 -- Introduction to Agentic AI for DevOps +--- + +## Challenge Tasks + +### Task 1: Understand Agentic AI for DevOps + +1. **What is an AI agent?** + - An LLM (Large Language Model) that can use **tools** to interact with the real world + - Unlike a chatbot that only generates text, an agent can run commands, read files, call APIs + - The LLM decides which tool to use, with what arguments, based on the user's question + +2. **Why agents for DevOps?** + - DevOps is tool-heavy: `docker`, `kubectl`, `terraform`, `gh`, `ansible` -- all CLI-based + - An agent wraps these CLIs as tools and lets the LLM reason about their output + - Example: "Why is my pod crashing?" -> agent calls `kubectl get pods`, sees `CrashLoopBackOff`, calls `kubectl describe pod`, reads the events, explains the root cause + +3. **The ReAct pattern** (Reason + Act): + ``` + User: "Why is broken-app crashing?" + + Agent THINKS: I should check which containers are running + Agent ACTS: calls list_containers() + Agent OBSERVES: broken-app is in "Restarting" state + + Agent THINKS: I should check the logs + Agent ACTS: calls get_logs("broken-app") + Agent OBSERVES: "exit code 1" after "app starting..." + + Agent THINKS: The container exits immediately after starting + Agent ANSWERS: "The container crashes because the entrypoint + command exits with code 1 after 2 seconds..." + ``` + +4. **Key components:** + - **LLM** -- the brain (Ollama/Gemma 4 locally, or Claude/GPT for production) + - **Tools** -- Python functions that wrap CLI commands (the hands) + - **Agent framework** -- LangChain's `create_react_agent` orchestrates the reasoning loop + - **MCP (Model Context Protocol)** -- a standard for exposing tools to any AI client (Day 88) + +--- + +### Task 2: Set Up the Environment +Clone the reference repository: +```bash +git clone https://github.com/TrainWithShubham/agentic-ai-for-devops.git +cd agentic-ai-for-devops +``` + +**Install Ollama** (local LLM runtime -- free, no API keys): +```bash +# macOS +brew install ollama + +# Linux +curl -fsSL https://ollama.com/install.sh | sh +``` + +![image](images/ollama_install.png) + +Start Ollama and pull the Gemma 4 model: +```bash +ollama serve & +ollama pull gemma4 +``` + +![image](images/pull_gemma4.png) + +Verify: +```bash +ollama list +# Should show gemma4 in the list +``` +**Set up Python environment:** +```bash +python3 -m venv .venv +source .venv/bin/activate + +pip install -r requirements.txt +``` +![image](images/verify.png) + +The `requirements.txt` installs: +- `ollama` -- Python client for Ollama +- `langchain` + `langchain-ollama` -- agent framework + Ollama integration +- `langgraph` -- graph-based agent execution (used by `create_react_agent`) +- `fastmcp` -- Model Context Protocol server framework +- `langchain-mcp-adapters` -- bridges MCP tools into LangChain + +**Run the pre-flight check:** +```bash +python3 module-0/verify_setup.py +``` + +You should see: +``` + [PASS] Python 3.10+ + [PASS] Docker + [PASS] kubectl + [PASS] Kind + [PASS] Ollama + gemma4 + + 5/5 -- you're ready for Day 1! +``` + +Fix any failures before proceeding. + +![image](images/pre-flight.png) +--- + +### Task 3: Build the Docker Error Explainer (Module 1) +This is the simplest possible LLM usage -- no agents, no tools. You paste a Docker error and the LLM explains it. + +Study `module-1/explainer.py`: +```python +import ollama + +SYSTEM_PROMPT = """You are a Docker expert. When given a Docker error, explain: +1. What went wrong (plain English) +2. Most likely cause +3. How to fix it (with commands) +Keep it short.""" + +# ... reads user input ... + +response = ollama.chat( + model="gemma4", + messages=[ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": error}, + ], + options={"temperature": 0.3}, +) +``` + +**Key concepts:** +- `system` prompt -- tells the LLM what persona to adopt and how to format responses +- `temperature: 0.3` -- low temperature = more deterministic output (good for technical answers) +- No tools, no agent loop -- just a single LLM call + +**Run it:** +```bash +python3 module-1/explainer.py +``` + +Paste one of these Docker errors: +``` +docker: Error response from daemon: Conflict. The container name "/myapp" is already in use. +``` + +Or: +``` +Error response from daemon: driver failed programming external connectivity on endpoint myapp: +Bind for 0.0.0.0:8080 failed: port is already allocated. +``` + +Or: +``` +Error response from daemon: pull access denied for mycompany/private-app, repository does not +exist or may require 'docker login'. +``` + +The LLM explains what went wrong and how to fix it -- no manual Googling needed. + +![image](images/1st_response.png) + + +![image](images/2nd_response.png) + + +![image](images/3rd_response.png) + +**Document:** How does the system prompt affect the quality of the response? Try changing it and see what happens. + +- The system prompt affects the quality by guiding the model on how to respond. +- Changing it changes the tone,structure and clarity of the answer. + +--- + +### Task 4: Build the Docker Troubleshooter Agent (Module 2) +Now the real thing -- an agent that autonomously uses tools to diagnose Docker issues. + +**First, create a broken container to diagnose:** +```bash +docker run -d --name broken-app nginx:alpine sh -c "echo 'app starting...' && sleep 2 && exit 1" +``` + +This container starts, prints "app starting...", waits 2 seconds, then crashes. Docker will keep restarting it (CrashLoopBackOff equivalent). + +**Study `module-2/agent.py`:** + +The agent has three tools: +```python +@tool +def list_containers() -> str: + """List all Docker containers (running and stopped).""" + result = subprocess.run(["docker", "ps", "-a"], capture_output=True, text=True) + return result.stdout or result.stderr + +@tool +def get_logs(container_name: str) -> str: + """Get the last 50 lines of logs from a Docker container.""" + result = subprocess.run( + ["docker", "logs", "--tail", "50", container_name], + capture_output=True, text=True, + ) + return result.stdout + result.stderr + +@tool +def inspect_container(container_name: str) -> str: + """Get detailed info about a Docker container (state, config, network).""" + result = subprocess.run( + ["docker", "inspect", container_name], + capture_output=True, text=True, + ) + return result.stdout or result.stderr +``` + +**How each tool works:** +- `@tool` decorator -- tells LangChain this function is available for the agent +- The docstring is critical -- the LLM reads it to decide when to use the tool +- `subprocess.run` -- executes the actual CLI command +- Returns stdout/stderr as a string for the LLM to read + +**The agent is created with:** +```python +llm = ChatOllama(model="gemma4", temperature=0) +tools = [list_containers, get_logs, inspect_container] +agent = create_react_agent(llm, tools) +``` + +`create_react_agent` builds the ReAct loop: the LLM reasons about the problem, picks a tool, calls it, reads the result, and repeats until it has an answer. + +**Run the agent:** +```bash +python3 module-2/agent.py +``` + +Ask it: +``` +> Why is broken-app crashing? +``` + +Watch the agent's reasoning: +1. It calls `list_containers()` -- sees broken-app in "Restarting" state +2. It calls `get_logs("broken-app")` -- sees "app starting..." then exit +3. It calls `inspect_container("broken-app")` -- sees exit code 1 +4. It answers: "The container crashes because the command exits with code 1..." + +**The LLM decided which tools to call and in what order.** You never told it to check logs -- it figured that out from the problem. + +![image](images/agent.png) + +Try more questions: +``` +> List all my running containers +> What image is broken-app using? +``` +![image](images/agent2.png) + +**Clean up:** +```bash +docker rm -f broken-app +``` + +--- + +### Task 5: Understand the Agent Architecture +Map out what you just built: + +``` +[User Question] + | + v +[LLM: Gemma 4 via Ollama] + | + | (ReAct: Reason what tool to use) + v +[Tool Selection] + | + +---> list_containers() --> docker ps -a + +---> get_logs() --> docker logs + +---> inspect_container() --> docker inspect + | + v +[Tool Output (text)] + | + v +[LLM reads output, reasons again] + | + | (repeat until answer is ready) + v +[Final Answer to User] +``` + +**Why this matters for DevOps:** +- The pattern is domain-agnostic. Replace Docker tools with Kubernetes tools, Terraform tools, or AWS CLI tools -- the architecture stays the same +- Tomorrow (Day 88) you will add Kubernetes tools to the same agent +- On Day 89, you will build a production-grade agent that automatically fixes broken pods + +**The tool pattern is always the same:** +```python +@tool +def my_tool(argument: str) -> str: + """Description the LLM reads to decide when to use this tool.""" + result = subprocess.run(["some-cli", "command", argument], capture_output=True, text=True) + return result.stdout or result.stderr +``` + +Any CLI command can become an agent tool. Any DevOps workflow can be automated this way. + +--- + +### Task 6: Experiment and Extend +Try adding a new tool to the agent. Edit `module-2/agent.py` and add: + +```python +@tool +def list_images() -> str: + """List all Docker images on this machine with their sizes.""" + result = subprocess.run(["docker", "images"], capture_output=True, text=True) + return result.stdout or result.stderr +``` + +Add it to the tools list: +```python +tools = [list_containers, get_logs, inspect_container, list_images] +``` + +Run the agent and ask: "What images do I have and how much space are they using?" + +The agent will call your new tool. + +![image](images/list_docker_images.png) + +**Try another:** Add a `restart_container` tool: +```python +@tool +def restart_container(container_name: str) -> str: + """Restart a Docker container.""" + result = subprocess.run(["docker", "restart", container_name], capture_output=True, text=True) + return result.stdout or result.stderr +``` + +Now ask: "broken-app keeps crashing, can you restart it?" + +**Think about the safety implications:** This tool can restart any container. In production, you would add guardrails (confirmation prompts, allowed container lists). You will learn about guardrails on Day 89. + +![image](images/cont_restart.png) + +--- + +**What are AI agents and how they differ from chatbots** + +- Chatbots mainly respond to user questions with text-based answers. +- AI agents can take actions using tools (like running commands, inspecting systems, or fixing issues). + +**The ReAct pattern explained with the broken-app example** + +`Thought (The Reasoning)` +- When you ask, "Why is broken-app crashing?", the agent doesn't just guess. It generates a "Thought." +- Agent's internal logic: "To find out why it's crashing, I need to check the container's status and logs." + +`Action (The Interaction)` +- The agent decides to use a tool. In this DevOps context, it likely called a function like docker_inspect() or docker_logs(). +- The Command: In the background, it executed commands to retrieve the container metadata. + +`Observation (The Evidence)` +- This is the data the agent receives back from the system. In your image, the "Observation" is the raw data showing: + - `Status: "exited"` + - `ExitCode: 1` + - `Cmd: sh -c "echo 'app starting...' && sleep 2 && exit 1"` + +**The agent architecture diagram** + +``` +[User Question] + | + v +[LLM: Gemma 4 via Ollama] + | + | (ReAct: Reason what tool to use) + v +[Tool Selection] + | + +---> list_containers() --> docker ps -a + +---> get_logs() --> docker logs + +---> inspect_container() --> docker inspect + | + v +[Tool Output (text)] + | + v +[LLM reads output, reasons again] + | + | (repeat until answer is ready) + v +[Final Answer to User] +``` + +**The tool you added and how the agent used it** +- A restart_container tool was added using docker restart and included in the tools list so the agent can use it. +- How the agent used it: + - When I asked to restart a container (e.g., broken-app) +- The agent called the restart_container tool to execute the restart command automatically +- It returned the result of the operation + +**System prompt and temperature explained** +- `System prompt` tells the LLM what role it should take and how to structure its responses. +- `Temperature` (e.g., 0.3) controls randomness; lower values make outputs more consistent and deterministic, which is better for technical answers. diff --git a/2026/day-87/images/1st_response.png b/2026/day-87/images/1st_response.png new file mode 100644 index 0000000000..944fef7fc7 Binary files /dev/null and b/2026/day-87/images/1st_response.png differ diff --git a/2026/day-87/images/2nd_response.png b/2026/day-87/images/2nd_response.png new file mode 100644 index 0000000000..904ac87f03 Binary files /dev/null and b/2026/day-87/images/2nd_response.png differ diff --git a/2026/day-87/images/3rd_response.png b/2026/day-87/images/3rd_response.png new file mode 100644 index 0000000000..a38be82644 Binary files /dev/null and b/2026/day-87/images/3rd_response.png differ diff --git a/2026/day-87/images/a b/2026/day-87/images/a new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/2026/day-87/images/a @@ -0,0 +1 @@ + diff --git a/2026/day-87/images/agent.png b/2026/day-87/images/agent.png new file mode 100644 index 0000000000..6516d6f60f Binary files /dev/null and b/2026/day-87/images/agent.png differ diff --git a/2026/day-87/images/pre-flight.png b/2026/day-87/images/pre-flight.png new file mode 100644 index 0000000000..5c2804eb1a Binary files /dev/null and b/2026/day-87/images/pre-flight.png differ diff --git a/2026/day-87/images/verify.png b/2026/day-87/images/verify.png new file mode 100644 index 0000000000..cedc050545 Binary files /dev/null and b/2026/day-87/images/verify.png differ