百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

kubernetes中pod间的通信

toyiye 2024-06-21 12:19 13 浏览 0 评论



我们如果创建了一些pod,那么它们之间是怎么通信的呢?因为pod的ip地址是有可能变化的,这里我们主要讨论几个场景

  • 同一网络下的不同pod间是怎么通信的?
  • 同一个pod中不同的容器是怎么通信的?
  • 不同的网络下不同的pod是怎么通信的?


一、同一网络下的不同pod间通信

第一种场景可能是应用最多的场景,比如我写了一个web应用,它使用python作为后端,使用redis作为数据库,redis和python分别创建在不同的pod里,我会使用deployment创建rs的方式再创建pod,正常情况下,我们是不希望这个redis被外面的应用访问到的,只允许在python的应用访问到,


如上图,用户可以使用python应用暴露出来的6000端口(其实是k8s里的service暴露出来的)来访问应用,但是并不能直接访问里面redis的6379端口。

1.1 创建redis pod

我们先创建一个redis pod

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
 ?  app: redis
  name: redis-master
spec:
  selector:
 ?  matchLabels:
 ? ?  app: redis
  replicas: 1
  template:
 ?  metadata:
 ? ?  labels:
 ? ? ?  app: redis
 ?  spec:
 ? ?  containers:
 ? ?  - image: redis
 ? ? ?  name: redis-master2
 ? ? ?  ports:
 ? ? ?  - containerPort: 6379


查看pod详细信息


# kubectl create -f r-deployment.yaml
deployment.apps/redis-master created
kubectl get pods -o wide
NAME ? ? ? ? ? ? ? ? ? ? ? ? ?  READY ? STATUS ?  RESTARTS ? AGE ? IP ? ? ? ?  NODE ? ? ? ? ? ? NOMINATED NODE ? READINESS GATES
redis-master-7f88b489b9-k4c58 ? 1/1 ? ? Running ? 0 ? ? ? ?  27s ? 10.1.0.59 ? docker-desktop ? <none> ? ? ? ? ? <none>
?

1.2 创建python应用

我先使用docker run 本地启一个redis用于代码调试,为了和上面启的redis pod 区分(其实也不用区分,上面的redis pod 本身也没有对外暴露端口),这里使用6380作为对外端口

docker run --name myredistest -d -p 6380:6379 redis

之后就可以使用redis客户端进行访问了,我在db1中创建了一个redistest的key



写一个python应用,读取redis中的数据

#-*- coding:utf-8 -*-
# author:Yang
# datetime:2020/2/10 16:07
# software: PyCharm
?
from flask import Flask
from flask_redis import FlaskRedis
import time
?
REDIS_URL = "redis://{}:{}/{}".format('127.0.0.1', 6380, 1)
?
?
app = Flask(__name__)
app.config['REDIS_URL'] = REDIS_URL
redis_client = FlaskRedis(app)
?
@app.route("/")
def index_handle():
 ? ?redis_client.set("reidstest",time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time())))
 ? ?name = redis_client.get("reidstest").decode()
 ? ?return "hello %s"% name
?
app.run(host='0.0.0.0', port=6000, debug=True)
?


之后用浏览器访问127.0.0.1:6000 就可以得到正常的输出了



1.3 在pod中访问redis

上面只是将python访问本地的redis,我们最终是要将这个python应用打包成镜像,放到k8s中,那么如果在k8s中这个flask应用该如果访问到redis呢?

为了实现一套代码可以在不同的环境中执行,我在redis的初始化时加上一点判断

if os.environ.get("envname") == "k8s": # 说明是在k8s中
 ? ?REDIS_URL = "redis://{}:{}/{}".format('redisIP', "redispord", 1)
else:
 ? ?REDIS_URL = "redis://{}:{}/{}".format('127.0.0.1', 6380, 1)


现在主要的问题在于,REDIS_URL = "redis://{}:{}/{}".format('redisIP', "redispord", 1) k8s中的redisIP和redispord这里该填写什么呢?

上面使用kubectl get pods -o wide 查看到redis的ip为10.1.0.59 ,那么我们试试能不能通过这个IP和端口来访问redis呢?

if os.environ.get("envname") == "k8s": # 说明是在k8s中
 ? ?REDIS_URL = "redis://{}:{}/{}".format('10.1.0.59', 6379, 1)
else:
 ? ?REDIS_URL = "redis://{}:{}/{}".format('127.0.0.1', 6380, 1)
?


先创建一个Dockerfile

# Use an official Python runtime as a parent image
From python:3.5.7
# Set the working directory to /app
WORKDIR /app
# ADD requirements.txt
COPY requirements.txt /app/
# Install any needed packages specified in requirement.txt
RUN pip install --trusted-host mirrors.aliyun.com -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
# Make port 6000 available to the world outside this container
EXPOSE 6000
# Define environment variable
ENV envname=k8s
# ADD application.py to /app
ADD application.py /app/
CMD ["python", "application.py"]


创建镜像

docker build -t flaskk8s .


