如何在Kubernetes上部署带有Redis的Node.js应用?

7

我有一个非常简单的 node.js 应用程序(HTTP 服务),它与 redis 进行交互。我想创建一个部署,并在 minikube 中运行它。

据我了解,我需要一个基于 Docker 镜像的 Kubernetes Pod 来运行我的应用程序。以下是我的 Dockerfile:

FROM node:8.9.1
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080
CMD ["npm", "start"]

我使用docker build -t my-app .命令构建了Docker镜像。

接下来,我为我的应用程序创建了一个Pod定义:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
  - name: my-app
    image: my-app:latest
    imagePullPolicy: Never
    ports:
    - containerPort: 8080

目前为止,还不错。但是从现在开始,我没有清楚的思路如何处理redis:

  1. redis应该是另一个Pod,还是一个Service(在Kubernetes中的术语)?

  2. 我如何从我的应用程序中引用redis?基于redis是否被定义为Pod/Service,我如何获取连接URL和端口?我读过关于由Kubernetes创建的环境变量,但我不确定这些是否适用于Pods或Services。

  3. 我如何将两者(我的应用程序和redis)聚合到单个配置中?如何确保redis先启动,然后是我的应用程序(需要运行redis实例),以及如何将我的HTTP端点暴露给“外部世界”?我读到过Deployments,但我不确定如何将这些组件连接在一起。

理想情况下,我希望所有配置都在YAML文件中,这样最终整个基础设施可以通过单个命令启动。


FYI:EXPOSE 定义已被弃用,但仅用于文档目的(它没有提供任何其他有用的功能)。 :) - yomateo
5个回答

5

我想我找到了一个解决方案(使用部署和服务)。

对于我的部署,我在一个 Pod 中使用了两个容器(webapp + redis),因为没有活动的 redis 实例,webapp 无法运行,而且它在应用程序启动时连接到 redis。如果您认为我理解有误,请随时纠正我。

这是我的部署:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-deployment
spec:
  selector:
    matchLabels:
      app: my-app-deployment
  template:
    metadata:
      labels:
        app: my-app-deployment
    spec:
      containers:
      - name: redis
        image: redis:latest
        ports:
        - containerPort: 6379
        volumeMounts:
        - mountPath: /srv/www
          name: redis-storage
      - name: my-app
        image: my-app:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 8080
      volumes:
      - name: redis-storage
        emptyDir: {}

这里是服务定义:

apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  ports:
  - port: 8080
    protocol: TCP
  type: NodePort
  selector:
    app: my-app-deployment

我使用以下命令创建deployment: kubectl create -f deployment.yaml 之后,我使用 kubectl create -f service.yaml 命令创建service。 我使用 minikube ip 命令读取IP地址,并从输出中提取端口号,使用 kubectl describe service my-app-service 命令。


将容器从一个 Pod 中分离出来运行的主要原因之一是为了可扩展性。例如:如果您需要扩展 Web 应用程序,则会得到不同的 Redis 实例。 - yomateo
1
谢谢@yomateo - 我在原始描述中没有表达得很准确 - 对于我的应用程序来说,这将是一种期望的情况 :) - mpb

4

我同意之前所有回答。我只是想通过执行单个命令来简化操作。

首先,在一个文件中创建redis的必要清单,比如redis.yaml,并创建服务以将其暴露在外部。

apiVersion: v1
kind: Service
metadata:
  name: redis
  labels:
    app: node-redis
spec:
  ports:
  - name: redis
    port: 6379
    targetPort: 6379
  type: NodePort
  selector:
    app: node-redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  selector:
    matchLabels:
      app: node-redis
  replicas: 1
  template:
    metadata:
      labels:
        app: node-redis
    spec:
      containers:
      - name: redis
        image: redis:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 6379
        # data volume where redis writes data
        volumeMounts:
        - name: data
          mountPath: /data
          readOnly: false
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: redis-data
---
# data volume
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-data
  labels:
    app: node-redis
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 100Mi

下一步,将应用程序的清单放在另一个文件中,比如说my-app.yaml。这里我加入了卷字段,以便您可以使用由redis存储的数据。
apiVersion: v1
kind: Pod
metadata:
  name: my-app
  labels:
    app: node-redis
spec:
  containers:
  - name: my-app
    image: my-app:latest
    ports:
    - containerPort: 8080
    # data volume from where my-app read data those are written by redis
    volumeMounts:
    - name: data
      mountPath: /data
      readOnly: false
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: redis-data

现在我们可以使用以下bash文件my-app.sh
#!/bin/bash

kubectl create -f redis.yaml

pod_name=$(kubectl get po -l app=node-redis | grep app-with-redis | awk '{print $1}')

# check whether redis server is ready or not
while true; do
  pong=$(kubectl exec -it $pod_name -c redis redis-cli ping)
  if [[ "$pong" == *"PONG"* ]]; then
    echo ok;
    break
  fi
done

kubectl create -f my-app.yaml

只需运行chmod +x my-app.sh; ./my-app.sh即可部署。要获取URL,请运行minikube service redis --url。您也可以类似地获取应用程序的URL。唯一需要注意的是,您需要为应用程序创建一个nodePort类型的服务,以便从集群外部访问它。
所以,现在一切都在你的掌握中。

3

我建议你将redis部署在一个单独的pod中,这样你的Web应用程序如果崩溃,则不会影响redis服务器。

以下是您的redis部署与服务:

deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  selector:
    matchLabels:
      app: redis
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
    spec:
      volumes:
        - name: host-sys
          hostPath:
            path: /sys
      initContainers:
        - name: disable-thp
          image: redis:4.0-alpine
          volumeMounts:
            - name: host-sys
              mountPath: /host-sys
          command: ["sh", "-c", "echo never > /host-sys/kernel/mm/transparent_hugepage/enabled"]
      containers:
      - name: redis
        image: redis:4.0-alpine
        imagePullPolicy: IfNotPresent
        resources:
          requests:
            cpu: 350m
            memory: 1024Mi
        ports:
        - containerPort: 6379

service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: redis
  labels:
    app: redis
spec:
  ports:
  - port: 6379
    name: redis
  selector:
    app: redis

既然我们公开了一个 Kubernetes Service,你可以通过主机名或它的“服务名称”redis访问 Redis 实例。

您可以在https://github.com/mateothegreat/k8-byexamples-redis上查看我的Kubernetes Redis存储库。如果您想要更简单的方法,只需运行make install即可。

祝好运,如果您仍然卡住,请联系我们!


1
  1. 是的,您需要为redis使用单独的部署服务

  2. 使用kubernetes服务发现,应该内置在KubeDNS、CoreDNS中

  3. 使用就绪探针和存活探针

是的,您可以编写一个大的yaml文件来描述所有的部署和服务。然后:

kubectl apply -f yourfile.yml

或者您可以将yaml放在单独的文件中,然后进行以下操作:

 kubectl apply -f dir/

你会在*.yaml文件中添加什么内容,以便完成我已经为my-app Pod编写的部分? - mpb
its for you to figure out - Ijaz Ahmad

1

我建议您进一步阅读k8s文档,但总的来说,针对您上面提出的问题:

  1. 是的,另一个pod(具有相关配置)和一个附加的服务取决于您的用例,请查看这个很好的示例:https://kubernetes.io/docs/tutorials/configuration/configure-redis-using-configmap/
  2. 使用服务,请在此处阅读更多信息:https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/
  3. 有几种管理依赖关系的方法 - 搜索部署依赖关系,但通常您可以在具有就绪端点的相同文件中添加它们,并使用Service公开 - 在第2个链接中阅读更多信息

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接