

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# PersistentVolumeClaims(PVC)를 사용하는 작업 문제 해결
<a name="permissions-for-pvc"></a>

작업의 PersistentVolumeClaims(PVC)를 생성, 나열 또는 삭제해야 하는 경우 기본 Kubernetes 역할 *emr-containers*를 추가하지 않으면 작업 제출 시 작업에 실패합니다. 이 권한이 없으면 *emr-container* 역할은 Spark 드라이버 또는 Spark 클라이언트에 필요한 역할을 생성할 수 없습니다. 오류 메시지에서 알 수 있듯이 Spark 드라이버 또는 클라이언트 역할에 권한을 추가하는 것만으로는 충분하지 않습니다. *emr-containers* 기본 역할에도 필수 권한이 포함되어야 합니다. 이 섹션에서는 *emr-containers* 기본 역할에 필수 권한을 추가하는 방법을 설명합니다.

## Verification(확인)
<a name="verification"></a>

*emr-containers* 역할에 필수 권한이 있는지 확인하려면 NAMESPACE 변수를 자체 값으로 설정한 후 다음 명령을 실행합니다.

```
export NAMESPACE=YOUR_VALUE
kubectl describe role emr-containers -n ${NAMESPACE}
```

또한 Spark 및 클라이언트 역할에 필수 권한이 있는지 확인하려면 다음 명령을 실행합니다.

```
kubectl describe role emr-containers-role-spark-driver -n ${NAMESPACE}
kubectl describe role emr-containers-role-spark-client -n ${NAMESPACE}
```

권한이 없는 경우 다음과 같이 패치를 진행합니다.

## 패치
<a name="patch"></a>

1. 권한이 없는 작업이 현재 실행 중인 경우 해당 작업을 중지합니다.

1. 다음과 같이 *RBAC\_Patch.py*라는 파일을 생성합니다.

   ```
   import os
   import subprocess as sp
   import tempfile as temp
   import json
   import argparse
   import uuid
   
   def delete_if_exists(dictionary: dict, key: str):
       if dictionary.get(key, None) is not None:
           del dictionary[key]
   
   def doTerminalCmd(cmd):
       with temp.TemporaryFile() as f:
           process = sp.Popen(cmd, stdout=f, stderr=f)
           process.wait()
           f.seek(0)
           msg = f.read().decode()
       return msg
   
   def patchRole(roleName, namespace, extraRules, skipConfirmation=False):
       cmd = f"kubectl get role {roleName} -n {namespace} --output json".split(" ")
       msg = doTerminalCmd(cmd)
       if "(NotFound)" in msg and "Error" in msg:
           print(msg)
           return False
       role = json.loads(msg)
       rules = role["rules"]
       rulesToAssign = extraRules[::]
       passedRules = []
       for rule in rules:
           apiGroups = set(rule["apiGroups"])
           resources = set(rule["resources"])
           verbs = set(rule["verbs"])
           for extraRule in extraRules:
               passes = 0
               apiGroupsExtra = set(extraRule["apiGroups"])
               resourcesExtra = set(extraRule["resources"])
               verbsExtra = set(extraRule["verbs"])
               passes += len(apiGroupsExtra.intersection(apiGroups)) >= len(apiGroupsExtra)
               passes += len(resourcesExtra.intersection(resources)) >= len(resourcesExtra)
               passes += len(verbsExtra.intersection(verbs)) >= len(verbsExtra)
               if passes >= 3:
                   if extraRule not in passedRules:
                       passedRules.append(extraRule)
                       if extraRule in rulesToAssign:
                           rulesToAssign.remove(extraRule)
                   break
       prompt_text = "Apply Changes?"
       if len(rulesToAssign) == 0:
           print(f"The role {roleName} seems to already have the necessary permissions!")
           prompt_text = "Proceed anyways?"
       for ruleToAssign in rulesToAssign:
           role["rules"].append(ruleToAssign)
       delete_if_exists(role, "creationTimestamp")
       delete_if_exists(role, "resourceVersion")
       delete_if_exists(role, "uid")
       new_role = json.dumps(role, indent=3)
       uid = uuid.uuid4()
       filename = f"Role-{roleName}-New_Permissions-{uid}-TemporaryFile.json"
       try:
           with open(filename, "w+") as f:
               f.write(new_role)
               f.flush()
           prompt = "y"
           if not skipConfirmation:
               prompt = input(
                   doTerminalCmd(f"kubectl diff -f {filename}".split(" ")) + f"\n{prompt_text} y/n: "
               ).lower().strip()
               while prompt != "y" and prompt != "n":
                   prompt = input("Please make a valid selection. y/n: ").lower().strip()
           if prompt == "y":
               print(doTerminalCmd(f"kubectl apply -f {filename}".split(" ")))
       except Exception as e:
           print(e)
       os.remove(f"./{filename}")
   
   if __name__ == '__main__':
       parser = argparse.ArgumentParser()
       parser.add_argument("-n", "--namespace",
                           help="Namespace of the Role. By default its the VirtualCluster's namespace",
                           required=True,
                           dest="namespace"
                           )
   
       parser.add_argument("-p", "--no-prompt",
                           help="Applies the patches without asking first",
                           dest="no_prompt",
                           default=False,
                           action="store_true"
                           )
       args = parser.parse_args()
   
       emrRoleRules = [
           {
               "apiGroups": [""],
               "resources": ["persistentvolumeclaims"],
               "verbs": ["list", "create", "delete", "patch"]
            }
           
       ]
   
       driverRoleRules = [
           {
               "apiGroups": [""],
               "resources": ["persistentvolumeclaims"],
               "verbs": ["list", "create", "delete", "patch", "deletecollection"]
           },
           {
               "apiGroups": [""],
               "resources": ["services"],
               "verbs": ["get", "list", "describe", "create", "delete", "watch", "deletecollection"]
           },
           {
               "apiGroups": [""],
               "resources": ["configmaps", "pods"],
               "verbs": ["deletecollection"]
           }
       ]
   
       clientRoleRules = [
           {
               "apiGroups": [""],
               "resources": ["persistentvolumeclaims"],
               "verbs": ["list", "create", "delete", "patch"]
           }
       ]
   
       patchRole("emr-containers", args.namespace, emrRoleRules, args.no_prompt)
       patchRole("emr-containers-role-spark-driver", args.namespace, driverRoleRules, args.no_prompt)
       patchRole("emr-containers-role-spark-client", args.namespace, clientRoleRules, args.no_prompt)
   ```

