cronの失敗をGoogle Apps Scriptで通知する

はじめに

cronが面倒くさい。特に失敗を通知するのが。

そもそも標準エラー出力があった時だけ通知されるようにするのも面倒なんですが、
AWSを使ってると迷惑メール扱いされないようにAmazon SES使うなり、DNS逆引き設定するなりしないといけないわけです。
こんなのサーバ毎にやってられんわけです。

ということで、そんな面倒なcronの失敗通知をGoogle Apps Scriptを使うことで楽にします。

スプレッドシート

まずは以下のようなスプレッドシートを作ります。

f:id:inocop:20190218175417p:plain

Google Apps Script

スプレッドシートの [ ツール ] > [ スクリプトエディタ ] を開き、以下の関数を登録します。

function doPost(e) {
  var sheet = SpreadsheetApp.getActive().getSheetByName('メーリングリスト');
  var to_emails = sheet.getRange(1, 1, sheet.getLastRow()).getValues();

  var subject = 'エラー通知';
  var body = "以下の処理でエラーが発生しました。\n\n";
  body += "タスク名: " + e.parameter.task_name;
   
  GmailApp.sendEmail(to_emails.join(','), subject, body);
}


続いて、作成した関数を公開します。

スクリプトエディタの [ 公開 ] > [ ウェブアプリケーションとして導入... ] を選択します。
GASを初めて公開する場合、Googleの認証が出るので許可してください。

公開の設定は以下になります。

f:id:inocop:20190218182428p:plain

ここで表示される「現在のウェブアプリケーションのURL」にリクエストを送ることでメールが送信されるようになります。

スクリプトを更新する場合
再度、 [ 公開 ] > [ ウェブアプリケーションとして導入... ] が必要になります。
この時にプロジェクトバージョンを上げないと反映されないので注意してください。

自分も毎回やり忘れます。

cron

cronの処理は何でもいいんですが、
例としてLet's Encriptの更新処理をcronに登録します。

/etc/crontab

# エラーはGASで通知するので、全ての出力をリダイレクト
0  6  *  *  mon root /opt/cron/certbot_renew.sh >> /var/log/certbot_renew.log 2>&1

certbot_renew .sh

#!/usr/bin/env bash

/opt/certbot/certbot-auto renew --no-self-upgrade --deploy-hook "systemctl restart nginx"

# 証明書の更新に失敗したらGAS経由でメール通知
if [ $? != 0 ]; then
  TASK_NAME="certbot_renew.sh"
  curl -X POST -d task_name=$TASK_NAME https://script.google.com/macros/s/${APP_ID}/exec
fi

最後に

メールにこだわらなくていいなら、Slackとかに通知した方が楽。


以上、現場からでした。

ポートマッピングを使わずにDockerコンテナにアクセスする方法

この記事はDocker Advent Calendar 2018の16日目になります。

はじめに

記事書いといてアレなんですが
基本的にはポートマッピングを使ったほうがいいと思うので、あんまり使い所はないかもしれません。

自分の場合は、とあるライブラリでWebSocketのコネクションがどうしても上手くいかず
今回の方法に行き着いた感じです。

どこかで誰かの参考になれば幸いなので記事にしたいと思います。

Linuxの場合

コンテナ起動

例として、apacheコンテナを使用していきます。

以下のdocker-compose.ymlを用意して、コンテナを起動します。

$ vi docker-compose.yml
version: '3'
services:
  web:
    image: httpd:2.4-alpine
    expose:
      - "80"
    network_mode: host  # ホストのネットワークでコンテナを実行

$ docker-compose up -d

network_mode: host とすることで、ホストのネットワークに対してポート番号をexposeするため、マッピング無しでアクセス可能となります。

この設定はLinux環境限定のようです。

docs.docker.com

確認
$ docker ps --format "table {{.Image}}\t{{.Ports}}\t{{.Names}}"
IMAGE               PORTS               NAMES
httpd:2.4-alpine                        test_web_1

$ curl http://localhost
<html><body><h1>It works!</h1></body></html>

ポートマッピングしていませんが、アクセスできました。

Docker for Windowsの場合

