Infrastructure complète avec déploiement automatisé
# main.tf - Infrastructure AWS complète
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.23"
}
}
backend "s3" {
bucket = "terraform-state-bucket"
key = "infrastructure/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "terraform"
}
}
}
# Variables
variable "aws_region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "project_name" {
description = "Name of the project"
type = string
default = "aws-infrastructure"
}
variable "environment" {
description = "Environment name"
type = string
default = "production"
}
# VPC Configuration
module "vpc" {
source = "./modules/vpc"
vpc_cidr = "10.0.0.0/16"
availability_zones = data.aws_availability_zones.available.names
project_name = var.project_name
environment = var.environment
}
# EKS Cluster
module "eks" {
source = "./modules/eks"
vpc_id = module.vpc.vpc_id
private_subnets = module.vpc.private_subnets
public_subnets = module.vpc.public_subnets
cluster_name = "${var.project_name}-${var.environment}"
cluster_version = "1.28"
node_groups = {
main = {
desired_capacity = 3
max_capacity = 10
min_capacity = 1
instance_types = ["t3.medium"]
k8s_labels = {
Environment = var.environment
NodeGroup = "main"
}
}
}
}
# RDS Database
module "rds" {
source = "./modules/rds"
vpc_id = module.vpc.vpc_id
private_subnets = module.vpc.private_subnets
security_group_ids = [module.eks.cluster_security_group_id]
identifier = "${var.project_name}-db"
engine = "postgres"
engine_version = "15.4"
instance_class = "db.t3.micro"
allocated_storage = 20
max_allocated_storage = 100
database_name = "appdb"
username = "dbadmin"
backup_retention_period = 7
backup_window = "03:00-04:00"
maintenance_window = "sun:04:00-sun:05:00"
deletion_protection = true
skip_final_snapshot = false
}
# CloudFront Distribution
module "cloudfront" {
source = "./modules/cloudfront"
origin_domain_name = module.alb.alb_dns_name
origin_path = ""
aliases = [var.domain_name]
default_cache_behavior = {
allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD"]
compress = true
target_origin_id = "alb"
viewer_protocol_policy = "redirect-to-https"
forwarded_values = {
query_string = true
headers = ["Host", "CloudFront-Forwarded-Proto"]
cookies = {
forward = "all"
}
}
}
price_class = "PriceClass_100"
custom_error_response = [
{
error_code = 404
response_code = 200
response_page_path = "/index.html"
},
{
error_code = 403
response_code = 200
response_page_path = "/index.html"
}
]
}
# S3 Bucket for Backups
module "s3_backup" {
source = "./modules/s3"
bucket_name = "${var.project_name}-backups-${var.environment}"
versioning = {
enabled = true
}
lifecycle_rules = [
{
id = "transition-to-glacier"
enabled = true
transition = [
{
days = 30
storage_class = "STANDARD_IA"
},
{
days = 90
storage_class = "GLACIER"
}
]
expiration = {
days = 365
}
}
]
backup_policy = {
schedule_expression = "cron(0 2 * * ? *)"
retention_days = 30
transition_to_cold_storage_days = 7
}
}
# Outputs
output "cluster_endpoint" {
description = "Endpoint for EKS control plane"
value = module.eks.cluster_endpoint
}
output "cluster_security_group_id" {
description = "Security group ids attached to the cluster control plane"
value = module.eks.cluster_security_group_id
}
output "rds_endpoint" {
description = "RDS instance endpoint"
value = module.rds.db_instance_endpoint
}
output "cloudfront_domain_name" {
description = "The domain name corresponding to the distribution"
value = module.cloudfront.cloudfront_domain_name
}
# .gitlab-ci.yml - Pipeline CI/CD complet
stages:
- build
- test
- security
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
TF_ROOT: ${CI_PROJECT_DIR}/terraform
TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}
# Build stage
build:
stage: build
image: docker:20.10.16
services:
- docker:20.10.16-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -t $CI_REGISTRY_IMAGE:latest .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main
- develop
# Tests unitaires avec Cypress
test:unit:
stage: test
image: cypress/browsers:node18.12.0-chrome107
before_script:
- npm ci
script:
- npm run test:unit
- npm run cypress:run
artifacts:
when: always
reports:
junit: cypress/results/junit.xml
paths:
- cypress/videos/
- cypress/screenshots/
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
only:
- merge_requests
- main
# Load Testing avec k6
test:load:
stage: test
image: grafana/k6:latest
script:
- k6 run --out influxdb=http://influxdb:8086/k6 tests/load-test.js
artifacts:
reports:
performance: tests/load-test.js
only:
- main
when: manual
# Scan de sécurité avec Trivy
security:scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --severity HIGH,CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
allow_failure: false
only:
- main
- develop
# Terraform Plan
terraform:plan:
stage: deploy
image: registry.gitlab.com/gitlab-org/terraform-images/stable:latest
before_script:
- cd ${TF_ROOT}
- gitlab-terraform init
script:
- gitlab-terraform plan
- gitlab-terraform plan-json
artifacts:
name: plan
paths:
- ${TF_ROOT}/plan.cache
reports:
terraform: ${TF_ROOT}/plan.json
only:
- merge_requests
environment:
name: $CI_ENVIRONMENT_NAME
action: prepare
# Terraform Apply
terraform:apply:
stage: deploy
image: registry.gitlab.com/gitlab-org/terraform-images/stable:latest
before_script:
- cd ${TF_ROOT}
- gitlab-terraform init
script:
- gitlab-terraform apply
only:
- main
when: manual
environment:
name: production
action: start
# Déploiement Kubernetes
deploy:k8s:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl apply -f k8s/
- kubectl rollout status deployment/app-deployment
only:
- main
environment:
name: production
url: https://app.example.com
when: manual
# Dockerfile - Application principale # Étape de build FROM node:18-alpine AS builder WORKDIR /app # Copie des fichiers de dépendances COPY package*.json ./ RUN npm ci --only=production # Copie du code source COPY . . # Build de l'application RUN npm run build # Étape de production FROM node:18-alpine AS production # Création d'un utilisateur non-root RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 WORKDIR /app # Copie des fichiers nécessaires COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json USER nextjs EXPOSE 3000 ENV NODE_ENV=production CMD ["npm", "start"]
# docker-compose.yml - Configuration locale
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://user:password@postgres:5432/appdb
depends_on:
- postgres
- redis
volumes:
- ./src:/app/src
- ./public:/app/public
networks:
- app-network
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: appdb
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
networks:
- app-network
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- app-network
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
networks:
- app-network
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
- ./monitoring/grafana:/etc/grafana/provisioning
depends_on:
- prometheus
networks:
- app-network
k6:
image: grafana/k6:latest
volumes:
- ./tests:/tests
command: run /tests/load-test.js
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres_data:
redis_data:
prometheus_data:
grafana_data:
# prometheus.yml - Configuration de monitoring
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "rules/*.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'kubernetes-apiservers'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https
- job_name: 'kubernetes-nodes'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- job_name: 'application-metrics'
static_configs:
- targets: ['app:3000']
metrics_path: '/metrics'
scrape_interval: 30s
# alert-rules.yml - Règles d'alerte Prometheus
groups:
- name: infrastructure
rules:
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "Instance {{ $labels.instance }} CPU usage is high"
description: "CPU usage is above 80% for more than 5 minutes"
- alert: HighMemoryUsage
expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100 > 85
for: 5m
labels:
severity: warning
annotations:
summary: "Instance {{ $labels.instance }} memory usage is high"
description: "Memory usage is above 85% for more than 5 minutes"
- alert: DiskSpaceLow
expr: (node_filesystem_avail_bytes{fstype!="tmpfs"} / node_filesystem_size_bytes{fstype!="tmpfs"}) * 100 < 10
for: 5m
labels:
severity: critical
annotations:
summary: "Disk space is running low"
description: "Disk usage is above 90% for more than 5 minutes"
- name: application
rules:
- alert: ApplicationDown
expr: up{job="application-metrics"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Application is down"
description: "The application has been down for more than 1 minute"
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "High error rate detected"
description: "Error rate is above 5% for more than 5 minutes"
# alertmanager.yml - Configuration des alertes Slack
global:
slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
route:
group_by: ['alertname', 'cluster', 'service']
group_wait: 10s
group_interval: 10s
repeat_interval: 1h
receiver: 'slack-notifications'
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#alerts'
title: 'Alert: {{ .GroupLabels.alertname }}'
text: |
{{ range .Alerts }}
*Alert:* {{ .Annotations.summary }}
*Description:* {{ .Annotations.description }}
*Instance:* {{ .Labels.instance }}
*Severity:* {{ .Labels.severity }}
{{ end }}
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'dev', 'instance']
# iam-policies.tf - Politiques IAM least-privilege
# EKS Cluster Role
resource "aws_iam_role" "eks_cluster_role" {
name = "${var.project_name}-eks-cluster-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "eks.amazonaws.com"
}
}]
})
}
resource "aws_iam_role_policy_attachment" "eks_cluster_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.eks_cluster_role.name
}
# EKS Node Group Role
resource "aws_iam_role" "eks_node_group_role" {
name = "${var.project_name}-eks-node-group-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}]
})
}
resource "aws_iam_role_policy_attachment" "eks_worker_node_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
role = aws_iam_role.eks_node_group_role.name
}
resource "aws_iam_role_policy_attachment" "eks_cni_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
role = aws_iam_role.eks_node_group_role.name
}
resource "aws_iam_role_policy_attachment" "eks_container_registry_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
role = aws_iam_role.eks_node_group_role.name
}
# RDS Access Policy
resource "aws_iam_policy" "rds_access_policy" {
name = "${var.project_name}-rds-access"
description = "Policy for RDS access with least privilege"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"rds:DescribeDBInstances",
"rds:DescribeDBSnapshots",
"rds:CreateDBSnapshot",
"rds:RestoreDBInstanceFromDBSnapshot"
]
Resource = [
aws_db_instance.main.arn,
"${aws_db_instance.main.arn}:*"
]
},
{
Effect = "Allow"
Action = [
"kms:Decrypt"
]
Resource = [aws_kms_key.rds.arn]
}
]
})
}
# S3 Backup Policy
resource "aws_iam_policy" "s3_backup_policy" {
name = "${var.project_name}-s3-backup"
description = "Policy for S3 backup operations"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
]
Resource = [
"${aws_s3_bucket.backup.arn}/*",
"${aws_s3_bucket.backup.arn}/*"
]
},
{
Effect = "Allow"
Action = [
"s3:ListBucket"
]
Resource = [
aws_s3_bucket.backup.arn,
aws_s3_bucket.backup.arn
]
},
{
Effect = "Allow"
Action = [
"s3:PutLifecycleConfiguration",
"s3:GetLifecycleConfiguration"
]
Resource = [
aws_s3_bucket.backup.arn,
aws_s3_bucket.backup.arn
]
}
]
})
}
# CloudWatch Logs Policy
resource "aws_iam_policy" "cloudwatch_logs_policy" {
name = "${var.project_name}-cloudwatch-logs"
description = "Policy for CloudWatch logs access"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
]
Resource = "arn:aws:logs:*:*:*"
}
]
})
}
Téléchargez l'ensemble des scripts générés dans une archive ZIP