One place for hosting & domains

      Ansible

      Automating Server Setup with Ansible: A DigitalOcean Workshop Kit


      Automating Server Setup with Ansible Workshop Kit Materials

      This workshop kit is designed to help a technical audience become familiar with configuration management concepts and how to use Ansible to automate server infrastructure setup.

      The aim is to provide a complete set of resources for a speaker to host an event and deliver an introductory talk on Ansible. It includes:

      • Slides and speaker notes including short demo videos and commands for running an optional live demo. This talk runs for roughly 50 minutes.
      • A GitHub repository containing the demo app code and the necessary Ansible scripts to deploy that application to an Ubuntu server.
      • This tutorial, which walks a user through rolling out the Travellist demo Laravel application on a remote server.

      This tutorial is intended to supplement the talk demo with additional detail and elucidation. It also serves as a reference for readers seeking to deploy a Laravel application to a remote Ubuntu server using Ansible.

      Introduction

      Server automation now plays an essential role in systems administration, due to the disposable nature of modern application environments. Configuration management tools such as Ansible are typically used to streamline the process of automating server setup by establishing standard procedures for new servers. This has the benefit of reducing human error associated with manual setups.

      Ansible offers a simplified architecture that doesn’t require special software to be installed on nodes. It also provides a robust set of features and built-in modules which facilitate writing automation scripts.

      This tutorial, designed to accompany the Slides and speaker notes for the Automating Server Setup with Ansible Workshop Kit, will show you how to set up an inventory file and execute a set of provisioning scripts to fully automate the process of setting up a remote LEMP server (Linux, (E)Nginx, MariaDB and PHP-FPM) on Ubuntu 18.04 and to deploy a demo Laravel application to this system.

      Note: This material is intended to demonstrate how to use playbooks to automate server setup with Ansible. Although our demo consists of a Laravel application running on a LEMP server, readers are encouraged to modify and adapt the included setup to suit their own needs.

      Prerequisites

      To follow this tutorial, you will need:

      • One Ansible control node: an Ubuntu 18.04 machine with Ansible installed and configured to connect to your Ansible hosts using SSH keys. Make sure the control node has a regular user with sudo permissions and a firewall enabled, as explained in our Initial Server Setup guide, and a set of valid SSH keys. To set up Ansible, please follow our guide on How to Install and Configure Ansible on Ubuntu 18.04.
      • One or more Ansible Hosts: one or more remote Ubuntu 18.04 servers. Each host must have the control node’s public key added to its authorized_keys file, as explained in Step 2 of the How to Set Up SSH Keys on Ubuntu 18.04 guide. In case you are using DigitalOcean Droplets as nodes, you can use the control panel to add your public key to your Ansible hosts.

      Step 1 — Cloning the Demo Repository

      The first thing we need to do is clone the repository containing the Ansible provisioning scripts and the demo Laravel application that we’ll deploy to the remote servers. All the necessary files can be found at the do-community/ansible-laravel-demo Github repository.

      After logging in to your Ansible control node as your sudo user, clone the repository and navigate to the directory created by the git command:

      • git clone https://github.com/do-community/ansible-laravel-demo.git
      • cd ansible-laravel-demo

      Now, you can run an ls command to inspect the contents of the cloned repository:

      • ls -l --group-directories-first

      You’ll see output like this:

      ansible-laravel-demo

      drwxrwxr-x 3 sammy sammy 4096 Mar 24 15:24 application
      drwxrwxr-x 2 sammy sammy 4096 Mar 24 15:24 group_vars
      drwxrwxr-x 7 sammy sammy 4096 Mar 24 15:24 roles
      -rw-rw-r-- 1 sammy sammy  102 Mar 24 15:24 inventory-example
      -rw-rw-r-- 1 sammy sammy 1987 Mar 24 15:24 laravel-deploy.yml
      -rw-rw-r-- 1 sammy sammy  794 Mar 24 15:24 laravel-env.j2
      -rw-rw-r-- 1 sammy sammy  920 Mar 24 15:24 readme.md
      -rw-rw-r-- 1 sammy sammy  318 Mar 24 15:24 server-setup.yml
      

      Here’s an overview of each of these folders and files and what they are:

      • application/: This directory contains the demo Laravel application that is going to be deployed on the remote server by the end of the workshop.
      • group_vars/: This directory holds variable files containing custom options for the application setup, such as database credentials and where to store the application files on the remote server.
      • roles/: This directory contains the different Ansible roles that handle the provisioning of an Ubuntu LEMP server.
      • inventory-example: This file can be used as a base to create a custom inventory for your infrastructure.
      • laravel-deploy.yml: This playbook will deploy the demo Laravel application to the remote server.
      • laravel-env.j2: This template is used by the laravel-deploy.yml playbook to set up the application environment file.
      • readme.md: This file contains general information about the provisioning contained in this repository.
      • server-setup.yml: This playbook will provision a LEMP server using the roles defined in the roles/ directory.

      Step 2 — Setting Up the Inventory File and Testing Connection to Nodes

      We’ll now create an inventory file to list the hosts we want to manage using Ansible. First, copy the inventory-example file to a new file called hosts:

      • cp inventory-example hosts

      Now, use your text editor of choice to open the new inventory file and update it with your own servers. Here, we’ll use nano:

      The example inventory that comes with the workshop kit contains two Ansible groups: dev and production. This is meant to demonstrate how to use group variables to customize deployment in multiple environments. If you wish to test this setup with a single node, you can use either the dev or the production group and remove the other one from the inventory file.

      ansible-laravel-demo/hosts

      [dev]
      203.0.113.0.101
      
      [prod]
      203.0.113.0.102
      
      [all:vars]
      ansible_python_interpreter=/usr/bin/python3
      

      Note: the ansible_python_interpreter variable defines the path to the Python executable on the remote host. Here, we’re telling Ansible to set this variable for all hosts in this inventory file.

      Save and close the file when you’re done. If you are using nano, you can do that by hitting CTRL+X, then Y and ENTER to confirm.

      Once you’re done adjusting your inventory file, you can execute the ping Ansible module to test whether the control node is able to connect to the hosts:

      • ansible all -i hosts -m ping -u root

      Let’s break down this command:

      • all: This option tells Ansible to run the following command on all hosts from the designated inventory file.
      • -i hosts: Specifies which inventory should be used. When this option is not provided, Ansible will try to use the default inventory, which is typically located at /etc/ansible/hosts.
      • -m ping: This will execute the ping Ansible module, which will test connectivity to nodes and whether or not the Python executable can be found on the remote systems.
      • -u root: This option specifies which remote user should be used to connect to the nodes. We’re using the root account here as an example because this is typically the only account available on fresh new servers. Other connection options might be necessary depending on your infrastructure provider and SSH configuration.

      If your SSH connection to the nodes is properly set up, you’ll get the following output:

      Output

      203.0.113.0.101 | SUCCESS => { "changed": false, "ping": "pong" } 203.0.113.0.102 | SUCCESS => { "changed": false, "ping": "pong" }

      The pong response means your control node is able to connect to your managed nodes, and that Ansible is able to execute Python commands on the remote hosts.

      Step 3 — Setting Up Variable Files

      Before running the playbooks that are included in this workshop kit, you’ll first need to edit the variable file that contains settings such as the name of the remote user to create and the database credentials to set up with MariaDB.

      Open the group_vars/all file using your text editor of choice:

      ansible-laravel-demo/group_vars/all.yml

      ---
      # Initial Server Setup
      remote_user: sammy
      
      # MySQL Setup
      mysql_root_password: MYSQL_ROOT_PASSWORD
      mysql_app_db: travellist
      mysql_app_user: travellist_user
      mysql_app_pass: DB_PASSWORD
      
      # Web Server Setup
      http_host: "{{ ansible_facts.eth0.ipv4.address }}"
      remote_www_root: /var/www
      app_root_dir: travellist-demo
      document_root: "{{ remote_www_root }}/{{ app_root_dir }}/public"
      
      # Laravel Env Variables
      app_name: Travellist
      app_env: dev
      app_debug: true
      app_url: "http://{{ http_host }}"
      db_host: localhost
      db_port: 3306
      db_database: "{{ mysql_app_db }}"
      db_user: "{{ mysql_app_user }}"
      db_pass: "{{ mysql_app_pass }}"
      

      The variables that need your attention are:

      • remote_user: The specified user will be created on the remote server and granted sudo privileges.
      • mysql_root_password: This variable defines the database root password for the MariaDB server. Note that this should be a secure password of your own choosing.
      • mysql_app_db: The name of the database to create for the Laravel application. You don’t need to change this value, but you are free to do so if you wish. This value will be used to set up the .env Laravel configuration file.
      • mysql_app_user: The name of the database user for the Laravel application. Again, you are not required to change this value, but you are free to do so.
      • mysql_app_pass: The database password for the Laravel application. This should be a secure password of your choosing.
      • http_host: The domain name or IP address of the remote host. Here, we’re using an Ansible fact that contains the ipv4 address for the eth0 network interface. In case you have domain names pointing to your remote hosts, you may want to create separate variable files for each of them, overwriting this value so that the Nginx configuration contains the correct hostname for each server.

      When you are finished editing these values, save and close the file.

      Creating additional variable files for multiple environments

      If you’ve set up your inventory file with multiple nodes, you might want to create additional variable files to configure each node accordingly. In our example inventory, we have created two distinct groups: dev and production. To avoid having the same database credentials and other settings in both environments, we need to create a separate variable file to hold production values.

      You might want to copy the default variable file and use it as base for your production values:

      • cp group_vars/all.yml group_vars/production.yml
      • nano group_vars/production.yml

      Because the all.yml file contains the default values that should be valid for all environments, you can remove all the variables that won’t need changing from the new production.yml file. The variables that you should update for each environment are highlighted here:

      ansible-laravel-demo/group_vars/production

      ---
      # Initial Server Setup
      remote_user: prod_user
      
      # MySQL Setup
      mysql_root_password: MYSQL_PROD_ROOT_PASSWORD
      mysql_app_pass: MYSQL_PROD_APP_PASSWORD
      
      # Laravel Env Variables
      app_env: prod
      app_debug: false
      

      Notice that we’ve changed the app_env value to prod and set the app_debug value to false. These are recommended Laravel settings for production environments.

      Once you’re finished customizing your production variables, save and close the file.

      Encrypting variable files with Ansible Vault

      If you plan on sharing your Ansible setup with other users, it is important to keep the database credentials and other sensitive data in your variable files safe. This is possible with Ansible Vault, a feature that is included with Ansible by default. Ansible Vault allows you to encrypt variable files so that only users with access to the vault password can view, edit or unencrypt these files. The vault password is also necessary to run a playbook or a command that makes use of encrypted files.

      To encrypt your production variable file, run:

      • ansible-vault encrypt group_vars/production.yml

      You will be prompted to provide a vault password and confirm it. Once you’re finished, if you check the contents of that file, you’ll see that the data is now encrypted.

      If you want to view the variable file without changing its contents, you can use the view command:

      • ansible-vault view group_vars/production.yml

      You will be prompted to provide the same password you defined when encrypting that file with ansible-vault. After providing the password, the file’s contents will appear in your terminal. To exit the file view, type q.

      To edit a file that was previously encrypted with Ansible Vault, use the edit vault command:

      • ansible-vault edit group_vars/production.yml

      This command will prompt you to provide the vault password for that file. Your default terminal editor will then be used to open the file for editing. After making the desired changes, save and close the file, and it will be automatically encrypted again by Ansible Vault.

      You have now finished setting up your variable files. In the next step, we’ll run the playbook to set up Nginx, PHP-FPM, and MariaDB (which, along with a Linux-based operating system like Ubuntu, form the LEMP stack) on your remote server(s).

      Step 4 — Running the LEMP Playbook

      Before deploying the demo Laravel app to the remote server(s), we need to set up a LEMP environment that will serve the application. The server-setup.yml playbook includes the Ansible roles necessary to set this up. To inspect its contents, run:

      ansible-laravel-demo/server-setup.yml

      ---
      - hosts: all
        become: true
        roles:
          - { role: setup, tags: ['setup'] }
      
          - { role: mariadb, tags: ['mysql', 'mariadb', 'db', 'lemp'] }
      
          - { role: php, tags: ['php', 'web', 'php-fpm', 'lemp'] }
      
          - { role: nginx, tags: ['nginx', 'web', 'http', 'lemp'] }
      
          - { role: composer, tags: ['composer'] }
      

      Here’s an overview of all the roles included within this playbook:

      • setup: Contains the tasks necessary to create a new system user and grant them sudo privileges, as well as enabling the ufw firewall.
      • mariadb: Installs the MariaDB database server and creates the application database and user.
      • php: Installs php-fpm and PHP modules that are necessary in order to run a Laravel application.
      • nginx: Installs the Nginx web server and enables access on port 80.
      • composer: Installs Composer globally.

      Notice that we’ve set up a few tags within each role. This is to facilitate re-running only parts of this playbook, if necessary. If you make changes to your Nginx template file, for instance, you might want to run only the Nginx role.

      The following command will execute this playbook on all servers from your inventory file. The --ask-vault-pass is only necessary in case you have used ansible-vault to encrypt variable files in the previous step:

      • ansible-playbook -i hosts server-setup.yml -u root --ask-vault-pass

      You’ll get output similar to this:

      Output

      PLAY [all] ********************************************************************************************** TASK [Gathering Facts] ********************************************************************************** ok: [203.0.113.0.101] ok: [203.0.113.0.102] TASK [setup : Install Prerequisites] ******************************************************************** changed: [203.0.113.0.101] changed: [203.0.113.0.102] ... RUNNING HANDLER [nginx : Reload Nginx] ****************************************************************** changed: [203.0.113.0.101] changed: [203.0.113.0.102] PLAY RECAP ********************************************************************************************** 203.0.113.0.101 : ok=31 changed=27 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1 203.0.113.0.102 : ok=31 changed=27 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1

      Your node(s) are now ready to serve PHP applications using Nginx+PHP-FPM, with MariaDB as database server. In the next step, we’ll deploy the included demo Laravel app with the laravel-deploy.yml Ansible playbook.

      Step 5 — Deploying the Laravel Application

      Now that you have a working LEMP environment on your remote server(s), you can execute the laravel-deploy.yml playbook. This playbook will execute the following tasks:

      1. Create the application document root on the remote server, if it hasn’t already been created.
      2. Synchronize the local application folder to the remote server using the sync module.
      3. Use the acl module to set permissions for the www-data user on the storage folder.
      4. Set up the .env application file based on the laravel-env.j2 template.
      5. Install application dependencies with Composer.
      6. Generate application security key.
      7. Set up a public link for the storage folder.
      8. Run database migrations and seeders.

      This playbook should be executed by a non-root user with sudo permissions. This user should have been created when you executed the server-setup.yml playbook in the previous step, using the name defined by the remote_user variable.

      When you’re ready, run the laravel-deploy.yml playbook with:

      • ansible-playbook -i hosts laravel-deploy.yml -u sammy --ask-vault-pass

      The --ask-vault-pass is only necessary in case you have used ansible-vault to encrypt variable files in the previous step.

      You’ll get output similar to this:

      Output

      PLAY [all] ********************************************************************************************** TASK [Gathering Facts] ********************************************************************************** ok: [203.0.113.0.101] ok: [203.0.113.0.102] TASK [Make sure the remote app root exists and has the right permissions] ******************************* ok: [203.0.113.0.101] ok: [203.0.113.0.102] TASK [Rsync application files to the remote server] ***************************************************** ok: [203.0.113.0.101] ok: [203.0.113.0.102] ... TASK [Run Migrations + Seeders] ************************************************************************* ok: [203.0.113.0.101] ok: [203.0.113.0.102] PLAY RECAP ********************************************************************************************** 203.0.113.0.101 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 203.0.113.0.102 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

      When the execution is finished, you can access the demo application by pointing your browser to your node’s domain name or IP address:

      http://node_domain_or_IP
      

      You will see a page like this:

      Laravel Travellist Demo

      Conclusion

      This tutorial demonstrates how to set up an Ansible inventory file and connect to remote nodes, and how to run Ansible playbooks to set up a LEMP server and deploy a Laravel demo application to it. This guide compliments the Automating Server Setup with Ansible Workshop Kit’s slides and speaker notes, and is accompanied by a demo GitHub repository containing all necessary files to follow up with the demo component of this workshop.



      Source link

      Como usar o Ansible para instalar e configurar o LAMP no Ubuntu 18.04


      Introdução

      Atualmente, a automação de servidores desempenha um papel fundamental na administração dos sistemas, devido à natureza descartável dos ambientes dos aplicativos modernos. Normalmente, as ferramentas de gerenciamento de configuração como o Ansible são usadas para simplificar o processo de automação da configuração dos servidores, estabelecendo procedimentos padrão para novos servidores, ao mesmo tempo que reduzindo os erros humanos associados à configuração manual.

      A ferramenta Ansible oferece uma arquitetura simples que não exige a instalação de softwares especiais nos diferentes nós. Ela também fornece um conjunto robusto de recursos e módulos integrados que facilitam a escrita de scripts de automação.

      Este guia explica como usar o Ansible para automatizar os passos contidos em nosso guia sobre Como instalar o Linux, Apache, MySQL e PHP (LAMP) no Ubuntu 18.04. Uma pilha “LAMP” é um grupo de softwares de código aberto que são normalmente instalados juntos para permitir que um servidor hospede sites dinâmicos e aplicativos Web. Este termo é, na verdade, uma sigla que representa o sistema operacional Linux, com o servidor Web do Apache. Os dados do site são armazenados em uma base de dados MySQL e o conteúdo dinâmico é processado pelo PHP.

      Pré-requisitos

      Para executar a configuração automatizada fornecida pelo playbook que estamos discutindo neste guia, você vai precisar de:

      Antes de prosseguir, primeiramente, você precisa garantir que seu nó de controle do Ansible consegue se conectar ao seu(s) host(s) do Ansible e executar comandos nele(s). Para obter um teste de conexão, verifique o passo 3 sobre Como instalar e configurar o Ansible no Ubuntu 18.04.

      O que esse playbook faz?

      O playbook, ou guia do Ansible traz um modo alternativo de executar manualmente o procedimento mostrado em nosso guia sobre Como instalar o Linux, Apache, MySQL e PHP (LAMP) no Ubuntu 18.04.

      A execução desse playbook realizará as ações a seguir nos seus hosts do Ansible:

      1. Instale o aptitude – preferido pelo Ansible como uma alternativa ao gerenciador de pacotes apt.
      2. Instale os pacotes LAMP necessários.
      3. Crie um novo VirtualHost do Apache e configure um diretório base, dedicado a esse host.
      4. Habilite o novo VirtualHost.
      5. Desabilite o site padrão do Apache, quando a variável *disable_default *estiver definida como true.
      6. Defina a senha para o usuário raiz do MySQL.
      7. Remova as contas anônimas do MySQL e do banco de dados de teste.
      8. Configure a ferramenta UFW para permitir tráfego do HTTP na porta configurada (80 por padrão).
      9. Defina um script de teste para PHP, usando o template fornecido.

      Assim que o playbook terminar de executar, você terá um ambiente PHP para Web em execução no Apache, com base nas opções que você definiu dentro das suas variáveis de configuração.

      Como usar este playbook

      A primeira coisa que precisamos fazer é obter o playbook do LAMP e suas dependências a partir do repositório do-community/ansible-playbooks. Precisamos clonar esse repositório em uma pasta local, dentro do Nó de controle do Ansible.

      Se você tiver clonado esse repositório anteriormente, enquanto seguia um outro guia, acesse a cópia do ansible-playbooks existente e execute um comando git pull para assegurar que você tem o conteúdo atualizado:

      • cd ~/ansible-playbooks
      • git pull

      Se esta é a primeira vez que você usa o repositório do-community/ansible-playbooks, você deve começar clonando o repositório para sua pasta inicial com:

      • cd ~
      • git clone https://github.com/do-community/ansible-playbooks.git
      • cd ansible-playbooks

      Os arquivos nos quais estamos interessados estão localizados dentro da pasta lamp_ubuntu1804, que tem a seguinte estrutura:

      lamp_ubuntu1804
      ├── files
      │   ├── apache.conf.j2
      │   └── info.php.j2
      ├── vars
      │   └── default.yml
      ├── playbook.yml
      └── readme.md
      

      A seguir, apresentamos a finalidade de cada um desses arquivos:

      • files/info.php.j2: arquivo de modelo para configurar uma página de teste PHP na raiz do servidor Web.
      • files/apache.conf.j2: arquivo de modelo para configurar o VirtualHost do Apache.
      • vars/default.yml: arquivo de variável para a personalizar as configurações do playbook.
      • playbook.yml: o arquivo do playbook, que contém as tarefas a serem executadas no(s) servidor(es) remoto(s).
      • readme.md: um arquivo de texto que contém informações sobre esse playbook.

      Vamos editar o arquivo de variáveis do playbook para personalizar as configurações tanto do MySQL quanto do Apache. Acesse o diretório lamp_ubuntu1804 e abra o arquivo vars/default.yml usando o editor de linha de comando de sua escolha:

      • cd lamp_ubuntu1804
      • nano vars/default.yml

      Este arquivo contém algumas variáveis que requerem sua atenção:

      vars/default.yml

      ---
      mysql_root_password: "mysql_root_password"
      app_user: "sammy"
      http_host: "your_domain"
      http_conf: "your_domain.conf"
      http_port: "80"
      disable_default: true
      

      A lista a seguir contém uma explicação breve de cada uma dessas variáveis e como você poderia querer modificá-las:

      • mysql_root_password: a senha desejada para a conta raiz do MySQL.
      • app_user: um usuário não raiz remoto no host do Ansible que será definido como o proprietário dos arquivos do aplicativo.
      • http_host: seu nome de domínio.
      • http_conf: o nome do arquivo de configuração que será criado dentro do Apache.
      • http_port: porta para o HTTP desse host virtual, a qual, por padrão, é a porta 80.
      • disable_default: escolher desativar ou não o site padrão que vem com o Apache.

      Assim que terminar de atualizar as variáveis dentro de vars/default.yml, salve e feche este arquivo. Se tiver usado o nano, para salvar e fechar o arquivo, pressione as teclas CTRL+X, Y e, depois, ENTER.

      Agora, você está pronto para executar este playbook em um ou mais servidores. Por padrão, a maioria dos playbooks vem configurada para execução em cada servidor de seu inventário. Podemos usar o sinalizador -l para garantir que apenas um subconjunto de servidores, ou um único servidor seja afetado pelo playbook. Também podemos usar o sinalizador -u para especificar qual usuário no servidor remoto estamos usando para nos conectar e executar os comandos do playbook nos hosts remotos.

      Para executar o playbook apenas no server1, conectando-se como sammy, utilize o seguinte comando:

      • ansible-playbook playbook.yml -l server1 -u sammy

      Você receberá um resultado similar a este:

      Output

      PLAY [all] ********************************************************************************************************* TASK [Gathering Facts] *********************************************************************************************************ok: [server1] TASK [Install prerequisites] *********************************************************************************************************ok: [server1] => (item=aptitude) ... TASK [UFW - Allow HTTP on port 80] ********************************************************************************************************* changed: [server1] TASK [Sets Up PHP Info Page] ********************************************************************************************************* changed: [server1] RUNNING HANDLER [Reload Apache] ********************************************************************************************************* changed: [server1] RUNNING HANDLER [Restart Apache] ********************************************************************************************************* changed: [server1] PLAY RECAP ********************************************************************************************************* server1 : ok=15 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

      Nota: para obter mais informações sobre como executar os playbooks do Ansible, consulte nosso Guia de folha de referências do Ansible.

      Quando o playbook terminar de executar, vá até seu navegador Web e acesse o endereço IP ou host do servidor, como configurado nas variáveis do playbook, seguido por /info.php:

      http://server_host_or_IP/info.php
      

      Você verá uma página como esta:

      Página do phpinfo

      Como essa página contém informações confidenciais sobre seu ambiente PHP, é recomendável que você a remova do servidor, executando um comando rm -f/var/www/info.php assim que terminar de configurá-lo.

      O conteúdo do playbook

      Você pode encontrar a configuração do servidor LAMP apresentada neste tutorial na pasta lamp_ubuntu1804, dentro do repositório de Playbooks da Comunidade DigitalOcean. Para copiar ou baixar o conteúdo do script diretamente, clique no botão Raw perto do topo de cada script.

      Para sua conveniência, incluimos aqui o conteúdo completo do playbook, bem como os respectivos arquivos associados.

      vars/default.yml

      O arquivo de variável default.yml contém os valores que serão usados dentro das tarefas do playbook, tais como a senha para a conta root do MySQL e o nome de domínio a configurar dentro do Apache.

      vars/default.yml

      ---
      mysql_root_password: "mysql_root_password"
      app_user: "sammy"
      http_host: "your_domain"
      http_conf: "your_domain.conf"
      http_port: "80"
      disable_default: true
      

      files/apache.conf.j2

      O arquivo apache.conf.j2 é um arquivo modelo de Jinja 2, que configura um novo VirtualHost do Apache. As variáveis usadas dentro desse modelo estão definidas no arquivo da variável vars/default.yml.

      files/apache.conf.j2

      <VirtualHost *:{{ http_port }}>
         ServerAdmin webmaster@localhost
         ServerName {{ http_host }}
         ServerAlias www.{{ http_host }}
         DocumentRoot /var/www/{{ http_host }}
         ErrorLog ${APACHE_LOG_DIR}/error.log
         CustomLog ${APACHE_LOG_DIR}/access.log combined
      
         <Directory /var/www/{{ http_host }}>
               Options -Indexes
         </Directory>
      
         <IfModule mod_dir.c>
             DirectoryIndex index.php index.html index.cgi index.pl  index.xhtml index.htm
         </IfModule>
      
      </VirtualHost>
      

      files/info.php.j2

      O arquivo info.php.j2 é outro modelo de Jinja, usado para configurar um script PHP de teste no diretório base do servidor LAMP recém-configurado.

      files/info.php.j2

      <?php
      phpinfo();
      
      

      playbook.yml

      O arquivo playbook.yml é onde fica a definição de todas as tarefas dessa configuração. Ele começa definindo o grupo de servidores que deverá ser objeto dessa configuração (all). Depois, ele usa become: true para definir que as tarefas deverão ser executadas – com elevação de privilégio (sudo), por padrão. Em seguida, ele inclui o arquivo de variável vars/default.yml para carregar as opções de configuração.

      playbook.yml

      
      ---
      - hosts: all
        become: true
        vars_files:
          - vars/default.yml
      
        tasks:
          - name: Install prerequisites
            apt: name={{ item }} update_cache=yes state=latest force_apt_get=yes
            loop: [ 'aptitude' ]
      
        #Apache Configuration
          - name: Install LAMP Packages
            apt: name={{ item }} update_cache=yes state=latest
            loop: [ 'apache2', 'mysql-server', 'python3-pymysql', 'php', 'php-mysql', 'libapache2-mod-php' ]
      
          - name: Create document root
            file:
              path: "/var/www/{{ http_host }}"
              state: directory
              owner: "{{ app_user }}"
              mode: '0755'
      
          - name: Set up Apache virtualhost
            template:
              src: "files/apache.conf.j2"
              dest: "/etc/apache2/sites-available/{{ http_conf }}"
            notify: Reload Apache
      
          - name: Enable new site
            shell: /usr/sbin/a2ensite {{ http_conf }}
            notify: Reload Apache
      
          - name: Disable default Apache site
            shell: /usr/sbin/a2dissite 000-default.conf
            when: disable_default
            notify: Reload Apache
      
        # MySQL Configuration
          - name: Sets the root password
            mysql_user:
              name: root
              password: "{{ mysql_root_password }}"
              login_unix_socket: /var/run/mysqld/mysqld.sock
      
          - name: Removes all anonymous user accounts
            mysql_user:
              name: ''
              host_all: yes
              state: absent
              login_user: root
              login_password: "{{ mysql_root_password }}"
      
          - name: Removes the MySQL test database
            mysql_db:
              name: test
              state: absent
              login_user: root
              login_password: "{{ mysql_root_password }}"
      
        # UFW Configuration
          - name: "UFW - Allow HTTP on port {{ http_port }}"
            ufw:
              rule: allow
              port: "{{ http_port }}"
              proto: tcp
      
        # PHP Info Page
          - name: Sets Up PHP Info Page
            template:
              src: "files/info.php.j2"
              dest: "/var/www/{{ http_host }}/info.php"
      
        handlers:
          - name: Reload Apache
            service:
              name: apache2
              state: reloaded
      
          - name: Restart Apache
            service:
              name: apache2
              state: restarted
      

      Sinta-se à vontade para modificar esses arquivos para melhor adequar-se às suas necessidades específicas, em seu próprio fluxo de trabalho.

      Conclusão

      Neste guia, usamos o Ansible para automatizar o processo de instalação e configuração de um ambiente LAMP em um servidor remoto. Considerando que, normalmente, as pesssoas têm necessidades diferentes ao trabalhar com bancos de dados e usuários do MySQL, recomendamos que você examine a documentação oficial do Ansible para obter mais informações e casos de uso do módulo mysql_user do Ansible.

      Se quiser incluir outras tarefas nesse playbook para personalizar ainda mais a configuração do seu servidor, consulte nosso guia de introdução ao Ansible, Gerenciamento de configuração 101: Como escrever playbooks no Ansible.



      Source link

      Como usar o Ansible para instalar e configurar o WordPress com LAMP no Ubuntu 18.04


      Introdução

      Atualmente, a automação de servidores desempenha um papel fundamental na administração dos sistemas, devido à natureza descartável dos ambientes dos aplicativos modernos. Normalmente, as ferramentas de gerenciamento de configuração como o Ansible são usadas para simplificar o processo de automação da configuração dos servidores, estabelecendo procedimentos padrão para novos servidores, ao mesmo tempo que reduzindo os erros humanos associados à configuração manual.

      A ferramenta Ansible oferece uma arquitetura simples que não exige a instalação de softwares especiais nos diferentes nós. Ela também fornece um conjunto robusto de recursos e módulos integrados que facilitam a escrita de scripts de automação.

      Este guia explica como usar o Ansible para automatizar os passos contidos em nosso guia sobre Como instalar o WordPress com LAMP no Ubuntu 18.04. O WordPress é o CMS (sistema de gerenciamento de conteúdo) mais popular na internet, permitindo que os usuários configurem blogs e sites flexíveis, além de um back-end do MySQL com processamento em PHP. Após a configuração, quase toda a administração pode ser feita através do front-end da Web.

      Pré-requisitos

      Para executar a configuração automatizada fornecida pelo playbook que estamos discutindo neste guia, você vai precisar de:

      Antes de prosseguir, primeiramente, você precisa garantir que seu nó de controle do Ansible consegue se conectar ao seu(s) host(s) do Ansible e executar comandos nele(s). Para obter um teste de conexão, verifique o passo 3 sobre Como instalar e configurar o Ansible no Ubuntu 18.04.

      O que esse playbook faz?

      O playbook, ou guia do Ansible traz um modo alternativo de executar manualmente o procedimento mostrado em nosso guia sobre Como instalar o WordPress com LAMP no Ubuntu 18.04.

      A execução desse playbook realizará as ações a seguir nos seus hosts do Ansible:

      1. Instalar o aptitude – preferido pelo Ansible como uma alternativa ao gerenciador de pacotes apt.
      2. Instalar os pacotes LAMP e extensões PHP necessários.
      3. Criar e habilitar um novo VirtualHost Apache para o site do WordPress.
      4. Habilitar o módulo de reescrita do Apache (mod_rewrite).
      5. Desabilitar o site padrão do Apache.
      6. Definir a senha para o usuário** raiz** do MySQL.
      7. Remover as contas anônimas do MySQL e do banco de dados de teste.
      8. Criar um novo banco de dados e usuário do MySQL para o site do WordPress.
      9. Configurar a ferramenta UFW para permitir tráfego do HTTP na porta configurada (80 por padrão).
      10. Fazer download e descompactar o WordPress.
      11. Configurar a propriedade e as permissões corretas do diretório.
      12. Definir o arquivo wp-config.php usando o modelo fornecido.

      Assim que o playbook terminar de executar, você terá uma instalação do WordPress em execução num ambiente LAMP, com base nas opções que você definiu dentro das suas variáveis de configuração.

      Como usar este playbook

      A primeira coisa que precisamos fazer é obter o playbook do WordPress em LAMP e suas dependências a partir do repositório do-community/ansible-playbooks. Precisamos clonar esse repositório em uma pasta local, dentro do Nó de controle do Ansible.

      Se você tiver clonado esse repositório anteriormente, enquanto seguia um outro guia, acesse a cópia do ansible-playbooks existente e execute um comando git pull para assegurar que você tem o conteúdo atualizado:

      • cd ~/ansible-playbooks
      • git pull

      Se esta é a primeira vez que você usa o repositório do-community/ansible-playbooks, você deve começar clonando o repositório para sua pasta inicial com:

      • cd ~
      • git clone https://github.com/do-community/ansible-playbooks.git
      • cd ansible-playbooks

      Os arquivos nos quais estamos interessados ficam na pasta wordpress-lamp_ubuntu1804, a qual tem a seguinte estrutura:

      wordpress-lamp_ubuntu1804
      ├── files
      │   ├── apache.conf.j2
      │   └── wp-config.php.j2
      ├── vars
      │   └── default.yml
      ├── playbook.yml
      └── readme.md
      

      A seguir, apresentamos a finalidade de cada um desses arquivos:

      • files/apache.conf.j2: arquivo de modelo para configurar o VirtualHost do Apache.
      • files/wp-config.php.j2: arquivo de modelo para configurar o arquivo de configuração do WordPress.
      • vars/default.yml: arquivo de variável para a personalizar as configurações do playbook.
      • playbook.yml: o arquivo do playbook, que contém as tarefas a serem executadas no(s) servidor(es) remoto(s).
      • readme.md: um arquivo de texto que contém informações sobre esse playbook.

      Vamos editar o arquivo da variável do playbook para personalizar suas opções. Acesse o diretório wordpress-lamp_ubuntu1804 e abra o arquivo vars/default.yml usando o editor de linha de comando de sua escolha:

      • cd wordpress-lamp_ubuntu1804
      • nano vars/default.yml

      Este arquivo contém algumas variáveis que requerem sua atenção:

      vars/default.yml

      ---
      #System Settings
      php_modules: [ 'php-curl', 'php-gd', 'php-mbstring', 'php-xml', 'php-xmlrpc', 'php-soap', 'php-intl', 'php-zip' ]
      
      #MySQL Settings
      mysql_root_password: "mysql_root_password"
      mysql_db: "wordpress"
      mysql_user: "sammy"
      mysql_password: "password"
      
      #HTTP Settings
      http_host: "your_domain"
      http_conf: "your_domain.conf"
      http_port: "80"
      

      A lista a seguir contém uma explicação breve de cada uma dessas variáveis e como você poderia querer modificá-las:

      • php_modules: uma matriz que contém extensões do PHP que devem ser instaladas para auxiliar sua configuração do WordPress. Você não precisa alterar essa variável, mas pode querer incluir novas extensões na lista caso sua configuração específica necessite.
      • mysql_root_password: a senha desejada para a conta root do MySQL.
      • mysql_db: o nome do banco de dados do MySQL que deverá ser criado para o WordPress.
      • mysql_user: o nome do usuário do MySQL que deverá ser criado para o WordPress.
      • mysql_password: a senha para o novo usuário do MySQL.
      • http_host: seu nome de domínio.
      • http_conf: o nome do arquivo de configuração que será criado dentro do Apache.
      • http_port: porta para o HTTP desse host virtual, a qual, por padrão, é a porta 80.

      Assim que terminar de atualizar as variáveis dentro de vars/default.yml, salve e feche este arquivo. Se tiver usado o nano, para salvar e fechar o arquivo, pressione as teclas CTRL+X, Y e, depois, ENTER.

      Agora, você está pronto para executar este playbook em um ou mais servidores. Por padrão, a maioria dos playbooks vem configurada para execução em cada servidor de seu inventário. Podemos usar o sinalizador -l para garantir que apenas um subconjunto de servidores, ou um único servidor seja afetado pelo playbook. Também podemos usar o sinalizador -u para especificar qual usuário no servidor remoto estamos usando para nos conectar e executar os comandos do playbook nos hosts remotos.

      Para executar o playbook apenas no server1, conectando-se como sammy, utilize o seguinte comando:

      • ansible-playbook playbook.yml -l server1 -u sammy

      Você receberá um resultado similar a este:

      Output

      PLAY [all] ***************************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** ok: [server1] TASK [Install prerequisites] *********************************************************************************************************** ok: [server1] … TASK [Download and unpack latest WordPress] ******************************************************************************************** changed: [server1] TASK [Set ownership] ******************************************************************************************************************* changed: [server1] TASK [Set permissions for directories] ************************************************************************************************* changed: [server1] TASK [Set permissions for files] ******************************************************************************************************* changed: [server1] TASK [Set up wp-config] **************************************************************************************************************** changed: [server1] RUNNING HANDLER [Reload Apache] ******************************************************************************************************** changed: [server1] RUNNING HANDLER [Restart Apache] ******************************************************************************************************* changed: [server1] PLAY RECAP ***************************************************************************************************************************** server1 : ok=22 changed=18 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

      Nota: para obter mais informações sobre como executar os playbooks do Ansible, consulte nosso Guia de folha de referências do Ansible.

      Quando o playbook terminar de executar, vá até seu navegador Web para concluir a instalação do WordPress a partir de lá.

      Navegue até o nome de domínio do seu servidor ou endereço IP público:

      http://server_host_or_IP
      

      Você verá uma página como esta:

      Página de seleção de idiomas do WordPress

      Após selecionar o idioma que deseja usar para sua instalação do WordPress, será mostrado um passo final para configurar seu usuário e senha do WordPress, de modo que você possa fazer logon em seu painel de controle:

      Configuração do WordPress

      Quando clicar adiante, você será levado para uma página que solicita que você faça login:

      Prompt de login do WP

      Assim que fizer login, será levado para o painel de administração do WordPress:

      Painel de admin do WP

      Alguns passos comuns seguintes – para personalizar sua instalação do WordPress – incluem escolher a configuração de permalinks para os seus posts (pode ser encontrado em Settings > Permalinks) e selecionar um novo tema (em Appearance > Themes).

      O conteúdo do playbook

      Você pode encontrar a configuração do WordPress no servidor LAMP, apresentada neste tutorial, na pasta wordpress-lamp_ubuntu1804, no repositório de Playbooks da Comunidade DigitalOcean. Para copiar ou baixar o conteúdo do script diretamente, clique no botão Raw perto do topo de cada script.

      Para sua conveniência, incluimos aqui o conteúdo completo do playbook, bem como os respectivos arquivos associados.

      vars/default.yml

      O arquivo de variável default.yml contém os valores que serão usados dentro das tarefas do playbook, como as configurações do banco de dados e do nome de domínio para configurar no Apache.

      vars/default.yml

      #System Settings
      php_modules: [ 'php-curl', 'php-gd', 'php-mbstring', 'php-xml', 'php-xmlrpc', 'php-soap', 'php-intl', 'php-zip' ]
      
      #MySQL Settings
      mysql_root_password: "mysql_root_password"
      mysql_db: "wordpress"
      mysql_user: "sammy"
      mysql_password: "password"
      
      #HTTP Settings
      http_host: "your_domain"
      http_conf: "your_domain.conf"
      http_port: "80"
      

      files/apache.conf.j2

      O arquivo apache.conf.j2 é um arquivo modelo de Jinja 2, que configura um novo VirtualHost do Apache. As variáveis usadas dentro desse modelo estão definidas no arquivo da variável vars/default.yml.

      files/apache.conf.j2

      <VirtualHost *:{{ http_port }}>
         ServerAdmin webmaster@localhost
         ServerName {{ http_host }}
         ServerAlias www.{{ http_host }}
         DocumentRoot /var/www/{{ http_host }}
         ErrorLog ${APACHE_LOG_DIR}/error.log
         CustomLog ${APACHE_LOG_DIR}/access.log combined
      
         <Directory /var/www/{{ http_host }}>
               Options -Indexes
         </Directory>
      
         <IfModule mod_dir.c>
             DirectoryIndex index.php index.html index.cgi index.pl  index.xhtml index.htm
         </IfModule>
      
      </VirtualHost>
      

      files/wp-config.php.j2

      O arquivo wp-config.php.j2 é outro modelo do Jinja, usado para configurar o arquivo de configuração principal usado pelo WordPress. As variáveis usadas dentro desse modelo estão definidas no arquivo da variável vars/default.yml. Chaves de autenticação e salts são gerados usando uma função hash.

      files/info.php.j2

      <?php
      /**
       * The base configuration for WordPress
       *
       * The wp-config.php creation script uses this file during the
       * installation. You don't have to use the web site, you can
       * copy this file to "wp-config.php" and fill in the values.
       *
       * This file contains the following configurations:
       *
       * * MySQL settings
       * * Secret keys
       * * Database table prefix
       * * ABSPATH
       *
       * @link https://codex.wordpress.org/Editing_wp-config.php
       *
       * @package WordPress
       */
      
      // ** MySQL settings - You can get this info from your web host ** //
      /** The name of the database for WordPress */
      define( 'DB_NAME', '{{ mysql_db }}' );
      
      /** MySQL database username */
      define( 'DB_USER', '{{ mysql_user }}' );
      
      /** MySQL database password */
      define( 'DB_PASSWORD', '{{ mysql_password }}' );
      
      /** MySQL hostname */
      define( 'DB_HOST', 'localhost' );
      
      /** Database Charset to use in creating database tables. */
      define( 'DB_CHARSET', 'utf8' );
      
      /** The Database Collate type. Don't change this if in doubt. */
      define( 'DB_COLLATE', '' );
      
      /** Filesystem access **/
      define('FS_METHOD', 'direct');
      
      /**#@+
       * Authentication Unique Keys and Salts.
       *
       * Change these to different unique phrases!
       * You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
       * You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
       *
       * @since 2.6.0
       */
      define( 'AUTH_KEY',         '{{ ansible_date_time.iso8601_micro | hash('sha256') }}' );
      define( 'SECURE_AUTH_KEY',  '{{ ansible_date_time.iso8601_micro | hash('sha256') }}' );
      define( 'LOGGED_IN_KEY',    '{{ ansible_date_time.iso8601_micro | hash('sha256') }}' );
      define( 'NONCE_KEY',        '{{ ansible_date_time.iso8601_micro | hash('sha256') }}' );
      define( 'AUTH_SALT',        '{{ ansible_date_time.iso8601_micro | hash('sha256') }}' );
      define( 'SECURE_AUTH_SALT', '{{ ansible_date_time.iso8601_micro | hash('sha256') }}' );
      define( 'LOGGED_IN_SALT',   '{{ ansible_date_time.iso8601_micro | hash('sha256') }}' );
      define( 'NONCE_SALT',       '{{ ansible_date_time.iso8601_micro | hash('sha256') }}' );
      
      /**#@-*/
      
      /**
       * WordPress Database Table prefix.
       *
       * You can have multiple installations in one database if you give each
       * a unique prefix. Only numbers, letters, and underscores please!
       */
      $table_prefix = 'wp_';
      
      /**
       * For developers: WordPress debugging mode.
       *
       * Change this to true to enable the display of notices during development.
       * It is strongly recommended that plugin and theme developers use WP_DEBUG
       * in their development environments.
       *
       * For information on other constants that can be used for debugging,
       * visit the Codex.
       *
       * @link https://codex.wordpress.org/Debugging_in_WordPress
       */
      define( 'WP_DEBUG', false );
      
      /* That's all, stop editing! Happy publishing. */
      
      /** Absolute path to the WordPress directory. */
      if ( ! defined( 'ABSPATH' ) ) {
          define( 'ABSPATH', dirname( __FILE__ ) . '/' );
      }
      
      /** Sets up WordPress vars and included files. */
      require_once( ABSPATH . 'wp-settings.php' );
      
      

      playbook.yml

      O arquivo playbook.yml é onde fica a definição de todas as tarefas dessa configuração. Ele começa definindo o grupo de servidores que deverá ser objeto dessa configuração (all). Depois, ele usa become: true para definir que as tarefas deverão ser executadas – com elevação de privilégio (sudo), por padrão. Em seguida, ele inclui o arquivo de variável vars/default.yml para carregar as opções de configuração.

      playbook.yml

      ---
      - hosts: all
        become: true
        vars_files:
          - vars/default.yml
      
        tasks:
          - name: Install prerequisites
            apt: name=aptitude update_cache=yes state=latest force_apt_get=yes
            tags: [ system ]
      
          - name: Install LAMP Packages
            apt: name={{ item }} update_cache=yes state=latest
            loop: [ 'apache2', 'mysql-server', 'python3-pymysql', 'php', 'php-mysql', 'libapache2-mod-php' ]
            tags: [ system ]
      
          - name: Install PHP Extensions
            apt: name={{ item }} update_cache=yes state=latest
            loop: "{{ php_modules }}"
            tags: [ system ]
      
        # Apache Configuration
          - name: Create document root
            file:
              path: "/var/www/{{ http_host }}"
              state: directory
              owner: "www-data"
              group: "www-data"
              mode: '0755'
            tags: [ apache ]
      
          - name: Set up Apache VirtualHost
            template:
              src: "files/apache.conf.j2"
              dest: "/etc/apache2/sites-available/{{ http_conf }}"
            notify: Reload Apache
            tags: [ apache ]
      
          - name: Enable rewrite module
            shell: /usr/sbin/a2enmod rewrite
            notify: Reload Apache
            tags: [ apache ]
      
          - name: Enable new site
            shell: /usr/sbin/a2ensite {{ http_conf }}
            notify: Reload Apache
            tags: [ apache ]
      
          - name: Disable default Apache site
            shell: /usr/sbin/a2dissite 000-default.conf
            notify: Restart Apache
            tags: [ apache ]
      
        # MySQL Configuration
          - name: Set the root password
            mysql_user:
              name: root
              password: "{{ mysql_root_password }}"
              login_unix_socket: /var/run/mysqld/mysqld.sock
            tags: [ mysql, mysql-root ]
      
          - name: Remove all anonymous user accounts
            mysql_user:
              name: ''
              host_all: yes
              state: absent
              login_user: root
              login_password: "{{ mysql_root_password }}"
            tags: [ mysql ]
      
          - name: Remove the MySQL test database
            mysql_db:
              name: test
              state: absent
              login_user: root
              login_password: "{{ mysql_root_password }}"
            tags: [ mysql ]
      
          - name: Creates database for WordPress
            mysql_db:
              name: "{{ mysql_db }}"
              state: present
              login_user: root
              login_password: "{{ mysql_root_password }}"
            tags: [ mysql ]
      
          - name: Create MySQL user for WordPress
            mysql_user:
              name: "{{ mysql_user }}"
              password: "{{ mysql_password }}"
              priv: "{{ mysql_db }}.*:ALL"
              state: present
              login_user: root
              login_password: "{{ mysql_root_password }}"
            tags: [ mysql ]
      
        # UFW Configuration
          - name: "UFW - Allow HTTP on port {{ http_port }}"
            ufw:
              rule: allow
              port: "{{ http_port }}"
              proto: tcp
            tags: [ system ]
      
        # WordPress Configuration
          - name: Download and unpack latest WordPress
            unarchive:
              src: https://wordpress.org/latest.tar.gz
              dest: "/var/www/{{ http_host }}"
              remote_src: yes
              creates: "/var/www/{{ http_host }}/wordpress"
            tags: [ wordpress ]
      
          - name: Set ownership
            file:
              path: "/var/www/{{ http_host }}"
              state: directory
              recurse: yes
              owner: www-data
              group: www-data
            tags: [ wordpress ]
      
          - name: Set permissions for directories
            shell: "/usr/bin/find /var/www/{{ http_host }}/wordpress/ -type d -exec chmod 750 {} \;"
            tags: [ wordpress ]
      
          - name: Set permissions for files
            shell: "/usr/bin/find /var/www/{{ http_host }}/wordpress/ -type f -exec chmod 640 {} \;"
            tags: [ wordpress ]
      
          - name: Set up wp-config
            template:
              src: "files/wp-config.php.j2"
              dest: "/var/www/{{ http_host }}/wordpress/wp-config.php"
            tags: [ wordpress ]
      
        handlers:
          - name: Reload Apache
            service:
              name: apache2
              state: reloaded
      
          - name: Restart Apache
            service:
              name: apache2
              state: restarted
      

      Sinta-se à vontade para modificar esses arquivos para melhor adequar-se às suas necessidades específicas, em seu próprio fluxo de trabalho.

      Conclusão

      Neste guia, usamos o Ansible para automatizar o processo de instalação e configuração de um site do WordPress com LAMP em um servidor Ubuntu 18.04.

      Se quiser incluir outras tarefas nesse playbook para personalizar ainda mais a configuração do seu servidor, consulte nosso guia de introdução ao Ansible, Gerenciamento de configuração 101: Como escrever playbooks no Ansible.



      Source link