{"id":766,"date":"2020-08-06T15:21:00","date_gmt":"2020-08-06T15:21:00","guid":{"rendered":"https:\/\/www.geekmungus.co.uk\/?p=766"},"modified":"2022-11-05T10:53:18","modified_gmt":"2022-11-05T10:53:18","slug":"microk8s-kubernetes-raspberry-pi-ubuntu-linux-basic-setup-guide-part-2-build-your-own-image-and-deploy-it","status":"publish","type":"post","link":"https:\/\/geekmungus.co.uk\/?p=766","title":{"rendered":"MicroK8s (Kubernetes) &#8211; Raspberry Pi Ubuntu Linux Basic Setup Guide &#8211; Part 2 (Build Your Own Image and Deploy It)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Log onto your master node via SSH.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;re going to build an image and then deploy it, so as a developer you&#8217;d probably be developing on your own machine possibly running docker locally to test how your containers work before deploying the code to a repository where it can then be applied to a production Kubernetes deployment for consumption by users.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So we&#8217;re going to install Docker, and Python 3 to allow us to quickly create an image, test it works, then deploy to our MicroK8s cluster.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">WARNING! The Raspberry Pi is an ARM based processor architecture, therefore you can only run images that are compiled for ARM, you can&#8217;t run x86\/x64 architecture images.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a rel=\"noreferrer noopener\" href=\"https:\/\/www.google.com\/url?q=https%3A%2F%2Fmicrok8s.io%2Fdocs%2Fregistry-private&amp;sa=D&amp;sntz=1&amp;usg=AFQjCNH28YHOhs3FkS-FTSAg07vD6VhZeQ\" target=\"_blank\">https:\/\/microk8s.io\/docs\/registry-private<\/a><\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Install Docker and Python<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apt install docker.io\n\napt install python3\n\napt install python3-pip<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Get a Sample Application<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For this example we&#8217;re going to be using Jason Haley&#8217;s hello world Python Flask Application because its quite a neat little application to show how requirements can be used and how you can build an application with some dependencies.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Create a directory for it first under the &#8220;ubuntu&#8221; home directory.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir ~\/application\n\ncd application<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s clone Jason&#8217;s Git Repository into the directory:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git clone https:\/\/github.com\/JasonHaley\/hello-python.git\n\ncd hello-python\/app\n\ncd app<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So now let&#8217;s install the requirements for Python as per what is in the requirements.txt file from the Git Repo.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pip3 install -r requirements.txt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now run the application:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python3 main.py<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then you can either run the above as a background task by adding &amp; to the end or open another SSH session to your master node then run the following, you get &#8220;Hello from Python!&#8221; back, the application is working!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ubuntu@k8s-master:~$ curl http:\/\/127.0.0.1:5000\n\nHello from Python!ubuntu@k8s-master:~$<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So that looks to be working!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Create the Docker File<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Okay now we know the application works, lets create a docker file to start to build our image.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd hello-python\/app<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create a file called &#8220;Dockerfile&#8221;, and put this in it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM python:3.7\n\nRUN mkdir \/app\n\nWORKDIR \/app\n\nADD . \/app\/\n\nRUN pip install -r requirements.txt\n\nEXPOSE 5000\n\nCMD &#91;\"python\", \"\/app\/main.py\"]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now create the image:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker build -f Dockerfile -t hello-python:local .<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wait for it to build, once it has been built, run the following to list the Docker images:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker image ls<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see there it is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>root@k8s-master:\/home\/ubuntu\/apps\/hello-python\/app# docker image ls\n\nREPOSITORY            TAG                 IMAGE ID            CREATED             SIZE\n\nhello-python          local             b3d4b07093ba        5 seconds ago       874MB<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Right quick explanation of the above, we created Docker image that includes the Python application wrapped up in Flask (so its a web site essentially), we then build the Docker file and add the tag &#8220;hello-python:local&#8221; (more about tags in a later guide). Also notice that the application is set within the Dockerfile to expose port 5000, what that means is that when the image is deployed in Kubernetes it will present port 5000 to within Kubernetes, we can then decide when presenting out the application what port we want used for the outside world.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker run -p 5001:5000 hello-python:local<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now its running you should see something like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># docker run -p 5001:5000 hello-python\n\n * Serving Flask app \"main\" (lazy loading)\n\n * Environment: production\n\n   WARNING: This is a development server. Do not use it in a production deployment.\n\n   Use a production WSGI server instead.\n\n * Debug mode: off\n\n * Running on http:\/\/0.0.0.0:5000\/ (Press CTRL+C to quit)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Okay so now from another terminal run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl http:\/\/127.0.0.1:5001<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And we see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ubuntu@k8s-master:~$ curl http:\/\/127.0.0.1:5001\n\nHello from Python!<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So what have we done, well, we&#8217;ve run the Docker image locally and got it to present on port 5001 to the application container&#8217;s port on 5000, now we&#8217;re ready to push this image into Kubernetes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Push Docker Image into Kuerbetes Repository (Local Image Repository Method)<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The image we created is known to Docker. However, Kubernetes is not aware of the newly built image. This is because your local Docker daemon is not part of the MicroK8s Kubernetes cluster. We can export the built image from the local Docker daemon and \u201cinject\u201d it into the MicroK8s image cache.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>root@k8s-master:\/home\/ubuntu\/apps\/hello-python# docker image ls\n\nREPOSITORY            TAG                 IMAGE ID            CREATED             SIZE\n\nhello-python          latest              b3d4b07093ba        About an hour ago   874MB<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">(You don&#8217;t need to include the &#8220;:local&#8221;, it will always take the latest otherwise)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So first let&#8217;s export the docker image out:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># docker save hello-python > hello-python.tar\n\n# microk8s ctr image import hello-python.tar<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And push it directly into the image cache:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>root@k8s-master:\/home\/ubuntu\/apps\/hello-python\/app# microk8s ctr image import hello-python.tar\n\nunpacking docker.io\/library\/hello-python:local (sha256:d84775f8b2406071344ceeb6a3007705dab7f7dae4b12727d26708902d007ab7)...done<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now check the image cache with the following and you should see it there ready for use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>microk8s ctr images ls<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Deploying the Application into Kubernetes<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To run on Kubernetes you can then create a file called deployment.yaml in the directory \/app that you were in earlier and put in these contents:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\n\nkind: Deployment\n\nmetadata:\n\n  name: hello-python\n\nspec:\n\n  selector:\n\n    matchLabels:\n\n      app: hello-python\n\n  replicas: 4\n\n  template:\n\n    metadata:\n\n      labels:\n\n        app: hello-python\n\n    spec:\n\n      containers:\n\n      - name: hello-python\n\n        image: hello-python:local\n\n        imagePullPolicy: Never\n\n        ports:\n\n        - containerPort: 5000<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then create a file called service.yaml and put the following contents in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\n\nkind: Service\n\nmetadata:\n\n  name: hello-python-service\n\nspec:\n\n  selector:\n\n    app: hello-python\n\n  ports:\n\n  - port: 5000\n\n  type: LoadBalancer<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s another example service.yaml file for you to experiement with, see if you can see what the difference is it makes!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\n\nkind: Service\n\nmetadata:\n\n  name: hello-python-service\n\nspec:\n\n  selector:\n\n    app: hello-python\n\n  ports:\n\n  - port: 8080\n\n    targetPort: 5000\n\n  type: LoadBalancer<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can also run the creation of a service from the command line, infact you can do the same thing with the deployment of the image if you want, you don&#8217;t need to do these steps, if you&#8217;ve created the yaml files though, these are here just for reference, now we have declared the application and service definitions we need to apply them.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>microk8s kubectl expose deploy hello-python --port 8080 --target-port 5000 --type LoadBalancer\n\nmicrok8s kubectl expose deployment hello-python --type=LoadBalancer --name hello-python-service<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So now let&#8217;s deploy our application to Kubernetes with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f deployment.yaml\n\nkubectl apply -f service.yaml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Okay so that it is deployed, check with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>microk8s kubectl get pods<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And we see our 4 replica pods running, after a few minutes for them to appear!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>root@k8s-master:\/home\/ubuntu\/application\/hello-python\/app# microk8s kubectl get pods\n\nNAME                            READY   STATUS    RESTARTS   AGE\n\nhello-python-6bfc96894d-bt7rg   1\/1     Running   0          11s\n\nhello-python-6bfc96894d-jx7f9   1\/1     Running   0          11s\n\nhello-python-6bfc96894d-rqmjm   1\/1     Running   0          11s\n\nhello-python-6bfc96894d-zcxhg   1\/1     Running   0          11s<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s check out the service too so run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>microk8s kubectl get services<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And there it is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>root@k8s-master:\/home\/ubuntu\/application\/hello-python\/app# microk8s kubectl get services\n\nNAME                   TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)          AGE\n\nhello-python-service   LoadBalancer   10.152.183.204   192.168.1.20   5000:30212\/TCP   26s\n\nkubernetes             ClusterIP      10.152.183.1     &lt;none>         443\/TCP          25m<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">What you can see is that our image\/application is running and is exposed on IP address: 192.168.1.20 on port 5000.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You should now be able to access it on http:\/\/192.168.1.20:5000 from your web browser (i.e. from your workstation not from the Raspberry Pi itself!)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Remove the Application (Deployment and Service)<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s clean up what we&#8217;ve deployed, so run the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>microk8s kubectl delete -f deployment.yaml\n\nmicrok8s kubectl delete -f service.yaml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Additional Information<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/www.google.com\/url?q=https%3A%2F%2Fdocs.docker.com%2Fengine%2Freference%2Fcommandline%2Frun%2F&amp;sa=D&amp;sntz=1&amp;usg=AFQjCNHN6UqaM5njil0Qzm6D-F6Is3sczg\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/docs.docker.com\/engine\/reference\/commandline\/run\/<\/a><\/li><li><a href=\"https:\/\/www.google.com\/url?q=https%3A%2F%2Fmicrok8s.io%2Fdocs%2Fregistry-images&amp;sa=D&amp;sntz=1&amp;usg=AFQjCNGIBprZeJajZwIlAgUaAel_OCr1EA\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/microk8s.io\/docs\/registry-images<\/a><\/li><li><a href=\"https:\/\/www.google.com\/url?q=https%3A%2F%2Fmicrok8s.io%2Fdocs%2Fregistry-built-in&amp;sa=D&amp;sntz=1&amp;usg=AFQjCNHpf1fz-Ik3iW4HteN2AJ_4tn0J_Q\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/microk8s.io\/docs\/registry-built-in<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Log onto your master node via SSH. We&#8217;re going to build an image and then deploy it, so as a developer you&#8217;d probably be developing on your own machine possibly running docker locally to test how your containers work before deploying the code to a repository where it can then be applied to a production &#8230; <a title=\"MicroK8s (Kubernetes) &#8211; Raspberry Pi Ubuntu Linux Basic Setup Guide &#8211; Part 2 (Build Your Own Image and Deploy It)\" class=\"read-more\" href=\"https:\/\/geekmungus.co.uk\/?p=766\" aria-label=\"Read more about MicroK8s (Kubernetes) &#8211; Raspberry Pi Ubuntu Linux Basic Setup Guide &#8211; Part 2 (Build Your Own Image and Deploy It)\">Read more<\/a><\/p>\n","protected":false},"author":4,"featured_media":763,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,11],"tags":[],"class_list":["post-766","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-kubernetes","category-linux"],"_links":{"self":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/766","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=766"}],"version-history":[{"count":1,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/766\/revisions"}],"predecessor-version":[{"id":1379,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/766\/revisions\/1379"}],"wp:attachment":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=766"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=766"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=766"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}