mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-03 18:53:13 -05:00 
			
		
		
		
	* Laying down kubernetes middleware foundation * Duplicated a bunch of code form etcd middleware * Duplicated code hacked to compile and load as a separate middleware * Adding verbose build option to Makefile * Removing stubzone and tls support tls and stubzone support was carried over from base etcd middleware code. Removing to simplify the kube middleware implementation. (For now.) * Adding conf directory for sample conf files * Removing stubzone support from query handler * Remove upstream and proxy from k8s corefile. Not sure that upstream or proxy makes sense for a k8s backed zone. * Comment out use of singleflight serialization * Removing parsing support for "upstream" directive from k8s * Removing upstream directive parsing code * Removing CNAME and TXT lookup implementation * Create README.md Brain-dump of DNS record name assembly and open work items. * Adding notes about wildcard handling * Adding basic k8s API client * Fleshing out methods on k8s connector * Remove PathPrefix from middleware init * Removing incorrect plural * Adding brute-force k8s service lookup functions * Initializing k8s API connector during startup * Hacking around to call k8s connector * Parsing incoming domain name into serviceName and namespace * Improving and simplifying k8s zone matching and label segmentation * Removing unused functions carried over from etcd middleware * Adding basic return of k8s data to DNS client * updated debugging println statements to flag with "[debug]" * removed code in kubernetes.go::Records that was a hold-over from etcd middleware. * Removed some random exploratory hacking. * Minior README.md updates * Updating with demo instructions * Updating README.md with CoreFile and removing completed TODO items * Updating conf file and README to reflect DNS response cache works * Disabling DNS response caching * Adding debug statement on entry to Records() * Changing port number in exampes to port 53. * Misc style and clarity changes * Removing empty function definitions * Adding comment to track future cleanup * Refactoring README to follow style of other middleware * Exposing dataobject field (typo)
		
			
				
	
	
		
			285 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# kubernetes
 | 
						|
 | 
						|
`kubernetes` enables reading zone data from a kubernetes cluster. Record names
 | 
						|
are constructed as "myservice.mynamespace.coredns.local" where:
 | 
						|
 | 
						|
* "myservice" is the name of the k8s service (this may include multiple DNS labels, such as "c1.myservice"),
 | 
						|
* "mynamespace" is the k8s namespace for the service, and
 | 
						|
* "coredns.local" is the zone configured for `kubernetes`.
 | 
						|
 | 
						|
 | 
						|
## Syntax
 | 
						|
 | 
						|
~~~
 | 
						|
kubernetes [zones...]
 | 
						|
~~~
 | 
						|
 | 
						|
* `zones` zones kubernetes should be authorative for.
 | 
						|
 | 
						|
 | 
						|
~~~
 | 
						|
kubernetes [zones] {
 | 
						|
    endpoint http://localhost:8080
 | 
						|
}
 | 
						|
~~~
 | 
						|
 | 
						|
* `endpoint` the kubernetes API endpoint, default to http://localhost:8080
 | 
						|
 | 
						|
## Examples
 | 
						|
 | 
						|
This is the default kubernetes setup, with everything specified in full:
 | 
						|
 | 
						|
~~~
 | 
						|
# Serve on port 53
 | 
						|
.:53 {
 | 
						|
    # use kubernetes middleware for domain "coredns.local"
 | 
						|
    kubernetes coredns.local {
 | 
						|
        # Use url for k8s API endpoint
 | 
						|
        endpoint http://localhost:8080
 | 
						|
    }
 | 
						|
#    cache 160 coredns.local
 | 
						|
}
 | 
						|
~~~
 | 
						|
 | 
						|
### Basic Setup
 | 
						|
 | 
						|
#### Launch Kubernetes
 | 
						|
 | 
						|
Kubernetes is launched using the commands in the following `run_k8s.sh` script:
 | 
						|
 | 
						|
~~~
 | 
						|
