레이블이 ansible인 게시물을 표시합니다. 모든 게시물 표시
레이블이 ansible인 게시물을 표시합니다. 모든 게시물 표시

old version ansible

# ansible playbook 이 jenkins job 에서 다음과 같은 에러가 발생했다.
Unsupported parameters for (dnf) module: sslverify Supported parameters include: 

# sslverify 는 ansible 2.13 에 추가되었다.
# 그런데 현재 사용하는 버전은 2.14 로 문제가 없어야 한다.
ansible --version
ansible [core 2.14.2]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/ysoftman/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.11/site-packages/ansible
  ansible collection location = /home/ysoftman/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.11.2 (main, Oct  5 2023, 16:06:03) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18)] (/usr/bin/python3.11)
  jinja version = 3.1.2
  libyaml = True

# 확인 결과 jenkins job 의 script 에서 아래와 같이 python 3.8 버전을 우선하게 설정이 되어 있었고
# python-3.8 /bin 에 ansible 바이너리가 있었다.
export PATH="/home/ysoftman/Python-3.8.17/bin:${PATH}"
export PYTHON_PATH="/home/ysoftman/Python-3.8.17"

# 그래서 Python-3.8.17/bin/ansible-playbook 2.9 버전을 사용하게 되는게 문제였다.
ansible-playbook --version
ansible-playbook 2.9.22
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/ysoftman/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ysoftman/Python-3.8.17/lib/python3.8/site-packages/ansible
  executable location = /home/ysoftman/Python-3.8.17/bin/ansible-playbook
  python version = 3.8.17 (default, Nov  2 2023, 17:16:07) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18)]

# 정리
# 로컬에서는 최신 ansible 버전 기준으로 playbook을 작성했는데
# 배포시에는 python, ansible 을 고정된 버전으로 사용하고 있었음.

install mysql8 in rocky8

# rocky8 에서 mysql8.0 설치하기 위해선 다음과 같이 외부 저장소로 부터 설치해야 했다.
# 참고로 프록시가 필요한 경우 sudo 실행시 -E 로 proxy 설정 유지가 필요하다.
(프록시설정) sudo -E yum -y install https://dev.mysql.com/get/mysql80-community-release-el8-9.noarch.rpm

# 혹시나 잘못되면 다시 지우고 설치하자!!!(요거 몰라서 삽질)
sudo yum erase -y mysql80-community-release-el8-9.noarch

# mysql80 저장소가 보인다.
yum repolist enabled | grep mysql.*-community

# 기존 mysql 모듈은 비활성화 처리해야 mysql-community-8.0 패키지를 사용할 수 있다.
sudo yum module reset -y mysql
sudo yum module disable -y mysql

# 패지키 확인, 버전이 8.0으로 보인다.
sudo yum info mysql-community-devel mysql-community-server

# 이제 설치하면 된다.
(프록시설정) sudo -E yum install -y mysql-community-devel mysql-community-server

#####

# ansible 사용시
- block
  - name: "Install MySQL8 remote repository for Rocky 8"
    yum:
      # redirect url 사용시 에러 발생
      #name: "https://dev.mysql.com/get/mysql80-community-release-el8-9.noarch.rpm"
      name: "https://repo.mysql.com/mysql80-community-release-el8-9.noarch.rpm"
      state: installed
      sslverify: false
      disable_gpg_check: true
      update_cache: true
    when: ansible_distribution == "Rocky" and ansible_distribution_major_version == "8"

  - name: "Install MySQL8 devel package for Rocky 8"
    yum:
      name: mysql-community-devel
      state: installed
    when: ansible_distribution == "Rocky" and ansible_distribution_major_version == "8"

  - name: "Install MySQL8 server package for Rocky 8"
    yum:
      name: mysql-community-server
      state: installed
    when: ansible_distribution == "Rocky" and ansible_distribution_major_version == "8"

  become: true
  environment: "{{ my_proxy }}"

ansible task elapsed time

# ansible.cfg 에 다음을 추가해서 오래 걸리는 부분을 파악해볼 수 있다.
[defaults] 
# 동시에 20개 호스트를 처리할 수 있도록 한다.
forks=20

# task,role 별 elapsed time 출력
callbacks_enabled = timer, profile_tasks, profile_roles

# 다음 playbook 실행하면 아래 스샷처럼 마지막에 각 task, role 처리 시간을 알 수 있다.
ansible-playbook -i ./inventory myplaybook.yml -t helloworld -vv --connection=local

install postresql by yum

# centos 7 에서 postresql 9.6 버전을 다음 repository로 설치하려고 하는데 해당 URL은 사라진 상태다.
# sudo yum install -y https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

# 대신 최신 yum repository 를 설치하자
sudo yum install -y -v https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

# 만약 gpg 체크가 필요하다는 에러 메시지가 나오면 다음 설정으로 비활성화 할 수 있다.
sudo vi /etc/yum.conf
gpgcheck=0

# 참고로 ansible 사용시 저장소 추가 예시
- name: "Install Postgresql 15 for centos/rocky"
  yum:
    name: "http://download.postgresql.org/pub/repos/yum/reporpms/EL-{{ansible_distribution_major_version}}-x86_64/pgdg-redhat-repo-latest.noarch.rpm"
    state: present
    sslverify: false
    disable_gpg_check: true
  become: yes

# 설치된 yum repository 확인
sudo yum repolist

# (필요시) /etc/yum.repo.d 내용을 다시 로딩
sudo yum makecache -q

# 기본 postgresql 패키지는 비활성화
sudo yum module disable postgresql

# 15버전 패키지가 리스트에 있는지 확인
yum list | grep -E ^postgresql15

# 설치
sudo yum install -y postgresql15-server postgresql15-contrib

# db초기화
sudo /usr/pgsql-15/bin/postgresql-15-setup initdb

# postgresql 서비스 활성화,시작,상태확인
sudo systemctl enable postgresql-15
sudo systemctl start postgresql-15
sudo systemctl status postgresql-15

ansible docker sdk for python 에러

# 다음과 같이 ansible 로 docker 처리를 하는 경우 
- name: ysoftman docker build
  docker_image:
    path: "/home/ysoftman/testdocker"
    name: "/ysofmtan/testdocker"
    tag: "test"
    state: build
    force: yes

# python 용 docker sdk 를 찾을 수 없다는 에러가 발생한다.
MSG:

Failed to import the required Python library (Docker SDK for Python: docker (Python >= 2.7) or docker-py (Python 2.6)) on xxxxx's Python /usr/bin/python. Please read module documentation and install in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter, for example via `pip install docker` or `pip install docker-py` (Python 2.6). The error was: No module named parse

# python 2.7.5 이라
ansible --version | command grep python
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  python version = 2.7.5 (default, Nov 16 2020, 22:23:17) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]

# docker 패키지를 설치했다.
sudo pip install docker

# 설치 확인
pip list | grep docker
10:docker (5.0.0)

# 설치해도 똑같은 에러가 발생한다.
# 혹시나 해서 docker-py 를 설치하면
sudo pip install docker-py

