Published on Jan 10, 2018
Richard Li
CEO and Co-Founder at Ambassador Labs

Quick Take on Docker for Mac with Kubernetes (and some tips on using Ingress)

On January 8, Docker released a beta version of Docker that includes Kubernetes support. I was excited to try it out on my Mac. Here are my notes and observations from experimenting with Docker for Mac with Kubernetes.

Installation

The Docker folks usually do a great job with a simple user experience, and installation was no exception. I downloaded the edge installer for Docker, which uninstalled my stable version of Docker. In the preferences pane, I enabled Kubernetes, and shortly thereafter, I had a working Kubernetes cluster.

Installing Docker for Mac with Kubernetes

I was also able to use the preexisting kubectl on my laptop. The installer assumes your Kubernetes configuration is stored in $HOME/.kube/config. If you have set KUBECONFIG to a different file, you’ll want to point it back to config.

Docker for Mac and Ingress

I decided to try installing Ambassador, our open source API Gateway built on the Envoy Proxy. Ambassador strives to be as idiomatic to Kubernetes as possible (e.g., it’s configured via annotations), so it’s a good real-world test for a Kubernetes implementation.

Docker for Mac is based on Kubernetes 1.8.2, so I installed Ambassador with RBAC:

shell
1---
2apiVersion: v1
3kind: Service
4metadata:
5 labels:
6 service: ambassador-admin
7 name: ambassador-admin
8spec:
9 type: NodePort
10 ports:
11 - name: ambassador-admin
12 port: 8877
13 targetPort: 8877
14 selector:
15 service: ambassador
16---
17apiVersion: rbac.authorization.k8s.io/v1beta1
18kind: ClusterRole
19metadata:
20 name: ambassador
21rules:
22- apiGroups: [""]
23 resources:
24 - services
25 verbs: ["get", "list", "watch"]
26- apiGroups: [""]
27 resources:
28 - configmaps
29 verbs: ["create", "update", "patch", "get", "list", "watch"]
30- apiGroups: [""]
31 resources:
32 - secrets
33 verbs: ["get", "list", "watch"]
34---
35apiVersion: v1
36kind: ServiceAccount
37metadata:
38 name: ambassador
39---
40apiVersion: rbac.authorization.k8s.io/v1beta1
41kind: ClusterRoleBinding
42metadata:
43 name: ambassador
44roleRef:
45 apiGroup: rbac.authorization.k8s.io
46 kind: ClusterRole
47 name: ambassador
48subjects:
49- kind: ServiceAccount
50 name: ambassador
51 namespace: default
52---
53apiVersion: extensions/v1beta1
54kind: Deployment
55metadata:
56 name: ambassador
57spec:
58 replicas: 1
59 template:
60 metadata:
61 labels:
62 service: ambassador
63 spec:
64 serviceAccountName: ambassador
65 containers:
66 - name: ambassador
67 image: datawire/ambassador:0.21.0
68 imagePullPolicy: Always
69 resources:
70 limits:
71 cpu: 1
72 memory: 400Mi
73 requests:
74 cpu: 200m
75 memory: 100Mi
76 env:
77 - name: AMBASSADOR_NAMESPACE
78 valueFrom:
79 fieldRef:
80 fieldPath: metadata.namespace
81 livenessProbe:
82 httpGet:
83 path: /ambassador/v0/check_alive
84 port: 8877
85 initialDelaySeconds: 3
86 periodSeconds: 3
87 readinessProbe:
88 httpGet:
89 path: /ambassador/v0/check_ready
90 port: 8877
91 initialDelaySeconds: 3
92 periodSeconds: 3
93 - name: statsd-sink
94 image: datawire/prom-statsd-exporter:0.6.0
95 restartPolicy: Always

I then deployed an Ambassador LoadBalancer service:

shell
1---
2apiVersion: v1
3kind: Service
4metadata:
5 labels:
6 service: ambassador
7 name: ambassador
8spec:
9 type: LoadBalancer
10 ports:
11 - name: ambassador
12 port: 80
13 targetPort: 80
14 selector:
15 service: ambassador

I wanted to try to connect to Ambassador, but this is what I saw:

$ kubectl get svc NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE ambassador 10.106.236.196 <pending> 80:30612/TCP 45m ambassador-admin 10.102.220.182 <nodes> 8877:31079/TCP 4h kubernetes 10.96.0.1 <none> 443/TCP 4h

Note that the ambassador service has been running for 45 minutes, and it was still for an external IP. Hopping on the Docker Slack channel, I found out that the service controller doesn’t update the service object (yet). It turns out that the service is actually exposed locally. So the following worked:

$ curl localhost:80

I added a mapping for Ambassador to route /httpbin/ to the httpbin.org service with the following configuration YAML:

shell
1apiVersion: v1
2kind: Service
3metadata:
4 name: httpbin
5 annotations:
6 getambassador.io/config: |
7 ---
8 apiVersion: ambassador/v0
9 kind: Mapping
10 name: httpbin_mapping
11 prefix: /httpbin/
12 service: httpbin.org:80
13 host_rewrite: httpbin.org
14spec:
15 ports:
16 - port: 80

And it worked perfectly:

$ curl localhost:80/httpbin/ip { "origin": "65.217.185.138" }

Some other notes on Ingress

In some conversations on the Slack channel, I learned a few other quirks:

  • To get a list of open ports, you can compile this binary. I haven’t tried this.
  • The service controller does not yet handle collisions between competing services. So the last service will win.

Docker Stacks and CRDs

Docker includes a native integration between Docker Swarm and Kubernetes with a stack custom resource definition. I’ve heard from many users how they love the simplicity of Docker Compose , and the stack CRD seems like a compelling approach.

Conclusion

Docker for Mac with Kubernetes has a lot of promise. While there are the rough edges you’d expect with any beta software, the Docker team has done an amazing job of building a useful alternative to Minikube. In addition, I’m excited to see how they’ve thought through how to make the experience idiomatic for Kubernetes users. I’m looking forward to updates!