# Managed Containers

> Octelium documentation. Canonical page: <https://octelium.com/docs/octelium/latest/management/core/service/managed-containers>.

In addition to serving internal and private resources behind NAT from anywhere as well as serving protected public resources such as SaaS APIs and databases, Octelium *Clusters* enable you to seamlessly deploy, manage and scale your containerized applications and serve them as *Services* by reusing the underlying Kubernetes infrastructure that runs the Octelium *Cluster* and deploying your Dockerized images on top of it. Here is a simple example of running an `nginx` image as an Octelium *Service*:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
# !mark(1:5)
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
```

> **Note:**
>
> Managed containers are automatically scheduled over data-plane nodes (read more [here](https://octelium.com/docs/octelium/latest/install/cluster/pre-install.md#data-plane-and-control-plane-nodes)).

## Environment Variables

You can also add your environment variables as follows:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
# !mark(1:5)
        env:
        - name: KEY1
          value: VALUE1
        - name: KEY2
          value: VALUE2
```

### Using a Secret as an Environment Variable

You can also use an Octelium *Secret* (read more [here](https://octelium.com/docs/octelium/latest/management/core/secret.md)) as a value for your environment variable. The *Cluster* automatically creates a Kubernetes secret for every Octelium *Secret* upon the deployment of the managed container. Here is an example:

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: ghcr.io/org/my-api:latest
# !mark(4:5)
        env:
        - name: KEY1
          value: VALUE1
        - name: VAULT_ACCESS_TOKEN
          fromSecret: vault-access-token
```

### Using a Kubernetes Secret as an Environment Variable

You can also use an existent Kubernetes secret in the `octelium` Kubernetes namespace as an environment variable value. Here is an example:

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: ghcr.io/org/my-api:latest
        env:
        - name: VAULT_ACCESS_TOKEN
          # !mark(1:3)
          kubernetesSecretRef:
            name: my-k8s-secret
            key: data
```

## Command and Arguments

You can override the container's "entrypoint" command via the `command` array as follows:

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: my-api:latest
# !mark(1:2)
        command:
        - /bin/another-binary
```

You can also add any command-line arguments via the `args` array as follows:

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: my-api:latest
# !mark(1:5)
        args:
        - --arg1=value1
        - --arg2=value2
        - --arg3
        - val3
```

## Scaling

You can easily scale up/down your upstream containerized application by controlling the number of replicas as follows:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
# !mark
        replicas: 4
```

## Private Registry

You can deploy Docker images from private container registries. In such case, all you have to do is add the registry credentials as follows:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: ghcr.io/org/image:tag
# !mark(1:5)
        credentials:
          usernamePassword:
            username: username
            password:
              fromSecret: secret-1
```

> **Note:**
>
> You can read more about *Secret* management [here](https://octelium.com/docs/octelium/latest/management/core/secret.md).

## Resource Limits

You can restrict how much memory and CPU your application can consume by forcing resource limits. Here is an example:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
# !mark(1:5)
        resourceLimit:
          cpu:
            millicores: 1500
          memory:
            megabytes: 512
```

### Extended Resources

You can also set an extended resource limit to request custom resource that can be recognized by your Kubernetes cluster. For example this can be useful to request and schedule a GPU for your managed container (read more [here](https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/)). Here is an example:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
        resourceLimit:
          cpu:
            millicores: 1500
          memory:
            megabytes: 512
          # !mark(1:2)
          ext:
            nvidia.com/gpu: "1"
```

## Security Context

You can also set a few security related configurations for your managed container such as enforcing a read-only root filesystem and setting the user ID. Here is an example:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
# !mark(1:3)
        securityContext:
          readOnlyRootFilesystem: true
          runAsUser: 1000
```

You can also add and drop Linux capabilities as follows:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
        securityContext:
        # !mark(1:3)
          capabilities:
            add: ["NET_ADMIN", "SYS_TIME"]
            drop: ["ALL"]
```

## Volumes

You can also define and mount Kubernetes volumes (read more [here](https://kubernetes.io/docs/concepts/storage/volumes/)) inside your managed containers.

### Persistent Volume Claim

You can use persistent volume claims (read more [here](https://kubernetes.io/docs/concepts/storage/volumes/#persistentvolumeclaim)) as follows:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
# !mark(1:8)
        volumes:
          - name: volume-1
            persistentVolumeClaim:
              name: pvc-1
        volumeMounts:
          - name: volume-1
            mountPath: /mnt/volume-1
            readOnly: true
```

### Empty Dir

You can use `emptyDir` volumes (read more [here](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir)) as follows:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
# !mark(1:7)
        volumes:
          - name: volume-1
            emptyDir:
              sizeLimitMegabytes: 1000
        volumeMounts:
          - name: volume-1
            mountPath: /mnt/volume-1
```

## Readiness and Liveness Probes

Octelium supports Kubernetes readiness and liveness probes (read more [here](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)) via `httpGet`, `tcpSocket` and `grpc` probes. Here is an example via `httpGet` probe:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
        livenessProbe:
          httpGet:
            port: 8080
            path: /healthz
          initialDelaySeconds: 20
          periodSeconds: 30
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
        readinessProbe:
          httpGet:
            port: 8080
            path: /healthz
          initialDelaySeconds: 20
          periodSeconds: 30
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
```

You can also use the `tcpSocket` as follows:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
        livenessProbe:
        # !mark(1:2)
          tcpSocket:
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 30
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
```

And for gRPC-based *Services*, you might want to use `grpc` probes as follows:

```yaml
kind: Service
metadata:
  name: my-grpc-api
spec:
  mode: GRPC
  config:
    upstream:
      container:
        port: 8080
        image: ghcr.io/org/grpc-api:latest
        livenessProbe:
          # !mark(1:2)
          grpc:
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 30
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
```

## Dynamic Configuration

You can also simultaneously deploy more than one container and dynamically route among them depending on identity as well as context (read more in detail about dynamic configuration [here](https://octelium.com/docs/octelium/latest/management/core/service/dynamic-config.md)). Here is an example:

```yaml
kind: Service
metadata:
  name: nginx
spec:
  mode: HTTP
  dynamicConfig:
    configs:
    - name: c1
      upstream:
        container:
          port: 80
          image: ghcr.io/org/image:v1
    - name: c2
      upstream:
        container:
          port: 80
          image: ghcr.io/org/image:v2
    rules:
    - condition:
        match: ctx.request.http.path.startsWith("/v1")
      configName: c1
    - condition:
        match: ctx.request.http.path.startsWith("/v2")
      configName: c2
```
