$ rvm use ruby-2.5.0@global --create \
&& gem install --no-rdoc --no-ri bundler \
&& gem install --no-rdoc --no-ri rails -v "5.1.2"
$ rvm use ruby-2.5.0@global --create \
&& gem install --no-rdoc --no-ri bundler \
&& gem install --no-rdoc --no-ri rails -v "5.1.2"
Using /Users/erinswenson-healey/.rvm/gems/ruby-2.5.0 with gemset global
Successfully installed bundler-1.16.1
1 gem installed
Successfully installed rails-5.1.2
1 gem installed
$ rails new websvc -d postgresql --skip-yarn --skip-spring \
&& cd websvc \
&& bundle \
&& rails generate scaffold Post title:string content:text
$ rails new websvc -d postgresql --skip-yarn --skip-spring \
&& cd websvc \
&& bundle \
&& rails generate scaffold Post title:string content:text
create
create README.md
create Rakefile
create config.ru
create .gitignore
$ rails new websvc -d postgresql --skip-yarn --skip-spring \
&& cd websvc \
&& bundle \
&& rails generate scaffold Post title:string content:text
create
create README.md
create Rakefile
create config.ru
create .gitignore
[...]
Using turbolinks 5.1.0
Using uglifier 4.1.5
Using web-console 3.5.1
Bundle complete! 14 Gemfile dependencies, 68 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
$ rails new websvc -d postgresql --skip-yarn --skip-spring \
&& cd websvc \
&& bundle \
&& rails generate scaffold Post title:string content:text
create
create README.md
create Rakefile
create config.ru
create .gitignore
[...]
Using turbolinks 5.1.0
Using uglifier 4.1.5
Using web-console 3.5.1
Bundle complete! 14 Gemfile dependencies, 68 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
[...]
invoke scss
create app/assets/stylesheets/posts.scss
invoke scss
create app/assets/stylesheets/scaffolds.scss
# ./websvc/Dockerfile
FROM ruby:2.5.0-alpine
RUN apk add --update postgresql-dev alpine-sdk nodejs tzdata
# install gems
COPY Gemfile* /opt/bundle/
WORKDIR /opt/bundle
RUN bundle update && bundle install
# copy app sources
COPY . /opt/app
WORKDIR /opt/app
# define flexible entrypoint
# usage: docker run -it foo:latest "rails migrate && rails server"
ENTRYPOINT ["/bin/ash", "-c"]
# ./websvc/Dockerfile
FROM ruby:2.5.0-alpine
RUN apk add --update postgresql-dev alpine-sdk nodejs tzdata
# install gems
COPY Gemfile* /opt/bundle/
WORKDIR /opt/bundle
RUN bundle update && bundle install
# copy app sources
COPY . /opt/app
WORKDIR /opt/app
# define flexible entrypoint
# usage: docker run -it foo:latest "rails migrate && rails server"
ENTRYPOINT ["/bin/ash", "-c"]
# ./websvc/Dockerfile
FROM ruby:2.5.0-alpine
RUN apk add --update postgresql-dev alpine-sdk nodejs tzdata
# install gems
COPY Gemfile* /opt/bundle/
WORKDIR /opt/bundle
RUN bundle update && bundle install
# copy app sources
COPY . /opt/app
WORKDIR /opt/app
# define flexible entrypoint
# usage: docker run -it foo:latest "rails migrate && rails server"
ENTRYPOINT ["/bin/ash", "-c"]
# ./websvc/Dockerfile
FROM ruby:2.5.0-alpine
RUN apk add --update postgresql-dev alpine-sdk nodejs tzdata
# install gems
COPY Gemfile* /opt/bundle/
WORKDIR /opt/bundle
RUN bundle update && bundle install
# copy app sources
COPY . /opt/app
WORKDIR /opt/app
# define flexible entrypoint
# usage: docker run -it foo:latest "rails migrate && rails server"
ENTRYPOINT ["/bin/ash", "-c"]
# ./websvc/Dockerfile
FROM ruby:2.5.0-alpine
RUN apk add --update postgresql-dev alpine-sdk nodejs tzdata
# install gems
COPY Gemfile* /opt/bundle/
WORKDIR /opt/bundle
RUN bundle update && bundle install
# copy app sources
COPY . /opt/app
WORKDIR /opt/app
# define flexible entrypoint
# usage: docker run -it foo:latest "rails migrate && rails server"
ENTRYPOINT ["/bin/ash", "-c"]
# ./websvc/Dockerfile
FROM ruby:2.5.0-alpine
RUN apk add --update postgresql-dev alpine-sdk nodejs tzdata
# install gems
COPY Gemfile* /opt/bundle/
WORKDIR /opt/bundle
RUN bundle update && bundle install
# copy app sources
COPY . /opt/app
WORKDIR /opt/app
# define flexible entrypoint
# usage: docker run -it foo:latest "rails migrate && rails server"
ENTRYPOINT ["/bin/ash", "-c"]
$ docker build . -t demo:latest
$ docker build . -t demo:latest
$ docker build . -t demo:latest
Sending build context to Docker daemon 140.3kB
Step 1/8 : from ruby:2.5.0-alpine
---> 308418a1844f
Step 2/8 : RUN apk add --update postgresql-dev alpine-sdk nodejs tzdata
---> Using cache
---> e7e55a707ca1
Step 3/8 : COPY Gemfile* /opt/bundle/
---> Using cache
---> 0942e5df00cf
Step 4/8 : WORKDIR /opt/bundle
---> Using cache
---> cd6ad4f315fc
$ docker build . -t demo:latest
Sending build context to Docker daemon 140.3kB
Step 1/8 : from ruby:2.5.0-alpine
---> 308418a1844f
Step 2/8 : RUN apk add --update postgresql-dev alpine-sdk nodejs tzdata
---> Using cache
---> e7e55a707ca1
Step 3/8 : COPY Gemfile* /opt/bundle/
---> Using cache
---> 0942e5df00cf
Step 4/8 : WORKDIR /opt/bundle
---> Using cache
---> cd6ad4f315fc
Step 5/8 : RUN bundle install
---> Using cache
---> fd49623c082c
Step 6/8 : COPY . /opt/app
---> a1734eb5074d
Step 7/8 : WORKDIR /opt/app
Removing intermediate container 92bf3ce950fa
---> ff996b3b426f
Step 8/8 : ENTRYPOINT ["/bin/ash", "-c"]
---> Running in 4258e14bcab6
Removing intermediate container 4258e14bcab6
---> a89cc7eda27f
$ docker build . -t demo:latest
Sending build context to Docker daemon 140.3kB
Step 1/8 : from ruby:2.5.0-alpine
---> 308418a1844f
Step 2/8 : RUN apk add --update postgresql-dev alpine-sdk nodejs tzdata
---> Using cache
---> e7e55a707ca1
Step 3/8 : COPY Gemfile* /opt/bundle/
---> Using cache
---> 0942e5df00cf
Step 4/8 : WORKDIR /opt/bundle
---> Using cache
---> cd6ad4f315fc
Step 5/8 : RUN bundle install
---> Using cache
---> fd49623c082c
Step 6/8 : COPY . /opt/app
---> a1734eb5074d
Step 7/8 : WORKDIR /opt/app
Removing intermediate container 92bf3ce950fa
---> ff996b3b426f
Step 8/8 : ENTRYPOINT ["/bin/ash", "-c"]
---> Running in 4258e14bcab6
Removing intermediate container 4258e14bcab6
---> a89cc7eda27f
Successfully built a89cc7eda27f
Successfully tagged demo:latest
# ./config/database.yml
default: &default
adapter: postgresql
encoding: unicode
development:
<<: *default
url: <%= ENV['DATABASE_URL'] %>
test:
<<: *default
url: <%= ENV['DATABASE_URL'] %>_test
production:
<<: *default
url: <%= ENV['DATABASE_URL'] %>
$ createuser --createdb foouser \
&& createdb --owner foouser foodb
$ docker run -it \
-v $(pwd):/opt/app \
-p 3333:3000 \
-e RAILS_ENV=development \
-e DATABASE_URL=postgresql://foouser@docker.for.mac.host.internal/foodb \
demo:latest "rails db:migrate && rails server -b 0.0.0.0"
$ docker run -it \
-v $(pwd):/opt/app \
-p 3333:3000 \
-e RAILS_ENV=development \
-e DATABASE_URL=postgresql://foouser@docker.for.mac.host.internal/foodb \
demo:latest "rails db:migrate && rails server -b 0.0.0.0"
$ docker run -it \
-v $(pwd):/opt/app \
-p 3333:3000 \
-e RAILS_ENV=development \
-e DATABASE_URL=postgresql://foouser@docker.for.mac.host.internal/foodb \
demo:latest "rails db:migrate && rails server -b 0.0.0.0"
$ docker run -it \
-v $(pwd):/opt/app \
-p 3333:3000 \
-e RAILS_ENV=development \
-e DATABASE_URL=postgresql://foouser@docker.for.mac.host.internal/foodb \
demo:latest "rails db:migrate && rails server -b 0.0.0.0"
$ docker run -it \
-v $(pwd):/opt/app \
-p 3333:3000 \
-e RAILS_ENV=development \
-e DATABASE_URL=postgresql://foouser@docker.for.mac.host.internal/foodb \
demo:latest "rails db:migrate && rails server -b 0.0.0.0"
$ docker run -it \
-v $(pwd):/opt/app \
-p 3333:3000 \
-e RAILS_ENV=development \
-e DATABASE_URL=postgresql://foouser@docker.for.mac.host.internal/foodb \
demo:latest "rails db:migrate && rails server -b 0.0.0.0"
=> Booting Puma
=> Rails 5.1.4 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.11.2 (ruby 2.5.0-p0), codename: Love Song
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
# image-repository.yml
Resources:
Repository:
Type: AWS::ECR::Repository
Outputs:
RepositoryUri:
Value:
Fn::Join:
- ""
- - !Ref AWS::AccountId
- ".dkr.ecr."
- !Ref AWS::Region
- ".amazonaws.com/"
- !Ref Repository
$ aws cloudformation deploy \
--stack-name demorepo \
--template-file ./image-repository.yml
$ aws cloudformation deploy \
--stack-name demorepo \
--template-file ./image-repository.yml
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - demorepo
$ eval $(aws ecr get-login --no-include-email)
$ eval $(aws ecr get-login --no-include-email)
WARNING! Using --password via the CLI is insecure. Use
--password-stdin.
Login Succeeded
$ docker tag demo:latest ${REPOSITORY_URI}:latest \
&& docker push ${REPOSITORY_URI}:latest
$ docker tag demo:latest ${REPOSITORY_URI}:latest \
&& docker push ${REPOSITORY_URI}:latest
The push refers to repository [166875342547.dkr.ecr.us-east-1.amazonaws.com/demor-repos-1as4v]
9354d709b983: Pushed
d768dd910be3: Pushing [============> ] 24.28MB/98.1MB
488a4eca2037: Pushed
602c1e0aeed9: Pushing [=====> ] 26.3MB/222.3MB
838e7becd078: Pushed
61cb6c204d39: Pushing [=======================> ] 26.95MB/56.56MB
e9bcacee1741: Pushed
cd7100a72410: Pushing [==================================================>] 4.403MB
$ docker tag demo:latest ${REPOSITORY_URI}:latest \
&& docker push ${REPOSITORY_URI}:latest
The push refers to repository [166875342547.dkr.ecr.us-east-1.amazonaws.com/demor-repos-1as4v]
9354d709b983: Pushed
d768dd910be3: Pushed
488a4eca2037: Pushed
602c1e0aeed9: Pushed
838e7becd078: Pushed
61cb6c204d39: Pushed
e9bcacee1741: Pushed
cd7100a72410: Pushed
latest: digest: sha256:d08ab6bbda1aa90bd76a035a8b262b66b21d2c1430cb27a745914fb88cce size: 1995
# database.yml
Resources:
Database:
Type: AWS::RDS::DBInstance
Properties:
AllocatedStorage: 5
BackupRetentionPeriod: 0
DBInstanceClass: db.t2.micro
DBName: foodb
Engine: postgres
EngineVersion: 9.6.5
MasterUsername: foouser
MasterUserPassword: foopassword
PubliclyAccessible: true
Outputs:
DatabaseUrl:
Description: A database connection string
Value:
Fn::Join:
- ""
- - "postgresql://foouser:foopassword@"
- !GetAtt Database.Endpoint.Address
- ":"
- !GetAtt Database.Endpoint.Port
- "/foodb"
$ aws cloudformation deploy \
--stack-name demodb \
--template-file ./database.yml
$ aws cloudformation deploy \
--stack-name demodb \
--template-file ./database.yml
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - demorepo
# cluster.yml
Parameters: [...]
Resources:
WideOpenSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties: [...]
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties: [...]
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties: [...]
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties: [...]
Cluster:
Type: AWS::ECS::Cluster
Properties: [...]
Service:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties: [...]
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties: [...]
LogGroup:
Type: AWS::Logs::LogGroup
Properties: [...]
TaskExecutionRole:
Type: AWS::IAM::Role
Properties: [...]
Outputs:
LoadBalancerUrl:
Description: The URL of the NLB
Value: !GetAtt LoadBalancer.DNSName
# cluster.yml
Parameters: [...]
Resources:
WideOpenSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties: [...]
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties: [...]
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties: [...]
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties: [...]
Cluster:
Type: AWS::ECS::Cluster
Properties: [...]
Service:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties: [...]
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties: [...]
LogGroup:
Type: AWS::Logs::LogGroup
Properties: [...]
TaskExecutionRole:
Type: AWS::IAM::Role
Properties: [...]
Outputs:
LoadBalancerUrl:
Description: The URL of the NLB
Value: !GetAtt LoadBalancer.DNSName
# cluster.yml
Parameters:
DatabaseUrl: { Type: String }
DockerImage: { Type: String }
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: !Sub ${AWS::StackName}-container
Image: !Ref DockerImage
Command:
- !Sub "rails db:migrate && rails assets:precompile && rails server -b 0.0.0.0"
Environment:
- { Name: RAILS_SERVE_STATIC_FILES, Value: true }
- { Name: RAILS_LOG_TO_STDOUT, Value: true }
- { Name: RAILS_ENV, Value: production }
- { Name: DATABASE_URL, Value: !Ref DatabaseUrl }
- { Name: SECRET_KEY_BASE, Value: [...] }
PortMappings:
- ContainerPort: 3000
Essential: true
LogConfiguration: [...]
Family: !Sub ${AWS::StackName}-task-family
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
[...]
# cluster.yml
Parameters:
DatabaseUrl: { Type: String }
DockerImage: { Type: String }
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: !Sub ${AWS::StackName}-container
Image: !Ref DockerImage
Command:
- !Sub "rails db:migrate && rails assets:precompile && rails server -b 0.0.0.0"
Environment:
- { Name: RAILS_SERVE_STATIC_FILES, Value: true }
- { Name: RAILS_LOG_TO_STDOUT, Value: true }
- { Name: RAILS_ENV, Value: production }
- { Name: DATABASE_URL, Value: !Ref DatabaseUrl }
- { Name: SECRET_KEY_BASE, Value: [...] }
PortMappings:
- ContainerPort: 3000
Essential: true
LogConfiguration: [...]
Family: !Sub ${AWS::StackName}-task-family
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
[...]
# cluster.yml
Parameters:
DatabaseUrl: { Type: String }
DockerImage: { Type: String }
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: !Sub ${AWS::StackName}-container
Image: !Ref DockerImage
Command:
- !Sub "rails db:migrate && rails assets:precompile && rails server -b 0.0.0.0"
Environment:
- { Name: RAILS_SERVE_STATIC_FILES, Value: true }
- { Name: RAILS_LOG_TO_STDOUT, Value: true }
- { Name: RAILS_ENV, Value: production }
- { Name: DATABASE_URL, Value: !Ref DatabaseUrl }
- { Name: SECRET_KEY_BASE, Value: [...] }
PortMappings:
- ContainerPort: 3000
Essential: true
LogConfiguration: [...]
Family: !Sub ${AWS::StackName}-task-family
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
[...]
# cluster.yml
Parameters:
DatabaseUrl: { Type: String }
DockerImage: { Type: String }
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: !Sub ${AWS::StackName}-container
Image: !Ref DockerImage
Command:
- !Sub "rails db:migrate && rails assets:precompile && rails server -b 0.0.0.0"
Environment:
- { Name: RAILS_SERVE_STATIC_FILES, Value: true }
- { Name: RAILS_LOG_TO_STDOUT, Value: true }
- { Name: RAILS_ENV, Value: production }
- { Name: DATABASE_URL, Value: !Ref DatabaseUrl }
- { Name: SECRET_KEY_BASE, Value: [...] }
PortMappings:
- ContainerPort: 3000
Essential: true
LogConfiguration: [...]
Family: !Sub ${AWS::StackName}-task-family
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
[...]
# cluster.yml
Parameters:
DatabaseUrl: { Type: String }
DockerImage: { Type: String }
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: !Sub ${AWS::StackName}-container
Image: !Ref DockerImage
Command:
- !Sub "rails db:migrate && rails assets:precompile && rails server -b 0.0.0.0"
Environment:
- { Name: RAILS_SERVE_STATIC_FILES, Value: true }
- { Name: RAILS_LOG_TO_STDOUT, Value: true }
- { Name: RAILS_ENV, Value: production }
- { Name: DATABASE_URL, Value: !Ref DatabaseUrl }
- { Name: SECRET_KEY_BASE, Value: [...] }
PortMappings:
- ContainerPort: 3000
Essential: true
LogConfiguration: [...]
Family: !Sub ${AWS::StackName}-task-family
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
[...]
# cluster.yml
Parameters:
DatabaseUrl: { Type: String }
DockerImage: { Type: String }
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: !Sub ${AWS::StackName}-container
Image: !Ref DockerImage
Command:
- !Sub "rails db:migrate && rails assets:precompile && rails server -b 0.0.0.0"
Environment:
- { Name: RAILS_SERVE_STATIC_FILES, Value: true }
- { Name: RAILS_LOG_TO_STDOUT, Value: true }
- { Name: RAILS_ENV, Value: production }
- { Name: DATABASE_URL, Value: !Ref DatabaseUrl }
- { Name: SECRET_KEY_BASE, Value: [...] }
PortMappings:
- ContainerPort: 3000
Essential: true
LogConfiguration: [...]
Family: !Sub ${AWS::StackName}-task-family
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
[...]
# cluster.yml
Parameters:
DatabaseUrl: { Type: String }
DockerImage: { Type: String }
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: !Sub ${AWS::StackName}-container
Image: !Ref DockerImage
Command:
- !Sub "rails db:migrate && rails assets:precompile && rails server -b 0.0.0.0"
Environment:
- { Name: RAILS_SERVE_STATIC_FILES, Value: true }
- { Name: RAILS_LOG_TO_STDOUT, Value: true }
- { Name: RAILS_ENV, Value: production }
- { Name: DATABASE_URL, Value: !Ref DatabaseUrl }
- { Name: SECRET_KEY_BASE, Value: [...] }
PortMappings:
- ContainerPort: 3000
Essential: true
LogConfiguration: [...]
Family: !Sub ${AWS::StackName}-task-family
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
[...]
# cluster.yml
Resources:
Service:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties:
Cluster: !Ref Cluster
LaunchType: FARGATE
DesiredCount: 4
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
TaskDefinition: !Ref TaskDefinition
NetworkConfiguration:
AwsvpcConfiguration:
Subnets: !Ref PublicSubnets
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref WideOpenSecurityGroup
LoadBalancers:
- ContainerName: !Sub ${AWS::StackName}-container
ContainerPort: 3000
TargetGroupArn: !Ref TargetGroup
# cluster.yml
Resources:
Service:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties:
Cluster: !Ref Cluster
LaunchType: FARGATE
DesiredCount: 4
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
TaskDefinition: !Ref TaskDefinition
NetworkConfiguration:
AwsvpcConfiguration:
Subnets: !Ref PublicSubnets
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref WideOpenSecurityGroup
LoadBalancers:
- ContainerName: !Sub ${AWS::StackName}-container
ContainerPort: 3000
TargetGroupArn: !Ref TargetGroup
# cluster.yml
Resources:
Service:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties:
Cluster: !Ref Cluster
LaunchType: FARGATE
DesiredCount: 4
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
TaskDefinition: !Ref TaskDefinition
NetworkConfiguration:
AwsvpcConfiguration:
Subnets: !Ref PublicSubnets
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref WideOpenSecurityGroup
LoadBalancers:
- ContainerName: !Sub ${AWS::StackName}-container
ContainerPort: 3000
TargetGroupArn: !Ref TargetGroup
# cluster.yml
Resources:
Service:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties:
Cluster: !Ref Cluster
LaunchType: FARGATE
DesiredCount: 4
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
TaskDefinition: !Ref TaskDefinition
NetworkConfiguration:
AwsvpcConfiguration:
Subnets: !Ref PublicSubnets
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref WideOpenSecurityGroup
LoadBalancers:
- ContainerName: !Sub ${AWS::StackName}-container
ContainerPort: 3000
TargetGroupArn: !Ref TargetGroup
# cluster.yml
Resources:
Cluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Sub ${AWS::StackName}-cluster
$ aws cloudformation deploy \
--capabilities CAPABILITY_IAM \
--stack-name democluster \
--template-file ./cluster.yml \
--parameter-overrides \
RailsSecretBase=$(rails secret) \
DatabaseUrl=${DATABASE_URL} \
VpcId=${VPC_ID} \
PublicSubnets=${PUBLIC_SUBNETS} \
DockerImage=${REPOSITORY_URI}:latest
$ aws cloudformation deploy \
--capabilities CAPABILITY_IAM \
--stack-name democluster \
--template-file ./cluster.yml \
--parameter-overrides \
RailsSecretBase=$(rails secret) \
DatabaseUrl=${DATABASE_URL} \
VpcId=${VPC_ID} \
PublicSubnets=${PUBLIC_SUBNETS} \
DockerImage=${REPOSITORY_URI}:latest
$ aws cloudformation deploy \
--capabilities CAPABILITY_IAM \
--stack-name democluster \
--template-file ./cluster.yml \
--parameter-overrides \
RailsSecretBase=$(rails secret) \
DatabaseUrl=${DATABASE_URL} \
VpcId=${VPC_ID} \
PublicSubnets=${PUBLIC_SUBNETS} \
DockerImage=${REPOSITORY_URI}:latest
$ aws cloudformation deploy \
--capabilities CAPABILITY_IAM \
--stack-name democluster \
--template-file ./cluster.yml \
--parameter-overrides \
RailsSecretBase=$(rails secret) \
DatabaseUrl=${DATABASE_URL} \
VpcId=${VPC_ID} \
PublicSubnets=${PUBLIC_SUBNETS} \
DockerImage=${REPOSITORY_URI}:latest
$ aws cloudformation deploy \
--capabilities CAPABILITY_IAM \
--stack-name democluster \
--template-file ./cluster.yml \
--parameter-overrides \
RailsSecretBase=$(rails secret) \
DatabaseUrl=${DATABASE_URL} \
VpcId=${VPC_ID} \
PublicSubnets=${PUBLIC_SUBNETS} \
DockerImage=${REPOSITORY_URI}:latest
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - demorepo
$ aws cloudformation deploy \
--capabilities CAPABILITY_IAM \
--stack-name democluster \
--template-file ./cluster.yml \
--parameter-overrides \
RailsSecretBase=$(rails secret) \
DatabaseUrl=${DATABASE_URL} \
VpcId=${VPC_ID} \
PublicSubnets=${PUBLIC_SUBNETS} \
DockerImage=${REPOSITORY_URI}:latest
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - demorepo
export LB_URL=$($(aws cloudformation describe-stacks \
--stack-name democluster \
| jq -r '(.Stacks[0].Outputs[] \
| select(.OutputKey == "LoadBalancerUrl")).OutputValue'))
$ aws cloudformation deploy \
--capabilities CAPABILITY_IAM \
--stack-name democluster \
--template-file ./cluster.yml \
--parameter-overrides \
RailsSecretBase=$(rails secret) \
DatabaseUrl=${DATABASE_URL} \
VpcId=${VPC_ID} \
PublicSubnets=${PUBLIC_SUBNETS} \
DockerImage=${REPOSITORY_URI}:latest
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - demorepo
export LB_URL=$($(aws cloudformation describe-stacks \
--stack-name democluster \
| jq -r '(.Stacks[0].Outputs[] \
| select(.OutputKey == "LoadBalancerUrl")).OutputValue'))
open http://${LB_URL}/posts
Service:
# ...
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
Properties:
DesiredCount: 4
# ...
Service:
# ...
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
Properties:
DesiredCount: 4
# ...
Service:
# ...
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
Properties:
DesiredCount: 4
# ...
Service:
# ...
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
Properties:
DesiredCount: 4
# ...
Service:
# ...
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
Properties:
DesiredCount: 4
# ...
<p id="notice"><%= notice %></p>
<h1>Posts</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>Content</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.title %></td>
<td><%= post.content %></td>
<td><%= link_to 'Show', post %></td>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
<td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Post', new_post_path %>
<p id="notice"><%= notice %></p>
<h1>MEOW MEOW MEOW MEOW MEOW</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>Content</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.title %></td>
<td><%= post.content %></td>
<td><%= link_to 'Show', post %></td>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
<td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Post', new_post_path %>
$ docker build . -t demo:latest \
&& docker tag demo:latest ${REPOSITORY_URI}:latest \
&& docker push ${REPOSITORY_URI}:latest
$ docker build . -t demo:latest \
&& docker tag demo:latest ${REPOSITORY_URI}:latest \
&& docker push ${REPOSITORY_URI}:latest
Sending build context to Docker daemon 137.2kB
Step 1/8 : FROM ruby:2.5.0-alpine
---> 308418a1844f
Step 2/8 : RUN apk add --update alpine-sdk nodejs postgresql-dev tzdata
---> Using cache
---> 8ed21fdcffa9
[...]
Successfully built 33d23bb7b314
$ docker build . -t demo:latest \
&& docker tag demo:latest ${REPOSITORY_URI}:latest \
&& docker push ${REPOSITORY_URI}:latest
Sending build context to Docker daemon 137.2kB
Step 1/8 : FROM ruby:2.5.0-alpine
---> 308418a1844f
Step 2/8 : RUN apk add --update alpine-sdk nodejs postgresql-dev tzdata
---> Using cache
---> 8ed21fdcffa9
[...]
Successfully built 33d23bb7b314
Successfully tagged demo:latest
$ docker build . -t demo:latest \
&& docker tag demo:latest ${REPOSITORY_URI}:latest \
&& docker push ${REPOSITORY_URI}:latest
Sending build context to Docker daemon 137.2kB
Step 1/8 : FROM ruby:2.5.0-alpine
---> 308418a1844f
Step 2/8 : RUN apk add --update alpine-sdk nodejs postgresql-dev tzdata
---> Using cache
---> 8ed21fdcffa9
[...]
Successfully built 33d23bb7b314
Successfully tagged demo:latest
The push refers to repository [166875342547.dkr.ecr.us-east-1.amazonaws.com/demor-repos-1as4v]
ed2e9d4c2ebf: Pushed
3736f3faece1: Layer already exists
960106cd0a97: Layer already exists
34f06064af2b: Layer already exists
838e7becd078: Layer already exists
61cb6c204d39: Layer already exists
e9bcacee1741: Layer already exists
cd7100a72410: Layer already exists
latest: digest: sha256:ee0e5e23725d8050b6499e4bd6bd783cbf8b2436629489e2f7a322e3d63 size: 1995
$ aws ecs update-service \
--service democluster-service \
--cluster democluster-cluster \
--force-new-deployment
$ while true; do \
echo $(date)-$(curl -s ${POSTS_URL} | grep \<h1); \
sleep 2; \
done;
Wed Jan 31 16:16:37 PST 2018-<h1>Posts</h1>
Wed Jan 31 16:16:40 PST 2018-<h1>Posts</h1>
Wed Jan 31 16:16:42 PST 2018-<h1>Posts</h1>
Wed Jan 31 16:16:44 PST 2018-<h1>Posts</h1>
Wed Jan 31 16:16:50 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:16:52 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:16:55 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:16:57 PST 2018-<h1>Posts</h1>
Wed Jan 31 16:16:59 PST 2018-<h1>Posts</h1>
Wed Jan 31 16:17:02 PST 2018-<h1>Posts</h1>
Wed Jan 31 16:17:04 PST 2018-<h1>Posts</h1>
Wed Jan 31 16:17:06 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:17:08 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:17:11 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:17:13 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:17:16 PST 2018-<h1>Posts</h1>
Wed Jan 31 16:17:18 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:17:20 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:17:26 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:17:28 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
Wed Jan 31 16:17:30 PST 2018-<h1>MEOW MEOW MEOW MEOW</h1>
# cluster.yml
Parameters:
DatabaseUrl: { Type: String }
DockerImage: { Type: String }
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: !Sub ${AWS::StackName}-container
Image: !Ref DockerImage
Command:
- !Sub "rails db:migrate && rails assets:precompile && rails server -b 0.0.0.0"
Environment:
- { Name: RAILS_SERVE_STATIC_FILES, Value: true }
- { Name: RAILS_LOG_TO_STDOUT, Value: true }
- { Name: RAILS_ENV, Value: production }
- { Name: DATABASE_URL, Value: !Ref DatabaseUrl }
- { Name: SECRET_KEY_BASE, Value: [...] }
PortMappings:
- ContainerPort: 3000
Essential: true
LogConfiguration: [...]
Family: !Sub ${AWS::StackName}-task-family
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
[...]
# cluster.yml
Parameters:
DatabaseUrl: { Type: String }
DockerImage: { Type: String }
Resources:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: !Sub ${AWS::StackName}-container
Image: !Ref DockerImage
Command:
- !Sub "rails db:migrate && rails assets:precompile && rails server -b 0.0.0.0"
Environment:
- { Name: RAILS_SERVE_STATIC_FILES, Value: true }
- { Name: RAILS_LOG_TO_STDOUT, Value: true }
- { Name: RAILS_ENV, Value: production }
- { Name: DATABASE_URL, Value: !Ref DatabaseUrl }
- { Name: SECRET_KEY_BASE, Value: [...] }
PortMappings:
- ContainerPort: 3000
Essential: true
LogConfiguration: [...]
Family: !Sub ${AWS::StackName}-task-family
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
[...]
MIGRATION_TASK_ARN=$(aws ecs run-task \
--task-definition migrations \
--cluster ${CLUSTER_NAME} \
--count 1 \
--started-by deploy | jq -r '.["tasks"][0]["taskArn"]')
# waits 600 seconds
aws ecs wait tasks-stopped \
--cluster ${CLUSTER_NAME} \
--tasks ${MIGRATION_TASK_ARN}
MIGRATION_EXIT_CODE=$(aws ecs describe-tasks \
--cluster cluster \
--tasks ${MIGRATION_TASK_ARN} | jq -r '.["tasks"][0]["containers"][0]["exitCode"]')
if [ "${MIGRATION_EXIT_CODE}" != "0" ] ; then
# fail build
exit 1
fi
$ awslogs get democluster ALL -s1d # all logs from group in last 24 hours
$ awslogs get democluster ALL -s1d # all logs from group in last 24 hours
democluster ecs/democluster-container/733eb314-0aaa-4ad0-a849-9c357b045aac 172.31.87.245 - - [01/Feb/2018:00:18:45 UTC] "GET /favicon.ico HTTP/1.1" 200 0
democluster ecs/democluster-container/733eb314-0aaa-4ad0-a849-9c357b045aac http://54.86.202.26/game/go/ttlz/index.html -> /favicon.ico
democluster ecs/democluster-container/32ed9165-8a8f-4e6b-b8b0-1f9b0c4532f2 172.31.8.236 - - [01/Feb/2018:00:18:46 UTC] "GET /posts HTTP/1.1" 200 847
democluster ecs/democluster-container/32ed9165-8a8f-4e6b-b8b0-1f9b0c4532f2 - -> /posts
democluster ecs/democluster-container/0a21e832-5640-4e16-af32-cd600c524f62 172.31.8.236 - - [01/Feb/2018:00:18:48 UTC] "GET /posts HTTP/1.1" 200 847
democluster ecs/democluster-container/0a21e832-5640-4e16-af32-cd600c524f62 - -> /posts
$ awslogs get democluster --watch # tail all log streams across cluster
$ awslogs get democluster --watch # tail all log streams across cluster
democluster ecs/democluster-container/79f6fe46-31e0-4e37-8f09-9b00ca19bf6d I, [2018-02-01T00:18:09.697899 #42] INFO -- : [c16ea66d-a7a1-429b-b706-c771f5ca8eee] Rendered posts/index.html.erb within layouts/application (2.7ms)
$ awslogs get democluster --watch # tail all log streams across cluster
democluster ecs/democluster-container/79f6fe46-31e0-4e37-8f09-9b00ca19bf6d I, [2018-02-01T00:18:09.697899 #42] INFO -- : [c16ea66d-a7a1-429b-b706-c771f5ca8eee] Rendered posts/index.html.erb within layouts/application (2.7ms)
democluster ecs/democluster-container/79f6fe46-31e0-4e37-8f09-9b00ca19bf6d I, [2018-02-01T00:18:09.698349 #42] INFO -- : [c16ea66d-a7a1-429b-b706-c771f5ca8eee] Completed 200 OK in 4ms (Views: 2.4ms | ActiveRecord: 1.1ms)
$ awslogs get democluster --watch # tail all log streams across cluster
democluster ecs/democluster-container/79f6fe46-31e0-4e37-8f09-9b00ca19bf6d I, [2018-02-01T00:18:09.697899 #42] INFO -- : [c16ea66d-a7a1-429b-b706-c771f5ca8eee] Rendered posts/index.html.erb within layouts/application (2.7ms)
democluster ecs/democluster-container/79f6fe46-31e0-4e37-8f09-9b00ca19bf6d I, [2018-02-01T00:18:09.698349 #42] INFO -- : [c16ea66d-a7a1-429b-b706-c771f5ca8eee] Completed 200 OK in 4ms (Views: 2.4ms | ActiveRecord: 1.1ms)
democluster ecs/democluster-container/79f6fe46-31e0-4e37-8f09-9b00ca19bf6d I, [2018-02-01T00:18:50.419423 #42] INFO -- : [a2213956-a4ce-4a50-90da-33dc39029475] Started GET "/game/go/jsmt/index.html" for 172.31.87.245 at 2018-02-01 00:18:50 +0000