[TechStory]: Migrating services and databases from an OpenShift (or K8s) cluster to another
Tags: kubernetes, openshift, mysql, techstory
Published: 2023-06-03 14:39:47

Welcome Friends to another TechStory!

My first experience with OpenShift was with a task which was kind of a heavy task, I didn't know anything about [OpenShift](https://docs.openshift.com) or K8s! but I was in charge of migrating resources from a cluster to one another! (twice!)

But I learned a lot of things in the middle, so let's have a look at some of those.

> Before going deep dive, you may wonder what is OpenShift and why do I put its name beside K8s everytime, It's actually becuase Openshift is built on top of K8s and it's kind of the same (+few more features), so don't worry about the difference.

## So, What's the problem?
As I told you before, I had to move resources such as Applications (pods+services+routes), Databases (+their data) from an [OKD3 cluster](https://www.okd.io) to an [OKD4 cluster](https://www.okd.io) (different versions of OpenShift)

The reason of this migration? I'm not sure! 
To be honest, this was already decided and it was on the board,
Also in some companies, Kubernetes has two sides, cloud side and developer side, we (developers) don't lunch Kubernetes, instead, we lunch our services on it.

## Where did I start?
I asked two question on Stackoverflow because it was not clear for me what to do at the first place, and there were a lot of suggestions, which I just mention:
- [monokle](https://github.com/kubeshop/monokle)
- [Velero](https://velero.io/docs/v1.10/migration-case)
- OpenShift Official Guide called [Migration Toolkit for Containers (MTC)](https://docs.openshift.com/container-platform/4.12/migration_toolkit_for_containers/about-mtc.html)
- [Another Official Guide] (https://docs.openshift.com/container-platform/4.12/migrating_from_ocp_3_to_4/about-migrating-from-3-to-4.html)

Note: Unfortunately I can't provide a link to questions, although they were well received but a moderator deleted them 🤔

I also found and provided my answer, I followed it and I think it taught me a lot of things and there is nothing wrong with it, but it was not good enough solution for this case.

## Not good enough solution!
For making resources in a K8s cluster you should be careful about their priority,

First things to make are secrets and config maps, because they don't depend on anything but everything can depend on them!

And last things are routes, guess why? because they depend on services but nothing depends on them!

So the priority is:
- Secrets and ConfigMaps
- StatefulSets 
- Deployments
- Services
- Routes

You may not need to move pods directly, because they are usually made and controlled by other resources such as 
Deployments or StatefulSets.

### Steps to follow:
- Login to the first cluster:
```
oc login --token="your-token-for-first-server" --server="your-first-server"
```

- Export your resources:
```
oc get -o yaml cm > configmaps.yaml
oc get -o yaml secrets > secrets.yaml
...
```

There are also some default ConfigMaps and Secrets which you don't need to copy, you can erase them from generated yaml files.

- Login to the second cluster:
```
oc login --token="your-token-for-second-server" --server="your-second-server"
```

If you forget this step, you may get an error that says `resource already exists`, but be careful not to forget this step.

- Load resources to the second cluster
```
oc create -f configmaps.yaml
oc create -f secrets.yaml
...
```

## Better solutions
There was a better solution, we can use [Templates](https://docs.openshift.com/container-platform/4.9/openshift_images/using-templates.html) and process them for different clusters, environments or namespaces.

Also, combining them with CI/CD pipelines, helps us to move our resources by just changing some variables!

[Helm](https://helm.sh) works even better! I personally prefer Helm over basic templating.

I improved our CI/CD pipelines and Helmified (used Helm 😁) our applications for easier, clean and seamless deployments.


## What about DB?
DB is also another important & long discussion here, but not the database application itself, we can easily create that, but what should we do about the data? data is everything!

In my case, they were two scenarios:
- Production
- Stage (testing environment - production with fake data)

In Production, we had a managed Database controlled and owned by DBA team, we just had URL for Accessing it and that was enough.

So in cases that your accessing data which is in an external database (out of cluster), you don't need any extra work.

But in Stage, our data was stored in a PVC attached to a Pod controlled by a StatefulSet.

Note: We had MySQL, you may have something else, but the fellow is almost the same.

### Steps to follow:

#### Step 1: Login and port-forward
Login to the first Openshift cluster:
```
oc login --token=<YOUR-TOKEN> --server=<YOUR-SERVER>
```
List all pods and select your db pod (if replicated, choose master (0)) and copy it:
```
oc get pods
```
Make a port-forwarding to your local computer:
```
oc port-forward <MYSQL-POD-NAME> <desired-local-port>:<mysql-on-server-port>
```
So your server database is now (like) your local database! and any change you want on server can be done on `localhost`.

This method is the same for all databases and also applications.

----

#### Step 2: tools
Install `mysql` command line tools (if you have them installed, skip this step).
Specifically, we need `mysqldump` and `mysql` commands.

To do so, on macos run:
```
brew install mysql-client
```
I needed to add them to path (it usually happens automatically), run command below and add `/opt/homebrew/opt/mysql-client/bin` to the end of the file:
```
sudo nano /etc/paths 
```
Other editors such as vscode (type `code` in terminal) can be used instead of nano.

Read [here](https://stackoverflow.com/a/54787376/12972198) and [here](https://www.architectryan.com/2012/10/02/add-to-the-path-on-mac-os-x-mountain-lion/#.Uydjga1dXDg) for more information.

----

## Step 3: take a dump
```
mysqldump --column-statistics=0 --no-create-info -P <local-port> -h 127.0.0.1 -u <YOUR-USERNAME> -p "YOUR-DATABASE-NAME" > file_name.sql
```
Then you'll be asked to enter the database password for <YOUR-USERNAME>.

In my case, I previously ran migration scripts so I didn't want my dump to include `CREATE TABLE...` queries, so I added `--no-create-info` argument, but you may want to erase it.

Also, I was facing with `Unknown table 'COLUMN_STATISTICS' in information_schema (1109)` error, so I added `--column-statistics=0` argument.
For more information, read [here](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html#option_mysqldump_column-statistics) and [here](https://serverfault.com/a/912677/959490).

About the host IP, do not use `localhost`, it probably connects by socket rather than by port.

----

## Step 4: load the dump
Now, login to the second cluster, make the StatefulSet, and port-forward its database as explained before,
(you can run your migration scripts here on this local-like database and they will be done on the cluster as well)

Then, load the dump with below command:
```
mysql -P <local-port> -h 127.0.0.1 -u <YOUR-USERNAME> -p -f "YOUR-DATABASE-NAME" < file_name.sql
```

In my case, I had some unimportant errors, I've passed `-f` argument to skip them, but be careful when using this argument as it forces the database to follow your introduction defined on `file_name.sql`.

## Conclusion
TechStories are my real job experiences, they might be confusing as they include a lot of information and links to other articles, that's my point! we (developers) don't work in the way people see in usual articles and tutorials, so I want to share my knowledge beside teaching a lot of different tips that get related in my task.

I hope you enjoyed! 🥳✌🏼