# 2개를(docker, docker-py)를 설치하면 안된다는 에러가 발생한다.
Cannot have both the docker-py and docker python modules (old and new version of Docker SDK for Python) installed together as they use the same namespace and cause a corrupt installation. Please uninstall both packages, and re-install only the docker-py or docker python module (for xxxxx's Python /usr/bin/python). It is recommended to install the docker module if no support for Python 2.6 is required. Please note that simply uninstalling one of the modules can leave the other module in a broken state.

# 둘다 지워 보자.
sudo pip uninstall docker docker-py

# 역시 위 첫번째 에러가 발생한다.
# 혹시나 해서 docker-py 만 설치해봤다.
sudo pip install docker-py

# 설치 확인
pip list | grep docker
10:docker-py (1.10.6)

# 에러가 발생 안한다. 뭐지? python 2.7 은 docker 패키지를 설치,
# python 2.6 은 docker-py 를 설치해야만 되는줄 알았는데,흠.

ansible ssh password 옵션

# ansible 수행시 target 서버에 커버로스 인증을 사용할 수 없어
# 직접 ssh password(암호)를 명시해야 되는 경우 --ask-pass 옵션을 사용하면
# 암호 입력 프롬프트가 표시해 암호를 입력 할 수 있다.
ansible-playbook -i testserver test.yaml --ask-pass

# 또는 인벤토리 파일에 ansible_password 변수로도 설정할 수 있다.
testserver ansible_host=ysoftman.test.com ansible_password="password123"

# 그런데 위 두방법 모두 다음과 같은 에러가 발생한다.
to use the 'ssh' connection type with passwords, you must install the sshpass program

# ansible 은 ssh 연결로 암호를 묻는 프롬프트가 뜨는데
# sshpass 는 암호 프롬프트 없이 인증 해준다.

# sshpass 설치
# centos 계열
yum install sshpass

# ubuntu 계열
apt-get install sshpass

# mac
brew install hudochenkov/sshpass/sshpass

# 이제 --ask-pass 사용시 최초 한번만 암호를 입력하면 에러 없이 진행된다.
ansible-playbook -i testserver test.yaml --ask-pass
# 또는 암호 프롬프트 없이 사용할 경우
sshpass -p password123 ansible-playbook -i testserver test.yaml --ask-pass

# 참고로 ssh 연결시에 암호 프롬프트 없이 사용하기
sshpass -p password123 ssh ysoftman@ysoftman.test.com

vscode path wildcard

# vscode 에서 .yml 파일이 위치에 따라 yaml 또는 ansible 로 사용해야 한다.
# **ansible, **roles 이름의 디렉토리내에 있는 .yml, .yaml 경우 ansible 로 지정 할 수 있다.
# 이때 path 에 대한 와일드카드로 **(double asterisk)를 사용한다.
# 파일 연결 및 제외 사용 예시
{
    "files.associations": {
        "**/roles/**/*.yml": "ansible",
        "**/ansible/**/*.yml": "ansible",
        "**/roles/**/*.yaml": "ansible",
        "**/ansible/**/*.yaml": "ansible",
        "*.yml.*": "yaml"
    },
    "files.exclude": {
        "**/.git": true,
        "**/.svn": true
    }
}

ansible 속도 개선

# ansible 은 기본적으로 대상 서버에 task 수행에 필요한 모듈을 복사 후
# task 를 실행하는데 이 모듈 복사 과정으로 task 수행이 느리다.
# 그런데 이 복사 ssh operation 횟수를 pipelining 으로 줄여 속도를 높일 수 있다.
https://docs.ansible.com/ansible/2.4/intro_configuration.html#pipelining

# 단 sudo 명령을 쓰는 경우 대상 호스트의 /etc/sudoers 권한작업이 필요하다.
# 다음과 같이 pipelining 활성화
# vi ansible.cfg
[ssh_connection]
pipelining = true

# 테스트 결과 수행 속도가 1/2 로 줄어 들었다.
pipelining = fasle : 5min
pipelining = true : 2min 30s

# 참고로
# ansible 1.3 ~ 1.5 에서는 accelerate_port 를 사용했었고
# ansible 1.5 이후 pipelining 추로 accelerate_port 는 설정할 필요가 없어졌다.

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

ansible Shared connection closed

# 주기적으로 ansible 을 실행하다 보면 다음과 같이 ssh 커넥션 에러가 발생할때가 있다.
# ansible 2.7.2

TASK [Gathering Facts] *********************************************************
fatal: [서버]: UNREACHABLE! => {
    "changed": false,
    "unreachable": true
}
MSG:
Failed to connect to the host via ssh: Shared connection to 서버 closed.

# 이와 같은 ansible issue 가 등록돼 있다.
https://github.com/ansible/ansible/issues/26359

# 뚜렷한 해결책은 없고 controlpersist timeout 을 늘리라 고만 한다.ㅠ
# ansible.cfg 에 ssh 옵션(man ssh_config 참고)
# TCP 커넥션 하나에 여러개의 ssh 커낵션 허용(multiplexing)을 여부
# yes: controlpath 로 설정된 소켓으로 연결을 기다린다. 이 소켓에 세션이 추가 될 수 있다.
# no: 새 소켓을 만들지 않고 controlpath 에 있다면 세션 추가해서 사용한다.
# auto: controlpath 소켓이 있으면 사용하고, 없으면 새 소켓을 만든다.
# ControlMaster=auto
# ssh 커넥션 유지 시간, 기본 단위는 초다.
# m,s: x분,x초 만큼 커넥션 유지
# no: 클라이언트 커넥션 종료되면 바로 종료
# yes or 0: 커넥션 indefinitely(무한정) 유지
# ControlPersist=30m
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=30m

# ssh 실패시 자세한 로그를 보기 위해 ansible -vvv 옵션을 주고 보면
# 에러 직전까지 여러번의 ssh 연결을 수행을 성공한다.
# 하지만 에러 직전의 ssh 커맨드가 실패하고 명령 옵션을 보면 다음과 같다.
ssh -C \
-o ControlMaster=auto \
-o ControlPersist=60s \
-o StrictHostKeyChecking=no \
-o KbdInteractiveAuthentication=no \
-o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey \
-o PasswordAuthentication=no \
-o User=ysoftman \
-o ConnectTimeout=10 \
-o ControlPath=/ysoftman/.ansible/cp/8a8131ca11
-tt ysoftman_server
'/bin/sh -c '"'"'/usr/bin/python /ysoftman/.ansible/tmp/ansible-tmp-1570416122.87-200449718228174/AnsiballZ_command.py && sleep

# 위를 실행하면 문제가 되는 에러가 발생한다.
Shared connection to ysoftman_server closed.

# 성공,실패의 차이를 보면 성공의 경우 -tt 옵션이 없었고,
# 실패의 경우에서 -tt 옵션을 빼고 실행하면 잘된다.
# 찾아보니 -t 옵션은 ssh 연결시 화면을 보기 위해 tty 를 할당한다.
# -tt 로 t 를 여러번 주면 강제 할당하게 된다.
# 참고로 -T 옵션으로 -tt 를 disable 할 수 있다.

# ansible.cfg 에 다음 옵션을 주면
# ssh -tt(tty 강제 할당) 옵션 비활성화한다.
[ssh_connection]
usetty = no

# 참고
https://linux.die.net/man/1/ssh
https://docs.ansible.com/ansible/latest/plugins/connection/ssh.html

ansible jinja template list 유니코드 에러

# 문제
# ansible jinja template 파일에서 다음과 같이 리스트를 사용하는 경우
# default/main.yml
ysoftman_servers:
  service:
    - ysoftman1:9001
    - ysoftman2:9001

# templates/ysoftman.yml.j2
hosts : {{ ysoftman_servers[env] }}

# ansible 수행하면 다음과 같이 u(유니코드)문자가 붙어 설정 에러가 발생한다.
hosts: [u'ysoftman1:9001', u'ysoftman2:9001']

# 원인
# ansible 버전을 확인해 보면 python2 를 사용하고 있다.
# python2 에서는 유니코드를 표현하기 위해 u'글자' 형식으로 표현되는게 문제다.
ansible --version
ansible 2.8.5
  ... 생략 ...
  python version = 2.7.10 .. 생략

# 해결방법
# python3 기반의 ansible 을 새롭게 설치하자.
pip3 install ansible

# 또는 brew 삭제하고 재설지 설치
# 최근 brew 는 python3 이 기본이 되어 그냥 python 으로 표기된다.
# python2 는 python@2 로 표기해야 한다.
brew uninstall ansible
brew install ansible

# ansible with python3 으로 실행하면 u(유니코드) 표기를 사용하지 않는다.
ansible 2.8.5
  python version = 3.7.4

# 만약 python2 를 쓸수 밖에 없는 상황이라면 다음처럼 u를 제거할 수도 있다.
hosts: ["{{ ysoftman_servers[env].stdout_lines | list | join("\", \"") }}"]

ansible pretty verbose

ansible 2.5 이전까지는 -v(verbose) 옵션을 주어 콘솔 메시지를 찍어 보면 newline, json 등이 한줄로 표시되어 보기가 많이 불편했다.
그런데 ansible 2.5 부터 ansible.cfg 에 다음 옵션을 주면 human readble 하게 출력된다.

[defaults]
stdout_callback=debug

참고
https://github.com/ansible/ansible/issues/27078#issuecomment-364560173
debug 외에도 다양한 플러그인 포맷팅을 사용할 수 있다.
https://docs.ansible.com/ansible/latest/plugins/callback.html

# 적용 전
changed: [localhost] => {"changed": true, "cmd": "make clean", "delta": "0:00:00.200834", "end": "2019-06-04 17:28:45.466554", "rc": 0, "start": "2019-06-04 17:28:45.265720", "stderr": "", "stderr_lines": [], "stdout": "if [ -d target ] ; then rm -rf target ; fi\nif [ -f ysoftman ] ; then rm ysoftman ; fi\ngo clean -testcache", "stdout_lines": ["if [ -d target ] ; then rm -rf target ; fi", "if [ -f ysoftman ] ; then rm ysoftman ; fi", "go clean -testcache"]}

# stdout_callback=debug  적용 후
changed: [localhost] => {
    "changed": true,
    "cmd": "make clean",
    "delta": "0:00:00.198913",
    "end": "2019-06-04 17:17:34.596399",
    "rc": 0,
    "start": "2019-06-04 17:17:34.397486"
}

STDOUT:
if [ -d target ] ; then rm -rf target ; fi
if [ -f ysoftman ] ; then rm ysoftman ; fi
go clean -testcache

(eval):1: command not found 에러

# zsh 환경에서 명령 옵션에 파일을 자동완성하기 위해 tab을 누르면
# 다음과 같은 에러가 발생하는 경우가 있다.
ansible-playbook -i ./in...(eval):1: command not found: _ansible-playbook

