<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Burhan Deniz Abdi - The tech guy]]></title><description><![CDATA[Tech blog about open source tools, containers, Docker, Kubernetes, Cloud, AWS, Prometheus, Monitoring]]></description><link>https://burhan.io/</link><image><url>https://burhan.io/favicon.png</url><title>Burhan Deniz Abdi - The tech guy</title><link>https://burhan.io/</link></image><generator>Ghost 3.0</generator><lastBuildDate>Tue, 16 Jun 2026 14:31:58 GMT</lastBuildDate><atom:link href="https://burhan.io/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Flask application monitoring with Prometheus]]></title><description><![CDATA[Monitoring a flask application with Prometheus on Kubernetes]]></description><link>https://burhan.io/flask-application-monitoring-with-prometheus-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c6b</guid><category><![CDATA[monitoring]]></category><category><![CDATA[kubernetes]]></category><category><![CDATA[flask]]></category><category><![CDATA[python]]></category><category><![CDATA[prometheus]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Sat, 02 Sep 2017 19:08:57 GMT</pubDate><media:content url="https://burhan.io/content/images/2017/09/prom-kub.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h4 id="intro">Intro</h4>
<img src="https://burhan.io/content/images/2017/09/prom-kub.png" alt="Flask application monitoring with Prometheus"><p>Prometheus is an open source project that is part of the Cloud Native Computing Foundation(CNCF). It serves as a monitoring system and time series database. The project was second to be part of CNCF after <a href="https://github.com/prometheus/prometheus">Kubernetes</a>. As such the two project play really nicely with one another.</p>
<p>In this post I'll take us through using Kubernetes(K8s for short) as our deployment platform for Prometheus, then setting up a simple python app for monitoring. I recommend checking out my previous post on standing up K8s on AWS first, <a href="http://burhan.io/kubernetes-on-aws/">here</a>. Otherwise if you already have a K8s cluster, lets get started.</p>
<h4 id="prerequisites">Prerequisites</h4>
<ul>
<li>A running Kubernetes cluster</li>
<li>Configured kubectl client</li>
</ul>
<h4 id="architecture">Architecture</h4>
<p>Not going to do a complete architecture run through here, but I want to mention some of the basics.</p>
<p>Prometheus takes a particular stance on how it goes about monitoring. Most monitoring systems out there rely on the use of an agent, that sits on the client machine. This agent will gather metric from the host and shoot them over to the monitoring system. This can be seen as a push method.</p>
<p>Prometheus takes more of a pull method to this. The idea behind this is that you tell Prometheus what it needs to monitor, and it will go out and start scraping that endpoint for metric.</p>
<p>This approach provides a lot of flexibility as application KPIs can be added/modified/removed very quickly and provides a modern devops approach to application monitoring. It also means application monitoring is driven by the individuals/teams who understand the application(to be monitored) best.</p>
<h4 id="jumpin">Jump in</h4>
<p>If you want to jump straight in, deploy the following in your k8s cluster:</p>
<ul>
<li>
<p><a href="https://hub.docker.com/r/vect0r/python-app/">python-app</a> - simple python app that incorporates Prometheus monitoring. Metrics provides by app: request_processing_seconds, index_request_processing_seconds, requests_for_host.</p>
</li>
<li>
<p><a href="https://hub.docker.com/r/prom/prometheus">Prometheus</a> - The official Prometheus image.</p>
</li>
<li>
<p><a href="https://github.com/V3ckt0r/Flask-application-monitoring-with-Prometheus">Assets</a> - All assets described in the post can be retrieved from this repo</p>
</li>
</ul>
<h4 id="deployprometheus">Deploy Prometheus</h4>
<p>Deploying Prometheus into K8s is super easy. Lets take a look at the deployment file deployment.yml.</p>
<pre><code>apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: prometheus-deployment-1.7.1
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: prometheus-server
    spec:
      containers:
        - name: prometheus
          image: prom/prometheus:v1.7.1
          args:
            - &quot;-config.file=/etc/prometheus/conf/prometheus.yml&quot;
            # Metrics are stored in an emptyDir volume which
            # exists as long as the Pod is running on that Node.
            # The data in an emptyDir volume is safe across 
            # container crashes.
            - &quot;-storage.local.path=/prometheus/&quot;
          ports:
            - containerPort: 9090
          volumeMounts:
            - name: prometheus-config-volume
              mountPath: /etc/prometheus/conf/
            - name: prometheus-storage-volume
              mountPath: /prometheus/
      volumes:
        # The config map we will create then bind to our volume mount.
        - name: prometheus-config-volume
          configMap:
            name: prometheus-server-conf
        # Create the actual volume for the metric data
        - name: prometheus-storage-volume
          emptyDir: {} # containers in the Pod can all read and write the same files here.
</code></pre>
<p>Before we deploy this let first create the configmap for the prometheus config. A configmap is a way we can decouple and store configuration files for use in different deployments. Lets looks at the prometheus.yml config.</p>
<pre><code>global:
  scrape_interval: 5s
  evaluation_interval: 5s

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'python-app'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_label_app]
        regex: python-app
        action: keep
</code></pre>
<p>Here we have setup two scrape jobs. One is simply telling Prometheus to scrape itself. The other is scraping our example python-app. Take notice of the <a href="https://prometheus.io/docs/operating/configuration/#%3Ckubernetes_sd_config%3E%3E">kubernetes_sd_configs</a> directive, very handy for using the kubernetes api to find scrape targets. Here we are using the pods role to discover all pods with an app label of 'python-app'. Also it is worth mentioning that by default Prometheus will scrape the /metrics endpoint unless stated otherwise.</p>
<p>We can create the configmap like so:</p>
<pre><code>kubectl create configmap prometheus-server-conf --from-file=prometheus.yml
</code></pre>
<p>Now that we have created that we can create our prometheus deployment.</p>
<pre><code>kubectl create -f deployment.yml
</code></pre>
<p>Once you have deployed Prometheus you will be able to see your deployment via</p>
<pre><code>kubectl get deploy
NAME                          DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
prometheus-deployment-1.7.1   1         1         1            1           40s
</code></pre>
<p>Now that Prometheus has been deployed, we need a method of accessing it. For this we will need to create a service(svc). A service manifest file looks like so:</p>
<pre><code>apiVersion: v1
kind: Service
metadata:
  name: prometheus-service
spec:
  selector: # exposes any pods with the following labels as a service
    app: prometheus-server
  type: NodePort
  ports:
    - port: 80 # this Service's port (cluster-internal IP clusterIP)
      targetPort: 9090 # pods expose this port
      # Kubernetes master will allocate a port from a flag-configured range (default: 30000-32767),
      # or we can set a specific port number (in our case).
      # Each node will proxy 32514 port (the same port number on every node) into this service.
      # Note that this Service will be visible as both NodeIP:nodePort and clusterIp:port
      nodePort: 32514
</code></pre>
<p>We can deploy a service in a similar fashion as the deployment:</p>
<pre><code>kubectl create -f svc.yml
</code></pre>
<p>Now that we have a service for prometheus we can access it simply by hitting the node on port 32514 as we specified. Take note, if you are using aws then use the public ip of the instance (make sure your security group allows you access over this port).</p>
<h4 id="deploypythonapp">Deploy Python-app</h4>
<p>Now that Prometheus has been deployed we can now deploy our simple python app and start scraping some metric in. Our deployment file my-app.yml:</p>
<pre><code>apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: python-app
spec:
  replicas: 1
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: python-app
    spec:
      containers:
      - name: python-app
        image: vect0r/python-app:latest
        ports:
        - containerPort: 5000
          name: web-app
</code></pre>
<p>Lets deploy this:</p>
<pre><code>kubectl create -f my-app.yml
</code></pre>
<p>As before we'll create a service manifest, my-app-svc.yml:</p>
<pre><code>apiVersion: v1
kind: Service
metadata:
  name: my-app-svc
spec:
  selector: # exposes any pods with the following labels as a service
    app: python-app
  type: NodePort
  ports:
    - port: 5000 # this Service's port (cluster-internal IP clusterIP)
      targetPort: 5000 # pods expose this port
</code></pre>
<p>Notice here we haven't specified a nodePort. Kubernetes will pick one out for us. Create the service:</p>
<pre><code>kubectl create -f my-app-svc.yml
kubectl get svc
NAME                  CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
prometheus-service    100.69.192.22   &lt;nodes&gt;       80:32514/TCP                    13d
my-app-svc            100.69.92.128   &lt;nodes&gt;      5000:32455/TCP   12d
</code></pre>
<p>Listing the services displays the nodePort selected, we can see the python app can be accessed on 32455. Remember back when we created our configmap for prometheus. We specified in our config to find pods with the 'python-app' app tag and scrape them. Hence now we have done this we should see the following targets in Prometheus.<br>
<img src="https://burhan.io/content/images/2017/09/prometheus_targets.png" alt="Flask application monitoring with Prometheus"></p>
<h4 id="appmonitoring">App monitoring</h4>
<p>We have two views we are monitoring in our web app. These are the '/' and '/host' endpoints. Lets take a look at our index view:</p>
<pre><code># Create a metric to track time spent and requests made.
INDEX_TIME = Summary('index_request_processing_seconds', 'DESC: INDEX time spent processing request')

# Create a metric to count the number of runs on process_request()
c = Counter('requests_for_host', 'Number of runs of the process_request method', ['method', 'endpoint'])

@app.route('/')
@INDEX_TIME.time()
def hello_world():
    path = str(request.path)
    verb = request.method
    label_dict = {&quot;method&quot;: verb,
                 &quot;endpoint&quot;: path}
    c.labels(**label_dict).inc()

    return 'Flask Dockerized'
</code></pre>
<p>This block of the code is doing serveral things, firstly we are creating a Summary metric type. This is one of the metric type the prometheus_client supports. The first parameter is the name of the metric, the second is a description text for the metric. The second metric we create is a Counter, similar to the Summary the first two parameters are name and description. However a third parameter is provided, this allows us to provide tags to differentiate targets. Think of it as allowing you to create another line on the graph.</p>
<p>Then we create our view using the flask decorator, notice we also use INDEX_TIME as a decorator and using the time() method to give us the time taken to execute this method. The view itself is just returning 'Flask Dockerized' to the webpage, however note that we are using flask to get the path requested and the HTTP verb used. We save this to a dictionary and pass it through to our counter.</p>
<h4 id="exposemetrics">Expose metrics</h4>
<p>How do we expose this to Prometheus? Well as mentioned before the Prometheus by default looks at the /metrics endpoint for all it's scraping. So lets build that endpoint into our app.</p>
<pre><code>@app.route('/metrics')
def metrics():
    return Response(generate_latest(), mimetype=CONTENT_TYPE_LATEST)
</code></pre>
<p>What we have done here is simply used Flask's routing mechanism to create the metrics endpoint. This view is returning a Flask Response object. the generate_latest() method is provided by the Prometheus client and this will provide a string with all the metrics defined in the app and their values, in the Prometheus standard. We wrap this in the Response object that is passed back to the client. Output looks like below:<br>
<img src="https://burhan.io/content/images/2017/09/output.png" alt="Flask application monitoring with Prometheus"><br>
Every time Prometheus scrapes this app, this block of code will run providing Prometheus with the latest metrics values, which it stores in its TSDB.</p>
<h4 id="graphinaction">Graph in action</h4>
<p>Lets take a look at what the code does for us. Navigate to your Prometheus UI and search on our metric 'index_request_processing_seconds', you will notice there is a count and sum. Every time you hit the index endpoint you will see both these metrics change. The count will show you how many times the view has been called, the sum will show you the total execution time taken by this method. You can see this in action below:<br>
<img src="https://burhan.io/content/images/2017/09/count.png" alt="Flask application monitoring with Prometheus"></p>
<p>The Counter metric we created can be found by searching requests_for_host.<br>
<img src="https://burhan.io/content/images/2017/09/count1-1.png" alt="Flask application monitoring with Prometheus"><br>
Here notice we have two lines one for our '/index' view and another for our '/hosts' view.</p>
<h4 id="wrapup">Wrap up</h4>
<p>So we've gone go over deploying Prometheus on Kubernetes and using the Prometheus_client to monitor our app. This has only scratched the surface of what you can do. I hope this has help you kick start your use of Prometheus.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Custom Sumologic alerting with Webhooks and AWS]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h5 id="intro">Intro</h5>
<p>So I've been using Sumologic for a while now. Its a great way to get up and running with centralised log monitoring very quickly. No need to install and maintain an ELK cluster. Just sign up and start throwing it logs and configure your dashboards!</p>
<p>Lately I had a</p>]]></description><link>https://burhan.io/custom-sumologic-alerting-with-webhooks-and-aws-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c69</guid><category><![CDATA[aws]]></category><category><![CDATA[sumologic]]></category><category><![CDATA[monitoring]]></category><category><![CDATA[api]]></category><category><![CDATA[aws lambda]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Tue, 29 Aug 2017 14:07:58 GMT</pubDate><media:content url="https://burhan.io/content/images/2017/08/Logo_AWS.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h5 id="intro">Intro</h5>
<img src="https://burhan.io/content/images/2017/08/Logo_AWS.png" alt="Custom Sumologic alerting with Webhooks and AWS"><p>So I've been using Sumologic for a while now. Its a great way to get up and running with centralised log monitoring very quickly. No need to install and maintain an ELK cluster. Just sign up and start throwing it logs and configure your dashboards!</p>
<p>Lately I had a need to integrate Sumologic's searching capabilities with a centralised alerting console I use. Here I am going to cover how I went about doing this with Sumologic and AWS. Note that the solution I am using here is not just applicable to Sumologic. Any system that has support for webhooks can use this solution.</p>
<h5 id="prerequisites">Prerequisites</h5>
<p>Before continuing with the rest of this article. Assumptions below have been made:</p>
<ul>
<li>You have an AWS account</li>
<li>You have a Sumologic account</li>
<li>Familiarity with Sumologic, AWS API Gateway, AWS Lambda, AWS SNS, AWS SQS and Boto3</li>
</ul>
<h5 id="architecture">Architecture</h5>
<p>To achieve this I am utilising scheduled views in Sumologic to raise an alert based on my search and threshold, then using Webhooks to shoot off to an AWS API Gateway method backed by a Lambda function. This Lambda function will publishes to an SNS which in turn sends the message to SQS ready to get picked up by my alerting console. Below diagram describes this.<br>
<img src="https://burhan.io/content/images/2017/05/sumo2siren.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<p>For the purpose of this example I will take us all the way to the SQS level. You may not want to go that far depending on your use case.</p>
<h5 id="snstopicandsqs">SNS topic and SQS</h5>
<p>Starting from the end of the diagram we will create a standard SQS queue using the quick-create queue.</p>
<p><img src="https://burhan.io/content/images/2017/06/sqs.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<p>You should end up with a queue like below.<br>
<img src="https://burhan.io/content/images/2017/06/sqs2.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<p>Then create your SNS topic and subscribe your SQS to it.</p>
<h5 id="lambda">Lambda</h5>
<p>Now we will take a look at the Lambda function powering the backend. See the code below:</p>
<pre><code>import json
import boto3

sns = boto3.client('sns')

def lambda_handler(event, context):
    snsARN = '&lt;ARN_OF_YOUR_SNS'
    data = event
    data[&quot;default&quot;] = json.dumps(event)

    try:
        print(&quot;Publishing message to sns&quot;)
        sns.publish(TargetArn=snsARN, Message=json.dumps(data), MessageStructure='json', Subject=&quot;Alert&quot;)
        print(&quot;Published to sns&quot;)

    except Exception as e:
        print(e)
        raise e
</code></pre>
<p>This simple lambda is using boto3 to publish the webhook into an sns topic, which has our SQS subcribed to.</p>
<p>Important that you give this lambda function the correct permissions. To do this I have create a role in IAM with the following permissions and attached this role to my function:</p>
<p><img src="https://burhan.io/content/images/2017/06/policy.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<h5 id="apigateway">API Gateway</h5>
<p>The front end to this service is the AWS API Gateway. Firstly create your API by clicking the &quot;Create API button&quot; in the API Gateway console.<br>
The API for simplicity and extensibility is very minimal by design. The below table describes the endpoint available:</p>
<p>POST	     /Sumo2Siren</p>
<p>This POST method provides the means for the Webhook from Sumologic to pass data in. Lets take a look at this method.</p>
<p><img src="https://burhan.io/content/images/2017/06/post-api.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<p>Nothing special here, you can set up your endpoint as you desire, I have opted to use API keys without any authorization. I will be using usage plans to specify connection limitations, we will see this soon.<br>
Now lets take a look at the integration setup for your method:</p>
<p><img src="https://burhan.io/content/images/2017/06/intergration.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<p>Here I am using simple Lambda integration. The proxy integration is handy if you want the entire request object to get wrapped inside a JSON blob. In this use case the simple body of the request is enough.</p>
<p>You should end up with a workflow that looks like this:</p>
<p><img src="https://burhan.io/content/images/2017/06/apigateway.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<h5 id="apikeysandusageplan">API Keys and usage plan</h5>
<p>Usage plans allow you to specify lower levels of control around what can be done with your API. You can enforce throttling, max hits, bursts and rate limits. If you haven't used Usage plans yet and don't see the option in your API console, you most likely need to enable it on your account.<br>
In the Usage plan menu, create your plan. Like below:</p>
<p><img src="https://burhan.io/content/images/2017/06/usageplan.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<p>Hit next when done, then skip on the next page as we still need to create our key.</p>
<p>Next create your API key. Go to the API Gateway console and select API keys, then create key. Once created click back on your key and you will see there is an &quot;Add to Usage plan&quot; option available. Hit that and you can now tie your key to the usage plan, like below:</p>
<p><img src="https://burhan.io/content/images/2017/06/key-1.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<p>Take a note of your API key by clicking on the &quot;Show&quot; button next to &quot;API key&quot;</p>
<h5 id="deployapi">Deploy API</h5>
<p>Now you are ready to deploy your API. To do this you need to Stage it. Go to your API and click on &quot;stage&quot; menu option. Create a stage like below:</p>
<p><img src="https://burhan.io/content/images/2017/06/deploy.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<p>I advise you pick an appropriate stage name as this will make up part of your endpoint. I like using name like &quot;vN&quot; where N is the version iteration, as this allows for easy tracking of deployments. Once created you will get an &quot;Invole URL&quot; which is just an endpoint to your API, take note of this.</p>
<h5 id="sumologicwebhook">Sumologic Webhook</h5>
<p>First you need to set up your Webhook connection in Sumologic. Log in and navigate to Manage --&gt; Data Configuration --&gt; Connections. Create a connection, in the wizard choose &quot;Webhook&quot;:</p>
<p><img src="https://burhan.io/content/images/2017/06/webhook-4.png" alt="Custom Sumologic alerting with Webhooks and AWS"></p>
<p>Don't forget to place your URL in there, this will be the endpoint you noted earlier.<br>
This payload comprises the body of the request, this is what will be passed to your Lambda function. For further payload examples see <a href="https://help.sumologic.com/Manage/Connections_and_Integrations/Webhook_Connections/About_Webhook_Connections">here</a>.</p>
<p>Now that you have created your connection to your API, you need to set a scheduled view that will make use of this webhook once alerted.</p>
<h5 id="scheduledview">Scheduled View</h5>
<p>You can follow the instructions <a href="https://help.sumologic.com/Manage/Search_Optimization_Tools/Manage_Scheduled_Views/Add_a_Scheduled_View">here</a>. The last part of the puzzle is to connect your scheduled view to the webhook, this is done through the &quot;connection&quot; field:<br>
<img src="https://burhan.io/content/images/2017/06/scheduledview.png" alt="Custom Sumologic alerting with Webhooks and AWS"><br>
Now sit back and watch those alerts fire when matching your condition.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Kubernetes on AWS]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h6 id="intro">Intro</h6>
<p>So creating a new K8s cluster on AWS is easy as ever with <a href="https://github.com/kubernetes/kops">Kops</a>. It can help you save a lot of time, help you in setting up your autoscaling groups and HA config, as well as help you with maintaining central versioned cluster config. We'll go through setting</p>]]></description><link>https://burhan.io/kubernetes-on-aws-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c6a</guid><category><![CDATA[kubernetes]]></category><category><![CDATA[k8s]]></category><category><![CDATA[aws]]></category><category><![CDATA[HA]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Fri, 19 May 2017 20:59:00 GMT</pubDate><media:content url="https://burhan.io/content/images/2017/06/kubernetes-logo.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h6 id="intro">Intro</h6>
<img src="https://burhan.io/content/images/2017/06/kubernetes-logo.png" alt="Kubernetes on AWS"><p>So creating a new K8s cluster on AWS is easy as ever with <a href="https://github.com/kubernetes/kops">Kops</a>. It can help you save a lot of time, help you in setting up your autoscaling groups and HA config, as well as help you with maintaining central versioned cluster config. We'll go through setting up Kops and using it to create a HA, autoscaling K8s cluster.</p>
<h6 id="prerequisites">Prerequisites</h6>
<p>You will need the following:</p>
<ul>
<li>An AWS account</li>
<li>A linux host or VM (sorry windows!)</li>
<li>A resolvable DNS or subdomain (this is important!)</li>
</ul>
<h6 id="installkops">Install Kops</h6>
<p>First thing is first, lets install the Kops tools. Its a Golang project so you can grab it from source via the following:</p>
<pre><code class="language-bash">go get -d k8s.io/kops
cd ${GOPATH}/src/k8s.io/kops/
git checkout release
make
</code></pre>
<p>Caveats:</p>
<ul>
<li>Make sure you have set your GOPATH in your env. Kops expects the source to be checked out in $GOPATH/src/k8s.io/kops. You will get errors if this is not the case.</li>
<li>You will require Go 1.6+</li>
</ul>
<p>Alternatively if you are using a Mac and have Homebrew you can simply do:</p>
<pre><code class="language-bash">brew update &amp;&amp; brew install --HEAD kops
</code></pre>
<p>Now that is done you should get something like below when using Kops in your terminal:</p>
<pre><code class="language-bash">kops version
Version 1.6.0-alpha.2 (git-3fe6e04)
</code></pre>
<h6 id="installkubectl">Install Kubectl</h6>
<p>Kubectl is the controller interface between you and your cluster. You will want this to talk to the API server and issue commands to your cluster.</p>
<p>For Mac users:</p>
<pre><code class="language-bash">brew install kubernetes-cli
</code></pre>
<p>Linux users:</p>
<ol>
<li>
<p><code>curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl</code></p>
</li>
<li>
<p>Make the binary excutable <code>chmod +x ./kubectl</code></p>
</li>
<li>
<p>Move the binary file to your PATH <code>sudo mv ./kubectl /usr/local/bin/kubectl</code></p>
</li>
</ol>
<h6 id="installawscli">Install AWSCLI</h6>
<p>This is be used to create AWS resource quickly as part of the initial setup. Simply install AWSCLI via:</p>
<pre><code class="language-bash">pip install awscli
</code></pre>
<p>We will use AWS CLI to create some initial resources in your AWS account. Make sure you have place an API key and secret pair for your AWS account in <code>$ ~/.aws/credentials</code>. An example of the file should look like so:</p>
<pre><code class="language-bash"># AWS creds
[default]
aws_access_key_id = XXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
</code></pre>
<p>Your awscli config can be found in <code>~/.aws/config</code>. Set up as desired, example below:</p>
<pre><code class="language-bash">[default]

# If web proxy is needed
#proxy=http://www-cache.my.web.proxy:80

