Blockmonkey

EKS ์‚ฌ์šฉํ•˜๊ธฐ ๋ณธ๋ฌธ

Web Development/Back-end

EKS ์‚ฌ์šฉํ•˜๊ธฐ

Blockmonkey 2025. 7. 21. 11:57

๐ŸŒˆ Kubernetes ๊ตฌ์„ฑ ์•„ํ‚คํ…์ณ

Kubernetes Layout

 

๐Ÿ”น Master Node (Control Plane)

Kubernetes ์ „์ฒด ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋‘๋‡Œ ์—ญํ• 

  • API Server: kubectl ๊ฐ™์€ ์š”์ฒญ์„ ๋ฐ›์•„๋“ค์ด๋Š” ์ฐฝ๊ตฌ
  • Scheduler: ์–ด๋–ค Pod์„ ์–ด๋–ค Node์— ๋ฐฐ์น˜ํ• ์ง€ ๊ฒฐ์ •
  • Controller Manager: ์ƒํƒœ ์œ ์ง€๋ฅผ ์œ„ํ•œ ์ž๋™ ์กฐ์ • (์˜ˆ: Pod์ด ์ฃฝ์œผ๋ฉด ๋‹ค์‹œ ๋„์›€)
  • etcd: Kubernetes์˜ ์„ค์ • ์ •๋ณด, ์ƒํƒœ ๋“ฑ์„ ์ €์žฅํ•˜๋Š” Key-Value ์ €์žฅ์†Œ

๐Ÿ”น Worker Nodes

์‹ค์ œ ์•ฑ์ด ๋ฐฐํฌ๋˜๋Š” ์ปดํ“จํŒ… ์ž์› (EC2 ๋“ฑ)

  • Master์˜ ๋ช…๋ น์— ๋”ฐ๋ผ Pod์„ ์‹คํ–‰ํ•˜๊ณ  ๊ด€๋ฆฌ
  • ํ•œ ๊ฐœ ์ด์ƒ์˜ Worker Node ์กด์žฌ ๊ฐ€๋Šฅ (์Šค์ผ€์ผ๋ง ๋Œ€์‘)

๐Ÿ”น Pods

Kubernetes์—์„œ ๊ฐ€์žฅ ์ž‘์€ ์‹คํ–‰ ๋‹จ์œ„ (์ปจํ…Œ์ด๋„ˆ ํ•œ ๊ฐœ or ์—ฌ๋Ÿฌ ๊ฐœ ํฌํ•จ ๊ฐ€๋Šฅ)

  • Spring App, ArgoCD, Redis ๋“ฑ ์‹ค์ œ ์•ฑ ์‹คํ–‰๋˜๋Š” ๊ณณ
  • Pod์ด ์ฃฝ์œผ๋ฉด ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์ƒ์„ฑ๋จ (by ReplicaSet ๋“ฑ)

๐Ÿ”น Service

Pod๋“ค์„ ์ถ”์ƒํ™”ํ•ด์„œ, ์•ˆ์ •์ ์ธ ์ ‘๊ทผ ๊ฒฝ๋กœ ์ œ๊ณต

  • Pod IP๋Š” ๋ฐ”๋€” ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ณ ์ •๋œ IP/๋„๋ฉ”์ธ ์—ญํ• 
  • ClusterIP: ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€ ํ†ต์‹ ์šฉ
  • NodePort: ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅ (๋…ธ๋“œ IP + ํฌํŠธ)
  • LoadBalancer: ํด๋ผ์šฐ๋“œ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ํ• ๋‹น (์™ธ๋ถ€ ํŠธ๋ž˜ํ”ฝ์šฉ)

 

 

 


 

๐ŸŒˆ Kubernetes - EKS ๊ตฌ์ถ•ํ•ด๋ณด๊ธฐ

  • ์‚ฌ์ „ ์ค€๋น„์‚ฌํ•ญ
    • AWS CLI ์„ค์น˜ ๋ฐ ์„ค์ •
    • Helm ์„ค์น˜
    • brea install helm ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด Helm ์„ค์น˜
    • VPC ๊ตฌ์„ฑํ•˜๊ธฐ
      • 2 Region ์ด์ƒ ๊ตฌ์„ฑ

*๊ธฐํƒ€ ์ฃผ์˜์‚ฌํ•ญ

EKS์šฉ VPC ์„œ๋ธŒ๋„ท์—๋Š” ์•„๋ž˜ ๋‘๊ฐœ ํƒœ๊ทธ๋ฅผ ํ•„์ˆ˜์ ์œผ๋กœ ๋ถ™์—ฌ์ค˜์•ผ ๋™์ž‘ํ•œ๋‹ค.

kubernetes.io/cluster/eks-test-cluster shared
kubernetes.io/role/elb 1

 

  • EKS IAM ROLE ์ƒ์„ฑํ•˜๊ธฐ
    • ํ•„์ˆ˜ ์š”๊ตฌ ์ •์ฑ…
      • EKSWorkerNodePolicy, EKSCNIPolicy, EC2ContainerReadOnlyPolicy
    • Example
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iam:CreateServiceLinkedRole" ], "Resource": "*", "Condition": { "StringEquals": { "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com" } } }, { "Effect": "Allow", "Action": [ "ec2:DescribeAccountAttributes", "ec2:DescribeAddresses", "ec2:DescribeAvailabilityZones", "ec2:DescribeInternetGateways", "ec2:DescribeVpcs", "ec2:DescribeVpcPeeringConnections", "ec2:DescribeSubnets", "ec2:DescribeSecurityGroups", "ec2:DescribeInstances", "ec2:DescribeNetworkInterfaces", "ec2:DescribeTags", "ec2:GetCoipPoolUsage", "ec2:DescribeCoipPools", "elasticloadbalancing:DescribeLoadBalancers", "elasticloadbalancing:DescribeLoadBalancerAttributes", "elasticloadbalancing:DescribeListeners", "elasticloadbalancing:DescribeListenerCertificates", "elasticloadbalancing:DescribeSSLPolicies", "elasticloadbalancing:DescribeRules", "elasticloadbalancing:DescribeTargetGroups", "elasticloadbalancing:DescribeTargetGroupAttributes", "elasticloadbalancing:DescribeTargetHealth", "elasticloadbalancing:DescribeTags", "elasticloadbalancing:DescribeListenerAttributes" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "cognito-idp:DescribeUserPoolClient", "acm:ListCertificates", "acm:DescribeCertificate", "iam:ListServerCertificates", "iam:GetServerCertificate", "waf-regional:GetWebACL", "waf-regional:GetWebACLForResource", "waf-regional:AssociateWebACL", "waf-regional:DisassociateWebACL", "wafv2:GetWebACL", "wafv2:GetWebACLForResource", "wafv2:AssociateWebACL", "wafv2:DisassociateWebACL", "shield:GetSubscriptionState", "shield:DescribeProtection", "shield:CreateProtection", "shield:DeleteProtection" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "ec2:AuthorizeSecurityGroupIngress", "ec2:RevokeSecurityGroupIngress" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "ec2:CreateSecurityGroup" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "ec2:CreateTags" ], "Resource": "arn:aws:ec2:*:*:security-group/*", "Condition": { "StringEquals": { "ec2:CreateAction": "CreateSecurityGroup" }, "Null": { "aws:RequestTag/elbv2.k8s.aws/cluster": "false" } } }, { "Effect": "Allow", "Action": [ "ec2:CreateTags", "ec2:DeleteTags" ], "Resource": "arn:aws:ec2:*:*:security-group/*", "Condition": { "Null": { "aws:RequestTag/elbv2.k8s.aws/cluster": "true", "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" } } }, { "Effect": "Allow", "Action": [ "ec2:AuthorizeSecurityGroupIngress", "ec2:RevokeSecurityGroupIngress", "ec2:DeleteSecurityGroup" ], "Resource": "*", "Condition": { "Null": { "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" } } }, { "Effect": "Allow", "Action": [ "elasticloadbalancing:CreateLoadBalancer", "elasticloadbalancing:CreateTargetGroup" ], "Resource": "*", "Condition": { "Null": { "aws:RequestTag/elbv2.k8s.aws/cluster": "false" } } }, { "Effect": "Allow", "Action": [ "elasticloadbalancing:CreateListener", "elasticloadbalancing:DeleteListener", "elasticloadbalancing:CreateRule", "elasticloadbalancing:DeleteRule" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "elasticloadbalancing:AddTags", "elasticloadbalancing:RemoveTags" ], "Resource": [ "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" ], "Condition": { "Null": { "aws:RequestTag/elbv2.k8s.aws/cluster": "true", "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" } } }, { "Effect": "Allow", "Action": [ "elasticloadbalancing:AddTags", "elasticloadbalancing:RemoveTags" ], "Resource": [ "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*", "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*", "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*", "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*" ] }, { "Effect": "Allow", "Action": [ "elasticloadbalancing:AddTags" ], "Resource": [ "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" ], "Condition": { "StringEquals": { "elasticloadbalancing:CreateAction": [ "CreateTargetGroup", "CreateLoadBalancer" ] }, "Null": { "aws:RequestTag/elbv2.k8s.aws/cluster": "false" } } }, { "Effect": "Allow", "Action": [ "elasticloadbalancing:ModifyLoadBalancerAttributes", "elasticloadbalancing:SetIpAddressType", "elasticloadbalancing:SetSecurityGroups", "elasticloadbalancing:SetSubnets", "elasticloadbalancing:DeleteLoadBalancer", "elasticloadbalancing:ModifyTargetGroup", "elasticloadbalancing:ModifyTargetGroupAttributes", "elasticloadbalancing:DeleteTargetGroup" ], "Resource": "*", "Condition": { "Null": { "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" } } }, { "Effect": "Allow", "Action": [ "elasticloadbalancing:RegisterTargets", "elasticloadbalancing:DeregisterTargets" ], "Resource": "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*" }, { "Effect": "Allow", "Action": [ "elasticloadbalancing:SetWebAcl", "elasticloadbalancing:ModifyListener", "elasticloadbalancing:AddListenerCertificates", "elasticloadbalancing:RemoveListenerCertificates", "elasticloadbalancing:ModifyRule" ], "Resource": "*" } ] }
  • EKS - Cluster ์ƒ์„ฑ - eks-test-cluster
    • ์„ค์ •
      • Custom Configuration์œผ๋กœ ์ง„ํ–‰.
      • EKS Auto Mode : ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ๋ชจ๋“œ๋กœ ALB, EBS ์—ฐ๊ฒฐ ๊ด€๋ฆฌ ๋“ฑ ์•Œ์•„์„œ ํ•ด์ฃผ๋Š” ์˜ต์…˜ → ๋น„ํ™œ์„ฑํ™”
      • Kubernetes version settings : ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฒ„์ „์„ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„
        • Standard : 14๊ฐœ์›” ๋™์•ˆ ๋ฒ„์ „ ์ง€์› → ์ดํ›„ ์ž๋™์—…๊ทธ๋ ˆ์ด๋“œ (๋น„์šฉ์—†์Œ)
        • Extended : 26๊ฐœ์›” ๋™์•ˆ ์ง€์› ์—ฐ์žฅ → 14๊ฐœ์›” ํ›„ ์š”๊ธˆ๋ฐœ์ƒ
        • ๊ฐœ๋ฐœ์šฉ : Standard / ์‹ค์ œ ์šด์˜ ์‹œ : Extended ๋ชจ๋“œ ๊ถŒ์žฅ
      • Auto Mode Compute : Node๋ฅผ ์ˆ˜๋™, ์ž๋™ ์ถ”๊ฐ€ ๊ด€๋ฆฌ ์„ค์ •
        • ์ถ”ํ›„ Helm, ArgoCD๋ฅผ ํ†ตํ•ด ์ง„ํ–‰ํ•˜๋ฏ€๋กœ → ๋น„ํ™œ์„ฑํ™”
  • EKS - Node Group ์ƒ์„ฑ - eks-test-node-group
    • Cluster - Compute - Node groups - Add Node Group ์—์„œ ์ƒ์„ฑ.
    • ์ตœ์†Œ ์ธ์Šคํ„ด์Šค ์„ฑ๋Šฅ์€ small ์ด๋‹ค.
    • Argo CD๋“ฑ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด Node 2๊ฐœ ์ด์ƒ ํ•  ๊ฒƒ. (t3.small ๊ธฐ์ค€)
  • EKS Kubectl ์—ฐ๊ฒฐ ๋ฐ ๊ฒ€์ฆ
    • ์—ฐ๊ฒฐ ๋ฐ ๊ฒ€์ฆ
      • ์—ฐ๊ฒฐ
      aws eks update-kubeconfig --region **{aws-region}** --name **{eks-cluster-name}**
      
      • ๊ฒ€์ฆ
      // Node ๋ชฉ๋ก ํ™•์ธ
      kubectl get nodes
      
  • ECR ์…‹ํŒ… ๋ฐ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์ง„ํ–‰
  • Kubernetes Resource ์ž‘์„ฑ (Deployment + Service)
    • ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ ์˜ˆ์‹œ
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: spring-app
  template:
    metadata:
      labels:
        app: spring-app
    spec:
      containers:
        - name: spring-container
          image: 211125374993.dkr.ecr.ap-southeast-1.amazonaws.com/eks-test-ecr:latest
          ports:
            - containerPort: 8080
          imagePullPolicy: Always
          
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: spring-app-service
spec:
  type: LoadBalancer
  selector:
    app: spring-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

 

  • ์ ์šฉํ•˜๊ธฐ ๋ฐ ์ ‘์†ํ•˜๊ธฐ
# ์ ์šฉ
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

# ์ ‘์†
kubectl get svc ์ž…๋ ฅ ํ›„, EXTERNAL-IP ์— ์ ‘์†ํ•˜์—ฌ ํ™•์ธ

 


 

๐ŸŒˆ  Kubernetes - EKS์— CICD ์…‹ํŒ…์„ ์ง„ํ–‰ํ•˜์ž

๊ตฌ์„ฑ ๊ธฐ๋Šฅ
Github Action (CI) Github Push → ECR์— ์ด๋ฏธ์ง€ ๋“ฑ๋ก (CI)
ArgoCD (CD) ์œ„ ๊ณผ์ •์„ ๊ฑฐ์น˜๋ฉด, Helm Repository์— Sed ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด Commit์„ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค (CD)
Helm Service, Deployment, pv, pvc ๋“ฑ ๋ฐฐํฌ์™€ ๊ด€๋ จ๋œ ๋‚ด์šฉ์„ yaml ํŒŒ์ผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.

 

EKS with Github Action (CI) ์…‹ํŒ…ํ•˜๊ธฐ

์‚ฌ์ „ ์ค€๋น„์‚ฌํ•ญ

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_REGION ์ •๋ณด
  • AWS_ACCOUNT_ID

 

1. .github/workflow/deploy.yml์„ ์ž‘์„ฑ, (์œ„ ๊ฐ’๋“ค์€ Github Secret์— ๋“ฑ๋ก์ด ํ•„์š”ํ•˜๋‹ค)

name: Deploy Spring App to ECR

on:
  push:
    branches:
      - main

jobs:
  build_and_push:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        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: Login to Amazon ECR
        id: ecr-login
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build, tag, and push image to ECR
        env:
          AWS_REGION: ${{ secrets.AWS_REGION }}
          AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
        run: |
          docker build -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:latest .
          docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:latest \
                     $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:${{ github.sha }}
          docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:latest
          docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:${{ github.sha }}

      - name: Update Kubernetes deployment yaml
        env:
          AWS_REGION: ${{ secrets.AWS_REGION }}
          AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
        run: |
          export NEW_IMAGE="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:${{ github.sha }}"
          echo "New Image: $NEW_IMAGE"
          sed -i "s|image:.*|image: $NEW_IMAGE|" ./k8s/deployment.yaml

      - name: Push updated deployment.yaml
        run: |
          git config user.name "GitHub Actions"
          git config user.email "actions@github.com"
          git add ./k8s/deployment.yaml
          git commit -m "Update image to ${{ github.sha }}"
          git push

 

2. ๊ฒ€์ฆ

  • Github ์— push ํ•ด ์ปค๋ฐ‹์„ ์˜ฌ๋ ธ์„ ๋•Œ, ECR์— ์ด๋ฏธ์ง€ ์ƒ์„ฑ๋˜๋ฉด ์„ฑ๊ณต!

 

EKS with ArgoCD (CD) ์…‹ํŒ…ํ•˜๊ธฐ

ArgoCD๋Š” Github์™€ ์—ฐ๋™ํ•˜์—ฌ, Helm Deployment Script๋ฅผ ์ฝ๊ณ  ์ด๋ฅผ ์ž๋™ ๋ฐฐํฌ(Continuous Deployment) ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์‚ฌ์ „์— Github Access Token์ด ํ•„์š”ํ•˜๋‹ค.

 

1. ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์— ArgoCD ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ƒ์„ฑ

$ kubectl create namespace argocd

 

2. ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์— ArgoCD ์„ค์น˜

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

 

3. ๊ฒ€์ฆ

kubectl get pods -n argocd

# Pending์ธ ๊ฒฝ์šฐ ์›์ธ ํ™•์ธ
kubectl describe pod {pod name} -n argocd

# ArgoCD๋ฅผ 443 -> LocalHost 8080์œผ๋กœ ํฌํŠธํฌ์›Œ๋”ฉ 
kubectl port-forward svc/argocd-server -n argocd 8080:443

# ArgoCD๋ฅผ ์ดˆ๊ธฐ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ (๋น„๋ฐ€๋ฒˆํ˜ธ ๋ฆฌํ„ด๋จ)
kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath="{.data.password}" | base64 --decode

 

4. ArgoCD์— Github ๋ฅผ ๋“ฑ๋กํ•˜์ž (์ปค๋ฐ‹์„ ํ™•์ธํ•˜๋ ค๋ฉด ์ ‘๊ทผ๊ถŒํ•œ์ด ์žˆ์–ด์•ผํ•˜์ง€ ์•Š๊ฒ ๋Š”๊ฐ€?)

  • ArgoCD์— ์ ‘์†ํ•˜์—ฌ Settings - Repository Tab์—์„œ ์ง„ํ–‰
    • Github Secret Token ํ•„์š” (Username & Secret ์ž‘์„ฑ)
    • ์•„๋ž˜์ฒ˜๋Ÿผ Connection ๋ชฉ๋ก ๋œจ๋ฉด ์„ฑ๊ณต

 

5. ArgoCD์— Application ๋“ฑ๋ก (Continuous Deployment๋ฅผ ์ˆ˜ํ–‰ํ•  Application ๋Œ€์ƒ์„ ๋“ฑ๋กํ•œ๋‹ค)

  • Applications - + New App ๋ฒ„ํŠผ ํด๋ฆญํ•ด์„œ ์ƒ์„ฑ ์‹œ์ž‘ํ•˜๊ณ , ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅ ํ›„, Create ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ƒ์„ฑํ•œ๋‹ค.
Application Name spring-server (์ž์œ ๋กญ๊ฒŒ)
Project default
Sync Policy Manual (์ˆ˜๋™๋ฐฐํฌ) || Auto (์ž๋™๋ฐฐํฌ)
Prune Propagation Policy background
Repository URL ์œ„ ์—ฐ๊ฒฐํ•œ Github ์ €์žฅ์†Œ ํด๋ฆญ
Revision (๋ธŒ๋žœ์น˜๋ช…) main
Path (k8s ์Šคํฌ๋ฆฝํŠธ ํด๋” ๋ช…) k8s
Cluster https://kubernetes.default.svc (EKS ๋‚ด๋ถ€ ๊ธฐ๋ณธ ํด๋Ÿฌ์Šคํ„ฐ ์ฃผ์†Œ)
Namespace default || ์›ํ•˜๋Š” ๋„ค์ž„์ŠคํŽ˜์ด์Šค (๋‚˜์˜ ์•ฑ์ด ๋ฐฐํฌ๋  ๋„ค์ž„์ŠคํŽ˜์ด์Šค)

๊ฒฐ๊ณผํ™”๋ฉด

 

6. ArgoCD๋ฅผ ์™ธ๋ถ€ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ClusterIP -> LoadBalancer ํƒ€์ž…์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.

# ๋ณ€๊ฒฝํ•˜๊ธฐ
$ kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'

# ๊ฒ€์ฆ
$ kubectl get svc -n argocd

 

*๊ธฐ๋ณธ์ ์œผ๋กœ ๋ณ„๋„์˜ ์„ค์ •์ด ์—†๋‹ค๋ฉด, ArgoCD๋Š” ๊นƒํ—ˆ๋ธŒ์˜ Deployment.yaml ํŒŒ์ผ์˜ ๋ณ€๋™์‚ฌํ•ญ์„ 3๋ถ„ ๋งˆ๋‹ค Pollingํ•ด ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ํ™•์ธํ•˜๊ณ  ๋™๊ธฐํ™”ํ•œ๋‹ค.

 

 

 

๐ŸŒˆ  Helm ์„ค์ • (Yaml ํŒŒ์ผ ๊ด€๋ฆฌ)

  • ์‚ฌ์ „์ค€๋น„์‚ฌํ•ญ
    • Helm ์šฉ Github Repository ์ƒ์„ฑ (ex> test-infra)
    • Helm ์„ค์น˜
// ์„ค์น˜ํ™•์ธ
$ helm version

// ์„ค์น˜ MAC
$ brew install helm

 

Helm ์ดˆ๊ธฐํ™”

$ helm create {์•ฑ ๋ช…์นญ}

 

Helm Chart ํด๋” ๊ตฌ์กฐ (์ฐธ๊ณ ์šฉ)

Chart.yaml Chart ์ด๋ฆ„, ๋ฒ„์ „ ๋“ฑ ๋ฉ”ํƒ€ ์ •๋ณด
values.yaml ์‚ฌ์šฉ์ž ์„ค์ • ๊ฐ’ (image, env ๋“ฑ) ๊ด€๋ฆฌ
charts/ ์ข…์† ์ฐจํŠธ (dependency) ํด๋”
templates/deployment.yaml ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ Deployment ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ (์ปจํ…Œ์ด๋„ˆ ์ŠคํŽ™)
templates/service.yaml ์„œ๋น„์Šค(Service) ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ (๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ)
templates/ingress.yaml Ingress ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ (๋„๋ฉ”์ธ ์—ฐ๊ฒฐ์šฉ)
templates/hpa.yaml Horizontal Pod Autoscaler (์ž๋™ ์Šค์ผ€์ผ๋ง) ๋ฆฌ์†Œ์Šค
templates/serviceaccount.yaml ServiceAccount ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ (๊ถŒํ•œ ๋ถ€์—ฌ), AWS IAM ์—ฐ๋™ ์‹œ ํ•„์š”
templates/_helpers.tpl ํ…œํ”Œ๋ฆฟ ๊ณตํ†ต ํ•จ์ˆ˜ (Label, ์ด๋ฆ„ ๋“ฑ ์žฌ์‚ฌ์šฉ)
templates/NOTES.txt ์„ค์น˜ ๊ฒฐ๊ณผ ์•ˆ๋‚ด๋ฌธ, ์—†์–ด๋„ ๋ฌด๊ด€
templates/tests/test-connection.yaml ํ—ฌ๋ฆ„ ํ…Œ์ŠคํŠธ ๋ฆฌ์†Œ์Šค (helm test ์šฉ)

 

๊ธฐ๋ณธ ํ—ฌ๋ฆ„ ํ…œํ”Œ๋ฆฟ ์ž‘์„ฑํ•ด๋ณด๊ธฐ

# ๐Ÿ“ templates/deployment.yaml
apiVersion: apps/v1 ### ๋ฒ„์ „์ •๋ณด
kind: Deployment ### Deployment Type ๋ช…์‹œ
### ์ด๋ฆ„ ์„ค์ • (Kubectl์—์„œ ๊ด€๋ฆฌํ•  ๋•Œ ์‹๋ณ„์ž)
metadata:
  name: spring-app

spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: spring-app
  template:
    metadata:
      labels:
        app: spring-app
    spec:
      containers:
        - name: spring-container
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: 8080
          env:
            {{- range $key, $value := .Values.env }}
            - name: {{ $key }}
              value: "{{ $value }}"
            {{- end }}
          imagePullPolicy: Always
          
          
# ๐Ÿ“ templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: spring-app
spec:
  selector:
    app: spring-app
  type: {{ .Values.service.type }}
  ports:
    - protocol: TCP
      port: {{ .Values.service.port }}
      targetPort: 8080
      
# ๐Ÿ“ Chart.yaml
apiVersion: v2
name: spring-app
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"

# ๐Ÿ“ values.yaml
# Default values for spring-app.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

### ์ด๋ฏธ์ง€ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๊ฐ’ ์„ค์ •
image:
  repository: 211125374993.dkr.ecr.ap-southeast-1.amazonaws.com/eks-test-ecr
  tag: latest

### ๋ฐฐํฌํ•  POD ๊ฐฏ์ˆ˜ ์„ค์ •
replicaCount: 1

### ์„œ๋น„์Šค ํƒ€์ž… ์„ค์ •
service:
  type: LoadBalancer
  port: 80

### ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ๋ณ€์ˆ˜
env:
  SPRING_PROFILES_ACTIVE: "prod"
  AWS_REGION: "ap-southeast-1"
  TEST_VALUE: "100"

 

์œ„ ๊ณผ์ •์—์„œ ArgoCD๋ž‘ Github Repository๋ฅผ ์—ฐ๊ฒฐํ•  ๋•Œ Application ๊ณผ ์—ฐ๊ฒฐํ•œ ๊ฒƒ์„ ์ด์ œ Helm Repository๋กœ ๋ณ€๊ฒฝํ•ด์ค€๋‹ค.

 

Github Action์—์„œ Helm Repsoitory ๋ฅผ Sed ๋ช…๋ น์–ด๋กœ ์ปค๋ฐ‹ํ•˜๋„๋ก ์„ค์ •ํ•œ๋‹ค.

  • ํ‘ธ์‹œํ•  ๋•Œ ์ด๋ฏธ์ง€๋„ Commit Hash๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ,
  • ArgoCD Deployment Script์˜ ํ•ด์‹œ๊ฐ’๋„ ์ปค๋ฐ‹ํ•ด์‹œ ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝ์ณ์ค€๋‹ค.
name: Deploy Spring App to ECR

on:
  push:
    branches:
      - main

jobs:
  build_and_push:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        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: Login to Amazon ECR
        id: ecr-login
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build, tag, and push image to ECR
        env:
          AWS_REGION: ${{ secrets.AWS_REGION }}
          AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
        run: |
          COMMIT_HASH=$(echo $GITHUB_SHA | cut -c1-7)
          docker build --no-cache -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:latest .
          docker tag $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:latest \
                     $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:$COMMIT_HASH
          docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:latest
          docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/eks-test-ecr:$COMMIT_HASH

      - name: Update Helm values.yaml in Helm Repo
        env:
          GH_HELM_REPO_PAT: ${{ secrets.GH_HELM_REPO_PAT }}
        run: |
          COMMIT_HASH=$(echo $GITHUB_SHA | cut -c1-7)
          git clone https://x-access-token:${GH_HELM_REPO_PAT}@github.com/MG8-Project/eks-test-infra.git
          cd eks-test-infra/spring-app
          sed -i "s/tag: .*/tag: $COMMIT_HASH/" values.yaml
          git config user.name "github-actions"
          git config user.email "actions@github.com"
          git commit -am "Update image tag to $COMMIT_HASH"
          git push https://x-access-token:${GH_HELM_REPO_PAT}@github.com/MG8-Project/eks-test-infra.git HEAD:main

 

 

