본문 바로가기
서버구축 (WEB,DB)

AWX Execution Node 독립 호스트로 분리 및 확장 분산처리

by 날으는물고기 2023. 11. 17.

AWX Execution Node 독립 호스트로 분리 및 확장 분산처리

AWX에 Kubernetes 클러스터 외부의 독립 호스트에서 작업을 실제 처리할 수 있는 노드를 추가하는 방법입니다.

1. 개요

  • AWX 21.7.0 버전부터 Execution Node(실행 노드)가 지원되었습니다. 이를 통해 AWX는 독립된 호스트에서 작업의 실행을 처리할 수 있게 됩니다. 아래는 개요와 구성 방법, 사용법을 설명한 내용입니다.

2. Execution Node란?

  • Execution Node는 AWX의 작업을 실행하는 역할을 하는 독립된 호스트입니다.

3. 메리트

  • 확장성: Execution Node를 추가하여 작업 처리 능력을 확장할 수 있습니다.
  • 보안: Execution Node를 격리된 네트워크 환경에서 실행하여 보안을 강화할 수 있습니다.

4. Execution Node의 구성

  • Execution Node를 구성하려면 다음 단계를 따릅니다.

5. 전제 조건

  • Kubernetes 클러스터 외부에 독립적인 호스트가 필요합니다.

6. Execution Node용 호스트의 준비

  • 독립 호스트를 구성하고 필요한 패키지와 설정을 설치합니다.

7. AWX에서의 새로운 인스턴스 정의

  • AWX 대시보드에서 Execution Node를 추가할 새로운 인스턴스를 정의합니다.

8. Execution Node용 설치 번들 획득

  • Execution Node를 위한 설치 번들을 다운로드합니다.

9. Execution Node용 설치 번들 실행

  • 호스트에 설치 번들을 전송하고 실행하여 Execution Node를 설정합니다.

10. AWX에서의 새로운 Instance Group 정의

  • AWX에서 Execution Node를 사용할 새로운 Instance Group을 정의합니다.

11. 동작 확인

  • Execution Node가 올바르게 작동하는지 확인합니다.
    • 기본 작동 확인
    • 격리된 네트워크의 대상에 작업 실행
    • 개인 컨테이너 레지스트리 사용

12. 보충

  • Execution Node용 인증서 내용
  • 만료가 임박한 인증서 갱신

13. Execution Node 삭제

  • 더 이상 필요하지 않은 Execution Node를 삭제합니다.

14. receptorctl을 사용한 확인 및 조사

  • Troubleshooting을 위해 receptorctl을 사용하여 서비스 및 로그를 확인합니다.

15. 문제 해결

  • Receptor 서비스의 실행 상태
  • AWX 측과 Execution Node 측의 문제 해결
  • Receptor를 통해 시작된 프로세스의 표준 입력 및 출력

이렇게 AWX에 Kubernetes 클러스터 외부에서 스탠드얼론 실행 노드를 추가할 수 있습니다.

 

Execution Node란?

개요

Kubernetes 클러스터에서 AWX를 사용하여 작업을 실행할 때, 기본 구성에서는 Execution Environment(실행 환경, EE)의 Pod가 작업별로 생성되어 처리됩니다. 이 경우, 작업의 대상 노드에는 EE의 Pod에서 연결해야 합니다.

이번에 지원된 Execution Node는 Kubernetes 클러스터 외부에 있는 독립적인 RHEL 호스트에 작업 실행을 위임할 수 있는 기능입니다.

Execution Node를 설정하는 과정에서 Receptor와 Podman이 도입되며, Receptor를 통해 Kubernetes 상의 AWX 인스턴스와 메시 네트워크가 구성됩니다. AWX에서 작업을 실행하면 Receptor를 통해 Ansible Runner의 Worker 프로세스가 시작되고, Podman 위에 EE 컨테이너가 생성되어 작업이 처리됩니다.

 

장점

Execution Node를 사용하면 대상 노드에 대한 직접적인 연결이 필요하지 않으므로 Kubernetes 클러스터에서 대상 노드로의 직접적인 연결성을 보장할 필요가 없습니다. 즉, "점프 서버"와 유사한 이미지를 사용할 수 있게 되며 ansible_ssh_common_args를 더 이상 수동으로 설정할 필요가 없습니다. 그러나 EE 이미지를 가져오는 것은 Execution Node의 Podman이 직접 수행하므로 컨테이너 레지스트리에 액세스할 수 있어야 합니다.

또한 대규모 환경이나 동시에 실행되는 작업 수가 많은 환경에서 작업 실행용 리소스를 (Kubernetes 클러스터에 노드를 추가하지 않고) 쉽게 확장할 수 있게 됩니다. 리소스를 작업 실행에 사용할 수 있는 것도 장점입니다.

 

AWX의 이전 구현에서도 기술적으로는 Kubernetes의 워커 노드를 별도로 준비하고 EE를 컨테이너 그룹에서 해당 노드로 제한함으로써 유사한 결과를 (조금 강제로) 얻을 수 있었습니다. 그러나 Kubernetes 클러스터가 관리형 서비스인 경우나 노드 간 네트워크의 논리적 및 물리적 제약이 강한 경우에는 그런 구성이 어려웠습니다.

 

