Merge pull request #8501 from ArtfulCoder/kubelet_svc_subdomain

Support old and new style dns service names.
This commit is contained in:
Dawn Chen 2015-05-21 09:22:03 -07:00
commit 9a316dd409
6 changed files with 69 additions and 40 deletions

View File

@ -13,6 +13,11 @@ crashes or scheduling changes). This maps well to DNS, which has a long
history of clients that, on purpose or on accident, do not respect DNS TTLs
(see previous remark about Pod IPs changing).
## DNS Name format for Services
Services get a DNS name with the format my-svc.my-namespace.svc.cluster.local
'svc' should not be used a namespace label to avoid conflicts.
The old format of my-svc.my-namespace.cluster.local has been deprecated.
## How do I find the DNS server?
The DNS server itself runs as a Kubernetes Service. This gives it a stable IP
address. When you run the SkyDNS service, you want to assign a static IP to use for
@ -35,12 +40,12 @@ example, see `cluster/gce/config-default.sh`.
```shell
ENABLE_CLUSTER_DNS=true
DNS_SERVER_IP="10.0.0.10"
DNS_DOMAIN="kubernetes.local"
DNS_DOMAIN="cluster.local"
DNS_REPLICAS=1
```
This enables DNS with a DNS Service IP of `10.0.0.10` and a local domain of
`kubernetes.local`, served by a single copy of SkyDNS.
`cluster.local`, served by a single copy of SkyDNS.
If you are not using a supported cluster setup, you will have to replicate some
of this yourself. First, each kubelet needs to run with the following flags

View File

@ -4,7 +4,7 @@
.PHONY: all kube2sky container push clean test
TAG = 1.5
TAG = 1.6
PREFIX = gcr.io/google_containers
all: container

View File

