mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 07:47:56 +00:00
Merge pull request #8501 from ArtfulCoder/kubelet_svc_subdomain
Support old and new style dns service names.
This commit is contained in:
commit
9a316dd409
@ -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
|
history of clients that, on purpose or on accident, do not respect DNS TTLs
|
||||||
(see previous remark about Pod IPs changing).
|
(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?
|
## How do I find the DNS server?
|
||||||
The DNS server itself runs as a Kubernetes Service. This gives it a stable IP
|
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
|
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
|
```shell
|
||||||
ENABLE_CLUSTER_DNS=true
|
ENABLE_CLUSTER_DNS=true
|
||||||
DNS_SERVER_IP="10.0.0.10"
|
DNS_SERVER_IP="10.0.0.10"
|
||||||
DNS_DOMAIN="kubernetes.local"
|
DNS_DOMAIN="cluster.local"
|
||||||
DNS_REPLICAS=1
|
DNS_REPLICAS=1
|
||||||
```
|
```
|
||||||
|
|
||||||
This enables DNS with a DNS Service IP of `10.0.0.10` and a local domain of
|
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
|
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
|
of this yourself. First, each kubelet needs to run with the following flags
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
.PHONY: all kube2sky container push clean test
|
.PHONY: all kube2sky container push clean test
|
||||||
|
|
||||||
TAG = 1.5
|
TAG = 1.6
|
||||||
PREFIX = gcr.io/google_containers
|
PREFIX = gcr.io/google_containers
|
||||||
|
|
||||||
all: container
|
all: container
|
||||||
|
@ -204,10 +204,14 @@ func newKubeClient() (*kclient.Client, error) {
|
|||||||
return kclient.New(config)
|
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)
|
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.
|
// Returns a cache.ListWatch that gets all changes to services.
|
||||||
func createServiceLW(kubeClient *kclient.Client) *cache.ListWatch {
|
func createServiceLW(kubeClient *kclient.Client) *cache.ListWatch {
|
||||||
return cache.NewListWatchFromClient(kubeClient, "services", kapi.NamespaceAll, kSelector.Everything())
|
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{}) {
|
func (ks *kube2sky) newService(obj interface{}) {
|
||||||
if s, ok := obj.(*kapi.Service); ok {
|
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) })
|
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{}) {
|
func (ks *kube2sky) removeService(obj interface{}) {
|
||||||
if s, ok := obj.(*kapi.Service); ok {
|
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) })
|
ks.mutateEtcdOrDie(func() error { return ks.removeDNS(name) })
|
||||||
|
name1 := buildNewServiceNameString(s.Name, s.Namespace, ks.domain)
|
||||||
|
ks.mutateEtcdOrDie(func() error { return ks.removeDNS(name1) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,8 +49,9 @@ func (ec *fakeEtcdClient) Delete(path string, recursive bool) (*etcd.Response, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testDomain = "cluster.local"
|
testDomain = "cluster.local"
|
||||||
basePath = "/skydns/local/cluster"
|
basePath = "/skydns/local/cluster"
|
||||||
|
serviceSubDomain = "svc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newKube2Sky(ec etcdClient) *kube2sky {
|
func newKube2Sky(ec etcdClient) *kube2sky {
|
||||||
@ -61,25 +62,12 @@ func newKube2Sky(ec etcdClient) *kube2sky {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddNoServiceIP(t *testing.T) {
|
func getEtcdOldStylePath(name, namespace string) string {
|
||||||
const (
|
return path.Join(basePath, namespace, name)
|
||||||
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 getEtcdPath(name, namespace string) string {
|
func getEtcdNewStylePath(name, namespace string) string {
|
||||||
return path.Join(basePath, namespace, name)
|
return path.Join(basePath, serviceSubDomain, namespace, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
type hostPort struct {
|
type hostPort struct {
|
||||||
@ -100,6 +88,39 @@ func getHostPortFromString(data string) (*hostPort, error) {
|
|||||||
return &res, err
|
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) {
|
func TestAddSinglePortService(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
testService = "testService"
|
testService = "testService"
|
||||||
@ -122,13 +143,8 @@ func TestAddSinglePortService(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
k2s.newService(&service)
|
k2s.newService(&service)
|
||||||
expectedKey := getEtcdPath(testService, testNamespace)
|
|
||||||
expectedValue := getHostPort(&service)
|
expectedValue := getHostPort(&service)
|
||||||
val, exists := ec.writes[expectedKey]
|
assertDnsServiceEntryInEtcd(t, ec, testService, testNamespace, expectedValue)
|
||||||
require.True(t, exists)
|
|
||||||
actualValue, err := getHostPortFromString(val)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, actualValue, expectedValue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateSinglePortService(t *testing.T) {
|
func TestUpdateSinglePortService(t *testing.T) {
|
||||||
@ -153,16 +169,11 @@ func TestUpdateSinglePortService(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
k2s.newService(&service)
|
k2s.newService(&service)
|
||||||
assert.Len(t, ec.writes, 1)
|
assert.Len(t, ec.writes, 2)
|
||||||
service.Spec.PortalIP = "0.0.0.0"
|
service.Spec.PortalIP = "0.0.0.0"
|
||||||
k2s.newService(&service)
|
k2s.newService(&service)
|
||||||
expectedKey := getEtcdPath(testService, testNamespace)
|
|
||||||
expectedValue := getHostPort(&service)
|
expectedValue := getHostPort(&service)
|
||||||
val, exists := ec.writes[expectedKey]
|
assertDnsServiceEntryInEtcd(t, ec, testService, testNamespace, expectedValue)
|
||||||
require.True(t, exists)
|
|
||||||
actualValue, err := getHostPortFromString(val)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, actualValue, expectedValue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteSinglePortService(t *testing.T) {
|
func TestDeleteSinglePortService(t *testing.T) {
|
||||||
@ -188,7 +199,9 @@ func TestDeleteSinglePortService(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// Add the service
|
// Add the service
|
||||||
k2s.newService(&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
|
// Delete the service
|
||||||
k2s.removeService(&service)
|
k2s.removeService(&service)
|
||||||
assert.Empty(t, ec.writes)
|
assert.Empty(t, ec.writes)
|
||||||
|
@ -30,7 +30,7 @@ spec:
|
|||||||
- -initial-cluster-token
|
- -initial-cluster-token
|
||||||
- skydns-etcd
|
- skydns-etcd
|
||||||
- name: kube2sky
|
- name: kube2sky
|
||||||
image: gcr.io/google_containers/kube2sky:1.5
|
image: gcr.io/google_containers/kube2sky:1.6
|
||||||
args:
|
args:
|
||||||
# command = "/kube2sky"
|
# command = "/kube2sky"
|
||||||
- -domain={{ pillar['dns_domain'] }}
|
- -domain={{ pillar['dns_domain'] }}
|
||||||
|
@ -72,6 +72,8 @@ var _ = Describe("DNS", func() {
|
|||||||
// TODO: Spin up a separate test service and test that dns works for that service.
|
// TODO: Spin up a separate test service and test that dns works for that service.
|
||||||
namesToResolve := []string{
|
namesToResolve := []string{
|
||||||
"kubernetes-ro.default",
|
"kubernetes-ro.default",
|
||||||
|
"kubernetes-ro.default.svc",
|
||||||
|
"kubernetes-ro.default.svc.cluster.local",
|
||||||
"kubernetes-ro.default.cluster.local",
|
"kubernetes-ro.default.cluster.local",
|
||||||
"google.com",
|
"google.com",
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user