kubernetes中pod间的通信
<p>我们如果创建了一些pod,那么它们之间是怎么通信的呢?因为pod的ip地址是有可能变化的,这里我们主要讨论几个场景</p><ul>
<li>同一网络下的不同pod间是怎么通信的?</li>
<li>同一个pod中不同的容器是怎么通信的?</li>
<li>不同的网络下不同的pod是怎么通信的?</li>
</ul>
<h2 id="一同一网络下的不同pod间通信">一、同一网络下的不同pod间通信</h2>
<p>第一种场景可能是应用最多的场景,比如我写了一个web应用,它使用python作为后端,使用redis作为数据库,redis和python分别创建在不同的pod里,我会使用deployment创建rs的方式再创建pod,正常情况下,我们是不希望这个redis被外面的应用访问到的,只允许在python的应用访问到,</p>
<p><img src="https://img2020.cnblogs.com/blog/1974833/202003/1974833-20200320174520028-1686570370.png" alt="" loading="lazy"></p>
<p>如上图,用户可以使用python应用暴露出来的6000端口(其实是k8s里的service暴露出来的)来访问应用,但是并不能直接访问里面redis的6379端口。</p>
<h3 id="11-创建redis-pod">1.1 创建redis pod</h3>
<p>我们先创建一个redis pod</p>
<pre><code class="language-yaml">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
</code></pre>
<p>查看pod详细信息</p>
<pre><code class="language-shell"># 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>
</code></pre>
<h3 id="12-创建python应用">1.2 创建python应用</h3>
<p>我先使用docker run 本地启一个redis用于代码调试,为了和上面启的redis pod 区分(其实也不用区分,上面的redis pod 本身也没有对外暴露端口),这里使用6380作为对外端口</p>
<pre><code class="language-shell">docker run --name myredistest -d -p 6380:6379 redis
</code></pre>
<p>之后就可以使用redis客户端进行访问了,我在db1中创建了一个redistest的key</p>
<p><img src="https://img2020.cnblogs.com/blog/1974833/202003/1974833-20200320174552857-1311340370.png" alt="" loading="lazy"></p>
<p>写一个python应用,读取redis中的数据</p>
<pre><code class="language-python">#-*- 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)
</code></pre>
<p>之后用浏览器访问127.0.0.1:6000 就可以得到正常的输出了<br>
<img src="https://img2020.cnblogs.com/blog/1974833/202003/1974833-20200320174628104-1493815574.png" alt="" loading="lazy"></p>
<h3 id="13-在pod中访问redis">1.3 在pod中访问redis</h3>
<p>上面只是将python访问本地的redis,我们最终是要将这个python应用打包成镜像,放到k8s中,那么如果在k8s中这个flask应用该如果访问到redis呢?</p>
<p>为了实现一套代码可以在不同的环境中执行,我在redis的初始化时加上一点判断</p>
<pre><code class="language-python">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)
</code></pre>
<p>现在主要的问题在于,<code>REDIS_URL = "redis://{}:{}/{}".format('redisIP', "redispord", 1)</code>k8s中的redisIP和redispord这里该填写什么呢?</p>
<p>上面使用<code>kubectl get pods -o wide</code> 查看到redis的ip为<code>10.1.0.59</code> ,那么我们试试能不能通过这个IP和端口来访问redis呢?</p>
<pre><code class="language-python">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)
</code></pre>
<p>先创建一个Dockerfile</p>
<pre><code class="language-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"]
</code></pre>
<p>创建镜像<code>docker build -t flaskk8s .</code></p>
<p>创建pod,创建flask-deployment.yaml</p>
<pre><code class="language-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
</code></pre>
<p>因为是本地的镜像,所以在加上<code>imagePullPolicy: Never</code>,否则k8s默认是会从dockerhub上去拉取。</p>
<pre><code class="language-shell"># 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
</code></pre>
<p>创建flasktest的service,让其可以通过浏览器访问</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Service
metadata:
name: flask-service
labels:
name: flaskservice
spec:
type: NodePort
ports:
- port: 6000
nodePort: 30002
selector:
app: flasktest
</code></pre>
<pre><code class="language-shell"># 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
</code></pre>
<p>可以看到30002端口已经被暴露出来,之后我们访问<code>http://127.0.0.1:30002/</code> ,看到可以正常的访问<br>
<img src="https://img2020.cnblogs.com/blog/1974833/202003/1974833-20200320174728317-1137606528.png" alt="" loading="lazy"></p>
<p>一切看着都很顺利对不对,但是我们来考虑两个问题</p>
<ol>
<li>如果redis的pod挂掉会怎么样?</li>
<li>如果创建redis时<code>replicas</code>为大于1时,那么指定某个POD的的IP是否妥当?</li>
</ol>
<p>第一个问题,由于是使用deployment创建的rs,再创建的pod,此时如果redis的某个pod挂了,由于rs中定义了<code>replicas: 1</code>,它会重新再起一个redis的pod,此时的IP可能就会变了。我们来试验一下,只需要将原来的pod删除掉即可,k8s会自动再创建一个新的pod</p>
<pre><code class="language-shell"># 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
</code></pre>
<p>我们先将<code>redis-master-7f88b489b9-k4c58</code>这个pod删除掉,之后k8s会自动又创建了新的pod <code>redis-master-7f88b489b9-6kk8l</code></p>
<p>此时再访问 <code>http://127.0.0.1:30002/</code> 则报错</p>
<p><code>redis.exceptions.ConnectionError: Error 113 connecting to 10.1.0.59:6379. No route to host.</code></p>
<p><img src="https://img2020.cnblogs.com/blog/1974833/202003/1974833-20200320174748635-1441051333.png" alt="" loading="lazy"></p>
<p>报 10.1.0.59:6379 连接失败了。</p>
<p>第二个问题,我们设置了<code>replicas</code>的数量是为了做负载均衡,所以如果你在应用里将ip写死的话那就起不到负载均衡了。</p>
<p>所以使用了k8s,如果要访问其它pod的话,则不可以将对方的ip直接写死到应用中的,我们需要通过 <strong>服务</strong> 来将各个pod进行通信。</p>
<h3 id="14-创建redis的service">1.4 创建redis的service</h3>
<p>Service就是为了能让应用有个稳定的入口,如这里的redis访问我们的应用服务,我们想</p>
<p>先将上面创建redis的pod通过service将端口暴露出来</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Service
metadata:
name: redis-master-sr
labels:
name: redis-master
spec:
ports:
- port: 6379
targetPort: 6379
selector:
app: redis
</code></pre>
<p>通过 <code>kubectl get service -o wide</code> 查看service详情</p>
<pre><code class="language-shell">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
</code></pre>
<p>可以看到有一个type为ClusterIP的service,这有一个ip,10.99.187.220 使用了6379作为对外端口,我们是不能通过这个IP的6379端口访问到redis-master-sr这个service。但是如果在k8s里的相同网络应用,是可以通过这个<code>CLUSTER-IP</code> 来访问到的。</p>
<p>我们来试一下</p>
<pre><code class="language-python">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)
</code></pre>
<p>重新打镜像包 <code>docker build -t flaskk8s:ClusterIP .</code>创建一个flaskk8s,tag为ClusterIP,修改flask-deployment.yaml</p>
<pre><code class="language-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
</code></pre>
<p>执行 <code>kubectl apply -f flask-deployment.yaml </code> 生效,再重新访问http://127.0.0.1:30002/ 则又可以正常访问了。</p>
<h3 id="15-使用环境变量来访问service">1.5 使用环境变量来访问service</h3>
<p>使用service的ClusterIP虽然可以解决了由于pod的重启更换IP的问题,但是如果一个service重启,或者环境重新部署了,那么service的IP又会变了,此时就要重新修改代码了,这肯定是不行的。</p>
<p>我们使用exec命令进入到pod内部,使用env命令查看系统的环境变量</p>
<pre><code class="language-shell"># 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
</code></pre>
<p>看到有两个和redis service有关的环境变量</p>
<pre><code class="language-shell">REDIS_MASTER_SR_SERVICE_HOST=10.103.116.170
REDIS_MASTER_SR_SERVICE_PORT=6379
</code></pre>
<p>k8s会为每个pod的容器里都增加一组service相关的环境变量,也会随着pod或者service的变化而变化,有了这两个环境变量,我们就可以动态获取IP,修改代码</p>
<pre><code class="language-python">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)
</code></pre>
<p>这时我们就可以不用修改IP来适应pod或者service的变动了。</p>
</div>
<div id="MySignature" role="contentinfo">
欢迎关注我的公众号-序语程言<br><br>
来源:https://www.cnblogs.com/yangfantuan/p/12533295.html
頁:
[1]