Aws
AwsIntermediate

aws-eks-gitops

12 min read

Implementing GitOps with AWS EKS and ArgoCD for Production Systems

GitOps, when applied effectively, transforms Kubernetes cluster management into a declarative, auditable, and automated process. This guide details how to establish a robust GitOps workflow using Amazon Elastic Kubernetes Service (EKS) and ArgoCD, targeting experienced engineers who need to manage production environments.

TL;DR: This guide outlines the integration of ArgoCD with AWS EKS for a GitOps workflow. It covers EKS cluster setup, ArgoCD installation and access, manifest repository structure, ArgoCD application definition, and best practices for production environments.

$1

GitOps is a paradigm for cloud-native application delivery and infrastructure management that leverages Git as the single source of truth. All desired cluster states and application configurations are version-controlled in Git. An automated agent, such as ArgoCD, continuously reconciles the cluster state with the Git repository.

Key benefits for production systems include:

* Auditable State: Every change is a Git commit, providing a complete audit trail of what changed, when, and by whom.

* Reproducibility: Entire environments can be recreated from Git, crucial for disaster recovery and testing.

* Reliability: Rollbacks are as simple as reverting a Git commit and letting GitOps reconcile.

* Developer Productivity: Developers manage infrastructure through familiar Git workflows.

* Enhanced Security: Reduced need for direct cluster access for deployments.

$1

Before proceeding, ensure you have:

1. An AWS account with sufficient IAM permissions to create and manage EKS clusters, IAM roles, and networking resources.

2. AWS CLI v2 installed and configured with credentials.

3. kubectl installed and configured to interact with AWS EKS.

4. eksctl installed for EKS cluster provisioning.

5. A Git provider account (e.g., GitHub, GitLab, Bitbucket) and a dedicated Git repository for cluster configurations.

$1

Production-grade EKS clusters require careful configuration beyond basic defaults. We'll use eksctl for initial provisioning, but emphasize that production setups should include robust networking, IAM, and logging configurations.

``bash

Example: Provisioning a production-ready EKS cluster

Consider using a dedicated VPC, subnets, and security groups for isolation.

Adjust instance types, scaling, and Kubernetes versions based on workload requirements.

eksctl create cluster \

--name my-prod-gitops-cluster \

--region us-west-2 \

--version 1.29 \

--nodegroup-name standard-workers \

--node-type t3.large \

--nodes 3 \

--nodes-min 2 \

--nodes-max 5 \

--managed \

--asg-access \

--external-dns-access \

--full-ecr-access \

--alb-ingress-access \

--ssh-access \

--with-oidc \

--scopes "eks:ClusterControlPlane:*" # Ensure necessary permissions for control plane

`

Note: For production, a dedicated VPC with appropriate subnet configurations (public/private) and NAT gateways is strongly recommended. IAM roles for service accounts (IRSA) are crucial for secure pod access to AWS services.

$1

ArgoCD will be the engine that synchronizes your Git repository with the EKS cluster.

`bash

Create the ArgoCD namespace

kubectl create namespace argocd

Apply the ArgoCD installation manifest

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

Monitor ArgoCD components until they are running

kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=argocd-server -n argocd --timeout=300s

kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=argocd-application-controller -n argocd --timeout=300s

`

$1

ArgoCD provides a web UI and a CLI for interaction.

$1

To access the UI securely, it's best practice to expose it via an Ingress controller or a LoadBalancer. For initial access or development, port-forwarding is convenient:

`bash

Retrieve the initial admin password

Note: In production, it's highly recommended to reset this password immediately.

initial_admin_secret=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)

echo "ArgoCD Initial Admin Password: $initial_admin_secret"

Port-forward the ArgoCD server (use a different local port if 8080 is in use)

kubectl port-forward svc/argocd-server -n argocd 8080:443

Access the UI at: https://localhost:8080

Username: admin

Password:

`

$1

Install the ArgoCD CLI and configure it to connect to your cluster:

`bash

Download and install ArgoCD CLI (example for Linux)

curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64

chmod +x argocd

sudo mv argocd /usr/local/bin/

Login to ArgoCD (if not using port-forwarding, adjust the --grpc-web flag and host)

argocd login localhost:8080 --username admin --password "$initial_admin_secret" --insecure

`

Security Note: For production, consider configuring ArgoCD to use TLS termination via an Ingress controller and integrate with your identity provider (e.g., OIDC, SAML) for authentication instead of relying on the static initial password.

$1

A well-organized Git repository is fundamental to GitOps. We recommend a structure that separates environments and leverages a configuration management tool like Kustomize or Helm.

Recommended Structure using Kustomize:

`

├── apps/

│ └── my-app/

│ ├── base/

│ │ ├── deployment.yaml

│ │ ├── service.yaml

│ │ ├── kustomization.yaml

│ ├── overlays/

│ │ ├── dev/

│ │ │ ├── kustomization.yaml

│ │ │ └── patch-replicas.yaml # Example patch

│ │ └── prod/

│ │ ├── kustomization.yaml

│ │ └── patch-resources.yaml # Example patch

├── clusters/

│ └── dev-cluster/

│ ├── argocd-apps/

│ │ ├── my-app.yaml

│ │ └── another-app.yaml

├── infrastructure/

│ └── ... # E.g., CNI, Ingress controller manifests

└── README.md

`

Example apps/my-app/base/deployment.yaml:

`yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: my-app

spec:

selector:

matchLabels:

app: my-app

template:

metadata:

labels:

app: my-app

spec:

containers:

- name: app-container

image: nginx:1.25-alpine # Use specific, immutable image tags

ports:

- containerPort: 80

resources:

requests:

memory: "64Mi"

cpu: "50m"

limits:

memory: "128Mi"

cpu: "100m"

`

Example apps/my-app/base/service.yaml:

`yaml

apiVersion: v1

kind: Service

metadata:

name: my-app-svc

spec:

selector:

app: my-app

ports:

- port: 80

targetPort: 80

type: ClusterIP # Use ClusterIP by default, expose via Ingress

`

Example apps/my-app/base/kustomization.yaml:

`yaml

apiVersion: kustomize.config.k8s.io/v1beta1

kind: Kustomization

resources:

- deployment.yaml

- service.yaml

Add common labels, annotations, or namespaces here if applicable to all environments

commonLabels:

app.kubernetes.io/managed-by: argocd

`

Example apps/my-app/overlays/dev/kustomization.yaml:

`yaml

apiVersion: kustomize.config.k8s.io/v1beta1

kind: Kustomization

resources:

- ../../base # Inherit base configurations

Environment-specific configurations for 'dev'

namespace: dev

patchesStrategicMerge:

- patch-replicas.yaml

- patch-image.yaml # Example to update image for a specific deployment

images:

- name: nginx

newName: nginx

newTag: 1.25-alpine # Ensure image tags are immutable and specific

`

Example apps/my-app/overlays/dev/patch-replicas.yaml:

`yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: my-app

spec:

replicas: 2 # Development environment may require fewer replicas

`

Example apps/my-app/overlays/prod/kustomization.yaml:

`yaml

apiVersion: kustomize.config.k8s.io/v1beta1

kind: Kustomization

resources:

- ../../base

namespace: prod

patchesStrategicMerge:

- patch-replicas.yaml

- patch-resources.yaml

`

Example apps/my-app/overlays/prod/patch-replicas.yaml:

`yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: my-app

spec:

replicas: 5 # Production environment requires higher replicas

`

Example apps/my-app/overlays/prod/patch-resources.yaml:

`yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: my-app

spec:

template:

spec:

containers:

- name: app-container

resources:

requests:

memory: "128Mi"

cpu: "100m"

limits:

memory: "256Mi"

cpu: "200m"

`

$1

An ArgoCD Application resource tells ArgoCD which Git repository to track, which path within that repository contains the manifests, and where in the cluster to apply them.

Create a YAML file for your ArgoCD Application, for example, clusters/dev-cluster/argocd-apps/my-app.yaml:

`yaml

apiVersion: argoproj.io/v1alpha1

kind: Application

metadata:

name: my-app-dev

namespace: argocd # ArgoCD applications are managed in the argocd namespace

spec:

project: default # Use projects for RBAC and organization

source:

repoURL: https://github.com/your-username/your-gitops-repo.git # Your Git repository URL

targetRevision: HEAD # Or a specific branch like 'main' or 'develop'

path: apps/my-app/overlays/dev # Path to Kustomize overlays for the dev environment

destination:

server: https://kubernetes.default.svc # Target cluster (default is in-cluster)

namespace: dev # Target namespace within the cluster

syncPolicy:

automated:

prune: true # Automatically prune resources deleted from Git

selfHeal: true # Automatically sync if the cluster state drifts from Git

syncOptions:

- CreateNamespace=true # Automatically create the target namespace if it doesn't exist

`

Apply this to your EKS cluster:

`bash

kubectl apply -f clusters/dev-cluster/argocd-apps/my-app.yaml

`

You can now see this application in the ArgoCD UI. Click "Sync" to apply the configurations, or let the automated sync (if enabled) handle it.

Multi-Cluster Strategy: For multi-cluster deployments, you'd create separate ArgoCD Application resources, potentially managed from a central Git repository, targeting different EKS clusters using their respective API server endpoints (e.g., https://).

$1

1. Developer Commits: A developer makes a code change and pushes it to the application's source code repository. Simultaneously, they update the Kubernetes manifests in the configuration Git repository (e.g., changing an image tag, resource limits).

2. Git Push: The developer commits and pushes changes to the configuration Git repository.

3. ArgoCD Detects Change: ArgoCD, configured to watch the specified repoURL and path, detects a new commit.

4. ArgoCD Pulls: ArgoCD pulls the latest manifests from the Git repository.

5. Reconciliation: ArgoCD compares the desired state (from Git) with the actual state of the EKS cluster.

6. Apply Changes: ArgoCD applies the necessary Kubernetes API calls (e.g., kubectl apply) to bring the cluster state in line with the Git repository.

7. Reporting: ArgoCD reports the synchronization status (Synced, OutOfSync, Syncing, Error) in its UI and via its API.

$1

Let's say you want to update the nginx image version for the dev environment.

1. Modify apps/my-app/overlays/dev/patch-image.yaml (or update the newTag directly in kustomization.yaml if not patching):

`yaml

# Example patch-image.yaml (if not updating kustomization.yaml directly)

apiVersion: apps/v1

kind: Deployment

metadata:

name: my-app

spec:

template:

spec:

containers:

- name: app-container

image: nginx:1.26-alpine # Updated image tag

`

2. Commit and push:

`bash

git add apps/my-app/overlays/dev/patch-image.yaml

git commit -m "feat: Update nginx image to 1.26 for dev environment"

git push origin main # Or your primary branch

`

3. ArgoCD will detect this change, pull it, and update the my-app-dev deployment in the dev namespace.

$1

* Repository Strategy:

* Monorepo vs. Polyrepo: For moderate complexity, a monorepo for configurations can be simpler. For large organizations, separate repos for applications and clusters (or environments) improve access control and modularity.

* Environment Separation: Clearly separate configurations for dev, staging, prod, etc., using overlays or separate directories.

* Immutable Image Tags: Always use specific, immutable image tags (e.g., nginx:1.26.0-alpine) instead of latest. Image updates should be a Git commit.

* Security:

* Access Control: Implement strict RBAC within ArgoCD and on EKS. Grant argocd-application-controller only the necessary permissions.

* Credentials: Use SSH keys or tokens for Git repository access, stored securely as Kubernetes secrets. Regularly rotate these credentials.

* Network Policies: Restrict network access to ArgoCD components and your EKS cluster.

* Manifest Scanning: Integrate security scanning tools (e.g., OPA Gatekeeper, Trivy) into your GitOps pipeline or as pre-commit hooks.

* Operational Hygiene:

* Health Checks: Define robust Kubernetes liveness and readiness probes. ArgoCD's Application has a healthPolicy setting to monitor application health.

* Resource Management: Set clear CPU and memory requests/limits for all application pods.

* Monitoring & Alerting: Monitor ArgoCD sync status, application health, and cluster resource utilization. Set up alerts for sync failures or application degradation.

* Rollbacks: Version-controlled manifests naturally support rollbacks. Revert a commit in Git and let ArgoCD reconcile.

* Progressive Delivery: Leverage Argo Rollouts or other tools for advanced deployment strategies like canary or blue/green deployments, managed through GitOps.

$1

* Learning Curve: While GitOps simplifies deployments, mastering ArgoCD, Kustomize/Helm, and Git strategies requires an initial investment in learning.

* Complexity of Setup: The initial setup of EKS, ArgoCD, and the Git repository structure can be intricate, especially for beginners.

State Drift: While selfHeal` addresses it, frequent manual interventions *outside of Git can lead to complex state drift scenarios that ArgoCD might struggle to reconcile automatically without careful debugging.

