ANSIBLE-PROJECT-DEPLOY
a re-usable Ansible role to deploy projects
ABOUT ME
2
Ramon de la Fuente
Future500 B.V.
@f_u_e_n_t_e
SweetlakePHP
WHY ANSIBLE?
• Easy. Period.
3
“I wrote Ansible because none of the existing tools fit my brain.”
- Michael de Haan
WHY ANSIBLE?
• Easy. Period.
• No unnecessary complexity → No agent!
4
WHY ANSIBLE?
• Easy. Period.
• No unnecessary complexity → No agent!
• Built for re-use and sharing.
5
WHY ANSIBLE?
• Easy. Period.
• No unnecessary complexity → No agent!
• Built for re-use and sharing.
• Extendable in your own language.
6
THE PROBLEM
• Continuous deployment
7
THE PROBLEM
• Continuous deployment
• Easy maintenance of the deploy procedure.
8
THE PROBLEM
• Continuous deployment
• Easy maintenance of the deploy procedure.
• Small learning curve.
9
THE PROBLEM
• Continuous deployment
• Easy maintenance of the deploy procedure.
• Small learning curve.
• Reuse between projects with little effort.
10
WHAT IS A DEPLOY?
Directory structure:
.
!"" releases
| !"" 20140415234508
| #"" 20140415235146
!"" shared
| !"" sessions
| !"" source
| #"" uploads
#"" current -> releases/20140415235146
11
WHAT IS A DEPLOY?
Directory structure:
.
!"" releases
| !"" 20140415234508
| #"" 20140415235146
!"" shared
| !"" sessions
| !"" source
| #"" uploads
#"" current -> releases/20140415235146
12
WHAT IS A DEPLOY?
1. Update the codebase + configuration
13
WHAT IS A DEPLOY?
1. Update the codebase + configuration
2. Install dependencies
14
WHAT IS A DEPLOY?
1. Update the codebase + configuration
2. Install dependencies
3. Preserve shared resources
15
WHAT IS A DEPLOY?
1. Update the codebase + configuration
2. Install dependencies
3. Preserve shared resources
4. Build tasks
16
WHAT IS A DEPLOY?
1. Update the codebase + configuration
2. Install dependencies
3. Preserve shared resources
4. Build tasks
5. Finalize
17
THE ROLE
18
https://galaxy.ansible.com/list#/roles/732
project_deploy
GETTINGTHE ROLE
Installation with ansible-galaxy command:
$ ansible-galaxy install f500.project_deploy,v2.1.0
Optional: create a galaxy file for all roles:
f500.nginx
f500.mariadb55
f500.php
f500.project_deploy,v2.1.0
$ ansible-galaxy install -r ansible/galaxy.txt
19
ROLE WALKTHROUGH
---
- name: Initialize
deploy_helper: "path={{ project_root }} state=present"
20
ROLE WALKTHROUGH
Deploy module variables:
deploy_helper:
project_path
current_path
releases_path
shared_path
previous_release
previous_release_path
new_release
new_release_path
unfinished_filename
21
ROLE WALKTHROUGH
Deploy module variables:
deploy_helper:
project_path: /path/to/project/
current_path: /path/to/project/current
releases_path: /path/to/project/releases
shared_path: /path/to/project/shared
previous_release: 20140415234508
previous_release_path: /path/to/project/releases/20140415234508
new_release: 20140415235146
new_release_path: /path/to/project/releases/20140415235146
unfinished_filename: DEPLOY_UNFINISHED
Used as:
{{ deploy_helper.new_release }}
22
1. UPDATETHE CODEBASE
- name: Clone project files
git: repo={{ project_git_repo }}
dest={{ project_source_path }}
version={{ project_version }}
when: project_deploy_strategy == 'git'
- name: Rsync project files
synchronize: src={{ project_local_path }}
dest={{ project_source_path }}
rsync_timeout={{ project_deploy_synchronize_timeout }}
recursive=yes
when: project_deploy_strategy == 'synchronize'
23
1. UPDATETHE CODEBASE
- name: Clone project files
git: repo={{ project_git_repo }}
dest={{ project_source_path }}
version={{ project_version }}
when: project_deploy_strategy == 'git'
- name: Rsync project files
synchronize: src={{ project_local_path }}
dest={{ project_source_path }}
rsync_timeout={{ project_deploy_synchronize_timeout }}
recursive=yes
when: project_deploy_strategy == 'synchronize'
24
1. UPDATETHE CODEBASE
- name: Clone project files
git: repo={{ project_git_repo }}
dest={{ project_source_path }}
version={{ project_version }}
when: project_deploy_strategy == 'git'
- name: Rsync project files
synchronize: src={{ project_local_path }}
dest={{ project_source_path }}
rsync_timeout={{ project_deploy_synchronize_timeout }}
recursive=yes
when: project_deploy_strategy == 'synchronize'
25
1. UPDATETHE CODEBASE
- name: Write unfinished file
file: path={{ project_source_path }}/{{ deploy_helper.unfinished_filename }}
state=touch
- name: Copy files to new build dir
command: "cp -pr {{project_source_path}} {{deploy_helper.new_release_path}}"
- name: Remove unwanted files/folders from new release
file: path={{ deploy_helper.new_release_path }}/{{ item }} state=absent
with_items: project_unwanted_items
26
1. UPDATETHE CONFIG FILES
- name: Copy project files
copy: src={{ item.src }}
dest={{ deploy_helper.new_release_path }}/{{ item.dest }}
mode={{ item.mode|default('0644') }}
with_items: project_files
- name: Copy project templates
template: src={{ item.src }}
dest={{ deploy_helper.new_release_path }}/{{ item.dest }}
mode={{ item.mode|default('0644') }}
with_items: project_templates
27
2. INSTALL DEPENDENCIES
- name: Do composer install
command: "{{ project_command_for_composer_install }} chdir=…"
environment: project_environment
when: project_has_composer
- name: Do npm install
command: "{{ project_command_for_npm_install }} chdir=…"
environment: project_environment
when: project_has_npm
- name: Do bower install
command: "{{ project_command_for_bower_install }} chdir=…"
environment: project_environment
when: project_has_bower
28
2. INSTALL DEPENDENCIES
- name: Do composer install
command: "{{ project_command_for_composer_install }} chdir=…"
environment: project_environment
when: project_has_composer
- name: Do npm install
command: "{{ project_command_for_npm_install }} chdir=…"
environment: project_environment
when: project_has_npm
- name: Do bower install
command: "{{ project_command_for_bower_install }} chdir=…"
environment: project_environment
when: project_has_bower
29
3. SHARED RESOURCES
- name: Ensure shared sources are present
file: path='{{ deploy_helper.shared_path }}/{{ item.src }}'
state={{ item.type }}
with_items: project_shared_children
- name: Ensure shared paths are absent
file: path='{{ deploy_helper.new_release_path }}/{{ item.path }}'
state=absent
with_items: project_shared_children
- name: Create shared symlinks
file: path='{{ deploy_helper.new_release_path }}/{{ item.path }}'
src='{{ deploy_helper.shared_path }}/{{ item.src }}'
state=link"
with_items: project_shared_children
30
4. BUILD STEPS
- name: Run post_build_commands in the new_release_path
command: "{{ item }} chdir={{ deploy_helper.new_release_path }}"
with_items: project_post_build_commands
environment: project_environment
31
project_post_build_commands:
- "app/console cache:clear"
- "app/console assets:install"
- "app/console assetic:dump"
5. FINALIZE
- name: Finalize the deploy

deploy_helper: path={{ project_root }}
release={{ deploy_helper.new_release }}
state=finalize

when: project_finalize

32
33
IT’S NOT COMPLICATED!
• Only 98 lines
• Number of tasks: 22
• Variables to configure: 28
34
MINIMAL PLAYBOOK
1. Set minimum variables
2. Add the role to “roles” section
35
MINIMAL PLAYBOOK
- name: Deploy the application
hosts: production
remote_user: deploy
sudo: no
vars:
project_root: /var/www/my_project
project_git_repo: git@github.com:me/my_project.git
project_deploy_strategy: git
roles:
- f500.project_deploy
36
EXAMPLE PLAYBOOK
- name: Deploy the application
hosts: production
remote_user: "{{ production_deploy_user }}"
sudo: no
vars:
project_root: "{{ sweetlakephp_root }}"
project_git_repo: "{{ sweetlakephp_github_repo }}"
project_deploy_strategy: git
37
EXAMPLE PLAYBOOK
- name: Deploy the application
hosts: production
remote_user: "{{ production_deploy_user }}"
sudo: no
vars:
project_root: "{{ sweetlakephp_root }}"
project_git_repo: "{{ sweetlakephp_github_repo }}"
project_deploy_strategy: git
project_environment:
SYMFONY_ENV: "prod"
38
EXAMPLE PLAYBOOK
project_environment:
SYMFONY_ENV: "prod"
project_shared_children:
- path: "/app/sessions"
src: "sessions"
- path: "/web/uploads"
src: "uploads"
project_templates:
- name: parameters.yml
src: "templates/parameters_prod.yml.j2"
dest: "/app/config/parameters_prod.yml"
39
EXAMPLE PLAYBOOK
project_environment:
SYMFONY_ENV: "prod"
project_shared_children:
- path: "/app/sessions"
src: "sessions"
- path: "/web/uploads"
src: "uploads"
project_templates:
- name: parameters.yml
src: "templates/parameters_prod.yml.j2"
dest: "/app/config/parameters.yml"
40
EXAMPLE PLAYBOOK
project_has_composer: yes
project_post_build_commands:
- "php vendor/sensio/…/DistributionBundle/…/bin/build_bootstrap.php”
- "app/console cache:clear"
- "app/console doctrine:migrations:migrate --no-interaction"
- "app/console assets:install"
- "app/console assetic:dump"
roles:
- f500.project_deploy
41
EXAMPLE PLAYBOOK
project_has_composer: yes
project_post_build_commands:
- "php vendor/sensio/…/DistributionBundle/…/bin/build_bootstrap.php”
- "app/console cache:clear"
- "app/console doctrine:migrations:migrate --no-interaction"
- "app/console assets:install"
- "app/console assetic:dump"
roles:
- f500.project_deploy
42
EXAMPLE PLAYBOOK
43
project_has_composer: yes
project_post_build_commands:
- "php vendor/sensio/…/DistributionBundle/…/bin/build_bootstrap.php”
- "app/console cache:clear"
- "app/console doctrine:migrations:migrate --no-interaction"
- "app/console assets:install"
- "app/console assetic:dump"
roles:
- f500.project_deploy
WHAT DOESN’T IT DO?
44
• Rollbacks (the cake rollback is a lie)
WHAT DOESN’T IT DO?
45
• Rollbacks
• Set maintenance mode
(the cake rollback is a lie)
WHAT DOESN’T IT DO?
46
• Rollbacks
• Set maintenance mode
• DB migrations
(the cake rollback is a lie)
WHAT’S NEXT?
47
• Injecting your own tasks in addition to commands
WHAT’S NEXT?
48
• Injecting your own tasks in addition to commands
• Copy vendor folders from previous release ✓
WHAT’S NEXT?
49
• Injecting your own tasks in addition to commands
• Copy vendor folders from previous release
• Setfacl support
✓
WHAT’S NEXT?
50
• Injecting your own tasks in addition to commands
• Copy vendor folders from previous release
• Setfacl support
• Your ideas?
✓
THANKYOU!
51
Feedback: joind.in 13405
f500/ansible-project_deploy
(But I’m also just a human. You could talk to me and tell me what you think…)
https://github.com/

