Ansible과 Jenkins 통합: CI/CD 파이프라인 자동화

Ansible과 Jenkins를 통합하여 원활한 애플리케이션 배포를 구현하세요. 이 포괄적인 가이드는 CI/CD 파이프라인 자동화를 위한 단계별 지침과 실용적인 예제를 제공합니다. Ansible을 위한 Jenkins 설정 방법, 자격 증명 관리 방법, 그리고 일관되고 효율적인 배포를 위해 Ansible 플레이북을 활용하는 선언적 파이프라인 작성 방법을 알아보세요. 프로젝트 구조화, Ansible Vault 사용, 멱등성 보장을 위한 모범 사례를 익히고 개발 워크플로우를 고도로 자동화되고 안정적인 프로세스로 전환하세요.

37 조회수

Jenkins와 Ansible 통합: CI/CD 파이프라인 자동화

현대 소프트웨어 개발은 효율성, 일관성, 속도를 중요하게 생각합니다. 지속적 통합(CI) 및 지속적 배포/전달(CD) 파이프라인은 이러한 목표를 달성하는 데 핵심적인 역할을 하며, 코드 커밋부터 프로덕션까지의 과정을 자동화합니다. 선도적인 오픈 소스 자동화 서버인 Jenkins와 강력한 구성 관리 및 애플리케이션 배포 도구인 Ansible은 견고한 CI/CD 워크플로우를 구축하는 데 중요한 역할을 합니다.

이 글에서는 Jenkins와 Ansible의 시너지 통합을 심층적으로 다루며, 두 도구의 강점을 결합하여 애플리케이션 배포 프로세스를 간소화하는 방법을 설명합니다. 빌드, 테스트, 배포 단계를 원활하게 자동화하기 위한 실질적인 단계, 구성 전략, 모범 사례를 탐구하여 개발 라이프사이클을 민첩하고 오류에 강한 운영으로 전환할 것입니다. 이 가이드가 끝나면 효율적이고 반복 가능하며 확장 가능한 배포를 달성하기 위해 이러한 도구를 활용하는 방법에 대한 명확한 이해를 갖게 될 것입니다.

CI/CD의 강력한 듀오: Jenkins와 Ansible

통합의 구체적인 내용으로 들어가기 전에 CI/CD 맥락에서 Jenkins와 Ansible의 역할과 그 협업이 왜 그렇게 효과적인지 간략하게 이해해 봅시다.

  • Jenkins (CI 오케스트레이터): Jenkins는 CI/CD 파이프라인의 중앙 오케스트레이터 역할을 합니다. 소스 코드 저장소를 모니터링하고, 빌드를 트리거하며, 테스트를 실행하고, 배포 프로세스를 주도합니다. 플러그인을 통한 확장성은 다양한 개발 생태계에 매우 유연하게 적용할 수 있게 해줍니다.
  • Ansible (배포 및 구성 관리): Ansible은 구성 관리, 애플리케이션 배포, 작업 오케스트레이션에 뛰어난 에이전트 없는 자동화 엔진입니다. 간단한 YAML 플레이북을 사용하여 원하는 상태를 정의하고 대상 기계(서버, 네트워크 장치 등)에서 작업을 실행합니다. 에이전트 없는 특성은 설정 및 유지 관리를 단순화합니다.

Jenkins와 Ansible을 통합해야 하는 이유?

Jenkins와 Ansible을 통합하면 CI/CD 파이프라인에 여러 가지 강력한 이점을 제공합니다.

  1. 오케스트레이션된 배포: Jenkins는 복잡한 Ansible 플레이북을 시작하여 통제되고 순차적인 방식으로 다양한 서버, 데이터베이스 및 서비스 전반에 걸쳐 다중 계층 애플리케이션 배포를 가능하게 합니다.
  2. 일관성 및 반복성: Ansible 플레이북은 배포가 매번 동일하게 실행되도록 보장하여 "내 컴퓨터에서는 작동하는데" 문제를 줄이고 개발, 스테이징, 프로덕션 전반에 걸쳐 일관된 환경을 보장합니다.
  3. 수동 오류 감소: Jenkins를 통한 Ansible 배포 자동화는 인간 오류의 위험을 최소화하여 보다 안정적인 릴리스를 제공합니다.
  4. 속도 및 효율성: 자동화된 배포는 수동 프로세스보다 훨씬 빠르므로 배달 주기를 가속화하고 더 빠른 반복을 가능하게 합니다.
  5. 에이전트 없는 단순성: Ansible의 에이전트 없는 아키텍처는 대상 노드에 특정 소프트웨어를 설치할 필요가 없음을 의미하며, 인프라 관리를 단순화합니다.
  6. 코드로 관리되는 파이프라인: Jenkins (Jenkinsfile 사용)와 Ansible (플레이북 사용) 모두 "코드로 관리" 접근 방식을 장려하여 버전 관리, 감사 가능성 및 재사용성을 통해 파이프라인과 인프라 구성을 관리할 수 있습니다.

통합을 위한 사전 요구 사항

시작하기 전에 다음 사항이 설정되었는지 확인하십시오.

  • Jenkins 서버: 작동 중인 Jenkins 인스턴스. Docker 컨테이너 또는 전용 VM에서 실행되는 Jenkins를 권장합니다.
  • Ansible 설치: Jenkins 에이전트(또는 단일 노드 설정에서 Jenkins 컨트롤러)에 Ansible이 설치되어 있어야 합니다. ansible 명령이 시스템의 PATH에 있는지 확인하십시오.
  • 대상 기계: 애플리케이션이 배포될 서버 또는 가상 머신으로, Jenkins 에이전트에서 SSH로 액세스 가능해야 합니다.
  • SSH 키 쌍: Jenkins가 대상 기계와 인증하기 위한 SSH 키 쌍(개인/공개). 개인 키는 Jenkins 자격 증명에 안전하게 저장됩니다.
  • 소스 코드 저장소: 애플리케이션 코드와 Ansible 플레이북은 버전 제어 시스템(예: Git)에 저장되어야 합니다.

Ansible을 위한 Jenkins 설정

Jenkins가 Ansible 명령을 실행하고 대상 인프라에 연결할 수 있도록 하려면 초기 설정이 필요합니다.

1. Ansible 플러그인 설치 (선택 사항이지만 권장)

엄밀히 말하면 필수적이지는 않지만(항상 셸 단계에서 ansible-playbook을 직접 호출할 수 있음), Jenkins Ansible 플러그인은 전용 빌드 단계와 향상된 통합을 제공하며, 특히 자격 증명 관리 및 자세한 출력에 유용합니다.

  1. Jenkins 관리 > 플러그인 관리로 이동합니다.
  2. 사용 가능한 탭으로 이동하여 "Ansible"을 검색합니다.
  3. "Ansible" 플러그인을 선택하고 다시 시작 없이 설치 또는 지금 다운로드하고 다시 시작 후 설치를 클릭합니다.

2. SSH 자격 증명 구성

Ansible은 SSH를 사용하여 대상 서버에 연결합니다. Jenkins에 SSH 개인 키를 저장해야 합니다.

  1. Jenkins 관리 > 자격 증명 관리로 이동합니다.
  2. Jenkins 범위 > 전역 자격 증명 (제한 없음)을 선택합니다.
  3. 자격 증명 추가를 클릭합니다.
  4. 종류 선택: 개인 키가 있는 SSH 사용자 이름.
  5. 범위: 전역
  6. ID: 고유 식별자(예: ansible-ssh-key).
  7. 사용자 이름: 대상 기계의 SSH 사용자(예: ubuntu, ec2-user).
  8. 개인 키: 직접 입력을 선택하고 SSH 개인 키를 붙여넣습니다. -----BEGIN OPENSSH PRIVATE KEY----- 또는 -----BEGIN RSA PRIVATE KEY-----로 시작하고 -----END OPENSSH PRIVATE KEY----- 또는 -----END RSA PRIVATE KEY-----로 끝나야 합니다.
  9. 확인을 클릭합니다.

팁: 키 관리 모범 사례

  • Jenkinsfile 또는 플레이북에 SSH 개인 키를 직접 하드코딩하지 마십시오.
  • 개인 사용자 키와 분리된 자동화를 위한 전용 SSH 키를 사용하십시오.
  • 자동화 키를 정기적으로 감사하고 교체하십시오.

Ansible을 사용한 Jenkins 파이프라인 설계

소스 코드 저장소에 저장된 Jenkinsfile로 정의된 선언적 파이프라인을 사용합니다. 이는 버전 관리, 감사 가능성 및 재사용성을 제공하는 "코드로 관리되는 파이프라인"을 촉진합니다.

기본 파이프라인 구조

애플리케이션을 배포하는 일반적인 파이프라인은 다음과 같습니다.

// Jenkinsfile
pipeline {
    agent any

    environment {
        // 필요한 경우 환경 변수 정의
        ANSIBLE_HOST_KEY_CHECKING = 'False' // 프로덕션에서는 주의, known_hosts 선호
    }

    stages {
        stage('소스 체크아웃') {
            steps {
                git 'https://your-scm-url/your-repo.git'
            }
        }

        stage('애플리케이션 빌드') {
            // 이 단계는 JAR, WAR, Docker 이미지 등을 빌드할 수 있습니다.
            // 예: Spring Boot JAR 빌드
            steps {
                sh 'mvn clean package'
            }
        }

        stage('단위 테스트 실행') {
            steps {
                sh 'mvn test'
            }
        }

        stage('스테이징 배포') {
            steps {
                script {
                    // SSH 에이전트를 사용하여 개인 키를 Ansible에서 사용할 수 있도록 합니다.
                    sshagent(credentials: ['ansible-ssh-key']) {
                        // Ansible 플레이북 실행
                        sh 'ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml \n                            -e "app_version=$(cat target/VERSION)"'
                    }
                }
            }
        }

        // 선택 사항: stage('통합 테스트 실행') { ... }

        stage('프로덕션 배포') {
            // 이 단계는 수동 승인이 필요할 수 있습니다.
            input {
                message "프로덕션 배포를 진행하시겠습니까?"
                ok "프로덕션 배포"
            }
            steps {
                script {
                    sshagent(credentials: ['ansible-ssh-key']) {
                        sh 'ansible-playbook -i inventory/production.ini playbooks/deploy_app.yml \n                            -e "app_version=$(cat target/VERSION)"'
                    }
                }
            }
        }
    }

    post {
        always {
            echo '파이프라인 완료.'
        }
        success {
            echo '파이프라인 성공!'
            // slackSend channel: '#deployments', message: "배포 성공: ${env.BUILD_URL}"
        }
        failure {
            echo '파이프라인 실패!'
            // slackSend channel: '#deployments', message: "배포 실패: ${env.BUILD_URL}"
        }
    }
}

주요 요소 설명:

  • agent any: 파이프라인이 사용 가능한 모든 에이전트에서 실행될 수 있음을 지정합니다. 프로덕션 설정의 경우 Ansible이 미리 설치된 에이전트에서 실행되도록 레이블을 지정할 수 있습니다(예: agent { label 'ansible-agent' }).
  • environment: 파이프라인에 대한 환경 변수를 정의합니다. ANSIBLE_HOST_KEY_CHECKING=False는 호스트 키 검사를 비활성화하며, 동적 환경에서 유용할 수 있지만 known_hosts를 신중하게 관리하지 않는 한 프로덕션에서는 일반적으로 권장되지 않습니다.
  • sshagent(credentials: ['ansible-ssh-key']) { ... }: 이것이 중요합니다. Jenkins 에이전트의 SSH 에이전트에 자격 증명 ID(ansible-ssh-key)로 지정된 개인 키를 주입합니다. 이 블록 내의 모든 sh 명령은 개인 키 파일을 명시적으로 전달하지 않고도 ssh(따라서 ansible)를 사용하여 대상 호스트에 연결할 수 있습니다.
  • sh 'ansible-playbook ...': Ansible 플레이북을 실행합니다.
    • -i inventory/staging.ini: 대상 환경의 인벤토리 파일을 지정합니다.
    • playbooks/deploy_app.yml: 실행할 기본 플레이북입니다.
    • -e "app_version=$(cat target/VERSION)": 플레이북에 추가 변수 app_version을 전달합니다. 이는 배포할 아티팩트의 버전 번호를 포함할 수 있습니다. 이는 빌드 단계 중에 VERSION 파일이 생성되었다고 가정합니다.
  • input 단계: 프로덕션 배포와 같은 중요한 단계에 대한 수동 승인 단계를 추가하는 방법을 보여줍니다.
  • post 블록: 파이프라인이 완료된 후 성공 또는 실패에 관계없이 수행할 작업을 정의합니다.

예제 Ansible 플레이북: 웹 애플리케이션 배포

playbooks/deploy_app.ymlinventory/staging.ini의 간단한 예제는 다음과 같습니다.

inventory/staging.ini

[web_servers]
web1.example.com
web2.example.com

[database_servers]
db1.example.com

[all:vars]
ansible_user=ubuntu

playbooks/deploy_app.yml

