Deploy back-end api application with database to the kubernetes cluster with Jenkins CD
Tags: kubernetes, graphql, jenkins, mongodb
Published: 2018-11-03 07:01:54

---
title: Deploy back-end api application with database to the kubernetes cluster with Jenkins CD
published: true
description: Deploy back-end api application with database to the kubernetes cluster with Jenkins CD
tags: #kubernetes,#graphql,#jenkins,#mongodb
cover_image: https://thepracticaldev.s3.amazonaws.com/i/spig0qnx1jbiwcnls6nh.jpg
---

[gqlcreate]: https://thepracticaldev.s3.amazonaws.com/i/f5ooc7mf9sjsjk80zng3.png "Running graphql server"
[gqlquery]: https://thepracticaldev.s3.amazonaws.com/i/a7ku76k7rf9bm3hlekfv.png "User list"
[folder]: https://thepracticaldev.s3.amazonaws.com/i/32chi85vb33g66fjr71a.png "Folders"
[jenkins1]: https://thepracticaldev.s3.amazonaws.com/i/e6bcn552z36tsvhh9nlo.png "Pipeline"
[jenkins2]: https://thepracticaldev.s3.amazonaws.com/i/cnh2c1qzayuknhz126yu.png "Git repo and credential"
[jenkins3]: https://thepracticaldev.s3.amazonaws.com/i/fcabu5re3q0nalq7lm4y.png "build success"
[result]: https://thepracticaldev.s3.amazonaws.com/i/0ki3zdgvokc3ohpg6i1i.png "api result"

We will create a simple graphql api application that connects to mongodb. Then deploy to kubernetes cluster using Jenkins CD.

In addition, we will create ingress that support https so we can access this application securely.

