<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Billy Ferguson</title>
    <description>The latest articles on DEV Community by Billy Ferguson (@fergyfresh).</description>
    <link>https://dev.to/fergyfresh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F201054%2F61787033-3ff8-4e30-903a-d32b4c3a3712.png</url>
      <title>DEV Community: Billy Ferguson</title>
      <link>https://dev.to/fergyfresh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fergyfresh"/>
    <language>en</language>
    <item>
      <title>How to put a django app in the cloud.</title>
      <dc:creator>Billy Ferguson</dc:creator>
      <pubDate>Mon, 29 Jul 2019 19:26:02 +0000</pubDate>
      <link>https://dev.to/fergyfresh/how-to-put-a-django-app-in-the-cloud-2clg</link>
      <guid>https://dev.to/fergyfresh/how-to-put-a-django-app-in-the-cloud-2clg</guid>
      <description>&lt;h1&gt;
  
  
  GKE cluster and CloudSQL, a saga
&lt;/h1&gt;

&lt;p&gt;Yupp, I said saga. So you wanna make a GKE cluster with Django and a CloudSQL (postgres) backend, hold on for the ride.&lt;/p&gt;

&lt;p&gt;Alright so you're here and reading this so I'm assuming you're struggling with &lt;a href="https://cloud.google.com/python/django/kubernetes-engine" rel="noopener noreferrer"&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the prereqs for this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you have a django app&lt;/li&gt;
&lt;li&gt;you can build a container of that app&lt;/li&gt;
&lt;li&gt;you're ok with using postgresql as the SQL backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don't you have one you can use &lt;a href="https://github.com/fergyfresh/bookface" rel="noopener noreferrer"&gt;this&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup project on Google Cloud.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create project
&lt;/h3&gt;

&lt;p&gt;First thing we need to do before we can setup the project we are going to want to Create a project in order to group all of our cloud components.&lt;/p&gt;

&lt;p&gt;You can create a project at &lt;a href="https://cloud.console.google.com" rel="noopener noreferrer"&gt;https://cloud.console.google.com&lt;/a&gt; after you create an account in the top left of the screen. See screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffergyfresh%2Ffergyfresh.github.io%2Fmaster%2Fassets%2Fgke-blog%2Fcreate-project.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffergyfresh%2Ffergyfresh.github.io%2Fmaster%2Fassets%2Fgke-blog%2Fcreate-project.png" alt="Create Project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create CloudSQL instance
&lt;/h3&gt;

&lt;p&gt;Create a postgres CloudSQL with a private IP since that is what we are going to need when connecting GKE to cloudSQL, you can technically do it with it being a public IP, but then you need to run a &lt;code&gt;cloud_sql_proxy&lt;/code&gt; inside of the django container, so let's not get into that right now, this is actually easier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffergyfresh%2Ffergyfresh.github.io%2Fmaster%2Fassets%2Fgke-blog%2Fcreate-sql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffergyfresh%2Ffergyfresh.github.io%2Fmaster%2Fassets%2Fgke-blog%2Fcreate-sql.png" alt="Create CloudSQL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you've created the CloudSQL instance we're going to want to create a database user and a database for our django project and grant &lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;READ&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt; to a user that I'll call &lt;code&gt;myproject-user&lt;/code&gt; to the  database we will call &lt;code&gt;myproject-db&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The things we need to remember from this are the private IP address (can be found under the &lt;code&gt;CONNECTION&lt;/code&gt; tab. Database name was &lt;code&gt;myproject-db&lt;/code&gt; and the database user was &lt;code&gt;myproject-user&lt;/code&gt; and the password that you set when you created the user. We will need this stuff later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pushing docker image to Google docker registry (GKE cluster deployment prereq)
&lt;/h3&gt;

&lt;p&gt;Before we create a cluster, we are going to want to figure out what your project ID is in order to use the Google cloud docker registry &lt;code&gt;gcr.io&lt;/code&gt;. Here is how you would do it if you were in the directory of the Dockerfile for the django app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build . -t gcr.io/&amp;lt;project-id&amp;gt;/django
docker push gcr.io/&amp;lt;project-id&amp;gt;/django
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give us an image to deploy to our cluster when we get there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a cluster
&lt;/h3&gt;

&lt;p&gt;A cluster is a set of nodes that will run your services (instances of workloads, we will get to this).&lt;/p&gt;

&lt;p&gt;I suggest if this is your first time to create the smallest physical node type and only create one node. I understand this kinda defeats the purpose, but it will save you money while you're still figuring everything out. Also clicking the &lt;code&gt;Your first cluster&lt;/code&gt; template is also a good start, but we need to click the &lt;code&gt;Availability, networking, security, and additional features&lt;/code&gt; link near the bottom of the cluster settings, and we are looking to ENABLE the setting VPC-native.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffergyfresh%2Ffergyfresh.github.io%2Fmaster%2Fassets%2Fgke-blog%2Fvpc-native.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffergyfresh%2Ffergyfresh.github.io%2Fmaster%2Fassets%2Fgke-blog%2Fvpc-native.png" alt="VPC Native"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy a workload
&lt;/h3&gt;

&lt;p&gt;After you click deploy under the workload tab you'll get a window that has nginx in the image section, we are gonna wanna use our own. In order to do this you'll want to click the blue SELECT link and navigate to the image we created earlier and here is where I will need you to fill in your environment variables that you set in section above that I said are things to remember.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffergyfresh%2Ffergyfresh.github.io%2Fmaster%2Fassets%2Fgke-blog%2Fworkload-finish.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffergyfresh%2Ffergyfresh.github.io%2Fmaster%2Fassets%2Fgke-blog%2Fworkload-finish.png" alt="Workload finish"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Exposing a service to the world.
&lt;/h3&gt;

&lt;p&gt;If we go into the Services section and select the service for the workload we just deployed we can PORT Forward the django port to port 80 or whatever port you want!&lt;/p&gt;

&lt;h3&gt;
  
  
  Important Key takeaways.
&lt;/h3&gt;

&lt;p&gt;The easiest way I could get this to work was to have a VPC-Native enabled GKE cluster and a CloudSQL instance that has the Private IP section enabled.&lt;/p&gt;

&lt;p&gt;Support articles &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/python/django/kubernetes-engine" rel="noopener noreferrer"&gt;Django on GKE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/sql/docs/postgres/connect-kubernetes-engine" rel="noopener noreferrer"&gt;Connecting to PostgresQL from Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  If you messed up Environment variables
&lt;/h3&gt;

&lt;p&gt;If you think you might have setup an environment variable incorrectly go to the &lt;code&gt;Configuration&lt;/code&gt; section and you can find a Config Map called &lt;code&gt;&amp;lt;service-name&amp;gt;-config&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Questions, comments, concerns
&lt;/h1&gt;

&lt;p&gt;Feel free to click one of these buttons in order to signal me with something that was messed up with this. I'd be glad to fix anything that didnt work for you.&lt;/p&gt;

</description>
      <category>django</category>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>How I setup my ssh config to be able to use different Gitlab/Github ssh keys.</title>
      <dc:creator>Billy Ferguson</dc:creator>
      <pubDate>Wed, 24 Jul 2019 19:20:40 +0000</pubDate>
      <link>https://dev.to/fergyfresh/how-i-setup-my-ssh-config-to-be-able-to-use-different-gitlab-github-ssh-keys-2gfd</link>
      <guid>https://dev.to/fergyfresh/how-i-setup-my-ssh-config-to-be-able-to-use-different-gitlab-github-ssh-keys-2gfd</guid>
      <description>&lt;h2&gt;
  
  
  Backstory
&lt;/h2&gt;

&lt;p&gt;So the original idea for this post actually stemmed from me adding some ssh keys for private gitlab repos to be able to git clone other private repos for building docker containers in our CI/CD stuff, but this was a perfect jump start for an issue that I had and how I solved it&lt;/p&gt;

&lt;h2&gt;
  
  
  What the problem was.
&lt;/h2&gt;

&lt;p&gt;Well, so you see, last week I setup a new ssh key to use the deploy key feature of Gitlab to give read only access to other computers, in this case it was a gitlab-ci runner, but thats beside the point. Also if you don't know ssh stuff and are still logging in every time you wanna clone/push to a remote git repo I suggest you head over here to a (great Digital Ocean breakdown of ssh connection education](&lt;a href="https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process"&gt;https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A few days after I added the key, upon a restart of my computer, I could no longer clone projects or push changes to remote repos with ssh. I know what it is, ssh is using my newer ssh key by default, lets test that theory!&lt;/p&gt;

&lt;p&gt;If I typed &lt;code&gt;ssh -T git@gitlab.com&lt;/code&gt; it would say what I expected, a failure right?:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  ~ ssh -T git@gitlab.com
Welcome to GitLab, @fergyfresh!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Damn! I was hoping that would fail. Let's try to clone a private repo again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  ~ git clone ssh@gitlab.com:path/to/my/private/repo.git
Cloning into 'repo'...
ssh@gitlab.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;:( so sad. Well, that stinks. I'm lost, or am I?&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;So I remember that I had used &lt;code&gt;~/.ssh/config&lt;/code&gt; to specify an &lt;code&gt;Identity&lt;/code&gt; before, so we can surely do that again. The file we are going to want to modify is &lt;code&gt;~/.ssh/config&lt;/code&gt;. Or in most cases, like mine, it didn't even exist so I had to create it.&lt;/p&gt;

&lt;p&gt;We are going to want to add an entry for a host and the ssh &lt;code&gt;IdentifyFile&lt;/code&gt; to use. All of my ssh keys are in the &lt;code&gt;~/.ssh/&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;host gitlab.com
    HostName gitlab.com
    IdentityFile /home/ferg/.ssh/id_rsa
    User git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, once you see that you know the power of the ssh config and can extrapolate that setup to use a different ssh key for any number of hosts. Don't forget to have the path to the private key at &lt;code&gt;IdentifyFile&lt;/code&gt; to be on your computer and not just blindly cut/paste my path, with MY username.&lt;/p&gt;

&lt;h1&gt;
  
  
  Questions, comments, concerns
&lt;/h1&gt;

&lt;p&gt;Feel free to click one of these buttons in order to signal me with something that was messed up with this. I'd be glad to fix anything that didnt work for you.&lt;/p&gt;

</description>
      <category>ssh</category>
      <category>devops</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>Benchmarking Go and Python</title>
      <dc:creator>Billy Ferguson</dc:creator>
      <pubDate>Wed, 24 Jul 2019 19:18:41 +0000</pubDate>
      <link>https://dev.to/fergyfresh/benchmarking-go-and-python-1d6f</link>
      <guid>https://dev.to/fergyfresh/benchmarking-go-and-python-1d6f</guid>
      <description>&lt;h1&gt;
  
  
  Simple Benchmarking for GET requests between Gin and Flask
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Preamble
&lt;/h2&gt;

&lt;p&gt;You need to have Go installed and Python installed for this to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Just noticed after finishing up all the code for this post that both of these are drinking related (Gin and Flask). After analyzing the go ecosystem for web frameworks (Gin, Martini, Web.go, Beego, Goji, Gorilla) I decided on pitting Gin against Flask. This appeared to be the most straight forward comparison between what I'm comfortable with as one of the more popular python web frameworks, but also it's known as one of the fastest ones of the bunch.&lt;/p&gt;

&lt;p&gt;Ideally I would do this in a few files, and probably use gunicorn in production for Python and structure the app differently, but I just wanna build the most stripped down version of this example in ython to compare pure speeds of Flask vs. Gin and the size of their minimal docker builds for just returning a simple JSON object on a get request to the app.&lt;/p&gt;

&lt;p&gt;Make a directory called pygo-benchmark and change your current directory to that dir.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go web-app with Gin
&lt;/h2&gt;

&lt;p&gt;Do this all in a subdirectory within pygo-benchmark, I called it go-stuff, but you can call it whatever you'd like.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write the file
&lt;/h3&gt;

&lt;p&gt;Lets get to building. I use vim-go so you get a whole package scaffold upon the first &lt;code&gt;vim file.go&lt;/code&gt; command. So when I type &lt;code&gt;vim main.go&lt;/code&gt; I get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"vim-go"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So you should also create a file called &lt;code&gt;main.go&lt;/code&gt; and add the code above to that file. Now we are going to want to install gin. In order to do this we are going to type &lt;code&gt;go get -u github.com/gin-gonic/gin&lt;/code&gt; and that will install the go module for us. Alright, now that we have gin installed lets modify the file to make our &lt;code&gt;main.go&lt;/code&gt; use gin now and return a simple json object on a GET to &lt;code&gt;localhost:8080&lt;/code&gt;, the default port for this webserver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/gin-gonic/gin"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"pong"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c"&gt;// router runs on :8080 by default&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Build and Run
&lt;/h3&gt;

&lt;p&gt;It is pretty simple to build a file with go, &lt;code&gt;go build main.go&lt;/code&gt; is all you need. Now you should have an executable file called main in the same directory you're in. If you just run &lt;code&gt;/main&lt;/code&gt; in the command line at the same directory you built the file from, it should have some standard output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /                         --&amp;gt; main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you've gotten this far congrats you built and ran your first go app!&lt;/p&gt;

&lt;h2&gt;
  
  
  Python web-app with Flask
&lt;/h2&gt;

&lt;p&gt;Do all of this in a subdirectory in pygo-benchmark, I called it flask-stuff, but once again do whatever you need to here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write the file
&lt;/h3&gt;

&lt;p&gt;In order to make this post shorter I'm not going to go through the whole setup, I'm just gonna &lt;em&gt;blurgghhhhh&lt;/em&gt; the file out here. Make a file called &lt;code&gt;main.py&lt;/code&gt; with the contents below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'{"message": "ping"}'&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Build and Run python
&lt;/h2&gt;

&lt;p&gt;So there is no build in python, we will get to that benefit as one the favors Go in our Benchmarking section.&lt;/p&gt;

&lt;p&gt;I like to do everything on the commandline in virtualenv, but if you dont mind messing up your packages do whatever you want, this is what I would do in order to get this file running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;virtualenv .venv -p python3.6
source .venv/bin/activate
pip install flask
python main.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Because of the way this file is setup with the &lt;code&gt;if __name__...&lt;/code&gt; stuff we can just run this file from the command line, like this &lt;code&gt;python main.py&lt;/code&gt; and we should see something like this if we did it successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(.venv) ➜  flask-stuff ✗ python main.py
 * Serving Flask app "main" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 313-728-815
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Benchmarking the pure speed of each setup.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  In python using requests and timeit
&lt;/h3&gt;

&lt;p&gt;Now let's get the standard boilerplate dislaimer out here. You know, the one about how results may vary on the machine that you run the benchmarks on and that you really need to have a multitude of machines in order to really prove out benchmars, yada-yada.&lt;/p&gt;

&lt;p&gt;Alright, so with that boring stuff out of the way I LOVEEE using &lt;code&gt;timeit&lt;/code&gt; to time stuff in python. You can do multiple runs one after the other in order to find a more valuable benchmark (the average response time).&lt;/p&gt;

&lt;p&gt;Since we should have both webservers running as of now in this blog the go server is running on &lt;code&gt;localhost:8080&lt;/code&gt; and the python on at &lt;code&gt;localhost:5000&lt;/code&gt;. Lets time them both, shall we?:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install requests
python
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt;import timeit
&amp;gt;&amp;gt;&amp;gt;import requests
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; # gin / go
&amp;gt;&amp;gt;&amp;gt; timeit.timeit("requests.get('http://localhost:8080')", setup="import requests", number=10000)
21.956489593023434
&amp;gt;&amp;gt;&amp;gt; # flask / python
&amp;gt;&amp;gt;&amp;gt; timeit.timeit("requests.get('http://localhost:5000')", setup="import requests", number=10000)
34.483878459897824
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Cool. So as you can see the the Go option is ~36% faster! That's amazing, no wonder people are going for go &amp;gt; python these days. But then again this isn't the most fair test as we can put a multithreadable WSGI server in front of our python setup. Now onto round two...&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a new fangled benchmarking tool called wrk
&lt;/h3&gt;

&lt;p&gt;and multithreading all the things! Thanks to a great suggestion by a dedicated reader who tweeted in, I am also adding in a multhreaded benchmark and adding gunicorn to sit in front of our Flask app.&lt;/p&gt;

&lt;p&gt;In order to get up to speed with me you'll have to download and make the binary for wrk using the instructions found &lt;a href="https://github.com/wg/wrk/wiki/Installing-wrk-on-Linux"&gt;here&lt;/a&gt; and if you look there are more options on the side if you're a Windows/Mac user. Also we are going to wanna &lt;code&gt;pip install gunicorn&lt;/code&gt; as well and in the flask-stuff directory we are going to want to type &lt;code&gt;gunicorn -w 8 main:app&lt;/code&gt; which tells gunicorn (our multithreaded WSGI server) to use 8 threads (since the output of &lt;code&gt;nproc --all&lt;/code&gt; on my machine was 8) and it says look at the &lt;code&gt;main.py&lt;/code&gt; file and the &lt;code&gt;app&lt;/code&gt; function as the entrypoint for the app.&lt;/p&gt;

&lt;p&gt;Now down to benchmarking the two using "a real benchmarking tool" - Some guy named Ben.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  go-hello wrk -t8 -c32 -d30s http://127.0.0.1:5000 # flask-python
Running 30s test @ http://127.0.0.1:5000
  8 threads and 32 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.58ms    1.70ms  42.16ms   94.75%
    Req/Sec     1.12k   158.54     2.83k    84.48%
  268403 requests in 30.10s, 45.82MB read
Requests/sec:   8917.30
Transfer/sec:      1.52MB
➜  go-hello wrk -t8 -c32 -d30s http://127.0.0.1:8080 # go-gin
Running 30s test @ http://127.0.0.1:8080
  8 threads and 32 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.64ms    4.71ms  60.39ms   85.07%
    Req/Sec     6.86k     0.93k   33.27k    82.09%
  1639477 requests in 30.10s, 220.46MB read
Requests/sec:  54471.39
Transfer/sec:      7.32MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Okay, maybe ben was right, this really shows how much better go is than python. Surprisingly though, the one thing python did have was a more deterministic response time as the stdev of the latency was &amp;lt; 2ms where it was almost 5ms in go.&lt;/p&gt;

&lt;p&gt;But once again, this is another benchmark proving out go is faster than python. Req/sec wise go is 6.125 times faster than flask, on average.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarking for Docker image size.
&lt;/h2&gt;

&lt;p&gt;Alrighty then. I'm just gonna go ahead and say it that this post is TOO freaking long already and you're just not going to get me to breakdown the multistage docker builds I iterated through in order to get the smallest image size for each one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python Dockerfile
&lt;/h3&gt;

&lt;p&gt;Lets put this file in the &lt;code&gt;flask-stuff/Dockerfile&lt;/code&gt; directory with the contents below:&lt;/p&gt;

&lt;p&gt;Our structure should look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;flask-stuff

&lt;ul&gt;
&lt;li&gt;main.py&lt;/li&gt;
&lt;li&gt;Dockerfile
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.6-alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;flask
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;gunicorn
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["gunicorn"  , "-w", "8", "main:app"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In order to build this you'd type &lt;code&gt;docker build . -t benchmark/py&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go Dockerfile
&lt;/h3&gt;

&lt;p&gt;Lets put this file in the &lt;code&gt;go-stuff/Dockerfile&lt;/code&gt; directory with the contents below:&lt;/p&gt;

&lt;p&gt;Our structure should look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;go-stuff

&lt;ul&gt;
&lt;li&gt;main.go&lt;/li&gt;
&lt;li&gt;Dockerfile
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; golang:latest&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go get &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/gin-gonic/gin
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux go build &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-installsuffix&lt;/span&gt; cgo &lt;span class="nt"&gt;-o&lt;/span&gt; main .
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:latest  &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk &lt;span class="nt"&gt;--no-cache&lt;/span&gt; add ca-certificates
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=0 /go/main .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./main"] &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In order to build this you'd type &lt;code&gt;docker build . -t benchmark/go&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surely what are the results?!?
&lt;/h3&gt;

&lt;p&gt;I have the results, and my name's not Shirley! Bad joke, I know, but I'm trying to keep you awake here.&lt;/p&gt;

&lt;p&gt;Alright so here's the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  pygo-benchmark docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
benchmark/go        latest              02ef15405471        About an hour ago   21.7MB
benchmark/py        latest              8fd24d5e54f1        About an hour ago   89.6MB
python              3.6-alpine          83d065b0546b        19 hours ago        79MB
golang              latest              901414995ecd        2 weeks ago         816MB
alpine              latest              caf27325b298        3 weeks ago         5.53MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now this is astonishing! The &lt;code&gt;benchmark/go&lt;/code&gt; image is ~76% smaller than the &lt;code&gt;benchmark/py&lt;/code&gt; image. This is because of a few reasons, go literally just needs that one binary &lt;code&gt;main&lt;/code&gt; to run, where as python needs all of the supporting libraries because it isn't compiled.&lt;/p&gt;

&lt;p&gt;If we used the golang image as a base instead of the alpine image as a base for our &lt;code&gt;benchmark/go&lt;/code&gt; image it would be ~835MB. The reason why Go docker images are so small is because you can run them directly on an alpine image, which is ~5MB.&lt;/p&gt;

&lt;h1&gt;
  
  
  Questions, comments, concerns
&lt;/h1&gt;

&lt;p&gt;Feel free to click one of these buttons in order to signal me with something that was messed up with this. I'd be glad to fix anything that didnt work for you.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>go</category>
      <category>python</category>
    </item>
  </channel>
</rss>
