So to deploy our pushed React front end
to ECS, I'll go back to my clusters
or to my Task Definitions,
go to my latest task revision here
and create a new one
because I now wanna add a new container.
And therefore, here, in the Container Definitions part,
we click Add container.
And give it any name of your choice.
I'll name it goals-frontend
and then we wanna use the image we just pushed.
In my case academind/goals-react.
Of course, use your repository name here.
Now, we wanna map port 80
because this image does expose port 80, right?
That's what we set up there.
So we wanna map that here.
We don't need to do anything special here
for the health checks.
For environment variables,
we also don't need to pass anything in
but we can actually do something here
on the Startup Dependency Ordering.
We could have done that before with Node
and the Mongo container as well
but now I would say it also makes sense.
We could ensure that goals-backend should be launched first
so that this is running successfully.
That it was started successfully
before we start this container
because this application won't really work,
this front end won't really work
if there is no backend to talk to.
You don't need to add this
but I wanna show you that you can configure this
on ECS as well.
So that's why I'm doing it here.
Now, we don't need any network settings here.
And we don't need any volumes
and therefore, we can click Add.
Now this container definition is added
and now with that, we can create this revision.
But you'll notice that we now have a problem here.
This button is grayed out
and we can see the reason for this problem up there.
We're mapping two containers to port 80.
Both goals-backend and goals-frontend
are mapped to port 80.
And within the same task,
this is not possible.
Only one container may be listening to port 80,
which makes sense I guess.
How would AWS and the load balancer know
to which container a request
should be forwarded otherwise, right?
Now, there are a couple of things you could do
to ensure that this all works again.
We could try to merge backend and frontend
into one container.
After all, our backend already has a Node server,
so we would also use this node server
to serve our front end application.
We could also expose the backend on a different port
so that we don't have a port clash.
But I don't like the different port idea
because having a port other than 80 is a bit confusing.
And having one merged container
instead of two would be absolutely fine
but I wanna also show you how you can build
and deploy multiple containers.
So I will stick to this standalone frontend container.
But still, there won't be a way of hosting them both
in the same ECS task.
And this is not as easy as restriction.
This is something, which technically can't work.
You can't have two web servers on the same host.
And that is what we would have.
Our Node backend spins up its own web server,
which listens on port 80.
And our Nginx frontend does the same.
And two web servers on the same host technically won't work.
So here, when we talk about ECS,
we therefore need different tasks.
So we need to create a new task definition
in addition to the goals task definition we already have.
Now, I will stick to Fargate here.
And name this one goals-react or goals-frontend.
I will use the same task role as I did for the backend.
And I will also assign the minimum amount
of CPU and memory here.
Now, it's here where I wanna add my goals-react container,
so this academind/goals-react container we pushed
to Docker Hub before.
Here, I now wanna map port 80
and here, since it's a new task
and a new process, this will work.
This also means that this task,
this service which will run the task later
will have it's own URL.
We'll now have two different URLs for frontend and backend.
Now, we can leave all the other settings here
the way they are.
And click Add.
And we can now create this task here.
So now we can view this task definition
and create a service based on this task.
But before we do that,
let's actually briefly go back to our code.
I just mentioned that if we now do use two different tasks,
we'll have two different URLs.
And that means that my solution,
which was to omit the domain here
and just rely on this being sent to localhost
won't work anymore because the frontend will now be served
on a different URL than the backend.
So we can't let the browser find that URL automatically.
And as a side note,
this also wouldn't have worked in development locally
when we spin this up in a separate container
than the backend.
And therefore, this runs on a different port
than the backed, port 3000 during development to be precise.
So we need to add a URL back into our code.
And it should be a flexible URL
because it will differ based on the environment
in which we're running.
Now, one important thing to realize here
is that we can't use Docker environment variables
in the React code
because this code is not executed inside
of a Docker container but in a browser.
We can use environment variables in that code
but it's the React build process
which needs to inject them.
And thankfully, this React project setup
supports environment variables.
It just manages them on its own detached from Docker
for the reasons I just explained.
So therefore, we can leverage environment variables here
and we can switch between different URLs here based
on the environment in which this code is being executed
and we could add a constant here
at the top of this file
and name this backendUrl
and this could now hold one of two values based
on the environment
in which this code executes.
And there is a process.env.NODE_ENV environment variable,
which is automatically available,
provided by this React build process.
So provided by the npm start script
and the npm run build script.
And this holds a value of development
if we executed npm start
and a value of production
if we executed the build script.
So we can check if that's development,
if the value in there is development
and in that case, and this is some JavaScript code.
In case you don't know it,
you can just copy and paste it essentially.
So in that case, if we have development as a mode,
I wanna set the URL to
localhost like this,
meaning localhost port 80,
which will refer to the backend container,
which is executed on that host in development.
Otherwise, in production,
we wanna set it to the URL we're going
to run our backend on here on AWS ECS.
And therefore, we need to grab that backend URL
from the load balancer we already created in the past
and we can already create a new load balancer
for the frontend, which will run in its own task
and which will therefore also need its own unchangeable URL
instead of that IP address,
which changes every time we deploy a new task.
So open the EC2 page in a new browser tab
and then here on load balancers,
let's actually add a new load balancer again.
Create a new application load balancer
and I'm going to name it goals-react-lb
and it's internet facing.
This all looks good.
I wanna expose this port.
I wanna add it to the same VPC as my other load balancer
because it will be part of the same cluster.
My new task will be part of the same cluster.
We wanna have the same settings here.
Move on to the next settings.
Attach it to the same security group
as my other load balancer is attached to
and create a new target group here,
which I'm going to name react-tg
and that's the group
to which ECS will automatically add the deployed containers.
The target type should be IP.
And the health check path here may be slash nothing
because the React app will return a valid response
on this path.
With that, we can go to Register Targets
and not enter anything here.
Review this and create this load balancer.
And now that we've got this load balancer created,
we can go to that load balancer configuration
and find its URL here.
And that's the URL which we can later use
to reach our React application.
Now, for our React application code,
we need the backend URL though.
And that's the load balancer we already got.
We will still need the second one though,
so creating it is already a good idea.
But grab the URL, this DNS name
from the first load balancer, which we created a couple
of lectures ago already,
copy that and use that as your production domain.
So that should be http and then this domain.
And with this, I've got this backendUrl,
which can now be used in all these requests
and it will hold the development
or the production URL depending
on this automatically set environment variable,
which is set by the React build process.
So now in all the places where a request is sent,
we wanna use the backendUrl in front of this path,
which is then added thereafter.
So use this here.
Use it here.
And there must be one other request.
Here.
We wanna use backendUrl here as well.
So now with that, we're setting this.
We now also already crated all the load balancers we need
and now we can finally rebuild this frontend image,
push it to Docker Hub
and then start our service based
on the task that uses that image.
So let's rebuild this frontend image
with the docker build command pointing
at the Dockerfile.prod file.
And push the updated image to Docker Hub thereafter.
Using that updated source code in the end.
And now with that, we can close the EC2 Management Console,
go back to ECS.
There we already created our task
and now we can create a service based on that task.
We wanna use Fargate as our launch type.
Use our goals-app cluster.
Give this service a name of goals-react.
Set the number of tasks to one.
Use rolling updates.
And on the next page,
we wanna add the two subnets we have
in this VPC our cluster provides.
On security group, we can actually edit this
and use an existing one,
which already is exposing port 80,
which is exactly what we need here.
We wanna have auto-assign public IP enabled.
Then add an application load balancer here
and choose that goals-react load balancer we just created.
And yep, click Add to load balancer here.
Choose the React target group under Target group name.
We'll leave all the other settings the way they are.
Click Next step.
Leave the settings here.
Click Next step and create this service.
And this will now start our frontend container
and frontend task therefore in a new service,
which now runs side by side
with our goals service, which is the backend.
So now we'll have that frontend service up and running too.
At least soon we'll have that.
And if everything worked correctly,
we should be able to use this service
to then send requests to our backend.
So let's wait for this to be up and running
and let's then use this load balancer URL
of the newly created load balancer
because whilst the IPO of our task will change
with every new deployment, you already learned
that load balancers help you get an address,
which never changes.
And of course, that address can also be bound
to any domain of your choice then.
And if I hit enter here.
That's looking good.
I see my goals app
and it already loads that goal,
which we added manually with Postman
to our backend before.
So if I now add another goal here,
that's also showing up
and it's also there if I reload.
And I can also delete a goal.
And that all works
and this application, therefore works as intended.
So with that,
we now successfully deployed another container.
In this case, since it also exposes a web server
in a different task,
if it wouldn't expose a web server,
if it would just be another container working together
with our Node API, for example,
if we had it before with our MongoDB container
before we switched to MongoDB Atlas,
we could have also deployed it in the same task
as the backend.
But since we now have two web servers,
we need two different tasks and services therefore,
and therefore, we have two different domains.
But this is how we can deploy this
and therefore, this is also an example
how multi-container projects could be split up
across multiple hosts, multiple tasks in this case,
with multiple URLs
and how you could implement that with AWS ECS
and which problem you should keep in mind in such cases.