IAM role을 EKS pod별로 설정하기

2021-10-24

.

Data_Engineering_TIL(20211024)

[학습자료]

“클라우드 네이티브를 위한 쿠버네티스 실전 프로젝트” 책을 읽고 정리한 내용입니다.

** 동양북스, 아이자와 고지&사토 가즈히코 지음, 박상욱 옮김

참고자료 URL : https://github.com/dybooksIT/k8s-aws-book

“EKS 어플리케이션 로그 관리방안 및 운영 노하우”에 이어서 공부한 내용을 정리한 내용임

** URL : https://minman2115.github.io/DE_TIL299

[기본개념]

앞서 실습한 cloudwatch 에이전트나 fluentd를 배포할때 cloudwatch로 데이터를 전송할 수 있도록 아래와 같이 EC2 노드에 IAM 역할을 연결했다.

1

현재 EC2 노드에 CloudWatchAgentServerPolicy 정책이 설정되어 있다면 이거를 먼저 제거해준다.

  • EKS 운영시 위와 같이 EC2 노드별로 IAM 역할을 설정할때 발생할 수 있는 문제

클러스터상에 배포된 파드에서 AWS 리소스를 사용하려면 필요한 IAM 정책을 EC2 노드에 부여해야했는데 이 방법의 경우 노드에 파드 각각에 필요한 IAM 정책을 모두 부여하게 되므로 모든 파드에서 필요하지 않은 권한이 일괄적으로 설정될 여지가 크다. 이는 보안상 바람직하지 않는다. 최근에 그래서 IAM Role for Service Account(IRSA)라는게 발표되었는데 이를 통해서 파드별로 IAM Role을 각각 부여할 수 있게 되었다.

  • IRSA란

1) EKS 클러스터 내의 서비스 계정과 IAM role을 안전하게 연결시키는 구조

2) 서비스 계정을 설정하여 파드를 동작시키면 이 파드에서 연결된 IAM Role을 사용할 수 있음.

  • IRSA를 사용하기 위해 아래와 같은 세가지 순서를 지켜야 한다.

1) EKS 클러스터의 OpenID Connect(OIDC) 공급자 기능을 활성화 하고 IAM과 연결

** OIDC : 신뢰받는 ID 발급자의 인증정보나 속성정보를 다른 컴포넌트와 안전하게 공유하는 시스템이다.

2) 서비스 계쩡과 IAM Role 연결

3) 서비스 계정을 설정하고 Pod 동작

[실습]

STEP 1) EKS 클러스터의 OpenID Connect(OIDC) 공급자 기능을 활성화 하고 IAM과 연결

EKS에서는 서비스 계정과 IAM을 연결하기 위해 OpenID Connect를 사용한다. 구체적으로 말하면 EKS 클러스터의 OIDC 공급자 기능을 활성화하고 그 공급자 정보를 IAM에 등록해 EKS의 서비스 계정 인증과 IAM 인가구조를 연결하는 것이다.

이 작업을 위해 아래와 같은 명령어를 실행한다.

[ec2-user@ip-10-10-1-195 ~]$ eksctl utils associate-iam-oidc-provider --name eks-work-cluster --approve
Flag --name has been deprecated, use --cluster
2021-10-24 10:08:36 [ℹ]  eksctl version 0.70.0
2021-10-24 10:08:36 [ℹ]  using region ap-northeast-2
2021-10-24 10:08:37 [ℹ]  will create IAM Open ID Connect provider for cluster "eks-work-cluster" in "ap-northeast-2"
2021-10-24 10:08:37 [✔]  created IAM Open ID Connect provider for cluster "eks-work-cluster" in "ap-northeast-2"

그러면 IAM에는 EKS 클러스터의 OIDC 공급자 URL이 설정된 자격증명 공급자가 아래 그림과 같이 생성된다. 이 명령으로 EKS 클러스터가 발행한 ID 토큰과 IAM을 연결할 수 있게 된다.

2

STEP 2) 서비스 계정과 IAM 역할 연결

그런 다음에 서비스 계정과 IAM 역할을 연결한다. 구체적으로는 서비스 계정이 사용할 권한을 부여받은 IAM 역할을 생성하고, 서비스 계정을 생성할때 그 IAM 역할의 ARN을 연결한다. 아래와 같이 명령어를 실행한다.

그러면 아래와 같이 자동으로 서비스 계정과 IAM 역할이 생성된다.

[ec2-user@ip-10-10-1-195 ~]$ eksctl create iamserviceaccount --name cloudwatch-agent --cluster eks-work-cluster --attach-policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy --namespace amazon-cloudwatch --override-existing-serviceaccounts --approve
2021-10-24 11:22:01 [ℹ]  eksctl version 0.70.0
2021-10-24 11:22:01 [ℹ]  using region ap-northeast-2
2021-10-24 11:22:02 [ℹ]  1 iamserviceaccount (amazon-cloudwatch/cloudwatch-agent) was included (based on the include/exclude rules)
2021-10-24 11:22:02 [!]  metadata of serviceaccounts that exist in Kubernetes will be updated, as --override-existing-serviceaccounts was set
2021-10-24 11:22:02 [ℹ]  1 task: {
    2 sequential sub-tasks: {
        create IAM role for serviceaccount "amazon-cloudwatch/cloudwatch-agent",
        create serviceaccount "amazon-cloudwatch/cloudwatch-agent",
    } }2021-10-24 11:22:02 [ℹ]  building iamserviceaccount stack "eksctl-eks-work-cluster-addon-iamserviceaccount-amazon-cloudwatch-cloudwatch-agent"
2021-10-24 11:22:02 [ℹ]  deploying stack "eksctl-eks-work-cluster-addon-iamserviceaccount-amazon-cloudwatch-cloudwatch-agent"
2021-10-24 11:22:02 [ℹ]  waiting for CloudFormation stack "eksctl-eks-work-cluster-addon-iamserviceaccount-amazon-cloudwatch-cloudwatch-agent"
2021-10-24 11:22:19 [ℹ]  waiting for CloudFormation stack "eksctl-eks-work-cluster-addon-iamserviceaccount-amazon-cloudwatch-cloudwatch-agent"
2021-10-24 11:22:36 [ℹ]  waiting for CloudFormation stack "eksctl-eks-work-cluster-addon-iamserviceaccount-amazon-cloudwatch-cloudwatch-agent"
2021-10-24 11:22:36 [ℹ]  created namespace "amazon-cloudwatch"
2021-10-24 11:22:36 [ℹ]  created serviceaccount "amazon-cloudwatch/cloudwatch-agent"

# 그러면 아래와 같이 서비스 계정에는 생성된 IAM 역할의 ARN이 설정된다.
# 생성된 서비스 계정 확인
[ec2-user@ip-10-10-1-195 ~]$ kubectl get sa -n amazon-cloudwatch -o yaml
apiVersion: v1
items:
- apiVersion: v1
  kind: ServiceAccount
  metadata:
    annotations:
      eks.amazonaws.com/role-arn: arn:aws:iam::xxxxxxx:role/eksctl-eks-work-cluster-addon-iamserviceacco-Role1-CTCH3P4P9GV9
    creationTimestamp: "2021-10-24T11:22:36Z"
    labels:
      app.kubernetes.io/managed-by: eksctl
    name: cloudwatch-agent
    namespace: amazon-cloudwatch
    resourceVersion: "1646693"
    selfLink: /api/v1/namespaces/amazon-cloudwatch/serviceaccounts/cloudwatch-agent
    uid: 34e54bfe-85d4-43fc-b190-ab0b9bf0c928
  secrets:
  - name: cloudwatch-agent-token-gchmf
- apiVersion: v1
  kind: ServiceAccount
  metadata:
    creationTimestamp: "2021-10-24T11:22:36Z"
    name: default
    namespace: amazon-cloudwatch
    resourceVersion: "1646692"
    selfLink: /api/v1/namespaces/amazon-cloudwatch/serviceaccounts/default
    uid: 966ebecb-aef2-48e9-829f-43216740472c
  secrets:
  - name: default-token-g7rvs
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

아래와 같이 aws 콘솔에서 arn:aws:iam::xxxxxxx:role/eksctl-eks-work-cluster-addon-iamserviceacco-Role1-CTCH3P4P9GV9 IAM role이 생성된 것을 확인할 수 있다.

3

STEP 3) 서비스 계정을 설정하여 파드 동작시키기

그런 다음에 생성한 서비스 계정을 설정하여 파드를 동작시키면 된다. 예를 들어서 여기서 배포한 Cloudwatch 에이전트의 데몬셋 매니페스트 파일 내용은 아래와 같다.

[ec2-user@ip-10-10-1-195 ~]$ ll
total 45824
-rw-rw-r--  1 ec2-user ec2-user      141 Oct 19 13:33 cloudwatch-namespace.yaml
-rw-rw-r--  1 ec2-user ec2-user      522 Oct 19 13:41 cwagent-configmap.yaml
-rw-rw-r--  1 ec2-user ec2-user     2560 Oct 19 13:43 cwagent-daemonset.yaml
-rw-rw-r--  1 ec2-user ec2-user     1120 Oct 19 13:35 cwagent-serviceaccount.yaml
drwxrwxr-x  2 ec2-user ec2-user       59 Oct 23 06:21 eks_log_exam
drwxrwxr-x 15 ec2-user ec2-user      315 Oct 18 13:49 k8s-aws-book
-rw-rw-r--  1 ec2-user ec2-user 46907392 Oct 18 12:16 kubectl
    
# 서비스 계정을 설정하여 파드를 동작시키는 매니페스트 cwagent-daemonset.yaml
[ec2-user@ip-10-10-1-195 ~]$ cat cwagent-daemonset.yaml
# deploy cwagent as daemonset
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: cloudwatch-agent
  namespace: amazon-cloudwatch
spec:
  selector:
    matchLabels:
      name: cloudwatch-agent
  template:
    metadata:
      labels:
        name: cloudwatch-agent
    spec:
      containers:
        - name: cloudwatch-agent
          image: amazon/cloudwatch-agent:1.247348.0b251302
          #ports:
          #  - containerPort: 8125
          #    hostPort: 8125
          #    protocol: UDP
          resources:
            limits:
              cpu:  200m
              memory: 200Mi
            requests:
              cpu: 200m
              memory: 200Mi
          # Please don't change below envs
          env:
            - name: HOST_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.hostIP
            - name: HOST_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            - name: K8S_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: CI_VERSION
              value: "k8s/1.3.8"
          # Please don't change the mountPath
          volumeMounts:
            - name: cwagentconfig
              mountPath: /etc/cwagentconfig
            - name: rootfs
              mountPath: /rootfs
              readOnly: true
            - name: dockersock
              mountPath: /var/run/docker.sock
              readOnly: true
            - name: varlibdocker
              mountPath: /var/lib/docker
              readOnly: true
            - name: containerdsock
              mountPath: /run/containerd/containerd.sock
              readOnly: true
            - name: sys
              mountPath: /sys
              readOnly: true
            - name: devdisk
              mountPath: /dev/disk
              readOnly: true
      volumes:
        - name: cwagentconfig
          configMap:
            name: cwagentconfig
        - name: rootfs
          hostPath:
            path: /
        - name: dockersock
          hostPath:
            path: /var/run/docker.sock
        - name: varlibdocker
          hostPath:
            path: /var/lib/docker
        - name: containerdsock
          hostPath:
            path: /run/containerd/containerd.sock
        - name: sys
          hostPath:
            path: /sys
        - name: devdisk
          hostPath:
            path: /dev/disk/
      terminationGracePeriodSeconds: 60
      serviceAccountName: cloudwatch-agent

이렇게 하면 EKS 클러스터가 IAM 역할을 부여하기 위해 필요한 토큰을 생성하고 그것이 파드 내의 특정 디렉토리에 마운트된다. 이 상태에서 파드 내부의 어플리케이션이 AWS SDK를 경유해서 각종 처리를 요청하면 자동으로 이 토큰을 사용해서 IAM 역할을 연결하고 AWS 리소스를 컨트롤 할 수 있게 된다.