Hyper-VでDockerデーモンが動いている都合上、仕方ないのかもしれませんが、
network_mode: host は使えないのでネットワークの設定が必要になります。

コンテナ起動

以下のdocker-compose.ymlを用意して、apacheコンテナを起動します。

$ vi docker-compose.yml
version: '3'
services:
  web:
    image: httpd:2.4-alpine
    expose:
      - "80"
    networks:
      my_network:
        ipv4_address: 172.99.0.2  # コンテナのIPを指定

networks:
  my_network:
    driver: bridge
    ipam:
     driver: default
     config:
       - subnet: 172.99.0.0/24  # ネットワークアドレスを指定

$ docker-compose up -d

ネットワークアドレスは172以外は自由に設定できます。
他のdocker networkで使われていないものを指定してください。

ルーティングテーブル設定

Docker for Windowsでは、デフォルトで以下の構成になっているようです。

  • 仮想スイッチ:10.0.75.0/24
  • DockerNAT:10.0.75.1
  • MobyLinuxVM:10.0.75.2

この内のMobyLinuxVMに、先ほどのネットワークアドレスをルーティングします。

$ route -p add 172.99.0.0/24 10.0.75.2
※管理者権限のコマンドプロンプトで実行

ルーティングテーブルを確認

$ route print

設定を削除する時はこちら

$ route delete 172.99.0.0

この設定については、以下の記事が大変参考になりました。 qiita.com

確認
$ docker ps --format "table {{.Image}}\t{{.Ports}}\t{{.Names}}"
IMAGE               PORTS               NAMES
httpd:2.4-alpine    80/tcp              test_web_1

$ curl http://172.99.0.2
<html><body><h1>It works!</h1></body></html>

アクセスできました。

Docker for Macの場合

すいません。分かりませんでした。

ただ、Macの場合はWndowsと違ってDocker for MacVirtualBoxが共存できるので
VirtualBox上のDockerで network_mode: host を使えば良いかなと思います。

最後に

Macについては思いっきり手抜きとなってしまいましたが、
そもそも今回の記事はDocker for WindowsVirtualBoxが共存できないことがきっかけになっています。

前述のWebSocketの件で最初は仕方なくVagrantで環境構築したんですが
DockerとVagrantを切り替えるために毎回PCを再起動する状況にノイローゼ寸前だったので、必死に調べた結果となります。 *1
何卒ご容赦ください。


以上、現場からでした。

*1:大人の事情でMacで開発するという選択肢はありません

プライベートDockerHubをゆるく使う

Dockerを使ってるとDockerHubも使いたくなることがあると思いますが、
業務だとimageをパブリックな所に置けなかったりしますよね。

Docker Registryを使えばプライベートなDockerHubを割と簡単に構築できるので、
作り方や使い方などを備忘録も兼ねて公開したいと思います。
imageを共有できれば良いや程度のゆるい感じでやっていきます。

使用マシン

  • registry-host(Registryコンテナを起動するマシン)
  • my-pc(自分の作業PC)

自分の場合、社内の空きPCをregistry-hostとして使いました。

構築

registry-hostに以下のdocker-compose.ymlを用意してコンテナを起動します。

[registry-host]$ mkdir ~/docker_hub && cd ~/docker_hub
[registry-host]$ vi docker-compose.yml
version: '2'
services:
  registry:
    image: registry:2
    volumes:
      - ./registry:/var/lib/registry
    ports:
        - "5000:5000"
    restart: always

[registry-host]$ docker-compose up -d

volumesの指定は登録したリポジトリデータを永続化するためのものです。
初回構築時にはdocker-compose.ymlだけあれば良いです。

登録

事前設定

httpで通信する場合、my-pcのDockerでregistry-hostを許可する設定が必要です。
registry-hostの部分はご自身の環境のIPやホスト名に置き換えてください。

f:id:inocop:20181105142229p:plain

これでmy-pcからregistry-hostへimageをpushできます。
例としてalpine linuxを登録してみます。

[my-pc]$ docker pull alpine:latest
[my-pc]$ docker tag  alpine:latest registry-host:5000/my-alpine:latest
[my-pc]$ docker push registry-host:5000/my-alpine:latest