This post is part of series ["Setup your own kubernetes cluster on VMs"](https://dev.to/pongsatt/setup-your-own-kubernetes-cluster-on-vms-5ln).

**Note:**
> This post assume that you follow from [previous post](https://dev.to/pongsatt/deploy-static-front-end-application-to-kubernetes-cluster-using-jenkins-cd-and-minio-with-automatic-https-cert-44gk)

##Prerequisite:##
- A running kubernetes cluster with storage-class support
- Jenkins server with pipeline support
- Docker private repository


#Build Application#

We will create a project called "simpleapi" which is a graphql with mongodb application using nodejs.

###1. Create project###

```
mkdir simpleapi
cd simpleapi
yarn init -y
```

###2. Build graphql api###

```
yarn add graphql-yoga mongodb
```

Create "db.js" at root folder with content below.

```
const mongoClient = require('mongodb').MongoClient

module.exports = {
    users: () => execute((coll) => coll.find().toArray()
        .then(results => results.map(idToString))),
    createUser: (name) => execute((coll) => coll.insertOne({ name })
        .then(result => idToString(result.ops[0])))
}

function execute(fn) {
    return mongoClient.connect(process.env.DB_URL || 'mongodb://localhost:27017/test')
        .then(client => {
            const result = fn(client.db().collection('users'))
            client.close()
            return result
        })
}

function idToString(doc) {
    const {_id, ...rest} = doc
    return {_id: _id.toString(), ...rest}
}
```

This program connects mongo at url "mongodb://localhost:27017/test" or url from environment name "DB_URL" if provided.

It also contains 2 functions for querying user list and create new user. We will use these functions in "index.js" below.


Create "index.js" at root folder with content below.

```
const { GraphQLServer } = require('graphql-yoga')
const db = require('./db')

const typeDefs = `
  type User {
      _id: ID
      name: String
  }

  type Query {
    users: [User]
  }

  type Mutation {
      createUser(name: String!): User!
  }
`

const resolvers = {
  Query: {
    users: (_, { }) => db.users()
  },
  Mutation: {
    createUser: (_, {name}) => db.createUser(name)
  }
}

const server = new GraphQLServer({ typeDefs, resolvers })
server.start(() => console.log('Server is running on http://localhost:4000'))

```

In this program, we define graphql schema with function to query user and user creation and start server at port 4000.

###3. Test graphql api###

**Note:**
> You need mongodb running at "mongodb://localhost:27017" to be able to test

Start server by running `node index.js` and open url "http://localhost:4000".

Create user.

![][gqlcreate]

Query user.

![][gqlquery]


#Add Jenkins pipeline to project#

The api application pipeline will need Dockerfile, kubernetes and Jenkins pipeline configurations.

###1. Dockerfile###

Here is the nodejs based Dockerfile configuration.

```
FROM node:8.11.0-alpine

WORKDIR /usr/src

COPY ./*.js package.json yarn.lock ./
RUN yarn install

EXPOSE 4000
CMD [ "node", "index.js"]
```

###1. Kubernetes for database###

Create file "k8s/db.yml" with content below.
```
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: simpleapi-db-claim
  annotations:
    volume.beta.kubernetes.io/storage-class: "ssdnfs"
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
---

apiVersion: v1
kind: ReplicationController
metadata:
  labels:
    name: simpleapi-db
  name: simpleapi-db
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: simpleapi-db
    spec:
      containers:
      - image: mongo
        name: mongo
        ports:
        - name: mongo
          containerPort: 27017
        volumeMounts:
            - name: mongo-persistent-storage
              mountPath: /data/db
      volumes:
        - name: mongo-persistent-storage
          persistentVolumeClaim:
            claimName: simpleapi-db-claim
---
apiVersion: v1
kind: Service
metadata:
  labels:
    name: simpleapi-db
  name: simpleapi-db
spec:
  ports:
    - port: 27017
      targetPort: 27017
  selector:
    app: simpleapi-db
```

This configuration will create Persistent Volume Claim that use storage class "ssdnfs" we created from [previous post](https://dev.to/pongsatt/setup-dynamic-storage-class-provisioner-based-on-nfs-1d4i). We also define mongodb instance and its service.

We can access this db within cluster using "simpleapi-db:27017".


###2. Kubernetes for application###

Create file "k8s/api.yml" with content.

```
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: simpleapi
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: simpleapi
    spec:
      containers:
        - name: simpleapi
          image: "<imageTag>"
          env:
          - name: DB_URL
            value: "mongodb://simpleapi-db:27017/test"
          ports:
          - name: http
            containerPort: 4000
          readinessProbe:
            httpGet:
              path: /
              port: 4000
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /
              port: 4000
            initialDelaySeconds: 3
            periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
  name: simpleapi
spec:
  ports:
    - name: http
      port: 80
      targetPort: 4000
  selector:
    app: simpleapi

```

This config composes of a Deployment and Service. We can access this application through service "simpleapi:80".

We also override database url with environment "DB_URL".

<imageTag> will be replaced in the build process since it's generated later.


###3. Kubernetes for ingress###

As promise, this api will be able to access through https via ingress configuration (Setup from [previous post](https://dev.to/pongsatt/set-up-nginx-ingress-with-letsencrypt-certificate-on-vms-or-bare-metal-kubernetes-cluster-j25)).

Create file "k8s/ingress.yml".

**Notes:**
> If you apply letsencrypt certificate using http01 protocol, you will need to uncomment/comment annotations.
> Replace "simpleapi.yourdomain.com" with your real domain name
> You will need to create DNS record for "simpleapi.yourdomain.com" so that it can apply for certificate (See [previous post](https://dev.to/pongsatt/deploy-static-front-end-application-to-kubernetes-cluster-using-jenkins-cd-and-minio-with-automatic-https-cert-44gk) for more detail)

```
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: simpleapi-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"

    # use http01 protocol. uncomment line below to use http01
    # kubernetes.io/tls-acme: "true"

    # use dns01 protocol. comment 2 lines below if use http01
    certmanager.k8s.io/acme-challenge-type: "dns01"
    certmanager.k8s.io/acme-dns01-provider: "aws-route53"

spec:
  tls:
  - hosts:
    - simpleapi.yourdomain.com
    secretName: simpleapi-tls
  rules:
  # The host must be identical to the above one
  - host: simpleapi.yourdomain.com
    http:
      paths:
      - path: /
        backend:
          # The name of your service
          serviceName: simpleapi
          servicePort: 80
```

###4. Jenkinsfile###

Create "Jenkinsfile" at the root folder.

**Notes:**
> Replace "192.168.1.105:8082" with your private registry
> You need to create "User/password" credential name "myregUserPass" to access your Docker repository in Jenkins

```
pipeline {
    environment {
        IMAGE_TAG = "${env.BUILD_NUMBER}"
        REGISTRY = "192.168.1.105:8082"
        APP = "simpleapi"
    }
    agent { label 'docker' }

    stages {
        stage('build and push image') {
            steps {
                script {
                    docker.withRegistry("http://${REGISTRY}", 'myregUserPass') {
                        docker.build("${APP}").push("${IMAGE_TAG}")
                    }
                }
            }
        }
        stage('deploy') {
            steps {
                sh("sed -i.bak 's|<imageTag>|${REGISTRY}/${APP}:${IMAGE_TAG}|' ./k8s/api.yml")
                sh("kubectl apply -f k8s/")
            }
        }

    }

}
```

There are 2 stages in this pipeline.

1. build and push image: build and push new image to docker private repo
2. deploy: replace <imageTag> and apply all yml files

At this point, your project folders should look like this.

![][folder]


###5. Publish project to git repository###

You can push to any git. We will use this to configure Jenkins in next step.


#Configure kubernetes for docker private registry#

Kubernetes cluster will need to know user and password in order to pull image from it. We can set it up by patching service account that pull the image, in this case, default service account.

###Create secret###

On your master node.

**Notes:**
> Replace <your-registry-server> with your private registry server
> Replace <your-name> <your-pword> <your-email> with your registry data

```
kubectl create secret docker-registry dockerconfig --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>

kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "dockerconfig"}]}'
```


#Create Jenkins Pipeline#

Now, we will create new pipeline project in Jenkins called "simpleapi".

![][jenkins1]


Set git repository and credential.

![][jenkins2]


Run build and wait until success.

![][jenkins3]

Run `kubectl get secret`. If you see "simpleapi-tls", means we can access our application through "https://simpleapi.yourdomain.com".

![][result]


#Summary#

This is the last post of this [series](https://dev.to/pongsatt/setup-your-own-kubernetes-cluster-on-vms-5ln). We learned how to setup cluster until create front and back end with ingress and dynamic storage. I hope these posts provide some values for you. Thanks.