diff --git a/.gitignore b/.gitignore index 3167cd9..37451da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ terraform.* .DS_Store +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..63fec30 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:3.5 +MAINTAINER "Titan Lien" + +ENV ANSIBLE_VERSION=2.2.2.0 + +RUN set -ex && \ + buildDeps="python-dev build-base libffi-dev openssl-dev" && \ + apk add --no-cache $buildDeps python py-pip ca-certificates bash tar && \ + pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir ansible==${ANSIBLE_VERSION} && \ + apk del --purge $buildDeps + +CMD ["ansible"] diff --git a/README.md b/README.md index 286b0d3..897ff00 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Practical example on how to get a Wordpress running under an Amazon ECS Cluster * [Terraform](https://www.terraform.io/) * [Amazon ECS](https://aws.amazon.com/ecs/) * [Amazon RDS](https://aws.amazon.com/es/rds/) +* [Amazon ELB](https://aws.amazon.com/elasticloadbalancing/) ## Requirements @@ -25,47 +26,44 @@ To use this example you will need an [AWS](https://aws.amazon.com/es/) account a 1. Build the Wordpress container. -Packer will use a [base Docker image with Ansible](https://github.com/jfusterm/dockerfiles/blob/master/ansible/Dockerfile) to provision all the applications needed to run a Wordpress. The result will be saved into a container named `jfusterm/wp-packer` with a version tag `4.4.2`. +Packer will use a [base Docker image with Ansible](https://github.com/titanlien/wordpress-ecs/blob/master/Dockerfile) to provision all the applications needed to run a Wordpress. The result will be saved into a container named `titanlien/wp-packer` with a version tag `4.7.3`. + +**Note 1**: If you want to change the image tag you have to change it in `wp-packer.json` and `wordpress.json`. + +**Note 2**: Packer will push the image to [Dockerhub](https://hub.docker.com/) automatically. -**Note**: If you want to change the image tag you have to change it in `wp-packer.json` and `wordpress.json`. ``` -# packer build wp-packer.json +# packer build -var 'DOCKER_PASSWD=[SECURITY]' wp-packer.json ``` -2. Push the container to [Dockerhub](https://hub.docker.com/) - Check that the image is ready. ``` # docker images -REPOSITORY TAG IMAGE ID CREATED SIZE -jfusterm/wp-packer 4.4.2 60bfb4ef7e9d 3 hours ago 138.2 MB +REPOSITORY TAG IMAGE ID CREATED SIZE +titanlien/wp-packer 4.7.3 334cf6396987 16 hours ago 155 MB ``` -Then you can push it to Dockerhub. - -``` -# docker login -# docker push jfusterm/wp-packer:4.4.2 +2. Deploy all the infrastructure needed on AWS using Terraform. +3. Create two amazon roles, *qq-ecs-role* and *qq-ec2-role*, to handle the EC2 resource. +4. Launching stack by following command. +```bash +$ env TF_VAR_aws_access_key=$AWS_ACCESS_KEY TF_VAR_aws_secret_key=$AWS_SCERET_KEY TF_VAR_key_name=titan@MBA terraform apply ``` -3. Deploy all the infrastructure needed on AWS using Terraform. - -``` -# terraform apply -``` - -Once deployed, Terraform will display the ECS Container Instances public IPs and the [ELB](https://aws.amazon.com/es/elasticloadbalancing/) URL that will distribute the traffic across the different Wordpress container instances. +Once deployed, Terraform will print out the ECS Container Instances public IPs and the [ELB](https://aws.amazon.com/es/elasticloadbalancing/) URL that will distribute the traffic across the different Wordpress container instances. The RDS connection parameters will be passed on runtime to the Wordpress containers via environment variables. -4. Once not needed, we can remove all the AWS infrastructure: +5. Upstream container log to cloudwatch awslogs. +6. Once not needed, we can remove all the AWS infrastructure: -``` -# terraform destroy + +```bash +$ terraform destroy ``` ## Considerations @@ -73,9 +71,10 @@ The RDS connection parameters will be passed on runtime to the Wordpress contain This example uses a basic and simple approach to get a ready to use Wordpress using different technology. Further modifications will be done to get a fully automated, scalable and high available Wordpress. Some thoughts: * Wrap all the steps in a single script: build the container, push the container to Dockerhub or a private registry and finally deploy all the infrastructure on AWS. -* ~~Automate Wordpress installation when the first instance is launched. **Note**: Currently the ELB won't work properly due to the health-checks configuration until Wordpress is installed from one of the Worpress instances.~~ * Distribute the ECS Container Instances across different availability zones and route the traffic using the ELB among them. * Decouple Nginx and PHP-FPM in separate containers so can be scaled independently. +* Sending log to [ELK](https://www.elastic.co/products) or [Amazon Elasticsearch Service](https://aws.amazon.com/elasticsearch-service/). +* Setting [cloudwtach](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/mon-scripts.html) to monitor CPU, memory and network traffic. * Use a shared or distributed storage system to persist Wordpress' data. Examples: * [Amazon EFS](https://aws.amazon.com/efs/) * [GlusterFS](https://www.gluster.org/) @@ -83,3 +82,8 @@ This example uses a basic and simple approach to get a ready to use Wordpress us * Remove the RDS single point of failure. Examples: * Deploy RDS on [Multi-AZ](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html) * Use [Percona XtraDB Cluster](https://www.percona.com/software/mysql-database/percona-xtradb-cluster) + +## Sample ELB page +![alt text][demo] + +[demo]: https://github.com/titanlien/wordpress-ecs/raw/master/raw/images/wp-demo.png "Wordpress Title Text 2" diff --git a/packer/ansible/roles/php-fpm/tasks/main.yml b/packer/ansible/roles/php-fpm/tasks/main.yml index 8b4e127..6a8ae9e 100644 --- a/packer/ansible/roles/php-fpm/tasks/main.yml +++ b/packer/ansible/roles/php-fpm/tasks/main.yml @@ -1,7 +1,7 @@ --- - name: PHP-FPM | Installation apk: - name=php-fpm,php-mysql,php-opcache + name=php5-fpm,php5-mysql,php5-opcache state=present update_cache=yes diff --git a/packer/ansible/roles/wordpress/files/entrypoint.sh b/packer/ansible/roles/wordpress/files/entrypoint.sh index b3d6c16..b001520 100644 --- a/packer/ansible/roles/wordpress/files/entrypoint.sh +++ b/packer/ansible/roles/wordpress/files/entrypoint.sh @@ -2,7 +2,7 @@ set -e -cd /usr/share/nginx/html/wordpress +cd /var/lib/nginx/html/wordpress if ! [ -e wp-config.php ] && [ -e wp-config-sample.php ]; then @@ -33,4 +33,4 @@ mv wp-config-sample.php wp-config.php cd / -exec "$@" \ No newline at end of file +exec "$@" diff --git a/packer/ansible/roles/wordpress/files/install-wp.sh b/packer/ansible/roles/wordpress/files/install-wp.sh index 2a15b9f..1feb26c 100644 --- a/packer/ansible/roles/wordpress/files/install-wp.sh +++ b/packer/ansible/roles/wordpress/files/install-wp.sh @@ -2,7 +2,7 @@ set -e -EC2_PUBLIC_IP=$(curl http://169.254.169.254/latest/meta-data/public-ipv4) +EC2_PUBLIC_IP=$(curl -s http://whatismyip.akamai.com/) WP_INSTALL_URL="http://$EC2_PUBLIC_IP/wp-admin/install.php?step=2" # Wait random time between 1s and 15s so the container with the lowest wait installs Wordpress, not all at once. diff --git a/packer/ansible/roles/wordpress/tasks/main.yml b/packer/ansible/roles/wordpress/tasks/main.yml index 8bb8540..d15bb7b 100644 --- a/packer/ansible/roles/wordpress/tasks/main.yml +++ b/packer/ansible/roles/wordpress/tasks/main.yml @@ -4,16 +4,17 @@ url=https://wordpress.org/wordpress-{{ wordpress_version }}.tar.gz dest=/wordpress.tar.gz checksum=md5:{{ wordpress_md5 }} - validate_certs=no # Alpine 3.3 doesn't recognize Godaddy's CA. + validate_certs=yes - name: Wordpress | Unarchive unarchive: src=/wordpress.tar.gz - dest=/usr/share/nginx/html + dest=/var/lib/nginx/html + remote_src=yes - name: Wordpress | Set permissions file: - path=/usr/share/nginx/html/wordpress + path=/var/lib/nginx/html/wordpress owner=nginx group=nginx state=directory diff --git a/packer/ansible/roles/wordpress/vars/main.yml b/packer/ansible/roles/wordpress/vars/main.yml index 7ecca83..d2ee705 100644 --- a/packer/ansible/roles/wordpress/vars/main.yml +++ b/packer/ansible/roles/wordpress/vars/main.yml @@ -1,3 +1,3 @@ --- -wordpress_version: "4.5" -wordpress_md5: "6beda5bee679ddff61cb8e2e163f23bf" +wordpress_version: "4.7.3" +wordpress_md5: "044729d30b720809f19e14ece49e119b" diff --git a/packer/wp-packer.json b/packer/wp-packer.json index f46a78a..7bf21fe 100644 --- a/packer/wp-packer.json +++ b/packer/wp-packer.json @@ -1,32 +1,34 @@ { - "builders": [ + "variables": { + "docker_passwd": "" + }, + "builders": [ { "type": "docker", - "image": "jfusterm/ansible", + "image": "titanlien/ansible:3.5", "commit": true } ], - "provisioners": [ - { - "type": "ansible-local", - "playbook_dir": "ansible", - "playbook_file": "ansible/wordpress.yml" - } - ], - "post-processors": [ - [ - { - "type": "docker-tag", - "repository": "jfusterm/wp-packer", - "tag": "4.5" - }, - { - "type": "docker-push", - "login": "true", - "login_username": "jfusterm", - "login_password": "", - "login_email": "joan.fuster@gmail.com" - } - ] - ] + "provisioners": [ + { + "type": "ansible-local", + "playbook_dir": "ansible", + "playbook_file": "ansible/wordpress.yml" + } + ], + "post-processors": [ + [ + { + "type": "docker-tag", + "repository": "titanlien/wp-packer", + "tag": "4.7.3" + }, + { + "type": "docker-push", + "login": "true", + "login_username": "titanlien", + "login_password": "{{user `docker_passwd`}}" + } + ] + ] } diff --git a/raw/images/wp-demo.png b/raw/images/wp-demo.png new file mode 100644 index 0000000..41c1d37 Binary files /dev/null and b/raw/images/wp-demo.png differ diff --git a/terraform/ec2.tf b/terraform/ec2.tf index d5967c1..9e76527 100755 --- a/terraform/ec2.tf +++ b/terraform/ec2.tf @@ -7,12 +7,13 @@ resource "aws_instance" "ecs-instance01" { subnet_id = "${aws_subnet.wp-public-tf.id}" key_name = "${var.key_name}" associate_public_ip_address = true - iam_instance_profile = "ecsInstanceRole" + iam_instance_profile = "${aws_iam_instance_profile.web_instance_profile.id}" security_groups = ["${aws_security_group.wp-ecs-sg-tf.id}"] user_data = "#!/bin/bash\necho ECS_CLUSTER=${aws_ecs_cluster.default.name} > /etc/ecs/ecs.config" tags { Name = "ecs-instance01" } + depends_on = ["aws_iam_instance_profile.web_instance_profile"] } resource "aws_instance" "ecs-instance02" { @@ -22,7 +23,7 @@ resource "aws_instance" "ecs-instance02" { subnet_id = "${aws_subnet.wp-public-tf.id}" key_name = "${var.key_name}" associate_public_ip_address = true - iam_instance_profile = "ecsInstanceRole" + iam_instance_profile = "${aws_iam_instance_profile.web_instance_profile.id}" security_groups = ["${aws_security_group.wp-ecs-sg-tf.id}"] user_data = "#!/bin/bash\necho ECS_CLUSTER=${aws_ecs_cluster.default.name} > /etc/ecs/ecs.config" tags { diff --git a/terraform/ecs.tf b/terraform/ecs.tf index 6bfc52e..139a782 100755 --- a/terraform/ecs.tf +++ b/terraform/ecs.tf @@ -13,11 +13,141 @@ resource "aws_ecs_service" "wp-ecs-svc" { task_definition = "${aws_ecs_task_definition.wordpress.arn}" desired_count = 2 - iam_role = "ecsServiceRole" + iam_role = "${aws_iam_role.qq-ecs-role.id}" load_balancer { elb_name = "${aws_elb.default.id}" container_name = "wordpress" container_port = 80 } -} \ No newline at end of file + depends_on = ["aws_iam_role.qq-ecs-role","aws_instance.ecs-instance01","aws_instance.ecs-instance02"] +} + +resource "aws_iam_role_policy" "qq-ecs-policy" { + name = "qq-ecs-policy" + role = "${aws_iam_role.qq-ecs-role.id}" + policy = <