diff --git a/examples/explorer/Dockerfile b/examples/explorer/Dockerfile new file mode 100644 index 00000000000..e6545402f20 --- /dev/null +++ b/examples/explorer/Dockerfile @@ -0,0 +1,20 @@ +# Copyright 2015 The Kubernetes Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM scratch +MAINTAINER Daniel Smith +ADD explorer explorer +ADD README.md README.md +EXPOSE 8080 +ENTRYPOINT ["/explorer"] diff --git a/examples/explorer/Makefile b/examples/explorer/Makefile new file mode 100644 index 00000000000..bbccac4e36b --- /dev/null +++ b/examples/explorer/Makefile @@ -0,0 +1,16 @@ +all: push + +# Keep this one version ahead, so no one accidentally blows away the latest published version. +TAG = 1.1 + +explorer: explorer.go + CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' ./explorer.go + +container: explorer + docker build -t gcr.io/google_containers/explorer:$(TAG) . + +push: container + gcloud preview docker push gcr.io/google_containers/explorer:$(TAG) + +clean: + rm -f explorer diff --git a/examples/explorer/README.md b/examples/explorer/README.md new file mode 100644 index 00000000000..f66ba01dfb6 --- /dev/null +++ b/examples/explorer/README.md @@ -0,0 +1,128 @@ +### explorer + +Explorer is a little container for examining the runtime environment kubernetes produces for your pods. + +The intended use is to substitute gcr.io/google_containers/explorer for your intended container, and then visit it via the proxy. + +Currently, you can look at: + * The environment variables to make sure kubernetes is doing what you expect. + * The filesystem to make sure the mounted volumes and files are also what you expect. + * Perform DNS lookups, to see how DNS works. + +`pod.json` is supplied as an example. You can control the port it serves on with the -port flag. + +Example from command line (the DNS lookup looks better from a web browser): +``` +$ alias kctl=../../../cluster/kubectl.sh +$ kctl create -f pod.json +$ kctl proxy & +Starting to serve on localhost:8001 + +$ curl localhost:8001/api/v1beta3/proxy/namespaces/default/pods/explorer:8080/vars/ +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +HOSTNAME=explorer +KIBANA_LOGGING_PORT_5601_TCP_PORT=5601 +KUBERNETES_SERVICE_HOST=10.0.0.2 +MONITORING_GRAFANA_PORT_80_TCP_PROTO=tcp +MONITORING_INFLUXDB_UI_PORT_80_TCP_PROTO=tcp +KIBANA_LOGGING_SERVICE_PORT=5601 +MONITORING_HEAPSTER_PORT_80_TCP_PORT=80 +MONITORING_INFLUXDB_UI_PORT_80_TCP_PORT=80 +KIBANA_LOGGING_SERVICE_HOST=10.0.204.206 +KIBANA_LOGGING_PORT_5601_TCP=tcp://10.0.204.206:5601 +KUBERNETES_PORT=tcp://10.0.0.2:443 +MONITORING_INFLUXDB_PORT=tcp://10.0.2.30:80 +MONITORING_INFLUXDB_PORT_80_TCP_PROTO=tcp +MONITORING_INFLUXDB_UI_PORT=tcp://10.0.36.78:80 +KUBE_DNS_PORT_53_UDP=udp://10.0.0.10:53 +MONITORING_INFLUXDB_SERVICE_HOST=10.0.2.30 +ELASTICSEARCH_LOGGING_PORT=tcp://10.0.48.200:9200 +ELASTICSEARCH_LOGGING_PORT_9200_TCP_PORT=9200 +KUBERNETES_PORT_443_TCP=tcp://10.0.0.2:443 +ELASTICSEARCH_LOGGING_PORT_9200_TCP_PROTO=tcp +KIBANA_LOGGING_PORT_5601_TCP_ADDR=10.0.204.206 +KUBE_DNS_PORT_53_UDP_ADDR=10.0.0.10 +MONITORING_HEAPSTER_PORT_80_TCP_PROTO=tcp +MONITORING_INFLUXDB_PORT_80_TCP_ADDR=10.0.2.30 +KIBANA_LOGGING_PORT=tcp://10.0.204.206:5601 +MONITORING_GRAFANA_SERVICE_PORT=80 +MONITORING_HEAPSTER_SERVICE_PORT=80 +MONITORING_HEAPSTER_PORT_80_TCP=tcp://10.0.150.238:80 +ELASTICSEARCH_LOGGING_PORT_9200_TCP=tcp://10.0.48.200:9200 +ELASTICSEARCH_LOGGING_PORT_9200_TCP_ADDR=10.0.48.200 +MONITORING_GRAFANA_PORT_80_TCP_PORT=80 +MONITORING_HEAPSTER_PORT=tcp://10.0.150.238:80 +MONITORING_INFLUXDB_PORT_80_TCP=tcp://10.0.2.30:80 +KUBE_DNS_SERVICE_PORT=53 +KUBE_DNS_PORT_53_UDP_PORT=53 +MONITORING_GRAFANA_PORT_80_TCP_ADDR=10.0.100.174 +MONITORING_INFLUXDB_UI_SERVICE_HOST=10.0.36.78 +KIBANA_LOGGING_PORT_5601_TCP_PROTO=tcp +MONITORING_GRAFANA_PORT=tcp://10.0.100.174:80 +MONITORING_INFLUXDB_UI_PORT_80_TCP_ADDR=10.0.36.78 +KUBE_DNS_SERVICE_HOST=10.0.0.10 +KUBERNETES_PORT_443_TCP_PORT=443 +MONITORING_HEAPSTER_PORT_80_TCP_ADDR=10.0.150.238 +MONITORING_INFLUXDB_UI_SERVICE_PORT=80 +KUBE_DNS_PORT=udp://10.0.0.10:53 +ELASTICSEARCH_LOGGING_SERVICE_HOST=10.0.48.200 +KUBERNETES_SERVICE_PORT=443 +MONITORING_HEAPSTER_SERVICE_HOST=10.0.150.238 +MONITORING_INFLUXDB_SERVICE_PORT=80 +MONITORING_INFLUXDB_PORT_80_TCP_PORT=80 +KUBE_DNS_PORT_53_UDP_PROTO=udp +MONITORING_GRAFANA_PORT_80_TCP=tcp://10.0.100.174:80 +ELASTICSEARCH_LOGGING_SERVICE_PORT=9200 +MONITORING_GRAFANA_SERVICE_HOST=10.0.100.174 +MONITORING_INFLUXDB_UI_PORT_80_TCP=tcp://10.0.36.78:80 +KUBERNETES_PORT_443_TCP_PROTO=tcp +KUBERNETES_PORT_443_TCP_ADDR=10.0.0.2 +HOME=/ + +$ curl localhost:8001/api/v1beta3/proxy/namespaces/default/pods/explorer:8080/fs/ +mount/ +var/ +.dockerenv +etc/ +dev/ +proc/ +.dockerinit +sys/ +README.md +explorer + +$ curl localhost:8001/api/v1beta3/proxy/namespaces/default/pods/explorer:8080/dns?q=elasticsearch-logging + +
+ + +
+

