Exemples Ansible Playbook
Exemples complets d'Ansible playbook pour l'automatisation, la gestion de configuration et le déploiement
Key Facts
- Category
- Configuration Management
- Items
- 3
- Format Families
- sql, text
Sample Overview
Exemples complets d'Ansible playbook pour l'automatisation, la gestion de configuration et le déploiement This sample set belongs to Configuration Management and can be used to test related workflows inside Elysia Tools.
💻 Configuration de Base de Serveur Web text
🟢 simple
⭐⭐
Automatisation complète de serveur web avec Nginx, SSL et configuration de pare-feu
⏱️ 15 min
🏷️ ansible, automation, web server, nginx
Prerequisites:
Ansible basics, Linux administration, Web server concepts
# Basic Web Server Setup with Ansible
# This playbook sets up a complete web server with Nginx, SSL, and security
---
# 1. Site Configuration (site.yml)
- name: Configure web servers
hosts: webservers
become: yes
vars_files:
- vars/main.yml
roles:
- common
- nginx
- ssl
- firewall
# 2. Inventory (inventory/hosts)
[webservers]
web1.example.com ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
web2.example.com ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
[webservers:vars]
ansible_python_interpreter=/usr/bin/python3
# 3. Variables (vars/main.yml)
# System Configuration
timezone: "America/Los_Angeles"
locale: "en_US.UTF-8"
# Web Server Configuration
server_name: "{{ inventory_hostname }}"
web_root: "/var/www/{{ server_name }}"
nginx_user: "www-data"
nginx_group: "www-data"
# SSL Configuration
ssl_enabled: true
ssl_country: "US"
ssl_state: "California"
ssl_city: "San Francisco"
ssl_organization: "Company Inc"
ssl_organizational_unit: "IT Department"
ssl_email: "admin@{{ server_name }}"
# Firewall Configuration
firewall_enabled: true
firewall_allowed_ports:
- 22 # SSH
- 80 # HTTP
- 443 # HTTPS
# Application Configuration
app_name: "myapp"
app_version: "1.0.0"
app_repo_url: "https://github.com/company/myapp.git"
app_branch: "main"
# 4. Common Role (roles/common/tasks/main.yml)
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- curl
- wget
- git
- unzip
- htop
- vim
- tree
- software-properties-common
state: present
- name: Set timezone
timezone:
name: "{{ timezone }}"
- name: Set locale
locale_gen:
name: "{{ locale }}"
state: present
- name: Create system user for web applications
user:
name: "{{ app_name }}"
shell: /bin/bash
home: "/opt/{{ app_name }}"
create_home: yes
state: present
- name: Create log directory
file:
path: "/var/log/{{ app_name }}"
state: directory
owner: "{{ app_name }}"
group: "{{ app_name }}"
mode: '0755'
- name: Create application directory
file:
path: "/opt/{{ app_name }}/apps"
state: directory
owner: "{{ app_name }}"
group: "{{ app_name }}"
mode: '0755'
# 5. Nginx Role (roles/nginx/tasks/main.yml)
- name: Install Nginx
apt:
name: nginx
state: present
- name: Create Nginx directories
file:
path: "{{ item }}"
state: directory
owner: "{{ nginx_user }}"
group: "{{ nginx_group }}"
mode: '0755'
loop:
- "{{ web_root }}"
- "/etc/nginx/sites-available"
- "/etc/nginx/sites-enabled"
- "/var/log/nginx"
- name: Copy Nginx main configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: restart nginx
- name: Create Nginx site configuration
template:
src: site.conf.j2
dest: "/etc/nginx/sites-available/{{ server_name }}"
owner: root
group: root
mode: '0644'
notify: restart nginx
- name: Enable Nginx site
file:
src: "/etc/nginx/sites-available/{{ server_name }}"
dest: "/etc/nginx/sites-enabled/{{ server_name }}"
state: link
notify: restart nginx
- name: Remove default Nginx site
file:
path: "/etc/nginx/sites-enabled/default"
state: absent
notify: restart nginx
- name: Start and enable Nginx
service:
name: nginx
state: started
enabled: yes
# 6. Nginx Templates (roles/nginx/templates/nginx.conf.j2)
user {{ nginx_user }};
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
application/json
application/javascript
text/xml
application/xml
application/xml+rss
text/javascript;
##
# Include server configurations
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
# 7. Nginx Site Template (roles/nginx/templates/site.conf.j2)
server {
listen 80;
listen [::]:80;
server_name {{ server_name }};
# Redirect HTTP to HTTPS
{% if ssl_enabled %}
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ server_name }};
{% endif %}
root {{ web_root }};
index index.html index.htm index.php;
# SSL Configuration
{% if ssl_enabled %}
ssl_certificate /etc/ssl/certs/{{ server_name }}.crt;
ssl_certificate_key /etc/ssl/private/{{ server_name }}.key;
ssl_trusted_certificate /etc/ssl/certs/{{ server_name }}-ca.crt;
{% endif %}
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
# Logging
access_log /var/log/nginx/{{ server_name }}_access.log;
error_log /var/log/nginx/{{ server_name }}_error.log;
# Main location
location / {
try_files $uri $uri/ =404;
}
# Static file caching
location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# PHP processing (if needed)
location ~ .php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
# Deny access to .htaccess files
location ~ /.ht {
deny all;
}
}
# 8. SSL Role (roles/ssl/tasks/main.yml)
- name: Create SSL directory
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- /etc/ssl/certs
- /etc/ssl/private
- name: Generate OpenSSL private key
openssl_privatekey:
path: "/etc/ssl/private/{{ server_name }}.key"
size: 2048
type: RSA
mode: '0600'
- name: Create OpenSSL certificate signing request
openssl_csr:
path: "/etc/ssl/{{ server_name }}.csr"
privatekey_path: "/etc/ssl/private/{{ server_name }}.key"
common_name: "{{ server_name }}"
country_name: "{{ ssl_country }}"
state_or_province_name: "{{ ssl_state }}"
locality_name: "{{ ssl_city }}"
organization_name: "{{ ssl_organization }}"
organizational_unit_name: "{{ ssl_organizational_unit }}"
email_address: "{{ ssl_email }}"
- name: Generate self-signed SSL certificate
openssl_certificate:
path: "/etc/ssl/certs/{{ server_name }}.crt"
privatekey_path: "/etc/ssl/private/{{ server_name }}.key"
csr_path: "/etc/ssl/{{ server_name }}.csr"
provider: selfsigned
selfsigned_not_after: "+3650d"
mode: '0644'
- name: Create SSL certificate bundle
shell: |
cat /etc/ssl/certs/{{ server_name }}.crt /etc/ssl/certs/ca-certificates.crt > /etc/ssl/certs/{{ server_name }}-ca.crt
chmod 644 /etc/ssl/certs/{{ server_name }}-ca.crt
# 9. Firewall Role (roles/firewall/tasks/main.yml)
- name: Install UFW (Uncomplicated Firewall)
apt:
name: ufw
state: present
- name: Reset UFW to default settings
ufw:
state: reset
- name: Set default firewall policies
ufw:
policy: deny
direction: incoming
ufw:
policy: allow
direction: outgoing
- name: Allow SSH connections
ufw:
rule: allow
name: OpenSSH
- name: Allow specific ports
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop: "{{ firewall_allowed_ports }}"
when: firewall_enabled
- name: Enable UFW firewall
ufw:
state: enabled
# 10. Handlers (handlers/main.yml)
- name: restart nginx
service:
name: nginx
state: restarted
- name: restart php-fpm
service:
name: php7.4-fpm
state: restarted
- name: reload firewall
ufw:
state: reloaded
# 11. README.md
# Ansible Web Server Setup
## Overview
This Ansible playbook automates the complete setup of a production-ready web server with:
- Nginx web server
- SSL/TLS configuration
- Firewall setup (UFW)
- Security hardening
- Performance optimization
## Prerequisites
- Ansible 2.9+
- Ubuntu 20.04+ target servers
- SSH access with sudo privileges
## Usage
1. **Update inventory**
```bash
# Edit inventory/hosts
vim inventory/hosts
```
2. **Configure variables**
```bash
# Edit variables
vim vars/main.yml
```
3. **Run the playbook**
```bash
ansible-playbook -i inventory/hosts site.yml
```
4. **Verify installation**
```bash
# Check Nginx status
sudo systemctl status nginx
# Test website
curl -I https://your-server.com
```
## File Structure
```
.
├── site.yml # Main playbook
├── inventory/
│ └── hosts # Server inventory
├── vars/
│ └── main.yml # Variables
├── roles/
│ ├── common/
│ │ └── tasks/
│ │ └── main.yml
│ ├── nginx/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ └── templates/
│ │ ├── nginx.conf.j2
│ │ └── site.conf.j2
│ ├── ssl/
│ │ └── tasks/
│ │ └── main.yml
│ └── firewall/
│ └── tasks/
│ └── main.yml
└── handlers/
└── main.yml # Event handlers
```
## Customization
### Add New Sites
1. Update inventory with new hosts
2. Adjust variables in vars/main.yml
3. Run playbook against new hosts
### Modify SSL Settings
Update SSL variables in vars/main.yml:
- ssl_enabled
- ssl_country
- ssl_state
- ssl_city
- ssl_organization
### Firewall Configuration
Adjust firewall_allowed_ports in vars/main.yml for your specific requirements.
💻 Configuration de Base de Données Automatisée text
🟡 intermediate
⭐⭐⭐⭐
Automatisation complète de base de données avec MySQL, réplication, backup et monitoring
⏱️ 25 min
🏷️ ansible, database, mysql, automation
Prerequisites:
Ansible advanced, MySQL administration, Database concepts
# Database Automation with Ansible
# Complete setup for MySQL with replication, backup, and monitoring
---
# 1. Database Setup Playbook (site.yml)
- name: Setup database servers
hosts: databases
become: yes
vars_files:
- vars/main.yml
roles:
- mysql
- replication
- backup
- monitoring
- name: Configure replication
hosts: db_replicas
become: yes
vars_files:
- vars/main.yml
roles:
- replica_setup
# 2. Inventory (inventory/hosts)
[databases]
db1.example.com ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
db2.example.com ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
[db_master]
db1.example.com
[db_replicas]
db2.example.com
[databases:vars]
ansible_python_interpreter=/usr/bin/python3
# 3. Variables (vars/main.yml)
# MySQL Configuration
mysql_root_password: "StrongPassword123!"
mysql_app_user: "app_user"
mysql_app_password: "AppPassword456!"
mysql_app_database: "production_app"
# MySQL Server Configuration
mysql_bind_address: "0.0.0.0"
mysql_server_id: "{{ 1 if inventory_hostname in groups['db_master'] else 2 + (groups['db_replicas'].index(inventory_hostname) if inventory_hostname in groups['db_replicas'] else 0) }}"
mysql_max_connections: 1000
mysql_innodb_buffer_pool_size: "256M"
mysql_log_bin: "mysql-bin"
mysql_binlog_format: "ROW"
mysql_expire_logs_days: 7
# Replication Configuration
replication_enabled: true
replication_user: "repl_user"
replication_password: "ReplPassword789!"
replication_master_host: "{{ hostvars[groups['db_master'][0]]['ansible_default_ipv4']['address'] }}"
# Backup Configuration
backup_enabled: true
backup_retention_days: 30
backup_s3_bucket: "company-mysql-backups"
backup_s3_region: "us-west-2"
backup_schedule: "0 2 * * *" # Daily at 2 AM
# Monitoring Configuration
monitoring_enabled: true
monitoring_user: "monitoring_user"
monitoring_password: "MonitoringPass123!"
monitoring_allowed_hosts:
- "10.0.0.0/8"
- "192.168.0.0/16"
# Security Configuration
mysql_remove_anonymous_users: true
mysql_remove_remote_root: true
mysql_disallow_remote_root: true
# Performance Tuning
performance_schema: true
slow_query_log: true
slow_query_log_file: "/var/log/mysql/slow.log"
long_query_time: 2
# 4. MySQL Role (roles/mysql/tasks/main.yml)
- name: Install MySQL dependencies
apt:
name:
- python3-pymysql
- mysql-server
- mysql-client
state: present
- name: Start MySQL service
service:
name: mysql
state: started
enabled: yes
- name: Update MySQL configuration
template:
src: my.cnf.j2
dest: /etc/mysql/my.cnf
owner: root
group: root
mode: '0644'
notify: restart mysql
- name: Set MySQL root password
mysql_user:
name: root
password: "{{ mysql_root_password }}"
login_unix_socket: /var/run/mysqld/mysqld.sock
state: present
- name: Create .my.cnf for root user
template:
src: my_root.cnf.j2
dest: /root/.my.cnf
owner: root
group: root
mode: '0600'
- name: Remove anonymous MySQL users
mysql_user:
name: ""
host_all: yes
state: absent
login_user: root
login_password: "{{ mysql_root_password }}"
- name: Remove remote MySQL root access
mysql_user:
name: root
host: "{{ item }}"
state: absent
login_user: root
login_password: "{{ mysql_root_password }}"
loop:
- "{{ ansible_fqdn }}"
- "{{ ansible_hostname }}"
- "{{ ansible_default_ipv4.address }}"
- "::1"
- "127.0.0.1"
- "%"
when: mysql_remove_remote_root
- name: Create application database
mysql_db:
name: "{{ mysql_app_database }}"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
- name: Create application user
mysql_user:
name: "{{ mysql_app_user }}"
password: "{{ mysql_app_password }}"
priv: "{{ mysql_app_database }}.*:ALL"
host: "%"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
- name: Create monitoring user
mysql_user:
name: "{{ monitoring_user }}"
password: "{{ monitoring_password }}"
priv: "*.*:SELECT,SHOW VIEW"
host: "{{ item }}"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
loop: "{{ monitoring_allowed_hosts }}"
when: monitoring_enabled
- name: Create replication user (master only)
mysql_user:
name: "{{ replication_user }}"
password: "{{ replication_password }}"
priv: "*.*:REPLICATION SLAVE"
host: "%"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
when: inventory_hostname in groups['db_master']
# 5. MySQL Configuration Template (roles/mysql/templates/my.cnf.j2)
[mysqld]
# Basic Settings
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
bind-address = {{ mysql_bind_address }}
# Character Settings
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# Performance Settings
max_connections = {{ mysql_max_connections }}
innodb_buffer_pool_size = {{ mysql_innodb_buffer_pool_size }}
innodb_log_file_size = 128M
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
# Logging
log_error = /var/log/mysql/error.log
slow_query_log = {{ 'ON' if slow_query_log else 'OFF' }}
slow_query_log_file = {{ slow_query_log_file }}
long_query_time = {{ long_query_time }}
# Binary Logging (for replication)
log-bin = {{ mysql_log_bin }}
binlog_format = {{ mysql_binlog_format }}
expire_logs_days = {{ mysql_expire_logs_days }}
server-id = {{ mysql_server_id }}
# Performance Schema
performance_schema = {{ 'ON' if performance_schema else 'OFF' }}
# Security
local-infile = 0
skip-show-database = 1
[mysqldump]
quick
quote-names
max_allowed_packet = 16M
[mysql]
auto-rehash
disable-auto-rehash
prompt = '\u@\h [\d]>\_ '
[isamchk]
key_buffer_size = 16M
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
# 6. Root MySQL Configuration (roles/mysql/templates/my_root.cnf.j2)
[client]
user=root
password={{ mysql_root_password }}
host=localhost
socket=/var/run/mysqld/mysqld.sock
[mysql]
user=root
password={{ mysql_root_password }}
[mysqldump]
user=root
password={{ mysql_root_password }}
# 7. Replication Role (roles/replication/tasks/main.yml)
- name: Get master status
mysql_replication:
mode: getmaster
login_user: root
login_password: "{{ mysql_root_password }}"
register: master_status
when: inventory_hostname in groups['db_master']
- name: Configure replica
mysql_replication:
mode: changemaster
master_host: "{{ replication_master_host }}"
master_user: "{{ replication_user }}"
master_password: "{{ replication_password }}"
master_log_file: "{{ hostvars[groups['db_master'][0]]['master_status']['File'] }}"
master_log_pos: "{{ hostvars[groups['db_master'][0]]['master_status']['Position'] }}"
login_user: root
login_password: "{{ mysql_root_password }}"
when: inventory_hostname in groups['db_replicas']
- name: Start replica
mysql_replication:
mode: startslave
login_user: root
login_password: "{{ mysql_root_password }}"
when: inventory_hostname in groups['db_replicas']
- name: Check replica status
mysql_replication:
mode: getslave
login_user: root
login_password: "{{ mysql_root_password }}"
register: replica_status
when: inventory_hostname in groups['db_replicas']
- name: Display replica status
debug:
var: replica_status
when: inventory_hostname in groups['db_replicas']
# 8. Backup Role (roles/backup/tasks/main.yml)
- name: Install backup dependencies
apt:
name:
- awscli
- python3-boto3
- percona-xtrabackup-24
state: present
when: backup_enabled
- name: Create backup directory
file:
path: /opt/mysql-backups
state: directory
mode: '0755'
when: backup_enabled
- name: Create backup script
template:
src: backup.sh.j2
dest: /opt/mysql-backups/backup.sh
mode: '0755'
when: backup_enabled
- name: Create backup cron job (master only)
cron:
name: "MySQL Backup"
minute: "0"
hour: "2"
job: "/opt/mysql-backups/backup.sh"
user: root
when: backup_enabled and inventory_hostname in groups['db_master']
# 9. Backup Script Template (roles/backup/templates/backup.sh.j2)
#!/bin/bash
# MySQL Backup Script
# Variables
BACKUP_DIR="/opt/mysql-backups"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="{{ mysql_app_database }}"
DB_USER="{{ mysql_app_user }}"
DB_PASS="{{ mysql_app_password }}"
S3_BUCKET="{{ backup_s3_bucket }}"
S3_REGION="{{ backup_s3_region }}"
RETENTION_DAYS={{ backup_retention_days }}
# Create backup directory
mkdir -p $BACKUP_DIR/$DATE
# Take database backup
echo "Starting database backup..."
mysqldump -u$DB_USER -p$DB_PASS --single-transaction --routines --triggers --all-databases | gzip > $BACKUP_DIR/$DATE/mysql_backup_$DATE.sql.gz
# Upload to S3
echo "Uploading backup to S3..."
aws s3 cp $BACKUP_DIR/$DATE/mysql_backup_$DATE.sql.gz s3://$S3_BUCKET/mysql/ --region $S3_REGION
# Clean local backups older than 7 days
find $BACKUP_DIR -type d -mtime +7 -exec rm -rf {} \;
# Clean S3 backups older than retention days
aws s3 ls s3://$S3_BUCKET/mysql/ --region $S3_REGION | while read -r line; do
createDate=$(echo $line|awk {'print $1" "$2'})
createDate=$(date -d"$createDate" +%s)
olderThan=$(date -d"$RETENTION_DAYS days ago" +%s)
if [[ $createDate -lt $olderThan ]]; then
fileName=$(echo $line|awk {'print $4'})
aws s3 rm s3://$S3_BUCKET/mysql/$fileName --region $S3_REGION
fi
done
echo "Backup completed successfully!"
# 10. Monitoring Role (roles/monitoring/tasks/main.yml)
- name: Install monitoring tools
apt:
name:
- monitoring-plugins
- nagios-plugins
- python3-psutil
state: present
when: monitoring_enabled
- name: Create monitoring script
template:
src: mysql_monitoring.py.j2
dest: /opt/mysql-monitoring/mysql_monitoring.py
mode: '0755'
when: monitoring_enabled
- name: Create monitoring directory
file:
path: /opt/mysql-monitoring
state: directory
mode: '0755'
when: monitoring_enabled
- name: Setup monitoring cron job
cron:
name: "MySQL Monitoring"
minute: "*/5"
job: "/opt/mysql-monitoring/mysql_monitoring.py"
user: root
when: monitoring_enabled
# 11. Handlers (handlers/main.yml)
- name: restart mysql
service:
name: mysql
state: restarted
- name: reload mysql
service:
name: mysql
state: reloaded
# 12. Additional Configuration Files
# Monitoring Script Template (roles/monitoring/templates/mysql_monitoring.py.j2)
#!/usr/bin/env python3
import pymysql
import sys
import os
# MySQL connection settings
MYSQL_USER = "{{ monitoring_user }}"
MYSQL_PASS = "{{ monitoring_password }}"
MYSQL_HOST = "localhost"
def check_mysql():
try:
# Connect to MySQL
conn = pymysql.connect(
host=MYSQL_HOST,
user=MYSQL_USER,
password=MYSQL_PASS
)
with conn.cursor() as cursor:
# Check if MySQL is running
cursor.execute("SELECT 1")
result = cursor.fetchone()
if result:
print("MySQL is running normally")
return 0
else:
print("MySQL is not responding")
return 1
except Exception as e:
print(f"MySQL check failed: {e}")
return 1
finally:
if 'conn' in locals():
conn.close()
if __name__ == "__main__":
sys.exit(check_mysql())
💻 Déploiement d'Application Kubernetes text
🔴 complex
⭐⭐⭐⭐⭐
Ansible avancé pour déploiement d'applications Kubernetes avec Helm, monitoring et intégration CI/CD
⏱️ 35 min
🏷️ ansible, kubernetes, helm, automation
Prerequisites:
Ansible expert, Kubernetes, Helm, DevOps concepts
# Kubernetes Application Deployment with Ansible
# Complete automation for Kubernetes apps with Helm, monitoring, and GitOps
---
# 1. Kubernetes Deployment Playbook (site.yml)
- name: Deploy Kubernetes Applications
hosts: k8s_cluster
become: yes
vars_files:
- vars/main.yml
roles:
- kubernetes-setup
- ingress-controller
- cert-manager
- monitoring
- application-deploy
- name: Setup monitoring stack
hosts: k8s_cluster
become: yes
vars_files:
- vars/main.yml
roles:
- prometheus
- grafana
- jaeger
# 2. Inventory (inventory/hosts)
[k8s_cluster]
k8s-master1.example.com ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
k8s-worker1.example.com ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
k8s-worker2.example.com ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
[k8s_cluster:vars]
ansible_python_interpreter=/usr/bin/python3
kubectl_config_file: "/root/.kube/config"
# 3. Variables (vars/main.yml)
# Kubernetes Configuration
kubernetes_version: "1.28.0"
cluster_name: "production-cluster"
cluster_domain: "k8s.example.com"
# Application Configuration
app_name: "microservice-app"
app_namespace: "production"
app_version: "2.1.0"
app_replicas: 3
# Container Registry
container_registry: "registry.example.com"
app_image: "{{ container_registry }}/{{ app_name }}:{{ app_version }}"
image_pull_secret: "registry-secret"
# Ingress Configuration
ingress_enabled: true
ingress_host: "{{ app_name }}.{{ cluster_domain }}"
ingress_tls_enabled: true
cert_issuer_email: "admin@{{ cluster_domain }}"
# Database Configuration
database_enabled: true
database_type: "postgresql"
database_name: "{{ app_name }}_db"
database_user: "dbuser"
database_password: "{{ vault_database_password }}"
# Redis Configuration
redis_enabled: true
redis_name: "{{ app_name }}-redis"
# Monitoring Configuration
monitoring_enabled: true
prometheus_enabled: true
grafana_enabled: true
jaeger_enabled: true
# CI/CD Integration
ci_cd_enabled: true
webhook_url: "https://github.com/{{ app_name }}/webhooks"
webhook_secret: "{{ vault_webhook_secret }}"
# Resource Limits
app_cpu_request: "100m"
app_cpu_limit: "500m"
app_memory_request: "128Mi"
app_memory_limit: "512Mi"
# Health Checks
health_check_path: "/health"
readiness_check_path: "/ready"
# Autoscaling
hpa_enabled: true
hpa_min_replicas: 3
hpa_max_replicas: 10
hpa_cpu_target: 70
hpa_memory_target: 80
# 4. Kubernetes Setup Role (roles/kubernetes-setup/tasks/main.yml)
- name: Install required packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Install Docker
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: present
- name: Add Kubernetes GPG key
apt_key:
url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
state: present
- name: Add Kubernetes repository
apt_repository:
repo: "deb https://apt.kubernetes.io/ kubernetes-xenial main"
state: present
- name: Install Kubernetes tools
apt:
name:
- kubelet={{ kubernetes_version }}-00
- kubeadm={{ kubernetes_version }}-00
- kubectl={{ kubernetes_version }}-00
state: present
update_cache: yes
force: yes
- name: Hold Kubernetes packages
dpkg_selections:
name: "{{ item }}"
selection: hold
loop:
- kubelet
- kubeadm
- kubectl
- name: Start and enable Docker
systemd:
name: docker
state: started
enabled: yes
- name: Configure Docker daemon
template:
src: daemon.json.j2
dest: /etc/docker/daemon.json
notify: restart docker
- name: Create Kubernetes configuration directory
file:
path: /etc/kubernetes
state: directory
mode: '0755'
- name: Generate cluster configuration (master only)
template:
src: cluster-config.yaml.j2
dest: /etc/kubernetes/cluster-config.yaml
when: inventory_hostname in groups['k8s_masters']
- name: Initialize Kubernetes cluster (master only)
shell: |
kubeadm init --config /etc/kubernetes/cluster-config.yaml
creates: /etc/kubernetes/admin.conf
when: inventory_hostname in groups['k8s_masters']
- name: Setup kubectl for root user (master only)
file:
src: /etc/kubernetes/admin.conf
dest: /root/.kube/config
state: link
force: yes
when: inventory_hostname in groups['k8s_masters']
- name: Get join command (master only)
shell: kubeadm token create --print-join-command
register: kubeadm_join_command
when: inventory_hostname in groups['k8s_masters']
- name: Join worker nodes to cluster
shell: "{{ kubeadm_join_command.stdout_lines[0] }}"
when: inventory_hostname in groups['k8s_workers']
- name: Install Flannel CNI plugin (master only)
shell: |
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
when: inventory_hostname in groups['k8s_masters']
# 5. Ingress Controller Role (roles/ingress-controller/tasks/main.yml)
- name: Add Helm repository
shell: |
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
when: inventory_hostname in groups['k8s_masters']
- name: Create ingress-nginx namespace
kubernetes.core.k8s:
kubeconfig: "{{ kubectl_config_file }}"
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
when: inventory_hostname in groups['k8s_masters']
- name: Install NGINX Ingress Controller
shell: |
helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx --create-namespace --set controller.replicaCount=2 --set controller.nodeSelector."kubernetes.io/os"=linux --set defaultBackend.nodeSelector."kubernetes.io/os"=linux --set controller.admissionWebhooks.patch.nodeSelector."kubernetes.io/os"=linux
when: inventory_hostname in groups['k8s_masters']
# 6. Cert Manager Role (roles/cert-manager/tasks/main.yml)
- name: Add Jetstack Helm repository
shell: |
helm repo add jetstack https://charts.jetstack.io
helm repo update
when: inventory_hostname in groups['k8s_masters']
- name: Create cert-manager namespace
kubernetes.core.k8s:
kubeconfig: "{{ kubectl_config_file }}"
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager
when: inventory_hostname in groups['k8s_masters']
- name: Install cert-manager CRDs
shell: |
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.crds.yaml
when: inventory_hostname in groups['k8s_masters']
- name: Install cert-manager
shell: |
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.13.0
when: inventory_hostname in groups['k8s_masters']
- name: Create ClusterIssuer for Let's Encrypt
kubernetes.core.k8s:
kubeconfig: "{{ kubectl_config_file }}"
state: present
definition:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: "{{ cert_issuer_email }}"
privateKeySecretRef:
name: letsencrypt-prod-private-key
solvers:
- http01:
ingress:
class: nginx
when: inventory_hostname in groups['k8s_masters']
# 7. Application Deployment Role (roles/application-deploy/tasks/main.yml)
- name: Create application namespace
kubernetes.core.k8s:
kubeconfig: "{{ kubectl_config_file }}"
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ app_namespace }}"
- name: Create image pull secret
kubernetes.core.k8s:
kubeconfig: "{{ kubectl_config_file }}"
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: "{{ image_pull_secret }}"
namespace: "{{ app_namespace }}"
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: "{{ vault_docker_config }}"
- name: Deploy application using Helm
kubernetes.core.helm:
kubeconfig: "{{ kubectl_config_file }}"
name: "{{ app_name }}"
chart_ref: "./charts/{{ app_name }}"
release_namespace: "{{ app_namespace }}"
values:
image:
repository: "{{ container_registry }}/{{ app_name }}"
tag: "{{ app_version }}"
pullSecret: "{{ image_pull_secret }}"
replicaCount: "{{ app_replicas }}"
service:
type: ClusterIP
port: 80
ingress:
enabled: "{{ ingress_enabled }}"
className: "nginx"
hosts:
- host: "{{ ingress_host }}"
paths:
- path: /
pathType: Prefix
tls:
- secretName: "{{ app_name }}-tls"
hosts:
- "{{ ingress_host }}"
resources:
requests:
cpu: "{{ app_cpu_request }}"
memory: "{{ app_memory_request }}"
limits:
cpu: "{{ app_cpu_limit }}"
memory: "{{ app_memory_limit }}"
livenessProbe:
httpGet:
path: "{{ health_check_path }}"
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: "{{ readiness_check_path }}"
port: http
initialDelaySeconds: 5
periodSeconds: 5
autoscaling:
enabled: "{{ hpa_enabled }}"
minReplicas: "{{ hpa_min_replicas }}"
maxReplicas: "{{ hpa_max_replicas }}"
targetCPUUtilizationPercentage: "{{ hpa_cpu_target }}"
targetMemoryUtilizationPercentage: "{{ hpa_memory_target }}"
# 8. Prometheus Role (roles/prometheus/tasks/main.yml)
- name: Create monitoring namespace
kubernetes.core.k8s:
kubeconfig: "{{ kubectl_config_file }}"
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
when: monitoring_enabled
- name: Install Prometheus Operator
shell: |
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack --namespace monitoring --create-namespace --set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=50Gi --set grafana.persistence.storageClassName=gp2 --set grafana.persistence.size=10Gi
when: monitoring_enabled
# 9. Grafana Dashboard Configuration (roles/monitoring/tasks/grafana.yml)
- name: Create Grafana dashboard for application
kubernetes.core.k8s:
kubeconfig: "{{ kubectl_config_file }}"
state: present
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: "{{ app_name }}-dashboard"
namespace: monitoring
labels:
grafana_dashboard: "1"
data:
dashboard.json: |
{
"dashboard": {
"id": null,
"title": "{{ app_name }} Application Metrics",
"tags": ["{{ app_name }}", "application"],
"timezone": "browser",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(http_requests_total[5m])",
"legendFormat": "{{ app_name }} requests"
}
],
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 0}
}
],
"time": {"from": "now-1h", "to": "now"},
"refresh": "30s"
}
}
when: monitoring_enabled
# 10. CI/CD Integration Role (roles/ci-cd/tasks/main.yml)
- name: Create Argo CD namespace
kubernetes.core.k8s:
kubeconfig: "{{ kubectl_config_file }}"
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: argocd
when: ci_cd_enabled
- name: Install Argo CD
shell: |
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
when: ci_cd_enabled
- name: Create Argo CD application
kubernetes.core.k8s:
kubeconfig: "{{ kubectl_config_file }}"
state: present
definition:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: "{{ app_name }}-app"
namespace: argocd
spec:
project: default
source:
repoURL: "https://github.com/company/{{ app_name }}.git"
targetRevision: main
path: k8s
destination:
server: https://kubernetes.default.svc
namespace: "{{ app_namespace }}"
syncPolicy:
automated:
prune: true
selfHeal: true
when: ci_cd_enabled
# 11. Additional Configuration Files
# Docker Daemon Template (roles/kubernetes-setup/templates/daemon.json.j2)
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"insecure-registries": ["{{ container_registry }}"]
}
# Cluster Configuration Template (roles/kubernetes-setup/templates/cluster-config.yaml.j2)
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: "{{ ansible_default_ipv4.address }}"
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
kubeletExtraArgs:
node-ip: "{{ ansible_default_ipv4.address }}"
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: "v{{ kubernetes_version }}"
clusterName: "{{ cluster_name }}"
controlPlaneEndpoint: "{{ ansible_default_ipv4.address }}:6443"
networking:
podSubnet: "10.244.0.0/16"
serviceSubnet: "10.96.0.0/12"
etcd:
external:
endpoints:
- http://{{ ansible_default_ipv4.address }}:2379
apiServer:
extraArgs:
service-node-port-range: "30000-32767"
certSANs:
- "{{ ansible_fqdn }}"
- "{{ ansible_hostname }}"
- "{{ ansible_default_ipv4.address }}"
- "{{ cluster_name }}"
# Helm Chart Structure (charts/microservice-app/)
# Chart.yaml
apiVersion: v2
name: microservice-app
description: A Helm chart for Microservice Application
type: application
version: 1.0.0
appVersion: "{{ app_version }}"
# values.yaml
replicaCount: 1
image:
repository: "{{ app_image }}"
pullPolicy: IfNotPresent
tag: "{{ app_version }}"
service:
type: ClusterIP
port: 80
targetPort: 3000
ingress:
enabled: false
className: "nginx"
annotations: {}
hosts:
- host: chart-example.local
paths:
- path: /
pathType: Prefix
tls: []
resources: {}
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "chart.fullname" . }}
labels:
{{- include "chart.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "chart.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "chart.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: {{ .Values.healthCheck.path | default "/health" }}
port: http
readinessProbe:
httpGet:
path: {{ .Values.readinessCheck.path | default "/ready" }}
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}