diff --git a/Makefile b/Makefile index de3486bbe..0441774f1 100644 --- a/Makefile +++ b/Makefile @@ -59,4 +59,4 @@ help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}' .PHONY: install run stop run-es build build-en build-es serve collections tutorials clear translations help -.DEFAULT_GOAL := help \ No newline at end of file +.DEFAULT_GOAL := help diff --git a/TUTORIALS_REVIEW.md b/TUTORIALS_REVIEW.md new file mode 100644 index 000000000..9732a6bfa --- /dev/null +++ b/TUTORIALS_REVIEW.md @@ -0,0 +1,64 @@ +# Revisión de Tutoriales — docs.sleakops.com + +> Rama de revisión: `feat/all-tutorials` +> Total: 47 tutoriales + +--- + +## Ya publicados (existían antes) + +- [ ] `config-aws-waf` — Configure AWS WAF +- [ ] `make-rds-public` — Make RDS Database Public +- [ ] `install-keda` — Install KEDA +- [ ] `dashboard-loki-not-responding` — Loki's Dashboard is not responding +- [ ] `s3-batch` — S3 Batch +- [ ] `s3-replication` — S3 Replication +- [ ] `third-party-integration-vpn` — Third Party VPN Integration +- [ ] `n8n-worker` — n8n + Worker Mode +- [ ] `django-celery` — Django + Celery +- [ ] `how-to-set-up-vpc-peering-between-aws-and-mongodb-atlas` — How to Set Up VPC Peering Between AWS and MongoDB Atlas + +--- + +## Migrados desde Notion — Con contenido completo + +- [ ] `migrate-rds-snapshot-between-accounts` — Migrate an Amazon RDS Snapshot Between Accounts +- [ ] `postgresql-dump-restore` — Restore a PostgreSQL Dump Using a SleakOps Job +- [ ] `migrate-postgres-heroku-to-rds` — Migrate a Large PostgreSQL Database from Heroku to RDS +- [ ] `migrate-ebs-volumes` — Migrate EBS Volumes to a New AWS Account +- [ ] `postgres-helm-existing-volume` — Deploy a PostgreSQL Helm Chart Using an Existing EBS Volume +- [ ] `migrate-external-s3` — Migrate an External S3 Bucket to SleakOps +- [ ] `migrate-files-volumes-copy` — Migrate Files Between Kubernetes Volumes Using kubectl cp +- [ ] `dms-rds-migration` — Use AWS DMS to Synchronize or Migrate RDS Databases +- [ ] `amazon-ses` — Get Started with Amazon SES +- [ ] `rds-external-access` — Access an RDS Instance from Outside Your VPC +- [ ] `transfer-domain-route53` — Transfer a Domain to Amazon Route 53 +- [ ] `lambda-cicd-github-actions` — Deploy AWS Lambda Functions with GitHub Actions +- [ ] `deploy-retool-helm` — Deploy Retool Using Helm Charts on SleakOps +- [ ] `lens-cluster-connectivity` — Troubleshoot Cluster Connectivity with Lens +- [ ] `pritunl-dns-universal` — Universal DNS Fix for Pritunl Client +- [ ] `workers-use-cases` — Worker Use Cases in SleakOps +- [ ] `aws-codeartifact-java` — Use AWS CodeArtifact with Java/Maven Projects +- [ ] `networking-vpc` — Networking and Network Resources in SleakOps +- [ ] `aws-local-authentication` — Configure AWS Authentication for Local Development +- [ ] `connect-aws-resources` — Connect to AWS Resources from Your Application +- [ ] `test-site-to-site-vpn` — Test a Site-to-Site VPN Created with SleakOps +- [ ] `deploy-datadog-operator` — Deploy Datadog Operator and DatadogAgent on SleakOps +- [ ] `install-datadog` — Install Datadog on a SleakOps EKS Cluster +- [ ] `install-new-relic` — Install New Relic on Your Application +- [ ] `optimize-aws-costs` — AWS Cost Optimization Strategies +- [ ] `bitnami-image-deprecated` — Bitnami Image Deprecation — What to Do +- [ ] `optimize-docker-image` — How to Optimize Your Docker Image Size + +--- + +## Migrados desde Notion — Skeleton (contenido pendiente de redactar) + +- [ ] `e2e-testing` — Build E2E Testing with Ephemeral Environments on SleakOps +- [ ] `write-dockerfile` — How to Write a Dockerfile +- [ ] `kubernetes-migration-tips` — Migrating to Kubernetes — Tips and Best Practices +- [ ] `migrate-ecs-to-kubernetes` — Migrate from ECS to Kubernetes on SleakOps +- [ ] `docker-desktop-alternative` — Docker Desktop Alternatives +- [ ] `tips-avoid-latency` — Tips to Reduce Latency in Your Applications +- [ ] `api-gateway-webservices` — Use an API Gateway in Front of Multiple Services +- [ ] `what-is-kubernetes-sleakops` — What is Kubernetes and How Does SleakOps Help? diff --git a/TUTORIAL_IMAGE_PROMPTS.md b/TUTORIAL_IMAGE_PROMPTS.md new file mode 100644 index 000000000..3f98d473e --- /dev/null +++ b/TUTORIAL_IMAGE_PROMPTS.md @@ -0,0 +1,160 @@ +# AI Image Generation Prompts — Tutorial Hero Images + +Prompts para generar las imágenes hero de cada tutorial nuevo. +Cada entrada incluye el comando `mkdir -p` y el path donde guardar la imagen. + +--- + +### Estilo base SleakOps + +> Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +## install-new-relic + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/install-new-relic +``` +**Archivo:** `static/img/tutorials/install-new-relic/install-new-relic.png` + +The New Relic diamond logo icon in the center, with language runtime icons arranged around it: Node.js green hexagon logo, Python snake icon, Java coffee cup. Glowing agent injection arrows point from each runtime into the New Relic hub. Performance graph lines and telemetry data streams radiate outward. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## optimize-aws-costs + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/optimize-aws-costs +``` +**Archivo:** `static/img/tutorials/optimize-aws-costs/optimize-aws-costs.png` + +An AWS cloud environment with cost reduction visual elements: Spot Instance nodes with a downward price arrow, S3 buckets with tiered storage layers stacked (hot, warm, cold), a Graviton ARM chip icon, and a Savings Plan shield. In the foreground a cost graph line trending sharply downward toward green. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## bitnami-image-deprecated + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/bitnami-image-deprecated +``` +**Archivo:** `static/img/tutorials/bitnami-image-deprecated/bitnami-image-deprecated.png` + +A Docker container image icon with a Bitnami logo overlaid with a deprecation warning symbol (triangle with exclamation mark). An arrow points from the deprecated image to a replacement image icon marked with a checkmark. A Helm chart icon and a Kubernetes pod are visible in the migration path. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## optimize-docker-image + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/optimize-docker-image +``` +**Archivo:** `static/img/tutorials/optimize-docker-image/optimize-docker-image.png` + +A Docker whale icon. On the left, a large bloated container with many stacked heavy layers. An optimization compression arrow points right to a sleek slim container with fewer layers. A size compression icon bridges the two. Layer icons show multi-stage build reduction effect. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## e2e-testing + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/e2e-testing +``` +**Archivo:** `static/img/tutorials/e2e-testing/e2e-testing.png` + +A browser window with a test automation cursor running across a web UI. Behind it, an ephemeral Kubernetes environment lifecycle: pod spinning up, running tests with checkmarks, then disappearing (create, test, delete as three glowing nodes). A clock icon suggests the temporary nature. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## write-dockerfile + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/write-dockerfile +``` +**Archivo:** `static/img/tutorials/write-dockerfile/write-dockerfile.png` + +A code editor panel showing Dockerfile syntax highlighted in glowing colors (FROM, RUN, COPY, CMD instructions visible as colored line blocks). A Docker whale icon emerges from the file, carrying a container. Stacked layer blocks below the whale represent the image layers being built. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## kubernetes-migration-tips + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/kubernetes-migration-tips +``` +**Archivo:** `static/img/tutorials/kubernetes-migration-tips/kubernetes-migration-tips.png` + +On the left, a traditional server rack with legacy application icons. A large migration arrow with a checklist scroll floats above it. On the right, a modern Kubernetes cluster with hexagonal node pattern. The arrow transitions from the old to the new infrastructure. Small lightbulb and best-practice icons dot the path. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## migrate-ecs-to-kubernetes + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/migrate-ecs-to-kubernetes +``` +**Archivo:** `static/img/tutorials/migrate-ecs-to-kubernetes/migrate-ecs-to-kubernetes.png` + +On the left, the AWS ECS logo (container task group icon) with orange AWS styling. A bold migration arrow flows to the right where the Kubernetes wheel logo glows. Container icons transform from ECS task definition shapes into Kubernetes pod hexagons along the migration path. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## docker-desktop-alternative + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/docker-desktop-alternative +``` +**Archivo:** `static/img/tutorials/docker-desktop-alternative/docker-desktop-alternative.png` + +A Docker Desktop icon on the left with a swap or alternative symbol. Branching arrows point to three alternative tool icons: Rancher Desktop, Podman (pod icon), and a terminal-based tool. A developer laptop sits below all options connected to each tool via glowing lines. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## tips-avoid-latency + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/tips-avoid-latency +``` +**Archivo:** `static/img/tutorials/tips-avoid-latency/tips-avoid-latency.png` + +A network request path visualization: a client icon on the far left sends a request through optimized hops (CDN edge node, load balancer, application pod, cache layer) to a database on the right. A speedometer gauge in the background shows latency dropping to a green low-latency zone. Lightning bolt icons on each hop represent optimization. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## api-gateway-webservices + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/api-gateway-webservices +``` +**Arquivo:** `static/img/tutorials/api-gateway-webservices/api-gateway-webservices.png` + +A central API Gateway hub icon in the middle. Multiple incoming client request arrows converge on it from the left side. From the right side of the gateway, traffic is routed to three separate backend service pods in different colors. Routing rule icons float above the output connections suggesting path-based routing. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## what-is-kubernetes-sleakops + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/what-is-kubernetes-sleakops +``` +**Archivo:** `static/img/tutorials/what-is-kubernetes-sleakops/what-is-kubernetes-sleakops.png` + +The Kubernetes wheel helm rudder logo prominently in the center glowing with cyan light. Around it, a cluster of hexagonal nodes connected by network lines. A cloud platform control plane icon oversees the cluster from above with management arrows. Application pods of different shapes (web service, worker, database) are deployed within the nodes. The composition communicates orchestration, automation, and control. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## openvpn-profile + +``` +mkdir -p /home/agustinoli/Documents/SleakOps/ia-resources/projects/docs/static/img/tutorials/openvpn-profile +``` +**Archivo:** `static/img/tutorials/openvpn-profile/openvpn-profile.png` + +The OpenVPN shield logo glowing in the center, with a .ovpn configuration file icon flowing into it as a stream of glowing connection data. Around it, client device icons (a laptop, a smartphone, a tablet) connected through encrypted tunnel lines visualized as glowing dashed pathways with small padlock icons along the route. On the far side, a secure VPN gateway server rack icon receives the tunnels. Isometric tech illustration. Dark background using SleakOps dark palette (#1a202d to #0a0b10). Primary glow and highlights in SleakOps cyan (#8bcff9, light sky blue). Secondary accents in SleakOps yellow (#f2e35e, warm gold). Deep dark shadows, subtle panel surfaces (#232a35). Clean, minimal, professional. No text, no letters, no labels anywhere in the image. 16:9 aspect ratio. + +--- + +## Notas de uso + +- Resolución sugerida: **1200×675px** (16:9) o **1600×900px**. +- Guardar como **PNG** en el path indicado en cada entrada. +- Para los tutoriales que ya tienen screenshots internos (networking-vpc, postgresql-dump-restore, bitnami-image-deprecated), esta imagen es la **hero image** del tutorial — diferente a las capturas de pantalla internas. +- Colores de referencia SleakOps: fondo `#1a202d`/`#0a0b10`, cyan `#8bcff9`, amarillo `#f2e35e`, superficie panel `#232a35`. diff --git a/content/docs/en/cli.mdx b/content/docs/en/cli.mdx index 035c82beb..c57d25d5a 100644 --- a/content/docs/en/cli.mdx +++ b/content/docs/en/cli.mdx @@ -55,6 +55,17 @@ As previously mentioned the key might be an input here or a environment variable Also, you might mark if you want the process to **wait** the build to be finished or not. +When you use **wait**, you can also set a **timeout** (in minutes) to cap how long the CLI waits for the build to finish. It defaults to **180 minutes**; pass `0` to wait indefinitely. You can also set it through the `BUILD_TIMEOUT_MINUTES` environment variable. + +| **Option** | **Description** | +| ------------- | --------------------------------------------------------------------------- | +| **--wait** | Wait for the build to finish before returning. | +| **--timeout** | Minutes to wait when using `--wait`. Default `180`. Use `0` for no timeout. | + +:::tip +The timeout is client-side: it controls how long the CLI watches the build, not the build itself on the backend. When it expires, the CLI prints a message and exits with code `1`, so a stuck build fails your CI pipeline instead of silently passing. +::: + ### 3. Make a Deploy Once your build is ready, you can effortlessly deploy your application using the following command: diff --git a/content/docs/en/cluster/addons/keda.mdx b/content/docs/en/cluster/addons/keda.mdx index 1a063a484..af99cbb7e 100644 --- a/content/docs/en/cluster/addons/keda.mdx +++ b/content/docs/en/cluster/addons/keda.mdx @@ -164,6 +164,97 @@ spec: For the full catalogue of triggers (Kafka, Redis, Prometheus, cron, NATS, MongoDB, …) refer to the [KEDA Scalers documentation ](https://keda.sh/docs/2.19/scalers/). +
+ +### How do I deploy `ScaledObject` manifests through SleakOps? + +You can deliver `ScaledObject` (and `TriggerAuthentication`) manifests to your Cluster without leaving the SleakOps console. Two paths are available — pick based on where the manifest needs to live. + +**Option 1 — KEDA addon Custom Values (`extraObjects`, cluster-scoped)** + +The upstream KEDA chart exposes an `extraObjects` list that ships any K8s manifest alongside the operator. Open the KEDA addon's detail drawer, toggle **Enable Custom Values**, and paste the YAML: + +```yaml +extraObjects: + - apiVersion: keda.sh/v1alpha1 + kind: ClusterTriggerAuthentication + metadata: + name: aws-irsa + spec: + podIdentity: + provider: aws + - apiVersion: keda.sh/v1alpha1 + kind: ScaledObject + metadata: + name: order-worker-scaler + namespace: my-app + spec: + scaleTargetRef: + name: order-worker + minReplicaCount: 0 + maxReplicaCount: 20 + triggers: + - type: aws-sqs-queue + authenticationRef: + kind: ClusterTriggerAuthentication + name: aws-irsa + metadata: + queueURL: https://sqs.us-east-1.amazonaws.com/123456789012/orders + queueLength: "10" + awsRegion: us-east-1 +``` + +These resources are owned by the KEDA addon release, which makes this the right path for **cluster-scoped resources** (`ClusterTriggerAuthentication`) and for any `ScaledObject` whose target namespace you set explicitly. Editing the YAML and clicking **Update** re-applies the set in place. + +**Option 2 — Project Chart Configuration (namespace-scoped, per-Project)** + +When the `ScaledObject` should live in the same namespace as the Workload it scales, deliver it through `Project > Settings > Chart Configuration`. The **Templates** field accepts raw Helm template YAML rendered as part of the Project's release, so `{{ .Values.global.namespace }}` resolves to the right Environment automatically: + +```yaml +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: order-worker-scaler + namespace: {{ .Values.global.namespace }} +spec: + scaleTargetRef: + name: order-worker-deployment + minReplicaCount: 0 + maxReplicaCount: 20 + pollingInterval: 30 + cooldownPeriod: 300 + triggers: + - type: aws-sqs-queue + authenticationRef: + name: keda-aws-trigger-auth + metadata: + queueURL: https://sqs.us-east-1.amazonaws.com/123456789012/orders + queueLength: "10" + awsRegion: us-east-1 + identityOwner: operator +--- +apiVersion: keda.sh/v1alpha1 +kind: TriggerAuthentication +metadata: + name: keda-aws-trigger-auth + namespace: {{ .Values.global.namespace }} +spec: + podIdentity: + provider: aws +``` + + + keda-chartconfig-scaledobject + + +With **Deploy?** toggled on, clicking **Apply changes** rolls out a new deployment that applies the manifest to the Project's namespace — the right path for app-specific scaling rules that should follow the Project across Environments. + +For a worked walk-through, see the [Install KEDA tutorial](/tutorial/install-keda). +
+
### Can I customize the KEDA deployment? diff --git a/content/docs/en/cluster/addons/keda/keda-chartconfig-scaledobject.png b/content/docs/en/cluster/addons/keda/keda-chartconfig-scaledobject.png new file mode 100644 index 000000000..a7fd53e44 Binary files /dev/null and b/content/docs/en/cluster/addons/keda/keda-chartconfig-scaledobject.png differ diff --git a/content/docs/en/powered-ai/autodiagnostic.mdx b/content/docs/en/powered-ai/autodiagnostic.mdx index d4efe399f..c5bd26f3a 100644 --- a/content/docs/en/powered-ai/autodiagnostic.mdx +++ b/content/docs/en/powered-ai/autodiagnostic.mdx @@ -12,7 +12,11 @@ import { FiExternalLink } from "react-icons/fi"; Autodiagnostics generates an AI-powered diagnosis for any resource managed by SleakOps. The AI reads logs and infrastructure state, identifies the root cause, and suggests corrective actions — so you spend less time debugging and more time shipping. -{/* TODO: screenshot - Autodiagnostics button on a resource detail page (e.g. a failed deployment or service) */} +autodiagnostics-demo ## FAQs diff --git a/content/docs/en/powered-ai/autodiagnostic/autodiagnostic.gif b/content/docs/en/powered-ai/autodiagnostic/autodiagnostic.gif new file mode 100644 index 000000000..6026f22dd Binary files /dev/null and b/content/docs/en/powered-ai/autodiagnostic/autodiagnostic.gif differ diff --git a/content/docs/en/powered-ai/conversation.mdx b/content/docs/en/powered-ai/conversation.mdx index 56584564b..824888938 100644 --- a/content/docs/en/powered-ai/conversation.mdx +++ b/content/docs/en/powered-ai/conversation.mdx @@ -12,7 +12,11 @@ import { FiExternalLink } from "react-icons/fi"; Infrastructure Chat lets you ask questions about your infrastructure in plain language and get instant answers, diagnostics, and reports — without navigating dashboards or remembering command syntax. -{/* TODO: screenshot - Support section showing the chat interface with an active conversation */} +infrastructure-chat-demo :::caution Alpha Infrastructure Chat is currently in **Alpha**. Core functionality is working, but the experience and available capabilities are still being refined. diff --git a/content/docs/en/powered-ai/conversation/conversation.gif b/content/docs/en/powered-ai/conversation/conversation.gif new file mode 100644 index 000000000..6026f22dd Binary files /dev/null and b/content/docs/en/powered-ai/conversation/conversation.gif differ diff --git a/content/docs/en/powered-ai/dockertron.mdx b/content/docs/en/powered-ai/dockertron.mdx index 111b217f2..8571d0a0e 100644 --- a/content/docs/en/powered-ai/dockertron.mdx +++ b/content/docs/en/powered-ai/dockertron.mdx @@ -12,6 +12,12 @@ import { FiExternalLink } from "react-icons/fi"; Dockertron analyzes your repository and generates everything needed to run your application on SleakOps: production-ready Dockerfiles, a docker-compose configuration, and the full infrastructure model — no config files to write, no templates to copy. +dockertron-demo + ai-reports-demo + ](https://keda.sh/docs/2.19/scalers/).
+
+ +### ¿Cómo aplico manifiestos de `ScaledObject` desde SleakOps? + +Podés entregar manifiestos de `ScaledObject` (y `TriggerAuthentication`) a tu Clúster sin salir de la consola de SleakOps. Hay dos caminos — elegí en base a dónde tiene que vivir el manifiesto. + +**Opción 1 — Custom Values del addon de KEDA (`extraObjects`, scope cluster)** + +El Helm chart de KEDA expone una lista `extraObjects` que despliega cualquier manifiesto de K8s junto al operador. Abrí el panel de detalle del addon de KEDA, activá **Enable Custom Values**, y pegá el YAML: + +```yaml +extraObjects: + - apiVersion: keda.sh/v1alpha1 + kind: ClusterTriggerAuthentication + metadata: + name: aws-irsa + spec: + podIdentity: + provider: aws + - apiVersion: keda.sh/v1alpha1 + kind: ScaledObject + metadata: + name: order-worker-scaler + namespace: my-app + spec: + scaleTargetRef: + name: order-worker + minReplicaCount: 0 + maxReplicaCount: 20 + triggers: + - type: aws-sqs-queue + authenticationRef: + kind: ClusterTriggerAuthentication + name: aws-irsa + metadata: + queueURL: https://sqs.us-east-1.amazonaws.com/123456789012/orders + queueLength: "10" + awsRegion: us-east-1 +``` + +Estos recursos quedan asociados al release del addon de KEDA, lo que hace de este el camino correcto para **recursos cluster-scoped** (`ClusterTriggerAuthentication`) y para cualquier `ScaledObject` cuyo namespace destino definas explícitamente en el manifiesto. Editar el YAML y hacer clic en **Update** vuelve a aplicar el conjunto in-place. + +**Opción 2 — Chart Configuration del Proyecto (scope namespace, por Proyecto)** + +Cuando el `ScaledObject` tiene que vivir en el mismo namespace que el Workload que escala, entregalo a través de `Project > Settings > Chart Configuration`. El campo **Templates** acepta YAML de templates de Helm que se renderiza como parte del release del Proyecto, así que `{{ .Values.global.namespace }}` resuelve al Environment correcto automáticamente: + +```yaml +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: order-worker-scaler + namespace: {{ .Values.global.namespace }} +spec: + scaleTargetRef: + name: order-worker-deployment + minReplicaCount: 0 + maxReplicaCount: 20 + pollingInterval: 30 + cooldownPeriod: 300 + triggers: + - type: aws-sqs-queue + authenticationRef: + name: keda-aws-trigger-auth + metadata: + queueURL: https://sqs.us-east-1.amazonaws.com/123456789012/orders + queueLength: "10" + awsRegion: us-east-1 + identityOwner: operator +--- +apiVersion: keda.sh/v1alpha1 +kind: TriggerAuthentication +metadata: + name: keda-aws-trigger-auth + namespace: {{ .Values.global.namespace }} +spec: + podIdentity: + provider: aws +``` + + + keda-chartconfig-scaledobject + + +Con **Deploy?** activado, hacer clic en **Apply changes** dispara un nuevo deployment que aplica el manifiesto en el namespace del Proyecto — el camino correcto para reglas de escalado específicas de la aplicación que tienen que seguir al Proyecto entre Environments. + +Para un walk-through completo, mirá el [tutorial de Instalar KEDA](/tutorial/install-keda). +
+
### ¿Puedo personalizar el deployment de KEDA? diff --git a/content/docs/es/cluster/addons/keda/keda-chartconfig-scaledobject.png b/content/docs/es/cluster/addons/keda/keda-chartconfig-scaledobject.png new file mode 100644 index 000000000..edcce7987 Binary files /dev/null and b/content/docs/es/cluster/addons/keda/keda-chartconfig-scaledobject.png differ diff --git a/content/docs/es/powered-ai/autodiagnostic.mdx b/content/docs/es/powered-ai/autodiagnostic.mdx index 3e0a4e1e4..b80873c16 100644 --- a/content/docs/es/powered-ai/autodiagnostic.mdx +++ b/content/docs/es/powered-ai/autodiagnostic.mdx @@ -12,7 +12,11 @@ import { FiExternalLink } from "react-icons/fi"; Los Autodiagnósticos generan un diagnóstico impulsado por IA para cualquier recurso administrado por SleakOps. La IA lee los logs y el estado de la infraestructura, identifica la causa raíz y sugiere acciones correctivas — para que pases menos tiempo depurando y más tiempo construyendo. -{/* TODO: screenshot - Botón Diagnosticar en la página de detalle de un recurso (ej. un deployment fallido o un servicio con error) */} +autodiagnosticos-demo ## Preguntas frecuentes diff --git a/content/docs/es/powered-ai/autodiagnostic/autodiagnostic.gif b/content/docs/es/powered-ai/autodiagnostic/autodiagnostic.gif new file mode 100644 index 000000000..6026f22dd Binary files /dev/null and b/content/docs/es/powered-ai/autodiagnostic/autodiagnostic.gif differ diff --git a/content/docs/es/powered-ai/conversation.mdx b/content/docs/es/powered-ai/conversation.mdx index 49b557fff..ba89f9d38 100644 --- a/content/docs/es/powered-ai/conversation.mdx +++ b/content/docs/es/powered-ai/conversation.mdx @@ -12,7 +12,11 @@ import { FiExternalLink } from "react-icons/fi"; El Chat de Infraestructura te permite hacer preguntas sobre tu infraestructura en lenguaje natural y obtener respuestas instantáneas, diagnósticos y reportes — sin navegar por dashboards ni recordar sintaxis de comandos. -{/* TODO: screenshot - Sección Support mostrando la interfaz de chat con una conversación activa */} +chat-infraestructura-demo :::caution Alpha El Chat de Infraestructura está actualmente en **Alpha**. La funcionalidad central está operativa, pero la experiencia y las capacidades disponibles siguen refinándose. diff --git a/content/docs/es/powered-ai/conversation/conversation.gif b/content/docs/es/powered-ai/conversation/conversation.gif new file mode 100644 index 000000000..6026f22dd Binary files /dev/null and b/content/docs/es/powered-ai/conversation/conversation.gif differ diff --git a/content/docs/es/powered-ai/dockertron.mdx b/content/docs/es/powered-ai/dockertron.mdx index c6d2efdf4..d0926daaa 100644 --- a/content/docs/es/powered-ai/dockertron.mdx +++ b/content/docs/es/powered-ai/dockertron.mdx @@ -12,6 +12,12 @@ import { FiExternalLink } from "react-icons/fi"; Dockertron analiza tu repositorio y genera todo lo necesario para correr tu aplicación en SleakOps: Dockerfiles listos para producción, una configuración de docker-compose y el modelo completo de infraestructura — sin escribir archivos de configuración ni copiar templates. +dockertron-demo + ai-reports-demo + + +Use case description: +We plan to use Amazon SES to send transactional emails to our users +(e.g., account notifications and password reset emails). Emails are +user-triggered and sent only to customers who have registered on our platform. + +List management: +We only send to recipients who are registered in our system. Users can +unsubscribe from non-essential communications through our website. + +Bounce/complaint handling: +We monitor bounces and complaints and suppress recipients when appropriate. + +Expected volume: +Our expected sending volume is emails per day initially, with gradual growth. + +Please let us know if any more information is needed. +``` + +:::tip +**Tips for faster approval:** + +- State clearly whether emails are **transactional** (password reset, order confirmations) or **marketing** +- Include an estimated sending volume per day/month +- Describe how your system handles bounces, complaints, and unsubscribes +- If possible, include an example of the email content +::: diff --git a/content/tutorials/en/aws-codeartifact-java.mdx b/content/tutorials/en/aws-codeartifact-java.mdx new file mode 100644 index 000000000..a60647687 --- /dev/null +++ b/content/tutorials/en/aws-codeartifact-java.mdx @@ -0,0 +1,211 @@ +--- +title: Use AWS CodeArtifact with Java/Maven Projects +sidebar_label: AWS CodeArtifact for Java +sidebar_position: 28 +description: Configure a Java Maven project to use AWS CodeArtifact as a private artifact repository inside Docker, with CI/CD pipeline examples for GitHub Actions, GitLab, and Bitbucket. +tags: + - aws + - ci-cd + - docker + - deployment +image: /img/tutorials/aws-codeartifact-java/aws-codeartifact-java.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Configure a Java Maven project to pull dependencies from [AWS CodeArtifact ](https://aws.amazon.com/codeartifact/) — a private artifact repository — inside a Dockerized build with CI/CD pipeline examples. + +## Prerequisites + +- An AWS CodeArtifact domain and repository created +- IAM credentials with `codeartifact:GetAuthorizationToken` and `codeartifact:ReadFromRepository` permissions +- Docker and Maven installed + +## Step 1 — Create `codeartifact_settings.xml` + +This file configures Maven to authenticate against CodeArtifact using a token injected via environment variable: + +```xml + + + + codeartifact + aws + ${env.CODEARTIFACT_AUTH_TOKEN} + + + + + codeartifact + ${env.CODEARTIFACT_REPOSITORY} + * + + + +``` + +## Step 2 — Dockerfile with CodeArtifact Authentication + +Use a multi-stage build so the authentication token never ends up in the final image: + +```dockerfile +# Build stage +FROM maven:3.9-eclipse-temurin-21 AS build + +# Install AWS CLI +RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \ + && unzip awscliv2.zip \ + && ./aws/install + +# Accept AWS credentials as build args (not stored in final image) +ARG CODEARTIFACT_ACCESS_KEY_ID +ARG CODEARTIFACT_SECRET_ACCESS_KEY +ARG CODEARTIFACT_REGION +ARG CODEARTIFACT_ACCOUNT_ID +ARG CODEARTIFACT_REPOSITORY +ARG CODEARTIFACT_DOMAIN + +# Set working directory and copy source code +WORKDIR /app +COPY . . + +# Copy Maven settings pointing to CodeArtifact +COPY codeartifact_settings.xml /root/.m2/settings.xml + +# Generate token and build +RUN export AWS_ACCESS_KEY_ID=$CODEARTIFACT_ACCESS_KEY_ID \ + && export AWS_SECRET_ACCESS_KEY=$CODEARTIFACT_SECRET_ACCESS_KEY \ + && export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \ + --domain $CODEARTIFACT_DOMAIN \ + --domain-owner $CODEARTIFACT_ACCOUNT_ID \ + --region $CODEARTIFACT_REGION \ + --query authorizationToken --output text) \ + && mvn clean install -s /root/.m2/settings.xml + +# Runtime stage — no credentials here +FROM eclipse-temurin:21-jre +COPY --from=build /app/target/app.jar /app.jar +ENTRYPOINT ["java", "-jar", "/app.jar"] +``` + +## Step 3 — Docker Compose (build args) + +```yaml +build: + args: + CODEARTIFACT_ACCESS_KEY_ID: $CODEARTIFACT_ACCESS_KEY_ID + CODEARTIFACT_SECRET_ACCESS_KEY: $CODEARTIFACT_SECRET_ACCESS_KEY + CODEARTIFACT_REGION: $CODEARTIFACT_REGION + CODEARTIFACT_ACCOUNT_ID: $CODEARTIFACT_ACCOUNT_ID + CODEARTIFACT_REPOSITORY: $CODEARTIFACT_REPOSITORY + CODEARTIFACT_DOMAIN: $CODEARTIFACT_DOMAIN +``` + +## Step 4 — CI/CD Pipeline Configuration + + + + +```yaml +# .github/workflows/maven.yml +name: Build and Deploy to CodeArtifact + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Get CodeArtifact Token + run: | + echo "CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \ + --domain ${{ secrets.CODEARTIFACT_DOMAIN }} \ + --domain-owner ${{ secrets.CODEARTIFACT_ACCOUNT }} \ + --query authorizationToken --output text)" >> $GITHUB_ENV + + - name: Build and Deploy + run: mvn -s codeartifact_settings.xml clean package deploy +``` + + + + +```yaml +# gitlab-ci.yml +variables: + MAVEN_DOCKER_IMAGE: "maven:3.9.6-eclipse-temurin-21" + +stages: + - build + +build_artifacts: + stage: build + image: $MAVEN_DOCKER_IMAGE + before_script: + - pip3 install awscli + script: + - export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token + --domain ${CODEARTIFACT_DOMAIN} + --domain-owner ${CODEARTIFACT_ACCOUNT} + --query authorizationToken --output text) + - mvn -s settings.xml clean package deploy + only: + - develop +``` + + + + +```yaml +# bitbucket-pipelines.yml +- step: + name: Build and Upload to AWS CodeArtifact + image: maven:3.8.6-openjdk-8 + caches: + - maven + script: + - export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token + --domain ${CODEARTIFACT_DOMAIN} + --domain-owner ${CODEARTIFACT_ACCOUNT_ID} + --region ${CODEARTIFACT_REGION} + --query authorizationToken --output text) + - mvn -B clean install deploy -DskipTests +``` + + + + +## Best Practices + +- **Avoid credentials in final images**: Use multi-stage builds so tokens are only present during the build stage. +- **Use IAM Roles in AWS**: On EC2, ECS, or EKS, replace access keys with IAM roles for automatic credential management. +- **Local Maven cache**: Mount `.m2` as a volume (`-./.m2:/root/.m2`) to speed up subsequent builds. + +## Troubleshooting + +| Error | Cause | Fix | +| --- | --- | --- | +| **Token Expired** | CodeArtifact tokens are valid for 12 hours | Ensure the build doesn't exceed this window | +| **Insufficient Permissions** | Missing IAM policy | Verify `codeartifact:GetAuthorizationToken` and `codeartifact:ReadFromRepository` are granted | +| **Incorrect URL** | Wrong repository format | Use `https://-.d.codeartifact..amazonaws.com/maven//` | diff --git a/content/tutorials/en/aws-local-authentication.mdx b/content/tutorials/en/aws-local-authentication.mdx new file mode 100644 index 000000000..ca384aaf8 --- /dev/null +++ b/content/tutorials/en/aws-local-authentication.mdx @@ -0,0 +1,103 @@ +--- +title: Configure AWS Authentication for Local Development +sidebar_label: AWS Local Authentication +sidebar_position: 30 +description: Set up AWS CLI credentials and cross-account role assumption on your local machine to access SleakOps AWS resources across development, management, and production accounts. +tags: + - aws + - security +image: /img/tutorials/aws-local-authentication/aws-local-authentication.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Set up AWS CLI credentials on your local machine to access SleakOps AWS resources across development, management, and production accounts using cross-account role assumption. + +## How SleakOps Manages AWS Users + +SleakOps creates users in the **Security account**. Each user can assume roles in other accounts (development, management, production). This means you don't use direct credentials for each account — you assume a role from your Security account user. + +**Flow:** Your user (Security account) → assumes a role (target account) → gets temporary credentials to operate. + +Depending on your role in SleakOps, you can assume one of: +- `SleakopsViewerRole` +- `SleakopsEditorRole` +- `SleakopsAdminRole` + +## Prerequisites + +- An **Access Key** and **Secret Key** from a user in the Security account +- The **ARN of the role** in the target account (e.g., `arn:aws:iam::123456789012:role/SleakopsEditorRole`) — get this from the **AWS Switch Role** action in SleakOps. See [AWS Console Authentication](/docs/user/aws_console_authentication) +- [AWS CLI v2 ](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) installed + +## Step 1 — Configure the Base User Credentials + +```bash +aws configure +``` + +Enter when prompted: +- **AWS Access Key ID** — your Security account access key +- **AWS Secret Access Key** — your Security account secret key +- **Default region** — e.g., `us-east-1` +- **Output format** — `json` + +This writes to: +- Linux/macOS: `~/.aws/credentials` and `~/.aws/config` +- Windows: `C:\Users\\.aws\credentials` + +:::warning +`~/.aws/credentials` stores your access keys in plain text. Restrict its permissions (`chmod 600 ~/.aws/credentials`) and never commit it to version control. +::: + +## Step 2 — Add Role Profiles for Each Target Account + +Edit `~/.aws/config` (Linux/macOS) or `C:\Users\\.aws\config` (Windows) and add: + +```ini +[profile security-account] +aws_access_key_id = YOUR_ACCESS_KEY +aws_secret_access_key = YOUR_SECRET_KEY +region = us-east-1 + +[profile dev-account] +role_arn = arn:aws:iam::111111111111:role/SleakopsEditorRole +source_profile = security-account +region = us-east-1 + +[profile prd-account] +role_arn = arn:aws:iam::333333333333:role/SleakopsEditorRole +source_profile = security-account +region = us-east-1 +``` + +- `security-account` — your base user with access keys +- `dev-account` / `prd-account` — profiles that use the base credentials to assume the target role + +## Step 3 — Test the Role Assumption + +```bash +aws sts get-caller-identity --profile dev-account +``` + +You should see a `UserId` and `Arn` corresponding to the **role in the target account**. + +## Step 4 — Use the Profile + +Run any AWS command with the target profile: + +```bash +aws s3 ls --profile dev-account +aws eks list-clusters --profile prd-account +``` + +## Summary + +| Step | Action | +| --- | --- | +| 1 | Install AWS CLI v2 | +| 2 | Configure base Security account credentials with `aws configure` | +| 3 | Edit `~/.aws/config` to add role profiles for each environment | +| 4 | Use `--profile ` in your commands | diff --git a/content/tutorials/en/bitnami-image-deprecated.mdx b/content/tutorials/en/bitnami-image-deprecated.mdx new file mode 100644 index 000000000..fae30cc2e --- /dev/null +++ b/content/tutorials/en/bitnami-image-deprecated.mdx @@ -0,0 +1,69 @@ +--- +title: Bitnami Image Deprecation — What to Do +sidebar_label: Bitnami Image Deprecation +sidebar_position: 37 +description: Understand the Bitnami image deprecation effective August 28, 2025, and how to update your SleakOps chart dependencies to avoid ImagePullBackOff errors. +tags: + - kubernetes + - helm + - deployment +image: /img/tutorials/bitnami-image-deprecated/bitnami-image-deprecated.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Starting August 28, 2025, Bitnami changed how it distributes Docker images and Helm charts. This article explains what changed, what breaks if you don't act, and what to do in SleakOps. + +## What Changed + +On **August 28, 2025**, Bitnami made the following changes to its image catalog: + +- **Existing images** (all tags/versions) were migrated from `docker.io/bitnami` to a **"Bitnami Legacy"** repository (`docker.io/bitnamilegacy`), which no longer receives updates or security patches. +- The main `docker.io/bitnami` repository now only maintains a **limited set of "hardened" images** with only a `latest` tag, under the new **Bitnami Secure Images (BSI)** program — intended for development use. +- **Helm charts** remain available as OCI artifacts but without image updates, unless you override the image references. + +## What Breaks Without Action + +If your Helm charts reference Bitnami versioned images (e.g., `postgresql:13.7.0`), your cluster will fail to pull those images after August 28 with errors such as: + +``` +ImagePullBackOff +ErrImagePull +``` + +## Immediate Fix (Stop the Bleeding) + +The fastest fix is to update your chart dependencies to pull from `bitnamilegacy` and set the security flag that allows legacy images: + +1. In SleakOps, navigate to the **Chart Configuration** screen for the affected workload. +2. Update the `image.repository` to use `docker.io/bitnamilegacy/` instead of `docker.io/bitnami/`. +3. Add `global.security.allowInsecureImages: true` to your chart values. +4. Click **Apply changes** to deploy. + +SleakOps has already updated your chart values — `image.repository` is set to `docker.io/bitnamilegacy/` and `global.security.allowInsecureImages: true` is added. You only need to trigger the apply from the chart configuration screen. + + + SleakOps Chart Configuration screen + + + + SleakOps Apply changes confirmation + + +:::warning +Using `bitnamilegacy` images is a **temporary workaround**. These images will not receive security patches. Plan a migration to alternative images (e.g., official upstream images or BSI-compliant images) as a follow-up action. +::: + +## Long-Term Migration Path + +1. Identify all Bitnami images in use across your deployments. +2. For each image, find the equivalent upstream or BSI image. +3. Test the replacement image in a staging environment. +4. Update chart values to reference the new image and remove the `allowInsecureImages` flag. + +## References + +- [Bitnami containers deprecation announcement ](https://github.com/bitnami/containers/issues/83267) +- [Bitnami Secure Images program ](https://bitnami.com/secure-images) diff --git a/content/tutorials/en/connect-aws-resources.mdx b/content/tutorials/en/connect-aws-resources.mdx new file mode 100644 index 000000000..c8dd98894 --- /dev/null +++ b/content/tutorials/en/connect-aws-resources.mdx @@ -0,0 +1,144 @@ +--- +title: Connect to AWS Resources from Your Application +sidebar_label: Connect to AWS Resources +sidebar_position: 31 +description: Learn how to authenticate to AWS and connect to resources like S3 from local development or CI/CD pipelines — IAM user creation, access key management, and Node.js SDK examples. +tags: + - aws + - security + - node +image: /img/tutorials/connect-aws-resources/connect-aws-resources.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Learn the recommended ways to authenticate to AWS and connect to resources like S3 from local development, CI/CD pipelines, or static integrations — including IAM user creation and Node.js SDK examples. + +## When to Use Each Approach + +| Scenario | Recommended approach | +| --- | --- | +| Per-user access with audit trail | [Configure personal AWS credentials](/tutorial/aws-local-authentication) | +| CI/CD pipeline or static integration | Dedicated IAM user with scoped permissions (this guide) | +| Production workloads on EKS/EC2 | IAM Role with STS (no long-lived keys) | + +This guide covers the **static integration** approach — creating a dedicated IAM user for a specific resource like S3. + +## Step 1 — Create a Dedicated IAM User + +1. Log into your [AWS Console ](https://console.aws.amazon.com/) and navigate to **IAM**. +2. Click **Users → Create user**. +3. Enter a descriptive name (e.g., `s3-app-user`). +4. Select **Programmatic access**. +5. Click **Next: Permissions**. + +## Step 2 — Attach an S3 Policy + +**Option A — Predefined policy (simple):** +- Select `AmazonS3FullAccess` or `AmazonS3ReadOnlyAccess`. + +**Option B — Custom policy (recommended for production):** + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:GetObjectVersion"], + "Resource": "arn:aws:s3:::your-bucket-name/*" + }, + { + "Effect": "Allow", + "Action": ["s3:ListBucket", "s3:GetBucketLocation"], + "Resource": "arn:aws:s3:::your-bucket-name" + } + ] +} +``` + +Replace `your-bucket-name` with the actual bucket name. + +## Step 3 — Download the Access Keys + +After creating the user, download the `.csv` file or copy the **Access Key ID** and **Secret Access Key**. This is the only time you can see the Secret Access Key. + +:::warning +Never commit credentials to a repository. Store them in a secrets manager or environment variables. +::: + +## Step 4 — Configure Credentials in Your Environment + +**`.env` file (recommended for local development):** + +```bash +AWS_ACCESS_KEY_ID=your_access_key_id +AWS_SECRET_ACCESS_KEY=your_secret_access_key +AWS_REGION=us-east-1 +S3_BUCKET_NAME=your-bucket-name +``` + +Add `.env` to `.gitignore`. + +**AWS CLI (alternative):** + +```bash +aws configure +``` + +## Step 5 — Use the SDK in Node.js + +**Install dependencies:** + +```bash +npm install @aws-sdk/client-s3 dotenv +``` + +**Example with AWS SDK v3:** + +```javascript +require('dotenv').config(); +const { S3Client, PutObjectCommand, GetObjectCommand, ListObjectsV2Command } = require('@aws-sdk/client-s3'); + +const s3 = new S3Client({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, +}); + +const bucket = process.env.S3_BUCKET_NAME; + +async function uploadFile(key, body) { + await s3.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: body })); +} + +async function downloadFile(key) { + const res = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key })); + return res.Body.transformToString(); +} + +async function listFiles() { + const res = await s3.send(new ListObjectsV2Command({ Bucket: bucket })); + return res.Contents ?? []; +} +``` + +## Security Best Practices + +- **Never hardcode credentials** in source code. +- **Rotate access keys** every 90 days. +- **Use the minimum required permissions** — avoid wildcard `Resource: "*"`. +- **Monitor usage** via AWS CloudTrail. +- **Prefer IAM Roles** over long-lived access keys for workloads running on EKS, EC2, or ECS. + +## Troubleshooting + +| Error | Cause | Fix | +| --- | --- | --- | +| **Access Denied** | Missing IAM permissions or wrong credentials | Verify the IAM policy and that the correct keys are set | +| **Region not specified** | `AWS_REGION` not defined | Set the region in your config or `.env` | +| **Bucket does not exist** | Wrong bucket name or wrong region | Confirm the bucket name and region | diff --git a/content/tutorials/en/deploy-datadog-operator.mdx b/content/tutorials/en/deploy-datadog-operator.mdx new file mode 100644 index 000000000..c6b98353a --- /dev/null +++ b/content/tutorials/en/deploy-datadog-operator.mdx @@ -0,0 +1,154 @@ +--- +title: Deploy Datadog Operator and DatadogAgent on SleakOps +sidebar_label: Deploy Datadog Operator +sidebar_position: 33 +description: Install the Datadog Operator (v2.8.0) and deploy a DatadogAgent (v7.63.3) on a SleakOps EKS cluster with Karpenter NodePool tolerations, APM, log collection, and admission controller. +tags: + - kubernetes + - monitoring + - deployment + - helm +image: /img/tutorials/deploy-datadog-operator/deploy-datadog-operator.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Install the [Datadog Operator ](https://docs.datadoghq.com/containers/datadog_operator/) and deploy a `DatadogAgent` custom resource on a SleakOps EKS cluster to enable APM, log collection, and Kubernetes event monitoring. + +:::info +This guide was tested with **DatadogOperator v2.8.0** and **DatadogAgent v7.63.3**. +::: + +## Prerequisites + +**In the cluster:** + +- Create the `datadog` namespace +- Create the `datadog-secret` Secret containing your Datadog API key: + +```bash +kubectl create secret generic datadog-secret \ + --from-literal=api-key= \ + -n datadog +``` + +**Optional but recommended:** Create a dedicated NodePool for Datadog (e.g., `datadog-app`). If you use a dedicated NodePool, workloads you want to monitor must also run on that NodePool. + +## Step 1 — Deploy the Datadog Operator + +```bash +helm upgrade -i datadog datadog/datadog-operator \ + --namespace datadog \ + --create-namespace \ + --set "tolerations[0].key=karpenter.sh/nodepool" \ + --set "tolerations[0].operator=Equal" \ + --set "tolerations[0].value=" +``` + +Replace `` with your NodePool name (e.g., `datadog-app-amd`). + +## Step 2 — Prepare Workloads for Instrumentation + +Add these annotations/labels to the **Deployment** spec of each workload you want Datadog to instrument: + +```yaml +spec: + template: + metadata: + labels: + admission.datadoghq.com/enabled: "true" + annotations: + admission.datadoghq.com/js-lib.version: v5.45.0 +``` + +In the workload's **Var Group**, add: + +``` +DD_LOGS_ENABLED=true +DD_LOGS_INJECTION=true +DD_ENV= +DD_SERVICE= +``` + +## Step 3 — Deploy the DatadogAgent + +Create `datadogagent.yaml`: + +```yaml +apiVersion: datadoghq.com/v2alpha1 +kind: DatadogAgent +metadata: + name: datadog + namespace: datadog +spec: + global: + clusterName: + site: datadoghq.com # see https://docs.datadoghq.com/getting_started/site/ + credentials: + apiSecret: + secretName: datadog-secret + keyName: api-key + features: + admissionController: + enabled: true + mutateUnlabelled: false + apm: + enabled: true + hostPortConfig: + enabled: true + hostPort: 8126 + instrumentation: + enabled: false + logCollection: + enabled: true + containerCollectAll: true + eventCollection: + collectKubernetesEvents: true + override: + clusterAgent: + tolerations: + - key: karpenter.sh/nodepool + operator: Equal + value: + nodeAgent: + env: + - name: DD_LOGS_CONFIG_AUTO_MULTI_LINE_DETECTION + value: "true" + - name: DD_PROFILING_ENABLED + value: "true" + - name: DD_TRACE_ENABLED + value: "true" + - name: DD_LOGS_ENABLED + value: "true" + - name: DD_LOGS_INJECTION + value: "true" + - name: DD_CONTAINER_EXCLUDE + value: "name:.*" + - name: DD_CONTAINER_INCLUDE + value: "name:" # match spec.template.spec.containers.name + tolerations: + - key: karpenter.sh/nodepool + operator: Equal + value: +``` + +Apply it: + +```bash +kubectl apply -f datadogagent.yaml -n datadog +``` + +## Step 4 — Verify the Deployment + +```bash +kubectl get datadogagent -n datadog +kubectl get pods -n datadog +``` + +All pods should reach `Running` state within a few minutes. + +:::tip +This is a reference configuration that can be adjusted for your environment — expand `DD_CONTAINER_INCLUDE` to cover more workloads or modify any feature flags as needed. +::: diff --git a/content/tutorials/en/deploy-retool-helm.mdx b/content/tutorials/en/deploy-retool-helm.mdx new file mode 100644 index 000000000..e116fecbc --- /dev/null +++ b/content/tutorials/en/deploy-retool-helm.mdx @@ -0,0 +1,158 @@ +--- +title: Deploy Retool Using Helm Charts on SleakOps +sidebar_label: Deploy Retool with Helm +sidebar_position: 24 +description: Deploy Retool v6.3.6 on a SleakOps Kubernetes cluster using the official Helm chart with EBS, EFS, and an AMD64-dedicated NodePool. +tags: + - kubernetes + - helm + - deployment +image: /img/tutorials/deploy-retool-helm/deploy-retool-helm.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Deploy [Retool ](https://retool.com) on a SleakOps Kubernetes cluster using [Helm chart v6.3.6 ](https://artifacthub.io/packages/helm/retool/retool/6.3.6). + +:::info +This guide targets **Helm Chart version 6.3.6**. Check [ArtifactHub ](https://artifacthub.io/packages/helm/retool/retool) for the latest version and any values changes. +::: + +## Prerequisites + +**SleakOps prerequisites:** + +- [EBS CSI Driver](/docs/cluster/addons/ebs) installed (required for the PostgreSQL volume) +- [EFS CSI Driver](/docs/cluster/addons/efs) installed (required for Retool's app and workflow storage) +- A dedicated AMD64 NodePool created (Retool only runs on AMD64 architecture) + +**Kubernetes prerequisites:** + +- Namespace `retool` created +- Secret `retool-keys` created with the following keys: + - `encryption-key`: generate with `openssl rand -base64 36` + - `jwt-secret`: generate with `openssl rand -base64 36` + - `license-key`: your Retool license key +- Secret `retool-postgresql` created with: + - `password` + - `postgres-password` + - `replication-password` +- Google OAuth credentials (`clientId` and `clientSecret`) for SSO + +## Let's Start + +### Step 1 — Create the `retool-values.yaml` file + +Replace all values highlighted with `{...}` for your deployment: + +```yaml +config: + auth: + google: + clientId: xxxxxxxxxxxxxxxxx.apps.googleusercontent.com + clientSecret: xxxxxxxxxxxxxxxxxxxxxxx + encryption-key: + jwt-secret: + licenseKeySecretName: retool-keys + useInsecureCookies: true # required when TLS is terminated at the load balancer + +env: + ALLOW_SAME_ORIGIN_OPTION: "true" + BASE_DOMAIN: https://retool.{domain}.com + COOKIE_INSECURE: "true" # required when TLS is terminated at the load balancer + DOMAINS: https://retool.{domain}.com + HOST_HEADER_NAME: x-forwarded-host + SANDBOX_DOMAIN: https://retool.{domain}.com + +image: + pullPolicy: IfNotPresent + repository: tryretool/backend + tag: 3.114.13-stable # replace with target version + +ingress: + enabled: true + annotations: + alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:111222333444:certificate/12121212-1212-1212-1212-121212232323 + alb.ingress.kubernetes.io/group.name: {group.name}-private + alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' + alb.ingress.kubernetes.io/target-type: ip + hosts: + - host: retool.{domain}.com + paths: + - path: / + pathType: Prefix + ingressClassName: alb-ingressclass-private + labels: + app.kubernetes.io/name: retool{domain}com + tls: + - hosts: + - retool.{domain}.com + +nodeSelector: + karpenter.sh/nodepool: {nodepool_name} # e.g.: amd64-utilities + kubernetes.io/arch: amd64 + +persistentVolumeClaim: + enabled: true + mountPath: /retool_backend/pv-data + size: 15Gi + storageClass: efs-sc-retain + +postgresql: + auth: + username: postgres + existingSecret: retool-postgresql + secretKeys: + adminPasswordKey: postgres-password + replicationPasswordKey: replication-password + userPasswordKey: password + primary: + tolerations: + - key: karpenter.sh/nodepool + operator: Equal + value: {nodepool_name} # e.g.: amd64-utilities + +replicaCount: 1 + +resources: + limits: + cpu: 4096m + memory: 8192Mi + requests: + cpu: 1024m + memory: 2048Mi + +serviceAccount: + name: retool-sa + +tolerations: +- key: karpenter.sh/nodepool + operator: Equal + value: {nodepool_name} # e.g.: amd64-utilities +``` + +### Step 2 — Deploy the chart + +Add the Retool Helm repo and install: + +```bash +helm repo add retool https://charts.retool.com +helm repo update + +helm upgrade --install retool retool/retool \ + --version 6.3.6 \ + --namespace retool \ + --values retool-values.yaml +``` + +### Step 3 — Verify the deployment + +Check that the Pods are running: + +```bash +kubectl get pods -n retool +``` + +Once the `retool` Pod is `Running`, open `https://retool.{domain}.com` to complete the initial setup. diff --git a/content/tutorials/en/dms-rds-migration.mdx b/content/tutorials/en/dms-rds-migration.mdx new file mode 100644 index 000000000..ee96f5721 --- /dev/null +++ b/content/tutorials/en/dms-rds-migration.mdx @@ -0,0 +1,236 @@ +--- +title: Use AWS DMS to Synchronize or Migrate RDS Databases +sidebar_label: DMS RDS Migration +sidebar_position: 19 +description: Step-by-step guide to migrate or continuously synchronize RDS databases across AWS accounts using AWS Database Migration Service (DMS). +tags: + - aws + - rds + - database + - dms + - migration +image: /img/tutorials/dms-rds-migration/dms-rds-migration.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Migrate or continuously synchronize a source RDS database to a SleakOps-managed RDS using AWS Database Migration Service (DMS), including cross-account networking and per-engine prerequisites. + +## Prerequisites + +- AWS CLI configured with profiles for both source and destination accounts +- Source and target RDS instances running and accessible +- Sufficient resources: the DMS Replication Instance should match or exceed the source database's specs +- The source database must have CDC (Change Data Capture) enabled — see the engine-specific steps below + +## Let's Start + +### Step 1 — Create the DMS Replication Instance + +Run this in the **source account**. Set `allocated_storage`, `replication_instance`, `availability_zone`, `source_db_name`, and `subnet_ids` (must match the source DB subnets): + +```bash +allocated_storage="25" +replication_instance="dms.r5.large" +availability_zone="us-east-1a" +source_db_name="sourcedatabase" +subnet_ids="subnet-0123456789abcdef0,subnet-0fedcba9876543210" + +replication_instance_id="replication-${source_db_name}" +subnet_group_name="dms-subnet-group-${source_db_name}" + +aws dms create-replication-subnet-group \ + --replication-subnet-group-identifier "$subnet_group_name" \ + --replication-subnet-group-description "Subnet group for DMS replication of $source_db_name" \ + --subnet-ids ${subnet_ids//,/ } + +latest_engine_version=$(aws dms describe-orderable-replication-instances \ + --query 'OrderableReplicationInstances[*].[EngineVersion]' --output text | sort -V | tail -n 1) + +aws dms create-replication-instance \ + --replication-instance-identifier "$replication_instance_id" \ + --replication-instance-class "$replication_instance" \ + --allocated-storage "$allocated_storage" \ + --availability-zone "$availability_zone" \ + --replication-subnet-group-identifier "$subnet_group_name" \ + --engine-version "$latest_engine_version" \ + --no-publicly-accessible \ + --tags Key=SourceDB,Value="$source_db_name" +``` + +### Step 2 — Create source and target endpoints + +**Source endpoint:** + +```bash +source_db_name="sourcedatabase" +source_engine="postgres" +source_username="mydbuser" +source_password="mypassword" +source_server_name="mydb.c1234567890.us-east-1.rds.amazonaws.com" +source_database_name="mydatabase" +source_port="5432" + +aws dms create-endpoint \ + --endpoint-identifier "source-endpoint-${source_db_name}" \ + --endpoint-type "source" \ + --engine-name "$source_engine" \ + --username "$source_username" \ + --password "$source_password" \ + --server-name "$source_server_name" \ + --database-name "$source_database_name" \ + --port "$source_port" +``` + +**Target endpoint:** + +```bash +target_db_name="targetdatabase" +target_engine="postgres" +target_username="targetdbuser" +target_password="targetpassword" +target_server_name="targetdb.c1234567890.us-east-1.rds.amazonaws.com" +target_database_name="targetdb" +target_port="5432" + +aws dms create-endpoint \ + --endpoint-identifier "target-endpoint-${target_db_name}" \ + --endpoint-type "target" \ + --engine-name "$target_engine" \ + --username "$target_username" \ + --password "$target_password" \ + --server-name "$target_server_name" \ + --database-name "$target_database_name" \ + --port "$target_port" +``` + +### Step 3 — Configure cross-account networking + +Both accounts need VPC Peering and Security Group rules so the Replication Instance can reach both databases. + +**Source account (full script):** + +```bash +REGION="us-east-1" +SOURCE_PROFILE="source-profile" +SOURCE_VPC_ID="" +TARGET_VPC_ID="" +TARGET_ACCOUNT_ID="" +SOURCE_RDS_SECURITY_GROUP="" +TARGET_RDS_CIDR_BLOCK="" + +peering_connection_id=$(aws ec2 create-vpc-peering-connection \ + --vpc-id "$SOURCE_VPC_ID" \ + --peer-vpc-id "$TARGET_VPC_ID" \ + --peer-owner-id "$TARGET_ACCOUNT_ID" \ + --region "$REGION" --profile "$SOURCE_PROFILE" \ + --query "VpcPeeringConnection.VpcPeeringConnectionId" --output text) + +SOURCE_ROUTE_TABLE_ID=$(aws ec2 describe-route-tables \ + --filters "Name=vpc-id,Values=$SOURCE_VPC_ID" \ + --profile "$SOURCE_PROFILE" \ + --query "RouteTables[0].RouteTableId" --output text) + +aws ec2 create-route \ + --route-table-id "$SOURCE_ROUTE_TABLE_ID" \ + --destination-cidr-block "$TARGET_RDS_CIDR_BLOCK" \ + --vpc-peering-connection-id "$peering_connection_id" \ + --profile "$SOURCE_PROFILE" + +aws ec2 authorize-security-group-ingress \ + --group-id "$SOURCE_RDS_SECURITY_GROUP" \ + --protocol tcp --port 5432 \ + --cidr "$TARGET_RDS_CIDR_BLOCK" \ + --profile "$SOURCE_PROFILE" + +aws iam create-role \ + --role-name "dms-access-role" \ + --profile "$SOURCE_PROFILE" \ + --assume-role-policy-document '{"Version":"2012-10-17","Statement":{"Effect":"Allow","Principal":{"Service":"dms.amazonaws.com"},"Action":"sts:AssumeRole"}}' + +aws iam attach-role-policy \ + --role-name "dms-access-role" --profile "$SOURCE_PROFILE" \ + --policy-arn arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole +``` + +**Target account:** Accept the VPC peering, update route tables, and create a cross-account IAM role — mirror the source account steps with target account credentials. + +### Step 4 — Engine-specific prerequisites + +Before running the migration task, the source database must have CDC enabled. + +**PostgreSQL — update `pg_hba.conf` and `postgresql.conf`:** + +``` +# pg_hba.conf — allow DMS replication instance +host all all /32 md5 +host replication dms /32 md5 +``` + +``` +# postgresql.conf +wal_level = logical +max_replication_slots = 2 +max_wal_senders = 2 +wal_sender_timeout = 0 +``` + +Reload: `SELECT pg_reload_conf();` + +**Oracle — enable archive log and supplemental logging:** + +```sql +-- Enable Archive Log Mode (if not already enabled) +SHUTDOWN TRANSACTIONAL; +STARTUP MOUNT; +ALTER DATABASE ARCHIVELOG; +ALTER DATABASE OPEN; + +ALTER DATABASE FORCE LOGGING; +ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; +ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY, UNIQUE, FOREIGN KEY) COLUMNS; +``` + +### Step 5 — Run the pre-migration assessment and start the task + +**Pre-migration assessment:** + +```bash +source_db_name="sourcedatabase" +target_db_name="targetdatabase" + +aws dms start-replication-task-assessment-run \ + --replication-task-assessment-run-name "premigration-${source_db_name}-to-${target_db_name}" \ + --replication-instance-arn "$(aws dms describe-replication-instances \ + --filters "Name=replication-instance-id,Values=replication-${source_db_name}" \ + --query "ReplicationInstances[0].ReplicationInstanceArn" --output text)" \ + --source-endpoint-arn "$(aws dms describe-endpoints \ + --filters "Name=endpoint-id,Values=source-endpoint-${source_db_name}" \ + --query "Endpoints[0].EndpointArn" --output text)" \ + --target-endpoint-arn "$(aws dms describe-endpoints \ + --filters "Name=endpoint-id,Values=target-endpoint-${target_db_name}" \ + --query "Endpoints[0].EndpointArn" --output text)" \ + --migration-type "full-load-and-cdc" \ + --table-mappings '{"rules":[{"rule-type":"selection","rule-id":"1","rule-name":"include-all","object-locator":{"schema-name":"%","table-name":"%"},"rule-action":"include"}]}' +``` + +**Start the migration task** (after the assessment passes): + +```bash +aws dms create-replication-task \ + --replication-task-identifier "migration-task-${source_db_name}-to-${target_db_name}" \ + --source-endpoint-arn "$(aws dms describe-endpoints \ + --filters "Name=endpoint-id,Values=source-endpoint-${source_db_name}" \ + --query "Endpoints[0].EndpointArn" --output text)" \ + --target-endpoint-arn "$(aws dms describe-endpoints \ + --filters "Name=endpoint-id,Values=target-endpoint-${target_db_name}" \ + --query "Endpoints[0].EndpointArn" --output text)" \ + --replication-instance-arn "$(aws dms describe-replication-instances \ + --filters "Name=replication-instance-id,Values=replication-${source_db_name}" \ + --query "ReplicationInstances[0].ReplicationInstanceArn" --output text)" \ + --migration-type "full-load-and-cdc" \ + --table-mappings '{"rules":[{"rule-type":"selection","rule-id":"1","rule-name":"include-all","object-locator":{"schema-name":"%","table-name":"%"},"rule-action":"include"}]}' +``` + diff --git a/content/tutorials/en/install-datadog.mdx b/content/tutorials/en/install-datadog.mdx new file mode 100644 index 000000000..aaae174dc --- /dev/null +++ b/content/tutorials/en/install-datadog.mdx @@ -0,0 +1,89 @@ +--- +title: Install Datadog on a SleakOps EKS Cluster +sidebar_label: Install Datadog +sidebar_position: 34 +description: Install the Datadog monitoring agent on an Amazon EKS cluster managed by SleakOps using Helm, with a values.yaml and recommended best practices. +tags: + - kubernetes + - monitoring + - helm + - aws +image: /img/tutorials/install-datadog/install-datadog.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Install the [Datadog ](https://www.datadoghq.com/) monitoring agent on a SleakOps EKS cluster using Helm to gain full visibility into application performance and infrastructure health. + +## Prerequisites + +- A SleakOps EKS cluster with `kubectl` configured +- A Datadog account and API key + +## Step 1 — Configure AWS CLI and kubectl + +Verify your environment is ready: + +```bash +aws --version +kubectl version --short +``` + +## Step 2 — Add the Datadog Helm Repository + +```bash +helm repo add datadog https://helm.datadoghq.com +helm repo update +``` + +## Step 3 — Create `values.yaml` + +```yaml +datadog: + apiKey: + clusterName: + logs: + enabled: true + containerCollectAll: true + apm: + portEnabled: true + processAgent: + enabled: true +``` + +:::warning +Do not commit `values.yaml` with the API key to a repository. Use a Kubernetes Secret or pass `--set datadog.apiKey=...` directly. +::: + +## Step 4 — Install the Datadog Agent + +```bash +helm install datadog-agent datadog/datadog \ + --namespace datadog \ + --create-namespace \ + -f values.yaml +``` + +## Step 5 — Verify the Installation + +```bash +kubectl get pods -n datadog +``` + +All `datadog-agent-*` pods should reach `Running` state within a few minutes. + +## Installation Options Comparison + +| Method | Advantages | Disadvantages | +| --- | --- | --- | +| **Helm (this guide)** | Flexible, configurable, standard | Requires Helm knowledge | +| **Datadog Operator** | Lifecycle management, CRD-based | More complex to set up | +| **Fargate Logging** | No node management | Works only for specific workloads | + +## Best Practices + +- **Secure the API key:** Store it in a Kubernetes Secret rather than directly in `values.yaml`. +- **Configure alerts:** Set up Datadog monitors to get notified when infrastructure problems are detected. +- **Keep versions updated:** Maintain both the Datadog agent and EKS cluster updated to avoid compatibility issues. diff --git a/content/tutorials/en/install-keda.mdx b/content/tutorials/en/install-keda.mdx index 91178ec9e..815158d5b 100644 --- a/content/tutorials/en/install-keda.mdx +++ b/content/tutorials/en/install-keda.mdx @@ -20,6 +20,12 @@ import { FiExternalLink } from "react-icons/fi"; Learn how to install KEDA (Kubernetes Event-Driven Autoscaling) on your cluster using different methods. KEDA allows you to automatically scale your applications based on events and custom metrics. +:::tip Install via the SleakOps Addon +If your cluster is managed by SleakOps, you can install KEDA in one click from the **Addons** section of your cluster — SleakOps deploys the operator, the metrics adapter, the admission webhooks, and wires up the IAM role via IRSA for the AWS scalers (SQS, CloudWatch, DynamoDB Streams, Kinesis, …). + +See the [KEDA Addon documentation](/docs/cluster/addons/keda) for full installation steps, AWS policy configuration, and `ScaledObject` examples. +::: + ## Installation Methods ### Using Lens Interface (option 1) diff --git a/content/tutorials/en/install-new-relic.mdx b/content/tutorials/en/install-new-relic.mdx new file mode 100644 index 000000000..435a29e05 --- /dev/null +++ b/content/tutorials/en/install-new-relic.mdx @@ -0,0 +1,116 @@ +--- +title: Install New Relic on Your Application +sidebar_label: Install New Relic +sidebar_position: 35 +description: Install and configure the New Relic monitoring agent for your application — account setup, language-specific agent installation, and configuration best practices. +tags: + - monitoring + - deployment + - node + - python +image: /img/tutorials/install-new-relic/install-new-relic.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Install and configure the [New Relic ](https://newrelic.com/) monitoring agent on your application to measure and improve performance, with step-by-step instructions for multiple runtimes. + +## Prerequisites + +- A New Relic account (create one at [newrelic.com ](https://newrelic.com/)) +- Your New Relic License Key (found under **Account settings**) +- Access to restart or redeploy your application + +## Step 1 — Get Your License Key + +1. Log into your New Relic account. +2. Navigate to **Account settings**. +3. Copy your **License Key** — you'll need it for the agent configuration. + +## Step 2 — Install the Agent + +Choose the agent for your runtime: + + + + +```bash +npm install newrelic --save +``` + +Create `newrelic.js` in the root of your project: + +```javascript +exports.config = { + app_name: ['Your App Name'], + license_key: process.env.NEW_RELIC_LICENSE_KEY, + logging: { + level: 'info', + }, +}; +``` + +Require the agent at the very top of your entry file: + +```javascript +require('newrelic'); +// rest of your app +``` + + + + +```bash +pip install newrelic +``` + +Generate a config file: + +```bash +export NEW_RELIC_LICENSE_KEY=your-license-key +newrelic-admin generate-config $NEW_RELIC_LICENSE_KEY newrelic.ini +``` + +Run your app through the agent: + +```bash +NEW_RELIC_CONFIG_FILE=newrelic.ini newrelic-admin run-program python app.py +``` + + + + +Add the agent JAR to your startup command: + +```bash +java -javaagent:/path/to/newrelic.jar -jar your-app.jar +``` + +Configure `newrelic.yml` with your `license_key` and `app_name`. + + + + +## Step 3 — Restart the Application + +Once the agent is configured, restart or redeploy your application. New Relic will start sending data to your account within a few minutes. + +## Monitoring Alternatives + +| Tool | Strengths | Considerations | +| --- | --- | --- | +| **New Relic** | Easy setup, broad language support | Cost at scale | +| **Datadog** | Excellent multi-cloud and integrations | Can be costly at scale | +| **Dynatrace** | AI-driven auto-optimization | Steeper learning curve | +| **AppDynamics** | Deep performance analytics | Requires detailed initial config | + +## Best Practices + +- **Continuous monitoring:** Regularly review metrics and configure alerts to respond quickly to incidents. +- **Cost analysis:** Re-evaluate your plan as application traffic grows to avoid unexpected charges. +- **Keep configuration updated:** Revisit agent settings after significant application changes. +- **Follow official docs:** Always check [New Relic's documentation ](https://docs.newrelic.com/) for the latest agent versions and configuration options. diff --git a/content/tutorials/en/lambda-cicd-github-actions.mdx b/content/tutorials/en/lambda-cicd-github-actions.mdx new file mode 100644 index 000000000..ea5485593 --- /dev/null +++ b/content/tutorials/en/lambda-cicd-github-actions.mdx @@ -0,0 +1,202 @@ +--- +title: Deploy AWS Lambda Functions with GitHub Actions +sidebar_label: Lambda CI/CD with GitHub Actions +sidebar_position: 23 +description: Build a GitHub Actions pipeline that packages, tests, and deploys AWS Lambda functions automatically on every push to main or staging. +tags: + - aws + - lambda + - ci-cd + - github-actions + - deployment +image: /img/tutorials/lambda-cicd-github-actions/lambda-cicd-github-actions.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Automate the build, test, and deployment of AWS Lambda functions using GitHub Actions, with branch-based environments and optional rollback on health-check failure. + +## Prerequisites + +- An AWS Lambda function already created +- An IAM user or role with the required permissions (see below) +- A GitHub repository containing your Lambda code + +## Let's Start + +### Step 1 — Project structure + +Organize your Lambda project like this: + +``` +lambda-project/ +├── .github/ +│ └── workflows/ +│ └── deploy.yml +├── src/ +│ └── handlers/ +│ └── example_handler.py +├── requirements.txt +└── README.md +``` + +### Step 2 — Example Lambda function (Python) + +```python +import json +import os +import boto3 +import logging + +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +def lambda_handler(event, context): + try: + table_name = os.environ.get('TABLE_NAME', 'default-table') + logger.info(f"Processing event: {json.dumps(event)}") + dynamodb = boto3.resource('dynamodb') + table = dynamodb.Table(table_name) + table.put_item(Item={ + 'id': event.get('id', 'unknown'), + 'data': event.get('data', {}), + 'request_id': context.aws_request_id + }) + return { + 'statusCode': 200, + 'body': json.dumps({'message': 'Success', 'request_id': context.aws_request_id}) + } + except Exception as e: + logger.error(f"Error: {str(e)}", exc_info=True) + return {'statusCode': 500, 'body': json.dumps({'error': str(e)})} +``` + +### Step 3 — GitHub Actions workflow + +Create `.github/workflows/deploy.yml`: + +```yaml +name: Deploy Lambda + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + AWS_REGION: us-east-1 + LAMBDA_FUNCTION_NAME: example-lambda + +jobs: + deploy: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} + + - name: Install dependencies + run: | + pip install -r requirements.txt -t ./package + cp -r src/* ./package/ + + - name: Create deployment package + run: | + cd package + zip -r ../lambda-deployment.zip . + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Deploy to Lambda + run: | + aws lambda update-function-code \ + --function-name ${{ env.LAMBDA_FUNCTION_NAME }} \ + --zip-file fileb://lambda-deployment.zip + + - name: Update Lambda configuration + run: | + aws lambda update-function-configuration \ + --function-name ${{ env.LAMBDA_FUNCTION_NAME }} \ + --environment Variables="{TABLE_NAME=${{ secrets.DYNAMODB_TABLE }}}" \ + --timeout 30 \ + --memory-size 256 + + - name: Health check + run: | + aws lambda invoke \ + --function-name ${{ env.LAMBDA_FUNCTION_NAME }} \ + --payload '{"id": "health-check"}' \ + response.json + status=$(jq -r '.statusCode' response.json) + if [ "$status" != "200" ]; then + echo "Health check failed (status: $status)" + exit 1 + fi +``` + +### Step 4 — Add secrets in GitHub + +Go to **Settings → Secrets and variables → Actions** and add: + +| Secret | Description | +| --- | --- | +| `AWS_ACCESS_KEY_ID` | AWS access key | +| `AWS_SECRET_ACCESS_KEY` | AWS secret key | +| `DYNAMODB_TABLE` | DynamoDB table name (optional) | + +### Step 5 — Required IAM permissions + +The IAM user or role used by GitHub Actions needs at minimum: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "lambda:UpdateFunctionCode", + "lambda:UpdateFunctionConfiguration", + "lambda:PublishVersion", + "lambda:UpdateAlias", + "lambda:InvokeFunction" + ], + "Resource": "arn:aws:lambda:us-east-1:*:function:example-lambda" + } + ] +} +``` + +## Troubleshooting + +**Package too large:** Use Lambda Layers for heavy dependencies: + +```bash +aws lambda publish-layer-version \ + --layer-name common-dependencies \ + --zip-file fileb://dependencies-layer.zip \ + --compatible-runtimes python3.11 + +aws lambda update-function-configuration \ + --function-name example-lambda \ + --layers arn:aws:lambda:us-east-1:123456789:layer:common-dependencies:1 +``` + +**Permission denied:** Verify the IAM policy in Step 5 is attached to the role used by GitHub Actions. diff --git a/content/tutorials/en/lens-cluster-connectivity.mdx b/content/tutorials/en/lens-cluster-connectivity.mdx new file mode 100644 index 000000000..9b89d8b5c --- /dev/null +++ b/content/tutorials/en/lens-cluster-connectivity.mdx @@ -0,0 +1,79 @@ +--- +title: Troubleshoot Cluster Connectivity with Lens +sidebar_label: Lens Connectivity Troubleshooting +sidebar_position: 25 +description: Diagnose and fix Lens connectivity issues to a SleakOps Kubernetes cluster — VPN checks, DNS configuration for Linux, macOS, and Windows. +tags: + - kubernetes + - lens + - vpn + - networking + - dns +image: /img/tutorials/lens-cluster-connectivity/lens-cluster-connectivity.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Diagnose and resolve connection problems between Lens and a SleakOps Kubernetes cluster, typically caused by VPN configuration or DNS resolution issues. + +## Prerequisites + +- Lens installed and configured with the cluster kubeconfig +- Pritunl client installed and a VPN profile configured + +## Diagnosis and fixes + +### Step 1 — Check VPN status + +- Make sure no other VPN is connected simultaneously. +- Verify that the Pritunl VPN connection is active. +- **Lens does not handle network changes gracefully** — if you connect or disconnect a VPN while Lens is open, restart Lens. +- Try the Pritunl reset buttons: + - **Reset local DNS service** + - **Reset Networking** + +### Step 2 — Fix DNS resolution + +The most common cause of Lens connectivity failures is the machine trying to resolve the cluster hostname using public DNS servers instead of the AWS VPC's private DNS resolver. + +**Linux and macOS — update `/etc/resolv.conf`:** + +```bash +sudo nano /etc/resolv.conf +``` + +Add the nameservers for your environments: + +``` +nameserver 10.110.0.2 # DEV VPC DNS +nameserver 10.120.0.2 # MGT VPC DNS +nameserver 10.130.0.2 # PRD VPC DNS +``` + +:::warning +`/etc/resolv.conf` is regenerated automatically when the network changes. You may need to re-apply this while connected to the VPN, and after reboots or network switches. +::: + +**Windows — update DNS settings:** + +1. Open **Control Panel → Network and Sharing Center**. +2. Click **Change adapter settings** (left column). +3. Right-click the active connection (Ethernet or Wi-Fi) → **Properties**. +4. Select **Internet Protocol Version 4 (TCP/IPv4)** → **Properties**. +5. Choose **Use the following DNS server addresses**. +6. Enter the VPC DNS for your environment (e.g., `10.130.0.2`) as the preferred server and a public fallback like `8.8.8.8`. +7. Click OK. + +### Step 3 — Enable "Force DNS" in Pritunl + +In the Pritunl connection profile, enable the **Force DNS configuration** option. This ensures Pritunl overrides the system DNS with the VPN's DNS servers when connected. + +## DNS reference by environment + +| Environment | VPC DNS | +| --- | --- | +| DEV | `10.110.0.2` | +| MGT | `10.120.0.2` | +| PRD | `10.130.0.2` | diff --git a/content/tutorials/en/migrate-ebs-volumes.mdx b/content/tutorials/en/migrate-ebs-volumes.mdx new file mode 100644 index 000000000..c2345ecc8 --- /dev/null +++ b/content/tutorials/en/migrate-ebs-volumes.mdx @@ -0,0 +1,164 @@ +--- +title: Migrate EBS Volumes to a New AWS Account +sidebar_label: Migrate EBS Volumes +sidebar_position: 15 +description: Step-by-step guide to snapshot, share, copy, and recreate EBS volumes from an external AWS account into a SleakOps-managed account. +tags: + - aws + - kubernetes + - storage + - migration + - ebs +image: /img/tutorials/migrate-ebs-volumes/migrate-ebs-volumes.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Move EBS volumes from an external AWS account into a SleakOps-managed account by creating snapshots, sharing them, copying to the destination, and recreating the volumes. + +## Prerequisites + +- AWS CLI configured with profiles for both source and destination accounts +- `kubectl` access to the source cluster to identify volume IDs +- The destination account ID (12-digit format) +- A SleakOps Project deployed in the destination account (to attach the IAM policy) + +## Let's Start + +### Step 1 — Identify the volume IDs to migrate + +Run this command against the source cluster to list all pods and their attached Persistent Volumes: + +```bash +kubectl get pods --all-namespaces -o json | jq -c '.items[] | + { + namespace: .metadata.namespace, + pod: .metadata.name, + pvc: (.spec.volumes[]? | select(.persistentVolumeClaim != null) | .persistentVolumeClaim.claimName) + } | select(.pvc != null)' +``` + +Build a list of pod names and their associated volume IDs: + +```bash +pod_volume_ids=( + "core-stg-postgresql-0 vol-025d6ddd6af9df250" + "core-stg-redis-master-0 vol-0a36b5ff1809fcc49" + "grafana-6dfd99b599-26gz9 vol-0f851cc80d49463b5" +) +``` + +### Step 2 — Create and share snapshots (source account) + +This script creates a snapshot for each volume and shares it with the destination account. Replace `target_account_id` with the destination AWS account ID: + +```bash +pod_volume_ids=( + "pod_name1 vol_id1" + "pod_name2 vol_id2" +) +target_account_id="111222333444" + +for entry in "${pod_volume_ids[@]}"; do + pod_name=$(echo "$entry" | awk '{print $1}') + volume_id=$(echo "$entry" | awk '{print $2}') + snapshot_name="Snapshot-$volume_id" + + snapshot_id=$(aws ec2 create-snapshot \ + --volume-id "$volume_id" \ + --description "Snapshot of $volume_id from Pod $pod_name" \ + --tag-specifications "ResourceType=snapshot,Tags=[{Key=Name,Value=$snapshot_name},{Key=Pod,Value=$pod_name},{Key=DestinationAccount,Value=$target_account_id}]" \ + --query SnapshotId --output text) + + echo "Waiting for $snapshot_id to complete..." + aws ec2 wait snapshot-completed --snapshot-ids "$snapshot_id" + + aws ec2 modify-snapshot-attribute \ + --snapshot-id "$snapshot_id" \ + --attribute createVolumePermission \ + --operation-type add \ + --user-ids "$target_account_id" + + echo "Snapshot $snapshot_id shared with account $target_account_id" +done +``` + +### Step 3 — Attach the required IAM policy (destination account) + +Attach this policy to the IAM role used by a Pod in your destination SleakOps Project. The role name follows the pattern `{PROJECT_NAME}-{SHORT_UUID}-sa`: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:DescribeSnapshots", + "ec2:CopySnapshot", + "ec2:CreateVolume", + "ec2:CreateTags", + "ec2:DescribeVolumes", + "ec2:DescribeSnapshotAttribute", + "ec2:DeleteSnapshot", + "ec2:DeleteVolume" + ], + "Effect": "Allow", + "Resource": "*" + } + ] +} +``` + +### Step 4 — Copy snapshots and create volumes (destination account) + +Run this script from a Pod in the destination account that has the policy from Step 3. Replace the regions and availability zone as needed: + +```bash +source_region="us-east-1" +destination_region="us-east-1" +availability_zone="us-east-1a" + +pod_volume_ids=( + "pod_name1 vol_id1" + "pod_name2 vol_id2" +) + +for entry in "${pod_volume_ids[@]}"; do + pod_name=$(echo "$entry" | awk '{print $1}') + volume_id=$(echo "$entry" | awk '{print $2}') + snapshot_id=$(aws ec2 describe-snapshots \ + --restorable-by-user-ids self \ + --filters "Name=tag:Name,Values=Snapshot-$volume_id" "Name=tag:Pod,Values=$pod_name" \ + --query "Snapshots[0].SnapshotId" \ + --output text \ + --region "$source_region") + + new_snapshot_id=$(aws ec2 copy-snapshot \ + --source-region "$source_region" \ + --source-snapshot-id "$snapshot_id" \ + --description "Copy of Snapshot-$volume_id from Pod $pod_name" \ + --tag-specifications "ResourceType=snapshot,Tags=[{Key=Name,Value=Copied-$volume_id},{Key=Pod,Value=$pod_name},{Key=OriginalVolumeId,Value=$volume_id}]" \ + --query SnapshotId --output text \ + --region "$destination_region") + + echo "Waiting for $new_snapshot_id to complete..." + aws ec2 wait snapshot-completed --snapshot-ids "$new_snapshot_id" --region "$destination_region" + + new_volume_id=$(aws ec2 create-volume \ + --snapshot-id "$new_snapshot_id" \ + --availability-zone "$availability_zone" \ + --volume-type gp3 \ + --tag-specifications "ResourceType=volume,Tags=[{Key=Name,Value=Volume-$volume_id},{Key=OriginalVolumeId,Value=$volume_id},{Key=Pod,Value=$pod_name}]" \ + --query VolumeId --output text \ + --region "$destination_region") + + aws ec2 wait volume-available --volume-ids "$new_volume_id" --region "$destination_region" + echo "New Volume ID: $new_volume_id (Original: $volume_id)" +done +``` + +:::warning +Record the new volume IDs and their availability zones. Workloads must be deployed in the same AZ as their volume. +::: diff --git a/content/tutorials/en/migrate-external-s3.mdx b/content/tutorials/en/migrate-external-s3.mdx new file mode 100644 index 000000000..0e20a82e1 --- /dev/null +++ b/content/tutorials/en/migrate-external-s3.mdx @@ -0,0 +1,95 @@ +--- +title: Migrate an External S3 Bucket to SleakOps +sidebar_label: Migrate External S3 +sidebar_position: 17 +description: Copy objects from a source S3 bucket in an external AWS account to a SleakOps-managed S3 bucket using cross-account bucket policies and aws s3 sync. +tags: + - aws + - s3 + - migration + - storage +image: /img/tutorials/migrate-external-s3/migrate-external-s3.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Import objects from an S3 bucket in an external AWS account into a SleakOps-managed S3 bucket using a cross-account bucket policy and `aws s3 sync` run from a Pod inside your SleakOps cluster. + +## Prerequisites + +- A Cluster configured in SleakOps ([Cluster docs](/docs/cluster)) +- The destination S3 bucket already created as a Dependency in SleakOps +- Access to the source AWS account to add a bucket policy +- The destination account ID and the Service Account role name for your Project + +## Let's Start + +### Step 1 — Add a cross-account bucket policy on the source bucket + +In the **source account**, attach this policy to the source S3 bucket. Replace the placeholders: + +- `MAIN-SOURCE-BUCKET-NAME` — source bucket name +- `DESTINATION_ACCOUNT_ID` — AWS account ID where SleakOps is deployed +- `SERVICE_ACCOUNT_NAME` — the IAM role of your SleakOps Project, following the pattern `{PROJECT_NAME}-{ENVIRONMENT_NAME}-{SHORT_UUID}-sa` + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "DelegateS3Access", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::DESTINATION_ACCOUNT_ID:role/SERVICE_ACCOUNT_NAME" + }, + "Action": ["s3:ListBucket", "s3:GetObject", "s3:GetObjectTagging"], + "Resource": [ + "arn:aws:s3:::MAIN-SOURCE-BUCKET-NAME/*", + "arn:aws:s3:::MAIN-SOURCE-BUCKET-NAME" + ] + } + ] +} +``` + +### Step 2 — Open a Pod shell in the destination namespace + +Open the shell of any Pod deployed in the same **namespace** as your destination S3 Dependency. The Pod's Service Account already has permission to write to the SleakOps-managed bucket. + +![Opening a Pod shell from the SleakOps console](/img/tutorials/migrate-external-s3/pod-shell.png) + +### Step 3 — Install AWS CLI and run the sync + +From the Pod shell, install the AWS CLI if it is not already available, then run a dry-run first to verify the command: + +```bash +aws s3 sync --dryrun \ + s3://MAIN-SOURCE-BUCKET-NAME \ + s3://SLEAKOPS-DESTINATION-BUCKET-NAME +``` + +If the output looks correct, run the actual sync: + +```bash +aws s3 sync \ + s3://MAIN-SOURCE-BUCKET-NAME \ + s3://SLEAKOPS-DESTINATION-BUCKET-NAME +``` + +:::warning +If the buckets are in different regions, add the region arguments to avoid errors: + +```bash +aws s3 sync \ + s3://MAIN-SOURCE-BUCKET-NAME \ + s3://SLEAKOPS-DESTINATION-BUCKET-NAME \ + --source-region SOURCE-REGION-NAME \ + --region DESTINATION-REGION-NAME +``` +::: + +### Step 4 — Remove the cross-account policy + +Once the migration is complete, detach the bucket policy from the source bucket to close the cross-account access. diff --git a/content/tutorials/en/migrate-files-volumes-copy.mdx b/content/tutorials/en/migrate-files-volumes-copy.mdx new file mode 100644 index 000000000..ce20703e2 --- /dev/null +++ b/content/tutorials/en/migrate-files-volumes-copy.mdx @@ -0,0 +1,95 @@ +--- +title: Migrate Files Between Kubernetes Volumes Using kubectl cp +sidebar_label: Migrate Files Between Volumes +sidebar_position: 18 +description: Copy files stored in Kubernetes Persistent Volumes from one cluster to another using kubectl cp, without requiring database dumps or snapshot tools. +tags: + - kubernetes + - migration + - storage + - kubectl +image: /img/tutorials/migrate-files-volumes-copy/migrate-files-volumes-copy.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Transfer files stored in Kubernetes Persistent Volumes from a source cluster to a target cluster using `kubectl cp`, routing through the local machine as an intermediary. + +## Prerequisites + +- `kubectl` installed and configured with contexts for both clusters +- Access to both clusters (kubeconfigs set up, e.g., via `aws eks update-kubeconfig`) +- The source Pod name, namespace, and the path inside the Pod where files are stored +- The target Pod name and namespace + +## Let's Start + +### Step 1 — Configure kubectl access to both clusters + +Make sure you have kubeconfig entries for both clusters. For example: + +```bash +aws eks update-kubeconfig --region us-east-1 --name source-cluster-name --profile source-profile +aws eks update-kubeconfig --region us-east-1 --name target-cluster-name --profile target-profile +``` + +Verify the contexts are available: + +```bash +kubectl config get-contexts +``` + +### Step 2 — Run the migration script + +Save the following script as `transfer.sh`, customize the variables at the top, and run it: + +```bash +#!/bin/bash +set -euo pipefail + +SOURCE_CONTEXT="source-context" # kubectl context for the source cluster +TARGET_CONTEXT="target-context" # kubectl context for the target cluster +SOURCE_POD="source-pod-name" +SOURCE_NAMESPACE="source-namespace" +SOURCE_PATH="/path/in/source-pod" + +TARGET_POD="target-pod-name" +TARGET_NAMESPACE="target-namespace" +TARGET_PATH="/path/in/target-pod" + +TEMP_LOCAL_DIR="/tmp/k8s-transfer" + +# Validate both contexts exist +for ctx in "$SOURCE_CONTEXT" "$TARGET_CONTEXT"; do + if ! kubectl config get-contexts "$ctx" &>/dev/null; then + echo "Error: kubectl context '$ctx' not found" >&2 + exit 1 + fi +done + +# Copy from source pod to local machine +echo "Copying files from $SOURCE_POD..." +mkdir -p $TEMP_LOCAL_DIR +kubectl --context="$SOURCE_CONTEXT" cp $SOURCE_NAMESPACE/$SOURCE_POD:$SOURCE_PATH $TEMP_LOCAL_DIR + +# Copy from local machine to target pod +echo "Copying files to $TARGET_POD..." +kubectl --context="$TARGET_CONTEXT" cp $TEMP_LOCAL_DIR/. $TARGET_NAMESPACE/$TARGET_POD:$TARGET_PATH + +# Clean up +rm -rf $TEMP_LOCAL_DIR +echo "Transfer complete." +``` + +Make it executable and run: + +```bash +chmod +x transfer.sh +./transfer.sh +``` + +:::info +This approach works best for application assets — uploaded files, media, configuration — that are not part of a database. For database data, use the [PostgreSQL dump restore guide](/tutorial/postgresql-dump-restore) instead. +::: diff --git a/content/tutorials/en/migrate-postgres-heroku-to-rds.mdx b/content/tutorials/en/migrate-postgres-heroku-to-rds.mdx new file mode 100644 index 000000000..e1be78e98 --- /dev/null +++ b/content/tutorials/en/migrate-postgres-heroku-to-rds.mdx @@ -0,0 +1,88 @@ +--- +title: Migrate a Large PostgreSQL Database from Heroku to RDS +sidebar_label: Migrate Postgres from Heroku to RDS +sidebar_position: 14 +description: Migrate large PostgreSQL databases (20 GB+) from Heroku to Amazon RDS using pgcopydb and a SleakOps Job, with support for incremental follow-up migrations. +tags: + - aws + - rds + - postgresql + - heroku + - migration + - database +image: /img/tutorials/migrate-postgres-heroku-to-rds/migrate-postgres-heroku-to-rds.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Migrate a large PostgreSQL database from Heroku to Amazon RDS using [pgcopydb ](https://pgcopydb.readthedocs.io/en/latest/) running inside a SleakOps Job, with an optional incremental follow-up to sync new data after the initial load. + +:::info +For databases larger than 20 GB, Heroku recommends [forking the database ](https://devcenter.heroku.com/articles/heroku-postgres-fork) before migrating to avoid impacting production traffic. +::: + +## Prerequisites + +- A Cluster configured in SleakOps ([Cluster docs](/docs/cluster)) +- An Environment and Project already deployed +- The destination RDS Dependency created in SleakOps +- The destination RDS must have **at least twice the storage** of the source data (e.g., if the source is 400 GB, provision at least 800 GB) +- Heroku database credentials and connection string + +## Let's Start + +### Step 1 — Create a Job in SleakOps + +Create a Job using a Postgres image matching your source or target database version. The Job creates a Pod where `pgcopydb` will run. + +:::tip +Example configuration: +- **Image URL:** `ghcr.io/dimitri/pgcopydb` +- **Image tag:** `latest` +- **Command:** `/bin/sh -c 'sleep infinity'` +::: + +{/* TODO: screenshot - SleakOps Job creation form */} + +### Step 2 — Open the Pod terminal + +Connect to the terminal of the Pod created by the Job (for example, via Lens). + +### Step 3 — Run the initial migration + +Set the source and target connection strings, then run `pgcopydb clone`: + +```bash +export PG_SOURCE_URL="postgres://user:pass@host:5432/dbname" +export PG_TARGET_URL="postgres://${USERNAME}:${PASSWORD}@${ADDRESS}:5432/${NAME}?sslmode=require&keepalives=1&keepalives_idle=30" + +pgcopydb clone \ + --source "$PG_SOURCE_URL" \ + --target "$PG_TARGET_URL" \ + --jobs N \ + --not-consistent \ + --no-owner \ + --dir /tmp/pgcopydb \ + --no-acl +``` + +Replace `N` with the number of parallel workers appropriate for your Pod's CPU. + +### Step 4 — (Optional) Incremental follow-up migration + +Once the initial clone is complete, run a follow-up migration to import any new rows written to the source after the initial load: + +```bash +cat /tmp/pgcopydb/snapshot.json | jq '.lsn' + +pgcopydb follow \ + --source "$PG_SOURCE_URL" \ + --target "$PG_TARGET_URL" \ + --dir /tmp/pgcopydb \ + --endpos "OBTAINED_LSN" \ + --jobs N +``` + +Replace `OBTAINED_LSN` with the LSN value returned by the first command. diff --git a/content/tutorials/en/migrate-rds-snapshot-between-accounts.mdx b/content/tutorials/en/migrate-rds-snapshot-between-accounts.mdx new file mode 100644 index 000000000..8e256f823 --- /dev/null +++ b/content/tutorials/en/migrate-rds-snapshot-between-accounts.mdx @@ -0,0 +1,138 @@ +--- +title: Migrate an Amazon RDS Snapshot Between Accounts +sidebar_label: Migrate RDS Snapshot +sidebar_position: 12 +description: Step-by-step guide to share and restore an Amazon RDS snapshot across AWS accounts, including encrypted and cross-region scenarios. +tags: + - aws + - rds + - database + - migration + - snapshot + - kms +image: /img/tutorials/migrate-rds-snapshot-between-accounts/migrate-rds-snapshot-between-accounts.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Share and restore an Amazon RDS snapshot across AWS accounts, covering unencrypted, default-KMS-encrypted, and custom-KMS-encrypted databases, as well as cross-region scenarios. + +## Prerequisites + +- Access to the AWS Console in both source and destination accounts +- A Cluster configured in SleakOps ([Cluster docs](/docs/cluster)) +- An Environment configured ([Environment docs](/docs/environment)) +- The destination account ID (12-digit format) + +## Let's Start + +### Case 1 — Unencrypted RDS + +**In the source account:** + +1. Go to **RDS → Snapshots**. +2. Select the snapshot → **Actions → Share snapshot**. +3. Enter the **destination account ID** (format: `123456789012`). +4. Confirm. + +**In the destination account:** + +1. Go to **SleakOps → Dependencies → Create**. +2. Select **Create an RDS from a snapshot**. +3. Enter the identifier of the shared snapshot. + +--- + +### Case 2 — RDS Encrypted with the Default AWS KMS Key (`aws/rds`) + +:::warning +Default AWS-managed KMS keys (`aws/rds`) **cannot be shared directly** across accounts. You must first re-encrypt the snapshot with a customer-managed key (CMK). +::: + +**In the source account:** + +1. Create a custom KMS key with the policy described in [Case 3](#case-3--rds-encrypted-with-a-custom-kms-key-cmk). +2. Go to **RDS → Snapshots**. +3. Select the encrypted snapshot → **Actions → Copy snapshot**, choosing the newly created KMS key. +4. Wait for the copy to finish. +5. On the new snapshot → **Actions → Share snapshot**. +6. Enter the destination account ID. + +**In the destination account:** + +1. Go to **SleakOps → Dependencies → Create**. +2. Select **Create an RDS from a snapshot**. +3. Enter the identifier of the shared snapshot. + +:::info +Unencrypted snapshots can be shared directly between accounts. If your RDS uses encryption, see Case 3 below for CMK key sharing. +::: + +--- + +### Case 3 — RDS Encrypted with a Custom KMS Key (CMK) + +**In the source account — share the KMS key:** + +1. Go to **KMS → Customer managed keys**. +2. Select the key used to encrypt the RDS. +3. Under **Key policy**, add the following statement to the permissions section: + +```json +{ + "Sid": "Allow use of the key from destination account", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::<>:root" + }, + "Action": [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey", + "kms:CreateGrant" + ], + "Resource": "*" +} +``` + +**In the source account — share the snapshot:** + +1. Go to **RDS → Snapshots**. +2. Select the snapshot → **Actions → Share snapshot**. +3. Enter the destination account ID. + +**In the destination account — copy with its own KMS key:** + +1. Go to **RDS → Snapshots → Shared with me**. +2. Select the snapshot → **Actions → Copy snapshot**. +3. Select a KMS key that belongs to the destination account (required). +4. Wait for the copy to finish. + +**In the destination account — restore:** + +1. Go to **SleakOps → Dependencies → Create**. +2. Select **Create an RDS from a snapshot**. +3. Enter the identifier of the copied snapshot. + +--- + +### Special Case — Cross-Region Snapshots + +If the snapshot is in a different region from where you want to restore it: + +**In the source account:** + +1. Go to **RDS → Snapshots**. +2. Select the snapshot → **Actions → Copy snapshot**. +3. Set the **Destination Region** to the target region. +4. If encrypted with a CMK, select a valid KMS key in the destination region. +5. Wait for the cross-region copy to finish. +6. Follow the normal sharing process for the applicable case (1, 2, or 3). + +:::info +KMS keys are region-specific. When copying across regions, always use a KMS key from the destination region. Cross-region copies can take hours depending on the snapshot size. +::: diff --git a/content/tutorials/en/networking-vpc.mdx b/content/tutorials/en/networking-vpc.mdx new file mode 100644 index 000000000..d8398f7d8 --- /dev/null +++ b/content/tutorials/en/networking-vpc.mdx @@ -0,0 +1,73 @@ +--- +title: Networking and Network Resources in SleakOps +sidebar_label: Networking & VPC +sidebar_position: 29 +description: Understand how SleakOps structures network architecture — VPCs, subnets, Security Groups, External-DNS, and VPC Peering between environments. +tags: + - networking + - aws + - kubernetes + - vpn +image: /img/tutorials/networking-vpc/networking-vpc.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Understand the network architecture that SleakOps deploys in customer environments — how it is organized, how resources are protected, and how internal and external communication is enabled. + +:::info +The network is designed to guarantee security, scalability, and high availability. It separates environments, protects sensitive data, and exposes services in a controlled and secure way. +::: + +## 1. Architecture Overview + +SleakOps network infrastructure is built around these core components: + +- **VPC (Virtual Private Cloud):** Segments the network by environment (management, production, development). +- **Subnets:** Separate traffic by function — public (internet-facing), private (restricted access, internet via NAT Gateway), and persistence (databases, storage). +- **Internet Gateway:** Enables communication between the VPC and the internet. +- **Route Tables:** Define traffic routes between subnets and to/from the internet. +- **Security Groups:** Virtual firewalls that control inbound and outbound traffic for resources. +- **Internal DNS:** Allows resources to communicate using names instead of IPs. +- **External-DNS:** A service running inside each EKS cluster that automatically manages public DNS records in Route53 for services exposed from the cluster. + +## 2. Typical Communication Flow + +1. **Access from the internet:** A user accesses an exposed service (e.g., an API). Traffic reaches the Internet Gateway and is directed to the public subnet. +2. **Access control:** The Security Group associated with the resource validates whether the connection is allowed. +3. **Internal communication:** Internal services (in private or persistence subnets) communicate with each other using internal DNS, always governed by Security Group rules. +4. **Service exposure:** If a service inside the Kubernetes cluster must be accessible from the internet, it is exposed through an Application Load Balancer — External-DNS then automatically registers the name in Route53. + +This segmentation ensures that only necessary services are exposed and that sensitive data remains protected. + + + SleakOps network architecture — VPCs, subnets, Internet Gateway, and traffic flow + + +## 3. External-DNS and Route53 + +An automated solution manages public DNS records for deployed services, integrating the infrastructure with Route53. This makes services securely and simply accessible from the internet. External-DNS does not expose services directly — it automates the management of public DNS records for resources that are already exposed (for example, via an Application Load Balancer). + +## 4. Inter-Environment Connectivity via VPC Peering + +To allow controlled communication between environments (for example, between management and production), SleakOps explicitly configures **VPC Peering connections** between the VPCs of each environment. + +A VPC Peering allows two VPCs to exchange internal traffic as if they were on the same network, without passing through the internet, NAT Gateway, or a VPN. It is a direct connection between two networks. + + + VPC Peering connections between management, development, and production environments + + +:::tip +In addition to Internet Gateway access, SleakOps supports other connectivity mechanisms such as **Pritunl VPN**, **NAT Gateway**, and **Transit Gateway**, depending on the use case and the required level of isolation. +::: + +## DNS Reference by Environment + +| Environment | VPC DNS | +| --- | --- | +| DEV | `10.110.0.2` | +| MGT | `10.120.0.2` | +| PRD | `10.130.0.2` | diff --git a/content/tutorials/en/nodepool-strategies.mdx b/content/tutorials/en/nodepool-strategies.mdx new file mode 100644 index 000000000..f144aaba3 --- /dev/null +++ b/content/tutorials/en/nodepool-strategies.mdx @@ -0,0 +1,155 @@ +--- +title: Nodepool Strategies in SleakOps +sidebar_label: Nodepool Strategies +sidebar_position: 47 +description: Learn how to configure nodepools in SleakOps to optimize costs, improve performance, and choose the right instance types for each workload. +tags: + - kubernetes + - aws + - cost-optimization + - scaling + - performance +image: /img/tutorials/nodepool-strategies/nodepool-strategies.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Learn how to configure nodepools in SleakOps to optimize costs, improve performance, and choose the right instance types for each workload. + +## What Is a nodepool? + +In SleakOps, a **nodepool** is a group of EC2 nodes within your Kubernetes cluster that share the same configuration — architecture, instance type, billing model, and resource limits. Karpenter manages autoscaling within each pool automatically. + +When you create a Cluster, SleakOps provisions a set of default nodepools: + +| **nodepool** | **Purpose** | +|---|---| +| `sleakops-build-arm64` / `sleakops-build-amd64` | Dedicated to project builds. Cannot be edited or deleted. | +| `sleakops-core` | Runs SleakOps internal components and cluster addons. | +| `ondemand-arm` / `ondemand-amd` | Ready-to-use On-Demand pools for your workloads. | +| `spot-arm` / `spot-amd` | Ready-to-use Spot pools for your workloads. | + +Each Project Environment is assigned to one nodepool. The assignment can be changed at any time from the environment settings. + +--- + +## Cost Optimization Strategies + +### 1. Spot with On-Demand Fallback + +The most effective way to reduce compute costs is to run workloads on **Spot instances** while keeping **On-Demand as an automatic fallback**. In SleakOps, configure this by selecting both `Spot` and `On-Demand` as Node Types when creating or editing a nodepool. The system prioritizes them in this order: **Reserved → Spot → On-Demand**. + +:::tip +It is recommended to always combine Spot with On-Demand. This way, if Spot capacity is unavailable, your workloads continue running on On-Demand nodes with no downtime. +::: + +**Best for:** stateless web services, background workers that can be safely restarted, dev and staging environments. + +### 2. ARM Architecture (Graviton) + +AWS Graviton (ARM64) instances are **20–30% cheaper** than equivalent x86 instances and deliver comparable or better performance for most containerized workloads. + +To use ARM in SleakOps, three conditions must be met: + +1. **Your application must be ARM-compatible** — verify that all dependencies and native binaries have ARM builds available. +2. **Your Docker image must be built for ARM** — in SleakOps, each project runs on a single architecture. The image must target `linux/arm64` specifically. Configure the build in your project settings. +3. **Your project must be configured to use an ARM nodepool** — you can check and update the nodepool assigned to a project from the project detail view in SleakOps. + +**Best for:** services written in Go, Python, or Node.js without native x86-only dependencies. + +--- + +## Performance Strategies + +### On-Demand for Critical Workers + +Workers that run long-running or non-interruptible processes should always run on **On-Demand** nodes. Spot instances can be reclaimed with only 2 minutes of notice — a process that cannot resume safely from mid-execution must never run on Spot. + +Common examples: +- Background job workers with non-idempotent operations +- Scheduled task runners processing large data volumes +- ETL pipelines that run for hours and cannot be safely restarted mid-execution + +Create a dedicated On-Demand nodepool and assign these Project Environments to it to isolate critical workers from Spot-based pools. + +--- + +## Spot vs On-Demand + +| | **Spot** | **On-Demand** | +|---|---|---| +| **Cost** | Up to 90% cheaper | Fixed, higher price | +| **Availability** | Variable, can be interrupted | Guaranteed | +| **Interruption notice** | 2 minutes | None | +| **Best for** | Stateless services, batch jobs, dev/staging | Critical workers, long-running processes | + +**Recommended setup:** select `[Spot, On-Demand]` so your pool always has a fallback. See [What are the different kinds of nodepools?](/docs/cluster/nodepools) for the full priority order. + +--- + +## ARM vs AMD (x86) + +| | **ARM64 (Graviton)** | **AMD64 (x86)** | +|---|---|---| +| **Cost** | Lower (20–30%) | Higher | +| **Compatibility** | Requires ARM-compatible app + ARM image | Universal | +| **Best for** | Greenfield services, pure Go/Python/Node workloads | Legacy apps, x86-only native binaries | + +:::info +Changing a Project Environment from one architecture to another triggers an **automatic rebuild** in SleakOps. Once the build completes, the new version is deployed automatically. +::: + +--- + +## Choosing Instance Types + +When creating a nodepool in SleakOps (**Cluster → Settings → nodepools → Create**), you control which EC2 instances Karpenter can provision in two ways: + +### By Instance Category (Recommended) + +Leave the **Instance Type** field empty. Karpenter will automatically pick the most cost-effective available instance from the default categories (`t`, `c`, `m`, `r`) based on current demand and spot prices. + +| **Category** | **Family examples** | **Best for** | +|---|---|---| +| `t` | t3, t4g (burstable) | Variable workloads, dev/staging | +| `c` | c5, c6i (compute-optimized) | CPU-intensive processing | +| `m` | m5, m6i (general purpose) | Balanced workloads | +| `r` | r5, r6i (memory-optimized) | Memory-intensive workloads | + +### By Specific Instance Type + +Select one or more explicit instance types when your workload has strict hardware requirements. The most common case is **GPU workloads** — machine learning inference, image/video processing, or scientific computing. + +``` +Examples: g4dn.xlarge, g5.2xlarge, p3.2xlarge +``` + +:::info +GPU instances (`g` and `p` families) are not enabled by default in AWS accounts. Before adding a GPU instance type to a nodepool, request a service quota increase from the [AWS Service Quotas console ](https://console.aws.amazon.com/servicequotas/) for the relevant instance family in your target region. +::: + +--- + +## All Available Configuration Parameters + +The following fields are available when creating or editing a nodepool in SleakOps (**Cluster → Settings → nodepools**): + +| **Field** | **Description** | +|---|---| +| **Name** | Unique identifier within the cluster (lowercase, hyphens allowed) | +| **Architecture** | `ARM64` or `AMD64` — cannot be changed after creation | +| **Node Type** | `Spot`, `On-Demand`, `Reserved` — select multiple for priority-based fallback | +| **Instance Type** | Specific EC2 types, or leave empty for category-based flexible selection | +| **CPU Limit** | Maximum total CPU cores the pool can use (autoscaler ceiling) | +| **Memory Limit** | Maximum total memory the pool can use (autoscaler ceiling) | +| **Per-Node Min CPU** | Minimum CPU per individual node | +| **Per-Node Min Memory** | Minimum memory per individual node | +| **Storage** | Root volume size per node (default 20 GB) | + +### What if I need a parameter SleakOps doesn't expose? + +Karpenter's `NodePool` spec supports additional fields — consolidation policies, expiration, custom taints, topology spread, and more. If your use case requires a configuration not available in the SleakOps console, contact support or open a ticket describing your requirement. + +Full Karpenter NodePool spec reference: [Karpenter NodePool documentation ](https://karpenter.sh/docs/concepts/nodepools/). diff --git a/content/tutorials/en/optimize-aws-costs.mdx b/content/tutorials/en/optimize-aws-costs.mdx new file mode 100644 index 000000000..c2c7226c2 --- /dev/null +++ b/content/tutorials/en/optimize-aws-costs.mdx @@ -0,0 +1,83 @@ +--- +title: AWS Cost Optimization Strategies +sidebar_label: Optimize AWS Costs +sidebar_position: 36 +description: Practical AWS cost optimization techniques — Spot Instances, S3 Intelligent-Tiering, Graviton processors, Savings Plans, CloudFront, and Compute Optimizer. +tags: + - aws + - cost-optimization +image: /img/tutorials/optimize-aws-costs/optimize-aws-costs.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Practical techniques to reduce AWS costs without sacrificing reliability — covering compute, storage, CDN, and right-sizing strategies. + +## 1. Spot Instances with Auto Scaling — Cut EC2 Costs Significantly + +Running all workloads on On-Demand EC2 instances is expensive. Moving stateless or fault-tolerant workloads to **Spot Instances** backed by an **Auto Scaling Group (ASG)** with On-Demand fallback can cut those costs substantially. + +**When to use:** Batch jobs, background workers, CI/CD build agents, stateless web services. + +```yaml +# Example ASG mixed instances policy +MixedInstancesPolicy: + InstancesDistribution: + OnDemandPercentageAboveBaseCapacity: 20 + SpotAllocationStrategy: capacity-optimized +``` + +## 2. S3 Intelligent-Tiering — Stop Paying for Cold Data + +Paying S3 Standard rates for logs, reports, and backups that are rarely accessed is wasteful. **S3 Intelligent-Tiering** automatically moves objects to cheaper storage classes when they're not accessed frequently. + +**Enable from the SleakOps console:** navigate to the S3 Dependency settings for the bucket, open the storage configuration, and activate Intelligent-Tiering. You can also configure it directly in the AWS S3 console under **Bucket → Properties → Intelligent-Tiering**. + +Storage cost can drop **20–30%** for cold data buckets. + +## 3. AWS Graviton — Cheaper and Faster EC2 + +Graviton2/3 (ARM-based) instances are **20–30% cheaper** than equivalent x86 instances and often faster for many workloads. Migration only requires rebuilding Docker images for ARM64: + +```dockerfile +FROM --platform=linux/arm64 node:20-alpine +``` + +```bash +docker buildx build --platform linux/arm64 -t your-image:latest . +``` + +Compatible instance families: `m7g`, `c7g`, `r7g`. + +:::warning +Before migrating production workloads to ARM64, verify that your application and all dependencies compile and run correctly on ARM64 architecture. Some native libraries and binaries may not have ARM64 builds available. +::: + +## 4. AWS Savings Plans — Predictable Discounts + +For workloads that always run, committing to a **1-year Compute Savings Plan** reduces EC2 costs by **up to 40%** compared to On-Demand pricing. + +Purchase via **AWS Cost Explorer → Savings Plans → Purchase a Savings Plan**. + +## 5. CloudFront — Reduce S3 Egress Costs + +Serving static assets directly from S3 generates high egress costs. Putting **CloudFront** in front of S3 reduces both GET requests and data transfer costs while improving page load times via CDN edge caching. + +## 6. Compute Optimizer — Find Right-Sizing Opportunities + +**AWS Compute Optimizer** analyzes usage metrics and recommends right-sized instance types. It often finds workloads running on oversized instances that can be downsized with no performance impact. + +Enable it at: **AWS Console → Compute Optimizer → Get started**. + +## Summary + +| Technique | Typical savings | Best for | +| --- | --- | --- | +| Spot Instances + ASG | 50–70% on compute | Stateless, fault-tolerant workloads | +| S3 Intelligent-Tiering | 20–30% on storage | Logs, backups, infrequently accessed data | +| Graviton instances | 20–30% on compute | Any containerized workload | +| Savings Plans | Up to 40% on compute | Always-on servers | +| CloudFront | Reduces egress + GET costs | Static assets, media | +| Compute Optimizer | Variable | Over-provisioned instances | diff --git a/content/tutorials/en/optimize-docker-image.mdx b/content/tutorials/en/optimize-docker-image.mdx new file mode 100644 index 000000000..2d6d7db83 --- /dev/null +++ b/content/tutorials/en/optimize-docker-image.mdx @@ -0,0 +1,119 @@ +--- +title: How to Optimize Your Docker Image Size +sidebar_label: Optimize Docker Images +sidebar_position: 42 +description: Reduce Docker image size and improve build times using Alpine base images, multi-stage builds, and .dockerignore — practical techniques for production-ready images. +tags: + - docker + - deployment + - performance +image: /img/tutorials/optimize-docker-image/optimize-docker-image.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Reduce Docker image size and improve build times using Alpine base images, multi-stage builds, and `.dockerignore` — making your images faster to pull, deploy, and more secure. + +## Why Image Size Matters + +Smaller images mean faster pull times, faster deployments, reduced registry storage costs, and a smaller attack surface with fewer installed packages. + +## Technique 1 — Use Alpine or Slim Base Images + +Instead of a full OS base image, use a minimal alternative: + +```dockerfile +# Before — large image +FROM node:20 + +# After — much smaller +FROM node:20-alpine +``` + +Common minimal base images: +| Runtime | Minimal image | +| --- | --- | +| Node.js | `node:20-alpine` | +| Python | `python:3.12-slim` | +| Java | `eclipse-temurin:21-jre-alpine` | +| Go | `scratch` or `gcr.io/distroless/static` | + +:::warning +Alpine uses `musl libc` instead of `glibc`. Some packages behave differently. Test your application thoroughly when switching. +::: + +## Technique 2 — Multi-Stage Builds + +Use one stage to build and a separate minimal stage for the runtime: + +```dockerfile +# Build stage — has all build tools +FROM node:20-alpine AS build +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Runtime stage — only what's needed to run +FROM node:20-alpine +WORKDIR /app +COPY --from=build /app/dist ./dist +COPY --from=build /app/node_modules ./node_modules +CMD ["node", "dist/index.js"] +``` + +This keeps build tools, source code, and intermediate files out of the final image. + +## Technique 3 — Use `.dockerignore` + +Prevent unnecessary files from being sent to the build context: + +``` +# .dockerignore +node_modules +.git +.env +*.log +dist +coverage +.DS_Store +README.md +``` + +A smaller build context speeds up every `docker build`. + +## Technique 4 — Order Layers for Cache Efficiency + +Docker caches layers. Put frequently-changing layers last to maximize cache hits: + +```dockerfile +# Good — dependencies cached separately from source code +COPY package*.json ./ +RUN npm ci +COPY . . # source changes don't bust the npm ci cache + +# Bad — npm ci re-runs on every source change +COPY . . +RUN npm ci +``` + +## Technique 5 — Remove Unnecessary Files After Installation + +```dockerfile +RUN apt-get update && apt-get install -y --no-install-recommends \ + some-package \ + && rm -rf /var/lib/apt/lists/* +``` + +## Summary + +| Technique | Impact | +| --- | --- | +| Alpine/slim base images | 3–10x size reduction | +| Multi-stage builds | Removes build tools and source from final image | +| `.dockerignore` | Faster builds, no accidental secret leaks | +| Layer ordering | Faster incremental builds via cache reuse | +| Cleanup after installs | Removes package manager caches | diff --git a/content/tutorials/en/postgres-helm-existing-volume.mdx b/content/tutorials/en/postgres-helm-existing-volume.mdx new file mode 100644 index 000000000..1526a6c35 --- /dev/null +++ b/content/tutorials/en/postgres-helm-existing-volume.mdx @@ -0,0 +1,129 @@ +--- +title: Deploy a PostgreSQL Helm Chart Using an Existing EBS Volume +sidebar_label: Postgres Helm with Existing Volume +sidebar_position: 16 +description: Deploy the Bitnami PostgreSQL Helm chart on SleakOps pointing to a pre-existing EBS volume by creating PV and PVC resources with the correct node affinity. +tags: + - kubernetes + - helm + - postgresql + - storage + - ebs +image: /img/tutorials/postgres-helm-existing-volume/postgres-helm-existing-volume.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Deploy the Bitnami PostgreSQL Helm chart inside a SleakOps cluster pointing to a pre-existing EBS volume, by creating the required PersistentVolume and PersistentVolumeClaim resources. + +:::info +This tutorial assumes you have already followed [Migrate EBS Volumes to a New Account](/tutorial/migrate-ebs-volumes) and have a migrated volume ID available. +::: + +## Prerequisites + +- A Cluster configured in SleakOps with EBS CSI Driver installed +- An Environment and Project deployed +- The EBS Volume ID and its availability zone +- `kubectl` and `helm` installed locally +- The SleakOps cluster's NodePool name + +## Let's Start + +### Step 1 — (Optional) Identify the volume ID + +If you migrated the volume using the EBS migration guide, run this command from a Pod with the required IAM permissions to list migrated volumes: + +```bash +aws ec2 describe-volumes \ + --filters Name=status,Values=available \ + --query "Volumes[?Tags[?Key=='Name'] && Tags[?Key=='Pod'] && Tags[?Key=='OriginalVolumeId']].[{VolumeId: VolumeId, Name: Tags[?Key=='Name']|[0].Value, Pod: Tags[?Key=='Pod']|[0].Value, OriginalVolumeId: Tags[?Key=='OriginalVolumeId']|[0].Value, Size: Size}]" \ + --output table +``` + +Copy the `VolumeId` of the target volume. + +### Step 2 — Create the PV, PVC, and deploy with Helm + +Set the variables, then run the script to create the Kubernetes resources and deploy the chart: + +```bash +volume_id="vol-111aaa222ddd3333fff44" +pv_name="your-pv-name" +storage_size="8Gi" +availability_zone="us-east-1a" +nodepool_name="your-nodepool-name" + +pvc_name="${pv_name}-claim" + +# Create PV and PVC +kubectl apply -f - < values.yaml <](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_CreateSnapshot.html) to do so. +::: + +## Prerequisites + +- A Cluster configured in SleakOps ([Cluster docs](/docs/cluster)) +- An Environment and Project already deployed +- The source database credentials and endpoint +- The destination RDS Dependency created in SleakOps + +## Let's Start + +### Step 1 — Generate the dump from the source database + +Run this command from any machine that has network access to the source database. It prompts for the password: + +```bash +pg_dump -h POSTGRESQL_ADDRESS -U POSTGRESQL_USERNAME -W -Fc -f dump.dump +``` + +For detailed options, refer to the [SleakOps PostgreSQL dependency docs](/docs/project/dependency/postgresql-aws#how-do-i-create-a-postgresql-database-dump). + +### Step 2 — Scale up the destination RDS instance + +Temporarily increase the RDS instance class to a larger size. This significantly reduces the time to load the dump. Do this early so the resize completes before you start the restore. + +### Step 3 — Create a Job in SleakOps + +Create a Job using the same Postgres version as your destination RDS Dependency. This Job creates a Pod you will attach to for running the restore. + +:::tip +Example configuration: +- **Image URL:** `docker.io/library/postgres` +- **Image tag:** `17` (match the version of your RDS) +::: + + + SleakOps Job creation form — image URL and tag fields + + + + SleakOps Job creation form — configuration preview + + +### Step 4 — Open the Pod terminal and run the restore + +Connect to the terminal of the Pod created by the Job (for example, via Lens), then run: + +```bash +pg_restore -j 8 -O -v -e --clean --if-exists --no-owner --no-privileges \ + --exit-on-error \ + -h POSTGRESQL_ADDRESS \ + -U POSTGRESQL_USERNAME \ + -W \ + -d POSTGRESQL_DATABASE \ + dump.dump +``` + +The `-j 8` flag uses 8 parallel workers to speed up the restore. Adjust based on available CPU. + +### Step 5 — Scale the RDS instance back down + +Once the restore is complete, revert the RDS instance class to its original size. diff --git a/content/tutorials/en/pritunl-dns-universal.mdx b/content/tutorials/en/pritunl-dns-universal.mdx new file mode 100644 index 000000000..b1416e709 --- /dev/null +++ b/content/tutorials/en/pritunl-dns-universal.mdx @@ -0,0 +1,245 @@ +--- +title: Universal DNS Fix for Pritunl Client +sidebar_label: Pritunl DNS Fix +sidebar_position: 26 +description: Fix Pritunl VPN DNS resolution on Linux, macOS, and Windows with OS-specific scripts and a universal cross-platform script. +tags: + - vpn + - networking + - dns + - pritunl +image: /img/tutorials/pritunl-dns-universal/pritunl-dns-universal.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Fix Pritunl Client DNS resolution so that private VPN domains (EKS endpoints, internal services, microservices) always resolve correctly on Linux, macOS, and Windows. + +## The Problem + +When using Pritunl Client, some operating systems do not correctly apply the **VPN's internal DNS**, causing **private or infrastructure domains to fail to resolve**. + +Each OS handles per-interface DNS differently — `systemd-resolved`, `resolvconf`, `NetworkManager`, `scutil`, the Windows DNS API — and in certain cases they **ignore the DNS servers delivered by the VPN**. + +The result is inconsistent behavior: +- On some machines, internal domains work fine +- On others, resolution fails and requires manual configuration +- On networks with private or dynamic IPs, the problem is even more frequent + +The universal fix is to **explicitly apply the VPN's DNS to the VPN interface** using native OS tools (`resolvectl`, `scutil`, PowerShell). This guarantees correct resolution without permanent system changes. + +## Prerequisites + +- Pritunl Client installed and connected to a VPN profile +- `sudo` / administrator access on the machine + +## Linux Script + +Compatible with Ubuntu, Debian, Fedora, Arch, Manjaro, Kali, PopOS, and any distribution with `systemd-resolved`. + +### `fix_vpn_dns_linux.sh` + +```bash +#!/bin/bash + +# CONFIGURATION: edit these values +VPN_IFACE="tun0" # Typical Pritunl interface +VPN_DNS="10.110.0.2" # Internal DNS server +VPN_DOMAIN="internal.us-east-1.eks.amazonaws.com" # Internal EKS domain + +if ip link show "$VPN_IFACE" >/dev/null 2>&1; then + echo "[+] VPN detected on $VPN_IFACE" + if command -v resolvectl >/dev/null 2>&1; then + echo "[+] Applying DNS..." + sudo resolvectl dns "$VPN_IFACE" "$VPN_DNS" + sudo resolvectl domain "$VPN_IFACE" "~$VPN_DOMAIN" + else + echo "[!] resolvectl not found. Cannot apply DNS non-intrusively." + fi +else + echo "[-] VPN not detected. DNS unchanged." +fi +``` + +```bash +chmod +x fix_vpn_dns_linux.sh +./fix_vpn_dns_linux.sh +``` + +**Verify:** + +```bash +resolvectl status tun0 +``` + +## macOS Script + +### `fix_vpn_dns_macos.sh` + +```bash +#!/bin/bash + +VPN_IFACE="utun2" +VPN_DNS="10.110.0.2" +VPN_DOMAIN="internal.us-east-1.eks.amazonaws.com" + +if ifconfig "$VPN_IFACE" >/dev/null 2>&1; then + echo "[+] VPN detected. Configuring DNS..." + sudo scutil </dev/null 2>&1; then + echo "[-] VPN not detected on $VPN_IFACE_LINUX." + return + fi + echo "[+] VPN detected." + if command -v resolvectl >/dev/null 2>&1; then + sudo resolvectl dns "$VPN_IFACE_LINUX" "$VPN_DNS" + sudo resolvectl domain "$VPN_IFACE_LINUX" "~$VPN_DOMAIN" + echo "[✓] DNS applied." + else + echo "[!] resolvectl not found." + fi +} + +run_macos() { + echo "[OS] macOS" + if ! ifconfig "$VPN_IFACE_MACOS" >/dev/null 2>&1; then + echo "[-] VPN not detected on $VPN_IFACE_MACOS." + return + fi + echo "[+] VPN detected. Applying DNS..." + sudo scutil </dev/null 2>&1 || command -v powershell >/dev/null 2>&1; then + run_windows + else + echo "[ERROR] Unsupported OS." + fi + ;; +esac +``` + +```bash +chmod +x universal_fix_vpn_dns.sh +./universal_fix_vpn_dns.sh +``` + +### Configuration variables + +| Variable | Description | Example | +| --- | --- | --- | +| `VPN_IFACE_LINUX` | VPN interface on Linux | `tun0` | +| `VPN_IFACE_MACOS` | VPN interface on macOS | `utun2` | +| `VPN_ADAPTER_WINDOWS` | Adapter name on Windows | `Pritunl` | +| `VPN_DNS` | Internal DNS server IP | `10.110.0.2` | +| `VPN_DOMAIN` | Internal domain | `internal.us-east-1.eks.amazonaws.com` | + +## Troubleshooting + +| Problem | Cause | Fix | +| --- | --- | --- | +| Script doesn't detect VPN | Wrong interface name | Check with `ip link` (Linux), `ifconfig` (macOS), `Get-NetAdapter` (Windows) | +| DNS not applied | Missing permissions | Run with `sudo` | +| Works but resets on reconnect | Not automated | Add a Pritunl post-connect hook to auto-run the script | + +:::warning +These scripts do **not** make permanent changes to the system. DNS configuration is removed automatically when the VPN disconnects. +::: diff --git a/content/tutorials/en/rds-external-access.mdx b/content/tutorials/en/rds-external-access.mdx new file mode 100644 index 000000000..a2edb195b --- /dev/null +++ b/content/tutorials/en/rds-external-access.mdx @@ -0,0 +1,89 @@ +--- +title: Access an RDS Instance from Outside Your VPC +sidebar_label: RDS External Access +sidebar_position: 21 +description: Options to securely access an Amazon RDS instance running in a private subnet from outside the VPC — Bastion Host, AWS Client VPN, PrivateLink, and public access. +tags: + - aws + - rds + - database + - networking + - security + - vpn +image: /img/tutorials/rds-external-access/rds-external-access.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Connect to an Amazon RDS instance running in a private subnet from outside the VPC, using the secure approach that fits your access pattern. + +:::warning +Exposing a database directly to the internet is not recommended. It significantly increases the attack surface. Use the options below to avoid unnecessary risk. +::: + +## Options + +| Option | Security | Ease of setup | +| --- | --- | --- | +| **Bastion Host (EC2)** | High | Medium | +| **AWS Client VPN** | Very high | Easy | +| **AWS PrivateLink** | Highest | Complex | +| **Direct public access** | Not recommended | Easy | + +## Option 1 — Bastion Host (Recommended for occasional admin access) + +A Bastion Host is an EC2 instance in a public subnet that acts as a relay. + +1. Create an EC2 instance in a **public subnet** with a public IP. +2. Connect to the EC2 via SSH. +3. From the EC2, connect to the RDS endpoint using your database client. + +**Example — Oracle with SQL*Plus from the EC2:** + +```bash +sqlplus $DB_USER/$DB_PASSWORD@//rds-endpoint:1521/service_name +``` + +**SSH tunnel for local SQL Developer:** + +```bash +ssh -L 1521:rds-endpoint:1521 ec2-user@public-ip-ec2 -i "key.pem" +``` + +Then connect your local SQL Developer to `localhost:1521`. + +## Option 2 — AWS Client VPN (Recommended for regular access from your laptop) + +Configure an AWS Client VPN in the same VPC as the RDS. Once connected, the RDS private endpoint becomes reachable from your local machine. + +See the [AWS Client VPN documentation ](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is.html) for setup steps, or follow the [SleakOps VPN tutorial](/tutorial/third-party-integration-vpn). + +## Option 3 — AWS PrivateLink (For cross-VPC or cross-account secure access) + +AWS PrivateLink exposes the RDS to other VPCs or accounts without traversing the public internet. + +1. Create an AWS PrivateLink (VPC Endpoint Service) in the VPC where the RDS lives. +2. Configure a VPC Endpoint in the other VPC (which can be public-facing). +3. Connect to the RDS through the PrivateLink endpoint. + +See the [PrivateLink documentation ](https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/endpoint-services-overview.html). + +## Option 4 — Direct Public Access (Not recommended) + +If you must make the RDS publicly reachable, follow the [Make an RDS Public tutorial](/tutorial/make-rds-public). + +:::warning +This process involves approximately 20 minutes of downtime. The simplified steps are: +1. Create a new VPC and a Subnet Group inside it. +2. Move the RDS to the new VPC. +3. Create a public Subnet Group in the SleakOps-managed VPC. +4. Move the RDS back to the SleakOps VPC using the public Subnet Group. +5. Enable **Public Access** on the RDS. +6. Create and attach a Security Group that restricts access to trusted IPs only. +::: + +:::info +**RDS Proxy does not support public access.** RDS Proxy is designed for internal VPC connection pooling only — it has no public IP. Use one of the options above instead. +::: diff --git a/content/tutorials/en/test-site-to-site-vpn.mdx b/content/tutorials/en/test-site-to-site-vpn.mdx new file mode 100644 index 000000000..087a064bf --- /dev/null +++ b/content/tutorials/en/test-site-to-site-vpn.mdx @@ -0,0 +1,100 @@ +--- +title: Test a Site-to-Site VPN Created with SleakOps +sidebar_label: Test Site-to-Site VPN +sidebar_position: 32 +description: Verify connectivity through a SleakOps site-to-site VPN using Nmap from a Kubernetes pod — step-by-step with expected results and a packet flow diagram. +tags: + - vpn + - networking + - kubernetes +image: /img/tutorials/test-site-to-site-vpn/test-site-to-site-vpn.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Verify that a SleakOps site-to-site VPN is working correctly by running Nmap connectivity tests from a Kubernetes pod inside the cluster. + +## Prerequisites + +- A site-to-site VPN already created and configured in SleakOps +- Lens or `kubectl` access to the target cluster + +## Step 1 — Create a Test Pod + +Using Lens or `kubectl`, create the following pod in the relevant namespace: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: vpn-test + namespace: +spec: + containers: + - name: tools + image: nicolaka/netshoot:latest + command: ["sleep", "3600"] +``` + +Apply it: + +```bash +kubectl apply -f vpn-test-pod.yaml +``` + +## Step 2 — Access the Pod Shell + +Open a shell inside the pod via Lens (right-click → Shell) or: + +```bash +kubectl exec -it vpn-test -n -- bash +``` + +## Step 3 — Run Nmap Tests + +You need: +- **NAT_IP**: IP of the NAT instance (e.g., `10.100.0.10`) +- **REMOTE_IP**: IP of the remote server on the other side of the VPN (e.g., `192.168.0.100`) +- **PORT**: Port to test (e.g., `443`) + +```bash +NAT_IP="10.100.0.10" +REMOTE_IP="192.168.0.100" +PORT="443" +nmap -p $PORT $REMOTE_IP +``` + +**Expected result:** + +``` +PORT STATE SERVICE +443/tcp open https +``` + +If the state is `filtered` or `closed`, check the VPN tunnel status and the firewall on the remote side. + +## Step 4 — Clean Up + +Once connectivity is confirmed, delete the test pod: + +```bash +kubectl delete pod vpn-test -n +``` + +## Packet Flow Reference + +The following diagram shows how packets travel through the VPN: + +``` +Client request → Route to NAT via Peering → NAT Instance (IP forwarding) +→ MASQUERADE → VPN Gateway → IPSec tunnel → Customer Gateway +→ Decrypt → Firewall check → Remote server +``` + +Key failure points to check at each step: +1. **No route to remote subnet** — Check VPC route tables +2. **IP forwarding disabled** on NAT instance — Enable via `sysctl net.ipv4.ip_forward=1` +3. **VPN tunnel DOWN** — Check VPN connection status in AWS Console +4. **Remote firewall blocking** — Verify security group / firewall rules on the remote side diff --git a/content/tutorials/en/transfer-domain-route53.mdx b/content/tutorials/en/transfer-domain-route53.mdx new file mode 100644 index 000000000..89667aebb --- /dev/null +++ b/content/tutorials/en/transfer-domain-route53.mdx @@ -0,0 +1,78 @@ +--- +title: Transfer a Domain to Amazon Route 53 +sidebar_label: Transfer Domain to Route 53 +sidebar_position: 22 +description: Checklist and step-by-step guide to transfer a domain from your current registrar to Amazon Route 53 without downtime. +tags: + - aws + - route53 + - dns + - domain + - networking +image: /img/tutorials/transfer-domain-route53/transfer-domain-route53.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Transfer a domain from your current registrar to Amazon Route 53 following this preparation checklist, DNS migration, and transfer request steps to avoid downtime. + +:::info +Verify that your domain's TLD (e.g., `.com`, `.net`, `.mx`) is supported by Route 53 before starting: [Supported TLD list ](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/registrar-tld-list.html). +::: + +## Prerequisites + +- Access to your current domain registrar account +- Access to the registrant email address listed in WHOIS (authorization emails will be sent there) +- The domain must be at least 60 days old since registration or last transfer +- If DNSSEC is enabled, you must disable it at the current registrar before transferring + +## Let's Start + +### Stage 1 — Pre-transfer checklist + +Complete all of these before touching the AWS Console: + +- [ ] **Confirm TLD support** — verify your domain extension is in the [supported TLD list ](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/registrar-tld-list.html) +- [ ] **Verify access to registrant email** — you will receive authorization emails there +- [ ] **Unlock the domain** — disable the "Transfer Lock" or "Domain Lock" at your current registrar +- [ ] **Confirm domain age** — at least 60 days since registration or last transfer +- [ ] **Get the Auth Code (EPP Code)** — request it from your current registrar; required for most TLDs +- [ ] **Disable DNSSEC** — if enabled, disable it at the current registrar before transferring +- [ ] **Check domain status in WHOIS** — status must be `ok`. Domains with any of these statuses cannot be transferred: + - `clientTransferProhibited` + - `pendingDelete` + - `pendingTransfer` + - `redemptionPeriod` + - `serverTransferProhibited` + +:::warning +If your domain expires in less than 7 days, renew it at the current registrar before attempting the transfer. +::: + +### Stage 2 — Migrate DNS records first (recommended) + +AWS recommends migrating your DNS service before transferring the domain registration to avoid downtime. + +1. **Create a hosted zone in Route 53** with the same name as your domain. SleakOps can create this for you. +2. **Copy all DNS records** (A, MX, CNAME, TXT) from your current provider to the new Route 53 hosted zone. +3. (Optional) **Update name servers** at your current registrar to the Route 53 name servers to test resolution before the transfer. + +### Stage 3 — Submit the transfer request in Route 53 + +1. Go to **Route 53 → Registered domains → Transfer in**. +2. Enter the domain name and click **Check** to confirm it is transferable. +3. Enter the **Auth Code** from Stage 1. +4. Configure DNS: select your Route 53 hosted zone (recommended) or keep your current name servers. +5. Review and complete the **registrant contact information** — it should match your current registrar to avoid ownership-change blocks. +6. Review the cost (typically includes a 1-year renewal) and confirm the order. + +### Stage 4 — Complete the transfer + +- Watch your registrant email for an authorization link. +- Monitor progress at **Route 53 → Pending requests**. +- Depending on the TLD, transfers take anywhere from a few minutes to 10 days. +- Once complete, AWS sends a confirmation email. +- (If applicable) **Re-enable DNSSEC** after the transfer is complete and DNS is working correctly. diff --git a/content/tutorials/en/workers-use-cases.mdx b/content/tutorials/en/workers-use-cases.mdx new file mode 100644 index 000000000..06f1d81ab --- /dev/null +++ b/content/tutorials/en/workers-use-cases.mdx @@ -0,0 +1,98 @@ +--- +title: Worker Use Cases in SleakOps +sidebar_label: Worker Use Cases +sidebar_position: 27 +description: Explore common use cases for SleakOps Worker Workloads — background processing, scheduled tasks, autoscaling, ETL, pipelines, and third-party API integrations. +tags: + - kubernetes + - background-jobs + - scaling + - deployment +image: /img/tutorials/workers-use-cases/workers-use-cases.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Explore the most common use cases for SleakOps Worker Workloads — from asynchronous message processing to scheduled tasks, autoscaling, and pipeline orchestration. + +## What is a Worker Workload? + +In SleakOps, a **Worker** is a Workload type designed for background processing. Unlike a Web Service, a Worker doesn't expose an HTTP endpoint — it runs continuously, consuming messages from a queue or executing tasks triggered by events or schedules. + +## Use Cases + +### 1. Background Message and Task Processing + +A Worker can consume messages from a queue or task backend such as **RabbitMQ, AWS SQS, Redis, PostgreSQL (listen/notify), Kafka**, etc. + +Common examples: +- Asynchronous event processing (email sending, push notifications, database updates) +- Data ingestion into real-time analytics systems +- Microservice orchestration via event queues + +### 2. Scheduled and Event-Driven Tasks + +When an application needs to execute tasks based on a **cron schedule, system events, or user-defined triggers**, Workers handle execution without blocking other processes. + +Common examples: +- **Recurring tasks**: log cleanup, cache regeneration, periodic data updates +- **Event-driven tasks**: when a user action requires background processing (report generation, image/video processing) + +Two execution strategies: +1. Run the task directly inside the Worker (simpler, less recommended for Kubernetes clusters) +2. Send a message to a queue so a specialized Worker processes it + +### 3. Autoscaling Workers Based on Load + +Workers can be scaled dynamically based on the number of pending messages in the queue or resource consumption in the cluster. + +Common examples: +- Launch additional Worker instances during traffic spikes to process tasks in parallel +- Use [**KEDA** (Kubernetes Event-Driven Autoscaling)](/tutorial/install-keda) to scale Workers based on queue depth metrics + +### 4. File Processing and ETL (Extract, Transform, Load) + +Workers are well-suited for handling heavy files or processing large data volumes asynchronously. + +Common examples: +- Processing CSV/JSON files and loading them into a database +- Format conversion (images, videos, PDFs) +- Log ingestion and real-time data analysis + +### 5. Workflow Orchestration and Pipelines + +Some tasks require multiple chained steps that can be managed by Workers. + +Common examples: +- Machine Learning pipeline: data download → preprocessing → model training → evaluation +- Approval workflows in enterprise applications (purchase requests moving through multiple stages) + +### 6. Security and Compliance Workers + +Workers can run security and compliance checks in the background. + +Common examples: +- **Log monitoring** for anomalies or unauthorized access attempts +- **Vulnerability scanning** on containers or infrastructure +- **Permission management and audit** in authentication systems + +### 7. Third-Party API Integrations + +Workers handle third-party API integrations without blocking the main application. + +Common examples: +- Data synchronization with CRM, ERP, or other external services +- Sending data to third-party services (Stripe, Twilio, Google Sheets) +- Monitoring external API changes and updating the application + +## Choosing the Right Worker Pattern + +| Pattern | When to use | +| --- | --- | +| **Single Worker consuming a queue** | Simple async tasks with one consumer | +| **Multiple Workers on the same queue** | High-throughput processing requiring parallelism | +| **Worker + [KEDA](/tutorial/install-keda) autoscaling** | Variable load; scale to zero when idle | +| **Chained Workers (pipeline)** | Complex multi-step workflows | +| **Cron-triggered Worker** | Recurring scheduled tasks | diff --git a/content/tutorials/es/amazon-ses.mdx b/content/tutorials/es/amazon-ses.mdx new file mode 100644 index 000000000..ac51390d9 --- /dev/null +++ b/content/tutorials/es/amazon-ses.mdx @@ -0,0 +1,101 @@ +--- +title: Comenzar con Amazon SES +sidebar_label: Amazon SES +sidebar_position: 20 +description: Configurá Amazon SES para enviar emails desde tu aplicación — verificá una identidad, probá en modo sandbox y solicitá acceso a producción. +tags: + - aws + - ses + - email +image: /img/tutorials/amazon-ses/amazon-ses.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Configurá Amazon Simple Email Service (SES) para enviar emails desde tu aplicación, desde el modo sandbox hasta la configuración en producción. + +## Prerrequisitos + +- Acceso a la consola de AWS con permisos para administrar SES +- Un dominio o dirección de email desde donde querés enviar +- Si usás dominio: acceso a tu proveedor de DNS para agregar registros de verificación + +## Empecemos + +### Paso 1 — Crear una identidad verificada + +Todas las cuentas nuevas de SES comienzan en **modo sandbox**, que solo permite enviar hacia y desde identidades verificadas. + +1. Ir a **AWS Console → Amazon SES → Identities**. +2. Hacer clic en **Create identity**. +3. Elegir una de las opciones: + - **Email address** — verifica una sola dirección. AWS envía un email de confirmación; hacé clic en el enlace para verificar. + - **Domain** — verifica el dominio completo (recomendado para producción). AWS provee registros DNS (TXT/CNAME) para agregar en tu proveedor de DNS. + +:::tip +Verificá tu dominio de producción temprano — podés reutilizar el mismo dominio verificado cuando salgas del sandbox. +::: + +### Paso 2 — Enviar un email de prueba en sandbox + +Con una identidad verificada ya podés enviar emails en modo sandbox. Todos los destinatarios también deben ser identidades verificadas mientras estés en sandbox. + +Usá el botón **Send test email** en la consola de SES o el AWS CLI para confirmar que tu configuración funciona antes de solicitar acceso a producción. + +### Paso 3 — Solicitar acceso a producción + +Para enviar a cualquier destinatario, necesitás salir del modo sandbox. + +**Antes de enviar la solicitud:** + +- Tener al menos una identidad verificada (preferentemente un dominio) +- Poder enviar un email de prueba desde SES +- Tener claro el caso de uso: tipo de email, volumen esperado y cómo manejás rebotes/quejas/bajas + +**Pasos:** + +1. En Amazon SES, navegar a **Account dashboard → Request production access** (o **Sending limits**). +2. Completar el formulario: + - **Email type:** Transactional o Marketing + - **Website URL:** el sitio asociado a los envíos + - **Use case description:** qué enviás, a quién y con qué frecuencia + - **Bounce/complaint/unsubscribe handling:** describí tu estrategia de supresión + +AWS abre un ticket en Support Center y puede solicitar información adicional. + +### Paso 4 — Escribir una descripción de caso de uso clara + +Una descripción clara y detallada es el factor más importante para la aprobación. Usá este modelo como punto de partida: + +``` +Service: Amazon SES (Sending Limits / Production Access) +Region: + +Use case description: +We plan to use Amazon SES to send transactional emails to our users +(e.g., account notifications and password reset emails). Emails are +user-triggered and sent only to customers who have registered on our platform. + +List management: +We only send to recipients who are registered in our system. Users can +unsubscribe from non-essential communications through our website. + +Bounce/complaint handling: +We monitor bounces and complaints and suppress recipients when appropriate. + +Expected volume: +Our expected sending volume is emails per day initially, with gradual growth. + +Please let us know if any more information is needed. +``` + +:::tip +**Tips para una aprobación más rápida:** + +- Indicá claramente si los emails son **transaccionales** (reseteo de contraseña, confirmaciones de pedido) o **marketing** +- Incluí un volumen estimado de envíos por día/mes +- Describí cómo tu sistema maneja rebotes, quejas y bajas +- Si es posible, incluí un ejemplo del contenido del email +::: diff --git a/content/tutorials/es/aws-codeartifact-java.mdx b/content/tutorials/es/aws-codeartifact-java.mdx new file mode 100644 index 000000000..dcec09762 --- /dev/null +++ b/content/tutorials/es/aws-codeartifact-java.mdx @@ -0,0 +1,211 @@ +--- +title: Usar AWS CodeArtifact con proyectos Java/Maven +sidebar_label: AWS CodeArtifact para Java +sidebar_position: 28 +description: Configurá un proyecto Java Maven para usar AWS CodeArtifact como repositorio privado de artefactos dentro de Docker, con ejemplos de pipelines CI/CD para GitHub Actions, GitLab y Bitbucket. +tags: + - aws + - ci-cd + - docker + - deployment +image: /img/tutorials/aws-codeartifact-java/aws-codeartifact-java.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Configurá un proyecto Java Maven para resolver dependencias desde [AWS CodeArtifact ](https://aws.amazon.com/codeartifact/) — un repositorio privado de artefactos — dentro de un build Dockerizado, con ejemplos de pipelines CI/CD. + +## Prerrequisitos + +- Un dominio y repositorio en AWS CodeArtifact ya creados +- Credenciales IAM con permisos `codeartifact:GetAuthorizationToken` y `codeartifact:ReadFromRepository` +- Docker y Maven instalados + +## Paso 1 — Crear `codeartifact_settings.xml` + +Este archivo configura Maven para autenticarse con CodeArtifact usando un token inyectado como variable de entorno: + +```xml + + + + codeartifact + aws + ${env.CODEARTIFACT_AUTH_TOKEN} + + + + + codeartifact + ${env.CODEARTIFACT_REPOSITORY} + * + + + +``` + +## Paso 2 — Dockerfile con autenticación a CodeArtifact + +Usá un build multi-stage para que el token de autenticación nunca quede en la imagen final: + +```dockerfile +# Etapa de build +FROM maven:3.9-eclipse-temurin-21 AS build + +# Instalar AWS CLI +RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \ + && unzip awscliv2.zip \ + && ./aws/install + +# Credenciales AWS como build args (no se almacenan en la imagen final) +ARG CODEARTIFACT_ACCESS_KEY_ID +ARG CODEARTIFACT_SECRET_ACCESS_KEY +ARG CODEARTIFACT_REGION +ARG CODEARTIFACT_ACCOUNT_ID +ARG CODEARTIFACT_REPOSITORY +ARG CODEARTIFACT_DOMAIN + +# Establecer directorio de trabajo y copiar código fuente +WORKDIR /app +COPY . . + +# Copiar settings de Maven apuntando a CodeArtifact +COPY codeartifact_settings.xml /root/.m2/settings.xml + +# Generar token y construir +RUN export AWS_ACCESS_KEY_ID=$CODEARTIFACT_ACCESS_KEY_ID \ + && export AWS_SECRET_ACCESS_KEY=$CODEARTIFACT_SECRET_ACCESS_KEY \ + && export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \ + --domain $CODEARTIFACT_DOMAIN \ + --domain-owner $CODEARTIFACT_ACCOUNT_ID \ + --region $CODEARTIFACT_REGION \ + --query authorizationToken --output text) \ + && mvn clean install -s /root/.m2/settings.xml + +# Etapa de runtime — sin credenciales +FROM eclipse-temurin:21-jre +COPY --from=build /app/target/app.jar /app.jar +ENTRYPOINT ["java", "-jar", "/app.jar"] +``` + +## Paso 3 — Docker Compose (build args) + +```yaml +build: + args: + CODEARTIFACT_ACCESS_KEY_ID: $CODEARTIFACT_ACCESS_KEY_ID + CODEARTIFACT_SECRET_ACCESS_KEY: $CODEARTIFACT_SECRET_ACCESS_KEY + CODEARTIFACT_REGION: $CODEARTIFACT_REGION + CODEARTIFACT_ACCOUNT_ID: $CODEARTIFACT_ACCOUNT_ID + CODEARTIFACT_REPOSITORY: $CODEARTIFACT_REPOSITORY + CODEARTIFACT_DOMAIN: $CODEARTIFACT_DOMAIN +``` + +## Paso 4 — Configuración del Pipeline CI/CD + + + + +```yaml +# .github/workflows/maven.yml +name: Build and Deploy to CodeArtifact + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Configurar JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Configurar credenciales AWS + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Obtener token CodeArtifact + run: | + echo "CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \ + --domain ${{ secrets.CODEARTIFACT_DOMAIN }} \ + --domain-owner ${{ secrets.CODEARTIFACT_ACCOUNT }} \ + --query authorizationToken --output text)" >> $GITHUB_ENV + + - name: Build y Deploy + run: mvn -s codeartifact_settings.xml clean package deploy +``` + + + + +```yaml +# gitlab-ci.yml +variables: + MAVEN_DOCKER_IMAGE: "maven:3.9.6-eclipse-temurin-21" + +stages: + - build + +build_artifacts: + stage: build + image: $MAVEN_DOCKER_IMAGE + before_script: + - pip3 install awscli + script: + - export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token + --domain ${CODEARTIFACT_DOMAIN} + --domain-owner ${CODEARTIFACT_ACCOUNT} + --query authorizationToken --output text) + - mvn -s settings.xml clean package deploy + only: + - develop +``` + + + + +```yaml +# bitbucket-pipelines.yml +- step: + name: Build y Upload a AWS CodeArtifact + image: maven:3.8.6-openjdk-8 + caches: + - maven + script: + - export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token + --domain ${CODEARTIFACT_DOMAIN} + --domain-owner ${CODEARTIFACT_ACCOUNT_ID} + --region ${CODEARTIFACT_REGION} + --query authorizationToken --output text) + - mvn -B clean install deploy -DskipTests +``` + + + + +## Buenas Prácticas + +- **Evitá credenciales en imágenes finales**: Usá builds multi-stage para que los tokens solo estén presentes durante la etapa de construcción. +- **Usá IAM Roles en AWS**: En EC2, ECS o EKS, reemplazá las access keys con roles IAM para gestión automática de credenciales. +- **Cache local de Maven**: Montá `.m2` como volumen (`-./.m2:/root/.m2`) para acelerar builds posteriores. + +## Solución de Problemas + +| Error | Causa | Solución | +| --- | --- | --- | +| **Token expirado** | Los tokens de CodeArtifact tienen validez de 12 horas | Asegurate de que el build no supere este tiempo | +| **Permisos insuficientes** | Política IAM incorrecta | Verificar que `codeartifact:GetAuthorizationToken` y `codeartifact:ReadFromRepository` están habilitados | +| **URL incorrecta** | Formato del repositorio incorrecto | Usar `https://-.d.codeartifact..amazonaws.com/maven//` | diff --git a/content/tutorials/es/aws-local-authentication.mdx b/content/tutorials/es/aws-local-authentication.mdx new file mode 100644 index 000000000..def9eec8d --- /dev/null +++ b/content/tutorials/es/aws-local-authentication.mdx @@ -0,0 +1,103 @@ +--- +title: Configurar autenticación de AWS para desarrollo local +sidebar_label: Autenticación AWS Local +sidebar_position: 30 +description: Configurá las credenciales de AWS CLI y la asunción de roles cross-account en tu máquina local para acceder a recursos AWS de SleakOps en las cuentas de desarrollo, management y producción. +tags: + - aws + - security +image: /img/tutorials/aws-local-authentication/aws-local-authentication.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Configurá las credenciales de AWS CLI en tu máquina local para acceder a recursos AWS de SleakOps en las cuentas de desarrollo, management y producción usando asunción de roles cross-account. + +## Cómo SleakOps Gestiona Usuarios AWS + +SleakOps crea usuarios en la **cuenta Security**. Cada usuario puede asumir roles en las demás cuentas (development, management, production). Esto significa que no usás credenciales directas para cada cuenta — asumís un rol desde tu usuario de la cuenta Security. + +**Flujo:** Tu usuario (cuenta Security) → asume un rol (cuenta destino) → obtiene credenciales temporales para operar. + +Dependiendo del rol que tengas en SleakOps, podés asumir uno de: +- `SleakopsViewerRole` +- `SleakopsEditorRole` +- `SleakopsAdminRole` + +## Prerrequisitos + +- Una **Access Key** y **Secret Key** de un usuario en la cuenta Security +- El **ARN del rol** en la cuenta destino (ej. `arn:aws:iam::123456789012:role/SleakopsEditorRole`) — obtenelo desde la acción **AWS Switch Role** en SleakOps. Ver [Autenticación en consola AWS](/docs/user/aws_console_authentication) +- [AWS CLI v2 ](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) instalado + +## Paso 1 — Configurar las credenciales del usuario base + +```bash +aws configure +``` + +Ingresá cuando se solicite: +- **AWS Access Key ID** — tu access key de la cuenta Security +- **AWS Secret Access Key** — tu secret key de la cuenta Security +- **Default region** — ej. `us-east-1` +- **Output format** — `json` + +Esto escribe en: +- Linux/macOS: `~/.aws/credentials` y `~/.aws/config` +- Windows: `C:\Users\\.aws\credentials` + +:::warning +`~/.aws/credentials` almacena tus access keys en texto plano. Restringí sus permisos (`chmod 600 ~/.aws/credentials`) y nunca lo incluyas en control de versiones. +::: + +## Paso 2 — Agregar perfiles de rol para cada cuenta destino + +Editá `~/.aws/config` (Linux/macOS) o `C:\Users\\.aws\config` (Windows) y agregá: + +```ini +[profile security-account] +aws_access_key_id = TU_ACCESS_KEY +aws_secret_access_key = TU_SECRET_KEY +region = us-east-1 + +[profile dev-account] +role_arn = arn:aws:iam::111111111111:role/SleakopsEditorRole +source_profile = security-account +region = us-east-1 + +[profile prd-account] +role_arn = arn:aws:iam::333333333333:role/SleakopsEditorRole +source_profile = security-account +region = us-east-1 +``` + +- `security-account` — tu usuario base con access keys +- `dev-account` / `prd-account` — perfiles que usan las credenciales base para asumir el rol en la cuenta destino + +## Paso 3 — Probar la asunción del rol + +```bash +aws sts get-caller-identity --profile dev-account +``` + +Deberías ver un `UserId` y un `Arn` que corresponden al **rol de la cuenta destino**. + +## Paso 4 — Usar el perfil + +Ejecutá cualquier comando AWS con el perfil correspondiente: + +```bash +aws s3 ls --profile dev-account +aws eks list-clusters --profile prd-account +``` + +## Resumen + +| Paso | Acción | +| --- | --- | +| 1 | Instalar AWS CLI v2 | +| 2 | Configurar credenciales base de la cuenta Security con `aws configure` | +| 3 | Editar `~/.aws/config` para agregar perfiles de rol por entorno | +| 4 | Usar `--profile ` en tus comandos | diff --git a/content/tutorials/es/bitnami-image-deprecated.mdx b/content/tutorials/es/bitnami-image-deprecated.mdx new file mode 100644 index 000000000..eab25acda --- /dev/null +++ b/content/tutorials/es/bitnami-image-deprecated.mdx @@ -0,0 +1,69 @@ +--- +title: Deprecación de Imágenes Bitnami — Qué Hacer +sidebar_label: Deprecación de Bitnami +sidebar_position: 37 +description: Entendé la deprecación de imágenes Bitnami efectiva el 28 de agosto de 2025, y cómo actualizar las dependencias de tus charts en SleakOps para evitar errores ImagePullBackOff. +tags: + - kubernetes + - helm + - deployment +image: /img/tutorials/bitnami-image-deprecated/bitnami-image-deprecated.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +A partir del 28 de agosto de 2025, Bitnami cambió la forma en que distribuye sus imágenes Docker y Helm charts. Este artículo explica qué cambió, qué se rompe si no actuás, y qué hacer en SleakOps. + +## Qué Cambió + +El **28 de agosto de 2025**, Bitnami realizó los siguientes cambios en su catálogo de imágenes: + +- **Las imágenes existentes** (todos los tags/versiones) fueron migradas de `docker.io/bitnami` a un repositorio **"Bitnami Legacy"** (`docker.io/bitnamilegacy`), que ya no recibe actualizaciones ni parches de seguridad. +- El repositorio principal `docker.io/bitnami` ahora solo mantiene un **conjunto limitado de imágenes "hardened"** con únicamente tag `latest`, bajo el nuevo programa **Bitnami Secure Images (BSI)** — orientadas a uso de desarrollo. +- Los **Helm charts** continúan disponibles como artefactos OCI pero sin actualizaciones de imágenes, a menos que se sobrescriban las referencias. + +## Qué se Rompe sin Acción + +Si tus Helm charts referencian imágenes Bitnami versionadas (ej. `postgresql:13.7.0`), el cluster no podrá descargar esas imágenes después del 28 de agosto con errores como: + +``` +ImagePullBackOff +ErrImagePull +``` + +## Fix Inmediato (Detener el Impacto) + +El fix más rápido es actualizar las dependencias de tus charts para usar `bitnamilegacy` y configurar el flag de seguridad que permite imágenes legacy: + +1. En SleakOps, navegá a la pantalla de **Chart Configuration** del workload afectado. +2. Actualizá `image.repository` para usar `docker.io/bitnamilegacy/` en lugar de `docker.io/bitnami/`. +3. Agregá `global.security.allowInsecureImages: true` a los values del chart. +4. Hacé clic en **Apply changes** para desplegar. + +SleakOps ya actualizó los values de tu chart — `image.repository` apunta a `docker.io/bitnamilegacy/` y se agregó `global.security.allowInsecureImages: true`. Solo necesitás disparar el apply desde la pantalla de configuración del chart. + + + Pantalla de Chart Configuration en SleakOps + + + + Confirmación de Apply changes en SleakOps + + +:::warning +Usar imágenes de `bitnamilegacy` es un **parche temporal**. Estas imágenes no recibirán parches de seguridad. Planificá una migración a imágenes alternativas (imágenes upstream oficiales o imágenes BSI-compliant) como acción de seguimiento. +::: + +## Plan de Migración a Largo Plazo + +1. Identificar todas las imágenes Bitnami en uso en tus despliegues. +2. Para cada imagen, encontrar la imagen upstream equivalente o BSI. +3. Probar la imagen de reemplazo en un entorno de staging. +4. Actualizar los values del chart para referenciar la nueva imagen y eliminar el flag `allowInsecureImages`. + +## Referencias + +- [Anuncio de deprecación de containers Bitnami ](https://github.com/bitnami/containers/issues/83267) +- [Programa Bitnami Secure Images ](https://bitnami.com/secure-images) diff --git a/content/tutorials/es/connect-aws-resources.mdx b/content/tutorials/es/connect-aws-resources.mdx new file mode 100644 index 000000000..12fbd5c25 --- /dev/null +++ b/content/tutorials/es/connect-aws-resources.mdx @@ -0,0 +1,144 @@ +--- +title: Conectarse a recursos de AWS desde tu aplicación +sidebar_label: Conectarse a AWS +sidebar_position: 31 +description: Aprendé cómo autenticarte en AWS y conectarte a recursos como S3 desde desarrollo local o pipelines CI/CD — creación de usuario IAM, gestión de access keys y ejemplos con el SDK de Node.js. +tags: + - aws + - security + - node +image: /img/tutorials/connect-aws-resources/connect-aws-resources.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Aprendé las formas recomendadas de autenticarte en AWS y conectarte a recursos como S3 desde desarrollo local, pipelines CI/CD o integraciones estáticas — incluyendo creación de usuario IAM y ejemplos con el SDK de Node.js. + +## Cuándo Usar Cada Enfoque + +| Escenario | Enfoque recomendado | +| --- | --- | +| Acceso por usuario con trazabilidad | [Configurar credenciales AWS personales](/tutorial/aws-local-authentication) | +| Pipeline CI/CD o integración estática | Usuario IAM dedicado con permisos acotados (esta guía) | +| Workloads en producción en EKS/EC2 | IAM Role con STS (sin keys permanentes) | + +Esta guía cubre el enfoque de **integración estática** — crear un usuario IAM dedicado para un recurso específico como S3. + +## Paso 1 — Crear un Usuario IAM Dedicado + +1. Iniciá sesión en la [consola de AWS ](https://console.aws.amazon.com/) y navegá a **IAM**. +2. Hacé clic en **Users → Create user**. +3. Ingresá un nombre descriptivo (ej. `s3-app-user`). +4. Seleccioná **Programmatic access**. +5. Hacé clic en **Next: Permissions**. + +## Paso 2 — Adjuntar una Política para S3 + +**Opción A — Política predefinida (simple):** +- Seleccioná `AmazonS3FullAccess` o `AmazonS3ReadOnlyAccess`. + +**Opción B — Política personalizada (recomendado para producción):** + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:GetObjectVersion"], + "Resource": "arn:aws:s3:::tu-bucket-name/*" + }, + { + "Effect": "Allow", + "Action": ["s3:ListBucket", "s3:GetBucketLocation"], + "Resource": "arn:aws:s3:::tu-bucket-name" + } + ] +} +``` + +Reemplazá `tu-bucket-name` con el nombre real del bucket. + +## Paso 3 — Descargar las Access Keys + +Después de crear el usuario, descargá el archivo `.csv` o copiá el **Access Key ID** y el **Secret Access Key**. Esta es la única oportunidad de ver el Secret Access Key. + +:::warning +Nunca committees credenciales en un repositorio. Guardalas en un gestor de secretos o en variables de entorno. +::: + +## Paso 4 — Configurar Credenciales en tu Entorno + +**Archivo `.env` (recomendado para desarrollo local):** + +```bash +AWS_ACCESS_KEY_ID=tu_access_key_id +AWS_SECRET_ACCESS_KEY=tu_secret_access_key +AWS_REGION=us-east-1 +S3_BUCKET_NAME=tu-bucket-name +``` + +Agregá `.env` al `.gitignore`. + +**AWS CLI (alternativo):** + +```bash +aws configure +``` + +## Paso 5 — Usar el SDK en Node.js + +**Instalar dependencias:** + +```bash +npm install @aws-sdk/client-s3 dotenv +``` + +**Ejemplo con AWS SDK v3:** + +```javascript +require('dotenv').config(); +const { S3Client, PutObjectCommand, GetObjectCommand, ListObjectsV2Command } = require('@aws-sdk/client-s3'); + +const s3 = new S3Client({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, +}); + +const bucket = process.env.S3_BUCKET_NAME; + +async function subirArchivo(key, body) { + await s3.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: body })); +} + +async function descargarArchivo(key) { + const res = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key })); + return res.Body.transformToString(); +} + +async function listarArchivos() { + const res = await s3.send(new ListObjectsV2Command({ Bucket: bucket })); + return res.Contents ?? []; +} +``` + +## Buenas Prácticas de Seguridad + +- **Nunca hardcodees credenciales** en el código fuente. +- **Rotá las access keys** cada 90 días. +- **Usá los permisos mínimos necesarios** — evitá `Resource: "*"` genérico. +- **Monitoreá el uso** mediante AWS CloudTrail. +- **Preferí IAM Roles** sobre access keys permanentes para workloads que corren en EKS, EC2 o ECS. + +## Solución de Problemas + +| Error | Causa | Solución | +| --- | --- | --- | +| **Access Denied** | Permisos IAM faltantes o credenciales incorrectas | Verificar la política IAM y que las claves correctas estén configuradas | +| **Region not specified** | `AWS_REGION` no definida | Configurar la región en tu config o `.env` | +| **Bucket does not exist** | Nombre de bucket incorrecto o región equivocada | Confirmar el nombre del bucket y la región | diff --git a/content/tutorials/es/deploy-datadog-operator.mdx b/content/tutorials/es/deploy-datadog-operator.mdx new file mode 100644 index 000000000..dae255277 --- /dev/null +++ b/content/tutorials/es/deploy-datadog-operator.mdx @@ -0,0 +1,154 @@ +--- +title: Desplegar Datadog Operator y DatadogAgent en SleakOps +sidebar_label: Desplegar Datadog Operator +sidebar_position: 33 +description: Instalá el Datadog Operator (v2.8.0) y desplegá un DatadogAgent (v7.63.3) en un cluster EKS de SleakOps con toleraciones de NodePool Karpenter, APM, recolección de logs y admission controller. +tags: + - kubernetes + - monitoring + - deployment + - helm +image: /img/tutorials/deploy-datadog-operator/deploy-datadog-operator.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Instalá el [Datadog Operator ](https://docs.datadoghq.com/containers/datadog_operator/) y desplegá un custom resource `DatadogAgent` en un cluster EKS de SleakOps para habilitar APM, recolección de logs y monitoreo de eventos de Kubernetes. + +:::info +Esta guía fue probada con **DatadogOperator v2.8.0** y **DatadogAgent v7.63.3**. +::: + +## Prerrequisitos + +**En el cluster:** + +- Crear el namespace `datadog` +- Crear el Secret `datadog-secret` con tu API key de Datadog: + +```bash +kubectl create secret generic datadog-secret \ + --from-literal=api-key= \ + -n datadog +``` + +**Opcional pero recomendado:** Crear un NodePool dedicado para Datadog (ej. `datadog-app`). Si usás un NodePool dedicado, los workloads que querés monitorear también deben correr en ese NodePool. + +## Paso 1 — Desplegar el Datadog Operator + +```bash +helm upgrade -i datadog datadog/datadog-operator \ + --namespace datadog \ + --create-namespace \ + --set "tolerations[0].key=karpenter.sh/nodepool" \ + --set "tolerations[0].operator=Equal" \ + --set "tolerations[0].value=" +``` + +Reemplazá `` con el nombre de tu NodePool (ej. `datadog-app-amd`). + +## Paso 2 — Preparar Workloads para Instrumentación + +Agregá estas annotations/labels al spec del **Deployment** de cada workload que querés que Datadog instrumente: + +```yaml +spec: + template: + metadata: + labels: + admission.datadoghq.com/enabled: "true" + annotations: + admission.datadoghq.com/js-lib.version: v5.45.0 +``` + +En el **Var Group** del workload, agregá: + +``` +DD_LOGS_ENABLED=true +DD_LOGS_INJECTION=true +DD_ENV= +DD_SERVICE= +``` + +## Paso 3 — Desplegar el DatadogAgent + +Creá `datadogagent.yaml`: + +```yaml +apiVersion: datadoghq.com/v2alpha1 +kind: DatadogAgent +metadata: + name: datadog + namespace: datadog +spec: + global: + clusterName: + site: datadoghq.com # ver https://docs.datadoghq.com/getting_started/site/ + credentials: + apiSecret: + secretName: datadog-secret + keyName: api-key + features: + admissionController: + enabled: true + mutateUnlabelled: false + apm: + enabled: true + hostPortConfig: + enabled: true + hostPort: 8126 + instrumentation: + enabled: false + logCollection: + enabled: true + containerCollectAll: true + eventCollection: + collectKubernetesEvents: true + override: + clusterAgent: + tolerations: + - key: karpenter.sh/nodepool + operator: Equal + value: + nodeAgent: + env: + - name: DD_LOGS_CONFIG_AUTO_MULTI_LINE_DETECTION + value: "true" + - name: DD_PROFILING_ENABLED + value: "true" + - name: DD_TRACE_ENABLED + value: "true" + - name: DD_LOGS_ENABLED + value: "true" + - name: DD_LOGS_INJECTION + value: "true" + - name: DD_CONTAINER_EXCLUDE + value: "name:.*" + - name: DD_CONTAINER_INCLUDE + value: "name:" # corresponde a spec.template.spec.containers.name + tolerations: + - key: karpenter.sh/nodepool + operator: Equal + value: +``` + +Aplicarlo: + +```bash +kubectl apply -f datadogagent.yaml -n datadog +``` + +## Paso 4 — Verificar el Despliegue + +```bash +kubectl get datadogagent -n datadog +kubectl get pods -n datadog +``` + +Todos los pods deberían estar en estado `Running` en pocos minutos. + +:::tip +Esta es una configuración de referencia que podés ajustar para tu entorno — expandí `DD_CONTAINER_INCLUDE` para cubrir más workloads o modificá los feature flags según necesites. +::: diff --git a/content/tutorials/es/deploy-retool-helm.mdx b/content/tutorials/es/deploy-retool-helm.mdx new file mode 100644 index 000000000..b9789e624 --- /dev/null +++ b/content/tutorials/es/deploy-retool-helm.mdx @@ -0,0 +1,158 @@ +--- +title: Desplegar Retool con Helm Charts en SleakOps +sidebar_label: Desplegar Retool con Helm +sidebar_position: 24 +description: Desplegá Retool v6.3.6 en un cluster de Kubernetes de SleakOps usando el Helm chart oficial con EBS, EFS y un NodePool dedicado para AMD64. +tags: + - kubernetes + - helm + - deployment +image: /img/tutorials/deploy-retool-helm/deploy-retool-helm.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Desplegá [Retool ](https://retool.com) en un cluster de Kubernetes de SleakOps usando el [Helm chart v6.3.6 ](https://artifacthub.io/packages/helm/retool/retool/6.3.6). + +:::info +Esta guía corresponde a la **versión 6.3.6 del Helm Chart**. Consultá [ArtifactHub ](https://artifacthub.io/packages/helm/retool/retool) para la versión más reciente y posibles cambios en los values. +::: + +## Prerrequisitos + +**Prerrequisitos en SleakOps:** + +- [EBS CSI Driver](/docs/cluster/addons/ebs) instalado (necesario para el volumen de PostgreSQL) +- [EFS CSI Driver](/docs/cluster/addons/efs) instalado (necesario para el almacenamiento de apps y flujos de Retool) +- Un NodePool AMD64 dedicado creado (Retool solo corre en arquitectura AMD64) + +**Prerrequisitos en Kubernetes:** + +- Namespace `retool` creado +- Secret `retool-keys` creado con las siguientes claves: + - `encryption-key`: generar con `openssl rand -base64 36` + - `jwt-secret`: generar con `openssl rand -base64 36` + - `license-key`: tu licencia de Retool +- Secret `retool-postgresql` creado con: + - `password` + - `postgres-password` + - `replication-password` +- Credenciales OAuth de Google (`clientId` y `clientSecret`) para SSO + +## Empecemos + +### Paso 1 — Crear el archivo `retool-values.yaml` + +Reemplazá todos los valores marcados con `{...}` para tu despliegue: + +```yaml +config: + auth: + google: + clientId: xxxxxxxxxxxxxxxxx.apps.googleusercontent.com + clientSecret: xxxxxxxxxxxxxxxxxxxxxxx + encryption-key: + jwt-secret: + licenseKeySecretName: retool-keys + useInsecureCookies: true # requerido cuando TLS termina en el load balancer + +env: + ALLOW_SAME_ORIGIN_OPTION: "true" + BASE_DOMAIN: https://retool.{dominio}.com + COOKIE_INSECURE: "true" # requerido cuando TLS termina en el load balancer + DOMAINS: https://retool.{dominio}.com + HOST_HEADER_NAME: x-forwarded-host + SANDBOX_DOMAIN: https://retool.{dominio}.com + +image: + pullPolicy: IfNotPresent + repository: tryretool/backend + tag: 3.114.13-stable # reemplazar con la versión objetivo + +ingress: + enabled: true + annotations: + alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:111222333444:certificate/12121212-1212-1212-1212-121212232323 + alb.ingress.kubernetes.io/group.name: {group.name}-private + alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' + alb.ingress.kubernetes.io/target-type: ip + hosts: + - host: retool.{dominio}.com + paths: + - path: / + pathType: Prefix + ingressClassName: alb-ingressclass-private + labels: + app.kubernetes.io/name: retool{dominio}com + tls: + - hosts: + - retool.{dominio}.com + +nodeSelector: + karpenter.sh/nodepool: {nodepool_name} # ej: amd64-utilities + kubernetes.io/arch: amd64 + +persistentVolumeClaim: + enabled: true + mountPath: /retool_backend/pv-data + size: 15Gi + storageClass: efs-sc-retain + +postgresql: + auth: + username: postgres + existingSecret: retool-postgresql + secretKeys: + adminPasswordKey: postgres-password + replicationPasswordKey: replication-password + userPasswordKey: password + primary: + tolerations: + - key: karpenter.sh/nodepool + operator: Equal + value: {nodepool_name} # ej: amd64-utilities + +replicaCount: 1 + +resources: + limits: + cpu: 4096m + memory: 8192Mi + requests: + cpu: 1024m + memory: 2048Mi + +serviceAccount: + name: retool-sa + +tolerations: +- key: karpenter.sh/nodepool + operator: Equal + value: {nodepool_name} # ej: amd64-utilities +``` + +### Paso 2 — Desplegar el chart + +Agregar el repo Helm de Retool e instalar: + +```bash +helm repo add retool https://charts.retool.com +helm repo update + +helm upgrade --install retool retool/retool \ + --version 6.3.6 \ + --namespace retool \ + --values retool-values.yaml +``` + +### Paso 3 — Verificar el despliegue + +Verificar que los Pods estén corriendo: + +```bash +kubectl get pods -n retool +``` + +Una vez que el Pod `retool` esté en estado `Running`, abrí `https://retool.{dominio}.com` para completar la configuración inicial. diff --git a/content/tutorials/es/dms-rds-migration.mdx b/content/tutorials/es/dms-rds-migration.mdx new file mode 100644 index 000000000..f1c5094d2 --- /dev/null +++ b/content/tutorials/es/dms-rds-migration.mdx @@ -0,0 +1,236 @@ +--- +title: Usar AWS DMS para sincronizar o migrar bases de datos RDS +sidebar_label: Migración RDS con DMS +sidebar_position: 19 +description: Guía paso a paso para migrar o sincronizar continuamente bases de datos RDS entre cuentas de AWS usando AWS Database Migration Service (DMS). +tags: + - aws + - rds + - database + - dms + - migration +image: /img/tutorials/dms-rds-migration/dms-rds-migration.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Migrá o sincronizá continuamente una base de datos RDS origen hacia una RDS administrada por SleakOps usando AWS Database Migration Service (DMS), incluyendo networking cross-account y prerrequisitos por motor de base de datos. + +## Prerrequisitos + +- AWS CLI configurado con perfiles para las cuentas origen y destino +- Instancias RDS origen y destino corriendo y accesibles +- Recursos suficientes: la Replication Instance de DMS debe igualar o superar las specs de la base de datos origen +- La base de datos origen debe tener CDC (Change Data Capture) habilitado — ver los pasos por motor más abajo + +## Empecemos + +### Paso 1 — Crear la Replication Instance de DMS + +Ejecutá esto en la **cuenta origen**. Configurá `allocated_storage`, `replication_instance`, `availability_zone`, `source_db_name` y `subnet_ids` (deben coincidir con las subnets de la DB origen): + +```bash +allocated_storage="25" +replication_instance="dms.r5.large" +availability_zone="us-east-1a" +source_db_name="sourcedatabase" +subnet_ids="subnet-0123456789abcdef0,subnet-0fedcba9876543210" + +replication_instance_id="replication-${source_db_name}" +subnet_group_name="dms-subnet-group-${source_db_name}" + +aws dms create-replication-subnet-group \ + --replication-subnet-group-identifier "$subnet_group_name" \ + --replication-subnet-group-description "Subnet group for DMS replication of $source_db_name" \ + --subnet-ids ${subnet_ids//,/ } + +latest_engine_version=$(aws dms describe-orderable-replication-instances \ + --query 'OrderableReplicationInstances[*].[EngineVersion]' --output text | sort -V | tail -n 1) + +aws dms create-replication-instance \ + --replication-instance-identifier "$replication_instance_id" \ + --replication-instance-class "$replication_instance" \ + --allocated-storage "$allocated_storage" \ + --availability-zone "$availability_zone" \ + --replication-subnet-group-identifier "$subnet_group_name" \ + --engine-version "$latest_engine_version" \ + --no-publicly-accessible \ + --tags Key=SourceDB,Value="$source_db_name" +``` + +### Paso 2 — Crear endpoints de origen y destino + +**Endpoint origen:** + +```bash +source_db_name="sourcedatabase" +source_engine="postgres" +source_username="mydbuser" +source_password="mypassword" +source_server_name="mydb.c1234567890.us-east-1.rds.amazonaws.com" +source_database_name="mydatabase" +source_port="5432" + +aws dms create-endpoint \ + --endpoint-identifier "source-endpoint-${source_db_name}" \ + --endpoint-type "source" \ + --engine-name "$source_engine" \ + --username "$source_username" \ + --password "$source_password" \ + --server-name "$source_server_name" \ + --database-name "$source_database_name" \ + --port "$source_port" +``` + +**Endpoint destino:** + +```bash +target_db_name="targetdatabase" +target_engine="postgres" +target_username="targetdbuser" +target_password="targetpassword" +target_server_name="targetdb.c1234567890.us-east-1.rds.amazonaws.com" +target_database_name="targetdb" +target_port="5432" + +aws dms create-endpoint \ + --endpoint-identifier "target-endpoint-${target_db_name}" \ + --endpoint-type "target" \ + --engine-name "$target_engine" \ + --username "$target_username" \ + --password "$target_password" \ + --server-name "$target_server_name" \ + --database-name "$target_database_name" \ + --port "$target_port" +``` + +### Paso 3 — Configurar el networking cross-account + +Ambas cuentas necesitan VPC Peering y reglas de Security Group para que la Replication Instance pueda alcanzar ambas bases de datos. + +**Cuenta origen (script completo):** + +```bash +REGION="us-east-1" +SOURCE_PROFILE="source-profile" +SOURCE_VPC_ID="" +TARGET_VPC_ID="" +TARGET_ACCOUNT_ID="" +SOURCE_RDS_SECURITY_GROUP="" +TARGET_RDS_CIDR_BLOCK="" + +peering_connection_id=$(aws ec2 create-vpc-peering-connection \ + --vpc-id "$SOURCE_VPC_ID" \ + --peer-vpc-id "$TARGET_VPC_ID" \ + --peer-owner-id "$TARGET_ACCOUNT_ID" \ + --region "$REGION" --profile "$SOURCE_PROFILE" \ + --query "VpcPeeringConnection.VpcPeeringConnectionId" --output text) + +SOURCE_ROUTE_TABLE_ID=$(aws ec2 describe-route-tables \ + --filters "Name=vpc-id,Values=$SOURCE_VPC_ID" \ + --profile "$SOURCE_PROFILE" \ + --query "RouteTables[0].RouteTableId" --output text) + +aws ec2 create-route \ + --route-table-id "$SOURCE_ROUTE_TABLE_ID" \ + --destination-cidr-block "$TARGET_RDS_CIDR_BLOCK" \ + --vpc-peering-connection-id "$peering_connection_id" \ + --profile "$SOURCE_PROFILE" + +aws ec2 authorize-security-group-ingress \ + --group-id "$SOURCE_RDS_SECURITY_GROUP" \ + --protocol tcp --port 5432 \ + --cidr "$TARGET_RDS_CIDR_BLOCK" \ + --profile "$SOURCE_PROFILE" + +aws iam create-role \ + --role-name "dms-access-role" \ + --profile "$SOURCE_PROFILE" \ + --assume-role-policy-document '{"Version":"2012-10-17","Statement":{"Effect":"Allow","Principal":{"Service":"dms.amazonaws.com"},"Action":"sts:AssumeRole"}}' + +aws iam attach-role-policy \ + --role-name "dms-access-role" --profile "$SOURCE_PROFILE" \ + --policy-arn arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole +``` + +**Cuenta destino:** Aceptar el VPC peering, actualizar las tablas de rutas y crear un rol IAM cross-account — replicar los pasos de la cuenta origen con credenciales de la cuenta destino. + +### Paso 4 — Prerrequisitos por motor de base de datos + +Antes de ejecutar la tarea de migración, la base de datos origen debe tener CDC habilitado. + +**PostgreSQL — actualizar `pg_hba.conf` y `postgresql.conf`:** + +``` +# pg_hba.conf — permitir la Replication Instance de DMS +host all all /32 md5 +host replication dms /32 md5 +``` + +``` +# postgresql.conf +wal_level = logical +max_replication_slots = 2 +max_wal_senders = 2 +wal_sender_timeout = 0 +``` + +Recargar: `SELECT pg_reload_conf();` + +**Oracle — habilitar archive log y supplemental logging:** + +```sql +-- Habilitar Archive Log Mode (si no está ya habilitado) +SHUTDOWN TRANSACTIONAL; +STARTUP MOUNT; +ALTER DATABASE ARCHIVELOG; +ALTER DATABASE OPEN; + +ALTER DATABASE FORCE LOGGING; +ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; +ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY, UNIQUE, FOREIGN KEY) COLUMNS; +``` + +### Paso 5 — Ejecutar la evaluación previa y arrancar la tarea + +**Evaluación pre-migración:** + +```bash +source_db_name="sourcedatabase" +target_db_name="targetdatabase" + +aws dms start-replication-task-assessment-run \ + --replication-task-assessment-run-name "premigration-${source_db_name}-to-${target_db_name}" \ + --replication-instance-arn "$(aws dms describe-replication-instances \ + --filters "Name=replication-instance-id,Values=replication-${source_db_name}" \ + --query "ReplicationInstances[0].ReplicationInstanceArn" --output text)" \ + --source-endpoint-arn "$(aws dms describe-endpoints \ + --filters "Name=endpoint-id,Values=source-endpoint-${source_db_name}" \ + --query "Endpoints[0].EndpointArn" --output text)" \ + --target-endpoint-arn "$(aws dms describe-endpoints \ + --filters "Name=endpoint-id,Values=target-endpoint-${target_db_name}" \ + --query "Endpoints[0].EndpointArn" --output text)" \ + --migration-type "full-load-and-cdc" \ + --table-mappings '{"rules":[{"rule-type":"selection","rule-id":"1","rule-name":"include-all","object-locator":{"schema-name":"%","table-name":"%"},"rule-action":"include"}]}' +``` + +**Iniciar la tarea de migración** (una vez que la evaluación pase): + +```bash +aws dms create-replication-task \ + --replication-task-identifier "migration-task-${source_db_name}-to-${target_db_name}" \ + --source-endpoint-arn "$(aws dms describe-endpoints \ + --filters "Name=endpoint-id,Values=source-endpoint-${source_db_name}" \ + --query "Endpoints[0].EndpointArn" --output text)" \ + --target-endpoint-arn "$(aws dms describe-endpoints \ + --filters "Name=endpoint-id,Values=target-endpoint-${target_db_name}" \ + --query "Endpoints[0].EndpointArn" --output text)" \ + --replication-instance-arn "$(aws dms describe-replication-instances \ + --filters "Name=replication-instance-id,Values=replication-${source_db_name}" \ + --query "ReplicationInstances[0].ReplicationInstanceArn" --output text)" \ + --migration-type "full-load-and-cdc" \ + --table-mappings '{"rules":[{"rule-type":"selection","rule-id":"1","rule-name":"include-all","object-locator":{"schema-name":"%","table-name":"%"},"rule-action":"include"}]}' +``` + diff --git a/content/tutorials/es/install-datadog.mdx b/content/tutorials/es/install-datadog.mdx new file mode 100644 index 000000000..f14289100 --- /dev/null +++ b/content/tutorials/es/install-datadog.mdx @@ -0,0 +1,89 @@ +--- +title: Instalar Datadog en un Cluster EKS de SleakOps +sidebar_label: Instalar Datadog +sidebar_position: 34 +description: Instalá el agente de monitoreo Datadog en un cluster Amazon EKS gestionado por SleakOps usando Helm, con un values.yaml y buenas prácticas recomendadas. +tags: + - kubernetes + - monitoring + - helm + - aws +image: /img/tutorials/install-datadog/install-datadog.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Instalá el agente de monitoreo [Datadog ](https://www.datadoghq.com/) en un cluster EKS de SleakOps usando Helm para obtener visibilidad completa del rendimiento de las aplicaciones y la salud de la infraestructura. + +## Prerrequisitos + +- Un cluster EKS de SleakOps con `kubectl` configurado +- Una cuenta de Datadog y una API key + +## Paso 1 — Verificar AWS CLI y kubectl + +Verificar que el entorno está listo: + +```bash +aws --version +kubectl version --short +``` + +## Paso 2 — Agregar el Repositorio Helm de Datadog + +```bash +helm repo add datadog https://helm.datadoghq.com +helm repo update +``` + +## Paso 3 — Crear `values.yaml` + +```yaml +datadog: + apiKey: + clusterName: + logs: + enabled: true + containerCollectAll: true + apm: + portEnabled: true + processAgent: + enabled: true +``` + +:::warning +No commities `values.yaml` con la API key en un repositorio. Usá un Secret de Kubernetes o pasá `--set datadog.apiKey=...` directamente. +::: + +## Paso 4 — Instalar el Agente de Datadog + +```bash +helm install datadog-agent datadog/datadog \ + --namespace datadog \ + --create-namespace \ + -f values.yaml +``` + +## Paso 5 — Verificar la Instalación + +```bash +kubectl get pods -n datadog +``` + +Todos los pods `datadog-agent-*` deberían llegar al estado `Running` en pocos minutos. + +## Comparación de Opciones de Instalación + +| Método | Ventajas | Desventajas | +| --- | --- | --- | +| **Helm (esta guía)** | Flexible, configurable, estándar | Requiere conocimiento de Helm | +| **Datadog Operator** | Gestión del ciclo de vida, basado en CRD | Más complejo de configurar | +| **Fargate Logging** | Sin gestión de nodos | Solo funciona para cargas específicas | + +## Buenas Prácticas + +- **Protegé la API key:** Guardarla en un Secret de Kubernetes en lugar de directamente en `values.yaml`. +- **Configurá alertas:** Creá monitores en Datadog para recibir notificaciones cuando se detecten problemas. +- **Mantené versiones actualizadas:** Mantener tanto el agente de Datadog como el cluster EKS actualizados para evitar incompatibilidades. diff --git a/content/tutorials/es/install-keda.mdx b/content/tutorials/es/install-keda.mdx index a02471e5c..00a16a281 100644 --- a/content/tutorials/es/install-keda.mdx +++ b/content/tutorials/es/install-keda.mdx @@ -20,6 +20,12 @@ import { FiExternalLink } from "react-icons/fi"; Aprende cómo instalar KEDA (Kubernetes Event-Driven Autoscaling) en tu clúster usando diferentes métodos. KEDA permite escalar automáticamente tus aplicaciones basándote en eventos y métricas personalizadas. +:::tip Instalá vía el Addon de SleakOps +Si tu clúster está gestionado por SleakOps, podés instalar KEDA con un solo clic desde la sección **Addons** de tu clúster — SleakOps despliega el operador, el metrics adapter, los admission webhooks, y configura el rol de IAM vía IRSA para los scalers de AWS (SQS, CloudWatch, DynamoDB Streams, Kinesis, …). + +Mirá la [documentación del Addon de KEDA](/docs/cluster/addons/keda) para los pasos completos de instalación, configuración de policies de AWS, y ejemplos de `ScaledObject`. +::: + ## Métodos de Instalación ### Usando la interfaz de Lens (opción 1) diff --git a/content/tutorials/es/install-new-relic.mdx b/content/tutorials/es/install-new-relic.mdx new file mode 100644 index 000000000..815df00d6 --- /dev/null +++ b/content/tutorials/es/install-new-relic.mdx @@ -0,0 +1,116 @@ +--- +title: Instalar New Relic en tu Aplicación +sidebar_label: Instalar New Relic +sidebar_position: 35 +description: Instalá y configurá el agente de monitoreo New Relic en tu aplicación — configuración de cuenta, instalación del agente por lenguaje y buenas prácticas de configuración. +tags: + - monitoring + - deployment + - node + - python +image: /img/tutorials/install-new-relic/install-new-relic.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Instalá y configurá el agente de monitoreo [New Relic ](https://newrelic.com/) en tu aplicación para medir y mejorar el rendimiento, con instrucciones paso a paso para múltiples runtimes. + +## Prerrequisitos + +- Una cuenta en New Relic (creala en [newrelic.com ](https://newrelic.com/)) +- Tu License Key de New Relic (la encontrás en **Account settings**) +- Acceso para reiniciar o redesplegar tu aplicación + +## Paso 1 — Obtener la License Key + +1. Iniciá sesión en tu cuenta de New Relic. +2. Navegá a **Account settings**. +3. Copiá tu **License Key** — la necesitarás para la configuración del agente. + +## Paso 2 — Instalar el Agente + +Elegí el agente para tu runtime: + + + + +```bash +npm install newrelic --save +``` + +Creá `newrelic.js` en la raíz de tu proyecto: + +```javascript +exports.config = { + app_name: ['Nombre de tu App'], + license_key: process.env.NEW_RELIC_LICENSE_KEY, + logging: { + level: 'info', + }, +}; +``` + +Requerí el agente al inicio de tu archivo de entrada: + +```javascript +require('newrelic'); +// resto de tu app +``` + + + + +```bash +pip install newrelic +``` + +Generar el archivo de configuración: + +```bash +export NEW_RELIC_LICENSE_KEY=tu-license-key +newrelic-admin generate-config $NEW_RELIC_LICENSE_KEY newrelic.ini +``` + +Ejecutar la app a través del agente: + +```bash +NEW_RELIC_CONFIG_FILE=newrelic.ini newrelic-admin run-program python app.py +``` + + + + +Agregá el JAR del agente al comando de inicio: + +```bash +java -javaagent:/ruta/a/newrelic.jar -jar tu-app.jar +``` + +Configurá `newrelic.yml` con tu `license_key` y `app_name`. + + + + +## Paso 3 — Reiniciar la Aplicación + +Una vez configurado el agente, reiniciá o redesplegá tu aplicación. New Relic empezará a enviar datos a tu cuenta en pocos minutos. + +## Alternativas de Monitoreo + +| Herramienta | Fortalezas | Consideraciones | +| --- | --- | --- | +| **New Relic** | Fácil configuración, amplio soporte de lenguajes | Costo a escala | +| **Datadog** | Excelente multi-cloud e integraciones | Puede ser costoso a escala | +| **Dynatrace** | Optimización automática con IA | Curva de aprendizaje pronunciada | +| **AppDynamics** | Análisis profundo de rendimiento | Requiere configuración inicial detallada | + +## Buenas Prácticas + +- **Monitoreo continuo:** Revisá regularmente las métricas y configurá alertas para responder rápidamente a incidentes. +- **Análisis de costo:** Revisá tu plan a medida que crece el tráfico de la aplicación para evitar cargos inesperados. +- **Mantener configuración actualizada:** Revisá la configuración del agente después de cambios significativos en la aplicación. +- **Seguir la documentación oficial:** Siempre consultá la [documentación de New Relic ](https://docs.newrelic.com/) para las últimas versiones y opciones de configuración. diff --git a/content/tutorials/es/lambda-cicd-github-actions.mdx b/content/tutorials/es/lambda-cicd-github-actions.mdx new file mode 100644 index 000000000..6c0f12cff --- /dev/null +++ b/content/tutorials/es/lambda-cicd-github-actions.mdx @@ -0,0 +1,202 @@ +--- +title: Desplegar funciones AWS Lambda con GitHub Actions +sidebar_label: Lambda CI/CD con GitHub Actions +sidebar_position: 23 +description: Construí un pipeline de GitHub Actions que empaqueta, testea y despliega funciones AWS Lambda automáticamente en cada push a main o staging. +tags: + - aws + - lambda + - ci-cd + - github-actions + - deployment +image: /img/tutorials/lambda-cicd-github-actions/lambda-cicd-github-actions.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Automatizá el build, test y despliegue de funciones AWS Lambda usando GitHub Actions, con entornos basados en ramas y rollback opcional ante fallo del health check. + +## Prerrequisitos + +- Una función AWS Lambda ya creada +- Un usuario o rol IAM con los permisos requeridos (ver más abajo) +- Un repositorio de GitHub con el código de la Lambda + +## Empecemos + +### Paso 1 — Estructura del proyecto + +Organizá tu proyecto Lambda de esta forma: + +``` +lambda-project/ +├── .github/ +│ └── workflows/ +│ └── deploy.yml +├── src/ +│ └── handlers/ +│ └── example_handler.py +├── requirements.txt +└── README.md +``` + +### Paso 2 — Función Lambda de ejemplo (Python) + +```python +import json +import os +import boto3 +import logging + +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +def lambda_handler(event, context): + try: + table_name = os.environ.get('TABLE_NAME', 'default-table') + logger.info(f"Processing event: {json.dumps(event)}") + dynamodb = boto3.resource('dynamodb') + table = dynamodb.Table(table_name) + table.put_item(Item={ + 'id': event.get('id', 'unknown'), + 'data': event.get('data', {}), + 'request_id': context.aws_request_id + }) + return { + 'statusCode': 200, + 'body': json.dumps({'message': 'Success', 'request_id': context.aws_request_id}) + } + except Exception as e: + logger.error(f"Error: {str(e)}", exc_info=True) + return {'statusCode': 500, 'body': json.dumps({'error': str(e)})} +``` + +### Paso 3 — Workflow de GitHub Actions + +Crear `.github/workflows/deploy.yml`: + +```yaml +name: Deploy Lambda + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + AWS_REGION: us-east-1 + LAMBDA_FUNCTION_NAME: example-lambda + +jobs: + deploy: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} + + - name: Install dependencies + run: | + pip install -r requirements.txt -t ./package + cp -r src/* ./package/ + + - name: Create deployment package + run: | + cd package + zip -r ../lambda-deployment.zip . + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Deploy to Lambda + run: | + aws lambda update-function-code \ + --function-name ${{ env.LAMBDA_FUNCTION_NAME }} \ + --zip-file fileb://lambda-deployment.zip + + - name: Update Lambda configuration + run: | + aws lambda update-function-configuration \ + --function-name ${{ env.LAMBDA_FUNCTION_NAME }} \ + --environment Variables="{TABLE_NAME=${{ secrets.DYNAMODB_TABLE }}}" \ + --timeout 30 \ + --memory-size 256 + + - name: Health check + run: | + aws lambda invoke \ + --function-name ${{ env.LAMBDA_FUNCTION_NAME }} \ + --payload '{"id": "health-check"}' \ + response.json + status=$(jq -r '.statusCode' response.json) + if [ "$status" != "200" ]; then + echo "Health check falló (status: $status)" + exit 1 + fi +``` + +### Paso 4 — Agregar secrets en GitHub + +Ir a **Settings → Secrets and variables → Actions** y agregar: + +| Secret | Descripción | +| --- | --- | +| `AWS_ACCESS_KEY_ID` | Access key de AWS | +| `AWS_SECRET_ACCESS_KEY` | Secret key de AWS | +| `DYNAMODB_TABLE` | Nombre de la tabla DynamoDB (opcional) | + +### Paso 5 — Permisos IAM requeridos + +El usuario o rol IAM usado por GitHub Actions necesita como mínimo: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "lambda:UpdateFunctionCode", + "lambda:UpdateFunctionConfiguration", + "lambda:PublishVersion", + "lambda:UpdateAlias", + "lambda:InvokeFunction" + ], + "Resource": "arn:aws:lambda:us-east-1:*:function:example-lambda" + } + ] +} +``` + +## Solución de problemas + +**Package demasiado grande:** Usá Lambda Layers para dependencias pesadas: + +```bash +aws lambda publish-layer-version \ + --layer-name common-dependencies \ + --zip-file fileb://dependencies-layer.zip \ + --compatible-runtimes python3.11 + +aws lambda update-function-configuration \ + --function-name example-lambda \ + --layers arn:aws:lambda:us-east-1:123456789:layer:common-dependencies:1 +``` + +**Permission denied:** Verificá que la política IAM del Paso 5 esté adjunta al rol usado por GitHub Actions. diff --git a/content/tutorials/es/lens-cluster-connectivity.mdx b/content/tutorials/es/lens-cluster-connectivity.mdx new file mode 100644 index 000000000..ccf421133 --- /dev/null +++ b/content/tutorials/es/lens-cluster-connectivity.mdx @@ -0,0 +1,79 @@ +--- +title: Solucionar problemas de conectividad al clúster con Lens +sidebar_label: Conectividad con Lens +sidebar_position: 25 +description: Diagnosticá y resolvé problemas de conexión entre Lens y un cluster de Kubernetes de SleakOps — verificación de VPN, configuración de DNS en Linux, macOS y Windows. +tags: + - kubernetes + - lens + - vpn + - networking + - dns +image: /img/tutorials/lens-cluster-connectivity/lens-cluster-connectivity.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Diagnosticá y resolvé problemas de conexión entre Lens y un cluster de Kubernetes de SleakOps, típicamente causados por configuración de VPN o resolución de DNS. + +## Prerrequisitos + +- Lens instalado y configurado con el kubeconfig del cluster +- Cliente Pritunl instalado y un perfil de VPN configurado + +## Diagnóstico y soluciones + +### Paso 1 — Verificar el estado de la VPN + +- Asegurarse de no tener otras VPNs conectadas simultáneamente. +- Verificar que la conexión VPN de Pritunl esté activa. +- **Lens no maneja bien los cambios de red** — si conectás o desconectás una VPN mientras Lens está abierto, reiniciá Lens. +- Probar los botones de reset en Pritunl: + - **Reset del servicio local de DNS** + - **Reset del servicio completo de Networking** + +### Paso 2 — Corregir la resolución DNS + +La causa más común de fallas de conectividad en Lens es que la máquina intente resolver el hostname del cluster usando servidores DNS públicos en lugar del resolvedor DNS privado de la VPC de AWS. + +**Linux y macOS — actualizar `/etc/resolv.conf`:** + +```bash +sudo nano /etc/resolv.conf +``` + +Agregar los nameservers para tus entornos: + +``` +nameserver 10.110.0.2 # DNS VPC DEV +nameserver 10.120.0.2 # DNS VPC MGT +nameserver 10.130.0.2 # DNS VPC PRD +``` + +:::warning +`/etc/resolv.conf` se regenera automáticamente cuando hay cambios en la red. Es posible que necesites re-aplicar esta configuración mientras estés conectado a la VPN, y después de reinicios o cambios de red. +::: + +**Windows — actualizar configuración de DNS:** + +1. Abrir **Panel de Control → Centro de redes y recursos compartidos**. +2. Hacer clic en **Cambiar configuración del adaptador** (columna izquierda). +3. Clic derecho sobre la conexión activa (Ethernet o Wi-Fi) → **Propiedades**. +4. Seleccionar **Protocolo de Internet versión 4 (TCP/IPv4)** → **Propiedades**. +5. Elegir **Usar las siguientes direcciones de servidor DNS**. +6. Ingresar el DNS de VPC para tu entorno (ej. `10.130.0.2`) como servidor preferido y `8.8.8.8` como alternativo público. +7. Aceptar los cambios. + +### Paso 3 — Activar "Force DNS" en Pritunl + +En el perfil de conexión de Pritunl, activar la opción **Forzar configuración de DNS**. Esto asegura que Pritunl sobreescriba el DNS del sistema con los servidores DNS de la VPN al conectarse. + +## Referencia de DNS por ambiente + +| Ambiente | DNS VPC | +| --- | --- | +| DEV | `10.110.0.2` | +| MGT | `10.120.0.2` | +| PRD | `10.130.0.2` | diff --git a/content/tutorials/es/migrate-ebs-volumes.mdx b/content/tutorials/es/migrate-ebs-volumes.mdx new file mode 100644 index 000000000..b5613aa91 --- /dev/null +++ b/content/tutorials/es/migrate-ebs-volumes.mdx @@ -0,0 +1,164 @@ +--- +title: Migrar volúmenes EBS a una nueva cuenta de AWS +sidebar_label: Migrar volúmenes EBS +sidebar_position: 15 +description: Guía paso a paso para crear snapshots, compartir, copiar y recrear volúmenes EBS desde una cuenta externa de AWS a una cuenta administrada por SleakOps. +tags: + - aws + - kubernetes + - storage + - migration + - ebs +image: /img/tutorials/migrate-ebs-volumes/migrate-ebs-volumes.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Mové volúmenes EBS desde una cuenta externa de AWS a una cuenta administrada por SleakOps creando snapshots, compartiéndolos, copiándolos al destino y recreando los volúmenes. + +## Prerrequisitos + +- AWS CLI configurado con perfiles para la cuenta origen y la cuenta destino +- Acceso `kubectl` al cluster origen para identificar los IDs de volumen +- El ID de la cuenta destino (formato de 12 dígitos) +- Un Project de SleakOps desplegado en la cuenta destino (para adjuntar la política IAM) + +## Empecemos + +### Paso 1 — Identificar los IDs de volumen a migrar + +Ejecutá este comando contra el cluster origen para listar todos los pods y sus Persistent Volumes adjuntos: + +```bash +kubectl get pods --all-namespaces -o json | jq -c '.items[] | + { + namespace: .metadata.namespace, + pod: .metadata.name, + pvc: (.spec.volumes[]? | select(.persistentVolumeClaim != null) | .persistentVolumeClaim.claimName) + } | select(.pvc != null)' +``` + +Construí una lista con los nombres de pods y sus IDs de volumen asociados: + +```bash +pod_volume_ids=( + "core-stg-postgresql-0 vol-025d6ddd6af9df250" + "core-stg-redis-master-0 vol-0a36b5ff1809fcc49" + "grafana-6dfd99b599-26gz9 vol-0f851cc80d49463b5" +) +``` + +### Paso 2 — Crear y compartir snapshots (cuenta origen) + +Este script crea una snapshot por cada volumen y la comparte con la cuenta destino. Reemplazá `target_account_id` con el ID de la cuenta destino: + +```bash +pod_volume_ids=( + "pod_name1 vol_id1" + "pod_name2 vol_id2" +) +target_account_id="111222333444" + +for entry in "${pod_volume_ids[@]}"; do + pod_name=$(echo "$entry" | awk '{print $1}') + volume_id=$(echo "$entry" | awk '{print $2}') + snapshot_name="Snapshot-$volume_id" + + snapshot_id=$(aws ec2 create-snapshot \ + --volume-id "$volume_id" \ + --description "Snapshot of $volume_id from Pod $pod_name" \ + --tag-specifications "ResourceType=snapshot,Tags=[{Key=Name,Value=$snapshot_name},{Key=Pod,Value=$pod_name},{Key=DestinationAccount,Value=$target_account_id}]" \ + --query SnapshotId --output text) + + echo "Esperando que $snapshot_id se complete..." + aws ec2 wait snapshot-completed --snapshot-ids "$snapshot_id" + + aws ec2 modify-snapshot-attribute \ + --snapshot-id "$snapshot_id" \ + --attribute createVolumePermission \ + --operation-type add \ + --user-ids "$target_account_id" + + echo "Snapshot $snapshot_id compartida con cuenta $target_account_id" +done +``` + +### Paso 3 — Adjuntar la política IAM requerida (cuenta destino) + +Adjuntá esta política al rol IAM usado por un Pod en tu Project de SleakOps destino. El nombre del rol sigue el patrón `{PROJECT_NAME}-{SHORT_UUID}-sa`: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:DescribeSnapshots", + "ec2:CopySnapshot", + "ec2:CreateVolume", + "ec2:CreateTags", + "ec2:DescribeVolumes", + "ec2:DescribeSnapshotAttribute", + "ec2:DeleteSnapshot", + "ec2:DeleteVolume" + ], + "Effect": "Allow", + "Resource": "*" + } + ] +} +``` + +### Paso 4 — Copiar snapshots y crear volúmenes (cuenta destino) + +Ejecutá este script desde un Pod en la cuenta destino que tenga la política del Paso 3. Ajustá las regiones y la zona de disponibilidad según corresponda: + +```bash +source_region="us-east-1" +destination_region="us-east-1" +availability_zone="us-east-1a" + +pod_volume_ids=( + "pod_name1 vol_id1" + "pod_name2 vol_id2" +) + +for entry in "${pod_volume_ids[@]}"; do + pod_name=$(echo "$entry" | awk '{print $1}') + volume_id=$(echo "$entry" | awk '{print $2}') + snapshot_id=$(aws ec2 describe-snapshots \ + --restorable-by-user-ids self \ + --filters "Name=tag:Name,Values=Snapshot-$volume_id" "Name=tag:Pod,Values=$pod_name" \ + --query "Snapshots[0].SnapshotId" \ + --output text \ + --region "$source_region") + + new_snapshot_id=$(aws ec2 copy-snapshot \ + --source-region "$source_region" \ + --source-snapshot-id "$snapshot_id" \ + --description "Copy of Snapshot-$volume_id from Pod $pod_name" \ + --tag-specifications "ResourceType=snapshot,Tags=[{Key=Name,Value=Copied-$volume_id},{Key=Pod,Value=$pod_name},{Key=OriginalVolumeId,Value=$volume_id}]" \ + --query SnapshotId --output text \ + --region "$destination_region") + + echo "Esperando que $new_snapshot_id se complete..." + aws ec2 wait snapshot-completed --snapshot-ids "$new_snapshot_id" --region "$destination_region" + + new_volume_id=$(aws ec2 create-volume \ + --snapshot-id "$new_snapshot_id" \ + --availability-zone "$availability_zone" \ + --volume-type gp3 \ + --tag-specifications "ResourceType=volume,Tags=[{Key=Name,Value=Volume-$volume_id},{Key=OriginalVolumeId,Value=$volume_id},{Key=Pod,Value=$pod_name}]" \ + --query VolumeId --output text \ + --region "$destination_region") + + aws ec2 wait volume-available --volume-ids "$new_volume_id" --region "$destination_region" + echo "Nuevo Volume ID: $new_volume_id (Original: $volume_id)" +done +``` + +:::warning +Registrá los nuevos IDs de volumen y sus zonas de disponibilidad. Los workloads deben desplegarse en la misma AZ que su volumen. +::: diff --git a/content/tutorials/es/migrate-external-s3.mdx b/content/tutorials/es/migrate-external-s3.mdx new file mode 100644 index 000000000..1b0ea6fd7 --- /dev/null +++ b/content/tutorials/es/migrate-external-s3.mdx @@ -0,0 +1,95 @@ +--- +title: Migrar un bucket S3 externo a SleakOps +sidebar_label: Migrar S3 externo +sidebar_position: 17 +description: Copiá objetos desde un bucket S3 en una cuenta AWS externa a un bucket S3 administrado por SleakOps usando políticas de bucket cross-account y aws s3 sync. +tags: + - aws + - s3 + - migration + - storage +image: /img/tutorials/migrate-external-s3/migrate-external-s3.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Importá objetos desde un bucket S3 en una cuenta externa de AWS hacia un bucket S3 administrado por SleakOps usando una política de bucket cross-account y `aws s3 sync` ejecutado desde un Pod dentro de tu cluster de SleakOps. + +## Prerrequisitos + +- Un Cluster configurado en SleakOps ([Documentación de Cluster](/docs/cluster)) +- El bucket S3 destino ya creado como Dependency en SleakOps +- Acceso a la cuenta AWS origen para agregar una política de bucket +- El ID de la cuenta destino y el nombre del rol Service Account del Project + +## Empecemos + +### Paso 1 — Agregar una política de bucket cross-account en el bucket origen + +En la **cuenta origen**, adjuntá esta política al bucket S3 de origen. Reemplazá los placeholders: + +- `MAIN-SOURCE-BUCKET-NAME` — nombre del bucket de origen +- `DESTINATION_ACCOUNT_ID` — ID de la cuenta AWS donde está desplegado SleakOps +- `SERVICE_ACCOUNT_NAME` — el rol IAM de tu Project en SleakOps, con el patrón `{PROJECT_NAME}-{ENVIRONMENT_NAME}-{SHORT_UUID}-sa` + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "DelegateS3Access", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::DESTINATION_ACCOUNT_ID:role/SERVICE_ACCOUNT_NAME" + }, + "Action": ["s3:ListBucket", "s3:GetObject", "s3:GetObjectTagging"], + "Resource": [ + "arn:aws:s3:::MAIN-SOURCE-BUCKET-NAME/*", + "arn:aws:s3:::MAIN-SOURCE-BUCKET-NAME" + ] + } + ] +} +``` + +### Paso 2 — Abrir la shell de un Pod en el namespace destino + +Abrí la shell de cualquier Pod desplegado en el mismo **namespace** que tu Dependency S3 destino. La Service Account del Pod ya tiene permisos para escribir en el bucket administrado por SleakOps. + +![Abrir shell de un Pod desde la consola de SleakOps](/img/tutorials/migrate-external-s3/pod-shell.png) + +### Paso 3 — Instalar AWS CLI y ejecutar el sync + +Desde la shell del Pod, instalá el AWS CLI si no está disponible, y luego ejecutá un dry-run primero para verificar el comando: + +```bash +aws s3 sync --dryrun \ + s3://MAIN-SOURCE-BUCKET-NAME \ + s3://SLEAKOPS-DESTINATION-BUCKET-NAME +``` + +Si el resultado parece correcto, ejecutá el sync real: + +```bash +aws s3 sync \ + s3://MAIN-SOURCE-BUCKET-NAME \ + s3://SLEAKOPS-DESTINATION-BUCKET-NAME +``` + +:::warning +Si los buckets están en regiones diferentes, agregá los argumentos de región para evitar errores: + +```bash +aws s3 sync \ + s3://MAIN-SOURCE-BUCKET-NAME \ + s3://SLEAKOPS-DESTINATION-BUCKET-NAME \ + --source-region SOURCE-REGION-NAME \ + --region DESTINATION-REGION-NAME +``` +::: + +### Paso 4 — Eliminar la política cross-account + +Una vez finalizada la migración, desvinculá la política del bucket de origen para cerrar el acceso cross-account. diff --git a/content/tutorials/es/migrate-files-volumes-copy.mdx b/content/tutorials/es/migrate-files-volumes-copy.mdx new file mode 100644 index 000000000..596997cbf --- /dev/null +++ b/content/tutorials/es/migrate-files-volumes-copy.mdx @@ -0,0 +1,95 @@ +--- +title: Migrar archivos entre volúmenes de Kubernetes con kubectl cp +sidebar_label: Migrar archivos entre volúmenes +sidebar_position: 18 +description: Copiá archivos almacenados en Persistent Volumes de Kubernetes de un cluster a otro usando kubectl cp, sin necesitar dumps de base de datos ni herramientas de snapshot. +tags: + - kubernetes + - migration + - storage + - kubectl +image: /img/tutorials/migrate-files-volumes-copy/migrate-files-volumes-copy.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Transferí archivos almacenados en Persistent Volumes de Kubernetes desde un cluster origen a uno destino usando `kubectl cp`, pasando por la máquina local como intermediario. + +## Prerrequisitos + +- `kubectl` instalado y configurado con contextos para ambos clusters +- Acceso a ambos clusters (kubeconfigs configurados, ej. via `aws eks update-kubeconfig`) +- Nombre del Pod origen, namespace y la ruta dentro del Pod donde se almacenan los archivos +- Nombre del Pod destino y su namespace + +## Empecemos + +### Paso 1 — Configurar acceso kubectl a ambos clusters + +Asegurate de tener entradas en el kubeconfig para ambos clusters. Por ejemplo: + +```bash +aws eks update-kubeconfig --region us-east-1 --name source-cluster-name --profile source-profile +aws eks update-kubeconfig --region us-east-1 --name target-cluster-name --profile target-profile +``` + +Verificá que los contextos estén disponibles: + +```bash +kubectl config get-contexts +``` + +### Paso 2 — Ejecutar el script de migración + +Guardá el siguiente script como `transfer.sh`, personalizá las variables al inicio y ejecutalo: + +```bash +#!/bin/bash +set -euo pipefail + +SOURCE_CONTEXT="source-context" # Contexto kubectl del cluster origen +TARGET_CONTEXT="target-context" # Contexto kubectl del cluster destino +SOURCE_POD="source-pod-name" +SOURCE_NAMESPACE="source-namespace" +SOURCE_PATH="/path/in/source-pod" + +TARGET_POD="target-pod-name" +TARGET_NAMESPACE="target-namespace" +TARGET_PATH="/path/in/target-pod" + +TEMP_LOCAL_DIR="/tmp/k8s-transfer" + +# Verificar que ambos contextos existen +for ctx in "$SOURCE_CONTEXT" "$TARGET_CONTEXT"; do + if ! kubectl config get-contexts "$ctx" &>/dev/null; then + echo "Error: el contexto kubectl '$ctx' no fue encontrado" >&2 + exit 1 + fi +done + +# Copiar del Pod origen a la máquina local +echo "Copiando archivos de $SOURCE_POD..." +mkdir -p $TEMP_LOCAL_DIR +kubectl --context="$SOURCE_CONTEXT" cp $SOURCE_NAMESPACE/$SOURCE_POD:$SOURCE_PATH $TEMP_LOCAL_DIR + +# Copiar de la máquina local al Pod destino +echo "Copiando archivos a $TARGET_POD..." +kubectl --context="$TARGET_CONTEXT" cp $TEMP_LOCAL_DIR/. $TARGET_NAMESPACE/$TARGET_POD:$TARGET_PATH + +# Limpiar +rm -rf $TEMP_LOCAL_DIR +echo "Transferencia completa." +``` + +Hacelo ejecutable y corrélo: + +```bash +chmod +x transfer.sh +./transfer.sh +``` + +:::info +Este enfoque es ideal para assets de aplicaciones — archivos subidos, multimedia, configuración — que no son parte de una base de datos. Para datos de bases de datos, usá la [guía de restauración de dump de PostgreSQL](/tutorial/postgresql-dump-restore). +::: diff --git a/content/tutorials/es/migrate-postgres-heroku-to-rds.mdx b/content/tutorials/es/migrate-postgres-heroku-to-rds.mdx new file mode 100644 index 000000000..fa277f722 --- /dev/null +++ b/content/tutorials/es/migrate-postgres-heroku-to-rds.mdx @@ -0,0 +1,88 @@ +--- +title: Migrar una base de datos PostgreSQL grande desde Heroku a RDS +sidebar_label: Migrar Postgres de Heroku a RDS +sidebar_position: 14 +description: Migrá bases de datos PostgreSQL grandes (20 GB+) desde Heroku a Amazon RDS usando pgcopydb y un Job de SleakOps, con soporte para migración incremental posterior. +tags: + - aws + - rds + - postgresql + - heroku + - migration + - database +image: /img/tutorials/migrate-postgres-heroku-to-rds/migrate-postgres-heroku-to-rds.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Migrá una base de datos PostgreSQL grande desde Heroku a Amazon RDS usando [pgcopydb ](https://pgcopydb.readthedocs.io/en/latest/) ejecutado dentro de un Job de SleakOps, con una migración incremental opcional para sincronizar datos nuevos después de la carga inicial. + +:::info +Para bases de datos mayores a 20 GB, Heroku recomienda [hacer un fork de la base de datos ](https://devcenter.heroku.com/articles/heroku-postgres-fork) antes de migrar, para no impactar el tráfico de producción. +::: + +## Prerrequisitos + +- Un Cluster configurado en SleakOps ([Documentación de Cluster](/docs/cluster)) +- Un Environment y Project ya desplegados +- La Dependency RDS destino creada en SleakOps +- La RDS destino debe tener **al menos el doble del almacenamiento** de los datos origen (ej: si el origen es 400 GB, aprovisioná al menos 800 GB) +- Credenciales y connection string de la base de datos en Heroku + +## Empecemos + +### Paso 1 — Crear un Job en SleakOps + +Creá un Job usando una imagen de Postgres compatible con la versión de tu base de datos. El Job crea un Pod donde correrá `pgcopydb`. + +:::tip +Configuración de ejemplo: +- **Image URL:** `ghcr.io/dimitri/pgcopydb` +- **Image tag:** `latest` +- **Command:** `/bin/sh -c 'sleep infinity'` +::: + +{/* TODO: screenshot - Formulario de creación de Job en SleakOps */} + +### Paso 2 — Abrir la terminal del Pod + +Conectate a la terminal del Pod creado por el Job (por ejemplo, desde Lens). + +### Paso 3 — Ejecutar la migración inicial + +Configurá las URLs de conexión origen y destino, y ejecutá `pgcopydb clone`: + +```bash +export PG_SOURCE_URL="postgres://user:pass@host:5432/dbname" +export PG_TARGET_URL="postgres://${USERNAME}:${PASSWORD}@${ADDRESS}:5432/${NAME}?sslmode=require&keepalives=1&keepalives_idle=30" + +pgcopydb clone \ + --source "$PG_SOURCE_URL" \ + --target "$PG_TARGET_URL" \ + --jobs N \ + --not-consistent \ + --no-owner \ + --dir /tmp/pgcopydb \ + --no-acl +``` + +Reemplazá `N` con la cantidad de workers paralelos apropiada para el CPU disponible en el Pod. + +### Paso 4 — (Opcional) Migración incremental posterior + +Una vez finalizado el clone inicial, ejecutá una migración de seguimiento para importar las filas nuevas escritas en el origen después de la carga inicial: + +```bash +cat /tmp/pgcopydb/snapshot.json | jq '.lsn' + +pgcopydb follow \ + --source "$PG_SOURCE_URL" \ + --target "$PG_TARGET_URL" \ + --dir /tmp/pgcopydb \ + --endpos "LSN_OBTENIDO" \ + --jobs N +``` + +Reemplazá `LSN_OBTENIDO` con el valor LSN devuelto por el comando anterior. diff --git a/content/tutorials/es/migrate-rds-snapshot-between-accounts.mdx b/content/tutorials/es/migrate-rds-snapshot-between-accounts.mdx new file mode 100644 index 000000000..1432701ee --- /dev/null +++ b/content/tutorials/es/migrate-rds-snapshot-between-accounts.mdx @@ -0,0 +1,138 @@ +--- +title: Migrar un snapshot de Amazon RDS entre cuentas +sidebar_label: Migrar snapshot de RDS +sidebar_position: 12 +description: Guía paso a paso para compartir y restaurar un snapshot de Amazon RDS entre cuentas de AWS, incluyendo escenarios con cifrado y cross-region. +tags: + - aws + - rds + - database + - migration + - snapshot + - kms +image: /img/tutorials/migrate-rds-snapshot-between-accounts/migrate-rds-snapshot-between-accounts.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Compartí y restaurá un snapshot de Amazon RDS entre cuentas de AWS, cubriendo bases de datos no cifradas, cifradas con la clave KMS predeterminada y cifradas con clave KMS personalizada, además del escenario cross-region. + +## Prerrequisitos + +- Acceso a la consola de AWS en la cuenta origen y la cuenta destino +- Un Cluster configurado en SleakOps ([Documentación de Cluster](/docs/cluster)) +- Un Environment configurado ([Documentación de Environment](/docs/environment)) +- El ID de la cuenta destino (formato de 12 dígitos) + +## Empecemos + +### Caso 1 — RDS no cifrada + +**En la cuenta origen:** + +1. Ir a **RDS → Snapshots**. +2. Seleccionar la snapshot → **Actions → Share snapshot**. +3. Ingresar el **ID de la cuenta destino** (formato: `123456789012`). +4. Confirmar. + +**En la cuenta destino:** + +1. Ir a **SleakOps → Dependencies → Create**. +2. Seleccionar **Create an RDS from a snapshot**. +3. Ingresar el identificador de la snapshot compartida. + +--- + +### Caso 2 — RDS cifrada con la clave KMS predeterminada de AWS (`aws/rds`) + +:::warning +Las claves KMS administradas por AWS (`aws/rds`) **no se pueden compartir directamente** entre cuentas. Primero es necesario re-cifrar la snapshot con una clave administrada por el cliente (CMK). +::: + +**En la cuenta origen:** + +1. Crear una clave KMS personalizada con la política descripta en el [Caso 3](#caso-3--rds-cifrada-con-clave-kms-personalizada-cmk). +2. Ir a **RDS → Snapshots**. +3. Seleccionar la snapshot cifrada → **Actions → Copy snapshot**, eligiendo la clave KMS recién creada. +4. Esperar a que finalice la copia. +5. Sobre la nueva snapshot → **Actions → Share snapshot**. +6. Ingresar el ID de la cuenta destino. + +**En la cuenta destino:** + +1. Ir a **SleakOps → Dependencies → Create**. +2. Seleccionar **Create an RDS from a snapshot**. +3. Ingresar el identificador de la snapshot compartida. + +:::info +Los snapshots sin cifrado pueden compartirse directamente entre cuentas. Si tu RDS usa cifrado, ver el Caso 3 más abajo para compartir la clave CMK. +::: + +--- + +### Caso 3 — RDS cifrada con clave KMS personalizada (CMK) + +**En la cuenta origen — compartir la clave KMS:** + +1. Ir a **KMS → Customer managed keys**. +2. Seleccionar la clave usada para cifrar la RDS. +3. En **Key policy**, agregar el siguiente statement en la sección de permisos: + +```json +{ + "Sid": "Allow use of the key from destination account", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::<>:root" + }, + "Action": [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey", + "kms:CreateGrant" + ], + "Resource": "*" +} +``` + +**En la cuenta origen — compartir la snapshot:** + +1. Ir a **RDS → Snapshots**. +2. Seleccionar la snapshot → **Actions → Share snapshot**. +3. Ingresar el ID de la cuenta destino. + +**En la cuenta destino — copiar con clave KMS propia:** + +1. Ir a **RDS → Snapshots → Shared with me**. +2. Seleccionar la snapshot → **Actions → Copy snapshot**. +3. Seleccionar una clave KMS de la cuenta destino (obligatorio). +4. Esperar a que finalice la copia. + +**En la cuenta destino — restaurar:** + +1. Ir a **SleakOps → Dependencies → Create**. +2. Seleccionar **Create an RDS from a snapshot**. +3. Ingresar el identificador de la snapshot copiada. + +--- + +### Caso especial — Snapshots en diferentes regiones + +Si la snapshot está en una región diferente a donde querés restaurarla: + +**En la cuenta origen:** + +1. Ir a **RDS → Snapshots**. +2. Seleccionar la snapshot → **Actions → Copy snapshot**. +3. En **Destination Region**, seleccionar la región destino. +4. Si está cifrada con CMK, seleccionar una clave KMS válida en la región destino. +5. Esperar que finalice la copia cross-region. +6. Continuar con el proceso normal de compartir según el caso (1, 2 o 3). + +:::info +Las claves KMS son específicas por región. Al copiar entre regiones, siempre usá una clave KMS de la región destino. Las copias cross-region pueden tardar horas dependiendo del tamaño de la snapshot. +::: diff --git a/content/tutorials/es/networking-vpc.mdx b/content/tutorials/es/networking-vpc.mdx new file mode 100644 index 000000000..36bf6168f --- /dev/null +++ b/content/tutorials/es/networking-vpc.mdx @@ -0,0 +1,73 @@ +--- +title: Networking y Recursos de Red en SleakOps +sidebar_label: Networking y VPC +sidebar_position: 29 +description: Entendé cómo SleakOps estructura la arquitectura de red — VPCs, subredes, Security Groups, External-DNS y VPC Peering entre entornos. +tags: + - networking + - aws + - kubernetes + - vpn +image: /img/tutorials/networking-vpc/networking-vpc.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Entendé la arquitectura de red que SleakOps despliega en los entornos de los clientes — cómo se organiza, cómo se protegen los recursos y cómo se facilita la comunicación tanto interna como externa. + +:::info +La red está diseñada para garantizar seguridad, escalabilidad y alta disponibilidad. Permite separar ambientes, proteger datos sensibles y exponer servicios de manera controlada y segura. +::: + +## 1. Descripción General de la Arquitectura + +La infraestructura de red en SleakOps se basa en los siguientes componentes principales: + +- **VPC (Virtual Private Cloud):** Segmenta la red por entorno (management, production, development). +- **Subredes:** Separan el tráfico según su función: públicas (expuestas a Internet), privadas (acceso restringido, acceden a Internet a través de un NAT Gateway) y de persistencia (bases de datos, almacenamiento). +- **Internet Gateway:** Permite la comunicación entre la VPC y el exterior (Internet). +- **Route Tables:** Definen las rutas de tráfico entre subredes y hacia/desde Internet. +- **Security Groups:** Firewalls virtuales que controlan el tráfico de entrada y salida de los recursos. +- **DNS Interno:** Permite que los recursos se comuniquen usando nombres en vez de IPs. +- **External-DNS:** Servicio que corre dentro de cada clúster Kubernetes (EKS), encargado de gestionar automáticamente los registros DNS públicos en Route53 para los servicios expuestos desde el clúster. + +## 2. Flujo Típico de Comunicación + +1. **Acceso desde Internet:** Un usuario accede a un servicio expuesto (por ejemplo, una API). El tráfico llega al Internet Gateway y es dirigido a la subred pública. +2. **Control de acceso:** El Security Group asociado al recurso valida si la conexión está permitida. +3. **Comunicación interna:** Los servicios internos (en subredes privadas o de persistencia) pueden comunicarse entre sí usando el DNS interno, siempre bajo las reglas de los Security Groups. +4. **Exposición de servicios:** Si un servicio dentro del clúster Kubernetes debe ser accesible desde Internet, se expone a través de un Application Load Balancer — External-DNS se encarga de registrar automáticamente el nombre en Route53. + +Esta segmentación asegura que solo los servicios necesarios sean expuestos y que los datos sensibles permanezcan protegidos. + + + Arquitectura de red de SleakOps — VPCs, subredes, Internet Gateway y flujo de tráfico + + +## 3. External-DNS y Route53 + +Se utiliza una solución automatizada para gestionar los registros DNS públicos de los servicios desplegados, integrando la infraestructura con Route53. Esto permite que los servicios sean accesibles de forma segura y sencilla desde Internet. External-DNS no expone servicios directamente, sino que automatiza la gestión de registros DNS públicos para recursos ya expuestos (por ejemplo, mediante Application Load Balancer). + +## 4. Conectividad entre Entornos mediante VPC Peering + +Para permitir la comunicación controlada entre entornos (por ejemplo, entre management y production), SleakOps configura **conexiones VPC Peering** de manera explícita entre las VPCs de los distintos entornos. + +Un VPC Peering permite que dos VPCs puedan intercambiar tráfico interno como si estuvieran en la misma red, sin pasar por Internet, NAT Gateway ni VPN. Es una conexión directa entre dos redes. + + + Conexiones VPC Peering entre los entornos management, development y production + + +:::tip +Además del acceso mediante Internet Gateway, SleakOps contempla otros mecanismos de conectividad como **Pritunl VPN**, **NAT Gateway** y **Transit Gateway**, dependiendo del caso de uso y el nivel de aislamiento requerido. +::: + +## Referencia de DNS por Ambiente + +| Ambiente | DNS VPC | +| --- | --- | +| DEV | `10.110.0.2` | +| MGT | `10.120.0.2` | +| PRD | `10.130.0.2` | diff --git a/content/tutorials/es/nodepool-strategies.mdx b/content/tutorials/es/nodepool-strategies.mdx new file mode 100644 index 000000000..481495f79 --- /dev/null +++ b/content/tutorials/es/nodepool-strategies.mdx @@ -0,0 +1,155 @@ +--- +title: Estrategias de Nodepools en SleakOps +sidebar_label: Estrategias de Nodepools +sidebar_position: 47 +description: Aprendé a configurar nodepools en SleakOps para optimizar costos, mejorar el rendimiento y elegir los tipos de instancia correctos para cada workload. +tags: + - kubernetes + - aws + - cost-optimization + - scaling + - performance +image: /img/tutorials/nodepool-strategies/nodepool-strategies.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Aprendé a configurar nodepools en SleakOps para optimizar costos, mejorar el rendimiento y elegir los tipos de instancia correctos para cada workload. + +## ¿Qué es un nodepool? + +En SleakOps, un **nodepool** es un grupo de nodos EC2 dentro de tu cluster de Kubernetes que comparten la misma configuración — arquitectura, tipo de instancia, modelo de facturación y límites de recursos. Karpenter gestiona el autoscaling dentro de cada pool automáticamente. + +Cuando creás un Cluster, SleakOps aprovisiona un conjunto de nodepools por defecto: + +| **nodepool** | **Propósito** | +|---|---| +| `sleakops-build-arm64` / `sleakops-build-amd64` | Dedicados a los builds de proyectos. No se pueden editar ni eliminar. | +| `sleakops-core` | Ejecuta los componentes internos de SleakOps y los addons del cluster. | +| `ondemand-arm` / `ondemand-amd` | Pools On-Demand listos para usar con tus workloads. | +| `spot-arm` / `spot-amd` | Pools Spot listos para usar con tus workloads. | + +Cada Project Environment se asigna a un nodepool. El cambio de asignación es posible en cualquier momento desde la configuración del environment. + +--- + +## Estrategias de Optimización de Costos + +### 1. Spot con Fallback On-Demand + +La forma más efectiva de reducir costos de cómputo es correr workloads en **instancias Spot** manteniendo **On-Demand como fallback automático**. En SleakOps, configurá esto seleccionando tanto `Spot` como `On-Demand` como Node Types al crear o editar un nodepool. El sistema los prioriza en este orden: **Reserved → Spot → On-Demand**. + +:::tip +Se recomienda siempre combinar Spot con On-Demand. Así, si la capacidad Spot no está disponible, tus workloads continúan corriendo en nodos On-Demand sin downtime. +::: + +**Ideal para:** web services stateless, workers que pueden reiniciarse de forma segura, entornos de dev y staging. + +### 2. Arquitectura ARM (Graviton) + +Las instancias AWS Graviton (ARM64) son **20–30% más baratas** que sus equivalentes x86 y entregan rendimiento comparable o superior para la mayoría de los workloads en contenedores. + +Para usar ARM en SleakOps, se deben cumplir tres condiciones: + +1. **Tu aplicación debe ser compatible con ARM** — verificá que todas las dependencias y binarios nativos tengan builds disponibles para ARM. +2. **Tu imagen Docker debe estar construida para ARM** — en SleakOps, cada proyecto corre en una única arquitectura. La imagen debe apuntar a `linux/arm64` específicamente. Configurá el build en la configuración de tu proyecto. +3. **Tu proyecto debe estar configurado para usar un nodepool ARM** — podés consultar y actualizar el nodepool asignado a un proyecto desde el detalle del proyecto en SleakOps. + +**Ideal para:** servicios escritos en Go, Python o Node.js sin dependencias nativas exclusivas de x86. + +--- + +## Estrategias de Rendimiento + +### On-Demand para Workers Críticos + +Los workers que ejecutan procesos de larga duración o no interrumpibles siempre deben correr en nodos **On-Demand**. Las instancias Spot pueden ser reclamadas con solo 2 minutos de aviso — un proceso que no puede resumirse de forma segura desde la mitad de su ejecución no debe correr en Spot. + +Ejemplos comunes: +- Workers de background jobs con operaciones no idempotentes +- Ejecutores de tareas programadas que procesan grandes volúmenes de datos +- Pipelines ETL que corren durante horas y no pueden reiniciarse de forma segura a mitad de ejecución + +Creá un nodepool On-Demand dedicado y asigná estos Project Environments a él para aislar los workers críticos de los pools Spot. + +--- + +## Spot vs On-Demand + +| | **Spot** | **On-Demand** | +|---|---|---| +| **Costo** | Hasta 90% más barato | Precio fijo, más alto | +| **Disponibilidad** | Variable, puede interrumpirse | Garantizada | +| **Aviso de interrupción** | 2 minutos | Sin interrupción | +| **Ideal para** | Servicios stateless, batch jobs, dev/staging | Workers críticos, procesos de larga duración | + +**Configuración recomendada:** seleccioná `[Spot, On-Demand]` para que el pool siempre tenga fallback. Ver [¿Cuáles son los diferentes tipos de nodepools?](/docs/cluster/nodepools) para la explicación completa del orden de prioridad. + +--- + +## ARM vs AMD (x86) + +| | **ARM64 (Graviton)** | **AMD64 (x86)** | +|---|---|---| +| **Costo** | Menor (20–30%) | Mayor | +| **Compatibilidad** | Requiere app compatible con ARM + imagen ARM | Universal | +| **Ideal para** | Servicios nuevos, workloads puros en Go/Python/Node | Apps legacy, binarios nativos solo x86 | + +:::info +Cambiar el Project Environment de una arquitectura a otra dispara un **rebuild automático** en SleakOps. Una vez que el build termina, la nueva versión se despliega automáticamente. +::: + +--- + +## Elegir Tipos de Instancia + +Al crear un nodepool en SleakOps (**Cluster → Settings → nodepools → Create**), podés controlar qué instancias EC2 puede aprovisionar Karpenter de dos formas: + +### Por Categoría de Instancia (Recomendado) + +Dejá el campo **Instance Type** vacío. Karpenter elegirá automáticamente la instancia más económica disponible de las categorías por defecto (`t`, `c`, `m`, `r`) según la demanda actual y los precios spot. + +| **Categoría** | **Familias ejemplo** | **Ideal para** | +|---|---|---| +| `t` | t3, t4g (burstable) | Workloads variables, dev/staging | +| `c` | c5, c6i (compute-optimized) | Procesamiento CPU-intensivo | +| `m` | m5, m6i (general purpose) | Workloads balanceados | +| `r` | r5, r6i (memory-optimized) | Workloads memory-intensivos | + +### Por Tipo de Instancia Específico + +Seleccioná uno o más tipos de instancia explícitos cuando tu workload tenga requisitos de hardware estrictos. El caso más común son los **workloads con GPU** — inferencia de machine learning, procesamiento de imágenes/video o computación científica. + +``` +Ejemplos: g4dn.xlarge, g5.2xlarge, p3.2xlarge +``` + +:::info +Las instancias GPU (familias `g` y `p`) no están habilitadas por defecto en las cuentas AWS. Antes de agregar un tipo de instancia GPU a un nodepool, solicitá un aumento de cuota desde la [consola de AWS Service Quotas ](https://console.aws.amazon.com/servicequotas/) para la familia de instancias correspondiente en tu región. +::: + +--- + +## Todos los Parámetros de Configuración + +Los siguientes campos están disponibles al crear o editar un nodepool en SleakOps (**Cluster → Settings → nodepools**): + +| **Campo** | **Descripción** | +|---|---| +| **Name** | Identificador único dentro del cluster (minúsculas, guiones permitidos) | +| **Architecture** | `ARM64` o `AMD64` — no se puede cambiar después de la creación | +| **Node Type** | `Spot`, `On-Demand`, `Reserved` — seleccioná múltiples para fallback por prioridad | +| **Instance Type** | Tipos EC2 específicos, o dejá vacío para selección flexible por categoría | +| **CPU Limit** | CPU total máxima que puede usar el pool (techo del autoscaler) | +| **Memory Limit** | Memoria total máxima que puede usar el pool (techo del autoscaler) | +| **Per-Node Min CPU** | CPU mínima por nodo individual | +| **Per-Node Min Memory** | Memoria mínima por nodo individual | +| **Storage** | Tamaño del volumen raíz por nodo (20 GB por defecto) | + +### ¿Qué hago si necesito un parámetro que SleakOps no expone? + +La spec de `NodePool` de Karpenter soporta campos adicionales — políticas de consolidación, expiración, taints personalizados, topology spread y más. Si tu caso de uso requiere una configuración no disponible en la consola de SleakOps, contactá al soporte o abrí un ticket describiendo tu necesidad. + +Referencia completa de la spec de Karpenter NodePool: [Documentación de Karpenter NodePool ](https://karpenter.sh/docs/concepts/nodepools/). diff --git a/content/tutorials/es/optimize-aws-costs.mdx b/content/tutorials/es/optimize-aws-costs.mdx new file mode 100644 index 000000000..fb38b453a --- /dev/null +++ b/content/tutorials/es/optimize-aws-costs.mdx @@ -0,0 +1,83 @@ +--- +title: Estrategias de Optimización de Costos en AWS +sidebar_label: Optimizar Costos AWS +sidebar_position: 36 +description: Técnicas prácticas de optimización de costos en AWS — Spot Instances, S3 Intelligent-Tiering, procesadores Graviton, Savings Plans, CloudFront y Compute Optimizer. +tags: + - aws + - cost-optimization +image: /img/tutorials/optimize-aws-costs/optimize-aws-costs.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Técnicas prácticas para reducir costos en AWS sin sacrificar confiabilidad — cubriendo cómputo, almacenamiento, CDN y estrategias de right-sizing. + +## 1. Spot Instances con Auto Scaling — Reducir Costos EC2 Significativamente + +Correr todos los workloads en instancias EC2 On-Demand es caro. Mover workloads sin estado o tolerantes a fallos a **Spot Instances** respaldadas por un **Auto Scaling Group (ASG)** con fallback a On-Demand puede reducir esos costos sustancialmente. + +**Cuándo usarlo:** Jobs batch, workers en segundo plano, agentes de CI/CD, web services sin estado. + +```yaml +# Ejemplo de política de instancias mixtas en ASG +MixedInstancesPolicy: + InstancesDistribution: + OnDemandPercentageAboveBaseCapacity: 20 + SpotAllocationStrategy: capacity-optimized +``` + +## 2. S3 Intelligent-Tiering — Dejar de Pagar por Datos Fríos + +Pagar tarifas de S3 Standard por logs, reportes y backups que casi no se acceden es innecesario. **S3 Intelligent-Tiering** mueve automáticamente los objetos a clases de almacenamiento más baratas cuando no se acceden con frecuencia. + +**Habilitarlo desde la consola de SleakOps:** navegá a la configuración de la Dependency S3 del bucket, abrí la configuración de almacenamiento y activá Intelligent-Tiering. También podés configurarlo directamente en la consola de AWS S3 en **Bucket → Properties → Intelligent-Tiering**. + +El costo de almacenamiento puede reducirse **20–30%** para buckets de datos fríos. + +## 3. AWS Graviton — EC2 Más Barato y Más Rápido + +Las instancias Graviton2/3 (basadas en ARM) son **20–30% más baratas** que las equivalentes x86 y a menudo más rápidas para muchos workloads. La migración solo requiere recompilar las imágenes Docker para ARM64: + +```dockerfile +FROM --platform=linux/arm64 node:20-alpine +``` + +```bash +docker buildx build --platform linux/arm64 -t tu-imagen:latest . +``` + +Familias de instancias compatibles: `m7g`, `c7g`, `r7g`. + +:::warning +Antes de migrar workloads de producción a ARM64, verificá que tu aplicación y todas sus dependencias compilen y funcionen correctamente en arquitectura ARM64. Algunas librerías nativas y binarios pueden no tener builds disponibles para ARM64. +::: + +## 4. AWS Savings Plans — Descuentos para Cargas Predecibles + +Para workloads que siempre corren, comprometerse a un **Compute Savings Plan de 1 año** reduce los costos EC2 **hasta un 40%** comparado con precios On-Demand. + +Comprar en: **Consola AWS → Cost Explorer → Savings Plans → Comprar un Savings Plan**. + +## 5. CloudFront — Reducir Costos de Egreso en S3 + +Servir activos estáticos directamente desde S3 genera altos costos de egreso. Poner **CloudFront** delante de S3 reduce tanto las solicitudes GET como los costos de transferencia de datos, mientras mejora los tiempos de carga mediante el caché en los edge nodes. + +## 6. Compute Optimizer — Encontrar Oportunidades de Right-Sizing + +**AWS Compute Optimizer** analiza métricas de uso y recomienda tipos de instancias del tamaño correcto. Frecuentemente encuentra workloads corriendo en instancias sobredimensionadas que pueden reducirse sin impacto en el rendimiento. + +Habilitarlo en: **Consola AWS → Compute Optimizer → Get started**. + +## Resumen + +| Técnica | Ahorro típico | Mejor para | +| --- | --- | --- | +| Spot Instances + ASG | 50–70% en cómputo | Workloads sin estado, tolerantes a fallos | +| S3 Intelligent-Tiering | 20–30% en almacenamiento | Logs, backups, datos poco accedidos | +| Instancias Graviton | 20–30% en cómputo | Cualquier workload containerizado | +| Savings Plans | Hasta 40% en cómputo | Servidores always-on | +| CloudFront | Reduce egreso + GET | Activos estáticos, media | +| Compute Optimizer | Variable | Instancias sobredimensionadas | diff --git a/content/tutorials/es/optimize-docker-image.mdx b/content/tutorials/es/optimize-docker-image.mdx new file mode 100644 index 000000000..6936405f3 --- /dev/null +++ b/content/tutorials/es/optimize-docker-image.mdx @@ -0,0 +1,117 @@ +--- +title: Cómo Optimizar el Tamaño de tu Imagen Docker +sidebar_label: Optimizar Imágenes Docker +sidebar_position: 42 +description: Reducí el tamaño de las imágenes Docker y mejorá los tiempos de build usando imágenes base Alpine, builds multi-stage y .dockerignore — técnicas prácticas para imágenes listas para producción. +tags: + - docker + - deployment + - performance +image: /img/tutorials/optimize-docker-image/optimize-docker-image.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Reducí el tamaño de las imágenes Docker y mejorá los tiempos de build usando imágenes base Alpine, builds multi-stage y `.dockerignore` — haciendo tus imágenes más rápidas de descargar, desplegar y más seguras. + +## Por Qué Importa el Tamaño de la Imagen + +Las imágenes más pequeñas significan tiempos de pull más rápidos, despliegues más ágiles, menor costo de almacenamiento en el registry, y una superficie de ataque reducida con menos paquetes instalados. + +## Técnica 1 — Usar Imágenes Base Alpine o Slim + +En lugar de una imagen base completa, usar una alternativa mínima: + +```dockerfile +# Antes — imagen grande +FROM node:20 + +# Después — mucho más pequeña +FROM node:20-alpine +``` + +Imágenes base mínimas comunes: +| Runtime | Imagen mínima | +| --- | --- | +| Node.js | `node:20-alpine` | +| Python | `python:3.12-slim` | +| Java | `eclipse-temurin:21-jre-alpine` | +| Go | `scratch` o `gcr.io/distroless/static` | + +:::warning +Alpine usa `musl libc` en lugar de `glibc`. Algunos paquetes se comportan diferente. Probá tu aplicación exhaustivamente al cambiar. +::: + +## Técnica 2 — Builds Multi-Stage + +Usar una etapa para construir y una etapa mínima separada para el runtime: + +```dockerfile +# Etapa de build — tiene todas las herramientas +FROM node:20-alpine AS build +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Etapa de runtime — solo lo necesario para correr +FROM node:20-alpine +WORKDIR /app +COPY --from=build /app/dist ./dist +COPY --from=build /app/node_modules ./node_modules +CMD ["node", "dist/index.js"] +``` + +Esto mantiene herramientas de build, código fuente y archivos intermedios fuera de la imagen final. + +## Técnica 3 — Usar `.dockerignore` + +Prevenir que archivos innecesarios sean enviados al build context: + +``` +# .dockerignore +node_modules +.git +.env +*.log +dist +coverage +.DS_Store +README.md +``` + +Un build context más pequeño acelera cada `docker build`. + +## Técnica 4 — Ordenar Capas para Eficiencia de Cache + +Docker cachea las capas. Ponés las capas que cambian frecuentemente al final para maximizar los cache hits: + +```dockerfile +# Bien — dependencias cacheadas separadas del código fuente +COPY package*.json ./ +RUN npm ci +COPY . . # los cambios de fuente no invalidan el cache de npm ci + +# Mal — npm ci se re-ejecuta en cada cambio de fuente +COPY . . +RUN npm ci +``` + +## Técnica 5 — Eliminar Archivos Innecesarios Post-Instalación + +```dockerfile +RUN apk add --no-cache algún-paquete +``` + +## Resumen + +| Técnica | Impacto | +| --- | --- | +| Imágenes base Alpine/slim | Reducción de tamaño 3–10x | +| Builds multi-stage | Elimina herramientas de build y fuente de la imagen final | +| `.dockerignore` | Builds más rápidos, sin filtrado accidental de secretos | +| Orden de capas | Builds incrementales más rápidos por reutilización del cache | +| Limpieza post-instalación | Elimina caches del gestor de paquetes | diff --git a/content/tutorials/es/postgres-helm-existing-volume.mdx b/content/tutorials/es/postgres-helm-existing-volume.mdx new file mode 100644 index 000000000..6ec305472 --- /dev/null +++ b/content/tutorials/es/postgres-helm-existing-volume.mdx @@ -0,0 +1,129 @@ +--- +title: Desplegar un Helm Chart de PostgreSQL con un volumen EBS existente +sidebar_label: Postgres Helm con volumen existente +sidebar_position: 16 +description: Desplegá el Helm chart de Bitnami PostgreSQL en SleakOps apuntando a un volumen EBS pre-existente creando recursos PV y PVC con el node affinity correcto. +tags: + - kubernetes + - helm + - postgresql + - storage + - ebs +image: /img/tutorials/postgres-helm-existing-volume/postgres-helm-existing-volume.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Desplegá el Helm chart de Bitnami PostgreSQL dentro de un cluster de SleakOps apuntando a un volumen EBS pre-existente, creando los recursos PersistentVolume y PersistentVolumeClaim requeridos. + +:::info +Este tutorial asume que ya seguiste [Migrar volúmenes EBS a una nueva cuenta](/tutorial/migrate-ebs-volumes) y tenés disponible un ID de volumen migrado. +::: + +## Prerrequisitos + +- Un Cluster configurado en SleakOps con EBS CSI Driver instalado +- Un Environment y Project desplegados +- El ID del volumen EBS y su zona de disponibilidad +- `kubectl` y `helm` instalados localmente +- El nombre del NodePool del cluster de SleakOps + +## Empecemos + +### Paso 1 — (Opcional) Identificar el ID del volumen + +Si migraste el volumen usando la guía de migración de EBS, ejecutá este comando desde un Pod con los permisos IAM necesarios para listar los volúmenes migrados: + +```bash +aws ec2 describe-volumes \ + --filters Name=status,Values=available \ + --query "Volumes[?Tags[?Key=='Name'] && Tags[?Key=='Pod'] && Tags[?Key=='OriginalVolumeId']].[{VolumeId: VolumeId, Name: Tags[?Key=='Name']|[0].Value, Pod: Tags[?Key=='Pod']|[0].Value, OriginalVolumeId: Tags[?Key=='OriginalVolumeId']|[0].Value, Size: Size}]" \ + --output table +``` + +Copiá el `VolumeId` del volumen objetivo. + +### Paso 2 — Crear el PV, PVC y desplegar con Helm + +Configurá las variables y ejecutá el script para crear los recursos de Kubernetes y desplegar el chart: + +```bash +volume_id="vol-111aaa222ddd3333fff44" +pv_name="your-pv-name" +storage_size="8Gi" +availability_zone="us-east-1a" +nodepool_name="your-nodepool-name" + +pvc_name="${pv_name}-claim" + +# Crear PV y PVC +kubectl apply -f - < values.yaml <](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_CreateSnapshot.html). +::: + +## Prerrequisitos + +- Un Cluster configurado en SleakOps ([Documentación de Cluster](/docs/cluster)) +- Un Environment y Project ya desplegados +- Credenciales y endpoint de la base de datos origen +- La Dependency RDS destino creada en SleakOps + +## Empecemos + +### Paso 1 — Generar el dump de la base de datos origen + +Ejecutá este comando desde cualquier máquina con acceso de red a la base de datos origen. Pedirá la contraseña: + +```bash +pg_dump -h POSTGRESQL_ADDRESS -U POSTGRESQL_USERNAME -W -Fc -f dump.dump +``` + +Para opciones detalladas, consultá la [documentación de la Dependency PostgreSQL de SleakOps](/docs/project/dependency/postgresql-aws#how-do-i-create-a-postgresql-database-dump). + +### Paso 2 — Escalar la instancia RDS destino + +Aumentá temporalmente el tipo de instancia RDS a uno de mayor tamaño. Esto reduce significativamente el tiempo de carga del dump. Hacelo al inicio para que el resize esté listo antes de comenzar la restauración. + +### Paso 3 — Crear un Job en SleakOps + +Creá un Job usando la misma versión de Postgres que tu Dependency RDS destino. Este Job crea un Pod al que te conectarás para ejecutar la restauración. + +:::tip +Configuración de ejemplo: +- **Image URL:** `docker.io/library/postgres` +- **Image tag:** `17` (debe coincidir con la versión de tu RDS) +::: + + + Formulario de creación de Job en SleakOps — campos de image URL y tag + + + + Formulario de creación de Job en SleakOps — vista previa de configuración + + +### Paso 4 — Abrir la terminal del Pod y ejecutar la restauración + +Conectate a la terminal del Pod creado por el Job (por ejemplo, desde Lens) y ejecutá: + +```bash +pg_restore -j 8 -O -v -e --clean --if-exists --no-owner --no-privileges \ + --exit-on-error \ + -h POSTGRESQL_ADDRESS \ + -U POSTGRESQL_USERNAME \ + -W \ + -d POSTGRESQL_DATABASE \ + dump.dump +``` + +El flag `-j 8` usa 8 workers en paralelo para acelerar la restauración. Ajustá según el CPU disponible. + +### Paso 5 — Volver al tamaño original de la instancia RDS + +Una vez finalizada la restauración, revertí el tipo de instancia RDS a su tamaño original. diff --git a/content/tutorials/es/pritunl-dns-universal.mdx b/content/tutorials/es/pritunl-dns-universal.mdx new file mode 100644 index 000000000..8cb23df1f --- /dev/null +++ b/content/tutorials/es/pritunl-dns-universal.mdx @@ -0,0 +1,245 @@ +--- +title: Solución Universal de DNS para Pritunl Client +sidebar_label: Fix de DNS para Pritunl +sidebar_position: 26 +description: Corregí la resolución DNS de la VPN Pritunl en Linux, macOS y Windows con scripts por OS y un script universal multiplataforma. +tags: + - vpn + - networking + - dns + - pritunl +image: /img/tutorials/pritunl-dns-universal/pritunl-dns-universal.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Corregí la resolución DNS del cliente Pritunl para que los dominios privados de la VPN (endpoints EKS, servicios internos, microservicios) siempre se resuelvan correctamente en Linux, macOS y Windows. + +## El Problema + +Al usar Pritunl Client, algunos sistemas operativos no aplican correctamente el **DNS interno de la VPN**, lo que provoca que los **dominios privados o de infraestructura no se resuelvan**. + +Cada OS maneja el DNS por interfaz de manera diferente — `systemd-resolved`, `resolvconf`, `NetworkManager`, `scutil`, la API de Windows — y en ciertos casos **ignoran el DNS que la VPN entrega**. + +El resultado es un comportamiento inconsistente: +- En algunas máquinas, los dominios internos funcionan +- En otras, la resolución falla y requiere configuración manual +- En redes con IPs privadas o dinámicas, el problema es aún más frecuente + +La solución universal es **aplicar el DNS de la VPN explícitamente sobre la interfaz VPN** usando herramientas nativas del sistema (`resolvectl`, `scutil`, PowerShell). Esto garantiza la resolución correcta sin modificar configuraciones permanentes del sistema. + +## Prerrequisitos + +- Pritunl Client instalado y conectado a un perfil de VPN +- Acceso `sudo` / administrador en la máquina + +## Script para Linux + +Compatible con Ubuntu, Debian, Fedora, Arch, Manjaro, Kali, PopOS y cualquier distribución con `systemd-resolved`. + +### `fix_vpn_dns_linux.sh` + +```bash +#!/bin/bash + +# CONFIGURACIÓN: editá estos valores +VPN_IFACE="tun0" # Interfaz típica de Pritunl +VPN_DNS="10.110.0.2" # DNS interno +VPN_DOMAIN="internal.us-east-1.eks.amazonaws.com" # Dominio interno EKS + +if ip link show "$VPN_IFACE" >/dev/null 2>&1; then + echo "[+] VPN detectada en $VPN_IFACE" + if command -v resolvectl >/dev/null 2>&1; then + echo "[+] Aplicando DNS..." + sudo resolvectl dns "$VPN_IFACE" "$VPN_DNS" + sudo resolvectl domain "$VPN_IFACE" "~$VPN_DOMAIN" + else + echo "[!] resolvectl no encontrado. No se puede aplicar DNS sin intrusión." + fi +else + echo "[-] VPN no detectada. DNS sin cambios." +fi +``` + +```bash +chmod +x fix_vpn_dns_linux.sh +./fix_vpn_dns_linux.sh +``` + +**Verificar:** + +```bash +resolvectl status tun0 +``` + +## Script para macOS + +### `fix_vpn_dns_macos.sh` + +```bash +#!/bin/bash + +VPN_IFACE="utun2" +VPN_DNS="10.110.0.2" +VPN_DOMAIN="internal.us-east-1.eks.amazonaws.com" + +if ifconfig "$VPN_IFACE" >/dev/null 2>&1; then + echo "[+] VPN detectada. Configurando DNS..." + sudo scutil </dev/null 2>&1; then + echo "[-] VPN no detectada en $VPN_IFACE_LINUX." + return + fi + echo "[+] VPN detectada." + if command -v resolvectl >/dev/null 2>&1; then + sudo resolvectl dns "$VPN_IFACE_LINUX" "$VPN_DNS" + sudo resolvectl domain "$VPN_IFACE_LINUX" "~$VPN_DOMAIN" + echo "[✓] DNS aplicado." + else + echo "[!] resolvectl no encontrado." + fi +} + +run_macos() { + echo "[OS] macOS" + if ! ifconfig "$VPN_IFACE_MACOS" >/dev/null 2>&1; then + echo "[-] VPN no detectada en $VPN_IFACE_MACOS." + return + fi + echo "[+] VPN detectada. Aplicando DNS..." + sudo scutil </dev/null 2>&1 || command -v powershell >/dev/null 2>&1; then + run_windows + else + echo "[ERROR] Sistema operativo no soportado." + fi + ;; +esac +``` + +```bash +chmod +x universal_fix_vpn_dns.sh +./universal_fix_vpn_dns.sh +``` + +### Variables de configuración + +| Variable | Descripción | Ejemplo | +| --- | --- | --- | +| `VPN_IFACE_LINUX` | Interfaz VPN en Linux | `tun0` | +| `VPN_IFACE_MACOS` | Interfaz VPN en macOS | `utun2` | +| `VPN_ADAPTER_WINDOWS` | Nombre del adaptador en Windows | `Pritunl` | +| `VPN_DNS` | IP del servidor DNS interno | `10.110.0.2` | +| `VPN_DOMAIN` | Dominio interno | `internal.us-east-1.eks.amazonaws.com` | + +## Solución de Problemas + +| Problema | Causa | Solución | +| --- | --- | --- | +| El script no detecta la VPN | Nombre de interfaz incorrecto | Verificar con `ip link` (Linux), `ifconfig` (macOS), `Get-NetAdapter` (Windows) | +| El DNS no se aplica | Falta de permisos | Ejecutar con `sudo` | +| Funciona pero se pierde al reconectar | No está automatizado | Crear un hook post-conexión en Pritunl para ejecutar el script automáticamente | + +:::warning +Estos scripts **no** realizan cambios permanentes en el sistema. La configuración DNS desaparece automáticamente al desconectar la VPN. +::: diff --git a/content/tutorials/es/rds-external-access.mdx b/content/tutorials/es/rds-external-access.mdx new file mode 100644 index 000000000..548b18ab5 --- /dev/null +++ b/content/tutorials/es/rds-external-access.mdx @@ -0,0 +1,89 @@ +--- +title: Acceder a una instancia RDS desde fuera de tu VPC +sidebar_label: Acceso externo a RDS +sidebar_position: 21 +description: Opciones para acceder de forma segura a una instancia de Amazon RDS en una subnet privada desde fuera de la VPC — Bastion Host, AWS Client VPN, PrivateLink y acceso público. +tags: + - aws + - rds + - database + - networking + - security + - vpn +image: /img/tutorials/rds-external-access/rds-external-access.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Conectate a una instancia de Amazon RDS en una subnet privada desde fuera de la VPC, usando la opción segura que se adapte a tu patrón de acceso. + +:::warning +Exponer una base de datos directamente a internet no es recomendable. Aumenta significativamente la superficie de ataque. Usá las opciones de abajo para evitar riesgos innecesarios. +::: + +## Opciones + +| Opción | Seguridad | Facilidad de configuración | +| --- | --- | --- | +| **Bastion Host (EC2)** | Alta | Media | +| **AWS Client VPN** | Muy alta | Fácil | +| **AWS PrivateLink** | Máxima | Compleja | +| **Acceso público directo** | No recomendado | Fácil | + +## Opción 1 — Bastion Host (Recomendado para acceso administrativo ocasional) + +Un Bastion Host es una instancia EC2 en una subnet pública que actúa como relay. + +1. Crear una instancia EC2 en una **subnet pública** con IP pública. +2. Conectarse a la EC2 via SSH. +3. Desde la EC2, conectarse al endpoint de la RDS usando el cliente de base de datos. + +**Ejemplo — Oracle con SQL*Plus desde la EC2:** + +```bash +sqlplus $DB_USER/$DB_PASSWORD@//rds-endpoint:1521/service_name +``` + +**Túnel SSH para SQL Developer local:** + +```bash +ssh -L 1521:rds-endpoint:1521 ec2-user@public-ip-ec2 -i "key.pem" +``` + +Luego conectar SQL Developer local a `localhost:1521`. + +## Opción 2 — AWS Client VPN (Recomendado para acceso regular desde tu laptop) + +Configurá un AWS Client VPN en la misma VPC que la RDS. Una vez conectado, el endpoint privado de la RDS quedará accesible desde tu máquina local. + +Ver la [documentación oficial de AWS Client VPN ](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is.html) para los pasos de configuración, o seguir el [tutorial de VPN de SleakOps](/tutorial/third-party-integration-vpn). + +## Opción 3 — AWS PrivateLink (Para acceso seguro entre VPCs o cuentas) + +AWS PrivateLink expone la RDS a otras VPCs o cuentas sin pasar por internet público. + +1. Crear un AWS PrivateLink (VPC Endpoint Service) en la VPC donde vive la RDS. +2. Configurar un VPC Endpoint en la otra VPC (que puede ser pública). +3. Conectarse a la RDS a través del endpoint de PrivateLink. + +Ver la [documentación de PrivateLink ](https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/endpoint-services-overview.html). + +## Opción 4 — Acceso público directo (No recomendado) + +Si necesitás que la RDS sea accesible públicamente, seguí el tutorial [Transformar RDS de privada a pública](/tutorial/make-rds-public). + +:::warning +Este proceso implica aproximadamente 20 minutos de downtime. Los pasos simplificados son: +1. Crear una nueva VPC y un Subnet Group dentro de ella. +2. Mover la RDS a la nueva VPC. +3. Crear un Subnet Group público en la VPC administrada por SleakOps. +4. Mover la RDS de vuelta a la VPC de SleakOps usando el Subnet Group público. +5. Habilitar **Public Access** en la RDS. +6. Crear y asociar un Security Group que restrinja el acceso solo a IPs de confianza. +::: + +:::info +**RDS Proxy no soporta acceso público.** RDS Proxy está diseñado exclusivamente para gestión de conexiones dentro de una VPC — no tiene IP pública. Usá una de las opciones anteriores. +::: diff --git a/content/tutorials/es/test-site-to-site-vpn.mdx b/content/tutorials/es/test-site-to-site-vpn.mdx new file mode 100644 index 000000000..cc896e9fb --- /dev/null +++ b/content/tutorials/es/test-site-to-site-vpn.mdx @@ -0,0 +1,100 @@ +--- +title: Probar una VPN Site to Site creada con SleakOps +sidebar_label: Probar VPN Site to Site +sidebar_position: 32 +description: Verificá la conectividad a través de una VPN site-to-site de SleakOps usando Nmap desde un pod de Kubernetes — paso a paso con resultados esperados y diagrama de flujo de paquetes. +tags: + - vpn + - networking + - kubernetes +image: /img/tutorials/test-site-to-site-vpn/test-site-to-site-vpn.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Verificá que una VPN site-to-site de SleakOps funciona correctamente ejecutando tests de conectividad con Nmap desde un pod de Kubernetes dentro del cluster. + +## Prerrequisitos + +- Una VPN site-to-site ya creada y configurada en SleakOps +- Acceso al cluster destino mediante Lens o `kubectl` + +## Paso 1 — Crear un Pod de Prueba + +Usando Lens o `kubectl`, creá el siguiente pod en el namespace correspondiente: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: vpn-test + namespace: +spec: + containers: + - name: tools + image: nicolaka/netshoot:latest + command: ["sleep", "3600"] +``` + +Aplicarlo: + +```bash +kubectl apply -f vpn-test-pod.yaml +``` + +## Paso 2 — Acceder a la Shell del Pod + +Abrí una shell dentro del pod via Lens (clic derecho → Shell) o: + +```bash +kubectl exec -it vpn-test -n -- bash +``` + +## Paso 3 — Ejecutar Tests con Nmap + +Necesitás: +- **NAT_IP**: IP de la instancia NAT (ej. `10.100.0.10`) +- **REMOTE_IP**: IP del servidor remoto al otro lado de la VPN (ej. `192.168.0.100`) +- **PUERTO**: Puerto a probar (ej. `443`) + +```bash +NAT_IP="10.100.0.10" +REMOTE_IP="192.168.0.100" +PUERTO="443" +nmap -p $PUERTO $REMOTE_IP +``` + +**Resultado esperado:** + +``` +PORT STATE SERVICE +443/tcp open https +``` + +Si el estado es `filtered` o `closed`, revisá el estado del túnel VPN y el firewall en el lado remoto. + +## Paso 4 — Limpiar + +Una vez confirmada la conectividad, eliminá el pod de prueba: + +```bash +kubectl delete pod vpn-test -n +``` + +## Referencia del Flujo de Paquetes + +El siguiente diagrama muestra cómo viajan los paquetes a través de la VPN: + +``` +Request del cliente → Ruta al NAT via Peering → NAT Instance (IP forwarding) +→ MASQUERADE → VPN Gateway → Túnel IPSec → Customer Gateway +→ Descifrar → Verificación de firewall → Servidor remoto +``` + +Puntos de falla clave a verificar en cada paso: +1. **Sin ruta a la subred remota** — Verificar route tables de la VPC +2. **IP forwarding deshabilitado** en la instancia NAT — Habilitar con `sysctl net.ipv4.ip_forward=1` +3. **Túnel VPN DOWN** — Verificar el estado de la conexión VPN en la consola AWS +4. **Firewall remoto bloqueando** — Verificar security group / reglas de firewall en el lado remoto diff --git a/content/tutorials/es/transfer-domain-route53.mdx b/content/tutorials/es/transfer-domain-route53.mdx new file mode 100644 index 000000000..436ff9d45 --- /dev/null +++ b/content/tutorials/es/transfer-domain-route53.mdx @@ -0,0 +1,78 @@ +--- +title: Transferir un dominio a Amazon Route 53 +sidebar_label: Transferir dominio a Route 53 +sidebar_position: 22 +description: Checklist y guía paso a paso para transferir un dominio desde tu registrador actual a Amazon Route 53 sin tiempo de inactividad. +tags: + - aws + - route53 + - dns + - domain + - networking +image: /img/tutorials/transfer-domain-route53/transfer-domain-route53.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Transferí un dominio desde tu registrador actual a Amazon Route 53 siguiendo este checklist de preparación, migración de DNS y solicitud de transferencia para evitar downtime. + +:::info +Verificá que la extensión de tu dominio (ej. `.com`, `.net`, `.mx`) esté soportada por Route 53 antes de comenzar: [Lista de TLDs soportados ](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/registrar-tld-list.html). +::: + +## Prerrequisitos + +- Acceso a tu cuenta en el registrador actual +- Acceso al correo del registrante en WHOIS (se recibirán correos de autorización ahí) +- El dominio debe tener al menos 60 días desde su registro o última transferencia +- Si DNSSEC está habilitado, debe desactivarse en el registrador actual antes de transferir + +## Empecemos + +### Etapa 1 — Checklist de pre-transferencia + +Completá todo esto antes de tocar la consola de AWS: + +- [ ] **Confirmar soporte de TLD** — verificar que la extensión esté en la [lista de TLDs soportados ](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/registrar-tld-list.html) +- [ ] **Verificar acceso al correo del registrante** — recibirás los correos de autorización ahí +- [ ] **Desbloquear el dominio** — desactivar el "Transfer Lock" o "Domain Lock" en el registrador actual +- [ ] **Confirmar antigüedad del dominio** — al menos 60 días desde el registro o la última transferencia +- [ ] **Obtener el Auth Code (EPP Code)** — solicitarlo al registrador actual; obligatorio para la mayoría de los TLDs +- [ ] **Desactivar DNSSEC** — si está habilitado, desactivarlo antes de la transferencia +- [ ] **Verificar el estado del dominio en WHOIS** — el estado debe ser `ok`. No se pueden transferir dominios en estos estados: + - `clientTransferProhibited` + - `pendingDelete` + - `pendingTransfer` + - `redemptionPeriod` + - `serverTransferProhibited` + +:::warning +Si el dominio vence en menos de 7 días, renovalo en el registrador actual antes de intentar la transferencia. +::: + +### Etapa 2 — Migrar los registros DNS primero (recomendado) + +AWS recomienda migrar el servicio DNS antes de transferir el registro del dominio para evitar downtime. + +1. **Crear una zona hospedada en Route 53** con el mismo nombre que tu dominio. SleakOps puede crearla por vos. +2. **Copiar todos los registros DNS** (A, MX, CNAME, TXT) del proveedor actual a la nueva zona en Route 53. +3. (Opcional) **Actualizar los name servers** en el registrador actual a los de Route 53 para probar la resolución antes de la transferencia. + +### Etapa 3 — Enviar la solicitud de transferencia en Route 53 + +1. Ir a **Route 53 → Registered domains → Transfer in**. +2. Ingresar el nombre del dominio y hacer clic en **Check** para confirmar que es transferible. +3. Ingresar el **Auth Code** obtenido en la Etapa 1. +4. Configurar DNS: seleccionar la zona hospedada en Route 53 (recomendado) o mantener los name servers actuales. +5. Revisar y completar la **información de contacto del registrante** — debe coincidir con la del registrador actual para evitar bloqueos por cambio de titularidad. +6. Revisar el costo (generalmente incluye renovación por 1 año) y confirmar el pedido. + +### Etapa 4 — Completar la transferencia + +- Estar atento al correo del registrante por si llega un enlace de autorización. +- Monitorear el progreso en **Route 53 → Pending requests**. +- Según el TLD, las transferencias pueden tardar desde unos minutos hasta 10 días. +- Una vez completada, AWS envía un correo de confirmación. +- (Si aplica) **Reactivar DNSSEC** una vez que la transferencia esté completa y el DNS funcione correctamente. diff --git a/content/tutorials/es/workers-use-cases.mdx b/content/tutorials/es/workers-use-cases.mdx new file mode 100644 index 000000000..c72d84d65 --- /dev/null +++ b/content/tutorials/es/workers-use-cases.mdx @@ -0,0 +1,98 @@ +--- +title: Casos de Uso para Workers en SleakOps +sidebar_label: Casos de Uso de Workers +sidebar_position: 27 +description: Explorá los casos de uso más comunes para Workloads Worker en SleakOps — procesamiento en segundo plano, tareas programadas, autoescalado, ETL, pipelines e integraciones con APIs externas. +tags: + - kubernetes + - background-jobs + - scaling + - deployment +image: /img/tutorials/workers-use-cases/workers-use-cases.png +--- + +import Zoom from "react-medium-image-zoom"; +import "react-medium-image-zoom/dist/styles.css"; +import { FiExternalLink } from "react-icons/fi"; + +Explorá los casos de uso más comunes para Workloads Worker en SleakOps — desde el procesamiento asíncrono de mensajes hasta tareas programadas, autoescalado y orquestación de pipelines. + +## ¿Qué es un Workload Worker? + +En SleakOps, un **Worker** es un tipo de Workload diseñado para procesamiento en segundo plano. A diferencia de un Web Service, un Worker no expone un endpoint HTTP — corre continuamente consumiendo mensajes de una cola o ejecutando tareas disparadas por eventos o schedules. + +## Casos de Uso + +### 1. Procesamiento de Mensajes o Tareas en Segundo Plano + +Un Worker puede consumir mensajes de una cola o backend de tareas como **RabbitMQ, AWS SQS, Redis, PostgreSQL (listen/notify), Kafka**, etc. + +Ejemplos comunes: +- Procesamiento de eventos asíncronos (envío de correos, notificaciones push, actualización de bases de datos) +- Ingesta de datos en sistemas de análisis en tiempo real +- Orquestación de microservicios mediante colas de eventos + +### 2. Ejecución de Tareas Programadas o Basadas en Eventos + +Cuando una aplicación necesita ejecutar tareas en base a un **crontab, eventos del sistema o triggers definidos por el usuario**, los Workers manejan la ejecución sin bloquear otros procesos. + +Ejemplos comunes: +- **Tareas recurrentes**: limpieza de logs, regeneración de caché, actualizaciones periódicas de datos +- **Tareas basadas en eventos**: cuando un usuario realiza una acción que requiere procesar datos en segundo plano (generación de reportes, procesamiento de imágenes o videos) + +Dos estrategias de ejecución: +1. Ejecutar la tarea dentro del mismo Worker (más simple, menos recomendable en clusters de Kubernetes) +2. Enviar un mensaje a una cola para que un Worker especializado lo procese + +### 3. Autoescalado de Workers Basado en Carga + +Los Workers pueden escalarse dinámicamente en función de la cantidad de mensajes pendientes en la cola o el consumo de recursos en el cluster. + +Ejemplos comunes: +- Lanzar instancias adicionales de Workers durante picos de tráfico para procesar tareas en paralelo +- Usar [**KEDA** (Kubernetes Event-Driven Autoscaling)](/tutorial/install-keda) para escalar Workers basándose en métricas de profundidad de la cola + +### 4. Procesamiento de Archivos y ETL (Extract, Transform, Load) + +Los Workers son ideales para manejar archivos pesados o procesar grandes volúmenes de datos de forma asíncrona. + +Ejemplos comunes: +- Procesamiento de archivos CSV/JSON y carga en una base de datos +- Conversión y transformación de formatos (imágenes, videos, PDFs) +- Ingesta de logs y análisis de datos en tiempo real + +### 5. Orquestación de Flujos de Trabajo y Pipelines + +Algunas tareas requieren múltiples pasos encadenados que pueden ser gestionados mediante Workers. + +Ejemplos comunes: +- Pipeline de Machine Learning: descarga de datos → preprocesamiento → entrenamiento de modelo → evaluación +- Flujos de aprobación en aplicaciones empresariales (solicitudes de compras que pasan por diferentes etapas) + +### 6. Workers para Seguridad y Compliance + +Los Workers pueden ejecutar verificaciones de seguridad y cumplimiento de políticas en segundo plano. + +Ejemplos comunes: +- **Monitoreo de logs** en busca de anomalías o intentos de acceso no autorizados +- **Escaneo de vulnerabilidades** en contenedores o infraestructura +- **Gestión de permisos y auditoría** de accesos en sistemas de autenticación + +### 7. Integraciones con APIs Externas + +Los Workers manejan integraciones con APIs de terceros sin bloquear la aplicación principal. + +Ejemplos comunes: +- Sincronización de datos con CRM, ERP u otros servicios externos +- Envío de datos a servicios de terceros (Stripe, Twilio, Google Sheets) +- Monitoreo de cambios en APIs externas y actualización en la aplicación + +## Eligiendo el Patrón Correcto + +| Patrón | Cuándo usarlo | +| --- | --- | +| **Worker único consumiendo una cola** | Tareas async simples con un consumidor | +| **Múltiples Workers en la misma cola** | Alto throughput que requiere paralelismo | +| **Worker + [autoescalado KEDA](/tutorial/install-keda)** | Carga variable; escalar a cero cuando no hay trabajo | +| **Workers encadenados (pipeline)** | Flujos de trabajo complejos de múltiples pasos | +| **Worker disparado por cron** | Tareas recurrentes programadas | diff --git a/docusaurus.config.js b/docusaurus.config.js index c2da7bbb5..9552263d6 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -46,6 +46,11 @@ const config = { // Eliminamos el tema easyops-cn/docusaurus-search-local themes: ["@docusaurus/theme-mermaid"], + clientModules: [ + require.resolve("./src/clientModules/languageDetect.js"), + require.resolve("./src/clientModules/hashOpen.js"), + ], + presets: [ [ "classic", @@ -262,6 +267,7 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, + additionalLanguages: ['powershell', 'bash', 'docker', 'yaml'], }, }, }; diff --git a/src/clientModules/hashOpen.js b/src/clientModules/hashOpen.js new file mode 100644 index 000000000..1ea463f55 --- /dev/null +++ b/src/clientModules/hashOpen.js @@ -0,0 +1,67 @@ +// Docusaurus's Details component calls e.stopPropagation() + e.preventDefault() +// for all clicks inside , which prevents anchor links from updating the +// URL. We intercept in capture phase (runs before React's delegation) to fix this. +if (typeof document !== "undefined") { + document.addEventListener( + "click", + (e) => { + // Case 1: clicking the # anchor icon on a heading inside + const anchor = e.target.closest("a[href]"); + if (anchor && anchor.closest("summary")) { + const href = anchor.getAttribute("href"); + if (href && href.startsWith("#")) { + setTimeout(() => window.history.pushState(null, "", href), 0); + return; + } + } + + // Case 2: clicking to open a
— update URL to heading ID + // so the open FAQ is directly linkable/shareable + const summary = e.target.closest("summary"); + if (summary) { + const details = summary.closest("details"); + if (details && !details.open) { + const heading = details.querySelector( + "h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]" + ); + if (heading) { + setTimeout( + () => window.history.pushState(null, "", "#" + heading.id), + 0 + ); + } + } + } + }, + true // capture phase — fires before Docusaurus's handler + ); +} + +export function onRouteDidUpdate() { + if (typeof window === "undefined") return; + + const hash = window.location.hash; + if (!hash) return; + + // Wait one frame so React has finished rendering the new page + requestAnimationFrame(() => { + const element = document.getElementById(hash.slice(1)); + if (!element) return; + + const targetDetails = + element.tagName === "DETAILS" ? element : element.closest("details"); + + if (targetDetails && !targetDetails.open) { + // Docusaurus Details is React-controlled — clicking the summary + // triggers its internal state update (setCollapsed/setOpen) + const summary = targetDetails.querySelector("summary"); + if (summary) { + summary.click(); + } + } + + setTimeout(() => { + element.scrollIntoView({ behavior: "smooth" }); + }, 100); + }); +} diff --git a/src/data/tutorials-generated.json b/src/data/tutorials-generated.json index 221e5df19..db647b500 100644 --- a/src/data/tutorials-generated.json +++ b/src/data/tutorials-generated.json @@ -174,6 +174,373 @@ ], "image": "/img/tutorials/openvpn-profile/openvpn-profile.png", "sidebar_position": 11 + }, + { + "id": "migrate-rds-snapshot-between-accounts", + "title": "Migrate an Amazon RDS Snapshot Between Accounts", + "description": "Step-by-step guide to share and restore an Amazon RDS snapshot across AWS accounts, including encrypted and cross-region scenarios.", + "tags": [ + "aws", + "rds", + "database", + "migration", + "snapshot", + "kms" + ], + "image": "/img/tutorials/migrate-rds-snapshot-between-accounts/migrate-rds-snapshot-between-accounts.png", + "sidebar_position": 12 + }, + { + "id": "postgresql-dump-restore", + "title": "Restore a PostgreSQL Dump Using a SleakOps Job", + "description": "Step-by-step guide to dump and restore a PostgreSQL database using a temporary SleakOps Job and a scaled-up RDS instance.", + "tags": [ + "aws", + "rds", + "postgresql", + "database", + "migration", + "dump" + ], + "image": "/img/tutorials/postgresql-dump-restore/postgresql-dump-restore.png", + "sidebar_position": 13 + }, + { + "id": "migrate-postgres-heroku-to-rds", + "title": "Migrate a Large PostgreSQL Database from Heroku to RDS", + "description": "Migrate large PostgreSQL databases (20 GB+) from Heroku to Amazon RDS using pgcopydb and a SleakOps Job, with support for incremental follow-up migrations.", + "tags": [ + "aws", + "rds", + "postgresql", + "heroku", + "migration", + "database" + ], + "image": "/img/tutorials/migrate-postgres-heroku-to-rds/migrate-postgres-heroku-to-rds.png", + "sidebar_position": 14 + }, + { + "id": "migrate-ebs-volumes", + "title": "Migrate EBS Volumes to a New AWS Account", + "description": "Step-by-step guide to snapshot, share, copy, and recreate EBS volumes from an external AWS account into a SleakOps-managed account.", + "tags": [ + "aws", + "kubernetes", + "storage", + "migration", + "ebs" + ], + "image": "/img/tutorials/migrate-ebs-volumes/migrate-ebs-volumes.png", + "sidebar_position": 15 + }, + { + "id": "postgres-helm-existing-volume", + "title": "Deploy a PostgreSQL Helm Chart Using an Existing EBS Volume", + "description": "Deploy the Bitnami PostgreSQL Helm chart on SleakOps pointing to a pre-existing EBS volume by creating PV and PVC resources with the correct node affinity.", + "tags": [ + "kubernetes", + "helm", + "postgresql", + "storage", + "ebs" + ], + "image": "/img/tutorials/postgres-helm-existing-volume/postgres-helm-existing-volume.png", + "sidebar_position": 16 + }, + { + "id": "migrate-external-s3", + "title": "Migrate an External S3 Bucket to SleakOps", + "description": "Copy objects from a source S3 bucket in an external AWS account to a SleakOps-managed S3 bucket using cross-account bucket policies and aws s3 sync.", + "tags": [ + "aws", + "s3", + "migration", + "storage" + ], + "image": "/img/tutorials/migrate-external-s3/migrate-external-s3.png", + "sidebar_position": 17 + }, + { + "id": "migrate-files-volumes-copy", + "title": "Migrate Files Between Kubernetes Volumes Using kubectl cp", + "description": "Copy files stored in Kubernetes Persistent Volumes from one cluster to another using kubectl cp, without requiring database dumps or snapshot tools.", + "tags": [ + "kubernetes", + "migration", + "storage", + "kubectl" + ], + "image": "/img/tutorials/migrate-files-volumes-copy/migrate-files-volumes-copy.png", + "sidebar_position": 18 + }, + { + "id": "dms-rds-migration", + "title": "Use AWS DMS to Synchronize or Migrate RDS Databases", + "description": "Step-by-step guide to migrate or continuously synchronize RDS databases across AWS accounts using AWS Database Migration Service (DMS).", + "tags": [ + "aws", + "rds", + "database", + "dms", + "migration" + ], + "image": "/img/tutorials/dms-rds-migration/dms-rds-migration.png", + "sidebar_position": 19 + }, + { + "id": "amazon-ses", + "title": "Get Started with Amazon SES", + "description": "Configure Amazon SES to send emails from your application — verify an identity, test in sandbox mode, and request production access.", + "tags": [ + "aws", + "ses", + "email" + ], + "image": "/img/tutorials/amazon-ses/amazon-ses.png", + "sidebar_position": 20 + }, + { + "id": "rds-external-access", + "title": "Access an RDS Instance from Outside Your VPC", + "description": "Options to securely access an Amazon RDS instance running in a private subnet from outside the VPC — Bastion Host, AWS Client VPN, PrivateLink, and public access.", + "tags": [ + "aws", + "rds", + "database", + "networking", + "security", + "vpn" + ], + "image": "/img/tutorials/rds-external-access/rds-external-access.png", + "sidebar_position": 21 + }, + { + "id": "transfer-domain-route53", + "title": "Transfer a Domain to Amazon Route 53", + "description": "Checklist and step-by-step guide to transfer a domain from your current registrar to Amazon Route 53 without downtime.", + "tags": [ + "aws", + "route53", + "dns", + "domain", + "networking" + ], + "image": "/img/tutorials/transfer-domain-route53/transfer-domain-route53.png", + "sidebar_position": 22 + }, + { + "id": "lambda-cicd-github-actions", + "title": "Deploy AWS Lambda Functions with GitHub Actions", + "description": "Build a GitHub Actions pipeline that packages, tests, and deploys AWS Lambda functions automatically on every push to main or staging.", + "tags": [ + "aws", + "lambda", + "ci-cd", + "github-actions", + "deployment" + ], + "image": "/img/tutorials/lambda-cicd-github-actions/lambda-cicd-github-actions.png", + "sidebar_position": 23 + }, + { + "id": "deploy-retool-helm", + "title": "Deploy Retool Using Helm Charts on SleakOps", + "description": "Deploy Retool v6.3.6 on a SleakOps Kubernetes cluster using the official Helm chart with EBS, EFS, and an AMD64-dedicated NodePool.", + "tags": [ + "kubernetes", + "helm", + "deployment" + ], + "image": "/img/tutorials/deploy-retool-helm/deploy-retool-helm.png", + "sidebar_position": 24 + }, + { + "id": "lens-cluster-connectivity", + "title": "Troubleshoot Cluster Connectivity with Lens", + "description": "Diagnose and fix Lens connectivity issues to a SleakOps Kubernetes cluster — VPN checks, DNS configuration for Linux, macOS, and Windows.", + "tags": [ + "kubernetes", + "lens", + "vpn", + "networking", + "dns" + ], + "image": "/img/tutorials/lens-cluster-connectivity/lens-cluster-connectivity.png", + "sidebar_position": 25 + }, + { + "id": "pritunl-dns-universal", + "title": "Universal DNS Fix for Pritunl Client", + "description": "Fix Pritunl VPN DNS resolution on Linux, macOS, and Windows with OS-specific scripts and a universal cross-platform script.", + "tags": [ + "vpn", + "networking", + "dns", + "pritunl" + ], + "image": "/img/tutorials/pritunl-dns-universal/pritunl-dns-universal.png", + "sidebar_position": 26 + }, + { + "id": "workers-use-cases", + "title": "Worker Use Cases in SleakOps", + "description": "Explore common use cases for SleakOps Worker Workloads — background processing, scheduled tasks, autoscaling, ETL, pipelines, and third-party API integrations.", + "tags": [ + "kubernetes", + "background-jobs", + "scaling", + "deployment" + ], + "image": "/img/tutorials/workers-use-cases/workers-use-cases.png", + "sidebar_position": 27 + }, + { + "id": "aws-codeartifact-java", + "title": "Use AWS CodeArtifact with Java/Maven Projects", + "description": "Configure a Java Maven project to use AWS CodeArtifact as a private artifact repository inside Docker, with CI/CD pipeline examples for GitHub Actions, GitLab, and Bitbucket.", + "tags": [ + "aws", + "ci-cd", + "docker", + "deployment" + ], + "image": "/img/tutorials/aws-codeartifact-java/aws-codeartifact-java.png", + "sidebar_position": 28 + }, + { + "id": "networking-vpc", + "title": "Networking and Network Resources in SleakOps", + "description": "Understand how SleakOps structures network architecture — VPCs, subnets, Security Groups, External-DNS, and VPC Peering between environments.", + "tags": [ + "networking", + "aws", + "kubernetes", + "vpn" + ], + "image": "/img/tutorials/networking-vpc/networking-vpc.png", + "sidebar_position": 29 + }, + { + "id": "aws-local-authentication", + "title": "Configure AWS Authentication for Local Development", + "description": "Set up AWS CLI credentials and cross-account role assumption on your local machine to access SleakOps AWS resources across development, management, and production accounts.", + "tags": [ + "aws", + "security" + ], + "image": "/img/tutorials/aws-local-authentication/aws-local-authentication.png", + "sidebar_position": 30 + }, + { + "id": "connect-aws-resources", + "title": "Connect to AWS Resources from Your Application", + "description": "Learn how to authenticate to AWS and connect to resources like S3 from local development or CI/CD pipelines — IAM user creation, access key management, and Node.js SDK examples.", + "tags": [ + "aws", + "security", + "node" + ], + "image": "/img/tutorials/connect-aws-resources/connect-aws-resources.png", + "sidebar_position": 31 + }, + { + "id": "test-site-to-site-vpn", + "title": "Test a Site-to-Site VPN Created with SleakOps", + "description": "Verify connectivity through a SleakOps site-to-site VPN using Nmap from a Kubernetes pod — step-by-step with expected results and a packet flow diagram.", + "tags": [ + "vpn", + "networking", + "kubernetes" + ], + "image": "/img/tutorials/test-site-to-site-vpn/test-site-to-site-vpn.png", + "sidebar_position": 32 + }, + { + "id": "deploy-datadog-operator", + "title": "Deploy Datadog Operator and DatadogAgent on SleakOps", + "description": "Install the Datadog Operator (v2.8.0) and deploy a DatadogAgent (v7.63.3) on a SleakOps EKS cluster with Karpenter NodePool tolerations, APM, log collection, and admission controller.", + "tags": [ + "kubernetes", + "monitoring", + "deployment", + "helm" + ], + "image": "/img/tutorials/deploy-datadog-operator/deploy-datadog-operator.png", + "sidebar_position": 33 + }, + { + "id": "install-datadog", + "title": "Install Datadog on a SleakOps EKS Cluster", + "description": "Install the Datadog monitoring agent on an Amazon EKS cluster managed by SleakOps using Helm, with a values.yaml and recommended best practices.", + "tags": [ + "kubernetes", + "monitoring", + "helm", + "aws" + ], + "image": "/img/tutorials/install-datadog/install-datadog.png", + "sidebar_position": 34 + }, + { + "id": "install-new-relic", + "title": "Install New Relic on Your Application", + "description": "Install and configure the New Relic monitoring agent for your application — account setup, language-specific agent installation, and configuration best practices.", + "tags": [ + "monitoring", + "deployment", + "node", + "python" + ], + "image": "/img/tutorials/install-new-relic/install-new-relic.png", + "sidebar_position": 35 + }, + { + "id": "optimize-aws-costs", + "title": "AWS Cost Optimization Strategies", + "description": "Practical AWS cost optimization techniques — Spot Instances, S3 Intelligent-Tiering, Graviton processors, Savings Plans, CloudFront, and Compute Optimizer.", + "tags": [ + "aws", + "cost-optimization" + ], + "image": "/img/tutorials/optimize-aws-costs/optimize-aws-costs.png", + "sidebar_position": 36 + }, + { + "id": "bitnami-image-deprecated", + "title": "Bitnami Image Deprecation — What to Do", + "description": "Understand the Bitnami image deprecation effective August 28, 2025, and how to update your SleakOps chart dependencies to avoid ImagePullBackOff errors.", + "tags": [ + "kubernetes", + "helm", + "deployment" + ], + "image": "/img/tutorials/bitnami-image-deprecated/bitnami-image-deprecated.png", + "sidebar_position": 37 + }, + { + "id": "optimize-docker-image", + "title": "How to Optimize Your Docker Image Size", + "description": "Reduce Docker image size and improve build times using Alpine base images, multi-stage builds, and .dockerignore — practical techniques for production-ready images.", + "tags": [ + "docker", + "deployment", + "performance" + ], + "image": "/img/tutorials/optimize-docker-image/optimize-docker-image.png", + "sidebar_position": 42 + "id": "nodepool-strategies", + "title": "Nodepool Strategies in SleakOps", + "description": "Learn how to configure nodepools in SleakOps to optimize costs, improve performance, and choose the right instance types for each workload.", + "tags": [ + "kubernetes", + "aws", + "cost-optimization", + "scaling", + "performance" + ], + "image": "/img/tutorials/nodepool-strategies/nodepool-strategies.png", + "sidebar_position": 47 } ], "es": [ @@ -351,6 +718,373 @@ ], "image": "/img/tutorials/openvpn-profile/openvpn-profile.png", "sidebar_position": 11 + }, + { + "id": "migrate-rds-snapshot-between-accounts", + "title": "Migrar un snapshot de Amazon RDS entre cuentas", + "description": "Guía paso a paso para compartir y restaurar un snapshot de Amazon RDS entre cuentas de AWS, incluyendo escenarios con cifrado y cross-region.", + "tags": [ + "aws", + "rds", + "database", + "migration", + "snapshot", + "kms" + ], + "image": "/img/tutorials/migrate-rds-snapshot-between-accounts/migrate-rds-snapshot-between-accounts.png", + "sidebar_position": 12 + }, + { + "id": "postgresql-dump-restore", + "title": "Restaurar un dump de PostgreSQL usando un Job de SleakOps", + "description": "Guía paso a paso para hacer dump y restaurar una base de datos PostgreSQL usando un Job temporal de SleakOps y una instancia RDS escalada.", + "tags": [ + "aws", + "rds", + "postgresql", + "database", + "migration", + "dump" + ], + "image": "/img/tutorials/postgresql-dump-restore/postgresql-dump-restore.png", + "sidebar_position": 13 + }, + { + "id": "migrate-postgres-heroku-to-rds", + "title": "Migrar una base de datos PostgreSQL grande desde Heroku a RDS", + "description": "Migrá bases de datos PostgreSQL grandes (20 GB+) desde Heroku a Amazon RDS usando pgcopydb y un Job de SleakOps, con soporte para migración incremental posterior.", + "tags": [ + "aws", + "rds", + "postgresql", + "heroku", + "migration", + "database" + ], + "image": "/img/tutorials/migrate-postgres-heroku-to-rds/migrate-postgres-heroku-to-rds.png", + "sidebar_position": 14 + }, + { + "id": "migrate-ebs-volumes", + "title": "Migrar volúmenes EBS a una nueva cuenta de AWS", + "description": "Guía paso a paso para crear snapshots, compartir, copiar y recrear volúmenes EBS desde una cuenta externa de AWS a una cuenta administrada por SleakOps.", + "tags": [ + "aws", + "kubernetes", + "storage", + "migration", + "ebs" + ], + "image": "/img/tutorials/migrate-ebs-volumes/migrate-ebs-volumes.png", + "sidebar_position": 15 + }, + { + "id": "postgres-helm-existing-volume", + "title": "Desplegar un Helm Chart de PostgreSQL con un volumen EBS existente", + "description": "Desplegá el Helm chart de Bitnami PostgreSQL en SleakOps apuntando a un volumen EBS pre-existente creando recursos PV y PVC con el node affinity correcto.", + "tags": [ + "kubernetes", + "helm", + "postgresql", + "storage", + "ebs" + ], + "image": "/img/tutorials/postgres-helm-existing-volume/postgres-helm-existing-volume.png", + "sidebar_position": 16 + }, + { + "id": "migrate-external-s3", + "title": "Migrar un bucket S3 externo a SleakOps", + "description": "Copiá objetos desde un bucket S3 en una cuenta AWS externa a un bucket S3 administrado por SleakOps usando políticas de bucket cross-account y aws s3 sync.", + "tags": [ + "aws", + "s3", + "migration", + "storage" + ], + "image": "/img/tutorials/migrate-external-s3/migrate-external-s3.png", + "sidebar_position": 17 + }, + { + "id": "migrate-files-volumes-copy", + "title": "Migrar archivos entre volúmenes de Kubernetes con kubectl cp", + "description": "Copiá archivos almacenados en Persistent Volumes de Kubernetes de un cluster a otro usando kubectl cp, sin necesitar dumps de base de datos ni herramientas de snapshot.", + "tags": [ + "kubernetes", + "migration", + "storage", + "kubectl" + ], + "image": "/img/tutorials/migrate-files-volumes-copy/migrate-files-volumes-copy.png", + "sidebar_position": 18 + }, + { + "id": "dms-rds-migration", + "title": "Usar AWS DMS para sincronizar o migrar bases de datos RDS", + "description": "Guía paso a paso para migrar o sincronizar continuamente bases de datos RDS entre cuentas de AWS usando AWS Database Migration Service (DMS).", + "tags": [ + "aws", + "rds", + "database", + "dms", + "migration" + ], + "image": "/img/tutorials/dms-rds-migration/dms-rds-migration.png", + "sidebar_position": 19 + }, + { + "id": "amazon-ses", + "title": "Comenzar con Amazon SES", + "description": "Configurá Amazon SES para enviar emails desde tu aplicación — verificá una identidad, probá en modo sandbox y solicitá acceso a producción.", + "tags": [ + "aws", + "ses", + "email" + ], + "image": "/img/tutorials/amazon-ses/amazon-ses.png", + "sidebar_position": 20 + }, + { + "id": "rds-external-access", + "title": "Acceder a una instancia RDS desde fuera de tu VPC", + "description": "Opciones para acceder de forma segura a una instancia de Amazon RDS en una subnet privada desde fuera de la VPC — Bastion Host, AWS Client VPN, PrivateLink y acceso público.", + "tags": [ + "aws", + "rds", + "database", + "networking", + "security", + "vpn" + ], + "image": "/img/tutorials/rds-external-access/rds-external-access.png", + "sidebar_position": 21 + }, + { + "id": "transfer-domain-route53", + "title": "Transferir un dominio a Amazon Route 53", + "description": "Checklist y guía paso a paso para transferir un dominio desde tu registrador actual a Amazon Route 53 sin tiempo de inactividad.", + "tags": [ + "aws", + "route53", + "dns", + "domain", + "networking" + ], + "image": "/img/tutorials/transfer-domain-route53/transfer-domain-route53.png", + "sidebar_position": 22 + }, + { + "id": "lambda-cicd-github-actions", + "title": "Desplegar funciones AWS Lambda con GitHub Actions", + "description": "Construí un pipeline de GitHub Actions que empaqueta, testea y despliega funciones AWS Lambda automáticamente en cada push a main o staging.", + "tags": [ + "aws", + "lambda", + "ci-cd", + "github-actions", + "deployment" + ], + "image": "/img/tutorials/lambda-cicd-github-actions/lambda-cicd-github-actions.png", + "sidebar_position": 23 + }, + { + "id": "deploy-retool-helm", + "title": "Desplegar Retool con Helm Charts en SleakOps", + "description": "Desplegá Retool v6.3.6 en un cluster de Kubernetes de SleakOps usando el Helm chart oficial con EBS, EFS y un NodePool dedicado para AMD64.", + "tags": [ + "kubernetes", + "helm", + "deployment" + ], + "image": "/img/tutorials/deploy-retool-helm/deploy-retool-helm.png", + "sidebar_position": 24 + }, + { + "id": "lens-cluster-connectivity", + "title": "Solucionar problemas de conectividad al clúster con Lens", + "description": "Diagnosticá y resolvé problemas de conexión entre Lens y un cluster de Kubernetes de SleakOps — verificación de VPN, configuración de DNS en Linux, macOS y Windows.", + "tags": [ + "kubernetes", + "lens", + "vpn", + "networking", + "dns" + ], + "image": "/img/tutorials/lens-cluster-connectivity/lens-cluster-connectivity.png", + "sidebar_position": 25 + }, + { + "id": "pritunl-dns-universal", + "title": "Solución Universal de DNS para Pritunl Client", + "description": "Corregí la resolución DNS de la VPN Pritunl en Linux, macOS y Windows con scripts por OS y un script universal multiplataforma.", + "tags": [ + "vpn", + "networking", + "dns", + "pritunl" + ], + "image": "/img/tutorials/pritunl-dns-universal/pritunl-dns-universal.png", + "sidebar_position": 26 + }, + { + "id": "workers-use-cases", + "title": "Casos de Uso para Workers en SleakOps", + "description": "Explorá los casos de uso más comunes para Workloads Worker en SleakOps — procesamiento en segundo plano, tareas programadas, autoescalado, ETL, pipelines e integraciones con APIs externas.", + "tags": [ + "kubernetes", + "background-jobs", + "scaling", + "deployment" + ], + "image": "/img/tutorials/workers-use-cases/workers-use-cases.png", + "sidebar_position": 27 + }, + { + "id": "aws-codeartifact-java", + "title": "Usar AWS CodeArtifact con proyectos Java/Maven", + "description": "Configurá un proyecto Java Maven para usar AWS CodeArtifact como repositorio privado de artefactos dentro de Docker, con ejemplos de pipelines CI/CD para GitHub Actions, GitLab y Bitbucket.", + "tags": [ + "aws", + "ci-cd", + "docker", + "deployment" + ], + "image": "/img/tutorials/aws-codeartifact-java/aws-codeartifact-java.png", + "sidebar_position": 28 + }, + { + "id": "networking-vpc", + "title": "Networking y Recursos de Red en SleakOps", + "description": "Entendé cómo SleakOps estructura la arquitectura de red — VPCs, subredes, Security Groups, External-DNS y VPC Peering entre entornos.", + "tags": [ + "networking", + "aws", + "kubernetes", + "vpn" + ], + "image": "/img/tutorials/networking-vpc/networking-vpc.png", + "sidebar_position": 29 + }, + { + "id": "aws-local-authentication", + "title": "Configurar autenticación de AWS para desarrollo local", + "description": "Configurá las credenciales de AWS CLI y la asunción de roles cross-account en tu máquina local para acceder a recursos AWS de SleakOps en las cuentas de desarrollo, management y producción.", + "tags": [ + "aws", + "security" + ], + "image": "/img/tutorials/aws-local-authentication/aws-local-authentication.png", + "sidebar_position": 30 + }, + { + "id": "connect-aws-resources", + "title": "Conectarse a recursos de AWS desde tu aplicación", + "description": "Aprendé cómo autenticarte en AWS y conectarte a recursos como S3 desde desarrollo local o pipelines CI/CD — creación de usuario IAM, gestión de access keys y ejemplos con el SDK de Node.js.", + "tags": [ + "aws", + "security", + "node" + ], + "image": "/img/tutorials/connect-aws-resources/connect-aws-resources.png", + "sidebar_position": 31 + }, + { + "id": "test-site-to-site-vpn", + "title": "Probar una VPN Site to Site creada con SleakOps", + "description": "Verificá la conectividad a través de una VPN site-to-site de SleakOps usando Nmap desde un pod de Kubernetes — paso a paso con resultados esperados y diagrama de flujo de paquetes.", + "tags": [ + "vpn", + "networking", + "kubernetes" + ], + "image": "/img/tutorials/test-site-to-site-vpn/test-site-to-site-vpn.png", + "sidebar_position": 32 + }, + { + "id": "deploy-datadog-operator", + "title": "Desplegar Datadog Operator y DatadogAgent en SleakOps", + "description": "Instalá el Datadog Operator (v2.8.0) y desplegá un DatadogAgent (v7.63.3) en un cluster EKS de SleakOps con toleraciones de NodePool Karpenter, APM, recolección de logs y admission controller.", + "tags": [ + "kubernetes", + "monitoring", + "deployment", + "helm" + ], + "image": "/img/tutorials/deploy-datadog-operator/deploy-datadog-operator.png", + "sidebar_position": 33 + }, + { + "id": "install-datadog", + "title": "Instalar Datadog en un Cluster EKS de SleakOps", + "description": "Instalá el agente de monitoreo Datadog en un cluster Amazon EKS gestionado por SleakOps usando Helm, con un values.yaml y buenas prácticas recomendadas.", + "tags": [ + "kubernetes", + "monitoring", + "helm", + "aws" + ], + "image": "/img/tutorials/install-datadog/install-datadog.png", + "sidebar_position": 34 + }, + { + "id": "install-new-relic", + "title": "Instalar New Relic en tu Aplicación", + "description": "Instalá y configurá el agente de monitoreo New Relic en tu aplicación — configuración de cuenta, instalación del agente por lenguaje y buenas prácticas de configuración.", + "tags": [ + "monitoring", + "deployment", + "node", + "python" + ], + "image": "/img/tutorials/install-new-relic/install-new-relic.png", + "sidebar_position": 35 + }, + { + "id": "optimize-aws-costs", + "title": "Estrategias de Optimización de Costos en AWS", + "description": "Técnicas prácticas de optimización de costos en AWS — Spot Instances, S3 Intelligent-Tiering, procesadores Graviton, Savings Plans, CloudFront y Compute Optimizer.", + "tags": [ + "aws", + "cost-optimization" + ], + "image": "/img/tutorials/optimize-aws-costs/optimize-aws-costs.png", + "sidebar_position": 36 + }, + { + "id": "bitnami-image-deprecated", + "title": "Deprecación de Imágenes Bitnami — Qué Hacer", + "description": "Entendé la deprecación de imágenes Bitnami efectiva el 28 de agosto de 2025, y cómo actualizar las dependencias de tus charts en SleakOps para evitar errores ImagePullBackOff.", + "tags": [ + "kubernetes", + "helm", + "deployment" + ], + "image": "/img/tutorials/bitnami-image-deprecated/bitnami-image-deprecated.png", + "sidebar_position": 37 + }, + { + "id": "optimize-docker-image", + "title": "Cómo Optimizar el Tamaño de tu Imagen Docker", + "description": "Reducí el tamaño de las imágenes Docker y mejorá los tiempos de build usando imágenes base Alpine, builds multi-stage y .dockerignore — técnicas prácticas para imágenes listas para producción.", + "tags": [ + "docker", + "deployment", + "performance" + ], + "image": "/img/tutorials/optimize-docker-image/optimize-docker-image.png", + "sidebar_position": 42 + "id": "nodepool-strategies", + "title": "Estrategias de Nodepools en SleakOps", + "description": "Aprendé a configurar nodepools en SleakOps para optimizar costos, mejorar el rendimiento y elegir los tipos de instancia correctos para cada workload.", + "tags": [ + "kubernetes", + "aws", + "cost-optimization", + "scaling", + "performance" + ], + "image": "/img/tutorials/nodepool-strategies/nodepool-strategies.png", + "sidebar_position": 47 } ] -} \ No newline at end of file +} diff --git a/static/img/tutorials/amazon-ses/amazon-ses.png b/static/img/tutorials/amazon-ses/amazon-ses.png new file mode 100644 index 000000000..65245894d Binary files /dev/null and b/static/img/tutorials/amazon-ses/amazon-ses.png differ diff --git a/static/img/tutorials/aws-codeartifact-java/aws-codeartifact-java.png b/static/img/tutorials/aws-codeartifact-java/aws-codeartifact-java.png new file mode 100644 index 000000000..6f08f46db Binary files /dev/null and b/static/img/tutorials/aws-codeartifact-java/aws-codeartifact-java.png differ diff --git a/static/img/tutorials/aws-local-authentication/aws-local-authentication.png b/static/img/tutorials/aws-local-authentication/aws-local-authentication.png new file mode 100644 index 000000000..d58aaafca Binary files /dev/null and b/static/img/tutorials/aws-local-authentication/aws-local-authentication.png differ diff --git a/static/img/tutorials/bitnami-image-deprecated/apply-changes.png b/static/img/tutorials/bitnami-image-deprecated/apply-changes.png new file mode 100644 index 000000000..447b6ea87 Binary files /dev/null and b/static/img/tutorials/bitnami-image-deprecated/apply-changes.png differ diff --git a/static/img/tutorials/bitnami-image-deprecated/bitnami-image-deprecated.png b/static/img/tutorials/bitnami-image-deprecated/bitnami-image-deprecated.png new file mode 100644 index 000000000..af471b257 Binary files /dev/null and b/static/img/tutorials/bitnami-image-deprecated/bitnami-image-deprecated.png differ diff --git a/static/img/tutorials/bitnami-image-deprecated/chart-configuration.png b/static/img/tutorials/bitnami-image-deprecated/chart-configuration.png new file mode 100644 index 000000000..11a3a58e3 Binary files /dev/null and b/static/img/tutorials/bitnami-image-deprecated/chart-configuration.png differ diff --git a/static/img/tutorials/connect-aws-resources/connect-aws-resources.png b/static/img/tutorials/connect-aws-resources/connect-aws-resources.png new file mode 100644 index 000000000..21a53c924 Binary files /dev/null and b/static/img/tutorials/connect-aws-resources/connect-aws-resources.png differ diff --git a/static/img/tutorials/deploy-datadog-operator/deploy-datadog-operator.png b/static/img/tutorials/deploy-datadog-operator/deploy-datadog-operator.png new file mode 100644 index 000000000..c09d6eefc Binary files /dev/null and b/static/img/tutorials/deploy-datadog-operator/deploy-datadog-operator.png differ diff --git a/static/img/tutorials/deploy-retool-helm/deploy-retool-helm.png b/static/img/tutorials/deploy-retool-helm/deploy-retool-helm.png new file mode 100644 index 000000000..d61a18eb7 Binary files /dev/null and b/static/img/tutorials/deploy-retool-helm/deploy-retool-helm.png differ diff --git a/static/img/tutorials/dms-rds-migration/dms-rds-migration.png b/static/img/tutorials/dms-rds-migration/dms-rds-migration.png new file mode 100644 index 000000000..f32942428 Binary files /dev/null and b/static/img/tutorials/dms-rds-migration/dms-rds-migration.png differ diff --git a/static/img/tutorials/install-datadog/install-datadog.png b/static/img/tutorials/install-datadog/install-datadog.png new file mode 100644 index 000000000..7b9b05bb7 Binary files /dev/null and b/static/img/tutorials/install-datadog/install-datadog.png differ diff --git a/static/img/tutorials/install-new-relic/install-new-relic.png b/static/img/tutorials/install-new-relic/install-new-relic.png new file mode 100644 index 000000000..ea7e6fe2f Binary files /dev/null and b/static/img/tutorials/install-new-relic/install-new-relic.png differ diff --git a/static/img/tutorials/lambda-cicd-github-actions/lambda-cicd-github-actions.png b/static/img/tutorials/lambda-cicd-github-actions/lambda-cicd-github-actions.png new file mode 100644 index 000000000..bbcbfe65f Binary files /dev/null and b/static/img/tutorials/lambda-cicd-github-actions/lambda-cicd-github-actions.png differ diff --git a/static/img/tutorials/lens-cluster-connectivity/lens-cluster-connectivity.png b/static/img/tutorials/lens-cluster-connectivity/lens-cluster-connectivity.png new file mode 100644 index 000000000..e9568ea89 Binary files /dev/null and b/static/img/tutorials/lens-cluster-connectivity/lens-cluster-connectivity.png differ diff --git a/static/img/tutorials/migrate-ebs-volumes/migrate-ebs-volumes.png b/static/img/tutorials/migrate-ebs-volumes/migrate-ebs-volumes.png new file mode 100644 index 000000000..69c11e4ce Binary files /dev/null and b/static/img/tutorials/migrate-ebs-volumes/migrate-ebs-volumes.png differ diff --git a/static/img/tutorials/migrate-external-s3/migrate-external-s3.png b/static/img/tutorials/migrate-external-s3/migrate-external-s3.png new file mode 100644 index 000000000..a1291b273 Binary files /dev/null and b/static/img/tutorials/migrate-external-s3/migrate-external-s3.png differ diff --git a/static/img/tutorials/migrate-external-s3/pod-shell.png b/static/img/tutorials/migrate-external-s3/pod-shell.png new file mode 100644 index 000000000..7b17b8646 Binary files /dev/null and b/static/img/tutorials/migrate-external-s3/pod-shell.png differ diff --git a/static/img/tutorials/migrate-files-volumes-copy/migrate-files-volumes-copy.png b/static/img/tutorials/migrate-files-volumes-copy/migrate-files-volumes-copy.png new file mode 100644 index 000000000..2a4a0a043 Binary files /dev/null and b/static/img/tutorials/migrate-files-volumes-copy/migrate-files-volumes-copy.png differ diff --git a/static/img/tutorials/migrate-postgres-heroku-to-rds/migrate-postgres-heroku-to-rds.png b/static/img/tutorials/migrate-postgres-heroku-to-rds/migrate-postgres-heroku-to-rds.png new file mode 100644 index 000000000..8abcd8f94 Binary files /dev/null and b/static/img/tutorials/migrate-postgres-heroku-to-rds/migrate-postgres-heroku-to-rds.png differ diff --git a/static/img/tutorials/migrate-rds-snapshot-between-accounts/migrate-rds-snapshot-between-accounts.png b/static/img/tutorials/migrate-rds-snapshot-between-accounts/migrate-rds-snapshot-between-accounts.png new file mode 100644 index 000000000..68de92762 Binary files /dev/null and b/static/img/tutorials/migrate-rds-snapshot-between-accounts/migrate-rds-snapshot-between-accounts.png differ diff --git a/static/img/tutorials/networking-vpc/network-architecture.png b/static/img/tutorials/networking-vpc/network-architecture.png new file mode 100644 index 000000000..5c5f8abe1 Binary files /dev/null and b/static/img/tutorials/networking-vpc/network-architecture.png differ diff --git a/static/img/tutorials/networking-vpc/networking-vpc.png b/static/img/tutorials/networking-vpc/networking-vpc.png new file mode 100644 index 000000000..15e80ac11 Binary files /dev/null and b/static/img/tutorials/networking-vpc/networking-vpc.png differ diff --git a/static/img/tutorials/networking-vpc/vpc-peering.svg b/static/img/tutorials/networking-vpc/vpc-peering.svg new file mode 100644 index 000000000..2b9b63438 --- /dev/null +++ b/static/img/tutorials/networking-vpc/vpc-peering.svg @@ -0,0 +1 @@ +