이러한 측면에서 이번 Execution Node 지원을 통해 복잡한 네트워크 환경에서도 간단하고 유연한 구성이 쉽게 가능해지는 것으로 보입니다. 예를 들어 클라우드 환경에서 지역을 가로질러 작업을 실행하는 것도 더 쉬워질 것입니다.

 

또한, AWX 인스턴스와 Execution Node 간의 통신은 TLS 인증 및 암호화를 통해 이루어집니다. 기본적으로 자체 서명 CA 인증서가 자동으로 생성되지만, Kubernetes에서 Secret 리소스로 만들 수 있는 임의의 CA 인증서로 교체할 수 있습니다.

 

구성 상의 주의점 (21.7.0 버전 전용)

최초 릴리스인 21.7.0에서만 Execution Node를 위해 생성되는 인증서의 유효 기간이 10일로 고정된 문제가 있습니다. AWX 및 Receptor에는 현재 자동 인증서 갱신 메커니즘이 없으므로 인증서의 만료 시 Receptor 통신이 불가능해집니다. 따라서 21.7.0에서이 기능을 적극적으로 사용하려면 인증서를 수동으로 자주 갱신해야 합니다. 인증서 갱신 절차는 후속 설명서에서 설명되어 있습니다.

 

이 문제는 이미 Issue에 보고되어 PR도 병합되었으므로 다음 릴리스에서 수정될 것입니다. 급한 경우가 아니라면 다음 릴리스를 기다리는 것이 좋습니다. (이 문제는 21.8.0에서 수정됨)

 

Execution Node 구성

준비 사항 및 작업 흐름
Execution Node를 구성하려면 다음 항목이 필요합니다.

  1. AWX (버전 21.7.0 이상)
  2. Execution Node로 구성할 RHEL 계열 호스트 (RHEL, CentOS, Fedora 등)
  3. Execution Node 호스트에서 Ansible을 실행할 수 있는 환경

세 번째 항목은 Execution Node용 호스트를 설치하는 플레이북 실행을 위해 사용됩니다. Execution Node용 호스트에 대해 Ansible로 연결할 수 있다면 어떤 환경이든 사용 가능하며, 또는 Execution Node 자체에서 스스로에 대해 실행할 수도 있습니다.

 

이제, 여기에서는 싱글 노드 K3s (awx.example.com) 위에 CentOS Stream 8 호스트 (exec01.ansible.internal)를 Execution Node로 추가하는 것을 고려합니다. "점프 서버"로 사용할 수 있는지 확인하고, Execution Node를 멀티홈으로 구성하고, 네트워크에서 직접 접근할 수 없는 대상 (kuro-dev01.ansible.private)을 작업에서 조작할 수 있는지도 확인합니다.

또한 작업에 사용할 Execution Environment (EE)은 기본 공개 이미지 (quay.io의 awx-ee) 외에도 AWX와 동일한 K3s 클러스터에서 구성한 개인 컨테이너 레지스트리 (registry.example.com)의 이미지도 사용합니다. 이 컨테이너 레지스트리는 자체 서명 인증서 HTTPS를 사용하며 인증이 필요합니다.

 

작업은 다음 순서로 진행됩니다. AWX 측의 조작은 API를 직접 사용하여 수행할 수 있지만, 이번에는 웹 GUI를 사용하여 조작합니다.

  1. Execution Node용 호스트 준비
  2. AWX에서 새 인스턴스 정의
  3. Execution Node용 설치 번들 얻기
  4. Execution Node에 대한 설치 번들 실행
  5. AWX에서 새 인스턴스 그룹 정의

 

Execution Node용 호스트 준비
Execution Node로 구성할 호스트를 준비합니다. 요구 사항은 다음과 같습니다.

  1. RHEL 계열 운영 체제 (RHEL, CentOS, Fedora 등)
  2. 고정된 IP 주소 또는 AWX에서 DNS를 통해 호스트 이름을 해결할 수 있는 호스트 이름
  3. AWX에서 Receptor 포트 (기본값은 27199/tcp)에 액세스할 수 있어야 함

호스트의 구체적인 자원 요구 사항은 AWX 문서에서 명시되어 있지 않지만, AAP 문서에서는 Execution Node가 4개의 CPU 코어와 16GB RAM이 필요하다고 언급하고 있습니다. 엄격하게 말하자면 별도의 제품이지만, Execution Node의 구현은 거의 동일하다고 생각되므로 참고할 수 있습니다. 이 호스트의 CPU 코어 수 및 메모리 용량은 사용하는 환경의 규모에 따라 조정해야 할 수 있으며, 이에 대한 정보는 Ansible Automation Controller (AAC) 문서도 함께 참고하여 조정하는 것이 좋습니다.

 

여기에서는 CentOS Stream 8을 준비했습니다. AAP 문서를 준수하여 4 vCPU, 16GB RAM을 사용하며 최소 설치를 수행했습니다. 미리 Receptor에서 사용할 포트를 방화벽에서 허용하십시오. 포트 번호는 기본적으로 27199/tcp입니다.

sudo firewall-cmd --add-port=27199/tcp --permanent
sudo firewall-cmd --reload

또한 나중에 설명할 단계에서 이 호스트에 대한 Ansible 연결에 사용할 사용자 및 해당 사용자의 SSH 인증을 구성하십시오.

 

AWX에서 새로운 인스턴스 정의
Execution Node는 AWX에서 인스턴스의 한 유형입니다. 먼저 AWX의 "Administration > Instances > Add"에서 추가할 Execution Node의 정보를 정의합니다.

계속 진행하기 전에 AWX에서 사전에 입력한 "Host Name"으로 통신이 수행되며, 인증서도 이 호스트 이름에 대한 인증서로 발급됩니다. 따라서 여기에서는 호스트 이름이 AWX에서 이름을 해결하고 액세스할 수 있는 이름이어야 합니다. 그 밖의 수정은 선택 사항입니다. 이 예에서는 모두 기본값을 사용하고 있습니다.

입력한 후 "Save"를 클릭하면 인스턴스가 추가됩니다.

이 단계에서 AWX는 새 인스턴스의 설치가 완료될 때까지 대기 상태로 들어가며 awx-ee 컨테이너에서 Receptor를 통해 노드의 상태를 주기적으로 확인하고, 현재 상태에서는 당연히 연결할 수 없습니다.

$ kubectl -n awx logs -f deployment/awx -c awx-ee
...
WARNING 2022/10/05 22:08:40 Backend connection failed (will retry): dial tcp 192.168.0.218:27199: connect: connection refused
WARNING 2022/10/05 22:09:00 Backend connection failed (will retry): dial tcp 192.168.0.218:27199: connect: connection refused
WARNING 2022/10/05 22:09:20 Backend connection failed (will retry): dial tcp 192.168.0.218:27199: connect: connection refused
...

 

Execution Node용 인스톨 번들 획득
위에서 언급한 화면에서 "Install Bundle" 다운로드 버튼을 클릭하여 해당 인스턴스용 인스톨 번들 (<호스트 이름>_install_bundle.tar.gz)을 다운로드합니다.

 

이 번들에는 Receptor 통신에 필요한 CA 인증서 및 해당 노드용 인증서와 해당 노드를 Execution Node로 구성하기 위해 필요한 플레이북 등이 포함되어 있습니다.

 

Execution Node용 인스톨 번들 실행
실제로 실행해야 하는 것은 Ansible 플레이북이 포함된 Execution Node용 호스트를 대상으로 실행됩니다. 수동으로 실행해야 합니다.

 

인스톨 번들을 배치하고 풀어서 사용하기 전에 requirements.yml에 포함된 컬렉션을 설치합니다.

ansible-galaxy collection install -r requirements.yml

그런 다음, 포함된 인벤토리 파일 (inventory.yml)을 편집합니다. 대상 노드의 호스트 이름은 기본적으로 AWX에서 입력한 호스트 이름이 이미 포함되어 있지만, 다른 부분은 수정해야 합니다. 플레이북은 권한 승격이 있는 상태에서 실행되므로 필요에 따라 수정합니다.

---
all:
  hosts:
    remote-execution:
      ansible_host: exec01.ansible.internal
      ansible_user: <username> # 사용자가 제공한 사용자
      ansible_ssh_private_key_file: ~/.ssh/id_rsa

여기에는 키 인증 관련 항목도 포함되어 있지만 단순히 연결할 수 있으면 됩니다. 비밀번호 인증을 사용하려면 해당 내용을 수정하거나 대상 노드에서 직접 실행하는 경우 ansible_connection을 local로 변경할 수 있습니다. 승격 비밀번호는 인벤토리 파일에 미리 작성해도 좋고, 플레이북 실행 시 --ask-become-pass를 사용해도 됩니다.

 

모두 준비되면 플레이북을 실행하고 완료될 때까지 기다립니다.

ansible-playbook -i inventory.yml install_receptor.yml

플레이북이 완료되고 AWX와 정상적으로 연결되면 몇 분 내에 AWX 측의 인스턴스 상태가 "Ready"로 변경됩니다. 이로써 Execution Node가 인스턴스로 추가되었습니다.

awx-ee 컨테이너 로그에서도 Receptor를 통한 과정을 볼 수 있습니다.

$ kubectl -n awx logs -f deployment/awx -c awx-ee
...
DEBUG 2022/10/06 20:12:16 Sending initial connection message
INFO 2022/10/06 20:12:16 Connection established with exec01.ansible.internal
...

"Ready"로 변경되지 않고 "Installed" 상태로 유지되거나 "Unavailable"로 변경된 경우 문제가 발생한 것으로 간주됩니다. 문제를 식별하고 수정하기 위해 아래에서 설명하는 문제 해결 절차를 참고하세요.

 

AWX에서 새로운 Instance Group 정의
AWX에서 작업을 실행하는 것은 인스턴스 수준이 아니라 인스턴스 그룹 (또는 컨테이너 그룹)에 할당됩니다. 따라서 특정 인스턴스에서 작업을 실행하려면 해당 인스턴스를 포함한 인스턴스 그룹이 필요합니다.

 

먼저 "Administration > Instance Groups > Add > Add instance group"에서 임의의 이름의 인스턴스 그룹을 정의합니다.

그런 다음 추가한 인스턴스 그룹의 "Instances" 탭에서 "Associate" 버튼을 클릭하고, 새로 추가한 Execution Node의 인스턴스를 선택한 다음 "Save" 버튼을 클릭합니다.

이로써 Execution Node가 인스턴스로 추가되어 작업을 실행하는 데 사용할 수 있는 인스턴스 그룹이 만들어졌습니다.

 

동작 확인

AWX에서 Execution Node의 동작을 확인하고 Execution Node를 사용하여 작업을 실행하는 방법을 설명합니다. 이 내용을 이해하기 위해서는 AWX 및 Execution Node의 기본 개념을 이해해야 합니다.

 

기본 동작 확인

먼저, 기본 동작을 확인하는 테스트를 수행합니다. AWX에서는 Execution Node를 사용하여 작업을 실행할 수 있으며, 작업은 Instance Group을 통해 지정됩니다. 이 Instance Group은 Organization, Inventory 또는 Job Template 수준에서 지정할 수 있습니다.

여기서는 Job Template 수준에서 Instance Group을 지정한 예를 다루고 있습니다.

  1. 먼저, AWX에서 "ansible.builtin.pause" 명령을 포함하는 간단한 Playbook을 사용하여 Job Template을 작성합니다.
---
- hosts: localhost
  tasks:
    - ansible.builtin.pause:
  1. Job Template을 실행할 때, 사용할 Execution Environment (EE)은 quay.io/ansible/awx-ee:latest를 사용합니다. 이 EE는 기본 이미지이며, Job Template을 실행하는데 사용됩니다.
  2. Job Template을 실행하면 Execution Node에서는 Podman을 사용하여 EE 컨테이너를 실행합니다. 이 컨테이너는 사용자 "awx"의 네임스페이스 내에서 실행되며, 실제 실행 작업은 컨테이너 내부에서 이루어집니다.

Execution Node에서 아래와 같이 확인할 수 있습니다.

# 사용자 "awx"로 전환
$ sudo su - awx

# Execution Node에서 실행 중인 컨테이너 확인
$ podman ps
CONTAINER ID  IMAGE                          COMMAND               CREATED         STATUS             PORTS       NAMES
3e02ac3e9685  quay.io/ansible/awx-ee:latest  ansible-playbook ...  39 seconds ago  Up 40 seconds ago              ansible_runner_8
  1. EE의 내부에서는 Ansible Playbook이 직접 실행되며, 관련 파일은 /tmp 디렉토리에 저장됩니다.

Execution Node에서 아래와 같이 관련 프로세스 및 파일 확인 가능합니다.

# 관련 프로세스 확인
$ ps -ef | grep -E '(receptor|ansible-runner)' | grep -v grep
awx 16373     1 0 05:11 ? 00:00:08 /usr/bin/receptor -c /etc/receptor/receptor.conf
awx 53942 16373 0 06:37 ? 00:00:00 /usr/bin/receptor --node id=worker --log-level info --command-runner command=ansible-runner params=worker --private-data-dir=/tmp/awx_8_2g2vrzz7 --delete unitdir=/tmp/receptor/exec01.ansible.internal/8CbcJ0G6
awx 53951 53942 1 06:37 ? 00:00:00 /usr/bin/python3.9 /usr/local/bin/ansible-runner worker --private-data-dir=/tmp/awx_8_2g2vrzz7 --delete

# 관련 파일 확인
$ ls -l /tmp/awx_<job_id>
total 0
drwxr-xr-x.  3 awx awx  15 Oct  7 06:37 artifacts
drwx------.  2 awx awx   6 Oct  6 21:37 cp
drwxr-xr-x.  2 awx awx  54 Oct  7 06:37 env
drwxr-xr-x.  2 awx awx  19 Oct  7 06:37 inventory
drwxr-xr-x. 14 awx awx 228 Oct  7 06:37 project
  1. 작업이 성공하면 AWX에서 결과를 확인할 수 있습니다.
$ podman events --filter event=pull --since 1h
...
2022-10-07 06:37:10.577200393 +0900 JST image pull  quay.io/ansible/awx-ee:latest
...
 
$ podman images
REPOSITORY              TAG         IMAGE ID      CREATED      SIZE
quay.io/ansible/awx-ee  latest      8982fdafc038  9 hours ago  1.96 GB

 

네트워크 격리된 대상 노드에서의 작업 실행

네트워크에서 직접 액세스할 수 없는 대상 노드(예: 격리된 네트워크 내에 있는 노드)에서 작업의 실행을 테스트합니다. 그림과 같이 Target은 Execution Node에서만 액세스 할 수 있습니다.

테스트를 위해 AWX에서 해당 대상 노드에 액세스할 Inventory 및 Credentials를 설정합니다. Job Template을 작성하여 대상 노드에서 실행할 Playbook을 정의합니다.

---
- hosts: all
  gather_facts: true
  tasks:
    - ansible.builtin.ping:
    - ansible.builtin.debug:
        var: ansible_facts

먼저, Instance Group을 지정하지 않고 작업을 실행하여 대상 노드에 도달할 수 없어 실패하는지 확인합니다.

 

다시 Job Template을 실행할 때, Execution Node의 Instance Group을 선택하도록 지정합니다. 작업을 다시 실행 하면 작업이 성공합니다. 이로써 해당 대상 노드는 Execution Node를 통해 접근됩니다.

Job Template을 실행하면 AWX에서 Execution Node에게 작업을 전달하고, Execution Node에서 작업이 실행됩니다.

과거에는 Kubernetes 클러스터의 Pod에서 직접 연결할 수 없는 네트워크에 있는 대상에는 SSH를 발판을 통해 도달시키기 위해서 ansible_ssh_common_args를 사용해 노력하는 등 복잡한 수단이었습니다만, Execution Node를 사용하면 매우 간단하게 구성할 수 있을 것 같습니다.

 

프라이빗 컨테이너 레지스트리 사용

프라이빗 컨테이너 레지스트리를 사용하여 Execution Node에서 이미지를 풀고 실행하는 방법을 확인합니다.

 

프라이빗 레지스트리에서 사용할 이미지를 준비하고 이미지가 SSL로 보호되고 인증이 필요한 경우, 이 정보를 AWX에 등록합니다.

Job Template을 작성하고 Execution Environment을 설정할 때, 프라이빗 레지스트리에 대한 Credentials를 지정합니다.

$ podman pull registry.example.com/ansible/ee:2.12-custom
Trying to pull registry.example.com/ansible/ee:2.12-custom...
Error: initializing source docker://registry.example.com/ansible/ee:2.12-custom: pinging container registry registry.example.com: Get "https://registry.example.com/v2/": x509: certificate signed by unknown authority

Job Template을 실행하면 Execution Node에서 이미지를 프라이빗 레지스트리에서 풀고 실행합니다.

AWX에서는 해당 Execution Node에서 작업의 인증 및 구성을 관리하고 Execution Node로 전달합니다.

...
2022-10-07 08:59:17.874383265 +0900 JST image pull  registry.example.com/ansible/ee:2.12-custom
...
 
$ podman images
REPOSITORY                       TAG          IMAGE ID      CREATED       SIZE
quay.io/ansible/awx-ee           latest       8982fdafc038  12 hours ago  1.96 GB
registry.example.com/ansible/ee  2.12-custom  196d9b9302d1  21 hours ago  1.18 GB

Execution Node에서 관련 프로세스와 인증 정보 파일의 위치를 확인하고, 프라이빗 레지스트리에서 이미지를 가져오는 과정을 확인합니다.

$ ps -ef | grep "podman run"
awx 58712 58704 0 08:59 pts/0 00:00:00
  /usr/bin/podman run
  --rm
  --tty
  --interactive
  --workdir /runner/project
  -v /tmp/awx_17_3za59d3w/:/runner/:Z
  --authfile=/tmp/ansible_runner_registry_17_dya5ku7p/auth.json
  -v /etc/pki/ca-trust/:/etc/pki/ca-trust/:O
  -v /usr/share/pki/:/usr/share/pki/:O
  --env-file /tmp/awx_17_3za59d3w/artifacts/17/env.list
  --quiet
  --name ansible_runner_17
  --user=root
  --network slirp4netns:enable_ipv6=true
  --pull=missing
  registry.example.com/ansible/ee:2.12-custom
  ansible-playbook -u root -i /runner/inventory/hosts -e @/runner/env/extravars runner/project/demo.yml

이러한 방식으로 AWX를 통해 Execution Node를 사용하여 작업을 실행하고, 네트워크 격리된 대상 노드에서 작업을 실행하며, 프라이빗 컨테이너 레지스트리를 사용하는 방법을 확인할 수 있습니다.

$ cat /tmp/ansible_runner_registry_17_dya5ku7p/auth.json
{
    "auths": {
        "registry.example.com": {
            "auth": "cmVndXNlcjpSZWdpc3RyeTEyMyE="
        }
    }
}
 
$ echo "cmVndXNlcjpSZWdpc3RyeTEyMyE=" | base64 -d
reguser:Registry123!

이를 통해 Ansible 및 AWX를 더 효과적으로 활용할 수 있게 됩니다.

$ cat /proc/58712/environ --show-nonprinting | sed 's/\^@/\n/g' | grep REGISTRIES
CONTAINERS_REGISTRIES_CONF=/tmp/ansible_runner_registry_17_dya5ku7p/registries.conf
REGISTRIES_CONFIG_PATH=/tmp/ansible_runner_registry_17_dya5ku7p/registries.conf
 
