记一次使用python-alpine镜像部署Django项目排错经历

背景


       在对公司的Django项目进行容器化的过程中,生产环境上出现了一个问题:客户在前台使用产品时有部分请求会返回403状态码,刷新一下后就又访问成功了。后端开发人员在进行排查时发现返回403状态码的原因是客户携带的token过期了,但客户是几分钟之前才登陆的。在咨询后端工程师对token校验的逻辑后发现,非正常的请求客户的token时间比passport服务器上的时间晚了8小时,即部分后端服务器上的程序所使用的时区是UTC,而passport服务器是CST,导致校验不通过。

      由于该项目是由我刚刚完成容器化并部署至生产环境上的,所以在nginx里我只将10%的请求转发至k8s集群,这也是为何客户刷新一下页面又访问正常的原因。

排查与解决


      经过检查容器服务器的时区后,/etc/timezone的内容确认是Asia/Shanghai,而且Django的配置文件中也是将时区设置为Asia/Shanghai,跑在服务器上的程序返回的时间也是正常的,所以初步猜想是容器的环境导致的问题。容器的基础镜像是python:3.7-alpine,所以我编写了一个非常简单的django配置文件分别在centos服务器和python-alpine服务器上进行测试,配置文件如下:

cat settings.py
USE_TZ = False
TIME_ZONE = 'Asia/Shanghai'
SECRET_KEY = 'hello'

   配置文件比较简单,use_tz字段禁止使用Django默认的时区,time_zone字段配置时区,secret_key配置一个密码(随便写,别为空就行),接下来使用“控制变量法”进行排查:

1、use_tz = True

#centos:
>>> import os
>>> os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
>>> from django.utils import timezone
>>> d1 = timezone.now()
>>> print(d1)
2020-06-04 10:58:28.405656+00:00                 #非北京时间
>>>  

#python-alpine:
>>> import os
>>> os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
>>> from django.utils import timezone
>>> d1 = timezone.now()
>>> print(d1)
2020-06-04 10:59:30.649517+00:00         #非北京时间,但与centos的时区相同
>>> 

2、use_tz=False, time_zone='Asia/Shanghai'

#centos:
>>> import os
>>> os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
>>> from django.utils import timezone
>>> d1 = timezone.now()
>>> print(d1)
2020-06-04 18:51:15.040756                 #取到的时间正常
>>>  

#python-alpine:
>>> import os
>>> os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
>>> from django.utils import timezone
>>> d1 = timezone.now()
>>> print(d1)
2020-06-04 10:55:19.288442            #该时间不是北京时间
>>> 

   以上两种情况表明,在使用默认时区情况下二者所取到的时间并无差别,都使用的是UTC时间,但当指定时区后,在alpine容器里似乎并没有生效,但是我的容器里的/etc/timezone和/etc/localtime的内容与centos的配置一模一样,出现这种情况是不是表明Django不是从这两个文件中的一个取的时间呢?我将容器和centos机器的timezone和localtime文件删除后得到的结果跟之前两种情况测试完全一致,这表明我们之前的猜想是对的:Django程序没有从这两个文件中取时间。

    由于我本人之前对Django不是特别熟悉,查阅了官方文档后发现,在Linux服务器上部署Django程序时,时区的选择是从/usr/share/zoneinfo目录下查找的,而查找的路径则是/usr/share/zoneinfo/settings.TIME_ZONE。这时我忽然想起来在制作容器镜像时我在做完容器的时区设定后将tzdata删除了(为了保障容器镜像尽可能小),Dockerfile文件内容如下:

cat Dockerfile
FROM python:3.7-alpine

WORKDIR /app

RUN apk add tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone \
    && apk del tzdata

COPY . /app
RUN pipenv install --deploy  --system --ignore-pipfile \
    && rm -fr ~/.cache/pip* \
    && rm -fr /tmp \
    && mkdir /app/logs \
    && touch /app/logs/access.log \
    && touch /app/logs/error.log

   在测试集群的容器里将镜像进行更新后,复测问题已经解决,而且访问日志的时间也一并调整为北京时间,问题圆满解决:

启示


    1、 在面对问题时需要先保障业务恢复,在本次问题出现时先将容器提供的服务从正式环境中摘离出来,保障后续客户使用正常,然后再进行排查解决;

    2、在将项目进行容器化之前一定要对项目框架熟悉一些,需要跟开发人员和测试人员多多交流,尽量规避一些可能出现的问题,在将容器化项目部署到生产环境时一定要与之前的环境实行蓝绿部署或者金丝雀部署等灰度发布规则,出现较大问题时能够第一时间回退,当容器化项目迭代一段时间后再考虑将将老环境摘离生产;

   3、遇到问题时有了猜想及排查思路就要尽快动手验证,多进行验证才能尽快找到突破口。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章