jenkins 동시 수행되는 job들 중 ssh 연결 실패

jenkins 동시 수행되는 job들 중 ssh 연결 실패(UNREACHABLE 에러)가 발생하는 경우가 있다.
jenkins 에 5개의 잡이 다음과 같은 주기로 실행되고 있다.
a job : 1분 주기
b job : 2분 주기
c job : 2분 주기
d job : 5분 주기
e job : 5분 주기
각 잡들은 ansible 로 모두 같은 파일 서버로 부터 각 잡에 맞는 파일을 다운로드 한다.
간헐적으로 잡들의 ansible 연결 실패 발생한다.
다음과 같은 증상들이 있다.
- 간헐적으로 ssh 연결 실패가 발생한다.
- 연결 실패가 발생하면 최소 2개의 잡이 동시에 발생한다.
- ansible -vvv 옵션으로 자세한 verbose 를 찍어보면, 다음 처럼 특정 controlpath(cp, 소켓파일)을 사용한다.
 예) 8a8131ca11
 ssh -o ControlPath=/home/ysoftman/.ansible/cp/8a8131ca11
- 간헐적 에러가 발생하는 시간은 8a8131ca11 가 생성되는 시간이다.
- 최초 8a8131ca11 생성 후 ansible ssh 연결이 잘 되다가 갑자기 끊어지는 현상이 있다.
- ssh -o ControlPersist=60s (ansible ssh 디폴트, https://docs.ansible.com/ansible/latest/plugins/connection/ssh.html) 옵션을 사용해 8a8131ca11 가 60초만 유지후 삭제 될거라고 생각했지만 60초 후에도 계속 유지되는것처럼 보였다.
우선 모든 잡들이 8a8131ca11 이름을 사용하고 있다.
ansible.cfg 에 ssh 관련 별다른 옵션은 설정하지 않았다.
따라서 ansible 의 기본 controlpath 생성 로직을 사용하게 된다.
https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/connection/ssh.py -> _create_control_path()
create_control_path() 는
remote host, remote port, remote user, connection, pid 로
sha1() -> hdex -> digest로 앞 10바이트만 잘라서 cp 이름을 정한다.
하지만 아래 소스를 보면 control_path 없으면, host, port, user만 사용된다.
결국 같은 host, port, user 를 사용하는 5개의 잡은 모두 같은 cp 이름을 생성한다.
https://github.com/ansible/ansible/blob/47aea84924e8149e153107564c8b029dc4f52c27/lib/ansible/plugins/connection/ssh.py#L663
if not self.control_path:
    self.control_path = self._create_control_path(
        self.host,
        self.port,
        self.user
    )
8a8131ca11(cp 파일) 60초 후 삭제될거라 생각했지만
1분 간격으로 실행되는 a잡이 실행시 ssh -o ControlPersist=60s 로
cp 60s 늘리거나 새로 생성 한다.
이런식으로 cp 파일은 의도하지 않게 몇일 동안 삭제 되지 않다가 잡(ansible) 수행시
어떤 이유에서인지 간헐적으로 cp 파일 삭제 후 생성되는 순간이 발생하고
이때 삭제된 cp 를 사용하려는 ansible 잡이 연결 실패 에러가 나는것으로 보인다.

# 해결
# ansible 파일 서버 inventory 파일에
# ansible_ssh_common_args 옵션을 사용하면
ysoftman.fileserver  ansible_ssh_common_args="-o ControlPersist=10s"

# 다음과 같이 명령 뒤에 설정값을 중복으로 붙게 되는데
# ssh 는 먼저 선언한 값으로 처리해 10s 값이 적용되지 않는다.
ssh -o ControlPersist=60s -o ControlPersist=10s

# 그래서 별도의 ansible_ysoftman.cfg 파일을 만들어 설정했다.
[defaults]
host_key_checking = False
remote_user = ysoftman
transport = ssh
stdout_callback = debug

[ssh_connection]
# UNREACHABLE 에러 발생시 발생시 2번은 재시도
retries = 2

# ssh -tt(tty 강제 할당) 옵션 비활성화
usetty = no

# ssh 옵션 자세한 설명은 man ssh_config 참고
# ControlMaster=auto 사용할 수 있는 cp 파일이 있으면 사용하고 없으면 새로 만든다.
# ControlPersist=yes(또는 0) cp 파일이 백그라운드로 계속 남아 있도록 한다.
# ssh_args = -o ControlMaster=auto -o ControlPersist=yes

# ansible 실행시 ANSIBLE_CONFIG 환경변수로 적용
ANSIBLE_CONFIG=ansible_ysoftman.cfg ansible-playbook -i ./inventory/aaa do_something.yml

# 참고
# ssh cp 관련 control command
# 8a8131ca11 종료
ssh -o ControlPath=/home/ysoftman/.ansible/cp/8a8131ca11 -O exit ysoftman@10.10.10.10

# 8a8131ca11 상태 확인
ssh -o ControlPath=/home/ysoftman/.ansible/cp/8a8131ca11 -O check ysoftman@10.10.10.10

comments:

댓글 쓰기