$ cat /tmp/ansible_runner_registry_17_dya5ku7p/registries.conf
[[registry]]
location = "registry.example.com"
insecure = true

Execution Node의 설정과 동작에 대한 보충 설명은 다음과 같습니다.

 

Execution Node용 인증서 내용

인스톨 묶음(Install Bundle)에는 Execution Node에서 사용하는 Receptor가 사용하는 인증서 및 비밀 키가 포함되어 있습니다. 이들은 Receptor의 CA에 의해 서명되었습니다. 이 인증서의 기본 유효 기간은 10일로 고정되어 있으며, 이 기한이 위에서 언급한 주의 사항의 근거입니다. 이 기한은 다음 버전에서 10년까지로 연장될 예정입니다.

 

인증서의 내용은 다음과 같이 보입니다.

$ openssl x509 -text -in receptor/tls/receptor.crt -noout
Certificate:
    Data:
        ...
        Issuer: CN = awx Receptor Root CA
        Validity
            Not Before: Oct 17 23:18:26 2022 GMT
            Not After : Oct 27 23:18:26 2022 GMT
        ...
        Subject: CN = exec01.ansible.internal
        ...
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:exec01.ansible.internal, othername:<unsupported>
    ...

Receptor의 CA 자체의 인증서도 포함되어 있으며, 기본적으로 자체 서명된 인증서입니다. 이 인증서는 10년 동안 유효합니다.

$ openssl x509 -text -in receptor/tls/ca/receptor-ca.crt -noout
Certificate:
    Data:
        ...
        Issuer: CN = awx Receptor Root CA
        ...
        Subject: CN = awx Receptor Root CA
        ...

 

기한이 다가오는 인증서 갱신

Execution Node의 인증서를 공식적인 방법으로 업데이트하려면 다음 단계를 수행할 수 있습니다. Receptor CA 인증서를 사용자 정의로 교체하는 경우에도 동일한 단계를 따릅니다.

  1. AWX에서 해당 Execution Node용 인스톨 묶음을 다시 다운로드합니다.
  2. 기본 설치와 동일한 방식으로 인스톨 묶음에 포함된 플레이북을 실행합니다.
  3. Execution Node에서 Receptor 서비스를 다시 시작합니다. sudo systemctl restart receptor

3단계의 수동 서비스 재시작은 번거로울 수 있지만, 2단계의 플레이북에서 처리할 수 있도록하기 위한 Issue와 PR이 이미 생성되어 있으므로 근일 내에 필요하지 않을 것으로 예상됩니다.

 

Execution Node 삭제

AWX에서는 해당 Execution Node를 등록된 화면에서 "Remove" 버튼을 통해 삭제할 수 있습니다. 그러나 Execution Node에서 플레이북에 의해 설정된 많은 항목이 자동으로 제거되지 않으므로, 초기 상태로 복원하기 위해 수동 작업이 필요합니다. Receptor 서비스를 중지하거나 제거해야 합니다.

 

receptorctl을 사용한 확인 및 조사

기본적으로 Receptor의 CLI 인 receptorctl은 설치되지 않지만 설치하면 Receptor 상태 및 작동을 더 자세히 확인할 수 있습니다.

 

AWX에서는 awx-ee 컨테이너에 설치하는 것이 좋습니다. 이 컨테이너는 영구적으로 보관되지 않으므로 Pod가 다시 생성될 때 깨끗한 상태로 되돌릴 수 있습니다. Execution Node에서는 pip를 직접 사용하며, awx 사용자로 전환한 다음 작업을 수행하면 다음 단계에서 문제를 피할 수 있습니다.

 

아래 명령을 통해 메시 네트워크의 상태를 확인할 수 있습니다.

 

AWX에서 실행 예시

kubectl -n awx exec deploy/awx -c awx-ee -- pip install receptorctl
/home/runner/.local/bin/receptorctl --socket /var/run/receptor/receptor.sock status

sudo su - awx
pip install receptorctl

 

Execution Node에서 실행 예시

receptorctl --socket /var/run/receptor/receptor.sock status

또한, AWX에서 Execution Node로의 연결을 확인하는 명령도 사용할 수 있습니다. AWX의 Run health check는 거의 동일한 작업을 수행합니다.

 

Execution Node에서 실행 예시 (ansible-runner worker --worker-info 명령 실행 및 결과 표시)

kubectl -n awx exec deploy/awx -c awx-ee -- /home/runner/.local/bin/receptorctl --socket /var/run/receptor/receptor.sock work submit -f --rm --node exec01.ansible.internal --tls-client tlsclient --signwork ansible-runner -n -a params="--worker-info"

결과를 확인하기 위해서는 receptorctl 명령 및 Receptor의 문서를 참조하는 것이 도움이 될 것입니다.

$ kubectl -n awx exec deploy/awx -c awx-ee -- /home/runner/.local/bin/receptorctl --socket /var/run/receptor/receptor.sock status
Warning: receptorctl and receptor are different versions, they may not be compatible
Node ID: awx-667b7b7b54-bwhmv
Version: 1.2.0+g3213360
System CPU Count: 4
System Memory MiB: 7762
 
