# Deployment

A Deployment manages a set of Pods to run an application workload, usually one that doesn't maintain state.

## Create Simple App

First lets create a simple golang apps by following these steps below:

* Create new folder called `simple-go`.
* Init Go Module using `go mod init`.
* Create new sub folder called `cmd`.
* Create `main.go` file.
* Your folder structure should look like this:

```bash
simple-go
├── go.mod
└── main.go
```

* Then lets create a simple http server where it will return json response like `{"message":"success"}`.
* You can copy paste the following code:

```go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
)

type Response struct {
    Message string `json:"message"`
}

func main() {
    srv := http.NewServeMux()
    srv.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        res := Response{
            Message: "Success",
        }

        err := json.NewEncoder(w).Encode(res)
        if err != nil {
            log.Fatal(err)
        }
    })

    fmt.Println("Server is running on port 8080...")
    err := http.ListenAndServe(":8080", srv)
    if err != nil {
        log.Fatal(err)
    }
}
```

* Let's try to run the code by using command `go run main.go`.
* You should see this output in your terminal:

```bash
Server is running on port 8080...
```

* Let's try to access is using `curl` to your localhost port `8080` and it should return with response like this:

```bash
➜ curl http://127.0.0.1:8080
{"message":"Success"}
```

## Build Docker Image

With the simple app running well the next step is to build a docker image for our app we previously built. Create a file called `Dockerfile` in the root folder and for now you can copy paste the configuration below:

```dockerfile
# Builder stage
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server .

# Runtime image stage
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/server .
CMD [ "./server" ]
```

To build the image so it will available in Minikube we need these following steps.

* First is to Switch to Minikube's Docker Daemon using command `eval $(minikube docker-env)`.
* Then we can build our docker image with tag `simple-go` like this:

```bash
docker build --tag simple-go .
```

* We can check using command `docker images` and the image `simple-go` should be in the list:

```bash
➜ docker images
REPOSITORY                                TAG        IMAGE ID       CREATED          SIZE
simple-go                                 latest     d47dfe963439   12 minutes ago   15.1MB
registry.k8s.io/kube-apiserver            v1.31.0    cd0f0ae0ec9e   5 months ago     91.5MB
registry.k8s.io/kube-scheduler            v1.31.0    fbbbd428abb4   5 months ago     66MB
registry.k8s.io/kube-controller-manager   v1.31.0    fcb0683e6bdb   5 months ago     85.9MB
registry.k8s.io/kube-proxy                v1.31.0    71d55d66fd4e   5 months ago     94.7MB
registry.k8s.io/etcd                      3.5.15-0   27e3830e1402   6 months ago     139MB
registry.k8s.io/pause                     3.10       afb61768ce38   8 months ago     514kB
registry.k8s.io/coredns/coredns           v1.11.1    2437cf762177   17 months ago    57.4MB
kubernetesui/dashboard                    <none>     20b332c9a70d   2 years ago      244MB
kubernetesui/metrics-scraper              <none>     a422e0e98235   2 years ago      42.3MB
gcr.io/k8s-minikube/storage-provisioner   v5         ba04bb24b957   3 years ago      29MB
```

## Create Deployment

With our image ready in Minikube docker daemon we then can proceed creating kubernetes deployment config. Lets name it `deployment.yaml` and you can copy paste the config below and break it down section by section so we know what it means.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-go
  labels:
    app: simple-go
spec:
  replicas: 3
  selector:
    matchLabels:
      app: simple-go
  template:
    metadata:
      labels:
        app: simple-go
    spec:
      containers:
      - name: server
        image: simple-go:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 8080
```

### General Structure

```yaml
apiVersion: apps/v1
kind: Deployment
```

* `apiVersion: apps/v1`: Specifies the API version of the Deployment resource. `apps/v1` is the most commonly used API version for Deployments in Kubernetes.
* `kind: Deployment`: Defines the type of Kubernetes resource being created. Here, it’s a Deployment.

### Metadata Section

```yaml
metadata:
  name: simple-go
  labels:
    app: simple-go
```

* `metadata:`: Contains metadata about the resource.
* `name: simple-go`: The name of the Deployment. This must be unique within the namespace.
* `labels:`: Key-value pairs used to tag the Deployment. These labels are useful for identifying and grouping resources.
  * `app: simple-go`: A label indicating this Deployment is related to the `simple-go` app.

### Spec Section

```yaml
spec:
  replicas: 3
  selector:
    matchLabels:
      app: simple-go
```

* `spec:`: Defines the desired state for the Deployment.
* `replicas: 2`: Specifies the number of Pod replicas to maintain. Kubernetes will ensure three Pods are running at all times.
* `selector:`: Defines how Kubernetes identifies Pods managed by this Deployment.
  * `matchLabels:`: Filters Pods by matching their labels.
    * `app: simple-go`: This matches Pods that have the label `app: simple-go`.

### Template Section

```yaml
template:
  metadata:
    labels:
      app: simple-go
```

* `template:`: Describes the Pods that will be created and managed by the Deployment.
* `metadata:`: Metadata specific to the Pods.
  * `labels:`: Labels assigned to each Pod created by this Deployment.
    * `app: simple-go`: Ensures the Pods have the label `app: simple-go`, which matches the selector.

### Pod Spec Section

```yaml
  spec:
    containers:
    - name: simple-go
      image: go-configurable-port
      imagePullPolicy: Never
      ports:
      - containerPort: 8080
```

* `spec:`: Defines the configuration for the Pods.
* `containers:`: Lists the containers to run in each Pod.
  * `- name: server`: The name of the container. This is used for identification within the Pod.
  * `image: simple-go`: Specifies the container image to use. Kubernetes will look for this image in the container registry or Minikube’s local Docker environment.
  * `imagePullPolicy: Never`: Because our minikube is local we need to specify image pull policy to `Never` so it will not trying to pull from container registry but use local instead.
  * `ports:`: Lists the ports exposed by the container.
    * `- containerPort: 8080`: The port number the container listens on. This allows Kubernetes to route traffic to the correct port inside the container.

### Apply

Lets apply our deployment configuration using command `kubectl apply -f deployment.yaml`.

```bash
➜ kubectl apply -f deployment.yaml
deployment.apps/simple-go created
```

And the we can check our pods using `kubectl get pods`. We should have `3` pods running like this.

```bash
➜ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
simple-go-65d7fccbfc-4f7xk   1/1     Running   0          10s
simple-go-65d7fccbfc-bgqlt   1/1     Running   0          10s
simple-go-65d7fccbfc-ltzgv   1/1     Running   0          10s
```

## Accessing the App

We successfully deploy our apps, it's not accessible yet. We will learn how to create a `service` to make our app accessible, but for now we can port forward using command `kubectl port-forward <pod-name> <source-port>:<target-port>`.

```bash
➜ kubectl port-forward simple-go-65d7fccbfc-4f7xk 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
```

Try again using curl to localhost port `8080` and we should get response like this:

```bash
➜ curl http://127.0.0.1:8080 
{"message":"Success"}
```

## References

* <https://kubernetes.io/docs/concepts/workloads/controllers/deployment/>
* <https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/>
