kmg28801 / kubernetes-study

2 stars 0 forks source link

Chapter 8. 애플리케이션에서 파드 메타데이터와 그 외의 리소스에 액세스하기 #9

Open kmg28801 opened 1 year ago

thesun4sky commented 1 year ago

8. 애플리케이션에서 파드 메타데이터와 그 외의 리소스에 액세스하기

8.1 Downward API로 메타데이터 전달

8.1.1 사용 가능한 메타데이터 이해

Downward API를 이용해 다음 정보를 컨테이너에 전달할 수 있다.

여기서 레이블과 어노테이션은 볼륨으로만 노출될 수 있다.

8.1.2 환경변수로 메타데이터 노출하기

apiVersion: v1
kind: Pod
metadata:
  name: downward
spec:
  containers:
  - name: main
    image: busybox
    command: ["sleep", "9999999"]
    resources:
      requests:
        cpu: 15m
        memory: 100Ki
      limits:
        cpu: 100m
        memory: 4Mi
    env:
    - name: POD_NAME
      valueFrom:                      # 특정 값을 설정하는 대신 파드 매니페스트의 metadata.name을 참조한다.
        fieldRef:
          fieldPath: metadata.name
    - name: POD_NAMESPACE
      valueFrom:                      # 특정 값을 설정하는 대신 파드 매니페스트의 metadata.namespace을 참조한다.
        fieldRef:
          fieldPath: metadata.namespace
    - name: POD_IP
      valueFrom:
        fieldRef:
          fieldPath: status.podIP
    - name: NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
    - name: SERVICE_ACCOUNT
      valueFrom:
        fieldRef:
          fieldPath: spec.serviceAccountName
    - name: CONTAINER_CPU_REQUEST_MILLICORES
      valueFrom:
        resourceFieldRef:       # 컨테이너의 CPU등은 filedRef대신 resourceFieldRef를 사용한다.
          resource: requests.cpu      
          divisor: 1m                 # 나눔수(제수)가 1m 이기때문에 15m 설정한경우 15 값이 들어간다.
    - name: CONTAINER_MEMORY_LIMIT_KIBIBYTES
      valueFrom:
        resourceFieldRef:
          resource: limits.memory
          divisor: 1Ki                 # 나눔수(제수)가 1Ki 이기때문에 4Mi 설정한경우 4096 값이 들어간다.

기본적으로 filedRef로 설정하고, 컨테이너의 CPU/메모리 요청/제한값은 filedRef대신 resourceFieldRef를 사용한다.

아그래 그림과 같이 환경변수를 이용해 파드의 메타데이터와 속성을 파드에 노출한다. image

파드를 만든 후에 아래와 같이 kubectl exec 를 사용해서 컨테이너의 모든 환경변수를 확인

$ kubectl exec downward env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=downward
CONTAINER_MEMORY_LIMIT_KIBIBYTES=4096
POD_NAME=downward
POD_NAMESPACE=default
POD_IP=10.0.0.10
NODE_NAME=gke-kubia-default-pool-32a2cac8-sgl7
SERVICE_ACCOUNT=default
CONTAINER_CPU_REQUEST_MILLICORES=15
KUBERNETES_SERVICE_HOST=10.3.240.1
KUBERNETES_SERVICE_PORT=443
...

8.1.3 downward API 볼륨에 파일로 메타데이터 전달

-환경변수가 아닌 파일로 메타데이터를 노출하기위해 downwardAPI 볼륨을 정의하고 컨테이너에 마운트한다.

apiVersion: v1
kind: Pod
metadata:
  name: downward
  labels:                         # 이 레이블과 어노테이션은 downward API 볼륨으로 노출된다.
    foo: bar
  annotations:
    key1: value1
    key2: |
      multi
      line
      value                       # 여기까지
spec:
  containers:
  - name: main
    image: busybox
    command: ["sleep", "9999999"]
    resources:
      requests:
        cpu: 15m
        memory: 100Ki
      limits:
        cpu: 100m
        memory: 4Mi
    volumeMounts:            # downward 볼륨은 /etc/downward아래에 마운트
    - name: downward
      mountPath: /etc/downward
  volumes:
  - name: downward         # downwardAPI 볼륨은 downward 이름으로 정의
    downwardAPI:                
      items:
      - path: "podName"         # 파드의 이름(매니페스트에 있는 metadata.name 필드)은 podName파일에 기록된다.
        fieldRef:
          fieldPath: metadata.name
      - path: "podNamespace"
        fieldRef:
          fieldPath: metadata.namespace
      - path: "labels"               # 파드의 레이블은  /etc/downward/labels 파일에 기록된다.
        fieldRef:
          fieldPath: metadata.labels
      - path: "annotations"       # 파드의 어노테이션을  /etc/downward/annotations 파일에 기록된다.
        fieldRef:
          fieldPath: metadata.annotations
      - path: "containerCpuRequestMilliCores"
        resourceFieldRef:
          containerName: main
          resource: requests.cpu
          divisor: 1m
      - path: "containerMemoryLimitBytes"
        resourceFieldRef:
          containerName: main
          resource: limits.memory
          divisor: 1
$ kubectl exec downward cat /etc/downward/labels
foo="bar"

$kubectl exec downward cat /etc/downward/annotations
key1="value1"
key2="multi\nline\nvalue\n"
kubernetes.io/config/seen="2023-01-06T00:04:45.6381834014Z"
kubernetes.io/config.source="api"
thesun4sky commented 1 year ago

8.2 쿠버네티스 API 서버와 통신하기

8.2.1 쿠버네티스 REST API 살펴보기