# vim 파일 찾기 위해 tab을 누를때도
_arguments:448: _vim_files: function definition file not found

# 고질적인 문제로 해결벙법은 zcomdump 파일을 삭제하고 쉘 다시 시작하면 된다.
rm -fv ~/.zcompdump

ansible local 테스트 환경 에러

# ansible 적용 대상이 local 인경우 다음과 같이 inventory 파일을 작성할 수 있다.
# ansible 은 기본으로 대상호스트에 ssh 로 접속하여 작업을 실행하지만
# ansible_connection=local 로 명시하면 ssh 를 사용하지 않아 빠르다.
# 참고
# https://docs.ansible.com/ansible/2.3/intro_inventory.html#non-ssh-connection-types

[mylocal]
localhost              ansible_connection=local


# 하지만 ansible 을 수행하면 ssh 를 사용하지 않아 ~/.bashrc 등을 환경을 사용할 수 없어 에러가 발생할 수 있다.
# 작업 시작의 맨처음 .bashrc 를 로드하도록 한다.

- name: load bashrc
  shell: source /home/ysoftman/.bashrc && ls -ahl

ssh 처음 접속시 호스트 체크 안하기

# ssh 로 처음 접속하는 호스트(~/.ssh/known_hosts 에 미등록)는 다음과 같이 물어본다.
The authenticity of host 'xxxxxxx' can't be established.
RSA key fingerprint is xxxxxxx.
Are you sure you want to continue connecting (yes/no)?

# yes 를 입력하여 known_hosts 에 기록되면 다음부터 물어보지 않지만
# 처음 접속하는 수백대의 호스트 접속마다 yes 를 치는건 힘들다.
# 이경우 다음과 같이 ssh 접속시 호스트 체크를 안할 수 있다.
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=1 ysoftman@10.10.10.10

# 수행하면 다음과 같은 경고 문구가 처음 접속시 한번 나오지만 
# ~/.ssh/know_hosts 에 등록되어 이후 접속부터는 표시되지 않는다.
"Warning: Permanently added 'xxxxx' (RSA) to the list of known hosts"

# ansible 사용시
# ansible.cfg 에 다음과 같이 명시하면 호스트 체크를 하지 않는다.
[default]
host_key_checking = false

# 또는 다음과 같이 ssh 에 직접 옵션을 줄 수 도 있다.
[ssh_connection]
ssh_args = '-o StrictHostKeyChecking=no'

ansible "Shared connection to xxx closed"

2개의 jenkins(젠키스) job(잡) 이 스케줄링에 의해 동시 실행될때
"Shared connection to 아이피 closed"

에러를 발생하며 ssh 접속이 되지 않는다.
각각의 젠킨스 잡은 ansible-playbook 을 수행하는데 2개 모두 같은 호스트를 대상으로 하고 있다.
ansible version : 2.2.1.0
user : ysoftman (가정)
jenkins host os : centos 7.2
target host name : ysoftman-file-server (10.10.10.10) (가정)
target host os : centos 7.2

다음과 같은 에러 메시지와 함께 접속 실패가 발생한다.
(ansible-playbook 실행시 -vvvv 옵션)
<10.10.10.10> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=30m -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=ysoftman -o ConnectTimeout=10 -o ControlPath=/ysoftman/.ansible/cp/ansible-ssh-%h-%p-%r -tt 10.10.10.10 '/bin/sh -c '"'"'/usr/bin/python /ysoftman/.ansible/tmp/ansible-tmp-1488621902.23-156878567418929/command.py; rm -rf "/ysoftman/.ansible/tmp/ansible-tmp-1488621902.23-156878567418929/" > /dev/null 2>&1 && sleep 0'"'"''
fatal: [ysoftman-file-server]: UNREACHABLE! => {
    "changed": false, 
    "msg": "Failed to connect to the host via ssh: OpenSSH_6.6.1, OpenSSL 1.0.1e-fips 11 Feb 2013\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 56: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 27663\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 4\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Control master terminated unexpectedly\r\nShared connection to 10.10.10.10 closed.\r\n", 
    "unreachable": true
}

관련해서 구글링을 열심해 해봤는데

에서는 ControlPath 에 명시된 소켓 경로가 너무 길어서 발생한것으로 2.3 에서는 수정되었다고 한다. 그런데 위 트레이스 로그를 보면 경로명이 긴것도 아니고 동시실행될때만 접속실패가 발생하고 각각의 ansible 잡이 독립적으로 수행될때는 접속이 문제가 없어 이 원인은 아닌것으로 생각된다.