登録の確認

リポジトリ一覧
[my-pc]$ curl http://registry-host:5000/v2/_catalog
{"repositories":["my-alpine"]}
タグ一覧

repositoriy名を指定して、タグ一覧を取得します。
この例ではmy-alpineを指定しています。

[my-pc]$ curl http://registry-host:5000/v2/my-alpine/tags/list
{"name":"my-alpine","tags":["latest"]}
Pull
[my-pc]$ docker pull registry-host:5000/my-alpine:latest

削除

リポジトリの削除
[registry-host]$ cd ~/docker_hub/registry/docker/registry/v2/repositories
[registry-host]$ rm -rf <repositoriy-name>
タグの削除
[registry-host]$ cd ~/docker_hub/registry/docker/registry/v2/repositories
[registry-host]$ rm -rf <repositoriy-name>/_manifests/tags/<tag-name>

たぶん削除用のAPIとかあると思うんですが、今のところディレクトリ削除で何とかなってます。
削除自体あまりしないですし、壊れたらまたPushすればいいやってノリです。


今回はゆるく使うということでimageの管理は適当ですが、
ちゃんと管理したい人はPortusというのを使うと色々出来そうです。

port.us.org

まぁ自分だったら、これ以上は面倒なので素直にaws ECRとかを使いますが。


以上、現場からでした。

vscodeで効率良くコードレビューする小技

コードレビューする時の変更箇所を比較する方法って色々あると思うんですが、
自分的にvscodeのGitLensを使ったやり方がいい感じなのでご紹介します。

marketplace.visualstudio.com

手順

レビューするブランチをコミットせずにマージします。
マージ後、resetでステージからも外します。

$ git checkout ${baseブランチ}
$ git merge --no-commit --no-ff ${レビューするブランチ}
$ git reset


vscodeで変更ファイルに色がつくので、
右クリックで[Compare File with Previous Revision]を選択します。

すると、こんな感じで差分が表示されます。 f:id:inocop:20180611172922p:plain

全体のコードを見つつ、差分は色付きで表示してくれるのでレビューしやすくなると思います。
定義へ移動とかも使えますし。

サイドバーの[ソース管理]でも同じように表示されますが
自分としては[エクスプローラー]でツリーを見ながらレビューしたいのでこの方法を使ってます。
コードレビューでお困りでしたら一度お試しください。


レビュー後は、マージした差分を取り消すのをお忘れなく

$ git reset --hard HEAD
$ git clean -fd


vscodeは知れば知るほど便利なエディタですね。

vscodeのextension(プラグイン)を過去のバージョンに戻す

vscodeのextensionを自動更新のままにしていたら、rubyのextensionが更新されて定義へ移動が効かなくなりました。
とりあえずダウングレードしたら直ったので、バージョンの戻し方を備忘録として書いておきます。

パッケージファイルのダウンロード

マーケットからは最新バージョンしかダウンロードできないので、
以下のURLを組み立ててダウンロードします。

https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${extension_name}/${version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage


publisherやextension_nameはマーケットURLのitemNameパラメータで確認できます。
https://marketplace.visualstudio.com/items?itemName=rebornix.Ruby

マーケットのChangelogから戻したいバージョンも確認します。

${publisher}      -> rebornix  
${extension_name} -> Ruby  
${version}        -> 0.16.0


今回の場合は以下のURLでダウンロードしました。

https://rebornix.gallery.vsassets.io/_apis/public/gallery/publisher/rebornix/extension/Ruby/0.16.0/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage

ダウンロード後にファイルの拡張子を.vsixに変更します。

インストール

vscodeですべてのコマンドの表示(F1)-> vsixと入力。
[拡張機能: VSIXからのインストール]でダウンロードしたファイルを選択すれば完了です。


思った以上に面倒だった。


最後にextensionの自動更新をオフにしておくことを忘れずに。

setting.json

"extensions.autoUpdate": false


以上、現場からでした。


参考
https://tpodolak.com/blog/2017/04/09/downgrading-visual-studio-code-extension/