k8sでpreStop.httpGetに指定したpathの先頭スラッシュが二重になる

2020/11/09

Categories: k8s

事象

preStop.httpGetに以下のような指定をした場合に、実際のhttpリクエストのpathがhttp://{podip:port}//prestopになる。

         lifecycle:
           preStop:
             httpGet:
               scheme: HTTP
               port: 80
               path: "/prestop"

少なくともgojiではpat.Get("/prestop", ...)にマッチせず404が返り、結果いきなりpodがterminateされる。
※net/httpだと//prestop/prestopにリダイレクトする(go1.15.3で確認)ので結果的に到達できているかも。

原因とworkaround

こんな感じでpreStopに対するURLの組み立ての中でスラッシュが2つになってしまうようだ。 なので、pathの値から先頭のスラッシュを削って/prestopprestopとすることでリクエストが到達するようになる。

github.com/kubernetes/kubernetes/pkg/kubelet/lifecycle/handlers.go@v1.19.1

 func (hr *handlerRunner) runHTTPHandler(pod *v1.Pod, container *v1.Container, handler *v1.Handler) (string, error) {
 	// 略
 	url := fmt.Sprintf("http://%s/%s", net.JoinHostPort(host, strconv.Itoa(port)), handler.HTTPGet.Path)
 	resp, err := hr.httpGetter.Get(url)
 	return getHTTPRespBody(resp), err
 }

ここにissueがあるけど放置されてる。
https://github.com/kubernetes/kubernetes/issues/24803

ちなみにprobeの方は

以下のような実装になっており/pingならhttp://{podip:port}/pingになる。

github.com/kubernetes/kubernetes/pkg/probe/http/http.go@v1.19.1

 func formatURL(scheme string, host string, port int, path string) *url.URL {
 	u, err := url.Parse(path)
 	// Something is busted with the path, but it's too late to reject it. Pass it along as is.
 	if err != nil {
 		u = &url.URL{
 			Path: path,
 		}
 	}
 	u.Scheme = scheme
 	u.Host = net.JoinHostPort(host, strconv.Itoa(port))
 	return u
 }

環境

>> Home