Connection              Cost
exec01.ansible.internal 1
 
Known Node              Known Connections
awx-667b7b7b54-bwhmv    exec01.ansible.internal: 1 
exec01.ansible.internal awx-667b7b7b54-bwhmv: 1 
 
Route                   Via
exec01.ansible.internal exec01.ansible.internal
 
Node                    Service   Type       Last Seen             Tags
awx-667b7b7b54-bwhmv    control   Stream     2022-10-07 01:32:20   {'type': 'Control Service'}
exec01.ansible.internal control   StreamTLS  2022-10-07 01:31:33   {'type': 'Control Service'}
 
Node                    Work Types
awx-667b7b7b54-bwhmv    local, kubernetes-runtime-auth, kubernetes-incluster-auth
 
Node                    Secure Work Types
exec01.ansible.internal ansible-runner

$ receptorctl --socket /var/run/receptor/receptor.sock status

이를 통해 Execution Node의 설정과 동작을 더 자세히 이해할 수 있으며 Receptor와 AWX의 통신 상태를 확인하고 분석할 수 있습니다.

$ kubectl -n awx exec deploy/awx -c awx-ee -- \
  /home/runner/.local/bin/receptorctl \
  --socket /var/run/receptor/receptor.sock \
  work submit \
  -f \
  --rm \
  --node exec01.ansible.internal \
  --tls-client tlsclient \
  --signwork \
  ansible-runner \
  -n \
  -a params="--worker-info"
 
{cpu_count: 4, errors: [], mem_in_bytes: 8139796480, runner_version: 2.2.1, uuid: 527e22aa-286b-4e07-bc1c-e7e0ec5ac7e3}
 
(BMq8X00P, released)

 

문제 해결

Execution Node를 정상적으로 추가하지 못하는 경우의 문제 해결 절차와 확인 방법에 대한 추가 정보는 다음과 같습니다.

 

Receptor 서비스의 상태 확인

먼저, 양쪽 Receptor 서비스가 실행 중인지 확인해야 합니다.

  • AWX 측
  • AWX 측에서 Receptor 프로세스는 AWX Pod의 awx-ee 컨테이너에서 실행됩니다. Receptor 프로세스가 실행되지 않으면 AWX Pod가 "Running" 상태로 변경되지 않으므로, AWX 자체가 정상적으로 작동한다면 Receptor 역시 작동 중인 것으로 간주됩니다.
  • Execution Node 측
    sudo systemctl status receptor
    설정 파일을 편집하거나 인증서를 업데이트한 후에는 서비스를 다시 시작해야 합니다.
  • sudo systemctl restart receptor
  • Execution Node 측에서 Receptor는 systemd로 관리됩니다. 서비스 상태는 다음과 같이 확인할 수 있습니다.

Receptor 로그 확인

Receptor 메시 네트워크에서 노드 간 통신은 Receptor 로그에서 확인할 수 있습니다.

  • AWX 측
    kubectl -n awx logs -f deploy/awx -c awx-ee
    호스트 이름이 해결되지 않거나 포트가 닫혀 있거나, Execution Node에 도달할 수 없는 이유로 실행 노드에 연결할 수 없거나, 인증서에 잘못된 호스트 이름이 포함되어 있거나, 인증서 유효 기간이 만료된 경우와 같이 발생할 수 있는 문제를 이 로그에서 확인할 수 있습니다.
  • AWX 측의 Receptor 로그는 AWX Pod의 awx-ee 컨테이너 로그로 확인할 수 있습니다. 기본적으로 디버그 수준의 로그가 기록됩니다. 아래 명령어로 확인할 수 있습니다.
  • Execution Node 측
    sudo cat /etc/receptor/receptor.conf
    ...
    - log-level: debug
    ...
    sudo systemctl status receptor
    또한, systemd 관리 하에 있으므로, journalctl을 사용하여 확인할 수도 있지만 유용한 정보가 거의 없을 수 있습니다. sudo journalctl -u receptor
  • Execution Node 측의 Receptor 로그는 /var/log/receptor/receptor.log에 기록됩니다. 기본적으로 정보 수준의 로그가 포함되어 있으므로, 보다 자세한 로그를 확인하려면 /etc/receptor/receptor.conf에서 log-leveldebug로 설정하고 Receptor 서비스를 다시 시작해야 합니다.

 

Receptor를 통해 시작된 프로세스의 표준 입출력

AWX에서 사용되는 Receptor는 Receptor를 통해 ansible-runner 명령을 시작할 수 있도록 구성되어 있습니다. 그러나 ansible-runner 명령 자체의 출력은 앞서 언급한 Receptor 로그에 포함되지 않습니다. 따라서 Receptor 메시 네트워크 구성이 올바르더라도, ansible-runner에서 오류가 발생하는 경우, Receptor 로그만으로는 문제를 식별하기가 어렵습니다.

 