LookupNS(elasticsearch-logging):
+Result: ([]*net.NS)
+Error: <*>lookup elasticsearch-logging: no such host
+
+LookupTXT(elasticsearch-logging):
+Result: ([]string)
+Error: <*>lookup elasticsearch-logging: no such host
+
+LookupSRV("", "", elasticsearch-logging):
+cname: elasticsearch-logging.default.cluster.local.
+Result: ([]*net.SRV)[<*>{Target:(string)elasticsearch-logging.default.cluster.local. Port:(uint16)9200 Priority:(uint16)10 Weight:(uint16)100}]
+Error: 
+
+LookupHost(elasticsearch-logging):
+Result: ([]string)[10.0.60.245]
+Error: 
+
+LookupIP(elasticsearch-logging):
+Result: ([]net.IP)[10.0.60.245]
+Error: 
+
+LookupMX(elasticsearch-logging):
+Result: ([]*net.MX)
+Error: <*>lookup elasticsearch-logging: no such host
+
+
+ + +``` diff --git a/examples/explorer/explorer.go b/examples/explorer/explorer.go new file mode 100644 index 00000000000..e10dfc925c9 --- /dev/null +++ b/examples/explorer/explorer.go @@ -0,0 +1,122 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// A tiny web server for viewing the environment kubernetes creates for your +// containers. It exposes the filesystem and environment variables via http +// server. +package main + +import ( + "flag" + "fmt" + "log" + "net" + "net/http" + "os" + + "github.com/davecgh/go-spew/spew" +) + +var ( + port = flag.Int("port", 8080, "Port number to serve at.") +) + +func main() { + flag.Parse() + hostname, err := os.Hostname() + if err != nil { + log.Fatalf("Error getting hostname: %v", err) + } + + links := []struct { + link, desc string + }{ + {"/fs/", "Complete file system as seen by this container."}, + {"/vars/", "Environment variables as seen by this container."}, + {"/hostname/", "Hostname as seen by this container."}, + {"/dns?q=google.com", "Explore DNS records seen by this container."}, + {"/quit", "Cause this container to exit."}, + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, " Kubernetes environment explorer

") + for _, v := range links { + fmt.Fprintf(w, `%v: %v
`, v.link, v.link, v.desc) + } + }) + + http.Handle("/fs/", http.StripPrefix("/fs/", http.FileServer(http.Dir("/")))) + http.HandleFunc("/vars/", func(w http.ResponseWriter, r *http.Request) { + for _, v := range os.Environ() { + fmt.Fprintf(w, "%v\n", v) + } + }) + http.HandleFunc("/hostname/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, hostname) + }) + http.HandleFunc("/quit", func(w http.ResponseWriter, r *http.Request) { + os.Exit(0) + }) + http.HandleFunc("/dns", dns) + + go log.Fatal(http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", *port), nil)) + + select {} +} + +func dns(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query().Get("q") + // Note that the below is NOT safe from input attacks, but that's OK + // because this is just for debugging. + fmt.Fprintf(w, ` +
+ + +
+

