<p>I'm having a bit of hard time figuring out whether the Guestbook example is working in Minikube. My main issue is possibly that the example description <a href="https://github.com/kubernetes/kubernetes/tree/master/examples/guestbook" rel="nofollow">here</a> details all the steps but <em>there is no indication about how to connect to the web application</em> once it's running from the default YAML files.</p>

<p>I'm using Minikube v. <code>0.10.0</code> in Mac OS X 10.9.5 (Mavericks) and this is what I eventually ended up with (which seems pretty good according to what I read from the example document):</p>

<pre><code>PolePro:all-in-one poletti$ kubectl get svc
NAME           CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
frontend       10.0.0.140   &lt;none&gt;        80/TCP     8s
kubernetes     10.0.0.1     &lt;none&gt;        443/TCP    2h
redis-master   10.0.0.165   &lt;none&gt;        6379/TCP   53m
redis-slave    10.0.0.220   &lt;none&gt;        6379/TCP   37m

PolePro:all-in-one poletti$ kubectl get deployments
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend       3         3         3            3           20s
redis-master   1         1         1            1           42m
redis-slave    2         2         2            2           37m

PolePro:all-in-one poletti$ kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
frontend-708336848-0h2zj        1/1       Running   0          29s
frontend-708336848-ds8pn        1/1       Running   0          29s
frontend-708336848-v8wp9        1/1       Running   0          29s
redis-master-2093957696-or5iu   1/1       Running   0          43m
redis-slave-109403812-12k68     1/1       Running   0          37m
redis-slave-109403812-c7zmo     1/1       Running   0          37m
</code></pre>

<p>I thought that I might connect to <code>http://10.0.0.140:80/</code> (i.e. the <code>frontend</code> address and port as returned by <code>kubectl get svc</code> above) and see the application running, but I'm getting a <code>Connection refused</code>:</p>

<pre><code>PolePro:all-in-one poletti$ curl -v http://10.0.0.140:80
* About to connect() to 10.0.0.140 port 80 (#0)
*   Trying 10.0.0.140...
* Adding handle: conn: 0x7fb0f9803a00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fb0f9803a00) send_pipe: 1, recv_pipe: 0
* Failed connect to 10.0.0.140:80; Connection refused
* Closing connection 0
curl: (7) Failed connect to 10.0.0.140:80; Connection refused
</code></pre>

<p>It's somehow suspicious that the example description misses such an important step though. What am I missing?</p>

<p>Well, it seems I figured it out myself (I'll probably send a PR too)</p>

<p>The main thing is that, at least in the Minikube setup, the <code>kubectl</code> command is run in Mac OS X but all the cool stuff happens inside a virtual machine. In my case, it's a VirtualBox VM (I'm still on Mavericks).</p>

<p>When <code>kubectl</code> shows addresses for services, like in this case:</p>

<pre><code>PolePro:all-in-one poletti$ kubectl get svc
NAME           CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
frontend       10.0.0.140   &lt;none&gt;        80/TCP     8s
kubernetes     10.0.0.1     &lt;none&gt;        443/TCP    2h
redis-master   10.0.0.165   &lt;none&gt;        6379/TCP   53m
redis-slave    10.0.0.220   &lt;none&gt;        6379/TCP   37m
</code></pre>

<p>these addresses are accessible from within the node, not necessarily from the outside. In my case, they were <em>not</em> accessible from the outside.</p>

<p>So what can you do about it?</p>

<p>First of all, to just check that it's actually running, you can log into the node and run curl from there:</p>

<pre><code># get the list of nodes, to get the name of the node we're interested into
PolePro:all-in-one poletti$ kubectl get nodes
NAME       STATUS    AGE
minikube   Ready     3h

# that was easy. Now we can get the address of the node
PolePro:all-in-one poletti$ kubectl describe node/minikube | grep '^Address'
Addresses:     192.168.99.100,192.168.99.100

# now we can log into the node. The username is "docker", the password is "tcuser"
# by default (without quotes):
PolePro:all-in-one poletti$ ssh docker@192.168.99.100
docker@192.168.99.100's password: 
                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/
 _                 _   ____     _            _
