投稿者「adachin」のアーカイブ

Lancers本番環境のコンテナ化が完了しました

adachin|2022年06月10日
SRE

Lancers Engineer Blog をご覧のみなさんこんにちは。開発部/技術基盤 SREの安達(@adachin0817)です。以下前回のブログから3ヶ月経ちましたが、ついにLancersのBatch、AppサーバーをEC2からECS/Fargateに移行完了しました。

そして長年自前で運用していたデプロイシステムを廃止して、CI/CDはCircleCIに完全統一しました。これにて、Lancersの全サービスをコンテナに移行完了となりました。

※見ていない方はぜひ一読してもらえると幸いです。

・LancersをAmazon Linux2へログ基盤のリニューアルと管理画面をECS/Fargateに移行しました

旧開発環境、EC2での運用課題

  • Ansibleコンテナによる開発環境の統一により工数がかかる

本番コンテナ化以前は、EC2で利用しているAnsibleの管理を開発環境にも適用するために、開発コンテナの構築も(Dockerfileではなく)Ansibleで行っていました。開発コンテナをAnsibleで構築する運用は、ECRで共用する必要がありました。パッケージの更新やNginxの設定などを変更するたびにコンテナを作り直してECRに都度pushする必要があり、SREの工数がかかる要因の一つになっていました。

  • Ansible修正後にオートスケールの再設定でAppサーバーを作り直さなければならない

Ansibleのplaybookの修正が入ると、オートスケールの起動に利用するAMIの更新が必要になります。ステージング、カナリア、本番環境のAppサーバーとオートスケールを作り直さなければなりません。また以下のように手動で設定をするというトイルが残ったままでした。

・AMI専用のEC2を起動し、Ansible実行
・AMIを取得後、オートスケールの起動設定をコピー
・Auto Scaling グループから起動タイプを最新に変更

ECS/Fargateであればこの部分をDockerfileとデプロイだけで済むので工数がかからないというメリットがあります。

  • 自前のデプロイシステムが属人化されてしまっており、不具合の対応に工数がかかる

こちらは後述します。

開発環境の改善

  • これまでの開発環境

> 開発環境のDocker化

  • 新開発環境
$ tree docker/dev/app
docker/dev/app
├── Dockerfile
├── README.md
├── awslogs
│   ├── awscli.conf
│   └── awslogs.conf
├── devx.sh
├── nginx
│   ├── _default.conf
│   ├── api.conf
│   ├── header.conf.include
│   ├── krgn.conf
│   ├── log.conf.http
│   ├── nginx.conf
│   ├── www.conf
│   ├── www.conf.proxy
│   └── www.conf.rewrite
├── php
│   ├── 15-xdebug.ini
│   ├── 40-apcu.ini
│   └── php.ini
├── php-fpm
│   ├── php-fpm.conf
│   └── www.conf
├── postfix
│   ├── main.cf
│   └── postfix.sh
├── src
│   ├── DatabaseDriver.php
│   ├── environment.php
│   └── operation_mode.php
└── supervisor
├── app.conf
└── supervisord.conf

Dockerfileによる一般的な運用になりました。また、別リポジトリ(playbook)で管理していた開発環境は、Lancers本体のリポジトリに統一しました。Docker ImageはAmazon Linux 2を利用しています。pine Linuxと比べるとパフォーマンスが良いと言われており、パッケージインストールのしやすさにより選択しました。

アクセス/アプリログは管理画面と同様にawslogsでシンク

ログサーバーを撤廃して、新ログ基盤であるAmazon Kinesisに移行して3ヶ月経ちました。今の所特にトラブルなく運用できている状況です。アクセスログやアプリケーションログはEC2時代と同様にコンテナでもawslogsを利用してCloudWatch Logsにシンクし、LambdaでLTSV形式に変換後、アクセスログのみKinesisでS3に保存しています。ちなみにAppコンテナはphp-fpm、Nginxを一つのコンテナ内で動作するようにしていますが、分離してしまうとシンプルではなくなってしまうと判断しました。また、FireLensもありますが、ログをstdoutする前提といったことから、現構成だと複数のログディレクトリを参照することができません。サイドカーとしてFluent Bitを利用する場合はコンテナを分離しなければならないでしょう。

またこの機会に分析基盤として利用しているRedashもECS/Fargateへ移行しました。オートスケーリングに対応したことにより、負荷の増大に自動的に対応できるようになりました。

BatchサーバーはECS Scheduled Taskに移行

Lancersのバッチは100個以上あります。今までBatchサーバーは冗長化せずEC2 1台のみで運用していたため、サーバーダウンやAZ障害が起きると、サービスに影響が出てしまう状況でした。そこで、CakePHP4で実装されているバッチ約80個のみECS Scheduled Taskに移行することで、Terraformで開発メンバーが管理できるようにしました。これにより、バッチ運用が冗長化され、耐障害性が上がりました。Cronで標準エラー出力をメール通知していた処理は、ECS Scheduled Taskで、CloudWatch Logs、SNS、Lambdaを利用してエラーのキーワードを検知し、Slack通知させる処理にしました。

残りのCakePHP2で動作しているバッチは現在QAチームが移行していますので、今後Batchサーバーは廃止していく予定になります。Scheduled Taskに移行すると、Nat Gatewayの通信費用が上がりますが、VPCエンドポイントを挟めば全体の費用は6分の1に削減されるため、コスト削減に効果的です。

CakePHP4のバッチはSendGridでメール送信するように実装していますが、一部Postfixを利用するバッチが残っています。Scheduled Taskは1コンテナにつき1プロセスしか実行できないため、工夫しないとメールを送信出来ませんでした。以下のようにSupervisor(Postfix)とPHPのバッチコマンドを実行するシェルスクリプトを書くことで対応しました。

#!/bin/bash

/usr/bin/supervisord &
sleep 10
/var/www/lancers_batch/bin/cake NewWorkEmail
sleep 10

フロントエンドの改善

前回のブログで「AppサーバーをECS/Fargateに移行するタイミングは今ではない」と話していましたが、ネックとなっていたのがフロントエンドのビルド処理でした。この問題を、フロントエンドのリポジトリを分割することで対応しようと考えていましたが、今のフロントエンドのソース構造上、パスによる処理分割が困難で工数がかかることから、一旦中止しました。

毎回リリース時にyarn install,buildをしてしまうと、CIでのビルドに10分以上かかってしまうので、予めローカルでビルドして生成されたファイルをGit管理する運用にしました。
以前の運用に戻した形になりますが、現状はこれが最善と判断しました。これにより、リリース時にyarn installは不要になり、CI時間が大幅に短縮されたため、Appサーバーのコンテナへ移行できる条件が整いました。

リポジトリの分割、およびCIでビルドする運用は将来再度検討予定です。

自前デプロイシステム(Deploy-San)について

自前デプロイシステムは通称「Deploy-San」と呼んでいました。Deploy-Sanは2015年頃に作られたシステムで、Ruby on RailsとJenkinsで実装されていました。Deploy-sanがGitHubからコードをpullした後にaws-cliでEC2のhostsを検知して、独自のシェルスクリプトでEC2にrsyncを実行する仕組みとなっていました。オートスケーリング時には、AMIからEC2サーバーを起動した後、Deploy-sanから最新のコードを取得していました。そのため、Deploy-sanが単一障害点になっていました。

独自のシェルスクリプトで差分更新し、リリース時間は1分以内に完了していました。コンテナになると、CircleCIでDockerfileのbuildから行うため、デプレイ時間が最低でも4分はかかってしまいます。しかしながら、CI/CDをCircleCIで一本化し、Immutableな運用になります。特にオートスケーリング時のソース更新等の考慮が必要なくなり、運用が極めてシンプルになりました。

CircleCIでのコンテナデプロイについて

このデプロイフローはグループ会社で培ったノウハウとまったく同様で、ステージング環境(Pre)、カナリア環境(Private)ではCircleCIのAPI/Trigger Pipelineを利用しており、好きなタイミングでAppコンテナをデプロイできるようになりました。また、Masterマージすることで本番環境にデプロイされます。それ以外のCIはPHPUnit、Terraform、CDとしてTerraformの差分(path-filtering)があればapplyされるような仕組みになりました。

  • コンテナデプロイ高速化

コンテナによるデプロイ時間は当初は10分以上かかっていました。デプロイ時間短縮策のために、以下の対策を行い、最終的にはデプロイ時間を5分以内まで縮めることができました。

・.dockerignoreによる.gitなどコンテナ内にコピーされないように修正
・Dockerfileで不要なパッケージの棚卸し
・CircleCIのcheckout_shallowを利用、depth: 1、fetch_depth: 1で最後のコミットのみコピー
・remote-docker-layer-cachingを有効化

負荷対策とコンテナコスト削減

コンテナ移行後は、サーバーレスポンスタイムが若干悪くなりました。EC2(c5系インスタンス)で運用していたときとCPU数、メモリ容量は同じ設定にしているのですが、ECSで使われているCPUは現状選択できないため、今後のCPU改善に期待したいです。

オートスケールの台数は最小で3台、最大で20台に設定しました。Fargate Spotも導入し、既存の3台はFargateで、オートスケールはFargate Spotで運用しています。

Fargate Spotは突然コンテナが落ちる可能性を想定し、Datadogでモニタリングしています(ECS Scheduled Taskも同様)。Fargate Spotに関しては個人ブログを参照ください。ちなみに、ECSはArm系CPUもサポートしていますが、現状はCircleCI側が対応していません。対応し次第移行する予定です。

  • php-fpm/www.conf
pm = static
pm.max_children = 150
pm.start_servers = 150
pm.min_spare_servers = 150
pm.max_spare_servers = 150
pm.process_idle_timeout = 10s;
pm.max_requests = 300
request_terminate_timeout = 100
  • ecs-lancers.tf
capacity_provider_strategy {
  base = 3
  capacity_provider = "FARGATE"
  weight = 0
}
capacity_provider_strategy {
  base = 0
  capacity_provider = "FARGATE_SPOT"
  weight = 1
}

[AWS][Terraform]ECS Scheduled TaskのコンテナをFargate Spotに切り替える

移行後のレスポンスについて

EC2のサーバーレスポンスを比較しても数十msほどの差分でした。

  • EC2

  • ECS

    • 移行後のAppコンテナの負荷について

    ランサーズは日中に負荷がピークになるサービスです。EC2時代と比べてCPUパフォーマンスが落ちる可能性が高いことを予想していたため、とりあえず、オートスケールはCPU20%以上で実行されるように余裕を持って設定しました。

    EC2

    ECS

    パフォーマンスに問題がないことを確認しながら、台数、およびCPU負荷をチューニング中です。引き続きモニタリングしていきます。

    まとめ

    入社してから4年で、グループ会社の各サービスでコンテナ運用のノウハウを蓄積し、その集大成をLancersに投入しました。今回のコンテナ移行によって、SREのトイル削減、および、開発部全体のリリースフロー改善による生産性向上が期待できます。今後もフロントエンドのモダン化や、PHP/CakePHPのバージョンアップも進め、より開発メンバーが開発しやすい環境にしていきます。

    最後に、移行プロジェクトを経て1年かかりましたが、個人の力では本家コンテナ化をやりきることができなかったと思います。SREチームの協力があって達成することができました。改めてありがとうございました。まだまだ改善の余地はありますし、ここから新しい仕組みでエンジニア全員が運用できるようになれれば幸いです。次回はMySQL8化についてブログしたいと思います。

    ※dipさんと登壇してきましたので参考に!

    > [Lancers x dip]第1回 Engineer Meetup を開催しました!

    LancersをAmazon Linux2へログ基盤のリニューアルと管理画面をECS/Fargateに移行しました

    adachin|2022年03月29日
    SRE

    Lancers Engineer Blog をご覧のみなさんこんにちは。開発部/技術基盤 SREの安達(@adachin0817)です。最近埼玉で激安マンションを購入しまして、快適な環境でバシバシとフルリモートワークを行っております。今年の目標はより健康的に、ジョギングは毎週続いているので筋トレを取り入れたいと思っております。

    さて、ようやくLancers本家の各サーバーをAmazon Linux2化、管理画面をECS/Fargate化、ログ基盤リニューアルを半年で実現できまして、一旦落ち着くことができました。苦労したところなど振り返ってみようと思います。

    ※去年12月に以下今期SREチームの取り組みについて書きましたが、見ていない方はぜひ一読してもらえると幸いです。

    ・今期SREチームの取り組みについて

    Lancers本体をAmazon Linux2化するにあたって

    ・2018年 ランサーズのサーバー構成

    • 現在 ランサーズのサーバー構成

    さてLancers本家ですが、今まではApp、管理画面(Admin)、BatchサーバーはAmazon Linux1で運用していました。また、デプロイ方法としては独自のシステム(Rails/Jenkins)で運用しています。しかしながらAmazon Linux1は2020年6月3日にセキュリティアップデートの提供が終了し、その後SREチームはLancers AgencyやMENTAなどグループ会社のインフラリプレイスも重なったのと、対応することが厳しかったというのがありました。そこでグループ会社のコンテナやログ基盤のノウハウを確立できたということもありまして、移行を決意しました。また、同時にMySQL8化も取り組みました。

    SREチームの体制について

    SREチームの体制としては前回のブログでも書きましたが、3人で担当しています。今まで私がグループ会社を担当していましたが、波平/@kata_devがジョインしてくれたこともありまして、オンボーディングを行いながら、代わりに運用、改善、新サービスのインフラ構築担当をしてもらっています。そして金澤/@yakitori009と私でLancersを担当していきましたが、波平/@kata_devがジョインしてくれなければまったく進まなかったのもありますし、非常にありがたかったなと感じております。

    Lancersは一部Terraform化をしているが完全ではない

    Lancersのグループ会社は全てTerraform化されていますが、本家は一部しかTerraform化されていません。EC2での構築は独自のawscliで自動化していましたが、それ以外は手動で設定をしていました。一部のTerraform化でも、例えばECSの部分でセキュリティーグループ、サブネット、ターゲットグループIDなどをそのまま指定しているところが多い状況です。この機会に全てをterraform import化をするには工数がかかってしまうため、フェーズ1として以下に絞りました。

    ■フェーズ1
    ・VPC
     ・サブネット
     ・ルートテーブル
     ・インターネットゲートウェイ
     ・Natゲートウェイ
     ・エンドポイント
     ・セキュリティグループ
    ・EC2
    ・ELB

     ・ターゲットグループ
     ・リスナールール
    ・RDS
    ・ACM

    まずは様々なエンジニアメンバーがAWSの操作していたということもあり、不要なセキュリティグループなどの棚卸しから始まりました。ステージングと本番環境合わせて約1ヶ月半かかりましたが、なんとかコード化を実現できました。以下TerraformでのCI/CD周りなどの運用方法はCircleCIユーザコミュニティミートアップでもLTしておりますので参考にしてみてください。

    https://circleci.com/ja/blog/serverless-terraform-release/?fbclid=IwAR1nS_JnehQOgI2Hfks3nZZqqrcKY_tW_JfPxHZtbG8AJEa2bAGtSjyw-x4

    来期はフェーズ2として以下terraform import化を行う予定です。

    ■フェーズ2
    ・Route53
    ・S3

    ・API Gateway
    ・Lambda
    ・CloudFront

    AppサーバーをECS/Fargateに移行するタイミングは今ではない

    コンテナのノウハウがあるのにも関わらず、なぜAmazon Linux2に移行?と思われる方が多いと思います。本家のソースコードは現状肥大化しており、フロントエンドのソースコードは5GB以上もあります。yarn installで30分以上かかってしまうため、コンテナの場合デプロイする時間が膨大にかかってしまうからです。来期はフロントエンドのリポジトリを分割するところから進めていく予定です。

    管理画面(Admin)はECS/Fargateへ移行

    また管理画面(Admin)は現在CakePHP2から4に移行している最中なので、Cake2はEC2で、Cake4はECS/Fargateで動作しています。コンテナのデプロイ方法としては以下、MENTAと同様にCircleCIで行っています。

    • CircleCIによるコンテナデプロイ

    • 社内での感謝

    • MENTAをAWSに移行して振り返る(ECS/Fargate+Laravel編)

    ログサーバーが単一障害点

    ・EC2で運用している分析基盤サーバー(Digdag + Embulk)をECS/Fargateに移行しました

    ランサーズでは分析基盤(Digdag/Embulk/BigQuery/Redash)を運用しており、ログ基盤(Fluentd)を運用していました。もともとAppサーバーにFluentdを常駐させ、ログサーバーにアクセスログやアプリケーションログを転送とS3格納後、Digdag/EmbulkでBigQueryにシンクをしていました。しかし、正常に動作していない状態が度々見受けられ、ブラックボックス化になってしまうことが起きました。そして単一障害点になってしまうという課題がありました。そこで、td-agentのバージョンアップを試みましたが、うまくいかずといったことが重なったため、ログサーバーを廃止して、awslogs/CloudWatch LogsとAmazon Kinesisに乗り換えました。

    awslogs/CloudWatch LogsとAmazon Kinesisを使ったログ基盤を実装

    開発メンバーでも、サーバーにログインせず、手軽にログ調査をできるように、CloudWatch Logs(awslogs)を採用しました。CloudWatch LogsであればAWSでIAMユーザーをreadonlyで作成すれば、エンジニア以外でも調査しやすくなりますし、簡単にCSVでもダウンロードが可能です。また、比較的学習コストが低いのもメリットです。以下管理画面/ECS/Fargateはstdoutせずにコンテナ内にログを配置して、awslogsを直接インストールしています。

    • awslogs.conf
    [general]
    state_file = /var/lib/awslogs/agent-state
    
    ## nginx
    [/lancers/app/nginx/access.log]
    datetime_format = %d/%b/%Y:%H:%M:%S %z
    file = /var/log/nginx/access.log
    buffer_duration = 5000
    log_stream_name = {instance_id}
    initial_position = start_of_file
    log_group_name = /lancers/app/nginx/access.log
    
    [/lancers/app/nginx/error.log]
    datetime_format = %d/%b/%Y:%H:%M:%S %z
    file = /var/log/nginx/error.log
    buffer_duration = 5000
    log_stream_name = {instance_id}
    initial_position = start_of_file
    log_group_name = /lancers/app/nginx/error.log
    
    ## cakephp log
    [/lancers/app/logs/cakephp_log]
    datetime_format = %Y/%m/%d %H:%M:%S
    file = /var/www/lancers/logs/*.log
    buffer_duration = 5000
    log_stream_name = {instance_id}
    initial_position = start_of_file
    log_group_name = /lancers/app/logs/cakephp_log
    
    ~省略~
    • 管理画面 本番Dockerfile
    FROM amazonlinux:2
    
    ENV APP_ROOT /var/www/lancers_admin
    ENV LANG ja_JP.utf8
    ENV LC_ALL ja_JP.utf8
    WORKDIR $APP_ROOT
    
    # Install amazon-linux-extras
    RUN amazon-linux-extras install -y epel
    RUN amazon-linux-extras enable nginx1
    
    RUN yum update -y && \
    yum -y install \
    awslogs \
    gcc \
    gd \
    git \
    glibc-langpack-ja \
    keyutils-libs-devel \
    libXpm \
    libedit-devel \
    libpng \
    libselinux-devel \
    libtiff \
    libtool \
    libverto-devel \
    libwebp \
    libxslt \
    passwd \
    procps \
    python-pip \
    sudo \
    supervisor \
    unzip \
    vim \
    xz-devel \
    yum-utils \
    zlib-devel
    
    RUN amazon-linux-extras install -y \
    nginx1 \
    php7.3
    
    # Install php common
    RUN yum -y install \
    pcre-devel \
    php-cli \
    php-common \
    php-devel \
    php-gd \
    php-intl \
    php-mbstring \
    php-mysqlnd \
    php-pear \
    php-pecl-apcu \
    php-process \
    php-fpm \
    php-opcache \
    php-redis \
    php-soap \
    php-xml \
    php-zip \
    https://github.com/DataDog/dd-trace-php/releases/download/0.70.1/datadog-php-tracer-0.70.1-1.x86_64.rpm \
    && \
    yum clean all
    
    # Setup UTC+9
    RUN unlink /etc/localtime
    RUN ln -s /usr/share/zoneinfo/Japan /etc/localtime
    
    #Create lancers user
    RUN useradd -u1000 lancers \
    && passwd -d lancers
    RUN sed -ri 's/^wheel:x:10:/wheel:x:10:lancers/' /etc/group \
    && sed -ri 's/^# %wheel/lancers/' /etc/sudoers
    
    # awslogs need pip install requests
    RUN pip install --upgrade pip
    RUN pip install requests
    
    # make directory
    RUN mkdir -p $APP_ROOT
    COPY . $APP_ROOT
    
    # php composer.phar install
    RUN php composer.phar install
    
    # copy src
    COPY docker/prd/app-admin/src/environment.php /usr/local/src/environment.php
    
    # nginx
    COPY docker/prd/app-admin/nginx/default.conf /etc/nginx/conf.d/default.conf
    COPY docker/prd/app-admin/nginx/fastcgi.conf /etc/nginx/conf.d/fastcgi.conf
    COPY docker/prd/app-admin/nginx/header.conf.include /etc/nginx/conf.d/header.conf.include
    COPY docker/prd/app-admin/nginx/krgn2.conf /etc/nginx/conf.d/krgn2.conf
    COPY docker/prd/app-admin/nginx/log.conf.http /etc/nginx/conf.d/log.conf.http
    COPY docker/prd/app-admin/nginx/nginx.conf /etc/nginx/nginx.conf
    RUN rm -rf /etc/nginx/conf.d/php-fpm.conf
    
    # awslogs
    RUN mv awscli.conf /etc/awslogs/
    COPY docker/prd/app-admin/awslogs/awslogs.conf /etc/awslogs/awslogs.conf
    COPY docker/prd/app-admin/logrotate/awslogs /etc/logrotate.d/awslogs
    
    # PHP
    COPY docker/prd/app-admin/php/php.ini /etc/php.ini
    COPY docker/prd/app-admin/php/40-apcu.ini /etc/php.d/40-apcu.ini
    COPY docker/prd/app-admin/php-fpm/php-fpm.conf /etc/php-fpm.conf
    COPY docker/prd/app-admin/php-fpm/www.conf /etc/php-fpm.d/www.conf
    
    ## Setting supervisor
    COPY docker/prd/app-admin/supervisor/supervisord.conf /etc/supervisord.conf
    COPY docker/prd/app-admin/supervisor/app.conf /etc/supervisord.d/app.conf
    
    RUN chmod 777 logs
    RUN chmod 777 tmp
    
    RUN touch logs/dummy.log
    
    # Service to run
    CMD ["/usr/bin/supervisord"]

    またログ基盤もあらゆるログが転送されていることもあったので、ポリシーを見直しました。

    NginxアクセスログはS3に集約
     ・CloudWatch Logsには永続
    ・アプリケーションログは分析用途として利用せずにS3には集約しない
     ・CloudWatch Logsには1ヶ月保持
    ・決済ログは3ヶ月保持
    ・ActivityログはAPI経由で実装を変更する

    アクセスログはJSONファイルでS3に格納されていましたが、生ログではなかったので、LTSV形式のままAmazon KinesisでLambdaを使い変換後、S3に集約するようになりました。これによりローカルでもアクセスログを確認したい場合も調査しやすくなりました。また、Amazon Kinesis Data Firehoseの設定でスループット上限を超過する場合、PutRecordBatchの上限である、コールごとに最大500レコード、または4 MBの制限があり、ServiceUnavailableExceptionが出る可能性があるので、Transform側のBuffer sizeとBuffer intervalは1MBと60秒にしてバッファ間隔を短くしています。

    ※Kinesisの設定等は以下個人ブログ書いてますので参考に

    [AWS][Terraform]CloudWatch LogsのアクセスログをLambdaでLTSV形式に変換してKinesisでS3に保存する

    Amazon Kinesisは他のサービスでも連携がしやすく、DatadogではAPMの監視はしていたものの、ログ周りのモニタリングは以前Kibanaを利用していましたが、DatadogのLogsに統一しました。Logsを新たに利用してリアルタイムにNginxのエラーログ、アプリケーションログ、スロークエリを可視化することで可観測性を高められるようになりました。エラーが出た場合もSlack通知するようにしているので、突発的なログも早期発見することが可能になりました。Datadog Logsのプランですが、7日間のみ保持するようにしているのでコストは月に$1000ほど上がりました。毎月10万ほと増えてしまうので、Nginxのログは連携せずに、アプリケーションログのみにしました。これにてログサーバーを撤退することができましたし、ECSにもawslogsをインストールさえすればログの転送も可能なので、コンテナに移行する準備も整うことができました。

    [AWS][Terraform]CloudWatch LogsをKinesis Data Firehose経由でDatadogに転送する

    開発環境の修正

    ランサーズの開発環境は本番環境と同じ構成を再現しております。ステージングや本番環境はAnsibleを利用しているので、開発環境用のAnsibleコンテナを動かして自動化しています。まずAnsibleのメンテナンスを怠っていたということもあるので、Ansibleの修正から行いました。変更点は以下となります。

    ・Docker Imageはamazonlinux2
    ・AnsibleコンテナをAmazon Linux2にリプレイス
    ・serviceからsystemdに
    ・supervisorでプロセス管理

    ・MySQL8化に伴い、mysql80-communityのインストール
    ・不要なパッケージを洗い出し削除
    ・amazon-linux-extrasによるPHPインストール
    ・ログサーバー廃止に伴いtd-agentの削除とawslogsのインストール
    ・管理画面はAnsibleから削除してDockerfileを一からbuildして構築

    開発環境のMySQLコンテナ8化は完了しましたが、STRICT_TRANS_TABLESを有効化しようと試みましたところnot null カラムに null が入る実装があったり、fixtureのクエリがエラー出てしまっていたため、sql_modeは0に変更し、一旦見送ることになりました。MySQL8化については別ブログで詳しく書きたいと思います。1週間ほどQAチームに動作確認をしてもらい、切り替えが完了しました。

    ステージング/カナリア/本番環境の構築

    新たなApp(EC2)、Batch(EC2)、管理画面(ECS)はTerraform/Ansibleを利用して、Amazon Linux2はArmで構築をしました。しかし、DatadogのAPMで利用しているdd-trace-phpがArmをサポートしていないということが判明しましたので、本番環境ではArmを断念することになりました。ただArmサポートは進んでいるようなので継続的に情報を待ち続けたいと思います。

    https://github.com/DataDog/dd-trace-php/issues/1252

    またECS/FargateもArmをサポートしましたが、CircleCIのOrbsがまだ対応していないのでこちらも待ちとなります。(もうすぐリリースされそう)

    https://github.com/CircleCI-Public/aws-ecs-orb/pull/141

    管理画面(Admin)、Batchはメンテナンスせずに事前に切り替えをして、Kinesisでのログ確認を行い、当日メンテナンスの切り替えはAppのみにしました。

    本番移行メンテナンス実施

    本番移行は3/23の2時から実施しました。以下実施したことをまとめました。

    ・リスナールールでメンテナンスモード
     ・旧インスタンスをターゲットグループから外す
     ・新Appサーバーをターゲットグループに追加
     ・KinesisによるログがS3に集約されているか確認
     ・オートスケールによるAMI作り直し
     ・Aurora MySQL5.7をスナップショット
     ・TerraformによるAurora MySQL8を復元

    Aurora MySQL8の不具合

    最後の最後でトラブルが発生しました。Aurora MySQL8の復元が何時間待っても終わらず、メンテナンスの予測時間を大幅に伸びてしまうことが起きました。ステージング環境では問題なくTerraformで復元できましたが、本番環境では想定外のことが起き、結局MySQL5.7への切り戻しを同時に始めたところ、5.7では1時間ほどで復旧することができました。そしてちょうどメンテナンスが終わった午前10時過ぎにAWS側から不具合の連絡が来たので、色々とタイミング等合わなかったと感じております。来月はMySQL8化に挑みたいと思います。

    まとめ

    ランサーズは10年以上運用しているサービスとなります。長い月日を経て本家のインフラ周りを少しずつモダンな形に前進しつつ、安定性も向上してきていると感じています。

    今回、一番達成感を感じるのはログサーバーの廃止です。ログサーバーは私が入社する前から運用していたので、何が何なのかわからない状態でした。マネージドサービスに移行することができて嬉しく思います。

    今回サポートしてくれたQAチームとSREチームでの協力がなければ達成することができませんでしたし、感謝しております。が、まだMySQL8化が残っています。MySQL8ではDatadogのDatabase Monitoringを導入し、来期はAppをECS/Fargateに移行し、独自のDeployシステムを廃止とCircleCIでのリリースに統一をする予定です。その時はまたブログで報告したいと思います。SREチーム募集していますので興味がある方は連絡ください!

    ※本体のAppもコンテナに移行しました

    >Lancers本番環境のコンテナ化が完了しました

    今期SREチームの取り組みについて

    adachin|2021年12月10日
    SRE

    ランサーズ Advent Calendar 2021 10日目の記事です。

    Lancers Engineer Blog をご覧のみなさんこんにちは。開発部/技術基盤 SREの安達(@adachin0817)です。今年2月からダイエットを始めていまして、ジョギングを週2~3日習慣付けられるようになりました。15キロ減量しましたが、まだまだ落とせると日々舞い上がっております。他にもCakePHPで個人開発したり、サーバーサイドやフロントにもチャレンジしています。

    さて、直近のSREチームの大きなイベントとしてはランサーズ本体のインフラを改善している最中でございます。まずは上期SREチームで取り組んだことをエンジニアブログを元にまとめていき、最後には今期(下期)での取り組みについてご紹介します。

    上期取り組んだこと

    ランサーズで利用している開発環境をすべてM1 Macに対応させることができました。素早い対応でしたが、M1とDocker for Macの相性があまり良くなく、フリーズやクラッシュしている方やbuildが遅いなど見受けられますがアップデートを見届けるしかないでしょう。

    ランサーズ以外のサービス3つをAWSに移行完了したということもあり、長年放置されていたTerraformのバージョンアップ(v1.0.0まで)を実施することができました。LTも出来たので知見を共有できましたし、やりきることができました。(思い出すと結構大変だった。。)

    こちらはAmazon Linux 2で運用していましたが、コンテナに無事移行することができました。EFSでの移行後課題がいくつかあり、レスポンスとコストパフォーマンスが悪く、デプロイの複雑さ等懸念点がありました。結局のところリポジトリを一つに集約したことによって従来のコストパフォーマンスやレスポンス等よくなったので、新規のメディアが増えても、導入の工数がかからなくなったのはメリットでした。

    分析で利用しているDigdagサーバーをコンテナに移行し、属人化の廃止とバージョンアップのしやすさによりメンテナンスがしやすくなりました。また、開発環境も新たに用意したので、SRE以外でもテーブルの追加依頼等任せることができたので、大きな変化となりました。

    SREの金澤さん(@yakitori009)がOSSとして公開したSendGrid用のMailモックコンテナですが、弊社で利用ケースの拡大に伴い、様々な要望が出てきたので自作しました。メールの誤送信は、最も発生件数の多いセキュリティインシデントの1つでもありますので、対応されていない方がいましたらぜひ利用してみてください。

    Lancers Assistant、Lancers Agency、MENTAの開発、Stg環境における本番DBマスキングリストアの仕組みを実装しました。実装内容がデータベースを触るということもあってかなりシビアでしたが、開発メンバーがより本番環境に近い環境で作業できるようになったのと、個人情報を取り扱っていないのでインシデントも起きない状態になったのはいい取り組みとなりました。

    • その他社内ツールをECS/Fargate移行し安定化稼働
    • Datadogでのアプリケーションログ、エラーログの可視化
    • E2EテストによるUbuntuVNCデスクトップの構築

    以上が上期取り組んだ内容になりましたが、グループ会社でのコンテナ移行の知見を生かしてランサーズで利用しているサーバーを少しずつコンテナへ移行出来たのが大半でした。またSRE以外にも開発メンバーが本番でのコンテナ運用もできるようになったのは良い取り組みでしょう。

    次は今期(下期)での取り組みについてご紹介します。

    現在のSREチームとオンボーディングについて

    10月から新たに1名SREとして波平(@kata_dev)さんがジョインしてくれまして、計3名となりました!SREを採用するのはどの会社も苦労していると思いますが、いい人に出会えて非常に感動でございます!

    また、私は入社して来年で4年目を迎えるのですが覚えることが多く、すぐに現場に慣れてもらいたいという気持ちがあったので、オンボーディング資料を今回初めて作成しました。これを見れば1発でどんな業務や課題があるのかなどイメージが湧き、今後のやりたいことなど議論等活発になります。

    まずは入社日に各サービスの構成等把握してもらい、2~3週間はわからないところがあれば気楽にSlackのハドルを利用してペアプロしながら業務を進めていきました。また業務で「これ改善したらもっと楽になりますが、どうでしょうか?」など本人の意見を尊重し合い、心理的安全性を保つようにチーム全員でコミュニケーションし合うことを努めています。オンボーディングについてはカジュアル面談等で見てみたい!という方がいれば気楽に連絡ください。

    • 現在のチーム構成

    ・@yakitori009: ランサーズ本体 PHPバージョンアップ
    ・@adachin0817: ランサーズ本体 インフラ改善/@kata_devさんフォロー
    ・@kata_dev: 分析基盤、Lancers Agency、Lancers Assistant、MENTA、新サービスインフラ

    新チームになって2ヶ月目ということもあり、担当するインフラも変化していきました。SREチームとしてさらに改善したい部分が見えてきてのと、よくサービス1人に複数担当で「あとはよろしく!」などは基本しません。3名で全プロジェクトのインフラを把握し、属人化がないように回すようにしています。

    個人的には私がようやくLancers本体を改善できる立ち位置になり、波平さん(@kata_dev)には他プロジェクトを担当してもらっているので非常に助かっております。一見上記の図を見ると私がラーメン食べて仕事するように見えますが気のせいです。(波平さん(@kata_dev)のアイコンです)

    ちなみに波平さん(@kata_dev)と1on1していますが「今までの技術力にプラスしてさらに経験が得られるので、毎日が勉強です!」とのことでした。良かった!

    今期(下期)SREチームの取り組みについて

    • Lancers本体をAmazon Linux 2(Arm)化とログ基盤リニューアル

    Lancers本体は現在Amazon Linux 1で運用しています。開発環境はDockerを利用していますが、Ansibleのメンテナンスを怠っていたということもあり、Docker ImageのAmazon Linux 1では動作しなくなってしまったので、この機会にAmazon Linux 2(Arm)への移行を決意しました。

    ■移行の流れ
    ・Amazon Linux 1 → Amazon Linux 2(Arm)移行
    ・パフォーマンス計測
    ・ECS/Fargate(Arm)移行

    また、wkhtmltopdfによるPDFへ変換するツールを利用しているためArm対応していなさそうと懸念していましたが、ステージング環境やdevx(社内向け開発環境)は無事にArmへ移行することができました。ちなみにAmazon Linux 2022も発表されましたが、タイミングが悪い!

    さらに分析としてログ基盤(サーバー)を運用していますが、ブラックボックス化してしまっているので、分析で利用しているログを全て見直して、以前までFluentdを利用していましたが、バージョンアップを試みたところ厳しかったので、廃止してAmazon Kinesisに移行することにしました。

    この運用により開発メンバーはわざわざログサーバーにSSHすることなく、CloudWatch Logsでログの確認やCSVでのダウンロードができるので、ブラックボックス化しにくくなります。またLambdaでLTSV形式にすれば生ログで出力されて、分析基盤で利用しているDigdag/Embulkにも相性は良くなります。さらにDatadogでのLogs機能とKinesisを連携すれば突発的なエラーも早期発見することができます。ログ基盤をリニューアルすることでECS/Fargateへの移行もできる準備も整うことができました。ECSのDocker ImageはAmazon Linux 2を利用する予定です。

    • ECS/Fargate Arm移行

    https://aws.amazon.com/jp/about-aws/whats-new/2021/11/aws-fargate-amazon-ecs-aws-graviton2-processors/

    https://aws.amazon.com/jp/blogs/aws/announcing-aws-graviton2-support-for-aws-fargate-get-up-to-40-better-price-performance-for-your-serverless-containers/

    待望のECS/FargateでもArmを利用できるようになりました。パフォーマンスが上がるのと同時にコストダウン可能なので全サービス適用する予定です。Datadogでどのくらい変化するか計測もしていきたいと思います。Terraformはタスク定義でArm指定が可能になりましたが、CircleCiでのコンテナbuildでOrbsが対応していないので継続的に情報を確認していきます。

    • MySQL8.0に移行

     

    https://aws.amazon.com/jp/about-aws/whats-new/2021/11/amazon-aurora-mysql-8-0/

    こちらも待望のRDS AuroraがMySQL8.0に対応しました。MySQL8は5.7よりパフォーマンスが2倍高速になったのと様々な新機能が追加されたので、まずは開発環境から移行を進めていきたいと思います。台数がかなり多いので、事前の検証と労力をかなり奪われそうなのと慎重に進めていきます。

    ちなみにLancers Creativeのステージング環境はAurora MySQL8.0にしましたが、5.7から8.0へのアップグレードはできないので、スナップショットあるいはmysql dumpしてrestoreするしかないので気をつけましょう。Terraformからのスナップショットはかなり神経使いますね。。

    • Digdagをv0.10.3にバージョンアップ

    こちらDigdagのみバージョンアップの対応が完了しました。ちなみにJava11 +Graaljsに対応してから安定性が増えるとのことでしたが、Embulk自体がJava11に対応していないので、様子見となります。Embulkのプラグインはまだバージョンアップしていないので、動作確認等対応していきます。また、最近テーブルのレコード数が増加しているので、差分更新するよう全てのテーブルを洗い出していこうと思います。

    • Redashをv10.0.0にバージョンアップとECS/Fargateに移行

    現在RedashサーバーはEC2/Ubuntu上のDockerで稼働しています。こちらもECSに移行することでより運用上と安定性が担保されるので、まずは本番環境と差異が生まれないように専用の開発環境の構築をしました。またRedashもバージョンアップが完了しましたが、V10.1.0でBigQueryのスキーマロードが60倍速度向上となりますので対応していきたいと思います。残りECS/Fargateへ移行のみとなりますので期待しましょう。

    その他取り組んでいることは以下となります。

    ・Lancers Agency/PROsheet PHP7バージョンアップ→完了
    ・マイページリニューアルと分離
    ・Deployサーバーを廃止してreleaseをCircleCIに移行

     ・Terraform→完了
     ・Lambda→完了
    ・Session Manager専用Ansible開発環境の構築

    ・Lancers本体Terraform化
    ・フロントエンドを分割
    ・BigQueryからAmazon Athenaに移行
    ・ランサーズ CakePHP2→CakePHP4 バージョンアップ
    ・ランサーズ 管理画面 CakePHP4 実装

     ・ECS/Fargate移行
    ・新サービスのインフラ構築(Lancers Digital Acadmy)
    ・OneLoginでアカウント周り連携
    ・簡単なシェルスクリプトをGoに置き換え

    まとめ

    今期は主にランサーズ本体の改善を行います。ランサーズは10年以上運用しているサービスでもありますし、課題が山積みです。グループ会社で培ってきたコンテナの活用をフルに発揮することができるので、ようやくこの時が来たか..といった心境で技術スタックもモダンに近づいています。移行できた際にはメンバー全員が喜ぶことでしょう。たくさんの壁がありますが、全てやりきっていきたいと思います。

    ちなみに私の来年の目標としてはインフラ以外にもサーバーサイド(CakePHP)も取り組んで行く予定なので気合入れて頑張っております。明日はMENTAで教えていた大平@koki0527くんです!ぜひお楽しみに!

    ※追記

    LancersをAmazon Linux2へログ基盤のリニューアルと管理画面をECS/Fargateに移行しました

    ~CircleCIでTerraformリリースのサーバーレス化~ インフラの独自リリースを継続的リリースへ

    adachin|2021年10月25日
    SRE

    SREチームの安達(@adachin0817)です。以前エンジニアブログにてこんなことを言っていたのを皆様覚えておりますでしょうか。

    それ以外のプロジェクトであるLancers CreativeProsheetLancers AgentMENTAはECS/Fargateに移行しているので、すべてTerraformでコード管理しています。また、CircleCIでTerraform CI(validate,plan)を実行しているということもあり、今後terraform applyもCircleCIで行う予定なので、Deployサーバーでの運用は廃止していく予定です。

    • 今までのTerraform管理方法

    そうです。以下のようにTerraformでの運用について課題がありました。

    • わざわざサーバーを用意したくないのと運用コストが上がる
    • Deployサーバーで管理するのはやめましょうといってもローカルPCで反映したくない
    • Terraformの適用(apply)はCircleCIのみでリリース(サーバーレス)ができればベスト

    ちなみにDeployサーバーというのはランサーズで利用している各サーバー側のデプロイ(Ansible)やアプリケーションのデプロイと様々な用途として利用しています。またランサーズで運用しているEC2はAmazon Linux2のarmに移行後、性能評価を比較して、ECS/Fargateに移行する予定です。なのでCircleCIのみでのリリースに統一となると、Deployサーバーは廃止する方向になります。そこで今回は上記をどのように改善していったのかブログしていきたいと思います。

    背景

    • Deployサーバーで適用してしまうとブランチの切り替えを忘れて他のリリースに影響が出る
    • Deployサーバーに依存してしまうので複数人でのTerraform開発スピードが遅れる
    • Terraformのバージョンアップ対応中でも急に依頼が来て、バージョンアップの切り替えをしなければならない
    • ECS Scheduled Taskによる頻繁に開発メンバーからの更新とそれに伴いリリースの際SREチームの工数を取ってしまう

    上記の4つが課題となります。まず問題なのが、Deployサーバーに依存してしまうということです。もちろんTerraformでの開発はterraform planだけでは判断できないことも多く、コード書く際にその場で適用し、デバッグをして修正といったことも多々あります。そこでローカルPCで管理してしまうとクレデンシャルを設定しなければならなく、非常に強い権限が必要なため、万が一、漏洩となることも考えると、なるべくローカルにはクレデンシャルを設置したくないという思いがありました。またECS Scheduled TaskによるバッチはTerraform化しているので、頻繁に開発メンバーからはレビューとマージ後にSREチームの誰かが適用しなければなりません。そういった運用工数も取られてしまうので自動化できればと考えておりました。そして考慮した結果、Terraform専用の開発環境を構築し、CircleCIで継続的にリリースをするという方向になりました。

    Terraform開発環境

    • ディレクトリ構成
    tree .
    .
    ├── Dockerfile
    ├── README.md
    ├── docker-compose.yml
    ├── root
    │   └── dev-credentials
    └── setting_dev.sh
    
    • docker-compose.yml
    version: '3'
    services:
      app:
        container_name: menta-terraform-dev
        build:
          context: .
          dockerfile: Dockerfile
        image: menta-terraform-dev
        volumes:
          - ~/hoge:/var/www/hoge/:delegated
        tty: true
    
    • Dockerfile
    FROM hashicorp/terraform:1.0.5
    ENV APP_ROOT /var/www/hoge
    WORKDIR $APP_ROOT
    
    # Setup UTC+9
    RUN apk --update add tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    apk del tzdata && \
    rm -rf /var/cache/apk/*
    
    # install packages
    RUN apk update && \
    apk upgrade && \
    apk add --update --no-cache \
    bash \
    coreutils \
    vim \
    python3 \
    py3-pip
    
    # install aws-cli
    RUN pip3 install --upgrade pip
    RUN pip3 install awscli && rm -rf /var/cache/apk/*
    
    # copy empty credentials
    RUN mkdir /root/.aws
    COPY root/dev-credentials /root/.aws/credentials
    COPY root/.bashrc /root/.bashrc
    
    ENTRYPOINT ["/bin/bash"]
    
    • setting_dev.sh
    #!/bin/bash
    
    aws --profile=terraform-kms s3 cp s3://hoge/dev.key /root/.aws/
    
    aws --profile=hoge kms decrypt \
    --ciphertext-blob fileb:///root/.aws/dev.key \
    --output text \
    --query Plaintext \
    | base64 --decode > /root/.aws/newcredentials
    
    mv /root/.aws/newcredentials /root/.aws/credentials

    まず各プロジェクトにTerraformの開発環境を準備しました。TerraformはDocker hub公式でコンテナが用意されているので現状のv1.0.5を指定し、API経由ということもあってDockerでのPortも特に開放する必要がありません。

    そしてクレデンシャルをどう管理するのかと考慮したところ、AWS KMSを利用してKMS専用のクレデンシャルから暗号化されたkeyをコピーし、新たにTerraformで利用するクレデンシャルを上書きで復号するようにシェルスクリプトで作成しました。コンテナ内にクレデンシャルを配置することで、セキュリティレベルが二重になり、リスクも軽減されるようになります。もちろんdocker-compose downをすればクレデンシャルは初期化されるので漏洩してしまう恐れも減ります。この開発環境の構築により、各々ローカルPCでTerraformのデバッグやバージョンアップがしやすい環境となりました。次はCircleCIでのリリースフローを説明していきましょう。

    CircleCIでのリリースフロー(MENTA)

    • .circleci/config.yml
    version: 2.1
    
    setup: true
    
    orbs:
      aws-ecr: circleci/aws-ecr@7.0.0
      aws-ecs: circleci/aws-ecs@2.2.0
      aws-cli: circleci/aws-cli@2.0.0
      path-filtering: circleci/path-filtering@0.0.3
      slack: circleci/slack@3.4.2
    
    parameters:
    deploy_app_stg:
    type: boolean
    default: false
    deploy_app_private:
    type: boolean
    default: false
    
    ~省略~
    
    workflows:
      version: 2
      ci:
        jobs:
          - laravel_test
          - stg_terraform_fmt_validate
          - stg_terraform_plan:
              requires:
                - stg_terraform_fmt_validate
          - prd_terraform_fmt_validate
          - prd_terraform_plan:
              requires:
          - prd_terraform_fmt_validate
          - path-filtering/filter:
              base-revision: origin/master
              config-path: .circleci/deploy.yml
              mapping: |
                terraform/stg/.* stg_terraform_apply true
                terraform/prd/.* prd_terraform_apply true

    MENTAの例ですが、TerraformのCIはvalidate,planをpush時に実行しています。以前masterマージでterraform applyという運用をしていましたが、SREチームがTerraformのデバッグをして、そのままapplyをしたままマージせずに他のプルリクをマージすることで元に戻ってしまうということがあり、更に他の部分で差分が出ているのに気づかず、applyされて変更されてしまったりと、危うくインシデントになりそうだったのでapplyの部分は一旦断念しました。またCircleCIのAPIを利用してシェルでリリースをするという方法も考えましたが、権限的に開発メンバーが全員リリース出来てしまうという懸念点もありました。

    そこで解決してくれるのが、CircleCIのpath-filteringというOrbsを利用して、terraformディレクトリにmasterとの差分があればapplyされるように実装しました。

    https://circleci.com/developer/orbs/orb/circleci/path-filtering

    path-filteringはsetup: true が付いていることによって複数のworkflowが無効化になります。その場合デプロイ系は全て別ymlに移行(deploy.yml)して、single workflowに変更する必要があります。single workflowになったことでCIが20秒ほど早くなりました。Terraform CIに関しては私の個人ブログを参考にしてみてください。

    [AWS]CircleCIでTerraformのCI/CD環境を実装してみた

    • .circleci/deploy.yml
    version: 2.1
    orbs:
      aws-ecr: circleci/aws-ecr@7.0.0
      aws-ecs: circleci/aws-ecs@2.2.0
      aws-cli: circleci/aws-cli@2.0.0
      slack: circleci/slack@3.4.2
    
    parameters:
    stg_terraform_apply:
    type: boolean
    default: false
    prd_terraform_apply:
    type: boolean
    default: false
    deploy_app_stg:
    type: boolean
    default: false
    deploy_app_private:
    type: boolean
    default: false
    
    ~省略~
    jobs:
      check_terraform_difference:
        <<: *default_config
        steps:
          - run:
             name: install package
             command: |
               apk add bash curl jq
          - run: echo "No terraform difference ok !!"
          - slack/status:
              fail_only: true
              mentions: 'here'
              failure_message: 'Error check terraform difference 🚨 \n :innocent: ${CIRCLE_USERNAME} :branch: ${CIRCLE_BRANCH}'
              webhook: ${SLACK_WEBHOOK}
          - slack/notify:
              title: 👍
              color: '#42f486'
              message: 'No terraform difference OK !! ✨ \n :grin: ${CIRCLE_USERNAME} :branch: ${CIRCLE_BRANCH}'
              webhook: ${SLACK_WEBHOOK}
    ~省略~
    
    workflows:
      version: 2.1
      check_terraform_difference:
        jobs:
         - check_terraform_difference
        when:
          and:
            - not: << pipeline.parameters.stg_terraform_apply >>
            - not: << pipeline.parameters.prd_terraform_apply >>
      stg_terraform_apply:
        when: << pipeline.parameters.stg_terraform_apply >>
        jobs:
          - stg_terraform_apply:
              filters:
                branches:
                  only: master
      prd_terraform_apply:
        when: << pipeline.parameters.prd_terraform_apply >>
        jobs:
          - prd_terraform_apply:
              filters:
                branches:
                  only: master
      deploy_app_stg:
        when: << pipeline.parameters.deploy_app_stg >>
        jobs:
          - deploy_app_stg
      deploy_app_private:
        when: << pipeline.parameters.deploy_app_private >>
        jobs:
          - deploy_app_private
      deploy_app_prd:
        jobs:
          - deploy_app_prd:
              filters:
                branches:
                  only: master

    新しいjobとしてcheck_terraform_differenceを作りましたが、CircleCIの仕様で差分がないときにmappingに検知されず、全てのパラメータはdefaultのfalseが返ってきてしまうという動作になってしまうため、実行するworkflowがない場合はエラーとなります。なのでechoでtrueを返すように作りました。ここはCircleCIさんぜひデフォでtrueを返すようにしてほしい!以下動作確認ですが、terraformディレクトリに差分がなければcheck_terraform_differenceが動作してそのままCIが動き、差分があればterraform applyされるようになりました。

    • 動作確認

    まとめ

    これでようやくDeployサーバーからの管理を脱却することができ、開発環境でのデバッグとTerraformのリリースを安全にサーバーレスでの実現や改善することができました。またLancers本体も完全Terraform化するための準備も整うことができました。CircleCIのpath-filteringはモノレポでもパス単位で自動リリースすることができるので、Lambdaなどにも応用ができることでしょう。またpath-filteringの今後のバージョンにも期待です!また課題等出てきたらブログしようと思います。

    SRE募集しているので気になる方はぜひTwitterでDMお待ちしています!

    ※CircleCIユーザーコミュニティでLTしてきました!

    https://circleci.com/ja/blog/serverless-terraform-release/

     

    開発/Stg環境のための本番DBマスキングと継続的リストアの仕組みを作りました

    adachin|2021年09月06日
    SRE

    SREチームの安達(@adachin0817)です。今回はMENTALancers CreativeLancers Agencyマスキングした本番環境のデータをStgや開発環境のMySQLコンテナへ毎週リストアする仕組みを実装しました。実際にここらへんは運用をしていく中で一苦労されている方も多いのではないでしょうか。それではまず背景と、実装するに当たっての活動含めてご紹介できればと思います。

    背景

    今回はMENTAを例にしています。各サービスの開発環境はDockerを利用しており、本番とStg環境はTerraformで管理しています。カラム追加ではマイグレーションを実行することでサンプルのスキーマファイルを投入して開発をしているのですが、たまに開発環境で動いていたソースがStgや本番で動かないといったことで開発効率が下がることが見受けられます。開発メンバーにとってはより本番環境に近い環境でテストをすることがベストです。また、各AWSのアカウントは本番環境とStg環境を分離しています。もちろん気楽にRDSのクローン機能を利用して即座に同じDBを構築することができないですし、クローンだと本番データが入っているためインシデントが起こる可能性があります。また、運用上再作成することも踏まえると時間もかかるのと工数もかかります。そこでちょっとした工夫と設計を考える必要があり、ランサーズ本家と同様に継続的にリストアできる仕組みを今回実装しました。

    ※以下スペースマーケットさんと以前LT大会をした際にMENTAのAWS移行について登壇したのでぜひ参考にしてください。

    [スペースマーケット×ランサーズ][SRE]第2回目のコラボイベント開催しました!

    auto-masking

    ・環境

    • EC2(ディスク100GB)
    • Local DB(MySQL 8.0.26)
    • RDS Aurora
    • S3

    ・my.cnf

    [mysqld]
    
    disable_log_bin
    datadir=/var/lib/mysql
    socket=/var/lib/mysql/mysql.sock
    log-error=/var/log/mysqld.log
    pid-file=/var/run/mysqld/mysqld.pid
    
    character_set_server=utf8mb4
    collation-server=utf8mb4_bin
    default-authentication-plugin = mysql_native_password
    default-time-zone='+9:00'
    
    skip-grant-tables
    innodb_file_per_table=ON
    innodb_buffer_pool_size=4G
    innodb_log_file_size=1G
    join_buffer_size=256K
    read_buffer_size=1M
    read_rnd_buffer_size=2M
    sort_buffer_size=4M
    max_heap_table_size=16M
    tmp_table_size=16M
    thread_cache_size=100
    wait_timeout = 86400
    max_allowed_packet = 1G
    innodb_buffer_pool_size = 1G
    max_allowed_packet = 16M
    innodb_strict_mode = 0
    long_query_time = 5
    
    sql_mode = 0
    
    [mysql]
    default-character-set=utf8mb4

    今回本番DBマスキングからStgへリストアするまでauto-maskingと名付けました。サーバー(devops)はコンテナではなく、EC2を選択しました。初期はECS/Fargateで挑戦しようと試みていましたが、現時点のエフェメラルストレージは200GBのみ対応しており、今後データが増える見込みであればストレージの拡張はEFSを利用するしかありません。もちろんEFSはコストが高いのと、エフェメラルストレージの変更はTerraformではサポートされておらず、aws-cliを利用して変更するしかありません。と考慮するとEC2で運用するのがふさわしいと考えました。

    また、devopsサーバーのローカルDBはマスキング用のSQLを流すために必要であり、先のことも考えてMySQL 8.0.26を利用しました。各サービスのmy.cnfはAnsibleで共通にしています。マスキング用のSQLは開発メンバーとやり取りさせてもらいながら、個人情報を含むものはUPDATEして全て書き換えるように作り込みをしました。DB Dump先ですが、RDSのRead ReplicaからDumpをすることによりクラスターDBに影響がなくなります。ポイントとしては–skip-lock-tables –single-transactionのオプションを追加してREAD LOCKをさせないようにしましょう。ちなみにMySQL 8.0から8.0以前のバージョンのみ、Dump時に–skip-column-statisticsを指定することでANALYZE TABLE文の自動生成をなくすことでエラーの回避ができます。 これはMySQL 8.0以降ではオプティマイザがヒストグラム統計というものを考慮するようになったからになります。またMySQL 8.0では認証プラグインを必ずmysql_native_passwordに変更し、sql_modeでは特に利用していないので0にしました。

    それでは実際のシェルスクリプトのコードと流れを記載しておきます。

    ・prd-cron

    $ crontab -l
    #Ansible: cron prd-masking.sh
    0 0 * * 6 /var/www/hoge/scripts/auto-masking/prd-masking.sh > /var/log/auto-masking/auto-masking.log 2>&1

    ・prd-masking.sh

    #!/bin/bash
    
    set -eu
    
    $("/home/hoge/.common/secrets.sh")
    
    DB=hoge
    MIGRATIONFILE01=masking.sql
    
    # git pull
    git -C /var/www/hoge/scripts/auto-masking pull
    rm -rf ~/backup/*
    
    # dump RDS prd
    echo "dump prd"
    mysqldump -v --skip-column-statistics --skip-lock-tables --single-transaction --default-character-set=utf8mb4 --set-gtid-purged=OFF -h "${PROD_HOST}" -u "${PROD_USER}" -p"${PROD_PWD}" "${DB}" | gzip > ~/backup/prd-bk.gz
    
    # local db restore
    echo "local drop/create db"
    mysql -h localhost -u root -e "drop database hoge;"
    mysql -h localhost -u root -e "create database hoge;"
    
    echo "local restore db"
    zcat ~/backup/prd-bk.gz | mysql -h localhost -u root "${DB}" -f
    
    # masking local db
    echo "masking local db"
    mysql -h localhost -u root "${DB}" < /var/www/hoge/masking/${MIGRATIONFILE01}
    
    # dump local masking db
    echo "local dump db"
    mysqldump --skip-column-statistics --skip-lock-tables --single-transaction --default-character-set=utf8mb4 --routines=0 --triggers=0 --events=0 --set-gtid-purged=OFF -h localhost -u root "${DB}" | gzip > ~/masking_db/mask-bk.gz
    
    # s3 copy s3
    echo "copy masking db s3"
    aws s3 cp ~/masking_db/mask-bk.gz s3://xxxxxxxxxxxxxxxxxxxx/mysql/masking/
    
    echo "delete dir backup masking_db"
    rm -rf ~/backup/*
    rm -rf ~/masking_db/*
    
    ## slack notify
    /var/www/hoge/scripts/auto-masking/post_slack.sh "Prd masking OK. Please wait restore Stg."
    • masking.sql
    use hoge;
    
    -- clients
    UPDATE clients SET
    name = concat('テスト会社', id),
    name_kana = concat('テストカイシャ', id),
    address = concat('address', id),
    tel = '00000000000';
    
    -- users
    UPDATE users SET
    last_name = concat('山田'),
    first_name = concat('太郎', id),
    last_name_kana = concat('ヤマダ'),
    first_name_kana = concat('タロウ', id),
    tel = '00000000000',
    email = concat('dev', id, '@example.com');
    • 毎週土曜日深夜0時に本番 devopsサーバーにてRDS(Read)からDump
    • 本番 devopsサーバーのローカルDBにリストア
    • 本番 devopsサーバーのローカルDBに対してマスキングSQLを適用
    • 本番 devopsサーバーのローカルDBをDump
    • StgのS3バケットにDumpファイルをコピー

    ・stg-cron

    $ crontab -l
    #Ansible: cron stg-masking-restore.sh
    0 5 * * 6 /var/www/hoge/scripts/auto-masking/stg-masking-restore.sh > /var/log/auto-masking/auto-masking.log 2>&1

    ・stg-masking-restore.sh

    #!/bin/bash
    
    set -eu
    
    $("/home/hoge/.common/secrets.sh")
    DB=hoge
    
    # git pull
    git -C /var/www/hoge/scripts/auto-masking pull
    rm -rf ~/masking_db/*
    
    # s3 local copy s3
    echo "copy masking db s3 local"
    aws s3 cp s3://xxxxxxxxxxxxx/mysql/masking/mask-bk.gz ~/masking_db/
    
    # restore masking stg RDS
    echo "stg drop/create db"
    mysql -h "${STG_HOST}" -u "${STG_USER}" -p"${STG_PWD}" -e "drop database hoge;"
    mysql -h "${STG_HOST}" -u "${STG_USER}" -p"${STG_PWD}" -e "create database hoge;"
    
    echo "stg restore db"
    zcat ~/masking_db/mask-bk.gz | mysql -h "${STG_HOST}" -u "${STG_USER}" -p"${STG_PWD}" "${DB}" -f
    
    echo "delete dir masking_db"
    rm -rf ~/masking_db/*
    
    ## slack notify
    /var/www/hoge/scripts/auto-masking/post_slack.sh "Stg restore OK"
    • 毎週土曜日5時にStg devopsサーバーからS3バケットに本番DBマスキングしたDumpファイルをコピー
    • Stg devopsサーバーから本番DBマスキングしたDumpファイルをStg用RDSにリストア

    ・Slack nofity

    auto-renew-mysql-container

    ・環境

    • AWS CodeBuild
    • CloudWatch Events Rule
    • AWS ECR
    • docker hub
    • GitHub

    次は開発環境のMySQLコンテナをリストアして自動化をします。(auto-renew-mysql-container) ここでは開発環境で利用しているMySQLのDockerfileを使いたいため、GitHubのリポジトリを連携したく、AWS CodeBuildとCloudWatch Events Ruleを利用して実装しました。毎週土曜日のAM9:00にマスキングされた本番DBのデータをMySQLコンテナにbuildしてリストアを行い、ECRにPushします。ちなみにCodeBuild上のインスタンスからMySQLコンテナを何度もpullしてしまうと以下Rate Limitsで制限がかかる仕様となりました。必ずdocker loginをしましょう。

    https://www.docker.com/increase-rate-limits

    また、CodebuildはTerraform化をしておらず、CloudWatch Events RuleのみTerraform化を行いました。以下buildspec.ymlとTerraformのコードを記載しておきます。

    • buildspec.yml
    version: 0.2
    
    phases:
      install:
        runtime-versions:
          docker: 18
        commands:
          - yum update -y && yum install -y curl
      pre_build:
        commands:
          - echo Login to AWS ECR...
          - aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin xxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com
          - echo Logging in to Docker Hub...
          - echo $DOCKERHUB_PASS | docker login -u $DOCKERHUB_USER --password-stdin
          - cp -rp docker/dev/mysql/ /tmp/
          - cd docker/dev/mysql
          - docker build --no-cache -t mysql-dev:$IMAGE_TAG .
          - docker run -v /tmp:/tmp --name mysql-dev -d mysql-dev:$IMAGE_TAG
      build:
        commands:
          - hostname -i
          - docker ps
          - while true; do docker exec -i mysql-dev mysqladmin -u root ping || (sleep 10; false) && break; done
          - for i in $(find /tmp/mysql/ -name "*.sql"); do docker exec -i mysql-dev mysql -u root < $i ; done
          - docker exec -i mysql-dev mysql -u root -e 'create database hoge'
          - docker exec -i mysql-dev mysql -u root -e 'show databases'
          - aws s3 cp s3://xxxxxxxxxxxxx/mysql/masking/mask-bk.gz /tmp/
          - gunzip /tmp/mask-bk.gz
          - docker exec -i mysql-dev mysql -u root hoge < /tmp/mask-bk
      post_build:
        commands:
          - docker commit mysql-dev $IMAGE_REPO_NAME:$IMAGE_TAG
          - echo Push image to Amazon ECR...
          - docker push $IMAGE_REPO_NAME:$IMAGE_TAG
          - cd ../../../
          - bash ./scripts/auto-masking/post_slack.sh "Dev MySQL container restore OK"
    • codebuild.json
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "codebuild:StartBuild"
                ],
                "Resource": [
                    "arn:aws:codebuild:ap-northeast-1:xxxxxxxxxx:project/auto-renew-mysql-container"
                ]
            }
        ]
    }
    • terraform/stg/iam.tf
    ## cloudwatch_events_codebuild_role
    resource "aws_iam_role" "cloudwatch_events_codebuild_role" {
      name = "cloudwatch_events_codebuild_role"
      assume_role_policy = file("files/assume_role_policy/ecs-scheduled-tasks.json")
    }
    
    resource "aws_iam_policy" "cloudwatch_events_codebuild_policy" {
      name = "cloudwatch_events_codebuild_policy"
      description = "cloudwatch_events_codebuild_policy"
      policy = file("files/assume_role_policy/codebuild.json")
    }
    
    resource "aws_iam_role_policy_attachment" "cloudwatch_events_codebuild_attach" {
      role = aws_iam_role.cloudwatch_events_codebuild_role.name
      policy_arn = aws_iam_policy.cloudwatch_events_codebuild_policy.arn
    }
    • terraform/stg/cloudwatch_event_target.tf
    # auto_renew_mysql_container
    resource "aws_cloudwatch_event_rule" "auto_renew_mysql_container" {
      name = "auto_renew_mysql_container"
      description = "auto_renew_mysql_container"
      schedule_expression = "cron(0 0 ? * SAT *)"
      is_enabled = "true"
    }
    
    resource "aws_cloudwatch_event_target" "auto_renew_mysql_container" {
      target_id = "auto_renew_mysql_container"
      arn = "arn:aws:codebuild:ap-northeast-1:xxxxxx:project/auto-renew-mysql-container"
      rule = aws_cloudwatch_event_rule.auto_renew_mysql_container.name
      role_arn = aws_iam_role.cloudwatch_events_codebuild_role.arn
    }
    • 作成されたロールにAmazonEC2ContainerRegistryPowerUser、S3の権限を付与
    • Slack nofity

    ※AWS Chatbotではfailのみ通知するように実装しています。

    開発環境 MySQLコンテナ更新スクリプト

    • docker-update.sh
    #!/bin/bash
    
    ## update MySQL container
    aws ecr get-login-password --profile ecr | docker login --username AWS --password-stdin xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com
    cd ~/www/hoge/docker/dev/
    docker-compose pull && docker image prune -f && docker-compose up -d
    docker exec -it menta-app php artisan migrate
    
    ## update App container
    docker stop menta-app-6 && docker rm menta-app
    docker rmi menta-app
    docker image prune -f && docker-compose up -d

    最後に開発環境のMySQLコンテナは毎週各自更新する必要があるので、専用のシェルスクリプトを作成することによって手動での漏れを減らしました。

    これにて実装は完了となります。

    まとめ

    実装内容がとにかくシビアで、疲労困憊でございます。たくさんの時間を使ってテストをしたおかげで継続的にリストアできる仕組みを作れたと感じています。開発メンバーからは非常にありがたいというお言葉もいただけたので、より良い環境を提供できたと思っています。また、現在本番DBの容量はまだまだ少ないので、今後データが増加していく場合、開発環境のMySQLコンテナも重くなってしまうので、データを一部分削る専用のTRUNCATE SQLを作成する必要があります。他にいいやり方あれば教えて下さい!

    EC2で運用している分析基盤(Digdag + Embulk)をECS/Fargateに移行しました

    adachin|2021年06月23日
    SRE

    SREチームの安達(@adachin0817)です。最近ではランサーズ本家のインフラをコンテナに移行しまくっております。今回ランサーズとMENTAで運用しているEC2/分析基盤サーバー(Digdag + Embulk)をECS/Fargateに移行完了しました。では早速概要と苦労した点、今後の展望などを振り返っていきたいと思います。

    分析基盤の紹介

    > ランサーズの分析基盤(capybara)と運用について紹介
    > MENTAをAWSに移行しました

    ちなみに私が入社して3年経つのですが、運用して変わったことは3年前よりデータの量が膨大になっていることと、現在、社内の分析チームにとって欠かせないシステムとなっております。その中でDigdagによるスケジューラーとEmbulkによるマルチソースバルクデータローダーである分析基盤専用のEC2サーバーがあり、毎日夜中にデータをBigQuryにシンクしています。もちろんMENTAも同様にグループジョインしてから同様な構成で運用しています。

    • 開発環境がない

    またEC2サーバー(Amazon Linux 1)では約5~6年以上動いていたということもあり、Ansible化はされているものの一部しかコード化をしておらず、手動でパッケージなどインストールしていたということもあり、2年前Amazon Linux 2にいざ移行しようとしても、ブラックボックスになり始め、メンテナンスがし難い状況でした。

    それと分析チームがStgのサーバーにログインして開発するしか手段がないため、作業のカニバリや外部の方の動作検証ができず、非常に開発しづらい状態でした。そのため、開発環境を作ることから始めました。

    ※上記分析基盤の運用についてブログしていますので参考にしてみてください。

    Digdag/Embulk 開発環境

    ランサーズの開発環境ですが、MySQLコンテナは毎週本番データをマスクして、AWS CodebuildでECRにpushしています。開発メンバーはMySQLコンテナをpullして本番環境により近い環境で開発を行っています。それに従い、DigdagコンテナとPostgreSQLコンテナを構築して、MySQLコンテナのデータを取り込みBigQueryにシンクするような環境を作りました。また、開発環境のBigQueyシンク先でDev用のプロジェクトを作成しようと思いましたが、管理が増えることから、Stgで利用しているプロジェクトにしました。まずはDockerfileから紹介していきましょう。

    • Dockerfile
    FROM openjdk:8-alpine
    
    ENV DIGDAG_VERSION 0.9.42
    ENV EMBULK_VERSION 0.9.23
    ENV GOPATH /go
    ENV PATH /usr/local/go/bin:/go/bin:$PATH
    
    ENV LANG C.UTF-8
    ENV APP_ROOT /opt/hoge
    
    # Setup UTC+9
    RUN apk --update add tzdata && \
        cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
        apk del tzdata && \
        rm -rf /var/cache/apk/*
    
    # install packages
    RUN apk update && \
        apk upgrade && \
        apk add --update --no-cache \
        autoconf \
        bash \
        curl \
        git \
        libc6-compat \
        make \
        mysql-client \
        postgresql-client \
        py3-pip \
        python \
        supervisor \
        tzdata \
        vim
    
    # Install awscli
    RUN pip3 install --upgrade pip
    RUN pip3 install awscli
    
    RUN mkdir /root/.aws/
    COPY digdag/credentials /root/.aws/credentials
    
    # go 1.12.5 install,delete gz
    RUN wget https://dl.google.com/go/go1.12.5.linux-amd64.tar.gz --no-check-certificate -P /tmp
    RUN tar -C /usr/local -xzf /tmp/go1.12.5.linux-amd64.tar.gz && rm /tmp/go1.12.5.linux-amd64.tar.gz
    
    # google sdk command install
    RUN curl -sSL https://sdk.cloud.google.com | bash
    ENV PATH $PATH:/root/google-cloud-sdk/bin
    RUN mkdir /root/.gcp
    
    # install digdag
    RUN curl --create-dirs -o /usr/local/bin/digdag \
        -L "https://dl.digdag.io/digdag-${DIGDAG_VERSION}" \
        && chmod +x /usr/local/bin/digdag \
    # install Embulk
        && curl --create-dirs -o /usr/local/bin/embulk \
        -L "https://dl.embulk.org/embulk-latest.jar" \
        && chmod +x /usr/local/bin/embulk
    
    # Setting Embulk version
    RUN embulk selfupdate ${EMBULK_VERSION}
    
    # Setting Digdag
    RUN mkdir /etc/digdag
    COPY digdag/server.properties /etc/digdag/server.properties
    
    # setting superviser
    COPY supervisor/supervisord.conf /etc/supervisord.conf
    COPY supervisor/app.conf /etc/supervisor/conf.d/app.conf
    RUN echo files = /etc/supervisor/conf.d/*.conf >> /etc/supervisord.conf
    
    # Port to expose *outside* the container
    EXPOSE 80
    
    # Service to run
    CMD ["/usr/bin/supervisord"]
    • supervisor/conf.d/app.conf
    [supervisord]
    nodaemon=true
    
    [program:digdag]
    directory=/etc/digdag
    command=java -Dio.digdag.cli.launcher=selfrun -XX:+AggressiveOpts -XX:TieredStopAtLevel=1 -Xverify:none -jar /usr/local/bin/digdag server --max-task-threads 2 --config server.properties -b 0.0.0.0 --log /var/log/digdag/digdag_server.log --task-log /var/log/digdag/tasklogs --access-log /var/log/digdag/accesslogs
    user=root
    autostart=true
    autorestart=true
    stopsignal=TERM
    stdout_logfile=/dev/stdout
    stdout_logfile_maxbytes=0
    stderr_logfile=/dev/stderr
    stderr_logfile_maxbytes=0
    • setting.bq.sh
    #!/bin/bash
    
    aws --profile=hoge s3 cp s3://hoge/digdag/kms/bq-encrypt.key /root/.gcp/
    aws --profile=hoge kms decrypt --ciphertext-blob fileb://~/.gcp/bq-encrypt.key --output text --query Plaintext | base64 -d > ~/.gcp/bq.key
    
    gcloud auth activate-service-account \
    bigquery-embulk@stg-capybara.iam.gserviceaccount.com \
    --key-file ~/.gcp/bq.key \
    --project stg-capybara

    今回利用したイメージはopenjdk:8-alpineを選択しました。またDigdagはJavaが必須となり、元々初めはDigdag公式のイメージがないことから、1からAlpineイメージで構築を始めていると、Javaのインストールで不要なパッケージも入ってしまうことからイメージの容量が2GBにも超えてしまい、DigdagのサーバーモードからいくらWebからRunしても起動されないことがありました。なので、openjdkのイメージを利用して構築することをオススメします。それ以外のプログラムでGoも利用しており、リポジトリも分割していることからインストールするようにしています。

    プロセス起動管理も同様にsupervisorで整えましたが、Stgや本番コンテナではaccess-log、digdag_server.logはCloudWatchLogsで出力されますので開発環境のみ指定しています。BigQueryで利用するサービスアカウントのJSONファイルはAWS KMSで暗号し、復号化するようにシェルスクリプト化を行いました。それ以外にEmbulkで利用しているDBのパス等は環境変数でenvファイルを利用してS3からコピーするように管理していますが、これも今後はKMSで暗号したほうがよりセキュアになるので改善していきたいと思います。

    この開発環境により、digdag run、スケジューラーのテスト、bqコマンドでの操作が簡単にできるようになりました。

    Stg/本番環境

    • setting-digdag-secret.sh
    #!/bin/bash
    
    ## project
    PROJECT1=hoge
    PROJECT2=hogee
    PROJECT3=hogeee
    ~省略~
    
    # decrypt bq.key
    aws --profile=hoge s3 cp s3://hoge/digdag/kms/bq-encrypt.key /root/.gcp/
    aws --profile=hoge kms decrypt --ciphertext-blob fileb:///root/.gcp/bq-encrypt.key --output text --query Plaintext | base64 -d > /root/.gcp/bq.key
    
    ## setting local mode digdag secrets
    cd /root/.gcp/ && cat /root/.gcp/bq.key |digdag secrets --local --set gcp.credential=@bq.key
    
    ## setting server mode digdag secrets
    for i in ${PROJECT1} ${PROJECT2} ${PROJECT3}
    do
    cd /hoge/digdag/${i} && digdag push ${i} && digdag secrets --project ${i} --set gcp.credential=@/root/.gcp/bq.key
    done
    
    # delete bq.key
    rm /root/.gcp/bq.key

    Stg環境は上記の開発環境のDockerfileを元に構築をし、コンテナデプロイもCircleCIでマージ後にリリースされるようになっております。ちなみにDigdagではサーバーモードとローカルモードと2つあります。分析チームや開発メンバーから、今すぐこのテーブルをBigQueryにシンクしてほしい!といったことがあるため、ECS-ExecでDigdagコンテナにログインして、ローカルモードで実行できるようにしました。ここはEC2にSSHして実行するのと変わりありませんね。

    digdag pushは手動で行っていましたが、どんなプロジェクトが動いているのか管理したかったというのもあったので、シェルスクリプトで自動化して、新規プロジェクトが増えた場合はそのシェルを変更してもらうようにしました。

    • ViewテーブルのクエリバックアップをECS schedule task化

    [BigQuery]すべてのviewテーブルのクエリをバックアップしてみた!

    BigQueryのviewテーブルのクエリは本番環境で間違えて消してもすぐ戻せるようにシェルスクリプト化をしているのですが、今まではEC2のCronからECS schedule taskに移行して、毎日0時に実行されるようにしました。上記のスクリプトは個人ブログ書いていますので参考にしてもらえばと思います。

    苦労した点

    • 本番環境だけdigdagコンテナが落ちたり上がったりの繰り返しになる

    Stg環境では動作等問題なかったのですが、移行時に本番環境のみコンテナが落ちて立ち上がるの繰り返しが起きました。実際にコンテナにログインしてデバッグしてみましたが、以下のエラーが出ており、調査に時間がかかってしまうということもあったので、一度DigdagのDBを作り直したところうまくいき、原因不明で移行完了してしまったというのが非常に残念でした。

    org.postgresql.util.PSQLException: ERROR: relation "session_attempts_on_site_id_and_state_flags_partial_2" already exists
    • 本番移行時にエラーが多発する

    embulkのプラグインでembulk-output-bigqueryを利用しているのですが、深夜のシンクで10分に1回以下のエラーが多発しました。プラグインのバージョンアップをしたところ多発することが少なくなったので、以前と同じで100以上あるテーブルのシンクに5時間で完了できるようになりました。

    ※以下追記

    [Digdag]チューニングとFailed to add subtasks because of task limitについて

    Error: OutputPlugin 'bigquery' is not found.

    今後の改善と展望

    • envファイルをKMS化
    • コンテナデプロイが遅すぎる

    デプロイに時間がかかると、コンテナ化のデメリットが目立ってしまいます。上記にも書きましたが、別のリポジトリで管理しているGoのプログラムがあるため、git cloneしてdep ensureをしている際にデプロイに10分以上かかってしまっているため、リポジトリをまずは一つにまとめてバイナリファイルのみを配布するように改善していこうと思います

    • DigdagコンテナのCPU使用率が高い

    コンテナのCPUは2coreでメモリーが2GBなのですが、スケールアップか、Digdagのチューニングをする必要があるかと思いますが、一旦は様子見して改善していきたいと思います。たた、Fargateであるとコンテナがもし落ちてしまっても自動起動されるので運用は非常に楽になります。

    • Rubyでデータを一部加工しているプログラムの移行

    ユーザー情報テーブルの各カラムなどをSQLで取得して、その日にアクセスしたユーザー一覧を取得しているプログラムがRubyで作られているのですが、これもEmbulkに移行しないとEC2サーバーを削除できないので、頑張ります…!

    まとめ

    コストはEC2と比べて少し割高になり、構築していて途中EC2でもいいんじゃない?と疑問を持ちましたが、Dockerによる属人化の廃止とバージョンアップのしやすさにより非常にメンテナンスしやすくなったと感じました。

    ランサーズではSREチーム大募集しております。少しでも気になった方はDMください!一緒に分析基盤改善していきましょう!

    EC2で運用しているWordPressサーバーをECS/Fargateに移行しました

    adachin|2021年05月06日
    SRE

    SREチームの安達(@adachin0817)です。今回WordPressサーバーであるEC2からECS/Fargateに移行しましたが、紆余曲折を得て、苦労したところ、技術的な部分、最終的には複数のリポジトリを一つにまとめたことなどを紹介したいと思います。まずはプロジェクトとサーバーの構成から説明していきましょう。

    ランサーズのWordPressとECS時代のサーバー構成

    • EC2時代のサーバー構成

    • デプロイリリースフローについて

    ランサーズのWordPressは現在12個のプロジェクトがあり、リポジトリ単位で管理していました。また、AWSではEC2/Amazon Linux 2(Nginx,PHP-FPM)で運用しており、オートスケールは使用せずに二台で冗長化構成を保っていました。開発環境はDockerでできているものの、AnsibleでのNginxの設定ファイルを管理したり、自前のデプロイシステムの設定などをしなければならなく、工数が非常にかかっていました。

    また、EC2でオートスケールを導入したとしても自前のデプロイシステムへの依存が発生してしまい、定期的にAMIの取り直しが必要となります。課題としてはデプロイシステムのメンテナンスも大変ですし、DockerベースとCircleCIのみに変更してデプロイサーバーを廃止することです。

    ECS/Fargate + EFSのWordPressコンテナ構成

    • 各リポジトリでのデプロイ

    そこで、Amazon EFSのパフォーマンスが向上したということもあり、ECS/Fargate + EFSの構成に移行しました。開発環境で利用しているDockerfileを元に、Stg環境、本番環境のWordPressコンテナのデプロイをCircleCIで行い、WordPressのソースコードはCodeBuildを利用してEFS内でgit pullを実行するように実装しました。また、自前のデプロイサーバーを廃止することができました。また、オートスケールによる負荷分散も今回導入しました。技術的な部分は以下私の個人ブログを参考にしてください!

    ECS/Fargate + EFSでのデプロイをCircleCI + CodeBuildで自動化する

    • サーバーレスポンス

    • コスト

    しかし、EFSだとサーバーレスポンスが平均で約3秒となってしまい、プロビジョニングドIOPのコストが高く、EC2と比べて6倍近く増加してしまいます。また、各リポジトリにCircleCIのデプロイも管理していることから、エラーが出てしまうと12個も修正をしなければなりません。CloudFrontも導入を考えましたが、一部対応していないドメインなどがあり、わざわざ複雑な構成にする必要にすべきなのかとチームと話し合った結果、この構成を諦めることにしました。以下SREチームの金澤(@yakitori009)が以前スペースマーケットさんと勉強会をした際に、LT資料を貼りますので、細かい技術的な部分は以下を参考にしてもらえばと!(ただオススメはしません。。)

    【スペースマーケット×ランサーズ】初コラボイベント開催しました!

     

    現在の構成とデプロイについて

    • ECS/サービス

    • CircleCIでのデプロイ

    • レイテンシー

    • リポジトリの容量
    [~/www/lancers_wordpress]
    adachin@ > du -sh
    1.8G .
    

    結局のところ、複数のリポジトリを一つにまとめてlancers_wordpressというリポジトリに移行しました。リポジトリ/コンテナの容量は約2GBほどでした。ここではDev、Stg、本番のDockerfileを管理し、CircleCIでのStg環境デプロイはAPIを利用しシェルスクリプトでリリース、Masterマージでの本番環境のリリースを実装しました。自前のデプロイツールでは約1分ほどでデプロイ可能ですが、ソースコードをコンテナにコピーするとなると約7~8分はかかります。しかし、コストがEC2時代とほぼ同等であること、レイテンシーも約1秒まで改善できました。

    振り返って

    Amazon EFSを導入するにはまだ早いと感じましたし、個人的には一つのリポジトリで全てのメディアを管理できたので分かりやすく、シンプルとなりました。今後新しいメディア等増えても工数をかけずに構築することも可能になりましたし、CodeBuidの経験もできたので結果的によかったと思います。ぜひWordPressの運用はECS/Fargateで参考にできればと思います!

    SREも募集しておりますので、興味がある方はTwitterでDMいただければと思います!

    Terraform v0.12.29 → v0.15.0にバージョンアップしました

    adachin|2021年04月19日
    SRE

    SREチームの安達 (@adachin0817)です。ランサーズやグループ会社ではTerraform v0.12.29を利用して、AWSのインフラコード化をしています。ようやくグループ会社すべてAWSに移行が完了となり、次なるチャレンジとしてはTerraformのバージョンアップを対応することとなりました。また、以下4/15にv0.15.0もリリースされたということもあり、非常に良い機会となりました。

    まずは各プロジェクトについて簡単に説明をしていきたいと思います!

    各サービスとDeployサーバーについて

    現状Terraformの管理としてはDeployサーバーの各ディレクトリごとにリポジトリがあり、terraform applyを実行して運用しています。ランサーズ本体WordPressや社内で利用しているコンテナなどはTerraformで管理されていますが、それ以外は完全にTerraform化されていない状態です。今後はterraform importですべてのリソースをコード管理していく予定なので、その際はまたブログ致します。それ以外のプロジェクトであるLancers CreativeProsheetLancers AgentMENTAはECS/Fargateに移行しているので、すべてTerraformでコード管理しています。また、CircleCIでTerraform CI(validate,plan)を実行しているということもあり、今後terraform applyもCircleCIで行う予定なので、Deployサーバーでの運用は廃止していく予定です。会社的にも成長していく中でバージョンアップが疎かになっているのは事実でした。

    Terraform CIに関しては個人ブログに書いていますので参考にしてみてください。

    [AWS]CircleCIでTerraformのCI/CD環境を実装してみた

    バージョンアップの流れとエラー対応について

    流れ

    • ブランチ作成後、DeployサーバーでTerraform v0.13.0に上げる
    • terraform init -reconfigure 
    • terraform 0.13upgrade
    • terraform planにより差分を修正する
    • tfstateファイルの修正
    • terraform planで差分がなくなったらv0.15.0にバージョンアップ
    • terraform state replace-provider registry.terraform.io/-/aws hashicorp/aws によるプロバイダー変更
    • terraform init
    • terraform planで差分がなくなっていればバージョンアップ完了

    エラー対応について以下まとめてみました。まず前提として、以下のようにv0.12系からいきなりv0.15にはバージョンアップができません。なのでv0.13にあげてから徐々に差分をなくすことが重要になります。

    $ terraform init -reconfigure 
    │ Error: Invalid legacy provider address
    │
    │ This configuration or its associated state refers to the unqualified provider "aws".
    │
    │ You must complete the Terraform 0.13 upgrade process before upgrading to later versions.
    
    $ terraform state replace-provider registry.terraform.io/-/aws hashicorp/aws 
    Terraform will perform the following actions: 
    
    ~ Updating provider: 
      - registry.terraform.io/-/aws 
      + registry.terraform.io/hashicorp/aws 
    
    Changing 33 resources:

    また、legacy providerになっているので、providerも新しく変更しました。

    • Warning: Version constraints inside provider configuration blocks are deprecated
    $ terraform init -reconfigure 
    │ Warning: Version constraints inside provider configuration blocks are deprecated
    │
    │ on backend.tf line 5, in provider "aws":
    │ 5: version = "= 3.36.0"
    │
    │ Terraform 0.13 and earlier allowed provider version constraints inside the provider configuration block, but that is now deprecated and will be removed in a future version of Terraform. To silence
    │ this warning, move the provider version constraint into the required_providers block.
    • backend.tf
    provider "aws" {
      region = "ap-northeast-1"
      profile = "prd-menta-terraform"
    }
    
    terraform {
      required_version = ">= 0.15"
      required_providers {
        aws = {
          source = "hashicorp/aws"
          version = "3.36.0"
      }
    }
    backend "s3" {
      bucket = "prd-menta-terraform"
      key = "terraform.tfstate"
      region = "ap-northeast-1"
      profile = "prd-menta-terraform"
      }
    }

    v0.13以前ではプロバイダー構成がブロック内でプロバイダーバージョンの制約がありましたが、現在は非推奨になっています。なので上記のようにプロバイダーバージョンをrequired_providersに移行しました。

    • Error: Invalid resource instance data in state
    $ terraform plan
    Error: Invalid resource instance data in state
    
    on autoscale.tf line 86:
    86: data "aws_iam_role" "ecs_service_autoscaling" {
    
    Instance data.aws_iam_role.ecs_service_autoscaling data could not be decoded
    from the state: unsupported attribute "assume_role_policy_document".
    
    
    Error: Invalid resource instance data in state
    
    on ecs.tf line 18:
    18: resource "aws_ecs_service" "menta-app-service" {
    
    Instance aws_ecs_service.menta-app-service data could not be decoded from the
    state: unsupported attribute "placement_strategy".

    0.13で廃止されたパラメーターがいくつがありますが、tfstateファイルに残っていると上記のようにエラーが起こります。対象パラメータを削除してterraform initし直せば解決します。必ずtfstateファイルはバックアップしましょう。以下不要なパラメーターの一覧を出してみました。対応後、terraform planが動作することができました。

    ・assume_role_policy_document
    ・role_id
    ・role_name
    ・adjustment_type
    ・placement_strategy
    ・cooldown
    ・metric_aggregation_type
    ・min_adjustment_magnitude
    ・step_adjustment
    ・vpc_id 
    ・vpc_region
    ・request_parameters_in_json

     

    • .terraform.lock.hclについて

    https://www.terraform.io/upgrade-guides/0-14.html#opting-out-of-dependency-locking

    次に、v0.15.0にバージョンアップをしてterraform initをするとterraform.lock.hclファイルというものが作成されます。これはv014.0から導入されましたが、.terraformサブディレクトリにキャッシュするロックファイルで、providerのバージョン指定がある場合に記録されます。こちらは公式がGitでバージョン管理すべきと記載されていたので対応しました。

    • 最後にCircleCIのTerraform CIを修正

    これにてバージョンアップは完了となります。下記のTerraform CIでも差分がないことを確認することができました。残り3つのプロジェクトも同様にバージョンアップします。

    まとめ

    Terraform v0.15.0の目立った新機能はあまり見受けられませんでしたが、バージョンアップの対応方法についてご紹介させていただきました。個人的に4つのプロジェクトをバージョンアップするにはなかなか荷が重かったですが、特にトラブルなく対応できたので良かったです。v0.15.0にしたということもあって、terraform planやapplyの速度が向上したと満足しております。この機会にv0.12系の方はぜひバージョンアップをしてみてください。

    またSREも募集しておりますので、興味がある方はTwitterでDMいただければと思います!

    ※追記 2021/08/23

    MENTAをAWSに移行しました

    adachin|2021年03月24日
    ECS/Fargate

    皆さんこんにちは。SREチームの安達(@adachin0817)です。去年10月にMENTAがランサーズにグループジョインされて、本日AWSへ移行が完了しました。この5ヶ月間どのような取り組みをしたか、改めて振り返ってみようと思います。

    AWSへ移行する前

    移行前の環境
    https://menta.work
    ・さくらのクラウド
       ・PHP 7.2 / Laravel 5.5
     ・Nginx / MariaDB /Redis
     ・SendGrid
    ・開発環境
     ・Docker
    ・リリース方法
     ・SSHによるシェルスクリプト

    ・移行プロジェクトメンバー

    元々MENTAは2018年にリリースしてからさくらのクラウドで運用していました。また、ランサーズにグループジョインする前は私が副業でサーバー保守していたということもあり、本番環境、Staging環境、Redash環境の3つで管理していましたが、デプロイがし難いことや、アクセス負荷によるレイテンシーの悪さ、冗長性や負荷分散などは担保していない状態でサービス拡大に伴い、今回AWSへ移行することになりました。他にも移行プロジェクトでは主にSREチーム @yakitori009と共に主体となり、入江さん、業務委託の方と共に進めていきました。個人的に副業で一緒に仕事していたので非常にコミュニケーションしやすかったですね。以下はAWSに移行する際にメリットやECS/Fargateによるコンテナ化することの目的をまとめてみました。

    目的とコンテナにするメリット
    ・内部統制対応
    ・S3、RDSを利用したバックアップ
    ・CloudWatchLogs+αを利用したログの保存
    ・冗長化による稼働率の向上
    ・開発効率のアップ
    ・共通開発環境の構築
    ・リリースの属人化の排除
    ・GitHubと連動してリリース
    ・運用効率のアップ
    ・Immutableなサーバーの運用
    ・タスク単位で自由に割当のリソースが変更できる
    ・オートスケールによる負荷分散
    ・AWS WAFによるセキュリティ強化

    新開発環境を作るポイント

    ・リポジトリ配下にdockerディレクトリを作成
    ・本番環境と同等の構成を再現

    ・ELB(H2O)コンテナ(リバースプロキシ)
    ・Appコンテナ
     ・PHP7.2-alpine/Laravel 5.5/Nginx
    ・phpMyAdminコンテナ
    ・MySQLコンテナ
    ・Redisコンテナ
    ・Stripeコンテナ
    ・Seleniumコンテナ

    ・SendGridコンテナ
    ・nodeコンテナ

    ・各アプリケーションの起動はSupervisorで管理
    ・composer installはAppコンテナでログイン時に実行
     ・supervisorctlでphp-fpmをrestartするように

    開発環境は以前移行したLancers Agencyのコンテナをベースにより本番環境に近い環境を作り直しました。Docker ImageはPHP7.2-alpineを利用しており、プロセス管理はSupervisorで管理しています。メール送信ではSendGridを利用していますが、送信テスト時に実際にメールが届いてしまうことからSendGridのモックコンテナを使って開発環境でテスト送信できるように実装しました。

    Staging環境/本番環境の構成

    ・Staging環境の構成

    ・本番環境の構成図

    ・分析基盤

    ・Amazon KinesisによるログストリーミングとDatadog

    ・Terraformで全リソースコード化
    ・Staging環境は極力コストを抑えタスク数は1つ
    ・社内用はランサーズProxyでアクセス(phpMyAdmin,SendGrid,Digdag,Redash)
    ・SendGridコンテナによるメールテスト
    ・phpMyAdminコンテナによるデータ出し
    ・オートスケールによる最大6台のAppコンテナで運用

    ・devops/SSHコンテナの利用でaws cli.MySQLの操作
    ※現在ではdevopsコンテナを廃止してECS Execで直接Appコンテナにログインしています
    ・Digdag/Embulk/BigQueryによる分析基盤の構築

    ・DBはRDS Auroraに移行
    ・ElastiCacheによるログインキャッシュ化
    ・画像等S3化
    ・ログはCloudWatch Logs
    ・バッチはECS Scheduled Tasks
    ・内部監査(ログの集約)
     ・Amazon Kinesis
    ・AWS KMSによる暗号化
    ・WAFでのDDosアタック制御
    ・監視はDatadogでコンテナリソースとAPM

    今回インフラ面で新しく挑戦した技術ですが、ECS/Fargateでのオートスケールを実装しました。今まではタスク数2台でしたが、AppコンテナのCPUが20%超えると最大6台までスケールアウトするようになっているのでアクセス負荷時にも耐えられる環境となりました。以下私の個人ブログで紹介しているので参考にしてみてください。また、分析基盤(Digdag,Embulk,BigQuery)はランサーズでも運用しており、EC2で運用するように環境を共通にしました。BigQueryのサービスアカウントの鍵はAWS KMSを利用して暗号化し、実行時に復号しています。他にもランサーズと同様の環境であるカナリア環境で本番同等の環境も構築しました。これにより本番リリースする前にチェックができるので、リリース後でのトラブルが減ります。

    https://blog.adachin.me/archives/47303

    CircleCIでのリリース

    ・CircleCIでのコンテナデプロイ
    ・Staging環境/カナリア環境へのデプロイ
    ・github flow
     ・シェルスクリプト
     ・任意のブランチを指定してAPI経由でデプロイ
     ・Masterマージで本番環境リリース
    ・素早くデプロイが可能
    ・開発効率がアップ
    ・Terraform CI環境を実装

    MENTAでは、AWS移行前からGitHub ActionsでUnit TestやE2E Testを行っていましたが、ランサーズではCircleCIを利用していたため移管しました。また、デプロイもCIで行うようにしました。コンテナデプロイは前回移行した方法と同様に、Terraform CI、API経由でのStaging環境デプロイ/カナリア環境デプロイ、Masterマージでの本番環境デプロイで成り立っています。

    大変だったこと、今後やること

    ・S3 画像アップロードが一部の機能しか対応していなかった

    ・Laravel5.8へのバージョンアップ

    ・バッチや過去の企画のソースを棚卸することで、負債となっていたソースを整理できた

    ・Unit/Feature/E2Eの自動テスト整備
     ・PHPCSを使った規約統一
     ・PHPStan(Larastan)使った静的解析での品質向上バグ削減
     ・リファクタリング

    今後やること
    ・Datadogでのアクセスログ可視化

    ・コンテナ脆弱性管理
    ・画像読み込みのレスポンスを改善
    ・Terraformバージョンアップ
    ・devopsコンテナを廃止してAPI経由でコンテナにログイン

    上記業務委託メンバーからコメントをいただきました。AWS移行をきっかけに企画・アイディアベースのプロトタイプ開発から将来を見据え運用・保守も意識した開発スタイルに変わりました。また、技術的な負債の整理を行い移行を実施できたと思います。ランサーズと同じ開発スタイルに変わったのでやりやすくなったので良かったと感じています。

    気になるコストですが、さくらのクラウド時代と比べて月額で数十万円高くなっています。しかしながら、冗長化やオートスケーリングを設定し、高い可用性を実現したサーバー構成になり、今後のMENTAを支えていく土台が整いました。

    移行プロジェクトのまとめ

    去年10月から始めていた移行プロジェクトですが、過去の経験が生きたこともあり、短期間で移行できたのでよかったです。協力していただいたメンバーの皆さんにも感謝いたします!個人的に副業で利用していたサービスの保守担当隣、さらにはランサーズグループへJOINし、AWSの移行を担当できたのはなかなかできないので、良い機会をいただくことができました!

    お疲れ様でした!!

    登壇してきました!

    Lancers AgencyのサービスをECS/Fargateに移行して振り返る

    adachin|2021年02月08日
    AWS

    SREチームの安達(@adachin0817)です。去年の5月から行っていた移行プロジェクト第二段であるグループ会社のLancers Agency株式会社の各サービスをAWS(ECS/Fargate)へ移行完了しました。今回、移行背景やECS/Fargateでのコンテナ運用について振り返りを行ってみたいと思います。

    AWSへ移行する前

    AWSへ移行する前はさくらのクラウドで運用していました。また今回の移行の目的としては前回のLCC移行(Rails)と同様でランサーズに統一させるということです。またECS/Fargateでの移行経験を生かして今回はCakePHPでのコンテナ運用にチャレンジすることとなりました。以下はコンテナ化にするメリットと各サービスの情報をまとめてみました。

    目的とコンテナ化にするメリット
    ・内部統制対応
    ・S3、RDSを利用したバックアップ
    ・CloudWatchLogs+αを利用したログの保存
    ・冗長化による稼働率の向上
    ・開発効率のアップ
    ・共通開発環境の構築
    ・リリースの属人化の排除
    ・GitHubと連動してリリース
    ・運用効率のアップ
    ・Immutableなサーバーの運用
    ・タスク単位で自由に割当のリソースが変更できる
    ・スケールアウトのしやすさ

    移行前の環境
    https://prosheet.jphttps://lancersagent.com/
       ・PHP5.4 CakePHP2.5.4
     ・Apache,MySQL5.7
     ・メール送信はPostfix

    ・コーポレートサイト https://lancers-agency.co.jp/

     ・PHP5.5

    ・メディアサイト(WordPress) https://prosheet.jp/blog

     ・PHP5.5,MySQL5.7

    ・開発環境
     ・VagrantによるAnsibleで管理

    ・リリース方法
     ・Fabric

    新開発環境を作るポイント

    ・リポジトリ配下にdockerディレクトリを作成
    ・本番環境と同等の構成を再現

    ・ELB(H2O)コンテナ(リバースプロキシ)
    ・Appコンテナ
     ・PHP5.5-alpine/CakePHP2.5.4/Nginx
    ・MySQLコンテナ
    ・SendGridコンテナ
    ・phpMyAdminコンテナ
    ・WordPressコンテナ(PHP7.3-alpine/Nginx)
    ・各アプリケーションの起動はSupervisorで管理
    ・composer installはAppコンテナでログイン時に実行
     ・supervisorctlでアプリをrestartするように

    ・開発環境のディレクトリ構成

    $ tree docker/dev
    docker/dev
    ├── README.md
    ├── app
    │   ├── Dockerfile
    │   ├── nginx
    │   │   ├── local.biz.prosheet.jp.conf
    │   │   ├── local.lancersagent.com.conf
    │   │   ├── local.prosheet.jp.conf
    │   │   └── nginx.conf
    │   ├── php
    │   │   ├── 15-xdebug.ini
    │   │   ├── config.inc.php
    │   │   ├── php-fpm.conf
    │   │   ├── php.ini
    │   │   ├── www.conf
    │   │   └── xdebug.so
    │   └── supervisor
    │       ├── app.conf
    │       └── supervisord.conf
    ├── docker-compose.noelb.yml
    ├── docker-compose.yml
    ├── elb
    │   ├── Dockerfile
    │   ├── h2o
    │   │   ├── conf.d
    │   │   │   ├── local.biz.prosheet.jp.conf
    │   │   │   ├── local.lancers-agency.co.jp.conf
    │   │   │   ├── local.lancersagent.com.conf
    │   │   │   ├── local.pma.prosheet.jp.conf
    │   │   │   ├── local.prosheet.jp.conf
    │   │   │   └── local.sendgrid.prosheet.jp.conf
    │   │   └── h2o.conf
    │   └── service.sh
    └── mysql
        ├── Dockerfile
        ├── mysql_init.sh
        ├── mysqld.cnf
        └──  service.sh

    ・supervisor/app.conf

    [supervisord]
    nodaemon=true
     
    [program:nginx]
    command=/usr/sbin/nginx -g "daemon off;"
    autostart=true
    autorestart=true
    stopsignal=TERM
    user=root
    stdout_logfile=/dev/stdout
    stdout_logfile_maxbytes=0
    stderr_logfile=/dev/stderr
    stderr_logfile_maxbytes=0
     
    [program:php-fpm]
    command=/usr/local/sbin/php-fpm -F
    autostart=true
    autorestart=true
    stopsignal=TERM
    user=root
    stdout_logfile=/dev/stdout
    stdout_logfile_maxbytes=0
    stderr_logfile=/dev/stderr
    stderr_logfile_maxbytes=0
     
    [program:sshd]
    command=/usr/sbin/sshd -D
    autostart=true
    autorestart=true
    stopsignal=TERM
    user=root

    開発環境はVagrantからDockerに移行しました。Docker ImageはPHP5.5-alpineを利用しており、Appコンテナ一つでProsheet、Lancers Agentをバーチャルホスト、プロセス管理はSupervisorで管理しています。メール送信ではPostfilxを利用していましたが、ランサーズはSendGridを利用しているため移行したいこともあり、SendGridのモックコンテナを使って開発環境でテスト送信できるように実装しました。またコーポレートサイトもCakePHPで動作していましたが、メンテナンスしやすいWordPressに移行しました。これにより素早く開発環境を提供できるようになりました。

    Stg/本番環境の構成

    ・Amazon KinesisによるログストリーミングとDatadog

    ・stg dockerディレクトリ構成

    $ tree docker/stg
    docker/stg
    ├── deploy.sh
    └── prosheet-app
        ├── Dockerfile
        ├── README.md
        ├── common
        │   └── prompt.sh
        ├── nginx
        │   ├── default.conf
        │   ├── stg.biz.prosheet.jp.conf
        │   ├── stg.lancersagent.com.conf
        │   ├── stg.prosheet.jp.conf
        │   ├── stg.try-out.work.conf
        │   └── nginx.conf
        ├── php
        │   ├── php-fpm.conf
        │   ├── php.ini
        │   └── www.conf
        └── supervisor
            ├── app.conf
            └── supervisord.conf
    ・Terraformで全リソースコード化
    ・Stg環境は極力コストを抑えタスク数は1つ
    ・devops/SSHコンテナの利用でaws cli.MySQLの操作
    ・DBはRDS Auroraに移行
    ・メール送信はPostfixからSendGrid化
    ・コーポレートサイトはWordPress化
    ・ALBのリスナールールによるパスによる転送
    ・画像等S3化
    ・WordPressの画像
     ・WP Offload Media LiteプラグインでS3に
    ・ログはCloudWatch Logs
    ・バッチはECS Scheduled Tasks
    ・内部監査(ログの集約)
     ・Amazon Kinesis
    ・デプロイはCircleCIのみで実装
    ・監視はDatadogでコンテナリソースとAPM

    前回のLCC移行と構成は同様ですが、一番大変だったのはSendGridへ移行、WordPressコンテナ化に伴う画像をS3に移行、職務経歴書などのファイルをS3に移行することでした。もちろんコンテナの場合ソースコードで管理していないファイルや画像等はデプロイ時に削除されてしまうので、これらの実装ができていないと移行ができません。主にアプリエンジニアが対応していただき、SendGrid移行時ではメール送信ロジックが一つにまとまっていなかったので対応箇所が多かったところと、SendGrid APIの仕様で送信結果を別途取得しなければならないのが想定外でした。S3移行ではファイルアップロード、ダウンロード処理が共通化されている部分と共通化されてない部分があり、対応方法もそれぞれ異なり、昔のソースコードへの対応後、確認方法がわからなくて苦戦しました。

    また、さくらのクラウドからAWSに移行してコストがどのくらい変わったのか確認してみたところ、さくらのスペックが高かったこともあり、年間約60万ほど削減できました。続いてはCircleCIでのコンテナデプロイについて紹介します。

    CircleCIでのコンテナデプロイ

    ・Stg環境のデプロイ
     ・github flow
     ・シェルスクリプト
  
      ・任意のブランチを指定してAPI経由でデプロイ
    ・素早くデプロイが可能
     ・開発効率がアップ
    ・Masterマージで本番環境リリース
    ・Terraform CI環境を実装

    $ sh deploy.sh 
    下記のようにブランチを指定して実行してください。 
    (例) sh deploy.sh ブランチ名
    
    $ sh deploy.sh
      
      下記のようにブランチを指定して実行してください。
      (例) sh deploy.sh ブランチ名
      
    $ sh deploy.sh fix-nginx
    {
      "number" : 74,
      "state" : "pending",
      "id" : "xxxxxx-xxxxx-xxxxx-xxx-xxxxxxxx",
      "created_at" : "2020-08-27T06:14:43.973Z"
    }
    ブランチ名/ fix-nginx のStgデプロイを開始しました!

    Stg、本番環境デプロイはCircleCI一つで実装しています。StgではCircleCIのAPIをシェルスクリプトで引数(ブランチ名)を指定してデプロイし、本番はMasterマージ後にデプロイするように実装しました。WordPressもリポジトリ化をしてデプロイも同様になります。CIではTerraformが動作していますが、今後PHPのCIも取り入れる予定です。

    移行プロジェクトの振り返りとまとめ

    今回はランサーズSREチーム、アプリメンバーと共に移行していきましたが、前回の経験もあったことで素早く構築することできましたし、PHPでの本番コンテナ運用もノウハウが高まったので非常にいい経験となりました。また、個人的な感想としてPROsheetは5年前のシェアゼロ時代から知っていたので、前CTOにも移行したことを報告して、メンテナンスされてありがたいとのことで感謝もされて非常に感慨深いです。

    今後やることとしては現在PHPは5.5、CakePHPは2.5.4を利用しています。先日PHP8もリリースしたということもあって、SREチームとアプリメンバー含めてバージョンアップの対応やコンテナの脆弱性検知(Trivy)やDatadogでのアクセスログ可視化を行っていきたいと考えています。半年間近くでしたが、レガシーシステムを移行するにあたって時間もかかりましたし、最後までやりきることができたので非常に感動です。次の移行プロジェクトはMENTAとなりますので、また移行したらブログ書いていこうと思います。ありがとうございました!

    ランサーズの各サービスをECS/Fargateへ移行する取り組みについて

    adachin|2020年12月07日
    SRE

    ランサーズ Advent Calendar 2020 7日目の記事でございます。

    皆様お久しぶりでございます。SREチームの@adachin0817です。タイトルの通り、SREチームではランサーズ内の各サービスをECS/Fargateへ移行する取り組みを行っています。その中でコンテナにしてよかったこと、課題等含めて振り返ってみたいと思います。

    2020/4〜

    去年にシクロマーケティング社がランサーズのグループにジョインされました。現Lancers Creativeという月額定額制のクリエイティブサービスをAWSに移行することになり、初の試みであるECS/Fargateにチャレンジすることになりました。新技術への挑戦に対してかなり時間をかけてしまいましたが、この経験を元に他のグループ会社のインフラやランサーズをコンテナへ移行する計画を整うことができました。次に今年4月に移行してから半年ほど経ちましたが、移行して良かったことや、課題をまとめていきたいと思います。

    ・開発メンバーだけでRuby/Railsを最新にバージョンアップができた
    EC2と比較すると、ランサーズではAnsibleを運用しており、我々SREチームへの工数がなんとしてもかかってしまいます。逆に開発メンバーにAnsibleを修正してもらうというのも学習コストもかかってしまいます。コンテナの場合はDockerfileで完結し、テスト等は開発環境やStg環境へCircleCIでデプロイすればいいので、バージョンアップ対応まで1ヶ月で対応することができました。基本的にはインフラやアプリ面、全て開発チームに任せることができ、インフラでのちょっとした相談で済むようになりました。これは凄い…

    ・本番環境にSSHでログインすることがない

    Fargateというマネージドサービスにしたこともあり、ログの確認はすべてCloudWatch Logsで確認することができました。またログの転送はAmazon KinesisでリアルタイムでS3にシンクしています。なので調査するために不要なSSHのportを開ける必要がないので、セキュアな環境が実現できたと思います。

    ・コンテナ台数を増やすためにはタスク起動数を変更するだけで良い

    こちらもEC2と比較すると、AMIを取得、もしくはAnsibleで構築してアプリをデプロイするまでの工数がかかりますが、コンテナの場合はサービスのタスク起動数をTerraformで変更するだけなので、あっという間に構築が終わります。

    ・開発環境が重い

    Docker for MacのファイルシステムがLinuxと違うため、ページ遷移が遅いという課題があります。ここらへんはdocker syncの検証や不要なファイルやディレクトリはmountしないなど改善していこうと思いましたが、以下の記事によりgRPC FUSE をOFFにすることで、ページ遷移が5秒から1秒まで高速化しました。開発環境が遅いことがあれば試してみてください。

    https://blog.hanhans.net/2020/11/28/docker-for-mac-slow-again/

    ・コンテナデプロイまでに10分以上かかる

    CircleCIでのコンテナデプロイでネックなのはbundle installに時間がかかってしまうことです。ここはマージ後に走らせるのではなく、commitしてpushするタイミングでコンテナデプロイするのが時間削減になるので修正していこうと思います。

    ・コンテナでのオートスケールを経験していない

    まだまだ小さいサービスということもあり、負荷対策としてコンテナでのオートスケールを経験したことがないので、ランサーズ本家移行の時にテストをする方向でいきたいと思います。

    あとはTryでやっていきましょう!以下技術的な内容はブログ書いているので参考にしてください。

    グループ会社のインフラをECS/Fargateに移行して振り返る

    2020/5~

    https://prosheet.jp/

    https://lancersagent.com/

    一段落して、5月からはグループ会社である「Lancers Agency株式会社」のサービスをECS/Fargate移行計画が始まりました。このサービスはCakePHPで動いているので、ランサーズに近い環境となっております。ようやくPostifixからSendgridに移行、アップロード系の処理をS3へ移行が完了したところで、あとはStg環境で一通りテストができれば今月12月には移行できる予定です。コーポレートサイトはWordPressで動いており、こちらはコンテナに既に移行済みとなっています。移行後にやることは以下になります。

    ・PHP/CakePHPのバージョンアップ

    現在PHPは5.5、CakePHPは2.5.4を利用しています。先月PHP8もリリースしたということもあって、SREチームと開発メンバー含めて対応していきたいと考えています。

    また、9月にアソビューさん、コネヒトさんで合同LT大会も行ったのでコンテナ活用について登壇させていただきました。技術的な部分は以下参考にできればと思います。

    【アソビュー×ランサーズ】AWSでのオーケストレーションツールの活用事例(EKS vs ECS)を開催しました!

    【コネヒト×ランサーズ】コネヒトマルシェ「事業を支えるWeb開発」vol.2を開催しました。


    2020/10~現在

    10月からはMENTAがグループジョインされました。個人なお話になるのですが、副業でMENTAを使っているということもあり、一緒に仕事ができる!ということもあって、なかなか感慨深いところもありました。また、業務委託でMENTAのサーバーを全て管理していたということもあり、アクセス数も伸びているので、こちらもさくらのクラウドからECS/Fargateに移行することになりました。現状のインフラ移行について進捗を共有しましょう。

    https://signal.diamond.jp/articles/-/325

    ・開発環境

    開発環境はランサーズと同様の環境にするため、作り直しからプロジェクトが始まりました。今回は機能ごとに8つのコンテナで実装することにしました。AppコンテナはAlpine Linuxで構築しています。LancersAgencyの開発環境を参考にしたということもあり、1日で実装することができました。MENTAではPHP7.2、Laravel5.4を利用していますが、今後バージョンアップをする予定です。

    ・Stg環境

    ・本番環境

    Stg環境は今までの構成とは少し違って、Redashやランサーズと同様に分析基盤/Embulk,Digdag,BigQuery(BI)を運用したいとのことで、コストダウンのためEC2で構築することにしました。それ以外はLancersAgencyと環境は同等です。現在は一部S3への画像アップロードが対応していないため、コンテナ内に画像がアップロードされてしまい、デプロイ時に消えるという現象が発生しています。こちらも早急に対応しているところです。Stg環境とは別に、本番のデータも見れるようにテスト環境を作りたいということで、もう一つAppコンテナを用意することになりました。こちらはランサーズと同じ運用でPrivate環境となります。

    ・デプロイ

    CIは現在GitHub Actionsを利用しているので、CircleCIに寄せようとしています。それ以外にもTerraform CIも動いているので、。コンテナデプロイはAPI経由でStg、Privateにリリース、Masterマージで本番リリースするように実装しています。Slackでもリリースを実装中です。

    MENTAは移行後に詳しく技術的な部分を書きますのでまたの機会に!

    ランサーズ本家の移行は?

    ・現状の構成

    CakePHP2→CakePHP4へのジャンプアップ

    現在ランサーズ本家はEC2でオートスケールを利用して運用しているので、まずはAmazon Linux 1から2に移行を取り組んでいます。またSRE/QAの金澤さん、QAのまみーさんが主体となって、CakePHP4にバージョンアップを行っています。その後管理画面をCake4とPHP8にしてからECS/Fargateに移行し、本体のバージョンアップが完了後、実施となる見込みになっています。なので長い時間をかけて取り組むという認識でございます。

    まとめ

    直近の課題としてはまずは今月中にLancersAgencyの各サービスを移行することと、MENTAの移行をやりきることですね!そしてSREチームが今どんな課題に向けて取り組んでいるのか雰囲気が伝わったかと思います。もちろんコンテナ移行以外にも改善するところはたくさんあります。なので….

    SREが足りません!

    気になる方はぜひ応募待ってます!
    明日はつっちーさんです!

    グループ会社のインフラをECS/Fargateに移行して振り返る

    adachin|2020年05月11日
    AWS

    皆さん元気ですか!?SREチームの@adachin0817です。去年から行っていた移行プロジェクトで、グループ会社である、シクロマーケティング株式会社の「ミギウデ」をさくらVPSからAWSへ移行しました。今回、移行背景やECS/Fargateでのコンテナ運用について簡単にご紹介と振り返りを行ってみたいと思います。

    なぜAWSへ移行するのか

    AWSへ移行すると冗長性の担保などが挙げられますが、一番は開発環境やインフラなど、すべてランサーズに統一させるということが第一の目的です。それに伴い、ミギウデ自体のサービスがシンプルなインフラ構成ということもあり、インフラ運用の手間をなくしたいということから、ECS/Fargateで初の外部サービスとしてコンテナ運用にチャレンジしてみようとなりました。

    目的とコンテナ化にするメリット

    ・内部統制対応
    ・S3、RDSを利用したバックアップ
    ・CloudWatchLogs+αを利用したログの保存
    ・冗長化による稼働率の向上
    ・開発効率のアップ
    ・共通開発環境の構築
    ・リリースの属人化の排除
    ・GitHubと連動してリリース(人件費削減)
    ・運用効率のアップ
    ・Immutableなサーバーの運用
    ・タスク単位で自由に割当のリソースが変更できる
    ・スケールアウトのしやすさ

    まずは開発環境から紹介していきましょう。

    開発環境

    ・構成

    $ tree dev
    dev
    ├── README.md
    ├── cyclo-app
    │    ├── Dockerfile
    │    ├── gemrc
    │    ├── nginx
    │    │   ├── app.conf
    │    │   └── nginx.conf
    │    └── supervisor
    │         ├── app.conf
    │         └── delayed_job.sh
    ├── docker-compose.yml
    ├── elb
    │   ├── Dockerfile
    │   ├── h2o
    │   │   ├── conf.d
    │   │   └── dev.cyclo-creativecloud.com.conf
    │   │   └── h2o.conf
    │   └── service.sh
    └── mysql
        ├── Dockerfile
        ├── mysql_init.sh
        ├── mysqld.cnf
        └── service.sh
    

    ・docker-compose.yml

    version: '2'
    
    services:
      elb:
        build: ./elb
        image: cyclo-elb
        hostname: elb
        networks:
          lancers:
            ipv4_address: xx.xxx.xx.xx
        extra_hosts:
          - "dev.cyclo-creativecloud.com:xx.xxx.xx.xx"
        container_name: cyclo-elb-xx-xx
        ports:
          - 80:80
          - 443:443
      app:
        build: ./cyclo-app
        image: cyclo-app
        hostname: cyclo-app
        networks:
          lancers:
            ipv4_address: xx.xxx.xx.xx
        extra_hosts:
          - "dev.cyclo-creativecloud.com:xx.xxx.xx.xx"
        container_name: cyclo-app-xx-xx
        volumes:
          - ~/www/app:/var/www/app/
      mysql:
        build: ./mysql
        image: cyclo-mysql
        hostname: mysql
        networks:
          lancers:
          ipv4_address: xx.xxx.xx.xx
        container_name: cyclo-mysql-xx-xx
        ports:
          - 3306:3306
    
    networks:
      lancers:
      external: true
    

    ・ランサーズ開発環境

    開発環境ですが、リバースプロキシとして使うELB(H2O)コンテナ、App(Rails)、MySQLコンテナの3つで動作しています。これにより、AWSやランサーズの開発環境(上記図)と同じ構成に仕上がることができました。

    このサービスはRailsでできているということもあり、bundle install はコンテナにログインしてから実行しています。また各アプリケーションの起動はSupervisorで管理しました。また、Dockerはプロセスがフォアグラウンドで動いてないと、コンテナが終了するため、フォアグラウンドで実行するように注意しましょう。

    ・cyclo-app/supervisor/app.conf

    
    [supervisord]
    nodaemon=true
    
    [program:app]
    command=bundle exec unicorn_rails -c config/dev_unicorn.rb -E development
    autostart=true
    autorestart=true
    stopsignal=TERM
    user=root
    directory=/var/www/app/
    stdout_logfile=/dev/stdout
    stdout_logfile_maxbytes=0
    stderr_logfile=/dev/stderr
    stderr_logfile_maxbytes=0
    
    [program:delayed_job]
    command=sh /etc/supervisor/conf.d/delayed_job.sh
    autostart=true
    stopsignal=TERM
    user=root
    stdout_logfile=/dev/stdout
    stdout_logfile_maxbytes=0
    
    [program:nginx]
    command=/usr/sbin/nginx -g "daemon off;"
    autostart=true
    autorestart=true
    stopsignal=TERM
    user=root
    stdout_logfile=/dev/stdout
    stdout_logfile_maxbytes=0
    stderr_logfile=/dev/stderr
    stderr_logfile_maxbytes=0
    
    [program:sshd]
    command=/usr/sbin/sshd -D
    autostart=true
    autorestart=true
    stopsignal=TERM
    user=root
    
    

    Stg/本番環境

    ・Stg

    ・本番環境

    ・Docker(Stg/Prd)

    $ tree
    ├── prd
    │   └── cyclo-app
    │       ├── Dockerfile
    │       ├── README.md
    │       ├── common
    │       │   └── prompt.sh
    │       ├── gemrc
    │       ├── nginx
    │       │   ├── app.conf
    │       │   └── nginx.conf
    │       └── supervisor
    │           ├── app.conf
    │           └── delayed_job.sh
    └── stg
        └── cyclo-app
            ├── Dockerfile
            ├── README.md
            ├── common
            │   └── prompt.sh
            ├── gemrc
            ├── nginx
            │   ├── app.conf
            │   └── nginx.conf
            └── supervisor
                ├── app.conf
                └── delayed_job.sh
    

    Stgと本番環境はすべてTerraformでインフラコード化をしています。これによりStg環境で一度作ってしまえば、本番環境構築に工数がかかることなく、設定変更時のミスが減ります。RDSAuroraを使い、コンテナのログはすべて標準出力をしてCloudWatch logに出力しています。各ログは今までLambdaを利用してS3に保存していましたが、Pythonのバージョンアップ対応でメンテをしたくないことから、今回はAmazon Kinesisを利用しました。他にもAppコンテナ以外にコマンドでRDSへ接続、aws cliの利用や開発者がrails consoleを使いたいため、Appコンテナとまったく同じコンテナであるdevopsコンテナを作成しました。このコンテナはランサーズの踏み台サーバーからSSHでログインできるようにVPC Peeringしています(下記図より)。SSMのセッションマネージャーもありますが、手軽に踏み台からSSHしたいということから、今回はこのような構成にしました。ちなみにECSのタスク定義ですが、AppコンテナはSupervisorで起動し、devopsコンテナはフォアグラウンドでSSHが起動するように制御しています。バッチは元々Railsのwheneverを入れて、schedule.rbファイルで管理していましたが、 ECS Scheduled Tasksを使って実装しました。監視はDatadogを使って、コンテナ内のCPU,メモリーなどのリソースと外形監視を行っています。

    ・VPC Peering

    ・コンテナデプロイの流れ

    コンテナのデプロイはCircleCIで実装しました。GitHubdevelopブランチにマージするとStgが反映され、masterブランチにマージすると本番が反映されます。CIrspecCDCircleCIのOrbsを利用しており、Orbsがなかった頃はecs-deployを使ってシェルスクリプトを使っていましたが、簡単にDockerfileのbuild、ECRへpushECSリビジョンの更新、サービスタスク定義の更新、DBマイグレーションまで可能になりました。今までCapistranoによってサービスのリリースを行っていましたが、CircleCIによってリリースフローを確立できました。

    移行プロジェクトと振り返り

    今回3社合同で行われた移行プロジェクトですが、SREチームでコンテナの設計、デプロイなどを実装し、チームの皆様にフォローして頂きつつ、開発チームと連携してサーバーサイドの動きなどをヒアリングしながら進めていきました。また、リモートワークが当たり前になってきた頃には、とにかく通話してコミュニケーションを取るように心がけました。最後にチームで良かったところ、悪かったところを振り返ってみます。

    Keep

    ・モダン技術へのチャレンジをやりきれた
    ・初めて外部サービスとしてコンテナ運用を行うことができた
    ・RailsでのECS/Fargateでの知見が高まった
    ・メンテナンス時にトラブルなく移行できた
    ・CircleCIのOrbsを使ったCI/CDの知見
    ・今後他サービスでのCI/CDの検証となった
    ・Datadogをサイドカーコンテナとして動かした際の知見
    ・コンテナの監視検証となった
    ・バッチ置き換えの検証となった
    ・内部統制対応

    ・ログの取得
    ・冗長性の確保
    ・データベースのバックアップが取れている
    ・リリースフローを確率できた

    Problem

    ・当初の移行スケジュールが伸びてしまった
    ・開発環境が不十分なできになっていた
    ・コンテナの設計とやり直しに時間かかりすぎていた
    ・突発的なエラーにも時間かかりすぎていた
    ・デプロイの実装に時間かかりすぎていた

    Try

    ・ECSの他サービスへの適用
    ・移行スケジュールとタスクを最初から明確にする

    まとめ

    今回外部サービスとしてコンテナ運用は初めてのチャレンジということもあり、やりきれて非常にいい経験となりました!今後はこの知見を活かしてランサーズもFargate化を計画をしていますので、その時はまたブログします。

    ※余談
    今回の移行プロジェクトで会社で表彰頂きました!ありがとうございました!!

    Lancers x ROBOT PAYMENT x LAPRASでLT交流会をしてきました!

    adachin|2020年02月18日
    イベント/登壇

    SREチームの@adachin0817です。今回はROBOT PAYMENTさん、LAPRASさんの三社合同でLT交流会を行いました。人数も約20名ほどで、かなりの盛り上がりを見受けられ、ランサーズでは私含めて3人がLTをしました。

    ちなみにロボペイさんもラプラスさんも弊社はユーザーとして使わさせて頂いております!ありがとうございます!!ではイベントレポートをいきましょう!

    LAPRAS社 19:00~

    LAPRAS本社にて行いましたがピザとお酒が用意されていました!ありがたや・・!後ほど串カツ田中もデリバリーで来たのでなかなかお腹が満たされました。イベントでは串カツ田中のデリバリーおすすめです!!オフィスには集中ルームやスタンディングデスクなど、エンジニアには最適の環境が用意されていると感じました。(写真撮るの忘れた)羨ましい!!!

    タイムスケジュール

    3社合わせて9人で、持ち時間は一人5分でした。過ぎてしまうと強制終了してしまうので、ここは腕の見せどころですね。ここから一人ずつ紹介していきます。

    LAPRASのスコアリングロジックについて @showwin

    LAPRASのスコア機能があるのですが、そのロジックをCTOの伊藤さんに説明していただきました。機械学習というよりもルールベースで実現しているとのことでした。結構大変そう。

    Do u know DigitalOcean? @adachin0817

    https://blog.adachin.me/archives/11245

    個人のブログサーバーの裏側(DigitalOcean)についてLTさせていただきました。5分で話せなかったことも多かったので自分のブログに書きましたのでぜひ!

    今から始める楽しいスマートホーム

    今から始める楽しい スマートホーム ←リンクより

    ロボペイの高木さんに自宅でのIoTスマートホームを実現したお話で非常に実用的なお話でした。私個人ではAlexaしか使っていなく放置気味なので、電気とかカーテンも自動化しよう!!

    フロントエンド設計の問題点と解決策について @nasum360

    LAPRASでのフロントはVue.js、vuex、vue-routerでLAPRAS SCOUTはTypeScriptに移行中とのことでした。現状としてはVue.jsとTypeScriptの相性が悪く、テストやパフォーマンスを意識できていないことから、今後はストア設計の見直しと共通コンポーネントの切り出しをしてパフォーマンスの計測をしていくとのことでした。テスト書きましょう!

    オプティマイザトレースで運用改善に愛を @mamy1326

    https://speakerdeck.com/mamy1326/love-operation-with-optimizer-trace

    1月にランサーズにジョインしてくれたまみーさんです。ぺちぱーで、DBにも鬼強なので日々私は学びしかないのですが、今回はオプティマイザトレースで愛をもって改善していこうというお話でございました。スライドが74枚という驚異的な量でしたが、すば抜けたトークで会場を盛り上げ、DBでの運用改善は実行結果を必ずチェックし、疑問はトレース、常に計測して監視することが重要です。そこから見直しをしていき、統計情報を調べて更新して愛を持って改善していきましょう!!

    SREが自宅のリフォームをしたお話 @j_untanaka

    ロボペイのSRE 田中さんでは自宅のリフォームをしたときに「POおよびPMは自分である」という名言に会場は盛り上がりました。結局はIT以外でのプロジェクトがあり、何を作りたいかは発注者が考える必要があるので、ソフトウェア開発とほぼ同じでした。確かに!!!

    API GatewayとLambdaとGolangで作るサムネイル生成システム @yakitori009

    ランサーズ SRE 金澤さんですが、元々ランサーズのサムネイル生成処理はAppサーバー内でImageMagickを起動して行っていました。しかし安定性の問題などで、Lambdaに置き換えたというお話です。5分で終わらず、ちょうどJAWSでも話すということもあって、続きは現地でお会いしましょう!

    ローラー開発の現実とつらみ @Chan_moro

    LAPRASのクローラーエンジニア両角さんでは実際にランサーズをリアルタイムクローリングをするということで、私はWAF設定しているのであまりやらないようにとツッコミを指摘しましたが笑 Pythonで簡単に可能であること。また、ランサーズのサイトはキレイなのでクローリングしやすいとのことでした!おもしろい!

    俺たちのパフォーマンス計測 @trunkatree

    最後はロボペイ SREの遠藤さんです。監視はMackerelとNewRelicを使い、レスポンスタイムのグラフがあっているのかバラつきがあるため、よりユーザーに寄り添った計測がしたいということでNewRelicのAPIからメトリックスを取って、RDSに保存してRedashで可視化しているそうです。インフラ系の可視化良き!!

    まとめ

    合同LT大会ありがとうございました!懇親会では各社エンジニアさんとの技術交流もでき、アットホームな雰囲気で非常に楽しむことができました。ちなみにLAPRAS社では個人でイベントするのにオフィス使ってもいいとのことです!ぜひこのブログを見て、ランサーズと一緒に合同LT大会やりましょう!ではまた!

    二次会はワイン!

    ランサーズのSREがリモートワークで気をつけていること

    adachin|2019年12月24日
    SRE

    ランサーズ Advent Calendar 2019 24日目の記事となります。
    SREチームの@adachin0817です。

    今年のクリスマスは平日ということで、先週の土日にパーティーを開催した方も多いのではないでしょうか。ちなみに私はディズニーランドへ15年ぶりに行ってきました。クリスマス仕様になっており、アトラクションでキャラクターたちがサンタコスになっていたり、大規模なイルミネーションの凄さに感銘を受けました。(ほんと凄い!!)

    さてさて、ランサーズは働き方の中でリモートワークが可能となっています。10月頃に週1~2で試してみたので、その時に業務で気をつけていることをご紹介したいと思います。

    We Are Lancers / 44のスライドで知るランサーズと勤怠報告

    https://speakerdeck.com/lancers_pr/44falsesuraidodezhi-ruransazu?slide=38

    ランサーズのエンジニアは基本「裁量労働制」なので、成果が出ていれば何時に出社してもいいですし、早く帰ることも可能です。勤怠は10時出社に遅れそうな場合、 slackに必ず報告をしています。裁量労働制と言っても「あの人がいない!」「え、仕事頼みたいけど何時に来るの!?」とならないように事前に報告するのは当たり前ですね。

    リモートワークに関わらず分報でやることを報告

    リモートワークで欠けてしまうのは「チーム内での雑談」だと思います。現在SREチームは3人で取り組んでいますが、質のいい雑談が多いので、「こういうのやりたいよね〜」「ここらへん課題かもね〜」「いやあだちんそこは気をつけようよ!」などのコミュニケーションが頻繁にあります。また、チャットでの質問をする際に発言しにくいことから業務にハマって進まないということも度々あります。そこらへんのフォローを含めてコミュニケーションを取るよう、分報での報告がベストかと感じています。

    分報では出社したら毎日 今日やること を中心に箇条書きで見える化していきます。タスクが終わったら以下のように スタンプをつけるとチーム内でも共有されるのでちゃんとアウトプットしているか判断できます。出社するときには口頭での報告が早いので分報には毎回書きませんが、リモートワーク時は以下のように、めいいっぱい つぶやくことを意識し、気楽なコミュニケーションでも相手のことを考えながらチャットするように心がけています。

    次は実際にリモートワークを通して感じたことをまとめてみました。

    リモートワークをやってみて気づいたこと

    メリット

    ・集中力が圧倒的に上がる
    ・ランチが自炊(思わず食べすぎてしまう)

    ・満員電車との戦いがない(通勤、帰宅がない)
    ・稼働率が上がる(朝起きてすぐデスクで始められる)
    ・休憩時間に家事ができる
    ・ちゃんとした夕飯が作れる

    会社での集中力とは違い、いつもとは違った能力が芽生え、ハマっていた技術が急に解決してしまうことに驚きました。また、ランチが自炊ということもあって、お金がかからず食べれることも。他にも満員電車に遭遇しないので、体力を使わずに業務ができるのはいいですね。あとは家事や夕飯もちゃんと作れるので嫁に感謝されます。

    デメリット

    ・1日の歩数が約200歩で運動不足
    ・チームとの距離感を感じる(出遅れてる感)
    ・返事/レビューをすぐもらえない
    ・チャットでの温度感が認識しづらい(気を遣ってしまう)
    ・成長してるのか不安
    ・通勤時間がないので読書がなくなる

    個人の見解ですが、久しぶりに会社へ向かうと相当運動不足で、坂や階段で息切れになってしまったということがありました。対策としては15分くらい外で散歩するのが良いでしょう。スタバ行ってコーヒーを買うという習慣づけをするとかですね。やはり、リモートワークの場合チームで仕事をすると出遅れてる感や、温度感などが明確に伝わらないので、自らコミュニケーションを取り、アクションを起こすことが大事ですね。一人でもくもくと開発をするには最高の働き方かと思います。

    まとめ

    リモートワークはメリット、デメリットがある中で、一人ひとりが何かしらルールを持って仕事をすることが大事かと思います。リモートでも出社しても個人のスキルをフルパワーに発揮できるように業務をしていきましょう!

    以前弊社の社員である市川さん(@EikoIchikawa)が海外でフルリモートワークを経験した記事があるので、ぜひご覧になってみてください!

    https://amp.review/2019/09/29/sp-lancers/

    [digdag]BigQueryのデータをシェルスクリプトでDBにインポートする方法

    adachin|2019年12月13日
    SRE

    ランサーズ Advent Calendar 2019 13日目の記事となります。

    SREチームの@adachin0817です。3日目の記事見てくれましたでしょうか!?(以下より)

    https://engineer.blog.lancers.jp/2019/12/lancers-tech-night/

    今回はBigQueryのデータをシェルスクリプトでDBにインポートする方法をブログしたいと思います。この裏で動いているのがご好評頂いているランサーズの機能であるユーザーレポートのプロフィール閲覧数のデータを表示しています。ユーザーレポートに関しては以下を参考にしてみてください!

    https://info.lancers.jp/20847

     

    今までの運用と問題点

    去年のアドベントカレンダーでもご紹介しましたが、上記のプロフィール閲覧数は深夜にdigdag/embulk(embulk-input-bigquery)でアプリケーションログをviewとして作成し、BigQueryのviewテーブルからRDSへシンクしています。去年ベータ版をリリースし、運用してきましたが、いくつかデータが取れないことがあり、取れる日に一気にdigdagで再シンクをしていました。さすがに手動で対応するには厳しいので、改善案をいくつか上げてみました。

    改善案その1

    • embulk-input-bigqueryプラグインのバージョンアップ

    このプラグインをバージョンアップすれば値が取れたり取れなかったりという事象がなくなるのではないのか、とチームで話し合いあったので取り組んだところ、バッチも動いており、Rubyやembulkのバージョンアップをしなければなく、各プロジェクトが問題なく動作するのかということもあり、断念しました。

    改善案その2

    ・BigQueryのデータをdigdagサーバーでcsvにエキスポートし、RDSへインポートする

    https://cloud.google.com/bigquery/docs/reference/bq-cli-reference?hl=ja

    bqコマンドには様々なフラグ(オプション)が対応しているので今回「改善案その2」を実装してみました。これであればdigdagでシェルスクリプトを実行できるので、代用案として最適かと思われます。

    bigquery-to-mysql.sh

    #!/bin/bash
    
    $(".common/secrets.sh")
    YESTERDAY=$(date +%Y-%m-%d -d '1 days ago')
    
    ## views out csv
    /home/hoge/google-cloud-sdk/bin/bq query --use_legacy_sql=false --format=csv --max_rows=100000 "SELECT * FROM \`prd-hoge.views.views\` where dt=\"${YESTERDAY}\"" > /tmp/views.csv
    
    ## add ,
    sed -i '1,2d' /tmp/views.csv
    sed -i "s/^/,/g" /tmp/views.csv
    
    # restore
    mysql -h "${HOST}" -u hoge -p"${PWD}" -P "${PORT}" hoge -N -e "LOAD DATA LOCAL INFILE '/tmp/views.csv' INTO TABLE views FIELDS TERMINATED BY ','"
    
    ## rm profile_views.
    rm -f /tmp/views.csv
    

    1. bqコマンドで対象のテーブル(1日前)を/tmpにcsvで吐き出す

    --max_rows のデフォルトは100なので100レコードしか取り出せません。明示的に10万レコードを指定しております。

    2. sedで2行分削除と置換

    3 .DBにリストアし、csvを削除

    次はdigdagで動かしてみます。

    run.dig

    timezone: Asia/Tokyo
    
    schedule:
      daily>: xx:xx:xx
    
    +views:
      !include : 'retry.dig'
      call>: views.dig
    

    retry.dig

    _retry:
      limit: 5
      interval: 60
      interval_type: exponential
    

    views.dig

    _error:
      sh>: /digdag/post_slack.sh "[${session_time}][${session_id}] DigDag Fail in Bigquery to MySQL views"
    
    +load:
      >: /digdag/bigquery_to_mysql/embulk/bigquery-to-mysql.sh
    

    retry.digを入れることでシェルスクリプトがエラーを起こしても5回再実行されるように設定しています。(以下参考に)

    https://blog.adachin.me/archives/10835

     確認

    今の所、毎日漏れなくプロフィール閲覧数が表示されるようになりました!

    まとめ

    今回初めてbqコマンドを使ってシェルスクリプトにしてみましたが、実際に書いてみると結構シンプルでした。皆さんもぜひ参考にしてみてください!