====== Ansible básico ====== ===== Lets skim it ===== **Inventory**: fichero en ''/etc'' u otro independiente donde se guarda una lista de hosts a los que ansible va a acceder. **Modulo**: utilidad dentro de Ansible para realizar unas tareas concretas en el host. **Playbook**: conjunto de instrucciones para que Ansible ejecute en el host indicado. Se escriben en YAML. **Tasks**: las instrucciones a ejecutar dentro del playbook. **Ansible Galaxy**: Módulos\playbooks no oficiales. El parámetro ''-b'' (become) hace que se ejecute como root. Para que nos pida la contraseña usaremos el parámetro ''-K''. Para ejecutar pequeños comandos independientes usaremos el comando ''ansible''. Para ejecutar un playbook usaremos ''ansible-playbook''. ''-i hosts.txt'' indica qué fichero de hosts usar. Por defecto Ansible lanzará los comandos en remoto como el usuario que estés en local. Puedes poner archivos de configuración como ''ansible.cfg'' en el directorio donde trabajes, ''.ansible.cfg'' en tu home o ''/etc/ansible/ansible.cfg''. Los **handlers** son notificaciones dentro de un playbook que se ejecutarán si ocurre algo con una task. ===== First steps and terms ===== ==== Install and config ==== sudo apt-get install ansible sudo cp /etc/ansible/hosts /etc/ansible/hosts.org Edit the ''/etc/ansible/hosts.org'' file. Now you can check if the configuration was okay: ansible all -m ping **Note**: Python software should be installed on the target machine. === Connect without pem === ansible all -m ping -u alfred -k * ''-u '' to set the username of the connection. * ''-k'' for asking the password. ==== Use modules ==== Modules let you to launch personalized commands on the target system. [[https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html|Modules list]] Parameters: * ''-b'' (become) to use sudo, for asking for password ''-K''. * ''-m '' exec that module. * ''-a '' '' arguments for the module === apt === Install nginx ansible all -b -K -m apt -a 'pkg=nginx state=installed update_cache=true' Inside a playbook: - name: Install Server Basics apt: pkg={{ item }} state=installed update_cache=true with_items: - acl - unattended-upgrades - policykit-1 - ntp - wget - curl - vim - ack-grep - git - unzip - htop - tmux === copy === - copy: src: "{{ item }}" dest: /etc/fooapp/ owner: root mode: 600 with_fileglob: - /playbooks/files/fooapp/* - name: Your copy task copy: src={{ item.src }} dest={{ item.dest }} with_items: - { src: 'containerizers', dest: '/etc/mesos/containerizers' } - { src: 'another_file', dest: '/etc/somewhere' } - { src: 'dynamic', dest: '{{ var_path }}' } - name: Copy multiple files to multiple directories copy: src={{ item.0 }} dest={{ item.1 }} with_together: - [ 'file1', 'file2', 'file3' ] - [ '/dir1/', '/dir2/', '/dir3/' ] === shell === ansible all -b -m shell -a 'apt-get install nginx' ==== Playbooks ==== You will have a playbook file ''example.yml'', to run it: '' ansible-playbook example.yml -b -K -u alfred -k''. Structure: --- - hosts: tasks: - name: <module>: <parameters> </code> Examples: <code> --- - hosts: local tasks: - name: Install Nginx apt: pkg=nginx state=installed update_cache=true </code> We can use Playbooks to run multiple Tasks, add in variables, define other settings and even include other playbooks. === Handlers === You can add handlers, which are tasks that will be automatically launched when an event occours. You only need to add this section: <code> handlers: - name: <identifier> <module>: <parameters> </code> Notifiers are only run if the Task is run. For example, if I already had Nginx installed, an "Install Nginx" task would not be run and the notifier would not be called. Example: <code> --- - hosts: local tasks: - name: Install Nginx apt: pkg=nginx state=installed update_cache=true notify: - Start Nginx handlers: - name: Start Nginx service: name=nginx state=started </code> === Variables === <code> - hosts: local vars: - docroot: /var/www/serversforhackers.com/public ... - name: Create Web Root when: nginxinstalled|success file: dest={{ '{{' }} docroot {{ '}}' }} mode=775 state=directory owner=www-data group=www-data notify: - Reload Nginx </code> If you are not quoting an argument that starts with a variable will throw an error. For example, <code> vars: age_path: {{vivek.name}}/demo/ {{vivek.name}} </code> The right one would be: <code> vars: age_path: "{{vivek.name}}/demo/" </code> ==== Roles ==== It's like a playbook but divided in several directories and files, each one with a file ''main.yml''. In those files we can add whatever is required into that role: * **files**: For files that we'll want copied into our servers. * **handlers**: For thase Handlers that were once within the Playbook. * **meta**: For dependencies. * **templates**: For Jinja2 templates. * **tasks**: For the tasks list. * **vars**: For a variables list. We need to configure ansible to tell it where to run the roles. For that we will use the file ''/etc/ansible/ansible.cfg''. For example, nginx Role located at ''/vagrant/ansible/roles/nginx'' will be configured as: <code> roles_path = /vagrant/ansible/roles </code> Then, to run it we will create the file ''server.yml'': <code> --- - hosts: all roles: - nginx </code> And to run them: <code> ansible-playbook -b server.yml </code> ==== Facts ==== They are variables that Ansible gather from the target system, such as the number of CPU cores, available ipv4 and ipv6 networks, mounted disks, Linux distribution and more. Facts are often useful in Tasks or Template configurations. For example Nginx is commonly set to use as any worker processors as there are CPU cores. <code> user www-data www-data; worker_processes {{ ansible_processor_cores }}; pid /var/run/nginx.pid; </code> Ansible facts all start with ''ansible_'' and are globally available for use any place variables can be used: Variable files, Tasks, and Templates. To access all the facts: <code> ansible hostname -m setup </code> Then: <code> {{ ansible_facts["eth0"]["ipv4"]["address"] }} </code> OR alternatively: <code> {{ ansible_facts.eth0.ipv4.address }} </code> You can disable facts: <code> - hosts: whatever gather_facts: no </code> ==== Vaults ==== To put encrypted information that will be used on the playbooks. Use Ansible Vault to encrypt secure data, making it more safe if your playbooks or Roles end up in version control or are otherwise shared. ===== Tips and tricks ===== ==== Become ==== <code> --- - hosts: webservers remote_user: yourname become: yes become_user: postgres </code> You can also use other privilege escalation methods, like su: <code> --- - hosts: webservers remote_user: yourname become: yes become_method: su </code> ==== Ansible command ==== === Version === <code> ansible --version </code> === List hosts === <code> ansible --list-hosts all ansible -i development.txt --list-hosts all ansible --list-hosts webserver </code> === Check connection === <code> ansible -m ping all </code> === Send commands === <code> ansible -m command -a "ls" all </code> ==== Environment variables ==== For using local environment variables: <code> - debug: msg="{{ lookup('env','HOME') }} is an environment variable" </code> In this way you can set variables: <code> - hosts: all vars: local_shell: "{{ lookup('env','SHELL') }}" tasks: - debug: msg: "{{ local_shell }}" output ------ "msg": "/bin/sh" </code> === For targetting proxies === <code> - hosts: all remote_user: root tasks: - apt: name=cobbler state=installed environment: http_proxy: http://proxy.example.com:8080 </code> The environment can also be stored in a variable, and accessed like so: <code> - hosts: all remote_user: root # here we make a variable named "proxy_env" that is a dictionary vars: proxy_env: http_proxy: http://proxy.example.com:8080 tasks: - apt: name=cobbler state=installed environment: "{{proxy_env}}" </code> You can also use it at a play level: <code> - hosts: testhost roles: - php - nginx environment: http_proxy: http://proxy.example.com:8080 </code> ==== Passing variables on the command line ==== In addition to vars_prompt and vars_files, it is possible to set variables at the command line using the ''<nowiki>--extra-vars</nowiki>'' (or -e) argument. <code> ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo" </code> It also allows to set in JSON format: <code> ansible-playbook release.yml --extra-vars '{"version":"1.23.45","other_variable":"foo"}' ansible-playbook arcade.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}' </code> Also in YAML string format: <code> ansible-playbook release.yml --extra-vars ' version: "1.23.45" other_variable: foo' ansible-playbook arcade.yml --extra-vars ' pacman: mrs ghosts: - inky - pinky - clyde - sue' </code> Even vars from a JSON or YAML file: <code> ansible-playbook release.yml --extra-vars "@some_file.json" </code> ==== Executing locally ==== ''local_action'' is an alternative way of doing ''delegate_to: localhost''. ==== Troubleshooting ==== Yaml does not support tab based indentation and supports space based indentation, so one needs to be careful about the same. **Use verbose mode** with ansible command parameters ''-v'' ''-vv''. **Use modules 'debug' and 'register'** ==== Notes ==== * [[https://medium.com/@denot/ansible-101-d6dc9f86df0a|Ansible 101]] === Inventory variables === <code> [localhost] 127.0.0.1 variable1=value1 variable2=value2 [webservers] webserver.awesomecompany.ly ansible_ssh_host=55.44.33.22 </code> === Split a command === You can split this... <code> - name: Find geographical region of this server local_action: uri url=http://locator/studio/{{ ansible_default_ipv4.address}} method=GET return_content=yes register=locator_output </code> ... in this: <code> - name: Find geographical region of this server uri: url: http://locator/studio/{{ ansible_default_ipv4.address}} method: GET return_content: yes register: locator_output delegate_to: localhost </code> ===== Playbooks examples ===== * https://github.com/ansible/ansible-examples <code> --- - hosts: local vars: - docroot: /var/www/serversforhackers.com/public tasks: - name: Add Nginx Repository apt_repository: repo='ppa:nginx/stable' state=present register: ppastable - name: Install Nginx apt: pkg=nginx state=installed update_cache=true when: ppastable|success register: nginxinstalled notify: - Start Nginx - name: Create Web Root when: nginxinstalled|success file: dest={{ '{{' }} docroot {{ '}}' }} mode=775 state=directory owner=www-data group=www-data notify: - Reload Nginx handlers: - name: Start Nginx service: name=nginx state=started - name: Reload Nginx service: name=nginx state=reloaded </code> <code> --- name: install and configure DB hosts: testServer become: yes vars: oracle_db_port_value : 1521 tasks: -name: Install the Oracle DB yum: <code to install the DB> -name: Ensure the installed service is enabled and running service: name: <your service name> </code> Setting the become and hosts in the header: <code> --- - name: Install nginx hosts: host.name.ip become: true tasks: - name: Add epel-release repo yum: name: epel-release state: present - name: Install nginx yum: name: nginx state: present - name: Insert Index Page template: src: index.html dest: /usr/share/nginx/html/index.html - name: Start NGiNX service: name: nginx state: started </code> Multiple plays: <code> --- - hosts: webservers remote_user: root tasks: - name: ensure apache is at the latest version yum: name: httpd state: latest - name: write the apache config file template: src: /srv/httpd.j2 dest: /etc/httpd.conf - hosts: databases remote_user: root tasks: - name: ensure postgresql is at the latest version yum: name: postgresql state: latest - name: ensure that postgresql is started service: name: postgresql state: started </code> A right one: <code> --- - hosts: puma gather_facts: no become: yes name: Deploy PUMA tasks: - name: Send Docker images debug: msg="hola" - name: Load Docker images debug: msg="{{ lookup('env','HOME') }} is an environment variable" - name: Start PUMA debug: msg="{{ lookup('env','HOME') }} is an environment variable" # copy: # src: "{{ item }}" # dest: ~ # owner: root # mode: 600 # with_fileglob: # - ../dist/* </code>