์„ค์ •์€ ๋๋‚ฌ๋‹ค !

ํ˜„์žฌ๊นŒ์ง€ ๊ตฌ์„ฑํ•œ ๊ตฌ์กฐ๋ฅผ ์ •๋ฆฌํ•ด๋ณด์ž.

1. Application Repository์— Code Commit์ด ์ด๋ฃจ์–ด์ง„๋‹ค.

2. GIthub Action Build in ECR ๊ณผ์ •์„ ๊ฑฐ์ณ, Docker๋กœ ๋ง์•„์„œ ECR ์ด๋ฏธ์ง€ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ์ €์žฅ๋œ๋‹ค.

3. ์ดํ›„, Github Action์€ Helm Repository์— Sed ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์ปค๋ฐ‹์„ ๋‚ ๋ฆฐ๋‹ค.

4. ArgoCD๋Š” Helm Repository๋ฅผ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ๋‹ค๊ฐ€, ์œ„ ์ปค๋ฐ‹ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๋ฐ›๊ณ  Deployment Script๋ฅผ ์ฝ์–ด ๋“ฑ๋ก๋œ Application์— ๋ฐฐํฌํ•œ๋‹ค.

 

 

์•„๋ž˜๋Š”, Helm์— Ingress ์„ค์ •์„ ํ†ตํ•ด ์›ํ•˜๋Š” ๋„๋ฉ”์ธ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

ํ•„์š”์‹œ ์ง„ํ–‰ํ•˜์ž ! ์•„๋งˆ ๋Œ€์ฒด๋กœ ํ•„์š”ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋˜์–ด ํ•œ ํฌ์ŠคํŒ…์— ํฌํ•จํ–ˆ๋‹ค.

 


๐ŸŒˆ  Helm - Ingress (๋„๋ฉ”์ธ & Https์„ค์ •)

Ingress ๋Š” ์™ธ๋ถ€ ์š”์ฒญ์„ ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด๋ถ€์˜ ์„œ๋น„์Šค์—์„œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์ •ํ•ด๋‘” ๊ทœ์น™์˜ ๋ชจ์Œ์ด๋ฉฐ ํŠธ๋ž˜ํ”ฝ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ, SSL ์ธ์ฆ์„œ์ฒ˜๋ฆฌ, ๋„๋ฉ”์ธ๊ธฐ๋ฐ˜ ๊ฐ€์ƒ ํ˜ธ์ŠคํŒ… ์ œ๊ณต ๋“ฑ์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
Ingress Controller๋Š” Ingress๊ฐ€ ๋™์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ํด๋Ÿฌ์Šคํ„ฐ์—์„œ ์‹คํ–‰๋˜๊ณ  ์š”์ฒญ์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ ํ”„๋กœ๊ทธ๋žจ์ด๋‹ค, ์ฆ‰ Ingress๋Š” ํŠธ๋ž˜ํ”ฝ ์ฒ˜๋ฆฌ ๊ทœ์น™(Rule)์ด๊ณ , Ingress Controller๋Š” ์‹ค์ œ ๋ผ์šฐํŒ… ์ˆ˜ํ–‰ ํ”„๋กœ๊ทธ๋žจ์ด๋‹ค.

 