`, q)
+	{
+		res, err := net.LookupNS(q)
+		spew.Fprintf(w, "LookupNS(%v):\nResult: %#v\nError: %v\n\n", q, res, err)
+	}
+	{
+		res, err := net.LookupTXT(q)
+		spew.Fprintf(w, "LookupTXT(%v):\nResult: %#v\nError: %v\n\n", q, res, err)
+	}
+	{
+		cname, res, err := net.LookupSRV("", "", q)
+		spew.Fprintf(w, `LookupSRV("", "", %v):
+cname: %v
+Result: %#v
+Error: %v
+
+`, q, cname, res, err)
+	}
+	{
+		res, err := net.LookupHost(q)
+		spew.Fprintf(w, "LookupHost(%v):\nResult: %#v\nError: %v\n\n", q, res, err)
+	}
+	{
+		res, err := net.LookupIP(q)
+		spew.Fprintf(w, "LookupIP(%v):\nResult: %#v\nError: %v\n\n", q, res, err)
+	}
+	{
+		res, err := net.LookupMX(q)
+		spew.Fprintf(w, "LookupMX(%v):\nResult: %#v\nError: %v\n\n", q, res, err)
+	}
+	fmt.Fprintf(w, `
+ +`) +} diff --git a/examples/explorer/pod.json b/examples/explorer/pod.json new file mode 100644 index 00000000000..0f3490610f7 --- /dev/null +++ b/examples/explorer/pod.json @@ -0,0 +1,38 @@ +{ + "kind": "Pod", + "apiVersion": "v1beta3", + "metadata": { + "name": "explorer" + }, + "spec": { + "containers": [ + { + "name": "explorer", + "image": "gcr.io/google_containers/explorer:1.0", + "args": [ + "-port=8080" + ], + "ports": [ + { + "containerPort": 8080, + "protocol": "TCP" + } + ], + "volumeMounts": [ + { + "name": "test-volume", + "mountPath": "/mount/test-volume" + } + ] + } + ], + "volumes": [ + { + "name": "test-volume", + "VolumeSorce": { + "EmptyDir": {} + } + } + ] + } +}