examples/jamstack: implemented backend deployment running on ecs
Signed-off-by: Sam Alba <sam.alba@gmail.com>
This commit is contained in:
parent
9d6551e0bf
commit
90936c450f
125
examples/jamstack/backend.cue
Normal file
125
examples/jamstack/backend.cue
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"dagger.io/dagger"
|
||||||
|
"dagger.io/aws"
|
||||||
|
"dagger.io/aws/ecs"
|
||||||
|
)
|
||||||
|
|
||||||
|
infra: {
|
||||||
|
// AWS auth & default region
|
||||||
|
awsConfig: aws.#Config
|
||||||
|
|
||||||
|
// VPC Id
|
||||||
|
vpcId: string
|
||||||
|
|
||||||
|
// ECR Image repository
|
||||||
|
ecrRepository: string
|
||||||
|
|
||||||
|
// ECS cluster name
|
||||||
|
ecsClusterName: string
|
||||||
|
|
||||||
|
// Execution Role ARN used for all tasks running on the cluster
|
||||||
|
ecsTaskRoleArn?: string
|
||||||
|
|
||||||
|
// ELB listener ARN
|
||||||
|
elbListenerArn: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backend configuration
|
||||||
|
backend: {
|
||||||
|
// Source code to build this container
|
||||||
|
source: dagger.#Artifact
|
||||||
|
|
||||||
|
// Container environment variables
|
||||||
|
environment: [string]: string
|
||||||
|
|
||||||
|
// Public hostname (need to match the master domain configures on the loadbalancer)
|
||||||
|
hostname: string
|
||||||
|
|
||||||
|
// Container configuration
|
||||||
|
container: {
|
||||||
|
// Desired number of running containers
|
||||||
|
desiredCount: *1 | int
|
||||||
|
// Time to wait for the HTTP timeout to complete
|
||||||
|
healthCheckTimeout: *10 | int
|
||||||
|
// HTTP Path to perform the healthcheck request (HTTP Get)
|
||||||
|
healthCheckPath: *"/" | string
|
||||||
|
// Number of times the health check needs to fail before recycling the container
|
||||||
|
healthCheckUnhealthyThreshold: *2 | int
|
||||||
|
// Port used by the process inside the container
|
||||||
|
port: *80 | int
|
||||||
|
// Memory to allocate
|
||||||
|
memory: *1024 | int
|
||||||
|
// Override the default container command
|
||||||
|
command: [...string]
|
||||||
|
// Custom dockerfile path
|
||||||
|
dockerfilePath: *"" | string
|
||||||
|
// docker build args
|
||||||
|
dockerBuildArgs: [string]: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init container runs only once when the main container starts
|
||||||
|
initContainer: {
|
||||||
|
command: [...string]
|
||||||
|
environment: [string]: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backend deployment logic
|
||||||
|
backend: {
|
||||||
|
let slug = name
|
||||||
|
|
||||||
|
// Docker image built from source, pushed to ECR
|
||||||
|
image: #ECRImage & {
|
||||||
|
source: source
|
||||||
|
repository: infra.ecrRepository
|
||||||
|
tag: slug
|
||||||
|
awsConfig: infra.awsConfig
|
||||||
|
if backend.container.dockerfilePath != "" {
|
||||||
|
dockerfilePath: backend.container.dockerfilePath
|
||||||
|
}
|
||||||
|
buildArgs: backend.container.dockerBuildArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an ECS Task + Service + deploy via Cloudformation
|
||||||
|
app: #ECSApp & {
|
||||||
|
awsConfig: infra.awsConfig
|
||||||
|
"slug": slug
|
||||||
|
clusterName: infra.ecsClusterName
|
||||||
|
vpcId: infra.vpcId
|
||||||
|
elbListenerArn: infra.elbListenerArn
|
||||||
|
if infra.ecsTaskRoleArn != _|_ {
|
||||||
|
taskRoleArn: infra.ecsTaskRoleArn
|
||||||
|
}
|
||||||
|
hostname: hostname
|
||||||
|
healthCheck: {
|
||||||
|
timeout: backend.container.healthCheckTimeout
|
||||||
|
path: backend.container.healthCheckPath
|
||||||
|
unhealthyThresholdCount: backend.container.healthCheckUnhealthyThreshold
|
||||||
|
}
|
||||||
|
desiredCount: backend.container.desiredCount
|
||||||
|
container: {
|
||||||
|
command: backend.container.command
|
||||||
|
environment: environment
|
||||||
|
port: backend.container.port
|
||||||
|
memory: backend.container.memory
|
||||||
|
"image": image.ref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional container to run one-time during the deploy (eg. db migration)
|
||||||
|
if len(backend.initContainer.command) > 0 {
|
||||||
|
initContainer: ecs.#RunTask & {
|
||||||
|
config: infra.awsConfig
|
||||||
|
containerName: slug
|
||||||
|
cluster: infra.ecsClusterName
|
||||||
|
if infra.ecsTaskRoleArn != _|_ {
|
||||||
|
roleArn: infra.ecsTaskRoleArn
|
||||||
|
}
|
||||||
|
containerEnvironment: backend.initContainer.environment
|
||||||
|
containerCommand: backend.initContainer.command
|
||||||
|
taskArn: app.taskArn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
examples/jamstack/ecr_image.cue
Normal file
55
examples/jamstack/ecr_image.cue
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"dagger.io/dagger"
|
||||||
|
"dagger.io/dagger/op"
|
||||||
|
"dagger.io/aws"
|
||||||
|
"dagger.io/aws/ecr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Build an image and push it to ECR
|
||||||
|
#ECRImage: {
|
||||||
|
source: dagger.#Artifact
|
||||||
|
// Path of the Dockerfile
|
||||||
|
dockerfilePath?: string
|
||||||
|
repository: string
|
||||||
|
tag: string
|
||||||
|
awsConfig: aws.#Config
|
||||||
|
buildArgs: [string]: string
|
||||||
|
|
||||||
|
pushTarget: "\(repository):\(tag)"
|
||||||
|
|
||||||
|
// Build the image
|
||||||
|
buildImage: op.#DockerBuild & {
|
||||||
|
context: source
|
||||||
|
if dockerfilePath != _|_ {
|
||||||
|
"dockerfilePath": dockerfilePath
|
||||||
|
}
|
||||||
|
buildArg: buildArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use these credentials to push
|
||||||
|
ecrCreds: ecr.#Credentials & {
|
||||||
|
config: awsConfig
|
||||||
|
target: pushTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
push: #up: [
|
||||||
|
op.#DockerBuild & {
|
||||||
|
context: source
|
||||||
|
if dockerfilePath != _|_ {
|
||||||
|
"dockerfilePath": dockerfilePath
|
||||||
|
}
|
||||||
|
buildArg: buildArgs
|
||||||
|
},
|
||||||
|
op.Export & {
|
||||||
|
format: "string"
|
||||||
|
source: op.#PushContainer & {
|
||||||
|
ref: pushTarget
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// FIXME: ref does not include the sha256: https://github.com/dagger/dagger/issues/303
|
||||||
|
ref: pushTarget
|
||||||
|
}
|
162
examples/jamstack/ecs.cue
Normal file
162
examples/jamstack/ecs.cue
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"dagger.io/aws"
|
||||||
|
"dagger.io/aws/elb"
|
||||||
|
"dagger.io/aws/cloudformation"
|
||||||
|
)
|
||||||
|
|
||||||
|
#ECSApp: {
|
||||||
|
awsConfig: aws.#Config
|
||||||
|
slug: string
|
||||||
|
clusterName: string
|
||||||
|
vpcId: string
|
||||||
|
elbListenerArn: string
|
||||||
|
tlsCertificateArn: string
|
||||||
|
taskRoleArn: *"" | string
|
||||||
|
hostname: string
|
||||||
|
healthCheck: {
|
||||||
|
timeout: *10 | int
|
||||||
|
path: *"/" | string
|
||||||
|
unhealthyThresholdCount: *2 | int
|
||||||
|
}
|
||||||
|
desiredCount: int
|
||||||
|
container: {
|
||||||
|
command: [...string]
|
||||||
|
environment: [string]: string
|
||||||
|
port: *80 | int
|
||||||
|
cpu: *256 | int
|
||||||
|
memory: *1024 | int
|
||||||
|
image: string
|
||||||
|
}
|
||||||
|
|
||||||
|
taskArn: cfnStack.outputs.TaskArn
|
||||||
|
|
||||||
|
elbRulePriority: elb.#RandomRulePriority & {
|
||||||
|
config: awsConfig
|
||||||
|
listenerArn: elbListenerArn
|
||||||
|
vhost: hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
cfnStack: cloudformation.#Stack & {
|
||||||
|
config: awsConfig
|
||||||
|
stackName: slug
|
||||||
|
onFailure: "DO_NOTHING"
|
||||||
|
parameters: {
|
||||||
|
ELBRulePriority: elbRulePriority.out
|
||||||
|
ImageRef: container.image
|
||||||
|
ELBListenerArn: elbListenerArn
|
||||||
|
TLSCertificateArn: tlsCertificateArn
|
||||||
|
}
|
||||||
|
source: json.Marshal(template)
|
||||||
|
}
|
||||||
|
|
||||||
|
template: {
|
||||||
|
AWSTemplateFormatVersion: "2010-09-09"
|
||||||
|
Description: "Blocklayer deployed app"
|
||||||
|
Parameters: {
|
||||||
|
ELBRulePriority: Type: "Number"
|
||||||
|
ImageRef: Type: "String"
|
||||||
|
ELBListenerArn: Type: "String"
|
||||||
|
TLSCertificateArn: Type: "String"
|
||||||
|
}
|
||||||
|
Resources: {
|
||||||
|
TLSCertificate: {
|
||||||
|
Type: "AWS::ElasticLoadBalancingV2::ListenerCertificate"
|
||||||
|
Properties: {
|
||||||
|
Certificates: [ {CertificateArn: Ref: "TLSCertificateArn"}]
|
||||||
|
ListenerArn: Ref: "ELBListenerArn"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ECSTaskDefinition: {
|
||||||
|
Type: "AWS::ECS::TaskDefinition"
|
||||||
|
Properties: {
|
||||||
|
Cpu: "\(container.cpu)"
|
||||||
|
Memory: "\(container.memory)"
|
||||||
|
if taskRoleArn != "" {
|
||||||
|
TaskRoleArn: taskRoleArn
|
||||||
|
}
|
||||||
|
NetworkMode: "bridge"
|
||||||
|
ContainerDefinitions: [{
|
||||||
|
if len(container.command) > 0 {
|
||||||
|
Command: container.command
|
||||||
|
}
|
||||||
|
Name: slug
|
||||||
|
Image: Ref: "ImageRef"
|
||||||
|
Essential: true
|
||||||
|
Environment: [ for k, v in container.environment {
|
||||||
|
Name: k
|
||||||
|
Value: v
|
||||||
|
}]
|
||||||
|
PortMappings: [{
|
||||||
|
ContainerPort: container.port
|
||||||
|
}]
|
||||||
|
StopTimeout: 5
|
||||||
|
LogConfiguration: {
|
||||||
|
LogDriver: "awslogs"
|
||||||
|
Options: {
|
||||||
|
"awslogs-group": "bl/provider/ecs/\(clusterName)"
|
||||||
|
"awslogs-region": Ref: "AWS::Region"
|
||||||
|
"awslogs-create-group": "true"
|
||||||
|
"awslogs-stream-prefix": slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ECSListenerRule: {
|
||||||
|
Type: "AWS::ElasticLoadBalancingV2::ListenerRule"
|
||||||
|
Properties: {
|
||||||
|
ListenerArn: Ref: "ELBListenerArn"
|
||||||
|
Priority: Ref: "ELBRulePriority"
|
||||||
|
Conditions: [{
|
||||||
|
Field: "host-header"
|
||||||
|
Values: [hostname]}]
|
||||||
|
Actions: [{
|
||||||
|
Type: "forward"
|
||||||
|
TargetGroupArn: Ref: "ECSTargetGroup"
|
||||||
|
}]}}
|
||||||
|
ECSTargetGroup: {
|
||||||
|
Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
|
||||||
|
Properties: {
|
||||||
|
Protocol: "HTTP"
|
||||||
|
VpcId: vpcId
|
||||||
|
Port: 80
|
||||||
|
HealthCheckPath: healthCheck.path
|
||||||
|
UnhealthyThresholdCount: healthCheck.unhealthyThresholdCount
|
||||||
|
HealthCheckTimeoutSeconds: healthCheck.timeout
|
||||||
|
HealthCheckIntervalSeconds: healthCheck.timeout + 1
|
||||||
|
HealthyThresholdCount: 3
|
||||||
|
TargetGroupAttributes: [{
|
||||||
|
Value: "10"
|
||||||
|
Key: "deregistration_delay.timeout_seconds"
|
||||||
|
}]}}
|
||||||
|
ECSService: {
|
||||||
|
Type: "AWS::ECS::Service"
|
||||||
|
Properties: {
|
||||||
|
Cluster: clusterName
|
||||||
|
DesiredCount: desiredCount
|
||||||
|
LaunchType: "EC2"
|
||||||
|
LoadBalancers: [{
|
||||||
|
ContainerPort: container.port
|
||||||
|
TargetGroupArn: Ref: "ECSTargetGroup"
|
||||||
|
ContainerName: slug
|
||||||
|
}]
|
||||||
|
ServiceName: slug
|
||||||
|
TaskDefinition: Ref: "ECSTaskDefinition"
|
||||||
|
DeploymentConfiguration: {
|
||||||
|
DeploymentCircuitBreaker: {
|
||||||
|
Enable: true
|
||||||
|
Rollback: true
|
||||||
|
}
|
||||||
|
MaximumPercent: 200
|
||||||
|
MinimumHealthyPercent: 100
|
||||||
|
}}
|
||||||
|
DependsOn: "ECSListenerRule"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outputs: TaskArn: Value: Ref: "ECSTaskDefinition"
|
||||||
|
}
|
||||||
|
}
|
4
examples/jamstack/main.cue
Normal file
4
examples/jamstack/main.cue
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Name of the application
|
||||||
|
name: string & =~"[a-z0-9-]+"
|
Reference in New Issue
Block a user