Memcache automatic horizontal scaling - memcached

We have a scenario with 2 web servers and 2 memcache servers. The memcache server nodes are configured within the configuration files on each web server.
In order for us to automatically horizontally scale the web servers we need to be able to automatically horizotally scale the memcache servers. Currently we are not able to do that due to the memcache nodes being defined in the web configuration files.
What is the best way to automatically horizontally scale the memcache servers with the least impact on the system.

You can try mcrouter.
What is Mcrouter?
It is a memcached protocol router that is used at Facebook to handle all traffic of cache servers across dozens of clusters distributed in Facebook data centres around the world.
How the scaling happens?
To adding a new server in to the cluster, just adding an IP entry in the route config file. 'mcrouter' automatically detects it and add to the cluster with consistent hashing.
The config file looks like below, with 2 servers entries.
{
"pools": {
"A": {
"servers": [
"SERVER1_IP:11211",
"SERVER2_IP:11211"
]
}
},
"route": {
"type": "OperationSelectorRoute",
"operation_policies": {
"add": "AllSyncRoute|Pool|A",
"delete": "AllSyncRoute|Pool|A",
"get": "RandomRoute|Pool|A",
"set": "AllSyncRoute|Pool|A"
}
}
}
Hope it helps!

Related

McRouter loses key on scale up

I'm running a k8s cluster in GKE and used their walkthrough of putting together a McRouter setup with memcached. Initially we were using consul keystores but our cache is too large and causes consul to use too much memory, so we decided to test out memcache in it's place. I spin up the mcrouter daemonset and have a single pod of memcache and everything works just fine. This is when I added some test keys. They get and delete ok. The issue comes when I leave keys in place and scale.
I scale up the memcache statefulset and add the second server name to the configmap for mcrouter. Once I see the new server using stats servers I then run a get and one of the keys is no longer there. I've telneted to 11211 on the original memcache pod and run a get and can retrieve the same key just fine. The config provided in the configmap is below:
{
"pools": {
"A": {
"servers": [
"memcached-0.memcached.default.svc.cluster.local:11211",
"memcached-1.memcached.default.svc.cluster.local:11211"
]
}
},
"route": "PoolRoute|A"
}
I've also moved to using a statefulset for mcrouter to limit to only one pod, and also switched to using the official docker image rather than the one in the k8s example Helm Chart, and had no luck. No matter what I do, I keep getting a "not found" via the mcrouter get on at least one key after scaling while other keys are still found fine. Help?
With PoolRoute, routes to the destination are based on key hash.
So, when you scale up the memcache stateful set with a new memcache, the key hash of your key change. That's why Mcrouter is not able to get your key even if it's already present in your cache.
Let's have a look at the wiki: https://github.com/facebook/mcrouter/wiki/List-of-Route-Handles
And maybe this kind of configuration may help you:
{
"pools": {
"A": {
"servers": [
"memcached-0.memcached.default.svc.cluster.local:11211",
"memcached-1.memcached.default.svc.cluster.local:11211"
]
},
"fallback": {
"servers": [
"memcached-fallback.memcached.default.svc.cluster.local:11211"
]
}
},
"route": {
"type": "FailoverRoute",
"normal": {
"type": "PoolRoute",
"pool": "A"
},
"failover": "PoolRoute|fallback"
}
}

Creating a 2-node CouchDB 2.1 cluster