# AWS regions
#London
#region = eu-west-2

# Frankfurt
region = eu-central-1

# Ireland
#region = eu-west-1
</code></pre>
<h6 id="prepareawsaccount">Prepare AWS Account</h6>
<p>Now you need to prep your account. We will create the following initial resources before using Kops to setup the cluster.</p>
<ul>
<li>Create a dedicated group for Kops in your AWS account.</li>
<li>Create a dedicated user for Kops in your AWS account.</li>
<li>Assign the neccessary IAM policies</li>
</ul>
<p>Firstly create the group with:</p>
<pre><code class="language-bash">aws iam create-group --group-name kops-group
</code></pre>
<p>Next assign the following group policies:</p>
<pre><code class="language-bash">aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess --group-name kops-group
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonRoute53FullAccess --group-name kops-group
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess --group-name kops-group
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/IAMFullAccess --group-name kops-group
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonVPCFullAccess --group-name kops-group
</code></pre>
<p>Then create the Kops user:</p>
<pre><code class="language-bash">aws iam create-user --user-name kops
</code></pre>
<p>Assign that user to the Kops group:</p>
<pre><code class="language-bash">aws iam add-user-to-group --user-name kops --group-name kops
</code></pre>
<p>Lastly create an API key and secret for that user. These credentials will be used by the Kops tool.</p>
<pre><code class="language-bash">aws iam create-access-key --user-name kops
</code></pre>
<p>This returns the pair to your terminal. Use these to update <code>~/.aws/credentials</code> with the new pair. Also export the following in your term with your new key and secret:</p>
<pre><code class="language-bash">export AWS_ACCESS_KEY_ID=&lt;access key&gt;
export AWS_SECRET_ACCESS_KEY=&lt;secret key&gt;
</code></pre>
<h6 id="dnsconfiguration">DNS configuration</h6>
<p>This is the important part, without this the rest won't work. Kops will by default rely on communicating with your cluster through a public DNS.<br>
As there are many providers out there I will talk about the high levels of what you need to do.</p>
<p>For a dedicated hosted DNS, all that is required is purchasing a domain with AWS. Simply go the Route53 console. Once this is done you will have a hosted zone. No other action is needed</p>
<p>For testing purposes you will probably want to use a subdomain, instead of purchasing and entire domain. Chances are this is hosted by a different DNS provider. Not an issue.</p>
<p>To do this, you will firstly need to create a hosted domain in Route53 for the subdomain you want to use. For example this my be, k8s.mydomain.com. In that hosted zone create an NS record set for your subdomain. Doing this will give 4 name servers. Make a note of these name servers. Log into your dns providers portal and create an NS record with them. Place the subdomain as your host and the name servers you took note of as the values. You should have something that looks like below for our example of k8s.mydomain.com.</p>
<p>Host: k8s<br>
Value: aws.example.dns1.com, aws.example.dns2.org, aws.example.dns3.net, aws.example.dns4.co.uk</p>
<p>Refer to your Domain providers documenation around creating a subdomain.</p>
<p>You can test your subdomain setup is correct by doing a dig and looking for the 4 noted NS servers to be returned:</p>
<pre><code class="language-bash">dig ns subdomain.example.com
</code></pre>
<h6 id="clusterstate">Cluster state</h6>
<p>Ok with DNS out the way, next we need to establish a place for our cluster state to live. Best place to put this is into an S3 bucket as it is a highly reliable source of truth for our cluster, bundled with the versioning capabilities S3 provides allows us to capture state changes to the cluster for auditing.</p>
<p>Create your S3 bucket in the region desired:</p>
<pre><code class="language-bash">aws s3api create-bucket --bucket k8s-mydomain-com-state-store --region eu-central-1
</code></pre>
<p>Enable versioning on the bucket (optional)</p>
<pre><code class="language-bash">aws s3api put-bucket-versioning --bucket k8s-mydomain-com-state-store  --versioning-configuration Status=Enabled
</code></pre>
<h6 id="letdothis">Let do this!</h6>
<p>So still with us?.....Great as now we are ready to kick this thing off. Export the following variable to your env:</p>
<pre><code class="language-bash"># simply a name you want to use for your cluster for my example it can be.
export NAME=myfirstcluster.k8s.mydomain.com

# The location of the S3 that holds the state of the cluster, we created this earlier
export KOPS_STATE_STORE=s3://k8s-mydomain-com-state-store
</code></pre>
<p>Now, lets create our cluster config.</p>
<pre><code class="language-bash">kops create cluster --master-zones=eu-central-1a,eu-central-1b,eu-central-1c \
--master-size=m4.large \
--zones=eu-central-1a,eu-central-1b,eu-central-1c \
--node-count=4 --node-size=m4.large ${NAME}
</code></pre>
<p>So what is going on here, we are tell Kops to:</p>
<ul>
<li><code>--master-zones</code> create our master nodes across 3 different availability zones for an HA deployment.</li>
<li><code>--master-size</code> tell it our EC2 size for the master node<br>
• <code>--zones</code> deploy our worker nodes between 3 different availability zones, again for HA.</li>
<li><code>--node-count</code> how many nodes you want in the cluster</li>
<li><code>--node-size</code> the size of the EC2 instances for the nodes.</li>
</ul>
<p>Once this has successfully completed, take a look in your S3 bucket. You will see the cluster config Kops as created for you. Now that this is done, you can deploy your cluster with</p>
<pre><code class="language-bash">kops update cluster ${NAME} --yes
</code></pre>
<p>This will take a little bit complete. Go take 20 mins, you've earned it at this point ^_^</p>
<p>Once done check out your cluster via <code>kops get clusters</code> or check out your instance groups for your cluster with <code>kops get ig</code>, you should see 4 groups split up between 3 separate masters and one node group.</p>
<p>Now you can start using kubectl to interact with our cluster. Luckily Kops has taken care of the kubectl config for us when we span up the cluster. Check out <code>~/.kube/config</code>.</p>
<p>You can check your cluster is happy by doing <code>kops validate cluster</code> and get your cluster nodes via <code>kubectl get nodes</code></p>
<h6 id="congratulations">Congratulations!</h6>
<p>You've stuck with it and now you have a fully HA K8s cluster on AWS!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Compiling OpenSSL]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h3 id="introduction">Introduction</h3>
<p>OpenSSL is a library that provides cryptographic functionality, specifically SSL/TLS for popular applications such as secure web servers. When going for that A+ on Qualys SSL labs, you most proberly want to complie your own OpenSSL. We'll go through doing that.</p>
<h3 id="requirements">Requirements</h3>
<p>This is going to be Fedora</p>]]></description><link>https://burhan.io/compiling-openssl-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c68</guid><category><![CDATA[openssl]]></category><category><![CDATA[ssl]]></category><category><![CDATA[complie]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Sun, 05 Mar 2017 22:52:26 GMT</pubDate><media:content url="https://burhan.io/content/images/2017/03/openssl.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h3 id="introduction">Introduction</h3>
<img src="https://burhan.io/content/images/2017/03/openssl.jpg" alt="Compiling OpenSSL"><p>OpenSSL is a library that provides cryptographic functionality, specifically SSL/TLS for popular applications such as secure web servers. When going for that A+ on Qualys SSL labs, you most proberly want to complie your own OpenSSL. We'll go through doing that.</p>
<h3 id="requirements">Requirements</h3>
<p>This is going to be Fedora base so grab yourself a RHEL 7 or Centos 7 box.</p>
<h3 id="installation">Installation</h3>
<p>Get your current version with “openssl version”:</p>
<pre><code>openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013 
</code></pre>
<p>and “yum info openssl” command :</p>
<pre><code>yum info openssl
</code></pre>
<p>To download the latest version of OpenSSL, do as follows:</p>
<pre><code>cd /usr/local/src
wget https://www.openssl.org/source/openssl-1.0.2-latest.tar.gz
tar -zxf openssl-1.0.2-latest.tar.gz
</code></pre>
<p>To manually compile OpenSSL and install/upgrade OpenSSL, do as follows:</p>
<pre><code>cd openssl-1.0.2a
./config
make
make test
make install
</code></pre>
<p>Next you need to swap our your binaries, move or delete your old version and place it with you latest build one:</p>
<pre><code>mv /usr/bin/openssl /root/
ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl
</code></pre>
<p>Now verify the OpenSSL version.</p>
<pre><code>openssl version
OpenSSL 1.0.2e 3 Dec 2015
</code></pre>
<p>That's it, job done. Hope this has been helpful.</p>
<p>Note: Compiling Openssl major version may case issues with other system binaries. So make sure you know what is depending on it.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Docker behind a proxy]]></title><description><![CDATA[How to use Docker from behind a proxy]]></description><link>https://burhan.io/docker-behind-a-proxy-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c66</guid><category><![CDATA[Docker]]></category><category><![CDATA[Proxy]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Tue, 08 Nov 2016 21:27:47 GMT</pubDate><media:content url="https://burhan.io/content/images/2016/11/proxy.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://burhan.io/content/images/2016/11/proxy.png" alt="Docker behind a proxy"><p>Working behind a proxy is always a bit of a pain. Luckly if you are doing so work with Docker behind a proxy you've come to the right place. Below steps should allow your docker instance to shot out to the internet to get images. Note, I will go about how to do this with systemd. You will need to adapt this slightly if using a different init system.</p>
<h5 id="config">Config</h5>
<p>First make the following directory</p>
<pre><code>mkdir /etc/systemd/system/docker.service.d
</code></pre>
<p>Then create a http-proxy.conf file under this directory. This conf file will hold the proxy details. The content as follows:</p>
<pre><code>[Service]
Environment=&quot;HTTP_PROXY=http://proxy.example.com:80/&quot;
</code></pre>
<p>Take note, if you have a local docker registry that you don't want to go through the proxy you can set a no proxy option like so:</p>
<pre><code>[Service]
Environment=&quot;NO_PROXY=localhost,127.0.0.0/8,docker-registry.local.com&quot;
</code></pre>
<p>Essentially place the dns names or ips of the address you don't want to go through the proxy.</p>
<p>Once this is done simply flush your changes:</p>
<pre><code>sudo systemctl daemon-reload
</code></pre>
<p>You can verify these changes have been applied via:</p>
<pre><code>systemctl show --property Environment docker
</code></pre>
<p>Finally restart docker</p>
<pre><code>systemctl restart docker
</code></pre>
<p>You should now be able to pull your images from your proxy.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Docker hacks for Production]]></title><description><![CDATA[Docker tips for running your containers in production environment.]]></description><link>https://burhan.io/docker-hacks-for-production-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c64</guid><category><![CDATA[Docker]]></category><category><![CDATA[containers]]></category><category><![CDATA[Production]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Mon, 17 Oct 2016 19:40:43 GMT</pubDate><media:content url="https://burhan.io/content/images/2016/10/docker.jpeg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h5 id="intro">Intro</h5>
<img src="https://burhan.io/content/images/2016/10/docker.jpeg" alt="Docker hacks for Production"><p>Ok so I've been working with Docker in production for a while now. If like me your sitting in the same boat, then I'm sure you can appreciate the pain that comes along with Docker and disk space in production environments. In this post I'm going to list some tasks you may find yourself having to do to get yourself out of a pickle.</p>
<h5 id="tasks">Tasks</h5>
<p>So what are these tasks I mentioned. Well, ever asked yourself any of these questions?;</p>
<ul>
<li>Why am I getting log messages about a read only file system?</li>
<li>I have a cluster of containers running, how can I find out which ones are read only?</li>
<li>How can reclaim space back from my docker-pool LV?</li>
<li>My docker-pool LV is full, how can I resize it?</li>
<li>My docker-pool LV is corrupted how can I recreated it?</li>
</ul>
<p>You will notice I've structured these questions in a logical way starting with the issue you might see and ending with the worst case scenario. We'll take a look at what tasks we can do to answer these questions.</p>
<h5 id="prerequisites">Prerequisites</h5>
<p>So going forwards, I'm expecting you to be;</p>
<ul>
<li>Familiar with Docker and comfortable with Docker terminology and concepts.</li>
<li>Proficient Linux administration skills and understand storage terminologies in Linux systems.</li>
</ul>
<h5 id="readonly">Read Only</h5>
<p>So your sitting back, drinking your ice tea. watching your container cluster at work..... Then you start seeing errors getting flagged up, lets say you see them coming into Logstash. Lets say you see an error &quot;filesystem is readonly&quot;. Of course this error message is going to be unique to your environment but the key point is that you've deduce you have a disk related issue.<br>
So what causes this? Could be a range of things, however there may be good chances that your docker-pool LV has run out of space. Do the below on your system;</p>
<pre><code># You should see output something like below when running lvs
$ lvs
LogVol00        VolGroup00 -wi-ao---- 37.97g
LogVol01        VolGroup00 -wi-ao----  1.50g
docker-pool     VolGroup00 -wi-a----- 40.00g          100%
</code></pre>
<p>The docker-pool lvs is where all your docker images, containers and ultimately data is stored in. So if you see 100% data utilisation against the docker-pool then you've just confirmed why you are seeing read only error messages.</p>
<h5 id="determinewhichcontainersarereadonly">Determine which containers are read only</h5>
<p>From what I've seen in production, when this occurs don't assume all containers on your host become read only. As I've seen only a handful of containers turning read only in my cluster when hitting this issue. Having said that, DO expect to see all sorts of issues occurring in your cluster.<br>
So at this point you will want to do some analysis to determine where all the space has gone. I'll always recommend you do this before jumping straight in and recreating/resizing your docker-pool. Blowing away your docker-pool and recreating it will resolve your problem in the short term, but if you haven't identified what has used up all your space, you are likely to run into this issue again.</p>
<p>Ok so before we can proceed, first we need to get the PID's of all the running containers. We can do this my running the following command on your host:</p>
<pre><code>docker ps -q | xargs docker inspect --format '{{.ID}}, {{.State.Pid}}' | awk {'print $2'}
</code></pre>
<p>Now that you have a list of all the PIDs, go into your /Proc directory. Here you will find directories for each of the PIDs you go back. If you have a look in there you will find a mounts file. This file countains the mount points of your running container. It essentially displays the out from a 'mount -l'. Run the below command and you can see if that running container is readonly.</p>
<pre><code>cat mounts | awk '/docker/'
</code></pre>
<p>I've created the below little script you can run to automate this process for all your running containers.</p>
<pre><code>#! /bin/bash

echo &quot;ContainerID  ContainerPID  Mounts&quot;

for i in $(/bin/docker ps -q); do
  container_pid=$(/bin/docker inspect -f '{{ .State.Pid }}' ${i})
  /bin/cat /proc/${container_pid}/mounts | /bin/echo ${i} $container_pid $(/bin/awk '/docker/ {print &quot;Container=&quot; $4}')
done
</code></pre>
<p>Now that you have identified your RO containers, you can start debugging further. In production I've seen containers not freeing up space because a file is still in use even though it has been deleted, or heavy log output taking up a lot of disk space. These are just some of the things that can be the cause of your disk space issue. Its up to you to identify this and resolve the issue.</p>
<h5 id="reclaimsomediskspace">Reclaim some diskspace</h5>
<p>Ok so the commands you will find here can be run against a running environment however I recommend bringing your services down before doing this, as if only a little bit of space is reclaimed, you could run the risk of instantly filling that back up again. Essentially what we are going to do here is run a fstrim on the filesystem for your containers. Now usually you would run your fstrim on the mountpoint in question. However for containers its a bit more tricky as you can't just do a df to find the mount point as you would on a normal linux host. To do this we need to employ a similar tactic as before. Use the command we saw earlier to get the PIDs of your running containers.</p>
<pre><code>docker ps -q | xargs docker inspect --format '{{.ID}}, {{.State.Pid}}' | awk {'print $2'}
</code></pre>
<p>Then cd into the proc locations i.e.;</p>
<pre><code>cd /proc/17756
</code></pre>
<p>In here you will see there is a root symlink. This is actually a symlink to your running container fs. Now that you found this you can do an fstrim on it like so;</p>
<pre><code>fstrim -v /proc/17756/root
</code></pre>
<p>You can apply this logical to all of your running containers to trim down any empty blocks. As before I've provided a script that will automate doing this for all your running containers.</p>
<pre><code>#! /bin/bash

for i in ‘docker ps -q’; do
  container_pid=$(/bin/docker inspect -f '{{ .State.Pid }}' ${i})
  fstrim -v /proc/${container_pid}/root
