반응형

 

장황한 설명보다는 예제가 이해가 쉬울 듯하여, 아래의 예제 코드를 작성했다.

 

 

Case:  Kubernetes Cluster 외부에서 Kubernetes Cluster 내부 Service에 접근

아래와 같은 Server, Client App의 구성을 가정하고 아래 예제를 따라해본다.

  • Server App 위치: Kubernetes Cluster 내부
  • Client App 위치: Kubernetes Cluster 외부

 


API 접근용 TOKEN 값을 알고 있다면, 바로 아래 예제를 따라서 수행하면 된다.

 

$  cat curl-pod-list.sh
##
## Create a Pod on myspace namespace
##

TOKEN=$(oc whoami -t)
ENDPOINT="https://api.ocp4.mycluster.io:6443"
NAMESPACE="myspace"

echo ""
echo "TOKEN:     $TOKEN"
echo "ENDPOINT:  $ENDPOINT"
echo "NAMESPACE: $NAMESPACE"
echo ""


##
## Get Pod list in a namespace
##

RESPONSE_FILE=./curl-pod-list-response.json

curl -v -k -o $RESPONSE_FILE \
    -H "Authorization: Bearer $TOKEN" \
    -H 'Accept: application/json' \
    $ENDPOINT/api/v1/namespaces/$NAMESPACE/pods


cat $RESPONSE_FILE


echo ""
echo "====================================================="
echo "                     Summary                         "
echo "====================================================="
echo ""

jq '.items[] | {PodName: .metadata.name, PodIP: .status.podIPs, StartTime: .status.startTime, Node: .spec.nodeName}' curl-pod-list-response.json

$

 

 

Case:  Kubernetes Cluster 내부에서 Kubernetes Cluster 내부 Service에 접근

아래와 같은 Server, Client App의 구성을 가정하고 아래 예제를 따라해본다.

  • Server App 위치: Kubernetes Cluster 내부
    • Server는 Elasticsearch API Server 역할
  • Client App 위치: Kubernetes Cluster 내부

즉, Cluster 내부에서 Pod간 통신이라고 가정한다.

 


[ 참고 ]
API 접근용 TOKEN은Pod 내부의 /var/run/secrets/kubernetes.io/serviceaccount/token 파일에 있다.
ServiceAccount 별로 Token을 생성하고 서비스 접근 권한을 부여하기 때문에 Client App 역할을 하는 Pod가 사용하는 ServiceAccount에게 Server Pod의 Service에 접근할 수 있도록 Role을 Binding해줘야 한다. 

 

 

$  cat clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cluster-admin-my-service-account
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: system:serviceaccount:my-namespace:my-service-account
  
  $  kubectl apply -f clusterrolebinding.yaml

 

 

#!/bin/bash


##
## Run this script in my example pod !
##
##  1) Jump into my example pod
##     $  kubectl exec -n my-namespace -it my-example-pod -- bash
##
##  2) Run this script in the above pod
##     $  chmod +x run-es-query-in-container.sh
##     $  ./run-es-query-in-container.sh
##


TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
ENDPOINT="https://elasticsearch.openshift-logging:9200"
RESPONSE_FILE=curl-es.json

rm  $RESPONSE_FILE

echo ""
echo "TOKEN:     $TOKEN"
echo "ENDPOINT:  $ENDPOINT"
echo ""


curl -k -o $RESPONSE_FILE \
    -H "Authorization: Bearer $TOKEN" \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json' \
    -d '{ "query": { "match": { "message": "cm-apiserver" } } }' \
    $ENDPOINT/infra-000094/_search?size=3



jq ".hits.hits[]._source | { kubernetes, message }" $RESPONSE_FILE
반응형

 

 

 

 

https://access.redhat.com/documentation/ko-kr/openshift_container_platform/4.6/html/networking/add-pod

 

11.7. SR-IOV 추가 네트워크에 pod 추가 OpenShift Container Platform 4.6 | Red Hat Customer Portal

The Red Hat Customer Portal delivers the knowledge, expertise, and guidance available through your Red Hat subscription.

access.redhat.com

 

 

 

https://dramasamy.medium.com/high-performance-containerized-applications-in-kubernetes-f494cef3f8e8

 

High-Performance Containerized Applications in Kubernetes

The Single Root I/O Virtualization (SR-IOV) specification is a standard for a type of PCI device assignment that can share a single device…

dramasamy.medium.com

 

반응형

 

Kubernetes를 사용하다보면, Pod가 Terminating 상태에서 종료(즉, Pod의 삭제)되지 않고 계속 머물러있는 경우가 종종 발생한다.

이렇게 Pod의 Terminating 교착 상태가 된 원인은 정확히 알 수는 없고, 

단지 이런 경우에 Pod를 종료시킬 수 없어서 당혹스럽다.

 

Ian Miell 이라는 사람이 상황별로 교착 상태에 빠진 Pod를 종료하는 방법을 정리한 Web Docs가  있어서 나한테 맞게 다시 메모를 해봤다.

 

$  kubectl  delete  -n istio-system  deployment  grafana

##
## 위 delete 명령을 수행 후, 1분이 넘도록 Pod가 Terminating 상태라면
## 이 Pod는 계속 Terminating 상태로 남고, 아래 예시처럼 Delete되지 않을 것이다.
##

$  kubectl  get -A pod

NAMESPACE      NAME                                       READY   STATUS        RESTARTS       AGE
istio-system   grafana-68cc7d6d78-7kjw8                   1/1     Terminating   0              37d

... 중간 생략 ...

$

 

 

위 현상을 세분화해서 해결 방법을 설명해보겠다.

 

 

Pod의 상세 정보를 확인

 

##
## (A) 강제로 Pod를 삭제하는 방법
##

$  kubectl  delete  pods <pod>  --grace-period=0  --force

## 웬만하면, 위 명령으로 Pod가 삭제되지만
## 만약 계속 Pod의 찌끄러기가 남아 있다면, 아래 (B) 절차를 추가로 수행해야 한다.



##
## (B) 위 명령을 수행하고도 Pod이 Stuck 상태 또는 Unknown 상태로 남아 있다면
##     아래의 방법으로 Pod를 끝장낼 수 있다.
##

$  kubectl  patch  pod <pod>  -p '{"metadata":{"finalizers":null}}'

 

 

 

Reference

 

Kubernetes.io에 Pod의 강제 종료에 대한 상세한 설명을 있으니, 시간이 있다면 꼼꼼히 읽어보면 도움이 된다.

 

https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/

 

Force Delete StatefulSet Pods

This page shows how to delete Pods which are part of a stateful set, and explains the considerations to keep in mind when doing so. Before you begin This is a fairly advanced task and has the potential to violate some of the properties inherent to Stateful

kubernetes.io

 

 

그리고 위에서 finalizers를 강제로 null로 patch했는데, finalizers에 관한 상세한 설명이 궁금하면 아래 kubernetes web docs를 읽어보는 것이 좋다.

 

https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/

 

Finalizers

Finalizers are namespaced keys that tell Kubernetes to wait until specific conditions are met before it fully deletes resources marked for deletion. Finalizers alert controllers to clean up resources the deleted object owned. When you tell Kubernetes to de

kubernetes.io

 

 

게시물 작성자: sejong.jeonjo@gmail.com

 

 


 

 

 

 

 

##
## 채용 관련 글
##
제가 일하고 있는 기업 부설연구소에서 저와 같이 연구/개발할 동료를 찾고 있습니다.
(이곳은 개인 블로그라서 기업 이름은 기재하지 않겠습니다. E-mail로 문의주시면 자세한 정보를 공유하겠습니다.)

근무지 위치:
  서울시 서초구 서초동, 3호선 남부터미널역 근처 (전철역 출구에서 회사 입구까지 도보로 328m)
