- 投稿日:2019-06-25T23:35:46+09:00
[AWS ECS]Fargateのcontainerにシェルで入りたい(sshd無しで!)
[AWS ECS]Fargateのcontainerにシェルで入りたい(sshd無しで!)
ecsのFargateいいですね。コンテナ単位でcpuとかメモリとか指定する必要はあれど、elastic beanstalkや ec2ベースのecsクラスタのようにec2を意識せずにクラスタ組めるのはともていい感じだと思います。
・・・が、ec2を意識しない故に、困ったときのsshアクセスができません。これはトラブルシュート時や開発初期はとても困ります。
かといって、docker containerにsshの口を開けるのもなんとも負けた感がします。Fargateいいけど、docker execできたらなーと思っていましたが、そんなことはみんなが思っていることのようで、
下記のようなissueが数年前から立っておりました。How to run "docker exec... " command in ECS
すると最近(2019/5/8)
「SSMつかったら、Fargateコンテナのシェルにアクセスできたよ」
というコメントが出てきました!! (参考: https://github.com/aws/containers-roadmap/issues/187#issuecomment-490347856)
セッションマネージャーでawsコンソール(やaws-cli)から、シェルアクセスできるのは知っていたのですが、ec2が見えないfargateに関しては無理だろうと思い込んでいたのですが、読んでみるとできるようです。
これは来た!!とおもい試してみたところ、いろいろ使いづらいところはありつつも成功したのでメモ代わりに残しておきます。
セッションマネージャー
AWS System managerの セッションマネージャーに関しては下記の記事を参照ください。
SSH不要時代がくるか!?AWS Systems Manager セッションマネージャーがリリースされました!
簡単にいうと、awsのコンソールや、cliの環境から、特定のaws内のec2にシェルアクセスができる、というものです。
インスタンスIDなどが必要なことから、実際に見えるEC2でないとできないのかな?と思いこんでいたのですが、上記のissueのコメントを見てみるとどうもそうではないらしかったので試してみます。
手順
Fargate自体の仕組みや、設定に関するところ全体を書くと、関係しない部分が多くなるため、必要な部分のみ抜粋します。
しくみの概要
詳細手順の前に、どのように(EC2の見えない)Fargateコンテナに対して設定するのかの概要です。
前提:セッションマネージャーには、aws内のec2以外でも、 AWSのコンソールから System Managerにアクティベーション処理を行い、
amazon-ssm-agent
を起動しておくことで、 任意の環境のホストをSystem Managerから セッションマネージャーの機能を使える用に登録できる。
- これを踏まえて、事前にアクティベーションを登録しておいて、
Activation Code
とActivatoin Id
を取得(これらに関しては後述)しておく- Fargateで動くコンテナのビルド時にDockerfileで、
amazon-ssm-agent
をインストールしておく。- Fargetでコンテナが起動する際に、 上記「1.」の
Activation Code
、Activation Id
を使って、自分のコンテナを System Managerに登録する、かつ、amazon-ssm-agent
を引数なしで起動する。これで、セッションマネージャーにFargateのコンテナが登録されてシェルを起動できるようになります。
しくみの詳細
Sysetem Managerにアクティベーション登録をする
AWS System Manager -> アクティベーションから「アクティベーションの作成」を実行する
「インスタンス制限」 というのがあり、一つのアクティベーションに対して、最大何個のホストを登録できるかの設定項目があります。
dockerで運用する場合、コンテナが起動する度に違うホスト扱いになり、コンテナを起動するたびに「登録できる残りコンテナ数」が減っていきます。コンテナ破棄時に、ここの登録済み数をクリアする事ができれば気にしなくても良さそうなのですが、ちょっと方法がわからず、知っている方がいたら教えてほしい(切実)
(また、やらしいことに、 managed ec2以外を対象にする場合、ここのアクティベーションと登録済みインスタンス数によって課金とかにも関わってくるらしく気にせず作りっぱなしとかにもできなさそう(あまり詳しく調べていない)なのがつらいところです)
このアクティベーションを行うことで、下記のような
Activation Code
とActivation ID
が生成されるのでメモします(一回しか出てこないので注意)エージェントのインストール
Fargateで動かすコンテナに
amazon-ssm-agent
をインストールする。上記のissueコメントにあるように、
https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-install-managed-linux.html
で、OS毎に agent のインストール方法がのっているので、これにしたがい Dockerfileに追記します。
今回自分の場合は ubuntu だったため、下記のようになります。Dockerfile
RUN curl https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/debian_amd64/amazon-ssm-agent.deb -o /tmp/amazon-ssm-agent.deb \ && dpkg -i /tmp/amazon-ssm-agent.deb \ && cp /etc/amazon/ssm/seelog.xml.template /etc/amazon/ssm/seelog.xmlコンテナの登録と、セッションマネージャー用エージェントの起動
コンテナの登録
Fargateのコンテナ起動時に、上記アクティベーション時のコードと、IDでコンテナをSSMに登録します。
Fargateコンテナからアクセスする必要がある秘匿情報なので、最近使えるようになった、暗号化されたSSMのパラメータストアから取得するといいと思います。
詳細は、下記参照
【祝!】FargateでもECSにごっつ簡単に環境変数に機密情報を渡せるようになりました! | DevelopersIO
SSM_AGENT_CODE
、SSM_AGENT_ID
が、それぞれ上記で、メモしたActivation Code
とActivation ID
です。amazon-ssm-agent -register -code "${SSM_AGENT_CODE}" -id "${SSM_AGENT_ID}" -region "ap-northeast-1"エージェントの起動
セッションマネージャーからアクセスする際にエージェントが起動していないといけないのでバックグラウンドで起動しておきます。
この際、awsのドキュメントだと、サービスとして動かしているサンプルばかりなのですが、dockerコンテナなので、サービスいれるのもちょっとなと思って、単純にバックグラウンド起動にしています。 (supervisordとかで起動するのもいいかも)# agent 起動 amazon-ssm-agent &
これで、fargateのコンテナが正常に起動すれば準備OKです!
セッションマネージャーからのアクセス
System Managerのセッションマネージャーからセッションの開始を選択し、今登録されたインスタンスID(
amazon-ssm-agent -register
のログなどから確認してください)を指定します。セッション開始!
下記のようにシェルのセッションが開始されます。
この際ユーザが
ssm-user
というユーザになっているため、 docker containerが root で実行されているといろいろ実行できません。
(例えば、bin/rails console
など)sudo可能なユーザなので、 Dockerfileで
sudo
入れておいてsudoするのがいいと思います。
・・・が、その際、sudoで実行中のコマンドの環境変数は、Fargateで設定した環境変数が設定されていないため、sudo -E bin/rails cと、 sudo に、環境変数を保存する、
-E
をつけて実行すると実行できるようになります。この辺よくわかってないですが、コンテナ上で実行されているユーザはrootで、そこに対して環境変数が設定されているのですが、 セッションマネージャーで、ssm-userとして実行する際は、ssm-userの環境に環境変数が引き継がれているが、 sudoで実行するrootの環境変数には何も設定されていないようです。
掃除
料金体系とかあまり詳しくまだ調べられていないのですが マネージドなEC2に対してセッションマネージャーからアクセスするのと違い、少し違う設定が有効になるようです。
とりあえず、Fargateでこの設定を行う場合は、スポットでアクセスする場合に限り、一通り使った後は登録済みインスタンスや、アクティベーション等は手動で削除しています。
最後に
Fargateで唯一困ってたシェルアクセスに関して、結構安全そうな手段が見つかってよかったです。
ですが、やはり本来は、Fargateの機能としてシェルにアクセスしたい感じですよね。そんな中、つい最近出ていた、Fargateの責任者の方へのインタビューだそうですが、
AWSのコンテナ責任者が語るFargate、ECS、App Mesh、EKS、そしてKubernetes (1/2):AWS Summit Tokyo 2019で聞いた - @IT
また、FargateコンテナにSSHして、内部の情報を確認できるようにするなども予定している
まさにこの機能ですね!!!
早く実現してほしいものですが、それまではこういう方法も便利かも!という記事でした。
- 投稿日:2019-06-25T22:14:05+09:00
Terraform で書く CodeBuild -> ECR -> ECS with Vapor その1
Terraform v0.12.1
Swift製 WebFrameWork である Vapor をECS へのデプロイするところまでをTerraformの練習として書きました。
解説記事というよりは個人的な忘備録、メモになります。Terraform が初めての場合は Terraform 公式のチュートリアルにまず取り掛かる事をお勧めします。
Terraform はバージョンによる差異も大きく、そしてどんどん良いものになっているため、これから始めるのであれば最新版を使うことをお勧めします。アプリケーションの作成
まずECS上で動かすためのアプリケーションが必要です。
Vapor のテンプレートをアプリケーションとして使用します。
今回は以下のような Dockerfile を作成し、この Dockerfile により作られるイメージをECS で動かすことを目的とします。https://github.com/O-Junpei/vapor-docker-sample
DockerfileFROM ubuntu:16.04 RUN apt-get update && apt-get install -y \ git \ vim \ wget \ curl RUN /bin/bash -c "$(wget -qO- https://apt.vapor.sh)" RUN apt-get install -y \ swift \ vapor RUN vapor new Hello --template=web WORKDIR /Hello RUN vapor build EXPOSE 80 CMD ["swift", "run", "Run", "--hostname", "0.0.0.0", "--port", "80"]Terraform の設定
次に Terraform から AWS にアクセスできることを確認します。
######################## ## Credential Infos ######################## variable "access_key" { description = "AWS access key" } variable "secret_key" { description = "AWS secret access key" } variable "region" { description = "AWS region to host your network" } provider "aws" { access_key = "${var.access_key}" secret_key = "${var.secret_key}" region = "${var.region}" }ECRの作成
CodeBuild で作成したイメージを格納する ECR を作成します。
######################## ## ECR ######################## # ECS Repository resource "aws_ecr_repository" "repository" { name = "my-repository" } # Repositry Policy # Permit pull image resource "aws_ecr_repository_policy" "repository_policy" { repository = "${aws_ecr_repository.repository.name}" policy = <<EOF { "Version": "2008-10-17", "Statement": [ { "Sid": "new statement", "Effect": "Allow", "Principal": "*", "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer" ] } ] } EOF }CodeBuildProjectの作成
CodeBuildProject を作成します。
######################## ## CodeBuild ######################## data "aws_iam_policy_document" "codebuild_assume_role_policy_document" { statement { sid = "CodebuildExecution" actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["codebuild.amazonaws.com"] } } } resource "aws_iam_role" "codebuild_role" { name = "my-codebuild" assume_role_policy = "${data.aws_iam_policy_document.codebuild_assume_role_policy_document.json}" } data "aws_iam_policy_document" "codebuild_policy_document" { statement { sid = "Logging" actions = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ] resources = ["*"] } statement { sid = "PushECR" actions = [ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:GetRepositoryPolicy", "ecr:DescribeRepositories", "ecr:ListImages", "ecr:DescribeImages", "ecr:BatchGetImage", "ecr:InitiateLayerUpload", "ecr:UploadLayerPart", "ecr:CompleteLayerUpload", "ecr:PutImage", ] resources = ["*"] } } resource "aws_iam_role_policy" "codebuild_role_policy" { role = "${aws_iam_role.codebuild_role.name}" policy = "${data.aws_iam_policy_document.codebuild_policy_document.json}" } # templete などを別ファイルに書き出すことをおすすめです。 data "template_file" "buildspec_template_file" { template = <<EOF version: 0.2 phases: pre_build: commands: - echo pre_build - echo Logging in to Amazon ECR... - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION) build: commands: - echo build - echo build docker image - docker build -t vapor:latest . post_build: commands: - echo post_build - docker tag vapor:latest ${aws_ecr_repository.repository.repository_url}:latest - docker push ${aws_ecr_repository.repository.repository_url}:latest EOF } resource "aws_codebuild_project" "codebuild_project" { name = "build-vapor-image" description = "build vapor image" build_timeout = "30" service_role = "${aws_iam_role.codebuild_role.arn}" artifacts { type = "NO_ARTIFACTS" } cache { type = "LOCAL" modes = ["LOCAL_DOCKER_LAYER_CACHE", "LOCAL_SOURCE_CACHE"] } environment { compute_type = "BUILD_GENERAL1_SMALL" image = "aws/codebuild/docker:18.09.0" type = "LINUX_CONTAINER" privileged_mode = true image_pull_credentials_type = "CODEBUILD" } source { type = "GITHUB" # プライベートリポジトリを使う場合はOAuthToken を設定する # パラメーターストアに保存し、取ってくるのが簡単 # auth { # type = "OAUTH" # resource = "${aws_ssm_parameter.github_oauth_token.value}" # } location = "https://github.com/O-Junpei/vapor-docker-sample.git" buildspec = "${data.template_file.buildspec_template_file.rendered}" } }その2に続きます。
- 投稿日:2019-06-25T21:33:56+09:00
laravelをAWS EC2にデプロイする
はじめに
vagrantなどのローカル開発環境で作成したlaravelプロジェクトをAWSのEC2上にデプロイする時に色々迷ったので、まとめとしてあげます。本番サーバにAWSを使ってみたいという方に見て頂ければと思います。
環境
php7.2.18
apache2.4.39
laravel5.7
EC2(Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type 無料枠)
RDS(mysqlを使用)前提
・ローカルで開発したプロジェクトをすでにgithubにプッシュ済み
・EC2にSSH接続できている
・EC2からRDSへ接続できる設定ができている(セキュリティグループなど)PHP7.2インストール
インストールできるリスト確認 sudo yum list available | grep php72 インストール sudo yum install -y \ php72 php72-devel php72-fpm php72-gd php72-mbstring \ php72-mysqlnd php72-pdo \ php72-xml php72-json インストール済みを確認 sudo yum list installed | grep php72 バージョン確認 php -vMysqlインストール RDSとの接続
インストール sudo yum -y install mysql 接続 mysql test_db -h <エンドポイント> -P 3306 -u test_user -papacheインストール
sudo yum install -y httpd24上記の流れでインストールを進めていると、なぜかすでにインストール済みと出た。
どこで入れたかわからなかった。
続きの設定を行っていくwabサーバの起動 sudo service httpd start システムがブートするたびにapacheが起動するよう設定 sudo chkconfig httpd on 有効か確認 chkconfig --list httpdドキュメントルートの権限変更
ユーザをapacheグループに追加 sudo usermod -a -G apache ec2-user 一旦ログアウト exit グループのメンバーシップを検証 groups グループ所有権をapacheグループに変更 sudo chown -R ec2-user:apache /var/www グループの書き込み許可追加 sudo chmod 2775 /var/www サブディレクトにグループ ID を設定するには、/var/www とサブディレクトのディレクトリ許可 find /var/www -type d -exec sudo chmod 2775 {} \; グループ書き込み許可を追加するには、/var/www とサブディレクトリのファイル許可を再帰的に変更します。 find /var/www -type f -exec sudo chmod 0664 {} \;Gitインストール
sudo yum install gitcomposer自身のインストール
インストール sudo curl -sS https://getcomposer.org/installer | php パスを通す sudo mv composer.phar /usr/local/bin/composerLaravelプロジェクトのClone
cd /var/www/html git clone <URL>apacheのドキュメントルート設定
設定ファイルを開く sudo vi /etc/httpd/conf/httpd.conf 以下に変更 DocumentRoot "/var/www/html/自分のlaravel_project/public 最終行に以下を追記 # .htaccess 有効化 <Directory /var/www/html/自分のlaravel_project/public> AllowOverride All </Directory>Laravelプロジェクトでcomposer インストール
cd /var/www/html/laravelプロジェクト composer installconfig, routeのキャッシュクリア
php artisan config:cache php artisan route:cacheプロジェクトのパーミッション変更
chmod 777 storage -R chmod 777 bootstrap/cache -R.envの設定
git cloneしたLaravelプロジェクトには.envファイルがないので、
composer insatallで作成された、.env.exsampleを使用するcp .env.example .env vi .env APP_URL=http://IPアドレス ←サーバーのIPにする DB_CONNECTION=mysql DB_HOST=←RDSのエンドポイント DB_DATABASE=データベース名 DB_USERNAME=RDS作成時のユーザ名 DB_PASSWORD=設定したパスワードキーを作成し、キャッシュをクリアする
php artisan key:generate php artisan config:clearテーブル作成
php artisan migrateシンボリックリンクの作成
これは作成したアプリで画像アップロードしているような場合は必要かと思います。
php artisan storage::linkブラウザで確認する
あとはブラウザでEC2のIPアドレスを入力するだけです!
ここまでくればブラウザで正しく表示されるかと思います!参考
https://qiita.com/reflet/items/11ad79e01e808876caa1
https://nori-life.com/install-laravel-aws/
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/install-LAMP.html
https://qiita.com/takg/items/9a044b36cdf216a42ba6
https://qiita.com/nbapps_dev/items/9307e6fc0edb59c09181
- 投稿日:2019-06-25T21:02:13+09:00
awsvpcTrunking設定を試す【ECS】
ENI制限の強化
2019年6月6日にAWSからawsvpcTrunkingアカウント設定が発表されました!
Amazon ECS が、awsvpc ネットワークモードのタスクに対する Elastic Network Interface (ENI) 制限の強化をサポート開始
今までインスタンスタイプごとにENIの上限は変更できませんでしたが、この度変更することができるようになりました。
利用可能な条件については下記です。
- ECS が使用できるすべてのリージョン
- Amazon ECS最適化AMI
*現時点では、Windows コンテナはサポートされていません。
awsvpcモードを利用したEC2起動タイプのコンテナが更に使い勝手がよくなりましたので、実際にやってみようと思います。
これまでのECS運用
私がECSを利用する際、以下の2つが選択において考慮するポイントでした。
- タスクの増減が頻繁か
- awsvpcモードを利用するか
タスクの増減が頻繁に起こるようなコンテナですと、EC2起動タイプよりもFargateの方がスムーズに増減が実現できかつスケーリングの設定などの煩わしさから解放されるためできれば利用したいです。
またawsvpcモードを利用する場合はインスタンス側のENI制限に依存するためFargateを選択して制限から解放されたいので、こちらも可能な限りFargateを利用したいと思っています。しかし、今年の1月に安くなったとはいえ金額的にFargateの方が高いため躊躇うこともあると思います。
そのためEC2起動タイプを選択する方はまだまだ少なくないと思いますが、その際にぶつかるのが上述したENI問題です。
awsvpcモードの場合、ENIをコンテナにアタッチして利用するため以下の利点が見込めます。
- ALB / NLBにIPターゲットとして登録ができる
- タスクごとにSecurityGroupを紐付けることができる
- 同一ホスト内でのタスクポートマッピングを考慮する必要がない
非常な便利かつ安価に使える反面、何度も繰り返しますがENIの数がインスタンスに依存します。
m5.large
のインスタンスの場合ENIは3つが上限です。
無料利用枠で750h利用できるt2.micro
に至っては2つが上限となっております。例えば
m5.large
のインスタンスでECS Clusterを利用した場合、
まずはホストがENIを1つ利用するため1インスタンスにつき最大2つまでawsvpcモードを利用したコンテナが利用できます。
負荷が集中しコンテナが2 -> 10
までスケールアウトが必要になった際、m5.large
インスタンスは5つ必要になります。
当然コンテナよりもEC2が立ち上がる方が時間がかかるためもしかしたら間に合わずに詰まってしまうかもしれません。あらかじめ待機させておいたり、Cloudwatch AlarmをトリガーにLambdaで一気に増やすなど色々な回避策を講じてきました。
便利なマネジメントツールが多くあるので合わせ技で回避することができる反面、管理すべきサービスが増えてしまうなんて状況です。今回の追加によって、そういった煩わしさを少しだけ解消してくれるのではないかと期待しています。
awsvpcTrunkingアカウント設定をしてみる
では早速やってみましょう。
アカウント設定に従って進めていきます。下準備
適当なECS周りのものを用意します。
今回は以下のものを用意しました。
- ECS Cluster
- Cluster用Launch Config
- Cluster用Instance Profile
- Cluster用IAM Role
- Cluster用IAM Policy
- Cluster用Security Group
- ECS Service
- Service用IAM Role
- Service用IAM Policy
- Service用Security Group
- ECS Task definition
- Task用IAM Role
- Task用IAM Policy
ECS最適化AMIについてはこちらから確認してください。
Terraformでぱぱっと作ります。
trunking_ecs.tfresource "aws_ecs_cluster" "trunking-cluster" { name = "trunking-ecs" } data "template_file" "trunking-ecs-userdata" { template = "${file("userdata-template/trunking-ecs.tpl") } resource "aws_launch_configuration" "trunking-ecs" { name_prefix = "trunking-ecs-launch-config-" image_id = "ami-02507631a9f7bc956" instance_type = "m5.large" iam_instance_profile = "${aws_iam_instance_profile.trunking-ecs.name}" key_name = "${var.own_key}" user_data = "${data.template_file.trunking-ecs-userdata.rendered}" security_groups = ["${aws_security_group.trunking-ecs.id}"] lifecycle { create_before_destroy = true } } resource "aws_iam_role" "trunking-ecs-role" { name = "${var.region}-trunking-ecs" assume_role_policy = "${file("./policy/iam_assumerole.json")}" } resource "aws_iam_instance_profile" "trunking-ecs" { depends_on = ["aws_iam_role.trunking-ecs-role"] name = "${var.region}-trunking-ecs" role = "${aws_iam_role.trunking-ecs-role.name}" } resource "aws_iam_role_policy_attachment" "trunking-ecs-attach" { depends_on = ["aws_iam_role.trunking-ecs-role"] role = "${aws_iam_role.trunking-ecs-role.name}" policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" } resource "aws_security_group" "trunking-ecs" { name = "trunking-ecs" description = "trunking ECS security Group" vpc_id = "${aws_vpc.test-vpc.id}" ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["${aws_vpc.test-vpc.cidr_block}"] } ingress { from_port = 31000 to_port = 61000 protocol = "tcp" cidr_blocks = ["${aws_vpc.test-vpc.cidr_block}"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags { "Name" = "trunking-ecs-ingress" "Service" = "trunking" } } resource "aws_autoscaling_group" "trunking-ecs" { name = "trunking-ecs-asg" max_size = 2 min_size = 1 health_check_grace_period = 300 health_check_type = "ELB" desired_capacity = 1 force_delete = true launch_configuration = "${aws_launch_configuration.trunking-ecs.name}" vpc_zone_identifier = ["${aws_subnet.private.*.id}"] tag { key = "Name" value = "${var.region}-trunking-ecs" propagate_at_launch = true } tag { key = "Service" value = "trunking" propagate_at_launch = true } lifecycle { create_before_destroy = true ignore_changes = ["desired_capacity"] } }trunking_service.tfresource "aws_ecs_service" "trunking-service" { name = "trunking" cluster = "${aws_ecs_cluster.trunking-cluster.id}" task_definition = "${aws_ecs_task_definition.trunking-task.arn}" desired_count = 2 network_configuration { security_groups = ["${aws_security_group.trunking-service.id}"] subnets = ["${aws_subnet.private.*.id}"] } load_balancer { target_group_arn = "${aws_lb_target_group.trunking.arn}" container_name = "trunking-service" container_port = "24224" } ordered_placement_strategy { type = "spread" field = "attribute:ecs.availability-zone" } ordered_placement_strategy { type = "spread" field = "instanceId" } lifecycle { ignore_changes = ["desired_count"] } } resource "aws_security_group" "trunking-service" { name = "trunking-service" description = "trunking service security Group" vpc_id = "${aws_vpc.test-vpc.id}" ingress { from_port = 24224 to_port = 24224 protocol = "tcp" self = true cidr_blocks = ["${aws_vpc.test-vpc.cidr_block}"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags { "Name" = "trunking-service-ingress" "Service" = "trunking" } } data "template_file" "trunking-task" { template = "${file("task-definitions/trunking-task.tpl")}" } resource "aws_ecs_task_definition" "trunking-task" { family = "trunking" network_mode = "awsvpc" container_definitions = "${data.template_file.trunking-task.rendered}" task_role_arn = "${aws_iam_role.task-execution.arn}" tags { Name = "trunking-task" Service = "trunking" } } resource "aws_iam_role" "trunking-service-role" { name = "${var.region}-trunking-service" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF } resource "aws_iam_role_policy_attachment" "trunking-service-attach" { role = "${aws_iam_role.trunking-service-role.name}" policy_arn = "${aws_iam_policy.trunking-cluster.arn}" } resource "aws_cloudwatch_log_group" "trunking" { name = "/${var.service_name}/trunking" } resource "aws_iam_policy" "trunking-cluster" { count = "${aws_ecs_cluster.trunking-cluster.count}" name = "${var.region}-TrunkingServicePolicy" policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Sid": "ECSTaskTrunking", "Effect": "Allow", "Action": [ "ec2:AttachNetworkInterface", "ec2:CreateNetworkInterface", "ec2:CreateNetworkInterfacePermission", "ec2:DeleteNetworkInterface", "ec2:DeleteNetworkInterfacePermission", "ec2:Describe*", "ec2:DetachNetworkInterface", "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", "elasticloadbalancing:DeregisterTargets", "elasticloadbalancing:Describe*", "elasticloadbalancing:RegisterInstancesWithLoadBalancer", "elasticloadbalancing:RegisterTargets", "route53:ChangeResourceRecordSets", "route53:CreateHealthCheck", "route53:DeleteHealthCheck", "route53:Get*", "route53:List*", "route53:UpdateHealthCheck", "servicediscovery:DeregisterInstance", "servicediscovery:Get*", "servicediscovery:List*", "servicediscovery:RegisterInstance", "servicediscovery:UpdateInstanceCustomHealthStatus", "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer", "ecr:GetAuthorizationToken", "application-autoscaling:*", "cloudwatch:DescribeAlarms", "cloudwatch:PutMetricAlarm" ], "Resource": "*" }, { "Sid": "TrunkingECSTagging", "Effect": "Allow", "Action": [ "ec2:CreateTags" ], "Resource": "arn:aws:ec2:*:*:network-interface/*" } ] } EOF }まずは確認
こちらを参考にまずは設定を確認してみました。(当然disableなのですが…。)
before_enable$ aws --region us-east-1 ecs list-account-settings --effective-settings { "settings": [ { "name": "awsvpcTrunking", "value": "disabled", "principalArn": "arn:aws:iam::123456789012:user/hogehoge.fugafuga" }, { "name": "containerInstanceLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" }, { "name": "serviceLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" }, { "name": "taskLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" } ] }無効になっていることを確認しました。
有効化
こちらを参考に有効化していきます。
今回はコンソールではなくCLIで有効化してみます。enable_trunking$ aws ecs --region us-east-1 put-account-setting --name awsvpcTrunking --value enabled --principal-arn arn:aws:iam::123456789012:user/hogehoge.fugafuga { "setting": { "name": "awsvpcTrunking", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:user/hogehoge.fugafuga" } } $ $ aws --region us-east-1 ecs list-account-settings --principal-arn arn:aws:iam::123456789012:user/hogehoge.fugafuga --effective-settings { "settings": [ { "name": "awsvpcTrunking", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:user/hogehoge.fugafuga" }, { "name": "containerInstanceLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" }, { "name": "serviceLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" }, { "name": "taskLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" } ] }問題なさそうですね。
実際に増やしてみる
では本当にENIの上限が変化するかを確認してみましょう。
今回はm5.large
のインスタンスを利用しているので、ホスト用を抜いた限界値9つまで上げてみます。chage_desired_count$ aws --region us-east-1 ecs update-service --cluster trunking-ecs --service trunking --desired-count 9 --query 'service[].desiredCount' { "desiredCount": 9, } $ $ aws --region us-east-1 ecs describe-services --cluster trunking-ecs --services trunking --query 'services[].deployments[]' [ { "id": "ecs-svc/9223370475408119189", "status": "PRIMARY", "taskDefinition": "arn:aws:ecs:us-east-1:123456789012:task-definition/trunking:2", "desiredCount": 9, "pendingCount": 0, "runningCount": 2, "createdAt": 1561446656.618, "updatedAt": 1561457505.944, "launchType": "EC2", "networkConfiguration": { "awsvpcConfiguration": { "subnets": [ "subnet-12345678901234567", "subnet-76543210987654321" ], "securityGroups": [ "sg-01928374656473829" ], "assignPublicIp": "DISABLED" } } } ]これで完了!と思いきや
error$ aws --region us-east-1 ecs describe-services --cluster trunking-ecs --services trunking --query 'services[].events[]' | head -n 6 [ { "id": "8e4600d0-206f-4bed-9388-2a86b4bcb54c", "createdAt": 1561457461.911, "message": "(service trunking) was unable to place a task because no container instance met all of its requirements. The closest matching (container-instance dab5c255b71247dba74f5cb126eb6db7) encountered error \"RESOURCE:ENI\". For more information, see the Troubleshooting section of the Amazon ECS Developer Guide." },どうやら即時反映ではなく設定後に起動されたインスタンスから反映されるようです。
ENI トランキングに関する考慮事項awsvpcTrunking のオプトイン後に起動された新しい Amazon EC2 インスタンスのみが、
引き上げられた ENI 制限とトランクネットワークインターフェイスを受け取ります。
以前に起動されたインスタンスは、実行されたアクションに関係なく、これらの機能を受け取りません。というわけでインスタンスを新しく立ち上げて確認します。
新しいエラーがなにやら発生しています。error_2$ aws --region us-east-1 ecs describe-services --cluster trunking-ecs --services trunking --query 'services[].events[]' | head -n 8 [ { "id": "5a41e1a5-7c35-4795-a9f5-1636d2dab3c5", "createdAt": 1561458498.055, "message": "(service trunking) was unable to place a task because no container instance met all of its requirements. The closest matching (container-instance dab5c255b71247dba74f5cb126eb6db7) doesn't have the agent connected. For more information, see the Troubleshooting section of the Amazon ECS Developer Guide." },インスタンスに入って
/var/log/ecs
のログを確認$ cd /var/log/ecs $ cat ecs-init.log 2019-06-25T10:40:45Z [INFO] pre-start 2019-06-25T10:40:46Z [INFO] start 2019-06-25T10:40:46Z [INFO] No existing agent container to remove. 2019-06-25T10:40:46Z [INFO] Starting Amazon Elastic Container Service Agent $ $ cat ecs-agent.log.2019-06-25-10 | head 2019-06-25T10:40:47Z [INFO] Loading configuration 2019-06-25T10:40:47Z [INFO] Image excluded from cleanup: amazon/amazon-ecs-agent:latest 2019-06-25T10:40:47Z [INFO] Image excluded from cleanup: amazon/amazon-ecs-pause:0.1.0 2019-06-25T10:40:47Z [INFO] Amazon ECS agent Version: 1.29.0, Commit: a190a73f 2019-06-25T10:40:47Z [INFO] Creating root ecs cgroup: /ecs 2019-06-25T10:40:47Z [INFO] Creating cgroup /ecs 2019-06-25T10:40:47Z [INFO] Loading state! module="statemanager" 2019-06-25T10:40:47Z [INFO] Event stream ContainerChange start listening... 2019-06-25T10:40:48Z [INFO] Registering Instance with ECS 2019-06-25T10:40:48Z [INFO] Remaining mem: 7681 $ $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2e9ce16d7b40 123456789012.dkr.ecr.us-east-1.amazonaws.com/test:trunking "/opt/sh/startup.sh …" 20 minutes ago Up 20 minutes ecs-trunking-2-trunking-service-de8897e0cb8dcded5b00 d84fc735402b 123456789012.dkr.ecr.us-east-1.amazonaws.com/test:trunking "/opt/sh/startup.sh …" 20 minutes ago Up 20 minutes ecs-trunking-2-trunking-service-d4918bbcbbf1fbd02d00 40177ea3b5d2 amazon/amazon-ecs-pause:0.1.0 "./pause" 20 minutes ago Up 20 minutes ecs-trunking-2-internalecspause-c8f988dcbae8b9bd5500 952b16278748 amazon/amazon-ecs-pause:0.1.0 "./pause" 20 minutes ago Up 20 minutes ecs-trunking-2-internalecspause-deabf4c4d4bc81eb8a01 f1413a19e94b amazon/amazon-ecs-agent:latest "/agent" 20 minutes ago Up 20 minutes ecs-agent $起動には成功しているみたいですね。
よくよく読むとここに書いてありました。アカウント、IAM ユーザー、またはロールは、awsvpcTrunking アカウント設定をオプトインする必要があります。詳細については、「アカウント設定」を参照してください。
どうやら自分のIAMに対してawsvpcTrunkingアカウント設定を有効化するだけではダメみたいですね。
サービスのロールに設定しようと試みましたが、怒られてしまいました。enable_service_role$ aws ecs --region us-east-1 put-account-setting --name awsvpcTrunking --value enabled --principal-arn arn:aws:iam::123456789012:role/us-east-1-trunking-service An error occurred (InvalidParameterException) when calling the PutAccountSetting operation: Only the root user can view or modify the account settings for another user. You do not need to specify a principalArn value to change your own account settings. $AMIも最新のもので起動されています。
ami_confirm$ aws --region us-east-1 ec2 describe-instances --instance-ids i-012345abcdef67gh8 --query 'Reservations[].Instances[].ImageId[]' [ "ami-02507631a9f7bc956" ]rootからアカウントに対して許可が必要なんですかね?
近いうちにrootで作業できる環境で再度検証してみようと思います。次回へつづく
おおーめっちゃ増えてるーってところを見たかったのですが残念
今週中にはrootで試してみようと思います。
- 投稿日:2019-06-25T20:55:27+09:00
awsvpcTrunking設定を試す【ECS】
ENI制限の強化
2019年6月6日にAWSからawsvpcTrunkingアカウント設定が発表されました!
Amazon ECS が、awsvpc ネットワークモードのタスクに対する Elastic Network Interface (ENI) 制限の強化をサポート開始
今までインスタンスタイプごとにENIの上限は変更できませんでしたが、この度変更することができるようになりました。
利用可能な条件については下記です。
- ECS が使用できるすべてのリージョン
- Amazon ECS最適化AMI
*現時点では、Windows コンテナはサポートされていません。
awsvpcモードを利用したEC2起動タイプのコンテナが更に使い勝手がよくなりましたので、実際にやってみようと思います。
これまでのECS運用
私がECSを利用する際、以下の2つが選択において考慮するポイントでした。
- タスクの増減が頻繁か
- awsvpcモードを利用するか
タスクの増減が頻繁に起こるようなコンテナですと、EC2起動タイプよりもFargateの方がスムーズに増減が実現できかつスケーリングの設定などの煩わしさから解放されるためできれば利用したいです。
またawsvpcモードを利用する場合はインスタンス側のENI制限に依存するためFargateを選択して制限から解放されたいので、こちらも可能な限りFargateを利用したいと思っています。しかし、今年の1月に安くなったとはいえ金額的にFargateの方が高いため躊躇うこともあると思います。
そのためEC2起動タイプを選択する方はまだまだ少なくないと思いますが、その際にぶつかるのが上述したENI問題です。
awsvpcモードの場合、ENIをコンテナにアタッチして利用するため以下の利点が見込めます。
- ALB / NLBにIPターゲットとして登録ができる
- タスクごとにSecurityGroupを紐付けることができる
- 同一ホスト内でのタスクポートマッピングを考慮する必要がない
非常な便利かつ安価に使える反面、何度も繰り返しますがENIの数がインスタンスに依存します。
m5.large
のインスタンスの場合ENIは3つが上限です。
無料利用枠で750h利用できるt2.micro
に至っては2つが上限となっております。例えば
m5.large
のインスタンスでECS Clusterを利用した場合、
まずはホストがENIを1つ利用するため1インスタンスにつき最大2つまでawsvpcモードを利用したコンテナが利用できます。
負荷が集中しコンテナが2 -> 10
までスケールアウトが必要になった際、m5.large
インスタンスは5つ必要になります。
当然コンテナよりもEC2が立ち上がる方が時間がかかるためもしかしたら間に合わずに詰まってしまうかもしれません。あらかじめ待機させておいたり、Cloudwatch AlarmをトリガーにLambdaで一気に増やすなど色々な回避策を講じてきました。
便利なマネジメントツールが多くあるので合わせ技で回避することができる反面、管理すべきサービスが増えてしまうなんて状況です。今回の追加によって、そういった煩わしさを少しだけ解消してくれるのではないかと期待しています。
awsvpcTrunkingアカウント設定をしてみる
では早速やってみましょう。
アカウント設定に従って進めていきます。下準備
適当なECS周りのものを用意します。
今回は以下のものを用意しました。
- ECS Cluster
- Cluster用Launch Config
- Cluster用Instance Profile
- Cluster用IAM Role
- Cluster用IAM Policy
- Cluster用Security Group
- ECS Service
- Service用IAM Role
- Service用IAM Policy
- Service用Security Group
- ECS Task definition
- Task用IAM Role
- Task用IAM Policy
ECS最適化AMIについてはこちらから確認してください。
Terraformでぱぱっと作ります。
trunking_ecs.tfresource "aws_ecs_cluster" "trunking-cluster" { name = "trunking-ecs" } data "template_file" "trunking-ecs-userdata" { template = "${file("userdata-template/trunking-ecs.tpl") } resource "aws_launch_configuration" "trunking-ecs" { name_prefix = "trunking-ecs-launch-config-" image_id = "ami-02507631a9f7bc956" instance_type = "m5.large" iam_instance_profile = "${aws_iam_instance_profile.trunking-ecs.name}" key_name = "${var.own_key}" user_data = "${data.template_file.trunking-ecs-userdata.rendered}" security_groups = ["${aws_security_group.trunking-ecs.id}"] lifecycle { create_before_destroy = true } } resource "aws_iam_role" "trunking-ecs-role" { name = "${var.region}-trunking-ecs" assume_role_policy = "${file("./policy/iam_assumerole.json")}" } resource "aws_iam_instance_profile" "trunking-ecs" { depends_on = ["aws_iam_role.trunking-ecs-role"] name = "${var.region}-trunking-ecs" role = "${aws_iam_role.trunking-ecs-role.name}" } resource "aws_iam_role_policy_attachment" "trunking-ecs-attach" { depends_on = ["aws_iam_role.trunking-ecs-role"] role = "${aws_iam_role.trunking-ecs-role.name}" policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" } resource "aws_security_group" "trunking-ecs" { name = "trunking-ecs" description = "trunking ECS security Group" vpc_id = "${aws_vpc.test-vpc.id}" ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["${aws_vpc.test-vpc.cidr_block}"] } ingress { from_port = 31000 to_port = 61000 protocol = "tcp" cidr_blocks = ["${aws_vpc.test-vpc.cidr_block}"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags { "Name" = "trunking-ecs-ingress" "Service" = "trunking" } } resource "aws_autoscaling_group" "trunking-ecs" { name = "trunking-ecs-asg" max_size = 2 min_size = 1 health_check_grace_period = 300 health_check_type = "ELB" desired_capacity = 1 force_delete = true launch_configuration = "${aws_launch_configuration.trunking-ecs.name}" vpc_zone_identifier = ["${aws_subnet.private.*.id}"] tag { key = "Name" value = "${var.region}-trunking-ecs" propagate_at_launch = true } tag { key = "Service" value = "trunking" propagate_at_launch = true } lifecycle { create_before_destroy = true ignore_changes = ["desired_capacity"] } }trunking_service.tfresource "aws_ecs_service" "trunking-service" { name = "trunking" cluster = "${aws_ecs_cluster.trunking-cluster.id}" task_definition = "${aws_ecs_task_definition.trunking-task.arn}" desired_count = 2 network_configuration { security_groups = ["${aws_security_group.trunking-service.id}"] subnets = ["${aws_subnet.private.*.id}"] } load_balancer { target_group_arn = "${aws_lb_target_group.trunking.arn}" container_name = "trunking-service" container_port = "24224" } ordered_placement_strategy { type = "spread" field = "attribute:ecs.availability-zone" } ordered_placement_strategy { type = "spread" field = "instanceId" } lifecycle { ignore_changes = ["desired_count"] } } resource "aws_security_group" "trunking-service" { name = "trunking-service" description = "trunking service security Group" vpc_id = "${aws_vpc.test-vpc.id}" ingress { from_port = 24224 to_port = 24224 protocol = "tcp" self = true cidr_blocks = ["${aws_vpc.test-vpc.cidr_block}"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags { "Name" = "trunking-service-ingress" "Service" = "trunking" } } data "template_file" "trunking-task" { template = "${file("task-definitions/trunking-task.tpl")}" } resource "aws_ecs_task_definition" "trunking-task" { family = "trunking" network_mode = "awsvpc" container_definitions = "${data.template_file.trunking-task.rendered}" task_role_arn = "${aws_iam_role.task-execution.arn}" tags { Name = "trunking-task" Service = "trunking" } } resource "aws_iam_role" "trunking-service-role" { name = "${var.region}-trunking-service" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF } resource "aws_iam_role_policy_attachment" "trunking-service-attach" { role = "${aws_iam_role.trunking-service-role.name}" policy_arn = "${aws_iam_policy.trunking-cluster.arn}" } resource "aws_cloudwatch_log_group" "trunking" { name = "/${var.service_name}/trunking" } resource "aws_iam_policy" "trunking-cluster" { count = "${aws_ecs_cluster.trunking-cluster.count}" name = "${var.region}-TrunkingServicePolicy" policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Sid": "ECSTaskTrunking", "Effect": "Allow", "Action": [ "ec2:AttachNetworkInterface", "ec2:CreateNetworkInterface", "ec2:CreateNetworkInterfacePermission", "ec2:DeleteNetworkInterface", "ec2:DeleteNetworkInterfacePermission", "ec2:Describe*", "ec2:DetachNetworkInterface", "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", "elasticloadbalancing:DeregisterTargets", "elasticloadbalancing:Describe*", "elasticloadbalancing:RegisterInstancesWithLoadBalancer", "elasticloadbalancing:RegisterTargets", "route53:ChangeResourceRecordSets", "route53:CreateHealthCheck", "route53:DeleteHealthCheck", "route53:Get*", "route53:List*", "route53:UpdateHealthCheck", "servicediscovery:DeregisterInstance", "servicediscovery:Get*", "servicediscovery:List*", "servicediscovery:RegisterInstance", "servicediscovery:UpdateInstanceCustomHealthStatus", "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer", "ecr:GetAuthorizationToken", "application-autoscaling:*", "cloudwatch:DescribeAlarms", "cloudwatch:PutMetricAlarm" ], "Resource": "*" }, { "Sid": "TrunkingECSTagging", "Effect": "Allow", "Action": [ "ec2:CreateTags" ], "Resource": "arn:aws:ec2:*:*:network-interface/*" } ] } EOF }まずは確認
こちらを参考にまずは設定を確認してみました。(当然disableなのですが…。)
before_enable$ aws --region us-east-1 ecs list-account-settings --effective-settings { "settings": [ { "name": "awsvpcTrunking", "value": "disabled", "principalArn": "arn:aws:iam::123456789012:user/hogehoge.fugafuga" }, { "name": "containerInstanceLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" }, { "name": "serviceLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" }, { "name": "taskLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" } ] }無効になっていることを確認しました。
有効化
こちらを参考に有効化していきます。
今回はコンソールではなくCLIで有効化してみます。enable_trunking$ aws ecs --region us-east-1 put-account-setting --name awsvpcTrunking --value enabled --principal-arn arn:aws:iam::123456789012:user/hogehoge.fugafuga { "setting": { "name": "awsvpcTrunking", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:user/hogehoge.fugafuga" } } $ $ aws --region us-east-1 ecs list-account-settings --principal-arn arn:aws:iam::123456789012:user/hogehoge.fugafuga --effective-settings { "settings": [ { "name": "awsvpcTrunking", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:user/hogehoge.fugafuga" }, { "name": "containerInstanceLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" }, { "name": "serviceLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" }, { "name": "taskLongArnFormat", "value": "enabled", "principalArn": "arn:aws:iam::123456789012:root" } ] }問題なさそうですね。
実際に増やしてみる
では本当にENIの上限が変化するかを確認してみましょう。
今回はm5.large
のインスタンスを利用しているので、ホスト用を抜いた限界値9つまで上げてみます。chage_desired_count$ aws --region us-east-1 ecs update-service --cluster trunking-ecs --service trunking --desired-count 9 --query 'service[].desiredCount' { "desiredCount": 9, } $ $ aws --region us-east-1 ecs describe-services --cluster trunking-ecs --services trunking --query 'services[].deployments[]' [ { "id": "ecs-svc/9223370475408119189", "status": "PRIMARY", "taskDefinition": "arn:aws:ecs:us-east-1:123456789012:task-definition/trunking:2", "desiredCount": 9, "pendingCount": 0, "runningCount": 2, "createdAt": 1561446656.618, "updatedAt": 1561457505.944, "launchType": "EC2", "networkConfiguration": { "awsvpcConfiguration": { "subnets": [ "subnet-12345678901234567", "subnet-76543210987654321" ], "securityGroups": [ "sg-01928374656473829" ], "assignPublicIp": "DISABLED" } } } ]これで完了!と思いきや
error$ aws --region us-east-1 ecs describe-services --cluster trunking-ecs --services trunking --query 'services[].events[]' | head -n 6 [ { "id": "8e4600d0-206f-4bed-9388-2a86b4bcb54c", "createdAt": 1561457461.911, "message": "(service trunking) was unable to place a task because no container instance met all of its requirements. The closest matching (container-instance dab5c255b71247dba74f5cb126eb6db7) encountered error \"RESOURCE:ENI\". For more information, see the Troubleshooting section of the Amazon ECS Developer Guide." },どうやら即時反映ではなく設定後に起動されたインスタンスから反映されるようです。
ENI トランキングに関する考慮事項awsvpcTrunking のオプトイン後に起動された新しい Amazon EC2 インスタンスのみが、
引き上げられた ENI 制限とトランクネットワークインターフェイスを受け取ります。
以前に起動されたインスタンスは、実行されたアクションに関係なく、これらの機能を受け取りません。というわけでインスタンスを新しく立ち上げて確認します。
新しいエラーがなにやら発生しています。error_2$ aws --region us-east-1 ecs describe-services --cluster trunking-ecs --services trunking --query 'services[].events[]' | head -n 8 [ { "id": "5a41e1a5-7c35-4795-a9f5-1636d2dab3c5", "createdAt": 1561458498.055, "message": "(service trunking) was unable to place a task because no container instance met all of its requirements. The closest matching (container-instance dab5c255b71247dba74f5cb126eb6db7) doesn't have the agent connected. For more information, see the Troubleshooting section of the Amazon ECS Developer Guide." },インスタンスに入って
/var/log/ecs
のログを確認$ cd /var/log/ecs $ cat ecs-init.log 2019-06-25T10:40:45Z [INFO] pre-start 2019-06-25T10:40:46Z [INFO] start 2019-06-25T10:40:46Z [INFO] No existing agent container to remove. 2019-06-25T10:40:46Z [INFO] Starting Amazon Elastic Container Service Agent $ $ cat ecs-agent.log.2019-06-25-10 | head 2019-06-25T10:40:47Z [INFO] Loading configuration 2019-06-25T10:40:47Z [INFO] Image excluded from cleanup: amazon/amazon-ecs-agent:latest 2019-06-25T10:40:47Z [INFO] Image excluded from cleanup: amazon/amazon-ecs-pause:0.1.0 2019-06-25T10:40:47Z [INFO] Amazon ECS agent Version: 1.29.0, Commit: a190a73f 2019-06-25T10:40:47Z [INFO] Creating root ecs cgroup: /ecs 2019-06-25T10:40:47Z [INFO] Creating cgroup /ecs 2019-06-25T10:40:47Z [INFO] Loading state! module="statemanager" 2019-06-25T10:40:47Z [INFO] Event stream ContainerChange start listening... 2019-06-25T10:40:48Z [INFO] Registering Instance with ECS 2019-06-25T10:40:48Z [INFO] Remaining mem: 7681 $ $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2e9ce16d7b40 123456789012.dkr.ecr.us-east-1.amazonaws.com/fr-gec-log-aggregator:staging "/opt/sh/startup.sh …" 20 minutes ago Up 20 minutes ecs-trunking-2-trunking-service-de8897e0cb8dcded5b00 d84fc735402b 123456789012.dkr.ecr.us-east-1.amazonaws.com/fr-gec-log-aggregator:staging "/opt/sh/startup.sh …" 20 minutes ago Up 20 minutes ecs-trunking-2-trunking-service-d4918bbcbbf1fbd02d00 40177ea3b5d2 amazon/amazon-ecs-pause:0.1.0 "./pause" 20 minutes ago Up 20 minutes ecs-trunking-2-internalecspause-c8f988dcbae8b9bd5500 952b16278748 amazon/amazon-ecs-pause:0.1.0 "./pause" 20 minutes ago Up 20 minutes ecs-trunking-2-internalecspause-deabf4c4d4bc81eb8a01 f1413a19e94b amazon/amazon-ecs-agent:latest "/agent" 20 minutes ago Up 20 minutes ecs-agent $起動には成功しているみたいですね。
よくよく読むとここに書いてありました。アカウント、IAM ユーザー、またはロールは、awsvpcTrunking アカウント設定をオプトインする必要があります。詳細については、「アカウント設定」を参照してください。
どうやら自分のIAMに対してawsvpcTrunkingアカウント設定を有効化するだけではダメみたいですね。
サービスのロールに設定しようと試みましたが、怒られてしまいました。enable_service_role$ aws ecs --region us-east-1 put-account-setting --name awsvpcTrunking --value enabled --principal-arn arn:aws:iam::123456789012:role/us-east-1-trunking-service An error occurred (InvalidParameterException) when calling the PutAccountSetting operation: Only the root user can view or modify the account settings for another user. You do not need to specify a principalArn value to change your own account settings. $AMIも最新のもので起動されています。
ami_confirm$ aws --region us-east-1 ec2 describe-instances --instance-ids i-012345abcdef67gh8 --query 'Reservations[].Instances[].ImageId[]' [ "ami-02507631a9f7bc956" ]rootからアカウントに対して許可が必要なんですかね?
近いうちにrootで作業できる環境で再度検証してみようと思います。次回へつづく
おおーめっちゃ増えてるーってところを見たかったのですが残念
今週中にはrootで試してみようと思います。
- 投稿日:2019-06-25T18:08:03+09:00
AWS Glueの概要を図と用語で整理する
AWS Glueをざっくりと理解するために基本的な概念とコンポーネントを、図と用語で理解してみます。
AWS Glueとは?
- フルマネージドETLツール
- ETL = どっかからデータ引っ張って、いい感じに変換してどっかに突っ込むこと
ざっくりとした概念図
特徴
- サーバレス
- 高セキュリティ
- etc..
用語
- データストア
- S3, DynamoDB, RDBなど
- データソース
- Glueへの入力に使われるデータストア
- データターゲット
- Glueからの出力に使われるデータストア
- データカタログ(Data Catalog)
- Glueを利用するための箱
- ジョブ、メタデータ(データベース,テーブル)などGlueに関わるコンポーネントはすべてここに含まれる
- 1AWSアカウントの1リージョンにつき、1データカタログ
- データベース
- データカタログに含まれる。テーブル等をまとめておくもの。
- テーブル
- データベースに含まれる
- データソースについてのメタデータを格納したもの
- あくまでメタデータを格納しており、実際のデータは含まない
- 分類子
- データのスキーマを決定する
- その他の用語
メモ
- テーブルは手動(またはCloudformation等)でも作成できるが、クローラから作成するのがミスが少なくて良い
- CloudformationはデータソースにDynamoDBを使用したクローラの作成には未対応(2019/06現在)
- というかDynamoDBはいろいろ未対応。まだDynamoDBには少々使いづらい印象。
- 投稿日:2019-06-25T16:29:39+09:00
AWS AuroraをMySQL Workbenchでいじる
はじめに
AWS上のAmazon Aurora(MySQL)を
MySQL Workbenchで外からいじってみました。
Amazon Auroraとは
- AWSが提供しているフルマネージドなデータベースサービス。
- 可用性99.99%
- 最低でも3つのAZにデータを6個複製、継続的にAmazon S3にバックアップ
- MySQL、PostgreSQL互換
- いろいろ自動でやってくれる(プロビジョニングとかバックアップとか)
- ストレージが64TBまで自動で増える。
- Aurora Serverlessを使えばインスタンスの性能まで自動で管理してくれる。
詳しくはこちら
Amazon Auroraデータベースのセットアップ
AWSコンソールのRDSからいきます。
データベースの作成からAmazonAuroraを選択します。
エディションはMySQLです。接続のところの追加の接続設定で「パブリックアクセス可能」を「あり」にします
これをしないとVPC外から接続できません。
あと付与するセキュリティグループでインスタンスのポートに接続可能にしておくのも忘れずに(デフォルトだと3306)
他の設定は適当で大丈夫です。作成できたらエンドポイントをメモっておいてください。
MySQL Workbenchを起動
起動したら下の矢印の+ボタンを押します。
そしたらこの画面が出てくるので先程のエンドポイントとユーザー名、パスワードを入力してOKを押します。
スキーマの作成
接続できたらこんな画面
左上のアイコンをクリックするとスキーマが作成できます。テーブルの作成
適当にスキーマを作成したら
今度はテーブルを作成します。
右隣のボタンです。PKとかNNとかなんやっておもったら右下のStorageってところに書いてありました。
PrimaryKeyとかの設定ですね。
データ挿入
テーブルが作れたらデータの挿入です
左側のテーブル名のところにカーソルを当てると表っぽいアイコンが出てきてそれをクリックするとテーブルの中身が見えます。
真ん中らへんのEditのところでいろいろ挿入したり削除したりできます。
変更したら右下のApplyで実際のコマンドとして入力するとテーブルが更新されます。コマンドで表を操作したり、GUI上でポチポチとテーブルを操作したりできます。
すぐに反映された結果のテーブルが見えるので便利です。まとめ
Amazon AuroraをGUIのMySQLクライアントから触ってみました。
いろいろ機能が豊富そうなので研究しがいがありそうです
- 投稿日:2019-06-25T16:06:44+09:00
【AWS】Well-Architected Frameworkをホワイトペーパーから紐解く ~信頼性~
はじめに
クラウドならではの強みである自動化をうまく活用・管理することで、より信頼性の高いアーキテクチャを設計することができます。
設計原則とベストプラクティス
設計原則と、ベストプラクティスに紐づくチェック項目を見てみましょう。
管理やモニタリングなど、セキュリティの柱と共通する文言が並びます。セキュリティが高い状態でないと、信頼性の高いアーキテクチャは設計できないませんね。ベストプラクティスの概要を抜き出してみます。
- 概要 基盤 信頼性に影響を与える基盤を整える 変更管理 変更がシステムに与える影響を把握し、変更があった場合にどうするか、事前に計画を作成できる状態にする 障害管理 論理エラーおよび物理エラーの両方から確実に復旧できる状態にする 候補サービス一覧
この柱のベースとなるサービスは、CloudWatchです。
下記のような構成が基本となります。
- IAMを中心にセキュリティに守られた基盤を構築
- CloudWatchなどで変更ログをトラッキング
- Auto Scalingで必要に応じてリソースをスケール
- 障害が起こった時のためにCloudFormationでテンプレを作成、ストレージにバックアップ
他のマネージドサービスも入れて、一覧表にしてみます。
まとめ
信頼性の高いアーキテクチャを設計するうえで留意しなくてはいけないポイントと、活用するサービスの概要、つかめましたでしょうか?次回はパフォーマンス効率の柱についてお送りします!
参考リンク
- 投稿日:2019-06-25T14:18:29+09:00
CentOS7でT2からT3へ変更時のNIC名変更への対応
はじめに
AWSでCentOS7で動いているEC2インスタンスをNitro世代(C5、M5、T3、…)に変更する際に
ハマったので、誰かの助けになればと思いメモっておきます。通常の手順
Nitroシステムを使うにはENAを有効にする必要があり、AWSの公式リファレンスやググってみると
以下のような手順が出ます
- ixgbevfモジュールを入れる
- enaモジュールを入れる(kernelアップデート)
- インスタンス停止
- modify-instance-attributeでena-supportを有効にする
- インスタンスタイプ変更
- インスタンス起動
CentOS7の場合、上記だけで対応できない部分があったので、その解決方法を紹介します。
ハマりポイント
CentOS7からNIC名はensXのような名前となり、従来のethXではなくなっています。
ですが、AWS EC2インスタンスのNitro世代前のインスタンスタイプでは、デフォルトでeth0が
使われます。これをNitro世代に変更するとensXという名称が使われるようになります。
(カーネル起動オプションなどで固定はできますが、クラウド上でそこいじりたくない、という前提)そのままENA有効化して起動してもネットワークが繋がりません。
以下のその際にシスログに出力される内容です。MetaDataアクセスができていません。Jun 25 10:47:02 sva0110 journal: [CLOUDINIT] url_helper.py[WARNING]: Calling 'http://169.254.169.254/2009-04-04/meta-data/instance-id' failed [87/120s]: unexpected error ['NoneType' object has no attribute 'status_code']変更後の手順
- ixgbevfモジュールを入れる
- enaモジュールを入れる(kernelアップデート)
- cloud-initアップデート
- インスタンス停止
- modify-instance-attributeでena-supportを有効にする
- AMI作成
- AMIからNitro世代インスタンスタイプを指定して起動
解説
最新版のcloud-init(現時点:18.2-1)ではインスタンス初回起動時の処理でethXからensXへの
変更も行ってくれるようです!
なので、AMIを作ってAMIからインスタンス作成すればNitro世代でも起動できるようになります。まとめ
書いている途中に カーネル起動オプションを修正している公式な手順が見つかりました!!
https://aws.amazon.com/jp/premiumsupport/knowledge-center/install-ena-driver-rhel-ec2/
ということで、意味のない記事になってしまいましたが、もしかしたらこっちの手順の方がよいという
人もいるかもしれないので、そのまま公開します。最後に、こっちもハマりどころだと思うので紹介しておきます。
NITRO世代(T3など)へのEC2インスタンスタイプ変更ではfstabに注意
- 投稿日:2019-06-25T14:09:45+09:00
AWS IAMの管理ポリシーはたまに勝手にアップデートするので要注意
背景
今日たまたまAWSのマネジメントコンソールにログインしたとき、IAMの管理画面に「AWS Organizations」が増えていたことに気づきました。
ログイン中のIAM Userでは、Oranizationsに対する一切の権限は与えられていないはずなので、押しても何も見えないはずと思い、恐る恐る押してみると、なんとOrganizationsの設定が見えてしまいました。IAMの管理画面がアップデートされた図
下部に「AWS Organizations」が増えています。
見えないはずのものが見えたの図
IAMの管理画面から、Organizationsの設定が見えています。
原因
Organizationsの権限が付与されたわけではない(その記憶がない)のに、なぜOrganizationsの設定が見えてしまうのか気になりました。
調査したところ、ログイン中のIAM Userには「IAMFullAccess」という管理ポリシーがアタッチされており、この管理ポリシーが最近アップデートされていました。
IAMの管理画面がアップデートされるのを機に、権限が増えたのでしょう。IAMの管理ポリシーがアップデートされたの図
Version 1
Version 2
「organizations」の権限が増えています。
現在の設定は、デフォルトでこのVersion 2になるようです。
教訓
今回、IAMの管理ポリシーが勝手にアップデートされ、権限が増えるという場面に遭遇しました。
つまり、意図せず権限が勝手に増えることを防ぐためには、管理ポリシーを使わず、独自にポリシーを作成してアタッチしなければならないようです。
管理ポリシーは便利ですが、勝手にアップデートすることがあると理解して使うべし、ということですね。
- 投稿日:2019-06-25T09:00:15+09:00
AWS LambdaでAWS CLIから関数にLayerを追加・削除する方法
AWS CLIからLambda関数にLayerを追加・削除する方法がわかりにくかったのでメモ。
前提
- AWSアカウントがある
- AWS CLIが利用可能
- 検証できるLambda関数がある
- なければ新規作成
Lambda Layerを作成
検証で利用するLayerを作成します。今回は関数内で参照しないので、ファイル内に
//
だけ含んだファイルでLayerを作成します。空ファイルだとだめでした。
AWS CLIだとaws lambda publish-layer-version
コマンドで作成ができます。> echo '//' layer.js > zip layer.zip layer.js > aws lambda publish-layer-version \ --layer-name test-layer-1 \ --zip-file fileb://layer.zip \ --compatible-runtimes nodejs8.10 { "Content": { "Location": "https://prod-04-2014-layers.s3.amazonaws.com/snapshots/(略)", "CodeSha256": "T4pRG6jK1iy8rjqOxxP2t3R1YSb5hl61Q90xHYHkePE=", "CodeSize": 168 }, "LayerArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-1", "LayerVersionArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-1:1", "Description": "", "CreatedDate": "2019-06-20T07:05:45.061+0000", "Version": 1, "CompatibleRuntimes": [ "nodejs8.10" ] } > aws lambda publish-layer-version \ --layer-name test-layer-2 \ --zip-file fileb://layer.zip \ --compatible-runtimes nodejs8.10 { "Content": { "Location": "https://prod-04-2014-layers.s3.amazonaws.com/snapshots/(略)", "CodeSha256": "T4pRG6jK1iy8rjqOxxP2t3R1YSb5hl61Q90xHYHkePE=", "CodeSize": 168 }, "LayerArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-2", "LayerVersionArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-2:1", "Description": "", "CreatedDate": "2019-06-20T07:07:28.453+0000", "Version": 1, "CompatibleRuntimes": [ "nodejs8.10" ] }Lambda Layerを関数に追加
検証用の関数
use-lambda-layer
がある前提です。Layerを指定する場合、上記にあるARNのうち、バージョンまで含まれている
LayerVersionArn
を指定する必要があります。複数Layerを追加するにはスペース区切りで指定します。# 1つ追加 > aws lambda update-function-configuration \ --function-name use-lambda-layer \ --layers \ "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-1:1" { "FunctionName": "use-lambda-layer", "FunctionArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:use-lambda-layer", "Runtime": "nodejs8.10", "Role": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/xxxxxxxxxxxx", "Handler": "index.handler", "CodeSize": 262, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2019-06-20T07:11:20.502+0000", "CodeSha256": "F8AUyhyiHz5dsBaSl6+u86B5oiwWD3jtdi3IuvB0eKE=", "Version": "$LATEST", "VpcConfig": { "SubnetIds": [], "SecurityGroupIds": [], "VpcId": "" }, "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "f8ad4fd8-b8d2-4aba-ba12-9f28759df79d", "Layers": [ { "Arn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-1:1", "CodeSize": 168 } ] } # 2つ追加 > aws lambda update-function-configuration \ --function-name use-lambda-layer \ --layers \ "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-1:1" \ "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-2:1" { "FunctionName": "use-lambda-layer", "FunctionArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:use-lambda-layer", "Runtime": "nodejs8.10", "Role": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/xxxxxxxxxxxx", "Handler": "index.handler", "CodeSize": 262, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2019-06-20T07:12:47.938+0000", "CodeSha256": "F8AUyhyiHz5dsBaSl6+u86B5oiwWD3jtdi3IuvB0eKE=", "Version": "$LATEST", "VpcConfig": { "SubnetIds": [], "SecurityGroupIds": [], "VpcId": "" }, "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "8737bdfe-3b0d-404e-b3a0-c9fd57e971a9", "Layers": [ { "Arn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-1:1", "CodeSize": 168 }, { "Arn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-2:1", "CodeSize": 168 } ] }Lambda Layerを関数から削除
Layerを削除というよりは利用するLayerを再指定することになります。
すべて削除するには--layers []
として、空配列
を指定する必要があります。わかりにくい# 1つ削除 > aws lambda update-function-configuration \ --function-name use-lambda-layer \ --layers \ "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-2:1" { "FunctionName": "use-lambda-layer", "FunctionArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:use-lambda-layer", "Runtime": "nodejs8.10", "Role": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/xxxxxxxxxxxx", "Handler": "index.handler", "CodeSize": 262, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2019-06-20T07:13:27.749+0000", "CodeSha256": "F8AUyhyiHz5dsBaSl6+u86B5oiwWD3jtdi3IuvB0eKE=", "Version": "$LATEST", "VpcConfig": { "SubnetIds": [], "SecurityGroupIds": [], "VpcId": "" }, "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "fa942867-332a-4d32-ae2d-31209ce9cc30", "Layers": [ { "Arn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:test-layer-2:1", "CodeSize": 168 } ] } # 全部削除 > aws lambda update-function-configuration \ --function-name use-lambda-layer \ --layers [] { "FunctionName": "use-lambda-layer", "FunctionArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:use-lambda-layer", "Runtime": "nodejs8.10", "Role": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/xxxxxxxxxxxx", "Handler": "index.handler", "CodeSize": 262, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2019-06-20T07:13:53.065+0000", "CodeSha256": "F8AUyhyiHz5dsBaSl6+u86B5oiwWD3jtdi3IuvB0eKE=", "Version": "$LATEST", "VpcConfig": { "SubnetIds": [], "SecurityGroupIds": [], "VpcId": "" }, "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "c060cba7-2cf4-48af-88d3-4e5f653965a2" }参考
AWS Lambda Layers - AWS Lambda
https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.htmlpublish-layer-version — AWS CLI 1.16.182 Command Reference
https://docs.aws.amazon.com/cli/latest/reference/lambda/publish-layer-version.html
- 投稿日:2019-06-25T00:35:46+09:00
AWS Fargate の「タスクのスケジューリング」で定時バッチ用コンテナを実行する
概要
- AWS Fargate には、スケジュールルールに従ってタスクを実行できる機能がある。
- スケジュールルールは次の2通りある。どちらもスケジュールルールに従って毎回新しいコンテナが起動して終了する。
- 固定された間隔で実行
- Cron式
- この「タスクのスケジューリング」機能を使って、これまで EC2 上の crond で起動して実行していたバッチ処理を、コンテナ化して AWS Fargate に移行してみる。
今回 Fargate に移行するバッチの概要
Elasticsearch の インデックスを1日1回削除するバッチ。
- サーバのアクセスログを Fluentd で Elasticsearch に送ってKibanaで可視化している。1日分のアクセスログを1つのインデックスに格納していて、30日前のインデックスファイルを削除するバッチを1日1回実行している。
バッチは bash スクリプトでこんな感じで書いている。
#!/bin/bash ESDOMAIN="hogehoge.ap-northeast-1.es.amazonaws.com" INDEXNAME=( "fastly-accesslog-" "Varnish-accesslog-" "nginx-accesslog-" ) DATA=`date -d '30 days ago' +"%Y-%m-%d"` for INDEX in ${INDEXNAME[@]}; do echo "Delete: ${INDEX}${DATA}" curl -s -X DELETE https://${ESDOMAIN}/${INDEX}${DATA} echo -e "\n" done echo "Execution Result" curl -s -X GET ${ESDOMAIN}/_aliases?pretty | grep -v '}' | sortバッチ処理を行うコンテナの Dockerfile 作成
FROM alpine:latest RUN apk --no-cache add tzdata && cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && apk del tzdata RUN apk --no-cache add coreutils bash bash-completion curl COPY del-index.sh /del-index.sh RUN chmod u+x /del-index.sh CMD /bin/bash /del-index.shDockerfileの簡単な説明
軽量な Alpine Linux を使う
- いろいろ削がれて軽量になっているので、Dockerfile で必要なものを追加する。
Alpine の timezone を JST に設定
- デフォルトは UTC になっているので、JST に設定する。
- Dockerfile追加設定
RUN apk --no-cache add tzdata && cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && apk del tzdata- 参考: https://wiki.alpinelinux.org/wiki/Setting_the_timezone
dataコマンド
- Alpine Linux のdataコマンドは、「date -d '30 days ago'」 を解釈してくれない。 なので、GNU coreutils の date をインストールする。
- Dockerfile 追加設定
RUN apk --no-cache add coreutilsbash
- Alpine Linux のデフォルトのシェルは sh なので、配列が使えない。bashで書いた次の部分でエラーになってしまう。
INDEXNAME=( "fastly-accesslog-" "Varnish-accesslog-" "nginx-accesslog-" )
- シェルスクリプトを sh 用に書き直すのは面倒なので、bashを入れてしまう。
- Dockerfile 追加設定
RUN apk --no-cache add bash bash-completioncurl
- 今回のバッチは、curl で Elasticsearch の API を叩くので、curl をインストールする。
- Dockerfile 追加設定
RUN apk --no-cache add curlbashスクリプト本体
- 今回は del-index.sh という名前で、上記のシェルスクリプトのファイルを作成する。これを Dockerfile と同じディレクトリに置く。
- docker build 時に del-index.sh ファイルを docker イメージの / にコピーして、実行権限を付与する。
- コンテナ起動時に、この del-index.sh を実行する。
- Dockerfile 追加設定
COPY del-index.sh /del-index.sh RUN chmod u+x /del-index.sh CMD /bin/bash /del-index.shDockerイメージをECRへプッシュして、Fargete の設定を行う
- 今回のバッチは、Elasticsearch 側で接続元IPアドレスの制限を行うので、Fargateによって起動したコンテナは固定のIPアドレスをもたせる必要がある。
- AWS Fargate で実行したコンテナに固定のグローバルIPアドレスを割り当てる を参考にして諸々設定する。
Fargate タスクのスケジューリング 設定
VPC とセキュリティグループ
AWS Fargate で実行したコンテナに固定のグローバルIPアドレスを割り当てる を参考作成
以上で、毎朝 06:00 に Elasticsearch のインデックスを削除するコンテナが起動して、処理を終えたらコンテナが消滅する。
実行ログは CloudWatch ログに送られる。おまけ
Slack にも実行結果を送りたい場合は、del-index.sh に Slack へ送る設定を追加する
#!/bin/bash ESDOMAIN="hogehoge.ap-northeast-1.es.amazonaws.com" INDEXNAME=( "fastly-accesslog-" "Varnish-accesslog-" "nginx-accesslog-" ) DATA=`date -d '30 days ago' +"%Y-%m-%d"` exec 3>&1 exec > /exec.log echo "Elasticsearch delete index" for INDEX in ${INDEXNAME[@]}; do echo "Delete Index: ${INDEX}${DATA}" curl -s -X DELETE https://${ESDOMAIN}/${INDEX}${DATA} echo -e "\n" done echo "curl -X GET https://${ESDOMAIN}/_aliases?pretty" curl -s -X GET ${ESDOMAIN}/_aliases?pretty | grep -v '}' | sort exec 1>&3 sed -i -e 's/\"//g' /exec.log channel="hoge" username="Container" text="Elasticsearch delete index" color="gray" message=`cat /exec.log` attachments=" { \"color\":\"${color}\", \"text\": \"${message}\" }" emoji=':envelope:' url="https://hooks.slack.com/services/hoge/hogehoge" payload="payload={\"channel\": \"${channel}\", \"username\": \"${username}\", \"text\": \"${text}\", \"attachments\":[${attachments}] , \"icon_emoji\": \"${emoji}\"}" curl -s -m 5 -d "${payload}" $url exit
- 標準出力をファイルへ出力する設定
exec 3>&1 exec > /exec.log 〜 exec 1>&3- 実行結果ファイルからダブルクォーテーションを除外(Slack送信時に invalid_payload エラーとなるため)
sed -i -e 's/\"//g' /exec.log
- 投稿日:2019-06-25T00:35:46+09:00
AWS Fargate の「タスクのスケジューリング」でバッチ用コンテナを定時実行する
概要
- AWS Fargate には、スケジュールルールに従ってタスクを実行できる機能がある。
- スケジュールルールは次の2通りある。どちらもスケジュールルールに従って毎回新しいコンテナが起動して終了する。
- 固定された間隔で実行
- Cron式
- この「タスクのスケジューリング」機能を使って、これまで EC2 上の crond で起動して実行していたバッチ処理を、コンテナ化して AWS Fargate に移行してみる。
今回 Fargate に移行するバッチの概要
Elasticsearch の インデックスを1日1回削除するバッチ。
- サーバのアクセスログを Fluentd で Elasticsearch に送ってKibanaで可視化している。1日分のアクセスログを1つのインデックスに格納していて、30日前のインデックスファイルを削除するバッチを1日1回実行している。
バッチは bash スクリプトでこんな感じで書いている。
#!/bin/bash ESDOMAIN="hogehoge.ap-northeast-1.es.amazonaws.com" INDEXNAME=( "fastly-accesslog-" "varnish-accesslog-" "nginx-accesslog-" ) DATA=`date -d '30 days ago' +"%Y-%m-%d"` for INDEX in ${INDEXNAME[@]}; do echo "Delete: ${INDEX}${DATA}" curl -s -X DELETE https://${ESDOMAIN}/${INDEX}${DATA} echo -e "\n" done echo "Execution Result" curl -s -X GET ${ESDOMAIN}/_aliases?pretty | grep -v '}' | sortバッチ処理を行うコンテナの Dockerfile 作成
FROM alpine:latest RUN apk --no-cache add tzdata && cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && apk del tzdata RUN apk --no-cache add coreutils bash bash-completion curl COPY del-index.sh /del-index.sh RUN chmod u+x /del-index.sh CMD /bin/bash /del-index.shDockerfileの簡単な説明
軽量な Alpine Linux を使う
- いろいろ削がれて軽量になっている。
- 今回のシェルスクリプトを実行するために必要なものを追加する。
Alpine Linux の timezone を JST に設定
- デフォルトは UTC になっているので、JST に設定する。
- Dockerfile 追加設定
RUN apk --no-cache add tzdata && cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && apk del tzdata- 参考: https://wiki.alpinelinux.org/wiki/Setting_the_timezone
dataコマンド
- Alpine Linux のdataコマンドは、「date -d '30 days ago'」 を解釈してくれない。 なので、GNU coreutils の date をインストールする。
- Dockerfile 追加設定
RUN apk --no-cache add coreutilsbash
- Alpine Linux のデフォルトのシェルは sh なので、配列が使えない。bashで書いた次の部分でエラーになってしまう。
INDEXNAME=( "fastly-accesslog-" "Varnish-accesslog-" "nginx-accesslog-" )
- シェルスクリプトを sh 用に書き直すのは面倒なので、bashを入れてしまう。
- Dockerfile 追加設定
RUN apk --no-cache add bash bash-completioncurl
- 今回のバッチは、curl で Elasticsearch の API を叩くので、curl をインストールする。
- Dockerfile 追加設定
RUN apk --no-cache add curlbashスクリプト本体
- 今回は del-index.sh という名前で、上記のシェルスクリプトのファイルを作成する。これを Dockerfile と同じディレクトリに置く。
- docker build 時に del-index.sh ファイルを docker イメージの / にコピーして、実行権限を付与する。
- コンテナ起動時に、この del-index.sh を実行する。
- Dockerfile 追加設定
COPY del-index.sh /del-index.sh RUN chmod u+x /del-index.sh CMD /bin/bash /del-index.shDockerイメージをECRへプッシュして、Fargete の設定を行う
- 今回のバッチは、Elasticsearch 側で接続元IPアドレスの制限を行うので、Fargateによって起動したコンテナは固定のIPアドレスをもたせる必要がある。
- AWS Fargate で実行したコンテナに固定のグローバルIPアドレスを割り当てる を参考にして諸々設定する。
Fargate タスクのスケジューリング 設定
Fargate タスクのスケジューリングの概要
Fargate は CloudWatch イベントに応じたタスクのスケジューリングをサポートしている。
スケジュールされたイベントルールは、特定の間隔 (N 分、時間、日ごとに実行)に設定することができるし、または、より複雑なスケジュールに設定する場合は、cron 式を使用することもできる(ルールのスケジュール式)。
すべてのスケジュールされたイベントは UTC タイムゾーンを使用している。
例えば、cron式で cron(0 10 * * ? *) と設定した場合、毎日午前 10:00 (UTC) に実行する。
日本時間で毎朝午前 10:00 に実行させるには、cron式で cron(0 1 * * ? *) と設定する(JST=UTC+9)。
- 参照:ルールのスケジュール式
タスクのスケジューリング設定例
スケジュールルール設定
今回は、毎日午前 6:00 (JST) に実行したいので、Cron 式は cron(0 21 * * ? *)
VPC とセキュリティグループ
AWS Fargate で実行したコンテナに固定のグローバルIPアドレスを割り当てる を参考作成
以上で、毎日午前 6:00 (JST) に Elasticsearch のインデックスを削除するコンテナが起動して、処理を終えたらコンテナが消滅する。
実行ログは CloudWatch ログに送られる。おまけ
Slack にも実行結果を送りたい場合は、del-index.sh に Slack へ送る設定を追加する
#!/bin/bash ESDOMAIN="hogehoge.ap-northeast-1.es.amazonaws.com" INDEXNAME=( "fastly-accesslog-" "Varnish-accesslog-" "nginx-accesslog-" ) DATA=`date -d '30 days ago' +"%Y-%m-%d"` exec 3>&1 exec > /exec.log echo "Elasticsearch delete index" for INDEX in ${INDEXNAME[@]}; do echo "Delete Index: ${INDEX}${DATA}" curl -s -X DELETE https://${ESDOMAIN}/${INDEX}${DATA} echo -e "\n" done echo "curl -X GET https://${ESDOMAIN}/_aliases?pretty" curl -s -X GET ${ESDOMAIN}/_aliases?pretty | grep -v '}' | sort exec 1>&3 sed -i -e 's/\"//g' /exec.log channel="hoge" username="Container" text="Elasticsearch delete index" color="gray" message=`cat /exec.log` attachments=" { \"color\":\"${color}\", \"text\": \"${message}\" }" emoji=':envelope:' url="https://hooks.slack.com/services/hoge/hogehoge" payload="payload={\"channel\": \"${channel}\", \"username\": \"${username}\", \"text\": \"${text}\", \"attachments\":[${attachments}] , \"icon_emoji\": \"${emoji}\"}" curl -s -m 5 -d "${payload}" $url exit
- 標準出力をファイルへ出力する設定
exec 3>&1 exec > /exec.log 〜 exec 1>&3- 実行結果ファイルからダブルクォーテーションを除外(Slack送信時に invalid_payload エラーとなるため)
sed -i -e 's/\"//g' /exec.log
- 投稿日:2019-06-25T00:25:57+09:00
docker-lambdaで関数連続実行をワンコンテナで
ワンコンテナでlambada関数を連続実行したい
lambci/docker-lambdaでローカルテストする際に、下記のように普通に関数を連続実行すると...
docker run --rm -v "$PWD":/var/task lambci/lambda:python3.6 sample_function.lambda_handler '{"Hello":"World"}' docker run --rm -v "$PWD":/var/task lambci/lambda:python3.6 sample_function.lambda_handler '{"Hello":"World"}'下記のようにコンテナが別々に生成される。(※docker container statsの結果を表示↓↓)
これをワンコンテナでやりたい場合は、下記のようにentrypointを上書きしてやれば可能。
docker run --rm -v "$PWD":/var/task --entrypoint "" lambci/lambda:python3.6 bash -c " \ python3.6 /var/runtime/awslambda/bootstrap.py sample_function.lambda_handler '{\"Hello\":\"World\"}' & \ python3.6 /var/runtime/awslambda/bootstrap.py sample_function.lambda_handler '{\"Hello\":\"World\"}' \ "