容器化Kubernetes的Node.js REST API
说明
在本教程中,我们将学习如何为Express REST API构建Node.js Docker镜像,并将其部署到Kubernetes。我们还将学习如何安全地存储敏感的后端凭据,并远离应用程序源代码。
1构建Express REST API
以下内容将向我们展示如何基于Chris Sevilleja编写的"使用Node和Express 4构建RESTful API"来构建Express REST API。该应用程序的目的是演示如何在Kubernetes中构建和部署Express应用程序Docker镜像
最重要的是,我们将学习如何正确处理环境设置,例如数据库凭据。
在项目目录中,创建一个package.json文件。本教程将使用以下内容,它将安装Express,Body Parser和Mongoose的软件包。
// package.json { "name": "express-api", "version": "1.0.0", "description": "An example Node.js Express API", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Hyman@localhost", "license": "ISC", "dependencies": { "body-parser": "^1.19.0", "express": "^4.17.1", "mongoose": "^5.5.11" } }
使用NPM安装express-api的依赖项
npm install
在同一目录中创建一个名为config.js的文件。该文件将包含我们所有应用程序的环境配置。
// config.js export function = { app: { port: process.env.PORT || 3000, }, db: { username: process.env.DB_USERNAME || '', password: process.env.DB_PASSWORD || '', host: process.env.DB_HOST || '', database: expressapi } }
请注意,大多数值是通过环境变量设置的。这样做是为了使应用程序与每种环境完全脱钩,从而使我们可以在任何地方部署应用程序。
然后分别由Kubernetes Secrets或者Kubernetes ConfigMaps设置这些值,分别用于敏感和不敏感设置。例如,数据库密码应存储为机密,而数据库主机可以存储在configMap中。
创建路线
const express = require('express'); const book = require('./bookController'); const router = express.Router(); router.use('/book', book); module.export = router;
建立模型
// bookModel.js const mongoose = require('mongoose'); var bookSchema = mongoose.Schema({ title: { type: String, required: true }, published: { type: Date } }); var Book = module.exports = mongoose.model('book', bookSchema);
创建一个控制器
// bookController.js Book = require('./bookModel'); exports.index = function (req, res) { Book.get(function (err, books) { if (err) { res.json({ status: "error", message: err }); } res.json({ status: "Success" data: books }); }); };
现在创建index.js文件。
// index.js const express = require('express'); const bodyParser = require('body-parser'); const routes = require('./routes'); const config = require('./config'); const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); router.get('/', function(req, res) { res.json({ message: 'hooray! welcome to our api!' }); }); app.use('/api', routes); app.listen(config.app.port); console.log('Express API running on port ' + config.app.port);
2构建Docker镜像
本教程中构建的镜像将基于Node的官方文档来进行dockerizing node.js应用程序。
在项目目录中,创建一个名为Dockerfile的新文件。我们构建的Docker镜像将基于官方的Node镜像。
FROM node:12 RUN apt update \ && apt update upgrade -y RUN apt install -y \ nginx WORKDIR /app COPY app/package.json /app/package.json COPY app/index.js /app/index.js COPY app/config.js /app/config.js COPY nginx/nginx.conf /etc/nginx.conf RUN npm install CMD ['startup.sh']
3在Kubernetes ConfigMap中存储配置
ConfigMap是Kubernetes中的资源类型,可用于存储容器的配置。然后可以将配置作为环境变量或者文件挂载,这最适合应用程序。
例如,该配置可以表示为Java Springboot应用程序的属性文件,也可以表示为Node.js应用程序的环境变量。
为Node.js API创建一个新的清单,名为expressapi-configmap.yml。
触摸expressapi-configmap.yml
向其添加以下配置。这些将用于我们API中的数据库主机和数据库名称。
--- apiVersion: v1 kind: ConfigMap metadata: name: expressapi-config data: mongodb_host: db.prod.theitroad.com mongodb_database: expressapi_prod
现在,使用kubectl apply命令在Kubernetes集群中创建ConfigMap资源。如果资源已经存在,将对其进行更新。
kubectl apply -f expressapi-configmap.yml
在Kubernetes秘密中存储3个秘密
与ConfigMaps不同,机密用于存储敏感信息。这是我们将用于存储凭据,ssh密钥,TLS密钥和证书以及应用程序使用的任何其他敏感数据的资源类型。
机密存储为Base64编码的字符串。为了存储我们的数据库用户名和密码,我们需要将其转换为base64. 使用以下命令执行此操作。
echo -n 'db-user' | base64 ZGItdXNlcg==
echo -n 'super-secret-password' | base64 c3VwZXItc2VjcmV0LXBhc3N3b3Jk
记下两个命令输出的base64编码的字符串。这些值将被插入Express API的Kubernetes机密中。
创建一个名为expressapi-secrets.yml的新文件
触摸expressapi-secrets.yml
将以下内容添加到文件中。请记住,对db_username和db_password键使用我们自己的base64编码的字符串。
--- apiVersion: v1 kind: Secret metadata: name: expressapi-secret data: mongodb_username: ZGItdXNlcg== mongodb_password: c3VwZXItc2VjcmV0LXBhc3N3b3Jk
使用kubectl apply命令在Kubernetes中创建秘密资源。
kubectl apply -f expressapi-secrets.yml
Base64没有加密,因此我们应该将此文件存储在安全的地方。或者更好的是,一起删除键值。这使我们可以保留键名,并提供一种以后可以轻松更新值的机制。
4为Node.js API创建部署
部署是一种扩展和更新无状态容器Pod的机制。由于我们的Express API不会维护状态,因此我们为其创建了一个部署资源,该资源随后将生成一个copySetSet资源。
副本集是控制器控制窗格副本的工具,它可以替换发生故障的窗格,放大或者缩小窗格。
创建一个名为expressapi-deployment.js的新文件
touch expressapi-deployment.js
向其中添加以下内容。该部署清单将创建Express API的3个副本。这些副本将基于本教程前面创建的theitroad / expressapi:1.0.0 Docker镜像。
apiVersion: v1 kind: Deployment metadata: name: expressapi labels: app: expressapi spec: replicas: 3 selector: matchLabels: app: expressapi template: metadata: labels: app: expressapi spec: containers: - name: expressapi image: theitroad/expressapi:1.0.0 env: - name: MONGODB_HOST valueFrom: configMapKeyRef: name: expressapi-configmap key: mongodb_host - name: MONGODB_DATABASE valueFrom: configMapKeyRef: name: expressapi-configmap key: mongodb_database - name: MONGODB_USER valueFrom: secretKeyRef: name: expressapi-secret key: mongodb_username - name: MONGODB_PASSWORD valueFrom: secretKeyRef: name: expressapi-secret key: mongodb_password
请注意然后env键下的值。从ConfigMap中获取的值由configMapKeyRef键引用,而秘密由secretKeyRef键引用。
我们从ConfigMap中获取数据库连接信息,并从Secret中获取数据库凭据。这样,任何新创建的Express API pod都可以连接到Mongo数据库。
5公开Node.js部署
要将Express API公开,我们需要为其创建服务资源。我们创建服务资源是因为Pod是短暂的,整个状态都是临时的。吊舱一经销毁,其IP地址即被吊销,新的吊舱将获得另一个地址。
服务是静态的,不是短暂的。因此,它们非常适合用作端点。 Pod通过标签添加到服务资源,该标签是使用服务上的选择器和Pod清单上的元数据名称设置的。
创建一个名为expressapi-service.yml的新文件。
触摸expressapi-service.yml
向其中添加以下内容。
apiVersion: v1 kind: Service metadata: name: expressapi-service spec: type: LoadBalancer selector: app: expressapi ports: - protocol: TCP port: 3000 targetPort: 3000
我们为我们的服务分配了LoadBalancer类型,这将导致Kubernetes正在运行的云平台上提供计算负载平衡器。然后,平衡器将在清单中设置的协议上公开端口。
选择器键被分配了expressapi,它映射到Deployment元数据名称值。只有与该标签匹配的豆荚才会添加到服务上。
使用kubetcl apply命令创建新的服务资源。
kubectl apply -f expressapi-service.yml