AWSLearn
DevOps Interview Hub
Ace your next technical interview. Browse battle-tested questions and detailed answers covering AWS architecture, Kubernetes orchestration, Linux system calls, CI/CD, and Terraform state management.
Security Groups and Network Access Control Lists (NACLs) are the two primary layers of security in an Amazon VPC:
Statefulness:
- Security Groups are stateful. If you send a request from your instance, the response traffic is automatically allowed back in, regardless of inbound rules.
- NACLs are stateless. Return traffic must be explicitly allowed by rules in the opposite direction.
Level of Operation:
- Security Groups operate at the instance/ENI level (Elastic Network Interface).
- NACLs operate at the subnet level, acting as a firewall for all instances within that subnet.
Rules:
- Security Groups only support allow rules. By default, all outbound traffic is allowed, and all inbound traffic is blocked.
- NACLs support both allow and deny rules, processed in numbered order.
Here is a summary table:
| Feature | Security Group | Network ACL (NACL) |
|---|---|---|
| Scope | Instance / ENI level | Subnet level |
| State | Stateful | Stateless |
| Rules supported | Allow rules only | Allow & Deny rules |
| Order of execution | All rules evaluated | Processed sequentially |
AWS RDS offers two primary features for database scaling and reliability:
RDS Multi-AZ (High Availability):
- Purpose: Disaster Recovery and High Availability.
- Mechanism: Synchronous replication from a primary DB instance to a standby DB instance in a different Availability Zone (AZ).
- Behavior: AWS automatically handles failover to the standby DB if the primary DB fails, preserving the same DNS endpoint. You cannot read from or write to the standby instance directly.
RDS Read Replicas (Scalability):
- Purpose: Read scalability and performance optimization.
- Mechanism: Asynchronous replication from the primary instance to one or more read-only replica databases.
- Behavior: You can query read replicas to offload read traffic (BI dashboards, reporting, static reads). Replicas can be promoted to standalone databases.
Here is how you might configure a Read Replica using Terraform:
# Primary Database
resource "aws_db_instance" "primary" {
identifier = "db-primary"
allocated_storage = 20
engine = "postgres"
engine_version = "15.4"
instance_class = "db.t3.micro"
username = "cloudadmin"
password = "SecurePassword123!"
backup_retention_period = 7 # Required to enable replication
}
# Read Replica DB
resource "aws_db_instance" "replica" {
identifier = "db-replica"
replicated_from_source = aws_db_instance.primary.identifier
instance_class = "db.t3.micro"
skip_final_snapshot = true
}
Kubernetes uses three types of container probes to monitor pod health:
startupProbe:
- Purpose: Checks if the application inside the container has successfully started.
- Behavior: All other probes (liveness/readiness) are disabled until the startupProbe succeeds. Useful for slow-starting legacy apps to prevent them from being killed prematurely.
livenessProbe:
- Purpose: Checks if the container needs to be restarted.
- Behavior: If this probe fails, the kubelet kills the container, and the pod's restart policy is triggered.
readinessProbe:
- Purpose: Checks if the container is ready to accept client traffic.
- Behavior: If this probe fails, the pod is removed from the Endpoints of all matching Services, preventing traffic from reaching it.
Here is an example YAML snippet from a Deployment manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
template:
spec:
containers:
- name: api-container
image: node-api:v1.0
ports:
- containerPort: 8080
# 1. Startup Probe
startupProbe:
httpGet:
path: /healthz/startup
port: 8080
failureThreshold: 30
periodSeconds: 10
# 2. Liveness Probe
livenessProbe:
httpGet:
path: /healthz/live
port: 8080
initialDelaySeconds: 5
periodSeconds: 15
# 3. Readiness Probe
readinessProbe:
httpGet:
path: /healthz/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
DaemonSet:
- Ensures that exactly one copy of a Pod runs on all (or a filtered selection of) Nodes in the cluster.
- As new nodes are added to the cluster by autoscalers, the DaemonSet automatically schedules pods onto them.
- Use Cases: System daemons like log shippers (Fluentbit), monitoring agents (Prometheus Node Exporter), or networking plugins (kube-proxy).
Deployment:
- Declares a desired number of replica Pods to run.
- The scheduler assigns them to nodes based on resource capacity, affinity, and anti-affinity rules, without binding them to every single node.
- Use Cases: Stateless applications, APIs, frontends, and microservices.
A multi-stage build allows you to use multiple FROM instructions in a single Dockerfile. Each FROM begins a new stage of the build using a different base image, allowing you to copy artifacts selectively from one stage to another.
Why use it?
- Reduces Image Size: Separates build-time dependencies (compilers, SDKs, test libraries) from runtime packages.
- Improves Security: Deploys only compiled binaries, minimizing tools that an attacker could exploit.
- Simplifies Pipeline: Eliminates the need to maintain separate build and run scripts.
Example Multi-stage Dockerfile:
# Stage 1: Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Runtime stage
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["npm", "start"]
Both CMD and ENTRYPOINT define the command executed when running a container, but they behave differently when arguments are passed:
ENTRYPOINT:
- Sets the default executable wrapper that will always be run when the container starts.
- It cannot be overridden by arguments passed to
docker run(unless you use the explicit--entrypointflag).
CMD:
- Defines the default arguments passed to the
ENTRYPOINT. - If no
ENTRYPOINTis defined,CMDis treated as the executable command. - Override: Passing any argument to
docker run <image> <args>completely overrides theCMDinstruction.
- Defines the default arguments passed to the
Best Practice Example:
Use them in combination. The ENTRYPOINT points to the binary, and CMD acts as the default argument.
ENTRYPOINT ["ping"]
CMD ["localhost"]
If run normally: docker run my-ping -> executes ping localhost.
If run with arguments: docker run my-ping google.com -> executes ping google.com.
Load average represents the average system load over 1, 5, and 15 minutes. It measures CPU demand, representing the number of processes that are:
- Running on the CPU.
- Runable and waiting for CPU time.
- In uninterruptible sleep state (blocked, usually waiting for disk or network I/O).
Interpreting the Values
You must know the number of CPU cores in the system (e.g. check nproc or htop).
Single Core CPU:
- Load average of 0.5: CPU is idle 50% of the time.
- Load average of 1.0: CPU is exactly at 100% capacity.
- Load average of 2.0: The queue is full; CPU is overloaded, and processes are waiting for 50% of their execution time.
4-Core CPU:
- Load average of 2.0: CPU is at 50% overall capacity.
- Load average of 4.0: CPU is at 100% capacity.
- Load average of 8.0: CPU is overloaded; 4 active processes are running on the cores, and 4 others are waiting in queue.
You can inspect load average using commands like uptime, w, or reading /proc/loadavg:
$ uptime
18:14:02 up 12 days, 3:14, 1 user, load average: 0.85, 1.20, 2.45
The Linux boot sequence consists of the following steps:
- BIOS / UEFI: Runs Power-On Self Test (POST), detects hardware configurations, and executes the primary Boot Loader from the Master Boot Record (MBR) or EFI partition.
- GRUB Bootloader: Loads the selected kernel image (
vmlinuz) and initial RAM disk (initramfs) into memory. - Kernel Initialization: Mounts the temporary root file system, configures hardware drivers, and executes the initial user space process.
- systemd: The kernel spawns
sbin/init, which is symlinked tosystemdon modern distributions.
The Role of systemd
systemd is the system init manager (Process ID 1) responsible for bootstrapping user space services.
- Targets: Group files (e.g.
multi-user.target) that define system states, replacing old SysV init runlevels. - Parallelization: Starts multiple system services in parallel utilizing socket activation to speed up boot times.
- Dependency Management: Defines exact relationships between services using parameters like
Requires=,After=, andWants=in unit configuration files.
For example, a basic systemd service file (/etc/systemd/system/app.service):
[Unit]
Description=AWSLearn Node Backend API
After=network.target
[Service]
Type=simple
User=node
WorkingDirectory=/opt/app
ExecStart=/usr/bin/npm start
Restart=on-failure
[Install]
WantedBy=multi-user.target
Terraform uses a state file (terraform.tfstate) to store the mappings between declared resource blocks in code and real-world resources provisioned in the cloud provider.
Why Remote State is Critical
- Team Collaboration: Storing state locally prevents multiple developers from working together. Remote backends (like AWS S3) act as a single source of truth.
- State Locking: Prevents concurrent execution of
terraform applythat could cause state corruption or duplicate resources. S3 uses DynamoDB tables to acquire locks. - Sensitive Information: State files contain passwords, database strings, and private keys in plain text. Storing state remotely permits restricting access using IAM policies and encrypting at rest.
Here is a standard Terraform configuration for a remote backend using S3 and DynamoDB:
terraform {
backend "s3" {
bucket = "awslearn-tf-state-bucket"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-locking-table"
}
}
Both commands integrate changes from one branch into another, but they differ in how they write commit history:
git merge:
- Mechanism: Creates a new "merge commit" that ties the history of both branches together.
- Pros: Preserves the exact chronological history and context of when branches were merged. It is non-destructive.
- Cons: Can pollute git history with frequent merge commits, making visual graphs hard to read.
git rebase:
- Mechanism: Moves the entire base of your branch to the tip of the target branch, rewriting commit hashes sequentially.
- Pros: Produces a clean, linear git history with no merge commits.
- Cons: Rewrites commit history. If done on shared public branches, it can cause sync chaos for other developers.
Rules of Thumb
- Rebase local branches before opening a Pull Request to align your work with the latest changes in the main branch.
- Merge branches when merging a Pull Request into main, preserving the integration point in the main branch.
- Rule: Never rebase commits that have been pushed to a public repository.
CI/CD pipelines automate code validation and shipping, but there is a distinct boundary between Continuous Delivery and Continuous Deployment:
Continuous Delivery:
- Code changes are automatically built, tested, and pushed to a repository (like Docker Hub or S3).
- Manual step: Deploying the release package to production requires manual trigger/approval (e.g. clicking a "Deploy to Prod" button in GitHub Actions or AWS CodePipeline).
Continuous Deployment:
- Every single commit that successfully passes the automated tests and quality checks is automatically deployed directly to production without any human intervention.
- Requires mature test suites, automated rollbacks, and canary release strategies.
Here is a visual progression flow:
Continuous Integration (CI):
[Code] -> [Build] -> [Test]
Continuous Delivery (CD):
[CI Flow] -> [Release Packaged] -> [Manual Staging Review] -> (Manual Approval) -> [Deploy to Prod]
Continuous Deployment (CD):
[CI Flow] -> [Release Packaged] -> [Deploy to Prod (Automated)]
GitOps is an operational model for Kubernetes and cloud infrastructure that uses Git as the single source of truth for declarative state.
Push-based Pipelines (Traditional)
- Flow: A CI system (like GitHub Actions, GitLab CI) runs a script that logs into the cloud environment (e.g. running
kubectl applyorterraform apply) to push configuration. - Risk: CI runners must store admin credentials (like kubeconfig, AWS keys) locally.
Pull-based Pipelines (GitOps)
- Flow: An operator (like ArgoCD or Flux) runs inside the Kubernetes cluster. It constantly polls the target Git repo, detects differences between code and cluster state, and pulls changes to reconcile them.
- Security: No external access credentials need to be exposed. The cluster pulls changes internally.
Here is a side-by-side comparison:
| Feature | Push-based CI/CD | Pull-based (GitOps) |
|---|---|---|
| Trigger | Code change -> CI runner runs | Git controller detects drift |
| Credentials | Stored in CI variables | Stored securely in cluster |
| Drift Correction | Runs only on code push | Corrects drift automatically |
| Source of Truth | Git + deployment runner | Git repository |
Get Cloud Architecture Tutorials
Join 10,000+ cloud developers. Subscribe to receive our bi-weekly tutorials on AWS, Kubernetes, Terraform, and system design patterns.
We respect your privacy. Unsubscribe at any time.