Amazon EKS — CI/CD & GitOps Hands-On Lab

3 minute read

In this lab, you will set up ECR, build and push a container image, install ArgoCD on EKS, and deploy an application using GitOps.


Prerequisites

  • Running EKS cluster
  • AWS CLI, kubectl, Helm configured
  • GitHub account

Step 1 — Create ECR Repository

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=us-east-1

aws ecr create-repository \
  --repository-name demo-app \
  --image-scanning-configuration scanOnPush=true \
  --image-tag-mutability IMMUTABLE

ECR_REPO="$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/demo-app"

Step 2 — Build and Push a Sample Image

# Create a simple app
mkdir demo-app && cd demo-app

cat > main.go << 'EOF'
package main

import (
    "fmt"
    "net/http"
    "os"
)

func main() {
    version := os.Getenv("APP_VERSION")
    if version == "" {
        version = "1.0.0"
    }
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello from EKS! Version: %s\n", version)
    })
    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    })
    fmt.Println("Starting server on :8080")
    http.ListenAndServe(":8080", nil)
}
EOF

cat > Dockerfile << 'EOF'
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY main.go .
RUN go build -o demo-app main.go

FROM alpine:3.19
COPY --from=builder /app/demo-app /demo-app
EXPOSE 8080
CMD ["/demo-app"]
EOF

# Build and push
aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com

docker build -t $ECR_REPO:v1.0.0 .
docker push $ECR_REPO:v1.0.0

Step 3 — Create Kubernetes Manifests in Git

Create a GitHub repository called demo-app-manifests with this structure:

demo-app-manifests/
├── deployment.yaml
├── service.yaml
└── namespace.yaml
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: demo

Replace ACCOUNT_ID with your actual AWS account ID:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
  namespace: demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-app
  template:
    metadata:
      labels:
        app: demo-app
    spec:
      containers:
        - name: demo-app
          image: ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/demo-app:v1.0.0
          ports:
            - containerPort: 8080
          env:
            - name: APP_VERSION
              value: "1.0.0"
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
          resources:
            requests:
              cpu: 100m
              memory: 64Mi
            limits:
              cpu: 200m
              memory: 128Mi
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: demo-app
  namespace: demo
spec:
  selector:
    app: demo-app
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

Push to GitHub.


Step 4 — Install ArgoCD

kubectl create namespace argocd

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s

# Get admin password
ARGOCD_PASSWORD=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)
echo "ArgoCD password: $ARGOCD_PASSWORD"

# Port forward to access UI
kubectl port-forward svc/argocd-server -n argocd 8080:443 &

Access ArgoCD at https://localhost:8080 (username: admin).


Step 5 — Create ArgoCD Application

# Install ArgoCD CLI
brew install argocd

argocd login localhost:8080 --username admin --password $ARGOCD_PASSWORD --insecure

# Create application
argocd app create demo-app \
  --repo https://github.com/YOUR_ORG/demo-app-manifests.git \
  --path . \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace demo \
  --sync-policy automated \
  --auto-prune \
  --self-heal

# Check status
argocd app get demo-app

Or apply as YAML:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: demo-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/YOUR_ORG/demo-app-manifests.git
    targetRevision: main
    path: .
  destination:
    server: https://kubernetes.default.svc
    namespace: demo
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
kubectl apply -f argocd-app.yaml

Step 6 — Verify Deployment

# Check ArgoCD sync status
argocd app get demo-app

# Check pods
kubectl get pods -n demo

# Test the app
kubectl port-forward svc/demo-app -n demo 9090:80 &
curl http://localhost:9090
# Output: Hello from EKS! Version: 1.0.0

Step 7 — Test GitOps (Update via Git)

Update the image tag in deployment.yaml:

image: ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/demo-app:v2.0.0

Commit and push. ArgoCD will detect the change and deploy automatically within ~3 minutes.

# Watch ArgoCD sync
argocd app get demo-app --refresh

# Verify new version
curl http://localhost:9090
# Output: Hello from EKS! Version: 2.0.0

Cleanup

argocd app delete demo-app --cascade
kubectl delete namespace argocd
kubectl delete namespace demo
aws ecr delete-repository --repository-name demo-app --force
kill %1 %2    # Stop port-forwards

Updated: