Kubernetes project - Microservices

Kubernetes project - Microservices

EKS Cluster in AWS

Project Overview

This project focuses on creating and managing an Elastic Kubernetes Service (EKS) cluster in AWS, automating infrastructure setup, and configuring IAM roles and policies for secure and efficient cluster operations. It showcases the application of DevOps practices, Kubernetes orchestration, and cloud infrastructure management on AWS.

Project Objectives

  • Automate the setup of an EKS cluster on AWS.
  • Implement secure IAM roles and policies for access control.
  • Manage and configure Kubernetes resources within the EKS cluster.
  • Integrate CI/CD pipelines with Jenkins for automated deployment to the EKS cluster.

Technologies and Tools

  • AWS: EKS, IAM, EC2, CloudFormation
  • Kubernetes: kubectl, eksctl
  • CI/CD: Jenkins
  • Scripting: Bash
  • Version Control: Git
  • Security: IAM roles, Service Accounts

Launch EC2 Instance

  • Software Image (AMI) :Canonical, Ubuntu, 24.04, amd6.
  • ami-0e86e20dae9224db8
  • Virtual server type (instance type) : t2.large
  • Firewall (security group): launch-wizard-7
  • Storage (volumes): 1 volume(s) - 30 GiB

Setup EKS Cluster

Setting Up AWS IAM User and Policies

  • Create IAM User: Set up an IAM user with the necessary permissions.
  • Attach Policies: Attach AWS managed policies like AmazonEC2FullAccess, AmazonEKS_CNI_Policy, AmazonEKSClusterPolicy, AmazonEKSWorkerNodePolicy, AWSCloudFormationFullAccess, and IAMFullAccess.
  • Create Inline Policy: Create a custom inline policy to allow full access to EKS resources.
  • Access Key Creation: Generate AccessKey and SecretKey for configuring AWS CLI.
  • Create Access key for the EKS User

Install CLIs

  1. AWSCLI

curl "<https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip>" -o "awscliv2.zip" sudo apt install unzip unzip awscliv2.zip sudo ./aws/install aws configure        

2. Kubectl

curl -o kubectl <https://amazon-eks.s3.us-west-2.amazonaws.com/1.19.6/2021-01-05/bin/linux/amd64/kubectl> chmod +x ./kubectl sudo mv ./kubectl /usr/local/bin kubectl version --short --client        

3. eksctl

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 eksctl version        

AWS CLI Configuration

Step 1: Install AWS CLI

  • Windows/Mac/Linux: Download the AWS CLI from the official AWS site or use a package manager like brew (Mac), apt (Linux), or MSI (Windows). Download link: AWS CLI Installation

Step 2: Open Terminal/Command Prompt

  • Open Terminal (Mac/Linux) or Command Prompt (Windows).

Step 3: Run AWS Configure Command

In your terminal or command prompt, run the following command to configure AWS CLI:

aws configure

Step 4: Enter AWS Access Key ID and Secret Access Key

You'll be prompted to enter:

  1. AWS Access Key ID – Copy and paste your Access Key ID.
  2. AWS Secret Access Key – Copy and paste your Secret Key.

Example:

AWS Access Key ID [None]: YOUR_ACCESS_KEY_ID

AWS Secret Access Key [None]: YOUR_SECRET_ACCESS_KEY

Step 5: Choose Default Region

Next, AWS CLI will ask for a Default region name. For example, if you're working in US East (N. Virginia), you can type:

Default region name [None]: us-east-1

For other regions, refer to the AWS Region Table.

Step : 6 Verify Configuration

Run the following command to confirm that AWS CLI is configured properly:

aws sts get-caller-identity

Create EKS CLUSTER

CloudFormation will create the cluster when the first set of commands are run

eksctl create cluster --name=my-eks22 \\
                      --region=us-east-1 \\
                      --zones=us-east-1a,us-east-1b \\
                      --without-nodegroup

eksctl utils associate-iam-oidc-provider \\
    --region us-east-1 \\
    --cluster my-eks22 \\
    --approve

eksctl create nodegroup --cluster=my-eks22 \\
                       --region=us-east-1 \\
                       --name=node2 \\
                       --node-type=t3.medium \\
                       --nodes=3 \\
                       --nodes-min=2 \\
                       --nodes-max=4 \\
                       --node-volume-size=20 \\
                       --ssh-access \\
                       --ssh-public-key=devopstack1 \\
                       --managed \\
                       --asg-access \\
                       --external-dns-access \\
                       --full-ecr-access \\
                       --appmesh-access \\
                       --alb-ingress-access
        