필요한 지식 (아래 내용 중에서 70% 정도를 미리 알고 있다면 빠르게 협업할 수 있음):
  - 운영체제 (학부 3~4학년 때, 컴퓨터공학 운영체제 과목에서 배운 지식 수준):
    예를 들어, Processor, Process 생성(Fork)/종료, Memory, 동시성, 병렬처리, OS kernel driver  
  - Linux OS에서 IPC 구현이 가능
    예를 들어, MSGQ, SHM, Named PIPE 등 활용하여 Process간 Comm.하는 기능 구현이 가능하면 됨. 
  - Algorithm(C언어, C++ 언어로 구현 가능해야 함)
    예를 들어, Hashtable, B-Tree, Qsort 정도를 C 또는 C++로 구현할 수 있을 정도 
  - Network 패킷 처리 지식(Layer 2 ~ 4, Layer 7)
    예를 들어, DHCP Server/Client의 주요 Feature를 구현할 정도의 능력이 있으면 됨.
  - Netfilter, eBPF 등 (IP packet hooking, ethernet packet 처리, UDP/TCP packet 처리)
  - IETF RFC 문서를 잘 읽고 이해하는 능력 ^^
  # 위에 열거한 내용 외에도 제가 여기 블로그에 적은 내용들이 대부분 업무하면서 관련이 있는 주제를 기록한 것이라서
  # 이 블로그에 있는 내용들을 잘 알고 있다면, 저희 연구소에 와서 연구/개발 업무를 수행함에 있어서 어려움이 없을 겁니다.
회사에서 사용하는 프로그래밍 언어:
  - 프로그래밍 언어: C, C++, Go
    (참고: 아직 연구소 동료들이 Rust를 사용하진 않습니다만, 새 언어로써 Rust를 사용하는 것을 고려하는 중)
근무 시간:
  - 출근: 8~10시 사이에서 자유롭게 선택
  - 퇴근: 8시간 근무 후 퇴근 (오후 5시 ~ 7시 사이)
  - 야근 여부: 거의 없음 (내 경우, 올해 상반기 6개월간 7시 이후에 퇴근한 경우가 2회 있었음)
  - 회식 여부: 자유 (1년에 2회 정도 회식하는데, 본인이 집에 가고 싶으면 회식에 안 감. 왜 참석 안 하는지 묻지도 않음)
외근 여부:
  - 신규 프로젝트 멤버 -> 외근 전혀 하지 않음 (나는 신규 프로젝트만 참여해서 지난 1년 동안 한번도 외근 없었음)
  - 상용 프로젝트 멤버 -> 1년에 5회 미만 정도로 외근
팀 워크샵 여부:
  - 팀 워크샵 자체를 진행하지 않음. (워크샵 참석하는 거 싫어하는 개발자 환영 ^^)
연락처:
  - "sejong.jeonjo@gmail.com"  # 궁금한 점은 이 연락처로 문의주세요.
  - 블로그 비밀 댓글 (제가 하루에 한번씩 댓글 확인하고 있음)
원하는 인재상:
  - 우리 부설연구소는 "긴 호흡으로 프로젝트를 진행"하기 때문에 최소 2년간 한 가지 주제를 꾸준하게 연구/개발할 수 있는 개발자를 원함.
  - 우리 부설연구소는 자주적으로 연구 주제를 찾아서 업무를 하기 때문에 능동적으로 생각하고 행동하는 동료를 원함.
  - 차분하게 연구 주제에 몰입하고, 해법을 찾는 것을 즐기는 사람.
내가 느끼는 우리 연구소의 장점:
  - 갑/을 관계가 없음. (제가 근무하고 있는 연구소는 SI업종이 아니라서 갑/을 회사 개념이 없음)
  - 연구소 자체적으로 연구 주제를 발굴하고 시스템을 개발하기 때문에 개발 일정에 대한 스트레스가 적음
  - 빌딩 전체를 우리 회사가 사용하므로 분위기가 산만하지 않음.
  - 근처에 예술의전당, 우면산 둘레길이 있어서 점심 시간에 산책하기 좋음 ^^
  - 연구소 동료들 매너가 Good (2년간 일하면서 한번도 감정에 스크레치 생기거나 얼굴 붉히며 싸운 적 없음 ^^)

 