에 나와 같은 문제를 제기하고 있었고 다음과 같이 ansible-playbook 실행시 paramiko(파이썬으로 구현한 openssh) connection 를 사용하라고 한다.
ansible-playbook -i ./ysoftman ysoftman.yml -c paramiko -vvvv

또는


와 같이 playbook(.yml) 에 명시할 수 도 있다.
connection: paramiko

또는

http://docs.ansible.com/ansible/intro_configuration.html#transport
와 같이 ansible.cfg 에 명시한다.
(디폴트 smart 로 설정하면 play에  명시된 connection 을 따른다.)
transport = paramiko

문제가 되었던 서버에서는 paramiko 사용으로 해결되었다.
그런데 다른 서버의 콘솔에서 ansible-playbook 명령을 실행하면 접속 인증 실패가 발생한다. -c ssh 나 -c smart 를 옵션을 주어 실행하면 잘된다.

A 장비(젠킨스)에서 ansible 실행시 -c ssh | smart | paramiko 모두 동작
B 장비(로컬)에서 ansible 실행시 -c paramiko 인증 실패

원인은 B 장비의에서 -c paramiko 를 사용할 경우 .ssh/config 설정파일을 사용하지 않고 .ssh/id_rsa (기본 개인키)파일만 바라보고 있어 인증을 할 수 없는 것이다.
id_rsa 파일이 존재하지 않는다면 : No authentication methods available
id_rsa 파일이 존재하지만 대상 호스트에 공개키가 등록되지 않았다면 : Authentication failed
에러가 발생한다.
.ssh/config 에 명시된 private_key 파일을 id_rsa 로 복사하였더니, -c paramiko 로도 실행이 잘 된다.

[참고1]

에 보면 레드햇계열의 버전 6 이하에서는 openssh 버전이 오래된 ControlPersist 같은 ssh 성능 향상 옵션을 사용할 수 없어 ansible 이 ssh 접속에 paramiko 를 강제로 사용했다고 한다.

[참고2]
controlpersist 로 유지되는 커넥션 종료하기
ssh -o ControlPath=/Users/ysoftman/.ansible/cp/ansible-ssh-10.10.10.10-ysoftman -O exit ysoftman@10.10.10.10
또는 소켓 파일을 모두 삭제
rm -rf ~/.ansible/cp/

ansible gather_facts True 설정시 느려지는 현상

ansible(playbook)에 gather_facts : True 옵션을 주고 실행하면 대상 호스트에 ansible 실행에 필요한 모듈들을 설치한다.
-vvv 로 보면 다음과같이 setup.py 를 실행하게 된다.

 /usr/bin/python /home/ysoftman/.ansible/tmp/ansible-tmp-1487557897.51-276619114541988/setup.py; rm -rf "/home/ysoftman/.ansible/tmp/ansible-tmp-1487557897.51-276619114541988/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''

그런데 ansible 실행시 sudo(root)로 하면 setup.py 실행에서 몇분간 지체되는 현상이 발생했다. 특정 서버에서만 발생하는것으로 대상 서버의 root 환경과 문제가 있는것으로 보인다.

우선 대상 호스트에서 setup.py 이 실행되면
setup.py --> /tmp/ansible_xxx/ -> ansible_modlib.zip 과 ansible_module_setup.py 이 생성하게 된다.

ansible_module_setup.py 에 문제가 있고 다음과 같이 이것만 테스트 해볼 수 있다.

1. ansible_modlib.zip 과 ansible_module_setup.py 를 홈디렉토리에 복사한다.
2. ansible_modlib.zip 은 압축을 해제하면 ansible_module_setup.py 를 실행 할 수 있다.
3. 다음과 같이 json 인자 값을 주어 실행한다.
sudo python ./ansible_module_setup.py '{"ANSIBLE_MODULE_ARGS": {"_ansible_version": "2.2.1.0", "_ansible_selinux_special_fs": ["fuse", "nfs", "vboxsf", "ramfs"], "_ansible_no_log": false, "gather_timeout": 10, "_ansible_module_name": "setup", "_ansible_verbosity": 3, "_ansible_syslog_facility": "LOG_USER", "gather_subset": "all", "_ansible_diff": false, "_ansible_debug": false, "_ansible_check_mode": false}}'
각 단계를 따라가면서 print 하여 오래걸리는 구간을 찾아봤는데

