Complete reference for Ansible automation and OpenShift/Kubernetes — playbooks, roles, inventory, modules, ad-hoc commands, and container orchestration.
Part of The Direct Path to Linux Ubuntu — free for all.
Install Ansible: pip install ansible. OpenShift CLI: download oc from Red Hat.
Most-used Ansible commands and playbook patterns.
# ── Install Ansible ────────────────────────────────────────── pip install ansible # pip install sudo apt install ansible # Ubuntu/Debian brew install ansible # macOS # ── Test connectivity ───────────────────────────────────────── ansible all -i inventory -m ping ansible webservers -i "web01,web02," -m ping # inline inventory # ── Ad-hoc commands ─────────────────────────────────────────── ansible all -i inventory -m command -a "uptime" ansible all -i inventory -m shell -a "df -h | grep /dev/sd" ansible all -i inventory -m apt -a "name=curl state=present" -b ansible all -i inventory -m service -a "name=apache2 state=restarted" -b ansible all -i inventory -m copy -a "src=file.txt dest=/tmp/" -b ansible all -i inventory -m setup -a "filter=ansible_distribution*" # ── Playbook commands ───────────────────────────────────────── ansible-playbook site.yml -i inventory ansible-playbook site.yml -i inventory --check # dry run ansible-playbook site.yml -i inventory --diff # show changes ansible-playbook site.yml -i inventory -v # verbose ansible-playbook site.yml -i inventory -vvv # very verbose ansible-playbook site.yml -i inventory --tags install ansible-playbook site.yml -i inventory --limit web01 ansible-playbook site.yml -i inventory --ask-vault-pass # ── Vault ──────────────────────────────────────────────────── ansible-vault create vars/secrets.yml ansible-vault encrypt vars/secrets.yml ansible-vault decrypt vars/secrets.yml ansible-vault edit vars/secrets.yml ansible-vault view vars/secrets.yml # ── Galaxy ─────────────────────────────────────────────────── ansible-galaxy install geerlingguy.apache ansible-galaxy install -r requirements.yml ansible-galaxy init my_new_role ansible-galaxy list # ── Debug ──────────────────────────────────────────────────── ansible-inventory -i inventory --list ansible-inventory -i inventory --graph ansible all -i inventory -m setup 2>/dev/null | python3 -m json.tool
Complete playbook deploying MyWebUniversity on Ubuntu.
# deploy.yml — deploy MyWebUniversity to Ubuntu servers
---
- name: Deploy MyWebUniversity
hosts: webservers
become: true
vars:
app_dir: /var/www/React/prod
cgi_dir: /usr/lib/cgi-bin
app_user: www-data
chapters:
- {name: Python, cgi: pydoc, ch: pydoc}
- {name: Go, cgi: Go, ch: Go}
- {name: Java, cgi: Java, ch: Java}
- {name: JavaScript, cgi: JavaScript, ch: JavaScript}
- {name: SQL, cgi: SQL, ch: SQL}
- {name: LDAP, cgi: LDAP, ch: LDAP}
- {name: Cloud, cgi: Cloud, ch: Cloud}
tasks:
- name: Ensure packages installed
ansible.builtin.apt:
name: [apache2, python3, libapache2-mod-cgid]
state: present
update_cache: true
- name: Enable Apache modules
community.general.apache2_module:
name: "{{ item }}"
state: present
loop: [cgid, rewrite, ssl, headers, expires]
notify: Restart Apache
- name: Create app directory
ansible.builtin.file:
path: "{{ app_dir }}"
state: directory
owner: "{{ app_user }}"
mode: '0755'
- name: Create CGI chapter directories
ansible.builtin.file:
path: "{{ cgi_dir }}/{{ item.cgi }}"
state: directory
owner: "{{ app_user }}"
mode: '0755'
loop: "{{ chapters }}"
- name: Deploy CGI scripts
ansible.builtin.copy:
src: "files/{{ item.cgi }}-Reference.cgi"
dest: "{{ cgi_dir }}/{{ item.cgi }}/{{ item.cgi }}-Reference.cgi"
owner: "{{ app_user }}"
mode: '0755'
loop: "{{ chapters }}"
when: lookup('fileglob', 'files/' + item.cgi + '-Reference.cgi') | length > 0
- name: Deploy HTML index files
ansible.builtin.copy:
src: "files/{{ item.cgi }}-index.html"
dest: "{{ app_dir }}/{{ item.cgi }}/index.html"
owner: "{{ app_user }}"
mode: '0644'
loop: "{{ chapters }}"
when: lookup('fileglob', 'files/' + item.cgi + '-index.html') | length > 0
- name: Set ownership on app directory
ansible.builtin.file:
path: "{{ app_dir }}"
owner: "{{ app_user }}"
group: "{{ app_user }}"
recurse: true
- name: Verify Apache is running
ansible.builtin.service:
name: apache2
state: started
enabled: true
- name: Health check
ansible.builtin.uri:
url: "http://{{ ansible_default_ipv4.address }}/toc1.html"
status_code: 200
register: check
ignore_errors: true
- name: Deployment status
ansible.builtin.debug:
msg: "{{ 'SUCCESS' if check.status == 200 else 'FAILED - check Apache logs' }}"
handlers:
- name: Restart Apache
ansible.builtin.service:
name: apache2
state: restarted
# Run: ansible-playbook deploy.yml -i "your-server," -u ubuntu --private-key ~/.ssh/key.pemComplete k8s Deployment, Service, Ingress, and ConfigMap.
# k8s-mywebuniversity.yaml
# kubectl apply -f k8s-mywebuniversity.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: mywebuniversity
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: mywebuniversity
data:
ENVIRONMENT: "production"
LOG_LEVEL: "info"
SITE_URL: "https://www.mywebuniversity.com"
---
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: mywebuniversity
type: Opaque
stringData:
DB_PASSWORD: "change-me-in-production"
API_KEY: "change-me-in-production"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mywebuniversity-web
namespace: mywebuniversity
labels:
app: mywebuniversity
tier: web
spec:
replicas: 3
selector:
matchLabels:
app: mywebuniversity
strategy:
type: RollingUpdate
rollingUpdate: {maxSurge: 1, maxUnavailable: 0}
template:
metadata:
labels:
app: mywebuniversity
spec:
containers:
- name: web
image: myregistry.io/mywebuniversity/web:latest
ports:
- containerPort: 80
envFrom:
- configMapRef: {name: app-config}
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef: {name: app-secrets, key: DB_PASSWORD}
resources:
requests: {cpu: "100m", memory: "128Mi"}
limits: {cpu: "500m", memory: "512Mi"}
livenessProbe:
httpGet: {path: /health, port: 80}
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet: {path: /ready, port: 80}
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: mywebuniversity-web
namespace: mywebuniversity
spec:
selector:
app: mywebuniversity
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mywebuniversity-ingress
namespace: mywebuniversity
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
rules:
- host: www.mywebuniversity.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: mywebuniversity-web
port: {number: 80}
tls:
- hosts: [www.mywebuniversity.com]
secretName: mywebuniversity-tls
# Commands:
# kubectl apply -f k8s-mywebuniversity.yaml
# kubectl get all -n mywebuniversity
# kubectl rollout status deployment/mywebuniversity-web -n mywebuniversity
# kubectl scale deployment mywebuniversity-web --replicas=5 -n mywebuniversityEncrypt, use, and manage secrets with Ansible Vault.
# ── Create encrypted vars file ───────────────────────────────
ansible-vault create vars/secrets.yml
# Opens editor — type your secrets:
# db_password: SuperSecret123!
# api_key: abc123xyz
# smtp_password: mailpass456
# ── Encrypt existing file ─────────────────────────────────────
ansible-vault encrypt vars/secrets.yml
# ── View encrypted file ───────────────────────────────────────
ansible-vault view vars/secrets.yml
# ── Edit encrypted file ───────────────────────────────────────
ansible-vault edit vars/secrets.yml
# ── Encrypt single value (embed in playbook) ──────────────────
ansible-vault encrypt_string 'SuperSecret123!' --name db_password
# Output:
# db_password: !vault |
# $ANSIBLE_VAULT;1.1;AES256
# 663...
# ── Use vault in playbook ─────────────────────────────────────
# secrets_playbook.yml
---
- name: Use vault secrets
hosts: databases
become: true
vars_files:
- vars/secrets.yml # encrypted!
tasks:
- name: Configure database
ansible.builtin.template:
src: templates/pg_hba.conf.j2
dest: /etc/postgresql/16/main/pg_hba.conf
# Template uses: {{ db_password }}
- name: Set DB password
ansible.builtin.postgresql_user:
name: myapp
password: "{{ db_password }}"
become_user: postgres
# ── Run with password ─────────────────────────────────────────
ansible-playbook secrets_playbook.yml -i inventory --ask-vault-pass
# Or use password file (for CI/CD):
echo "myVaultPassword" > ~/.vault_pass
chmod 600 ~/.vault_pass
ansible-playbook secrets_playbook.yml -i inventory --vault-password-file ~/.vault_pass
# ── Multiple vault IDs ────────────────────────────────────────
ansible-vault create --vault-id dev@prompt vars/dev_secrets.yml
ansible-vault create --vault-id prod@prompt vars/prod_secrets.yml
ansible-playbook site.yml \
--vault-id dev@~/.dev_vault_pass \
--vault-id prod@~/.prod_vault_pass
# ── Rekey (change password) ───────────────────────────────────
ansible-vault rekey vars/secrets.ymlSource-to-Image build and deploy on OpenShift.
# OpenShift Source-to-Image (S2I) Workflow # S2I builds container images directly from source code # ── Login and setup ─────────────────────────────────────────── oc login https://api.myocp.example.com:6443 --token=mytoken oc new-project mywebuniversity # ── S2I from Git repo ───────────────────────────────────────── # Python app oc new-app python~https://github.com/myorg/mywebuniversity-api.git \ --name=api \ --env=FLASK_ENV=production \ --env=DATABASE_URL=postgresql://db:5432/mywebuniversity # Node.js app oc new-app nodejs~https://github.com/myorg/mywebuniversity-frontend.git \ --name=frontend # ── S2I from local directory ────────────────────────────────── # Create build config oc new-build python --name=myapp --binary # Build from current directory oc start-build myapp --from-dir=. --follow # ── Monitor build ───────────────────────────────────────────── oc get builds oc logs build/myapp-1 -f oc describe build myapp-1 # ── Expose with Route ───────────────────────────────────────── oc expose service api oc get routes # Edge TLS termination oc create route edge api \ --service=api \ --hostname=api.mywebuniversity.com # ── Environment and secrets ─────────────────────────────────── oc create secret generic db-creds \ --from-literal=DATABASE_URL="postgresql://admin:pass@db:5432/mywebuniversity" oc set env deployment/api --from=secret/db-creds oc set env deployment/api LOG_LEVEL=info WORKERS=4 # ── Scaling and updates ─────────────────────────────────────── oc scale deployment api --replicas=5 oc rollout status deployment/api oc set image deployment/api api=myregistry.io/mywebuniversity/api:v2.0 oc rollout undo deployment/api # rollback if needed # ── Resource quotas ─────────────────────────────────────────── oc create quota mywebuniversity-quota \ --hard=pods=20,requests.cpu=4,requests.memory=8Gi,limits.cpu=8,limits.memory=16Gi oc describe quota mywebuniversity-quota # ── Useful oc commands ──────────────────────────────────────── oc get all # all resources in project oc get pods -o wide # pods with node info oc logs deployment/api -f # stream logs oc exec -it api-xxx-yyy -- bash # shell into pod oc port-forward api-xxx-yyy 8080:8080 # local port forward oc get events --sort-by=.lastTimestamp # recent events