'Server/AWS'에 해당하는 글 49건

 

Fargate 에서 실행 중인 pods 를 모니터링 할 수는 있을까.

 

초창기에 EKS 를 fargate 로 세팅한 후에 모니터링을 위해 prometheus 까지 만들어 놓으려다가, 실패하고 이제야 설치를 해봤다. 기억으로는 아마도 fargate 때문이었다. 그때까지만 해도 대부분 문서들이 전부 EC2 기반이라 삽질 조금 하다가 깔끔하게 접었었다.

 

지금은 Fargate / EC2 상관없이 AWS 에서 제공하는 AMP(Amazon Managed Service for Prometheus) 를 이용하면 쉽게 연동이 가능할 것 같은데, 역시나 서울 리전은 아직 해당 서비스를 제공하지 않는다. 그리하여 짬 내서 다시 한번 시도해 보았다.

 

일단 나는 EC2 를 쓸 생각이 없었는데, Prometheus 는 Fargate 로 설치할 수 없다. Prometheus 의 시계열 데이터베이스가 컨테이너 재시작 중에 손실되지 않도록 영구 볼륨(Persistent Volumes) 을 사용하여 Prometheus 에 지속성을 제공해야 한다. 그러려면 우선 EKS 에서 EC2 노드 그룹 (Node Group) 을 생성해야 한다.

 

 

 

1. Add Node Group

 

노드 그룹을 생성할 때는, 노드 IAM 역할(role) 을 필요로 한다.

 

IAM 대시보드에서 아래 두가지 정책을 포함하는 역할(ex. EksEC2nodeGroupRole) 을 만든다.
- AmazonEKSWorkerNodePolicy
- AmazonEC2ContainerRegistryReadOnly

 

또한, 쿠버네티스의 서비스계정에서 IAM 역할을 사용할 수 있도록 Amazon VPC CNI plugin 을 구성한다.

 

# aws-node 서비스계정 생성/업데이트
$ eksctl create iamserviceaccount \
    --name aws-node \
    --namespace kube-system \
    --cluster my-cluster \
    --role-name "AmazonEKSVPCCNIRole" \
    --attach-policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy \
    --override-existing-serviceaccounts \
    --approve
    
2023-01-31 17:42:15 [ℹ]  eksctl version 0.75.0
2023-01-31 17:42:15 [ℹ]  using region ap-northeast-2
2023-01-31 17:42:16 [ℹ]  1 iamserviceaccount (kube-system/aws-node) was included (based on the include/exclude rules)
2023-01-31 17:42:16 [!]  metadata of serviceaccounts that exist in Kubernetes will be updated, as --override-existing-serviceaccounts was set
2023-01-31 17:42:16 [ℹ]  1 task: {
    2 sequential sub-tasks: {
        create IAM role for serviceaccount "kube-system/aws-node",
        create serviceaccount "kube-system/aws-node",
    } }2023-01-31 17:42:16 [ℹ]  building iamserviceaccount stack "eksctl-my-cluster-addon-iamserviceaccount-kube-system-aws-node"
2023-01-31 17:42:16 [ℹ]  deploying stack "eksctl-my-cluster-addon-iamserviceaccount-kube-system-aws-node"
2023-01-31 17:42:16 [ℹ]  waiting for CloudFormation stack "eksctl-my-cluster-addon-iamserviceaccount-kube-system-aws-node"
2023-01-31 17:42:33 [ℹ]  waiting for CloudFormation stack "eksctl-my-cluster-addon-iamserviceaccount-kube-system-aws-node"
2023-01-31 17:42:49 [ℹ]  waiting for CloudFormation stack "eksctl-my-cluster-addon-iamserviceaccount-kube-system-aws-node"
2023-01-31 17:43:09 [ℹ]  waiting for CloudFormation stack "eksctl-my-cluster-addon-iamserviceaccount-kube-system-aws-node"
2023-01-31 17:43:26 [ℹ]  waiting for CloudFormation stack "eksctl-my-cluster-addon-iamserviceaccount-kube-system-aws-node"
2023-01-31 17:43:26 [ℹ]  serviceaccount "kube-system/aws-node" already exists
2023-01-31 17:43:26 [ℹ]  updated serviceaccount "kube-system/aws-node"

 

role 과 serviceAccounts 설정을 마쳤으면 eks 대시보드에서 노드 그룹을 추가/확인 한다. (node 수는 1개만 설정했다. 100G)

 

$ kubectl get pods -w -n kube-system
NAME                                            READY   STATUS    RESTARTS   AGE
aws-node-q123f                                  1/1     Running   0          4h5m
kube-proxy-abcmh                                1/1     Running   0          4h5m
...

 

EKS 콘솔의 추가기능에서 Amazon EBS CSI 드라이버(v1.23 버전부터 EC2 노드그룹 사용시 필요) 를 설치해야 하는데 IAM 역할을 미리 생성 해준다. (해당 권한이 올바로 설치되지 않으면 아래 storage 가 생성되지 못한다.)

 

$ eksctl create iamserviceaccount \
  --name ebs-csi-controller-sa \
  --namespace kube-system \
  --cluster my-cluster \
  --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
  --approve \
  --role-only \
  --role-name AmazonEKS_EBS_CSI_DriverRole

 

 

2. Prometheus 설치

 

# prometheus install
$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

$ helm install prometheus prometheus-community/prometheus \
     --namespace prometheus \
     --set alertmanager.persistentVolume.storageClass="gp2" \
     --set server.persistentVolume.storageClass="gp2"

NAME: prometheus
LAST DEPLOYED: Tue Jan 31 13:04:08 2023
NAMESPACE: prometheus
STATUS: deployed
REVISION: 1
NOTES:
The Prometheus server can be accessed via port 80 on the following DNS name from within your cluster:
prometheus-server.prometheus.svc.cluster.local


Get the Prometheus server URL by running these commands in the same shell:
  export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=server" -o jsonpath="{.items[0].metadata.name}")
  kubectl --namespace prometheus port-forward $POD_NAME 9090


The Prometheus alertmanager can be accessed via port  on the following DNS name from within your cluster:
prometheus-%!s(<nil>).prometheus.svc.cluster.local


Get the Alertmanager URL by running these commands in the same shell:
  export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=" -o jsonpath="{.items[0].metadata.name}")
  kubectl --namespace prometheus port-forward $POD_NAME 9093
#################################################################################
######   WARNING: Pod Security Policy has been disabled by default since    #####
######            it deprecated after k8s 1.25+. use                        #####
######            (index .Values "prometheus-node-exporter" "rbac"          #####
###### .          "pspEnabled") with (index .Values                         #####
######            "prometheus-node-exporter" "rbac" "pspAnnotations")       #####
######            in case you still need it.                                #####
#################################################################################


The Prometheus PushGateway can be accessed via port 9091 on the following DNS name from within your cluster:
prometheus-prometheus-pushgateway.prometheus.svc.cluster.local


Get the PushGateway URL by running these commands in the same shell:
  export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus-pushgateway,component=pushgateway" -o jsonpath="{.items[0].metadata.name}")
  kubectl --namespace prometheus port-forward $POD_NAME 9091

For more information on running Prometheus, visit:
https://prometheus.io/

 

모니터링은 내부에서만 사용할 예정이라 기본사양인 gp2 EBS 볼륨을 사용했으며, alertmanager 용 2G / server 용 8G 볼륨이 생성된다.

 

# check prometheus pods
$ kubectl get all -n prometheus
NAME                                                    READY   STATUS    RESTARTS   AGE
pod/prometheus-alertmanager-0                           1/1     Running   0          85s
pod/prometheus-kube-state-metrics-7cdcf7cc98-m7n9q      1/1     Running   0          85s
pod/prometheus-prometheus-node-exporter-552n8           0/1     Pending   0          85s
pod/prometheus-prometheus-node-exporter-5hkjp           0/1     Pending   0          84s
pod/prometheus-prometheus-node-exporter-5k54s           0/1     Pending   0          85s
pod/prometheus-prometheus-node-exporter-77gsb           1/1     Running   0          84s
pod/prometheus-prometheus-node-exporter-9ckh5           1/1     Running   0          85s
pod/prometheus-prometheus-node-exporter-gtvht           0/1     Pending   0          85s
pod/prometheus-prometheus-node-exporter-hxkn6           0/1     Pending   0          84s
pod/prometheus-prometheus-node-exporter-rxl4b           0/1     Pending   0          85s
pod/prometheus-prometheus-node-exporter-xr468           0/1     Pending   0          85s
pod/prometheus-prometheus-node-exporter-zqxrm           0/1     Pending   0          85s
pod/prometheus-prometheus-pushgateway-9d598d466-844ct   1/1     Running   0          85s
pod/prometheus-server-6487b794-qtddq                    2/2     Running   0          85s

NAME                                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/prometheus-alertmanager               ClusterIP   172.20.40.182    <none>        9093/TCP   85s
service/prometheus-alertmanager-headless      ClusterIP   None             <none>        9093/TCP   85s
service/prometheus-kube-state-metrics         ClusterIP   172.20.106.187   <none>        8080/TCP   85s
service/prometheus-prometheus-node-exporter   ClusterIP   172.20.144.95    <none>        9100/TCP   85s
service/prometheus-prometheus-pushgateway     ClusterIP   172.20.17.45     <none>        9091/TCP   85s
service/prometheus-server                     ClusterIP   172.20.39.89     <none>        80/TCP     85s

NAME                                                 DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/prometheus-prometheus-node-exporter   10        10        2       10           2           <none>          85s

NAME                                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/prometheus-kube-state-metrics       1/1     1            1           85s
deployment.apps/prometheus-prometheus-pushgateway   1/1     1            1           85s
deployment.apps/prometheus-server                   1/1     1            1           85s

NAME                                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/prometheus-kube-state-metrics-7cdcf7cc98      1         1         1       86s
replicaset.apps/prometheus-prometheus-pushgateway-9d598d466   1         1         1       86s
replicaset.apps/prometheus-server-6487b794                    1         1         1       86s

NAME                                       READY   AGE
statefulset.apps/prometheus-alertmanager   1/1     86s

 

만약 alertmanager 가 Pending 상태라면 prometheus uninstall 후에, 해당 EBS 볼륨(아마도 2G)도 직접 삭제하고, pvc 도 조회하여 삭제한다. (kubectl delete pvc name -n namespace)... prometheus uninstall 때 기존 alertmanager 관련 볼륨정보가 삭제되지 않는듯... 여러번 설치테스트 하다가 이것 때문에 삽질 좀 했다.

 

0/9 nodes are available: 1 node(s) had volume node affinity conflict, 8 node(s) had taint {eks.amazonaws.com/compute-type: fargate}, that the pod didn't tolerate.

 

또한 node-exporter 는 ec2 노드에서 작동하는 daemonset 으로 실행되지만 이미 생성된 fargate 노드들이 daemonset 을 지원하지 않기 때문에 fargate 에서 schedule 을 수행할 수 없다. node-exporter 의 역할은 실행중인 node에 배치되어 해당 호스트의 low-level 매트릭(vCPU, Mem, Network, Disk) 수집을 담당하는데, kube-state-metrics 가 이 메트릭을 대신 수집해 주어 node-exporter 없이도 지표 수집이 가능한 것 같아, node-exporter 를 전부 삭제했다.

 

# node-exporter 삭제
$ kubectl delete daemonset prometheus-prometheus-node-exporter -n prometheus

 

 

3. prometheus URL 접속 확인 (선택)

 

# Prometheus server URL
$ export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=server" -o jsonpath="{.items[0].metadata.name}")
# 127.0.0.1:9090
$ kubectl --namespace prometheus port-forward $POD_NAME 9090

# Alertmanager URL
$ export POD_NAME=$(kubectl get pods --namespace prometheus -l "app.kubernetes.io/name=alertmanager" -o jsonpath="{.items[0].metadata.name}")
# 127.0.0.1:9093
$ kubectl --namespace prometheus port-forward $POD_NAME 9093

# PushGateway URL 
$ export POD_NAME=$(kubectl get pods --namespace prometheus -l "app.kubernetes.io/name=prometheus-pushgateway" -o jsonpath="{.items[0].metadata.name}")
# 127.0.0.1:9091
$ kubectl --namespace prometheus port-forward $POD_NAME 9091

 

 

 

4. Grafana 설치

 

$ vi grafana.yaml
datasources:
  datasources.yaml:
    apiVersion: 1
    datasources:
    - name: Prometheus
      type: prometheus
      url: http://prometheus-server.prometheus.svc.cluster.local
      access: proxy
      isDefault: true


# grafana install
$ helm repo add grafana https://grafana.github.io/helm-charts
$ helm install grafana grafana/grafana \
    --namespace prometheus \
    --set persistence.storageClassName="gp2" \
    --set persistence.enabled=true \
    --set adminPassword='admin_password' \
    --values ./grafana.yaml \
    --set service.type=LoadBalancer

W0131 18:50:26.890856   44556 warnings.go:70] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
W0131 18:50:27.492718   44556 warnings.go:70] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
NAME: grafana
LAST DEPLOYED: Tue Jan 31 18:50:26 2023
NAMESPACE: prometheus
STATUS: deployed
REVISION: 1
NOTES:
1. Get your 'admin' user password by running:

   kubectl get secret --namespace prometheus grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