ansible_modlib.zip 을 압축해제 후 생성된 ansible/module_utils/facts.py -> ansible_facts() 함수에서 subset 이 hardware 인 경우가 업데이트가 느려지게 된다. 아래와 코드의 주석을 해제하여 hardward 일 경우 스킵하면 기다림 없이 진행되더라.

def ansible_facts(module, gather_subset):
    facts = {}
    facts['gather_subset'] = list(gather_subset)
    facts.update(Facts(module).populate())
    for subset in gather_subset:
        print subset

        # if subset == 'hardware':
            # continue

        facts.update(FACT_SUBSETS[subset](module,
                                         load_on_init=False,
                                         cached_facts=facts).populate())
    return facts

ansible 문서 http://docs.ansible.com/ansible/intro_configuration.html 를 찾아보니 다음과 같이 gather_subset = all 기본 설정되어 hardware 의 경우 검색시 오랜 시간이 걸리 수 있다고 나와 있다.

all: gather all subsets (the default)
network: gather network facts
hardware: gather hardware facts (longest facts to retrieve)
virtual: gather facts about virtual machines hosted on the machine
ohai: gather facts from ohai
facter: gather facts from facter

플레이북에서 hardware 를 빼고 다음과 같이 명시하니 기다림 없이 진행되는것을 확인했다.
gather_subset: network,virtual 
또는 
gather_facts: False
로 setup.py 자체를 실행하지 않으면 된다.

최초 한번은 시간이 오래 걸리더라도 gather_facts: True (gather_subset: all 디폴트)로 모두 설치해주자.

ansible role task 실행 순서

# ansible playbook(.yml) 작성시 tasks 와 roles 을 같이 사용하는 경우가 있다.
# 우선 tasks 는 pre_tasks 와 post_tasks 로 작업 전/후에 수행 사항에 대해 구분할 수 있다.
# 다음과 같이 tasks, roles,  pre_tasks, post_taks 등이 혼재되어 있는 경우가 있다.
- host: all
  pre_tasks:
  - name: ysoftman pre_task1
    command: "echo pre_task1"
  tasks:
  - name: ysoftman task1
    command: "echo task1"
  post_tasks:
  - name: ysoftman post_task1
    command: "echo post_task1"
  roles:
  - role: ysoftman_role1

# roles/ysoftman_role1/tasks/main.html
- name ysoftman role1
  command: "echo ysoftman role1"

# 실행 순서는 다음과 같다. (pre_task -> role -> task -> post_task)
TASK [ysoftman pre_task1]
TASK [ysoftman role1]
TASK [ysoftman task1]
TASK [ysoftman post_task1]

ansible 사용하기

# ansible 은 시스템 설정이나 배포등과 같은 관리 작업을 편리하게 해주는 자동화 툴정도로 생각할 수 있다.
# ansible 설치(맥 brew 기준)
brew install ansible

# ansible 은 내부적으로 ssh 로 사용하고 있어 ssh-keygen 을 생성해서 등록해 둔다.
# 관련 내용은 https://ysoftman.blogspot.kr/2009/04/linux-ssh.html 포스트 참고

# 접속할 대상 서버 호스트를 명시한 inventory 라는 파일을 작성한다.
# 디폴트로 /usr/local/etc/ansible/hosts 파일을 참고 하지만 -i 로 inventory 를 지정할 수 있다.
vi host
[testhost1]
192.168.0.1
192.168.0.2

[testhost2]
192.168.0.3

# 설치하면 ansible 과 ansible-playbook 을 사용할 수 있다.
# ansible 은 ad hoc(설정을 저장하지 않고 즉석에서 명령어 옵션으로 처리) 방식
# 대상 호스트에 command 모듈로 쉘 명령 실행하기
ansible -i host testhost1 -u ysoftman -m command -a "ls -ahl"

# 소스 https://github.com/ysoftman/test_code/tree/master/ansible
# ansible-playbook 은 .yml 설정을 이용한는 방식
# .yml(playbook)에 어떤 서버에서 어떤 계정으로 어떤 작업을 할것인지 등을 명시한다.
# 대상 서버에 hello world 출력하는 플레이북 작성(들여쓰기시 2칸 공백을 사용해야 한다.)
# 자세한 내용은 http://docs.ansible.com/ansible/playbooks_best_practices.html
vi myplaybook.yml
- name: hello world by ansible
  # inventory 에 명시된 호스트그룹, inventory 의 모든 호스그룹이라면 all 로 명시
  hosts: testhost1
  # 작업을 수행하는 계정
  user: ysoftman
  # 대상 서버에서 수행할 작업들 명시
  tasks:
  - name: print hello world
    shell: echo "hello world" > helloworld.txt
    tags: [ helloworld, myname ]
  - name: current path
    shell: pwd > pwd.txt
    tags: mylocation
  - name: who am i
    shell: whoami > whoami.txt
    tags: myname
  - name: clear all files
    shell: rm -fv helloworld.txt pwd.txt whoami.txt
    tags: clearall

