<p>Until now I developed Python applications locally using docker and docker-compose. Now I'd like to change my development workflow to use <code>skaffold</code> with <code>docker</code> as builder, with <code>kubectl</code> as deployer and with <code>minikube</code> for managing the local kubernetes cluster.</p>
<p>Let's say I've this docker based hello world for FastAPI:</p>
<p>project structure:</p>
<pre><code>app/app.py
Dockerfile
</code></pre>
<p>app/app.py</p>
<pre><code>from typing import Optional

from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;Hello&quot;: &quot;World&quot;}

@app.get(&quot;/items/{item_id}&quot;)
def read_item(item_id: int, q: Optional[str] = None):
    return {&quot;item_id&quot;: item_id, &quot;q&quot;: q}
</code></pre>
<p>Dockerfile:</p>
<pre><code>FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7

COPY ./app /app
</code></pre>
<p>If I run <code>docker build -t hello-fastapi .</code> and <code>docker run -p 80:80 hello-fastapi</code> I can access the service via <code>0.0.0.0</code> or <code>localhost</code>. I skip the <code>docker-compose</code> things here cause it does not matter w.r.t. the skaffold setup.</p>
<p>To use <code>skaffold</code> I have the exact same project structure and content but I added the skaffold + kubectl specific things (<code>skaffold.yaml</code>, <code>deployment.yaml</code>):</p>
<p>project structure:</p>
<pre><code>app/app.py
k8s/deployment.yaml
Dockerfile
skaffold.yaml
</code></pre>
<p>k8s/deployment.yaml</p>
<pre><code>apiVersion: v1
kind: Service
metadata:
  name: fastapi-service
  labels:
    app: fastapi-service
spec:
  clusterIP: None
  ports:
    - port: 80
      name: fastapi-service
  selector:
    app: fastapi-service
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-service
  labels:
    app: fastapi-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fastapi-service
  template:
    metadata:
      labels:
        app: fastapi-service
    spec:
      containers:
      - name: fastapi-service
        image: fastapi-service
        ports:
        - containerPort: 80
</code></pre>
<p>skaffold.yaml</p>
<pre><code>apiVersion: skaffold/v2beta10
kind: Config
build:
  artifacts:
  - image: fastapi-image
deploy:
  kubectl:
    manifests:
      - k8s/*
</code></pre>
<p>If I run <code>skaffold dev</code> everything seems to be fine:</p>
<pre><code>Listing files to watch...
 - fastapi-service
Generating tags...
 - fastapi-service -&gt; fastapi-service:latest
Some taggers failed. Rerun with -vdebug for errors.
Checking cache...
 - fastapi-service: Found Locally
Tags used in deployment:
 - fastapi-service -&gt; fastapi-service:17659a877904d862184d7cc5966596d46b0765f1995f7abc958db4b3f98b8a35
Starting deploy...
 - service/fastapi-service created
 - deployment.apps/fastapi-service created
Waiting for deployments to stabilize...
 - deployment/fastapi-service is ready.
Deployments stabilized in 2.165700782s
Press Ctrl+C to exit
Watching for changes...
[fastapi-service] Checking for script in /app/prestart.sh
[fastapi-service] Running script /app/prestart.sh
[fastapi-service] Running inside /app/prestart.sh, you could add migrations to this file, e.g.:
[fastapi-service] 
[fastapi-service] #! /usr/bin/env bash
[fastapi-service] 
[fastapi-service] # Let the DB start
[fastapi-service] sleep 10;
[fastapi-service] # Run migrations
[fastapi-service] alembic upgrade head
[fastapi-service] 
[fastapi-service] [2020-12-15 19:02:57 +0000] [1] [INFO] Starting gunicorn 20.0.4
[fastapi-service] [2020-12-15 19:02:57 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[fastapi-service] [2020-12-15 19:02:57 +0000] [1] [INFO] Using worker: uvicorn.workers.UvicornWorker
[fastapi-service] [2020-12-15 19:02:57 +0000] [8] [INFO] Booting worker with pid: 8
...
</code></pre>
<p>However I cannot access the service via my web browser. How can I access the service from my local machine via e.g. the web browser?</p>
<p><strong>EDIT</strong>:</p>
<p>According to <code>minikube service list</code> the service <code>fastapi-service</code> exists:</p>
<pre><code>|----------------------|---------------------------|--------------|-----|
|      NAMESPACE       |           NAME            | TARGET PORT  | URL |
|----------------------|---------------------------|--------------|-----|
| default              | fastapi-service           | No node port |
| default              | kubernetes                | No node port |
| kube-system          | kube-dns                  | No node port |
| kubernetes-dashboard | dashboard-metrics-scraper | No node port |
| kubernetes-dashboard | kubernetes-dashboard      | No node port |
|----------------------|---------------------------|--------------|-----|

</code></pre>
<p>But I cannot access it via <code>curl $(minikube service fastapi-service --url)</code>:</p>
<pre><code>curl: (3) Failed to convert 😿 to ACE; string contains a disallowed character

curl: (6) Could not resolve host: service
curl: (6) Could not resolve host: default
curl: (6) Could not resolve host: has
curl: (6) Could not resolve host: no
curl: (6) Could not resolve host: node
curl: (6) Could not resolve host: port
</code></pre>
<p>Probably this is related to <a href="https://stackoverflow.com/questions/60556096/unable-to-get-clusterip-service-url-from-minikube">Unable to get ClusterIP service url from minikube</a> . If I change <code>deployment.yaml</code> to</p>
<pre><code>apiVersion: v1
kind: Service
metadata:
  name: fastapi-service
  labels:
    app: fastapi-service
spec:
  type: NodePort
  ports:
    - targetPort: 80
      port: 80
  selector:
    app: fastapi-service
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-service
  labels:
    app: fastapi-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fastapi-service
  template:
    metadata:
      labels:
        app: fastapi-service
    spec:
      containers:
      - name: fastapi-service
        image: fastapi-service
        ports:
        - containerPort: 80
</code></pre>
<p>accessing the service via <code>curl $(minikube service fastapi-service --url)</code> is successful:</p>
<pre><code>{&quot;message&quot;:&quot;Hello world! From FastAPI running on Uvicorn with Gunicorn. Using Python 3.7&quot;}

</code></pre>
<p>However I cannot access the service via the web browser.</p>

thinwybk
<p>Use Skaffold <a href="https://skaffold.dev/docs/pipeline-stages/port-forwarding/" rel="nofollow noreferrer"><code>--port-forward</code></a> to enable automatic port fowarding of services and user-defined port-forwards.</p>
<p>Skaffold normally tries select a local port that is close to the remote port where possible.  But since port 80 is a protected port, Skaffold will choose a different local port.  You can explicitly <a href="https://skaffold.dev/docs/pipeline-stages/port-forwarding/#user-defined-port-forwarding" rel="nofollow noreferrer">configure a port forward  your <code>skaffold.yaml</code></a> to specify the local port.  Using user-defined port-forwards can help avoid potential confusion should you add other services later on.</p>
<pre><code>apiVersion: skaffold/v2beta10
kind: Config
build:
  artifacts:
  - image: fastapi-service
deploy:
  kubectl:
    manifests:
      - k8s/*
portForward:
  - resourceType: deployment
    resourceName: fastapi-service
    port: 80
    localPort: 9000
</code></pre>
<p>Instead of running <code>skaffold dev</code> run with <code>skaffold dev --port-forward</code>. Now you can access the service via <code>localhost:9000</code>.</p>
<p>You can also use <code>kubectl port-forward</code> to manually forward ports; Skaffold uses this same functionality to implement its port forwarding.</p>
