LoginSignup
5
2

More than 3 years have passed since last update.

AWS Lambda を使って GitLab private repository へ push しちゃう【2019 アドカレ】

Last updated at Posted at 2019-12-01

対象読者

  • AWS に触れてみたいけどよくわかんない。 Lambda っていうやつが無料っぽいし、いっとう手軽そうだからちょっと触ってみたい。
  • GitLab から自動で clone, commit, push するスクリプトが書きたい。
  • そういうスクリプトを AWS で実行してみたい。

つまり先日の私です。

概要

こんなことをしてみます。

  • GitLab の private repository が対象です。
  • repository を clone, 編集, add, commit, push をするプログラムを作ります。
  • private repository へのアクセスには ssh キーを使います。
  • プログラムは AWS Lambda に無料で置きます。

本記事は GitLab CI/CD とは関係ありません。

はじめる前に知っておくとスムーズになるかもしれないこと

キホン的すぎることが混じっていて鼻白むかもわかりませんが、私自身のレベルがこんなもんなのでご容赦ください。

使用する Python モジュールについて

  • private repository へアクセスするために、 ssh モジュールの Paramiko を使います。 pip でインストールします。
  • git 操作については、 Dulwich を使います。 pip でインストールします。

AWS Lambda について

  • Java, Node, Go, Python, Ruby といった言語のスクリプトをちゃちゃっとアップロードしてちゃちゃっと実行できるサービスです。
  • 上述のモジュールを含むプログラムを AWS Lambda へアップするときは、 AWS 上で pip を実行するのではなく、我々のぱそこ上で全モジュールの実ファイルを含む zip ファイル(デプロイパッケージ)を作成し、それをアップロードするという手順になります。
    • というより AWS Lambda 上ではコマンドを実行するとかそういうのがそもそもありません。
    • AWS Lambda は100万リクエスト/月、320万秒/月まで無料なので、遊ぶのによさそうです。(AWS 無料利用枠)
  • その zip ファイルは Linux 上で作成します。 Linux を持っている人はそれを使われたらいいですが、私は Vagrant で Ubuntu 仮想マシンを立てて作業を行おうと思います。

そのほか

  • Paramiko や Dulwich のバージョンによっては、掲載する Python スクリプトが動かなくなるかもしれません。そんなわけで今回使うバージョンの一覧(requirements.txt)は後述します。

これからやることを図にして整理

図にして整理してみます。

↓図のうち、 GitLab private repository だけはもうあるものとさせてください。 AWS Lambda とか、自分のぱそこの py スクリプトや依存モジュール、 ssh 鍵の作成は本記事で扱います。

1.png

スクリプトの作成やら Lambda の準備やらが終わったら、↓図のように、自分の環境にあるファイルを zip 化してアップロードです。ちなみに、アップロードしなくても、自分の環境でこの py スクリプト実行は可能です。

2.png

Lambda を起動すると、↓図のようにアップロードしたスクリプトが動き出し、 clone から push を行ってくれるはずです。 clone したモノは Lambda 上の /tmp フォルダにダウンロードする予定です。

3.png

ssh キー

でははじめます。プロジェクト・ディレクトリを作ります。なんでもいいですが project-dir とでもしておきます。ちなみに、このディレクトリを zip 化するわけではありません。

ディレクトリの中で ssh キーを作ります。

# passphrase はスキップして OK です。
ssh-keygen -t rsa -b 4096 -C "your-email-address@example.com" -m PEM -f ./gitlab_id_rsa

次の2ファイルが作られます。それぞれ処理します。

  • gitlab_id_rsa(秘密鍵): project-dir/ssh ディレクトリを作り、その中に入れておきます。
  • gitlab_id_rsa.pub(公開鍵): 中のテキストを GitLab の自分のアカウントに登録します。
    • GitLab 画面右上の自分のアイコン > Settings > SSH Keys > テキストをコピペで登録

この鍵セットがあるおかげで、 private repository にアクセスできるというわけです。

Python スクリプト

AWS Lambda で実行するスクリプトを作ります。自分用メモ、解説のコメントを付しておりちょっと長くなっちゃっています。これを lambda_function.py というファイル名で保存です。まあ別に aaaa.py でもなんでもいいのですが。

project-dir/lambda_function.py
import os
import json
import datetime
import shutil
import paramiko
import dulwich
import dulwich.client
from dulwich import porcelain
from dulwich.contrib.paramiko_vendor import _ParamikoWrapper


class KeyParamikoSSHVendor(object):

    def __init__(self):
        # 秘密鍵のパスを書きます。
        self.ssh_kwargs = {'key_filename': './ssh/gitlab_id_rsa'}

    def run_command(self, host, command, username=None, port=None):
        """ssh 接続でコマンドを実行します。たぶん。"""
        if port is None:
            port = 22
        client = paramiko.SSHClient()
        policy = paramiko.client.MissingHostKeyPolicy()
        client.set_missing_host_key_policy(policy)
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(host, username=username, port=port, **self.ssh_kwargs)
        channel = client.get_transport().open_session()
        channel.exec_command(command)
        return _ParamikoWrapper(client, channel)


def lambda_handler(event, context):
    """AWS Lambda から実行する、トップレベルメソッドです。"""

    # ssh vendor を定義します。
    dulwich.client.get_ssh_vendor = KeyParamikoSSHVendor

    # コミット先のリポジトリです。環境変数 GITLAB_REPO は AWS Lambda で設定します。
    GITLAB_REPO = os.environ['GITLAB_REPO']

    # 現在日時です。
    # NOTE: フォルダ名とかコミットメッセージに使用します。
    current_time = datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')

    # Clone するフォルダです。
    # NOTE: AWS Lambda は一時作業ディレクトリとして /tmp を提供してくれています。
    local_repo_path = '/tmp/repo' + current_time

    # Clone
    repo = dulwich.porcelain.clone(
        GITLAB_REPO,
        target=local_repo_path,
    )

    # Edit
    open(f'{local_repo_path}/file', 'a', encoding='utf-8').write(current_time + '\n')

    # Add
    dulwich.porcelain.add(repo, f'{local_repo_path}/file')

    # Commit
    # NOTE: 返り値は commit のハッシュ値みたいです。
    # NOTE: author と committer は両方設定します。でないと Lambda のログインユーザがコミッターになったりします。
    dulwich.porcelain.commit(
        repo,
        message=f'Update at {current_time}',
        author=os.environ['GIT_COMMITTER'],
        committer=os.environ['GIT_COMMITTER'],
    )

    # Push
    dulwich.porcelain.push(repo, GITLAB_REPO, 'master')

    # Clone した repository の片付けです。
    shutil.rmtree(local_repo_path)

    # リクエストの返却値です。
    return {
        'statusCode': 200,
        'body': json.dumps('OK!')
    }


# AWS Lambda に載せなくとも、これで実行することもできます。
# NOTE: 引数の event, context は関数内で使っていないのでてきとーです。
if __name__ == '__main__':
    lambda_handler(None, None)

Vagrant

私は Linux マシンを持っていないので、 Vagrant で Linux を用意します。今回使う Vagrantfile はこちら。 project-dir の中身が Ubuntu の /project-dir に同期されるようにしています。

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  config.vm.network "private_network", ip: "192.168.33.12"
  config.vm.synced_folder ".", "/project-dir"
end

Vagrant についての解説は割愛させてください。ようは Linux を用意するということです。

デプロイパッケージ

vagrant upvagrant ssh で Ubuntu へ入ったあと、 AWS Lambda へアップロードする zip ファイル(デプロイパッケージ)を作ります。上述しましたが、 AWS Lambda の中で pip install したりはできないので、あらかじめ必要なライブラリを全部詰め込んだ zip を作るわけです。それに、 pip ライブラリ以外にも必要なファイルがあるので……。

# For install Python3.6 add-apt-repository ppa:deadsnakes/ppa
sudo add-apt-repository -y ppa:deadsnakes/ppa