반응형

 


 

 

이 블로그 내용을 테스트한 날짜: 2023년 02월 17일

 

 

아래 예시처럼 따라하면, Kubernetes 자원에 대한 Metrics 정보를 볼 수 있다.

단, 아래 절차에서 "NOTE:" 라고 표시된 부분을 추가해야 한다.

 

##
## 아래 명령을 수행해서 metrics-server를 설치하기 위한 manifest file을 download한다.
##
$  curl -L https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -o components.yaml


## 
## 다운로드한 components.yaml 파일에
## "--kubelet-insecure-tls=true" 옵션을 추가한다.
##  (아래 예제를 따라하면 된다)
$  vi components.yaml
... 중간 생략 ...
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=4443
        - --kubelet-insecure-tls=true     ## <-- NOTE: 이 부분을 추가한다.
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --metric-resolution=15s
        image: k8s.gcr.io/metrics-server/metrics-server:v0.6.2
... 중간 생략 ...        


$  kubectl apply -f components.yaml
serviceaccount/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
service/metrics-server created
deployment.apps/metrics-server created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
$

$  kubectl  get  -n kube-system  pod  -l k8s-app=metrics-server
NAME                             READY   STATUS    RESTARTS   AGE
metrics-server-ddfcd777b-lktxx   1/1     Running   0          67s

$ kubectl  top  node
NAME      CPU(cores)   CPU%   MEMORY(bytes)    MEMORY%
node-69   219m         5%     23607Mi          24%
node-73   202m         5%     48753Mi          50%
node-75   173m         4%     51048Mi          52%
... 중간 생략 ...

$ kubectl  top  -n istio-system  pod --containers
POD                                     NAME                                 CPU(cores)   MEMORY(bytes)
grafana-68cc7d6d78-7kjw8                grafana                              3m           51Mi
istio-egressgateway-6cb7bdc7fb-2wg57    istio-proxy                          3m           47Mi
istio-ingressgateway-694d8d7656-fk4tw   istio-proxy                          7m           62Mi
istiod-6c68579c55-xk6cm                 discovery                            2m           80Mi
jaeger-5d44bc5c5d-rb4v6                 jaeger                               10m          3612Mi
kiali-fd9f88575-z4wrm                   kiali                                1m           131Mi
prometheus-77b49cb997-zvjm8             prometheus-server                    9m           454Mi
prometheus-77b49cb997-zvjm8             prometheus-server-configmap-reload   1m           6Mi
... 중간 생략 ...
$

 

위에서 "--kubelet-insecure-tls=true" 라고 설정을 추가했는데, 자세한 설명은 아래 원문을 참고할 것~~~

Metrics Server Pod랑 Kubelet 이랑 API 연동할 때, CA(Certificate Authority)를 검사하지 말고 연동하라는 뜻이다.

 

kubelet-insecure-tls 옵션에 대한 설명

 

 

Reference

딱히 어려운 내용이 없어서, 아래 원문을 읽고 따라해도 잘 설치되고, 동작한다.

 

GitHub - kubernetes-sigs/metrics-server: Scalable and efficient source of container resource metrics for Kubernetes built-in aut

Scalable and efficient source of container resource metrics for Kubernetes built-in autoscaling pipelines. - GitHub - kubernetes-sigs/metrics-server: Scalable and efficient source of container reso...

github.com

 

반응형

 

Istio를 사용해서 Pod간 Traffic을 제어하다보면, 특정 Pod의 특정 TCP Port는 Proxy 처리에서 제외(Exclude)시키고 싶을 때가 있다.

(즉, Istio Enovy Proxy Container를 경유하지 않고, 바로 다른 연동할 Pod로 TCP Traffic을 보낸다는 뜻)

 

아마, 대부분 HTTP가 아닌 회사 내부에서 자체적으로 Protocol을 정해놓고 연동하는 Traffic 들이 대부분일 듯.

또는 NATS, Kafka 같은 Message Broker와 연동하는 경우에도 굳이 Service Mesh를 사용할 일이 없다.

