This is a MariaDB Operator (ansible-based), which makes management of MariaDB instances (or clusters) running inside Kuberenetes clusters easy. It was built with the Operator SDK using Ansible.
The Operator SDK provides a way to build an Operator that will run Ansible playbooks to react to CR changes. The SDK supplies the code for the Kubernetes pieces, such as the controller, allowing you to focus on writing the playbooks themselves.
If you want you can use ansible role and playbook file as base and build similiar MariaDB Operator using Operator SDK.
$ cd ansible-base
$ OPERATOR_NAME=mariadb-ansible-operator
$ operator-sdk new $OPERATOR_NAME --api-version=mariadb.mariadb.com/v1alpha1 --kind=MariaDB --type=ansible
INFO[0000] Creating new Ansible operator 'mariadb-ansible-operator'.
INFO[0000] Created deploy/service_account.yaml
INFO[0000] Created deploy/role.yaml
INFO[0000] Created deploy/role_binding.yaml
INFO[0000] Created deploy/crds/mariadb.mariadb.com_mariadbs_crd.yaml
INFO[0000] Created deploy/crds/mariadb.mariadb.com_v1alpha1_mariadb_cr.yaml
INFO[0000] Created build/Dockerfile
INFO[0000] Created roles/mariadb/README.md
INFO[0000] Created roles/mariadb/meta/main.yml
INFO[0000] Created roles/mariadb/files/.placeholder
INFO[0000] Created roles/mariadb/templates/.placeholder
INFO[0000] Created roles/mariadb/vars/main.yml
INFO[0000] Created molecule/test-local/playbook.yml
INFO[0000] Created roles/mariadb/defaults/main.yml
INFO[0000] Created roles/mariadb/tasks/main.yml
INFO[0000] Created molecule/default/molecule.yml
INFO[0000] Created build/test-framework/Dockerfile
INFO[0000] Created molecule/test-cluster/molecule.yml
INFO[0000] Created molecule/default/prepare.yml
INFO[0000] Created molecule/default/playbook.yml
INFO[0000] Created build/test-framework/ansible-test.sh
INFO[0000] Created molecule/default/asserts.yml
INFO[0000] Created molecule/test-cluster/playbook.yml
INFO[0000] Created roles/mariadb/handlers/main.yml
INFO[0000] Created watches.yaml
INFO[0000] Created deploy/operator.yaml
INFO[0000] Created .travis.yml
INFO[0000] Created molecule/test-local/molecule.yml
INFO[0000] Created molecule/test-local/prepare.yml
INFO[0000] Project creation complete.
Operator SDK generates a project skeleton and you can start editing (fixing/extending code).
Run script:
$ ./setup_environment.sh
Check:
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T19:18:23Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T19:09:08Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
Note: Server & Client has to be the same Minor versions.
There are a few moving parts to this project:
- The Docker image which powers MariaDB Operator.
- The
mariadb-operator.yaml
Kubernetes manifest file which initially deploys the Operator into a cluster.
Each of these must be appropriately built in preparation for a new tag:
Run the following command inside this directory:
$ operator-sdk build davarski/mariadb-ansible-operator:1.0.0
Then push the generated image to Docker Hub:
$ docker login
$ docker push davarski/mariadb-ansible-operator:1.0.0
Verify the build/chain-operator-files.yml
playbook has the most recent version/tag of the Docker image, then run the playbook in the build/
directory:
ansible-playbook chain-operator-files.yml
After it is built, test it on a local cluster:
kubectl apply -f deploy/mariadb-operator.yaml
kubectl create namespace example-mariadb
kubectl apply -f deploy/crds/mariadb_v1alpha1_mariadb_cr.yaml
<test everything>
If everything is deployed correctly, commit the updated version and push it up to GitHub, tagging a new repository release with the same tag as the Docker image.
Ensure you have the testing dependencies installed (in addition to Docker):
pip install docker molecule openshift jmespath
Run the local molecule test scenario:
molecule test -s test-local
Running the Ansible Operator outside of the cluster:
$ cp watches.yaml local-watches.yaml
$ kubectl apply -f deploy/crds/*_crd.yaml
$ operator-sdk up local --watches-file ./local-watches.yaml
INFO[0000] Running the operator locally.
INFO[0000] Using namespace default.
Check OLM:
$ kubectl get ns olm
$ kubectl get pods -n olm
$ kubectl get crd
$ kubectl get catalogsource -n olm
$ kubectl describe catalogsource/operatorhubio-catalog -n olm
$ kubectl get packagemanifest -n olm
OLM Bundle Creation
$ operator-sdk olm-catalog gen-csv --csv-version 1.0.0
INFO[0000] Generating CSV manifest version 1.0.0
INFO[0000] Fill in the following required fields in file deploy/olm-catalog/mariadb-operator/1.0.0/mariadb-operator.v1.0.0.clusterserviceversion.yaml:
spec.keywords
spec.maintainers
spec.provider
INFO[0000] Created deploy/olm-catalog/mariadb-operator/1.0.0/mariadb-operator.v1.0.0.clusterserviceversion.yaml
INFO[0000] Created deploy/olm-catalog/mariadb-operator/mariadb-operator.package.yaml
$ mkdir -p olm/bundle
$ cp deploy/olm-catalog/mariadb-operator/1.0.0/mariadb-operator.v1.0.0.clusterserviceversion.yaml olm/bundle/
$ cp deploy/olm-catalog/mariadb-operator/mariadb-operator.package.yaml olm/bundle/
$ cp deploy/crds/mariadb_v1alpha1_mariadb_crd.yaml olm/bundle/
Edit bundle files:
Create the OperatorSource:
$ cd olm
$ kubectl apply -f operator-source.yaml
$ kubectl get catalogsource -n marketplace
$ kubectl get opsrc -n marketplace
If there are no bundles at the endpoint when you create the source, the status will be Failed . You can ignore this for now; you’ll refresh this list later, once you’ve uploaded a bundle.
Building the OLM Bundle:
$ operator-courier verify ./bundle
$ ./get-quay-token
Username: davarski
Password:
========================================
Auth Token is: basic XXXXXXXXXX=
The following command will assign the token to the expected variable:
export QUAY_TOKEN="basic XXXXXXXXX="
$ OPERATOR_DIR=bundle
$ QUAY_NAMESPACE=davarski
$ PACKAGE_NAME=visitors-ansible-operator
$ PACKAGE_VERSION=1.0.0
$ QUAY_TOKEN="basic XXXXXXXXX="
$ operator-courier push "$OPERATOR_DIR" "$QUAY_NAMESPACE" \
"$PACKAGE_NAME" "$PACKAGE_VERSION" "$QUAY_TOKEN"
By default, bundles pushed to Quay.io in this fashion are marked as private. Navigate to the image at https://quay.io/application/ and mark it as public so that it is accessible to the cluster.
$ kubectl get pods -n marketplace
$ kubectl delete pod davarski-operators-5969c68d68-vfff6 -n marketplace
$ kubectl delete -f operator-source.yaml
$ kubectl apply -f operator-source.yaml
$ OP_SRC_NAME=davarski-operators
$ kubectl get opsrc $OP_SRC_NAME -o=custom-columns=NAME:.metadata.name,PACKAGES:.status.packages -n marketplace
NAME PACKAGES
davarski-operators mariadb-operator,visitors-ansible-operator
Installing the Operator Through OLM
$ kubectl apply -f operator-group.yaml
$ kubectl apply -f subscription.yaml
$ kubectl get pods -n marketplace
Cleaning up:
$ kubectl delete -f subscription.yaml
This Kubernetes Operator is meant to be deployed in your Kubernetes cluster(s) and can manage one or more MariaDB database instances or clusters in any namespace.
First you need to deploy MariaDB Operator into your cluster:
kubectl apply -f https://raw.githubusercontent.com/adavarski/k8s-mariadb-ansible-operator/main/deploy/mariadb-operator.yaml
or git pull ; kubectl apply -f deploy/mariadb-operator.yaml
Then you can create instances of MariaDB in any namespace, for example:
-
Create a file named
my-database1.yml
with the following contents:--- apiVersion: mariadb.mariadb.com/v1alpha1 kind: MariaDB metadata: name: example-mariadb namespace: example-mariadb spec: masters: 1 replicas: 0 mariadb_password: CHANGEME mariadb_image: mariadb:10.4 mariadb_pvc_storage_request: 1Gi
-
Use
kubectl
to create the MariaDB instance in your cluster:kubectl apply -f my-database1.yml
You can also deploy
MariaDB
instances into other namespaces by changingmetadata.namespace
, or deploy multipleMariaDB
instances into the same namespace by changingmetadata.name
.
Once the database instance has been deployed and initialized (this can take up to a few minutes), you can connect to it using the MySQL CLI from within the same namespace as your database instance, for example:
kubectl -n example-mariadb run -it --rm mysql-client --image=arey/mysql-client --restart=Never -- -h example-mariadb-0.example-mariadb.example-mariadb.svc.cluster.local -u db_user -pCHANGEME -D db
Other applications can connect to the database instance from within the same namespace using the following connection parameters:
- Host:
example-mariadb-0.example-mariadb.example-mariadb.svc.cluster.local
- Username:
db_user
- Password:
CHANGEME
- Database:
db
You can also expose a database instance to the outside world by adding a Service
to connect to port 3360
:
---
apiVersion: v1
kind: Service
metadata:
name: mariadb-master
namespace: example-mariadb
spec:
type: NodePort
selector:
statefulset.kubernetes.io/example-mariadb: example-mariadb-0
ports:
- protocol: TCP
port: 3360
targetPort: 3360
Once you create that service in the same namespace, you can connect to MariaDB via the NodePort assigned to the service.
Note: mariadb operator/db (k3s deploy/test/clean)
#Install k3s
$ curl -sfL https://get.k3s.io | sh -
#Deploy operator
$ kubectl apply -f https://raw.githubusercontent.com/adavarski/k8s-mariadb-ansible-operator/main/deploy/mariadb-operator.yaml
clusterrole.rbac.authorization.k8s.io/mariadb-operator created
clusterrolebinding.rbac.authorization.k8s.io/mariadb-operator created
serviceaccount/mariadb-operator created
deployment.apps/mariadb-operator created
customresourcedefinition.apiextensions.k8s.io/mariadbs.mariadb.mariadb.com created
$ kubectl get po
NAME READY STATUS RESTARTS AGE
mariadb-operator-77559d7c49-gfczb 2/2 Running 0 102s
#Deploy DB1
$ cat db1.yaml
---
apiVersion: mariadb.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: example-mariadb
namespace: example-mariadb
spec:
masters: 1
replicas: 0
mariadb_password: CHANGEME
mariadb_image: mariadb:10.4
mariadb_pvc_storage_request: 1Gi
$ kubectl create ns example-mariadb
namespace/example-mariadb created
$ kubectl apply -f db1.yaml
mariadb.mariadb.mariadb.com/example-mariadb created
$ kubectl get po -n example-mariadb
NAME READY STATUS RESTARTS AGE
example-mariadb-0 1/1 Running 0 56s
#Test DB
$ kubectl -n example-mariadb run -it --rm mysql-client --image=arey/mysql-client --restart=Never -- -h example-mariadb-0.example-mariadb.example-mariadb.svc.cluster.local -u db_user -pCHANGEME -D db
If you don't see a command prompt, try pressing enter.
MariaDB [db]> show databases;
+--------------------+
| Database |
+--------------------+
| db |
| information_schema |
+--------------------+
2 rows in set (0.00 sec)
MariaDB [db]>
#Clean DB/Operator
$ kubectl delete -f db1.yaml
mariadb.mariadb.mariadb.com "example-mariadb" deleted
$ kubectl delete ns/example-mariadb
namespace "example-mariadb" deleted
$ kubectl delete -f https://raw.githubusercontent.com/adavarski/k8s-mariadb-ansible-operator/main/deploy/mariadb-operator.yaml
clusterrole.rbac.authorization.k8s.io "mariadb-operator" deleted
clusterrolebinding.rbac.authorization.k8s.io "mariadb-operator" deleted
serviceaccount "mariadb-operator" deleted
deployment.apps "mariadb-operator" deleted
customresourcedefinition.apiextensions.k8s.io "mariadbs.mariadb.mariadb.com" deleted