I tried to create a 2-node cluster with CouchDB 2.1 multiple times (both on windows and ubuntu) and it never worked. I did exactly as described in the official documentation here.
When I finish the cluster-configuration of the two nodes, I expect to create a database on node1, which should show up on node2. Verification on both nodes via fauxton fails also "internal server error" - that happens both under Linux (ubuntu 14.04) and windows (10, Server 2012, Server 2016) with version 2.1
Configuring both CouchDB-Nodes via API:
node1: (10.0.0.1)
1. POST {"action": "enable_cluster", "bind_address":"0.0.0.0", "username": "admin", "password":"mypassword", "node_count":"2"}
POST {"action": "enable_cluster", "bind_address":"0.0.0.0", "username": "admin", "password":"mypassword", "port": 5984, "node_count": "2", "remote_node": "10.0.0.2", "remote_current_user": "admin", "remote_current_password": "mypassword" }
POST {"action": "add_node", "host":"10.0.0.2", "port": "5984", "username": "admin", "password":"mypassword"}
POST {"action": "finish_cluster"}
http://10.0.0.1:5984/_membership
{"all_nodes":["couchdb#localhost"],"cluster_nodes":["couchdb#10.0.0.2","couchdb#localhost"]}
node2(10.0.0.2)
same configuration as node1, but IP address for other node changes to 10.0.0.1
http://10.0.0.2:5984/_membership
{"all_nodes":["couchdb#localhost"],"cluster_nodes":["couchdb#10.0.0.1","couchdb#localhost"]}
I never defined a zone - is this mandatory for the cluster to work?
Is anybody here who already set up a working Couchdb-Cluster with 2 or more nodes ?
Is anybody able to see a mistake I made whilst configuring the nodes? Please let me know if I can provide more information.
Help would be greatly appreciated.
best regards,
Harald
I've configured a 3 node cluster of CouchDB 2
In my opinion the main difficulty is to correctly setting the networking configuration between nodes for the erlang communication.
http://docs.couchdb.org/en/2.1.1/cluster/setup.html#cluster-setup
First, you should be sure that erlang is communicating between nodes. (http://docs.couchdb.org/en/2.1.1/cluster/setup.html#first-time-in-erlang-time-to-play)
You should set the erlang node name value in the vm.args file of your couchdb installation.
The name you use should be resolvable by a DNS or local hosts file in both nodes.
Finally, when you are sure that erlang is communicating you should register both nodes in the cluster.

Utilize Managed Disk for Service Fabric temporary storage

Is it possible to configure and deploy a Service Fabric cluster that uses a Managed Disk as the temporary storage location for things like the replicator log and app type/versions?
For example, I can't use an A1_v2 VM instance size because the D: (Temporary Storage) drive is too small. If I could leverage a Managed Disk and configure SF to use it instead of the local SSD then this instance size would work for my dev/test scenarios.
Any idea if and how I can make this work?
Disclaimer: You can do this, but you shouldn't. Details below.
Consider changing the size of the shared log file instead if you really want to use such small VMs.
"fabricSettings": [{
"name": "KtlLogger",
"parameters": [{
"name": "SharedLogSizeInMB",
"value": "4096"
}]
}]
More info on configuration here.
Now to actually answer:
Here are the settings. You'd probably change Setup/FabricDataRoot to move the Service Fabric local installation and all of the local application working directories, and/or TransactionalReplicator/SharedLogPath to move the reliable collections shared log.
Some things to consider:
Service Fabric Services (and Service Fabric itself) are built to work on local disks and generally should not be hosted on XStore backed disks (premium or not):
Reliable Collections are definitely built to operate against local drives. There's no internal testing that I'm aware of that runs them in this configuration.
Waste of IO: Assuming LRS replicates changes 3 times and you set TargetReplicaSetSize to 3, this configuration will generate 9 copies of the state. Do you need 9 copies of your state?
Impact on Latency and Performance: What should be a local disk IO will turn into network + disk IO, which has a chance to hurt your performance.
Impact on Availability: At a minimum you're adding another dependency, which usually reduces overall availability. If storage ever has an issue you're now more coupled to that other service. Today you're pretty coupled already since the VMSS drives are backed by blobs, so VM provisioning would fail, but that's different than the read/write/activation path for your services.

MongoDB sharding: mongos and configuration servers together?

We want to create a MongoDB shard (v. 2.4). The official documentation recommends to have 3 config servers.
However, the policies of our company won't allow us to get 3 extra servers for this purpose. Since we have already 3 application servers (1 web node, 2 process nodes) we are considering to put the configuration servers in the same application servers, with the mongos. Availability is not critical for us.
What do you think about this configuration? Can we face some problem or is it discouraged for some reason?
Given that Availability is not critical for your use case, I would say it should be fine to place the config servers in the same application servers and mongos.
If one of the process nodes is down, you will lose: 1 x mongos, 1 application server and 1 config server. During this down time, the other two config servers will be read-only , which means there won't be balancing of shards, modification to cluster config etc. Although your other two mongos should still be operational (CRUD wise). If your web-node is down, then you have a bigger problem to deal with.
If two of the nodes are down (2 process nodes, or 1 web server and process node), again, you would have bigger problem to deal with. i.e. Your applications are probably not going to work anyway.
Having said that, please consider the capacity of these nodes to be able to handle a mongos, an application server and a config server. i.e. CPU, RAM, network connections, etc.
I would recommend to test the deployment architecture in a development/staging cluster first under your typical workload and use case.
Also see Sharded Cluster High Availability for more info.
Lastly, I would recommend to check out MongoDB v3.2 which is the current stable release. The config servers in v3.2 are modelled as a replica set, see Sharded Cluster config servers for more info.

Mongodb cluster with aws cloud formation and auto scaling

I've been investigating creating my own mongodb cluster in AWS. Aws mongodb template provides some good starting points. However, it doesn't cover auto scaling or when a node goes down. For example, if I have 1 primary and 2 secondary nodes. And the primary goes down and auto scaling kicks in. How would I add the newly launched mongodb instance to the replica set?
If you look at the template, it uses an init.sh script to check if the node being launched is a primary node and waits for all other nodes to exist and creates a replica set with thier ip addresses on the primary. When the Replica set is configured initailly, all the nodes already exist.
Not only that, but my node app uses mongoose. Part of the database connection allows you to specify multiple nodes. How would I keep track of what's currently up and running (I guess I could use DynamoDB but not sure).
What's the usual flow if an instance goes down? Do people generally manually re-configure clusters if this happens?
Any thoughts? Thanks.
This is a very good question and I went through this very painful journey myself recently. I am writing a fairly extensive answer here in the hope that some of these thoughts of running a MongoDB cluster via CloudFormation are useful to others.
I'm assuming that you're creating a MongoDB production cluster as follows: -
3 config servers (micros/smalls instances can work here)
At least 1 shard consisting of e.g. 2 (primary & secondary) shard instances (minimum or large) with large disks configured for data / log / journal disks.
arbiter machine for voting (micro probably OK).
i.e. https://docs.mongodb.org/manual/core/sharded-cluster-architectures-production/
Like yourself, I initially tried the AWS MongoDB CloudFormation template that you posted in the link (https://s3.amazonaws.com/quickstart-reference/mongodb/latest/templates/MongoDB-VPC.template) but to be honest it was far, far too complex i.e. it's 9,300 lines long and sets up multiple servers (i.e. replica shards, configs, arbitors, etc). Running the CloudFormation template took ages and it kept failing (e.g. after 15 mintues) which meant the servers all terminated again and I had to try again which was really frustrating / time consuming.
The solution I went for in the end (which I'm super happy with) was to create separate templates for each type of MongoDB server in the cluster e.g.
MongoDbConfigServer.template (template to create config servers - run this 3 times)
MongoDbShardedReplicaServer.template (template to create replica - run 2 times for each shard)
MongoDbArbiterServer.template (template to create arbiter - run once for each shard)
NOTE: templates available at https://github.com/adoreboard/aws-cloudformation-templates
The idea then is to bring up each server in the cluster individually i.e. 3 config servers, 2 sharded replica servers (for 1 shard) and an arbitor. You can then add custom parameters into each of the templates e.g. the parameters for the replica server could include: -
InstanceType e.g. t2.micro
ReplicaSetName e.g. s1r (shard 1 replica)
ReplicaSetNumber e.g. 2 (used with ReplicaSetName to create name e.g. name becomes s1r2)
VpcId e.g. vpc-e4ad2b25 (not a real VPC obviously!)
SubnetId e.g. subnet-2d39a157 (not a real subnet obviously!)
GroupId (name of existing MongoDB group Id)
Route53 (boolean to add a record to an internal DNS - best practices)
Route53HostedZone (if boolean is true then ID of internal DNS using Route53)
The really cool thing about CloudFormation is that these custom parameters can have (a) a useful description for people running it, (b) special types (e.g. when running creates a prefiltered combobox so mistakes are harder to make) and (c) default values. Here's an example: -
"Route53HostedZone": {
"Description": "Route 53 hosted zone for updating internal DNS (Only applicable if the parameter [ UpdateRoute53 ] = \"true\"",
"Type": "AWS::Route53::HostedZone::Id",
"Default": "YA3VWJWIX3FDC"
},
This makes running the CloudFormation template an absolute breeze as a lot of the time we can rely on the default values and only tweak a couple of things depending on the server instance we're creating (or replacing).
As well as parameters, each of the 3 templates mentioned earlier have a "Resources" section which creates the instance. We can do cool things via the "AWS::CloudFormation::Init" section also. e.g.
"Resources": {
"MongoDbConfigServer": {
"Type": "AWS::EC2::Instance",
"Metadata": {
"AWS::CloudFormation::Init": {
"configSets" : {
"Install" : [ "Metric-Uploading-Config", "Install-MongoDB", "Update-Route53" ]
},
The "configSets" in the previous example shows that creating a MongoDB server isn't simply a matter of creating an AWS instance and installing MongoDB on it but also we can (a) install CloudWatch disk / memory metrics (b) Update Route53 DNS etc. The idea is you want to automate things like DNS / Monitoring etc as much as possible.
IMO, creating a template, and therefore a stack for each server has the very nice advantage of being able to replace a server extremely quickly via the CloudFormation web console. Also, because we have a server-per-template it's easy to build the MongoDB cluster up bit by bit.
My final bit of advice on creating the templates would be to copy what works for you from other GitHub MongoDB CloudFormation templates e.g. I used the following to create the replica servers to use RAID10 (instead of the massively more expensive AWS provisioned IOPS disks).
https://github.com/CaptainCodeman/mongo-aws-vpc/blob/master/src/templates/mongo-master.template
In your question you mentioned auto-scaling - my preference would be to add a shard / replace a broken instance manually (auto-scaling makes sense with web containers e.g. Tomcat / Apache but a MongoDB cluster should really grow slowly over time). However, monitoring is very important, especially the disk sizes on the shard servers to alert you when disks are filling up (so you can either add a new shard to delete data). Monitoring can be achieved fairly easily using AWS CloudWatch metrics / alarms or using the MongoDB MMS service.
If a node goes down e.g one of the replicas in a shard, then you can simply kill the server, recreate it using your CloudFormation template and the disks will sync across automatically. This is my normal flow if an instance goes down and generally no re-configuration is necessary. I've wasted far too many hours in the past trying to fix servers - sometimes lucky / sometimes not. My backup strategy now is run a mongodump of the important collections of the database once a day via a crontab, zip up and upload to AWS S3. This means if the nuclear option happens (complete database corruption) we can recreate the entire database and mongorestore in an hour or 2.
However, if you create a new shard (because you're running out of space) configuration is necessary. For example, if you are adding a new Shard 3 you would create 2 replica nodes (e.g. primary with name => mongo-s3r1 / secondary with name => mongo-s3r2) and 1 arbitor (e.g. with name mongo-s3r-arb) then you'd connect via a MongoDB shell to a mongos (MongoDB router) and run this command: -
sh.addShard("s3r/mongo-s3r1.internal.mycompany.com:27017,mongo-s3r2.internal.mycompany.com:27017")
NOTE: - This commands assumes you are using private DNS via Route53 (best practice). You can simply use the private IPs of the 2 replicas in the addShard command but I have been very badly burned with this in the past (e.g. serveral months back all the AWS instances were restarted and new private IPs generated for all of them. Fixing the MongoDB cluster took me 2 days as I had to reconfigure everything manually - whereas changing the IPs in Route53 takes a few seconds ... ;-)
You could argue we should also add the addShard command to another CloudFormation template but IMO this adds unnecessary complexity because it has to know about a server which has a MongoDB router (mongos) and connect to that to run the addShard command. Therefore I simply run this after the instances in a new MongoDB shard have been created.
Anyways, that's my rather rambling thoughts on the matter. The main thing is that once you have the templates in place your life becomes much easier and defo worth the effort! Best of luck! :-)