어플리케이션이 다른 파드와 클러스터에 정의된 다른 리소스에 대해 알아야할 경우에는 API 서버와 직접 통신해야 한다.
컨테이너 내부에서 실행되는 어플리케이션이 쿠버네티스 API 서버와 통신해 클러스터에 배포된 리소스 정보를 얻는 방법에 대해 살펴보자.
kubectl proxy
는 로컬 시스템에서 HTTP 연결을 허용하고 k8s API 서버와 통신한다. 요청할 때마다 인증 토큰을 전달할 필요가 없다.
# 로컬 k8s 프록시 실행
$ kubectl proxy
Starting to serve on 127.0.0.1:8001
# 로컬 포트 8001에서 연결 수락
$ curl localhost:8001
{
"paths": [
"/api",
"/api/v1",
"/apis",
"/apis/",
"/apis/apps",
"/apis/apps/v1",
"/apis/batch",
"/apis/batch/v1",
"/apis/batch/v1beta1",
...
반환한 결과의 paths
는 파드, 서비스 등과 같은 리소스를 생성할 때 리소스 정의에 지정한 API 그룹 및 버전에 해당한다. /apis/batch/v1
경로의 batch/v1
은 잡(Job) 리소스의 버전으로 인식할 수 있다.
🔎 리소스 API 탐색 (1)
# 잡 리소스 API 탐색
$ curl http://localhost:8001/apis/batch
{
"kind": "APIGroup",
"apiVersion": "v1",
"name": "batch",
"versions": [
{
"groupVersion": "batch/v1",
"version": "v1"
},
{
"groupVersion": "batch/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "batch/v1",
"version": "v1"
}
}
batch API 그룹에는 v1
와 v1beta1
두 가지 버전이 있는 것을 확인할 수 있다. preferredVersion
항목을 통해 v1 버전을 권장하는 것을 알 수 있다.
🔎 리소스 API 탐색 (2)
$ curl http://localhost:8001/apis/batch/v1
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "batch/v1",
"resources": [
{
"name": "cronjobs",
"singularName": "",
"namespaced": true,
"kind": "CronJob",
"verbs": ["create", "delete","deletecollection","get","list","patch","update","watch"],
"shortNames": ["cj"],
"categories": ["all"],
"storageVersionHash": "sd5LIXh4Fjs="
},
...
{
"name": "jobs",
"singularName": "",
"namespaced": true,
"kind": "Job",
"verbs": ["create","delete","deletecollection","get","list","patch","update","watch"],
"categories": ["all"],
"storageVersionHash": "mudhfqk/qZY="
},
...
]
}
반환된 목록은 API 서버에 공개된 REST 리소스를 설명한다. 리소스의 name
과 kind
뿐만 아니라 리소스가 namespace
인지 아닌지, 함께 사용할 수 있는 verbs
목록이 포함된다. "name: jobs"는 API에 "/apis/batch/v1/jobs" 엔드포인트가 있음을 알려준다. verbs 배열은 해당 엔드포인트를 통해 작업 리소스를 생성, 검색, 업데이트, 삭제할 수 있다고 말해준다.
🔎 클러스터에 있는 모든 잡 인스턴스 목록 탐색
클러스터 잡 리소스 job.yaml를 배포하고 클러스터에 있는 모든 잡 인스턴스 목록을 조회한다.
$ curl http://localhost:8001/apis/batch/v1/jobs
{
"kind": "JobList",
"apiVersion": "batch/v1",
"metadata": {
"resourceVersion": "4536"
},
"items": [
{
"metadata": {
"name": "pi",
"namespace": "default",
...
}
🔎 이름으로 특정한 잡 인스턴스 탐색
하나의 특정한 잡을 조회하려면 URL에 이름과 네임스페이스를 지정해야 한다.
$ curl http://localhost:8001/apis/batch/v1/namespaces/default/jobs/pi
{
"kind": "Job",
"apiVersion": "batch/v1",
"metadata": {
"name": "pi",
"namespace": "default",
...
프록시를 사용하지 않고 파드 내부에서 API 서버와 통신하려면, API 서버의 위치를 알아야하고 서버로 인증을 해야 한다.
apiVersion: v1
kind: Pod
metadata:
name: curl
spec:
containers:
- name: main
image: curlimages/curl
command: ["sleep", "9999999"]
오직 컨테이너에서 sleep 명령을 실행하는 파드
를 생성한다.
# kubectl exec로 컨테이너에서 쉘을 실행
$ kubectl exec -it curl /bin/sh
/ $
curl를 사용해 해당 쉘 내에서 API 서버에 접근을 시도한다.
k8s API 서버의 IP와 포트를 찾아야 한다. k8s라는 서비스는 자동으로 namepsace에 노출돼 API 서버를 가리키도록 구성돼 있다.
# 서비스 조회
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 92m
API 서버가 HTTPS의 기본 포트인 포트 443에서 수신 중임을 알 수 있다.
# API 서버에 접속
/ $ curl https://kubernetes
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
API 서버에 접속 시 인증이 없어서 에러가 나는 것을 알 수 있다.
모든 파드에는 자동으로 연결된 secret
볼륨이 있다. default-token 시크릿은 각 컨테이너의 /var/run/secrets/kubernetes.io/serviceaccount/에 마운트된다.
# 해당 디렉토리에 파일을 나열해 시크릿 내용 조회
/ $ ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt namespace token
시크릿에는 ca.crt
, namespace
, token
세 개의 항목이 있다. ca.crt
파일은 k8s API 서버 인증서에 서명하는데 사용된 인증기관(CA)의 인증서를 보유하고 있다. token
파일은 서버에 인증하는데 이용된다. namespace
파일은 파드가 실행중인 네임스페이스가 포함돼 있다.
# 내부 API 서버 호스트 이름을 가리킨다
APISERVER=https://kubernetes
# 서비스어카운트(ServiceAccount) 토큰 경로
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
# 이 파드의 네임스페이스를 읽는다
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
# 서비스어카운트 베어러 토큰을 읽는다
TOKEN=$(cat ${SERVICEACCOUNT}/token)
# 내부 인증 기관(CA)을 참조
CACERT=${SERVICEACCOUNT}/ca.crt
# --cacert 옵션을 통해 인증서 지정 및 Authorization 헤더에 토큰 지정하여 API를 탐색
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/apis/batch/v1
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "batch/v1",
"resources": [
{
"name": "cronjobs",
"singularName": "",
"namespaced": true,
"kind": "CronJob",
...
# 파드 탐색
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/$NAMESPACE/pods
{
"kind": "PodList",
"apiVersion": "v1",
...
요청의 Authorization HTTP 헤더 안에 토큰을 전달한다. API 서버는 토큰을 인증된 것으로 인식하고 적절한 응답을 반환한다.
- API 서버의 인증서가
ca.crt
파일에 있는 certificate 기관에 의해 서명됐는지 여부를 확인한다.- 어플리케이션은
token
파일에서 무기명 토큰과 함께 Authorization 헤더를 보내 자신을 인증한다.namespace
파일은 파드의 네임스페이스 안에 있는 API 객체에 대해 CRUD 작업을 수행할 때 네임스페이스를 API 서버로 전달하는 데 사용한다.
파드에서 k8s API를 사용하는 가장 쉬운 방법은 클라이언트 라이브러리 중 하나를 사용하는 것이다. 이 라이브러리들은 API 서버를 자동으로 감지하고 인증할 수 있다.
공식 클라이언트 라이브러리
사용자 제공 클라이언트 라이브러리
참고 : https://kubernetes.io/ko/docs/reference/using-api/client-libraries/
스웨거 API 프레임워크는 Web API 문서화를 위한 도구이다. k8s API 서버는 /swaggerapi 에서 스웨거 API 정의를 공개하고, /swagger.json에서 OpenAPI 스펙을 제공한다.
스웨거 UI를 통해 REST API를 탐색할 수 있다. API 서버를 --enable-swagger-ui=true
옵션으로 실행하면 이 기능을 활성화할 수 있다.
UI를 활성화한 후에는 웹 브라우저에서 https://<api server>:<port>/swagger-ui
url을 통해 확인할 수 있다.
스웨거 UI (출처: segmentfault.com/a/1190000040875079/en)
References