アイリッジ開発者ブログ

アイリッジに所属するエンジニアが技術情報を発信していきます。

VPC LambdaでEC2内のDockerコンテナを操作する

こんちには!アイリッジプロダクト開発グループの森山です。2021年10月に入社しました。

入社してからは日々成長といった形ですが、入社してすぐに携わったリカバリーシステムで

学んだことを書かせていただきます。

ずっとずっとアウトプットしたかったのでようやく夢が叶います。

今回のケース

実装したのはFANSHIPの要であるプッシュ通知が万が一失敗したときのリカバリーシステムです。

新規作成、また入社後初コーディングということもあり、意欲高めで取り組みました。

リカバリーシステムの中でも

  • EC2内のiridgeと名前のついたDockerコンテナを再起動する

という機能の実装を担当しました。

やろうとしたこと

今回の開発はAWS Lambdaを使っての開発でした。

前段処理のLambdaが僕のLambdaをinvokeする流れとなっています。

僕のLambdaで主にやることは、Dockerコンテナの再起動です。

そこで下記のような流れでの実装を計画しました。

  1. Lambdaを作成する
  2. boto3を利用してSystems Managerを使用
  3. Systems Manager経由でEC2内のDockerへコマンドを実行する

構成図

f:id:jeniferaaaaa:20220308184611p:plain
失敗したパターン

コードは下記です。ssmAWS-RunShellScriptを用いてDockerを操作しようと試みました。

まずはコマンドが通るのかの確認です。

コード

import boto3

instance = "XXXXXXXXXXXXXXXXX"
aws_id = "XXXXXXXXXXXXXX"
aws_key = "XXXXXXXXXXXXXXXXXXX"
commands = ["ls"]

ssm = boto3.client(
    "ssm",
    region_name="ap-northeast-1",
    aws_access_key_id=aws_id,
    aws_secret_access_key=aws_key,
)

res = ssm.send_command(
    InstanceIds=[instance],
    DocumentName="AWS-RunShellScript",
    Parameters={"commands": commands},
)

command_id = res["Command"]["CommandId"]

失敗

実行したのですが、うまく疎通できませんでした。EC2内にSystems Managerが存在していないので実行できなかったようです。

既に稼働中のインスタンスということもあり、Systems Managerをインストールして実行するという作業はやりたくはないところ。。。

LambdaからVPCの外に出てSystems Managerを経由できないか調査したけどもダメそうでした。

新たな手法

完全に行き詰まってしまったので、社内の頼れる先輩に相談しました。

以下、当時の会話


ぼく「Systems Managerがインスタンス内にないので、うまくいかないんです。どうしたらよいでしょうか。。。」

先輩「同一サブネットに属しているのでSystems Managerは使わずにDocker daemonにTCP接続してみた方が良いですよ」

先輩「Pythonならdocker-pyというライブラリがあるので使ってみてください」

ぼく「そうかなるほど!ありがとうございます!」


同一サブネットという前提条件が抜けており、なおかつTCP接続して操作するという概念がなかったため、このアイディアが浮かびませんでした。

が、確かにきちんと調べてみるとこちらの方法の方が簡単に実装できそう。

というわけで方針修正です。

  1. Lambdaを作成する
  2. docker-pyを使用してdocker daemonにtcp接続する
  3. docker-pyを利用してコンテナを再起動する

構成図

f:id:jeniferaaaaa:20220308184542p:plain
成功したパターン

コード

import docker

# Docker daemonの接続情報
docker_daemon_info = "tcp://xxx.xxxx.xxxx:2375"

# Docker clientへTCP接続
containers_object = docker.DockerClient(base_url=docker_daemon_info)

# プリントしてみる
print(containers_object)

pythonのライブラリであるdocker-pyは標準ライブラリではないのでインストールが必要になります。

https://github.com/docker/docker-py

解決

先ほどのコードを実行してみると・・・

# printした中身
<docker.client.DockerClient object at 0xxxxxxxxxxxx0>

無事成功したようです。あとはiridgeと名前のついたコンテナを再起動するだけです。

コンテナの名前も.nameで簡単に取得できます。

再起動はrestart()です。

import docker

# Docker daemonの接続情報
docker_daemon_info = "tcp://xxx.xxxx.xxxx:2375"

# Docker clientへTCP接続
containers_object = docker.DockerClient(base_url=docker_daemon_info)

# コンテナを再起動
for container in containers_object:
    container_name = container.name
    # "iridge"という名前のコンテナのみ再起動をかける
    if "iridge" in container_name:
        container.restart()

終わりに

AWSのサービスを独立して考えすぎていたというところがダメだったなと思いました。

同一サブネットに属しているのだからSSMを使わずとも・・・となれなかったので、インフラ的な部分でも自分のエンジニアとしての知識不足を痛感しました。

コードを書きながらインフラも学べるこの環境に感謝しつつ、FANSHIPと共に成長していこうと思います。

参考