背景
https://github.com/kubesphere/kubesphere/issues/3554#event-4499512886
istio bookinfo微服务中,reviews v2会访问ratings服务;ratings v2默认使用mongodb存储数据;
根本原因是ratings服务有报错,导致reviews在访问ratings服务时超时失败,而reviews对返回的数据处理不够,导致reviews访问出错。
现在将服务全部更新到最新镜像 docker.io/istio/examples-bookinfo-reviews-v1:1.16.3
;发现productpage无法正常访问。
/ # curl productpage:9080/productpage?u=test%
</div>
<div class="col-md-6">
<h4 class="text-center text-primary">Error fetching product reviews!</h4>
<p>Sorry, product reviews are currently unavailable for this book.</p>
</div>
</div>
</div>
老的问题,没有解决,新的问题又来了。
其实将productpage的镜像改成低版本docker.io/istio/examples-bookinfo-reviews-v1:1.16.2
;ratings改成高版本 1.16.3
或是release,都会正常。
但是稀里糊涂绕过去了,总不痛快,现在结合源码,做一次完全的定位与分析。
复现
直接部署微服务,由于这个问题是代码问题,与istio无关,不用注入sidecar。
kubectl apply -f https://raw.githubusercontent.com/istio/istio/89850ae760e3fefb80955b3318a0d41b633dd09f/samples/bookinfo/platform/kube/bookinfo.yaml
➜ ~ k get po
NAME READY STATUS RESTARTS AGE
details-v1-5f449bdbb9-f7b4x 1/1 Running 0 16h
productpage-v1-5598888b56-86n8p 1/1 Running 0 127m
ratings-v1-857bb87c57-hqsxn 1/1 Running 0 16h
reviews-v2-5d56c488f5-mxwnf 1/1 Running 0 16h
此时访问Productpage就会出现上面的错误 Sorry, product reviews are currently unavailable for this book.
检查product日志,发现并没有请求reviews服务:
➜ istio git:(master2) ✗ k logs -f deploy/productpage-v1 -c productpage
INFO:root:start at port 9080
* Serving Flask app "productpage" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: on
INFO:werkzeug: * Running on http://0.0.0.0:9080/ (Press CTRL+C to quit)
INFO:werkzeug: * Restarting with stat
INFO:root:start at port 9080
WARNING:werkzeug: * Debugger is active!
INFO:werkzeug: * Debugger PIN: 257-177-282
INFO:werkzeug:10.233.70.169 - - [30/Mar/2021 00:00:37] "GET /productpage?u=test HTTP/1.1" 200 -
INFO:werkzeug:10.233.70.169 - - [30/Mar/2021 00:00:53] "GET /productpage?u=test HTTP/1.1" 200 -
默认开启的debug日志,会打印所有的http请求。
我们直接看它的代码,就是发个http请求,http://reviews:9080/reviews/0
:
def getProductReviews(product_id, headers):
# Do not remove. Bug introduced explicitly for illustration in fault injection task
# TODO: Figure out how to achieve the same effect using Envoy retries/timeouts
for _ in range(2):
try:
url = reviews['name'] + "/" + reviews['endpoint'] + "/" + str(product_id)
res = requests.get(url, headers=headers, timeout=3.0) # http请求
except BaseException
res = None
if res and res.status_code == 200:
return 200, res.json()
status = res.status_code if res is not None and res.status_code else 500
return status, {'error': 'Sorry, product reviews are currently unavailable for this book.'}
为什么这个http请求没有响应的呢?是网络?还是什么?
进productpage这个pod发现无法使用常用的所有的命令 curl
dig
wget
nslookup
ping
,也无法装软件。
进productpage这个pod 的ns后,主动curl 这个服务的ip地址+路径,能正常返回。
通过dns,能正常解析reviews服务。
在同一个namespace手动起一个alpine的pod作为客户端,来请求reviews服务,正常返回。
kubectl run alpine -it --image=alpine --restart=Never --rm
一切看似正常,代码也正常,但就是在productpage使用代码访问时就不对,而且这个Pod里面不能手动模拟请求。。。what a fuck!!
调试
现在想使用master分支代码来调试下服务。
该如何调试呢?
- 1.方案一:在本地运行服务后,使用telepresence swap线上的deployment。
但是在mac本地跑这个py时,有些包安装出错,没有继续去折腾。换下个方案
- 2.方案二:直接在Linux上跑这个py,然后使用external service方式在k8s内部使用该外部服务。
export ip=192.168.0.12
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: test-productpage
namespace: default
spec:
ports:
- name: http
port: 9080
---
apiVersion: v1
kind: Endpoints
metadata:
name: test-productpage
namespace: default
subsets:
- addresses:
- ip: ${ip}
ports:
- name: http
port: 9080
protocol: TCP
EOF
然后访问的时候正常,打了些断点,手动打印debug日志,都符合预期。
- 3.方案三:build镜像,并更新
由于每次会把所有的镜像全部构建一次,所有有点慢,且在本地打镜像受网络影响严重。
curl https://raw.githubusercontent.com/istio/istio/e622dc31d88de79074536c65c54a690c06afff56/samples/bookinfo/build_push_update_images.sh
bash build_push_update_images.sh debug --prefix=zackzhangkai
更新上去后,productpage也是正常的。
- 4.方案四:更改productpage的镜像为alpine,并提权
FROM alpine
COPY requirements.txt ./
RUN apk add --update python3 py3-pip python3-dev gcc musl-dev
RUN pip3 install -r requirements.txt
RUN ln -s /usr/bin/python3 /usr/bin/python
COPY test-requirements.txt ./
RUN pip3 install --no-cache-dir -r test-requirements.txt
COPY productpage.py /opt/microservices/
COPY tests/unit/* /opt/microservices/
COPY templates /opt/microservices/templates
COPY static /opt/microservices/static
COPY requirements.txt /opt/microservices/
ARG flood_factor
ENV FLOOD_FACTOR ${flood_factor:-0}
EXPOSE 9080
WORKDIR /opt/microservices
RUN python -m unittest discover
CMD ["python", "productpage.py", "9080"]
更改为alpine基础镜像后,可以随意安装想用的软件,也更方便调试了。
分析
说明线上最新的版本,其实并不是Master的代码。
当某一个镜像中的代码不正常的时候,怎么办?
在linux一切皆文件,包括环境变量、进程信息等,在linux上都可以看到。
首先把productpage更新到有问题的镜像(Istio官方最新镜像,目前是Latest镜像)。
找出该Pod的docker -> 进程PID —> cd /proc/$PID/cwd
此时可以看到该进程的运行目录。把它的代码拷贝到本地,与master代码对比:
可以看到引用了xxxPORTS
变量。
cat environ
发现有这个变量,但是显示不对,到此真相大白:
现在明白了,原来是这个PORT不对,造成没有发送http请求,也就没有被http Debug日志记录。