왜냐하면 NATS, Kafka는 원래 메시지 분산 처리를 할 수 있도록 설계되어 있으니까 굳이 Istio의 제어를 받지 않아도 된다.

 

만약, TCP Port 50001를 사용하는 TCP Traffic을 Istio Envoy Proxy를 경유하지 않고, Main App Container (A)에서 바로 다른  Main App Container(B)에게 보내려면 아래와 같이 Pod에 Exclude Annotation을 설정한다.

 

apiVersion: apps/v1
kind: StatefulSet   ## or Deployment
metadata:
  name: myapp

... 중간 생략 ...

spec:
  template:
    metadata:
      annotations:
        sidecar.istio.io/inject: "true"
        traffic.sidecar.istio.io/excludeInboundPorts: "34432,50001,50002,50003,50100,50102,50103,50200,50201,50203,50300,50301,50302"
        traffic.sidecar.istio.io/excludeOutboundPorts: "34432,50001,50002,50003,50100,50102,50103,50200,50201,50203,50300,50301,50302"
반응형

 

가끔 kubernetes에서 istio를 사용하다보면, Pod 내부의 iptables 구성 상태가 어떠한지 궁금할 때가 있다.

매번 여기저기 명령 찾아서 짜집기하고 있었는데, 이렇게 하니까 찾는 시간도 오래 걸리고, 나의 기억도 오래가지 않아서 여기에 메모를 하고 필요할 때마다 아래 명령을 copy & paste 해야겠다.

 

(이 명령은 Istio README.md에도 있으니까, 좀더 깊게 볼려면 Istio README.md에서 찾아보길~)

 

##
## pod의 container id 확인하기
##

$ ns=ns-a

$ podnm=ratings-v1-587d5cfb9-mxhxc

$ container_id=$(kubectl get pod -n ${ns} ${podnm} -o jsonpath="{.status.containerStatuses[?(@.name=='istio-proxy')].containerID}" | sed -n 's/docker:\/\/\(.*\)/\1/p')

$ echo $container_id
6dbeeb047da9149f0735943cd95885a156dbdc766b48145c40e7346ea7f6bcfb


##
## 어떤 worker node에서 ratings-v1 pod가 구동되어 있는지 확인하기
##

$ kubectl get -n ns-a pod -l app=ratings -o wide
NAME                         READY   STATUS    RESTARTS   AGE     IP               NODE      NOMINATED NODE   READINESS GATES
ratings-v1-587d5cfb9-mxhxc   2/2     Running   0          2d17h   10.244.91.198    node-73   <none>           <none>
ratings-v1-587d5cfb9-s6g6r   2/2     Running   0          2d17h   10.244.142.131   node-75   <none>           <none>


##
## ratings-v1 pod가 구동된 worker node에 ssh 접속하기
##

$ ssh root@node-73

##
## NOTE: worker node에 접속했으면, 위에서 알아냈던 container_id를 아래와 같이 설정하고
##       아래 명령 순서대로 실행한다.
##

[root@node-73 ~]$ container_id=6dbeeb047da9149f0735943cd95885a156dbdc766b48145c40e7346ea7f6bcfb

[root@node-73 ~]$ cpid=$(docker inspect --format '{{ .State.Pid }}' $container_id)


##
## Worker Node의 nsenter 명령을 이용하여 Container 내부의 iptables 정보를 조회한다.
##
[root@node-73 ~]$ nsenter -t $cpid -n iptables -L -t nat -n -v --line-numbers -x

Chain PREROUTING (policy ACCEPT 118222 packets, 7093320 bytes)
num      pkts      bytes target     prot opt in     out     source               destination
1      118224  7093440 ISTIO_INBOUND  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain INPUT (policy ACCEPT 118224 packets, 7093440 bytes)
num      pkts      bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 10650 packets, 924376 bytes)
num      pkts      bytes target     prot opt in     out     source               destination
1         400    24000 ISTIO_OUTPUT  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain POSTROUTING (policy ACCEPT 10650 packets, 924376 bytes)
num      pkts      bytes target     prot opt in     out     source               destination