done
</code></pre>
<p>After running this you should have reclaimed so disk space back. Check your docker-pool and see if it back to reasonable levels with;</p>
<pre><code>lvs
</code></pre>
<h5 id="resizeit">Resize it?</h5>
<p>So trimming down has not helped and you need to resize. Ok no problem, if you have free space left in the volume group your docker-pool is sitting on you can do something like;</p>
<pre><code>lvextend -l 100%FREE /dev/VolGroup00/docker-pool
</code></pre>
<p>This will take all available space left in your volume group and assign it to your docker-pool. Of course if you no longer have space left in your volume group you will first have to assign more space to it before performing the above.</p>
<h5 id="recreateit">Recreate it?</h5>
<p>You might not care for the data in your docker-pool, or there is some sort of corruption that has gotten too messy to unpick. Well there is a last resort.....Nuke it.<br>
It should go without saying that blowing away the docker-pool will wipe and data Docker will be holding on your cluster. This will be all container resources, metadata and container images..... proceed at your own peril...</p>
<p>So to do this first stop your cluster then stop docker;</p>
<pre><code>systemctl stop docker
</code></pre>
<p>Once docker has stopped remove the below files;</p>
<pre><code>rm -rf /var/lib/docker/*
</code></pre>
<p>Once these steps are done you will can now blow away the docker pool LV;</p>
<pre><code>lvremove VolGroup00/docker-pool
</code></pre>
<p>Now that is done we can create the pool;</p>
<pre><code>lvcreate -T --name docker-pool -l 90%FREE VolGroup00
</code></pre>
<p>Now your done, do an lvs and you should see your pool is clean and back down to zero. (note the 90%, leaving space just incase...paranoia is a good thing here. ^_^ )</p>
<h5 id="fin">Fin</h5>
<p>Hope this post has helped. Till next time!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Control Center - A container-based management system]]></title><description><![CDATA[Docker Container Management System ]]></description><link>https://burhan.io/serviced-a-container-based-management-system-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c63</guid><category><![CDATA[serviced]]></category><category><![CDATA[Control Center]]></category><category><![CDATA[container management system]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Sun, 26 Jun 2016 19:04:22 GMT</pubDate><media:content url="https://burhan.io/content/images/2016/06/theMcKrakenLogo.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h6 id="intro">Intro</h6>
<img src="https://burhan.io/content/images/2016/06/theMcKrakenLogo.png" alt="Control Center - A container-based management system"><p>Sorry for the long absence. Been struggling to find some time to sit down and write, so glad to be finally doing it! Today's topic I am going to go through some work I've been doing with Control Center. (note American spelling!)</p>
<h6 id="whatiscontrolcenter">What is Control Center?</h6>
<p>You probably would not of heard of <a href="https://github.com/control-center/serviced">Control Centre</a> unless you've been working with Zenoss. The project seems to have stated, publicly anyway, in Aug 2013 and is actively being developed.<br>
Control Centre is essentially a neat container management system. No, not the type you find at the Docklands.<br>
<img src="https://burhan.io/content/images/2016/05/container_stacking.gif" alt="Control Center - A container-based management system"><br>
It's key components are:</p>
<ul>
<li>Docker &amp; Docker Registry</li>
<li>OpentTSDB &amp; Hbase</li>
<li>Logstash - process logs</li>
<li>Elasticsearch - Store logs</li>
<li>Zookeeper - cluster coordination</li>
<li>AngularJS, Bootstrap and nvd3.js for UI</li>
<li>Linux kernel for NFS, Btrfs and cgroups.</li>
</ul>
<h6 id="overview">Overview</h6>
<p>At the heart of Control Centre's automation are JSON service files, it is in here where you define what Control Centre should do to deliver your application. As part of this article we will:</p>
<ul>
<li>Install Control Centre</li>
<li>Build a Docker image for our app</li>
<li>Provide the config files for our app to Control Centre</li>
<li>Configure the Networking for our container via Control Centre</li>
<li>Create persistent storage with Control Centre with the use of Docker Volumes</li>
</ul>
<h6 id="installingcontrolcentre">Installing Control Centre</h6>
<p>Before we start, I am assuming you already have docker installed and set up on your system. If not, <a href="https://docs.docker.com/engine/installation">install</a> it before continuing.</p>
<p>Hate to start this off with a caveat but according to the official docs, you have to make sure you have set a hostname/IP in your host file. Control Center will not function correctly if the hostname is mapped to the loopback adapter IP: 127.0.0.1. Once you've address this you are ready to start with the below:</p>
<pre><code>$&gt;sudo apt-key adv --keyserver keys.gnupg.net --recv-keys AA5A1AD7
$&gt;sudo sh -c 'echo &quot;deb [ arch=amd64 ] http://get.zenoss.io/apt/ubuntu trusty universe&quot; \
    &gt; /etc/apt/sources.list.d/zenoss.list'
$&gt;sudo apt-get update
$&gt;sudo apt-get install serviced           
</code></pre>
<p>Once you let that complete you should be able to start it up:</p>
<pre><code>$&gt;sudo start serviced
</code></pre>
<p>Control Center's GUI is accessible over https thus upon starting serviecd the webserver binds to port 443. Make sure you don't already have a daemon bound to this port or you will see start up errors in the logs /var/log/upstart/serviced.log.</p>
<h6 id="deployanapplicationtocontrolcenter">Deploy an application to Control Center</h6>
<p>Before showing you how to deploy an application in Control Center, lets first cover some theory. So firstly Control Center provides a layer of abstraction from Docker. There is no need to write Dockerfiles for your containers. Instead you provide are template files, that are in JSON format, to instruct Control Center. It is beneficial to also note that Control Center refers to containers as applications, we will use this terminology going forwards to avoid confusion.</p>
<p>Now lets look at and example Control Center application. I have taken uploaded an example called <a href="https://gist.github.com/V3ckt0r/d346c7f4b8a956111424d63a7756221f">webserver.json</a>. There is alot of configuration options in the template. I will covered the options we are using.</p>
<pre><code> 2/ &quot;ID&quot;: &quot;&quot;,
 3/ &quot;Name&quot;: &quot;Python-Web-Server&quot;,
 4/ &quot;Description&quot;: &quot;Python Web Server&quot;,
</code></pre>
<p>The first couple of lines just define some metadata. This can be whatever you want. Here we have decided not to give an ID, in which case and automatic one is given. An appropriate description is given along with the name.</p>
<pre><code>5/   &quot;Services&quot;: [
6/ {
7/   &quot;Name&quot;: &quot;Python Web Server&quot;,
8/   &quot;Command&quot;: &quot;&quot;,
9/   &quot;Description&quot;: &quot;Python Web Server&quot;,
10/   &quot;Tags&quot;: null,
11/   &quot;ImageID&quot;: &quot;&quot;,
12/   &quot;Instances&quot;: {
13/     &quot;Min&quot;: 0,
14/     &quot;Max&quot;: 0
15/   },
16/   &quot;ChangeOptions&quot;: null,
17/   &quot;Launch&quot;: &quot;auto&quot;,
18/   &quot;HostPolicy&quot;: &quot;&quot;,
19/   &quot;Hostname&quot;: &quot;&quot;,
20/   &quot;Privileged&quot;: false,
21/   &quot;ConfigFiles&quot;: {},
22/   &quot;Context&quot;: null,
23/   &quot;Endpoints&quot;: null,
24/   &quot;Services&quot;: [
</code></pre>
<p>The &quot;Services&quot; definition accepts and array of objects and allows you to specify a hierarchy to your application. What we are doing here is creating a set of resources underneath the Python-Web-Server application. You can think of the Python-Web-Server application being a group that has a &quot;Python Web Server&quot; service within in. The config following on is pretty self explanatory, the ones to point out are;</p>
<ul>
<li>&quot;Instances&quot;: Its possible to have multiple instances of a service, for example multiple nginx web service.</li>
<li>&quot;Launch&quot;: Specify whether the service starts automatically, manually etc.</li>
<li>&quot;Hostname&quot;: Hostname of the container spawned by Control Center.</li>
</ul>
<p>Following on from the other Services directive we have the bulk of configuration:</p>
<pre><code>25/     {
26/       &quot;Name&quot;: &quot;python&quot;,
27/       &quot;Command&quot;: &quot;/usr/local/bin/python /data/webserver.py -p 8000 -l /var/log/webserver.log&quot;,
28/       &quot;Description&quot;: &quot;&quot;,
29/       &quot;Tags&quot;: [
30/         &quot;daemon&quot;
31/       ],
32/       &quot;ImageID&quot;: &quot;python:2.7.11&quot;,
33/       &quot;Instances&quot;: {
34/         &quot;Min&quot;: 1,
35/         &quot;Max&quot;: 0
36/       },
</code></pre>
<p>We start off by giving a name to the docker image, and give the image a command to run in the container. Notice we feed in a webserver.py file, we will see how the container knows about this later.<br>
On line 32 we give the imageID of the docker base image we want to use. I am using the 2.7.11 release of the python image found on <a href="https://hub.docker.com/_/python/">dockerhub</a>. By specifying a min of 1 instance ensures there is always at least one container running for this service.<br>
So remember the webserver.py we spoke about. The next couple of lines down we actually define this file.</p>
<pre><code>42/       &quot;ConfigFiles&quot;: {
43/         &quot;/data/webserver.py&quot;: {
44/           &quot;Filename&quot;: &quot;/data/webserver.py&quot;,
45/           &quot;Owner&quot;: &quot;root&quot;,
46/           &quot;Permissions&quot;: &quot;&quot;,
47/           &quot;Content&quot;: 
48/         }
49/       }
</code></pre>
<p>Note the content directive is where you specify the actual file content. This will need to be provided as one line in the json template, so make sure you use the ascii characters for new lines etc.</p>
<pre><code>51/       &quot;Endpoints&quot;: [
52/         {
53/           &quot;Name&quot;: &quot;http&quot;,
54/           &quot;Purpose&quot;: &quot;export&quot;,
55/           &quot;Protocol&quot;: &quot;tcp&quot;,
56/           &quot;PortNumber&quot;: 8000,
57/           &quot;PortTemplate&quot;: &quot;&quot;,
58/           &quot;VirtualAddress&quot;: &quot;&quot;,
59/           &quot;Application&quot;: &quot;http&quot;,
60/           &quot;ApplicationTemplate&quot;: &quot;&quot;,
61/           &quot;AddressConfig&quot;: {
62/             &quot;Port&quot;: 8000,
63/             &quot;Protocol&quot;: &quot;tcp&quot;
64/           },
65/           &quot;VHosts&quot;: [
66/             &quot;web&quot;
67/           ]
68/         }
69/       ]
</code></pre>
<p>The Endpoints directive allows you to sort out the networking mapping between your host and your container. The above snippet is exposing container port via the &quot;PortNumber&quot; and making it accessible via host port 8000, as can be seen from the &quot;AddressConfig&quot; object. The vhost object tells control center to expose an endpoint over a vhost name so you don't have to use the ip address, here we just gave the name &quot;web&quot;.</p>
<p>Other important sections to note:</p>
<pre><code>74/      &quot;LogConfigs&quot;: [
75/         {
76/           &quot;Path&quot;: &quot;/var/log/webserver.log&quot;,
77/           &quot;Type&quot;: &quot;apache&quot;,
78/           &quot;Filters&quot;: null,
79/           &quot;LogTags&quot;: null
80/         }
81/       ],
</code></pre>
<p>The &quot;LogConfig&quot; directive allows your to provide the location of the logs we're interested within the container.</p>
<pre><code>117/       &quot;RAMCommitment&quot;: 1073741824,
118/       &quot;CPUCommitment&quot;: 2,
</code></pre>
<p>&quot;RAMCommitment&quot; directive below specific how much RAM in bytes the container takes from the host.&quot;CPUCommitment&quot; dedicates CPU cores from the host.</p>
<p>Right so we need to add this template into Control Center. Take the webserver.json template and do:</p>
<pre><code># add template to serviced
$&gt;serviced template add webserver.json

# then deploy the template into Control Center
$&gt;serviced template deploy &lt;template_id&gt;
</code></pre>
<p>Once the template is deployed you should be able to see the service in Control Center. You can run the service via:</p>
<pre><code>$&gt;serviced service start &lt;serviced_id&gt;
</code></pre>
<p>Once the service is up an running you can access the python web server via <a href="https://web">https://web</a>. &lt; hostname &gt;</p>
<h6 id="conclusion">Conclusion</h6>
<p>Control Center allows you to design your systems with service oriented architecture in mind. I hope this post provides you with a jump start into Control Center and how it can be used to manage and run your container based services.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Flask web API with Firebase]]></title><description><![CDATA[How to use Firebase with Flask to make a quick web API]]></description><link>https://burhan.io/flask-web-api-with-firebase-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c61</guid><category><![CDATA[WTForms]]></category><category><![CDATA[flask]]></category><category><![CDATA[Firebase]]></category><category><![CDATA[python]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Fri, 19 Feb 2016 07:32:19 GMT</pubDate><media:content url="https://burhan.io/content/images/2016/02/flask-firebase.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://burhan.io/content/images/2016/02/flask-firebase.png" alt="Flask web API with Firebase"><p>Hello again, today I’m going to pick up where I left off with more Flask~ing (…..can I say that? o_0 ). As usual with my multi-part posts I suggest you start with the first, you can find that <a href="http://burhan.io/python-web-development-with-flask/">here</a>. I spoke about setting up your Flask environment and touched on routing your web application. I want to now move onto showing you how to incorporate an API with Flask.  API-First design has now become common practise with teams of developers, often the API is designed to serve your front end web site as a client with the scope of opening up the API for public use.</p>
<p>To do this I will be using Firebase. Firebase provides a great backend as a service (BaaS) product to quickly get up and running with you API. You can log in with your Google <a href="https://www.firebase.com/login/">account</a> to use the service, the free service is perfectly adequate for our purposes. The documentation on the firebase website is very good and easy to pick up so I won’t go through it. However I will provide you a base you can import to your app database, which is just one big JSON object.</p>
<pre><code class="language-language-javascript">    {
      &quot;films&quot; : {
        &quot;film1&quot; : {
          &quot;Rating&quot; : &quot;9&quot;,
          &quot;Title&quot; : &quot;The Matrix&quot;,
          &quot;Year&quot; : &quot;1999&quot;
           }
        }
    }
</code></pre>
<p>Now that you have Firebase set up we are ready to build our Flask API.</p>
<h6 id="theory">Theory######</h6>
<p>Just a quick run through about what we are trying to achieve. In this example, we are going to create a form where someone can enter information about a movie. We will ask for title, the year of release and a rating. Upon submitting the form we are going to use the api service to shoot the information as JSON to our database on firebase. In order to do this we are going to use a few different packages. We will use;</p>
<ul>
<li><a href="https://github.com/wtforms/wtforms">WTForms</a> - a python library for form rendering and validation</li>
<li><a href="https://github.com/lepture/flask-wtf">Flask-WTF</a> – a library which intergrates WTForms with Flask</li>
<li><a href="https://github.com/ozgur/python-firebase">python-firebase</a> – a python interface to Firebase.</li>
</ul>
<h6 id="letsgettoit">Let’s get to it######</h6>
<p>To make our life easier there is a great python package for quickly allowing us to use the Firebase API. We will need this library for our example so please go ahead and get it, note – don’t use the package from the PIP repo as it has some bugs. Instead grab it from <a href="http://ozgur.github.io/python-firebase/">github</a>. Credit to Ozgur.</p>
<p>Let pick up with the code we left off in the last post, view.py:</p>
<pre><code class="language-language-python">    from firebase import firebase
    from flask import Flask
    from .forms import FirePut

    app = Flask(__name__)
    firebase = firebase.FirebaseApplication(‘&lt;path tofirebase app&gt;’, None)

    @app.route(‘/’)
    def index():
    return “&lt;h1&gt;Hello, world!&lt;/h1&gt;”

    @app.route(‘/testing’)
    def testing():
    return “&lt;h1&gt;This is another testing page&lt;/h1&gt;”

    If __name__ == ‘__main__’:
    app.run(debug=True)
</code></pre>
<p>The astute among you would of noticed that the above code has three amendments. The first is importing the firebase package, the next is where we import a package called FirePut, this will be explained later. Lastly firebase is initisalised. The <path to firebase app> would be your own link to your firebase app. It will follow the convention of https://<app-name>.firebaseIO.com. Now that we have a added the package, lets create the route to our api.</app-name></path></p>
<pre><code class="language-language-python">    count = 0
    @app.route(‘/api/put’, methods=[‘GET’, ‘POST’])
    def fireput():
      form = FirePut()
      If form.validate_on_submit():
        global count
        count += 1
        putData = {‘Title’ : form.title.data, ‘Year’ : form.year.data, ‘Rating’ : form.rating.data}
        firebase.put(‘/films’, ‘film’ + str(count), putData)
        return render_template(‘api-put-result.html’, form=form, putData=putData)
      return render_template(‘My-Form.html’)
</code></pre>
<p>Ok that’s a lot of new code to digest, let’s start at the top. I started off by declaring a global variable ‘count’, then we created a route for the api declaring GET and POST methods for the route. Next I declared my function fireput() but this can really be another of your choosing. Then I declared a form field and initialised the FirePut that was imported earlier.</p>
<p>Next I constructed an if statement that uses the wtform method validate_on_submit() to check that the form submitted meets it’s validators( we will see how this works later). The “global count” is telling our method that we want to access the count variable and we are simply incrementing the value by one every time the method runs, (of course in the real world this value would need to be persistent). The putData variable is a dictionary containing the data we want to send over to firebase. We are using the form variable to access our FirePut method, (we still need to see what this is) for now all you need to know is this is allowing us to access information that is typed on our form.</p>
<p>The putData variable is used in the next line, we call the firebase put method, specify the films object that we want to add to as the first parameter then the film id (in this case I have chosen to use filmN where N is the incrementing count). The third parameter is the data we want to send, in this case the putData variable. Lastly use flasks render_template method and pass our html page, along with creating parameters that contain the form and putData.</p>
<p>So in short we are saying:</p>
<ol>
<li>If the form is posted correctly</li>
<li>Increase our count</li>
<li>Take the captured form data and place it in the putData dictionary</li>
<li>Shoot that data off to firebase</li>
<li>And return the api-put-result html page</li>
<li>Else return the My-Form.html page<br>
Now as it stands this alone won’t work. Remember the .forms import of FirePut? This will handle our form processing, we need to create the file before the above will work. Let us take a look at form.py;</li>
</ol>
<pre><code class="language-language-python">    from flask.ext.wtf import Form
    from wtforms import StringField, BooleanField
    from wtforms.validators import DataRequired

    Class FirePut(Form):
    	title = StringField(‘title’, validators=[DataRequired()])
    	year = StringField(‘year’, validators=[DataRequired()])
	    rating = StringField(‘rating’, validators=[DataRequired()])
</code></pre>
<p>Here we are using wtform and its flask intergration library flask.ext.wtf along with form validation with wtf.forms.validators. So this class we defined takes the wtf Form as a parameter and within it we define variables of object type StringField, where we pass the name of the field we want to map to in the html as param1 and a validator of DataRequired in param2. This is a simple validator that says we won’t allow nothing to be entered in this field. We do the same for year and rating. Now lets look at the html for this. My-Form.html</p>
<pre><code>{% block content %}
  &lt;h3&gt; Complete below&lt;/h3&gt;
    &lt;p&gt;Please complete the form below&lt;p&gt;
    &lt;form action=&quot;&quot; method=&quot;post&quot; name=&quot;api&quot;&gt;
      &lt;p&gt; Please enter a movie you want to add.: &lt;br&gt;
      {{ form.title(size=40) }}&lt;br&gt;
      {% for error in form.title.errors %}
        &lt;span style=&quot;color: red;&quot;&gt;[{{ error }}]&lt;/span&gt;
      {% endfor %}&lt;br&gt;
      &lt;p&gt; Please enter the year the movie was released.: &lt;br&gt;
      {{ form.year(size=40) }} &lt;br&gt;
      {% for error in form.year.errors %}
        &lt;span style=&quot;color: red;&quot;&gt;[{{ error }}]&lt;/span&gt;
      {% endfor %}&lt;br&gt;
      &lt;p&gt; Please enter the rating you give the movie out of 10.: &lt;br&gt;
      {{ form.rating(size=40) }} &lt;br&gt;
      {% for error in form.rating.errors %}
        &lt;span style=&quot;color: red;&quot;&gt;[{{ error }}]&lt;/span&gt;
      {% endfor %}&lt;br&gt;
      &lt;p&gt;&lt;input type =&quot;submit&quot; value=&quot;Add&quot; class=&quot;btn btn-sm btn-primary&quot;&gt;&lt;/p&gt;
    &lt;/form&gt;
{% endblock %}
</code></pre>
<p>You would have noticed that this doesn’t look like your conventional html code. What we are seeing here is the Jinja template engine at work. Here we are creating a form with 3 fields, these fields map to the variables we created in form.py - movie title, movie year and movie rating. Jinja allows us to map our wtform to the actual input from the client.</p>
<pre><code>{{ form.title(size=40) }}
</code></pre>
<p>This is how we are accessing our form variables. Remember when we set up our route, we created a variable form = FirePut(). Our template has access to this variable so when we say form.title we are referencing the title variable we set up in our FirePut() method, the parameter size=40 is specifying the size of the input box. The validation has been handled on the next line.</p>
<pre><code>{% for error in form.title.errors %}
  &lt;span style=”color: red;”&gt;[{{ error }}]&lt;/span&gt;
</code></pre>
<p>Our validator of DataRequired runs if an error is found we produce the error message giving it a red styling. We repeat this for Year and Rating.<br>
The last piece of the puzzle is the view the user will get after submitting the form. Lets take a look at the api-form-result.html:</p>
<pre><code>{% block content %}
  &lt;h3&gt; Complete below&lt;/h3&gt;
  &lt;p&gt; The movie you entered is: {{PutData[‘Title’] }} &lt;br&gt;
  &lt;p&gt; The year this movie was made is: {{ putData[‘Year’] }} &lt;br&gt;
  &lt;p&gt; The rating you gave this movie is: {{ putData[‘Rating’] }} &lt;br&gt;
{% endblock %}
</code></pre>
<p>Here we are simply presenting the data submitted back to the user. The putData dictionary is getting accessed to place the values on the page.</p>
<h6 id="soletsseethisinaction">So lets see this in action######</h6>
<p>If all has gone well, you should end up with a form looking like below:</p>
<p><img src="https://burhan.io/content/images/2016/02/form3.png" alt="Flask web API with Firebase"></p>
<p>Enter your movie credentials. Once you submit these, the firebase put() method will place the information in the database and then the api-form-result.html will get rendered to the browser</p>
<p><img src="https://burhan.io/content/images/2016/02/result.png" alt="Flask web API with Firebase"></p>
<h6 id="fin">Fin######</h6>
<p>We covered a lot of concepts from templating to form processing and using Firebase in flask. I hope this post has demonstrated how you can go about building a quick api for your web application.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Introduction to Python Web development with Flask]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h4 id="intro">Intro####</h4>
<p>Hi all, belated xmas and new year salutations! Had a busy time over the period and have only just gotten things settled to carry on my tinkering ways!<br>
So, todays post is going to be about Python and the Flask framework. I was reading someone’s comments the other</p>]]></description><link>https://burhan.io/python-web-development-with-flask-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c60</guid><category><![CDATA[python]]></category><category><![CDATA[flask]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Thu, 14 Jan 2016 23:33:04 GMT</pubDate><media:content url="https://burhan.io/content/images/2016/01/flasking-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h4 id="intro">Intro####</h4>
<img src="https://burhan.io/content/images/2016/01/flasking-1.png" alt="Introduction to Python Web development with Flask"><p>Hi all, belated xmas and new year salutations! Had a busy time over the period and have only just gotten things settled to carry on my tinkering ways!<br>
So, todays post is going to be about Python and the Flask framework. I was reading someone’s comments the other day about how easy, straight forward and clean Flask is.</p>
<p>After reading this I stopped and pondered a bit.......<br>
<img src="https://burhan.io/content/images/2016/01/Gl85q.gif" alt="Introduction to Python Web development with Flask"><br>
then muttered “I like easy and clean……”. So I decided to play around with it. ^_ ^</p>
<p>I haven’t really gotten far, spent about an hour and a half, but decided to stop and write this up, I've come across some points I really like already that I want to write down before I forget!</p>
<h4 id="installation">Installation####</h4>
<p>To start off I’ll talk about the installation. I’ll begin with the virtual environment install first. Those of you new to Python may not know about the python tool Virtualenv. This tool is very handy for python developers. It allows you to have isolated pockets for your various different python projects. It is not a full blown virtualisation technology alongside the likes of VMware or VirtualBox. It is simply a clever way wrapping up your projects, all you get are independent lib directories, and project local binaries for python. The great thing about it is its really fast to get up and running and comes bolted together with Python3.</p>
<p>All you need to do to set up a env (with Python3) is:</p>
<pre><code>python3 –m venv &lt;environment-name&gt;
</code></pre>
<p>This command would create a folder in your current dir with the title of the env-name you give. A list of this dir will show you something like:</p>
<pre><code>bin   include  lib   lib64    pyvenv.cfg
</code></pre>
<p>All you’ll find is a python binary in bin and something like 3 lines of config in pyvenv.cfg which speaks for itself. Simple and clear...nice eh?</p>
<p><img src="https://burhan.io/content/images/2016/01/hans-thumbs.gif" alt="Introduction to Python Web development with Flask"></p>
<p>The lib folders will contain any modules you add to your environment as not to get mixed in with your global modules thus keeping it neatly tucked away.<br>
So at this stage, you may of gotten an error already!?! Don’t panic, it happens to the best of us ^_^. After running the venv module you may seen an error message like:</p>
<pre><code>Error: Command ‘[‘&lt;your-pwd&gt;/bin/python3’, ‘-Im’, ‘ensurepip’, ‘—upgrade’, ‘—default-pip’]’       return non-zero exit status 1
</code></pre>
<p>You may be wondering what pip has to do with anything?!?!</p>
<p>Well, when you create your venv by default the –default-pip argument is passed, this installs pip into your venv. For reasons I have not yet looked into, you may get this message indicating that pip was not installed ( believe it may be a bug with a particular version of python3). I'll leave a comment at the bottom once I've looked into this.</p>
<p>To fix this, delete your environment, then create it again using the above command, but use the &quot;with_pip=False&quot; option. This will create you env without any errors. However you will now have to install pip ( or any other package manager) into you new env manually.<br>
To do this we first have to start up our environment. All you need to do is:</p>
<pre><code>Source /&lt;env-name&gt;/bin/activate
</code></pre>
<p>Your prompt should change to look like:</p>
<pre><code>(&lt;env-name&gt;) [b@localhost]
</code></pre>
<p>This is how you can tell you are now working within your new environment. You can also tell by using the which command on python:</p>
<pre><code>which python
</code></pre>
<p>This will show you that your using the python binary under your project as oppose to your global.<br>
Now you have your environment up and running you can install pip via:</p>
<pre><code>curl https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py | python
</code></pre>
<p>and once you have install pip into your project you can start by installing flask:</p>
<pre><code>pip install flask
</code></pre>
<p>……..installation complete. Now let’s use flask!<br>
As is custom I have started off by creating a simple hello word page. I’ve create a file under my environment hello.py, which looks like this:</p>
<pre><code>From flask import Flask
app = Flask(__name__)

@app.route(‘/’)
def index():
    return “&lt;h1&gt;Hello, world!&lt;/h1&gt;”
@app.route(‘/testing’)
def testing():
    return “&lt;h1&gt;This is another testing page&lt;/h1&gt;”
If __name__ == ‘__main__’:
    app.run(debug=True)
</code></pre>
<p>Lets run through this code. We've imported the Flask framework and initialised it under the 'app' parameter. The routing that Flask gives us can be utilised via the '@app.route()' method. Our first route demonstrates creating our index page. Flask will implement the method when the route is accessed. In this case the index method simply returns a string to the client accessing the '/' route, and of course your browser will interpret this as a headline tag and render it accordingly. The last line runs the simple app in debug mode.</p>
<h6 id="untilnexttime">Until next time######</h6>
<p>Flask is a small compact framework. When you install Flask, all your getting really is a routing system - Werkzeug and a templating engine - Jinja2. Today I only touched on the routing system, My next post will build on this and show you how Jinja2 can be used to help you build you application.</p>
<p>The ideology that the Flask framework promotes is flexibility. The framework doesn't believe in force feeding you, Flask embraces that everyone has different needs, ideas and ways of working that is specific to you. It encourage you to take charge and decide your own way of doing things.<br>
There is no database layer incorporated into the framework. This may seem odd at first glance, this is because Flask looks after the little man first ^_^, not all applications will need a database layer, especially small applications. So if you decide you need one, it’s up to you to pick the one you need and plug it in.<br>
If I had to sum up Flask, I would use a quote by a wise man we all know:</p>
<blockquote>
<p>“Be like water my friend…”</p>
</blockquote>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[LAMP service with Docker]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h6 id="dockerround2fight">Docker Round 2…..Fight!</h6>
<p>Back at ya with some more Docker fun. In this post I’ll build upon the <a href="http://www.burhan.io/apache-fun-with-docker/">Apache fun with Docker</a> post. In the last post we saw an example of using Docker to set up a webserver. This post will show you how you can use</p>]]></description><link>https://burhan.io/lamp-service-with-docker-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c5f</guid><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Tue, 22 Dec 2015 15:32:40 GMT</pubDate><media:content url="https://burhan.io/content/images/2015/12/LAMP_small.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h6 id="dockerround2fight">Docker Round 2…..Fight!</h6>
<img src="https://burhan.io/content/images/2015/12/LAMP_small.jpg" alt="LAMP service with Docker"><p>Back at ya with some more Docker fun. In this post I’ll build upon the <a href="http://www.burhan.io/apache-fun-with-docker/">Apache fun with Docker</a> post. In the last post we saw an example of using Docker to set up a webserver. This post will show you how you can use Docker to produce a complete LAMP stack, which you could then use as a template to deliver a lot of different LAMP sites off this one Docker image. You sure your ready bub?</p>
<h6 id="prerequisites">Prerequisites</h6>
<p>What do you need to follow this post? Firstly you’ll need to install Docker, install instructions <a href="http://docs.docker.com/linux/step_one/">here</a>. You’ll also need:</p>
<ul>
<li>A good understanding of Apache HTTP Server</li>
<li>A good understanding of PHP</li>
<li>A understanding of Supervisord</li>
<li>A good understanding of MySQL</li>
<li>A decent understanding of Docker, if you’ve completed my previous <a href="http://www.burhan.io/apache-fun-with-docker/">post</a> then that would be adequate.</li>
</ul>
<h6 id="jumpin">Jump in</h6>
<p>As always, if you’re feeling pro, jump on ahead. You can use my pre-built Docker <a href="https://hub.docker.com/r/vect0r/lamp/">image</a> or follow along with the post and build it yourself <a href="https://github.com/V3ckt0r/docker-lamp">here</a>.</p>
<h6 id="suitup">Suit up</h6>
<p>Once you’ve downloaded the files off Github, let’s quickly skim through the Dockerfile and look at what is going on.</p>
<pre><code class="language-language-docker">
    FROM ubuntu:trusty
    MAINTAINER vect0r

    # Install packages
    ENV DEBIAN_FRONTEND noninteractive
    RUN apt-get update &amp;&amp; \
      apt-get -y install supervisor git apache2 libapache2-mod-php5 mysql-server php5-mysql pwgen php-apc php5-mcrypt &amp;&amp; \  
      echo &quot;ServerName localhost&quot; &gt;&gt; /etc/apache2/apache2.conf

    # Add image configuration and scripts best practice to start service from start up script to avoid problems observed with starting via service directive
    ADD start-apache2.sh /start-apache2.sh
    ADD start-mysqld.sh /start-mysqld.sh
    ADD run.sh /run.sh
    RUN chmod 755 /*.sh          # */ delete this comment
    ADD my.cnf /etc/mysql/conf.d/my.cnf
    ADD supervisord-apache2.conf /etc/supervisor/conf.d/supervisord-apache2.conf
    ADD supervisord-mysqld.conf /etc/supervisor/conf.d/supervisord-mysqld.conf

    # Remove pre-installed database
    RUN rm -rf /var/lib/mysql/*   # */ delete this comment

    # Add MySQL utils
    ADD create_mysql_admin_user.sh /create_mysql_admin_user.sh
    RUN chmod 755 /*.sh    # */ delete this comment

    # config to enable .htaccess
    ADD apache_default /etc/apache2/sites-available/000-default.conf
    RUN a2enmod rewrite

    # Configure /app folder with sample app
    RUN git clone https://github.com/V3ckt0r/hello-docker.git /app
    RUN mkdir -p /app &amp;&amp; rm -fr /var/www/html &amp;&amp; ln -s /app /var/www/html

    #Enviornment variables to configure php
    ENV PHP_UPLOAD_MAX_FILESIZE 10M
    ENV PHP_POST_MAX_SIZE 10M

    # Add volumes for MySQL 
    VOLUME  [&quot;/etc/mysql&quot;, &quot;/var/lib/mysql&quot; ]

    EXPOSE 80 3306
    CMD [&quot;/run.sh&quot;]

