Showing posts with label go. Show all posts
Showing posts with label go. Show all posts

Wednesday, March 11, 2020

Running a rich Microservice constellation in Kubernetes

Languages have trade-offs like any solution in tech. Microservices are great for many reasons, one of them allows the best tool for the job. Kubernetes allows us to have the same operational standards and procedures even using different languages and solutions. For this blog post, I want to show a simple project I build which is a constellation of services. There are 5 microservices, written in several languages like Scala, Java, Go, Python and Rust. The services do a pretty basic math operation like (+, - , / , *) and the Scala one does the aggregation and orchestrate the other services using a polish notation algorithm and REST calls.

The Video



Calc Services (5 Microservices Scala, Go, Rust, Python, and Java) running on K8s from Diego Pacheco on Vimeo.

The Source Code

The complete source code is here on my github.

Cheers,
Diego Pacheco

Saturday, April 20, 2019

Kind - Kubernetes in Docker

Kind - Allow us to run Kubernetes in Docker. This is not a production-ready solution - however, has a lot of potential in order to make a safe and lightweight alternative option to Minikube.  Kind is built with Go and uses Docker API in order to get things spinning.
I got impressed with the speed thing runs with kind, this is an interesting alternative together with k3s and Minikube.




Getting Started with Kind



Cheers,
Diego Pacheco

Monday, July 30, 2018

Experiences Building a Cassandra Orchestrator(CM)

Cassandra is a kick-ass NoSQL database. Battle tested by Netflix, Apple, Uber and so many great cloud scale companies. If you want to run Cassandra in production you need to buy DataStax Enterprise or engineer your own solution - since Cassandra community is not enough. My company loves open source and my customers love open as well so we decided to build our own Cassandra orchestrator for a lack of better name we called it CM(Cassandra Manager).  CM runs on AWS(Ec2) and managed single and multi-region clusters for Cassandra. CM has java interfaces so even today it just runs on AWS it could easily be ported to in containers or other fabric runtimes, which may happen on the long-term future. So it might sound crazy when you think about to build engineering around Cassandra but is actually not several companies in Silicon Valey like Netflix and outside of the valley do similar things or Databases being NoSQL or Relational. Automation is must if you want to scale, automation is way more them "automated deploys" that's easy the ultimate automation is when you automate your operation and them allow you to truly scale.

Why Build CM?

Basically, we won't be able to Scale AWS Single and Multi-Regional deploys and Operations so we don't need to code any Jenkins or bash script in order to serve more clusters. Before CM we were doing backups with Jenkins jobs and we did not have ASG on top of Cassandra nodes so cloud ops team always had issues when a node goes down and had to run bash scripts to put the node back online with CM all that changes. The single region is not that complex but when we talk about multi-region things gets more complicated. So automation is really the solution.

Who we build complex system on the fly? 

In order to Build CM we had to some POCs and also drawn some Color UML diagrams based on Responsibility-Driven Design(RDD). This might sound a bit old school and design up to the front, however, there is no way to build such a system without some up to front thinking and strong design and architecture leadership. My team works with Kanban and I do multiple roles in my team playing as Tech Manager / Agile Coach, Engineering, Architect, and DevOps Engineer Testing and Developer Support. My team does multiple roles as well so an engineer in my team does Software Engineering, DevOps Engineering, Testing and Developer Support. I and my team work with the idea of Self Service systems where a developer could easily consume what we do, so there are generic Jenkins jobs and good documentation is the place(Internal Wiki).

We use Kanban and track stories per week with simple math items/weeks we easily could keep develop and deliver CM in time. CM took 3 months to get it done. Thanks to our Kanban management, and internal system design with RDD and Color UML we were able to work normal hours. However, I underestimate the effort to get the system STABLE and the stabilization period that should be 2 weeks become 2 months. During CM tests me and my team found ~30 bugs in CM. I work with a small team right now(me and 2 people) when we start coding CM was 4 people and 2 guys left so after stabilization all gets a bit complicated and we had to work several hours in order to keep with new team and lots of CM bugs this was a bit stressful but we overcome that and deliver to production :D

It's all about Availability

