“Ansible를 이용한 Apache 서버 제어”
Ansible(앤서블)은 여러 개의 서버를 효율적으로 관리할 수 있게 해주는 IT 환경 구성 자동화 도구이다.
Ansible(앤서블)은 시스템 구성하고, 소프트웨어 배포하고, 지속적인 배포 또는 제로 다운 타임 롤링 업데이트와 같은 고급 IT 작업이 가능하다.
앤서블은 플레이북 이라는 곳에 실행할 구성을 선언해 놓으면, 필요할 때 마다 자동으로 실행 시킬 수 있는 것이 가장 큰 특징이다. 웹서버의 구성 등을 선언해 놓으면 관리자들은 필요할 때마다 그 구성대로 서버의 설정을 베포 할 수 있게 해주는 것이다.
또한 멱등성(Idempotency, 여러 번 적용해도 결과가 동일하며, 수정된 부분이 있다면 그 부분만 새롭게 반영 됨. 예를들어 install java 명령어 실행 시 이미 해당 서버에 java가 설치되어 있으면 재 설치하지 않음)의 특징이 있으며, 데이터 전송을 위해 OpenSSH를 이용하고, yaml 언어를 사용한다.
이 포스팅에서는 Ansible를 이용한 Apache 서버 제어하는 방법(VirtualHost, SSL)을 설명합니다.
[개 요]종 류 : 엔지니어링 |
“시작하며”
이 포스트는 Ansible를 이용한 Apache 서버 제어 적용 방법 과 활용사례에 대해 아래 순서로 설명합니다.
1. 마스터서버에서 Ansible 과 ssh-keygen 설치 2. ssh-keygen 생성 및 배포 3. Ansible 제어노드, 인벤토리, 플레이북 생성 4. Ansible적용 및 실행 5. 호스트 WEB 서버에서 확인 |
- 자동화 프로그램 앤서블(ansible) 소개자동화 프로그램 앤서블(ansible) 소개 글을 참고하시길 바랍니다.
1) 마스터서버에서 Ansible과 ssh-keygen 설치
Ansible을 사용하기 위해서 마스터서버에서 설치를 진행합니다.
[root@localhost]# apt update -y [root@localhost]# apt upgrade -y [root@localhost]# apt install -y ansible sshpass tree
2) ssh-keygen 생성 및 배포
저장소를 추가하고 활성화 하기 위해 아래의 작업을 수행합니다.
[root@localhost]# ssh-copy-id root@WEB서버IP
sshpass를 설치를 진행하였으니, ssh-keygen 작업을 진행합니다.
[root@localhost]# ssh-keygen
ssh-key를 만들때 여러번 묻는 상황이 나옵니다. 해당 부분에 아무것도 입력하지 않고 엔터 키보드만 누르면 됩니다.
아래 그림처럼 하면 key가 생성됩니다.
3) Ansible 제어노드, 인벤토리, 플레이북 생성
환경변수 설정을 위해 아래 작업을 수행합니다. 일단 디렉토리 구조체부터 생성합니다.
[root@localhost]# mkdir /root/ansible_project [root@localhost]# cd /root/ansible_project [root@localhost]# mkdir -p playbooks/handlers/ playbooks/templates/ playbooks/roles/common/tasks playbooks/roles/web/tasks playbooks/db/common/tasks
인벤토리 생성
[root@localhost]# vi /root/ansible_project/inventory.ini
master ansible_host=10.0.0.2 [web] web ansible_host=10.0.0.3 [db] db ansible_host=10.0.0.4
playbooks 설정 (WEB, DB)
vi /root/ansible_project/playbooks/configure_web.yml
- name: Configure Web Server hosts: web become: true roles: - common - web handlers: - name: Reload Apache import_tasks: handlers/main.yml
vi /root/ansible_project/playbooks/configure_db.yml
- name: Configure Database Server hosts: db become: true roles: - common - db
Apache Virtual Host 설정
vi /root/ansible_project/playbooks/add_vhost.yml
- name: Add Virtual Hosts hosts: web become: true vars: domain_name: "93it-serverengineer.kr" document_root: "/home/93it/www" tasks: - name: Create directory for the virtual host file: path: "{{ document_root }}" state: directory mode: '0755' become: true - name: Create index.html file copy: web content: "93it-serverengineer.kr index.html" dest: "{{ document_root }}/index.html" become: true - name: Create phpinfo.php file copy: content: "93it-serverengineer.kr phpinfo.php <?php phpinfo();?>" dest: "{{ document_root }}/phpinfo.php" become: true - name: Create Apache virtual host configuration template: src: vhost.conf.j2 dest: "/etc/apache2/sites-available/{{ domain_name }}.conf" notify: - Reload Apache - name: Enable the virtual host command: "a2ensite {{ domain_name }}" args: creates: "/etc/apache2/sites-enabled/{{ domain_name }}.conf" notify: - Reload Apache handlers: - name: Reload Apache import_tasks: handlers/main.yml
SSL 노트 설정
vi /root/ansible_project/playbooks/letsencrypt.yml
--- - name: Add Virtual Hosts and SSL Certificates hosts: web become: true vars: domains: - "93it-serverengineer.kr" document_root: "/home/93it/www" # letsencrypt.yml handlers: - import_tasks: handlers/main.yml tasks: - name: Install Certbot and Apache plugin apt: name: - certbot - python3-certbot-apache state: present - name: Check if Certbot renewal configuration file exists stat: path: "/etc/letsencrypt/renewal/{{ item }}.conf" register: certbot_renewal_info with_items: "{{ domains }}" ignore_errors: yes - debug: var: certbot_renewal_info - name: Renew Let's Encrypt SSL certificates if domains.conf doesn't exist command: "certbot certonly --webroot -w {{ document_root }} -d {{ item }} --email resave177@naver.com --agree-tos --no-eff-email --rsa-key-size 4096" with_items: "{{ domains }}" when: "not certbot_renewal_info is defined or (certbot_renewal_info.results | default([]) | map(attribute='stat.exists') | bool)" ignore_errors: yes - name: Create SSL virtual host configuration template: src: ssl_vhost.conf.j2 dest: "/etc/apache2/sites-available/{{ item }}-ssl.conf" with_items: "{{ domains }}" notify: - Reload Apache - name: Enable SSL virtual host command: "a2ensite {{ item }}-ssl.conf" args: creates: "/etc/apache2/sites-enabled/{{ item }}-ssl.conf" with_items: "{{ domains }}" notify: - Reload Apache handlers: - name: Restart Cron systemd: name: cron state: restarted - name: Add Certbot renewal and Apache reload to /etc/crontab blockinfile: path: "/etc/crontab" block: | 0 4 * * * root certbot renew --quiet 1 4 * * * root systemctl reload apache2 marker: "# {mark} Ansible managed block" notify: - Restart Cron
Apache 재시작 핸들러
vi /root/ansible_project/playbooks/handlers/main.yml
--- - name: Reload Apache service: name: apache2 state: reloaded
Vhost 설정 생성할때 설정
vi /root/ansible_project/playbooks/templates/vhost.conf.j2
vi /root/ansible_project/playbooks/roles/web/templates/vhost.conf.j2
<VirtualHost *:80> ServerAdmin webmaster@{{ domain_name }} ServerName {{ domain_name }} DocumentRoot {{ document_root }} ErrorLog ${APACHE_LOG_DIR}/{{ domain_name }}_error.log CustomLog ${APACHE_LOG_DIR}/{{ domain_name }}_access.log combined <Directory {{ document_root }}> Options Indexes FollowSymLinks AllowOverride None Require all granted # PHP settings <FilesMatch \.php$> SetHandler application/x-httpd-php </FilesMatch> </Directory> </VirtualHost>
SSL Vhost 설정 생성할때 설정
vi /root/ansible_project/playbooks/templates/ssl_vhost.conf.j2
vi /root/ansible_project/playbooks/roles/web/templates/vhost.conf.j2
<IfModule mod_ssl.c> {% for domain in domains %} <VirtualHost *:443> ServerAdmin webmaster@{{ domain }} ServerName {{ domain }} DocumentRoot {{ document_root }} ErrorLog ${APACHE_LOG_DIR}/{{ domain }}_ssl_error.log CustomLog ${APACHE_LOG_DIR}/{{ domain }}_ssl_access.log combined SSLEngine on SSLCertificateFile /etc/letsencrypt/live/{{ domain }}/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/{{ domain }}/privkey.pem SSLCertificateChainFile /etc/letsencrypt/live/{{ domain }}/fullchain.pem <Directory {{ document_root }}> Options Indexes FollowSymLinks AllowOverride None Require all granted # PHP settings <FilesMatch \.php$> SetHandler application/x-httpd-php </FilesMatch> </Directory> </VirtualHost> {% endfor %} </IfModule>
vi /root/ansible_project/playbooks/roles/common/tasks/main.yml
--- - name: Install common packages apt: name: "{{ item }}" state: present with_items: - curl - vim # Add other common packages as needed - name: Ensure time is synchronized apt: name: ntp state: present become: true
vi /root/ansible_project/playbooks/roles/db/tasks/main.yml
--- - name: Install MariaDB Server apt: name: mariadb-server state: present - name: Enable MariaDB service service: name: mariadb enabled: yes state: started
vi /root/ansible_project/playbooks/roles/web/tasks/main.yml
--- - name: Install Apache apt: name: apache2 state: present - name: Enable Apache service service: name: apache2 enabled: yes state: started - name: Ensure Apache modules are enabled apache2_module: state: present name: "{{ item }}" with_items: - rewrite - headers - ssl - socache_shmcb - socache_dbm notify: - Reload Apache - name: Install PHP 8.1 and Apache PHP module apt: name: - php8.1 - libapache2-mod-php8.1 state: present notify: - Reload Apache
vi /root/ansible_project/playbooks/roles/web/templates/php.ini.j2
vi /root/ansible_project/playbooks/templates/php.ini.j2
; PHP settings display_errors = On error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED date.timezone = UTC
4) ansible로 호스트 WEB서버 VirtualHost , SSL 인증서 적용시키기
cd /root/ansible_project
ansible-playbook -i inventory.ini playbooks/configure_web.yml
ansible-playbook -i inventory.ini playbooks/configure_db.yml
ansible-playbook -i inventory.ini playbooks/add_vhost.yml
ansible-playbook -i inventory.ini playbooks/letsencrypt.yml
5) 호스트 WEB 서버에서 확인
# apache2ctl -S
WEB 서버에서 Virtual 도메인과 SSL인증서 발급되었습니다