# apt update and upgrade
sudo apt -y update
sudo apt -y upgrade

# Install Python3.6
sudo apt install -y python3.6 python3.6-dev

# Install pip
# NOTE: After commands below can use both pip and pip3 commands. But they are symbolic links of the same file.
wget https://bootstrap.pypa.io/get-pip.py
sudo python3.6 get-pip.py
rm get-pip.py

# Create pipenv environment
# NOTE: Paramiko and Dulwich are included in Pipfile
cd /project-dir
sudo pip install pipenv
pipenv install

# Install zip and unzip
sudo apt install -y zip unzip

# Create zip file
cd /project-dir
rm project.zip
# py files
zip project.zip lambda_function.py
# ssh private key
zip project.zip ssh/*
# libffi
VENV_PATH=`pipenv --venv`
zip --junk-paths project.zip "$VENV_PATH/lib/python3.6/site-packages/.libs_cffi_backend/libffi-806b1a9d.so.6.0.4"
# libssl and libcrypto
zip --junk-paths project.zip /lib/x86_64-linux-gnu/libssl.so.1.0.0 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
# pip libraries
cd "$VENV_PATH/lib/python3.6/site-packages"
zip /project-dir/project.zip ./* -r

# environ
# Vagrant 環境でも実行してみたいならばここで環境変数を定義します。
export GITLAB_REPO='git@gitlab.com:[username]/[repo].git'
export GIT_COMMITTER='[username] <[mail-address]>'

これで project-dir に project.zip デプロイパッケージができます。

ここで一度実行してみることもできます。

cd /project-dir
pipenv run python lambda_function.py

AWS Lambda

今回はじめて AWS を触ったので、いろいろ迷子になりましたが、次のような感じで設定します。

  1. AWS アカウントを作成します。
  2. AWS コンソールを開きます。
  3. 「サービスを検索する」から Lambda を選択します。
  4. 「関数の作成」
    • 「一から作成」選択
    • 関数名はお好み
    • ランタイムはもちろん我らが Python 3.6
  5. 「関数コード」のところ
    • 「コード エントリ タイプ」で「.zip ファイルをアップロード」を選びます。
    • 「関数パッケージ」にデプロイパッケージ project.zip をアップロードします。
    • 「ハンドラ」は lambda_function.lambda_handler。 上で作った Python スクリプトに aaaa.py なんて名前をつけた人は aaaa.lambda_handler になります。
  6. 「環境変数」のところ
    • GITLAB_REPO: git@gitlab.com:[username]/[repo].git
    • GIT_COMMITTER: [username] <[mail-address]>
    • 趣味で環境変数にしているだけで、べつにスクリプトにベタ書きしてもよいと思います。
  7. 「基本設定」のところ
    • 「タイムアウト」はデフォルトの3秒では足りないかもしれないので適宜(Task timed out エラーが出たタイミングなど)増やします。
  8. 「保存」を押します。
  9. 保存が終わったら「テスト」を押します。

「実行結果: 成功」が出れば OK です。

1.png

できました! GitLab のほうを見てみると、 commit が増えています。 GitLab private repository への push、成功です。

このあと

本記事はここでおしまいです。この状態ですと、「テスト」ボタンを押すことでしかプログラムを実行できません。このあとは他の方法でこの Lambda へアクセスしたいところです。今のところ以下のようなものを試してみました。

  • CloudWatch Events
    • cron 式や rate 式で Lambda を定期実行。
    • 作った Lambda のページの「Designer」のところの「トリガーを追加」から追加できます。
  • API Gateway
    • 作った Lambda に URL を与え、インターネットからアクセス。
    • 同じく「Designer」のところの「トリガーを追加」から追加できます。

requirements.txt

project-dir で使用したモジュール群です。バージョンの参考になればと思い載せておきます。

bcrypt==3.1.7
certifi==2019.9.11
cffi==1.13.2
cryptography==2.8
dulwich==0.19.13
paramiko==2.6.0
pycparser==2.19
PyNaCl==1.3.0
six==1.13.0
urllib3==1.25.7
5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2