Chain ISTIO_INBOUND (1 references)
num      pkts      bytes target     prot opt in     out     source               destination
1           0        0 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:15008
2           0        0 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22
3           0        0 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:15090
4      118222  7093320 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:15021
5           0        0 RETURN     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:15020
6           2      120 ISTIO_IN_REDIRECT  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain ISTIO_IN_REDIRECT (3 references)
num      pkts      bytes target     prot opt in     out     source               destination
1           2      120 REDIRECT   tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            redir ports 15006

Chain ISTIO_OUTPUT (1 references)
num      pkts      bytes target     prot opt in     out     source               destination
1           3      180 RETURN     all  --  *      lo      127.0.0.6            0.0.0.0/0
2           0        0 ISTIO_IN_REDIRECT  all  --  *      lo      0.0.0.0/0           !127.0.0.1            owner UID match 1337
3           0        0 RETURN     all  --  *      lo      0.0.0.0/0            0.0.0.0/0            ! owner UID match 1337
4         397    23820 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner UID match 1337
5           0        0 ISTIO_IN_REDIRECT  all  --  *      lo      0.0.0.0/0           !127.0.0.1            owner GID match 1337
6           0        0 RETURN     all  --  *      lo      0.0.0.0/0            0.0.0.0/0            ! owner GID match 1337
7           0        0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner GID match 1337
8           0        0 RETURN     all  --  *      *       0.0.0.0/0            127.0.0.1
9           0        0 ISTIO_REDIRECT  all  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain ISTIO_REDIRECT (1 references)
num      pkts      bytes target     prot opt in     out     source               destination
1           0        0 REDIRECT   tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            redir ports 15001

[root@node-73 ~]$

 

 

위 iptables 구성에 관한 이론 및 개념 설명은 아래 blog를 참고하길~

 

 

iptables and netfilter

