DevOps & Cloud
DevOps Engineer
A complete guide covering Linux, Git, CI/CD, Docker, Kubernetes, Terraform, Ansible, AWS, Monitoring, and SRE concepts.
What you will be asked about
How to prepare
- Go through the topic list above and mark every one you cannot explain for five minutes unprepared. Those are your gaps.
- Pair every concept with a story from your own work — interviewers probe depth, and depth comes from having actually done it.
- Do the DSA rounds anyway. Almost every role in this list still screens with coding.
- Prepare two projects you can whiteboard end to end, including what you would change now.
Also do
DevOps Engineer interview questions399
Linux & OS30
A Soft Link (symbolic link) is like a shortcut that points to the filename of the target; it has its own inode and becomes invalid if the original file is deleted. A Hard Link points directly to the inode (data) of the original file; it shares the same inode number and remains valid even if the original filename is removed. Hard links cannot span across different filesystems or be created for directories.
The boot process follows these stages: 1) BIOS/UEFI: Performs POST (Power-On Self-Test). 2) MBR/GPT: Locates the bootloader. 3) GRUB/Bootloader: Loads the kernel into memory. 4) Kernel: Initializes hardware and mounts the root filesystem. 5) Init/Systemd: The first process (PID 1) that starts all other system services. 6) Runlevel/Target: Reaches the final state (e.g., multi-user target).
An inode (Index Node) is a data structure on a Linux filesystem that stores metadata about a file, such as its size, owner, permissions, and location of data blocks on the disk. It does not store the filename (which is kept in the directory entry) or the actual file data. Every file/directory is uniquely identified by an inode number within its filesystem.
I use two primary commands: 'df -h' (Disk Free) to check the total space, used space, and availability on all mounted filesystems in human-readable format, and 'du -sh [dir]' (Disk Usage) to check the size of a specific directory or file. To find the largest files, I often use 'du -ah | sort -rh | head -n 10'.
/etc/passwd contains basic user account information (username, UID, GID, home directory, shell) and is world-readable. /etc/shadow contains the actual encrypted password hashes and password aging information (expiration, warning periods). /etc/shadow is only readable by the root user, which enhances security by preventing regular users from accessing password hashes for offline cracking.
Permissions are defined for Owner, Group, and Others using Read (4), Write (2), and Execute (1). chmod changes these permission bits (e.g., 'chmod 755' gives full access to owner). chown changes the ownership of a file to a different user, and chgrp specifically changes the group ownership.
Umask (User Mask) is a four-digit octal number used to determine the default permissions for newly created files and directories. It works by subtracting the mask from the system defaults (usually 666 for files and 777 for directories). For example, a umask of 022 results in files having 644 (rw-r--r--) and directories having 755 (rwxr-xr-x).
find searches the live filesystem based on attributes like name, size, or time (e.g., 'find / -name test.txt'). locate is faster as it searches a pre-built database (updated by updatedb). grep is used to search for specific text patterns *inside* files (e.g., 'grep -r "error" /var/log').
The kill command sends a signal to a specific process using its Process ID (PID) (e.g., 'kill 1234'). The killall command sends a signal to all processes matching a specific name (e.g., 'killall nginx'), which is useful for stopping multiple instances of a service at once.
SIGTERM (15): The default 'soft kill' signal that asks a process to shut down gracefully (it can catch the signal and cleanup). SIGKILL (9): A 'hard kill' that force-stops a process immediately (cannot be ignored). SIGHUP (1): Often used to tell a daemon to reload its configuration without stopping.
A daemon is a background process that runs independently of the user terminal. It usually starts during system boot and waits for specific events or requests to provide services (e.g., httpd for web requests, sshd for SSH connections). Most daemons end with the letter 'd'.
ps gives a static snapshot of current processes (e.g., 'ps aux'). top provides a real-time, dynamic view of system processes and resource usage. htop is an interactive, color-coded version of top that is more user-friendly and allows for easier searching and killing of processes.
service is an older command used for SysVinit systems. systemctl is the modern command for systemd-based Linux distributions. systemctl offers more advanced features like dependency management, parallel service startup, and detailed status reporting. It is now the industry standard.
1) Use 'top' or 'htop' to identify the process consuming the most CPU. 2) Check if it's a specific thread using 'top -H'. 3) Use 'strace' to see the system calls the process is making. 4) Check application logs to see if the process is stuck in a loop or handling heavy traffic. 5) Use 'uptime' to check load averages (1, 5, 15 mins).
1) Use 'free -m' to check total, used, and swap memory. 2) Use 'top' (press 'M' to sort by memory). 3) Look for 'OOM Killer' (Out of Memory) messages in /var/log/syslog or dmesg. 4) Identify processes with large 'Resident Set Size' (RSS). 5) Check for memory leaks in application-specific monitoring tools.
Swap is a portion of the hard drive designated as virtual memory. When the physical RAM is full, the Linux kernel moves inactive pages from RAM to swap space. While it prevents system crashes, using swap significantly slows down performance because disk I/O is much slower than RAM.
netstat is the traditional tool (e.g., 'netstat -tuln' for listening ports). ss (Socket Statistics) is the modern, faster replacement for netstat. lsof -i (List Open Files) can specifically show which process is using a certain port (e.g., 'lsof -i :80').
These are Security Modules for Linux that implement Mandatory Access Control (MAC). They restrict what actions processes can take based on security profiles, even if the process is running as root. SELinux (common in RHEL/CentOS) uses labels, while AppArmor (common in Ubuntu/Debian) uses file paths.
Using systemd commands: 'systemctl start [name]' to start, 'stop' to stop, 'restart' to bounce it, 'enable' to make it start automatically on boot, and 'status' to check if it's running and see recent logs.
Cron is a time-based job scheduler. I use 'crontab -e' to edit the cron table. The format is: `* * * * * command` representing Minute, Hour, Day of Month, Month, and Day of Week. For example, `0 2 * * * /backup.sh` runs a backup daily at 2:00 AM.
Cron assumes the system is running 24/7; if the computer is off when a task is scheduled, the task is skipped. Anacron is designed for systems that are not always on; it checks when the task was last run and executes it as soon as the system boots if it was missed.
Most logs are stored in /var/log/. I use 'tail -f' to follow live log updates (e.g., 'tail -f /var/log/nginx/error.log'), 'less' to scroll through large files, and 'grep' to search for specific error keywords within multiple log files.
journalctl is the command-line utility for querying and displaying logs from the systemd journal. Since systemd logs are stored in a binary format for performance, you must use journalctl (e.g., 'journalctl -u nginx' to see logs for a specific service) to read them.
I use tar for archiving multiple files (e.g., 'tar -cvf archive.tar files/'). For compression, I use gzip ('tar -czvf archive.tar.gz' - faster) or bzip2 ('tar -cjvf archive.tar.bz2' - better compression). To extract, I use 'tar -xzvf' for .gz files.
/bin: Essential user binaries (ls, cp). /sbin: Essential system binaries for root (fdisk, reboot). /usr/bin: Non-essential user binaries (python, git). /usr/sbin: Non-essential system binaries (tcpdump, nginx). In modern distros, these are often symlinked to /usr/bin and /usr/sbin.
I use the mount command to attach a device to a directory (e.g., 'mount /dev/sdb1 /mnt/data'). To make it permanent, I add an entry to /etc/fstab. To safely detach, I use umount (e.g., 'umount /mnt/data').
LVM is a device mapper framework that provides logical volume management for the Linux kernel. It allows for flexible disk management, such as resizing partitions on the fly, creating snapshots, and spanning a single filesystem across multiple physical disks.
1) ping to check connectivity. 2) traceroute to see where packets are dropping. 3) nslookup/dig to check DNS resolution. 4) telnet/nc to check if a specific port is open. 5) ip addr to check the local interface configuration.
SSH (Secure Shell) is a protocol for secure remote access. Security best practices: 1) Disable root login. 2) Change default port 22. 3) Use SSH keys instead of passwords. 4) Limit access to specific IPs using firewalls. 5) Set up Fail2Ban to block brute-force attempts.
1) Generate keys on local machine: `ssh-keygen`. 2) Copy public key to server: `ssh-copy-id user@server`. 3) This adds the public key to `~/.ssh/authorized_keys` on the server, allowing passwordless login.
Shell Scripting & Automation25
Shell scripting is the process of writing a series of commands in a file that the shell executor (like Bash) can run as a single program. In DevOps, it is essential for automating repetitive tasks like system backups, log rotation, environment setup, and integrating different tools in a CI/CD pipeline where a native plugin might not exist.
sh (Bourne Shell) is the original, basic shell with limited features. bash (Bourne Again Shell) is the most common Linux default, adding features like command history and tab completion. zsh (Z Shell) is an extended version of bash with even more user-friendly features like better themes (Oh-My-Zsh), advanced globbing, and superior auto-correction.
I use the chmod command: `chmod +x script.sh`. This adds the execute bit to the file permissions. After that, I can run it directly using `./script.sh` instead of calling the interpreter manually like `bash script.sh`.
The shebang is a special line at the very beginning of a script (e.g., `#!/bin/bash` or `#!/usr/bin/env python`) that tells the operating system which interpreter to use to execute the file. Without it, the system might try to run the script using the default shell of the current user, which can cause syntax errors.
Variables are used to store data for reuse. In shell, they are defined without spaces: `NAME='DevOps'`. To access the value, I use the `$` symbol: `echo $NAME`. Variables can also store the output of commands using command substitution: `CURRENT_DATE=$(date)`.
Single quotes (' ') preserve the literal value of every character within the quotes (no variable expansion). Double quotes (" ") allow for 'Parameter Expansion' (interpreting variables like `$VAR`) and 'Command Substitution' while still treating the string as a single unit.
Arguments are passed by typing them after the script name: `./script.sh arg1 arg2`. Inside the script, these are accessed using positional parameters like `$1`, `$2`, and so on. `$0` refers to the script name itself.
$0: Script name. $1-$9: Positional arguments. $#: Total number of arguments passed. $@: All arguments as separate quoted strings (preferred). $*: All arguments as a single string. $?: Exit status of the last command (0 for success, non-zero for failure).
The syntax follows: `if [ condition ]; then ... elif [ condition ]; then ... else ... fi`. Conditions are often wrapped in square brackets and check for things like string equality (`==`), numerical comparisons (`-eq`, `-gt`), or file existence (`-f`).
for: Iterates over a list (e.g., `for i in {1..5}; do ... done`). while: Runs as long as a condition is true. until: Runs until a condition becomes true. I use `for` loops most often for processing lists of files or server names.
`[ ]` is the older, standard POSIX test command. `[[ ]]` is a bash extension that is more powerful; it supports logical operators like `&&` and `||` natively, allows for pattern matching/regex with `=~`, and is generally safer because it handles empty variables better.
I use the read command: `read -p 'Enter your name: ' USERNAME`. The `-p` flag allows me to provide a prompt string. The input is then stored in the variable `USERNAME`.
Functions are blocks of code that can be reused within a script to avoid repetition. They are defined as `function_name() { ... }`. You call them by their name and can pass arguments to them, which the function accesses using `$1`, `$2`, etc., independent of the main script's arguments.
I check the exit status using `$?` after critical commands. For robust scripts, I use `if ! command; then echo 'Failed'; exit 1; fi`. I also use `trap` to catch signals and perform cleanup actions if a script is interrupted.
set -e: Exit immediately if a command fails (non-zero exit code). set -u: Exit if a script tries to use an undefined variable. set -x: Print each command before executing it (useful for debugging). Combined as `set -eux`, this is often called 'Bash Strict Mode'.
1> redirects standard output (stdout). 2> redirects standard error (stderr). I often use `command > file.txt 2>&1` to send both output and errors to the same file, or `&> file.txt` as a bash shortcut.
> is the redirection operator that overwrites the target file's existing content. >> is the redirection operator that appends the output to the end of the file, which is ideal for maintaining log files.
Piping uses the `|` symbol to take the standard output of one command and send it as the standard input to the next command. For example, `ps aux | grep nginx` takes the list of all processes and filters them to only show those containing 'nginx'.
grep: Used for searching text. sed (Stream Editor): Used for basic text transformation like find-and-replace (`sed 's/old/new/g'`). awk: A full programming language used for advanced text processing and report generation, especially for column-based data (`awk '{print $1}'`).
grep: Standard search using basic regular expressions. egrep (grep -E): Extended regex, allowing for `|`, `+`, and `?` without escaping. fgrep (grep -F): Fixed-string search, it doesn't interpret regex and is faster for searching literal strings.
jq is the industry-standard command-line tool for JSON processing. I use it to extract values (`jq '.key'`), iterate over arrays, or transform JSON structures directly in the pipeline, which is vital when working with cloud APIs (AWS/GCP).
1) Add `set -x` at the top to see the execution trace. 2) Use 'shellcheck', a linting tool that identifies syntax errors and bad practices. 3) Use `echo` statements to inspect variable values at different stages of the script.
Every command returns an exit code between 0 and 255. 0 always means success. Any other number indicates an error. In scripts, I use `exit 1` to manually stop the script and signal to the calling process (like Jenkins) that something went wrong.
I add an ampersand & to the end of a command (e.g., `./backup.sh &`). To keep it running after I disconnect from SSH, I use nohup (`nohup ./backup.sh &`) or run it inside a terminal multiplexer like screen or tmux.
I use `crontab -e` and add a line specifying the schedule and the script path. For example, `30 5 * * 1 /scripts/cleanup.sh` runs the cleanup script every Monday at 5:30 AM. I always use absolute paths in cron to avoid 'command not found' errors.
Git25
Git is a distributed version control system. It is critical in DevOps because it enables 'Infrastructure as Code' (IaC), allows for seamless collaboration through branching, provides a history of all changes (audit trail), and serves as the trigger for automated CI/CD pipelines.
Git is the local tool (the engine) used to track code changes. GitHub (or GitLab/Bitbucket) is a web-based hosting service (the garage) that stores Git repositories in the cloud and adds social/collaboration features like Pull Requests, Issue tracking, and CI/CD integrations.
The standard flow is: 1) Working Directory: Make changes to files. 2) Staging Area (Index): Use `git add` to mark changes to be committed. 3) Local Repository: Use `git commit` to save the snapshot. 4) Remote Repository: Use `git push` to share changes with the team.
git fetch only downloads the latest metadata and changes from the remote repository but does not change your local working files. git pull is a combination of `git fetch` followed by `git merge`; it downloads the data AND immediately tries to update your local code.
merge creates a new 'merge commit' that ties two branches together, preserving the exact history of when things happened. rebase moves the entire branch to begin on the tip of another branch, resulting in a cleaner, linear commit history without the extra merge commit.
I use rebase when I want to maintain a clean, linear project history. It is ideal for feature branches that haven't been shared with others yet. By rebasing, I avoid 'merge commits' that can clutter the history. However, I never rebase public branches as it rewrites history and can cause major issues for team members.
A merge conflict occurs when Git cannot automatically reconcile differences between two commits (usually when the same line in a file is changed differently). To resolve it, I open the conflicted file, look for the '<<<< HEAD' markers, manually choose the correct code, remove the markers, and then run `git add` and `git commit` to finalize the merge.
Cherry-picking is the act of choosing a specific commit from one branch and applying it to another. This is useful when I want to bring a specific bug fix from a development branch into the production branch without merging the entire branch which might contain unfinished features.
Git stash temporarily shelves (or 'stashes') changes I've made to my working directory so I can work on something else, and then come back and re-apply them later. It is perfect for when I'm mid-task and need to switch branches to fix an urgent bug without committing incomplete work.
git revert creates a new commit that 'undoes' the changes of a previous commit; it is safe for public history. git reset moves the branch pointer back to a previous commit, effectively 'deleting' the history after that point. Reset should generally only be used on local, private branches.
--soft: Keeps changes in the staging area. --mixed (default): Keeps changes in the working directory but unstages them. --hard: Completely discards all changes in both the staging area and working directory, returning the project to the exact state of the specified commit.
A branch is a lightweight movable pointer to a commit. A branching strategy (like GitFlow or Trunk-based) defines how a team uses branches to manage features, releases, and hotfixes to ensure code quality and avoid deployment bottlenecks.
GitFlow is a strict branching model that uses: 1) Main for production-ready code. 2) Develop for integration. 3) Feature branches for new work. 4) Release branches for preparation. 5) Hotfix branches for production bugs.
Trunk-based development is a strategy where all developers work on a single branch (the 'trunk' or 'main'). They commit small, frequent updates, which reduces merge friction and enables 'Continuous Integration.' It often relies on 'Feature Flags' to hide unfinished features from users.
A Pull Request (PR) is a way to propose changes to a repository. It allows team members to review the code, discuss potential improvements, and run automated tests before the changes are merged into the main codebase, serving as a critical gatekeeper for quality.
Git hooks are scripts that run automatically every time a particular event occurs in a Git repository (e.g., `pre-commit`, `post-merge`). I use them to automate linting, run unit tests before allowing a commit, or enforce commit message formats.
To undo the commit but keep the changes for editing, I use `git reset --soft HEAD~1`. To completely delete the last commit and all associated changes, I use `git reset --hard HEAD~1`.
A `.gitignore` file specifies intentionally untracked files that Git should ignore. I use it to prevent sensitive files (like `.env`), build artifacts (like `node_modules` or `dist/`), and OS-specific files from being accidentally committed to the repository.
To rename: `git branch -m <old_name> <new_name>`. To delete a local branch: `git branch -d <branch_name>`. To force delete an unmerged branch: `git branch -D <branch_name>`. To delete a remote branch: `git push origin --delete <branch_name>`.
Git bisect is a binary search tool used to find the specific commit that introduced a bug. I mark a 'good' commit and a 'bad' commit, and Git automatically checks out commits in between for me to test until the culprit is identified.
I use `git blame <file_name>`. This displays the file line-by-line, showing the commit hash, the author, and the timestamp of the last modification for every single line. It is invaluable for finding context on why a specific line was written.
A 'detached HEAD' occurs when I check out a specific commit hash or a tag instead of a branch. In this state, 'HEAD' points to a commit rather than a branch name. Any commits made here will not belong to any branch and can be easily lost if I switch back to a branch.
I use interactive rebase: `git rebase -i HEAD~<n>`. In the editor that opens, I change 'pick' to 'squash' (or 's') for the commits I want to combine into the previous one. This is standard practice before merging a feature branch to keep the main history clean.
A tag is a permanent marker pointing to a specific point in Git history. I use it primarily to mark release points (e.g., `v1.0.0`, `v2.1.0`). Unlike branches, tags do not change once created.
If I've made changes I want to keep, I create a new branch from that state: `git checkout -b <new_branch_name>`. This 'attaches' the HEAD to a new branch, and I can then merge it back into my main development line.
CI/CD30
CI/CD is a set of practices that automate the software delivery process. CI (Continuous Integration) involves automatically building and testing code on every commit. CD can mean Continuous Delivery (ready to deploy to prod at any time) or Continuous Deployment (automatically deploying every change to prod).
CI: Automated testing/building. Continuous Delivery: Automates the entire release process up to the 'Production' gate, which requires manual approval. Continuous Deployment: Fully automated through to production with no manual intervention.
Common tools include Jenkins (highly customizable), GitLab CI (integrated with repo), GitHub Actions (modern, cloud-native), and ArgoCD (GitOps for Kubernetes). The choice usually depends on the existing infrastructure and team preference.
Jenkins follows a Controller-Agent (Master-Slave) architecture. The Controller handles the UI, configuration, and scheduling. The Agents are separate machines or containers that perform the actual heavy lifting (building and testing) to ensure the Controller stays responsive.
A Jenkins pipeline is a suite of plugins that supports implementing and integrating continuous delivery pipelines into Jenkins. It allows you to define the entire build process as code (Pipeline-as-Code), which is more maintainable and version-controllable than manual UI jobs.
Declarative is modern, uses a structured syntax (`pipeline { ... }`), and is easier to write and read. Scripted uses Groovy code directly (`node { ... }`), offering maximum flexibility but with a steeper learning curve and higher complexity.
A Jenkinsfile is a text file that contains the definition of a Jenkins Pipeline and is checked into source control. This enables 'Pipeline-as-Code,' allowing the build process to be versioned and reviewed just like the application code.
I use the `parallel` keyword within a stage in a Declarative pipeline. This allows multiple steps (like running unit tests and integration tests) to run simultaneously on different agents, significantly reducing the total pipeline time.
Agents are the workhorses of Jenkins. They are external machines or containers that connect to the Jenkins Controller and execute the build jobs. By using agents, we can scale Jenkins horizontally and run builds in different environments (Linux, Windows, Docker).
1) Enable 'Project-based Matrix Authorization.' 2) Use 'Credentials Plugin' for secrets. 3) Disable the 'Groovy Script Console' for non-admins. 4) Use HTTPS. 5) Regularly update Jenkins and its plugins to patch vulnerabilities.
Jenkins Blue Ocean is a modern user interface for Jenkins that provides a more intuitive, visual representation of pipelines. It makes it easier to visualize the flow of the CI/CD process, identify bottlenecks, and diagnose failures through a simplified dashboard compared to the traditional 'classic' UI.
GitLab CI/CD is a built-in feature of GitLab that automates the building, testing, and deployment of code. It uses a single application for the entire DevOps lifecycle, meaning your repository, issue tracker, and CI/CD pipelines are all in one place, reducing the need for external integrations like Jenkins.
This is a YAML file located in the root of a GitLab repository that defines the CI/CD pipeline. It specifies the stages (e.g., build, test, deploy), the scripts to run, and the conditions under which jobs should execute. It is the GitLab equivalent of a Jenkinsfile.
GitLab Runners are lightweight agents that execute the jobs defined in the `.gitlab-ci.yml` file. They can run on local machines, virtual machines, or inside Docker containers. Runners communicate with GitLab over a secure API to pick up jobs and report results.
GitHub Actions is a CI/CD and automation platform integrated directly into GitHub. It allows you to automate workflows based on GitHub events (like pushes, PRs, or releases). It uses YAML files stored in the `.github/workflows` directory to define automated tasks.
A workflow is a configurable automated process made up of one or more jobs. It is triggered by specific events in your repository. For example, you can have a 'CI Workflow' that runs tests on every push and a 'Release Workflow' that deploys the application when a tag is created.
CircleCI is a popular cloud-based CI/CD tool known for its speed and ease of configuration. It emphasizes high performance through features like automatic parallelism, caching, and 'Orbs' (reusable snippets of configuration), making it a favorite for fast-moving startup teams.
Travis CI is one of the earliest cloud-based CI services. It is historically known for its strong integration with open-source projects on GitHub. It uses a `.travis.yml` file to define build environments and test scripts.
ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes. It monitors your Git repository for changes to Kubernetes manifests and automatically synchronizes the cluster state with the desired state defined in Git, ensuring consistency and enabling easy rollbacks.
Push-based (e.g., Jenkins): The CI tool pushes code/configurations to the target environment. Pull-based (e.g., ArgoCD): An agent inside the target environment (like Kubernetes) 'pulls' the configuration from Git. Pull-based is considered more secure and reliable for GitOps.
I integrate testing into the pipeline stages: 1) Unit Tests run immediately after the build. 2) Integration Tests run after deploying to a staging environment. 3) Smoke Tests run after production deployment. If any test fails, the pipeline stops to prevent buggy code from moving forward.
Artifact management involves storing and versioning the output of a build process (e.g., .jar files, .war files, or Docker images). This ensures that the exact same binary that was tested in the CI stage is what gets deployed to production, maintaining the 'build once, deploy many' principle.
Sonatype Nexus and JFrog Artifactory are Repository Managers. They act as a central hub for all build artifacts and third-party dependencies. They provide security scanning, access control, and versioning for binaries used across the organization.
I use a 'Shift Left' approach by adding security steps early in the pipeline: 1) SAST (SonarQube) for code analysis. 2) SCA (Snyk) for dependency scanning. 3) Container Scanning (Trivy) for Docker images. This catches vulnerabilities before they reach production.
SAST (Static Application Security Testing) analyzes source code while it's 'at rest' to find vulnerabilities like SQL injection. DAST (Dynamic Application Security Testing) tests the running application from the outside, acting like a hacker to find security flaws in the environment.
Dependency scanning (or Software Composition Analysis) checks the open-source libraries used by your application for known vulnerabilities (CVEs). Since most modern apps are 80% third-party code, this is a critical step to prevent supply-chain attacks.
Blue-Green: Switch traffic between two identical environments. Canary: Deploy to 5% of users first to test stability. Rolling: Replace old instances with new ones one-by-one. In Kubernetes, these are handled via the 'Deployment' strategy or service meshes like Istio.
Pipeline as code is the practice of defining your CI/CD pipelines using code (YAML, Groovy) rather than manual GUI clicks. This allows the pipeline to be versioned in Git, peer-reviewed, and easily replicated across multiple projects.
I never hardcode secrets. I use tool-specific secret stores (Jenkins Credentials, GitHub Secrets) or external Secret Managers like HashiCorp Vault or AWS Secrets Manager. Secrets are injected as environment variables at runtime.
1) Build only once. 2) Test in environments identical to production. 3) Automate everything, including rollbacks. 4) Keep the pipeline fast (under 10 mins). 5) Use short-lived feature branches. 6) Security and compliance checks should be part of the flow.
Docker35
Docker is a platform that uses containerization to package an application and its dependencies into a single image. Containerization is an OS-level virtualization method that allows multiple isolated systems (containers) to run on a single host, sharing the same OS kernel.
VMs virtualize the hardware and include a full Guest OS (heavy, slow boot). Containers virtualize the OS and share the host kernel (lightweight, near-instant boot, higher density). Containers are more efficient but offer slightly less isolation than VMs.
A Docker image is a read-only template that contains the source code, libraries, and environment settings needed to run an application. It is composed of multiple layers stacked on top of each other. Once an image is started, it becomes a container.
A Docker container is a runnable instance of an image. It provides an isolated environment for an application to run. While images are static and read-only, containers have a thin 'writable layer' on top where changes are stored during execution.
A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using `docker build`, Docker reads the instructions in a Dockerfile and automatically generates a Docker image.
FROM: Sets base image. RUN: Executes commands during build (creates layer). CMD: Default command when container starts. ENTRYPOINT: Main executable that container runs. COPY: Copies files from host. ADD: Similar to COPY but handles URLs and .tar extraction.
ENTRYPOINT defines the command that will always run when the container starts. CMD provides default arguments for that ENTRYPOINT. If you run `docker run image arg1`, it overrides the CMD but the ENTRYPOINT still executes with the new argument.
COPY is preferred for simple local-to-container file transfers. ADD is more powerful but less transparent; it can fetch files from remote URLs and automatically extract compressed archives (like .tar.gz), which can sometimes lead to unexpected results.
Each instruction in a Dockerfile creates a new read-only layer in the image. Docker uses a 'Union File System' to stack these layers. Layers are cached, meaning if you haven't changed a line in your Dockerfile, Docker will reuse the cached layer during the next build, making it very fast.
1) Use small base images (e.g., Alpine). 2) Use Multi-stage builds. 3) Minimize the number of layers by combining RUN commands (e.g., using `&&`). 4) Use `.dockerignore` to avoid copying unnecessary files. 5) Clear package manager caches in the same RUN layer.
A multi-stage build uses multiple `FROM` statements in a single Dockerfile. You can use one stage to 'build' the app (with all compilers/bloat) and then 'copy' only the final binary into a second, tiny production stage. This results in extremely small and secure production images.
`.dockerignore` is a file that tells Docker which local files and directories should not be sent to the Docker daemon during the build process. This speeds up the build and prevents sensitive files or large `node_modules` folders from being included in the image.
Docker Hub is a public, cloud-based repository (Registry) where users can share and find Docker images. It hosts 'Official Images' for most popular software like Nginx, Python, and Ubuntu, serving as the default library for the Docker community.
A Docker registry is a storage and distribution system for named Docker images. You 'push' images to a registry to store them and 'pull' them to use them on other servers. Docker Hub is the most famous public registry, but many companies use private ones.
A Public registry (like Docker Hub) is accessible to everyone; anyone can pull your images. A Private registry (like AWS ECR, Azure ACR, or Harbor) requires authentication and is used by companies to securely store their proprietary code and internal images.
I use the 'docker run' command. Common flags: `-d` for detached mode, `-p 80:80` for port mapping, `--name` to name the container, and `-v` to mount a volume. Example: `docker run -d -p 8080:80 --name my-web nginx`.
Docker volumes are the preferred mechanism for persisting data generated by and used by Docker containers. Since container filesystems are deleted when the container is removed, volumes allow you to store data (like databases) on the host machine or remote storage.
Bind mounts map a specific path on the host machine to a path in the container (dependent on host OS structure). Volumes are managed by Docker itself in a specific directory (`/var/lib/docker/volumes`), making them more portable and safer for production.
Docker networking allows containers to communicate with each other, the host, and external networks. Docker creates a virtual network interface on the host, and each container gets its own IP address within that virtual network.
Bridge: The default, isolated private network on a single host. Host: Container shares the host's networking namespace directly (no isolation). Overlay: Connects containers across multiple physical hosts (Docker Swarm). None: No networking for the container.
On a single host, containers can communicate via the default Bridge network using IP addresses. A better way is using User-Defined Bridge Networks, which allow containers to resolve each other by container name (DNS). For communication across different hosts, an Overlay network or a container orchestrator like Kubernetes is used.
Docker Compose is a tool for defining and running multi-container Docker applications. It uses a YAML file to configure the application's services, networks, and volumes. With a single command (`docker-compose up`), you can create and start all the services defined in your configuration.
This is the configuration file for Docker Compose. It defines the version of the compose file format, services (containers), their images, build instructions, ports, environment variables, dependencies (`depends_on`), and volumes. It allows for infrastructure-as-code at a local or small-scale development level.
I use the `--scale` flag followed by the service name and the number of instances. For example: `docker-compose up -d --scale web=3`. This will spin up three instances of the 'web' service. Note that host port mapping must be handled carefully to avoid conflicts.
'docker run' creates a new container from an image and starts it. 'docker start' restarts a container that has been stopped. 'docker run' is for new instances; 'docker start' is for existing ones.
'docker stop' sends a SIGTERM signal to the process, allowing the container to perform a graceful shutdown. 'docker kill' sends a SIGKILL signal, which terminates the process immediately without cleanup. I always prefer 'docker stop' for production stability.
I use 'docker logs [container_id]'. To follow the logs in real-time, I use the `-f` (follow) flag. I can also use `--tail` to see only the last few lines or `--since` to view logs from a specific time period.
I use 'docker exec -it [container_id] /bin/bash' (or `/bin/sh` for Alpine images). The `-i` flag stands for interactive and `-t` for terminal, allowing me to run commands inside the container's environment.
'docker exec' creates a new process inside the running container (ideal for debugging). 'docker attach' connects your local terminal to the container's main process (the one defined in CMD/ENTRYPOINT). If you exit an attached shell, you might stop the container.
1) `docker ps -a` to check the exit code of failed containers. 2) `docker logs` to see application errors. 3) `docker inspect` to check networking and volume mounts. 4) `docker stats` to check for resource exhaustion (CPU/RAM). 5) Exec into the container to verify file paths and environment variables.
Docker Swarm is Docker’s native orchestration tool. It turns a group of Docker hosts into a single, virtual Docker host. It is simpler to set up than Kubernetes but lacks many of the advanced features required for massive, complex production environments.
Docker Swarm is easy to use, lightweight, and built into Docker. Kubernetes is more complex, highly extensible, and handles massive-scale automation, self-healing, and complex networking much more robustly. K8s is currently the industry standard for production.
1) Use official, verified base images. 2) Run containers as a non-root user. 3) Use `docker scan` or Trivy for vulnerability checks. 4) Limit resource usage (CPU/Memory). 5) Keep the host OS patched. 6) Use Read-Only filesystems for the container if possible.
A container runtime is the software responsible for running containers. While Docker was the original, Kubernetes now uses lower-level runtimes like containerd (Docker's core) or CRI-O via the Container Runtime Interface (CRI) to manage the container lifecycle more efficiently.
1) One process per container. 2) Minimize layers. 3) Don't store data in containers (use volumes). 4) Use `.dockerignore`. 5) Use multi-stage builds. 6) Tag your images with version numbers, not just 'latest'.
Kubernetes40
Kubernetes (K8s) is an open-source container orchestration platform that automates the deployment, scaling, and management of containerized applications. It groups containers that make up an application into logical units for easy discovery and management.
Kubernetes follows a Master-Worker (Control Plane-Node) architecture. The Control Plane manages the cluster state, and the Worker Nodes run the actual applications. They communicate via the API server.
A Kubernetes cluster is a set of node machines for running containerized applications. At a minimum, a cluster contains a control plane and one or more compute nodes. This distribution provides high availability and scalability.
The Master Node (Control Plane) makes global decisions about the cluster (e.g., scheduling) and detects/responds to cluster events. Worker Nodes maintain the running pods and provide the Kubernetes runtime environment to actually execute application code.
API Server: Front end for the control plane. Scheduler: Assigns pods to nodes. Controller Manager: Runs controller processes (like node or job controllers). etcd: Consistent and highly-available key-value store for all cluster data.
kubelet: An agent that ensures containers are running in a pod. kube-proxy: Manages network rules for communication. Container Runtime: The software that runs containers (like containerd).
etcd is the 'source of truth' for Kubernetes. It is a distributed key-value store that holds all configuration data, state data, and metadata for the cluster. If etcd is lost, the cluster's state is lost, making its backup critical.
A Pod is the smallest deployable unit in Kubernetes. It represents a single instance of a running process in your cluster and can contain one or more containers that share the same network IP and storage volumes.
A ReplicaSet's purpose is to maintain a stable set of replica Pods running at any given time. It is often used to guarantee the availability of a specified number of identical Pods.
A Deployment provides declarative updates for Pods and ReplicaSets. You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate, enabling features like rolling updates and rollbacks.
A ReplicaSet ensures a specific number of pod replicas are running. A Deployment is a higher-level object that manages ReplicaSets and allows for seamless updates (like changing an image version) and easy rollbacks. You should almost always use Deployments instead of ReplicaSets directly.
A Service is an abstraction that defines a logical set of Pods and a policy by which to access them. Since Pods are ephemeral (they die and get replaced with new IPs), a Service provides a stable IP address or DNS name to reach the pods.
ClusterIP: Internal-only IP (default). NodePort: Exposes service on each Node's IP at a static port. LoadBalancer: Exposes service externally using a cloud provider's load balancer. ExternalName: Maps service to a DNS name.
Namespaces are virtual clusters within a physical Kubernetes cluster. They are used to divide cluster resources between multiple users or projects (e.g., 'dev', 'staging', 'prod' namespaces), preventing resource name collisions.
A ConfigMap is an API object used to store non-confidential data in key-value pairs. It allows you to decouple environment-specific configuration from container images, making your applications more portable across different stages of the pipeline.
Secrets are similar to ConfigMaps but are intended for sensitive data like passwords, tokens, or SSH keys. Kubernetes stores Secrets in a base64 encoded format and can be configured to encrypt them at rest in etcd for better security.
ConfigMap is for plain-text configuration data. Secret is for sensitive data. In technical terms, Secrets are handled differently by the K8s system (stored in tmpfs, not written to disk on nodes) to minimize risk of exposure.
A PersistentVolume is a piece of storage in the cluster that has been provisioned by an administrator or dynamically provisioned using Storage Classes. It exists independently of the lifecycle of any individual pod that uses it.
A PVC is a request for storage by a user. It is similar to a Pod; while a Pod consumes node resources, a PVC consumes PV resources. PVCs allow a developer to consume abstract storage without knowing the details of the underlying cloud provider.
A StorageClass provides a way for administrators to describe the 'classes' of storage they offer (e.g., 'fast-ssd' vs 'slow-hdd'). It enables Dynamic Provisioning, where a PV is created automatically as soon as a PVC is requested.
StatefulSet is used for applications that require a stable identity and persistent data (like databases). Unlike Deployments where pods are interchangeable, StatefulSet pods have unique, persistent identifiers and stable network hostnames.
Deployment is for stateless apps where pods can be deleted and replaced randomly. StatefulSet is for stateful apps (databases) where the order of deployment, termination, and stable network/storage identity is required.
A DaemonSet ensures that a copy of a specific Pod runs on every node in the cluster. It is commonly used for background services like log collectors (Fluentd) or monitoring agents (Prometheus Node Exporter).
A Job creates one or more Pods and ensures that a specified number of them successfully terminate (runs to completion). A CronJob runs Jobs on a time-based schedule, just like a standard crontab in Linux.
Ingress is an API object that manages external access to the services in a cluster, typically HTTP. It can provide load balancing, SSL termination, and name-based virtual hosting, acting as an entry point into the cluster.
An Ingress Controller is the actual load balancer (like Nginx, Traefik, or AWS ALB) that fulfills the rules defined in the Ingress resource. Kubernetes does not come with a default Ingress Controller; you must install one.
A NetworkPolicy is a set of rules that controls the traffic flow between pods. By default, all pods can talk to each other in K8s; NetworkPolicy allows you to implement 'Zero Trust' by specifying exactly which pods can communicate.
Role-Based Access Control (RBAC) is a method of regulating access to the Kubernetes API based on the roles of individual users. It uses Roles/ClusterRoles (permissions) and RoleBindings (assigning roles to users or service accounts).
A ServiceAccount provides an identity for processes that run in a Pod. When you access the cluster, you use a user account; when a Pod (like a Jenkins agent) needs to access the Kubernetes API, it uses a ServiceAccount.
kubectl is the command-line tool used to communicate with the Kubernetes API server. It allows you to create, inspect, update, and delete Kubernetes objects and troubleshoot cluster issues.
1) `kubectl get pods` - list pods. 2) `kubectl apply -f file.yaml` - create/update resource. 3) `kubectl describe pod name` - detailed info. 4) `kubectl logs name` - view logs. 5) `kubectl exec -it name -- bash` - interactive shell.
A rolling update is the default deployment strategy in K8s. It replaces the old version of an app with the new version by gradually killing old pods and spinning up new ones, ensuring zero downtime during the transition.
I use the command `kubectl rollout undo deployment/[name]`. Kubernetes keeps a history of revisions, allowing you to quickly revert to a previous working state if a new deployment is faulty.
Liveness Probe: Checks if the container is running. If it fails, K8s kills the pod and restarts it. Readiness Probe: Checks if the app is ready to serve traffic. If it fails, K8s removes the pod from the Service load balancer.
Liveness fixes a hung application by restarting it. Readiness protects the application by ensuring traffic only flows to pods that have finished their startup tasks (like loading data) and are healthy.
HPA automatically scales the number of Pods in a replication controller, deployment, or replica set based on observed CPU utilization or other custom metrics. It ensures that the application has enough resources to handle high traffic while saving costs during low-demand periods.
VPA automatically sets the resource requests and limits for your containers based on their actual usage. Unlike HPA, which adds more pods, VPA increases or decreases the CPU and Memory allocated to existing pods, which is useful for applications that cannot scale horizontally.
Requests are the minimum resources (CPU/Memory) a container needs to run; the scheduler uses this to place pods on nodes. Limits are the maximum resources a container can consume. If a container exceeds its memory limit, it is OOMKilled. If it exceeds CPU limits, it is throttled.
A Helm chart is a bundle of YAML manifests that describe a related set of Kubernetes resources. It uses templating, allowing you to define variables in a `values.yaml` file so you can deploy the same application across different environments (Dev, Prod) with different configurations.
Helm is a package manager for Kubernetes (often called the 'apt' or 'yum' of K8s). It simplifies the deployment of complex applications, manages release versions, and allows for easy rollbacks. It is essential for managing reproducible deployments in a large cluster.
Terraform25
IaC is the practice of managing and provisioning computing infrastructure through machine-readable definition files rather than manual physical hardware configuration or interactive configuration tools. It ensures consistency, speed, and version control for infrastructure.
Terraform is an open-source IaC tool created by HashiCorp. It uses a declarative language (HCL) to define and provide data center infrastructure across multiple cloud providers (AWS, Azure, GCP). It focuses on the 'end state' of the infrastructure.
Terraform is primarily an orchestration tool used to provision infrastructure (creating servers, VPCs, DBs). Ansible is primarily a configuration management tool used to install software and configure settings *on* those existing servers. Terraform is declarative; Ansible is procedural.
The state file (`terraform.tfstate`) is a local or remote JSON file that keeps track of the resources Terraform has created. It maps your configuration to real-world resources and is used by Terraform to determine what changes need to be applied during a plan/apply.
Remote state involves storing the state file in a central location like AWS S3 or Terraform Cloud rather than on a developer's local machine. This is critical for team collaboration, security (encrypting secrets in state), and enabling state locking.
State locking prevents multiple team members from running Terraform at the same time on the same project, which could lead to state corruption. Tools like DynamoDB (with S3) or Terraform Cloud provide the locking mechanism.
Providers are plugins that Terraform uses to communicate with various APIs (e.g., AWS, Azure, Kubernetes, GitHub). They define which resources and data sources are available for use within your HCL code.
Resources are the most important element in HCL. They describe one or more infrastructure objects, such as a virtual network, compute instance, or higher-level component such as DNS records. Example: `resource "aws_instance" "web" { ... }`.
Modules are containers for multiple resources that are used together. They allow you to package and reuse infrastructure code, making your configurations modular, easier to maintain, and easier to share across different teams or projects.
Resource creates and manages an object in the infrastructure. Data source allows Terraform to use information defined outside of Terraform, or by another separate Terraform configuration (e.g., fetching an existing AMI ID from AWS).
This is the first command that should be run after writing a new Terraform configuration. It initializes the working directory by downloading the necessary provider plugins and setting up the backend for the state file.
This command creates an execution plan. It compares the current state of the infrastructure with the desired state in the code and shows exactly what changes (add, change, destroy) will be made without actually applying them.
This command executes the actions proposed in a terraform plan. It makes the actual calls to the provider APIs to provision or update the infrastructure as defined in the configuration files.
This command is used to terminate all resources managed by your specific Terraform project. It is a graceful way to clean up resources that are no longer needed, following the dependencies in reverse order.
It checks the configuration files in a directory to ensure they are syntactically valid and internally consistent. It is a best practice to run this in CI/CD pipelines before performing a plan or apply.
This command is used to rewrite Terraform configuration files to a canonical format and style. It improves code readability and ensures that all team members are following the same formatting standards.
Variables allow you to parameterize your Terraform code. You can define input variables in a `variables.tf` file and provide values via a `terraform.tfvars` file, environment variables, or CLI flags, making the code reusable across environments.
Variables are input parameters passed from outside the module (like function arguments). Locals are internal to the module and are used for calculating values once to avoid repetition (like local variables in a function).
Outputs are like return values for a Terraform module. They are used to print specific information to the CLI after an apply (e.g., an EC2 public IP) or to pass information from a child module to a parent module.
This command allows you to bring existing infrastructure (created manually or by other tools) under Terraform management. It associates a real-world resource ID with a resource block defined in your `.tf` file.
Tainting a resource tells Terraform to mark it for destruction and recreation during the next apply. This is useful when a resource is in a 'degraded' state (e.g., a software install failed) and you want a fresh instance.
Provisioners (like `local-exec` or `remote-exec`) are used to execute scripts or shell commands on a local or remote machine as part of resource creation. Terraform recommends using them only as a last resort when native resources aren't available.
The lifecycle block allows you to customize how Terraform handles resource changes. Options include `create_before_destroy` (for zero-downtime updates), `prevent_destroy`, and `ignore_changes` to specific attributes.
count is used for creating multiple identical resources based on an integer. for_each is used for creating resources based on a map or set of strings, offering much more flexibility and making it easier to manage resource identity.
Workspaces allow you to manage multiple distinct states from a single configuration. This is often used to manage separate environments (e.g., Dev vs. Prod) using the exact same code but with different state files.
Ansible20
Ansible is an open-source automation engine for configuration management, application deployment, and task automation. It is agentless, meaning it doesn't require any software to be installed on target nodes; it communicates over standard SSH.
Terraform focuses on the Infrastructure level (provisioning resources). Ansible focuses on the Operating System and Application level (configuring software). A common workflow is to use Terraform to build a server and Ansible to install Nginx on it.
A playbook is a YAML file where you define the automation tasks you want Ansible to perform. It describes a 'play'—mapping a group of hosts to a set of tasks. It is the heart of Ansible's automation capabilities.
Roles are a way to bundle related tasks, variables, files, and templates together in a structured directory format. They make your Ansible code modular and reusable across different playbooks and projects.
An inventory is a file (INI or YAML) that lists the hosts (nodes) that Ansible will manage. It allows you to group servers (e.g., `[webservers]`, `[dbservers]`) so you can target specific sets of infrastructure in your playbooks.
Static inventory is a manually edited file. Dynamic inventory uses a script or plugin to pull host information from external sources (like AWS EC2 or GCP) in real-time, which is essential for auto-scaling cloud environments.
Modules are the small programs that Ansible executes on target nodes to perform specific tasks (e.g., `yum` for packages, `copy` for files, `service` for managing daemons). Ansible comes with thousands of built-in modules.
Idempotency is a core feature of Ansible. It ensures that running a playbook multiple times results in the same state without making unnecessary changes. If a package is already installed, Ansible will detect it and do nothing.
Handlers are special tasks that are only executed if they are 'notified' by another task. A common use case is restarting a service (like Nginx) only if the configuration file was actually changed by a previous task.
Facts are system-specific information gathered by Ansible when it connects to a target host (e.g., IP address, OS version, total memory). This information is stored in the `ansible_facts` variable and can be used to customize tasks.
Ansible Vault is a feature that allows you to keep sensitive data such as passwords or keys in encrypted files rather than plain text. You can encrypt entire files or individual variables and decrypt them at runtime using a password.
I use Ansible Vault for local encryption. In larger enterprise setups, I integrate Ansible with external secret managers like HashiCorp Vault or AWS Secrets Manager to pull credentials dynamically during playbook execution.
Variables allow for dynamic playbooks. They can be defined in multiple places: global inventory, playbook `vars` section, included files, roles, or gathered as facts. They are accessed using Jinja2 syntax like `{{ variable_name }}`.
Ansible has about 22 levels of precedence. Broadly: 1. Extra vars (`-e` on CLI) always win. 2. Playbook vars. 3. Role vars. 4. Host facts. 5. Inventory vars. Understanding this is key to debugging complex automation.
Tags allow you to run only specific parts of a playbook. For example, if I have a playbook that sets up an entire server, I can tag the 'Nginx' tasks so I can run only those tasks later when I need to update the web server config.
Import (static) happens at the time the playbook is parsed; it is fast and rigid. Include (dynamic) happens at runtime, allowing you to use variables and conditionals to decide whether or not to run certain tasks.
1. Syntax Check: `ansible-playbook --syntax-check`. 2. Check Mode: `ansible-playbook --check` (Dry run). 3. Molecule: A specialized framework for testing Ansible roles in isolated Docker containers or VMs.
Ansible Galaxy is a public repository of community-contributed Ansible roles. It allows you to jumpstart your automation by using pre-written roles for common tasks like installing databases, monitoring tools, or web servers.
1. Always use Roles for organization. 2. Use human-readable task names. 3. Keep sensitive data in Ansible Vault. 4. Use `check_mode` before applying. 5. Use dynamic inventory for cloud. 6. Aim for simple, readable playbooks.
1. Use the `debug` module to print variable values. 2. Increase verbosity with `-v`, `-vv`, or `-vvv`. 3. Use `check_mode`. 4. Use the `assert` module to verify that certain conditions are met during execution.
AWS20
The core AWS services for DevOps include EC2 (Compute), S3 (Storage), VPC (Networking), IAM (Security), RDS (Databases), and the 'Code' suite (CodeCommit, CodeBuild, CodeDeploy, CodePipeline) for CI/CD. For containerization, EKS (Kubernetes) and ECS (Docker) are essential, along with CloudWatch for monitoring and CloudFormation/CDK for IaC.
Elastic Compute Cloud (EC2) provides scalable computing capacity in the AWS cloud. It allows users to launch virtual servers (instances), manage security and networking, and manage storage. It is the fundamental building block for hosting applications where you need full control over the OS.
AWS offers different families optimized for specific use cases: General Purpose (T and M types) for balanced workloads, Compute Optimized (C types) for batch processing, Memory Optimized (R types) for high-performance databases, and Storage Optimized (I and D types) for high-throughput local storage.
An ASG contains a collection of EC2 instances that are treated as a logical grouping for the purposes of automatic scaling and management. It ensures that you have the correct number of instances to handle the load of your application by automatically adding or removing instances based on metrics like CPU usage.
ELB automatically distributes incoming application traffic across multiple targets, such as EC2 instances, containers, and IP addresses. It increases the availability and fault tolerance of your application by ensuring only healthy targets receive traffic.
ALB (Application Load Balancer): Operates at Layer 7 (HTTP/HTTPS) and supports path-based routing. NLB (Network Load Balancer): Operates at Layer 4 (TCP/UDP) for ultra-high performance and static IPs. CLB (Classic Load Balancer): The legacy load balancer that works at both layers but lacks advanced features of ALB/NLB. [Image of AWS Load Balancer comparison table]
A VPC is a logically isolated section of the AWS Cloud where you can launch AWS resources in a virtual network that you define. You have complete control over your virtual networking environment, including selection of your own IP address range, creation of subnets, and configuration of route tables and network gateways.
A Public Subnet has a route to an Internet Gateway, allowing resources (like web servers) to be accessed from the internet. A Private Subnet does not have a direct route to the internet; resources here (like databases) are more secure and typically access the internet through a NAT Gateway for updates.
An Internet Gateway (IGW) allows resources in your VPC to communicate with the internet. A NAT Gateway allows instances in a private subnet to connect to the internet (e.g., for software patches) but prevents the internet from initiating a connection with those instances.
Security Groups are stateful firewalls that act at the Instance level (allow rules only). Network ACLs (NACLs) are stateless firewalls that act at the Subnet level (support both allow and deny rules). Statefulness means if you allow inbound traffic, outbound traffic is automatically allowed in a Security Group.
Simple Storage Service (S3) is an object storage service offering industry-leading scalability and data availability. Common DevOps use cases include: 1) Hosting static websites. 2) Storing backup files. 3) Storing build artifacts. 4) Centralized logging. 5) Storing Terraform state files.
AWS offers classes optimized for access patterns: S3 Standard (frequent access), S3 Intelligent-Tiering (automatic cost savings), S3 Standard-IA (infrequent access), and S3 Glacier (long-term archive). Choosing the right class is key to cost optimization.
CloudFront is a fast content delivery network (CDN) service that securely delivers data, videos, applications, and APIs to customers globally with low latency and high transfer speeds. It uses edge locations to cache content closer to the end users.
Route 53 is a highly available and scalable cloud Domain Name System (DNS) web service. It is used for domain registration, DNS routing (mapping names to IPs), and health checking of resources to ensure traffic is only sent to healthy endpoints.
Relational Database Service (RDS) makes it easy to set up, operate, and scale a relational database in the cloud. It automates time-consuming administration tasks such as hardware provisioning, database setup, patching, and backups. It supports engines like MySQL, PostgreSQL, and SQL Server.
RDS is for Relational (SQL) data, supports complex joins, and follows ACID properties. DynamoDB is a NoSQL, key-value database that is serverless and offers single-digit millisecond latency at any scale. Use RDS for traditional ERP/CRM apps; use DynamoDB for high-scale web apps and real-time data.
AWS Lambda is a serverless compute service that lets you run code without provisioning or managing servers. You only pay for the compute time you consume. In DevOps, it's often used for automation tasks, such as triggering a cleanup script when a file is uploaded to S3.
ECS (Elastic Container Service): AWS's native container orchestrator; simple to use and deeply integrated with AWS. EKS (Elastic Kubernetes Service): Managed Kubernetes service; more complex but offers industry-standard portability and a massive ecosystem of tools.
Elastic Container Registry (ECR) is a fully managed Docker container registry that makes it easy for developers to store, manage, and deploy Docker container images. It integrates directly with ECS, EKS, and Lambda for seamless deployments.
CloudFormation is an AWS service that allows you to model and set up your AWS resources using JSON or YAML templates. It is AWS's native Infrastructure as Code tool, providing a single source of truth for your cloud infrastructure.
Monitoring & Logging24
Observability is the ability to measure the internal state of a system only by looking at its external outputs (Logs, Metrics, Traces). While monitoring tells you *that* something is wrong, observability helps you understand *why* it is wrong by providing deep context across the entire stack.
Monitoring is about tracking predefined metrics (e.g., CPU > 90%). Observability is about being able to answer questions you didn't know you had by exploring the data. Monitoring is 'Known-Unknowns,' whereas Observability is for 'Unknown-Unknowns.'
Prometheus is an open-source systems monitoring and alerting toolkit originally built at SoundCloud. It uses a Pull-based model to collect metrics, stores them in a time-series database, and is the standard for monitoring Kubernetes environments.
Grafana is a multi-platform open-source analytics and interactive visualization web application. It provides charts, graphs, and alerts for the web when connected to supported data sources like Prometheus, InfluxDB, or CloudWatch.
1) Install both services. 2) In Grafana, go to 'Data Sources'. 3) Select 'Prometheus'. 4) Enter the Prometheus server URL (e.g., `http://prometheus:9090`). 5) Click 'Save & Test'. 6) You can now create dashboards using PromQL queries.
The core is the Prometheus Server which scrapes and stores time-series data. It uses Exporters (like Node Exporter) to collect metrics from targets, a Pushgateway for short-lived jobs, and an Alertmanager to handle notifications. [Image of Prometheus Architecture diagram]
PromQL (Prometheus Query Language) is a functional query language that lets the user select and aggregate time-series data in real-time. For example, `rate(http_requests_total[5m])` calculates the per-second rate of HTTP requests over the last 5 minutes.
Alertmanager handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or Slack. It also handles silencing and inhibition of alerts.
The ELK stack is a collection of three open-source products: Elasticsearch (Search and Analytics engine), Logstash (Log processing pipeline), and Kibana (Visualization dashboard). It is the most popular solution for centralized logging.
Logstash is an open-source data collection engine with real-time pipelining capabilities. It can dynamically unify data from disparate sources (logs, metrics, web applications) and normalize the data into destinations of your choice (usually Elasticsearch). It uses three stages: Inputs, Filters, and Outputs.
Kibana is a browser-based user interface that lets you visualize and navigate the Elasticsearch index. I use it to create bar charts, pie charts, and maps to represent log data. It also allows for real-time monitoring of application health and security auditing through its 'Discover' and 'Dashboard' features.
Filebeat is a lightweight shipper for forwarding and centralizing log data. Installed as an agent on your servers, Filebeat monitors the log files or locations that you specify, collects log events, and forwards them either to Elasticsearch or Logstash for indexing.
Filebeat is a lightweight agent designed only to ship logs; it has a very low resource footprint. Logstash is a heavy-duty processing engine that can filter, transform, and enrich data before sending it to a destination. A common pattern is to use Filebeat to collect logs and send them to Logstash for heavy processing.
EFK is a popular alternative to ELK, especially in Kubernetes environments. It replaces Logstash with Fluentd. Fluentd is often preferred in K8s because it is written in C/Ruby, has a smaller memory footprint than Logstash, and has excellent native integration with container logs via 'Input' plugins.
Splunk is a proprietary software platform for searching, monitoring, and analyzing machine-generated big data. Unlike the open-source ELK stack, Splunk is an all-in-one commercial solution that offers powerful built-in AI/ML capabilities, sophisticated reporting, and premium support, though it comes with high licensing costs.
Datadog is a cloud-scale monitoring and analytics platform. It provides 'Full-Stack' observability by integrating metrics, traces, and logs in a single SaaS platform. It is highly popular in DevOps for its easy-to-install agents and vast library of integrations for cloud-native technologies like AWS and Kubernetes.
New Relic is an observability platform designed to help engineers monitor, debug, and improve their entire software stack. It is particularly well-known for its APM (Application Performance Monitoring) capabilities, allowing developers to see code-level performance issues and transaction traces in real-time.
I monitor System Metrics (CPU usage, Memory saturation, Disk I/O, Network throughput) and Application Metrics (HTTP response times, Error rates/5xx codes, Request volume). I also monitor 'Saturation'—how close a resource is to its limit (e.g., database connection pool utilization).
APM involves monitoring the performance and availability of software applications. It provides deep visibility into application dependencies, database query performance, and slow code paths. Tools like New Relic or Dynatrace help identify if a slow user experience is caused by a slow SQL query or a third-party API call.
Distributed tracing is a method used to profile and monitor applications, especially those built using a microservices architecture. It tracks a single request as it travels through various services, generating a 'Trace ID'. This helps identify which specific service is causing latency in a complex request chain.
Jaeger is an open-source, end-to-end distributed tracing system. It was originally built by Uber and is now part of the Cloud Native Computing Foundation (CNCF). I use it to monitor microservices-based distributed systems and perform root-cause analysis on latency issues.
Defined by Google's SRE book, they are: 1) Latency: Time it takes to service a request. 2) Traffic: Demand placed on the system (e.g., HTTP requests/sec). 3) Errors: Rate of failed requests. 4) Saturation: How 'full' your service is (e.g., % of CPU used).
I set up alerts based on thresholds (e.g., 5xx errors > 1%) or anomalies. Alerts should be 'Actionable.' I route critical alerts to PagerDuty/SMS and non-critical warnings to Slack. I avoid 'Alert Fatigue' by ensuring we only notify humans for issues that require immediate intervention.
SLI (Indicator): A specific metric like 'Up-time'. SLO (Objective): The target value for the SLI (e.g., 99.9% up-time). SLA (Agreement): The legal contract with the customer that defines consequences if the SLO is not met (e.g., financial credits).
Networking20
The TCP/IP model is a concise framework of networking protocols. It consists of four layers: 1) Network Access (Physical/Data Link), 2) Internet (IP), 3) Transport (TCP/UDP), and 4) Application (HTTP/SSH). It is the foundation of the modern internet.
The OSI (Open Systems Interconnection) model is a conceptual model that characterizes and standardizes the communication functions of a telecommunication or computing system into seven layers: Physical, Data Link, Network, Transport, Session, Presentation, and Application.
TCP (Transmission Control Protocol) is connection-oriented, ensures data delivery, and maintains the order of packets (reliable but slower). UDP (User Datagram Protocol) is connectionless, sends data without checking if it was received (unreliable but faster). Use TCP for web/email; use UDP for streaming/gaming.
DNS (Domain Name System) is the 'phonebook' of the internet. It translates human-readable domain names (e.g., google.com) into machine-readable IP addresses (e.g., 142.250.190.46). When you visit a site, your computer queries a recursive DNS resolver, which eventually finds the authoritative record for that domain.
Load balancing is the process of distributing network or application traffic across multiple servers. This ensures no single server bears too much demand, improving responsiveness and increasing availability. If one server fails, the load balancer redirects traffic to the remaining healthy servers.
1) Round Robin: Sends requests sequentially. 2) Least Connections: Sends requests to the server with fewest active connections. 3) IP Hash: Uses client IP to determine which server gets the request (sticky sessions). 4) Weighted Round Robin: Accounts for server capacity.
A Forward Proxy sits in front of clients and hides them from the internet (e.g., a corporate VPN). A Reverse Proxy sits in front of servers and hides them from clients (e.g., Nginx in front of an app). Reverse proxies are used for load balancing, SSL termination, and caching.
NGINX is a high-performance web server, reverse proxy, load balancer, and HTTP cache. It is designed for maximum performance and stability, known for its ability to handle thousands of concurrent connections with very low memory usage using an asynchronous, event-driven architecture.
Apache is process-based (creates a new thread per request), which can be resource-heavy under high load. NGINX is event-based and handles many requests in a single thread. NGINX is typically better for serving static content and acting as a reverse proxy, while Apache is more flexible with its `.htaccess` and module system.
SSL (Secure Sockets Layer) and its successor TLS (Transport Layer Security) are cryptographic protocols designed to provide communications security over a computer network. They provide Encryption (privacy), Authentication (identity verification), and Integrity (data protection).
HTTPS is HTTP over TLS. It uses Asymmetric Encryption (public/private keys) to establish a secure connection through a 'TLS Handshake.' Once the connection is established, it switches to Symmetric Encryption for faster data transfer during the session.
A firewall is a network security device that monitors and filters incoming and outgoing network traffic based on an organization's previously established security policies. It acts as a barrier between a trusted internal network and untrusted external networks (like the internet).
A Virtual Private Network (VPN) creates a secure, encrypted 'tunnel' over a less secure network, such as the public internet. In DevOps, we use VPNs to allow developers to securely access private cloud resources (like databases or Jenkins) from their local machines.
Port forwarding (or port mapping) is a technique used to allow external devices to access a service on a private network by mapping an external port on a gateway/router to an internal IP address and port. For example, mapping port 80 of a router to port 8080 of an internal web server.
A Subnet Mask defines which part of an IP address refers to the network and which refers to the host. CIDR (Classless Inter-Domain Routing) is a compact way to represent this. For example, `10.0.0.0/24` means the first 24 bits are the network, leaving 8 bits for hosts (up to 254 usable IPs).
NAT is a process where a network device (like a router) assigns a public IP address to a computer or group of computers inside a private network. This allows multiple devices with private IPs to share a single public IP to access the internet, while also providing a layer of security.
1) ping to check basic reachability. 2) traceroute to find where the connection drops. 3) nslookup/dig to verify DNS. 4) telnet/nc to check if the specific port is open. 5) Check local firewall rules (iptables/ufw) and cloud Security Groups.
ping: Tests reachability using ICMP. traceroute: Shows the path/hops to a destination. nslookup: Basic DNS query tool. dig: Advanced DNS lookup tool that provides detailed information about DNS records (A, CNAME, MX).
Stateful firewalls track the state of active connections (e.g., AWS Security Groups); if you allow inbound, the return outbound traffic is automatically allowed. Stateless firewalls treat each packet in isolation (e.g., AWS NACLs); you must explicitly define both inbound and outbound rules.
A CDN is a distributed network of servers that delivers web content (images, JS, CSS) to users based on their geographic location. By caching content at 'Edge Locations' closer to the user, a CDN significantly reduces latency and lowers the load on the origin server.
Security25
DevSecOps is the practice of integrating security early into every stage of the DevOps lifecycle—from design and development to deployment and monitoring. Instead of treating security as a final check, it becomes a shared responsibility (Shift Left), using automated tools to audit code and infrastructure continuously.
I implement security by adding specific 'gates' in the pipeline: 1) Pre-commit hooks for secret detection. 2) SAST during the build phase. 3) SCA to check for vulnerable dependencies. 4) Container scanning after the image is built. 5) DAST in the staging environment before production rollout.
Vulnerability scanning is the automated process of identifying, evaluating, and reporting security weaknesses in software, networks, or container images. In DevOps, we use tools like Trivy, Grype, or Amazon Inspector to scan our environment for known CVEs (Common Vulnerabilities and Exposures).
SAST analyzes the application's source code, byte code, or binaries for security vulnerabilities without executing the code. It finds issues like SQL injection, cross-site scripting (XSS), and buffer overflows early in the development cycle. Common tools include SonarQube and Snyk Code.
DAST interacts with a running application to find security vulnerabilities such as exposed sensitive data or injection flaws that only appear during execution. Unlike SAST, which 'sees' the code, DAST 'sees' the application from the outside, acting like an external attacker. Common tools include OWASP ZAP.
SCA is the process of automating the visibility into open-source software (OSS) used in an application. It identifies the inventory of all open-source components, their licenses, and known vulnerabilities. This is critical because modern applications are largely built from third-party libraries.
Container scanning specifically looks for vulnerabilities within the OS packages and libraries inside a Docker image. It checks against databases like the NVD (National Vulnerability Database) to ensure that the base image and any installed packages are secure before being pushed to a registry.
Image signing is a process that allows developers to sign container images with a digital signature. This ensures the integrity and authenticity of the image, allowing the container orchestrator (like Kubernetes) to verify that the image hasn't been tampered with since it was built and signed.
Secrets management involves the secure storage, distribution, and rotation of sensitive credentials like API keys, database passwords, and SSH keys. It replaces hardcoded secrets in code or config files with references to a secure, centralized vault.
HashiCorp Vault is a centralized tool for managing secrets and protecting sensitive data. It provides features like dynamic secrets (creating temporary credentials on the fly), data encryption, and detailed audit logs. It is the gold standard for secrets management in multi-cloud environments.
Secrets rotation involves periodically changing credentials to limit the damage if a secret is leaked. I implement this using automated tools (like AWS Secrets Manager or Vault) that can update the password in the database and the application simultaneously without downtime.
PoLP is the practice of limiting access rights for users and processes to only the bare minimum permissions they need to perform their jobs. For example, a Jenkins agent should have permission to upload to an S3 bucket, but not the permission to delete the entire account.
Zero Trust is a security framework based on the principle of 'never trust, always verify.' It assumes that threats exist both inside and outside the network. Every request for access must be authenticated, authorized, and encrypted, regardless of whether it comes from within the office or a remote location.
Network segmentation involves dividing a larger network into smaller, isolated sub-networks (subnets). This limits the 'blast radius' of a security breach; if one subnet is compromised, the attacker cannot easily move laterally to other parts of the network, such as the database layer.
Encryption at rest protects data stored on physical disks (e.g., S3 buckets, RDS volumes) using keys (AWS KMS). Encryption in transit protects data as it moves between a client and server or between internal services, usually using SSL/TLS (HTTPS).
Certificate management is the process of issuing, renewing, and revoking SSL/TLS certificates. Tools like AWS Certificate Manager (ACM) or Let's Encrypt automate this to prevent service outages caused by expired certificates, which is a common production issue.
OAuth is an open-standard authorization protocol that allows applications to access user data without seeing their password (e.g., Login with Google). JWT (JSON Web Token) is a compact, URL-safe way of representing claims between two parties, often used as an identity token after a successful login.
Penetration testing (Pentesting) is a simulated cyberattack against your computer system to check for exploitable vulnerabilities. In DevOps, we often perform 'Automated Pentesting' in the staging environment to catch high-level flaws before a professional third-party audit.
Compliance refers to adhering to specific industry standards for data protection. SOC2 focuses on service organization controls; HIPAA is for healthcare data; PCI-DSS is for credit card processing. DevOps ensures compliance through 'Compliance as Code' (e.g., using AWS Config or Terraform-compliance).
I follow an Incident Response plan: 1) Identification (detect the breach). 2) Containment (isolate affected systems). 3) Eradication (remove the threat). 4) Recovery (restore services from backups). 5) Post-Mortem (root cause analysis and future prevention).
Security hardening is the process of securing a system by reducing its surface of vulnerability. This includes disabling unused services, closing unnecessary ports, removing default passwords, and applying the latest security patches to the OS and applications.
Patch management is the process of managing a network of computers by regularly deploying software updates to address security vulnerabilities and bugs. In DevOps, this is automated using tools like AWS Systems Manager (SSM) or by frequently 'recycling' immutable servers with fresh images.
Backup is the regular copying of data so that it may be restored after a loss. Disaster Recovery is a documented process or set of procedures to execute an organization's response to a major failure (e.g., an entire cloud region going offline).
RTO (Recovery Time Objective): The maximum acceptable length of time the system can be down. RPO (Recovery Point Objective): The maximum acceptable amount of data loss measured in time (e.g., losing up to 1 hour of data). Lower numbers mean a more expensive, robust DR strategy.
1. Enable Multi-Factor Authentication (MFA) for all users. 2. Use IAM Roles instead of long-lived access keys. 3. Encrypt all sensitive data at rest. 4. Use Private Subnets for databases. 5. Enable logging and monitoring (CloudTrail/CloudWatch). 6. Regularly perform security audits and rotating secrets.
Troubleshooting20
1) Check infrastructure metrics (CPU/RAM/IO) in Grafana. 2) Look at application logs for slow queries or errors. 3) Use an APM tool (Datadog/New Relic) to find slow code paths. 4) Use `top` or `htop` on the server to see real-time resource contention. 5) Check for network latency between services.
1) `docker ps -a` to check the exit code (e.g., 137 means OOM). 2) `docker logs` to see the last output before the crash. 3) `docker inspect` to check configuration or health checks. 4) If the container is still running but unstable, `docker exec` into it to check local processes and disk space.
1) `kubectl get pods` to see the status (CrashLoopBackOff, Pending, etc.). 2) `kubectl describe pod` to see the 'Events' log (useful for finding image pull or scheduling errors). 3) `kubectl logs` for the container output. 4) `kubectl describe node` if the pod is 'Pending' due to resource limits.
1) Is it network-level? Use `ping` and `mtr`. 2) Is it application-level? Check APM traces. 3) Is it DB-level? Check for slow queries or lock contention. 4) Is it resource-level? Check for CPU throttling or high memory swap usage.
1) Check `/var/log/syslog` or `dmesg` for 'OOM Killer' logs. 2) In K8s, check if the pod status is `OOMKilled`. 3) Use profiling tools to find memory leaks. 4) Increase the resource 'limits' in your manifest if the application legitimately needs more memory for the current load.
I use a layered approach: 1) Verify the physical/virtual link (`ip link`). 2) Test reachability with `ping`. 3) Trace the route with `mtr` or `traceroute` to find bottlenecks. 4) Check if the port is listening using `netstat` or `ss`. 5) Use `nc -zv` to test if a port is open across the network. 6) Check firewall rules (Security Groups/NACLs/iptables).
1) Use `nslookup` or `dig` to see if the domain resolves to an IP. 2) Check `/etc/resolv.conf` for correct nameserver configuration. 3) In Kubernetes, check if `CoreDNS` pods are running and healthy. 4) Use `dig +trace` to see the full resolution path from root servers down to the authoritative record.
1) `df -h` to find the full partition. 2) `du -sh /* | sort -h` to find the largest directories. 3) Check for 'deleted' files still held open by processes using `lsof +L1`. 4) Check for 'Inode exhaustion' using `df -i`. 5) Look for overgrown log files in `/var/log` that may need rotation.
1) Inspect the 'Console Output' or build logs for specific error codes. 2) Verify environment variables and secrets are correctly mapped. 3) Check for network connectivity between the build agent and external registries. 4) Run the build script locally in a container to see if it's a code issue or a pipeline infrastructure issue.
1) Check if the health checks (Liveness/Readiness) are failing. 2) Verify that the new Docker image exists in the registry. 3) Check for configuration mismatches (ConfigMaps/Secrets). 4) Use 'Rollback' immediately if production is impacted, then investigate the logs in a staging environment to find the root cause.
1) Isolate the affected systems to prevent lateral movement. 2) Review Audit Logs (CloudTrail/System logs). 3) Check for unauthorized SSH keys or IAM users. 4) Analyze network traffic for unusual egress patterns. 5) Preserve evidence for a forensic audit and perform a full password/secret rotation.
1) Compare the current configuration against the 'last known good' version in Git. 2) Use 'Dry Run' flags (e.g., `ansible-playbook --check` or `terraform plan`). 3) Check environment variables inside the container shell. 4) Use configuration validation tools (e.g., `nginx -t`) to catch syntax errors.
1) Check if the service is correctly registered in the discovery tool (Consul/K8s Service). 2) Verify that the 'selectors' match the 'labels' on the target pods. 3) Test the internal DNS record (e.g., `my-service.namespace.svc.cluster.local`). 4) Check for NetworkPolicies that might be blocking communication between the consumer and the service.
1) Check if the database service is running. 2) Verify the connection string (Host, Port, User, DB Name). 3) Check Security Groups to ensure the application IP is whitelisted. 4) Check if the database has reached its maximum connection limit. 5) Test connectivity using a CLI client like `psql` or `mysql` from the app server.
1) Check the certificate expiration date using `openssl s_client -connect host:443`. 2) Verify that the Common Name (CN) or SAN matches the domain. 3) Ensure the full certificate chain (including CA) is installed. 4) Check for 'Mixed Content' issues in the browser console where HTTPS pages load HTTP assets.
1) Check the 'Target Group' health checks; if they are 'Unhealthy', the LB will drop traffic. 2) Verify the LB listeners are configured for the correct ports. 3) Check if the Security Group allows traffic from the LB to the instances. 4) Review LB access logs to see if it's returning 5xx errors from the backend.
1) Look for HTTP error codes (4xx for client errors, 5xx for server errors). 2) Check API Gateway logs for timeout or throttling issues. 3) Use `curl -v` to see the full request/response headers. 4) Review application-side logs for stack traces or unhandled exceptions.
1) Look for patterns in time, specific nodes, or specific versions. 2) Increase logging levels to 'DEBUG'. 3) Use distributed tracing (Jaeger) to see if the issue is a race condition or a specific slow downstream dependency. 4) Correlate the issue with external events (e.g., high traffic spikes or scheduled cron jobs).
I use a 'Swiss Army Knife' of tools: Linux: `top`, `htop`, `lsof`, `strace`, `tcpdump`. Network: `ping`, `mtr`, `nc`, `dig`. K8s: `kubectl`, `stern`, `k9s`. Cloud: AWS CloudWatch, CloudTrail. Monitoring: Grafana, Prometheus, ELK.
I use the '5 Whys' technique. 1) Define the problem. 2) Gather evidence (logs/metrics). 3) Ask 'Why' until the underlying systemic failure is found. 4) Create a timeline of the incident. 5) Define 'Action Items' to prevent recurrence (e.g., adding an automated test or increasing monitoring).
Scenario20
1) Source in Git (GitHub/GitLab). 2) CI triggers on PR: Linter, Unit Tests, SAST. 3) Build stage: Create Docker image and push to ECR/Artifactory. 4) Dev/Staging Deploy: Use Helm to deploy to K8s. 5) Integration & DAST tests. 6) Production Deploy: Blue-Green or Canary using ArgoCD/GitOps.
I would use Rolling Updates in Kubernetes or Blue-Green Deployment. Rolling updates slowly replace old pods with new ones. Blue-Green involves running two identical environments and switching traffic at the Load Balancer level once the 'Green' environment is verified as healthy.
1) Metrics: Prometheus for scraping, Grafana for visualization. 2) Logs: ELK or EFK stack for centralizing logs. 3) Tracing: Jaeger for microservice bottlenecks. 4) Alerting: Alertmanager configured with Slack for warnings and PagerDuty for critical production failures based on the 'Four Golden Signals'.
1) Containerize the app with Docker. 2) Identify dependencies (DBs, Caches). 3) Write K8s manifests (Deployments, Services, ConfigMaps). 4) Set up a CI/CD pipeline to build/push images. 5) Perform a pilot migration in a 'Dev' namespace. 6) Use a DNS cutover to move traffic to the K8s cluster.
1) Backup: Regular RDS snapshots and S3 versioning. 2) Infrastructure: Terraform code stored in Git to recreate the environment. 3) Multi-Region: Cross-region replication for data. 4) Strategy: Choose between 'Pilot Light' (minimal resources) or 'Warm Standby' based on the business RTO/RPO requirements.
I would use a two-pronged approach: 1) HPA (Horizontal Pod Autoscaler) to scale pods based on CPU/RAM usage. 2) Cluster Autoscaler (on AWS/GCP) to automatically add or remove physical nodes to the cluster when the HPA creates more pods than the current nodes can handle.
1) Global Load Balancer (AWS Route 53 Geolocation or Global Accelerator). 2) Independent K8s clusters in each region. 3) Global database replication (e.g., Aurora Global Database). 4) CI/CD pipeline that can deploy to all regions sequentially or in parallel with region-specific configurations.
1) Right-sizing instances based on actual usage. 2) Using Spot Instances for non-critical workloads (CI agents). 3) Implementing S3 Lifecycle policies to move old data to Glacier. 4) Scheduling 'Auto-stop' for Dev environments during non-working hours. 5) Deleting orphaned EBS volumes and idle Load Balancers.
1) Code scanning (SAST/SCA). 2) Secrets scanning (prevent commits of keys). 3) Container image signing and vulnerability scanning. 4) Use of a private registry. 5) Least-privilege IAM roles for the CI/CD service account. 6) Audit logs enabled for all pipeline actions.
1) Detect: Monitoring alerts trigger. 2) Communicate: Inform stakeholders and start a bridge call. 3) Mitigate: Rollback the latest change or scale up resources. 4) Analyze: Once stable, investigate logs to find the cause. 5) RCA: Write a post-mortem and implement fixes to prevent a repeat.
My strategy follows the 3-2-1 rule: 3 copies of data, on 2 different media, with 1 off-site. Technically, I implement automated snapshots (AWS RDS/EBS), enable S3 Cross-Region Replication for off-site storage, and version control all database migration scripts. I perform quarterly 'Game Day' exercises where we practice restoring the entire stack from scratch to verify the RTO (Recovery Time Objective).
I set up two identical environments: Blue (current production) and Green (new version). In Kubernetes, this can be done by deploying a new set of pods and then updating the Service's 'selector' or using an Ingress weighted routing. Once Green is verified, 100% of traffic is switched via the Load Balancer. If issues arise, a single click redirects traffic back to Blue.
I implement a centralized stack: 1) Collection: Fluent-bit as a DaemonSet to collect logs; Prometheus for metrics. 2) Storage: Elasticsearch for logs; Prometheus TSDB for metrics; Jaeger for traces. 3) Visualization: Kibana for log searching; Grafana for real-time dashboards. I ensure every log entry includes a 'Correlation ID' to trace requests across service boundaries.
1) Audit: Identify OS dependencies and hardcoded configs. 2) Environment: Use a multi-stage Dockerfile to keep the image slim. 3) Externalize: Move local file storage to S3/EFS and hardcoded configs to environment variables. 4) Logging: Change app logs to output to stdout/stderr (12-factor app principle). 5) Orchestration: Deploy to a small K8s cluster with a persistent volume if state is still required.
I use a 'Zero Trust' approach. Secrets are stored in HashiCorp Vault. Applications use a 'Sidecar' container or an Init-container to fetch secrets at runtime via a ServiceAccount token. I implement 'Dynamic Secrets' where Vault generates temporary DB credentials that expire after an hour, significantly reducing the impact of a potential leak.
I use a Service Mesh like Istio or an Ingress Controller like Nginx. I deploy the new version to a small subset of pods (e.g., 5% of the total). I use traffic splitting to route 5% of users to the canary. I monitor the 'Error Rate' and 'Latency' of the canary; if stable, I gradually increase the percentage (10%, 25%, 50%, 100%) until the rollout is complete.
A multi-cloud strategy focuses on avoiding vendor lock-in and improving availability. I use Terraform as the agnostic IaC tool and Kubernetes as the compute abstraction layer. I use a 'Global Traffic Manager' (like Cloudflare or Akamai) to route users to whichever cloud (AWS/GCP/Azure) is performing better or is currently available.
1) Front-end: Implement a CDN (CloudFront) and browser caching. 2) Compute: Right-size instances and use Auto-scaling. 3) Database: Add Read Replicas, implement Redis caching, and optimize slow SQL queries identified by APM. 4) Network: Use a Service Mesh to optimize internal communication and move to gRPC for low-latency microservice calls.
I use ArgoCD and a dedicated 'Infrastructure Repo'. 1) Developers push code to an App Repo. 2) CI builds an image and updates the image tag in the Infrastructure Repo. 3) ArgoCD detects the change in the Infrastructure Repo and automatically 'pulls' the new state into the Kubernetes cluster. This ensures that Git is always the single source of truth for the production environment.
I start by defining the VPC and core networking in Terraform. I use 'Modules' to make the code reusable. All Terraform state is stored in a remote backend (S3 with DynamoDB locking). I run `terraform plan` in a CI/CD pipeline (GitHub Actions) for every PR, requiring peer review before the `apply` is triggered by a merge to the main branch.
Behavioral15
I focus on a complex migration or a massive system failure. I explain the Situation, the Task (e.g., migrating 50 services to K8s), the Action I took (automating the manifest creation, handling persistent data), and the Result (zero downtime, 40% reduction in deployment time).
1. Stabilize: Focus on fixing the issue first (rollback or scale up), not finding the root cause. 2. Communicate: Use a status page or Slack to keep stakeholders informed. 3. Analyze: Once stable, dive into logs and metrics. 4. Post-Mortem: Conduct a blameless review to identify systemic fixes, not individuals to blame.
I noticed developers spent 2 hours weekly manually updating staging environments. I wrote a Python script integrated with GitHub Actions that automated the entire process. This saved 8 hours of engineering time monthly and eliminated human errors during config updates.
I follow CNCF (Cloud Native Computing Foundation) announcements, read engineering blogs from companies like Netflix and Uber, subscribe to newsletters like 'DevOps Weekly', and experiment with new tools (like Crossplane or OpenTelemetry) in my personal lab.
I implemented Liveness and Readiness probes and defined Resource Requests/Limits for all pods in our K8s cluster. This prevented 'noisy neighbors' from crashing other services and allowed K8s to automatically restart hung containers, reducing manual interventions by 70%.
I approach it as a partner, not a gatekeeper. If a developer wants to skip a security scan for speed, I show them the 'why' (e.g., a critical CVE). We work together to optimize the scan time or find a 'middle ground' (like scanning in parallel) that doesn't compromise stability.
I am used to a rotating schedule. I prioritize documentation and 'Runbooks' so that when a 2 AM page happens, the person on call has clear steps to follow. My goal is always to automate the fix for recurring issues so the pager doesn't go off for the same thing twice.
I use the Impact vs. Urgency matrix. Production outages always come first. Security vulnerabilities come second. Long-term automation projects are scheduled to prevent the first two from happening. I communicate clearly with my manager if a priority shift means a deadline will be missed.
Early in my career, I ran a `terraform destroy` in the wrong terminal (Prod instead of Dev). I immediately alerted my lead, used our DR plan to restore from backups, and then implemented State Locking and CI/CD gates to ensure no one could run 'destroy' from a local terminal again.
I stay focused on the logs and the metrics. I follow the 'Incident Commander' model where one person directs the effort and others perform tasks. Clear, calm communication is key to preventing panic, which leads to more mistakes.
I'm comfortable with 2-week sprints, daily stand-ups, and retrospectives. In DevOps, I treat 'Infrastructure' as a product, using Jira to track stories and ensuring we deliver 'incremental' value (like a new monitoring dashboard) every sprint.
I avoid jargon. Instead of saying 'The Nginx ingress is returning 504 Gateway Timeouts,' I say, 'The system is overloaded and can't handle the current number of users. We are adding more capacity now to fix the delay.' I focus on the 'impact' and the 'resolution time'.
I conducted a 'Cloud Waste' audit and found we had 50 idle load balancers and hundreds of unattached EBS volumes. By cleaning these up and moving our CI/CD agents to AWS Spot Instances, I reduced the monthly cloud bill by 25% ($5,000/month).
This is the core of DevOps. I use Automation to provide speed and Testing/Gating to provide stability. If a deployment is slow, I don't remove tests; I parallelize them. If it's unstable, I add automated health checks and rollbacks.
I led the transition from manual SSH deployments to Ansible. I started by containerizing one small service as a PoC (Proof of Concept), demonstrated the time savings to the team, and then created a migration plan that gradually moved all services over 3 months.
Advanced Topics25
GitOps is an operational framework that takes DevOps best practices (version control, collaboration, CI/CD) and applies them to infrastructure automation. The core idea is that Git is the 'Single Source of Truth'. Any change in Git is automatically synced to the live environment by a controller.
These are the two leading GitOps controllers for Kubernetes. They sit in the cluster and 'pull' the desired state from Git. ArgoCD has a powerful UI and is great for multi-tenancy; FluxCD is more lightweight and follows a more 'native' Kubernetes CLI approach.
A Service Mesh is a dedicated infrastructure layer built into an app to handle service-to-service communication. It provides features like mutual TLS (encryption), traffic splitting, retries, and deep observability (tracing) without requiring changes to the application code.
Envoy is a high-performance L7 proxy designed for large modern service-oriented architectures. It is most commonly used as the 'Sidecar' proxy in service meshes like Istio, handling all inbound and outbound traffic for a service while the service itself remains unaware.
Chaos engineering is the discipline of experimenting on a system in order to build confidence in its capability to withstand turbulent conditions in production. I use tools like 'Chaos Mesh' or 'AWS Fault Injection' to intentionally kill pods or induce latency to test our self-healing and alerting.
Infrastructure that is never modified after it's deployed. If a change is needed, a new server/container is built from a common image with the change, and the old one is destroyed. This eliminates 'Configuration Drift' and makes deployments much more predictable.
An architecture where the cloud provider manages the server allocation. Developers only focus on individual functions (FaaS) or services. It scales automatically and you only pay for what you use. Examples include AWS Lambda, Fargate, and S3.
FaaS is a category of cloud computing services that provides a platform allowing customers to develop, run, and manage application functionalities without the complexity of building and maintaining the infrastructure typically associated with developing and launching an app.
A distributed computing paradigm that brings computation and data storage closer to the sources of data (the 'edge' of the network). This reduces latency and bandwidth use. Examples include AWS Lambda@Edge or Cloudflare Workers.
The automated process of managing the lifecycle of containers, including their deployment, scaling, networking, and availability. Kubernetes is the leading orchestrator, handling tasks like 'Self-healing' (restarting failed containers) and 'Load balancing' automatically.
Nomad is a flexible, enterprise-grade cluster orchestrator. Unlike Kubernetes which is container-only, Nomad can manage containers, legacy applications (Java, binaries), and even virtual machines. It is much simpler to operate than K8s but has a smaller ecosystem.
Consul is a multi-cloud service networking platform. I use it for Service Discovery (finding where services are running), Health Checking, and as a Key-Value Store for configuration. It's often used alongside Nomad or in hybrid-cloud environments.
OpenShift is Red Hat's enterprise-grade Kubernetes distribution. It adds additional features like integrated CI/CD, a built-in container registry, and stricter security defaults. It's designed for large organizations that need a 'Turnkey' Kubernetes experience with full support.
Rancher is an open-source multi-cluster management platform. It allows you to manage multiple Kubernetes clusters across different cloud providers (EKS, GKE, On-prem) from a single 'pane of glass' UI, simplifying operations like cluster upgrades and access control.
Platform Engineering is the discipline of designing and building toolchains and workflows that enable self-service capabilities for software engineering teams. The goal is to build an Internal Developer Platform (IDP) that reduces cognitive load and allows devs to deploy code without knowing the underlying K8s/Cloud complexity.
An IDP is a layer on top of a company's infrastructure that provides 'Golden Paths' for developers. It allows a developer to 'Provision a Database' or 'Create a Preview Environment' via a simple CLI or UI, with all the company's security and cost standards pre-baked in.
Policy as Code (like Open Policy Agent) allows you to define compliance and security rules using code. For example, I can write a policy that says 'No Kubernetes service can be created without a LoadBalancer label.' If a dev tries to push a manifest that breaks this, the CI/CD pipeline or K8s Admission Controller blocks it.
FinOps (Cloud Financial Management) is the practice of bringing financial accountability to the variable spend model of the cloud. It involves developers, finance, and business teams working together to optimize costs (e.g., using Reserved Instances, right-sizing) while maintaining performance.
SRE is a discipline that incorporates aspects of software engineering and applies them to infrastructure and operations problems. As Google says, 'SRE is what happens when you ask a software engineer to design an operations team.' It focuses on availability, latency, performance, and capacity.
An error budget is the maximum amount of time a technical system can fail without contractual consequences. If an SLO is 99.9% uptime, the error budget is 0.1%. If the budget is exhausted, the team stops new feature releases and focuses entirely on reliability.
Toil is the kind of work tied to running a production service that tends to be manual, repetitive, automatable, tactical, and devoid of enduring value. SRE teams aim to limit toil to less than 50% of their time, spending the rest on project work that improves the system.
A version control strategy where developers merge small, frequent updates to a core 'trunk' (usually the main branch). It is a key enabler of CI/CD because it avoids 'Merge Hell' and ensures the codebase is always in a deployable state.
A technique that allows teams to change system behavior without changing code. I can deploy a new feature to production but keep it 'hidden' behind a toggle. This allows for 'Dark Launches' and safer testing of new code in the real environment.
Drift detection is the process of identifying when the actual state of your infrastructure (e.g., someone manually changed a Security Group in the AWS console) no longer matches the desired state in your code (Terraform). Tools like Terraform or AWS Config alert you to these changes.
The future is moving toward AIOps (using AI for root-cause analysis and automated remediation), Platform Engineering (hiding complexity from developers), and Zero Trust by default. We are moving away from manual scripting and toward 'Declarative' everything, where we describe 'what' we want, and the system handles 'how'.
Related question banks5
Git & GitHub Questions
200 questionsComprehensive guide to Version Control basics, core Git commands, branching strategies, and remote synchronization. Essential for developers and DevOps engineers.
AWS Questions
229 questionsTechnical guide covering AWS Cloud Fundamentals, Compute, Storage, Networking, Security, and DevOps practices.
Docker Questions
149 questionsComprehensive technical guide covering Docker Architecture, Images, Networking, Volumes, Orchestration (Swarm/K8s), and Security.
Kubernetes Questions
200 questionsA deep-dive technical guide covering K8s Architecture, Workloads, Networking, Storage, Security, and Troubleshooting scenarios.
Terraform Questions
141 questionsComprehensive technical guide covering Terraform HCL, State Management, Modules, Providers, and CI/CD best practices.