之后就可以在本地查看到镜像 falskk8s,使用这个k8s创建pod,创建flask-deployment.yaml


apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
 ?  app: flasktest
  name: flasktest
spec:
  selector:
 ?  matchLabels:
 ? ?  app: flasktest
  replicas: 1
  template:
 ?  metadata:
 ? ?  labels:
 ? ? ?  app: flasktest
 ?  spec:
 ? ?  containers:
 ? ?  - image: flaskk8s
 ? ? ?  name: flaskweb
 ? ? ?  imagePullPolicy: Never
 ? ? ?  ports:
 ? ? ?  - containerPort: 6000


因为是本地的镜像,所以在加上imagePullPolicy: Never ,否则k8s默认是会从dockerhub上去拉取。

# kubectl get pod
NAME ? ? ? ? ? ? ? ? ? ? ? ? ?  READY ? STATUS ?  RESTARTS ? AGE
flasktest-68cfdcc66d-d2tb7 ? ? ?1/1 ? ? Running ? 0 ? ? ? ?  7s
redis-master-7f88b489b9-k4c58 ? 1/1 ? ? Running ? 0 ? ? ? ?  126m
# kubectl get deployment
NAME ? ? ? ? ? READY ? UP-TO-DATE ? AVAILABLE ? AGE
flasktest ? ? ?1/1 ? ? 1 ? ? ? ? ? ?1 ? ? ? ? ? 44s
redis-master ? 1/1 ? ? 1 ? ? ? ? ? ?1 ? ? ? ? ? 127m
?


创建flasktest的service,让其可以通过浏览器访问

apiVersion: v1
kind: Service
metadata:
  name: flask-service
  labels:
 ?  name: flaskservice
spec:
  type: NodePort
  ports:
  - port: 6000
 ?  nodePort: 30002
  selector:
 ?  app: flasktest


# kubectl get service
NAME ? ? ? ? ? ?  TYPE ? ? ?  CLUSTER-IP ? ?  EXTERNAL-IP ? PORT(S) ? ? ? ?  AGE
flask-service ? ? NodePort ? ?10.97.54.167 ?  <none> ? ? ? ?6000:30002/TCP ? 15s
kubernetes ? ? ?  ClusterIP ? 10.96.0.1 ? ? ? <none> ? ? ? ?443/TCP ? ? ? ?  3d5h
redis-master-sr ? ClusterIP ? 10.99.187.220 ? <none> ? ? ? ?6379/TCP ? ? ? ? 79m


可以看到30002端口已经被暴露出来,之后我们访问http://127.0.0.1:30002/ ,看到可以正常的访问



一切看着都很顺利对不对,但是我们来考虑两个问题

  1. 如果redis的pod挂掉会怎么样?
  2. 如果创建redis时replicas为大于1时,那么指定某个POD的的IP是否妥当?

第一个问题,由于是使用deployment创建的rs,再创建的pod,此时如果redis的某个pod挂了,由于rs中定义了replicas: 1,它会重新再起一个redis的pod,此时的IP可能就会变了。我们来试验一下,只需要将原来的pod删除掉即可,k8s会自动再创建一个新的pod

# kubectl get pod
NAME ? ? ? ? ? ? ? ? ? ? ? ? ?  READY ? STATUS ?  RESTARTS ? AGE
flasktest-68cfdcc66d-d2tb7 ? ? ?1/1 ? ? Running ? 0 ? ? ? ?  27m
redis-master-7f88b489b9-k4c58 ? 1/1 ? ? Running ? 0 ? ? ? ?  154m
?
# kubectl delete pod redis-master-7f88b489b9-k4c58
pod "redis-master-7f88b489b9-k4c58" deleted
?
# kubectl get pod
NAME ? ? ? ? ? ? ? ? ? ? ? ? ?  READY ? STATUS ?  RESTARTS ? AGE
flasktest-68cfdcc66d-d2tb7 ? ? ?1/1 ? ? Running ? 0 ? ? ? ?  27m
redis-master-7f88b489b9-6kk8l ? 1/1 ? ? Running ? 0 ? ? ? ?  12s

我们先将redis-master-7f88b489b9-k4c58这个pod删除掉,之后k8s会自动又创建了新的pod redis-master-7f88b489b9-6kk8l


此时再访问 http://127.0.0.1:30002/ 则报错

redis.exceptions.ConnectionError: Error 113 connecting to 10.1.0.59:6379. No route to host.



报 10.1.0.59:6379 连接失败了。


第二个问题,我们设置了replicas的数量是为了做负载均衡,所以如果你在应用里将ip写死的话那就起不到负载均衡了。


所以使用了k8s,如果要访问其它pod的话,则不可以将对方的ip直接写死到应用中的,我们需要通过 服务 来将各个pod进行通信。


1.4 创建redis的service

Service 就是为了能让应用有个稳定的入口,如这里的redis访问我们的应用服务,我们想

先将上面创建redis的pod通过service将端口暴露出来

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

通过 kubectl get service -o wide 查看service详情

kubectl get service -o wide
NAME ? ? ? ? ? ?  TYPE ? ? ?  CLUSTER-IP ? ?  EXTERNAL-IP ? PORT(S) ?  AGE ?  SELECTOR
kubernetes ? ? ?  ClusterIP ? 10.96.0.1 ? ? ? <none> ? ? ? ?443/TCP ?  3d3h ? <none>
redis-master-sr ? ClusterIP ? 10.99.187.220 ? <none> ? ? ? ?6379/TCP ? 44s ? ?name=redis-master
?

可以看到有一个type为ClusterIP的service,这有一个ip,10.99.187.220 使用了6379作为对外端口,我们是不能通过这个IP的6379端口访问到redis-master-sr这个service。但是如果在k8s里的相同网络应用,是可以通过这个CLUSTER-IP 来访问到的。

我们来试一下

if os.environ.get("envname") == "k8s": # 说明是在k8s中
 ? ?REDIS_URL = "redis://{}:{}/{}".format('10.99.187.220', 6379, 1)
else:
 ? ?REDIS_URL = "redis://{}:{}/{}".format('127.0.0.1', 6380, 1)


重新打镜像包 docker build -t flaskk8s:ClusterIP . 创建一个flaskk8s,tag为ClusterIP,修改flask-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
 ?  app: flasktest
  name: flasktest
spec:
  selector:
 ?  matchLabels:
 ? ?  app: flasktest
  replicas: 1
  template:
 ?  metadata:
 ? ?  labels:
 ? ? ?  app: flasktest
 ?  spec:
 ? ?  containers:
 ? ?  - image: flaskk8s:ClusterIP
 ? ? ?  name: flaskweb
 ? ? ?  imagePullPolicy: Never
 ? ? ?  ports:
 ? ? ?  - containerPort: 6000


执行 kubectl apply -f flask-deployment.yaml 生效,再重新访问http://127.0.0.1:30002/ 则又可以正常访问了。



1.5 使用环境变量来访问service

使用service的ClusterIP虽然可以解决了由于pod的重启更换IP的问题,但是如果一个service重启,或者环境重新部署了,那么service的IP又会变了,此时就要重新修改代码了,这肯定是不行的。


我们使用exec命令进入到pod内部,使用env命令查看系统的环境变量

# kubectl get pod
NAME ? ? ? ? ? ? ? ? ? ? ? ? ?  READY ? STATUS ?  RESTARTS ? AGE
flasktest-74865c4b59-6l86m ? ? ?1/1 ? ? Running ? 0 ? ? ? ?  19m
flasktest-74865c4b59-k4pkk ? ? ?1/1 ? ? Running ? 0 ? ? ? ?  19m
redis-master-7f88b489b9-6kk8l ? 1/1 ? ? Running ? 0 ? ? ? ?  160m
?
# kubectl exec -it flasktest-74865c4b59-6l86m /bin/bash
root@flasktest-74865c4b59-6l86m:/app# env
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
REDIS_MASTER_SR_PORT_6379_TCP_PROTO=tcp
HOSTNAME=flasktest-74865c4b59-6l86m
PYTHON_VERSION=3.5.7
envname=k8s
REDIS_MASTER_SR_PORT_6379_TCP_PORT=6379
PWD=/app
REDIS_MASTER_SR_SERVICE_HOST=10.103.116.170
REDIS_MASTER_SR_PORT=tcp://10.103.116.170:6379
HOME=/root
LANG=C.UTF-8
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
REDIS_MASTER_SR_PORT_6379_TCP=tcp://10.103.116.170:6379
GPG_KEY=97FC712E4C024BBEA48A61ED3A5CA953F73C700D
TERM=xterm
SHLVL=1
REDIS_MASTER_SR_PORT_6379_TCP_ADDR=10.103.116.170
KUBERNETES_PORT_443_TCP_PROTO=tcp
PYTHON_PIP_VERSION=19.3.1
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
REDIS_MASTER_SR_SERVICE_PORT=6379
PYTHON_GET_PIP_SHA256=b86f36cc4345ae87bfd4f10ef6b2dbfa7a872fbff70608a1e43944d283fd0eee
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/ffe826207a010164265d9cc807978e3604d18ca0/get-pip.py
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env
?


看到有两个和redis service有关的环境变量

REDIS_MASTER_SR_SERVICE_HOST=10.103.116.170
REDIS_MASTER_SR_SERVICE_PORT=6379


k8s会为每个pod的容器里都增加一组service相关的环境变量,也会随着pod或者service的变化而变化,有了这两个环境变量,我们就可以动态获取IP,修改代码

if os.environ.get("envname") == "k8s": # 说明是在k8s中
    redis_server = os.environ.get("REDIS_MASTER_SR_SERVICE_HOST")
    redis_port = os.environ.get("REDIS_MASTER_SR_SERVICE_PORT")
    REDIS_URL = "redis://{}:{}/{}".format(redis_server, redis_port, 1)
else:
    REDIS_URL = "redis://{}:{}/{}".format('127.0.0.1', 6380, 1)


这时我们就可以不用修改IP来适应pod或者service的变动了。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码