1. Python 스크립트를 실행합니다.

   ```
   python3 RBAC_Patch.py -n ${NAMESPACE}
   ```

1. 새 권한과 이전 권한 간의 kubectl 차이가 표시됩니다. y를 눌러 역할을 패치합니다.

1. 다음과 같이 추가 권한이 있는 세 가지 역할을 확인합니다.

   ```
   kubectl describe role -n ${NAMESPACE}
   ```

1. Python 스크립트를 실행합니다.

   ```
   python3 RBAC_Patch.py -n ${NAMESPACE}
   ```

1. 명령을 실행하면 새 권한과 이전 권한 사이에 kubectl 차이가 표시됩니다. y를 눌러 역할을 패치합니다.

1. 추가 권한이 있는 세 가지 역할을 확인합니다.

   ```
   kubectl describe role -n ${NAMESPACE}
   ```

1. 작업을 다시 제출합니다.

## 수동 패치
<a name="manual-patch"></a>

애플리케이션에 필요한 권한이 PVC 규칙 이외의 다른 항목에 적용되는 경우 필요에 따라 Amazon EMR 가상 클러스터에 대한 Kubernetes 권한을 수동으로 추가할 수 있습니다.

**참고**  
*emr-containers* 역할은 기본 역할입니다. 즉, 기본 드라이버 또는 클라이언트 역할을 변경하려면 먼저 필요한 모든 권한을 제공해야 합니다.

1. 아래 명령을 실행하여 현재 권한을 yaml 파일로 다운로드합니다.

   ```
   kubectl get role -n ${NAMESPACE} emr-containers -o yaml >> emr-containers-role-patch.yaml
   kubectl get role -n ${NAMESPACE} emr-containers-role-spark-driver -o yaml >> driver-role-patch.yaml
   kubectl get role -n ${NAMESPACE} emr-containers-role-spark-client -o yaml >> client-role-patch.yaml
   ```

1. 애플리케이션에 필요한 권한에 따라 각 파일을 편집하고 다음과 같은 추가 규칙을 추가합니다.
   + emr-containers-role-patch.yaml

     ```
     - apiGroups:
       - ""
       resources:
       - persistentvolumeclaims
       verbs:
       - list
       - create
       - delete
       - patch
     ```
   + driver-role-patch.yaml

     ```
     - apiGroups:
       - ""
       resources:
       - persistentvolumeclaims
       verbs:
       - list
       - create
       - delete
       - patch
       - deletecollection
     - apiGroups:
       - ""
       resources:
       - services
       verbs:
       - get 
       - list 
       - describe 
       - create
       - delete 
       - watch
       - deletecollection
     - apiGroups:
       - ""
       resources:
       - configmaps
       - pods
       verbs:
       - deletecollection
     ```
   + client-role-patch.yaml

     ```
     - apiGroups:
       - ""
       resources:
       - persistentvolumeclaims
       verbs:
       - list
       - create
       - delete
       - patch
     ```

1. 다음 속성을 해당 값과 함께 제거합니다. 업데이트를 적용하는 데 필요합니다.
   + creationTimestamp
   + resourceVersion
   + uid

1. 마지막으로 패치를 실행합니다.

   ```
   kubectl apply -f emr-containers-role-patch.yaml
   kubectl apply -f driver-role-patch.yaml
   kubectl apply -f client-role-patch.yaml
   ```