1. EKS ctl ์„ค์น˜

- Ingress Controller ๋Š” AWS Type, Nginx Type๋“ฑ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ์œผ๋‚˜, AWS LoadBalancer ์‚ฌ์šฉ์„ ์œ„ํ•ด aws-load-balancer-webhook-service๋ฅผ ์‚ฌ์šฉ

$ brew tap weaveworks/tap
$ brew install weaveworks/tap/eksctl

 

2. IAM Role ์ƒ์„ฑ

3. Service Account ์ƒ์„ฑ (IRSA, IAM Roles for Service Account)

*Service Account๋ž€?
Kubernetes์—์„œ Pod๊ฐ€ ์„œ๋ฒ„์— ์ ‘๊ทผ ์‹œ ์‚ฌ์šฉํ•˜๋Š” ๊ณ„์ •์œผ๋กœ, ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  Pod๋Š” default Service Account๋ฅผ ์†Œ์œ ํ•จ. ๋”ฐ๋ผ์„œ, ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค(AWS)์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ์ ์ ˆํ•œ ๊ถŒํ•œ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ
eksctl create iamserviceaccount \
  --cluster eks-test-cluster \
  --namespace kube-system \
  --name aws-load-balancer-controller \ # ์„œ๋น„์Šค ์–ด์นด์šดํŠธ ์ด๋ฆ„
  --attach-role-arn arn:aws:iam::<ACCOUNT_ID>:role/AmazonEKSLoadBalancerControllerRole \
  --approve

 

