-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit fe752fc
Showing
9 changed files
with
467 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Contributing | ||
Thanks for contributing to this repository. Please read the following guidelines before you submit a pull request. | ||
|
||
## Pull request and issues | ||
If your commit does not change something fundamental, it is fine to create a pull request directly. | ||
If you want to make a larger change or are unsure of the implication(s) of your change, please first create an issue, so that we can discuss it. | ||
|
||
In all cases, please give the change a short and concise description. | ||
|
||
## Commit messages | ||
Every commit message must be meaningful. | ||
Please do not write meaningless words or sentences like "fix", but be short and concise as in "fix divide by zero where altitude is zero in CalculateDistance()". | ||
If the added code requires greater explanation, please document it in the code. | ||
|
||
## Tests | ||
If you introduce some new function or make a behavioural change, please create tests for the change. | ||
However, it is better to write no tests than make nonsensical ones that convey a false sense of confidence. | ||
|
||
## Code quality | ||
Please check what other code in this repository looks like and adhere to language specific guidelines. | ||
The code should be clear and any "magic" must be appropriately documented. Please rethink your work twice, as clear, safe and performant code will make you feel better. | ||
|
||
## Security | ||
Please try to ensure that your code has no vulnerabilities and document every possible security impact. | ||
|
||
## Documentation | ||
If reasonable, please document the changes either in the code itself or separately. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Copyright 2018 PostFinance AG | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Kubenurse | ||
kubenurse is a little service that monitors all network connections in a kubernetes | ||
cluster and exports the taken metrics as prometheus endpoint. | ||
|
||
## Project state | ||
This project was written in only a few hours without receiving a polish but worked well. | ||
Documentation and polish will come. | ||
|
||
## Deployment | ||
TODO | ||
|
||
## Configuration | ||
TODO | ||
|
||
### SSL | ||
The http client appends the certificate `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` if found. You | ||
can disable certificate validation with `KUBENURSE_INSECURE=true`. | ||
|
||
## Alive Endpoint | ||
The following json will be returned when accessing `http://0.0.0.0:8080/alive`: | ||
|
||
```json | ||
{ | ||
"api_server_direct": "ok", | ||
"api_server_dns": "ok", | ||
"me_ingress": "ok", | ||
"me_service": "ok", | ||
"hostname": "example.com", | ||
"neighbourhood_state": "ok", | ||
"neighbourhood" : [neighbours], | ||
"headers": {http_request_headers} | ||
} | ||
``` | ||
|
||
if everything is alright it returns status code 200, else an 500. | ||
|
||
## Health Checks | ||
The checks are described in the follwing subsections | ||
|
||
### api_server_direct | ||
Checks if the `/version` of the Kubernetes API Server is available through | ||
the direct link provided by the kubelet. | ||
|
||
### api_server_dns | ||
Checks if the `/version` of the Kubernetes API Server is available through | ||
the Cluster DNS URL `https://kubernetes.default.svc:PORT`. | ||
|
||
### me_ingress | ||
Checks if itself is reachable at the `/alwayshappy` endpoint behind the ingress. | ||
The address is provided by the env var `KUBENURSE_INGRESS_URL` which | ||
could look like `https://kubenurse.example.com` | ||
|
||
### me_service | ||
Checks if it isself reachable at the `/alwayshappy` endpoint over the kubernetes service. | ||
The address is provided by the env var `KUBENURSE_SERVICE_URL` which | ||
could look like `http://kubenurse.kube-system.default.svc:8080` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/tls" | ||
"crypto/x509" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"os" | ||
"strconv" | ||
"time" | ||
|
||
"github.com/postfinance/kubenurse/pkg/checker" | ||
"github.com/postfinance/kubenurse/pkg/kubediscovery" | ||
"github.com/prometheus/client_golang/prometheus/promhttp" | ||
) | ||
|
||
var ( | ||
chk = &checker.Checker{} | ||
) | ||
|
||
const ( | ||
caFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" | ||
nurse = "I'm ready to help you!" | ||
) | ||
|
||
func main() { | ||
// Setup http transport | ||
transport, err := GenerateRoundTripper() | ||
if err != nil { | ||
log.Printf("using default transport: %s", err) | ||
transport = http.DefaultTransport | ||
} | ||
|
||
client := &http.Client{ | ||
Timeout: 5 * time.Second, | ||
Transport: transport, | ||
} | ||
|
||
// Setup checker | ||
chk.KubenurseIngressUrl = os.Getenv("KUBENURSE_SERVICE_URL") | ||
chk.KubenurseServiceUrl = os.Getenv("KUBENURSE_SERVICE_URL") | ||
chk.KubernetesServiceHost = os.Getenv("KUBERNETES_SERVICE_HOST") | ||
chk.KubernetesServicePort = os.Getenv("KUBERNETES_SERVICE_PORT") | ||
chk.KubeNamespace = os.Getenv("KUBE_NAMESPACE") | ||
chk.NeighbourFilter = os.Getenv("KUBENURSE_NEIGHBOUR_FILTER") | ||
chk.HttpClient = client | ||
|
||
// Setup http routes | ||
http.HandleFunc("/alive", aliveHandler) | ||
http.HandleFunc("/alwayshappy", func(http.ResponseWriter, *http.Request) {}) | ||
http.Handle("/metrics", promhttp.Handler()) | ||
http.Handle("/", http.RedirectHandler("/alive", http.StatusMovedPermanently)) | ||
|
||
fmt.Println(nurse) // most important line of this project | ||
|
||
// Start listener and checker | ||
go func() { | ||
chk.RunScheduled(5 * time.Second) | ||
log.Fatalln("checker exited") | ||
}() | ||
|
||
log.Fatal(http.ListenAndServe(":8080", nil)) | ||
} | ||
|
||
func aliveHandler(w http.ResponseWriter, r *http.Request) { | ||
type Output struct { | ||
Hostname string `json:"hostname"` | ||
Headers map[string][]string `json:"headers"` | ||
|
||
// checker.Result | ||
APIServerDirect string `json:"api_server_direct"` | ||
APIServerDNS string `json:"api_server_dns"` | ||
MeIngress string `json:"me_ingress"` | ||
MeService string `json:"me_service"` | ||
|
||
// kubediscovery | ||
NeighbourhoodState string `json:"neighbourhood_state"` | ||
Neighbourhood []kubediscovery.Neighbour `json:"neighbourhood"` | ||
} | ||
|
||
// Run checks now | ||
res, haserr := chk.Run() | ||
if haserr { | ||
w.WriteHeader(http.StatusInternalServerError) | ||
} | ||
|
||
// Add additional data | ||
out := Output{ | ||
APIServerDNS: res.APIServerDNS, | ||
APIServerDirect: res.APIServerDirect, | ||
MeIngress: res.MeIngress, | ||
MeService: res.MeService, | ||
Headers: r.Header, | ||
Neighbourhood: res.Neighbourhood, | ||
NeighbourhoodState: res.NeighbourhoodState, | ||
} | ||
out.Hostname, _ = os.Hostname() | ||
|
||
// Generate output output | ||
enc := json.NewEncoder(w) | ||
enc.SetIndent("", " ") | ||
enc.Encode(out) | ||
|
||
} | ||
|
||
func GenerateRoundTripper() (http.RoundTripper, error) { | ||
insecureEnv := os.Getenv("KUBENURSE_INSECURE") | ||
insecure, _ := strconv.ParseBool(insecureEnv) | ||
|
||
rootCAs, _ := x509.SystemCertPool() | ||
if rootCAs == nil { | ||
rootCAs = x509.NewCertPool() | ||
} | ||
|
||
caCert, err := ioutil.ReadFile(caFile) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not load certificate %s: %s", caFile, err) | ||
} | ||
|
||
if ok := rootCAs.AppendCertsFromPEM(caCert); !ok { | ||
return nil, errors.New("could not append ca cert to system certpool") | ||
} | ||
|
||
tlsConfig := &tls.Config{ | ||
InsecureSkipVerify: insecure, | ||
RootCAs: rootCAs, | ||
} | ||
|
||
transport := &http.Transport{TLSClientConfig: tlsConfig} | ||
|
||
return transport, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package checker | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/postfinance/kubenurse/pkg/kubediscovery" | ||
"github.com/postfinance/kubenurse/pkg/metrics" | ||
) | ||
|
||
func (c *Checker) Run() (Result, bool) { | ||
var haserr bool | ||
var err error | ||
|
||
// Run Checks | ||
res := Result{} | ||
|
||
res.APIServerDirect, err = meassure(c.ApiServerDirect, "api_server_direct") | ||
haserr = haserr || (err != nil) | ||
|
||
res.APIServerDNS, err = meassure(c.ApiServerDNS, "api_server_dns") | ||
haserr = haserr || (err != nil) | ||
|
||
res.MeIngress, err = meassure(c.MeIngress, "me_ingress") | ||
haserr = haserr || (err != nil) | ||
|
||
res.MeService, err = meassure(c.MeService, "me_service") | ||
haserr = haserr || (err != nil) | ||
|
||
res.Neighbourhood, err = kubediscovery.GetNeighbourhood(c.KubeNamespace, c.NeighbourFilter) | ||
haserr = haserr || (err != nil) | ||
|
||
// Neighbourhood special error treating | ||
if err != nil { | ||
res.NeighbourhoodState = err.Error() | ||
} else { | ||
res.NeighbourhoodState = "ok" | ||
|
||
// Check all neighbours | ||
c.checkNeighbours(res.Neighbourhood) | ||
} | ||
|
||
return res, haserr | ||
} | ||
|
||
func (c *Checker) RunScheduled(d time.Duration) { | ||
for range time.Tick(d) { | ||
c.Run() | ||
} | ||
} | ||
|
||
func (c *Checker) ApiServerDirect() (string, error) { | ||
apiurl := fmt.Sprintf("https://%s:%s/version", c.KubernetesServiceHost, c.KubernetesServicePort) | ||
return c.doRequest(apiurl) | ||
} | ||
|
||
func (c *Checker) ApiServerDNS() (string, error) { | ||
apiurl := fmt.Sprintf("https://kubernetes.default.svc:%s/version", c.KubernetesServicePort) | ||
return c.doRequest(apiurl) | ||
} | ||
|
||
func (c *Checker) MeIngress() (string, error) { | ||
return c.doRequest(c.KubenurseIngressUrl + "/alwayshappy") | ||
} | ||
|
||
func (c *Checker) MeService() (string, error) { | ||
return c.doRequest(c.KubenurseServiceUrl + "/alwayshappy") | ||
} | ||
|
||
func (c *Checker) checkNeighbours(nh []kubediscovery.Neighbour) { | ||
for _, neighbour := range nh { | ||
check := func() (string, error) { | ||
return c.doRequest("http://" + neighbour.PodIP + ":8080/alwayshappy") | ||
} | ||
|
||
meassure(check, "path_"+neighbour.NodeName) | ||
} | ||
} | ||
|
||
func meassure(check Check, label string) (string, error) { | ||
start := time.Now() | ||
|
||
// Execute check | ||
res, err := check() | ||
|
||
// Process metrics | ||
metrics.DurationSummary.WithLabelValues(label).Observe(time.Since(start).Seconds()) | ||
if err != nil { | ||
log.Printf("failed request for %s with %v", label, err) | ||
metrics.ErrorCounter.WithLabelValues(label).Inc() | ||
} | ||
|
||
return res, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package checker | ||
|
||
import ( | ||
"errors" | ||
"net/http" | ||
) | ||
|
||
func (c *Checker) doRequest(url string) (string, error) { | ||
resp, err := c.HttpClient.Get(url) | ||
if err != nil { | ||
return err.Error(), err | ||
} | ||
|
||
// Body is non-nil if err is nil, so close it | ||
resp.Body.Close() | ||
|
||
if resp.StatusCode == http.StatusOK { | ||
return "ok", nil | ||
} | ||
|
||
return resp.Status, errors.New(resp.Status) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package checker | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/postfinance/kubenurse/pkg/kubediscovery" | ||
) | ||
|
||
type Checker struct { | ||
// Ingress and service config | ||
KubenurseIngressUrl string | ||
KubenurseServiceUrl string | ||
|
||
// Kubernetes API | ||
KubernetesServiceHost string | ||
KubernetesServicePort string | ||
|
||
// Neighbourhood | ||
KubeNamespace string | ||
NeighbourFilter string | ||
|
||
// Http Client for https requests | ||
HttpClient *http.Client | ||
} | ||
|
||
type Result struct { | ||
APIServerDirect string `json:"api_server_direct"` | ||
APIServerDNS string `json:"api_server_dns"` | ||
MeIngress string `json:"me_ingress"` | ||
MeService string `json:"me_service"` | ||
NeighbourhoodState string `json:"neighbourhood_state"` | ||
Neighbourhood []kubediscovery.Neighbour `json:"neighbourhood"` | ||
} | ||
|
||
type Check func() (string, error) |
Oops, something went wrong.