OIDC Provider Association: Associate an IAM OIDC provider with the EKS cluster.

The OIDC provider acts as a bridge between Kubernetes and AWS IAM, enabling service accounts in your EKS cluster to assume IAM roles. This allows pods running in the cluster to securely access AWS services (like S3, DynamoDB, etc.) using the least privilege principle.

eksctl utils associate-iam-oidc-provider --region us-east-1 --cluster my-eks22 --approve

        

Node Group Creation: Create a managed node group within the cluster.

eksctl create nodegroup --cluster=my-eks22 --region=us-east-1 --name=node2 --node-type=t3.medium --nodes=3 --nodes-min=2 --nodes-max=4 --node-volume-size=20 --ssh-access --ssh-public-key=devopstack1 --managed

        

Jenkins Installation and Configuration

Java Development Kit (JDK)

Jenkins is a Java-based application, so you need to install the Java Development Kit (JDK) before installing Jenkins.

  • Java Version: Jenkins requires Java 17
  • Installation: Ubuntu/Debian:


sudo apt update
sudo apt install openjdk-17-jdk -y

        

Jenkins installation script :https://www.jenkins.io/doc/book/installing/linux/

sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \\
  <https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key>
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc]" \\
  <https://pkg.jenkins.io/debian-stable> binary/ | sudo tee \\
  /etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt-get update
sudo apt-get install jenkins
sudo systemctl enable jenkins
sudo systemctl start jenkins
        

change the permission for installation script

