Skip to content

Deploy from Docker Compose using Kompose

Kompose is a simple utility you can use to convert your existing docker-compose files into Kubernetes Workloads. In this guide, we'll walk you through using the kompose to convert a NextCloud Compose file to a full Kubernetes workload.

Prerequisites

  • A Namespace in a Subkube Project
  • Kubectl
  • Optional: Kompose (we'll install kompose as part of this guide)

Steps

Installing Kompose

On Linux or macOS, installing compose is easy as:

1
2
3
4
5
6
7
8
# Linux
curl -L https://github.com/kubernetes/kompose/releases/download/v1.22.0/kompose-linux-amd64 -o kompose

# macOS
curl -L https://github.com/kubernetes/kompose/releases/download/v1.22.0/kompose-darwin-amd64 -o kompose

chmod +x kompose
sudo mv ./kompose /usr/local/bin/kompose

More installation instructions, including for windows, can be found on the kompose.io/installation page

Inspecting our Compose file

For this guide, we will be using the following docker-compose.yaml file to deploy a NextCloud instance, including a MariaDB and Nginx instance:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
version: '2'
services:
  app:
    image: nextcloud:fpm-alpine
    links:
      - db
    ports:
      - 9000
    volumes:
      - nextcloud:/var/www/html
    environment:
      - MYSQL_HOST=db
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_ROOT_PASSWORD=nextcloud
      - MYSQL_PASSWORD=nextcloud
    restart: always

  web:
    image: nginxinc/nginx-unprivileged
    ports:
      - 8080:8080
    links:
      - app
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    volumes_from:
      - app
    restart: always

  db:
    image: mariadb
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: always
    ports:
      - 3306
    volumes:
      - db:/var/lib/mysql
    environment:
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_ROOT_PASSWORD=nextcloud
      - MYSQL_PASSWORD=nextcloud

volumes:
  nextcloud:
  db:

As you can see it is using the nextcloud:fpm image to run the NextCloud php application, and an nginxinc/nginx-unprivileged image to run Nginx as non-root on port 8080.

Running the Conversion

The kompose utilty has various capabilities, including the option to generate a Helm Chart from a Compose file, create Openshift-compliant resources, or create ReplicationController or DaemonSet workloads.

In this example, we will use the --out option to tell kompose to put the generated manifests in the output directory, which we have to create manually before running kompose:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
> mkdir output
> kompose convert --out output/
WARN Unsupported root level volumes key - ignoring
WARN Volume mount on the host "./nginx.conf" isn\'t supported - ignoring path on the host
INFO Kubernetes file "output/app-service.yaml" created
INFO Kubernetes file "output/db-service.yaml" created
INFO Kubernetes file "output/web-service.yaml" created
INFO Kubernetes file "output/app-deployment.yaml" created
INFO Kubernetes file "output/nextcloud-persistentvolumeclaim.yaml" created
INFO Kubernetes file "output/db-deployment.yaml" created
INFO Kubernetes file "output/db-persistentvolumeclaim.yaml" created
INFO Kubernetes file "output/web-deployment.yaml" created
INFO Kubernetes file "output/web-claim0-persistentvolumeclaim.yaml" created

As you can see, Kompose has created both a Deployment and a Service for each entry in our Compose file. You also see a warning about the nginx.conf file used for the Nginx web container, which we will address in the upcoming section.

Updating the generated resources

Before we can apply these resources, we want to make some changes.

Add our Configuration

We use a custom Nginx Configuration for the nginx container serving as HTTP proxy to Nextloud's php-fpm app container. As you cannot simply mount a file inside a container on Kubernetes, we need to convert the nginx.conf file to a ConfigMap. Luckily, kubectl can do this largely for us:

1
2
3
4
kubectl create configmap -o yaml --dry-run \
    web \
    --from-file nginx.conf \
    > output/web-configmap.yaml

Now we should replace the generated web-claim0 PersistentVolumeClaim with our ConfigMap:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert --out output/
    kompose.version: 1.22.0 (HEAD)
  creationTimestamp: null
  labels:
    io.kompose.service: web
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      io.kompose.service: web
  strategy:
    type: Recreate
  template:
    metadata:
      annotations:
        kompose.cmd: kompose convert --out output/
        kompose.version: 1.22.0 (HEAD)
      creationTimestamp: null
      labels:
        io.kompose.service: web
    spec:
      containers:
        - image: nginxinc/nginx-unprivileged
          name: web
          envFrom:
          - configMapRef:
              name: web
          ports:
            - containerPort: 8080
          resources:
            limits:
              cpu: '200m'
              memory: '200Mi'
            requests:
              cpu: '100m'
              memory: '100Mi'
          volumeMounts:
            - name: config-volume
              mountPath: /etc/nginx/nginx.conf
              subPath: nginx.conf
            - mountPath: /var/www/html
              name: nextcloud
      restartPolicy: Always
      volumes:
        - name: config-volume
          configMap:
            name: web
        - name: nextcloud
          persistentVolumeClaim:
            claimName: nextcloud
status: {}

Do not forget to remove the unneeded web-claim0 PersistentVolumeClaim manifest:

1
rm output/web-claim0-persistentvolumeclaim.yaml

Use a Secret

In our docker-compose.yaml, we specified the MySQL connection parameters directly as environment variables to the container. In Kubernetes, we use Secrets for this sort of thing. Let's setup a secret we can use for our MariaDB deployment, using kubectl:

1
2
3
4
5
6
 kubectl create secret generic -oyaml --dry-run \
    db \
    --from-literal=MYSQL_USER=nextcloud \
    --from-literal=MYSQL_ROOT_PASSWORD=nextcloud \
    --from-literal=MYSQL_PASSWORD=nextcloud \
    > output/db-secret.yaml

Inside our Deployments, we can use the envFrom directive to tell Kubernetes which Secret to use for the app and db workloads:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert --out output/
    kompose.version: 1.22.0 (HEAD)
  creationTimestamp: null
  labels:
    io.kompose.service: app
  name: app
spec:
  replicas: 1
  selector:
    matchLabels:
      io.kompose.service: app
  strategy:
    type: Recreate
  template:
    metadata:
      annotations:
        kompose.cmd: kompose convert --out output/
        kompose.version: 1.22.0 (HEAD)
      creationTimestamp: null
      labels:
        io.kompose.service: app
    spec:
      containers:
        - env:
            - name: MYSQL_DATABASE
              value: nextcloud
            - name: MYSQL_HOST
              value: db
          envFrom:
          - secretRef:
              name: db
          image: nextcloud:fpm
          name: app
          resources:
            limits:
              cpu: '200m'
              memory: '200Mi'
            requests:
              cpu: '100m'
              memory: '100Mi'
          volumeMounts:
            - mountPath: /var/www/html
              name: nextcloud
      restartPolicy: Always
      volumes:
        - name: nextcloud
          persistentVolumeClaim:
            claimName: nextcloud
status: {}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert --out output/
    kompose.version: 1.22.0 (HEAD)
  creationTimestamp: null
  labels:
    io.kompose.service: db
  name: db
spec:
  replicas: 1
  selector:
    matchLabels:
      io.kompose.service: db
  strategy:
    type: Recreate
  template:
    metadata:
      annotations:
        kompose.cmd: kompose convert --out output/
        kompose.version: 1.22.0 (HEAD)
      creationTimestamp: null
      labels:
        io.kompose.service: db
    spec:
      containers:
        - args:
            - --transaction-isolation=READ-COMMITTED
            - --binlog-format=ROW
          env:
            - name: MYSQL_DATABASE
              value: nextcloud
          envFrom:
          - secretRef:
              name: db
          image: mariadb
          name: db
          resources:
            limits:
              cpu: '200m'
              memory: '200Mi'
            requests:
              cpu: '100m'
              memory: '100Mi'
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: db
      restartPolicy: Always
      volumes:
        - name: db
          persistentVolumeClaim:
            claimName: db
status: {}

Setting Resources & Security Contexts

As with any workload running on Subkube, we should set appropriate resources for the deployments, as well as securityContexts.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert --out output/
    kompose.version: 1.22.0 (HEAD)
  creationTimestamp: null
  labels:
    io.kompose.service: app
  name: app
spec:
  replicas: 1
  selector:
    matchLabels:
      io.kompose.service: app
  strategy:
    type: Recreate
  template:
    metadata:
      annotations:
        kompose.cmd: kompose convert --out output/
        kompose.version: 1.22.0 (HEAD)
      creationTimestamp: null
      labels:
        io.kompose.service: app
    spec:
      containers:
        - env:
            - name: MYSQL_DATABASE
              value: nextcloud
            - name: MYSQL_HOST
              value: db
          envFrom:
          - secretRef:
              name: db
          image: nextcloud:fpm
          name: app
          resources:
            limits:
              cpu: '200m'
              memory: '200Mi'
            requests:
              cpu: '100m'
              memory: '100Mi'
          volumeMounts:
            - mountPath: /var/www/html
              name: nextcloud
      restartPolicy: Always
      volumes:
        - name: nextcloud
          persistentVolumeClaim:
            claimName: nextcloud
status: {}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert --out output/
    kompose.version: 1.22.0 (HEAD)
  creationTimestamp: null
  labels:
    io.kompose.service: db
  name: db
spec:
  replicas: 1
  selector:
    matchLabels:
      io.kompose.service: db
  strategy:
    type: Recreate
  template:
    metadata:
      annotations:
        kompose.cmd: kompose convert --out output/
        kompose.version: 1.22.0 (HEAD)
      creationTimestamp: null
      labels:
        io.kompose.service: db
    spec:
      containers:
        - args:
            - --transaction-isolation=READ-COMMITTED
            - --binlog-format=ROW
          env:
            - name: MYSQL_DATABASE
              value: nextcloud
          envFrom:
          - secretRef:
              name: db
          image: mariadb
          name: db
          resources:
            limits:
              cpu: '200m'
              memory: '200Mi'
            requests:
              cpu: '100m'
              memory: '100Mi'
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: db
      restartPolicy: Always
      volumes:
        - name: db
          persistentVolumeClaim:
            claimName: db
status: {}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert --out output/
    kompose.version: 1.22.0 (HEAD)
  creationTimestamp: null
  labels:
    io.kompose.service: web
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      io.kompose.service: web
  strategy:
    type: Recreate
  template:
    metadata:
      annotations:
        kompose.cmd: kompose convert --out output/
        kompose.version: 1.22.0 (HEAD)
      creationTimestamp: null
      labels:
        io.kompose.service: web
    spec:
      containers:
        - image: nginxinc/nginx-unprivileged
          name: web
          envFrom:
          - configMapRef:
              name: web
          ports:
            - containerPort: 8080
          resources:
            limits:
              cpu: '200m'
              memory: '200Mi'
            requests:
              cpu: '100m'
              memory: '100Mi'
          volumeMounts:
            - name: config-volume
              mountPath: /etc/nginx/nginx.conf
              subPath: nginx.conf
            - mountPath: /var/www/html
              name: nextcloud
      restartPolicy: Always
      volumes:
        - name: config-volume
          configMap:
            name: web
        - name: nextcloud
          persistentVolumeClaim:
            claimName: nextcloud
status: {}

Apply and Verify

After we have made these changes, we can deploy the resources:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
> kubectl apply -n demo -f output/
deployment.apps/app created
service/app created
deployment.apps/db created
persistentvolumeclaim/db created
secret/db created
service/db created
persistentvolumeclaim/nextcloud created
configmap/web created
deployment.apps/web created
service/web created

We can monitor our Deployments' to see them progressing:

1
kubectl get deployment -n demo -w

Once everything is ready, we can setup a port-forward to access NextCloud and trigger the setup process, by creating an Admin user.

1
kubectl port-forward service/web -n demo 8080:8080

More Resources & Further Reading