Creating a portfolio from a state where I had no knowledge of AWS, personally deploying to AWS and automatic deployment of CircleCI were the most difficult, so a memorandum ・ I will create a portfolio from now on ・ For those who will use AWS from now on I thought it would be nice if there was an article.
An overview of the portfolio I created [AWS? Docker?] Summarize the necessary knowledge in your portfolio in an easy-to-understand manner [terraform? CircleCI?]
The predecessor of this article is [Part 1] WEB service created with Rails + Nuxt + MySQL + Docker is automatically tested and deployed with ECS / ECR / CircleCI and converted to terraform
I would appreciate it if you could take a look.
First: [Part 1] Automatically deploy WEB service created with Rails + Nuxt + MySQL + Docker with ECS / ECR / CircleCI and make it terraform Second: This article Third: [Part 2] Automatically deploy WEB service created with Rails + Nuxt + MySQL + Docker with ECS / ECR / CircleCI and make it terraform
--You can make basic network settings such as VPC, IGW, subnet, route table, and security group. --You can build a WEB service that allows SSL communication at all times using Route53, ACM, and ALB. --Can be deployed in a production environment using ECS / ECR. --You can use CircleCI to push to master and deploy automatically at the same time.
--You have already obtained an AWS account, IAM account, and key pair (you should be able to do it right away)
--aws-cli installed
--The region used is Tokyo (ap-northeast-1)
--Implementation is proceeding up to the previous article.
[Part 1] WEB service created with Rails + Nuxt + MySQL + Docker is automatically tested and deployed with ECS / ECR / CircleCI and converted to terraform
What you can do with this article is usually ** paid ** beyond the AWS free tier. Also, because you get a domain with your name.com, it also costs ** money. ** ** Please read only those who think that it is a study fee and can divide it. For the time being, I would like to publish a loosely coupled web service using ECS and ECR! For those who say.
Let's be enthusiastic!
CIDR block is an abbreviation for Classless Inter-Domain Routing. One of the notation methods of IP address. Simply put, it's the range of VPCs.
By the way, here it is a display like / 16
.
In CIDR notation, it is written as / 16
, / 24
, 32
.
/ 16
can specify up to the third octet.
For example, the 0.0
of 10.20.0.0
You can specify this part.
/24
can specify up to the 4th octet.
For example, the last 0
in 10.20.0.0
You can specify this part.
/ 32
can specify a specific IP address.
When creating a VPC, you have to secure an IP in the subnet or AZ inside, and the change does not work, so
In many cases, specify / 16
to secure many available IPs.
Here we use AZ ** ap-northeast-1a and ap-northeast-1c **. The reason for using the two AZs is that we will use ALB later.
Create the following subnet from Subnet> Create Subnet.
name | CIDR block | Region/AZ |
---|---|---|
qiita-sample-front-subnet-1a | 10.20.1.0/24 | ap-northeast-1a |
qiita-sample-front-subnet-1c | 10.20.2.0/24 | ap-northeast-1c |
qiita-sample-back-subnet-1a | 10.20.3.0/24 | ap-northeast-1a |
qiita-sample-back-subnet-1c | 10.20.4.0/24 | ap-northeast-1c |
qiita-sample-rds-subnet-1a | 10.20.5.0/24 | ap-northeast-1a |
qiita-sample-rds-subnet-1c | 10.20.6.0/24 | ap-northeast-1c |
Create the following route table from Route table> Create route table.
name |
---|
qiita-sample-front-route-table |
qiita-sample-back-route-table |
qiita-sample-rds-route-table |
Create the following Internet gateway from Internet Gateway> Create Internet Gateway.
Associate VPC and IGW.
name |
---|
qiita-sample-igw |
--Select the Edit tab of the route table and associate the IGW created earlier with 0.0.0.0 to the route table other than RDS. --Select the Subnet Association tab and associate as shown in the table below.
Subnet | Route table |
---|---|
qiita-sample-front-subnet-1a | qiita-sample-front-route-table |
qiita-sample-front-subnet-1c | qiita-sample-front-route-table |
qiita-sample-back-subnet-1a | qiita-sample-back-route-table |
qiita-sample-back-subnet-1c | qiita-sample-back-route-table |
qiita-sample-rds-subnet-1a | qiita-sample-rds-route-table |
qiita-sample-rds-subnet-1c | qiita-sample-rds-route-table |
Create security groups in the table below
Security group name | type | protocol | Port range | Source |
---|---|---|---|---|
qiita-sample-front-sg | HTTP | TCP | 80 | 0.0.0.0/0 |
Same as above | SSH | TCP | 22 | Your own IP |
Same as above | HTTPS | TCP | 443 | 0.0.0.0/0 |
Same as above | custom | TCP | 3000 | 0.0.0.0/0 |
qiita-sample-back-sg | custom | TCP | 3000 | 0.0.0.0/0 |
Same as above | HTTPS | TCP | 443 | 0.0.0.0/0 |
qiita-sample-rds-sg | MYSQL/Aurora | TCP | 3306 | qiita-sample-back-sg |
This is the end of the network.
Get the domain from Name.com. Please get the domain of your choice (preferably 1 yen) for those who are referring to this article.
For information on how to get a domain with your name.com, please refer to [Get a domain] Get a domain with your name.com and aws.
** * Turn off the automatic domain update setting! (It costs money every year) **
This time, in my environment, I got the following domain.
Domain name | role |
---|---|
qiita-sample.work | front |
qiita-sample-api.work | back |
Create a host zone from Route53.
From the host zone details, copy the ** value / traffic routing destination ** of the NS record.
It will take some time to reflect. (It may take up to 1 day)
dig qiita-sample.work(Acquired domain) +short NS
Is typed from the terminal, and if the NS record registered successfully is displayed, it is reflected.
Request a certificate from ACM. Select Request Public Certificate to request a certificate.
Domain name |
---|
qiita-sample.work |
*.qiita-sample.work |
Fill in the above domain name and select DNS validation. Similarly, do the same for qiita-sample-api.work.
Then, you will be taken to the verification screen, where you will ** create a record with Route53 **. (Create records in all of the above domains.) After a while, the verification will be pending and the status will be published. If the verification is pending for a long time, it is possible that the NS server settings are delayed. Let's check with the dig command.
It takes some time to set up Route53 and domain name server, so let's create RDS quickly here.
Please refer to the following settings and create RDS from RDS> Create Database.
Configuration | Remarks | |
---|---|---|
Engine type | MySQL | |
version | 5.7.30 | This time 5.7 |
template | development of/test | Free tier is fine, but this time for studying |
DB instance identifier | qiita-sample-rds | Anything is OK |
Master username | ** | Anything is OK |
Master password | ** | Anything is OK ・ Do not let it leak |
DB instance class information | db.t2.micro | I will keep it the cheapest |
Storage type | General purpose SSD | |
Storage allocation | 20 | 20 is the smallest |
Virtual Private Cloud (VPC) | The VPC I made earlier | |
Subnet group | 先ほど作ったSubnet group | |
Existing VPC security group | The security group you created earlier | |
Database authentication | Password authentication | |
First database name | production | |
DB parameter group | default | This time, set it to default. (You can make it yourself!) |
Option group | default | This time, set it to default. (You can make it yourself!) |
After a while, it will be available, so RDS creation is complete.
yml:./back/config/database.yml
production:
<<: *default
host: <%= Rails.application.credentials.rds[:host] %>
database: <%= Rails.application.credentials.rds[:database] %>
username: <%= Rails.application.credentials.rds[:username] %>
password: <%= Rails.application.credentials.rds[:password] %>
./
docker-compose run -e EDITOR="vi" back rails credentials:edit
Then, the vim screen will appear, so enter the following settings.
rds:
host: {end point}
database: {database name (created early)}
username: {Set user name}
password: {Password set}
Let's create a load balancer from EC2> Create load balancer.
front
back Register the VPC and subnet you created earlier in the Availability Zone.
Select the certificate you created earlier. If you haven't been authenticated yet, wait a minute.
Please attach the security group you created earlier.
Create a new target group. This time in front, to start in ** host mode ** in EC2 generated by ECS Set the target to the instance and accept the port at 3000. In back, the target is ip because it starts in ** awsvpc mode ** in the container in the instance of ECS. This refers to the container's private IP.
front
back
As we will see later, don't worry, the target will be selected automatically when the ECS service starts. Let's create a load balancer as it is!
From Route53> Host Zone, add A record and set as follows. Process front and back in the same way.
js:./front/plugins/axios.js
import axios from "axios"
export default axios.create({
//baseURL: "http://localhost:3000"
baseURL: "https://www.qiita-sample-api.work"
})
** * Let's use aws cli! ** ** Install AWS CLI version 1 on macOS Install AWS CLI
Create a repository from Amazon ECR. Please fill in the repository name and create it as it is. If you select a repository and press the push command, the command to push to the repository will be displayed, so create a repository for both front and back and execute it in the local environment.
When you execute these commands, an image will be created based on the Dockerfile in the directory. It will be in the same state as when docker-compose build is done in the local environment. Using the image created here and pushed to ECR, load it on ECS and start it.
Please note that ** front and back, please check which repository you want to push to, move to the directory where the Dockerfile is located, and then execute. ** **
In ECS
Cluster> Services> Tasks
It has become the particle size.
A cluster is a logical framework, a box.
The service is like auto scaling + ALB that starts on the cluster.
Automatically manage the number of tasks and configure networking settings.
Think of a task as a unit that runs a Docker image.
The service creates tasks based on the task definition. Forcibly, the taiyaki mold is a service, and the taiyaki itself is a task. The cluster is a stall of a Taiyaki restaurant.
This time I chose EC2 Linux + networking
For networking, select the VPC / subnet (front) you created earlier. Also, select Enabled for Auto assign public IP. Let's set the key pair so that you can connect with ssh.
Amazon EC2 Key Pairs and Linux Instances
After creating the cluster, if the EC2 instance is started as shown below, it is successful. Good thing If EC2 instances are not tied, security groups and route tables are often the problem.
Select Create new task definition from ECS
Select EC2
front
Configuration | Remarks | |
---|---|---|
Task definition name | qiita-sample-front | Anything is OK |
Task role | Roles that have permission to run ECS | |
Network mode | host | |
Task execution role | Roles that have permission to run ECS | |
Task memory(MiB) | 700 | Fit within the size of the instance |
Task CPU(unit) | 256 | Fit within the size of the instance |
back
Configuration | Remarks | |
---|---|---|
Task definition name | qiita-sample-back | Anything is OK |
Task role | Roles that have permission to run ECS | |
Network mode | aws-vpc | |
Task execution role | Roles that have permission to run ECS | |
Task memory(MiB) | 400 | Fit within the size of the instance |
Task CPU(unit) | 128 | Fit within the size of the instance |
Create a service from the service tag of the cluster.
front
Configuration | Remarks | |
---|---|---|
Launch type | EC2 | |
Task definition | What I created earlier | |
Service name | qiita-front | Anything is OK |
Service type | REPLICA | |
Number of tasks | 1 | 1~Anything above is OK |
For load balancing, select Application Load Balancer and select the ALB and target created earlier.
back
Configuration | Remarks | |
---|---|---|
Launch type | EC2 | |
Task definition | What I created earlier | |
Service name | qiita-back | Anything is OK |
Service type | REPLICA | |
Number of tasks | 1 | 1~Anything above is OK |
In the network configuration, select the VPC and back subnet security group you created earlier. For load balancing, select Application Load Balancer and select the ALB and target created earlier.
When created as above, one task should be added automatically. If it looks like the following, it's OK! The container should have been started inside EC2 from the image safely.
db:migrate
You can create a task definition and execute the task, but this time I will ssh connect to the EC2 instance, then go inside the docker container and try rails db: migrate
.
Originally, I had to create a DB (db: create), but this time I set RDS as production in the initial DB at the time of creation, and it is not necessary because it has already been created.
[Pem file DL](https://qiita.com/Shogo1222/items/dcbc1e50f6fc83f48b44#ecs%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF% E3% 83% BC% E3% 81% AE% E4% BD% 9C% E6% 88% 90)
ssh -i {pem file path} ec2-user@{Public IP of the instance}
__| __| __|
_| ( \__ \ Amazon Linux 2 (ECS Optimized)
____|\___|____/
For documentation, visit http://aws.amazon.com/documentation/ecs
In an EC2 instance
docker ps
Will display the currently running container. Specify the CONTAINER ID of the container running rails and type the following command to put it inside the Docker container.
In an EC2 instance
docker exec -it {CONTAINER ID} sh
Here, let's specify the environment and db: migrate!
Inside the Docker container
rails db:migrate RAILS_ENV=production
Thank you for your hard work!
ttps: // www. {your own front domain} / users It is OK if you can access and register the user as well as local, as shown in the screen below. ttps: // www. {Back domain set by myself} / users It is OK if you access and the json of the registered user is returned.
After pushing to master at the last, CircleCI, let's update the task definition automatically, update the ECS service, and make it possible to update the task.
Push from local to github
→ Start CircleCI from github
→ Run docker build on CircleCI
→ Push the image to ECR on CircleCI
→ Update ECS service ...
It will be.
The difference from building locally is the presence or absence of environment variables.
The build run on CircleCI is ** referencing the source code in the git repository ** and is also ** running in a virtual machine **. If the environment variable required to execute Rails does not exist in the git repository, the environment variable cannot be referenced and it will fail. However, CircleCI allows you to set environment variables on the virtual machine.
Project Settings>Environment Variables
AWS_ACCESS_KEY_ID //AWS access key
AWS_SECRET_ACCESS_KEY //AWS secret access key
AWS_ACCOUNT_ID //See URL below
AWS_ECR_ACCOUNT_URL //ECR URL example:{Account ID}.dkr.ecr.{region}.amazonaws.com
AWS_REGION //region
RAILS_MASTER_KEY // ./back/config/Master below.key value
AWS_RESOURCE_NAME_PREFIX //The prefix of the service I created (qiita this time-sample)
CLUSTER_NAME //The name of the Cluster created earlier
REPO_NAME_FRONT //front ECR repository name
REPO_NAME_BACK //back ECR repository name
FAMILY_NAME_FRONT //The name of the task definition of front
FAMILY_NAME_BACK //name of back task definition
SERVICE_NAME_FRONT //The name of the front ECS service created earlier
SERVICE_NAME_BACK //The name of the front ECS service created earlier
yml:./front/.circleci/config.yml
version: 2.1
orbs:
aws-ecr: circleci/[email protected]
aws-ecs: circleci/[email protected]
#Job to run
jobs:
#job to build
build:
machine:
image: circleci/classic:edge
steps:
- checkout
- run:
name: docker-compose build
command: docker-compose build
#job to test
test:
machine:
image: circleci/classic:edge
steps:
- checkout
- run:
name: docker-compose up -d
command: docker-compose up -d
- run: sleep 30
- run:
name: docker-compose run back rails db:create RAILS_ENV=test
command: docker-compose run back rails db:create RAILS_ENV=test
- run:
name: docker-compose run back rails db:migrate RAILS_ENV=test
command: docker-compose run back rails db:migrate RAILS_ENV=test
- run:
name: docker-compose run back bundle exec rspec spec
command: docker-compose run back bundle exec rspec spec
- run:
name: docker-compose down
command: docker-compose down
#Workflow to control the order
workflows:
build_and_test_and_deploy:
jobs:
- build
- test:
requires:
- build
- aws-ecr/build-and-push-image:
name: 'build-and-push-back'
account-url: AWS_ECR_ACCOUNT_URL
region: AWS_REGION
repo: ${REPO_NAME_BACK}
tag: "${CIRCLE_SHA1}"
path: './back'
dockerfile: back/Dockerfile.pro
extra-build-args: '--build-arg RAILS_MASTER_KEY=$RAILS_MASTER_KEY'
requires:
- test
filters:
branches:
only:
- master
- aws-ecr/build-and-push-image:
name: 'build-and-push-front'
account-url: AWS_ECR_ACCOUNT_URL
region: AWS_REGION
repo: ${REPO_NAME_FRONT}
tag: "${CIRCLE_SHA1}"
path: './front'
dockerfile: front/Dockerfile.pro
requires:
- test
filters:
branches:
only:
- master
- aws-ecs/deploy-service-update:
family: ${FAMILY_NAME_BACK}
service-name: ${SERVICE_NAME_BACK}
cluster-name: ${CLUSTER_NAME}
#"container="Please note that is the container name set in Task Definition.
container-image-name-updates: 'container=${AWS_RESOURCE_NAME_PREFIX}-back,image-and-tag=${AWS_ECR_ACCOUNT_URL}/${REPO_NAME_BACK}:${CIRCLE_SHA1}'
requires:
- build-and-push-back
filters:
branches:
only:
- master
- aws-ecs/deploy-service-update:
family: ${FAMILY_NAME_FRONT}
service-name: ${SERVICE_NAME_FRONT}
cluster-name: ${CLUSTER_NAME}
#"container="Please note that is the container name set in Task Definition.
container-image-name-updates: 'container=${AWS_RESOURCE_NAME_PREFIX}-front,image-and-tag=${AWS_ECR_ACCOUNT_URL}/${REPO_NAME_FRONT}:${CIRCLE_SHA1}'
requires:
- build-and-push-front
filters:
branches:
only:
- master
dockerfile:./back.Dockerfile.pro
#Specifying the image
FROM ruby:2.6.3-alpine3.10
#Download required packages
ENV RUNTIME_PACKAGES="linux-headers libxml2-dev make gcc libc-dev nodejs tzdata mysql-dev mysql-client yarn" \
DEV_PACKAGES="build-base curl-dev" \
HOME="/app" \
LANG=C.UTF-8 \
TZ=Asia/Tokyo
#Move to working directory
WORKDIR ${HOME}
#Copy the necessary files from the host (files on your computer) to Docker
ADD Gemfile ${HOME}/Gemfile
ADD Gemfile.lock ${HOME}/Gemfile.lock
RUN apk update && \
apk upgrade && \
apk add --update --no-cache ${RUNTIME_PACKAGES} && \
apk add --update --virtual build-dependencies --no-cache ${DEV_PACKAGES} && \
bundle install -j4 && \
apk del build-dependencies && \
rm -rf /usr/local/bundle/cache/* \
/usr/local/share/.cache/* \
/var/cache/* \
/tmp/* \
/usr/lib/mysqld* \
/usr/bin/mysql*
#Copy the necessary files from the host (files on your computer) to Docker
ADD . ${HOME}
#Open port 3000
EXPOSE 3000
#Execute command
CMD ["bundle", "exec", "rails", "s", "puma", "-b", "0.0.0.0", "-p", "3000", "-e", "production"]
dockerfile:./front/Dockerfile.pro
FROM node:12.5.0-alpine
ENV HOME="/app" \
LANG=C.UTF-8 \
TZ=Asia/Tokyo
ENV HOST 0.0.0.0
WORKDIR ${HOME}
COPY package.json .
COPY . .
RUN apk update && \
apk upgrade && \
npm install -g n && \
yarn install &&\
rm -rf /var/cache/apk/*
RUN yarn run build
EXPOSE 3000
CMD ["yarn", "start"]
yml:./docker-compose.yml
version: "3"
services:
db:
image: mysql:5.7
env_file:
- ./back/environments/db.env
restart: always
volumes:
- db-data:/var/lib/mysql:cached
back:
build: back/
# rm -f tmp/pids/server.Useful when you fail to erase the rails server with pid
command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
env_file:
- ./back/environments/db.env
environment: #add to
RAILS_MASTER_KEY: ${RAILS_MASTER_KEY}
volumes:
- ./back:/app:cached
depends_on:
- db
#Host computer port: Port in Docker
ports:
- 3000:3000
front:
build: front/
command: yarn run dev
volumes:
- ./front:/app:cached
ports:
#Host computer port: Port in Docker
- 8080:3000
depends_on:
- back
volumes:
public-data:
tmp-data:
log-data:
db-data:
Thank you for your hard work! As shown below, if all the processes are successful, the deployment is completed successfully.
Those who want to learn VPC, AWS network, etc. AWS course learned with video VPC [section1] Region, Availability Zone, Subnet
Those who want to know more about Docker's network and ECS Docker course to catch up from now on! Aim for container master with AWS ECS and Fargate! ~ First series ~
It's a really convenient time to learn from videos ... I repeated it about 30 times.
Thank you for your hard work. Now you can deploy Nuxt and Rails to production using ECS / ECR / CircleCI. This is just an example, and I think there are other good configurations. However, once you build and debug it, you'll know how to do it yourself. (I still have more Maybe there are typos or mistakes in this article. In such a case, I would appreciate it if you could gently throw Masakari.
Next time I wish I could write up to terraform.
Recommended Posts