#!/bin/bash
 | 
						|
 | 
						|
# Based on instructions at: http://kubernetes.io/docs/getting-started-guides/docker/
 | 
						|
 | 
						|
#K8S_VERSION=$(curl -sS https://storage.googleapis.com/kubernetes-release/release/latest.txt)
 | 
						|
K8S_VERSION="v1.2.4"
 | 
						|
 | 
						|
ARCH="amd64"
 | 
						|
 | 
						|
export K8S_VERSION
 | 
						|
export ARCH
 | 
						|
 | 
						|
#DNS_ARGUMENTS="--cluster-dns=10.0.0.10 --cluster-domain=cluster.local"
 | 
						|
DNS_ARGUMENTS=""
 | 
						|
 | 
						|
docker run -d \
 | 
						|
    --volume=/:/rootfs:ro \
 | 
						|
    --volume=/sys:/sys:ro \
 | 
						|
    --volume=/var/lib/docker/:/var/lib/docker:rw \
 | 
						|
    --volume=/var/lib/kubelet/:/var/lib/kubelet:rw \
 | 
						|
    --volume=/var/run:/var/run:rw \
 | 
						|
    --net=host \
 | 
						|
    --pid=host \
 | 
						|
    --privileged \
 | 
						|
    gcr.io/google_containers/hyperkube-${ARCH}:${K8S_VERSION} \
 | 
						|
    /hyperkube kubelet \
 | 
						|
    --containerized \
 | 
						|
    --hostname-override=127.0.0.1 \
 | 
						|
    --api-servers=http://localhost:8080 \
 | 
						|
    --config=/etc/kubernetes/manifests \
 | 
						|
    ${DNS_ARGUMENTS} \
 | 
						|
    --allow-privileged --v=2
 | 
						|
~~~
 | 
						|
 | 
						|
#### Configure kubectl and test
 | 
						|
 | 
						|
The kubernetes control client can be downloaded from the generic URL:
 | 
						|
`http://storage.googleapis.com/kubernetes-release/release/${K8S_VERSION}/bin/${GOOS}/${GOARCH}/${K8S_BINARY}`
 | 
						|
 | 
						|
For example, the kubectl client for Linux can be downloaded using the command:
 | 
						|
