Deploy Docker To Ec2

Deploy Docker To Ec2

Reading time1 min
#Cloud#DevOps#Containers#Docker#AWS#EC2

Seamless Docker Deployment on AWS EC2: An Engineer’s Practical Reference

Cloud-native doesn’t always mean orchestration at scale. Sometimes, direct control is essential—whether for hybrid migrations, systems with brittle network dependencies, or running bespoke workloads with exact kernel requirements. EC2 with Docker offers this control, but deployments are only as robust as their weakest operational detail.


EC2 for Docker: Rationale and Constraints

Why not just use ECS, Fargate, or Kubernetes? In certain cases, teams need:

  • Custom OS images for legacy libraries or device drivers.
  • Persistent network identities or static EIPs for whitelisted partners.
  • Tight coupling with other EC2-bound services.
  • Non-standard VPC, IAM, or EBS configurations.

But direct responsibility means direct risk: patching, cgroup accounting, and SIGTERM handling become your concern.


Launch EC2: Practical Steps

Spin up the instance. Don’t default to t2.micro: for small Node.js or Go applications, t3.medium is the baseline—older families or too-small types throttle CPU credits unpredictably.

AMI selection:

DistributionVersionNotes
Amazon Linux 2latestFast Docker support, lightweight
Ubuntu22.04 LTSLarger ecosystem, apt-based workflow

When configuring the Security Group, open only the absolute minimum—typically TCP 22 for SSH, and the public/external facing port your container exposes (e.g., 80 or 443). Do not open direct Docker API ports (2375/2376).

Side note: Attach an IAM role with only the permissions required for your container. For S3 access, avoid wildcards—use resource-level policies for defense in depth.


Install Docker: No Nonsense Instructions

SSH to your instance:

ssh -i path/to/your-key.pem ec2-user@a.b.c.d

Substitute ubuntu for Ubuntu-based AMI.

Amazon Linux 2:

sudo yum update -y
sudo amazon-linux-extras enable docker
sudo yum install -y docker
sudo systemctl enable --now docker
sudo usermod -aG docker ec2-user

Ubuntu 22.04:

sudo apt-get update
sudo apt-get install -y docker.io
sudo systemctl enable --now docker
sudo usermod -aG docker ubuntu

Log out and back in, or newgrp docker, to refresh group membership.

Check everything:

docker version
docker info

Docker engine <24.0 is still common in repos as of mid-2024. If you need BuildKit or Compose v2 integration, consider Docker’s official installation scripts instead of OS packages.


Ship Images: Local Build vs. Remote Pull

Where you build is not trivial. For large monolithic images, local EC2 builds can clog disk I/O or exceed root volume defaults (often <10GiB). Prefer build-and-push from CI/CD runner, then pull.

Typical Dockerfile for a production Node app:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
USER node
EXPOSE 3000
CMD ["node","server.js"]

Build and push (ideally from CI):

docker build -t yourrepo/myapp:latest .
docker login --username yourrepo
docker push yourrepo/myapp:latest

Note: For AWS ECR, use aws ecr get-login-password and tag appropriately.


Deploy and Operate the Container

Back on EC2:

docker pull yourrepo/myapp:latest
docker run -d --restart unless-stopped --name myapp -p 80:3000 yourrepo/myapp:latest

Why --restart unless-stopped? It ensures auto-recovery after reboots but allows you to stop the container for maintenance without it immediately respawning.

To mount persistent storage (critical if the app writes logs or uploads):

docker run -d \
  --restart unless-stopped \
  --name myapp \
  -p 80:3000 \
  -v /data/app-logs:/app/logs \
  yourrepo/myapp:latest

Automate the Flow: Simple Update Script

For single-instance CI/CD without full-blown agents, a Bash deployer minimizes manual error.

#!/bin/bash

NAME="myapp"
IMAGE="yourrepo/myapp:latest"

echo "Fetching latest image..." >&2
docker pull $IMAGE

if docker ps -q -f name=$NAME >/dev/null; then
  echo "Stopping and removing container..." >&2
  docker stop $NAME && docker rm $NAME
fi

echo "Launching container..." >&2
docker run -d --restart unless-stopped --name $NAME -p 80:3000 $IMAGE

Known issue: If your image has different EXPOSE or entrypoint on update, check for port clashes and review run args.


Practical Issues and Advanced Notes

  • User Data scripts: Automate setup (Docker install, image pull, run) via EC2 User Data. Example:

    #!/bin/bash
    yum update -y
    amazon-linux-extras install docker -y
    systemctl enable --now docker
    docker pull yourrepo/myapp:latest
    docker run -d --restart unless-stopped --name myapp -p 80:3000 yourrepo/myapp:latest
    
  • Health checks: Docker’s HEALTHCHECK doesn’t integrate with AWS ELB. Set up an Application Load Balancer probing container endpoints (/healthz) for real liveness tracking.

  • Monitoring: Install cloudwatch-agent for CPU/disk metrics, or docker stats for per-container use. In production, enable Docker’s log-driver with awslogs/syslog for log aggregation.

  • Security: Don’t run as root inside containers—use USER in Dockerfile. Use minimal base images and prune build dependencies. Never expose the Docker daemon API to 0.0.0.0.

  • Upgrades & patching: OS and Docker engine security patches are not automatic—integration with SSM Patch Manager or custom cron scripts is advised.

  • Disk space: Container log growth and orphaned images (see docker system prune) are a chronic source of silent failures.


Recap

Deploying Docker directly to EC2 remains a viable path for tightly controlled workloads, single-tenancy, or non-standard infrastructure requirements. Tighten security, script your updates, and monitor actively—AWS offloads hardware but you’re still wearing the sysadmin hat.

In production CI/CD, consider baking custom AMIs with Docker preinstalled, or using HashiCorp Packer to enforce baseline configs and accelerate autoscaling.
Multi-container/compose workloads? ECS or systemd-based supervision (with Restart=on-failure) is often less brittle than inventing your own orchestrator scripts.

Never treat single-instance patterns as production HA. For that, look to managed container platforms or true distributed designs.

Further reading: audit your user data scripts for secrets, rotate SSH keys on rebuild, and don’t assume instance storage sticks around after termination—or you’re in for an expensive surprise.