>  기사  >  시스템 튜토리얼  >  반년 동안 나를 괴롭혔던 문제

반년 동안 나를 괴롭혔던 문제

PHPz
PHPz앞으로
2024-01-16 23:33:181323검색

이 글에서는 작성자가 거의 반년 동안 고민했던 가상화 환경의 어려운 오류를 소개하겠습니다. 오류의 최종 원인과 복구 방법도 말도 안됩니다. 이 과정이 복잡해서가 아니라 심리적 과정을 공유하고, 실패에 직면했을 때 비즈니스와 기술의 균형을 맞추는 방법, 검색 엔진을 올바르게 사용하는 방법에 대해 고민합니다.

결함현상

우리는 내부 테스트 단계에서 안정적으로 실행되는 고성능 프록시 클러스터를 보유하고 있습니다. 그러나 정식 출시된 지 보름도 채 되지 않아 프록시 서비스를 제공하는 호스트가 갑자기 중단되면서 호스트의 모든 서비스가 중단되었습니다. 방해를 받다.

결함 분석

장애가 발생하면 호스트가 직접 충돌하여 원격으로 로그인할 수 없습니다. 전산실은 현장의 키보드 입력에 응답합니다. 호스트 syslog가 ELK에 연결되어 있으므로 충돌 전후의 다양한 syslog를 수집했습니다.

오류 로그

충돌된 호스트의 syslog를 확인한 결과 시스템이 충돌하기 전에 다음 커널 오류가 보고되었음을 발견했습니다.

으아악

커널 널 포인터에 액세스한 후 시스템 버그가 발생하여 일련의 호출 스택 오류가 발생하고 결국 충돌이 발생하는 것을 보여줍니다.

오류 현상을 더 자세히 분석하려면 먼저 이 고성능 프록시 클러스터의 아키텍처를 이해해야 합니다.

건축 소개

반년 동안 나를 괴롭혔던 문제

단일 노드는 10G 네트워크 카드가 장착된 호스트에서 Docker 컨테이너를 실행한 후, 컨테이너에서 Haproxy 인스턴스를 실행합니다. 각 노드와 인스턴스의 구성 정보 및 비즈니스 정보는 스케줄러에서 호스팅됩니다.

특별한 점은 호스트가 Linux Bridge를 사용하여 Docker 컨테이너의 IP 주소를 직접 구성한다는 것입니다. 호스트 자체 외부 네트워크 IP를 포함한 모든 외부 서비스 IP가 Linux Bridge에 연결됩니다.

신청 소개

각 호스트의 운영체제, 하드웨어, Docker 버전은 모두 동일합니다.

으아악 사전 분석

이 클러스터의 호스트 구성이 일관되고 오류 증상도 일관됩니다.

1. Docker 버전이 호스트 커널 버전과 호환되지 않습니다

세 호스트의 환경은 원래 동일하지만, 한 호스트는 서비스를 2개월 동안 안정적으로 실행하다가 다운되고, 한 호스트는 서비스를 실행하고 한 달 만에 다운되고, 다른 호스트는 서비스를 온라인으로 운영한 지 일주일 만에 다운되었습니다.
비정상적인 충돌 로그 외에도 각 호스트에도 동일한 오류 로그가 있는 것으로 확인되었습니다.

으아악

위 팁에 따르면 이 문제는 운영 체제 커널 버전이 이 Docker 버전의 특정 기능을 지원하지 않기 때문에 발생하는 것입니다. 그러나 검색 엔진에서 검색해도 Docker의 기능이나 시스템 안정성에는 영향을 미치지 않습니다.

예:

으아악

이 문제는 Docker 1.9부터 존재했으며 1.12.3에서 수정되었습니다.

예를 들어 Github의 누군가가 다음과 같이 답했습니다.

으아악

여기서 언급한 것은 버전 v1.12.2에서 해결할 수 있는 문제들일 뿐입니다. Docker 버전을 업그레이드한 후에도 여전히 충돌이 지속되는 것을 발견했습니다.