# 작업 수행하기
# 옵션 참고
# -v ~ -vvv 는 단계에 따라 많은 verbose
# -C 를 사용하면 변경부분은 적용하지 않고 체크만 수행한다.
# 서버 2곳에 "hellow world" 내용의 helloworld.txt 파일이 생성
ansible-playbook -i host myplaybook.yml -t helloworld -vv

# 서버 2곳에 whoami 출력결과 내용의 whoami.txt 파일이 생성
ansible-playbook -i host myplaybook.yml -t myname -vv


# 참고
http://docs.ansible.com/ansible/intro_adhoc.html
http://docs.ansible.com/ansible/YAMLSyntax.html
https://github.com/ansible/ansible-examples
https://ko.wikipedia.org/wiki/YAML

ASCII Art 생성하기

# 텍스트 문구를 ASCII Art 로 생성하는 툴 들 설치
# ubuntu
sudo apt-get install figlet toilet fortune cowsay
# mac
brew install figlet toilet fortune cowsay

# fitlet 사용(toilet 사용방법도 비슷하다.)
# 예시) figlet -w 출력 폭 100으로 -f starwars 폰트로 'ysoftman' 출력
figlet -w 100 -f starwars ysoftman

# figlet 모든 폰트 적용해보기
showfigfonts ysoftman

# toilet 모든 폰트 출력해보기
for font in $(ls -1 /usr/local/Cellar/toilet/0.3/share/figlet | sed 's/.tlf//'); do echo $font; toilet -f $font "ysoftman"; done

# ASCII Arts 에는 "'\ 등의 문자가 포함되어 있어 다음과 같이 echo 로 출력하기 힘들다.
# echo '
#                   __ _
#  _   _ ___  ___  / _| |_ _ __ ___   __ _ _ __
# | | | / __|/ _ \| |_| __| '_ ` _ \ / _` | '_ \
# | |_| \__ \ (_) |  _| |_| | | | | | (_| | | | |
#  \__, |___/\___/|_|  \__|_| |_| |_|\__,_|_| |_|
#  |___/
# '
# 다음과 같은 방법을 사용하자.
# zzz 가 입력되기까지의 모든 문자를 연결해서 stdout 으로 보낸다.
cat << "zzz"
                  __ _
 _   _ ___  ___  / _| |_ _ __ ___   __ _ _ __
| | | / __|/ _ \| |_| __| '_ ` _ \ / _` | '_ \
| |_| \__ \ (_) |  _| |_| | | | | | (_| | | | |
 \__, |___/\___/|_|  \__|_| |_| |_|\__,_|_| |_|
 |___/
zzz

# fortune 은 실행시 마다 랜덤하게 명언들을 출력해준다.
# cowsay 귀여운 소(당나귀 같은데..ㅋ)가 말을 해준다.
fortune | cowsay
 ____________________________________
/ Happiness is good health and a bad \
| memory.                            |
|                                    |
\ -- Ingrid Bergman                  /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

# -f 옵션으로 다양한 그림을 선택할 수 있다.(-l 그림 리스트로 확인)
cowsay -f cheese "ysoftman"
 __________
< ysoftman >
 ----------
   \
    \
      _____   _________
     /     \_/         |
    |                 ||
    |                 ||
   |    ###\  /###   | |
   |     0  \/  0    | |
  /|                 | |
 / |        <        |\ \
| /|                 | | |
| |     \_______/   |  | |
| |                 | / /
/||                 /|||
   ----------------|
        | |    | |
        ***    ***
       /___\  /___\

# cowsay 모든 종류 그림 샘플 보기
for i in $(cowsay -l); do cowsay -f $i "$i"; done

# fortune 을 설치하면 oh-my-zsh, prezto 에 login 할때
# fortune 기능이 활성화 되어 fortune 메시지가 나온다.
# .zlogin 파일에서 fortune 을 명령을 확인할 수 있다.
vi .zlogin

# cowsay 를 설치하면 ansible 실행시 매번 cowsay 메시지가 출력된다.
# 이를 막으려면 ANSIBLE_NOCOWS 환경변수를 설정해야 한다.
export ANSIBLE_NOCOWS=1