@ -204,10 +204,14 @@ func newKubeClient() (*kclient.Client, error) {
return kclient.New(config)
}
func buildNameString(service, namespace, domain string) string {
func buildOldNameString(service, namespace, domain string) string {
return fmt.Sprintf("%s.%s.%s.", service, namespace, domain)
}
func buildNewServiceNameString(service, namespace, domain string) string {
return fmt.Sprintf("%s.%s.svc.%s.", service, namespace, domain)
}
// Returns a cache.ListWatch that gets all changes to services.
func createServiceLW(kubeClient *kclient.Client) *cache.ListWatch {
return cache.NewListWatchFromClient(kubeClient, "services", kapi.NamespaceAll, kSelector.Everything())
@ -215,15 +219,20 @@ func createServiceLW(kubeClient *kclient.Client) *cache.ListWatch {
func (ks *kube2sky) newService(obj interface{}) {
if s, ok := obj.(*kapi.Service); ok {
name := buildNameString(s.Name, s.Namespace, ks.domain)
//TODO(artfulcoder) stop adding and deleting old-format string for service
name := buildOldNameString(s.Name, s.Namespace, ks.domain)
ks.mutateEtcdOrDie(func() error { return ks.addDNS(name, s) })
name1 := buildNewServiceNameString(s.Name, s.Namespace, ks.domain)
ks.mutateEtcdOrDie(func() error { return ks.addDNS(name1, s) })
}
}
func (ks *kube2sky) removeService(obj interface{}) {
if s, ok := obj.(*kapi.Service); ok {
name := buildNameString(s.Name, s.Namespace, ks.domain)
name := buildOldNameString(s.Name, s.Namespace, ks.domain)
ks.mutateEtcdOrDie(func() error { return ks.removeDNS(name) })
name1 := buildNewServiceNameString(s.Name, s.Namespace, ks.domain)
ks.mutateEtcdOrDie(func() error { return ks.removeDNS(name1) })
}
}

View File

@ -49,8 +49,9 @@ func (ec *fakeEtcdClient) Delete(path string, recursive bool) (*etcd.Response, e
}
const (
testDomain = "cluster.local"
basePath = "/skydns/local/cluster"
testDomain = "cluster.local"
basePath = "/skydns/local/cluster"
serviceSubDomain = "svc"
)
func newKube2Sky(ec etcdClient) *kube2sky {
@ -61,25 +62,12 @@ func newKube2Sky(ec etcdClient) *kube2sky {
}
}
func TestAddNoServiceIP(t *testing.T) {
const (
testService = "testService"
testNamespace = "default"
)
ec := &fakeEtcdClient{make(map[string]string)}
k2s := newKube2Sky(ec)
service := kapi.Service{
ObjectMeta: kapi.ObjectMeta{
Name: testNamespace,
Namespace: testNamespace,
},
}
k2s.newService(&service)
assert.Empty(t, ec.writes)
func getEtcdOldStylePath(name, namespace string) string {
return path.Join(basePath, namespace, name)
}
func getEtcdPath(name, namespace string) string {
return path.Join(basePath, namespace, name)
func getEtcdNewStylePath(name, namespace string) string {
return path.Join(basePath, serviceSubDomain, namespace, name)
}
type hostPort struct {
@ -100,6 +88,39 @@ func getHostPortFromString(data string) (*hostPort, error) {
return &res, err
}
func assertDnsServiceEntryInEtcd(t *testing.T, ec *fakeEtcdClient, serviceName, namespace string, expectedHostPort *hostPort) {
oldStyleKey := getEtcdOldStylePath(serviceName, namespace)
val, exists := ec.writes[oldStyleKey]
require.True(t, exists)
actualHostPort, err := getHostPortFromString(val)
require.NoError(t, err)
assert.Equal(t, actualHostPort, expectedHostPort)
newStyleKey := getEtcdNewStylePath(serviceName, namespace)
val, exists = ec.writes[newStyleKey]
require.True(t, exists)
actualHostPort, err = getHostPortFromString(val)
require.NoError(t, err)
assert.Equal(t, actualHostPort, expectedHostPort)
}
func TestHeadlessService(t *testing.T) {
const (
testService = "testService"
testNamespace = "default"
)
ec := &fakeEtcdClient{make(map[string]string)}
k2s := newKube2Sky(ec)
service := kapi.Service{
ObjectMeta: kapi.ObjectMeta{
Name: testNamespace,
Namespace: testNamespace,
},
}
k2s.newService(&service)
assert.Empty(t, ec.writes)
}
func TestAddSinglePortService(t *testing.T) {
const (
testService = "testService"
@ -122,13 +143,8 @@ func TestAddSinglePortService(t *testing.T) {
},
}
k2s.newService(&service)
expectedKey := getEtcdPath(testService, testNamespace)
expectedValue := getHostPort(&service)
val, exists := ec.writes[expectedKey]
require.True(t, exists)
actualValue, err := getHostPortFromString(val)
require.NoError(t, err)
assert.Equal(t, actualValue, expectedValue)
assertDnsServiceEntryInEtcd(t, ec, testService, testNamespace, expectedValue)
}
func TestUpdateSinglePortService(t *testing.T) {
@ -153,16 +169,11 @@ func TestUpdateSinglePortService(t *testing.T) {
},
}
k2s.newService(&service)
assert.Len(t, ec.writes, 1)
assert.Len(t, ec.writes, 2)
service.Spec.PortalIP = "0.0.0.0"
k2s.newService(&service)
expectedKey := getEtcdPath(testService, testNamespace)
expectedValue := getHostPort(&service)
val, exists := ec.writes[expectedKey]
require.True(t, exists)
actualValue, err := getHostPortFromString(val)
require.NoError(t, err)
assert.Equal(t, actualValue, expectedValue)
assertDnsServiceEntryInEtcd(t, ec, testService, testNamespace, expectedValue)
}
func TestDeleteSinglePortService(t *testing.T) {
@ -188,7 +199,9 @@ func TestDeleteSinglePortService(t *testing.T) {
}
// Add the service
k2s.newService(&service)
assert.Len(t, ec.writes, 1)
// two entries should get created, one with the svc subdomain (new-style)
// , and one without the svc subdomain (old-style)
assert.Len(t, ec.writes, 2)
// Delete the service
k2s.removeService(&service)
assert.Empty(t, ec.writes)

View File

@ -30,7 +30,7 @@ spec:
- -initial-cluster-token
- skydns-etcd
- name: kube2sky
image: gcr.io/google_containers/kube2sky:1.5
image: gcr.io/google_containers/kube2sky:1.6
args:
# command = "/kube2sky"
- -domain={{ pillar['dns_domain'] }}

View File

@ -72,6 +72,8 @@ var _ = Describe("DNS", func() {
// TODO: Spin up a separate test service and test that dns works for that service.
namesToResolve := []string{
"kubernetes-ro.default",
"kubernetes-ro.default.svc",
"kubernetes-ro.default.svc.cluster.local",
"kubernetes-ro.default.cluster.local",
"google.com",
}