When I was thinking about CM I knew we will need to make CM highly available. In order todo, we made CM optinional so Cassandra can operate without if CM dies or crashes after deploy. In case CM dies or crashes Cassandra keep operating normally and since CM is an orchestrator developer and application who w/r in cass don't see cm. So if CM goes down we lose some operations like:

  • Backups
  • Repairs
  • Node_Replaces
CM has an Autoscaling group for it and persists its internal state in S3 so we build a recovery process for CM in case that happens ASG will spin a new ec2 instance and CM will keep running again. CM was built with multiple layouts in mind so you can have 1 CM for several Cassandra clusters since CM has it own thread Pool or you can have 1 CM per Cassandra cluster, this is great because it provides more availability and allows us to save costs in low production environments like DEV and TEST where we can use shared CM or in production for small clusters we can share CM as well. For clusters that are sensible, they can have they own CM. This is so true because we had an outage in production with CM, however, Cassandra was not affected and was running fine. All this can be seen as Reliable or Anti-Fragility system and as Failure Degradation mode(meaning CM fails don't make cass fails) all great Netflix ideas we stole :D 


CM Philosophy - Self Healing and Self-Operating

CM was a set of philosophies like Self Healing and Self-Operating system. Ops teams are used to Operate systems in production due to the lack of built-in philosophy. CM has a different philosophy so basically, you don't operate CM - CM does everything by itself(Unless there is a BUG in CM - them you need RECYCLE / OPERATE CM).  CM was highly inspired by Dynomite, Dynomite-Manager, and Priam from Netflix, Although CM has a different architecture. I worked a lot with Dynomite and Dynomite manager on the last 3 years and this experience was very important and allow me to get it here.

Why not use Priam? Priam is great don't get me wrong. However, for my use case I need to support Cassandra 2, Cassandra 3 and soon Cassandra 4(when gets released). Priam has support for Cass 2 and cass 3x, however, Priam has branches and Netflix is migrating the whole fleet to latest Cassandra and we want to have a more long-term support for cass 2 and cass 3 and have our time windows. Priam is a co-processor also know as sidecar and Cassandra Manager is an orchestrator meaning it doesn't have in the same node as Cassandra so we can precisely control what runs in each not and when.

We don't have run multiple things on same time for same clusters like backups and repairs so we want to make TAKS run SERIALIZED like 1 at the time per cluster and parallel within multiple clusters in CM - this will be better covered later on this post.

Jenkins Builds



There are any Jenkins jobs - Basically, there are 2 main jobs. 1 to bake AMIS we can bake amis for Cassandra 2x, Cassandra 3x and CM. Them we have a job for lunch CM. For Devops Engineering we use Ansible in order to provision things in Amazon-Linux OS(Our Cassandras are running in Amazon-Linux latest version - we don't use ubuntu anymore).  We use Packer in order to build the AMI for AWS ec2 - We do build in 2 regions. We create LC, ASG and EC2 instances for CM. Once CM is deploying we use CM to deploy Cassandra clusters. CM has java code in order to provision all Cassandra clusters this way we do almost all work in Java, so we have less code in bash as possible and this is great because we take advantage of great troubleshooting infrastructure and tools for Java and also the JVM compiler and good IDEs for refactoring like Eclipse.

Flacky Integration Tests

Since almost all the system is built in java it was very easy to build integration tests. We had to have 2 Tests Suites. One suite for Cassandra 2.x and other for Cassandra 3.x We were able to have the same code and same integration(end-2-end-tests) for cass 2.x and 3.x. However, we had to create a configuration that in the beginning of the sets the AMI and configs for cass 2x and later one switch to cass 3.x. My goal was to use Junit 5, however, looks like more and more Junit is moving away from integration tests and remove suite features and runners(we don't want to use legacy support thanks) so we end up using JUnit 4 which was better for integration tests.

Integration tests stated great and they easily were taking 20min or more to run since we deploy real Cassandra clusters(2x and 3x) during tests and we need to wait for aws to create objects and later on in the same suites we shut down this cass clusters. We test all major capability like backups, restore, seeds manager, restore. So as expected out tests get Flacky. Flacky tests suck. The main issues with our tests(discussed in an Agile Retrospective) were:

1. Timeouts are evil - We can't keep tracking timeouts - So we decide to switch to progressive timeouts.
2. Lack of 9 Rules: In math we can veryfied everything using math so we can check if is correct. we was not doing that in the tests and also in several tasks on the code. So if you dont 1 like that create a file - next line should check if the file as proper created.

We still have lots of work to improve our automated integration tests. What really saved us for the release was combination of Chaos Testings with Exporatory tests and checklists. I don't want to paint a rosy picture about CM so I still have mixed feeling with integration tests and we lost so much time with this.

Generic Remediation for Cassandra

Since dynomite, I had build remediation systems, So which Dynomite I was able to replace AWs AMI or scale UP dynomite without data loss or downtime. This was possible because 2 things: First because dynomie-manager had cold bootstrap feature and second because I coded a remediation process which would call DM(Dynomite-Manager) health checker and check if the node is ok and not doing anything and them kill the node wait for the node finish bootstrap and move on and so on and son. I basically refactord this code(which was and is written in java) and extend it to be a generic Gemeriation so same code wotks for Dynomite and Cassandra. We have some properties we can remediatie any cassandra node without data loss or downtime, who? Same idea we have the generic generiation process going node to node but calling CM(Cassandra-Manager) health-checker and asking if CM is not doing anything with thay node them we run a node_repair in cass so data if copied from other node this is very similar to a node_replace operation when a node dies. If you want leran more about this I made a separated post in the past its here and here.

CM Architecture


CM is built primarily with Java 8 and Inside of Cassandra node we have a Heart-Beat daemon written in Python which calls CM and decides with the node need to check in in CM(new node) or recovery(meaning node_replace) in cass.

We deploy high available Cassandra clusters where we have at least 1 node per AZ begin at least 3 nodes cluster. For multi-region, we do at least 6 nodes cluster. CM replicate its internal state to other CMs meanly in other regions in order to sync multi-region check-in for seeds management. CM was build using PEM files(Cassandra-Nodes) and we are currently looking to change this code in order to use AWS Multi-Region VPC Peering feature so we can switch from EIP and public IPs to private IPs.

Let's take a closer look at CM architecture now.

CM Architecture - More Internal Details

Basically, we have REST interfaces exposing all CM core functionality - This interfaces can be accessed with any REST tool - we build ours on a tool called CMSH which was built in Go and will be cover later on in this very post.

CM has a core class called Internal State Manager which managed all Cassandra clusters and state transitions. There is a bunch of QuartzTasks which are responsible for doing backups, repairs, restore, seeds management and so on and on. Internal State is backed up to S3 and time to time CM sends internal metrics to SignalFX. We use collected to send all OS level metrics and CM calls Injection API in Sfx in order to send application metrics.

CM has an internal Tracker class which knows what every class is doing we have an internal Framework called Step Framework which keeps tracks of all sub-task level execution state and stores metadata internal. Later one this internal state in CM is persisted in disk and time to and also sent to S3. In the future, we will backup CM state in Cassandra nodes.

When CM talks to Cassandra it uses SSH in order to manipulate Cassandra filesystem and run local nodetool for backup and repairs. All heavy work happens in cass node, not in CM - Cm just does light state and coordination work. Even s3 Upload is done in cass node, not in CM.

CM Thread Model and Issues

First Thread Scheduling Implementation was using Quartz and Reflections and we realize this was open too much treads. Basically, you want to open your threads up to the front and they just re-use them like a Thread-Pool. However, with CM we have an interesting problem since we don't know how many cass clusters we would need to deploy. For the first implementation was creating a group of threads to run each cluster operation so was basically 4 threads per group. However we were running this on Quartz Threads without the need so first refactor was move some of the work that was in quartz threads like Heart-Beats, State Request, HealthChecker to REST operation only since they only interacted with cm internal state and was a very short living process. Second refactoring was changed quartz and open just one time the pool, however, this created a problem because if we have a big pool we would run things(TASKS) in parallel that we did not want like(backups and repairs) and if we put 1 thread it would be inefficient since the machine could handle more load and we would have 2 different Cassandra clusters competing with same resources. So it was clear that we need a new thread model.


CM Old Thread Model - Thread Issues

So basically the new Thread model would need to provide the following properties/requirements:
  • Within the same cluster be serial - run tasks in sequential arrival order
  • Within different clusters - run all in parallel
  • Run CM tasks in parallel - SignalFX metrics, Internode communication, Recovery.
  • Don't break current TASKS - we don't want re-write all tasks code(quartz tasks)
  • Replace Threads from Tasks and reuse-tasks
Whats the solution? Simple we use QUEUEs and Workers. So we create and destroy Queues and workers all the time(As a Cassandra clusters do check-in(created) and checkout(destroyed). In order to make this work I had to create a QueueManager and a WorkerManager, So the QueueManager is responsible to assign Tasks to Queues and the WorkerManager to allocate workers in Threads from the pool. This solutions turn out to be more efficient and provided all design requirements we want.


CM New Thread Model - Queues and Workers

Now Quartz is gone and all threads are managed by java concurrent Executors. There are 4 main thread pools. 2 Pools for Cassandra Clusters and 2 Pools for CM. 1 Pool is for Queue and 2 Pool is to schedule recurrent tasks(like backups and repairs) on the queue. Since is a queue one you consume it there is nothing more to do there.

Footnote: Back in 2010 I built a Scala Middleware using this concepts and ActiveMQ and JBoss 5 so I basically apply similar ideas on a smaller scale.

CMSH - Build a REPL in Go


If you work with Cassandra you might be familiar with cqlsh. Cmsh is like Cqlsh but for CM and for Cassandra clusters. Cmsh is written in go, it allows us to stress tests Cassandra and create a schema, insert data and remove data and also plays a role and centralized control panel for CM so we can trigger backups and repairs from cmsh and we can see logs can all cm rest operations pretty easily. CMsh is a REPL.So its much more productive them ssh each box and run alias scripts. This is also created because we can add functionality without having to rebake Cassandra or cm amis.  Cmsh is very productive because you can connect in multiple cms with cmsh and also there is tab autocomplete, reverse back search, persistent history and much more. First was coding my own REPL later one moved to iShell which was great and save me lots of time.

Whats Next

There are many cool and challenge aspects of CM that was not covered in this post like Telemetry and Self-Healing, Multi-Region deploys and Checking, TTL Eviction for Nodes, Stress Testing CM, Unit Testing, Tuning and optimizations and much more this might be covered in future posts. Now the main focus of my team is to keep improving CM and support VPC Peering for multi-region deployments maybe later on this year we might do experiences with Containers(Docker).  although I'm the tech leader and lead engineer this is a team effort and I want to thank Jackson and Tarzan for the hard working hours and fun for working together as an awesome team and tech challenged that is CM.

I hope you guys like it, take care.

cheers,
Diego Pacheco

Friday, July 13, 2018

Writing a simple REPL in Go

Go is a great language for writing tooling. It's very easy to produce a self-contained binary in go which makes very easy to distributed tools written in go. Today I want to show how easy is to write a simple REPL in Go. REPLs are quite popular in Functional Programming community like Scala, Clojure, and Haskell. Today even Java has a REPL :-). Cassandra has interesting REPL called CQLSH which allow local and remote connections. Currently, I'm thinking to write REPLs for some things I´m working with. REPLs are great for productivity and context. No to mention the could reduce and simplify discovery and troubleshooting. You might be thinking Gosh to write a REPL is tough we need to have parsers, print things nicely with colors, use tab completion, remember all previous commands. and one and on... I'm a full fledge REPL might require more work however a basic and simple version is something we can do in less them 5 minutes so I won't be doing any complicated parser or parsing combinators here, however, I will show how easy we can hook simple commands and expressions in a smooth command line experience. This is not a production ready REPL but some simple ideas I want to share.  Go has some nice APIs to deal with IO, for Simple Math and Boolean Expressions I will be using Govaluate. The only dependency(besides the core language packages) we will use will be Govaluate. So, in order to install Govaluate, we need to run in the console: $ go get github.com/Knetic/govaluate I'm only using it here because I show how to do math expressions but depending what you are doing you really won't need it - Feel free to drop it and have a light binary in the end :-)


Down to the Code

Let's take a look on the code - main.go


Basically, we will write our code in a loop. We will keep waiting for commands until the user type "exit". Go does not have a while or do-while commands so we need use for. Go can be a bit verbose sometimes(Nothing compared with Java) but a trick that works for me is creating lots of functions.  With functions, we can HIDE some details and also end up having a code that is easier to READ. I will use a Map where the key will be a String and the value will be an Interface{}. So don't have proper generics so when we need do things in a generic way we need to use Interface[]. So this map will map String alias to functions. Functions will be actual command implementations. I defined my functions before the main function so when you see a function as a value on the command maps you now function is defined outside of the map. 

The code will check if what the user type on REPL contains on the map, so let's say the user type "cls" which is the command to clear the terminal screen. This key will be on the map so we will get Interface{} back, now we need the call the function which is held as Interface{} so the syntax is a bit strange but is something like: variable.(func())() So here we are CASTING the Interface{} to a function which no arguments and we are calling the function. 

The code makes OS interaction easy we only need to do exec.Command("my Linux command") and that's it we are executing OS level commands and scripts. This happens inside the cls function. In case that we cant find the function on the Map we go to a callback function. So the fallback function will ignore the ENTER key by replacing "\n" on the string. So, even so, we still have one more fallback so it might be a simple math expression so I will try to evaluate the text with Govaluate however it could panic. In order to make the program safe we need use DEFER and RECOVER. Which is the right way to handle PANIC in go. Defer will make the function be pushed to the end of the stack and recover will only return data in case of PANIC.

Here we have some basic functions like help, cls and time. Help just print what are the available functions with some hints and basic documentation. Time uses time. Now in order to get current Date / Time in Go. Exist is not a function but a condition to break the for loop that you can see on the function shouldContinue. you might realize the code in full of underlines "_" often used when you want to ignore some return value. Go don't have exceptions - so you need return and deal with errors on every function call. Sometimes you don't handle the error or you can handle it later so you can use "_" to ignore the variable. As you might realize go allow you to return multiple values in functions.

If you want you can run our REPL doing $ go run main.go or if you want to create a single binary you just type $ go build on the same folder as you have the main.go file. So now we have a simple REPL working with less tham 100 lines of code.

Cheers,
Diego Pacheco







Tuesday, June 19, 2018

github-fecther: Checking new Repos from Github with Go

Go lang is a very interesting language. Github already is the central hub for the open source on the Internet. Companys have lots of repositories and it's very hard to catch up nowadays. So I build a simple silly tool in Go in order to fetch all repositories a company might have and they compare with the previous fetch and see if there are new repositories.  Thanks to GO elegance I was able to do that with less than 200 lines of code. In order to do what we need to do, we will use some basic functionality in Go such as IO, Http Calls, JSON support for Structs, Slices, Maps, Error Handling(Which is the sucking part) and a bit of logic. This program can fail since I'm not using a proper TOKEN for github so you will HIT the github Throttling limits pretty quickly if you keep running the program. You can create a proper DEV account and fix the code to use your token or you can schedule the program to run in different times in your crontab. This is not production ready, but it's a way to have fun with go and build something somehow useful :D Let's get started!


github-fetcher: How the Program Works?

First of all the program looks into the DISK to see if there is a JSON file(using the name of the organization you want to check) and if there is that JSON file is loaded to memory. IF there is no JSON file in DISK(First time you run for each organization) is fine.

After Loading the file from DISK if present we will go to the Github API and fetch all repositories from the Organization you pass by the parameter. GIthub organization / repos API is paginated. So I do multiples calls in a loop in order to get reports from all pages. Nowadays is very common to have companies with more than 100 repositories.

After getting the repositories from Github API we will compare the repositories from Disk(previous run) with repositories from the site. This will give us a DIFF - which will be new repos or deleted ones. Then the JSON is updated in DISK with current run.

Getting the Code and Running 

Download the main.go file them we can run in 2 ways. We can do: go run main.go facebook or we can build a binary file by doing: go build and then we can run with: ./github-fetcher facebook. When you run it you will see an output like this:



The Go Code



So let's go through the code. First of all, we are importing the libs we need for this code. After imports, we are creating a Struct called Repo. Here we are using an interesting Go lang feature which allows us Map JSON to Structs and vice versa. Github API has many attributes but I just care about the repository full_name so that's why there is just 1 field there.

There is a function called extractRepos which receives the pagination and the organization name. This function returns 2 things: A slice(which is like an Array but not) of Repo and error if happens. This is how we do error handler in go - since there are no exceptions, every function needs to return 2 things.  I do the HTTP call and parse the result. You can see there is a json.Unmarshal which receives the http body content and a pointer reference to an slice of repos called &repos. So &repos means we are pasing repos by reference not by value. In the previous line, you might realize we are using the make function - that's there in order to create an Array.

The next function is getAllRepos which will call extractRepos with different pagination until we receive an error - this is how I know how many pages are there. You might realize when I call extractRepos I have repos, _ this means repos will be the array of repos and _ is the error, since I won't ignore that I use underline. The current repo is appended to the array of repos - this is done by using the function append where we pass the 2 arrays we want append and this results in a 3rd array.

Next function is persistInDisk, here we receive a path(which is a string) so this is the location where we want to persist in the disk and receives an []Repo - this is an array of Repos. Here we are using json.Marshal and passing the array of Repos in order to transfer our array of Struct Repo in JSON string. Them we use io.copy yo copy to the file in the disk and persist it.

Next function is loadFromDisk which also receives a path but now there is *[]Repo which is a pointer to Repo array. We need that because we will load a value by reference. We will read the content from the file and decode it to JSON and send back to the array struct.

Next function is the diff one. Here we receive 2 slices(arrays) which will be the array from DISK and the array from github call. In order to get the difference, we will do the following algorithm. First, we will loop throw the first array and add all items on a first slice(array from disk) to a map which the key will be the repo name and we will assign a counter - for this loop will be 1 to all keys. Them we will be doing the same with the other slice(from github api call) however now we will get the value from ma ap if exit and add 1. If we got a duplicated key the value will be 2 otherwise 1. Finally, we loop throu our map and find keys where the counter is 1 so this means they are unique and thats waht we want this is the diff. Right now the algorithm doesn't make difference between new repos and deleted ones this could be pretty easy to do just by checking the source of the number or by using the different number when is the second array.

Finally the main function. Here we orchestrate the main flow described previously in this post. We are getting the organization name by parameter doing os.Args so we get from command line arguments. We call other functions and if there are errors I dont proceed.

Thats it!

Cheers,
Diego Pacheco

Monday, June 18, 2018

Running Gorilla/Mux in Docker

Go is a very simple and powerful language. I like Go.Go performance is amazing. I don't like that Go don't have proper Generics and Exceptions but besides that is all great. Go is great for DevOps because go generate a simple binary that is executable and this is really awesome.
Today I want to show to build a simple counter service using Go, Gorilla/Mux and Docker. Gorilla/Mux is a simple HTTP Router for Go. Using native JSON support from Go + Mux you can build some simple and yet powerful services.
We will use sync/atomic in order to make sure if we have a code that is threadsafe.  You will need to have Go and Docker installed in your machine.  We will use Go language version 1.10 and latest Gorilla/Mux. Let's get started!




The Dockerfile


Here we are using Go 1.10 docker image as the image. We are using to get in order to install gorilla/mux. Now we can build the docker image. We just need to run the command: $ docker run diegopacheco/go-counter.

Go Code

Go code is so simple. Here we create a router and add a simple route to "/" path. We will run the server on port 8080. As you can see here we are using sync/atomic in order to create a thread-safe integer. We are using the method atomic.AddUint64 in order to safe increment it.  This is done the function called Counter. Which just get the integer and increment and return.


Now we can run this docker by doing: $ docker run diegopacheco/go-counter then you can go to your browser and see the counter working :D

That's it. You can get the whole code here. As you can see go is simple and object which very few lines of code we can do a lot.

Cheers,
Diego Pacheco

Monday, August 7, 2017

Go and Redis running on Kubernetes with Minukube


Go is a simple, fast and powerful programing language. Go is growing a lot into the DevOps Engineering scene like Hashicorp Stack or even Kubernetes.  One of the main advantages of GO is the fact that you can generate a single binary with all you needed, bundled in a single file. This makes distribution so much easier.

Go is also very compact for some use cases and you can write so less code and still very very efficient and get best of performance.  Today we will see how to create a very, very, very simple service in go. This service will access redis to increment how many times it was called.  We will use Minikube in order to run kubernetes locally and we will store our data in Redis.




The Go Service

Let's get down to the code.

Here we are using an external library called go-redis. We need this library to communicate with Redis using Go. You can run this code on your machine right now but first, you need to install the Redis driver you can do it so by running $ go get -u github.com/go-redis/redis .Now you can simply run the app with $  go run main.go .Keep in mind this won't work right now because we don't have Redis running. You can download, install and run Redis or you can use docker. We will run redis with docker but on Kubernetes using Minikube.

As you can see we are exposing a service on the port :9090 and he have a handler function which takes care of the HTTP request to this very server. We want to avoid double counting for this service and some user might call it from the browser so we need to ignore the /favicon.ico request that's why we use the if on the very first lines of the code.

We are connecting on Redis but we are reading Redis URL from an OS env var. I'm doing this so we can run this code in many ways like locally, docker, docker-compose, and kubernetes. IF you have Redis locally now you can do $ REDIS_URL=localhost:6379 go run main.go .For this simple service, we are using Redis commands INCR and GET in order to increment keys(+1) and get the current value of the key.

Go Dockerfile

Right now we need to create a Dockerfile to run our go simple service. In order to do that we will create a Dockerfile file and add the following content:

Dockerfile is quite straightforward we are using GO lang 1.8 and we are installing the Redis driver and we copy the go source code to the container.

Kubernetes deployment

Now we need create 2 yaml files in order to describe the kuerbenetes deployment and Service for the go web app. However before doing that we will create 2 other yaml files in order to deploy Redis so our redis docker container runs on kubernetes as well. We also link connect this 2 containers using kubernetes internal DNS service.

Redis Deployment on Kubernetes

Let's create a file called: redis-deployment.yaml.

We also need to create a service. So create a file called redis-service.yaml.

The most important information here is that we are using the image called redis. We are also setting the spec on the service for LoadBalancing on the port 6379 and this means we will be able to access outside of the cluster. This is not required but is quite useful so you can use redis-cli and access the redis instance on Kubernetes.

Right now we can create the deployment and service yaml files for the web go simple service application.  So first create a file called: webappgo-deployment.yaml


Then, create another file called webappgo-service.yaml.

Here we have some important things that need to be noted such as the image which is: webappgo:v1. This image does not exist on public docker repo so we need to build on our local machine and send the docker image to minikube dockers registry.

When we write the go code we were expecting to receive the redis URL via OS ENV var. We can set this vars via Kubernetes and kubernetes will forward this vars to Docker we did this on the property called env. You might notice some specific name for the redis url which is: redis.default.svc.cluster.local:6379 .This is the kubernetes internal DNS. Where redis is the name of the service, the default is the default namespace and cluster.local is the domain.

Another important thing to notice is that the spec type on the service in LoadBalancer so we are exposing the port 9090.

kubectl and Minikube

okay, now we can go on and deploy theses files in kubernetes on our minikube local cluster.

There are some important steps here.  We are doing eval on minikube docker-env so we can use docker commands on out machine but send docker images to minikube docker registry. We are also baking a docker images for the go service application.

You might notify we are using kubectl create -f and passing a directory. This is created because you don't need to specify a file by files and long as you have redis and go deployment and services files properly in each respective directory.

Now we can run the GO simple service in our browser. You might need to run  $ kubectl describe services webappgo to get the proper port mapping.



IF you go to the minikube dashboard(http://192.168.99.100:30000/#!/pod?namespace=default) you can the see the GO service LOGs, like this:



You can get all the files in my github.

Cheers,
Diego Pacheco

Monday, October 26, 2015

How to Install GO Lang on CentOS / Amazon Linux?

Go Lang is growing a lot.  It`s a C on Steroids language. You have great performance and also some nice features that are built in on the language. In this post i will show how to install and configure GO on Amazon Linux / CentOS based.

Go has Closures, so you can have performance and still do some kind of functional programming.  There are lots of infrastructures tools being built on Go as well.  Besides go have great multi-core support and async mechanisms.

Lots of great infrastructure solutions use go like Docker, CoreOS, HarishCorp solutions.  Le`ts see how install and configure it on Amazon Linux(AWS Cloud).




Go on Linux

Cheers,
Diego Pacheco

Chuyên mục văn hoá giải trí của VnExpress

.

© 2017 www.blogthuthuatwin10.com

Tầng 5, Tòa nhà FPT Cầu Giấy, phố Duy Tân, Phường Dịch Vọng Hậu, Quận Cầu Giấy, Hà Nội
Email: nguyenanhtuan2401@gmail.com
Điện thoại: 0908 562 750 ext 4548; Liên hệ quảng cáo: 4567.