VPC Development (10.110.0.0/16)

VPC Production (10.130.0.0/16)

VPC Management (10.120.0.0/16)

Subred Pública

Subred Privada

Subred Persistencia

DNS Interno (10.110.0.10)

Subred Pública

Subred Privada

Subred Persistencia

DNS Interno (10.130.0.10)

Subred Pública

Subred Privada

Subred Persistencia

DNS Interno (10.120.0.10)

🌍 Usuario desde Internet

Internet Gateway

Security Groups (Control de tráfico)

Route53 / External-DNS

HTTP/HTTPS

Ruta

Ruta

Ruta

\ No newline at end of file diff --git a/static/img/tutorials/nodepool-strategies/nodepool-strategies.png b/static/img/tutorials/nodepool-strategies/nodepool-strategies.png new file mode 100644 index 000000000..8c98ddb0e Binary files /dev/null and b/static/img/tutorials/nodepool-strategies/nodepool-strategies.png differ diff --git a/static/img/tutorials/openvpn-profile/openvpn-profile.png b/static/img/tutorials/openvpn-profile/openvpn-profile.png new file mode 100644 index 000000000..f9cb4e12a Binary files /dev/null and b/static/img/tutorials/openvpn-profile/openvpn-profile.png differ diff --git a/static/img/tutorials/optimize-aws-costs/optimize-aws-costs.png b/static/img/tutorials/optimize-aws-costs/optimize-aws-costs.png new file mode 100644 index 000000000..4efae6ecf Binary files /dev/null and b/static/img/tutorials/optimize-aws-costs/optimize-aws-costs.png differ diff --git a/static/img/tutorials/optimize-docker-image/optimize-docker-image.png b/static/img/tutorials/optimize-docker-image/optimize-docker-image.png new file mode 100644 index 000000000..36fef49cf Binary files /dev/null and b/static/img/tutorials/optimize-docker-image/optimize-docker-image.png differ diff --git a/static/img/tutorials/postgres-helm-existing-volume/postgres-helm-existing-volume.png b/static/img/tutorials/postgres-helm-existing-volume/postgres-helm-existing-volume.png new file mode 100644 index 000000000..c67358ff6 Binary files /dev/null and b/static/img/tutorials/postgres-helm-existing-volume/postgres-helm-existing-volume.png differ diff --git a/static/img/tutorials/postgresql-dump-restore/job-creation-1.png b/static/img/tutorials/postgresql-dump-restore/job-creation-1.png new file mode 100644 index 000000000..249dd7443 Binary files /dev/null and b/static/img/tutorials/postgresql-dump-restore/job-creation-1.png differ diff --git a/static/img/tutorials/postgresql-dump-restore/job-creation-2.png b/static/img/tutorials/postgresql-dump-restore/job-creation-2.png new file mode 100644 index 000000000..563d30e82 Binary files /dev/null and b/static/img/tutorials/postgresql-dump-restore/job-creation-2.png differ diff --git a/static/img/tutorials/postgresql-dump-restore/postgresql-dump-restore.png b/static/img/tutorials/postgresql-dump-restore/postgresql-dump-restore.png new file mode 100644 index 000000000..4c165d331 Binary files /dev/null and b/static/img/tutorials/postgresql-dump-restore/postgresql-dump-restore.png differ diff --git a/static/img/tutorials/pritunl-dns-universal/pritunl-dns-universal.png b/static/img/tutorials/pritunl-dns-universal/pritunl-dns-universal.png new file mode 100644 index 000000000..b0c7dacbd Binary files /dev/null and b/static/img/tutorials/pritunl-dns-universal/pritunl-dns-universal.png differ diff --git a/static/img/tutorials/rds-external-access/rds-external-access.png b/static/img/tutorials/rds-external-access/rds-external-access.png new file mode 100644 index 000000000..c3461ab3d Binary files /dev/null and b/static/img/tutorials/rds-external-access/rds-external-access.png differ diff --git a/static/img/tutorials/test-site-to-site-vpn/test-site-to-site-vpn.png b/static/img/tutorials/test-site-to-site-vpn/test-site-to-site-vpn.png new file mode 100644 index 000000000..dba0045fc Binary files /dev/null and b/static/img/tutorials/test-site-to-site-vpn/test-site-to-site-vpn.png differ diff --git a/static/img/tutorials/transfer-domain-route53/transfer-domain-route53.png b/static/img/tutorials/transfer-domain-route53/transfer-domain-route53.png new file mode 100644 index 000000000..bfd52f3c3 Binary files /dev/null and b/static/img/tutorials/transfer-domain-route53/transfer-domain-route53.png differ diff --git a/static/img/tutorials/workers-use-cases/workers-use-cases.png b/static/img/tutorials/workers-use-cases/workers-use-cases.png new file mode 100644 index 000000000..b25a6d3d2 Binary files /dev/null and b/static/img/tutorials/workers-use-cases/workers-use-cases.png differ