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.


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:

2apiVersion: v1
3kind: Service
5 labels:
6 service: ambassador-admin
7 name: ambassador-admin
9 type: NodePort
10 ports:
11 - name: ambassador-admin
12 port: 8877
13 targetPort: 8877
14 selector:
15 service: ambassador
18kind: ClusterRole
20 name: ambassador
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"]
35apiVersion: v1
36kind: ServiceAccount
38 name: ambassador
41kind: ClusterRoleBinding
43 name: ambassador
45 apiGroup:
46 kind: ClusterRole
47 name: ambassador
49- kind: ServiceAccount
50 name: ambassador
51 namespace: default
53apiVersion: extensions/v1beta1
54kind: Deployment
56 name: ambassador
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:
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:

2apiVersion: v1
3kind: Service
5 labels:
6 service: ambassador
7 name: ambassador
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 <pending> 80:30612/TCP 45m ambassador-admin <nodes> 8877:31079/TCP 4h kubernetes <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 service with the following configuration YAML:

1apiVersion: v1
2kind: Service
4 name: httpbin
5 annotations:
6 |
7 ---
8 apiVersion: ambassador/v0
9 kind: Mapping
10 name: httpbin_mapping
11 prefix: /httpbin/
12 service:
13 host_rewrite:
15 ports:
16 - port: 80

And it worked perfectly:

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

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.


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!