현수의 제 2의 뇌
article thumbnail
반응형

안녕하세요! 데브옵스를 공부하고 있는 부현수 입니다.

 

저는 대덕 소프트웨어 마이스터고등학교에 재학중이며 학교 학생들이 개발에 집중할 수 있도록 배포 자동화, 모니터링 등을 플랫폼을 통해 제공하고 있습니다.

 

인프라를 구축하는 과정에서 일어났던 간단한 문제점을 소개하고, 오픈소스 기여까지 하게된 이야기를 소개해드리려고 합니다.

 

문제상황

인프라 팀은 학교 예산을 지원받아, 한정된 예산으로 환경을 만들어야 했기 때문에, Spot Instance를 사용하며 Deployment의 Replica 또한 1개로 유지하였습니다. Replica의 개수가 1개인 탓에 새로운 버전의 파드가 올라왔을때 서버가 실행되는 과정동안 Downtime이 생기는 것을 확인했습니다.

 

대안 도출

이를 해결하기 위한 대안의 폭은 그리 넓지 않다고 생각했습니다.

Kubernetes에서 기본적으로 제공하는 ReadinessProbe기능을 사용하여 해당 파드에서 포트를 지정해 정상적으로 열렸는지 확인하면 되는 간단한 문제였죠.

readinessProbe:
  tcpSocket:
    port: {{ .Values.containerPort }}
  initialDelaySeconds: 20
  periodSeconds: 10
  successThreshold: 3

 

문제점

하지만 이 방법에서 약간의 한계가 있었습니다.

Spring Boot 서버를 예시로 들자면, 보통 Health Check를 할때 actuator 를 통해 Database, Kafka 등 서버가 제대로 작동하기 위한 리소스들의 상태까지 확인합니다.

하지만 저희의 플랫폼은 학생들이 작성한 서버 혹은 프론트엔드를 자유롭게 배포하는 서비스인데요, 따라서 위 예시와 같은 설정들을 모두 만족할 수 없다는 제약이 있기 때문에 더욱 정확하고 안전한 서비스를 제공할 수 없었습니다.

결과

Readiness Probe설정을 하고 난 뒤, 새로운 버전이 올라오더라도 새로운 버전의 파드에서 서비스의 포트가 열리기 전까지는 이전 버전의 파드가 종료되지 않기 때문에 더욱더 안정적인 서비스를 제공할 수 있었습니다.

 

 


기여를 하게된 이야기

 

저는 설정을 마친 뒤 ReadinessProbe가 어떻게 작동하는지 궁금하였습니다.

작동방식이 어떻게 되는지 인터넷에 찾아보았지만, 제가 원하는 내용이 많지 않아 저는 바로 소스코드를 살펴보았습니다.

Kubernetes의 내부 동작 코드가 있는 pkg를 살펴보면, 여러 디렉토리가 있고, kubelet 파일 안에있는 prober 폴더를 중심적으로 보았습니다.

 

probe.go 에 있는 runProbe를 읽던 도중, 뭔가 불편한 코드가 있었습니다.

func (pb *prober) runProbe(ctx context.Context, probeType probeType, p *v1.Probe, pod *v1.Pod, status v1.PodStatus, container v1.Container, containerID kubecontainer.ContainerID) (probe.Result, string, error) {
	timeout := time.Duration(p.TimeoutSeconds) * time.Second
	if p.Exec != nil {
		klog.V(4).InfoS("Exec-Probe runProbe", "pod", klog.KObj(pod), "containerName", container.Name, "execCommand", p.Exec.Command)
		command := kubecontainer.ExpandContainerCommandOnlyStatic(p.Exec.Command, container.Env)
		return pb.exec.Probe(pb.newExecInContainer(ctx, container, containerID, command, timeout))
	}
	if p.HTTPGet != nil {
		req, err := httpprobe.NewRequestForHTTPGetAction(p.HTTPGet, &container, status.PodIP, "probe")
		if err != nil {
			return probe.Unknown, "", err
		}
		if klogV4 := klog.V(4); klogV4.Enabled() {
			port := req.URL.Port()
			host := req.URL.Hostname()
			path := req.URL.Path
			scheme := req.URL.Scheme
			headers := p.HTTPGet.HTTPHeaders
			klogV4.InfoS("HTTP-Probe", "scheme", scheme, "host", host, "port", port, "path", path, "timeout", timeout, "headers", headers)
		}
		return pb.http.Probe(req, timeout)
	}
	if p.TCPSocket != nil {
		port, err := probe.ResolveContainerPort(p.TCPSocket.Port, &container)
		if err != nil {
			return probe.Unknown, "", err
		}
		host := p.TCPSocket.Host
		if host == "" {
			host = status.PodIP
		}
		klog.V(4).InfoS("TCP-Probe", "host", host, "port", port, "timeout", timeout)
		return pb.tcp.Probe(host, port, timeout)
	}

	if p.GRPC != nil {
		host := status.PodIP
		service := ""
		if p.GRPC.Service != nil {
			service = *p.GRPC.Service
		}
		klog.V(4).InfoS("GRPC-Probe", "host", host, "service", service, "port", p.GRPC.Port, "timeout", timeout)
		return pb.grpc.Probe(host, service, int(p.GRPC.Port), timeout)
	}

	klog.InfoS("Failed to find probe builder for container", "containerName", container.Name)
	return probe.Unknown, "", fmt.Errorf("missing probe handler for %s:%s", format.Pod(pod), container.Name)
}

 

probe가 어떤 종류인지 판단하고 로직을 수행하는 코드인데요.

종류를 판단하는 로직이 모두 if 문으로 작성되어 있었습니다.

if문이 3개 이상 있는 상황에서는 switch case가 더 효율적이고, 가독성을 더 높일 수 있을것이라고 생각하였습니다.

 

저는 이 부분은 수정하여 Pull Request 를 요청했습니다. [ https://github.com/kubernetes/kubernetes/pull/123336 ]

PR을 진행하는 과정에서 실제로 Kubernetes Main Contributer 와 대화하며 협업해 볼 수 있다는 사실이 흥미로웠습니다.

Merge 되는데 대략 3주 정도 시간걸렸고, v1.30 부터 저의 코드가 적용될 것이라고 명시되어 있네요.

 

저는 오픈소스 기여를 해보고 싶다는 마음이 있었지만 어떻게 해야할지 감도 오지 않았습니다.

이번에도 기여를 할 생각으로 코드를 읽은게 아니었죠, 하지만 이번 계기로 코드를 읽다보며 자연스럽게 더 좋은 코드를 작성해 기여하게 되었네요.

 

사소한 리펙토링만으로도 Kubernetes에 기여하여 도움이 된 것 같아 기분이 좋습니다.

 

긴 글 읽어주셔서 감사하고 다음에는 더 좋은 글로 찾아뵙겠습니다. 감사합니다 ☺️

 

LinkedIn - https://www.linkedin.com/in/%ED%98%84%EC%88%98-%EB%B6%80-5031902b3/

Github - https://github.com/HyunSu1768

profile

현수의 제 2의 뇌

@부현수

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!