はじめに
私は2021年現在新卒入社から2年目で主に自然言語処理を扱う会社で仕事をしています。
普段はAWSとは無縁(だいたいは先輩がやってくれている)の生活を送っているため、触る機会がありませんでしたが、AWSのSageMarkerを今後使ったらどうかという話になり調査をする必要がありました。
そこで重い腰をあげ、AWSをやろうとしたところ、さっそくエラーに当たり色々調べて対処したのでまとめたいと思います。
エラー内容は基本的なことになります。
Dockerにそこまで詳しくなく、AWSを初めての方でしたら直面するかもしれない問題でした。
環境
- windows
- Docker
- Python3
- JupyterNotebook
問題
Dockerを利用して、JupyterNotebook環境を立ち上げて、AWS SDK for pythonを使おうと以下のコードを実行しました。
!pip install --upgrade boto3
import boto3
client = boto3.client('s3', region_name='ap-northeast-1')
client.list_buckets()
すると、client.list_buckets()
でエラーになりました。
apiuser
AKIA5YFBRBZR23P37GGM
S8PNL4hhV5wWqkvZgpQIrR1YW/7codZHE7oZHry7
---------------------------------------------------------------------------
NoCredentialsError Traceback (most recent call last)
<ipython-input-2-e72cca7ea9ce> in <module>
2
3 client = boto3.client('s3', region_name='ap-northeast-1')
----> 4 client.list_buckets()
/opt/conda/lib/python3.8/site-packages/botocore/client.py in _api_call(self, *args, **kwargs)
384 "%s() only accepts keyword arguments." % py_operation_name)
385 # The "self" in this scope is referring to the BaseClient.
--> 386 return self._make_api_call(operation_name, kwargs)
387
388 _api_call.__name__ = str(py_operation_name)
/opt/conda/lib/python3.8/site-packages/botocore/client.py in _make_api_call(self, operation_name, api_params)
689 http, parsed_response = event_response
690 else:
--> 691 http, parsed_response = self._make_request(
692 operation_model, request_dict, request_context)
693
/opt/conda/lib/python3.8/site-packages/botocore/client.py in _make_request(self, operation_model, request_dict, request_context)
709 def _make_request(self, operation_model, request_dict, request_context):
710 try:
--> 711 return self._endpoint.make_request(operation_model, request_dict)
712 except Exception as e:
713 self.meta.events.emit(
/opt/conda/lib/python3.8/site-packages/botocore/endpoint.py in make_request(self, operation_model, request_dict)
100 logger.debug("Making request for %s with params: %s",
101 operation_model, request_dict)
--> 102 return self._send_request(request_dict, operation_model)
103
104 def create_request(self, params, operation_model=None):
/opt/conda/lib/python3.8/site-packages/botocore/endpoint.py in _send_request(self, request_dict, operation_model)
130 def _send_request(self, request_dict, operation_model):
131 attempts = 1
--> 132 request = self.create_request(request_dict, operation_model)
133 context = request_dict['context']
134 success_response, exception = self._get_response(
/opt/conda/lib/python3.8/site-packages/botocore/endpoint.py in create_request(self, params, operation_model)
113 service_id=service_id,
114 op_name=operation_model.name)
--> 115 self._event_emitter.emit(event_name, request=request,
116 operation_name=operation_model.name)
117 prepared_request = self.prepare_request(request)
/opt/conda/lib/python3.8/site-packages/botocore/hooks.py in emit(self, event_name, **kwargs)
354 def emit(self, event_name, **kwargs):
355 aliased_event_name = self._alias_event_name(event_name)
--> 356 return self._emitter.emit(aliased_event_name, **kwargs)
357
358 def emit_until_response(self, event_name, **kwargs):
/opt/conda/lib/python3.8/site-packages/botocore/hooks.py in emit(self, event_name, **kwargs)
226 handlers.
227 """
--> 228 return self._emit(event_name, kwargs)
229
230 def emit_until_response(self, event_name, **kwargs):
/opt/conda/lib/python3.8/site-packages/botocore/hooks.py in _emit(self, event_name, kwargs, stop_on_response)
209 for handler in handlers_to_call:
210 logger.debug('Event %s: calling handler %s', event_name, handler)
--> 211 response = handler(**kwargs)
212 responses.append((handler, response))
213 if stop_on_response and response is not None:
/opt/conda/lib/python3.8/site-packages/botocore/signers.py in handler(self, operation_name, request, **kwargs)
88 # this method is invoked to sign the request.
89 # Don't call this method directly.
---> 90 return self.sign(operation_name, request)
91
92 def sign(self, operation_name, request, region_name=None,
/opt/conda/lib/python3.8/site-packages/botocore/signers.py in sign(self, operation_name, request, region_name, signing_type, expires_in, signing_name)
160 raise e
161
--> 162 auth.add_auth(request)
163
164 def _choose_signer(self, operation_name, signing_type, context):
/opt/conda/lib/python3.8/site-packages/botocore/auth.py in add_auth(self, request)
371 def add_auth(self, request):
372 if self.credentials is None:
--> 373 raise NoCredentialsError()
374 datetime_now = datetime.datetime.utcnow()
375 request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP)
NoCredentialsError: Unable to locate credentials
ユーザーへの権限がないようでした。
また、この時点でIAMユーザーを作成してあり、AmazonS3FullAccess
の権限を追加してあり、手元にはアクセスキーID
とシークレットアクセスキー
がある状態です。
この状態でない方は、そちらの準備を調べて行ってください。
簡単ですので、説明は省略します。
C:\Users\<ユーザー名>/.aws/credentials
に以下を記入して認証情報の保存をします。
[default]
aws_access_key_id = <アクセスキーID>
aws_secret_access_key = <シークレットアクセスキー>
この設定をしているので、先ほどのJupyterのコードで認証されるかと思いましたがされませんでした。
解決方法
そもそも、Dockerとローカルの環境は別ですので、認証情報は読み取れないのが当たり前でした。
ですので、Dockerfileの最後に環境変数として記述することにしました。
Dockerfile
ENV AWS_ACCESS_KEY_ID <アクセスキーID>
ENV AWS_SECRET_ACCESS_KEY <シークレットアクセスキー>
この状態で先ほどのエラーが出たコードを実施したところ成功しました。
注意点
この方法で行うと、Dockerfileに直接アクセスキーを記述するためGitなどにPushした際に、他の人からキーを確認することができてしまいます。
ですので、.env
とdocker-compose.yml
、.gitignore
を利用して、他人にわからないように設定したほうが良いです。
こちらは別記事にまとめていますので、ご確認いただければと思います。
おわりに
以前Azureの初めてのデプロイでかなり苦戦したので、次はAWSの基本もこれを機会に抑えていきたいなと思っています。
Dockerもわかった気でいて、案外わからないことばかりですので記事にまとめていきたいです。
参考記事