AWS Security Token Serviceを使ってみる
AWS SDKやAWSコマンドラインツールを使う場合、Security Credentialsが必要になります。Security Credentialsの中にはアクセスキーとシークレットアクセスキーが含まれています。AWSのサービスやリソースをAPIを使って管理するときには、この2つのキーを使って認証します。
この2つのキーをどうやって保管するか?が本記事の目標とするところです。
2つのキーをソースコードやもしくはテキストファイル、DB等に格納して使うこともできますが、これだとキーの置き換えをするのが大変です。そこでAWSから提供されているSecurity Token Serviceというサービスを使って、キーの管理を安全に、かつ効率的に行うことを試してみました。
Security Token Serviceとは?
一時的な、かつ制限された特権を持つAWSアカウント、またはIAMユーザのCredential情報を取得可能にする機能です。Security Token Serviceは、以下の3つのアクションが可能です。
- AssumeRole
- IAM Roleの権限を一時的に取得可能。AssumeRoleを実行する時にはIAMのCredentialを使って呼び出す必要あり。AWS親アカウントでは呼び出せないらしい。
- GetFederationToken
- IAMユーザにfederatedされた権限を一時的に取得可能。リクエスト内でユーザ名とポリシーを指定する。
- GetSessionToken
- AWSアカウントまたはIAMユーザの権限を一時的に取得可能。
AWS SDKでSecurity Token Serviceより権限を取得できる。対応しているSDKは2013/04/16時点では以下。
http://docs.aws.amazon.com/STS/latest/UsingSTS/AccessingSTS.html
AWS SDK for Javaを使って、GetSessionTokenした例が以下。今回は取得した権限でS3のバケットリストを表示させている。
import java.io.IOException; import java.util.List; import com.amazonaws.AmazonWebServiceClient; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.BasicSessionCredentials; import com.amazonaws.auth.PropertiesCredentials; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.Bucket; import com.amazonaws.services.s3.model.ObjectListing; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; import com.amazonaws.services.securitytoken.model.Credentials; import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest; import com.amazonaws.services.securitytoken.model.GetSessionTokenResult; public class MySecurityTokenSample { public static void main(String[] args) throws IOException { //STSからトークンを取得するために必要なCredential情報 AWSCredentials credentials = new PropertiesCredentials( MySecurityTokenSample.class.getResourceAsStream("AwsCredentials.properties")); //SecurityTokenServiceClientの設定 AWSSecurityTokenServiceClient sts = new AWSSecurityTokenServiceClient(credentials); GetSessionTokenRequest req = new GetSessionTokenRequest(); GetSessionTokenResult res = sts.getSessionToken(req); Credentials tmpCredentials = res.getCredentials(); String accessKeyId = tmpCredentials.getAccessKeyId(); String secretAccessKeyId = tmpCredentials.getSecretAccessKey(); //取得したアクセスキー、シークレットアクセスキーでS3のバケットリストを表示してみる。 BasicSessionCredentials c = new BasicSessionCredentials(tmpCredentials.getAccessKeyId(), tmpCredentials.getSecretAccessKey(), tmpCredentials.getSessionToken()); AmazonS3Client s3Client = new AmazonS3Client(c); s3Client.setEndpoint("s3-ap-northeast-1.amazonaws.com"); List<Bucket> list = s3Client.listBuckets(); for(Bucket b : list) { System.out.println(b.getName()); } } }
こちらを実行すると、バケット名のリストが表示された。
トークンを取得するためには、やはりSecurity Credentialsの情報が必要。なので、システムの中に1台だけトークン管理サーバなるものを立てて、そこの中では唯一Credentials情報を持つ。他のサーバで一時的にCredentials情報が必要な場合は、このサーバが何かしらの安全な方法で配ってあげる形が良さそう。取得したトークンは、デフォルトで12時間まで有効。その時間が過ぎると無効になる。以下のように実行することで、その時間も変更できる。
getSessionTokenRequest.setDurationSeconds(7200);
上記例では、AWSアカウントの権限を取得していたが、以下のようにgetFederatedTokenを使って指定したIAM Userの権限を取得もできる。
import java.io.IOException; import java.util.List; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.BasicSessionCredentials; import com.amazonaws.auth.PropertiesCredentials; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.Bucket; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; import com.amazonaws.services.securitytoken.model.Credentials; import com.amazonaws.services.securitytoken.model.GetFederationTokenRequest; import com.amazonaws.services.securitytoken.model.GetFederationTokenResult; import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest; import com.amazonaws.services.securitytoken.model.GetSessionTokenResult; public class MySecurityTokenSample2 { public static void main(String[] args) throws IOException { //STSからトークンを取得するために必要なCredential情報 AWSCredentials credentials = new PropertiesCredentials( MySecurityTokenSample.class.getResourceAsStream("AwsCredentials.properties")); //SecurityTokenServiceClientの設定 AWSSecurityTokenServiceClient sts = new AWSSecurityTokenServiceClient(credentials); GetFederationTokenRequest req = new GetFederationTokenRequest(); req.setName("TVMUser1"); req.setPolicy("{\"Statement\": [{\"Effect\": \"Allow\",\"Action\": \"s3:*\",\"Resource\": \"*\"}]}"); GetFederationTokenResult res = sts.getFederationToken(req); Credentials tmpCredentials = res.getCredentials(); //取得したアクセスキー、シークレットアクセスキーでS3のバケットリストを表示してみる。 BasicSessionCredentials c = new BasicSessionCredentials(tmpCredentials.getAccessKeyId(), tmpCredentials.getSecretAccessKey(), tmpCredentials.getSessionToken()); AmazonS3Client s3Client = new AmazonS3Client(c); s3Client.setEndpoint("s3-ap-northeast-1.amazonaws.com"); List<Bucket> list = s3Client.listBuckets(); for(Bucket b : list) { System.out.println(b.getName()); } } }
TVMUser1というIAM Userで、S3のみにアクセス可能なポリシーをJSONで書いたものを使ってGetFederationTokenしている。
req.setPolicyでは以下のJSONデータを引数に入れている。
{ "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": "*" } ] }
このように、Security Token Serviceを使えば、一時的なCredentials情報を取得できることがわかった!
リンク
- http://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingTempFederationTokenJava.html
- http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/securitytoken/AWSSecurityTokenServiceClient.html
- http://docs.aws.amazon.com/general/latest/gr/rande.html#sts_region
- http://docs.aws.amazon.com/STS/latest/UsingSTS/STSMgmtConsole.html
DynamoDBでscanを実行してみる
DynamoDBのテーブル名が SampleTable, AttributeがIdとNameのみのテーブルに対してScanを実行した。
AWS SDK for Javaで以下のように実行してみた。
import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.PropertiesCredentials; import com.amazonaws.services.dynamodb.AmazonDynamoDBClient; import com.amazonaws.services.dynamodb.model.AttributeValue; import com.amazonaws.services.dynamodb.model.ComparisonOperator; import com.amazonaws.services.dynamodb.model.Condition; import com.amazonaws.services.dynamodb.model.GetItemRequest; import com.amazonaws.services.dynamodb.model.GetItemResult; import com.amazonaws.services.dynamodb.model.Key; import com.amazonaws.services.dynamodb.model.PutItemRequest; import com.amazonaws.services.dynamodb.model.PutItemResult; import com.amazonaws.services.dynamodb.model.ScanRequest; import com.amazonaws.services.dynamodb.model.ScanResult; public class DynamoDBLoadGenerator { static AmazonDynamoDBClient dynamoDB; public static void main(String[] args) throws IOException { //Credentialsの設定 AWSCredentials credentials = new PropertiesCredentials( DynamoDBSample.class.getResourceAsStream("AwsCredentials.properties")); //リクエストのパラメータの設定 dynamoDB = new AmazonDynamoDBClient(credentials); dynamoDB.setEndpoint("http://dynamodb.ap-northeast-1.amazonaws.com"); String tableName = "SampleTable"; //ScanFilterConditionの設定 Condition scanFilterCondition = new Condition() .withComparisonOperator(ComparisonOperator.LT.toString()) //〇〇よりも小さい .withAttributeValueList(new AttributeValue().withN("500")); //500 Map<String, Condition> conditions = new HashMap<String, Condition>(); conditions.put("Id", scanFilterCondition); ScanRequest scanRequest = new ScanRequest() .withTableName(tableName) .withScanFilter(conditions) .withAttributesToGet(Arrays.asList("Id")); //Idに対してScanFilterを実行 //Scan実行 ScanResult scanResult = dynamoDB.scan(scanRequest); //結果表示 for (Map<String, AttributeValue> item : scanResult.getItems()){ System.out.println(item.get("Id")); } } }
実行結果は以下のように出力される。
{N: 251, } {N: 187, } {N: 154, } {N: 7, } {N: 115, } {N: 286, } {N: 361, } {N: 380, } {N: 117, } {N: 401, } {N: 47, } {N: 184, } {N: 403, } {N: 304, } {N: 156, } {N: 122, } {N: 273, } 以下省略
IDが500以下のデータが出力された。
また今回使用したScanFilterConditionでComparisonOperatorで、BETWEENやBEGIN WITHやCONTAINなどいろいろ指定できる。今回使ったのはLT(おそらくはLess Thanのことだと思う)。これでscanするフィルターをいろいろ設定できそう。
試しにNameが"Book 500"とイコールであるIdをFilterConditionで指定したのが以下。
//ScanFilterConditionの設定 Condition scanFilterCondition = new Condition() .withComparisonOperator(ComparisonOperator.EQ.toString()) .withAttributeValueList(new AttributeValue().withS("Book 500"));
以下はComparisonOperatorのJavaDoc
[http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodb/model/ComparisonOperator.html:title=
http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodb/model/ComparisonOperator.html]
AWS OpsWorksで起動されたインスタンスの中身を見てみる
AWS OpsWorksを使ってHAProxyのレイヤーを追加し、HAProxyインスタンスを立ち上げてみる。
自分のkeypairを使うように設定すれば、SSHでログインすることができる。
早速ログインしてみる。
ec2-userでログインができた。
OpsWorksの動作に関連しそうなものは以下のディレクトリにあった。
[ec2-user@stratus aws]$ sudo ls opsworks/current/bin chef-client gli opsworks-agent-cli restclient chef-solo knife opsworks-agent-installer s3curl.pl convert_to_should_syntax lockrun opsworks-agent-updater shef edit_json.rb nokogiri prettify_json.rb thor erubis ohai rake extract opsworks-agent rake2thor
chef-soloやchef-client, opsworks-agent-cliなどがある。
AWSのドキュメントには、それぞれのインスタンスでchef-soloが動いていると書かれていた。
以下抜粋
Here's how it works: Chef Solo runs on every instance, and AWS OpsWorks sends commands to each instance to run recipes that set up, configure, and shut down the instance. You can extend these actions with your own custom recipes. You can also run your custom recipes on a specific instance or on all instances in your stack.
さらにopsworks-agent-cliで以下のようなコマンドも実行できる。
- agent_report
- get_json
- instance_report
- list_commands
- run_command
- show_log
- stack_state
詳細は以下。
http://docs.aws.amazon.com/opsworks/latest/userguide/agent.html
試しにget_jsonコマンドを実行してみた。(環境をまるさらしだが、すぐ消すので良しとしよう。。)
$ sudo opsworks/current/bin/opsworks-agent-cli get_json { "recipes": [ "opsworks_custom_cookbooks::load", "opsworks_ganglia::configure-client", "ssh_users", "agent_version", "haproxy::configure", "opsworks_stack_state_sync", "opsworks_custom_cookbooks::execute", "test_suite", "opsworks_cleanup" ], "opsworks_bundler": { "version": "1.0.10", "manage_package": null }, "opsworks_rubygems": { "version": "1.8.24" }, "opsworks": { "deployment": null, "valid_client_activities": [ "reboot", "stop", "setup", "configure", "update_dependencies", "install_dependencies", "update_custom_cookbooks", "execute_recipes" ], "applications": [ { "application_type": "php", "slug_name": "php_app", "name": "php-app" } ], "ruby_version": "1.8.7", "activity": "configure", "ruby_stack": "ruby_enterprise", "stack": { "name": "SampleStack3" }, "instance": { "ip": null, "id": "3d9fc521-1bfa-4d82-b74c-f0761383394c", "private_ip": "10.132.159.132", "aws_instance_id": "i-d75147d4", "backends": 5, "layers": [ "lb" ], "public_dns_name": null, "hostname": "stratus", "region": "ap-northeast-1", "instance_type": "m1.small", "availability_zone": "ap-northeast-1a", "private_dns_name": "ip-10-132-159-132.ap-northeast-1.compute.internal", "architecture": "i386" }, "agent_version": "104", "layers": { "lb": { "id": "f64dc03e-0c01-4f41-b588-b8289e03deca", "name": "Load Balancer", "instances": { "stratus": { "ip": null, "elastic_ip": "54.249.245.56", "private_ip": "10.132.159.132", "aws_instance_id": "i-d75147d4", "created_at": "2013-03-13T06:38:58+00:00", "status": "online", "booted_at": "2013-03-13T06:40:49+00:00", "backends": 5, "public_dns_name": null, "region": "ap-northeast-1", "instance_type": "m1.small", "availability_zone": "ap-northeast-1a", "private_dns_name": "ip-10-132-159-132.ap-northeast-1.compute.internal" } } }, "php-app": { "id": "c38c9a7f-90eb-4764-8ebd-3b48592f57cb", "name": "PHP Application Server", "instances": { "altocumulus": { "ip": "54.249.207.244", "elastic_ip": null, "private_ip": "10.162.54.166", "aws_instance_id": "i-2b5d4b28", "created_at": "2013-03-13T06:48:12+00:00", "status": "online", "booted_at": "2013-03-13T06:49:30+00:00", "backends": 5, "public_dns_name": "ec2-54-249-207-244.ap-northeast-1.compute.amazonaws.com", "region": "ap-northeast-1", "instance_type": "m1.small", "availability_zone": "ap-northeast-1a", "private_dns_name": "ip-10-162-54-166.ap-northeast-1.compute.internal" }, "stratocumulus": { "ip": "54.249.153.134", "elastic_ip": null, "private_ip": "10.152.46.168", "aws_instance_id": "i-07405604", "created_at": "2013-03-13T07:30:05+00:00", "status": "online", "booted_at": "2013-03-13T07:31:22+00:00", "backends": 5, "public_dns_name": "ec2-54-249-153-134.ap-northeast-1.compute.amazonaws.com", "region": "ap-northeast-1", "instance_type": "m1.small", "availability_zone": "ap-northeast-1a", "private_dns_name": "ip-10-152-46-168.ap-northeast-1.compute.internal" } } } }, "sent_at": 1363160598, "rails_stack": { "name": null } }, "opsworks_custom_cookbooks": { "recipes": [ ], "enabled": false }, "deploy": { "php_app": { "memcached": { "host": null, "port": 11211 }, "rails_env": null, "migrate": false, "ssl_certificate_ca": null, "auto_bundle_on_deploy": true, "ssl_support": false, "domains": [ "php_app" ], "database": { "reconnect": true, "host": null, "database": "php_app", "password": null, "username": "root" }, "ssl_certificate": null, "symlink_before_migrate": { "config/opsworks.php": "opsworks.php" }, "application_type": "php", "symlinks": { }, "mounted_at": null, "restart_command": "echo 'restarting app'", "document_root": null, "sleep_before_restart": 0, "application": "php_app", "deploy_to": "/srv/www/php_app", "scm": { "user": null, "repository": "git://github.com/funasaki/helloworld-php.git", "password": null, "revision": null, "scm_type": "git", "ssh_key": null }, "ssl_certificate_key": null } }, "haproxy": { "static_applications": { }, "health_check_method": "GET", "rails_applications": { }, "nodejs_backends": [ ], "php_applications": { "php_app": { "domains": [ "php_app" ], "application_type": "php", "mounted_at": null } }, "enable_stats": true, "nodejs_applications": { }, "php_backends": [ { "ip": "10.162.54.166", "backends": 10, "name": "altocumulus" }, { "ip": "10.152.46.168", "backends": 10, "name": "stratocumulus" } ], "rails_backends": [ ], "stats_url": "/haproxy?stats", "static_backends": [ ], "stats_password": "mypassword", "stats_user": "opsworks", "health_check_url": "/" }, "ssh_users": { } }
さらにログファイルも当然だがあった。
$ ls /var/log/aws/opsworks
installer.log opsworks-agent.log user-data.log
opsworks-agent-cli.log updater.log
Amazon LinuxにZabbix 1.8.16をインストール
Amazon Linux 2012.09にZabbix 1.8.16をインストールしました。
そのときに以下のページを参照させて頂きました。
http://dev.classmethod.jp/cloud/aws/zabbix1_8_15-on-amazon-linux/
Amazon RDS(MySQL)のログファイル出力について
Amazon RDSでログファイルにアクセス可能になりました。
【AWS発表】Amazon RDS - より簡単にログファイルにアクセス可能に
今まではRDS MySQLのgeneral logなどはRDSのテーブル内に出力していました。
上記機能により、テーブルではなくファイルのみに出力させることが可能となっています。
TABLEかFILEか、どちらかを選択する形です。
ファイルの出力先の場所はgeneral_log_fileの場合は、/rdsdbdata/log/general/mysql-general.logですが、こちらの出力先の場所を変更することは出来ないようです。
また、ログファイルのサイズが大きくなりすぎてしまい、DBのパフォーマンスに影響が出てしまうのを避けるため、以下の条件になったときにログファイルが自動的にrotateされます。
- When disk space usage is greater than 90% of the allocated space and a log uses either more than 10% of the allocated storage or the log uses greater than 5 GB.
- If a log uses more than 20% of the allocated storage or a log uses greater than 10 GB, regardless of total disk usage.
参照
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_LogAccess.Concepts.MySQL.html
GitマスターサーバをAmazon Linuxにインストールしてみる
Gitマスターサーバにgitをインストールする。
$ sudo yum install git git-all git-daemon $ git --version git version 1.7.4.5
xinetdを使ってgitサーバを起動する
sudo /etc/init.d/xinetd start
gitの設定ファイルでdisable = no に変更
sudo vi /etc/xinedt.d/git # default: off # description: The git damon allows git repositories to be exported using \ # the git:// protocol. service git { disable = no socket_type = stream wait = no user = nobody server = /usr/libexec/git-core/git-daemon server_args = --base-path=/var/lib/git --export-all --user-path=publ ic_git --syslog --inetd --verbose log_on_failure += USERID }
xinetdをrestartする。
sudo /etc/init.d/xinetd restart
リポジトリを作成する。
cd /var/lib/git sudo mkdir test cd test sudo git --bare init --shared sudo chmod 777 -R test
次にリモートのGitクライアントの設定
Gitをインストールする。
sudo yum install git
サンプルテキストの作成
mkdir ~/test cd ~/test vi test.txt aiueo
作成したテキストをcommitする
git init git add . git commit -m "First Commit"
リモートのGitマスターサーバに接続するためのSSHの設定をする
vi ~/.ssh/config Host ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com User ec2-user IdentityFile ~/.ssh/xxxxxx.pem sudo chmod 600 /home/ec2-user/.ssh/config sudo chmod 600 /home/ec2-user/.ssh/xxxxxx.pem
リモートのマスターを追加する
$ git remote add origin ssh://ec2-user@ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com/var/lib/git/test
リモートのマスターのリポジトリにpushする。
$ git push origin master
これで、マスター側の該当ディレクトリを見ると
$ ls -F branches/ config* description* HEAD* hooks/ info/ objects/ refs/
branchesディレクトリ等いろいろできている。
まだ確認できることはありそうだけど、今回はここまで。
ElastiCacheを使ってみる
Amazon ElastiCacheはインストール、構築不要のmemcachedクラスタ。
現状はMemcached 1.4.5, 1.4.14 に対応しているようです。
使ってみて気づいたことは以下です。
- ElastiCacheへはEC2(VPC)インスタンスからのみアクセスが可能。
- ElastiCacheのセキュリティグループの設定により、指定したセキュリティグループを持ったインスタンスしかアクセスを許さない。
- Management ConsoleでJavaとPHPのクライアントアクセスライブラリを取得可能
それではElasticCacheを起動して、Java Clientからアクセスしてみます。
まずは、作成・起動。
Management Consoleから、
ElastiCacheを起動します。
起動画面で名前、Node Type(インスタンスのタイプ)、Nodeの数、等を指定します。
Cache Security Groupを指定します。ここで指定するものは事前に作成しておいたものを利用します。Cache Security Groupの中では、EC2のどのセキュリティグループ(を持つインスタンス)をアクセス許可するかを指定します。
確認してLaunch Cache Clusterします。
これで起動完了すれば、エンドポイントおよびデフォルトポート11211番を使ってアクセスできるようになります。
ただ、上記にもあるようにEC2のセキュリティグループを持ったインスタンスしかアクセスできないため、ローカルのPCからElastiCacheクラスタノードに直接アクセスできません。(試したかったのですが。)
なので、今回はEC2インスタンス上で以下のJavaのソースコードを作成して、コンパイルしたものでアクセスしてみました。
import java.io.IOException; import java.net.InetSocketAddress; import net.spy.memcached.AddrUtil; import net.spy.memcached.BinaryConnectionFactory; import net.spy.memcached.ClientMode; import net.spy.memcached.MemcachedClient; public class ElastiCacheSample { public static void main(String[] args) { String hostname = "cachecluster2.wfisum.cfg.apne1.cache.amazonaws.com"; int portNum = 11211; String hostnamePort = hostname + ":11211"; String key = "12345"; try { MemcachedClient c = new MemcachedClient(new InetSocketAddress(hostname, portNum)); // Store a value (async) for one hour c.set("key1", 3600, key); // Retrieve a value. Object myObject=c.get("key1"); c.shutdown(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }
今回はMemcachedClientのshutdown()メソッドを呼び出して、終了するようにしています。
こちらを実行すると
$ java -cp ".:./AmazonElastiCacheClusterClient-1.0.jar" ElastiCacheSample 2013-02-14 18:20:25.826 INFO net.spy.memcached.MemcachedConnection: Added {QA sa=/10.160.135.101:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue 2013-02-14 18:20:25.837 INFO net.spy.memcached.MemcachedConnection: Connection state changed for sun.nio.ch.SelectionKeyImpl@5ca46701 2013-02-14 18:20:25.858 INFO net.spy.memcached.MemcachedConnection: Added {QA sa=/10.160.135.101:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue 2013-02-14 18:20:25.859 INFO net.spy.memcached.MemcachedConnection: Added {QA sa=/10.160.83.93:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue 2013-02-14 18:20:25.865 INFO net.spy.memcached.MemcachedConnection: Connection state changed for sun.nio.ch.SelectionKeyImpl@42704baa 2013-02-14 18:20:25.866 INFO net.spy.memcached.MemcachedConnection: Connection state changed for sun.nio.ch.SelectionKeyImpl@5a77a7f9 2013-02-14 18:20:25.915 INFO net.spy.memcached.MemcachedConnection: Shut down memcached client
以上のようにmemcachedに指定したkeyが追加され、取得も無事できました。