Receptor는 Receptor를 통해 시작된 프로세스의 표준 입력 및 출력을 임시 디렉토리에 저장합니다. 오류가 예상되는 경우, 이를 확인하면 원인을 조사하는 데 도움이 될 수 있습니다. 주로 다음과 같은 상황에서 유용합니다.

  • AWX 측에서 Execution Node가 "Unavailable" 상태임
  • AWX 측에서 "Run health check"를 실행하면 "exit status 1"만 표시되어 자세한 내용이 알 수 없는 경우
  • Receptor 로그에서도 유용한 정보를 얻을 수 없는 경우

임시 디렉토리는 AWX 측 (awx-ee 컨테이너), Execution Node 측 모두에서 공유되며, 기본적으로 /tmp/receptor/<노드 이름>/<유닛 ID>로 설정됩니다. 파일에는 표준 입력 및 표준 출력이 덤프됩니다. 아래는 Execution Node 측의 출력 예시입니다.

$ sudo ls -lR /tmp/receptor/exec01.ansible.internal
/tmp/receptor/exec01.ansible.internal:
total 0
drwx------. 2 awx awx 66 Oct  7 08:59 ZKLrOH9m

/tmp/receptor/exec01.ansible.internal/ZKLrOH9m:
total 248
-rw-------. 1 awx awx    182 Oct  7 08:59 status
-rw-------. 1 awx awx      0 Oct  7 08:59 status.lock
-rw-------. 1 awx awx 118050 Oct  7 08:59 stdin
-rw-------. 1 awx awx  78388 Oct  7 08:59 stdout

그러나 유닛 ID는 통신할 때마다 변경되며, AWX를 통해 수행된 처리는 유닛 ID 디렉토리가 종료 후 즉시 제거되므로 해당 파일을 볼 수 있는 시간은 매우 짧습니다.

 

따라서 유닛 ID마다 확인하는 대신, 작업을 기다리고 다음과 같이 명령어를 사용하여 모든 파일을 확인하는 것이 편리합니다. 예를 들어, AWX 측에서 "Run health check"를 실행하고 Execution Node 측에서 이 명령을 실행하면 ansible-runner의 출력도 확인할 수 있습니다.

grep -R "" /tmp/receptor/<노드 이름>

아래는 강제로 ansible-runner를 실패시켰을 때의 예시입니다. 파이썬 Traceback까지 추적할 수 있는 구체적인 실패 사례입니다. 이 경우, /home/awx 디렉토리 권한이 잘못 설정되어 있습니다.

$ sudo grep -R "" /tmp/receptor/exec01.ansible.internal
/tmp/receptor/exec01.ansible.internal/FW69cPcD/status:{"State":3,"Detail":"exit status 1","StdoutSize":630,"WorkType":"ansible-runner","ExtraData":null}
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:Traceback (most recent call last):
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:  File "/usr/local/bin/ansible-runner", line 8, in <module>
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:    sys.exit(main())
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:  File "/usr/local/lib/python3.9/site-packages/ansible_runner/__main__.py", line 745, in main
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:    uuid = ensure_uuid()
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:  File "/usr/local/lib/python3.9/site-packages/ansible_runner/utils/capacity.py", line 31, in ensure_uuid
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:    if uuid_file_path.exists():
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:  File "/usr/lib64/python3.9/pathlib.py", line 1424, in exists
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:    self.stat()
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:  File "/usr/lib64/python3.9/pathlib.py", line 1232, in stat
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:    return self._accessor.stat(self)
/tmp/receptor/exec01.ansible.internal/FW69cPcD/stdout:PermissionError: [Errno 13] Permission denied: '/home/awx/.ansible_runner_uuid'

해당 정보를 통해 Execution Node에 대한 문제를 자세히 분석할 수 있습니다.

 

마무리

AWX 21.7.0에서 소개된 Execution Node의 개요, 구성 방법 및 사용 방법에 대한 간략한 소개를 제공했습니다. 이는 확장 가능성을 향상시키는 데 큰 기회를 제공할 뿐만 아니라, 소규모 환경에서도 격리된 네트워크로의 게이트웨이 역할을 수행할 수 있는 재미있는 기능입니다.

 

하지만 언급했듯이 21.7.0 단계에서는 인증서 유효 기간의 문제가 있으므로 본격적인 활용은 다소 어려울 수 있습니다. 그러나 다음 릴리스 이후에는 안정적으로 사용할 수 있게 될 것입니다. 현재 요구 사항에 따라 편리하게 활용하실 수 있습니다.

 

이상으로 AWX Execution Node의 주요 특징과 설정 방법을 살펴보았습니다. Execution Node는 대규모 및 소규모 환경 모두에서 AWX의 확장성을 향상시키는 데 유용한 기능이며, 격리된 네트워크로의 역할을 수행할 수 있습니다. 현재는 인증서 유효 기간 문제가 있지만 이는 향후 업데이트로 해결될 것으로 예상됩니다. AWX Execution Node를 활용하여 자동화 및 배포 프로세스를 효과적으로 관리하실 수 있습니다.


원문 : blog.kurokobo.com

참고 : https://github.com/ansible/awx/blob/devel/docs/execution_nodes.md

728x90

댓글