* Large Monorepos: For extremely large numbers of applications and environments, managing a single monorepo can become unwieldy. This necessitates exploring multi-repo strategies.

* CI/CD Integration: GitOps is often complemented by CI pipelines that build container images and update manifest repositories. Integrating these requires careful design.

$1

* Ephemeral Environments/Testing: For temporary development environments or quick, unmanaged testing where a strict Git-based workflow is overhead.

* Highly Manual/Interactive Operations: Environments where direct, frequent, manual Kubernetes resource manipulation is a primary operational mode.

* Simple, Single-Pod Applications: For very small, non-critical applications where the complexity of ArgoCD and GitOps might outweigh the benefits.

* Lack of Git Expertise: If your team does not have a strong understanding or adoption of Git practices, implementing GitOps will face significant cultural and technical hurdles.

Strictly Regulated Environments with Manual Approval Steps: While GitOps can *facilitate* manual approval (e.g., pull request reviews), environments requiring complex, multi-party manual sign-offs *before any action can be taken might need custom tooling or a hybrid approach.

$1

Before adopting GitOps with ArgoCD and EKS:

* \[ ] Git Adoption: Is your team proficient and comfortable with Git workflows (commits, pull requests, branching)?

* \[ ] Automation Goals: Do you aim to automate application deployments and infrastructure management?

* \[ ] Auditability Requirements: Is an auditable history of all infrastructure and application changes critical?

* \[ ] Reproducibility Needs: Do you need the ability to reliably recreate environments from scratch?

* \[ ] Kubernetes Expertise: Does your team have a solid understanding of Kubernetes concepts (Deployments, Services, ConfigMaps, etc.)?

* \[ ] Scalability: Do you anticipate managing a growing number of applications and environments?

* \[ ] Security Posture: Can you integrate GitOps practices with your existing security policies (RBAC, secret management, network segmentation)?

* \[ ] Tooling Investment: Are you prepared to invest time in setting up and maintaining ArgoCD, Git repositories, and potentially CI pipelines?

* \[ ] Error Handling Strategy: Do you have a plan for how to handle sync failures and application misconfigurations within a GitOps framework?

$1

Implementing GitOps with AWS EKS and ArgoCD provides a powerful, scalable, and auditable framework for managing Kubernetes environments. By adopting this approach, you enhance reliability, improve developer velocity, and establish a robust audit trail for all cluster and application state changes. Careful planning of your Git repository structure, security configurations, and operational practices will be key to a successful production deployment.

$1

1. ArgoCD Documentation: [https://argo-cd.readthedocs.io/](https://argo-cd.readthedocs.io/)

2. AWS EKS Best Practices: [https://aws.github.io/aws-eks-best-practices/](https://aws.github.io/aws-eks-best-practices/)

3. Kubernetes Documentation: [https://kubernetes.io/docs/](https://kubernetes.io/docs/)

4. Kustomize Documentation: [https://kubectl.docs.kubernetes.io/references/kustomize/](https://kubectl.docs.kubernetes.io/references/kustomize/)

5. GitOps Principles: [https://opengitops.dev/](https://opengitops.dev/)

Why This Matters

Understanding the business and technical context helps you make informed decisions rather than blindly following patterns.

Trade-offs to Consider

Every architectural decision involves trade-offs. Consider your specific requirements, team expertise, and scale when evaluating options.

When NOT to Use This

Knowing when a solution doesn't apply is as valuable as knowing when it does. Consider alternatives for your specific situation.

Decision Framework

Use this framework to evaluate whether this approach is right for your use case based on your specific constraints and requirements.