Moving monolithic to microservices

Typically, application architecture was the monolithic design that contains Model-View-Controller (MVC) and every component within a single big binary. Monolithic has some benefits, such as less latency within components, all in one straightforward packaging, and being easy to deploy and test.

However, a monolithic design has some downsides because the binary will be getting bigger and bigger. You always need to take care of the side effects when adding or modifying the code, therefore, making release cycles longer.

Containers and Kubernetes give more flexibility in using microservices for your application. The microservices architecture is very simple that can be divided into some modules or some service classes with MVC together.

Moving monolithic to microservices

Monolithic and microservices design

Each microservice provides Remote Procedure Call (RPC) using RESTful or some standard network APIs to other microservices. The benefit is that each microservice is independent. There are minimal side effects when adding or modifying the code. Release the cycle independently, so it perfectly fits with the Agile software development methodology and allows to reuse these microservices to construct another application that builds the microservices ecosystem.

Getting ready

Prepare the simple microservices program. In order to push and pull your microservices, please register to Docker Hub (https://hub.docker.com/) to create your free Docker Hub ID in advance:

Tip

Attention: If you push the Docker image to Docker Hub, it will be public; anyone can pull your image. Therefore, don't put any confidential information into the image.

Getting ready

Docker Hub registration page

Once you successfully log in to your Docker Hub ID, you will be redirected to your Dashboard page as follows:

Getting ready

After logging to Docker Hub

How to do it…

Prepare both microservices and the Frontend WebUI as a Docker image. Then, deploy them using the Kubernetes replication controller and service.

Microservices

  1. Here is the simple microservice using Python Flask (http://flask.pocoo.org/):
    $ cat entry.py
    from flask import Flask, request
    
    app = Flask(__name__)
    
    @app.route("/")
    def hello():
        return "Hello World!"
    
    @app.route("/power/<int:base>/<int:index>")
    def power(base, index):
        return "%d" % (base ** index)
    
    @app.route("/addition/<int:x>/<int:y>")
    def add(x, y):
        return "%d" % (x+y)
    
    @app.route("/substraction/<int:x>/<int:y>")
    def substract(x, y):
        return "%d" % (x-y)
    
    
    if __name__ == "__main__":
        app.run(host='0.0.0.0')
    
  2. Prepare a Dockerfile as follows in order to build the Docker image:
    $ cat Dockerfile
    FROM ubuntu:14.04
    
    # Update packages
    RUN apt-get update -y
    
    # Install Python Setuptools
    RUN apt-get install -y python-setuptools git telnet curl
    
    # Install pip
    RUN easy_install pip
    
    # Bundle app source
    ADD . /src
    WORKDIR /src
    
    # Add and install Python modules
    RUN pip install Flask
    
    # Expose
    EXPOSE  5000
    
    # Run
    CMD ["python", "entry.py"]
    
  3. Then, use the docker build command to build the Docker image as follows:

    Tip

    If you publish the Docker image, you should use Docker Hub ID/image name as the Docker image name.

    //name as "your_docker_hub_id/my-calc"
    $ sudo docker build -t hidetosaito/my-calc .
    Sending build context to Docker daemon 3.072 kB
    Step 1 : FROM ubuntu:14.04
     ---> 6cc0fc2a5ee3
    Step 2 : RUN apt-get update -y
     ---> Using cache
    
    (snip)
    
    Step 8 : EXPOSE 5000
     ---> Running in 7c52f4bfe373
     ---> 28f79bb7481f
    Removing intermediate container 7c52f4bfe373
    Step 9 : CMD python entry.py
     ---> Running in 86b39c727572
     ---> 20ae465bf036
    Removing intermediate container 86b39c727572
    Successfully built 20ae465bf036
    
    //verity your image
    $ sudo docker images
    REPOSITORY            TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    hidetosaito/my-calc   latest              20ae465bf036        19 seconds ago      284 MB
    ubuntu                14.04               6cc0fc2a5ee3        3 weeks ago         187.9 MB
    
  4. Then, use the docker login command to log in to Docker Hub:
    //type your username, password and e-mail address in Docker hub
    $ sudo docker login
    Username: hidetosaito
    Password:
    Email: [email protected]
    WARNING: login credentials saved in /home/ec2-user/.docker/config.json
    Login Succeeded 
    
  5. Finally, use the docker push command to register to your Docker Hub repository as follows:
    //push to your docker index
    $ sudo docker push hidetosaito/my-calc
    The push refers to a repository [docker.io/hidetosaito/my-calc] (len: 1)
    20ae465bf036: Pushed 
    (snip)
    92ec6d044cb3: Pushed 
    latest: digest: sha256:203b81c5a238e228c154e0b53a58e60e6eb3d1563293483ce58f48351031a474 size: 19151
    

    On accessing Docker Hub, you can see your microservices in the repository:

    Microservices

    Microservice image on Docker Hub

Frontend WebUI

  1. Here is the simple Frontend WebUI that is also using Python Flask:
    import os
    import httplib
    from flask import Flask, request, render_template
    app = Flask(__name__)
    @app.route("/")
    def index():
        return render_template('index.html')
    
    @app.route("/add", methods=['POST'])
    def add():
        #
        # from POST parameters
        #
        x = int(request.form['x'])
        y = int(request.form['y'])
    
        #
        # from Kubernetes Service(environment variables)
        #
        my_calc_host = os.environ['MY_CALC_SERVICE_SERVICE_HOST']
        my_calc_port = os.environ['MY_CALC_SERVICE_SERVICE_PORT']
    
        #
        # remote procedure call to MicroServices(my-calc)
        #
        client = httplib.HTTPConnection(my_calc_host, my_calc_port)
        client.request("GET", "/addition/%d/%d" % (x, y))
        response = client.getresponse()
        result = response.read()
    
        return render_template('index.html',
            add_x=x, add_y=y, add_result=result)
    
    if __name__ == "__main__":
        app.debug = True
        app.run(host='0.0.0.0')

    Tip

    Kubernetes service generates the Kubernetes service name and port number as an environment variable to the other pods. Therefore, the environment variable's name and the Kubernetes service name must be consistent.

    In this scenario, the my-calc service name must be my-calc-service.

  2. Frontend WebUI uses the Flask HTML template; it is similar to PHP and JSP such that entry.py will pass the parameter to the template (index.html) to render the HTML:
    <html>
    <body>
    <div>
        <form method="post" action="/add">
            <input type="text" name="x" size="2"/>
            <input type="text" name="y" size="2"/>
            <input type="submit" value="addition"/>
        </form>
    
        {% if add_result %}
        <p>Answer : {{ add_x }} + {{ add_y }} = {{ add_result }}</p>
        {% endif %}
    </div>
    </body>
    </html>
  3. Dockerfile is exactly the same as microservices. So, eventually, the file structure will be as follows. Note that index.html is a template file; therefore, put it under the templates directory:
    /Dockerfile
    /entry.py
    /templates/index.html
    
  4. Then, build a Docker image and push to Docker Hub as follows:

    Note

    In order to push your image to Docker Hub, you need to log in using the docker login command. It is needed only once; the system checks ~/.docker/config.json to read from there.

    //build frontend Webui image 
    $ sudo docker build -t hidetosaito/my-frontend .
    
    //login to docker hub, if not login yet
    $ sudo docker login
    
    //push frontend webui image
    $ sudo docker push hidetosaito/my-frontend
    
    Frontend WebUI

    Microservices and Frontend WebUI image on Docker Hub

How it works…

Launch both microservices and the Frontend WebUI.

Microservices

Microservices (my-calc) uses the Kubernetes replication controller and service, but it needs to communicate to other pods only. In other words, there's no need to expose it to the outside Kubernetes network. Therefore, the service type is set as ClusterIP:

# cat my-calc.yaml 
apiVersion: v1
kind: ReplicationController
metadata:
  name: my-calc-rc
spec:
  replicas: 2
  selector:
        app: my-calc
  template:
    metadata:
      labels:
        app: my-calc
    spec:
      containers:
      - name: my-calc
        image: hidetosaito/my-calc
---
apiVersion: v1
kind: Service
metadata:
  name: my-calc-service

spec:
  ports:
    - protocol: TCP
      port: 5000
  type: ClusterIP
  selector:
     app: my-calc

Use the kubectl command to load the my-calc pods as follows:

$ sudo kubectl create -f my-calc.yaml
replicationcontroller "my-calc-rc" created
service "my-calc-service" created

Frontend WebUI

Frontend WebUI also uses the replication controller and service, but it exposes the port (TCP port 30080) in order to access it from an external web browser:

$ cat my-frontend.yaml 
apiVersion: v1
kind: ReplicationController
metadata:
  name: my-frontend-rc
spec:
  replicas: 2
  selector:
        app: my-frontend
  template:
    metadata:
      labels:
        app: my-frontend
    spec:
      containers:
      - name: my-frontend
        image: hidetosaito/my-frontend
---
apiVersion: v1
kind: Service
metadata:
  name: my-frontend-service

spec:
  ports:
    - protocol: TCP
      port: 5000
      nodePort: 30080
  type: NodePort
  selector:
     app: my-frontend

$ sudo kubectl create -f my-frontend.yaml 
replicationcontroller "my-frontend-rc" created
service "my-frontend-service" created

You have exposed your service to an external port on all the nodes in your cluster. If you want to expose this service to the external Internet, you may need to set up firewall rules for the service port(s) (TCP port 30080) to serve traffic. Refer to http://releases.k8s.io/release-1.1/docs/user-guide/services-firewalls.md for more details.

Let's try to access my-frontend using a web browser. You can access any Kubernetes node's IP address; specify the port number 30080 as follows:

Frontend WebUI

Access to the Frontend WebUI

When you click on the addition button, it will forward a parameter to microservices (my-calc). Microservices compute the addition (yes, just an addition!) and then return the result back to the Frontend WebUI as follows:

Frontend WebUI

Get a result from microservices and render the HTML

So now, it is easy to adjust the number of replicas for the Frontend WebUI and microservices independently. For example, WebUI replicas range from 2 to 8 and microservice replicas range from 2 to 16.

Also, if there's a need to fix some bugs, for example, there's a frontend need to validate the input parameter to check whether it is numeric or string (yes, if you type string and then submit, it will show an error!); it will not affect the build and deploy the cycle against microservices:

Frontend WebUI

The frontend WebUI and microservices diagram

In addition, if you want to add an additional microservice, for example, subtract microservices, you may need to create another Docker image and deploy with another replication controller and service, so it will be independent from the current microservices.

Then, you can keep accumulate your own microservices ecosystem to re-use for another application.

See also

This recipe described how your application aligns to the microservices architecture. The microservices and the Docker container perfectly fit the concept. The following recipe also helps in managing your microservices' container images:

  • Working with the private Docker registry
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset