Today I added a WordPress blog to my site…cool story bro.
I wanted to bolt a blog on to my existing site over running a separate WordPress instance. The reason for this was basically to increase my skills with GCP, ansible, PHP, Python and MySQL.
If all you are after is to get a WordPress site up and running, then its probably much easier to go with the GCP Marketplace WP instance and walk away. I initially tried this and it was almost too easy. You more or less fill out a form and its built for you, and all of the automation behind this is available and unit tested.
That being said my work is not fully absent of a quest for shortcuts. I played with the Google Marketplace instance, a canned WP ansible role, and installing WP manually. At this point I don’t remember what I ended with, but it didn’t matter once I learned how to deploy the existing site repeatably. The key here was two google buckets, one for the WP content files, and another for a restorable database dump.
Here is how this site is built and deployed:
The site is deployed across two Google Cloud instances. The first runs nginx and PHP. It serves a static webpage, as well as the WordPress site. The second is a database instance.
The two instances are fully orchestrated using ansible. When you run the playbook, it builds the two instances, and deploys roles to them.
The playbook pulls the static/wordpress content, along with a database dump from Google Storage buckets.
To facilitate updates and repeatability, a python script is used to keep the buckets updated.
Security??…Gosh I hope so.
Our variables are encrypted using Ansible Vault. Whats cool is under the hood, we use a private-key file to encrypt everything…no passphrase at command line:
Deploying to Google Cloud using Ansible.
Here is the primary playbook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
- name: Create instance(s) hosts: localhost connection: local gather_facts: yes vars: service_account_email: XXXXXX-compute@developer.gserviceaccount.com credentials_file: /Users/tsimson/project-name.json project_id: project-name machine_type: g1-small image: centos-7 tasks: - name: Launch Web Host gce: network: ts-tech-vpc-1 subnetwork: ts-tech-vpc-1 zone: us-east1-b instance_names: ts-web-host-1 machine_type: "{{ machine_type }}" image: "{{ image }}" service_account_permissions: storage-full service_account_email: "{{ service_account_email }}" credentials_file: "{{ credentials_file }}" project_id: "{{ project_id }}" ip_forward: True tags: [ssh, http-server, https-server, subnet] register: gce_web_host_1 - debug: var=gce_web_host_1 - name: Wait for SSH to come up wait_for: host={{ item.public_ip }} port=22 delay=10 timeout=300 with_items: "{{ gce_web_host_1.instance_data }}" - name: Add host to groupname add_host: hostname={{ item.name }} ansible_ssh_host={{ item.public_ip }} groupname=ts-web-hosts with_items: "{{ gce_web_host_1.instance_data }}" - name: Launch DB Host gce: network: ts-tech-vpc-1 subnetwork: ts-tech-vpc-1 zone: us-east1-b instance_names: ts-db-host-1 machine_type: "{{ machine_type }}" image: "{{ image }}" service_account_email: "{{ service_account_email }}" credentials_file: "{{ credentials_file }}" project_id: "{{ project_id }}" ip_forward: True tags: [ssh, subnet] register: gce_db_host_1 - debug: var=gce_db_host_1 - name: Wait for SSH to come up wait_for: host={{ item.public_ip }} port=22 delay=10 timeout=300 with_items: "{{ gce_db_host_1.instance_data }}" - name: Add host to groupname add_host: hostname={{ item.name }} ansible_ssh_host={{ item.public_ip }} groupname=ts-db-hosts with_items: "{{ gce_db_host_1.instance_data }}" - name: Manage web-host hosts: ts-web-hosts connection: ssh sudo: True roles: - role: web_host_role - role: wordpress - name: Manage db-host hosts: ts-db-hosts connection: ssh sudo: True roles: - role: db_host_role |
Here is the directory structure that ansible uses:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
ts_tech_web_public$ tree . ├── README.md ├── ansible.cfg ├── create_ts_tech_website.retry ├── create_ts_tech_website.yml ├── group_vars │ └── all ├── host_vars ├── hosts └── roles ├── db_host_role │ ├── files │ │ └── my.cnf │ ├── tasks │ │ └── main.yml │ └── templates ├── web_host_role │ ├── files │ ├── tasks │ │ └── main.yml │ └── templates │ ├── nginx.conf │ └── wp_db_gb_sync.py └── wordpress ├── README.md ├── defaults │ └── main.yml ├── files ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── tasks │ └── main.yml ├── templates │ └── wp-config.php ├── tests │ ├── inventory │ └── test.yml └── vars └── main.yml 20 directories, 20 files |
This python script is run to keep the content and database updated off platform. You run this after making live changes to maintain repeatability:
Python script …for the boys: wp_db_gb_sync.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
from google.cloud import storage import os import glob from subprocess import Popen import requests import time storage_client = storage.Client() ts_wp_files_bucket = storage_client.get_bucket('ts-wp-files') ts_wp_db_dump_bucket = storage_client.get_bucket('ts-wp-db-dump') test_path = '' os.chdir('/root') Popen('mysqldump -h {{ hostvars['localhost']['gce_db_host_1']['instance_data'][0]['private_ip'] }} -u {{ mysql_user }} -p{{ mysql_pass }} --databases ts_tech_db > ts_tech_db.sql', shell=True) response = requests.request("GET", "http://metadata/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip", headers={"Metadata-Flavor": "Google"}) pub_ip = response.content time.sleep(5) with open("ts_tech_db.sql", 'r') as f: s = f.read() x = s.replace(pub_ip, "www.tangosierratech.com") with open("ts_tech_db.sql", 'w') as f: f.write(x) def copy_local_file_to_gcs(bucket, local_file): blob = bucket.blob(local_file) blob.upload_from_filename(local_file) copy_local_file_to_gcs(ts_wp_db_dump_bucket, 'ts_tech_db.sql') os.chdir('/var/www/') def copy_local_directory_to_gcs(bucket, local_path): """Recursively copy a directory of files to GCS. local_path should be a directory and not have a trailing slash. """ assert os.path.isdir(local_path) def walk(local_path): for path in glob.glob(local_path + '/**'): if os.path.isdir(path): walk(path) else: if test_path: remote_path = os.path.join(test_path, path) print remote_path blob = bucket.blob(remote_path) blob.upload_from_filename(path) else: remote_path = path print remote_path blob = bucket.blob(remote_path) blob.upload_from_filename(path) walk(local_path) copy_local_directory_to_gcs(ts_wp_files_bucket, 'html') |