Simple Nodejs application connecting to MySQL and with hooks to Docker Jenkins and Kubernetes running in Minikube. This application is part of the 'Simplify your life with Docker, Jenkins and Minikube' presentation at DevOps World 2020.
This project was initially created for a demo. If you see something that could be improved either in the code or this document please feel free to open a pull request.
All my code in this repo is under MIT license, so feel free to use as needed. For external libraries and images used in this repo, please refer to their own licensing terms.
In order to run the project in its entirety, you will need to have :
- A git account (I used Bitbucket)
- A docker repository (I used Docker hub)
- Docker installed in your machine ( I used Docker Desktop on my Mac)
- Minikube installed in your machine
Also, this document refers to the image in docker hub as bolbeck/dow2020simplelife. You should change this to your own image name so that it can run under your own docker hub account (otherwise you will not be able to push the image out).
The app has two parts:
-
MySQL database: based on the official MySQL image. The DB is initialized to have a test DB and a Test table with some sample dummy data. The initialization is used with the script in the mySqlInit directory. This is run only once and only if the data volume (./mySqlDB) is empty
-
Nodejs: app which is built from the
Dockerfile
in the nodeApp directory. The app has two entry points:- Root ("/") just pulls writes hello world and the hostname
- /mysql pull data from the test DB in MYSql and posts the JSON on the browser
Note that the application sends back pre-rendered page back to the client and uses pug as the rendering engine.
To bring up just the nodejs app:
- Go to the ./nodeApp directory
- Build the app:
docker build -t nodewithmysql_nodemysql .
- Run the container:
docker run -p 3000:3000 --env-file ./docker-node.env --name nodemysqlcont nodewithmysql_nodemysql
- Open
localhost:3000
in your browser
Note that this will bring up only the nodejs application and not the DB, so the app will fail if you try to access the second page (localhost:3000/mysql
)
If this is the first time you will try to bring up the application, you will need to create the node_modules
folder since that is not checked into source control.
If you have npm installed in your machine:
cd ./nodeApp
npm install
If you do not have npm installed in your machine, from the root folder of our repo (where we have the docker-compose file):
docker-compose run --rm nodemysql bash
npm install
exit
The above commands will:
- Start the nodemysql service defined in our docker-compose file and log you into the console in the container
- In the container, run npm install, which creates the node_modules folder in the container. Since we have a volume mounted in our container to the nodeApp folder in our machine (as defined in our dockercompose file), the node_modules folder gets created in our host machine as well and is ready for use.
- Exit the container and return to our host machine
Use docker-compose up
in the same directory where you have the docker-compose file to bring the application up . It uses a docker-compose.env
file to pass the environment variables to the mysql service (better than keeping them in the docker-compose.yaml file).
-
With the application running, login to the container with:
docker exec -it nodemysqlcont 'bash'
-
Run
npm test
-
To exit the container just use
exit
.
The application test scripts were created using mocha and chai.
Also, use npm run test-exp
to run some tests and export the results to a file in a format that Jenkins can understand. In this last case, the results will be saved to the ./test/results/test-results.xml file
During development, you may wantto restart the nodejs container. You can do this with: docker restart nodemysqlcont
Alternatively you can install something like nodemon in your image to monitor for changes in the file system.
Use docker-compose down
in the same directory where you have the docker-compose file to bring the application down.
To push this the node image to dockerhub, we will first need to tag it properly, based in the dockerhub account id. we can also give it a proper tag so that we can keep a history.
- login to dockerhub, tag image and push to docker hub:
docker login --username <dockerUserId>
docker tag nodewithmysql_nodemysql <dockerUserId>/simplenodemysql
docker push <dockerUserId>/simplenodemysql:latest
Note that you will need to change the name of the image to match your own docker hub account
The K8s manifests can be found in the ./Kubernetes
folder. they were created using Kompose and then tweaked to match the needs of the demo.
Kompose out of the box may not create exactly what you need, but gets you 80% - 90% there. The final modified files are in the Kubernetes folder already, but you could recreate the original output from Kompose using:
mkdir KubernetesOrig
cd KubernetesOrig
kompose --file docker-compose.yml convert
For individual manifests execute
kubectl apply -f <path/filename.yaml>
For all the manifests at once:
kubectl apply -f <Foldername>
Similarly to delete resources created by the manifests:
kubectl delete -f <path/filename.yaml>
or
kubectl delete -f <Foldername>
To see how the resources spin or down, use the dashboard
minikube dashboard
To find the url where the application is running:
minikube service list
Application uses a pipeline created using the Jenkinsfile in the application root directory. This file tells Jenkins what it needs to do. The steps required are basically:
Pull latest version from Bitbucket -> Build image -> Start container and run tests -> Deploy to Minikube
Note that since the Jenkins image does not come pre-installed with Kubectl, we build a custom image that has both Jenkins and Kubectl in the same image. The required Dockerfile can be found in the jenkinsWithK8s directory.
In order to authenticate to minikube, we need to copy some certificates from our local machine to our custom Jenkins image. For obvious reasons, these certificates are not checked in to source control. Here is what needs to be done to set them up:
- In the jenkinsWithK8s directory, create two directories kubeconfig and minikConfig
- Copy the contents from your machine's ~/.kube directory to the newly created kubeconfig directory (note that the path for the .kube directory given here is for a mac, it may be different in other OS)
- Copy the following certificates from your machine's ~/.minikube directory to the newly created minikConfig directory (note that the path for the .minikube directory given here is for a mac, it may be different in other OS):
- client.crt
- client.key
- ca.crt
Once the certificates are in these directories, the image will pick them up automatically
From within the jenkinsWithK8s folder:
mkdir kubeconfig
mkdir minikConfig
cp -r ~/.kube/* kubeconfig/
cp ~/.minikube/client.crt minikConf
cp ~/.minikube/client.key minikConf
cp ~/.minikube/ca.crt minikConfig
To run the Jenkins image:
cd ./jenkinsWithK8s
docker build -t jenk .
docker run -u root --rm -d -p 8080:8080 -p 50000:50000 --name jenkcont -v jenkins-data:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock jenk
After the image is running:
– Login on the web browser at localhost:8080
– Get the initial admin pwd :
docker exec -it jenkcont 'bash'
cd /var/jenkins_home/secrets/
cat initialAdminPassword
- Paste the admin password in the Jenkins UI at localhost:8080
- Install required plugins (the defaults should be fine)
- Create a new user
- Add credentials for Bitbucket and Docker Hub in the credentials section in Jenkins
- Create a new pipeline job that gets source code from the source control manager (SMC) and uses our Jenkins file.
- Description
- Build trigger (for our demo, we just scheduled to run every 10 mins)
- Pipeline from scm
- Location of the repo (bitbucket url)
- Credentials to bitbucket if using a private repository
- Location of the Jenkins file. In our project the jenkins file is in the root, so you can just enter jenkinsfile without a path.