如何为Kubernetes构建RESTful Python Flask API
说明
在本教程中,我们将学习如何通过构建可用于生产环境的Docker容器来为Kubernetes构建RESTful Flask API。
这篇文章提供的解决方案将使用Kubernetes部署,这将允许我们扩展应用程序。部署还提供了其他功能,以控制新映像更新的部署方式以及如何处理故障。
我们还将学习如何为Pod创建服务以暴露它们,以及如何使用机密获取敏感信息。
uWSGI应用服务器
为了运行Flask API,需要一个应用服务器。有一些服务器可用,但是,本教程将介绍uWSGI。另一个受欢迎的选择是gUnicorn。
NGINX Web服务器
Web服务器将处理所有传入的请求,然后将它们反向代理到应用程序服务器。尽管容器可以单独运行应用程序服务器,但是Web服务器可以更好地控制命中Flask API的流量。
NGINX是一种高效的,事件驱动的Web服务器,能够处理大量流量。添加uWSGI后端支持也非常简单。
基本的Flask API
以下是基本的Flask REST API,将用于演示目的。该应用程序将被编写为将与MySQL数据库连接的简单CRUD api。
配置存储在config.py文件中,该文件将存储数据库连接设置。
请注意,没有任何环境信息静态存储在config.py中。作为Docker的最佳实践以及扩展的Kubernetes,特定于环境的信息不应存储在Docker容器中。
更重要的是不要将凭据存储在容器或者代码中。所有敏感信息(称为机密信息)都应由机密文件库处理,例如Hashicorp Vault,或者在本教程中为Kubernetes Secrets。
可以在运行时从Vault或者Kubernetes Secrets中获取机密。
关于Kubernetes秘密的注释
Kubernetes Secrets可轻松存储敏感数据和文件。虽然在本教程中以处理机密为例,但必须记住,其目的只是将机密传输到Pod,以供容器使用。
强烈建议设置为在音乐会中使用Hashicorp Vault和Kubernetes机密。 Hashicorp Vault将是所有机密的授权,并且这些机密将同步到Kubernetes机密。
不幸的是,这超出了本教程的范围。
构建Docker映像
在创建Docker映像之前,我们需要一个Dockerfile。让我们花一点时间来理解支持我们编写的REST API所需的Dockerfile。
Flask具有内置的Web服务器,在开发过程中非常有用,但是,强烈建议我们不要将其用于生产。原因很简单,内置服务器是单处理单线程的。
线程不足是并发问题。 REST API将无法处理大量流量。
按照最佳实践,本教程中的Flask应用程序将使用uWSGI作为服务器。
回到Dockerfile,没有运行Flask应用程序的官方映像。相反,我们将使用官方的Python3映像并在此映像的基础上进行构建。
在项目目录中创建一个名为Dockerfile的新文件
touch Dockerfile
用以下行启动Dockerfile
FROM python:3.6-slim
该映像将提供一个基于Ubuntu的容器,该容器经过精简后可以运行Python。社区提供的图像要小得多。
Dockerfile还应该包含用于安装软件包更新的行,以解决错误修复和漏洞,以及安装所有必备组件。 Flask应用程序的前提条件是:python3-dev和build-essentials。
RUN apt-get clean \ && apt-get -y update RUN apt-get -y install \ nginx \ python3-dev \ build-essential
将任何配置添加到以上行下方的容器中安装的服务中。
COPY nginx.conf /etc/nginx/nginx.com
该应用程序将从容器内名为/ app的目录中运行。将此设置为工作目录。
WORKDIR /app
现在,让我们将requirements.txt文件添加到容器中,然后安装Flask应用程序的依赖项。
COPY requirements.txt /app/requirements.txt RUN pip install -r requirements.txt --src /usr/local/src
该容器仍缺少Flask应用程序本身,将在以下几行中添加它。
COPY app.py /app/app.py COPY config.py /app/config.py COPY uwsgi.ini /app/wsgi.ini COPY startup.sh /app/startup.sh
启动脚本将需要是可执行的,因此需要针对它运行chmod命令。
RUN chmod +x ./startup.sh
最后,让我们通过端口80公开容器,并使该容器作为startup.sh脚本运行。
EXPOSE 80 CMD [ "./startup.sh" ]
将映像发布到注册表
为了在Kubernetes中部署Flask Docker映像,必须将其发布到Kubernetes可以访问的容器注册表中。将新版本推入注册表很重要。
Dockerhub
要将映像部署到Dockerhub,我们需要首先进行身份验证。
码头工人登录
出现提示时,输入Dockerhub登录凭据。成功通过身份验证后,身份验证令牌将存储在本地,这将使我们可以在不包含凭证的情况下针对Dockerhub运行命令。
任何推送到存储库的映像都必须在其名称中包含存储库信息。对于Dockerhub,该映像必须以Dockerhub ID开头。
使用以下命令为存储库添加标签。
docker -t flask-api:1.0.0 theitroad/flask-api:1.0.0
可以使用我们应用于它的新标签将我们的映像推送到Dockerhub。
docker push theitroad/flask-api:1.0.0
在Kubernetes机密中存储数据库凭证
绝不能将秘密存储为纯文本,代码或者任何Kubernetes清单中的内容。密码,证书和任何其他敏感数据应远离应用程序存储。
在本教程中,我们将使用Kubernetes Secrets作为我们的秘密存储区。 Flask应用程序的数据库凭据可以存储在此处,并在将其Docker映像部署为Pod时检索。
机密存储为Base64编码的字符串。让我们在base64中编码数据库的用户名和密码。
echo -n 'db-username' | base64
echo -n 'my-super-secret-password' | base64
这两个命令都将输出一串看似随机的字符,例如下面的示例。记下它们。
c3VwZXItc2VjcmV0LXBhc3N3b3JkCg==
现在创建一个名为flaskapi-secrets.yml的新文件,并向其中添加以下内容。
--- apiVersion: v1 kind: Secret metadata: name: flaskapi-secrets type: Opaque data: db_username: ZGItdXNlcm5hbWU= db_password: c3VwZXItc2VjcmV0LXBhc3N3b3JkCg==
在数据下添加了两个键。一个键将存储数据库用户名的值,另一个键将存储数据库密码。
键的值是先前创建的base64编码的字符串。
使用kubectl apply命令将密钥添加到Kubernetes。
kubectl apply -f flaskapi-secrets.yml
创建完成后,将此文件存储在远离应用程序和任何其他清单的地方。它应该始终受到严格的保护。或者,我们可以在完成后删除此文件。
创建Kubernetes部署
本教程中使用的Flask应用程序是无状态的,这意味着会话不绑定到特定实例,也不存储任何用户会话信息。这是一个简单的API,这意味着我们可以放大或者缩小它而不必担心状态。
Kubernetes部署是在Kubernetes中部署应用程序的理想方式。部署使我们可以根据需要轻松地放大或者缩小应用程序。
部署允许我们设置部署策略,该策略用于处理Pod故障和Docker映像更新。
当Flask应用程序的Deployment清单更新为使用新的映像版本并随后应用时,Deployment控制器允许我们设置部署策略。默认情况下,必须先成功部署应用程序的新版本,然后才能删除旧版本。
以下是Kubernetes部署的示例。它将用于部署具有3个副本的Flask应用程序。
创建一个名为flaskapp-deployment.yml的新文件,并将以下内容添加到其中。确保将图像名称(当前为theitroad / flask-api:1.0.0)替换为我们自己的图像名称。
请注意清单中CONTAINER键下的ENV键。 Flask API会获取环境设置的环境变量。这使我们能够在从DEV到UAT到Production的任何环境中使用完全相同的映像。
部署部署
使用kubectl apply命令在Kubernetes集群上创建部署。还有一个create命令,但是apply命令使我们能够更轻松地应用更新的清单。
kubectl apply -f flaskapi-deployment.yml
要验证是否成功创建了部署,请使用kubectl get svc命令。
kubectl get svc
最后,要验证创建的部署是否正常,请使用kubectl get pods命令。
kubectl get pods
如果任何Pod处于失败状态,则kubectl get pods命令将仅显示基本信息。有关Pod的更多详细信息,请使用describe命令。
kubectl describe pod <pod-name>
Pod日志
日志对于识别和排除任何错误至关重要。要查看容器中的日志,请使用kubectl log <pod name>命令。
kubectl log flaskapi-abcd1234
重要的是要记住,Docker和Kubernetes只会输出发送到STDOUT的日志。始终确保应用程序运行应用程序导出的任何服务将日志记录为STDOUT。
创建一个Kubernetes服务
服务使我们可以将Pod内部公开给其他Pod,外部公开给公众。每个服务都分配有一个静态分配的内部IP地址,这使我们可以连接到该服务。
另一方面,Pod是临时的,替换旧的或者失效的Pod的新Pod将不会重新使用IP地址。因此,始终建议我们连接到服务而不是Pods。
要为Flask应用程序创建服务,请创建一个名为flaskapi-service.yml的新文件。
向其添加以下行: