> For the complete documentation index, see [llms.txt](https://kubernetes.youngwjung.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kubernetes.youngwjung.com/core-concepts/service/lab.md).

# 실습

### ClusterIP

1. Deployment 생성&#x20;

   ```
   cat <<'EOF' | kubectl apply -f -
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: nginx
   spec:
     replicas: 3
     selector:
       matchLabels:
         app: nginx
     template:
       metadata:
         labels:
           app: nginx
       spec:
         containers:
         - name: nginx
           image: nginx
           ports:
           - containerPort: 80
           volumeMounts:
           - name: html
             mountPath: /usr/share/nginx/html
         initContainers:
         - name: index
           image: curlimages/curl
           command:
           - "sh"
           - "-c"
           - "echo 'hello from $(POD_NAME), my ip is $(POD_IP)' > /data/index.html"
           volumeMounts:
           - name: html
             mountPath: /data
           env:
           - name: POD_NAME
             valueFrom:
               fieldRef:
                 fieldPath: metadata.name
           - name: POD_IP
             valueFrom:
               fieldRef:
                 fieldPath: status.podIP
         volumes:
         - name: html
           emptyDir: {}
   EOF
   ```
2. Service 생성 &#x20;

   ```
   kubectl expose deployment nginx --port 80
   ```
3. 생성된 Service 확인&#x20;

   ```
   kubectl get svc nginx -o wide
   ```
4. 생성된 Endpoint 확인&#x20;

   ```
   kubectl get ep nginx
   ```
5. 생성된 Pod의 IP주소 확인 &#x20;

   ```
   kubectl get pod -l app=nginx -o wide
   ```
6. Endpoint에 타겟으로 등록되어 있는 Pod 목록 확인

   ```
   kubectl get ep nginx \
   -o jsonpath='{range .subsets[*].addresses[*]}{.targetRef.name}{"\t"}{.ip}{"\n"}{end}'
   ```
7. 생성된 Deployment의 Replica 갯수를 6개로 조정&#x20;

   ```
   kubectl scale deployment nginx --replicas=6
   ```
8. 생성된 Pod의 IP주소 확인 &#x20;

   ```
   kubectl get pod -l app=nginx -o wide
   ```
9. Endpoint에 타겟으로 등록되어 있는 Pod 목록 확인

   ```
   kubectl get ep nginx \
   -o jsonpath='{range .subsets[*].addresses[*]}{.targetRef.name}{"\t"}{.ip}{"\n"}{end}'
   ```
10. Pod 생성&#x20;

    ```
    kubectl run curl --image=nginx -- sleep 3600
    ```
11. 위에서 생성한 Pod에서 Service 호출&#x20;

    ```
    kubectl exec curl -- \
    bash -c "for i in {1..20};do curl -s $(kubectl get svc nginx -o=jsonpath='{.spec.clusterIP}');done"
    ```
12. 새로운 터미널을 열고 kube-proxy 로그 확인 - *아래의 명령어를 입력하고 엔터키를 몇번 입력해서 간격을 만들어두면 새로운 로그를 좀 더 쉽게 알아볼수 있음*

    ```
    kubectl -n kube-system logs ds/kube-proxy -f
    ```
13. 기존 터미널로 돌아와서 생성된 Deployment의 Replica 갯수를 3개로 조정&#x20;

    ```
    kubectl scale deployment nginx --replicas=3
    ```
14. 다른 터미널로 이동해서 kube-proxy 로그 확인&#x20;
15. 새로운 터미널을 열고 한개의 Node로 Session Manager 연결

    ```
    aws ssm start-session --target \
    $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+")
    ```
16. Iptable의 모든 규칙 확인&#x20;

    ```
    sudo iptables-save
    ```
17. Iptable의 NAT 규칙 확인

    ```
    sudo iptables -L -t nat
    ```
18. KUBE-SERVICES 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L KUBE-SERVICES -n  | column -t
    ```
19. 생성된 Service의 Cluster IP로 연결된 규칙 값을 환경변수로 저장

    ```
    export SERVICE_CHAIN=$(sudo iptables -t nat -L KUBE-SERVICES -n  | column -t | grep "default/nginx" | grep -oE "^KUBE-SVC-[A-Z0-9]+")
    echo $SERVICE_CHAIN
    ```
20. Service의 Cluster IP로 연결된 규칙의 상세내용 확인

    ```
    sudo iptables -t nat -L $SERVICE_CHAIN -n  | column -t
    ```
21. 위의 명령어로 나온 결과중의 한개의 Chain 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L $(sudo iptables -t nat -L $SERVICE_CHAIN -n  | column -t | grep -oE "^KUBE-SEP-[A-Z0-9]+" | head -1) \
    -n  | column -t
    ```
22. 첫번째 터미널로 이동해서 생성된 Pod의 IP주소 확인 &#x20;

    ```
    kubectl get pod -o wide -l app=nginx
    ```
23. 생성된 Deployment의 Replica 갯수를 6개로 조정&#x20;

    ```
    kubectl scale deployment nginx --replicas=6
    ```
24. 두번째 터미널에서 kube-proxy 로그 확인&#x20;
25. 세번째 터미널로 이동해서 20번 명령어 재실행&#x20;
26. 첫번째 터미널로 이동해서 CoreDNS 설정 파일 확인&#x20;

    ```
    kubectl -n kube-system get cm coredns -o yaml | yq e '.data' -
    ```
27. log 플러그인 활성화 - [https://coredns.io/plugins/log](https://coredns.io/plugins/log/)

    ```
    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: coredns
      namespace: kube-system
      labels:
        eks.amazonaws.com/component: coredns
        k8s-app: kube-dns
    data:
      Corefile: |
        .:53 {
            errors
            health
            kubernetes cluster.local in-addr.arpa ip6.arpa {
              pods insecure
              fallthrough in-addr.arpa ip6.arpa
            }
            prometheus :9153
            forward . /etc/resolv.conf
            cache 30
            loop
            reload
            loadbalance
            log
        }
    EOF
    ```
28. CoreDNS Pod 재배포&#x20;

    ```
    {
        kubectl -n kube-system scale deployment coredns --replicas=0
        sleep 60
        kubectl -n kube-system scale deployment coredns --replicas=2
        sleep 10
        kubectl -n kube-system get pod -l k8s-app=kube-dns
    }
    ```
29. 두번째 터미널에서 CoreDNS 로그 확인&#x20;

    ```
    kubectl -n kube-system logs deploy/coredns -f
    ```
30. 첫번째 터미널로 이동해서 Pod안에 설정된 DNS 설정 파일 확인&#x20;

    ```
    kubectl exec curl -- cat /etc/resolv.conf
    ```
31. CoreDNS 주소 확인

    ```
    kubectl -n kube-system get svc kube-dns
    ```
32. CURL 명령어로 Service 이름 호출&#x20;

    ```
    kubectl exec curl -- curl -s nginx
    ```
33. 두번째 터미널에서 CoreDNS 로그 확인 - 새로운 로그 출력되지 않으면 32번 명령어를 다시 수행
34. 첫번째 터미널로 이동해서 새로운 Namespace 생성&#x20;

    ```
    kubectl create ns web
    ```
35. 생성한 Namespace 안에 Deployment 생성&#x20;

    ```
    kubectl -n web create deployment httpd --image=httpd --port=80
    ```
36. Service 생성 &#x20;

    ```
    kubectl -n web expose deployment httpd
    ```
37. CURL 명령어로 Service 이름 호출 &#x20;

    ```
    kubectl exec curl -- curl -s httpd
    ```
38. 두번째 터미널에서 CoreDNS 로그 확인 - 새로운 로그 출력되지 않으면 37번 명령어를 다시 수행
39. CURL 명령어로 Service 호출 -  service.namespace

    ```
    kubectl exec curl -- curl -s httpd.web
    ```
40. CURL 명령어로 Service 호출 -  service.namespace.svc

    ```
    kubectl exec curl -- curl -s httpd.web.svc
    ```
41. CURL 명령어로 Service 호출 -  service.namespace.svc.cluster

    ```
    kubectl exec curl -- curl -s httpd.web.svc.cluster
    ```
42. CURL 명령어로 Service 호출 -  FQDN

    ```
    kubectl exec curl -- curl -s httpd.web.svc.cluster.local
    ```
43. 생성한 리소스 삭제&#x20;

    ```
    {
        kubectl delete ns web
        kubectl delete pod curl
        kubectl delete svc nginx
        kubectl delete deploy nginx
    }
    ```

### NodePort

1. Deployment 생성&#x20;

   ```
   kubectl create deployment nginx --image=nginx --port=80 --replicas=3
   ```
2. Service 생성 &#x20;

   ```
   kubectl expose deployment nginx --type=NodePort
   ```
3. 생성된 Service 확인&#x20;

   ```
   kubectl get svc nginx -o wide
   ```
4. Node의 공인 IP 주소 확인&#x20;

   ```
   kubectl get node -o wide
   ```
5. 웹 브라우저를 열고 NODE\_EXTERNAL\_IP:NODEPORT 주소로 접속 시도 - *아래의 명령어로 주소 확인 가능*&#x20;

   ```
   echo $(kubectl get node -o=jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')\
   :$(kubectl get svc nginx -o=jsonpath='{.spec.ports[0].nodePort}')
   ```
6. 접속이 안될 경우에는 Node의 보안그룹 확인

   ```
   aws ec2 describe-security-groups --group-ids \
   $(aws ec2 describe-instances --instance-ids \
   $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+") \
   --query 'Reservations[0].Instances[0].SecurityGroups[*].GroupId' --output text) \
   --query "SecurityGroups[*].IpPermissions"
   ```
7. 보안그룹에 Service에 명시된 포트에 대한 인바운드 규칙이 없을 경우에 아래의 명령어로 규칙 추가

   ```
   aws ec2 authorize-security-group-ingress --group-id \
   $(aws ec2 describe-instances --instance-ids \
   $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+") \
   --query 'Reservations[0].Instances[0].SecurityGroups[0].GroupId' --output text) \
   --protocol tcp \
   --port $(kubectl get svc nginx -o=jsonpath='{.spec.ports[0].nodePort}') \
   --cidr 0.0.0.0/0
   ```
8. 보안그룹에 인바운드 규칙이 추가되었는지 확인

   ```
   aws ec2 describe-security-groups --group-ids \
   $(aws ec2 describe-instances --instance-ids \
   $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+") \
   --query 'Reservations[0].Instances[0].SecurityGroups[*].GroupId' --output text) \
   --query "SecurityGroups[*].IpPermissions"
   ```
9. 웹 브라우저를 열고 NODE\_EXTERNAL\_IP:NODEPORT 주소로 접속 시도 - *아래의 명령어로 주소 확인 가능*&#x20;

   ```
   echo $(kubectl get node -o=jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')\
   :$(kubectl get svc nginx -o=jsonpath='{.spec.ports[0].nodePort}')
   ```
10. 새로운 터미널을 열고 한개의 Node로 Session Manager 연결

    ```
    aws ssm start-session --target \
    $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+")
    ```
11. Iptable의 모든 규칙 확인&#x20;

    ```
    sudo iptables-save
    ```
12. KUBE-SERVICES 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L KUBE-SERVICES -n  | column -t
    ```
13. KUBE-NODEPORTS 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L KUBE-NODEPORTS -n  | column -t
    ```
14. 생성된 Service의 NodePort로 연결된 규칙 값을 환경변수로 저장

    ```
    export NODEPORT_CHAIN=$(sudo iptables -t nat -L KUBE-NODEPORTS -n  | column -t | grep "default/nginx" | grep -oE "^KUBE-SVC-[A-Z0-9]+")
    echo $NODEPORT_CHAIN
    ```
15. KUBE-SERVICES 규칙 중에서 NodePort로 연결된 규칙 확인

    ```
    sudo iptables -t nat -L KUBE-SERVICES -n  | column -t | grep $NODEPORT_CHAIN
    ```
16. 첫번째 터미널로 이동해서 Service의 Cluster IP 확인

    ```
    kubectl get svc nginx
    ```
17. 다른 터미널로 이동해서 NodePort로 연결된 규칙의 상세내용 확인

    ```
    sudo iptables -t nat -L $NODEPORT_CHAIN -n  | column -t
    ```
18. 위의 명령어로 나온 결과중의 한개의 Chain 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L \
    $(sudo iptables -t nat -L $NODEPORT_CHAIN -n  | column -t | grep -oE "^KUBE-SEP-[A-Z0-9]+" | head -1) \
    -n  | column -t
    ```
19. 첫번째 터미널로 이동해서 생성된 Pod의 IP주소 확인 &#x20;

    ```
    kubectl get pod -o wide -l app=nginx
    ```
20. 다른 터미널로 이동해서 Node에 열려있는 포트 정보 확인&#x20;

    ```
    sudo netstat -tlnp
    ```
21. CURL 명령어로 Service 호출&#x20;

    ```
    curl localhost:NODEPORT
    ```
22. 첫번째 터미널로 이동해서 보안그룹에 추가한 인바운드 규칙 삭제

    ```
    aws ec2 revoke-security-group-ingress --group-id \
    $(aws ec2 describe-instances --instance-ids \
    $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+") \
    --query 'Reservations[0].Instances[0].SecurityGroups[0].GroupId' --output text) \
    --protocol tcp \
    --port $(kubectl get svc nginx -o=jsonpath='{.spec.ports[0].nodePort}') \
    --cidr 0.0.0.0/0
    ```
23. 인바운드 규칙이 삭제되었는지 확인

    ```
    aws ec2 describe-security-groups --group-ids \
    $(aws ec2 describe-instances --instance-ids \
    $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+") \
    --query 'Reservations[0].Instances[0].SecurityGroups[*].GroupId' --output text) \
    --query "SecurityGroups[*].IpPermissions"
    ```
24. 생성한 리소스 삭제&#x20;

    ```
    {
        kubectl delete deploy nginx
        kubectl delete svc nginx
    }
    ```

### LoadBalancer

1. Deployment 생성&#x20;

   ```
   kubectl create deployment nginx --image=nginx --port=80 --replicas=3
   ```
2. Service 생성 &#x20;

   ```
   kubectl expose deployment nginx --type=LoadBalancer
   ```
3. 생성된 Service 확인&#x20;

   ```
   kubectl get svc nginx -o wide
   ```
4. 생성된 Service 객체에 발생한 Event 확인

   ```
   kubectl describe svc nginx
   ```
5. 웹 브라우저를 열고 Service의 External IP 주소로 접속 - *아래의 명령어로 주소 확인 가능*

   ```
   kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}{"\n"}' 
   ```
6. 생성된 Service 상세 내용 확인&#x20;

   ```
   kubectl get svc nginx -o=jsonpath='{.spec}' | jq
   ```
7. 생성된 ELB 이름 확인&#x20;

   ```
   kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | grep -o -E '^[a-z0-9]+' 
   ```
8. ELB 상세 내용 확인 &#x20;

   ```
   aws elb describe-load-balancers --load-balancer-names \
   $(kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | grep -o -E '^[a-z0-9]+' )
   ```
9. ELB의 Listener 설정 확인

   ```
   aws elb describe-load-balancers --load-balancer-names \
   $(kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | grep -o -E '^[a-z0-9]+' ) \
   --query 'LoadBalancerDescriptions[*].ListenerDescriptions'
   ```
10. ELB의 보안그룹 확인

    ```
    aws elb describe-load-balancers --load-balancer-names \
    $(kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | grep -o -E '^[a-z0-9]+' ) \
    --query 'LoadBalancerDescriptions[*].SecurityGroups'
    ```
11. 노드에 부여된 보안그룹에 ELB 보안그룹에 대한 새로운 인바운드 규칙이 추가 됐는지 확인&#x20;

    ```
    aws ec2 describe-security-groups --group-ids \
    $(aws ec2 describe-instances --instance-ids \
    $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+") \
    --query 'Reservations[0].Instances[0].SecurityGroups[*].GroupId' --output text) \
    --query "SecurityGroups[*].IpPermissions"
    ```
12. 새로운 터미널을 열고 한개의 Node로 Session Manager 연결

    ```
    aws ssm start-session --target \
    $(kubectl get node -o jsonpath='{.items[0].spec.providerID}{"\n"}' | grep -oE "i-[a-z0-9]+")
    ```
13. KUBE-NODEPORTS 규칙 확인&#x20;

    ```
    sudo iptables -t nat -L KUBE-NODEPORTS -n  | column -t
    ```
14. 첫번째 터미널로 이동해서 생성한 리소스 삭제&#x20;

    ```
    {
        kubectl delete deploy nginx
        kubectl delete svc nginx
    }
    ```

### Network Load Balancer

1. Deployment 생성

   ```
   kubectl create deployment tcp-echo --image=istio/tcp-echo-server:1.2 --port=9000
   ```
2. Pod 생성 확인

   ```
   kubectl get pod -l app=tcp-echo
   ```
3. AWS Cloud Controller Manager를 통해서 CLB 및 NLB를 생성 및 구성하는 방법 리뷰\
   <https://github.com/kubernetes/cloud-provider-aws/blob/master/docs/service_controller.md>\
   <https://kubernetes.io/docs/concepts/services-networking/service/#aws-nlb-support>
4. Service 생성 &#x20;

   ```
   cat <<EOF | kubectl apply -f -
   apiVersion: v1
   kind: Service
   metadata:
     name: tcp-echo
     annotations:
       service.beta.kubernetes.io/aws-load-balancer-type: nlb
   spec:
     selector:
       app: tcp-echo
     ports:
       - protocol: TCP
         port: 9000
         targetPort: 9000
     type: LoadBalancer
   EOF
   ```
5. Service 상태 확인

   ```
   kubectl describe svc tcp-echo
   ```
6. 생성된 ELB 상세 내용 확인

   ```
   aws elbv2 describe-load-balancers --names \
   $(kubectl get svc tcp-echo \
   -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' \
   | grep -o -E '^[a-z0-9]+')
   ```
7. Pod 생성

   ```
   kubectl run netcat --image=youngwjung/netcat -- sleep 3600
   ```
8. Pod 생성 확인&#x20;

   ```
   kubectl get pod netcat
   ```
9. 생성한 Pod에서 NLB로 TCP 연결&#x20;

   ```
   kubectl exec -it netcat -- \
   nc -v -q 1 $(kubectl get svc tcp-echo -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') 9000
   ```
10. 연결에 성공했다는 메시지가 나오면 터미널에 아무 글자나 입력해서 응답이 오는지 확인

    ```
    $ kubectl exec -it netcat -- nc -v -q 1 $(kubectl get svc tcp-echo -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') 9000
    Connection to a0db8c37386d148afbd97efc9dc15a8f-744e5fa4f97a926c.elb.ap-northeast-2.amazonaws.com 9000 port [tcp/*] succeeded!
    kubernetes
    hello kubernetes
    aws
    hello aws
    world
    hello world
    ```
11. 생성한 리소스 삭제&#x20;

    ```
    {
        kubectl delete deploy tcp-echo
        kubectl delete svc tcp-echo
        kubectl delete pod netcat
    }
    ```

### Kubectl Proxy & Port-forwarding

1. Deployment 생성&#x20;

   ```
   kubectl create deployment nginx --image=nginx --port=80 --replicas=1
   ```
2. 생성된 Pod 확인&#x20;

   ```
   kubectl get po -l app=nginx
   ```
3. Service 생성 &#x20;

   ```
   kubectl expose deployment nginx
   ```
4. 생성된 Service 확인&#x20;

   ```
   kubectl get svc nginx
   ```
5. Kubectl proxy 구동&#x20;

   ```
   kubectl proxy --port=8080 --accept-hosts=.* --address=0.0.0.0 
   ```
6. 새로운 터미널을 열고 아래의 주소로 접근 시도

   ```
   curl -L localhost:8080/api/v1/namespaces/default/services/nginx:80/proxy
   ```
7. 생성된 Pod 이름을 환경변수로 저장

   ```
   {
       export POD_NAME=$(kubectl get pod -l app=nginx -o=jsonpath='{.items[0].metadata.name}')
       echo $POD_NAME
   }
   ```
8. 아래의 주소로 접근 시도

   ```
   curl -L localhost:8080/api/v1/namespaces/default/pods/$POD_NAME:80/proxy
   ```
9. 첫번째 터미널로 이동해서 실행중인 kubectl proxy 프로세스 종료&#x20;
10. 생성된 Pod 이름을 환경변수로 저장

    ```
    {
        export POD_NAME=$(kubectl get pod -l app=nginx -o=jsonpath='{.items[0].metadata.name}')
        echo $POD_NAME
    }
    ```
11. 로컬포트를 Pod의 포트로 포워딩

    ```
    kubectl port-forward $POD_NAME 8080:80 --address=0.0.0.0
    ```
12. 다른 터미널로 이동해서 Pod에 배포된 NGINX 서버로 접근 시도&#x20;

    ```
    curl localhost:8080
    ```
13. 첫번째 터미널로 이동해서 실행중인 kubectl port-forward 프로세스 종료&#x20;
14. 로컬포트를 Service의 포트로 포워딩

    ```
    kubectl port-forward svc/nginx 8080:80 --address=0.0.0.0
    ```
15. 다른 터미널로 이동해서 Service로 접근 시도&#x20;

    ```
    curl localhost:8080
    ```
16. 첫번째 터미널로 이동해서 실행중인 kubectl port-forward 프로세스 종료&#x20;
17. 생성한 리소스 삭제&#x20;

    ```
    {
        kubectl delete deploy nginx
        kubectl delete svc nginx
    }
    ```

### ExternalName

1. Service 생성 &#x20;

   ```
   cat <<EOF | kubectl apply -f -
   apiVersion: v1
   kind: Service
   metadata:
     name: k8s
   spec:
     type: ExternalName
     externalName: kubernetes.io
   EOF
   ```
2. Service 상태 확인

   ```
   kubectl describe svc k8s
   ```
3. Pod 생성

   ```
   kubectl run curl --image=curlimages/curl --command -- sleep 3600
   ```
4. 위에서 생성한 Service 호출&#x20;

   ```
   kubectl exec curl -- curl -sL k8s
   ```
5. 위에서 생성한 Service 호출 - *호스트 명시*

   ```
   kubectl exec curl -- curl -sL -H "Host: kubernetes.io" k8s
   ```
6. 위에서 생성한 Service 호출 - *호스트 명시하고 새로운 경로 호출*&#x20;

   ```
   kubectl exec curl -- curl -sL -H "Host: kubernetes.io" k8s/docs
   ```
7. 리소스 삭제

   ```
   {
       kubectl delete svc k8s
       kubectl delete pod curl
   }
   ```
