{"id":1,"date":"2018-12-27T17:14:47","date_gmt":"2018-12-27T17:14:47","guid":{"rendered":"http:\/\/www.tangosierratech.com\/blog\/wordpress\/?p=1"},"modified":"2019-01-05T01:51:28","modified_gmt":"2019-01-05T01:51:28","slug":"adding-worpress-blog-to-ts-tech","status":"publish","type":"post","link":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/2018\/12\/27\/adding-worpress-blog-to-ts-tech\/","title":{"rendered":"Today I added a WordPress blog to my site&#8230;and it was pretty good."},"content":{"rendered":"\r\n\r\n\r\n<p>Today I added a WordPress blog to my site\u2026cool story bro.<\/p>\r\n\r\n\r\n\r\n<p>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.<\/p>\r\n\r\n\r\n\r\n<p>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.<\/p>\r\n\r\n\r\n\r\n<p>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\u2019t remember what I ended with, but it didn\u2019t 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.<\/p>\r\n\r\n\r\n\r\n<p>Here is how this site is built and deployed:<\/p>\r\n<p>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.<\/p>\r\n<p>The two instances are fully orchestrated using ansible. When you run the playbook, it builds the two instances, and deploys roles to them.<\/p>\r\n<p>The playbook pulls the static\/wordpress content, along with a database dump from Google Storage buckets.<\/p>\r\n<p>To facilitate updates and repeatability, a python script is used to keep the buckets updated.<\/p>\r\n<p>Security??&#8230;Gosh I hope so.<br \/>Our variables are encrypted using Ansible Vault. Whats cool is under the hood, we use a private-key file to encrypt everything\u2026no passphrase at command line:<\/p>\r\n<p>Deploying to Google Cloud using Ansible.<\/p>\r\n<p>Here is the primary playbook:<\/p>\r\n\r\n<pre class=\"lang:default decode:true\" title=\"create_ts_tech_website.yml\">- name: Create instance(s)\r\n  hosts: localhost\r\n  connection: local\r\n  gather_facts: yes\r\n\r\n  vars:\r\n    service_account_email: XXXXXX-compute@developer.gserviceaccount.com\r\n    credentials_file: \/Users\/tsimson\/project-name.json\r\n    project_id: project-name\r\n    machine_type: g1-small\r\n    image: centos-7\r\n  tasks:\r\n\r\n   - name: Launch Web Host\r\n     gce:\r\n         network: ts-tech-vpc-1\r\n         subnetwork: ts-tech-vpc-1\r\n         zone: us-east1-b\r\n         instance_names: ts-web-host-1\r\n         machine_type: \"{{ machine_type }}\"\r\n         image: \"{{ image }}\"\r\n         service_account_permissions: storage-full\r\n         service_account_email: \"{{ service_account_email }}\"\r\n         credentials_file: \"{{ credentials_file }}\"\r\n         project_id: \"{{ project_id }}\"\r\n         ip_forward: True\r\n         tags: [ssh, http-server, https-server, subnet]\r\n     register: gce_web_host_1\r\n   - debug: var=gce_web_host_1\r\n\r\n   - name: Wait for SSH to come up\r\n     wait_for: host={{ item.public_ip }} port=22 delay=10 timeout=300\r\n     with_items: \"{{ gce_web_host_1.instance_data }}\"\r\n\r\n   - name: Add host to groupname\r\n     add_host: hostname={{ item.name }} ansible_ssh_host={{ item.public_ip }} groupname=ts-web-hosts\r\n     with_items: \"{{ gce_web_host_1.instance_data }}\"\r\n\r\n   - name: Launch DB Host\r\n     gce:\r\n         network: ts-tech-vpc-1\r\n         subnetwork: ts-tech-vpc-1\r\n         zone: us-east1-b\r\n         instance_names: ts-db-host-1\r\n         machine_type: \"{{ machine_type }}\"\r\n         image: \"{{ image }}\"\r\n         service_account_email: \"{{ service_account_email }}\"\r\n         credentials_file: \"{{ credentials_file }}\"\r\n         project_id: \"{{ project_id }}\"\r\n         ip_forward: True\r\n         tags: [ssh, subnet]\r\n     register: gce_db_host_1\r\n   - debug: var=gce_db_host_1\r\n\r\n   - name: Wait for SSH to come up\r\n     wait_for: host={{ item.public_ip }} port=22 delay=10 timeout=300\r\n     with_items: \"{{ gce_db_host_1.instance_data }}\"\r\n\r\n   - name: Add host to groupname\r\n     add_host: hostname={{ item.name }} ansible_ssh_host={{ item.public_ip }} groupname=ts-db-hosts\r\n     with_items: \"{{ gce_db_host_1.instance_data }}\"\r\n\r\n- name: Manage web-host\r\n  hosts: ts-web-hosts\r\n  connection: ssh\r\n  sudo: True\r\n  roles:\r\n    - role: web_host_role\r\n    - role: wordpress\r\n\r\n- name: Manage db-host\r\n  hosts: ts-db-hosts\r\n  connection: ssh\r\n  sudo: True\r\n  roles:\r\n    - role: db_host_role\r\n<\/pre>\r\n\r\n\r\n\r\n<p>Here is the directory structure that ansible uses:<\/p>\r\n\r\n<pre class=\"lang:default decode:true\" title=\"ts_tech_web_public$ tree\">ts_tech_web_public$ tree\r\n.\r\n\u251c\u2500\u2500 README.md\r\n\u251c\u2500\u2500 ansible.cfg\r\n\u251c\u2500\u2500 create_ts_tech_website.retry\r\n\u251c\u2500\u2500 create_ts_tech_website.yml\r\n\u251c\u2500\u2500 group_vars\r\n\u2502   \u2514\u2500\u2500 all\r\n\u251c\u2500\u2500 host_vars\r\n\u251c\u2500\u2500 hosts\r\n\u2514\u2500\u2500 roles\r\n    \u251c\u2500\u2500 db_host_role\r\n    \u2502   \u251c\u2500\u2500 files\r\n    \u2502   \u2502   \u2514\u2500\u2500 my.cnf\r\n    \u2502   \u251c\u2500\u2500 tasks\r\n    \u2502   \u2502   \u2514\u2500\u2500 main.yml\r\n    \u2502   \u2514\u2500\u2500 templates\r\n    \u251c\u2500\u2500 web_host_role\r\n    \u2502   \u251c\u2500\u2500 files\r\n    \u2502   \u251c\u2500\u2500 tasks\r\n    \u2502   \u2502   \u2514\u2500\u2500 main.yml\r\n    \u2502   \u2514\u2500\u2500 templates\r\n    \u2502       \u251c\u2500\u2500 nginx.conf\r\n    \u2502       \u2514\u2500\u2500 wp_db_gb_sync.py\r\n    \u2514\u2500\u2500 wordpress\r\n        \u251c\u2500\u2500 README.md\r\n        \u251c\u2500\u2500 defaults\r\n        \u2502   \u2514\u2500\u2500 main.yml\r\n        \u251c\u2500\u2500 files\r\n        \u251c\u2500\u2500 handlers\r\n        \u2502   \u2514\u2500\u2500 main.yml\r\n        \u251c\u2500\u2500 meta\r\n        \u2502   \u2514\u2500\u2500 main.yml\r\n        \u251c\u2500\u2500 tasks\r\n        \u2502   \u2514\u2500\u2500 main.yml\r\n        \u251c\u2500\u2500 templates\r\n        \u2502   \u2514\u2500\u2500 wp-config.php\r\n        \u251c\u2500\u2500 tests\r\n        \u2502   \u251c\u2500\u2500 inventory\r\n        \u2502   \u2514\u2500\u2500 test.yml\r\n        \u2514\u2500\u2500 vars\r\n            \u2514\u2500\u2500 main.yml\r\n\r\n20 directories, 20 files\r\n<\/pre>\r\n<p>This python script is run to keep the content and database updated off platform. You run this after making live changes to maintain repeatability:<\/p>\r\n<p>Python script &#8230;for the boys:\u00a0\u00a0 wp_db_gb_sync.py<\/p>\r\n<pre class=\"lang:default decode:true\" title=\"Python script ...for the boys: wp_db_gb_sync.py\">from google.cloud import storage\r\nimport os\r\nimport glob\r\nfrom subprocess import Popen\r\nimport requests\r\nimport time\r\n\r\nstorage_client = storage.Client()\r\nts_wp_files_bucket = storage_client.get_bucket('ts-wp-files')\r\nts_wp_db_dump_bucket = storage_client.get_bucket('ts-wp-db-dump')\r\ntest_path = ''\r\n\r\nos.chdir('\/root')\r\n\r\nPopen('mysqldump -h {{ hostvars['localhost']['gce_db_host_1']['instance_data'][0]['private_ip'] }} -u {{ mysql_user }} -p{{ mysql_pass }} --databases ts_tech_db &gt; ts_tech_db.sql', shell=True)\r\n\r\nresponse = requests.request(\"GET\", \"http:\/\/metadata\/computeMetadata\/v1\/instance\/network-interfaces\/0\/access-configs\/0\/external-ip\", headers={\"Metadata-Flavor\": \"Google\"})\r\n\r\npub_ip = response.content\r\n\r\ntime.sleep(5)\r\n\r\nwith open(\"ts_tech_db.sql\", 'r') as f:\r\n    s = f.read()\r\n    x = s.replace(pub_ip, \"www.tangosierratech.com\")\r\n\r\nwith open(\"ts_tech_db.sql\", 'w') as f:\r\n    f.write(x)\r\n\r\ndef copy_local_file_to_gcs(bucket, local_file):\r\n    blob = bucket.blob(local_file)\r\n    blob.upload_from_filename(local_file)\r\n\r\ncopy_local_file_to_gcs(ts_wp_db_dump_bucket, 'ts_tech_db.sql')\r\n\r\nos.chdir('\/var\/www\/')\r\n\r\ndef copy_local_directory_to_gcs(bucket, local_path):\r\n    \"\"\"Recursively copy a directory of files to GCS.\r\n\r\n    local_path should be a directory and not have a trailing slash.\r\n    \"\"\"\r\n    assert os.path.isdir(local_path)\r\n    def walk(local_path):\r\n        for path in glob.glob(local_path + '\/**'):\r\n            if os.path.isdir(path):\r\n                walk(path)\r\n            else:\r\n                if test_path:\r\n                    remote_path = os.path.join(test_path, path)\r\n                    print remote_path\r\n                    blob = bucket.blob(remote_path)\r\n                    blob.upload_from_filename(path)\r\n                else:\r\n                    remote_path = path\r\n                    print remote_path\r\n                    blob = bucket.blob(remote_path)\r\n                    blob.upload_from_filename(path)\r\n\r\n    walk(local_path)\r\n\r\ncopy_local_directory_to_gcs(ts_wp_files_bucket, 'html')\r\n<\/pre>\r\n<p>&nbsp;<\/p>\r\n\r\n\r\n","protected":false},"excerpt":{"rendered":"<p>Today I added a WordPress blog to my site\u2026cool 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 &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.tangosierratech.com\/blog\/wordpress\/2018\/12\/27\/adding-worpress-blog-to-ts-tech\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Today I added a WordPress blog to my site&#8230;and it was pretty good.&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":5,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-basic-blog"],"_links":{"self":[{"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/posts\/1","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/comments?post=1"}],"version-history":[{"count":9,"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/posts\/1\/revisions"}],"predecessor-version":[{"id":112,"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/posts\/1\/revisions\/112"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/media\/5"}],"wp:attachment":[{"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/media?parent=1"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/categories?post=1"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tangosierratech.com\/blog\/wordpress\/wp-json\/wp\/v2\/tags?post=1"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}