Deploying Yii2 Application to Amazon ECS and AWS Fargate using AWS CDK - Building Modern PHP/Yii2 Application using AWS
Tags: aws, container, php, yii2
Published: 2019-12-24 11:00:02

This is my first tutorial posted at **dev.to**. I am currently building a series of tutorial on **Building Modern PHP/Yii2 Application using AWS** posted in [my Personal Blog](https://blog.petrabarus.net/) but in Indonesian language. Since I am now trying to improve my English, I will post the translated version here.

The series will follow a journey building a Yii2-based application from scratch to full application using AWS services. In this case we will use [Amazon Elastic Container Service](https://aws.amazon.com/ecs/) and [AWS Fargate](https://aws.amazon.com/fargate/) to host the application. We also will use [AWS Cloud Development Kit](https://aws.amazon.com/cdk/) to define the AWS resources we need to run the application. The application will follow some best practices and opinions from my personal experience.

I assume the readers already have AWS account and knows a bit about AWS, Docker, and PHP. Let's get started.

Here's the software that I used when building this tutorial

- AWS CLI 1.16
- PHP 7.3
- Composer 1.19
- Node 10
- Node Package Manager 6.9
- AWS CDK 1.19
- Docker 19.03
- Docker Compose 1.25

## Installing Yii2 Basic App Template

In this series, I will build the Yii2 application using the [`yii2-app-basic`](https://github.com/yiisoft/yii2-app-basic) template. As suggested by the name, it's a basic template that contains one single web application with small functionalities like home page, menu, login, and register.

To install the template, execute the command below.

```bash
composer create-project --prefer-dist yiisoft/yii2-app-basic myapp
```

We will now have a fresh Yii2 application project in `myapp` directory.

Let's try to run the application in local. In the `myapp` directory, execute the command below.

```bash
docker-compose up
```

This command will take a while to download the container image for running the application. It will then show logging line like this.

![docker log result](http://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-24-at-4.20.22-PM.png)

You can now access the application through URL `http://127.0.0.1:8000`. It should look like this.

![screenshot 1](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-22-at-10.33.03-AM-2048x1141.png)

## Running AWS CDK

### Adding CDK Script

To run the CDK you will need the scripts and some configurations. I already made a patch that you can apply on your project. Execute the command below.

```bash
curl -L -O http://bit.ly/modern-yii2-app_part-1_patch
patch -p1 < modern-yii2-app_part-1_patch
```

This patch will installs 9 files; 3 files for NodeJS and Typescript configuration, 3 files for Docker configuration, 1 `.gitignore`, and we have only 2 for the CDK itself.

![the patch files](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-22-at-7.47.14-PM.png)

### Installing Requirements

After the scripts are patched, we now need to install Node libraries. Execute the command below.

```bash
npm install
```

To see if the CDK can be run, execute the command below.

```bash
cdk ls
```

The command will show a text `WebStack`. Up to this point, you are in the correct path. Please let me know if you have difficulty so far.

Because the current sample I wrote need a staging environment for the CDK itself to store data and configurations, we need to do bootstrapping first. The command below will try to create S3 bucket required for the CDK.

```
cdk bootstrap
```

It will show something like this.

![bootstrap](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-22-at-8.55.34-PM-2048x358.png)

### Deployment

Now we try to deploy the application into AWS. You simply execute the command below.

```bash
cdk deploy
```

The command will show first changes of configuration that will be made, such as IAM and Security Group. It will ask you if you agree with the change or not. The configurations will affect the resources that you will provision, not existing in your account. Press `y` to continue.

![confirmation](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-22-at-9.11.21-PM-2048x605.png)

We are now going to the deployment process. This process can take some time. It will first build the docker image locally and push to Amazon Elastic Container Repository. For Internet connection in Indonesia, this can take really while, that is why in the next series I will write about how to run the process in build server.

![build image](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-22-at-9.14.43-PM-2048x810.png)

It then will provision the required resources like VPC, Subnets, ECS Clusters, and Load Balancer.

When it finished, the command will show the URL to the load balancer.

![terminal shows finish](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-22-at-9.25.42-PM.png)

Open the URL and see that the Yii2 application is now deployed.

![browser showing the app](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-22-at-9.31.16-PM.png)

If we open the [Amazon ECS console](https://console.aws.amazon.com/ecs/home), we can see a new ECS cluster has been provisioned.

![ecs console](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-23-at-11.15.36-AM-2048x1047.png)

We can also see the service and task created after we opened the details for the cluster. It is displayed in the screenshot below that we now have 1 Fargate task to run the PHP application.

![ecs task screenshot](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-23-at-11.18.44-AM-2048x967.png)

### Redeploying

The command `cdk deploy` can also be executed again after we modified the code, say updating the PHP, HTML, JS, etc. Because the resources have been provisioned in the first deployment, the subsequent deployments will not take as much time unless you add new resources.

### Deleting Resource

To avoid additional cost incurring, we need to destroy the resource if we don't need it again. We simply execute command below.

```bash
cdk destroy
```


## Explanation

From all the files that we patched earlier, the one first one I like to explain is the `app.ts`. This Typescript file declares the infrastructure that we want to provision to run the application.

```typescript
#!/usr/bin/env node
import 'source-map-support/register';
import cdk = require('@aws-cdk/core');
import ecs = require("@aws-cdk/aws-ecs");
import ecsPatterns = require("@aws-cdk/aws-ecs-patterns");

class WebStack extends cdk.Stack {
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
        this.createECSCluster();
    }

    createECSCluster() {
        const ecsCluster = new ecs.Cluster(this, 'ecsCluster');
        new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'Service', {
            cluster: ecsCluster,
            taskImageOptions: {
                image: ecs.ContainerImage.fromAsset('.')
            },
        });
    }
}

const app = new cdk.App();
new WebStack(app, 'WebStack');

app.synth();
```

Today AWS CDK only supports Javascript/Typescript, Python, Java, and .NET. There is no support for PHP yet. But you want to see AWS CDK in PHP as much as I do, please send your vote in this [Github issue](https://github.com/aws/aws-cdk/issues/548).
 
With very simple code shown above we can have application with complete architecture similar to below.

![architecture](https://blog.petrabarus.net/wp-content/uploads/2019/12/Slide5-1024x647-1.png)

Image taken from blog post [**Task Networking in AWS Fargate**](https://aws.amazon.com/blogs/compute/task-networking-in-aws-fargate/)

Throughout the process, the code will be synthesized to CloudFormation template that can reach 700 lines like [this](https://gist.github.com/petrabarus/39153972b153c96a69fc579dd534f6e5). To see the generated CloudFormation template, execute the command below.

```bash
cdk synth
```

### Construct

Now let us start with a simple line taken from `app.ts`.

```typescript
const ecsCluster = new ecs.Cluster(this, 'ecsCluster');
```

The code above shows we are instantiating class `ecs.Cluster`. In AWS CDK, this is called as **Construct**. Construct is a building block that represents one or more components. It is useful to encapsulate multiple cloud components to be reusable as one unit. When building more complex architecture we can build a construct that composes multiple construct.

### Stack

Next we will see the class `WebStack`.

```typescript
export class WebStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    //....
  }
  //....
}
```

In this part, we define what we call as **Stack**. A stack is unit that defines the CloudFormation stack that we need to provisioned the resources. You can see in the [CloudFormation console](https://aws.amazon.com/cloudformation/home), that we have a new stack called `WebStack`, just like the one we instantiate.

![cloudformation](https://blog.petrabarus.net/wp-content/uploads/2019/12/Screen-Shot-2019-12-23-at-9.49.19-AM-2048x519.png)

### App

In the last three lines, we instantiate what we call as **App**.

```typescript
const app = new cdk.App();
new WebStack(app, 'WebStack');

app.synth();
```

The app will represent CDK application that we are building and defines the scope of the Stacks. An App can have more than one Stack. This is very useful to separate multiple parts of an application, for example the web stack and worker stack. In the code sample below we defines two stacks of which we name `MyStack1` and `MyStack2` respectively.

```typescript
const app = new cdk.App();
new Stack1(app, 'MyStack1');
new Stack2(app, 'MyStack2');
app.synth();

```

If we execute `cdk ls`, we can see the two stacks will be displayed. It is also possible to deploy the two stacks in separate commands.

```text
$cdk deploy MyStack1
$cdk deploy MyStack2
```

### High Level Constructs

In the code `app.ts`, class `ApplicationLoadBalancedFargateService` is also a construct but represents very complex architecture. A construct can compose of many other construct. We can imagine that the `ApplicationLoadBalancedFargateService` can have other constructs for VPC, Subnet, Internet Gateway, NAT Gateway, ENI, Load Balancer, and Fargate. By using that construct, we don't have to worry about orchestrating all of the other constructs. We only need to instantiate the construct and give some parameters.

```typescript
 const ecsCluster = new ecs.Cluster(this, 'ecsCluster');
        new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'Service', {
            cluster: ecsCluster,
            taskDefinition: {
                cpu: 512,
                memoryLimitMiB: 1024
            },
            taskImageOptions: {
                image: ecs.ContainerImage.fromAsset('.')
            },
        });
```
The code above shows that we gives parameters to Fargate task specifying that we need CPU with 512 units and memory 1024 MiB.

Class `ApplicationLoadBalancedFargateService` is just an example of a construct already provided by `aws-ecs-patterns` library. The library also provides other patterns for ECS that we can choose. If we need EC2 for compute rather than Fargate, we can use `ApplicationLoadBalancedEc2Service`. Or if we choose to use Network Load Balancer instead of Application Load Balancer, because we are building games using UDP for protocol, we can use `NetworkLoadBalancedFargateService`. You can see the list of the patterns [here](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-ecs-patterns-readme.html).

### Decomposing Using Construct

In the future we will definitely add more resource definition into the CDK code. Construct will help us to decompose the code so our code will stay simple and readable. The decomposition will further enable us for reusing the piece of code and even share it to other developers in form of libraries.

For example, the part in the `app.ts` where we define the ECS cluster and fargate can be refactor into new Construct, let's say `WebECSCluster`. We can also move the class to other file so our main file `app.ts` will stay short.

```typescript
class WebECSCluster extends cdk.Construct {
    constructor(scope: cdk.Construct) {
        super(scope, "WebECSCluster");

        const ecsCluster = new ecs.Cluster(this, 'ecsCluster');
        new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'Service', {
            cluster: ecsCluster,
            taskImageOptions: {
                image: ecs.ContainerImage.fromAsset('.')
            },
        });
    }
}


class WebStack extends cdk.Stack {
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
        new WebECSCluster(this);
    }
}
```

That is all for now. Please drop your comments or questions in the comment section below. I'd love to hear what you think I can improve in the next articles!

## References

You can learn more about AWS CDK here

- [Getting Started With The AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html)
- [AWS CDK Examples](https://github.com/aws-samples/aws-cdk-examples/)
- [AWS CDK Github Page](https://github.com/aws/aws-cdk)