Kubernetes

  • Published on
    컨테이너 이미지에 최소한으로 필요한 software만 포함하는 것이 보안적으로 모범사례이다. 이러한 모범사례를 따르는 가장 쉽고 합리적인 방법은 구글에서 제공하는 Distroless Image를 활용하는 것이다. Distroless Image는 nonroot, debug, debug-nonroot Tag를 가지고 있다. nonroot은 conatiner에게 root 권한을 주지않고, shell도 없어서 Terminal로 접속이 불가능하다. 그런데 Distroless Image에 추가로 필요한 debian package를 설치하면 어떻게 해야할까? 이번 글에서는 bazel로 빌드할 때 원하는 debian package를 추가하는 방법을 알아 본다.
  • Published on
    ModSecurity는 오픈소스로 제공하는 WAF이다. Nginx connector를 통해서 ModSecurity를 Nginx에 쉽게 연동할 수 있다. Nginx Ingress Controller에서는 해당 설정을 할 수 있도록 Configmap에 설정 옵션들을 제공한다. 해당 옵션들을 설정함으로서 쉽게 Nginx에 WAF를 구현할 수 있다. ModSecurity는 Trustware라는 회사가 관리하다가 2024년 1월에 OWASP foundation으로 넘어가게 되었다. ModSecurity는 오래된 프로젝트이고, Production Ready라고 소개되고 있다. 하지만 ModSecurity가 앞으로도 계속 커뮤니티를 통해서 활발히 관리될지는 지켜봐야겠다.
  • Published on
    소스코드를 변경하지 않고 opentelemetry-operator의 Instrumentation CRD를 통해서 Python Application을 자동으로 Instrumentation을 해봤다. 이 과정에서 OpenTelemetry Logging의 경우에는 기존 Logging Libarary에 Bridge API로 연동하는 구조로 설계된 것을 알게 되었고, Python opentelemetry sdk가 logger에 handler를 추가하는 것을 알게 되었다. 또한 sitecustomize.py을 통해서 Python Application이 실행되기 전에 Instrumentation library를 셋팅하는 것을 이해하게 되었다. Gunicorn으로 Flask를 실행할 때와 Uvicorn으로 FastAPI를 실행할 때, Auto Instrumentation이 잘 되는 것을 확인하였다.
  • Published on
    Kubernetes Worker Node와 Pod Container에 Terminal shell로 접근하여 명령어를 입력하면 로그를 남기고, 경우에 따라서 알림을 보내고 싶었다. CNCF 졸업한 Project인 Falco를 활용하면 이를 구현할 수 있지 않을까 하여 테스트를 해보게 되었다. Falco는 kernel module이나 eBPF probe등을 통해서 kernel event를 저장하고, 설정된 rule에 따라서 해당 event를 filter하여 원하는 output 형태로 저장한다. Falco는 모든 로그를 저장하기보다는 Rule에 따라 선택적으로 보안 위협이 있는 이벤트를 수집하고 실시간으로 알림하는 것이 목적이다. 따라서 Falco를 통해서 원하던 기능을 구현하는 것은 합리적인 방법이 아닌 것으로 판단 된다.
  • Published on
    Ubuntu 20.04 서버의 auth.log, syslog 로그 값들을 OpenTelemetry를 통해서 수집하고 싶었다. 처음에는 Filelog Receiver를 통해서 수집하려고 하였고, rsyslog의 설정값을 변경하여 Filelog로 수집하도록 구성했다. 그런데 이후에 Syslog Receiver가 존재하는 것을 확인하게 되었고, 훨씬 간단하게 syslog를 수집할 수 있었다.
  • Published on
    사이즈가 큰 컨테이너 이미지를 내려받기 위해서 Containerd의 설정값 root을 새로운 스토리지를 추가한 경로로 수정하였다. Kubelet은 imageGCHighThresholdPercent로 설정된 임계치보다 disk 사용량이 많으면, 컨테이너 이미지를 정리하여 disk 공간을 확보하려고 한다. imageGCHighThresholdPercent이 기본값으로 85로 설정되어 있고, disk의 85 퍼센트 이상 사용했을 때 정리 프로세스가 실행된다. 문제는 Containerd root 경로의 volume이 거의 꽉 차더라도, 전체 volume의 사용량은 85 퍼센트가 되지 않을 수 있다는 것이다. Containerd root 경로의 volume에 여유가 없어서 새로 스케쥴된 Pod의 컨테이너 이미지를 내려 받지 못하는 문제가 발생할 수 있다. imageGCHighThresholdPercent 설정값을 조절하여 이 문제를 해결할 수 있다.
  • Published on
    Loki로 Log를 수집하고, Grafana로 Log를 보여줄 때 Grafana user별로 볼 수 있는 Log를 제한하고 싶었다. Grafana Enterpirse의 경우에는 Label-based access control을 제공하여, Loki label별로 Query할 수 있는 권한을 제한할 수 있는 것처럼 보인다. 하지만 아쉽게 오픈소스에서는 해당 기능을 제공하지 않는다. 그래서 Loki multi tenant를 통해서 Log를 tenent별로 그룹핑하고, tenant별로 query하는 방법을 사용했다. 그리고 Loki는 인증 layer가 존재하지 않기 때문에 Nginx를 통해서 인증을 하여 query할 수 있도록 하였다. 마지막으로 Network Policy로 Nginx 인증을 통해서 Loki에 접근하도록 강제하여 원하는 구성을 할 수 있었다.
  • Published on
    Kubernetes에서 k8s-device-plugin을 통해서 NVIDIA GPU 자원을 쉽게 사용할 수 있다. 하나의 어플리케이션에서 GPU 자원을 온전히 사용하지 못하고 낭비될 때, Time slicing, MPS, MIG 등을 사용하여 여러 프로세스가 GPU 자원을 공유하도록 설정할 수 있다. 네이버 공공 클라우드에서 Tesla T4와 Telsa V100을 제공한다. 해당 GPU 아키텍쳐가 MIG을 지원히지 않기 때문에, MPS를 적용하여 GPU 자원을 공유하는 것을 고려하였다. 최근에 release된 k8s-device-plugin v0.15.0-rc.1부터 MPS가 지원되기 시작되어 해당 버전으로 MPS를 설정해보았다.
  • Published on
    Kubernetes에서 pod가 실행될 때, 해당 process의 open file 갯수 limit이 어떻게 되는지 궁금하였다. Containerd가 systemd service로 동작하는 상황에서 systemd에서 LimitNOFILE이 inifinity로 설정되어 있는 것을 확인하였다. infinity가 Ubuntu systemd 240 버전 이상에서는 process에 최대로 할당할 수 있는 File 갯수인 fs.nr_open 값으로 설정된다. 실제로 test용 pod를 띄어서 containerd에 의해서 실행되는 process의 nofile limit이 LimitNOFILE에 설정된 값으로 동일하게 설정되는 것을 확인하였다.
  • Published on
    Kubernetes에서 Tekton Chain을 통해서 어떻게 Software Supply Chain Security를 구성할 수 있는지 확인했다. Tekton Pipeline으로 git clone을 하고, container image를 build하고, 최종적으로 OCI registry에 push하도록 구성했다. 그리고 Tekton Chain이 어떻게 in-toto spec의 Attestation 정보를 남기는지 확인하였다.
  • Published on
    네이버 클라우드 쿠버네티스 서비스에서 HostPort를 설정하였는데, 정상적으로 Node Port를 통해서 Container에 접근할 수가 없었다. 처음에는 CNI plugin portmap을 설정하여 HostPort를 Iptables Rule로 적용하도록 하였다. 하지만 현재 운영하는 Kernel 버전이 kube proxy를 대체할 수 있는 것을 파악하였고, default로 설정된 KubeProxyReplacement=disabled을 KubeProxyReplacement=strict으로 설정하여 Cilium eBPF로 대체하여 사용하였다.
  • Published on
    Cert Manager의 DNS provider 목록에 Naver Cloud DNS는 없었다. 하지만 Cert Manager에서 새로운 DNS provider를 직접 연결해서 사용할 수 있도록 webhook solver라는 것을 제공한다. 따라서 Naver Cloud API를 사용하여 webhook 코드를 작성하고, Github Action을 통해서 Image와 Helm chart를 배포해서 사용했다. Cert Manager에서 Boilerplate code와 test framework를 제공하여 비교적 쉽게 작성해서 사용할 수 있었다.
  • Published on
    OpenTelemetry collector를 사용하여 tracing과 logging을 같이 하는 것을 검토하였다. Kubernetes와 멀어져 있던 사이에 OpenTelemetry 커뮤니티가 엄청나게 성장한 것을 깨닫게 되었다. Log에 traceID를 남기고 그걸로 Tracing 정보를 볼 수 있도록 구성했고, Grafana 하나에서 통합적으로 볼 수 있도록 Loki와 Tempo를 Exporter로 사용했다. 아직 Python과 Nodejs에서는 Log쪽의 상태는 Development나 Experimental이기 때문에, receiver에서 filelog를 사용하여 Kubernetes log file을 fluentbit처럼 tail해서 가져오고 traceId를 log에 넣어주는 instrument libary를 사용했다.
  • Published on
    GitOps에서 Secret을 어떻게 관리할지 고민을 하였고, 개발자들의 인지부하를 줄이기 위해서 Vault UI로 자신의 앱의 비밀값을 관리하는 것이 제일 효율적이라는 판단을 했다. Vault secrets operator가 GA로 공유가 되었고, Secrets Store CSI나 External secrets 프로젝트보다 깔끔한 방식이라는 생각이 들었다. Vault secrets operator의 CRD로 vault secret과 kubernetes secret의 sync를 맞추고, reloader로 secret이 변경되었을 때 다시 pod를 배포하는 것을 테스트해보았다.
  • Published on
    Kubernetes cluster에서 Nginx로 허용 가능한 IP를 설정하고 싶었다. 간단하게 끝날 줄 알았던 작업은 또다른 삽질기가 되었다.😭 Kubernetes에서 SNAT이 되는 과정을 이해하고 externalTrafficPolicy를 Local로 설정했다. 그리고 External Load balancer에서는 client IP를 전달하기 위해서 Proxy Protocol와 같은 것을 설정하고, Nginx에서 그 Proxy protocol로 전달된 IP address로 Access control을 할 수 있도록 설정하였다.
  • Published on
    Pytorch를 컨테이너로 띄우고 GPU를 사용하고 싶었다. 따라서 Docker container에서 NVIDIA GPU를 사용할 수 있도록 셋팅을 해보았다. 그리고 최종적으로 Kubernetes에서 GPU hardware를 사용할 수 있도록 nvidia device plugin을 DaemonSet으로 띄우고 Pod를 실행해보았다.
  • Published on
    aws-ebs-csi-driver 소스코드를 보고 csi driver가 하는 역할을 이해해보았다. 크게 Controller plugin과 Node plugin으로 구성되어 있고, 다양한 sidecar container들이 존재한다. 이 sidecar container들이 Volume을 생성하고, 노드에 부착하고, Host 혹은 container directory에 mount하는 것을 진행한다. 이과정에서 CSI driver로 cloud vendor마다 다른 로직으로 그들의 volume을 제어하게 된다. 네이버 클라우드에서 storage가 처음 생성될 때 서버에 부착이 되어야만 하는 제약사항이 있어서, PVC로 PV 동적 할당을 할 때 volumeBindingMode이 Immediate이면 자동으로 Node에 부착이 된다.
  • Published on
    Kubeadm으로 만든 Kubernetes Cluster에서 AWS ECR를 private container image repository를 사용하고자 하였다. private repository를 접근하기 위해서 Kubernetes 1.26부터 stable feature로 제공하는 kubelet crendential provider를 사용하였다.
  • Published on
    CRD와 Custom Controller를 사용하는 프로젝트에 대해서 더 알고 싶은 상황에서, External Secrets이라는 프로젝트를 발견하게 되었다. External Secrets는 Vault, AWS secret manager와 같이 secret 관리하는 Tool들과 Kubernetes secret을 CRD을 통해서 sync할 수 있게 해준다. 이 프로젝트는 kubebuilder를 사용하였는데, 소스코드를 보면서 어떻게 구현한 건지 자세히 살펴보았다.
  • Published on
    Calico에서 어떻게 CRD를 활용하는지 이해하기 위해서 Calico Opensource 버전의 깃헙 소스코드를 살펴보게 되었다. Calico archiecture에서 kube-controller 부분이 어떤 역할을 하는지 소스 코드를 통해서 이해할 수 있게 되었다. kube-controller들은 kubernetes native resource에 대한 변경을 calico data store와 sync해주는 역할을 하고 있다. 내가 사용하는 Minikbue Kubernetes Cluster에서는 Calico의 data store는 kubernetes로 설정되어 있기 때문에, CRD로 Calico data들이 저장되고 Felix가 이것을 watch하여 변화에 대해서 network rule을 업데이트 하게 된다.
  • Published on
    어제는 Programming Kubernetes에 나오는 예제를 kubebuilder로 작성해보았다. if(kakao)2022에서 controller를 테스트하는 방법에 대해서 설명한 발표가 있었다. 어제 이해한 내용을 바탕으로 이 발표에서 사용한 BlueGreen controller 예제를 따라서 작성해보았다.
  • Published on
    Programming Kubernetes 책에서 나온 예제를 따라서 custom controller를 작성해보았다. CustomResourceDefinition를 어떻게 정의하고, kubebuilder로 어떻게 나만의 business logic을 작성할 수 있는지 예제를 통해서 이해했다.
  • Published on
    오늘 대왕 흑역사를 생산했다. Kubernetes Community Days에서 발표를 하게 되었는데, 30분 발표시간에 맞춰서 발표자료를 잘 준비하지 못해서 부끄러웠다. 30분에 너무 많은 내용을 담을려고 했고, 라이브코딩 형식으로 해서 시간도 많이 걸렸다. 나는 발표를 왜 할까?