iptables의 Chain(Rule) & Table 구조 iptables는 다수의 Chain(예: PREROUTING, INPUT, OUTPUT, POSTROUTING, FORWARD)과 그 Chain에 정의된 Rule, 그리고 각 Chain에는 다수의 Table(raw, mangle, nat, filter,..

andrewpage.tistory.com

 

'kubernetes' 카테고리의 다른 글

Kubernetes Metrics Server 설치  (0) 2021.11.26
[TODO] Read - Istio Memo  (0) 2021.11.23
Kubernetes imagePullSecret 설정  (0) 2021.11.19
kubectl 명령 예제 & JSON 포맷으로 출력하기  (0) 2021.11.17
Setup Kubernetes Using Ansible  (0) 2021.11.15
반응형

`kubectl apply` 명령을 통해서 container image를 pull하려면, Docker 회사의 container image pulling에 대한 rate limit 정책 때문에 docker.io 또는 docker.com에서 image를 pulling하지 못하는 경우가 있다.

그래서 일반적으로 docker.com에 회원 가입하고, 무제한으로 image pulling 할 수 있도록 유료 서비스도 이용하는데, `docker login ....` 그리고 `docker image pull ...`하는 것은 잘 되지만, kubectl 명령으로 pod의 container image를 pulling하려면 rate limit제약이 있다. 바로 해당 Pod의 'imagePullSecret' 정보가 없이  때문이다. Pod가 생성될 때, Pod의 manifest에 있는 imagePullSecret 정보를 보고, Container Image Registry에 인증 요청을 하는 것인데 대부분 Pod manifest에 이 imagePullSecret 정보를 설정하지 않는다. (일부러 안 하는 것은 아니고, 이런 것이 있는 줄 모르니까 사용자들이 실수를 한다)

 

가장 흔한 Use Case가 namespace 마다 Docker Registry에 접속할 수 있는 secret을 등록하고, Pod manifest에 이 secret을 참조하도록 하는 것이다.  그런데 이것도 실제 사용하려면 문제가 있다. 내가 직접 작성한 Pod manifest라면, imagePullSecret을 추가하면 끝날 일이지만 istio처럼 istioctl 이라는 명령이 내부적으로 pod manifest를 생성해서 pod 생성을 시도하는 경우라면, imagePullSecret을 내 마음대로 추가하기 어렵다.

 

그래서~  나는 어떻게 이 문제를 풀어야 하지? ㅠㅠ

 

 

우선 즉흥적으로 생각나는 대로 메모를 해보면,  (깊게 생각한다고 풀릴 문제는 아닌 듯...)

  • Cluster 전체에서 공용으로 imagePullSecret을 사용할 수 있도록 설정하는 기능이 있는지 확인한다.
  • ServiceAccount에 Secret을 등록하고, 항상 이 ServiceAccount로 생성되는 Pod에 imagePullSecret을 사용할 수 있도록 한다.
  • istioctl 같은 별개의 deployment 관리 명령이 있는 경우라면, istioctl option 중에서 imagePullSecret을 설정하는 parameter가 있는지 찾아본다.

Web Docs를 잘 찾아보니까, 고맙게도 kubernetes.io의 Web Docs에 내가 고민하고 있는 내용을 잘 설명해주고 있다.

https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry

 

Images

A container image represents binary data that encapsulates an application and all its software dependencies. Container images are executable software bundles that can run standalone and that make very well defined assumptions about their runtime environmen

kubernetes.io

 

 

위 Web Docs에서는 아래의 4 가지 답안을 줬는데, 가장 사람 손을 덜 타는 (A) 방법으로 문제를 풀어봤다.

 

(A) Configuring Nodes to Authenticate to a Private Registry      ## 내가 선택한 해결 방법
    - all pods can read any configured private registries
    - requires node configuration by cluster administrator

(B) Pre-pulled Images
    - all pods can use any images cached on a node
    - requires root access to all nodes to setup

(C) Specifying ImagePullSecrets on a Pod
    - only pods which provide own keys can access the private registry

(D) Vendor-specific or local extensions
    - if you're using a custom node configuration,
       you (or your cloud provider) can implement your mechanism for authenticating the node to the container registry.

 

 

그래서 위 (A) 방법대로 문제를 해결했다~  자세한 절차는 아래 Web Docs에 있다.  단순하게 따라하면 잘 동작한다.


https://kubernetes.io/docs/concepts/containers/images/#configuring-nodes-to-authenticate-to-a-private-registry

위 원문을 조금 요약해보자면 아래와 같다.

##
## If you run Docker on your nodes, 
## you can configure the Docker container runtime to authenticate to a private container registry.
## This approach is suitable if you can control node configuration.

## Here are the recommended steps to configuring your nodes to use a private registry. 
## In this example, run these on your desktop/laptop PC:

##   1. Run docker login "docker.io" for each set of credentials you want to use. 
##      This updates $HOME/.docker/config.json on your PC.
##   2. View $HOME/.docker/config.json in an editor to ensure it contains only the credentials you want to use.
##   3. Get a list of your nodes; for example:
##     - if you want the names: 
##       nodes=$( kubectl get nodes -o jsonpath='{range.items[*].metadata}{.name} {end}' )
##     - if you want to get the IP addresses: 
##       nodes=$( kubectl get nodes -o jsonpath='{range .items[*].status.addresses[?(@.type=="ExternalIP")]}{.address} {end}' )
##   4. Copy your local .docker/config.json to one of the search paths list above.
##     - for example, to test this out: 
##       for n in $nodes; do scp ~/.docker/config.json root@"$n":/var/lib/kubelet/config.json; done
##

위 설명대로 /root/.docker/config.json 파일을 모든 master node, worker node의 /var/lib/kubelet/config.json 경로에 복사한 후에 아래와 같이 Pod를 deploy하면서  docker.io에서 rate limit이 걸리지 않고 container image를 pulling 할 수 있는지 확인한다. 

 

$  kubectl  apply -f -  <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep
spec:
  containers:
  - name: busybox
    image: busybox
    imagePullPolicy: Always
    args:
    - sleep
    - "1000000"
EOF

pod/busybox-sleep created

$ kubectl get pod
NAME            READY   STATUS    RESTARTS   AGE
busybox-sleep   1/1     Running   0          7s
$

 

주의:
나는 docker.io 유료 가입자이다.  이 글을 읽는 분도 본인이 사용하는 kubernetes cluster가 container image pulling rate limit 없이 동작하게 하려면 꼭 docker.io에 유료 가입해야 한다)

 

 