2. The Grafana server can be accessed via port 80 on the following DNS name from within your cluster:

   grafana.prometheus.svc.cluster.local

   Get the Grafana URL to visit by running these commands in the same shell:
   NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        You can watch the status of by running 'kubectl get svc --namespace prometheus -w grafana'
     export SERVICE_IP=$(kubectl get svc --namespace prometheus grafana -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
     http://$SERVICE_IP:80

3. Login with the password from step 1 and the username: admin

 

 

grafana 설치 확인

 

# Grafana URL 
$ kubectl get svc --namespace prometheus grafana -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
abc29d87564994207b5dfb965886a11e-1234385928.ap-northeast-2.elb.amazonaws.com

 

 

 

[ dashboard id: 12006 ]

 

 

 

# 만약 망했으면 uninstall 하고 처음부터 다시 try...
$ helm uninstall prometheus --namespace prometheus
$ helm uninstall grafana --namespace prometheus

 

삭제되지 않은 볼륨들도 모두 삭제한다.

 

대부분의 쿠버네티스 스펙이 helm 으로 한방에 설치되니까 편하긴 한듯...

 


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

일반적으로 EKS cluster 를 생성한 계정 이외에 다른 IAM 사용자나 role 을 세분화하여 추가할 수 있으며, 심플한 사용자 추가를 진행해 보았다. 예전에 뭣 때문에 eksctl 을 설치했는지는 모르겠다만 땡큐~

 

eksctl 이 설치되어 있지 않다면... https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/eksctl.html

 

eksctl 설치 또는 업데이트 - Amazon EKS

GitTag 버전은 0.105.0 이상이어야 합니다. 그렇지 않은 경우 터미널 출력에서 설치 또는 업그레이드 오류가 있는지 확인하거나, 1단계의 주소를 https://github.com/weaveworks/eksctl/releases/download/v0.105.0/eksct

docs.aws.amazon.com

 

eksctl 을 이용하면 간단히 cluster 정보와 추가할 iam 계정의 arn, ClusterRoleBinding group 을 지정한다.

 

$ eksctl create iamidentitymapping \
    --cluster cluster-name \
    --region=ap-northeast-2 \
    --arn arn:aws:iam::111122223333:user/ggamzzak \
    --group system:masters \
    --profile my-profile

2022-08-02 17:25:32 [ℹ]  eksctl version 0.75.0
2022-08-02 17:25:32 [ℹ]  using region ap-northeast-2
2022-08-02 17:25:32 [ℹ]  adding identity "arn:aws:iam::111122223333:user/ggamzzak" to auth ConfigMap

 

"system:masters" 그룹의 자격 증명을 사용하면 cluster-admin 슈퍼유저의 역할을 바인딩하여 모든 작업을 수행할 수 있다.

 


 

configmap/aws-auth 를 확인하면 다음과 같이 추가되어 있다.

 

$ kubectl describe configmap -n kube-system aws-auth
Name:         aws-auth
Namespace:    kube-system
Labels:       <none>
Annotations:  <none>

Data
====
mapUsers:
----
- groups:
  - system:masters
  userarn: arn:aws:iam::111122223333:user/ggamzzak 

mapRoles:
...

 

또는,

 

$ eksctl get iamidentitymapping --cluster my-cluster --region=ap-northeast-2 --profile my-profile
2022-08-02 20:52:23 [ℹ]  eksctl version 0.75.0
2022-08-02 20:52:23 [ℹ]  using region ap-northeast-2
ARN                                                             USERNAME                        GROUPS                                                                          ACCOUNT
...
arn:aws:iam::111122223333:user/ggamzzak                                                         system:masters

 

추가된 IAM 계정으로 테스트 ㄱㄱ~

 

 

 


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

AWS CDK

Server/AWS 2022. 2. 13. 23:54

AWS CDK(Cloud Development Kit) 를 이용하면 Java, Python, Script 등을 통해 AWS CloudFormation 을 만들어 AWS 클라우드 리소스를 프로비저닝 할 수 있다. AWS 콘솔 창에서 클릭과 타이핑으로 생성하던 리소스들을 code 화 하여, 반복적인 리소스 생성이 필요할 때 유용하다.

 

쉽게 말해 CDK 에서 지원하는 언어(Java, Python, TypeScript, ...) 로 프로그래밍하고, 커맨드 라인 도구인 CDK 툴킷을 통해 AWS CloudFormation 템플릿을 생성하고, stack 을 배포하여 AWS 의 모든 리소스들을 생성/설정할 수 있다.

 

준비해야 할 것은..

  • AWS cli 설치
  • 자격증명(credentials)
  • cdk 설치...
    > npm install -g aws-cdk@1.140.0

 

현재 CDK 는 v2 버전대가 있지만, 이런저런 이유으로 인하여 v1 을 사용하였다. 차이라면 v2 는 새로나온 리소스등을 계속해서 추가해 줄 것이고, v1 은 점점 deprecated 되는 메소드들이 많아질 것이다. 또 bootstrap 도 v2 에서는 필수로 설치하는 거 같고... 참고로 AWS 는 2023년 6월 1일에 CDK v1 에 대한 지원을 종료한다.

 

준비를 마쳤다면  Java(JDK 8+) 로 S3 버킷을 하나 생성하는 간단한 단계는 다음과 같다.

 

 

1. cdk 프로젝트 자동 생성

 

> cdk init app --language java

 

위 명령은 현재 디렉토리에서 cdk 를 쉽게 개발할 수 있도록 관련 library 들의 설정이 포함된 maven 기반의 빈 프로젝트를 생성해 준다. IDE 로 열어보면 Java 기본 구조로 된 maven 프로젝트를 볼 수 있다.

 

 

2. dependency 추가

 

<dependency>
    <groupId>software.amazon.awscdk</groupId>
    <artifactId>s3</artifactId>
    <version>${cdk.version}</version>
</dependency>

 

v2 는 대부분의 리소스를 기본적으로 지원하는 거 같긴한데... 필요하다면 추가할 artifactId 는 대부분의 AWS 서비스 이름으로 검색하고 dependency 에 추가해야 한다.

 

 

3. java 파일 확인/수정/빌드

 

main 메소드가 들어있는 App.java 파일과, 기본적인 Stack.java 파일이 생성되어 있다. App 에는 하나 이상의 Stack(구체적인 AWS 리소스) 을 정의할 수 있다. Stack 파일을 바탕으로 리소스를 추가하면 되고, main 메소드에는 계정 정보와 함께 새로 생성한 Stack 파일을 로드하면 된다.

 

// App.java
public class App {
    public static void main(final String[] args) {
        App app = new App();

        new MyS3Stack(app, "MyCdkS3Stack", StackProps.builder()
                .env(Environment.builder()
                        .account("111122223333")
                        .region("ap-northeast-2")
                        .build())
                build());

        app.synth();
    }
}

 

// MyS3Stack
public class MyS3Stack extends Stack {
    public MyS3Stack(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public MyS3Stack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        Bucket.Builder.create(this, "MyFirstBucket")
                .bucketName("cdk-test1")
                .build();
    }
}

 

Stack 의 모든 리소스는 scope, id, props 등으로 구성되는데, scope 는 부모 stack 을 명시한다. id 는 App 내부에서 사용될 id(hash 문자열이 붙음). props 는 해당 리소스의 속성이다.(null 가능) 위 예제에서는 각각 this, MyfirstBucket, bucketName 등이 되겠다. Java에서는 props를 전달하기 위해 Builder가 제공된다. 위 예에서는 BucketProps 나 Bucket 을 사용할 수 있다.

 

// Bucket
Bucket.Builder.create(this, "MyFirstBucket")
    .bucketName("cdk-test1")
    .build();

// BucketProps
new Bucket(this, "MyFirstBucket", new BucketProps.Builder()
    .bucketName("cdk-test1")
    .build());

 

 

4. CDK 병합(synth)

 

코드를 모두 작성했다면 App 의 루트 디렉토리에서 synth / deploy 명령으로 병합/배포해야 한다. cdk synth 를 실행하면 자동으로 빌드(mvn package) 도 되지만, 수동으로 빌드하여 미리 에러 등을 체크할 수 있다. 마찬가지로 deploy 역시 synth 를 자동으로 실행 해주기 때문에 사실상 빌드없이 deploy 만 해도 무방하긴 하다.

 

> cdk synth --profile my
Resources:
  MyFirstBucketB8881111:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: cdk-test1
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Metadata:
      aws:cdk:path: MyS3Stack/MyFirstBucket/Resource
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:H5sIAAAAAAAA/yWKQQ5EIAwA3+Idq1UfYNbbHvUFpNYEiZBQ0APh70Y8zSQzCNgN0FajvqSm1TaZfGDIS9Rk1eSdxJAoqmlzM4tPgbio55Ue8i+R4bd9Vor651M3iNAC9tUuxtQhuWgOhvnjDSNKfjhxAAAA
    Metadata:
      aws:cdk:path: MyS3Stack/CDKMetadata/Default

 

위와 비슷한 yaml 형식으로 출력되며 cdk.out/MyS3Stack.template.json 파일에 저장되고, 배포시 cdk.out 디렉토리를 기반으로 배포된다.

aws-cli 에 profile 이 여러개일 경우 원하는 profile 로 지정해야 한다. 소스와 aws-cli 의 계정이 일치하지 않는 경우 아래와 같은 에러가 발생한다.

 

Need to perform AWS calls for account 111122223333, but the current credentials are for 444455556666

 

위 오류가 발생해서 profile 을 정상적으로 설정했는데 아래 오류가 또 발생했다.

 

MyCdkS3Stack (MyFirstBucketB8881111) cdk-test1 already exists
The stack named KpsCdkS3Stack failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE

 

S3 를 확인해보니 비어 있는데 웬 cdk-test1 already exists... 처음 profile 오류나면서 잘못된 계정으로 cdk-test1 을 생성하려는 시도가 있었을 것이고, 그 정보가 어딘가에 남아 있는듯...; (이해할 수 없음)

 

 

5. CDK 배포(destroy)

 

deploy 로 배포하고 나면, CloudFormation 과 S3 에서 리소스들을 확인할 수 있다.

 

> cdk deploy --profile my
MyS3Stack: deploying...
MyS3Stack: creating CloudFormation changeset...
  0/3 |오후 12:12:13 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack | MyS3Stack User Initiated
  0/3 |오후 12:12:18 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | MyS3Stack User Initiated
  0/3 |오후 12:12:22 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata)
  0/3 |오후 12:12:22 | CREATE_IN_PROGRESS   | AWS::S3::Bucket    | MyFirstBucket (MyFirstBucketB8881111)
  0/3 |오후 12:12:24 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
  0/3 |오후 12:12:24 | CREATE_IN_PROGRESS   | AWS::S3::Bucket    | MyFirstBucket (MyFirstBucketB8881111) Resource creation Initiated
  1/3 |오후 12:12:24 | CREATE_COMPLETE      | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata)
  2/3 |오후 12:12:45 | CREATE_COMPLETE      | AWS::S3::Bucket    | MyFirstBucket (MyFirstBucketB8881111)
  3/3 |오후 12:12:46 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | MyS3Stack

 ✅  KpsCdkS3Stack

