Allow to enable replication service
Introduce the ability to enable a replication service that will expose
port 9148 and 1022 via a NLB to accept git traffic. The data will be
persisted on EFS volume mounted by the gerrit masters.
This is a step forward towards modeling a dual-master recipe that
supports multi-site deployment, since it allows other sites to replicate
git data across regions via the replication service.
Feature: Issue 13287
Change-Id: Ib0ef28cc8be0b1a406480e7c1da9ab72ca16c37e
diff --git a/dual-master/Makefile b/dual-master/Makefile
index f298ed9..7a8a325 100644
--- a/dual-master/Makefile
+++ b/dual-master/Makefile
@@ -7,19 +7,26 @@
SERVICE_SLAVE_TEMPLATE:=cf-service-slave.yml
DNS_ROUTING_TEMPLATE:=cf-dns-route.yml
LOAD_BALANCER_TEMPLATE:=cf-service-lb.yml
+SERVICE_REPLICATION_TEMPLATE:=cf-service-replication.yml
AWS_FC_COMMAND=export AWS_PAGER=;aws cloudformation
.PHONY: create-all delete-all \
+ wait-for-replication-creation wait-for-service-replication-deletion service-replication delete-service-replication \
cluster cluster-keys service-master-1 service-master-2 service-slave dns-routing \
wait-for-cluster-creation wait-for-service-master-1-creation wait-for-service-master-2-creation wait-for-service-slave-creation wait-for-dns-routing-creation \
wait-for-cluster-deletion wait-for-service-master-1-deletion wait-for-service-master-2-deletion wait-for-service-slave-deletion wait-for-dns-routing-deletion \
service-lb wait-for-service-lb-deletion wait-for-service-lb-creation \
gerrit-build gerrit-publish haproxy-publish syslog-sidecar-publish
+ifeq ($(REPLICATION_SERVICE_ENABLED),true)
+optional_replication_targets=service-replication wait-for-service-replication-creation
+endif
+
create-all: upload-common-templates \
git-daemon-publish git-ssh-publish \
gerrit-publish haproxy-publish syslog-sidecar-publish \
cluster wait-for-cluster-creation \
+ $(optional_replication_targets) \
service-slave service-master-1 \
wait-for-service-master-1-creation wait-for-service-slave-creation \
service-master-2 wait-for-service-master-2-creation \
@@ -148,6 +155,22 @@
$(METRICS_CW_OPTIONAL_PARAMS) \
$(SMTP_OPTIONAL_PARAMS)
+service-replication:
+ $(AWS_FC_COMMAND) create-stack \
+ --stack-name $(SERVICE_REPLICATION_STACK_NAME) \
+ --capabilities CAPABILITY_IAM \
+ --template-body file://`pwd`/$(SERVICE_REPLICATION_TEMPLATE) \
+ --region $(AWS_REGION) \
+ --parameters \
+ ParameterKey=GitReplicationServiceName,ParameterValue=$(SERVICE_REPLICATION_STACK_NAME) \
+ ParameterKey=ClusterStackName,ParameterValue=$(CLUSTER_STACK_NAME) \
+ ParameterKey=TemplateBucketName,ParameterValue=$(TEMPLATE_BUCKET_NAME) \
+ ParameterKey=DockerRegistryUrl,ParameterValue=$(DOCKER_REGISTRY_URI) \
+ ParameterKey=DesiredCount,ParameterValue=$(SERVICE_REPLICATION_DESIRED_COUNT) \
+ ParameterKey=GerritKeyPrefix,ParameterValue=$(GERRIT_KEY_PREFIX) \
+ ParameterKey=HostedZoneName,ParameterValue=$(HOSTED_ZONE_NAME) \
+ ParameterKey=GitReplicationSubdomain,ParameterValue=$(GIT_REPLICATION_SUBDOMAIN)
+
service-slave: set-optional-params-metrics-cloudwatch
ifdef GERRIT_SLAVE_INSTANCE_ID
$(eval SLAVE_SERVICE_OPTIONAL_PARAMS := $(SLAVE_SERVICE_OPTIONAL_PARAMS) ParameterKey=InstanceId,ParameterValue=$(GERRIT_SLAVE_INSTANCE_ID))
@@ -202,6 +225,7 @@
ParameterKey=Subdomain,ParameterValue=$(LB_SUBDOMAIN) \
ParameterKey=HAProxyDockerImage,ParameterValue=aws-gerrit/haproxy:$(HAPROXY_HEAD_SHA1) \
ParameterKey=SidecarDockerImage,ParameterValue=aws-gerrit/syslog-sidecar:$(SYSLOG_HEAD_SHA1) \
+ $(REPLICATION_SERVICE_OPTIONAL_PARAMS) \
$(SERVICE_OPTIONAL_PARAMS)
dns-routing:
@@ -229,6 +253,13 @@
--region $(AWS_REGION)
@echo "*** Service stack '$(SERVICE_MASTER1_STACK_NAME)' created"
+wait-for-service-replication-creation:
+ @echo "*** Wait for service replication stack '$(SERVICE_REPLICATION_STACK_NAME)' creation"
+ $(AWS_FC_COMMAND) wait stack-create-complete \
+ --stack-name $(SERVICE_REPLICATION_STACK_NAME) \
+ --region $(AWS_REGION)
+ @echo "*** Service stack '$(SERVICE_REPLICATION_STACK_NAME)' created"
+
wait-for-service-master-2-creation:
@echo "*** Wait for service master stack '$(SERVICE_MASTER2_STACK_NAME)' creation"
$(AWS_FC_COMMAND) wait stack-create-complete \
@@ -299,6 +330,13 @@
--region $(AWS_REGION)
@echo "*** DNS routing stack '$(DNS_ROUTING_STACK_NAME)' deleted"
+wait-for-service-replication-deletion:
+ @echo "*** Wait for service replication stack '$(SERVICE_REPLICATION_STACK_NAME)' deletion"
+ $(AWS_FC_COMMAND) wait stack-delete-complete \
+ --stack-name $(SERVICE_REPLICATION_STACK_NAME) \
+ --region $(AWS_REGION)
+ @echo "*** Service stack '$(SERVICE_REPLICATION_STACK_NAME)' deleted"
+
delete-cluster:
$(AWS_FC_COMMAND) delete-stack \
--stack-name $(CLUSTER_STACK_NAME) \
@@ -324,6 +362,11 @@
--stack-name $(LOAD_BALANCER_STACK_NAME) \
--region $(AWS_REGION)
+delete-service-replication:
+ $(AWS_FC_COMMAND) delete-stack \
+ --stack-name $(SERVICE_REPLICATION_STACK_NAME) \
+ --region $(AWS_REGION)
+
delete-dns-routing:
$(AWS_FC_COMMAND) delete-stack \
--stack-name $(DNS_ROUTING_STACK_NAME) \
@@ -333,6 +376,7 @@
delete-service-lb wait-for-service-lb-deletion \
delete-service-master-1 delete-service-master-2 delete-service-slave \
wait-for-service-master-1-deletion wait-for-service-master-2-deletion wait-for-service-slave-deletion \
+ delete-service-replication wait-for-service-replication-deletion \
delete-cluster wait-for-cluster-deletion
gerrit-publish:
diff --git a/dual-master/README.md b/dual-master/README.md
index cd62d2e..3e592d0 100644
--- a/dual-master/README.md
+++ b/dual-master/README.md
@@ -12,6 +12,12 @@
* `cf-service-slave`: define the service stack running the gerrit replica
* `cf-service-lb`: define the LBs in front of gerrit masters (this includes haproxy as well as NLB)
+When the recipe enables the replication_service (see [docs](#replication-service))
+then these additional templates will be executed:
+
+* `cf-service-replication`: Define a replication stack that will allow git replication
+over the EFS volume, which is mounted by the master instances.
+
### Networking
* Single VPC:
@@ -100,6 +106,16 @@
* `GERRIT_MASTER2_INSTANCE_ID`: Optional. Identifier for the Gerrit master2 instance.
"gerrit-dual-master-MASTER2" by default.
+#### REPLICATION SERVICE
+
+* `REPLICATION_SERVICE_ENABLED`: Optional. Whether to expose a replication endpoint.
+"false" by default.
+* `SERVICE_REPLICATION_STACK_NAME`: Optional. The name of the replication service stack.
+"git-replication-service" by default.
+* `SERVICE_REPLICATION_DESIRED_COUNT`: Optional. Number of wanted replication tasks.
+"1" by default.
+* `GIT_REPLICATION_SUBDOMAIN`: Optional. The subdomain to use for the replication endpoint.
+"git-replication" by default.
### 2 - Deploy
@@ -119,6 +135,27 @@
and stored in a `pem` file on the current directory.
To use when ssh-ing into your instances as follow: `ssh -i cluster-keys.pem ec2-user@<ec2_instance_ip>`
+#### Replication-Service
+
+Optionally this recipe can be deployed so that replication onto the share EFS volume
+is available.
+
+By setting the environment variable `REPLICATION_SERVICE_ENABLED=true`, this recipe will
+set up and configure additional resources that will allow other other sites to replicate
+to a specific endpoint, exposed as:
+
+* For GIT replication
+`$(GIT_REPLICATION_SUBDOMAIN).$(HOSTED_ZONE_NAME):9148`
+
+* For Git Admin replication
+`$(GIT_REPLICATION_SUBDOMAIN).$(HOSTED_ZONE_NAME):1022`
+
+The service will persist git data on the same EFS volume mounted by the gerrit
+master1 and gerrit master2.
+
+Note that the replication endpoint is not internet-facing, thus replication requests
+must be coming from a peered VPC.
+
### Cleaning up
```
diff --git a/dual-master/cf-service-replication.yml b/dual-master/cf-service-replication.yml
new file mode 100644
index 0000000..e85baf5
--- /dev/null
+++ b/dual-master/cf-service-replication.yml
@@ -0,0 +1,232 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Description: Service to allow replication onto EFS
+Parameters:
+ GitReplicationServiceName:
+ Description: The name of the Git replication service
+ Type: String
+ Default: git-replication-service
+ GitReplicationSubdomain:
+ Description: The subdomain of the Git replication endpoint
+ Type: String
+ Default: git-replication
+ HostedZoneName:
+ Description: The route53 HostedZoneName.
+ Type: String
+ GitAdminSSHContainerName:
+ Description: The name of the Git Admin SSH container
+ Type: String
+ Default: git-admin-ssh-container
+ GitAdminSSHContainerPort:
+ Description: Git Admin SSH Container port
+ Type: Number
+ Default: 22
+ GitAdminSSHHostPort:
+ Description: Git ssh port
+ Type: Number
+ Default: 1022
+ GitDaemonContainerName:
+ Description: The name of the Git Daemon container
+ Type: String
+ Default: git-daemon-container
+ GitDaemonContainerPort:
+ Description: Git Daemon Container port
+ Type: Number
+ Default: 9418
+ GitDaemonHostPort:
+ Description: Git Daemon Host port
+ Type: Number
+ Default: 9418
+ ClusterStackName:
+ Description: Stack name of the ECS cluster to deply the serivces
+ Type: String
+ Default: gerrit-cluster
+ TemplateBucketName:
+ Description: S3 bucket containing cloudformation templates
+ Type: String
+ EnvironmentName:
+ Description: An environment name used to build the log stream names
+ Type: String
+ Default: test
+ GitSSHDockerImage:
+ Description: Git SSH Docker image
+ Type: String
+ Default: aws-gerrit/git-ssh:latest
+ GitDaemonDockerImage:
+ Description: Git Daemon Docker image
+ Type: String
+ Default: aws-gerrit/git-daemon:latest
+ DockerRegistryUrl:
+ Description: Docker registry URL
+ Type: String
+ DesiredCount:
+ Description: How many instances of this task should we run across our cluster?
+ Type: Number
+ Default: 1
+ GerritKeyPrefix:
+ Description: Gerrit credentials keys prefix
+ Type: String
+ GerritGitVolume:
+ Description: Gerrit git volume name
+ Type: String
+ Default: gerrit-git-master
+
+Resources:
+ Service:
+ Type: AWS::ECS::Service
+ DependsOn:
+ - GitAdminSSHListener
+ - GitDaemonListener
+ Properties:
+ Cluster:
+ Fn::ImportValue:
+ !Join [':', [!Ref 'ClusterStackName', 'ClusterName']]
+ DesiredCount: !Ref DesiredCount
+ TaskDefinition: !Ref TaskDefinition
+ LoadBalancers:
+ - ContainerName: !Ref GitAdminSSHContainerName
+ ContainerPort: !Ref GitAdminSSHContainerPort
+ TargetGroupArn: !Ref GitAdminSSHTargetGroup
+ - ContainerName: !Ref GitDaemonContainerName
+ ContainerPort: !Ref GitDaemonContainerPort
+ TargetGroupArn: !Ref GitDaemonTargetGroup
+ TaskDefinition:
+ Type: AWS::ECS::TaskDefinition
+ Properties:
+ Family: !Join ['', [!Ref GitReplicationServiceName, TaskDefinition]]
+ TaskRoleArn: !GetAtt ECSTaskExecutionRoleStack.Outputs.TaskExecutionRoleRef
+ ExecutionRoleArn: !GetAtt ECSTaskExecutionRoleStack.Outputs.TaskExecutionRoleRef
+ NetworkMode: bridge
+ ContainerDefinitions:
+ - Name: !Ref GitAdminSSHContainerName
+ Essential: true
+ Image: !Sub '${DockerRegistryUrl}/${GitSSHDockerImage}'
+ Environment:
+ - Name: TZ
+ Value: US/Pacific
+ - Name: SSH_USERS
+ Value: gerrit:1000:1000
+ - Name: AWS_REGION
+ Value: !Ref AWS::Region
+ - Name: GERRIT_KEY_PREFIX
+ Value: !Ref GerritKeyPrefix
+ MountPoints:
+ - SourceVolume: !Ref GerritGitVolume
+ ContainerPath: /var/gerrit/git
+ Cpu: 256
+ Memory: 512
+ PortMappings:
+ - ContainerPort: !Ref GitAdminSSHContainerPort
+ HostPort: !Ref GitAdminSSHHostPort
+ Protocol: tcp
+ LogConfiguration:
+ LogDriver: awslogs
+ Options:
+ awslogs-group: !Ref ClusterStackName
+ awslogs-region: !Ref AWS::Region
+ awslogs-stream-prefix: !Ref EnvironmentName
+ - Name: !Ref GitDaemonContainerName
+ Essential: true
+ Image: !Sub '${DockerRegistryUrl}/${GitDaemonDockerImage}'
+ MountPoints:
+ - SourceVolume: !Ref GerritGitVolume
+ ContainerPath: /var/gerrit/git
+ Cpu: 256
+ Memory: 512
+ PortMappings:
+ - ContainerPort: !Ref GitDaemonContainerPort
+ HostPort: !Ref GitDaemonHostPort
+ Protocol: tcp
+ LogConfiguration:
+ LogDriver: awslogs
+ Options:
+ awslogs-group: !Ref ClusterStackName
+ awslogs-region: !Ref AWS::Region
+ awslogs-stream-prefix: !Ref EnvironmentName
+ Volumes:
+ - Name: !Ref GerritGitVolume
+ Host:
+ SourcePath: "/mnt/efs/gerrit-shared/git"
+
+ LoadBalancer:
+ Type: AWS::ElasticLoadBalancingV2::LoadBalancer
+ Properties:
+ Type: network
+ Scheme: internal
+ Subnets:
+ - Fn::ImportValue:
+ !Join [':', [!Ref 'ClusterStackName', 'PublicSubnetOne']]
+ Tags:
+ - Key: Name
+ Value: !Join ['-', [!Ref 'EnvironmentName', !Ref GitReplicationServiceName, 'nlb']]
+
+ GitAdminSSHTargetGroup:
+ Type: AWS::ElasticLoadBalancingV2::TargetGroup
+ DependsOn: LoadBalancer
+ Properties:
+ VpcId:
+ Fn::ImportValue:
+ !Join [':', [!Ref 'ClusterStackName', 'VPCId']]
+ Port: !Ref GitAdminSSHHostPort
+ Protocol: TCP
+
+ GitDaemonTargetGroup:
+ Type: AWS::ElasticLoadBalancingV2::TargetGroup
+ DependsOn: LoadBalancer
+ Properties:
+ VpcId:
+ Fn::ImportValue:
+ !Join [':', [!Ref 'ClusterStackName', 'VPCId']]
+ Port: !Ref GitDaemonHostPort
+ Protocol: TCP
+
+ GitAdminSSHListener:
+ Type: AWS::ElasticLoadBalancingV2::Listener
+ DependsOn: LoadBalancer
+ Properties:
+ DefaultActions:
+ - Type: forward
+ TargetGroupArn: !Ref GitAdminSSHTargetGroup
+ LoadBalancerArn: !Ref LoadBalancer
+ Port: !Ref GitAdminSSHHostPort
+ Protocol: TCP
+
+ GitDaemonListener:
+ Type: AWS::ElasticLoadBalancingV2::Listener
+ DependsOn: LoadBalancer
+ Properties:
+ DefaultActions:
+ - Type: forward
+ TargetGroupArn: !Ref GitDaemonTargetGroup
+ LoadBalancerArn: !Ref LoadBalancer
+ Port: !Ref GitDaemonHostPort
+ Protocol: TCP
+
+ ReplicationDnsRecord:
+ Type: AWS::Route53::RecordSet
+ Properties:
+ Name: !Sub '${GitReplicationSubdomain}.${HostedZoneName}'
+ HostedZoneName: !Sub '${HostedZoneName}.'
+ Comment: DNS name for the Replication service.
+ Type: A
+ AliasTarget:
+ DNSName: !GetAtt 'LoadBalancer.DNSName'
+ HostedZoneId: !GetAtt 'LoadBalancer.CanonicalHostedZoneID'
+ EvaluateTargetHealth: False
+
+ ECSTaskExecutionRoleStack:
+ Type: AWS::CloudFormation::Stack
+ Properties:
+ TemplateURL: !Join [ '', ['https://', !Ref TemplateBucketName, '.s3.amazonaws.com/cf-gerrit-task-execution-role.yml'] ]
+ TimeoutInMinutes: '5'
+
+Outputs:
+ ReplicationLoadBalancerDNSName:
+ Description: The url of the replication load balancer
+ Value: !GetAtt 'LoadBalancer.DNSName'
+ Export:
+ Name: !Join [ ':', [ !Ref 'AWS::StackName', 'ReplicationLoadBalancerDNSName' ] ]
+ ReplicationDNSRecord:
+ Description: Alias DNS record for the replication load balancer URL
+ Value: !Sub '${GitReplicationSubdomain}.${HostedZoneName}'
+ Export:
+ Name: !Join [ ':', [ !Ref 'AWS::StackName', 'ReplicationDNSRecord' ] ]
\ No newline at end of file
diff --git a/dual-master/setup.env.template b/dual-master/setup.env.template
index 333bd89..e4a6c2b 100644
--- a/dual-master/setup.env.template
+++ b/dual-master/setup.env.template
@@ -2,6 +2,12 @@
CLUSTER_INSTANCE_TYPE:=m4.2xlarge
SERVICE_MASTER1_STACK_NAME:=$(AWS_PREFIX)-service-master-1
SERVICE_MASTER2_STACK_NAME:=$(AWS_PREFIX)-service-master-2
+
+REPLICATION_SERVICE_ENABLED:=false
+SERVICE_REPLICATION_STACK_NAME:=$(AWS_PREFIX)-service-replication
+SERVICE_REPLICATION_DESIRED_COUNT:=1
+GIT_REPLICATION_SUBDOMAIN:=$(AWS_PREFIX)-replication
+
SERVICE_SLAVE_STACK_NAME:=$(AWS_PREFIX)-service-slave
HTTP_HOST_PORT_MASTER2:=8081
SSH_HOST_PORT_MASTER2:=29419