</code></pre>
<p>I will only go through the noteworthy points here as I expect you to be able to understand the majority of what is going on here without explanations. So let’s start with that odd ENV param on line 4. This is specific to the Ubuntu base image, it’ll be a bit long winded and slightly off topic to talk about this in great detail. For now just accept that this needs to be there so avoid seeing some fairly ominous error messages later on! You can do some background reading on this <a href="https://github.com/docker/docker/issues/4032">here</a>.<br>
Next we install all our LAMP stack dependencies and then move onto the configuration. What we are doing here is, as before, using start up scripts to start our services. However we are using Supervisord to actually execute these start up scripts. Supervisord benefits us in this context as it watches our services and automatically starts them up for us on boot, or in the event that the application crashes.<br>
Our Dockerfile then clears out any default database files that get installed then configures the Mysql admin user. Let’s take a quick look at it:</p>
<pre><code>#!/bin/bash

/usr/bin/mysqld_safe &gt; /dev/null 2&gt;&amp;1 &amp;

RET=1
while [[ RET -ne 0 ]]; do
    echo &quot;=&gt; Waiting for confirmation of MySQL service startup&quot;
    sleep 5
    mysql -uroot -e &quot;status&quot; &gt; /dev/null 2&gt;&amp;1
    RET=$?
done

