728x90
반응형
1. Kubenetes Storage
- 파드(컨테이너) 환경에서 별도의 마운트를 진행하지 않는다면 데이터는 컨테이너가 종료될 경우 모두 삭제됩니다.
- 즉, 상태가 저장되지 않는(Stateless) 형태로 동작합니다.
- 하지만 Kubenetes로 운영을 하다보면 DB(데이터베이스)와 같이 데이터가 보존될 필요가 있는 경우가 생기게 되며, Kubenetes에서는 PV/PVC를 통해 데이터를 마운트 시켜 상태를 보존할 수 있는 Stateful 형태로 관리할 수 있습니다.
- Persistent Volume(PV)은 실제 스토리지 볼륨으로 PV는 Kubernetes Pods의 수명 주기와 별개로 자체의 수명 주기를 가집니다. 예를 들어 NFS, AWS EBS, Ceph등을 사용할 수 있습니다.
- 접근 모드(Access mode)
- 접근 모드는 PV가 생성될 때 설정되며 Kubernetes가 볼륨을 마운트하는 방법으로 Persistent Volumes은 세 가지의 접근 모드 지원됩니다.
- ReadWriteOnce: 볼륨은 동시에 하나의 노드에서만 읽기/쓰기를 허용합니다.
- ReadOnlyMany: 볼륨은 동시에 여러 노드에서 읽기 전용 모드를 허용합니다.
- ReadWriteMany: 볼륨은 동시에 여러 노드에서 읽기/쓰기를 허용합니다.
- 접근 모드는 PV가 생성될 때 설정되며 Kubernetes가 볼륨을 마운트하는 방법으로 Persistent Volumes은 세 가지의 접근 모드 지원됩니다.
- PersistentVolumeClaim(PVC)은 Kubernetes는 PV를 포드에 연결하는데 필요한 추가 추상화 계층을 뜻합니다.
2. CSI (Container Storage Interface)
- Kubernetes source code 내부에 존재하는 AWS EBS provisioner는 당연히 Kubernetes release lifecycle을 따라서 배포되므로, provisioner 신규 기능을 사용하기 위해서는 Kubernetes version을 업그레이드해야 하는 제약 사항이 있습니다.
- 따라서, Kubernetes 개발자는 Kubernetes 내부에 내장된 provisioner (in-tree)를 모두 삭제하고, 별도의 controller Pod을 통해 동적 provisioning을 사용할 수 있도록 만들었습니다. 이것이 바로 CSI (Container Storage Interface) driver입니다.
- AWS EBS CSI driver 역시 아래와 같은 구조를 가지는데, 오른쪽 StatefulSet 또는 Deployment로 배포된 controller Pod이 AWS API를 사용하여 실제 EBS volume을 생성하는 역할을 합니다.
- 왼쪽 DaemonSet으로 배포된 node Pod은 AWS API를 사용하여 Kubernetes node (EC2 instance)에 EBS volume을 attach 해줍니다.
3. AWS EBS Controller
3-1) EBS CSI Driver
- Elastic Block Store CSI Driver는 Amazon EBS 볼륨의 수명주기를 관리하기 위해 컨테이너 조정자가 사용하는 CSI 인터페이스를 제공합니다.
- persistentvolume, persistentvolumeclaim의 accessModes는 ReadWriteOnce로 설정해야 합니다.
- EBS Storage의 기본 설정이 동일 AZ(가용역역)에 있는 EC2 인스턴스에 배포된 파드에 연결되기 때문입니다.
3-2) Terraform을 통한 EBS CSI Driver 배포
- EBS CSI Driver를 위한 Role 생성을 생성합니다.
data "aws_iam_policy_document" "ebs_csi_role_assume_role_policy" {
statement {
sid = ""
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]
condition {
test = "StringEquals"
variable = "${replace(aws_iam_openid_connect_provider.oidc_cluster.url, "https://", "")}:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "${replace(aws_iam_openid_connect_provider.oidc_cluster.url, "https://", "")}:sub"
values = ["system:serviceaccount:kube-system:ebs-csi-controller-sa"]
}
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.oidc_cluster.arn]
}
}
}
resource "aws_iam_role" "ebs_csi_driver_role" {
assume_role_policy = data.aws_iam_policy_document.ebs_csi_role_assume_role_policy.json
name = "${local.company_name}-ebs-csi-driver-role"
depends_on = [aws_iam_openid_connect_provider.oidc_cluster]
tags = {
"ServiceAccount" = "ebs-csi-controller-sa"
"ServiceAccountNameSpace" = "kube-system"
}
}
resource "aws_iam_role_policy_attachment" "ebs_csi_driver_policy_attach" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"
role = aws_iam_role.ebs_csi_driver_role.name
}
- Helm을 통해 EBS CSI Driver를 생성하는 경우 CSI Driver를 사용하는 Storage Class를 Custom하게 설치할 것이기 때문에 다음과 같이 yaml파일을 생성합니다.
- eks 생성 시 기본 제공하는 'gp2'보다 'gp3' 이 성능과 가격 측면에서 우월하기 때문에 gp3로 생성합니다.
controller:
replicaCount: 3
region: ap-northeast-2
serviceAccount:
create: false
# name: ebs-csi-controller-sa
# annotations:
# eks.amazonaws.com/role-arn: ${ebs-csi-controller-role-arn}
node:
tolerateAllTaints: true
storageClasses:
- name: gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: true
parameters:
type: gp3
csi.storage.k8s.io/fstype: ext4
- Terraform의 Kubenetes Provider를 사용하여 Service Account를 생성합니다.
- Helm Provider를 이용하여 EBS CSI Driver를 생성합니다.
- Helm Provider에서 Service Account를 생성하지 않고 미리 만든 Service Account를 적용할 것이기 때문에 set을 통해 sa create 부분을 false로 적용하였습니다.
resource "kubernetes_service_account" "ebs_csi_service_account" {
metadata {
name = "ebs-csi-controller-sa"
namespace = "kube-system"
labels = {
"app.kubernetes.io/name" = "aws-ebs-csi-driver"
}
annotations = {
"eks.amazonaws.com/role-arn" = module.eks.ebs_csi_driver_role_arn
}
}
depends_on = [module.eks]
}
resource "helm_release" "aws_ebs_csi_driver" {
name = "aws-ebs-csi-driver"
repository = "https://kubernetes-sigs.github.io/aws-ebs-csi-driver"
chart = "aws-ebs-csi-driver"
namespace = "kube-system"
version = "2.24.0"
set {
name = "controller.serviceAccount.create"
value = "false"
}
set {
name = "controller.serviceAccount.name"
value = "ebs-csi-controller-sa"
}
values = [
templatefile("${path.module}/custom-yaml/ebs_csi_values.yaml",
{
ebs-csi-controller-role-arn = module.eks.ebs_csi_driver_role_arn,
}
)
]
depends_on = [
kubernetes_service_account.ebs_csi_service_account,
module.eks
]
}
- 생성확인
3-3) PVC/PV Pod 테스트
# PVC 생성
cat <<EOT > awsebs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
storageClassName: gp3
EOT
kubectl apply -f awsebs-pvc.yaml
kubectl get pvc,pv
# 파드 생성
cat <<EOT > awsebs-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
terminationGracePeriodSeconds: 3
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-claim
EOT
kubectl apply -f awsebs-pod.yaml
# 워커노드에서 파드에 추가한 EBS 볼륨 모니터링
while true; do aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" --output text; date; sleep 1; done
# PVC, 파드 확인
kubectl get pvc,pv,pod
kubectl get VolumeAttachment
curl https://krew.sh/df-pv | bash
source ~/.bashrc
kubectl df-pv
- 현재 pv 의 이름을 기준하여 4G > 10G 로 증가 : .spec.resources.requests.storage의 4Gi 를 10Gi로 변경 테스트
kubectl get pvc ebs-claim -o jsonpath={.spec.resources.requests.storage} ; echo
kubectl get pvc ebs-claim -o jsonpath={.status.capacity.storage} ; echo
kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'
- 변경 후 확인
4. Snapshot CSI Controller
4-1) Snapshot CSI Controller
- CSI(컨테이너 스토리지 인터페이스) Snapshot Controller를 사용하면 Amazon EBS CSI 드라이버와 같은 호환 가능한 CSI 드라이버에서 스냅샷 기능을 사용할 수 있습니다.
- Install Snapshot CRDs
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl apply -f snapshot.storage.k8s.io_volumesnapshots.yaml,snapshot.storage.k8s.io_volumesnapshotclasses.yaml,snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl get crd | grep snapshot
kubectl api-resources | grep snapshot
- Install Common Snapshot Controller
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml
kubectl apply -f rbac-snapshot-controller.yaml,setup-snapshot-controller.yaml
kubectl get deploy -n kube-system snapshot-controller
kubectl get pod -n kube-system -l app=snapshot-controller
- Install Snapshotclass
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/examples/kubernetes/snapshot/manifests/classes/snapshotclass.yaml
kubectl apply -f snapshotclass.yaml
kubectl get vsclass # 혹은 volumesnapshotclasses
4-2) PVC/Pod 생성 테스트
- PVC 및 파드 생성
# PVC 생성
cat <<EOT > awsebs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
storageClassName: gp3
EOT
kubectl apply -f awsebs-pvc.yaml
kubectl get pvc,pv
# 파드 생성
cat <<EOT > awsebs-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
terminationGracePeriodSeconds: 3
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-claim
EOT
kubectl apply -f awsebs-pod.yaml
- Pod 의 파일 내용 추가 저장 확인
- VolumeSnapshot 생성 : Create a VolumeSnapshot referencing the PersistentVolumeClaim name >> EBS 스냅샷 확인
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ebs-volume-snapshot.yaml
cat ebs-volume-snapshot.yaml | yh
kubectl apply -f ebs-volume-snapshot.yaml
- VolumeSnapshot 확인
kubectl get volumesnapshot
kubectl get volumesnapshot ebs-volume-snapshot -o jsonpath={.status.boundVolumeSnapshotContentName} ; echo
kubectl describe volumesnapshot.snapshot.storage.k8s.io ebs-volume-snapshot
kubectl get volumesnapshotcontents
- VolumeSnapshot ID 확인
kubectl get volumesnapshotcontents -o jsonpath='{.items[*].status.snapshotHandle}' ; echo
5. AWS EFS Controller
5-1) EFS CSI Driver
- Elastic File System CSI Driver는 AWS에서 실행되는 Kubernetes 클러스터가 Amazon EFS 파일 시스템의 수명 주기를 관리할 수 있도록 하는 CSI 인터페이스를 제공합니다.
5-2) Terraform을 통한 EFS CSI Driver 배포
- EFS CSI Driver를 위한 Role 생성을 생성합니다.
data "http" "efs_controller_policy" {
url = "https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/docs/iam-policy-example.json"
request_headers = {
Accept = "application/json"
}
}
resource "aws_iam_policy" "efs_controller_policy" {
name = "${local.company_name}-efs-controller-policy"
description = "efs controller policy"
policy = data.http.efs_controller_policy.response_body
}
data "aws_iam_policy_document" "efs_csi_role_assume_role_policy" {
statement {
sid = ""
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]
condition {
test = "StringEquals"
variable = "${replace(aws_iam_openid_connect_provider.oidc_cluster.url, "https://", "")}:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "${replace(aws_iam_openid_connect_provider.oidc_cluster.url, "https://", "")}:sub"
values = ["system:serviceaccount:kube-system:efs-csi-*"]
}
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.oidc_cluster.arn]
}
}
}
resource "aws_iam_role" "efs_csi_driver_role" {
assume_role_policy = data.aws_iam_policy_document.efs_csi_role_assume_role_policy.json
name = "${local.company_name}-efs-csi-driver-role"
depends_on = [aws_iam_openid_connect_provider.oidc_cluster]
tags = {
"ServiceAccount" = "efs-csi-controller-sa"
"ServiceAccountNameSpace" = "kube-system"
}
}
resource "aws_iam_role_policy_attachment" "efs_csi_driver_policy_attach" {
policy_arn = aws_iam_policy.efs_controller_policy.arn
role = aws_iam_role.efs_csi_driver_role.name
}
- Terraform의 Kubenetes Provider를 사용하여 Service Account를 생성합니다.
- Helm Provider를 이용하여 EFS CSI Driver를 생성합니다.
- Helm Provider에서 Service Account를 생성하지 않고 미리 만든 Service Account를 적용할 것이기 때문에 set을 통해 sa create 부분을 false로 적용하였습니다.
resource "kubernetes_service_account" "efs_csi_service_account" {
metadata {
name = "efs-csi-controller-sa"
namespace = "kube-system"
labels = {
"app.kubernetes.io/name" = "aws-efs-csi-driver"
}
annotations = {
"eks.amazonaws.com/role-arn" = module.eks.efs_csi_driver_role_arn
}
}
depends_on = [module.eks]
}
resource "helm_release" "aws_efs_csi_driver" {
name = "aws-efs-csi-driver"
repository = "https://kubernetes-sigs.github.io/aws-efs-csi-driver"
chart = "aws-efs-csi-driver"
namespace = "kube-system"
version = "2.5.0"
set {
name = "controller.serviceAccount.create"
value = "false"
}
set {
name = "controller.serviceAccount.name"
value = "efs-csi-controller-sa"
}
depends_on = [
kubernetes_service_account.efs_csi_service_account,
module.eks
]
}
- 생성 확인
5-3) Terraform을 통한 EFS 배포
- EFS CSI Driver를 사용하기 위해서는 EFS가 생성되어 있어야합니다.
resource "aws_security_group" "efs_sg" {
name = "efs_sg"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 2049
to_port = 2049
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# EFS 파일 시스템 생성
resource "aws_efs_file_system" "efs" {
encrypted = true
performance_mode = "generalPurpose"
throughput_mode = "bursting"
lifecycle_policy {
transition_to_ia = "AFTER_30_DAYS"
}
}
resource "aws_efs_mount_target" "mount" {
count = length(module.subnets.ap_subnet_id)
file_system_id = aws_efs_file_system.efs.id
subnet_id = element(module.subnets.ap_subnet_id, count.index)
security_groups = [aws_security_group.efs_sg.id]
}
- 생성 확인
5-4) EFS 파일시스템을 다수의 파드가 사용하게 설정 테스트
- 실습 코드 clone
git clone https://github.com/kubernetes-sigs/aws-efs-csi-driver.git /root/efs-csi
cd /root/efs-csi/examples/kubernetes/multiple_pods/specs && tree
- EFS 스토리지클래스 생성 및 확인
cat storageclass.yaml | yh
kubectl apply -f storageclass.yaml
kubectl get sc efs-sc
- PV 생성 및 확인 : volumeHandle을 자신의 EFS 파일시스템ID로 변경
EfsFsId=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text)
sed -i "s/fs-4af69aab/$EfsFsId/g" pv.yaml
kubectl apply -f pv.yaml
kubectl get pv; kubectl describe pv
- PVC 생성 및 확인
cat claim.yaml | yh
kubectl apply -f claim.yaml
kubectl get pvc
- 파드 생성 및 연동 : 파드 내에 /data 데이터는 EFS를 사용
cat pod1.yaml pod2.yaml | yh
kubectl apply -f pod1.yaml,pod2.yaml
- 파드 정보 확인 : PV에 5Gi 와 파드 내에서 확인한 NFS4 볼륨 크리 8.0E의 차이는 무엇? 파드에 6Gi 이상 저장 가능한가?
kubectl exec -ti app1 -- sh -c "df -hT -t nfs4"
kubectl exec -ti app2 -- sh -c "df -hT -t nfs4"
- 공유 저장소 저장 동작 확인
6. NodeGroup
- EKS Node는 관리형 노드그룹관리가 가능하며, 부하가 높아지면 Auto Scailing 기능을 제공합니다.
- 온디맨드 인스턴스, 스팟 인스턴스, EKS 관리형 노드 그룹 3가지 프로세서 유형을 제공합니다.
- 또한 인텔, AMD 및 ARM(AWS Graviton) 프로세서를 선택해서 사용 가능하며, AWS는 AWS Graviton 프로세서를 아마존 EC2에서 실행되는 클라우드 워크로드에 최적의 가격 성능을 제공하고 있습니다.
- Graviton 기반 인스턴스는 인스턴스 유형 명명 규칙
- Terraform을 통한 ARM 기반 graviton 프로세서를 사용하는 t4g.medium 을 노드 그룹에 생성 및 확인
resource "aws_eks_node_group" "test_worker_node_group" {
cluster_name = aws_eks_cluster.cluster.name
node_group_name = "${local.company_name}-${local.server_env}-gravition-eksng"
node_role_arn = aws_iam_role.worker_ng_role.arn
subnet_ids = local.ap_subnet_id
version = local.eks_node_version
ami_type = "AL2_ARM_64"
instance_types = ["t4g.medium"]
disk_size = 30
scaling_config {
desired_size = 1 # 노드 그룹 내 현재 생성할 노드 수
min_size = 1 # 노드 그룹이 유지할 최소 노드 수
max_size = 1 # 노드 그룹이 제한할 최대 노드 수
}
update_config {
max_unavailable = 1
}
tags = {
Name = "${local.company_name}-${local.server_env}-gravition-eksng"
Production = local.server_env
}
# Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling.
# Otherwise, EKS will not be able to properly delete EC2 Instances and Elastic Network Interfaces.
depends_on = [
aws_eks_cluster.cluster,
aws_iam_role_policy_attachment.eks-AmazonEKSWorkerNodePolicy,
# aws_iam_role_policy_attachment.eks-AmazonEKS_CNI_Policy,
aws_iam_role_policy_attachment.eks-AmazonEC2ContainerRegistryReadOnly
]
}
- taints 셋팅
aws eks update-nodegroup-config --cluster-name junho-test-eks --nodegroup-name junho-test-gravition-eksng --taints "addOrUpdateTaints=[{key=frontend, value=true, effect=NO_EXECUTE}]"
aws eks describe-nodegroup --cluster-name junho-test-eks --nodegroup-name junho-test-gravition-eksng | jq .nodegroup.taints
- Run pods on Graviton
- taint 설정을 통해 pod가 스케줄링 될때 ARM 기반 graviton 프로세서를 사용한 노드그룹인 junho-test-gravition-eksng에 생성되도록 합니다.
- nodeAffinity 는 어느 자원에 붙을 지 명시하는 것이고, taint 는 toleration (용인) 이 적혀있는 리소스만 붙을 수 있도록 접근을 제한합니다.
- NoExecute 옵션은 toleration이 없으면 파드가 스케줄링 되지 않으며, 기존에 실행되는 파드도 toleration이 없으면 종료시키게 됩니다.
728x90
반응형
'EKS' 카테고리의 다른 글
[EKS] Security (0) | 2024.04.14 |
---|---|
[EKS] Autoscaling (0) | 2024.04.06 |
[EKS] Observability (0) | 2024.03.28 |
[EKS] AWS EKS의 Networking (VPC CNI, LB Controller) (0) | 2024.03.16 |
[EKS] AWS EKS의 특징과 Cluster Endpoint 통신 방식 (0) | 2024.03.09 |