Ansible Project Deploy (phpbenelux 2015)

  • 1.
  • 2.
    ABOUT ME 2 Ramon dela Fuente Future500 B.V. @f_u_e_n_t_e SweetlakePHP
  • 3.
    WHY ANSIBLE? • Easy.Period. 3 “I wrote Ansible because none of the existing tools fit my brain.” - Michael de Haan
  • 4.
    WHY ANSIBLE? • Easy.Period. • No unnecessary complexity → No agent! 4
  • 5.
    WHY ANSIBLE? • Easy.Period. • No unnecessary complexity → No agent! • Built for re-use and sharing. 5
  • 6.
    WHY ANSIBLE? • Easy.Period. • No unnecessary complexity → No agent! • Built for re-use and sharing. • Extendable in your own language. 6
  • 7.
  • 8.
    THE PROBLEM • Continuousdeployment • Easy maintenance of the deploy procedure. 8
  • 9.
    THE PROBLEM • Continuousdeployment • Easy maintenance of the deploy procedure. • Small learning curve. 9
  • 10.
    THE PROBLEM • Continuousdeployment • Easy maintenance of the deploy procedure. • Small learning curve. • Reuse between projects with little effort. 10
  • 11.
    WHAT IS ADEPLOY? Directory structure: . !"" releases | !"" 20140415234508 | #"" 20140415235146 !"" shared | !"" sessions | !"" source | #"" uploads #"" current -> releases/20140415235146 11
  • 12.
    WHAT IS ADEPLOY? Directory structure: . !"" releases | !"" 20140415234508 | #"" 20140415235146 !"" shared | !"" sessions | !"" source | #"" uploads #"" current -> releases/20140415235146 12
  • 13.
    WHAT IS ADEPLOY? 1. Update the codebase + configuration 13
  • 14.
    WHAT IS ADEPLOY? 1. Update the codebase + configuration 2. Install dependencies 14
  • 15.
    WHAT IS ADEPLOY? 1. Update the codebase + configuration 2. Install dependencies 3. Preserve shared resources 15
  • 16.
    WHAT IS ADEPLOY? 1. Update the codebase + configuration 2. Install dependencies 3. Preserve shared resources 4. Build tasks 16
  • 17.
    WHAT IS ADEPLOY? 1. Update the codebase + configuration 2. Install dependencies 3. Preserve shared resources 4. Build tasks 5. Finalize 17
  • 18.
  • 19.
    GETTINGTHE ROLE Installation withansible-galaxy command: $ ansible-galaxy install f500.project_deploy,v2.1.0 Optional: create a galaxy file for all roles: f500.nginx f500.mariadb55 f500.php f500.project_deploy,v2.1.0 $ ansible-galaxy install -r ansible/galaxy.txt 19
  • 20.
    ROLE WALKTHROUGH --- - name:Initialize deploy_helper: "path={{ project_root }} state=present" 20
  • 21.
    ROLE WALKTHROUGH Deploy modulevariables: deploy_helper: project_path current_path releases_path shared_path previous_release previous_release_path new_release new_release_path unfinished_filename 21
  • 22.
    ROLE WALKTHROUGH Deploy modulevariables: deploy_helper: project_path: /path/to/project/ current_path: /path/to/project/current releases_path: /path/to/project/releases shared_path: /path/to/project/shared previous_release: 20140415234508 previous_release_path: /path/to/project/releases/20140415234508 new_release: 20140415235146 new_release_path: /path/to/project/releases/20140415235146 unfinished_filename: DEPLOY_UNFINISHED Used as: {{ deploy_helper.new_release }} 22
  • 23.
    1. UPDATETHE CODEBASE -name: Clone project files git: repo={{ project_git_repo }} dest={{ project_source_path }} version={{ project_version }} when: project_deploy_strategy == 'git' - name: Rsync project files synchronize: src={{ project_local_path }} dest={{ project_source_path }} rsync_timeout={{ project_deploy_synchronize_timeout }} recursive=yes when: project_deploy_strategy == 'synchronize' 23
  • 24.
    1. UPDATETHE CODEBASE -name: Clone project files git: repo={{ project_git_repo }} dest={{ project_source_path }} version={{ project_version }} when: project_deploy_strategy == 'git' - name: Rsync project files synchronize: src={{ project_local_path }} dest={{ project_source_path }} rsync_timeout={{ project_deploy_synchronize_timeout }} recursive=yes when: project_deploy_strategy == 'synchronize' 24
  • 25.
    1. UPDATETHE CODEBASE -name: Clone project files git: repo={{ project_git_repo }} dest={{ project_source_path }} version={{ project_version }} when: project_deploy_strategy == 'git' - name: Rsync project files synchronize: src={{ project_local_path }} dest={{ project_source_path }} rsync_timeout={{ project_deploy_synchronize_timeout }} recursive=yes when: project_deploy_strategy == 'synchronize' 25
  • 26.
    1. UPDATETHE CODEBASE -name: Write unfinished file file: path={{ project_source_path }}/{{ deploy_helper.unfinished_filename }} state=touch - name: Copy files to new build dir command: "cp -pr {{project_source_path}} {{deploy_helper.new_release_path}}" - name: Remove unwanted files/folders from new release file: path={{ deploy_helper.new_release_path }}/{{ item }} state=absent with_items: project_unwanted_items 26
  • 27.
    1. UPDATETHE CONFIGFILES - name: Copy project files copy: src={{ item.src }} dest={{ deploy_helper.new_release_path }}/{{ item.dest }} mode={{ item.mode|default('0644') }} with_items: project_files - name: Copy project templates template: src={{ item.src }} dest={{ deploy_helper.new_release_path }}/{{ item.dest }} mode={{ item.mode|default('0644') }} with_items: project_templates 27
  • 28.
    2. INSTALL DEPENDENCIES -name: Do composer install command: "{{ project_command_for_composer_install }} chdir=…" environment: project_environment when: project_has_composer - name: Do npm install command: "{{ project_command_for_npm_install }} chdir=…" environment: project_environment when: project_has_npm - name: Do bower install command: "{{ project_command_for_bower_install }} chdir=…" environment: project_environment when: project_has_bower 28
  • 29.
    2. INSTALL DEPENDENCIES -name: Do composer install command: "{{ project_command_for_composer_install }} chdir=…" environment: project_environment when: project_has_composer - name: Do npm install command: "{{ project_command_for_npm_install }} chdir=…" environment: project_environment when: project_has_npm - name: Do bower install command: "{{ project_command_for_bower_install }} chdir=…" environment: project_environment when: project_has_bower 29
  • 30.
    3. SHARED RESOURCES -name: Ensure shared sources are present file: path='{{ deploy_helper.shared_path }}/{{ item.src }}' state={{ item.type }} with_items: project_shared_children - name: Ensure shared paths are absent file: path='{{ deploy_helper.new_release_path }}/{{ item.path }}' state=absent with_items: project_shared_children - name: Create shared symlinks file: path='{{ deploy_helper.new_release_path }}/{{ item.path }}' src='{{ deploy_helper.shared_path }}/{{ item.src }}' state=link" with_items: project_shared_children 30
  • 31.
    4. BUILD STEPS -name: Run post_build_commands in the new_release_path command: "{{ item }} chdir={{ deploy_helper.new_release_path }}" with_items: project_post_build_commands environment: project_environment 31 project_post_build_commands: - "app/console cache:clear" - "app/console assets:install" - "app/console assetic:dump"
  • 32.
    5. FINALIZE - name:Finalize the deploy
 deploy_helper: path={{ project_root }} release={{ deploy_helper.new_release }} state=finalize
 when: project_finalize
 32
  • 33.
  • 34.
    IT’S NOT COMPLICATED! •Only 98 lines • Number of tasks: 22 • Variables to configure: 28 34
  • 35.
    MINIMAL PLAYBOOK 1. Setminimum variables 2. Add the role to “roles” section 35
  • 36.
    MINIMAL PLAYBOOK - name:Deploy the application hosts: production remote_user: deploy sudo: no vars: project_root: /var/www/my_project project_git_repo: git@github.com:me/my_project.git project_deploy_strategy: git roles: - f500.project_deploy 36
  • 37.
    EXAMPLE PLAYBOOK - name:Deploy the application hosts: production remote_user: "{{ production_deploy_user }}" sudo: no vars: project_root: "{{ sweetlakephp_root }}" project_git_repo: "{{ sweetlakephp_github_repo }}" project_deploy_strategy: git 37
  • 38.
    EXAMPLE PLAYBOOK - name:Deploy the application hosts: production remote_user: "{{ production_deploy_user }}" sudo: no vars: project_root: "{{ sweetlakephp_root }}" project_git_repo: "{{ sweetlakephp_github_repo }}" project_deploy_strategy: git project_environment: SYMFONY_ENV: "prod" 38
  • 39.
    EXAMPLE PLAYBOOK project_environment: SYMFONY_ENV: "prod" project_shared_children: -path: "/app/sessions" src: "sessions" - path: "/web/uploads" src: "uploads" project_templates: - name: parameters.yml src: "templates/parameters_prod.yml.j2" dest: "/app/config/parameters_prod.yml" 39
  • 40.
    EXAMPLE PLAYBOOK project_environment: SYMFONY_ENV: "prod" project_shared_children: -path: "/app/sessions" src: "sessions" - path: "/web/uploads" src: "uploads" project_templates: - name: parameters.yml src: "templates/parameters_prod.yml.j2" dest: "/app/config/parameters.yml" 40
  • 41.
    EXAMPLE PLAYBOOK project_has_composer: yes project_post_build_commands: -"php vendor/sensio/…/DistributionBundle/…/bin/build_bootstrap.php” - "app/console cache:clear" - "app/console doctrine:migrations:migrate --no-interaction" - "app/console assets:install" - "app/console assetic:dump" roles: - f500.project_deploy 41
  • 42.
    EXAMPLE PLAYBOOK project_has_composer: yes project_post_build_commands: -"php vendor/sensio/…/DistributionBundle/…/bin/build_bootstrap.php” - "app/console cache:clear" - "app/console doctrine:migrations:migrate --no-interaction" - "app/console assets:install" - "app/console assetic:dump" roles: - f500.project_deploy 42
  • 43.
    EXAMPLE PLAYBOOK 43 project_has_composer: yes project_post_build_commands: -"php vendor/sensio/…/DistributionBundle/…/bin/build_bootstrap.php” - "app/console cache:clear" - "app/console doctrine:migrations:migrate --no-interaction" - "app/console assets:install" - "app/console assetic:dump" roles: - f500.project_deploy
  • 44.
    WHAT DOESN’T ITDO? 44 • Rollbacks (the cake rollback is a lie)
  • 45.
    WHAT DOESN’T ITDO? 45 • Rollbacks • Set maintenance mode (the cake rollback is a lie)
  • 46.
    WHAT DOESN’T ITDO? 46 • Rollbacks • Set maintenance mode • DB migrations (the cake rollback is a lie)
  • 47.
    WHAT’S NEXT? 47 • Injectingyour own tasks in addition to commands
  • 48.
    WHAT’S NEXT? 48 • Injectingyour own tasks in addition to commands • Copy vendor folders from previous release ✓
  • 49.
    WHAT’S NEXT? 49 • Injectingyour own tasks in addition to commands • Copy vendor folders from previous release • Setfacl support ✓
  • 50.
    WHAT’S NEXT? 50 • Injectingyour own tasks in addition to commands • Copy vendor folders from previous release • Setfacl support • Your ideas? ✓
  • 51.
    THANKYOU! 51 Feedback: joind.in 13405 f500/ansible-project_deploy (ButI’m also just a human. You could talk to me and tell me what you think…) https://github.com/