---
- name: 웹 애플리케이션 배포
  hosts: web_servers
  become: yes # sudo/root 권한으로 작업 실행
  vars:
    app_name: my-webapp
    app_path: /opt/{{ app_name }}
    app_port: 8080
    app_version: "{{ app_version | default('1.0.0') }}" # extra_var로 전달되지 않은 경우 기본값

  tasks:
    - name: 애플리케이션 디렉터리 존재 확인
      ansible.builtin.file:
        path: "{{ app_path }}"
        state: directory
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0755'

    - name: 애플리케이션 JAR을 대상으로 복사
      ansible.builtin.copy:
        src: "target/{{ app_name }}-{{ app_version }}.jar" # Jenkins에서 빌드된 JAR로 가정
        dest: "{{ app_path }}/{{ app_name }}.jar"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'
      notify: restart app service

    - name: systemd 서비스 파일 존재 확인
      ansible.builtin.template:
        src: templates/my-webapp.service.j2
        dest: /etc/systemd/system/{{ app_name }}.service
        owner: root
        group: root
        mode: '0644'
      notify: restart app service

    - name: 앱 서비스 시작 및 활성화 확인
      ansible.builtin.systemd:
        name: "{{ app_name }}"
        state: started
        enabled: yes

  handlers:
    - name: 앱 서비스 재시작
      ansible.builtin.systemd:
        name: "{{ app_name }}"
        state: restarted
        daemon_reload: yes

templates/my-webapp.service.j2 (Systemd 서비스 템플릿)

[Unit]
Description={{ app_name }} 애플리케이션
After=network.target

[Service]
User={{ ansible_user }}
ExecStart=/usr/bin/java -jar {{ app_path }}/{{ app_name }}.jar --server.port={{ app_port }}
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

이 플레이북은 애플리케이션 디렉터리가 존재하는지 확인하고, 애플리케이션 JAR을 복사하고, systemd 서비스를 설정하고, 서비스가 실행되도록 보장하는 작업을 정의합니다. 핸들러를 사용하여 필요한 경우에만 서비스를 재시작하여 멱등성을 보장합니다.

모범 사례 및 팁

  • 멱등성 있는 플레이북: Ansible 플레이북에서는 항상 멱등성을 유지하도록 노력하십시오. 플레이북을 여러 번 실행해도 의도하지 않은 부작용 없이 동일한 결과가 나와야 합니다.
  • Ansible Vault: Ansible Vault를 사용하여 플레이북 및 변수 파일 내의 민감한 데이터(암호, API 키, 인증서)를 암호화하십시오. Vault 암호는 Jenkins 자격 증명에 저장하고 -e "ansible_vault_password=$VAULT_PASSWORD" 또는 vault_password_file을 통해 Ansible에 전달하십시오.
  • 구조를 위한 역할: 유지 관리 및 재사용성을 높이기 위해 Ansible 콘텐츠를 역할로 구성하십시오. 각 역할은 특정 구성 요소(예: webserver, database, app_deploy)에 중점을 둡니다.
  • 동적 인벤토리: 대규모 또는 클라우드 기반 인프라의 경우 동적 인벤토리를 사용하여 클라우드 공급자(AWS EC2, Azure, GCP)에서 호스트 정보를 자동으로 가져오는 것을 고려하십시오.
  • Jenkins 에이전트: 컨트롤러가 아닌 전용 Jenkins 에이전트에서 Ansible 작업을 실행하십시오. 이러한 에이전트는 배포에 필요한 특정 도구 및 리소스로 구성할 수 있습니다.
  • 출력 관리: Jenkins 파이프라인 출력이 명확한지 확인하십시오. Ansible은 디버깅에 필요한 경우 더 자세한 정보를 표시하기 위해 상세 옵션(-v, -vv 등)을 제공합니다.
  • 오류 처리: 플레이북 및 Jenkinsfile에서 강력한 오류 처리를 구현하십시오(예: Groovy의 try-catch 블록 또는 Ansible의 failed 조건).
  • 환경별 변수: 다양한 환경(dev, staging, production)에 대해 별도의 인벤토리 파일 또는 group_vars/host_vars를 사용하여 환경별 구성을 관리하십시오.
  • Ansible 플레이북 테스트: Jenkins에 통합하기 전에 Molecule과 같은 도구를 사용하거나 테스트 환경에서 수동으로 실행하여 Ansible 플레이북을 철저히 테스트하십시오.

결론

Ansible과 Jenkins를 통합하는 것은 CI/CD 파이프라인을 자동화하여 지속적 통합과 지속적 배포 간의 격차를 해소하는 강력한 전략입니다. Jenkins의 오케스트레이션 기능과 Ansible의 강력하고 에이전트 없는 자동화를 결합하면 더 빠르고 안정적이며 일관된 애플리케이션 배포를 달성할 수 있습니다. 이 가이드는 자격 증명 구성부터 선언적 파이프라인 구조화 및 효과적인 Ansible 플레이북 작성에 이르기까지 이러한 통합을 설정하기 위한 기반을 제공합니다. 이러한 모범 사례를 채택하여 DevOps 워크플로우를 향상시키고 더 큰 자신감과 효율성으로 소프트웨어를 제공하십시오.