Stack ARN:
arn:aws:cloudformation:ap-northeast-2:111122223333:stack/MyS3Stack/1231dca0-8a2f-12ec-8c62-0618df8ff132

 

cloudformation-cdk

 

s3-cdk

 

여러 스택을 동시 실행하는 것도 가능하다.

 

> cdk deploy Happy Grumpy   # app defines two or more stacks; two are deployed
> cdk synth "Stack?"    # Stack1, StackA, etc.
> cdk deploy "*Stack"   # PipeStack, LambdaStack, etc.

 

 

6. CDK 재배포

 

> cdk diff --profile my

 

위 명령으로 기존 소스와 수정된 소스간의 변경점을 출력해 볼 수 있으며, cdk deploy 로 계속해서 코드의 변경사항을 리소스에 적용할 수 있다. (버전 관리가 유용할 수 있다.)

 

 

7. stack 삭제

 

생성한 stack 을 삭제한다.

 

> cdk ls
MyS3Stack

> cdk destroy MyS3Stack --profile my
Are you sure you want to delete: MyS3Stack (y/n)? y
MyS3Stack: destroying...

 ✅  MyS3Stack: destroyed

 

CloudFormation 의 Stack 은 정상적으로 삭제되었지만, S3 버킷은 삭제되지 않았다. 기본적으로 사용자 데이터가 포함될 수 있는 리소스에는 RETAIN 의 removePolicy 속성이 있으며, 해당 리소스는 생성될 때 스택과 분리되므로 수동으로 삭제해야 한다. Stack 삭제시 비어있는 버킷을 삭제하고자 한다면 removePolicy 설정을 Destroy 변경하면 된다. 또한 버킷이 비어있지 않다면 이 또한 실패하는데, 버킷의 autoDeleteObjects 속성을 true로 설정할 수 있다.

 

.removalPolicy(RemovalPolicy.DESTROY)
.autoDeleteObjects(true)

 

코드 변경하며 여러 버킷을 만들었었는데 마지막 소스에만 국한하지 않고, 모든 버킷을 삭제한 건 조금 굿잡...

 

 

Java 의 더 많은 예제는 요기...
https://github.com/aws-samples/aws-cdk-examples/tree/master/java

 