그래서 저희는 여러 Google을 통해 저희와 동일한 오류 현상으로 많은 문제를 확인하였고, 오류와 Docker 사이의 상관관계를 최초에 확인하였으며, 공식 이슈를 기반으로 Docker 버전이 시스템 커널과 호환되지 않는다는 사실도 최초에 확인하였습니다. 버전이 다운될 수 있으므로 공식 변경 로그 및 이슈를 통해 호스트에서 사용하는 Docker 버전이 시스템 커널 버전과 호환되지 않음을 확인하여 Docker 버전을 1.12.2로 업그레이드했습니다. , 그러나 여전히 사고 없이 충돌이 발생했습니다.

2. Linux 브리지 방법을 사용하여 호스트 네트워크 카드를 수정하면 버그가 발생할 수 있습니다

일주일 동안 서비스를 실행한 후 크래시가 발생한 호스트를 발견하고 Docker 실행을 중지하고 네트워크만 수정한 후 일주일 동안 안정적으로 실행되었으며 아무런 이상이 발견되지 않았습니다.

3. Docker 컨테이너용 IP를 구성하기 위해 파이프 작업을 사용하면 버그가 발생할 수 있습니다

컨테이너에 IP를 할당할 때 오픈 소스 Pipework 스크립트를 사용했기 때문에 Pipework의 작동 원리에 버그가 있다고 의심하여 ​​Pipework를 사용하지 않고 IP 주소를 할당하려고 시도했지만 호스트가 여전히 충돌하는 것을 발견했습니다.

그래서 초기 문제 해결에 어려움을 겪었고, 적어도 한 달에 한 번씩 호스트가 충돌하는 것을 보고 매우 답답했습니다.

故障定位

因为还有线上业务在跑,所以没有贸然升级所有宿主内核,而是期望能通过升级Docker或者其它热更新的方式修复问题。但是不断的尝试并没有带来理想中的效果。

直到有一天,在跟一位对Linux内核颇有研究的老司机聊起这个问题时,他三下五除二,Google到了几篇文章,然后提醒我们如果是这个 bug,那是在 Linux 3.18 内核才能修复的。

原因:

从sched: Fix race between task_group and sched_task_group的解析来看,就是parent 进程改变了它的task_group,还没调用cgroup_post_fork()去同步给child,然后child还去访问原来的cgroup就会null。

不过这个问题发生在比较低版本的Docker,基本是Docker 1.9以下,而我们用的是Docker1.11.1/1.12.1。所以尽管报错现象比较相似,但我们还是没有100%把握。

但是,这个提醒却给我们打开了思路:去看内核代码,实在不行就下掉所有业务,然后全部升级操作系统内核,保持一个月观察期。

于是,我们开始啃Linux内核代码之路。先查看操作系统本地是否有源码,没有的话需要去Linux kernel官方网站搜索。

下载了源码包后,根据报错syslog的内容进行关键字匹配,发现了以下内容。由于我们的机器是x86_64架构,所以那些avr32/m32r之类的可以跳过不看。结果看下来,完全没有可用信息。

/kernel/linux-3.16.39#grep -nri “unable to handle kernel NULL pointer dereference” *

arch/tile/mm/fault.c:530:              pr_alert(“Unable to handlekernel NULL pointer dereference/n”);

arch/sparc/kernel/unaligned_32.c:221:                  printk(KERN_ALERT “Unable to handle kernel NULL pointerdereference in mna handler”);

arch/sparc/mm/fault_32.c:44:           “Unable to handle kernel NULL pointer dereference/n”);

arch/m68k/mm/fault.c:47:                   pr_alert(“Unable tohandle kernel NULL pointer dereference”);

arch/ia64/mm/fault.c:292:            printk(KERN_ALERT “Unable tohandle kernel NULL pointer dereference (address %016lx)/n”, address);

debian/patches/bugfix/all/mpi-fix-null-ptr-dereference-in-mpi_powm-ver-3.patch:20:BUG:unable to handle kernel NULL pointer dereference at           (null)

最后,我们还是下线了所有业务,将操作系统内核和Docker版本全部升级到最新版。这个过程有些艰难,当初推广这个系统时拉的广告历历在目,现在下线业务,回炉重造,挺考验勇气和决心的。

故障处理

下面是整个故障处理过程中,我们进行的一些操作。

升级操作系统内核

对于Docker 1.11.1与内核4.9不兼容的问题,可以删除原有的Docker配置,然后使用官方脚本重新安装最新版本Docker

