Kubernetes Cookbook 编程指南 中文版教程
创建时间:2018-12-07  访问量:3661  7  0

Kubernetes Cookbook 编程指南 中文版教程

从整体到微服务

典型的,应用程序架构是包含Model-View-Controller(MVC)的整体设计并且每个组件都包含在一个单独的大的二进制中。整体构建有一些好处,如组件中更少的延时,所有组件直接打包,并且容易部署和测试。

然而,一个整体的设计有一些缺点,因为二进制文件将会越来越大。你在添加或修改代码时总是需要关注它的副作用,因此,会使发布周期延长。

容器和Kubernetes 为你的应用程序使用微服务提供更多的灵活性。微服务架构是非常简单的,它可以将MVC分成一些模块和服务类。

每个微服务都通过使用RESTful或一些标准的网络API为其它微服务提供了远程过程调用(RPC)。好外是,每个微服务都是独立的。当添加或修改代码时具有最小的副作用。独立循环发布,因此它完美适用于Agile软件开发方式并允许重用这些微服务来组织另一个微服务生态系统中的应用程序。

开始

准备一个简单的微服务程序。为了能推送拉取你的微服务,请先注册Docker Hub(https://hub.docker.com/)以创建一个免费的Docker Hub ID:

注意:如果你将Docker镜像推到Docker Hub上,它将是公有的;任何人都可以拉取你的镜像。因此,不要任何认证信息放在镜像中。

一旦你成功登入了你的Docker Hub ID,你就可以转以如下所示的Dashboard页面:

如何去做...

准备微服和前端WebUI作为一个Docker镜像,然后,使用Kubernetes副本控制和服务部署它们。

微服务

  1. 这里简单使用了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')
  1. 准备如下所示的Dockerfile来构建一个Docker镜像:
$ 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"]
  1. 然后,使用docker build命令构建Docker镜像,如下所示:

如果你要发布Docker镜像,你应该使用Docker Hub ID/image name作为Docker镜像的名称。

//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
  1. 然后,使用docker login命令登录到Docker Hub:
//type your username, password and e-mail address in Docker hub
$ sudo docker login
Username: hidetosaito
Password:
Email: hideto.saito@yahoo.com
WARNING: login credentials saved in /home/ec2-user/.docker/config.
json
Login Succeeded
  1. 最后,使用docker push命令来注册你的Docker Hub仓库,如下所示:
//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:203b81c5a238e228c154e0b53a58e60e6eb3d156329
3483ce58f48351031a474 size: 19151

在访问Docker Hub时,你可以看看你构建的仓库中的微服务:

前端WebUI

  1. 这里是一个简单的前端WebUI,也是使用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')

Kubernetes服务为其它Pod生成服务名称与端口的环境变量。因此,环境变量的名称与Kubernetes服务的名称必须一致。

在这种情形下,my-calc服务名称必须是my-calc-service。

  1. 前端WebUI使用了Flask HTML模板;它与PHP和JSP类似,entry.py将参数传给模板(index.html)来渲染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>
  1. Dockerfile与微服务的完全相同。因此,最终,文件结构如下所示。注意index.html是一个模板文件;因此,将它放到模板目录中:
/Dockerfile
/entry.py
/templates/index.html
  1. 然后,构建一个Docker镜像并推送到Docker Hub如下所示:

为了将你的镜像推送到Docker Hub,你需要使用docker login命令登录。它只需要登录一次;系统检查~/.docker/config.json并从这里读取。

//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

它是如何工作的...

运行微服务和前端WebUI。

微服务

微服务(my-calc)使用Kubernetes副本控制器和服务,量它仅需要与其它Pod通信。用另一句话说,不需要向Kubernetes网络外部暴露它。因此,这个服务的类型是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

使用kubectl命令加载my-calc Pod,如下所示:

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

前端WebUI

前端WebUI也使用副本控制器和服务,但是为了能通过外部浏览器访问它,它需要暴露端口(TCP端口30080):

$ 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

你已经暴露了集群所有节点中的服务到外部端口。如果你想将服务暴露到外部因特网,你可能需要为服务端口(TCP 端口30080)设置防火墙规则。更多信息参见http://releases.k8s.io/release-1.1/docs/user-guide/services-firewalls.md

让我们尝试使用Web浏览器访问my-frontend。你可以访问Kubernetes节点的IP地址;指定端口号,如下所示:

当你点击addition按钮时,它将把参数传给微服务(my-calc)。微服务计算加法(是的,仅仅是加法)然后将结果返回给前端WebUI:如下所示:

所以现在,很容器独立调整前端WebUI和微服务的副本数量。例如,WebUI副本范围从2到8并且微服务的副本范围是从2到16。

同样,如果有需要修改的Bug,例如,前端需要验证输入参数来检查输入的是数字还是字符串(是的,如果你输入字符串然后提交,它将显示一个错误!);它将不会影响微服务的构建和部署周期:

另外,如果你想要添加另一个微服务,例如,减法微服务,你可能需要创建另外一个Docker镜像并使用另一个副本控制器和服务进行部署。因此它将与当前微服务独立。

然后,你可以保持你的微服务生态系统的积累,以为另一个应用程序重用这些微服务。

还可以参考

本节描述了如何将你的应用程序转为微服务架构。微服务和Docker容器完美符合这个概念。下面的章节也帮助管理你的微服务容器镜像:

  • 私有Docker Registry的使用