GitHub - aws-samples/aws-cdk-examples: Example projects using the AWS CDK

Example projects using the AWS CDK. Contribute to aws-samples/aws-cdk-examples development by creating an account on GitHub.

github.com

 

 

약 3일 정도 CDK 를 사용해 봤다. 위에 s3 의 예는 약 100개 이상의 리소스 중에 하나일 뿐이다. API 꾸역꾸역 찾아가며 각종 리소스를 생성해 봤다. Terraform 이든 CDK 든 당연히 누군가에게는 좋은 IaC 도구이다. 본인이 얼마나 많은 리소스를 관리해야 할지에 따라 필요할 수도, 그렇지 않을 수도 있다. 디테일한 설정을 위해서 리소스마다 많은 옵션들을 찾아봐야 하며 정상적으로 세팅이 되는지 테스트해야 하고... 이런 시간들이 만만치 않다. 여러 설정들을 생성하고 수정하고를 테스트해 보았는데 실제 운영중인 리소스들을 과연 CDK 를 이용하여 한번에 컨트롤할 수 있을지는 확신하지 못했다. 그리고 반복적인 작업도 마찬가지로 코드레벨에서 naming 과 ip 대역 등의 무수한 수정들이 동반된다고 할 때 CDK 가 과연 나에게 필요한 것인지에 대해서는 확신할 수 없었다. 반복적인 작업에 대해 시간을 단축하려고 맛을 보긴 했지만, 내가 앞으로 지금까지의 시스템을 복제하는데 3일은 안걸릴 것이라는 확신과 함께 빨리 손을 떼었다. 언젠가 시간이 좀 날때 다시 봐야할 듯...


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

 

eks-vcpu-memory

 

EKS Fargate 를 기본사양으로 약 1달 정도 사용해 봤다. Fargate 의 기본사양(최소사양)은 vCPU 0.25 / memory 0.5GB 이다. 1개의 CPU 코어가 2 vCPU 니까 0.25면... Spring Jar 파일 돌리는데 부팅시 스프링 로그가 2초에 1줄 정도씩 나왔나. 당연히 부팅시간도 상당히 늘어난다. ALB 의 health check 도 길어지고... 부팅된 후에는 Lazy 설정 때문에 Slave DataSource 가 처음 세팅될 때도 10초 이상의 응답으로 Timeout 이 발생하기도 했다. 그것 빼고는 단일 어플리케이션 돌아가는데 큰 문제는 없어보였다. 이것저것 다 빼다보면 어디다 써야할지... t2.nano 보다도 낮은 사양... 우선 xms / xmx 을 확인해 봤다.

 

public static void main(String[] args) {
    int mb = 1024 * 1024;
    MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    long xmx = memoryBean.getHeapMemoryUsage().getMax() / mb;
    long xms = memoryBean.getHeapMemoryUsage().getInit() / mb;
    logger.info("xms: {}", xms);
    logger.info("xmx: {}", xmx);
}

// print
xms: 8
xmx: 123

 

오마이... 저런 단위는 처음보는 수치이다. 이 상태로 잘도 돌아갔다. 일단 컴퓨팅 사양부터 높였다.

 

vCPU 1개(1000m) 당 : 월 $1.2
memory 1GB 당 : 월 $1.3

 

pod 를 정의한 deployment yaml 파일에서 cpu 와 memory 를 수정하였다.

 

spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory

 

$ vi my-deployment.yaml
...
spec:
  containers:
  - name: web
    image: 1234567890.dkr.ecr.ap-northeast-2.amazonaws.com/web:latest
    resources:
      limits:
        cpu: 1
        memory: 2048Mi
      requests:
        cpu: 1
        memory: 2048Mi

$ kubectl describe pods podname -n mynamespace
...
Annotations:          CapacityProvisioned: 0.25vCPU 0.5GB

 

재배포하였으나 cpu / memory 는 변경되지 않았다. 어딘가에 제한이 걸려있는 느낌.

 

LimitRange 를 사용하여 namespace 레벨에서 cpu 와 memory 를 설정해 보았다.

 

$ my-usage-range.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: low-usage-range
spec:
  limits:
  - max:
      cpu: 2
      memory: 4G 
    min:
      cpu: 1
      memory: 1G
    type: Container
    
$ kubectl apply -f my-usage-range.yaml -n my-namespace

 

다시 재배포해보니 잘 바뀌었다.

 

$ kubectl describe pods podname -n mynamespace
...
Annotations:          CapacityProvisioned: 1vCPU 3GB
...
    Limits:
      cpu:     1
      memory:  2048Mi
    Requests:
      cpu:     1
      memory:  2048Mi

 

뭔가 바뀌었지만 memory 2048Mi 를 설정했는데 3GB 로 세팅됐다. Fargate 는 각 pod 의 메모리 예약에 256MB 를 추가하기 때문에 기본 단위상 1G가 더 늘어났다. 256MB 를 제외하고 1792Mi 로 설정하면 2G로 뙇!

 

Dockerfile 에 Xms / Xmx 설정도 뙇!

ENTRYPOINT ["java","-Xms1024m","-Xmx1792m","-jar","/app.jar"]

 

 


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

EKS 무중단 배포

Server/AWS 2022. 1. 11. 19:57

 