4. Helm์„ ํ†ตํ•ด aws-load-balancer-controller ์„ค์น˜

ํด๋Ÿฌ์Šคํ„ฐ๋ช…, ๋ฆฌ์ „, vpc, vpcId, ์œ„์—์„œ ์ƒ์„ฑํ•œ service account ๋ฅผ ๋ถ™์—ฌ์ค˜์•ผํ•œ๋‹ค.

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=eks-test-cluster \
--set region=ap-southeast-1 \
--set vpcId=vpc-0fa9b98d41c276bdf \
--set serviceAccount.name=aws-load-balancer-controller

### ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ Service Account Update
helm upgrade --install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
	--set clusterName=eks-test-cluster \
	--set region=ap-southeast-1 \
	--set vpcId=vpc-0fa9b98d41c276bdf \
  --set serviceAccount.name=aws-load-balancer-controller \
  --set serviceAccount.create=false
  
### ๊ฒ€์ฆ -> ๋‚ด๊ฐ€ ์„ค์ •ํ•œ serviceAccount ๊ฐ€ ์ •์ƒ ์ถœ๋ ฅ๋˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
kubectl get pod -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller -o jsonpath="{.items[0].spec.serviceAccountName}"

### ์ˆ˜์ • ๋ฐ ์žฌ์ ์šฉ ์‹œ (restart aws-load-balancer-controller)
kubectl rollout restart deployment aws-load-balancer-controller -n kube-system