sudo chmod +x [jenkins.sh](<https://jenkins.sh/>)
        

Copy the ip address of the ec2 install and use port 8080 to access jenkins

54.147.43.251:8080

To unlock Jenkins find the password at on your ec2 server: /var/lib/jenkins/secrets/initialAdminPassword

sudo cat /var/lib/jenkins/secrets/initialAdminPassword


Create Admin User




Docker Installation

sudo apt install docker.io

sudo chmod 666 /var/run/docker.sock

Install Jenkins Plugins

  • docker pipeline
  • kubernetes
  • kubernetes cli
  • multibranch scan webhook trigger

Configure Plugins That were installed

  1. Go to Managed Jenkins > Tools


Connect to Git Repo and Create Pipeline

Create docker credential

Create a multi branch pipeline

Setup Webhook


Copy the jenkins url and the token for the webhook and use them as follows:

https://54.147.43.251:8080/multibranch-webhook-trigger/invoke?token=aghdtdhf



Continuous Deployment Stage

Kubernetes Role-Based Access Control (RBAC)

Create a namespace:

kubeclt create namespace webapps        

  • Service Account Creation: Create a Service Account for Jenkins in the webapps namespace.
  • kubectl apply -f svc.yml
  • Role Creation: Define a Role with permissions to access necessary Kubernetes resources.
  • kubectl apply -f role.yml
  • Role Binding: Bind the Role to the Service Account.
  • kubectl apply -f role-binding.yml

Service Account Token Creation

  • Token Generation: Generate a token using the Service Account to allow Jenkins access to the Kubernetes API.

apiVersion: v1
kind: Secret
type: [kubernetes.io/service-account-token](<https://kubernetes.io/service-account-token>)
metadata:
name: mysecretname
annotations:
[kubernetes.io/service-account.name:](<https://kubernetes.io/service-account.name:>) jenkins
        

Run the command to get the token

kubectl describe secret mysecretname -n webapps        


Go to AWS EKS and copy the API Server Endpoint : https://127E9436BA00F4179627C6652364C965.gr7.us-east-1.eks.amazonaws.com


---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: emailservice
spec:
  selector:
    matchLabels:
      app: emailservice
  template:
    metadata:
      labels:
        app: emailservice
    spec:
      serviceAccountName: default
      terminationGracePeriodSeconds: 5
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
      containers:
      - name: server
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: adijaiswal/emailservice:latest
        ports:
        - containerPort: 8080
        env:
        - name: PORT
          value: "8080"
        - name: DISABLE_PROFILER
          value: "1"
        readinessProbe:
          periodSeconds: 5
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:8080"]
        livenessProbe:
          periodSeconds: 5
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:8080"]
        resources:
          requests:
            cpu: 100m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: emailservice
spec:
  type: ClusterIP
  selector:
    app: emailservice
  ports:
  - name: grpc
    port: 5000
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkoutservice
spec:
  selector:
    matchLabels:
      app: checkoutservice
  template:
    metadata:
      labels:
        app: checkoutservice
    spec:
      serviceAccountName: default
      containers:
        - name: server
          image: adijaiswal/checkoutservice:latest
          ports:
          - containerPort: 5050
          readinessProbe:
            exec:
              command: ["/bin/grpc_health_probe", "-addr=:5050"]
          livenessProbe:
            exec:
              command: ["/bin/grpc_health_probe", "-addr=:5050"]
          env:
          - name: PORT
            value: "5050"
          - name: PRODUCT_CATALOG_SERVICE_ADDR
            value: "productcatalogservice:3550"
          - name: SHIPPING_SERVICE_ADDR
            value: "shippingservice:50051"
          - name: PAYMENT_SERVICE_ADDR
            value: "paymentservice:50051"
          - name: EMAIL_SERVICE_ADDR
            value: "emailservice:5000"
          - name: CURRENCY_SERVICE_ADDR
            value: "currencyservice:7000"
          - name: CART_SERVICE_ADDR
            value: "cartservice:7070"
          resources:
            requests:
              cpu: 100m
              memory: 64Mi
            limits:
              cpu: 200m
              memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: checkoutservice
spec:
  type: ClusterIP
  selector:
    app: checkoutservice
  ports:
  - name: grpc
    port: 5050
    targetPort: 5050
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: recommendationservice
spec:
  selector:
    matchLabels:
      app: recommendationservice
  template:
    metadata:
      labels:
        app: recommendationservice
    spec:
      serviceAccountName: default
      terminationGracePeriodSeconds: 5
      containers:
      - name: server
        image: adijaiswal/recommendationservice:latest
        ports:
        - containerPort: 8080
        readinessProbe:
          periodSeconds: 5
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:8080"]
        livenessProbe:
          periodSeconds: 5
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:8080"]
        env:
        - name: PORT
          value: "8080"
        - name: PRODUCT_CATALOG_SERVICE_ADDR
          value: "productcatalogservice:3550"
        - name: DISABLE_PROFILER
          value: "1"
        resources:
          requests:
            cpu: 100m
            memory: 220Mi
          limits:
            cpu: 200m
            memory: 450Mi
---
apiVersion: v1
kind: Service
metadata:
  name: recommendationservice
spec:
  type: ClusterIP
  selector:
    app: recommendationservice
  ports:
  - name: grpc
    port: 8080
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
      annotations:
        sidecar.istio.io/rewriteAppHTTPProbers: "true"
    spec:
      serviceAccountName: default
      containers:
        - name: server
          image: adijaiswal/frontend:latest
          ports:
          - containerPort: 8080
          readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "shop_session-id=x-readiness-probe"
          livenessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "shop_session-id=x-liveness-probe"
          env:
          - name: PORT
            value: "8080"
          - name: PRODUCT_CATALOG_SERVICE_ADDR
            value: "productcatalogservice:3550"
          - name: CURRENCY_SERVICE_ADDR
            value: "currencyservice:7000"
          - name: CART_SERVICE_ADDR
            value: "cartservice:7070"
          - name: RECOMMENDATION_SERVICE_ADDR
            value: "recommendationservice:8080"
          - name: SHIPPING_SERVICE_ADDR
            value: "shippingservice:50051"
          - name: CHECKOUT_SERVICE_ADDR
            value: "checkoutservice:5050"
          - name: AD_SERVICE_ADDR
            value: "adservice:9555"
          # # ENV_PLATFORM: One of: local, gcp, aws, azure, onprem, alibaba
          # # When not set, defaults to "local" unless running in GKE, otherwies auto-sets to gcp
          # - name: ENV_PLATFORM
          #   value: "aws"
          - name: ENABLE_PROFILER
            value: "0"
          # - name: CYMBAL_BRANDING
          #   value: "true"
          # - name: FRONTEND_MESSAGE
          #   value: "Replace this with a message you want to display on all pages."
          resources:
            requests:
              cpu: 100m
              memory: 64Mi
            limits:
              cpu: 200m
              memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-external
spec:
  type: LoadBalancer
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: paymentservice
spec:
  selector:
    matchLabels:
      app: paymentservice
  template:
    metadata:
      labels:
        app: paymentservice
    spec:
      serviceAccountName: default
      terminationGracePeriodSeconds: 5
      containers:
      - name: server
        image: adijaiswal/paymentservice:latest
        ports:
        - containerPort: 50051
        env:
        - name: PORT
          value: "50051"
        - name: DISABLE_PROFILER
          value: "1"
        readinessProbe:
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:50051"]
        livenessProbe:
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:50051"]
        resources:
          requests:
            cpu: 100m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: paymentservice
spec:
  type: ClusterIP
  selector:
    app: paymentservice
  ports:
  - name: grpc
    port: 50051
    targetPort: 50051
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: productcatalogservice
spec:
  selector:
    matchLabels:
      app: productcatalogservice
  template:
    metadata:
      labels:
        app: productcatalogservice
    spec:
      serviceAccountName: default
      terminationGracePeriodSeconds: 5
      containers:
      - name: server
        image: adijaiswal/productcatalogservice:latest
        ports:
        - containerPort: 3550
        env:
        - name: PORT
          value: "3550"
        - name: DISABLE_PROFILER
          value: "1"
        readinessProbe:
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:3550"]
        livenessProbe:
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:3550"]
        resources:
          requests:
            cpu: 100m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: productcatalogservice
spec:
  type: ClusterIP
  selector:
    app: productcatalogservice
  ports:
  - name: grpc
    port: 3550
    targetPort: 3550
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cartservice
spec:
  selector:
    matchLabels:
      app: cartservice
  template:
    metadata:
      labels:
        app: cartservice
    spec:
      serviceAccountName: default
      terminationGracePeriodSeconds: 5
      containers:
      - name: server
        image: adijaiswal/cartservice:latest
        ports:
        - containerPort: 7070
        env:
        - name: REDIS_ADDR
          value: "redis-cart:6379"
        resources:
          requests:
            cpu: 200m
            memory: 64Mi
          limits:
            cpu: 300m
            memory: 128Mi
        readinessProbe:
          initialDelaySeconds: 15
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:7070", "-rpc-timeout=5s"]
        livenessProbe:
          initialDelaySeconds: 15
          periodSeconds: 10
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:7070", "-rpc-timeout=5s"]
---
apiVersion: v1
kind: Service
metadata:
  name: cartservice
spec:
  type: ClusterIP
  selector:
    app: cartservice
  ports:
  - name: grpc
    port: 7070
    targetPort: 7070
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: loadgenerator
spec:
  selector:
    matchLabels:
      app: loadgenerator
  replicas: 1
  template:
    metadata:
      labels:
        app: loadgenerator
      annotations:
        sidecar.istio.io/rewriteAppHTTPProbers: "true"
    spec:
      serviceAccountName: default
      terminationGracePeriodSeconds: 5
      restartPolicy: Always
      containers:
      - name: main
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: adijaiswal/loadgenerator:latest
        env:
        - name: FRONTEND_ADDR
          value: "frontend:80"
        - name: USERS
          value: "10"
        resources:
          requests:
            cpu: 300m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: currencyservice
spec:
  selector:
    matchLabels:
      app: currencyservice
  template:
    metadata:
      labels:
        app: currencyservice
    spec:
      serviceAccountName: default
      terminationGracePeriodSeconds: 5
      containers:
      - name: server
        image: adijaiswal/currencyservice:latest
        ports:
        - name: grpc
          containerPort: 7000
        env:
        - name: PORT
          value: "7000"
        - name: DISABLE_PROFILER
          value: "1"
        readinessProbe:
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:7000"]
        livenessProbe:
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:7000"]
        resources:
          requests:
            cpu: 100m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: currencyservice
spec:
  type: ClusterIP
  selector:
    app: currencyservice
  ports:
  - name: grpc
    port: 7000
    targetPort: 7000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: shippingservice
spec:
  selector:
    matchLabels:
      app: shippingservice
  template:
    metadata:
      labels:
        app: shippingservice
    spec:
      serviceAccountName: default
      containers:
      - name: server
        image: adijaiswal/shippingservice:latest
        ports:
        - containerPort: 50051
        env:
        - name: PORT
          value: "50051"
        - name: DISABLE_PROFILER
          value: "1"
        readinessProbe:
          periodSeconds: 5
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:50051"]
        livenessProbe:
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:50051"]
        resources:
          requests:
            cpu: 100m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: shippingservice
spec:
  type: ClusterIP
  selector:
    app: shippingservice
  ports:
  - name: grpc
    port: 50051
    targetPort: 50051
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cart
spec:
  selector:
    matchLabels:
      app: redis-cart
  template:
    metadata:
      labels:
        app: redis-cart
    spec:
      containers:
      - name: redis
        image: redis:alpine
        ports:
        - containerPort: 6379
        readinessProbe:
          periodSeconds: 5
          tcpSocket:
            port: 6379
        livenessProbe:
          periodSeconds: 5
          tcpSocket:
            port: 6379
        volumeMounts:
        - mountPath: /data
          name: redis-data
        resources:
          limits:
            memory: 256Mi
            cpu: 125m
          requests:
            cpu: 70m
            memory: 200Mi
      volumes:
      - name: redis-data
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: redis-cart
spec:
  type: ClusterIP
  selector:
    app: redis-cart
  ports:
  - name: tcp-redis
    port: 6379
    targetPort: 6379
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: adservice
spec:
  selector:
    matchLabels:
      app: adservice
  template:
    metadata:
      labels:
        app: adservice
    spec:
      serviceAccountName: default
      terminationGracePeriodSeconds: 5
      containers:
      - name: server
        image: adijaiswal/adservice:latest
        ports:
        - containerPort: 9555
        env:
        - name: PORT
          value: "9555"
        resources:
          requests:
            cpu: 200m
            memory: 180Mi
          limits:
            cpu: 300m
            memory: 300Mi
        readinessProbe:
          initialDelaySeconds: 20
          periodSeconds: 15
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:9555"]
        livenessProbe:
          initialDelaySeconds: 20
          periodSeconds: 15
          exec:
            command: ["/bin/grpc_health_probe", "-addr=:9555"]
---
apiVersion: v1
kind: Service
metadata:
  name: adservice
spec:
  type: ClusterIP
  selector:
    app: adservice
  ports:
  - name: grpc
    port: 9555
    targetPort: 9555
        
pipeline { 
    agent any   

    stages {
        stage('Deploy to kubernetes') {
            steps {
               withKubeCredentials(kubectlCredentials: [[caCertificate: '', clusterName: ' my-eks22', contextName: '', credentialsId: 'kube-token', namespace: 'webapps', serverUrl: '<https://127E9436BA00F4179627C6652364C965.gr7.us-east-1.eks.amazonaws.com>']]) {
                sh "kubectl apply -f deployment-service.yml"
                sleep 60
                }
            }
        }
        stage('Verify Deployment') {
            steps {
              withKubeCredentials(kubectlCredentials: [[caCertificate: '', clusterName: ' my-eks22', contextName: '', credentialsId: 'kube-token', namespace: 'webapps', serverUrl: '<https://127E9436BA00F4179627C6652364C965.gr7.us-east-1.eks.amazonaws.com>']]) {
                 sh "kubectl get all -n webapps"
                }
            }
        }
    }
}
        


Clean Up the Deployment

eksctl delete cluster --name my-eks22 --region us-east-1        
Amissah Daniel Yaw

Mechanic, for havy duty track, Mafi and Cranes at Ghana Port and harbour Authority. ( Tema)

2 个月

I agree!

回复
Emmanuel Adamah, CC, AWS CCP

AWS Certified Cloud Practitioner | Certified In Cybersecurity | Information Security (INFOSEC) | SOC Analyst | Security Analyst

2 个月

Great I’m also working on a kubenetes project I will hit you up for some insights??

Kamalpreet KAUR

Open for New Opportunities : Senior IT Content Writer - Intern | 1* (CEH) | 1* CCNA Fundamentals | AWS Skill Builder Enthusiast

2 个月
Ernest Kwasi Henry Nyarko

DevOps | Linux | GitHub Actions | Maven | Tomcat | SAST | DAST | Jenkins | Docker | Kubernetes(EKS/Fargate) | Terraform | AWS | Azure | Python | Prometheus | Grafana | Node Exporter

2 个月

Very helpful

回复

要查看或添加评论,请登录

社区洞察

其他会员也浏览了