diff --git a/.github/workflows/ci-cd-pipeline.yml b/.github/workflows/ci-cd-pipeline.yml new file mode 100644 index 00000000..67f9a0d2 --- /dev/null +++ b/.github/workflows/ci-cd-pipeline.yml @@ -0,0 +1,58 @@ +name: CI/CD Pipeline + +on: + push: + branches: [ latin ] + workflow_dispatch: + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + permissions: + id-token: write # For AWS OIDC + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and Push Vote + run: | + cd vote + docker build -t ${{ secrets.DOCKER_USERNAME }}/vote:latest . + docker push ${{ secrets.DOCKER_USERNAME }}/vote:latest + + - name: Build and Push Result + run: | + cd result + docker build -t ${{ secrets.DOCKER_USERNAME }}/result:latest . + docker push ${{ secrets.DOCKER_USERNAME }}/result:latest + + - name: Build and Push Worker + run: | + cd worker + docker build -t ${{ secrets.DOCKER_USERNAME }}/worker:latest . + docker push ${{ secrets.DOCKER_USERNAME }}/worker:latest + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::686699774218:role/github-actions-eks-role + aws-region: ${{ secrets.AWS_REGION }} + + - name: Install eksctl + run: | + curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp + sudo mv /tmp/eksctl /usr/local/bin + + - name: Update Kubeconfig + run: eksctl utils write-kubeconfig --cluster voting-app-cluster --region ${{ secrets.AWS_REGION }} + + - name: Deploy Kubernetes Manifests + run: kubectl apply -R -f k8s/ # Asumiendo YAMLs están en carpeta k8s/ \ No newline at end of file diff --git a/infra/eks/cluster.yaml b/infra/eks/cluster.yaml new file mode 100644 index 00000000..1e77d7db --- /dev/null +++ b/infra/eks/cluster.yaml @@ -0,0 +1,48 @@ +apiVersion: eksctl.io/v1alpha5 +kind: ClusterConfig + +metadata: + name: voting-app-cluster + region: sa-east-1 # cambia si prefieres otra región (ej: eu-west-1) + version: "1.31" # o la versión más reciente soportada + +managedNodeGroups: + - name: standard-workers + instanceType: t2.small + minSize: 1 + maxSize: 3 + desiredCapacity: 2 + volumeSize: 30 + volumeType: gp3 + ssh: + allow: false + iam: + withAddonPolicies: + autoScaler: true + externalDNS: true + certManager: true + + - name: standard-workers-medium + instanceType: t3.medium + minSize: 1 + maxSize: 3 + desiredCapacity: 2 + volumeSize: 30 + volumeType: gp3 + ssh: + allow: false + iam: + withAddonPolicies: + autoScaler: true + externalDNS: true + certManager: true + +addons: + - name: vpc-cni + - name: coredns + - name: kube-proxy + - name: aws-ebs-csi-driver + - name: eks-pod-identity-agent + +addonsConfig: + autoApplyPodIdentityAssociations: true \ No newline at end of file diff --git a/ironhack-project-2-latin-team.sln b/ironhack-project-2-latin-team.sln new file mode 100644 index 00000000..e85edc8e --- /dev/null +++ b/ironhack-project-2-latin-team.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worker", "worker\Worker.csproj", "{69C79C25-F84F-479E-3E79-EC68C71F7F70}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {69C79C25-F84F-479E-3E79-EC68C71F7F70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69C79C25-F84F-479E-3E79-EC68C71F7F70}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69C79C25-F84F-479E-3E79-EC68C71F7F70}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69C79C25-F84F-479E-3E79-EC68C71F7F70}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ACB31A12-1815-4DB8-BAB2-C86F254A0117} + EndGlobalSection +EndGlobal diff --git a/k8s/cert-manager/clusterissuer.yaml b/k8s/cert-manager/clusterissuer.yaml new file mode 100644 index 00000000..5590560b --- /dev/null +++ b/k8s/cert-manager/clusterissuer.yaml @@ -0,0 +1,22 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: de.gerardogmilian@gmail.com + privateKeySecretRef: + name: letsencrypt-prod + preferredChain: "ISRG Root X1" + solvers: + - dns01: + route53: + region: sa-east-1 + accessKeyID: AKIAZ7YT7AEFB5WDTS7V + secretAccessKeySecretRef: + name: route53-secret + key: secret-access-key + selector: + dnsZones: + - "app-vote-latin.com" \ No newline at end of file diff --git a/k8s/ingress/ingress.yaml b/k8s/ingress/ingress.yaml new file mode 100644 index 00000000..4778dd8e --- /dev/null +++ b/k8s/ingress/ingress.yaml @@ -0,0 +1,47 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: voting-ingress + annotations: + kubernetes.io/ingress.class: "nginx" + cert-manager.io/cluster-issuer: "letsencrypt-prod" +spec: + ingressClassName: nginx + tls: + - hosts: + - app-vote-latin.com + secretName: voting-tls-selfsigned + rules: + - host: app-vote-latin.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: vote-service + port: + number: 80 + - path: /result + pathType: Prefix + backend: + service: + name: result-service + port: + number: 80 + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: vote-service + port: + number: 80 + - path: /result + pathType: Prefix + backend: + service: + name: result-service + port: + number: 80 \ No newline at end of file diff --git a/k8s/postgres/postgres-deployment.yaml b/k8s/postgres/postgres-deployment.yaml new file mode 100644 index 00000000..e4a9d605 --- /dev/null +++ b/k8s/postgres/postgres-deployment.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:15-alpine + ports: + - containerPort: 5432 + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: db-credentials + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: db-credentials + key: POSTGRES_PASSWORD + - name: POSTGRES_DB + value: votes + volumeMounts: + - mountPath: /var/lib/postgresql/data + subPath: pgdata + name: postgres-data + volumes: + - name: postgres-data + persistentVolumeClaim: + claimName: postgres-pvc \ No newline at end of file diff --git a/k8s/postgres/postgres-pvc.yaml b/k8s/postgres/postgres-pvc.yaml new file mode 100644 index 00000000..85879b6d --- /dev/null +++ b/k8s/postgres/postgres-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: gp2 + resources: + requests: + storage: 5Gi # Ajusta a 5Gi si ya existe uno con ese tamaño (como en tu error anterior) + storageClassName: gp2 # O el StorageClass de tu cluster EKS (gp2 es común en AWS) diff --git a/k8s/postgres/postgres-service.yaml b/k8s/postgres/postgres-service.yaml new file mode 100644 index 00000000..04ac60c6 --- /dev/null +++ b/k8s/postgres/postgres-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: postgres-service +spec: + selector: + app: postgres + ports: + - protocol: TCP + port: 5432 + targetPort: 5432 + type: ClusterIP \ No newline at end of file diff --git a/k8s/redis/redis-deployment.yaml b/k8s/redis/redis-deployment.yaml new file mode 100644 index 00000000..4118e4c3 --- /dev/null +++ b/k8s/redis/redis-deployment.yaml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + securityContext: + fsGroup: 999 + containers: + - name: redis + image: redis:alpine + securityContext: + runAsUser: 999 + runAsGroup: 999 + ports: + - containerPort: 6379 + volumeMounts: + - mountPath: /data + name: redis-data + volumes: + - name: redis-data + persistentVolumeClaim: + claimName: redis-pvc \ No newline at end of file diff --git a/k8s/redis/redis-pvc.yaml b/k8s/redis/redis-pvc.yaml new file mode 100644 index 00000000..c590442b --- /dev/null +++ b/k8s/redis/redis-pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: redis-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: gp2 + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/k8s/redis/redis-service.yaml b/k8s/redis/redis-service.yaml new file mode 100644 index 00000000..5d7dbf99 --- /dev/null +++ b/k8s/redis/redis-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: redis-service +spec: + selector: + app: redis + ports: + - protocol: TCP + port: 6379 + targetPort: 6379 + type: ClusterIP \ No newline at end of file diff --git a/k8s/result/result-deployment.yaml b/k8s/result/result-deployment.yaml new file mode 100644 index 00000000..81804633 --- /dev/null +++ b/k8s/result/result-deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: result-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: result + template: + metadata: + labels: + app: result + spec: + containers: + - name: result + image: cesarboada/result:latest # Use your Docker Hub image + ports: + - containerPort: 80 + env: + - name: PG_HOST + value: postgres-service + - name: PG_PORT + value: "5432" + - name: PG_USER + valueFrom: + secretKeyRef: + name: db-credentials + key: POSTGRES_USER + - name: PG_PASSWORD + valueFrom: + secretKeyRef: + name: db-credentials + key: POSTGRES_PASSWORD + - name: PG_DATABASE + value: votes \ No newline at end of file diff --git a/k8s/result/result-service.yaml b/k8s/result/result-service.yaml new file mode 100644 index 00000000..d02ef526 --- /dev/null +++ b/k8s/result/result-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: result-service +spec: + selector: + app: result + ports: + - protocol: TCP + port: 80 + targetPort: 80 + type: ClusterIP \ No newline at end of file diff --git a/k8s/vote/vote-deployment.yaml b/k8s/vote/vote-deployment.yaml new file mode 100644 index 00000000..6842422c --- /dev/null +++ b/k8s/vote/vote-deployment.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vote-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: vote + template: + metadata: + labels: + app: vote + spec: + containers: + - name: vote + image: cesarboada/vote:latest # Use your Docker Hub image + ports: + - containerPort: 80 + env: + - name: REDIS_HOST + value: redis-service \ No newline at end of file diff --git a/k8s/vote/vote-service.yaml b/k8s/vote/vote-service.yaml new file mode 100644 index 00000000..2b29c545 --- /dev/null +++ b/k8s/vote/vote-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: vote-service +spec: + selector: + app: vote + ports: + - protocol: TCP + port: 80 + targetPort: 80 + type: ClusterIP \ No newline at end of file diff --git a/k8s/worker/worker-deployment.yaml b/k8s/worker/worker-deployment.yaml new file mode 100644 index 00000000..4310783e --- /dev/null +++ b/k8s/worker/worker-deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: worker-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: worker + template: + metadata: + labels: + app: worker + spec: + containers: + - name: worker + image: cesarboada/worker:latest # Use your Docker Hub image + env: + - name: REDIS_HOST + value: redis-service + - name: REDIS_PORT + value: "6379" + - name: DB_HOST + value: postgres-service + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: db-credentials + key: POSTGRES_USER + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: db-credentials + key: POSTGRES_PASSWORD + - name: DB_NAME + value: votes \ No newline at end of file diff --git a/tls.crt b/tls.crt new file mode 100644 index 00000000..5fa0478f --- /dev/null +++ b/tls.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIUAKYbNV6k7tMeiP2nQFqF5ePsuWswDQYJKoZIhvcNAQEL +BQAwHTEbMBkGA1UEAwwSYXBwLXZvdGUtbGF0aW4uY29tMB4XDTI2MDIyMzE1MDYz +MVoXDTI3MDIyMzE1MDYzMVowHTEbMBkGA1UEAwwSYXBwLXZvdGUtbGF0aW4uY29t +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtgBEWVhD3tk2tQoClUEK +tSTXKzn32I+uRaVJxGuvPQ3W8+wna5ElN9dzu5g2OzLI6ewiFiH2t30VpeZAIGUl +xbSFm0vtZC/73p2AnJr8EUT7YLf+kX06BADXU73JMox1EJrSMLLfumT9mfQD4qw/ +TGYTuzfRoD+FDYMr/uGVcehccUJpGeVqMvyYw91IkpHPQxcHMUmARNtE/ubKWTf1 +7HatkBauqrTarsM+LJnt1QSCyUvacjZvmPWYFncshDwu6RhQ1Jf7IsIXzGBzjNyU +hZhuaGGhQSJTOiJflhcMJyfFNe1HUrfd7nga3gFSr0c0CPMA88+5ps2+m0dTud8B +VQIDAQABo1MwUTAdBgNVHQ4EFgQUvUKefVJ4YJm5FtHwzOFRKt6eZ3kwHwYDVR0j +BBgwFoAUvUKefVJ4YJm5FtHwzOFRKt6eZ3kwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAQEAHQWlIT2qY9T7DE+qX7toUHM+srYhTkBuWmP23eT22DZQ +/GxY3VOZaiQO6BeChhyvatBRBIqIuf/DpVtR8SUdhVprzxAAIaZwzE0LspFUzCnb +f45vX76a2kXd2qgC9G5t4HYIbngYdQiUeizttw9+6OGD3ptuV/jOCI6MsJmKBOqC +Z9MW0oTFzJyUsGgoNq5og/T1DVHEoqq4vIXpHX4zCnEhsT1dfvBny6jZseDMuHWz +2GOe6obSpIB2JS6pThQJgVxcZ4ou1U5FTY+izhhOb9ONwp2AMUjKvLhNQ0fLTkit +BPs2AzTFDVIJVnw9EWFW8vYuSVMmrZ++ZHkMTLGWXQ== +-----END CERTIFICATE----- diff --git a/tls.key b/tls.key new file mode 100644 index 00000000..d6acce1c --- /dev/null +++ b/tls.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC2AERZWEPe2Ta1 +CgKVQQq1JNcrOffYj65FpUnEa689Ddbz7CdrkSU313O7mDY7Msjp7CIWIfa3fRWl +5kAgZSXFtIWbS+1kL/venYCcmvwRRPtgt/6RfToEANdTvckyjHUQmtIwst+6ZP2Z +9APirD9MZhO7N9GgP4UNgyv+4ZVx6FxxQmkZ5Woy/JjD3UiSkc9DFwcxSYBE20T+ +5spZN/Xsdq2QFq6qtNquwz4sme3VBILJS9pyNm+Y9ZgWdyyEPC7pGFDUl/siwhfM +YHOM3JSFmG5oYaFBIlM6Il+WFwwnJ8U17UdSt93ueBreAVKvRzQI8wDzz7mmzb6b +R1O53wFVAgMBAAECggEAVMhcDqjU+R+D4iMXH/NMKyVNzNe0st//5XbBnewyFoV3 +cGq+1FepmFFYGvglrz8RpBL5sAjDVyfAaWeQ0OZ9iZN2N+lPXYAS21nQu2NhcCQL +3DrqLllBKPNBW8PJ0a/nqsQMjV4OZdS85LZtBu+JjyvwHiM7FmTwtOI5oQLyLLyx +TjZxJSKs2CQykltEUXBRBWh+dAF7dj2lEOZowuJ4BijKRC8ZDX8GyPrPZhwFZcdB +QPkEGAUJTTcfhDJuJS4WZ1T/to8qUL1zGYki6JKYK5tiN03jKIngXJ88iXINzCX/ +Jsg1YTG/Xe68noiDbgT95VnXw8itxrWWFJK+YYkNAwKBgQDroozGi1OfS01kSXE9 +aUmdzojQH5BWxxlGnC/gNxuudiZTJ96TLz4OFgvmOyP7/auAyhRvc6zbSDCzjZFR +5oSp+EaOg5XU9bxeZ4Vi/myWdtjvYxF0y+f1hfWxC6haACiXA8r0vZrtFGWE0y0B +aRLPlI+bTCczGVWVb2d2pJNXqwKBgQDFuw+DrWIGlcA/E6pUENiUn17zzVZWRoKY +BGAjOw8gpTgC2ydjoySNsrfiqZUTPvBVW5mAKJfMVfbmoN86CCd1Zi98WDQ7SFTH +O+zIiYcgITU4JooIY5flmLUijfMEw087koIDv6Ngzp4L26+YHyNXbvxx+M7N27Sn +hS4C1woK/wKBgQDp2u8/9PLO+xsZ1tGuExpepaWeeFT16q8pJsVVDxxH7zZl/QGm +rZI2xKaWnkc+Rim5rdTOL81z65d5+5wn3lH3plHQCsPfEOsb/j2hW8RFKDpwiq++ +bMgPIh4O0fUJLXMbPUZl0u85L675PeRCrBdhMJ29kLyuuAv6PRA/7WHV9QKBgDa2 +Oea/lP4pdZZ+hITaXHNTXdysFpWs0PnrTAazks3/qW3w4gdJ3xVuPD91mRBIPAwC +WokBsm8R4k2SefH+y5xKeB058snsi7jNNgmPNFqJlcZBs7P6is823/f9ux64RDZB +pcQPxM9GnKi/UukOxKKuzR9kLyT7BBPOYBrdEaRVAoGBAIHKctvTb+UrJg0cEyVN +M8Qa5Jf+mWJPHZYoYAdmBwr/5kj+slPUekzJ26Ylb5PH1zxPoh8Dm9P1Y+XGPyTg ++wPXa0W1W9UdMkGxGqfbvdw1UvAZOyz1Kw1zzmsRR3NrGLqMUpIDEx/g5rePLpgI +gkqo0rOBajFYDFoSHdUiQ9d+ +-----END PRIVATE KEY-----