### ๋กœ๊ทธํ™•์ธ
kubectl logs -n kube-system deployment/aws-load-balancer-controller

 

5. Helm Chart ์—์„œ Application ์šฉ Ingress ์ž‘์„ฑ

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: spring-app-ingress
  annotations:
    alb.ingress.kubernetes.io/group.name: shared-alb-group
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/certificate-arn: {{ .Values.ingress.certificateArn }}
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80,"HTTPS":443}]'
    alb.ingress.kubernetes.io/ssl-redirect: '443'
    alb.ingress.kubernetes.io/backend-protocol: HTTP
    alb.ingress.kubernetes.io/healthcheck-path: /health  # LoadBalancer Health Check
spec:
  ingressClassName: alb
  rules:
    - host: {{ .Values.ingress.host }}
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: spring-app
                port:
                  number: 80

6. ArgoCD์šฉ Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    alb.ingress.kubernetes.io/group.name: shared-alb-group
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-southeast-1:211125374993:certificate/9382fcc5-245e-46be-a1ec-c36189171073 # ACM ์ธ์ฆ์„œ ARN
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80,"HTTPS":443}]'
    alb.ingress.kubernetes.io/force-ssl-redirect: 'true'
    alb.ingress.kubernetes.io/backend-protocol: HTTP
    alb.ingress.kubernetes.io/healthcheck-path: /login
    alb.ingress.kubernetes.io/success-codes: '200-399'