$ kubectl cluster-info
Kubernetes control plane is running at https://34.68.118.85
GLBCDefaultBackend is running at https://34.68.118.85/api/v1/namespaces/kube-system/services/default-http-backend:http/proxy
KubeDNS is running at https://34.68.118.85/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
KubeDNSUpstream is running at https://34.68.118.85/api/v1/namespaces/kube-system/services/kube-dns-upstream:dns/proxy
Metrics-server is running at https://34.68.118.85/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy
$ curl https://34.68.118.85 -k
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {},
  "code": 403
}
$ curl http://localhost:8001/apis/batch
{
  "kind": "APIGroup",
  "apiVersion": "v1",
  "name": "batch",
  "versions": [          # 배치 API 그룹은 2가지 버전을 갖는다.
    {
      "groupVersion": "batch/v1",
      "version": "v1"
    },
    {
      "groupVersion": "batch/v1beta1",
      "version": "v1beta1"
    }
  ],
  "preferredVersion": {            # 클라이언트는 v1 버전을 사용해야 한다.
    "groupVersion": "batch/v1",
    "version": "v1"
  }
$ curl http://localhost:8001/apis/batch/v1
{
  "kind": "APIResourceList",        # batch/v1 API 그룹내의 API 리소스 목록
  "apiVersion": "v1",
  "groupVersion": "batch/v1",
  "resources": [
    {
      "name": "cronjobs",             # 네임스페이스 지정된(true로 설정된) 잡 리소스에 관한 설명 (/apis/batch/v1/cronjobs)
      "singularName": "",
      "namespaced": true,
      "kind": "CronJob",
      "verbs": [                              # 사용할 수 있는 동사(동작)
        "create",
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "update",
        "watch"
      ],
    },
    {
      "name": "cronjobs/status",         # cronjobs 의 상태만 변경 가능한 API 엔드포인트
      "singularName": "",
      "namespaced": true,
      "kind": "CronJob",
      "verbs": [
        "get",
        "patch",
        "update"
      ]
    },
     ....
    }
  ]
$ curl http://localhost:8001/apis/batch/v1/jobs
{
  "kind": "JobList",
  "apiVersion": "batch/v1",
  "metadata": {
    "resourceVersion": "449081"
  },
  "items": []

처음엔 아무 잡도 없기 때문에 items 가 비어있다. 아래와 같이 my-job.yaml 으로 잡을 띄우고 다시 조회해보자.

apiVersion: batch/v1
kind: Job
metadata:
  name: my-job
spec:
  template:
    metadata:
      labels:
        app: batch-job
    spec:
      restartPolicy: OnFailure
      containers:
      - name: main
        image: luksa/batch-job       # 교재에서 제공해주는 샘플 잡
$ kubectl apply -f my-job.yaml
Warning: Autopilot set default resource requests for Job default/my-job, as resource requests were not specified. See http://g.co/gke/autopilot-defaults
job.batch/my-job created

$ curl http://localhost:8001/apis/batch/v1/jobs
{
  "kind": "JobList",
  "apiVersion": "batch/v1",
  "metadata": {
    "resourceVersion": "451356"
  },
  "items": [
    {
      "metadata": {
        "name": "my-job",       # 방금 띄워준 my-job 이 Items에 조회된다.
        "namespace": "default",
        "uid": "0268927a-5050-480f-9423-826c1545f06e",
        "resourceVersion": "451332",
        "generation": 1,
        "creationTimestamp": "2023-01-07T15:06:31Z",
        "labels": {
          "app": "batch-job"
        },
        ....
     ]
}

8.2.2 파드 내에서 API 서버와 통신

curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above.

하지만 조회하려고 하면 위와 같이 서버 인증서 확인이 안되었기 때문에 조회가 실패한다.

- 서버의 아이덴티티 검증 (서버 인증서 확인하기)
7장에서 시크릿을 설명했던 내용중에, 각 컨테이너의 /var/run/secrets/kubernetes.io/service/account 에 마운트되는 자동생성된 `default-token-xyz` 라는 이름의 시크릿이 있다. 이걸로 시크릿의 내용을 살펴본다.
```shell
/ $ ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt     namespace  token

위 파일중 ca.crt 파일이 API 서버의 인증서를 서명하는 데 사용되는 인증 기관(CA)의 인증서이다. 2. API 서버와 통신확인 을 위해서 서버의 인증서가 CA로 서명됐는지 확인하면 된다.

/ $ curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt https://kubernetes
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {},
  "code": 403
}

아직 인증에 실패한다.

8.2.3 앰배서더 컨테이너를 이용한 API 서버 통신 간소화

8.2.4 클라이언트 라이브러리를 사용해 API 서버와 통신

public class Test { public static void main(String[] args) throws Exception { KubernetesClient client = new DefaultKubernetesClient();

// list pods in the default namespace
PodList pods = client.pods().inNamespace("default").list();
pods.getItems().stream()
  .forEach(s -> System.out.println("Found pod: " +
           s.getMetadata().getName()));

// create a pod
System.out.println("Creating a pod");
Pod pod = client.pods().inNamespace("default")
  .createNew()
  .withNewMetadata()
    .withName("programmatically-created-pod")
  .endMetadata()
  .withNewSpec()
    .addNewContainer()
      .withName("main")
      .withImage("busybox")
      .withCommand(Arrays.asList("sleep", "99999"))
    .endContainer()
  .endSpec()
  .done();
System.out.println("Created pod: " + pod);

// edit the pod (add a label to it)
client.pods().inNamespace("default")
  .withName("programmatically-created-pod")
  .edit()
  .editMetadata()
    .addToLabels("foo", "bar")
  .endMetadata()
  .done();
System.out.println("Added label foo=bar to pod");

System.out.println("Waiting 1 minute before deleting pod...");
Thread.sleep(60000);

// delete the pod
client.pods().inNamespace("default")
  .withName("programmatically-created-pod")
  .delete();
System.out.println("Deleted the pod");

} }



- 그외에도 스웨거 API를 활성화 시켜서 사용하는 방법도 있다. (REST API를 탐색하는 더 좋은 방법)
thesun4sky commented 1 year ago

8.3 요약

image