| |__   ___   ___ | |_|___ \ __| | ___   ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__|   &lt;  __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
Boot2Docker version 1.11.1, build master : 901340f - Fri Jul  1 22:52:19 UTC 2016
Docker version 1.11.1, build 5604cbe
docker@minikube:~$ curl -v http://10.0.0.140/
*   Trying 10.0.0.140...
* Connected to 10.0.0.140 (10.0.0.140) port 80 (#0)
&gt; GET / HTTP/1.1
&gt; Host: 10.0.0.140
&gt; User-Agent: curl/7.49.1
&gt; Accept: */*
&gt; 
&lt; HTTP/1.1 200 OK
&lt; Date: Mon, 19 Sep 2016 13:37:56 GMT
&lt; Server: Apache/2.4.10 (Debian) PHP/5.6.20
&lt; Last-Modified: Wed, 09 Sep 2015 18:35:04 GMT
&lt; ETag: "399-51f54bdb4a600"
&lt; Accept-Ranges: bytes
&lt; Content-Length: 921
&lt; Vary: Accept-Encoding
&lt; Content-Type: text/html
&lt; 
&lt;html ng-app="redis"&gt;
  &lt;head&gt;
    &lt;title&gt;Guestbook&lt;/title&gt;
    &lt;link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css"&gt;
    &lt;script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"&gt;&lt;/script&gt;
    &lt;script src="controllers.js"&gt;&lt;/script&gt;
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.0/ui-bootstrap-tpls.js"&gt;&lt;/script&gt;
  &lt;/head&gt;
  &lt;body ng-controller="RedisCtrl"&gt;
    &lt;div style="width: 50%; margin-left: 20px"&gt;
      &lt;h2&gt;Guestbook&lt;/h2&gt;
    &lt;form&gt;
    &lt;fieldset&gt;
    &lt;input ng-model="msg" placeholder="Messages" class="form-control" type="text" name="input"&gt;&lt;br&gt;
    &lt;button type="button" class="btn btn-primary" ng-click="controller.onRedis()"&gt;Submit&lt;/button&gt;
    &lt;/fieldset&gt;
    &lt;/form&gt;
    &lt;div&gt;
      &lt;div ng-repeat="msg in messages track by $index"&gt;
        {{msg}}
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
* Connection #0 to host 10.0.0.140 left intact
</code></pre>

<p>Yay! There's actually something running on port 80.</p>

<p>Anyway, this is still a bit cumbersome and we would like to see this inside a browser in Mac OS X. One way to do this is to use <code>NodePort</code> to make the node map a Service's port to a Node's port; this is accomplished adding the following line in the <code>frontend</code> service definition, which becomes:</p>

<pre><code>apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # if your cluster supports it, uncomment the following to automatically create
  # an external load-balanced IP for the frontend service.
  # type: LoadBalancer
  type: NodePort
  ports:
    # the port that this service should serve on
  - port: 80
  selector:
    app: guestbook
    tier: frontend
</code></pre>

<p>This change might be requested in either <code>frontend-service.yaml</code>, <code>all-in-one/frontend.yaml</code> or <code>all-in-one/guestbook-all-in-one.yaml</code> depending on which file you are using.</p>

<p>If you re-create the whole guestbook (I don't know if it's necessary but I'll remain on the safe side) you will get a message about ports and firewalls, like this:</p>

<pre><code># delete previous instance to start from "scratch"
PolePro:all-in-one poletti$ kubectl delete deployments,svc -l 'app in (redis, guestbook)'
deployment "frontend" deleted
deployment "redis-master" deleted
deployment "redis-slave" deleted
service "frontend" deleted
service "redis-master" deleted
service "redis-slave" deleted

# we'll use the all-in-one here to get quickly to the point
PolePro:all-in-one poletti$ vi guestbook-all-in-one.yaml

# with the new NodePort change in place, we're ready to start again
PolePro:all-in-one poletti$ kubectl create -f guestbook-all-in-one.yaml
service "redis-master" created
deployment "redis-master" created
service "redis-slave" created
deployment "redis-slave" created
You have exposed your service on an external port on all nodes in your
cluster.  If you want to expose this service to the external internet, you may
need to set up firewall rules for the service port(s) (tcp:30559) to serve traffic.

See http://releases.k8s.io/release-1.3/docs/user-guide/services-firewalls.md for more     details.
service "frontend" created
deployment "frontend" created
</code></pre>

<p>Now, port <code>30559</code> on the node maps onto the frontend port <code>80</code>, so we can open the browser at address <code>http://192.168.99.100:30559/</code> (i.e. <code>http://&lt;NODE-IP&gt;:&lt;EXTERNAL-PORT&gt;/</code>) and we can use the guestbook!</p>