$ kubectl get pods -o wide -w -n exapi
NAME                         READY   STATUS    RESTARTS   AGE   IP            NODE                                                     NOMINATED NODE                                READINESS GATES
oauth-b88bb75fb-cbpfb        1/1     Running             0          12m     10.1.19.219   fargate-ip-10-1-19-219.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-b88bb75fb-cbpfb        1/1     Running             0          12m     10.1.19.219   fargate-ip-10-1-19-219.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-b88bb75fb-zx68l        1/1     Running             0          9m47s   10.1.27.53    fargate-ip-10-1-27-53.ap-northeast-2.compute.internal    <none>                                        1/1
oauth-b88bb75fb-zx68l        1/1     Running             0          9m47s   10.1.27.53    fargate-ip-10-1-27-53.ap-northeast-2.compute.internal    <none>                                        1/1
oauth-6889b9d9d8-89f8d       0/1     Pending             0          0s      <none>        <none>                                                   <none>                                        0/1
oauth-6889b9d9d8-89f8d       0/1     Pending             0          1s      <none>        <none>                                                   3e4a3b4284-f3a1e8e9245847e19d248aa203cec099   0/1
oauth-6889b9d9d8-89f8d       0/1     Pending             0          65s     <none>        fargate-ip-10-1-22-204.ap-northeast-2.compute.internal   3e4a3b4284-f3a1e8e9245847e19d248aa203cec099   0/1
oauth-6889b9d9d8-89f8d       0/1     ContainerCreating   0          65s     <none>        fargate-ip-10-1-22-204.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-89f8d       1/1     Running             0          100s    10.1.22.204   fargate-ip-10-1-22-204.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-89f8d       1/1     Running             0          101s    10.1.22.204   fargate-ip-10-1-22-204.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-89f8d       1/1     Running             0          102s    10.1.22.204   fargate-ip-10-1-22-204.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-89f8d       1/1     Running             0          2m11s   10.1.22.204   fargate-ip-10-1-22-204.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-89f8d       1/1     Running             0          3m34s   10.1.22.204   fargate-ip-10-1-22-204.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-6889b9d9d8-89f8d       1/1     Running             0          3m34s   10.1.22.204   fargate-ip-10-1-22-204.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-b88bb75fb-zx68l        1/1     Terminating         0          14m     10.1.27.53    fargate-ip-10-1-27-53.ap-northeast-2.compute.internal    <none>                                        1/1
oauth-6889b9d9d8-89f8d       1/1     Running             0          3m34s   10.1.22.204   fargate-ip-10-1-22-204.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-6889b9d9d8-8n2mf       0/1     Pending             0          0s      <none>        <none>                                                   <none>                                        0/1
oauth-6889b9d9d8-8n2mf       0/1     Pending             0          1s      <none>        <none>                                                   16625974aa-a093eb0abc954f958086de0c456c0ea2   0/1
oauth-b88bb75fb-zx68l        0/1     Terminating         0          14m     10.1.27.53    fargate-ip-10-1-27-53.ap-northeast-2.compute.internal    <none>                                        1/1
oauth-b88bb75fb-zx68l        0/1     Terminating         0          14m     10.1.27.53    fargate-ip-10-1-27-53.ap-northeast-2.compute.internal    <none>                                        1/1
oauth-b88bb75fb-zx68l        0/1     Terminating         0          14m     10.1.27.53    fargate-ip-10-1-27-53.ap-northeast-2.compute.internal    <none>                                        1/1
oauth-6889b9d9d8-8n2mf       0/1     Pending             0          50s     <none>        fargate-ip-10-1-18-160.ap-northeast-2.compute.internal   16625974aa-a093eb0abc954f958086de0c456c0ea2   0/1
oauth-6889b9d9d8-8n2mf       0/1     ContainerCreating   0          50s     <none>        fargate-ip-10-1-18-160.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-8n2mf       1/1     Running             0          85s     10.1.18.160   fargate-ip-10-1-18-160.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-8n2mf       1/1     Running             0          86s     10.1.18.160   fargate-ip-10-1-18-160.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-8n2mf       1/1     Running             0          87s     10.1.18.160   fargate-ip-10-1-18-160.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-8n2mf       1/1     Running             0          116s    10.1.18.160   fargate-ip-10-1-18-160.ap-northeast-2.compute.internal   <none>                                        0/1
oauth-6889b9d9d8-8n2mf       1/1     Running             0          3m19s   10.1.18.160   fargate-ip-10-1-18-160.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-6889b9d9d8-8n2mf       1/1     Running             0          3m19s   10.1.18.160   fargate-ip-10-1-18-160.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-6889b9d9d8-8n2mf       1/1     Running             0          3m19s   10.1.18.160   fargate-ip-10-1-18-160.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-b88bb75fb-cbpfb        1/1     Terminating         0          20m     10.1.19.219   fargate-ip-10-1-19-219.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-b88bb75fb-cbpfb        0/1     Terminating         0          20m     10.1.19.219   fargate-ip-10-1-19-219.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-b88bb75fb-cbpfb        0/1     Terminating         0          20m     10.1.19.219   fargate-ip-10-1-19-219.ap-northeast-2.compute.internal   <none>                                        1/1
oauth-b88bb75fb-cbpfb        0/1     Terminating         0          20m     10.1.19.219   fargate-ip-10-1-19-219.ap-northeast-2.compute.internal   <none>                                        1/1

 

자알 돌아간다~ 정상적인 것처럼 보이나...

 

 

 

Default 값을 믿고 열심히 삽질해 준 나에게 감사한다.

 

Rolling Update 시 가장 먼저 선행해야 할 것은 strategy 명시이다. 기본값으로 테스트하겠다고 이 부분을 생략한게 큰 타격이 됐다.

 

$ my-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: exapi
  name: api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 0         # 0 fast
      maxUnavailable: 1   # 0 slow

 

maxSurge 와 maxUnavailable 값은 배포시 최대 생성 pod 수와 종료 수를 명시할 수 있다. 각각 0/1, 1/0 으로 설정할 경우 running 상태 이후에 terminating 을 시키느냐, pending 과 동시에 terminating 을 시키느냐의 차이이며 약 30초 정도의 차이를 확인했다.

 

겉보기에는 잘 작동하는 것처럼 보이나 배포시 약 5~10초간 502 Gateway Error 뒤에 504 Gateway Time-out 이 발생한다. 우선 502 에러가 발생하는 시점을 찾아봤다.

 

쿠버네티스에서 kubectl rollout restart 명령으로 배포할 경우, 새 pod 가 추가되고 Running 상태가 되면 기존 포드가 삭제되는 식이다. 확인 결과 pod 가 Terminating 되는 순간에 502 에러가 발생했다. ALB 의 Target 이 동시에 draining 되는 시점이기도 하다. 인터넷을 후벼파서 결과, 502 에러를 최소화 할 수 있는 방법을 찾아 결국 해냈다.

 

 

 

502 Gateway Error / 504 Gateway Time-out Error 최소화

 

1. ingress : connection-draining 설정

 

ALB 에 connection-draining 관련 옵션을 주어 기존 연결에 대한 처리를 유지한다. (이미 기본값으로 동작중. 효과없음.)

 

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  ...
  annotations:
    ...
    service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "30"

 

 

2. pod : readiness-gate 설정

 

readiness-gate 옵션을 활성화 하여 새로운 target 이 healthy 상태로 연결되기 전까지 기존 target 을 종료하지 않음. (마찬가지로 502 에러는 발생하나, ALB 내의 target 이 healthy 상태가 한개도 유지되지 않는 어이없는 상황은 막을 수 있음). 반드시 필요!

 

$ kubectl label namespace {mynamespace} elbv2.k8s.aws/pod-readiness-gate-inject=enabled

 

apiVersion: apps/v1
kind: Deployment
metadata:
spec:
  template:
    spec:
      containers:
      - name: api
      readinessGates:
      - conditionType: target-health.alb.ingress.k8s.aws/api-ingress_api-nodeport_80

 

readiness-gate 옵션을 활성화할 namespace 에 레이블을 설정하고, target-health.alb.ingress.k8s.aws/{ingress-name}_{service-name}_{port} 를 위처럼 삽입한다. 설정이 올바르지 않으면 ALB 에서 target 그룹을 인식하지 못할 수도 있으니 주의!

 

 

3. pod : preStop 설정

 

life-cycle hooks 에서 pod 가 중지되기 전에(preStop) 딜레이를 가지며 기존 연결을 마저 처리한다. 새로운 연결이 이루어지지 않는다. (인터넷에서 좋아요도 가장 많고, 실제로 preStop 설정만으로 502 에러를 해결하였음.)

 

kind: Deployment
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: api
        lifecycle:
          preStop:
            exec:
              command: ["sleep", "60"]

 

 

4. pod : terminationGracePeriodSeconds 설정

 

preStop 가 실패했을 때 대신 컨테이너를 종료시킨다. (preStop sleep 값보다 +10초 정도로 설정: default 값은 45초)

 

kind: Deployment
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: web-api
      ...
      terminationGracePeriodSeconds: 70

 

 

5. pod livenessProbe / readinessProbe 설정

 

pod 의 활성화 상태를 나타내는 livenessProbe 가 실패하면 재시작 정책의 대상이 된다. pod 준비 상태를 나타내는 readinessProbe 가 실패하면 해당 pod 는 모든 엔드포인트에서 제거된다. (두 방법 모두 비정상 pod 에 연결을 못받게 하여 502 에러를 줄인다고는 하는데 효과는 잘 모르겠음.)

 

kind: Deployment
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: api
      ...
        livenessProbe:
          httpGet:
            path: /
            port: 80
          periodSeconds: 4
          timeoutSeconds: 5
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /
            port: 80
            scheme: HTTP
          initialDelaySeconds: 30
          periodSeconds: 10

 

 


 

정리

 

이 5가지 설정 중 3가지를 사용하여 무중단 배포를 완벽하게 해결했다.

 

preStop
readinessGates
terminationGracePeriodSeconds

 

readinessGates 설정을 가장 먼저 한 바람에 필수요소인지는 확실치 않다. (조만간 테스트 예정)
preStop 설정으로 무중단 배포를 해결했다. (본인의 서비스와 사양에 맞는 설정이 필요할 수 있다.)
terminationGracePeriodSeconds 설정을 하지 않아도 문제는 없었다. 내 경우 기본값이 45초 만으로 충분한 듯.

 

한가지 더... kubectl rollout 으로 테스트 할 때 기존의 pod 가 원치 않는 동작을 할 수도 있다. 항상 기존 pod 는 delete 로 삭제한 후에 새로 생성하여 테스트 하는 것이 원하는 결과를 얻는데 도움이 될 것 같다.

 

무중단 배포도 안된다고 며칠을 eks 욕하고 있었는데... 정말 다행이다. ^_______________^

 

 


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,