<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>chat &#8211; Blog of Kliment Andreev &#8211; A place so I won&#039;t forget things</title>
	<atom:link href="https://blog.andreev.it/tag/chat/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.andreev.it</link>
	<description></description>
	<lastBuildDate>Mon, 02 Nov 2020 13:57:06 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>DevOps: CI/CD using git, Jenkins, Docker, Kubernetes</title>
		<link>https://blog.andreev.it/2019/11/devops-ci-cd-using-git-jenkins-docker-kubernetes/</link>
					<comments>https://blog.andreev.it/2019/11/devops-ci-cd-using-git-jenkins-docker-kubernetes/#comments</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Sun, 03 Nov 2019 21:34:00 +0000</pubDate>
				<category><![CDATA[Containers]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[chat]]></category>
		<category><![CDATA[containers]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[Jenkins]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=5869</guid>

					<description><![CDATA[In this post I&#8217;ll explain how to create a Node.js chat program, put the&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>In this post I&#8217;ll explain how to create a Node.js chat program, put the source on Github and then using Jenkins we&#8217;ll deploy the code to Docker Hub and from there to Docker containers that are managed by Kubernetes. There are many prerequisites for this. </p>
<ul>
&#8211; Linux workstation with Node.js and git installed (I&#8217;ll use CentOS 7)<br />
&#8211; Valid <a href="https://github.com/" rel="noopener noreferrer" target="_blank">GitHub </a>account. Create a repo called chitchat.<br />
&#8211; Valid <a href="https://hub.docker.com/" rel="noopener noreferrer" target="_blank">DockerHub </a>account. Create a repo called chitchat.<br />
&#8211; Jenkins server with a public IP, git and Docker installed (I&#8217;ll use CentOS 7)<br />
&#8211; Kubernetes cluster running Docker</ul>
<p>See my other posts on how to install <a href="https://blog.andreev.it/?p=5482" rel="noopener noreferrer" target="_blank">Jenkins </a>and <a href="https://blog.andreev.it/?p=5127" rel="noopener noreferrer" target="_blank">Kubernetes </a>cluster. </p>
<h1>The chat program</h1>
<p>For this purpose I&#8217;ve found a simple Node.js chat program that uses Express framework. The program can be found <a href="https://sabe.io/tutorials/how-to-build-real-time-chat-app-node-express-socket-io" rel="noopener noreferrer" target="_blank">here</a>, but we&#8217;ll modify it a bit. On a Linux workstation (I use CentOS, so some commands might differ), log as your regular user and create some necessary directories, a directory called chitchat and two subdirectories, public and test and two subdirectories for public, called js and css.</p>
<pre class="brush: bash; title: ; notranslate">
mkdir -p chitchat/test
mkdir -p chitchat/public/{js,css} 
cd chitchat
</pre>
<p>Create the source files for the chat program. First, let&#8217;s create the HTML. Click the (+) sign to expand. Just copy and paste everything. The first line (cat) will take care of the file creation.</p>
<pre class="brush: xml; collapse: true; light: false; title: ; toolbar: true; notranslate">
cat &lt;&lt; EOF &gt; index.html
&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Chit Chat App&lt;/title&gt;
        &lt;link href=&quot;css/styles.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
        &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width,minimum-scale=1,initial-scale=1&quot;&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;ul class=&quot;messages&quot;&gt;&lt;/ul&gt;
        &lt;form&gt;
            &lt;input type=&quot;text&quot; class=&quot;input&quot; autocomplete=&quot;off&quot; autofocus /&gt;
            &lt;button&gt;Send&lt;/button&gt;
        &lt;/form&gt;
        &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.dev.js&quot;&gt;&lt;/script&gt;
        &lt;script src=&quot;js/app.js&quot;&gt;&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;
EOF
</pre>
<p>Then, the Node.js code.</p>
<pre class="brush: jscript; collapse: true; light: false; title: ; toolbar: true; notranslate">
cat &lt;&lt; EOF &gt; index.js
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = process.env.PORT || 4141;
var path = require('path');

app.get('/', function(req, res){
    res.sendFile(__dirname + '/index.html');
});

app.use(express.static(path.join(__dirname + '/public')));


io.on('connection', function(socket) {

    socket.on('user_join', function(data) {
        this.username = data;
        socket.broadcast.emit('user_join', data);
    });

    socket.on('chat_message', function(data) {
        data.username = this.username;
        socket.broadcast.emit('chat_message', data);
    });

    socket.on('disconnect', function(data) {
        socket.broadcast.emit('user_leave', this.username);
    });
});

http.listen(port, function() {
    console.log('Listening on *:' + port);
});
EOF
</pre>
<p>Then, the actual code for the app.</p>
<pre class="brush: jscript; collapse: true; light: false; title: ; toolbar: true; notranslate">
cat &lt;&lt; EOF &gt; public/js/app.js
const form = document.querySelector(&quot;form&quot;);
const input = document.querySelector(&quot;.input&quot;);
const messages = document.querySelector(&quot;.messages&quot;);
const username = prompt(&quot;Please enter a nickname: &quot;, &quot;&quot;);
const socket = io();

form.addEventListener(&quot;submit&quot;, function(event) {
        event.preventDefault();

        addMessage(username + &quot;: &quot; + input.value);

        socket.emit(&quot;chat_message&quot;, {
                        message: input.value
        });

        input.value = &quot;&quot;;
        return false;
}, false);

socket.on(&quot;chat_message&quot;, function(data) {
        addMessage(data.username + &quot;: &quot; + data.message);
});

socket.on(&quot;user_join&quot;, function(data) {
        addMessage(data + &quot; just joined the chat!&quot;);
});

socket.on(&quot;user_leave&quot;, function(data) {
        addMessage(data + &quot; has left the chat.&quot;);
});

addMessage(&quot;You have joined the chat as '&quot; + username  + &quot;'.&quot;);
socket.emit(&quot;user_join&quot;, username);

function addMessage(message) {
        const li = document.createElement(&quot;li&quot;);
        li.innerHTML = message;
        messages.appendChild(li);
        window.scrollTo(0, document.body.scrollHeight);
}
EOF
</pre>
<p>And finally the CSS stylesheet.</p>
<pre class="brush: css; collapse: true; light: false; title: ; toolbar: true; notranslate">
cat &lt;&lt; EOF &gt; public/css/styles.css
body {
    margin: 0;
    font-family: sans-serif;
}

form {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    display: flex;
    box-sizing: border-box;
    padding: 0.25rem;
}

form input {
    border: 0;
    padding: 0.5rem;
    width: 100%;
    outline: 0;
    margin-right: 0.5rem;
    border-radius: 0.25rem;
    background: #ccc;
}

form button {
    width: 6rem;
    background-color: #1b8c00;
    color: white;
    border: none;
    padding: 0.5rem;
    cursor: pointer;
    border-radius: 0.25rem;
    text-transform: uppercase;
}

form button:hover {
    background-color: #166d01;
}

.messages {
    margin: 0;
    padding: 0;
    margin-bottom: 3rem;
}

.messages li {
    padding: 0.5rem;
}

.messages li:nth-child(odd) {
    background: #eee;
}
EOF
</pre>
<p>If you don&#8217;t have Node.js installed, then install it. If you already have Node.js, skip this step. While still logged as the regular user, sudo to install Node.js and npm and check the versions. Make sure you are in chitchat directory.</p>
<pre class="brush: bash; title: ; notranslate">
curl -sL https://rpm.nodesource.com/setup_10.x | sudo bash -
sudo yum -y install nodejs
node -v &amp;&amp; npm -v
</pre>
<p>Initiate a new project. Make sure you are in chitchat directory.</p>
<pre class="brush: bash; title: ; notranslate">
npm init
</pre>
<p>Answer the questions for the initialization. Just hit Enter for every question, we&#8217;ll replace this file later. Then install Express and socket.io.</p>
<pre class="brush: bash; title: ; notranslate">
npm install -save express
npm install -save socket.io
</pre>
<p>Finally, start the program. If you have a firewall enabled, you have to poke a hole.</p>
<pre class="brush: bash; title: ; notranslate">
sudo firewall-cmd --add-port=4141/tcp --zone=public --permanent
sudo firewall-cmd --reload
node index.js
</pre>
<p>The program should listen on port 4141. Open up a browser and access the Node.js app. (<strong>http://workstation_ip:4141</strong>).<br />
I&#8217;ve opened two windows and logged as UserA and UserB to test the functionality. It looks like this.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-01.png"><img fetchpriority="high" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-01.png" alt="" width="562" height="386" class="aligncenter size-full wp-image-5924" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-01.png 562w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-01-300x206.png 300w" sizes="(max-width: 562px) 100vw, 562px" /></a></p>
<h1>The GitHub repository</h1>
<p>Now that we have the program ready, let&#8217;s upload it on GitHub. Log with your account and create a new repository. In my case, I&#8217;ll name it chitchat.<br />
On your workstation, initiate the git repo, add the files, do the first commit and push the files to GitHub. It should look like this. Replace your username and the repo name in line 5.</p>
<pre class="brush: bash; highlight: [5]; title: ; notranslate">
cd chitchat
git init
git add .
git commit -m &quot;First commit.&quot;
git remote add origin https://github.com/&lt;your_username&gt;/&lt;your_repo&gt;
git push -u origin master
</pre>
<h1>Jenkins</h1>
<h2>Install Node.js plugin, Git and Docker</h2>
<p>If you installed Jenkins with the default settings, the Git and Docker plugins are probably installed. Node.js plugin is not installed by default so we&#8217;ll have to take care of that. Go to <strong>Manage Jenkins</strong> from the menu and then <strong>Manage Plugins</strong>. From the <strong>Available</strong> tab, find the NodeJS plugin and install it.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-03.png"><img decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-03.png" alt="" width="495" height="83" class="aligncenter size-full wp-image-5927" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-03.png 495w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-03-300x50.png 300w" sizes="(max-width: 495px) 100vw, 495px" /></a><br />
Again, go to <strong>Manage Jenkins</strong>, <strong>Global Tool Configuration</strong> and click on <strong>Add NodeJS</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/11/P138-16.jpg"><img decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/11/P138-16.jpg" alt="" width="438" height="116" class="aligncenter size-full wp-image-5965" srcset="https://blog.andreev.it/wp-content/uploads/2019/11/P138-16.jpg 438w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-16-300x79.jpg 300w" sizes="(max-width: 438px) 100vw, 438px" /></a><br />
Enter a name and choose the the latest 10.x version. Make sure that the <strong>Install automatically</strong> is checked. Also, I named my Node.js installation &#8220;<strong>node</strong>&#8221; and I&#8217;ll reference it as &#8220;<strong>node</strong>&#8221; further in the pipeline script. If you name your installation differently, you&#8217;ll have to change that in line 4 in the script below.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/11/P138-17.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/11/P138-17.png" alt="" width="769" height="410" class="aligncenter size-full wp-image-5986" srcset="https://blog.andreev.it/wp-content/uploads/2019/11/P138-17.png 769w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-17-300x160.png 300w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-17-768x409.png 768w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-17-585x312.png 585w" sizes="(max-width: 769px) 100vw, 769px" /></a><br />
Again, go to <strong>Manage Jenkins</strong> and then <strong>Manage Plugins</strong> in the center. Click on the <strong>Installed </strong>tab and look for Docker Pipeline plugin and Git plugin. They should be there and already installed (see the grayed checkmark on the left).<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/11/P138-18.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/11/P138-18.jpg" alt="" width="429" height="138" class="aligncenter size-full wp-image-5989" srcset="https://blog.andreev.it/wp-content/uploads/2019/11/P138-18.jpg 429w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-18-300x97.jpg 300w" sizes="(max-width: 429px) 100vw, 429px" /></a><br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/11/P138-19.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/11/P138-19.jpg" alt="" width="692" height="62" class="aligncenter size-full wp-image-5990" srcset="https://blog.andreev.it/wp-content/uploads/2019/11/P138-19.jpg 692w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-19-300x27.jpg 300w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-19-585x52.jpg 585w" sizes="(max-width: 692px) 100vw, 692px" /></a><a href="https://blog.andreev.it/wp-content/uploads/2019/11/P138-20.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/11/P138-20.jpg" alt="" width="679" height="61" class="aligncenter size-full wp-image-5991" srcset="https://blog.andreev.it/wp-content/uploads/2019/11/P138-20.jpg 679w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-20-300x27.jpg 300w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-20-585x53.jpg 585w" sizes="(max-width: 679px) 100vw, 679px" /></a><br />
If not, click on the <strong>Available </strong>tab, search for these plugins and install them similarly to what you did with Node.js plugin. Once installed, SSH to the Jenkins server and install Git first. We&#8217;ll install it from the source. This is for CentOS 7 only.</p>
<pre class="brush: bash; title: ; notranslate">
yum -y groupinstall &quot;Development Tools&quot;
yum -y install gettext-devel openssl-devel perl-CPAN perl-devel zlib-devel wget
wget https://github.com/git/git/archive/v2.23.0.tar.gz -O /tmp/git.tgz
cd /tmp
tar xzvf git.tgz 
cd git*
make configure
./configure
make install
cd /tmp
rm -Rf git*
</pre>
<p>Run <strong>git &dash;&dash;version</strong> to check if it works properly.<br />
Then, install Docker.</p>
<pre class="brush: bash; title: ; notranslate">
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum -y install docker-ce
systemctl enable docker
systemctl start docker
usermod jenkins -g docker
systemctl restart jenkins
</pre>
<p>Now, Jenkins can execute Docker commands.</p>
<h2>Jenkins pipeline</h2>
<p>Once installed from the main menu, in the upper-left corner, click on <strong>New Item</strong>. Enter the project name and choose <strong>Pipeline</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-02.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-02.png" alt="" width="468" height="299" class="aligncenter size-full wp-image-5926" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-02.png 468w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-02-300x192.png 300w" sizes="(max-width: 468px) 100vw, 468px" /></a><br />
Scroll down until you find the <strong>Pipeline script</strong> and paste this code. We just want to test pulling the repo from Github. Make sure you replace your username and repo name in line 10.</p>
<pre class="brush: bash; highlight: [10]; title: ; notranslate">
pipeline {
    agent any

    tools {nodejs &quot;node&quot;}

    stages {

        stage('Cloning Git') {
            steps {
                git 'https://github.com/&lt;your_name&gt;/&lt;your_repo&gt;'
            }
        }
    }
}
</pre>
<p>It looks like this.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-04.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-04.png" alt="" width="806" height="561" class="aligncenter size-full wp-image-5928" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-04.png 806w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-04-300x209.png 300w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-04-768x535.png 768w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-04-585x407.png 585w" sizes="(max-width: 806px) 100vw, 806px" /></a><br />
Click on <strong>Save </strong>and once back, click on <strong>Build Now</strong> from the menu.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-05.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-05.png" alt="" width="257" height="375" class="aligncenter size-full wp-image-5929" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-05.png 257w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-05-206x300.png 206w" sizes="(max-width: 257px) 100vw, 257px" /></a><br />
Once completed, you&#8217;ll see that everything is running OK.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-06.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-06.png" alt="" width="604" height="303" class="aligncenter size-full wp-image-5930" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-06.png 604w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-06-300x150.png 300w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-06-585x293.png 585w" sizes="(max-width: 604px) 100vw, 604px" /></a><br />
Click on <strong>Console Output</strong> to see the actual logs from git.<br />
Now, that we have everything verified, we want to add another step of testing the application. We&#8217;ll have to install the pre-requisites for the Node.js and create a test scenario. On the workstation where your project is, create a test scenario. We&#8217;ll use <a href="https://mochajs.org/" rel="noopener noreferrer" target="_blank">mocha</a> and <a href="https://www.chaijs.com/" rel="noopener noreferrer" target="_blank">chai</a> for this. </p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; EOF &gt; test/test.js
var chai = require('chai');
var io = require('socket.io-client')
var app = require('../index');
var client = io('http://localhost:4141');

console.log('Server started...Waiting for client connection...');
it('Client connected...', function(done) {
  client.on('connect', function (data) {
    done();
  });
});
EOF
</pre>
<p>We also need to define the pre-requisites. Change some of the lines here to suit your needs. This is the file that we are replacing, the one that <strong>npm init</strong> creates.</p>
<pre class="brush: xml; title: ; notranslate">
cat &lt;&lt; EOF &gt; package.json
{
  &quot;name&quot;: &quot;chitchat&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;A simple chat program&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;node index.js&quot;,
    &quot;test&quot;: &quot;mocha -R spec test/test.js --exit&quot;
  },
  &quot;author&quot;: &quot;Kliment Andreev&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;dependencies&quot;: {
    &quot;express&quot;: &quot;^4.17.1&quot;,
    &quot;socket.io&quot;: &quot;^2.3.0&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;chai&quot;: &quot;^4.2.0&quot;,
    &quot;mocha&quot;: &quot;^5.2.0&quot;
  }
}
EOF
</pre>
<p>As you can see the first file goes under chitchat/test directory and the package.json file is in the root of chitchat directory. Don&#8217;t forget to push these files to GitHub.</p>
<pre class="brush: bash; title: ; notranslate">
git add .
git commit -m &quot;Added package.json and test unit&quot;
git push -u origin master
</pre>
<p>Now, go back to your project in Jenkins (click <strong>Configure </strong>on the left) and replace the pipeline script with this one. Make sure you change your name and repo in line 10.</p>
<pre class="brush: bash; collapse: true; highlight: [10]; light: false; title: ; toolbar: true; notranslate">
pipeline {
    agent any

    tools {nodejs &quot;node&quot;}

    stages {

        stage('Cloning Git') {
            steps {
                git 'https://github.com/&lt;your_name&gt;/&lt;your_repo&gt;'
            }
        }

        stage('Install dependencies') {
            steps {
                sh 'npm install'
            }
        }

        stage('Test') {
            steps {
                sh 'npm test'
            }
        }
    }
}
</pre>
<p>Click on <strong>Build Now</strong> and you&#8217;ll see that all stages completed fine. But, we want to go a step further and automate the build process anytime we make a change in the source code. In order to do that we need a webhook between GitHub and our Jenkins server. Make sure your Jenkins server is publicly available, otherwise this integration won&#8217;t work.<br />
But before we do that, let&#8217;s add the script above as a file under our source code for the chat program. Make sure you change your name and repo in line 11. This file goes in the root of the chitchat directory.</p>
<pre class="brush: bash; collapse: true; highlight: [11]; light: false; title: ; toolbar: true; notranslate">
cat &lt;&lt; EOF &gt; Jenkinsfile
pipeline {
    agent any

    tools {nodejs &quot;node&quot;}

    stages {

        stage('Cloning Git') {
            steps {
                git 'https://github.com/&lt;your_name&gt;/&lt;your_repo&gt;'
            }
        }

        stage('Install dependencies') {
            steps {
                sh 'npm install'
            }
        }

        stage('Test') {
            steps {
                sh 'npm test'
            }
        }
    }
}
EOF
</pre>
<p>Add this file to Github.</p>
<pre class="brush: bash; title: ; notranslate">
git add .
git commit -m &quot;Added Jenkinsfile&quot;
git push -u origin master
</pre>
<h1>GitHub integration with Jenkins</h1>
<p>Log in to Github and from the upper right corner, click on your avatar and click on <strong>Settings</strong>. Then on the menu on the left side, all the way to the bottom click on <strong>Developer settings</strong>. Click on <strong>Personal access tokens</strong> and click on <strong>Generate new token</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-07.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-07.png" alt="" width="792" height="208" class="aligncenter size-full wp-image-5940" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-07.png 792w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-07-300x79.png 300w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-07-768x202.png 768w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-07-585x154.png 585w" sizes="(max-width: 792px) 100vw, 792px" /></a><br />
Give a name to your token (I&#8217;ll name mine Jenkins Token) and click on <strong>admin:repo_hook</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-08.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-08.png" alt="" width="458" height="100" class="aligncenter size-full wp-image-5942" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-08.png 458w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-08-300x66.png 300w" sizes="(max-width: 458px) 100vw, 458px" /></a><br />
Click on <strong>Generate token</strong> after. You&#8217;ll get a hex token, click on the clipboard icon next to it. You&#8217;ll need this token for the Jenkins configuration.<br />
Go to Jenkins and click on <strong>Manage Jenkins</strong> on the left followed by <strong>Configure System</strong> in the middle. Scroll down to the Git section, click on <strong>Add GitHub</strong> server, type <strong>GitHub </strong>for <strong>Name </strong>and next to <strong>Credentials </strong>click on <strong>Add</strong>, then <strong>Jenkins</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-09.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-09.png" alt="" width="1515" height="382" class="aligncenter size-full wp-image-5943" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-09.png 1515w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-09-300x76.png 300w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-09-768x194.png 768w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-09-1024x258.png 1024w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-09-1170x295.png 1170w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-09-585x148.png 585w" sizes="(max-width: 1515px) 100vw, 1515px" /></a><br />
When this window shows up, change the <strong>Kind </strong>to <strong>Secret text</strong>. Paste the token under <strong>Secret </strong>and enter something for <strong>ID </strong>and <strong>Description</strong>. Click <strong>Add</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-10.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-10.png" alt="" width="627" height="505" class="aligncenter size-full wp-image-5944" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-10.png 627w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-10-300x242.png 300w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-10-585x471.png 585w" sizes="(max-width: 627px) 100vw, 627px" /></a><br />
Once back, select the credential that we just created and click on <strong>Test connection</strong> on the right. You should get a message that the connection is fine. Make sure that <strong>Manage hooks</strong> is checked. Click on <strong>Save </strong>at the bottom once everything is OK.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-11.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-11.jpg" alt="" width="1113" height="230" class="aligncenter size-full wp-image-5948" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-11.jpg 1113w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-11-300x62.jpg 300w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-11-768x159.jpg 768w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-11-1024x212.jpg 1024w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-11-585x121.jpg 585w" sizes="(max-width: 1113px) 100vw, 1113px" /></a><br />
Go back to your project in Jenkins and click <strong>Configure </strong>on the left. Scroll down where the <strong>Pipeline </strong>is and change the <strong>Definition </strong>from <strong>Pipeline </strong>script to <strong>Pipeline script from SCM</strong>. SCM means Source Code Management. Change <strong>SCM </strong>to <strong>Git</strong>, enter the repo URL under <strong>Repositories </strong>. If your repo is not public, you&#8217;ll have to specify some credentials under <strong>Credentials</strong>. If your repo is public, you can leave them as <strong>none</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-12.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-12.png" alt="" width="1395" height="413" class="aligncenter size-full wp-image-5950" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-12.png 1395w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-12-300x89.png 300w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-12-768x227.png 768w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-12-1024x303.png 1024w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-12-1170x346.png 1170w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-12-585x173.png 585w" sizes="(max-width: 1395px) 100vw, 1395px" /></a><br />
Then scroll up a bit and check <strong>GitHub hook trigger for GitScm polling</strong> and then click <strong>Save</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-13.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-13.png" alt="" width="420" height="306" class="aligncenter size-full wp-image-5952" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-13.png 420w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-13-300x219.png 300w" sizes="(max-width: 420px) 100vw, 420px" /></a><br />
If you go back to GitHub, click on your project and all the way on the right click on <strong>Settings</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-14.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-14.png" alt="" width="812" height="114" class="aligncenter size-full wp-image-5953" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-14.png 812w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-14-300x42.png 300w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-14-768x108.png 768w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-14-585x82.png 585w" sizes="(max-width: 812px) 100vw, 812px" /></a><br />
Then click on <strong>Webhooks </strong>and you&#8217;ll see a green checkmark next to your Jenkins URL.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/10/P138-15.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/10/P138-15.png" alt="" width="1007" height="283" class="aligncenter size-full wp-image-5954" srcset="https://blog.andreev.it/wp-content/uploads/2019/10/P138-15.png 1007w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-15-300x84.png 300w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-15-768x216.png 768w, https://blog.andreev.it/wp-content/uploads/2019/10/P138-15-585x164.png 585w" sizes="(max-width: 1007px) 100vw, 1007px" /></a><br />
Go to your workstation and edit the index.html file. Change the line 4 for example so instead of Chit Chat App, replace the blank with a hyphen. e.g. Chit Chat to Chit-Chat. Save it and push to GitHib.</p>
<pre class="brush: bash; title: ; notranslate">
git add .
git commit -m &quot;Minor change in index.html&quot;
git push -u origin master
</pre>
<p>If you go back to Jenkins, you&#8217;ll see that there is a build there triggered automatically. This part is <strong>CI (Continous Integration)</strong>. But, let&#8217;s go further and expand our CI with creating a Docker image.</p>
<h1>Docker image and Docker Hub</h1>
<p>In order to create a Docker image, we need a <strong>Dockerfile</strong>. I won&#8217;t explain the meaning of each line in the Dockerfile, so just copy &#038; paste the following snippet in the chitchat directory.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; EOF &gt; Dockerfile
FROM node:10
WORKDIR /usr/src/app
COPY package*.json ./

RUN npm install
COPY . .

EXPOSE 4141
CMD &#x5B; &quot;npm&quot;, &quot;start&quot; ]
EOF
</pre>
<p>This file will tell Docker exactly what to do when creating the image. But, we also have to tell Jenkins that we need to build the image, test the image and deploy it to Docker Hub. So, your new Jenkinsfile will look like this. Change the URLs in lines 6 and 7 to suit your needs. Before you deploy the script, go to Jenkins, click on <strong>Credentials </strong>on the left, click on <strong>System </strong>below, click on <strong>Global credentials (unrestricted)</strong> in the middle and then <strong>Add Credentials</strong> on the left. Fill out the form with your Docker Hub credentials. Whatever you put as ID for these username/password pair is what you have to put in line 8 in the script.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/11/P138-21.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/11/P138-21.jpg" alt="" width="524" height="369" class="aligncenter size-full wp-image-5999" srcset="https://blog.andreev.it/wp-content/uploads/2019/11/P138-21.jpg 524w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-21-300x211.jpg 300w" sizes="(max-width: 524px) 100vw, 524px" /></a><br />
And, now copy &#038; paste the script in the chitchat directory. If everything goes well, you&#8217;ll see your image deployed in Docker Hub and tagged as whatever your latest build was and the tag latest too.</p>
<pre class="brush: bash; highlight: [6,7]; title: ; notranslate">
cat &lt;&lt; 'EOF' &gt; Jenkinsfile
pipeline {

    environment {
        dockerregistry = 'https://registry.hub.docker.com'
        dockerhuburl = &quot;klimenta/chitchat&quot;
        githuburl = &quot;klimenta/chitchat&quot;
        dockerhubcrd = 'dockerhub'
    }

    agent any

    tools {nodejs &quot;node&quot;}

    stages {

        stage('Clone git repo') {
            steps {
                git 'https://github.com/' + githuburl
            }
        }

        stage('Install Node.js dependencies') {
            steps {
                sh 'npm install'
            }
        }

        stage('Test App') {
            steps {
                sh 'npm test'
            }
        }

        stage('Build image') {
          steps{
            script {
              dockerImage = docker.build(dockerhuburl + &quot;:$BUILD_NUMBER&quot;)
            }
          }
        }

        stage('Test image') {
            steps {
                sh 'docker run -i ' + dockerhuburl + ':$BUILD_NUMBER npm test'
            }
        }

        stage('Deploy image') {
          steps{
            script {
              docker.withRegistry(dockerregistry, dockerhubcrd ) {
                dockerImage.push(&quot;${env.BUILD_NUMBER}&quot;)
                dockerImage.push(&quot;latest&quot;)
              }
            }
          }
        }

        stage('Remove image') {
          steps{
            sh &quot;docker rmi $dockerhuburl:$BUILD_NUMBER&quot;
          }
        }
    }
}
EOF
</pre>
<p>Of course, you&#8217;ll have to tell GitHub that there is a change is a file.</p>
<pre class="brush: bash; title: ; notranslate">
git add .
git commit -m &quot;Added Jenkinsfile for Docker&quot;
git push -u origin master
</pre>
<h1>Deployment to Kubernetes using Jenkins</h1>
<p>Now that we are finished with the CI (Continuous Integration), it&#8217;s time for the CD (Continuous Deployment). We&#8217;ll use the existing Docker image that we put on Docker Hub. First thing first, we&#8217;ll have to install a plugin for a Kubernetes integration. Go to <strong>Manage Jenkins</strong> on the left, then click on <strong>Manage plugins</strong> in the center and click on <strong>Available </strong>tab. Install the <strong>Kubernetes Continuous Deploy</strong> plugin.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/11/P138-22.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/11/P138-22.jpg" alt="" width="510" height="71" class="aligncenter size-full wp-image-6003" srcset="https://blog.andreev.it/wp-content/uploads/2019/11/P138-22.jpg 510w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-22-300x42.jpg 300w" sizes="(max-width: 510px) 100vw, 510px" /></a><br />
Once installed and Jenkins is restarted, click on <strong>Credentials </strong>on the left, then <strong>System </strong>just below and then click on <strong>Global credentials (unrestricted)</strong> in the middle. Click on <strong>Add Credentials</strong> on the left. When this window pops up, change the <strong>Kind </strong>to <strong>Kubernetes configuration (kubeconfig)</strong>, enter an <strong>ID</strong>, in my case it&#8217;s k8s. Remember the <strong>ID</strong>, you&#8217;ll reference it in the <strong>Jenkinsfile </strong>later, type a description and select <strong>Enter directly</strong>. Before you click OK, we&#8217;ll have to go to our Kubernetes cluster and log to it.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/11/P138-23.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/11/P138-23.jpg" alt="" width="627" height="509" class="aligncenter size-full wp-image-6004" srcset="https://blog.andreev.it/wp-content/uploads/2019/11/P138-23.jpg 627w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-23-300x244.jpg 300w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-23-585x475.jpg 585w" sizes="(max-width: 627px) 100vw, 627px" /></a><br />
Once you are logged to your master node, log as the user that you use to manage the k8s cluster and run this command.</p>
<pre class="brush: bash; title: ; notranslate">
cat .kube/config
</pre>
<p>You&#8217;ll get a long list of text. Paste this back in the Jenkins window under <strong>Content</strong> and click OK. This is how Jenkins will communicate with your Kubernetes cluster. We also need to tell Kubernetes how we want our app deployed. As you know this is done with a YAML file. So, we have to add that YAML file under the chitchat directory.Change the IP in line 25 to match your master k8s node IP. I am deploying a load balancer that will listen on port 8080 and 2 pods in a deployment.</p>
<pre class="brush: xml; title: ; notranslate">
cat &lt;&lt; 'EOF' &gt; k8s.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: chitchat
spec:
  replicas: 2
  template:
    metadata:
      name: chitchat
      labels:
        app: chitchat
    spec:
      containers:
      - image: $dockerhuburl:$BUILD_NUMBER
        name: nodejs
---
apiVersion: v1
kind: Service
metadata:
  name: loadbalancer
spec:
  type: LoadBalancer
  externalIPs:
  - 192.168.1.7
  selector:
    app: chitchat
  ports:
  - port: 8080
    targetPort: 4141
EOF
</pre>
<p>And we also want to tell Jenkins that we are adding an extra step to deploy it to the cluster, so the final Jenkins file looks like this.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; 'EOF' &gt; Jenkinsfile
pipeline {

  environment {
    dockerregistry = 'https://registry.hub.docker.com'
    dockerhuburl = 'klimenta/chitchat'
    githuburl = 'klimenta/chitchat'
    dockerhubcrd = 'dockerhub'
    dockerImage = ''
  }

  agent any

  tools {nodejs &quot;node&quot;}

  stages {

    stage('Clone git repo') {
      steps {
         git 'https://github.com/' + githuburl
      }
    }

    stage('Install Node.js dependencies') {
      steps {
        sh 'npm install'
      }
    }

    stage('Test App') {
        steps {
            sh 'npm test'
        }
    }

    stage('Build image') {
      steps{
        script {
          dockerImage = docker.build(dockerhuburl + &quot;:$BUILD_NUMBER&quot;)
        }
      }
    }

    stage('Test image') {
      steps {
        sh 'docker run -i ' + dockerhuburl + ':$BUILD_NUMBER npm test'
      }
    }

    stage('Deploy image') {
      steps{
        script {
          docker.withRegistry(dockerregistry, dockerhubcrd ) {
            dockerImage.push(&quot;${env.BUILD_NUMBER}&quot;)
            dockerImage.push(&quot;latest&quot;)
          }
        }
      }
    }

    stage('Remove image') {
      steps{
        sh &quot;docker rmi $dockerhuburl:$BUILD_NUMBER&quot;
      }
    }

    stage('Deploy k8s') {
      steps {
        kubernetesDeploy(
          kubeconfigId: 'k8s',
          configs: 'k8s.yaml',
          enableConfigSubstitution: true
        )
      }
    }
  }
}
EOF
</pre>
<p>Trigger the change.</p>
<pre class="brush: bash; title: ; notranslate">
git add .
git commit -m &quot;Added Jenkinsfile for kubernetes and yaml&quot;
git push -u origin master
</pre>
<p>After a minute or so, you&#8217;ll see your pods running and if you go to your master node on port 8080 you&#8217;ll see the app happily running.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2019/11/P138-24.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2019/11/P138-24.jpg" alt="" width="424" height="344" class="aligncenter size-full wp-image-6025" srcset="https://blog.andreev.it/wp-content/uploads/2019/11/P138-24.jpg 424w, https://blog.andreev.it/wp-content/uploads/2019/11/P138-24-300x243.jpg 300w" sizes="(max-width: 424px) 100vw, 424px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2019/11/devops-ci-cd-using-git-jenkins-docker-kubernetes/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>
