kubernetes中pod間的通信


我們如果創建了一些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 .

創建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的變動了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM