AGAIN: NOT A GUIDE, BUT MAY BE HELPFULL TO......SOMEONE.....MAYBE.
I left this portion out from my docker/portainer to kubernetes/rancher post because it seemed (and still does) more complicated and warranted its own post. The reading time will probably be lengthy because of all the yml files, but hey ho, let's go.
So, as always, I have a semblence of a plan that I'll probably not follow to the letter. Step 1, pick a docker-compose.yml from my collection to migrate over. Easy, I pick bookstack as that's what I use for my internal notes and stuff. Here's the file.
version: "2"
services:
bookstack:
image: linuxserver/bookstack
container_name: bookstack
environment:
- PUID=1000
- PGID=1000
- DB_HOST=bookstack_db
- DB_USER=bookstack
- DB_PASS=<PASSWORD>
- DB_DATABASE=bookstackapp
volumes:
- bookstack:/config
ports:
- 6875:80
restart: unless-stopped
depends_on:
- bookstack_db
bookstack_db:
image: linuxserver/mariadb
container_name: bookstack_db
environment:
- PUID=1000
- PGID=1000
- MYSQL_ROOT_PASSWORD=<PASSWORD>
- TZ=Europe/London
- MYSQL_DATABASE=bookstackapp
- MYSQL_USER=bookstack
- MYSQL_PASSWORD=<PASSWORD>
volumes:
- bookstack:/config
restart: unless-stopped
Pretty much copied exactly from the fine folks over at linuxserver.io. For those unfamiliar with docker-compose....well, wrong post. For those familiar with docker-compose, you'll see that there are 2 distinct things going on here. The actual bookstack server, and a mariadb instance for storing the data. There's also the small matter of persistant storage in the form of the shared volume.
Step 2. What I would like is to magically convert this to a file/a collection of files to pump into rancher and have everything just work. I don't think this is fully possible (for my specific weirdness), but there's a tool that will get us part of the way. Introducing Kompose. Easy to install by following the instructions on that page so I'll skip over that.
With it installed, we need to dump a copy of the docker-compose.yml file we want to convert somewhere we can work on it. Then, in the same directory as that file, run
$ kompose convert
In my instance, it complained about the restart policy, but still made 4 files for us to look at. Here's the directory:
deploy@webdev: ~/docker-compose-files/bookstack
$ tree
.
├── bookstack-db-deployment.yaml
├── bookstack-deployment.yaml
├── bookstack-persistentvolumeclaim.yaml
├── bookstack-service.yaml
└── docker-compose.yml
0 directories, 5 files
And here's what each generated file looks like:
bookstack-db-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack-db
name: bookstack-db
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: bookstack-db
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack-db
spec:
containers:
- env:
- name: MYSQL_DATABASE
value: bookstackapp
- name: MYSQL_PASSWORD
value: <PASSWORD>
- name: MYSQL_ROOT_PASSWORD
value: <PASSWORD>
- name: MYSQL_USER
value: bookstack
- name: PGID
value: "1000"
- name: PUID
value: "1000"
- name: TZ
value: Europe/London
image: linuxserver/mariadb
name: bookstack-db
resources: {}
volumeMounts:
- mountPath: /config
name: bookstack
restartPolicy: Always
volumes:
- name: bookstack
persistentVolumeClaim:
claimName: bookstack
status: {}
bookstack-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: bookstack
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
spec:
containers:
- env:
- name: DB_DATABASE
value: bookstackapp
- name: DB_HOST
value: bookstack_db
- name: DB_PASS
value: <PASSWORD>
- name: DB_USER
value: bookstack
- name: PGID
value: "1000"
- name: PUID
value: "1000"
image: linuxserver/bookstack
name: bookstack
ports:
- containerPort: 80
resources: {}
volumeMounts:
- mountPath: /config
name: bookstack
restartPolicy: Always
volumes:
- name: bookstack
persistentVolumeClaim:
claimName: bookstack
status: {}
bookstack-persistentvolumeclaim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
status: {}
bookstack-service.yaml
apiVersion: v1
kind: Service
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack
spec:
ports:
- name: "6875"
port: 6875
targetPort: 80
selector:
io.kompose.service: bookstack
status:
loadBalancer: {}
Well. That's a lotta yaml. BUT with the structure split like this, it has made things simpler for me to understand. The way I see it, we have a seperate deployment file for the bookstack app itself, and the associated mariadb. Then we have another file for setting up the persistant volume. Finally, the service file contains our port mapping.
Ok, yeah, see, things are now lining up. The first thing I want to do is modify the persistant volume claim because I know I want to use longhorn to store the data. According to the official documentation, a simple claim looks like this.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: longhorn-simple-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 1Gi
This looks very similar to the file dumped out by Kompose. I'll make some changes to the bookstack-persistentvolumeclaim.yaml file so it looks like this:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 2Gi
status: {}
Easy enough. Next up, the db deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack-db
name: bookstack-db
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: bookstack-db
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack-db
spec:
containers:
- env:
- name: MYSQL_DATABASE
value: bookstackapp
- name: MYSQL_PASSWORD
value: <PASSWORD>
- name: MYSQL_ROOT_PASSWORD
value: <PASSWORD>
- name: MYSQL_USER
value: bookstack
- name: PGID
value: "1000"
- name: PUID
value: "1000"
- name: TZ
value: Europe/London
image: linuxserver/mariadb
name: bookstack-db
resources: {}
volumeMounts:
- mountPath: /config
name: bookstack-data
restartPolicy: Always
volumes:
- name: bookstack
persistentVolumeClaim:
claimName: bookstack-data
status: {}
Ok, all I changed here was the claim names to match what I changed previously. Easy enough. Next up, the bookstack deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: bookstack
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
spec:
containers:
- env:
- name: DB_DATABASE
value: bookstackapp
- name: DB_HOST
value: bookstack_db
- name: DB_PASS
value: <PASSWORD>
- name: DB_USER
value: bookstack
- name: PGID
value: "1000"
- name: PUID
value: "1000"
image: linuxserver/bookstack
name: bookstack
ports:
- containerPort: 80
resources: {}
volumeMounts:
- mountPath: /config
name: bookstack-data
restartPolicy: Always
volumes:
- name: bookstack
persistentVolumeClaim:
claimName: bookstack-data
status: {}
Ok, yeah, same again. This is feeling too easy. Just changed the volume name again. Obviously the passwords will need changing, but that's a different problem that I'll get back to in a bit.
Final part of the puzze is the service file. From my previous forrays into these shenanigans, I know that I want to access bookstack through a nodeport. Easy enough to find a good example of this to see what changes I need to make. Here's the example. Even has handy comments :D
apiVersion: v1
kind: Service
metadata:
name: svc-nodeport-httpd
spec:
type: NodePort
ports:
- port: 3050 # This is the port to use by other pods to reach target port
targetPort: 80 # This is the port the destination pod is listening on.
nodePort: 31000 # port to use from your macbook
selector:
component: httpd # this service will forward traffic to any pods with this label.
Here's my completed file.
apiVersion: v1
kind: Service
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack
spec:
type: NodePort
ports:
- name: "bookstack-port"
port: 30060
targetPort: 80
selector:
io.kompose.service: bookstack
The changes I made here were adding type: NodePort, changing the name to match the naming scheme I've been using, providing a nodeport number that's free, and removing "status: loadBalancer: {}" from the end, as I don't think I need that.
Ok, finally, I just want to touch on the topic of passwords and how they "should" be stored. Best practice is to use secrets and then pass those secrets as and when needed. However, the secrets stored in rancher seem to only be base64 encoded to prevent them from becoming human readable, but is by no means secure. There is a method to add this in properly, and it doesn't seem overly complicated, but, I'm meandering. The point is, I'm not going to use secrets just yet because my cluster is not exposed to the world and only I have access internally.
So, with these changes, and the files and concatenated into one, and passwords added, I can try deploying it. Let's see what happens.
Go to Rancher UI -> Minions Cluster -> Import YAML
We have our first error! Wooooo.....
persistentvolumeclaim/bookstack-data created
service/bookstack created
Error from server (Invalid): error when creating "management-state/tmp/yaml-809813384": Deployment.apps "bookstack-db" is invalid: spec.template.spec.containers[0].volumeMounts[0].name: Not found: "bookstack-data"
Error from server (Invalid): error when creating "management-state/tmp/yaml-809813384": Deployment.apps "bookstack" is invalid: spec.template.spec.containers[0].volumeMounts[0].name: Not found: "bookstack-data"
So, what I'm gathering from this is that the persistant volume claim was created, but for some reason the bookstack app and the db can't find the volumes, meaning it wasn't mounted? Maybe. Bare with.
Ok, so I made shmoll error by not changing the name properly, as the error suggested. Made the volume section of both the app and db look like this:
volumes:
- name: bookstack-data
persistentVolumeClaim:
claimName: bookstack-data
So the db got deployed, but the app itself failed to start. Looking at the event log shows the following:
Warning FailedMount Unable to attach or mount volumes: unmounted volumes=[bookstack-data], unattached volumes=[bookstack-data default-token-xx4xk]: timed out waiting for the condition 2 minutes ago
Warning FailedMount Unable to attach or mount volumes: unmounted volumes=[bookstack-data], unattached volumes=[default-token-xx4xk bookstack-data]: timed out waiting for the condition 4 minutes ago
Warning FailedAttachVolume Multi-Attach error for volume "pvc-d9021720-348d-4f79-a7c5-693c8747e6cb" Volume is already used by pod(s) bookstack-db-76d6767d5f-tmwgh 6 minutes ago
Normal Scheduled Successfully assigned bookstack/bookstack-7bfd6c558d-rfzck to k8s-minion-03 6 minutes ago
Warning FailedScheduling 0/3 nodes are available: 3 pod has unbound immediate PersistentVolumeClaims. 6 minutes ago
The interesting part is "Volume is already used by pod(s) bookstack-db-76d6767d5f-tmwgh". I think I know how to fix this....
In the interest of open-ness, I tried
accessModes:
- ReadWriteMany
In the persistant volume claim, but that caused the issue where it wouldn't be mounted by anything for some reason. Similar to when I was trying to get Nextcloud up and running in other post. I did find this but I don't think that helps my current situation.
What I think I'll do then, is to create a seperate pvc for the bookstack database, and mount it. However, for some reason, the docker-compose has both the db and app using data that's mounted in /config. Let me just have a quick rummage around the container to see what's actually inside the /config folder.
Ok, so inside the data folder for bookstack, I see the following things:
root@dh2:/var/lib/docker/volumes/bookstack/_data# ls
BOOKSTACK_APP_KEY.txt custom.cnf databases keys log nginx php www
Maybe if I make a pvc, and mount it to /config/databases ?
Lets give it a go.
Ok, that fixed the pvc issue, I think..... but for some reason my service section is being ignored and the port isn't being mapped correctly. Wonder why......
Ok, I think I got that bit working. Here's the fixed services file.
apiVersion: v1
kind: Service
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack
spec:
type: NodePort
ports:
- name: bookstack-port
port: 80
nodePort: 30060
protocol: TCP
selector:
io.kompose.service: bookstack
I can see it's partially working as the address:nodeport resolves, I get a 504 error, but the bookstack icon appears in the tab......so it's trying. Lets see what the logs say about this issue.
Exception trace:
1 Doctrine\DBAL\Driver\PDOException::("SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Try again")
/var/www/html/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:31
2 PDOException::("PDO::__construct(): php_network_getaddresses: getaddrinfo failed: Try again")
/var/www/html/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:27
From what I gather, there's a failure in connecting to the db.....why tho?
Figured it out :3
Answer was here. What did I learn? Communication between anything within a deployment requires a service. Not just when you're trying to expose stuff. I needed to add one more service in my case for the communication between the app and the db. Here it is.
apiVersion: v1
kind: Service
metadata:
name: bookstack-db
spec:
selector:
io.kompose.service: bookstack-db
ports:
- protocol: TCP
port: 3306
targetPort: 3306
The important thing here is the io.kompose.service: bookstack-db line. That tells the service what it's for. I also amended the pvc and the db deployments to use the same label. Finally, a small aside. You will also want to add the following as an environment variable so everything is displayed correctly if you're going to be doing any reverse proxy/ssl stuff.
- name: APP_URL
value: "https://fqdmn"
For completeness, here is the full deployment in one file that I easily imported into Rancher.
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: bookstack-db
name: bookstack-db
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 2Gi
status: {}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 2Gi
status: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack-db
name: bookstack-db
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: bookstack-db
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack-db
spec:
containers:
- env:
- name: MYSQL_DATABASE
value: bookstackapp
- name: MYSQL_PASSWORD
value: <PASSWORD_1>
- name: MYSQL_ROOT_PASSWORD
value: <PASSWORD_2>
- name: MYSQL_USER
value: bookstack
- name: PGID
value: "1000"
- name: PUID
value: "1000"
- name: TZ
value: Europe/London
image: linuxserver/mariadb
name: bookstack-db
resources: {}
volumeMounts:
- mountPath: /config
name: bookstack-db
restartPolicy: Always
volumes:
- name: bookstack-db
persistentVolumeClaim:
claimName: bookstack-db
status: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: bookstack
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
spec:
containers:
- env:
- name: DB_DATABASE
value: bookstackapp
- name: DB_HOST
value: bookstack-db
- name: DB_PASS
value: <PASSWORD_1>
- name: DB_USER
value: bookstack
- name: PGID
value: "1000"
- name: PUID
value: "1000"
- name: APP_URL
value: "https://fqdn"
image: linuxserver/bookstack
name: bookstack
ports:
- containerPort: 80
resources: {}
volumeMounts:
- mountPath: /config
name: bookstack-data
restartPolicy: Always
volumes:
- name: bookstack-data
persistentVolumeClaim:
claimName: bookstack-data
status: {}
---
apiVersion: v1
kind: Service
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.22.0 (955b78124)
creationTimestamp: null
labels:
io.kompose.service: bookstack
name: bookstack
spec:
type: NodePort
ports:
- name: bookstack-port
port: 80
nodePort: 30060
protocol: TCP
selector:
io.kompose.service: bookstack
---
apiVersion: v1
kind: Service
metadata:
name: bookstack-db
spec:
selector:
io.kompose.service: bookstack-db
ports:
- protocol: TCP
port: 3306
targetPort: 3306
I did wonder if a 2GB per pvc would be enough, so I had a quick peek within my docker-host that's hosting my bookstack at the moment with all my things. Total usage is showing as 211M. Almost all of which is the database. I may increase the size of each claim to 5GB just to be on the safe side, but yeah.
Also, there does seem to be a helm chart to do this, but this was a pretty good learning experience. The final result is a mish mash, for sure, but with the knowledge I've gained, I should be able to implement one wholly from scratch with a bit more practice.