/proxy/bin#ls /var/lib/dpkg/info/docker-engine.
docker-engine.conffiles  docker-engine.md5sums    docker-engine.postrm     docker-engine.prerm
docker-engine.list       docker-engine.postinst   docker-engine.preinst
#Getthe latest Docker package.
$curl -fsSL https://get.docker.com/ | sh
#启动
nohupdocker daemon -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock-s=devicemapper&

这里需要注意的是,Docker安装方式在不同操作系统版本上不尽相同,甚至相同发行版上也有不同,比如原来我们使用以下方式安装Docker:

apt-get install docker-engine

然后在早些时候,还有使用下面的安装方式:

apt-get install lxc-docker

可能是基于原来安装方式的千奇百怪导致问题丛出,所以Docker官方提供了一个脚本用于适配不同系统、不同发行版本Docker安装的问题,这也是一个比较奇怪的地方,所以Docker生态还是蛮乱的。

验证
16:44:15 up 28 days, 23:41,  2 users, load average: 0.10, 0.13, 0.15
docker    30320     1  0 Jan11 ?        00:49:56 /usr/bin/docker daemon -p/var/run/docker.pid

Docker内核升级到1.19,Linux内核升级到3.19后,保持运行至今已经2个月多了,都是ok的。

总结

这个故障的处理时间跨度很大,都快半年了,想起今年除夕夜收到服务器死机报警的情景,心里像打破五味瓶一样五味杂陈。期间问过不少研究Docker和操作系统内核的同事,往操作系统内核版本等各个方向进行了测试,但总与正确答案背道而驰或差那么一点点。最后发现原来是处理得不够彻底,比如升级不彻底,环境被污染;比如升级的版本不够新,填的坑不够厚。回顾了整个故障处理过程,总结下来大概如下:

回归运维的本质

运维要具有预见性、长期规划,而不能仅仅满足于眼前:

  1. 비상 계획: 시스템이 온라인 상태가 된 후 발생할 수 있는 장애 유형을 요약하고 비상 계획을 제공합니다.
  2. 서비스 구축: 서비스의 우선순위를 정한 후 오류를 처리합니다.
  3. 애플리케이션 버전 선택 등 기술적 선택 문제: 환경 배포 및 애플리케이션 선택 시 다양한 버전에 특별한 주의가 필요합니다. 커뮤니티에서 일반적이거나 다른 사람들이 테스트 또는 검증한 버전을 사용하는 것이 가장 좋습니다. 회사에 다니는 학생.
  4. 운영 체제 커널: 커널을 합리적으로 업그레이드하려면 특정 버전에서 문제를 찾아 커널 버전을 목표 방식으로 업그레이드할 수 있습니다. 그렇지 않으면 모든 것이 헛된 것입니다.
  5. 원래 설계에서는 서로 다른 사용자 스케줄러에 동일한 컨테이너에 대한 동시 작업을 위한 잠금 메커니즘이 없었으며 소스 판단 원칙도 따르지 않았습니다. 마이그레이션 중에는 마이그레이션할 대상 주소가 로컬 주소인지 여부를 판단하여 로컬 주소인 경우 작업을 거부해야 합니다. 이 질문이 여러분에게도 친숙하게 들리는지 궁금합니다. 많은 사람들이 프로그램 개발 과정에서 입력 소스나 작업의 소스 상태를 판단하지 않아 다양한 버그가 발생하는 경우가 많다는 것을 알게 되었습니다.
Google의 기능

이 문제를 해결하는 과정에서 다양한 사람들이 Google을 사용하여 다양한 것을 검색한다는 사실을 알게 될 것입니다. 나는 이것이 검색 엔진이 결함으로 가득 차 있거나 유연한 곳이라고 생각합니다. 이 결함에 대해서는 Linux Docker Unable을 사용하여 커널 NULL 포인터 역참조를 처리하여 검색했지만 결과는 "커널 NULL 포인터 역참조를 처리할 수 없음"을 사용한 다른 결과와 달랐습니다. 그 이유는 ""를 추가하면 검색이 더 정확해지기 때문입니다. Google을 여는 올바른 방법에 대해.

위 내용은 반년 동안 나를 괴롭혔던 문제의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 linuxprobe.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제