背景
あるシステムをkubernetesで動かすために、ローカル環境にあるDockerイメージをECRにプッシュする必要があって、aws-cli
とdocker
コマンドでのやり方は理解していたが、他のメンバーにaws-cli
入れてもらうのとかシェルのスクリプト書くのもなんだかなーと思っていた。
そこで、そのシステムがRailsで動いているのでRubyでローカルでDockerイメージをビルドして、ECRにプッシュできないかなと考えていた。
概要
docker-apiでDockerイメージのビルドはできそうだったので、あとはaws-sdk-ecrのAws::ECR::Client
のput_image
かdocker-apiのDocker::Image
のpush
でECRへのイメージのプッシュができそうだった。
aws-sdk-ecrのAws::ECR::Client
のput_imageの必須オプションでimage_manifest
があったのだけど、今まで扱ったことないし、docker-apiでも取得できなそうだった。
Dockerのドキュメント見たら、docker manifest
関連のコマンド自体がexperimental
(実験的)っぽいので、Aws::ECR::Client
のput_image
を使う方法は断念。
Docker::Image
のpush
の方法は、Docker.authenticate!
通せるかが肝になる。
aws ecr get-login --region region --no-include-email
でdocker login -u AWS -p password https://aws_account_id.dkr.ecr.us-east-1.amazonaws.com
とdocker
でECR
にログインできる情報が取得できるので、Aws::ECR::Client
でなんとかその情報取れれば良さそう。
Aws::ECR::Client
のget_authorization_tokenのドキュメント見るとレスポンスの中のauthorizationToken
が
A base64-encoded string that contains authorization data for the specified Amazon ECR registry. When the string is decoded, it is presented in the format user:password for private registry authentication using docker login .
とdocker login
で使えるuser:password
がbase64
でエンコードされたものと書いてある。
要はbase64
デコードするとAWS:[docker loginで使えるパスワード]
が取得できる。
これで、Docker.authenticate!
通せそう。
コード
require 'docker'
require 'aws-sdk'
aws_access_key_id, aws_secret_access_key = [何らかの方法で取得(環境によると思うので省略)]
Aws.config.update(credentials: Aws::Credentials.new(aws_access_key_id, aws_secret_access_key))
tag = '0.0.1' # 実際はYAMLに書くかコマンド引数にすべきだかここでは一旦変数で持つ
ecr_client = Aws::ECR::Client.new
# 既にECRにプッシュされたものがないか確認
list_images_response = ecr_client.list_images(repository_name: 'hoge')
pushed_image = list_images_response.image_ids.detect { |image_id| image_id.image_tag == tag }
if pushed_image
puts 'Pushed image is found. Nothing to do.'
return
end
# Docker.authenticate!に必要な情報だけでなくendpointもget_authorization_tokenで取得できる
authorization_token_response = ecr_client.get_authorization_token
authorization_token = authorization_token_response.authorization_data[0].authorization_token
proxy_endpoint = authorization_token_response.authorization_data[0].proxy_endpoint
full_repository_name = proxy_endpoint.sub(Regexp.escape('https://'), '') + '/hoge'
# 既にローカルにビルドされたイメージがあれば取得
builded_image = Docker::Image.all.detect { |image| image.info['RepoTags'].first == "#{full_repository_name}:#{tag}" }
unless builded_image
# ビルドしてタグ付け
puts 'Building image'
builded_image = Docker::Image.build_from_dir('.')
builded_image.tag('repo' => full_repository_name, 'tag' => tag)
end
# docker loginで使えるユーザー名とパスワードの取得
username, password = Base64.decode64(authorization_token).split(':')
options = {'username' => username, 'password' => password, 'serveraddress' => proxy_endpoint, 'email' => 'none'}
Docker.authenticate!(options)
# ECRにプッシュ
builded_image.push
実行
あとはDockerfike
と同じディレクトリにpush_image_to_ecr.rb
を配置して、bundle exec rails r push_image_to_ecr.rb
するだけ(aws_access_key_id, aws_secret_access_key
の取得方法によってはruby push_image_to_ecr.rb
でも)。