참고:

가장 일반적인 Use Case는 아래와 같이 namespace에 secret을 생성하고, 이 regcred 라는 secret을 Pod manifest에서 참조하도록 한다. 그런데, 모든 Pod의 manifest를 수정해야 하는 노동력이 추가되므로 이 방법을 권장하지 않는다.

(아마 imagePullSecret 정보를 자동으로 patch해주는 Mutating Admission Controller / WebHook Server를 만들어서 나의 Kubernetes Cluster에 추가해주면 사람이 개입할 일은 없겠지만, Webhook Server를 작성하는 것도 일인지라... ㅠㅠ)

 

$  kubectl create  -n ${MY-NS}  secret docker-registry  regcred  --docker-server=docker.io  --docker-username=${MYID}  --docker-password=${MYPW}

 

 

참고:

이 container image pulling rate limit 해결 방법은 순정 kubernetes cluster에서만 유효하고, Red Hat Openshift(OCP)는 Docker Registry 인증 문제를 해결하는 방식이 다르다. (결론만 말하자면, Red Hat OCP 쪽이 더 명령이 간단하고 쉽게 해결된다)

Red Hat에 비용을 내고 기술 지원 서비스를 받으면, Red Hat 직원이 알아서 해결해줬기 때문에 굳이 남의 밥벌이 영역의 일을 이렇게 공개된 글에 올리는 것은 예의가 아닌 것 같아서, Red Hat OCP에서의 Docker Registry 인증 문제에 대한 해결 방법은 언급하진 않겠다.

 

반응형

 

 


작성일: 2024년 3월 21일

 

 

일반적으로 자주 사용하는 명령은 kubectl cheat sheet를 보는 것을 추천 !

  https://kubernetes.io/docs/reference/kubectl/cheatsheet/

 

 

Pod 이름 가져오기

$  kubectl get pod -l app=myapp  -o jsonpath='{.items[0].metadata.name}'
myapp-6c68579c55-xk6cm

 

Pod 내부에서 bash shell command 수행하기

Script 중간에 kubectl 명령을 이용하는 경우에

아래 예제처럼 --output=jsonpath 옵션을 활용하면 편하면서 복잡한 로직을 만들 수 있다.

##
## jsonpath를 이용하면, 복잡한 script를 작성할 때 편하다.
##

$  kubectl exec -it  -n myns  $(kubectl get  -n nrf  pod -l app=myapp --output=jsonpath="{.items[0].metadata.name}") -c mybox -- bash

 

 

 

Service 리소스의 Public IP address (EXTERNAL-IP) 값 가져오기

jsonpath 옵션을 사용해서 IP Address를 추출하기 전에

아래 명령 예제와 같이 JSON 포맷으로 전체 내용을 출력해본다.

이 전체 JSON 출력 내용을 보면, 내가 추출하고 싶은 Item의 JSON Path를 짐작할 수 있다.

예를 들어, Service resource의 IP "193.173.24.21" 값을 추출하고 싶다면

.status.loadBalancer.ingress 경로로 JSONPATH를 지정하면 된다.

$ kubectl get  -n myns  svc mysvc  -o json

{
    "apiVersion": "v1",
    "kind": "Service",
    "metadata": {

... 중간 생략 ...

    },
    "status": {
        "loadBalancer": {
            "ingress": [
                {
                    "ip": "193.173.24.21"
                }
            ]
        }
    }
}

$

 

위 JSON 전체 내용을 보고, JSONPATH를 확인 후 아래 명령을 입력한다.

 

$ kubectl get  -n myns  svc mysvc  --output=jsonpath="{.status.loadBalancer.ingress[0].ip}"

193.173.24.21

$

 

 

 


 

+ Recent posts