spec:
  ingressClassName: alb
  rules:
    - host: test-argo.luckypanda.io
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: argocd-server
                port:
                  number: 80

7. ์ ์šฉํ•˜๊ธฐ

### ์Šคํฌ๋ฆฝํŠธ ์ ์šฉ
$ kubectl apply -f {ํŒŒ์ผ๊ฒฝ๋กœ}

###  ์„œ๋ฒ„ ๋ฆฌ์Šคํƒ€ํŠธ
$ rollout restart deployment -n {๋„ค์ž„์ŠคํŽ˜์ด์Šค} {์„œ๋ฒ„๋ช…}

 



 

์ •๋ง ๋.!


 

์ง„ํ–‰ ์‹œ ๋งŒ๋‚˜๊ฒŒ ๋  ์˜ค๋ฅ˜์— ๋Œ€ํ•˜์—ฌ..

(Err) ArgoCD - err_too_many_redirects ์˜ค๋ฅ˜ ํ•ด๊ฒฐํ•˜๊ธฐ

  • ArgoCD๋ฅผ Https๋ฅผ ์—ฐ๊ฒฐํ•ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ™•์ธํ•˜๋ฉด too_many_redirect ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์›์ธ
    • ALB์˜ ๋ฆฌ๋””๋ ‰์…˜๊ณผ ArgoCD ๋ฆฌ๋””๋ ‰์…˜์ด ๊ฒน์ณ์„œ ๋ฐœ์ƒ
    • https → ALB → HTTP → ArgoCD → ๋‹ค์‹œ HTTPS ๋ฆฌ๋””๋ ‰์…˜ → ALB → …
    • → ๋ฌดํ•œ ๋ฐ˜๋ณต ๋ฆฌ๋””๋ ‰์…˜ ๋ฐœ์ƒ
  • ํ•ด๊ฒฐ๋ฐฉ์•ˆ
    • ConfigMap์˜ insecure : true ์„ค์ • ์ถ”๊ฐ€
      • ArgoCD๊ฐ€ HTTPS ๋ฆฌ๋””๋ ‰์…˜์„ ํ•˜์ง€ ์•Š๋„๋ก ์„ค์ •
    • ConfigMap ์ˆ˜์ •ํ•˜๊ธฐ
### ์ˆ˜์ • vi
kubectl edit configmap argocd-cmd-params-cm -n argocd

### ์ถ”๊ฐ€ ํ•„์š”
data:
  server.insecure: "true"
# ArgoCD Server ์žฌ์‹œ์ž‘ํ•˜์—ฌ ์ ์šฉํ•˜๊ธฐ
kubectl rollout restart deployment argocd-server -n argocd