`curl -sSL "http://storage.googleapis.com/kubernetes-release/release/v1.2.4/bin/linux/amd64/kubectl"
 | 
						|
 | 
						|
The following `setup_kubectl.sh` script can be stored in the same directory as 
 | 
						|
kubectl to setup
 | 
						|
kubectl to communicate with kubernetes running on the localhost:
 | 
						|
 | 
						|
~~~
 | 
						|
#!/bin/bash
 | 
						|
 | 
						|
BASEDIR=`realpath $(dirname ${0})`
 | 
						|
 | 
						|
${BASEDIR}/kubectl config set-cluster test-doc --server=http://localhost:8080
 | 
						|
${BASEDIR}/kubectl config set-context test-doc --cluster=test-doc
 | 
						|
${BASEDIR}/kubectl config use-context test-doc
 | 
						|
 | 
						|
alias kubctl="${BASEDIR}/kubectl"
 | 
						|
~~~
 | 
						|
 | 
						|
 | 
						|
Verify that kubectl is working by querying for the kubernetes namespaces:
 | 
						|
 | 
						|
~~~
 | 
						|
$ ./kubectl get namespaces
 | 
						|
NAME      STATUS    AGE
 | 
						|
default   Active    8d
 | 
						|
test      Active    7d
 | 
						|
~~~
 | 
						|
 | 
						|
 | 
						|
#### Launch a kubernetes service and expose the service
 | 
						|
 | 
						|
The following commands will create a kubernetes namespace "demo",
 | 
						|
launch an nginx service in the namespace, and expose the service on port 80:
 | 
						|
 | 
						|
~~~
 | 
						|
$ ./kubectl create namespace demo
 | 
						|
$ ./kubectl get namespace
 | 
						|
 | 
						|
$ ./kubectl run mynginx --namespace=demo --image=nginx
 | 
						|
$ /kubectl get deployment --namespace=demo
 | 
						|
 | 
						|
$ ./kubectl expose deployment mynginx --namespace=demo --port=80
 | 
						|
$ ./kubectl get service --namespace=demo
 | 
						|
~~~
 | 
						|
 | 
						|
 | 
						|
#### Launch CoreDNS
 | 
						|
 | 
						|
Build CoreDNS and launch using the configuration file in `conf/k8sCorefile`.
 | 
						|
This configuration file sets up CoreDNS to use the zone `coredns.local` for
 | 
						|
the kubernetes services.
 | 
						|
 | 
						|
The command to launch CoreDNS is:
 | 
						|
 | 
						|
~~~
 | 
						|
$ ./coredns -conf conf/k8sCoreFile
 | 
						|
~~~
 | 
						|
 | 
						|
In a separate terminal a dns query can be issued using dig:
 | 
						|
 | 
						|
~~~
 | 
						|
$ dig @localhost mynginx.demo.coredns.local
 | 
						|
 | 
						|
; <<>> DiG 9.9.4-RedHat-9.9.4-29.el7_2.3 <<>> @localhost mynginx.demo.coredns.local
 | 
						|
; (2 servers found)
 | 
						|
;; global options: +cmd
 | 
						|
;; Got answer:
 | 
						|
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47614
 | 
						|
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 | 
						|
 | 
						|
;; OPT PSEUDOSECTION:
 | 
						|
; EDNS: version: 0, flags:; udp: 4096
 | 
						|
;; QUESTION SECTION:
 | 
						|
;mynginx.demo.coredns.local.    IN  A
 | 
						|
 | 
						|
;; ANSWER SECTION:
 | 
						|
mynginx.demo.coredns.local. 0   IN  A   10.0.0.10
 | 
						|
 | 
						|
;; Query time: 2 msec
 | 
						|
;; SERVER: ::1#53(::1)
 | 
						|
;; WHEN: Thu Jun 02 11:07:18 PDT 2016
 | 
						|
;; MSG SIZE  rcvd: 71
 | 
						|
~~~
 | 
						|
 | 
						|
 | 
						|
 | 
						|
## Implementation Notes/Ideas
 | 
						|
 | 
						|
### Basic Zone Mapping (implemented)
 | 
						|
The middleware is configured with a "zone" string. For 
 | 
						|
example: "zone = coredns.local".
 | 
						|
 | 
						|
The Kubernetes service "myservice" running in "mynamespace" would map 
 | 
						|
to: "myservice.mynamespace.coredns.local".
 | 
						|
 | 
						|
The middleware should publish an A record for that service and a service record.
 | 
						|
 | 
						|
Initial implementation just performs the above simple mapping. Subsequent 
 | 
						|
revisions should allow different namespaces to be published under different zones.
 | 
						|
 | 
						|
For example:
 | 
						|
 | 
						|
    # Serve on port 53
 | 
						|
    .:53 {
 | 
						|
        # use kubernetes middleware for domain "coredns.local"
 | 
						|
        kubernetes coredns.local {
 | 
						|
            # Use url for k8s API endpoint
 | 
						|
            endpoint http://localhost:8080
 | 
						|
        }
 | 
						|
        # Perform DNS response caching for the coredns.local zone
 | 
						|
        # Cache timeout is provided by the integer argument in seconds
 | 
						|
        # This works for the kubernetes middleware.)
 | 
						|
        #cache 20 coredns.local
 | 
						|
        #cache 160 coredns.local
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
### Internal IP or External IP?
 | 
						|
* Should the Corefile configuration allow control over whether the internal IP or external IP is exposed?
 | 
						|
* If the Corefile configuration allows control over internal IP or external IP, then the config should allow users to control the precidence.
 | 
						|
 | 
						|
For example a service "myservice" running in namespace "mynamespace" with internal IP "10.0.0.100" and external IP "1.2.3.4".
 | 
						|
 | 
						|
This example could be published as:
 | 
						|
 | 
						|
| Corefile directive           | Result              |
 | 
						|
|------------------------------|---------------------|
 | 
						|
| iporder = internal           | 10.0.0.100          |
 | 
						|
| iporder = external           | 1.2.3.4             |
 | 
						|
| iporder = external, internal | 10.0.0.100, 1.2.3.4 |
 | 
						|
| iporder = internal, external | 1.2.3.4, 10.0.0.100 |
 | 
						|
| _no directive_               | 10.0.0.100, 1.2.3.4 |
 | 
						|
 | 
						|
 | 
						|
### Wildcards
 | 
						|
 | 
						|
Publishing DNS records for singleton services isn't very interesting. Service
 | 
						|
names are unique within a k8s namespace therefore multiple services will be
 | 
						|
commonly run with a structured naming scheme.
 | 
						|
 | 
						|
For example, running multiple nginx services under the names:
 | 
						|
 | 
						|
| Service name |
 | 
						|
|--------------|
 | 
						|
| c1.nginx     |
 | 
						|
| c2.nginx     |
 | 
						|
 | 
						|
or:
 | 
						|
 | 
						|
| Service name |
 | 
						|
|--------------|
 | 
						|
| nginx.c3     |
 | 
						|
| nginx.c4     |
 | 
						|
 | 
						|
A DNS query with wildcard support for "nginx" in these examples should
 | 
						|
return the IP addresses for all services with "nginx" in the service name.
 | 
						|
 | 
						|
TBD:
 | 
						|
* How does this relate the the k8s load-balancer configuration?
 | 
						|
* Do wildcards search across namespaces?
 | 
						|
* Initial implementation assumes that a namespace maps to the first DNS label below the zone managed by the kubernetes middleware. This assumption may need to be revised.
 | 
						|
 | 
						|
 | 
						|
## TODO
 | 
						|
* Implement namespace filtering to different zones.
 | 
						|
* Implement IP selection and ordering (internal/external).
 | 
						|
* Implement SRV-record queries using naive lookup.
 | 
						|
* Flatten service and namespace names to valid DNS characters. (service names
 | 
						|
  and namespace names in k8s may use uppercase and non-DNS characters. Implement
 | 
						|
  flattening to lower case and mapping of non-DNS characters to DNS characters
 | 
						|
  in a standard way.)
 | 
						|
* Do we need to generate synthetic zone records for namespaces?
 | 
						|
* Implement wildcard-based lookup.
 | 
						|
* Improve lookup to reduce size of query result obtained from k8s API.
 | 
						|
  (namespace-based?, other ideas?)
 | 
						|
* How to support label specification in Corefile to allow use of labels to 
 | 
						|
  indicate zone? (Is this even useful?) For example, the following configuration
 | 
						|
  exposes all services labeled for the "staging" environment and tenant "customerB"
 | 
						|
  in the zone "customerB.stage.local":
 | 
						|
 | 
						|
~~~
 | 
						|
kubernetes customerB.stage.local {
 | 
						|
    # Use url for k8s API endpoint
 | 
						|
    endpoint http://localhost:8080
 | 
						|
    label "environment" : "staging", "tenant" : "customerB"
 | 
						|
}
 | 
						|
~~~
 | 
						|
 | 
						|
* Test with CoreDNS caching. CoreDNS caching for DNS response is working using
 | 
						|
  the `cache` directive. Tested working using 20s cache timeout and A-record queries.
 | 
						|
* DNS response caching is good, but we should also cache at the http query 
 | 
						|
  level as well. (Take a look at https://github.com/patrickmn/go-cache as 
 | 
						|
  a potential expiring cache implementation for the http API queries.)
 | 
						|
 |