PASS=${MYSQL_PASS:-$(pwgen -s 12 1)}
_word=$( [ ${MYSQL_PASS} ] &amp;&amp; echo &quot;preset&quot; || echo &quot;random&quot; )
echo &quot;=&gt; Creating MySQL admin user with ${_word} password&quot;

mysql -uroot -e &quot;CREATE USER 'admin'@'%' IDENTIFIED BY '$PASS'&quot;
mysql -uroot -e &quot;GRANT ALL PRIVILEGES ON *.* TO 'admin'@'%' WITH GRANT OPTION&quot;


echo &quot;=&gt; Done!&quot;

echo &quot;========================================================================&quot;
echo &quot;You can now connect to this MySQL Server using:&quot;
echo &quot;&quot;
echo &quot;    mysql -uadmin -p$PASS -h&lt;host&gt; -P&lt;port&gt;&quot;
echo &quot;&quot;
echo &quot;Please remember to change the above password as soon as possible!&quot;
echo &quot;MySQL user 'root' has no password but only allows local connections&quot;
echo &quot;========================================================================&quot;

mysqladmin -uroot shutdown
</code></pre>
<p>In order for us to create a set up the root account we need to bring up myqld, hence we begin with mysqld_safe.</p>
<p>Next we have a simple loop that checks for a successful startup of MySQL, while RET is not equals to 0 then echo a message sleep for 5 seconds and check the status of MySQL. We keep doing this loop until we get a success code back from $? ($? Returns the status code of the last command, so if MySql status is successful a code of 0 is returned). It is worth a mention that you would of noticed we are routing all output from this command to /dev/null, this is because it will look very confusing on your terminal when bundled together with your Docker logs.</p>
<p>Then we create the variable PASS for our randomly generated password and use this variable in MySql when we create the admin user. Once the grants are given we can echo out the information so it gets captured in the Docker logs. Lastly we shutdown MySql, don’t forget that we want Supervisord to handle the MySql process.<br>
Back to the DockerFile, we get to the point of configuring Apache. If you followed the last <a href="http://burhan.io/apache-fun-with-docker/">post</a> this part will look familiar to you. Our Apache vhost configuration is in the apache_default file. This is all standard stuff. With the below line we add this into the 000-default.conf. Please note – the 000-default file is specific to Apache installations on Ubuntu, you won’t find this on Fedora based systems.<br>
ADD apache_default /etc/apache2/sites-available/000-default.conf<br>
RUN a2enmod rewrite</p>
<p>The a2emod rewrite link can be skipped if you are not using rewriting in Apache, however might as well include it as this is a quiet common requirement.<br>
Once our Apache config is set up we need supply our content. There are multiple ways you can include your content, the method I prefer is using a git repo to pull the content into your container, remember when we installed git back at the top of the DockerFile.</p>
<pre><code>RUN git clone https://github.com/V3ckt0r/hello-docker.git /app
RUN mkdir -p /app &amp;&amp; rm -fr /var/www/html &amp;&amp; ln -s /app /var/www/html
</code></pre>
<p>Here we’re telling Docker to clone a repo from Github then place it in the directory /app. On the next line we remove all the default html content that comes with apache and we create a symlink to our content location /app. On this line I included the mkdir –p /app option, in case you don’t want to use a git repo. If you don’t want to use it you can remote to git clone line and put a ADD command after the second line to add local content.<br>
System environments can be setup with the ENV tag as demonstrated below;</p>
<pre><code>ENV PHP_UPLOAD_MAX_FILESIZE 10M
ENV PHP_POST_MAX_SIZE 10M
</code></pre>
<p>Docker volumes is a new concept we haven’t see yet. The VOLUME tag allows us to tell Docker about a resistant path we want to use for our application. When Docker builds a container from an image it builds it out of a series of read only layers, you see these layers when creating and pulling images. Once the container is fully initialised, then Docker adds a read/write layer on top of it all and that becomes your active container. The combination of layers is referred to as a Union File System.<br>
When you want to have a location where you are able to share resources between different containers or have files accessible outside of the container, this is where Volume’s come in handy. Volumes essentially allow you to map host filesystem paths into your containers. So the line</p>
<pre><code>VOLUME [&quot;/etc/mysql&quot;, &quot;/var/lib/mysql&quot; ]
</code></pre>
<p>Is mapping out host location /etc/mysql on our container location /var/lib/mysql. In my build we aren’t using this VOLUME but I have included it to provide give you an example<br>
Lastly we EXPOSE both ports 80 and 3306 for our Apache and MySql instances. Then start the run.sh, that does some validation checks on the database before executing Supervisord.<br>
Now that we’ve gone through what the Docker file is actually doing. We are ready to run our container.<br>
If you have built your Dockerfile yourself then make sure you build your image:</p>
<pre><code>Docker build –t &lt;username/name&gt;
</code></pre>
<p>And run your container with:</p>
<pre><code>Docker run –d –p 80:80 –p 3306:3306 &lt;username/name&gt;
</code></pre>
<p>Then just open up a web browser and check it out <a href="http://localhost/">http://localhost/</a></p>
<h6 id="conclusion">Conclusion</h6>
<p>We went through a lot in the post. I hope this has demonstrated a possible use case for Docker. In the article we have essentially containerised our web application for deployment. If you are in an environment where you are managing multiple different web applications, this method of bundling all your application dependencies into one container will make managing your Docker host much easier. Not to say that this is the way to go every time, this is just one road of many. Go find yours ;-)</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Forever with NPM]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h6 id="forevernpmmodule">Forever NPM module</h6>
<p>So you've heard of the Forever NPM module right? Its a very simple node module that makes sure your NodeJS script runs.....forever! Check it out <a href="https://www.npmjs.com/package/forever">here</a>.</p>
<h6 id="normalusecase">Normal use case</h6>
<p>Most of you will be familiar with the notion of starting your NodeJS applications via;</p>
<pre><code>$node start</code></pre>]]></description><link>https://burhan.io/fore-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c5d</guid><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Tue, 24 Nov 2015 20:51:21 GMT</pubDate><media:content url="https://burhan.io/content/images/2015/11/npm-forever-mini.svg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h6 id="forevernpmmodule">Forever NPM module</h6>
<img src="https://burhan.io/content/images/2015/11/npm-forever-mini.svg" alt="Forever with NPM"><p>So you've heard of the Forever NPM module right? Its a very simple node module that makes sure your NodeJS script runs.....forever! Check it out <a href="https://www.npmjs.com/package/forever">here</a>.</p>
<h6 id="normalusecase">Normal use case</h6>
<p>Most of you will be familiar with the notion of starting your NodeJS applications via;</p>
<pre><code>$node start app.js
</code></pre>
<p>and by extension using Forever to start your application;</p>
<pre><code>$forever start app.js
</code></pre>
<h6 id="startingappusingnpm">Starting app using NPM</h6>
<p>Those of you who are more experienced in the NodeJS world may prefer starting your NodeJS applications using package.json to configure your entry point and NPM start from your application directory;</p>
<pre><code>$npm start
</code></pre>
<p>So to use Forever to start up your application is this manner, you can do;</p>
<pre><code>$forever start -c &quot;npm start&quot; ./
</code></pre>
<p>Of course this command needs to be run from your application directory, or just specify it at the end. Success! Your application has been started in the background, and will run forever. Happy days!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Apache fun with Docker]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h6 id="intro">Intro</h6>
<p>Hi all, in this post I will show you an example of how you can use Docker to deliver a simple Apache http server for your website or application. I plan to make another posts that builds on top of this for a more advance use case.</p>
<p>So first</p>]]></description><link>https://burhan.io/apache-fun-with-docker-2/</link><guid isPermaLink="false">5dc88aa1b313f2022f427c5c</guid><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Sun, 22 Nov 2015 18:35:02 GMT</pubDate><media:content url="https://burhan.io/content/images/2015/11/docker-apache-mini-blue.svg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h6 id="intro">Intro</h6>
<img src="https://burhan.io/content/images/2015/11/docker-apache-mini-blue.svg" alt="Apache fun with Docker"><p>Hi all, in this post I will show you an example of how you can use Docker to deliver a simple Apache http server for your website or application. I plan to make another posts that builds on top of this for a more advance use case.</p>
<p>So first a little introduction to what Docker is – I’ll be brief as there is plenty of information out there on it. Docker is a containerisation technology that allows you to wrap and enclose an entire service into a container for deployment on any Linux infrastructure without having to worry about the hosts configuration, setup, software dependencies, the lot. Docker Inc describes their product as</p>
<blockquote>
<p>&quot;Docker containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in.&quot;</p>
</blockquote>
<h6 id="prerequisites">Prerequisites</h6>
<p>To get started you will need to have Docker installed on your system, I am using CentOS, however any Fedora or Debian based system can be used. <a href="http://docs.docker.com/linux/step_one/">install instructions here</a>.<br>
You also need a good understanding of Apache.</p>
<h6 id="jumpin">Jump in</h6>
<p>Feeling a little pro? Jump straight in via <a href="https://github.com/V3ckt0r/docker-httpd-proxy">github</a>.</p>
<h6 id="letsstart">Let’s Start</h6>
<p>Ok, so we’re going to build an Apache webserver to serve up content, however that is really easy so I’ll make it a little bit more challenging by showing you how to set up vhosts, so that you can host more than one website from your Docker container.</p>
<p>First lets have a look at our Dockerfile;</p>
<pre><code>FROM centos:latest
Maintainer vect0r
LABEL Vendor=&quot;CentOS&quot;
RUN yum -y update &amp;&amp; yum clean all
RUN yum -y install httpd &amp;&amp; yum clean all
EXPOSE 80 81
#Simple startup script to aviod some issues observed with container restart
ADD run-httpd.sh /run-httpd.sh
RUN chmod -v +x /run-httpd.sh
#Copy config file across
COPY ./httpd.conf /etc/httpd/conf/httpd.conf
COPY ./example.com /var/www/example.com
COPY ./example2.com /var/www/example2.com
COPY ./sites-available /etc/httpd/sites-available
COPY ./sites-enabled /etc/httpd/sites-enabled
</code></pre>
<p>When Docker builds a container it builds it in layers. So when you go to create your Dockerfile you would see the essential stuff at the top and as you go down the file you will start to see more lower level configuration. Hence the first line. “FROM centos:latest” here we are telling Docker to go off and download the latest image of CentOS. This will be the base flavour our container uses.</p>
<p>“But I’m not using Centos.” I hear you say ^_^. It doesn't matter! As long as your using a Linux kernel, this will work. One of the beauties of Docker. Next we just add some fluffy meta data to our image. MAINTAINER AND LABEL. Maintainer would typically be your email address. In my case I've just added my tag. For the LABEL I've just specified CentOS as I'm using their base image. This interesting stuff starts happening at the RUN command. The RUN command shockingly tells Docker to run the subsequent command on the base image when building. Here we update all packages first then on the next line install Apache and clean yum.</p>
<p>Now at this point before we look at the rest let’s start looking at our Apache config, there will be subtle differences between Linux distros. I will follow CentOS’s style however I’m sure you can easily adapt this if your using Ubuntu. Right in our httpd.conf (apache.conf for Ubuntu) - we are mainly interested in setting our server to listen to ports 80 and 81, a port for our respective sites. You might want to also double check that  sites-enable directory is also included. That’s it! The result of the defaults could be left as they are;</p>
<pre><code>#make sure you have the below entries in your config #file.
Listen 80
Listen 81
IncludeOptional sites-enabled/*.conf
</code></pre>
<p>Inside our sites-enabled directory is where we’ll find our vhost.conf files for our two sites, in my case example.com.conf and example2.com.conf. You can specify this file however you want, example of mine looks like;</p>
<pre><code>&lt;VirtualHost *:80&gt;
  ServerName www.example.com
  ServerAlias example.com
  DocumentRoot /var/www/example.com/public_html
  ScriptAlias /cgi-bin/ /var/www/cgi-bin/

&lt;Directory &quot;/var/cgi-bin&quot;&gt;
  AllowOverride None
  Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
  Require all granted
&lt;/Directory&gt;
    
  ScriptAlias /httpd/ /etc/httpd/

  ReWriteEngine on
  RewriteRule '/home/b/test'

&lt;Location &quot;/status&quot;&gt;
  SetHandler server-status
  Order deny,allow
  Require all granted
  Allow from all
&lt;/Location&gt;

  ErrorLog /var/www/example.com/error.log
  CustomLog /var/www/example.com/requests.log combined
&lt;/VirtualHost&gt;
</code></pre>
<p>That’s all we need to worry about so now lets get back to our Dockerfile. So now that we've seen that Apache is listening to ports 80 and 81 we are now going to tell Docker to EXPOSE ports 80 and 81 on the docker container we build, as Docker doesn't do this automatically (that’s a good thing!).</p>
<pre><code>EXPOSE 80 81
</code></pre>
<p>Next now that is done, you’ll see I’m using an ADD directive. This allows us to tell Docker to ADD a file from my desktop/host and put it into the container;</p>
<pre><code>ADD &lt;source-file-location&gt; &lt;container-file-location&gt;
</code></pre>
<p>The source file goes into the container. So in my example run-httpd.sh is in the directory where I am running the build command and I’m placing the file on the root level of the container /. Important note about the ADD function is that it does not simply add local files. I can be used with URL supports certain compression types. See the Docker website for a more extensive explanation.</p>
<p>Let’s have a look at run-httpd.sh and why we need it;</p>
<pre><code>#!/bin/bash

rm –rf /run/httpd/* /tmp/httpd*
exec /usr/sbin/apachectl –D FOREGROUND
</code></pre>
<p>So you might be wondering why we are removing files. This is simply to clean up the system in the event that you server crashed or was interrupted and didn’t shutdown properly. If that does happen and these directories aren’t cleaned out Apache will fail to start as the system will think it’s already running.</p>
<p>You can see here we are starting up Apache with its cli command as oppose to using the more conventional service or systemctl. This is more of a best practice convention if anything. There is nothing stopping you from using these utilities, however with all the different base images out there some may not have these services available by default!<br>
Another point to highlight here is that we need to start Apache in the FOREGROUND, this may sound unconventional as Apache is usually started as a background daemon. This is done because of the way Docker works. When you run a Docker container, the containers only remain up for the lifetime of the command you give it. This is determined by the stdout output. So if we started Apache in the usual manner, as a daemon, then our container would start then immediately stop as it would think its purpose is complete.</p>
<p>Our next command we give Docker is RUN, here we are telling Docker to run a command at build time. In our example we are simply making the script we just added executable;</p>
<pre><code>RUN chmod -v +x /run-httpd.sh
</code></pre>
<p>On to the COPY function lines. COPY is similar to the ADD function in that it allows use to place files in our container. However COPY isn’t as sophisticated as ADD, copy simply puts local files into your container. Why not use ADD? Well COPY is just more transparent in its functionality so it’s usually preferred when copying config files.</p>
<pre><code>COPY ./httpd.conf /etc/httpd/conf/httpd.conf
COPY ./example.com /var/www/example.com
COPY ./example2.com /var/www/example2.com
COPY ./sites-available /etc/httpd/sites-available
COPY ./sites-enabled /etc/httpd/sites-enabled
</code></pre>
<p>I have taken the apache files we look at earlier and placed them in the relevant places.<br>
Last but not least. CMD this is going to execute our run-httpd.sh script once all the above is executed. You have just completed a Dockerfile – was easy huh!</p>
<h6 id="inaction">In action</h6>
<p>Let’s see this in container in action. First we need to build this sucker, make sure your in run the below command in the directory your Dockerfile is located;</p>
<pre><code>docker build –rm –t &lt;username&gt;/httpd-proxy .
</code></pre>
<p>You can call your image whatever you want if you don’t like httpd-proxy. When you run this you will see a bunch of stuff output to your console, this is Docker building your container in the layered instructions we gave it. Once that’s finished if you type;</p>
<pre><code>docker images
</code></pre>
<p>You will see the image you’ve just created listed. Once this is built you can use this image to create your container. Now to create the container we also need to bind our host ports to our container, otherwise our web request won’t go through! We can do this as part of our run command like so;</p>
<pre><code>docker run –d –p 80-81:80-81 &lt;username&gt;/httpd-proxy
</code></pre>
<p>Here we are telling docker to create a container the –d tell its to daemonise the container and the –p is binding the ports. We are saying bind our host ports 80-81 to our containers ports 80-81. Then  we are simply giving the name of the image we want to use. When our container is build it will be given a random name like “firey-scotty”, if we want to specify a name yourself we can use the --name option. In my example I’m happy with a random name. Once this command is executed you can see the container running via;</p>
<pre><code>docker ps
</code></pre>
<p>Now you can open your browser and navigate to your sites via <a href="http://localhost">http://localhost</a> and <a href="http://localhost:81">http://localhost:81</a></p>
<h6 id="conclusion">Conclusion</h6>
<p>To conclude, we have gone through setting up, building, and then running our Docker container. I hope this demonstrates and makes you think about the fun that could be had with Docker.<br>
Sorry if this went on for too long. Please leave a comment if this helped you, if you have any questions, or if you have any recommendations. I look forward to hearing from you.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Hello World!]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>So this is my first blog in Ghost. Must admit, I'm liking how easy it is to get up and running!</p>
<p>So firstly a little bit about me. I started this blog as a place to store and share my ideas and experiences I encounter as a student in the</p>]]></description><link>https://burhan.io/hello-world-2/</link><guid isPermaLink="false">5dc88a84b313f2022f427bec</guid><dc:creator><![CDATA[Burhan Deniz Abdi]]></dc:creator><pubDate>Thu, 15 Oct 2015 10:12:51 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>So this is my first blog in Ghost. Must admit, I'm liking how easy it is to get up and running!</p>
<p>So firstly a little bit about me. I started this blog as a place to store and share my ideas and experiences I encounter as a student in the road to IT geekness! I would love this blog to become a learning playground both for myself and you readers. I will incorporate a commenting section so we can discuss various topics to facilitate further learning.</p>
<p>A lot of my passion for IT belongs in the domains of programming, administration, automation, of all sorts of web technologies. My programming passions cover both front-end and back-end technologies, my new pet love is Javascripts MEAN stack, although I have interests with Python and Java too. In regards to administration I love working with Linux and the Open Source world, so I hope to show some cool tips for using Linux to perform certain web applications needs, leading to my next passion automation. There are some great things being done in this field, especially with tools such as Ansible, Docker, Jenkins and services such as Tutum, Heroku, not least to mention Digital Ocean, AWS etc.</p>
<p>Now this post wouldn't be complete if I didn't admit to being a gamer! Pretty much comes with the job description right?!?! Being a late 80's kid I grew up with the Saga Mega drive and the SNES. I've watched the industry grow into what it is today, but you'd still hear me argue in the pub that some of the best games came out on these platforms! ^_^</p>
<h6 id="funfacts">Fun Facts</h6>
<ul>
<li><strong>First game I ever played?</strong> I believe it was Pong.</li>
<li><strong>First game I owned?</strong> Sonic the Hedgehog (1991).</li>
<li><strong>My favourite game series?</strong> Gotta be Metal Gear!!!!</li>
<li><strong>Favourite beat'em up?</strong> Hard one but jointly - Street Fighter, Tekken, SmashBro's and MVC.</li>
</ul>
<p>This list can go on but I'll leave it there for now. Right, that's enough from me for now. Chat to ya later! ^_^<br>
<img src="https://burhan.io/content/images/2015/11/tumblr_mavnqdzzI81qeh39oo1_r2_500.gif" alt></p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>