ssh forwardで転送している鍵をdocker containerの中で使う方法

  • このエントリーをはてなブックマークに追加

docker containerの中でAgent転送した鍵を使いたいことがあります。
次のような場合です。
bastionにagent forwardで鍵を転送して、その鍵をdockerの中に持ち込みたい。

mac -> bastion
docker run fabric
[in container]
fabricでsshの設定が必要

AgentForwardしていると環境変数$SSH_AUTH_SOCKが設定され、そこにsocketファイルのPATHが入っています。これをdockerの中に持ち込めれば良いので、次のようにします。

docker run -it --rm \
-v $SSH_AUTH_SOCK:/ssh-agent \
-e SSH_AUTH_SOCK=/ssh-agent \
image command

もしbastionに存在する鍵や、SSHの設定も一緒にdocker containerの中で使いたい場合は、.sshもmountします。

-v ${HOME}/.ssh:/root/.ssh -v $SSH_AUTH_SOCK:/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent

なおmountする時に、mount元が存在しない場合はrootでディレクトリが作られてしまうので注意が必要です。-v /home/user/.m2:/root/.m2とした場合、/home/user/.m2というディレクトリが存在しないとrootユーザーの.m2ディレクトリが作成されてしまいます。

参考

datadogのauto mutingとlimit metrics collectionについて

  • このエントリーをはてなブックマークに追加

9月11日(火)の早朝から、DatadogのAutomutingが効かなくなりました。その原因を探ったのでその記録です。

AutoMutingとは?

ASGを使って、ScaleOut/ScaleInを頻繁にするようなシステムの場合、ScaleInで縮退する時(Instanceの削除)にはそのInstanceは監視対象外にしたいです。でないと、Instanceが削除されるたびにMetricsが来ないNODATAやdatadog.agent.upを見ている場合にアラートが発生します。

それを解決するためにDatadogではAutomutingをサポートしており、CloudWatchの情報を使ってInstanceが死んだら自動でInstanceのMuteをするようにしています。
これはDatadogのAWSとの連携設定の中にあります。

何が原因なのか?

ホストベースの簡単なMonitorを作成すると、次のようなクエリの監視ができあがります。

"datadog.agent.up".over("*").by("host").last(2).count_by_status()

これは通常、InstanceがTerminatedされた時にはDatadog側でそのホストは自動的にMuteされるのですが、この機能が動かずアラートが鳴りまくるという事象が発生しました。

原因について

結論から言えば、Datadog側の挙動が変わりました。サポートに連絡したところ、いくつかのIssueがあって解決中とのことなので、また元の挙動に戻るかもしれません。
ひとまず、現時点での挙動を記録に残しておきます。

原因判明について

こちらが解決のヒントになりました。
ありがたや、ありがたや。

このスレッドに、様々なスクリーンショットがありますのでご参考まで。

現時点の挙動について

Datadogは課金対象のInstanceを、Agentが動いている もしくは Datadogが認識したEC2インスタンスとしています。Agentを動かしていなくても、DatadogがEC2 Instanceを認識すればCloudWatch経由でMetricsを取ってきます。これも課金対象になります。
そのため、開発用にささっと立てたInstanceにDatadogのAgentを入れていなくても課金対象になってしまいます。

それを回避するため、limit metrics collectionの設定をします(下図参照)。例えば、ここにdatadog:enabledと入れるとAWSのタグにdatadog:enabledと設定されているInstanceが対象になります。(※当然ですがタグの設定がなくてもAgentが動いていれば課金対象です)

これらがどのように変わったのか、下の図にまとめました。
端的に言えば、limit metrics collectionの設定がある場合に、EC2にそのTagを設定していなくてもAgentの起動をしていればAutomutingの対象でしたが、該当日から対象外になりました。

Before

Agent起動 Tag有り 課金対象 Automutingが動作するか
F F F F
F T T T
T F T T
T T T T

Aftre

Agent起動 Tag有り 課金対象 Automutingが動作するか
F F F F
F T T T
T F T F
T T T T

サポートに連絡したところ、最終的には次の回答をもらいました。

I found out that we do have some issues with the EC2 automuting feature and the autoscaling groups. Our engineers are currently working on it.
We’re sorry for the inconvenience and will keep you updated.

しばらく様子を見ます。

なお、タグの設定を一回外すということをすると、このように課金対象ホストが増えてしまうので注意です。(これは実験のために意図して外したので真似はしないように。。)

MySQLのCLIで日本語が入力できない

  • このエントリーをはてなブックマークに追加

文字コードの設定関係なしに、MySQLのCLIに日本語を貼り付けても入力できないことがあります。AmazonLinuxのMySQL56がそうでした。

参考: MySQLクライアントに日本語が入力できない理由

理由は上記のブログにも書いてあるとおり、ReadlineではなくLibEditをLinkしているからなのですが、実はLibEditは2016年にマルチバイトに対応しています。

http://thrysoee.dk/editline/

2016-06-18 Jess Thrysoee

  • version-info: 0:54:0

  • all: sync with upstream source

  • deprecate option –enable-widec.
    Upstream now always build with unicode (wide-char/UTF-8) support.

ではなぜ入力できないのか、それはAmazonLinuxに入っているlibeditが古いからです。2008年のものです。とても古いです😱

$ sudo yum list | grep libedit

libedit.x86_64 2.11-4.20080712cvs.1.6.amzn1 installed
libedit.i686 2.11-4.20080712cvs.1.6.amzn1 amzn-main
libedit-devel.x86_64 2.11-4.20080712cvs.1.6.amzn1 amzn-main
$ mysql --version
mysql Ver 14.14 Distrib 5.6.41, for Linux (x86_64) using EditLine wrapper

対応はいくつかあります。

  • MySQL55を使う
  • MySQL56を使いつつ、LibEditは新しいものを使う

MySQL55を使う

MySQL5.6.12からReadlineではなくなっています。そのため、MySQL55のクライアントを使えば日本語は入力できます。

$ sudo yum install mysql55
$ mysql --version
mysql Ver 14.14 Distrib 5.5.61, for Linux (x86_64) using readline 5.1

新しいLibEditを使う

そうは言ってもMySQL5.6や5.7のクライアントを使いたい。
幸いにもstatic linkではないので、新しいLibEditをインストールしてそちらを参照するようにしてみます。

$ sudo yum install mysql56
$ ldd /usr/bin/mysql | grep libedit
libedit.so.0 => /usr/lib64/libedit.so.0 (0x00007fd0a7a03000)

ビルドしてインストール

wget http://thrysoee.dk/editline/libedit-20180525-3.1.tar.gz
tar zxvf libedit-20180525-3.1.tar.gz
cd libedit-20180525-3.1

sudo yum install gcc ncurses-devel
./configure --prefix=/usr/local --libdir=/usr/local/lib64
make
sudo make install

何も指定しないでconfigureすると/usr/local/libに入ってしまうので、一応/usr/local/lib64に入れるようにします。

$ ll /usr/local/lib64/

total 2052
-rw-r--r-- 1 root root 1315136 Aug 29 20:53 libedit.a
-rwxr-xr-x 1 root root 930 Aug 29 20:53 libedit.la
lrwxrwxrwx 1 root root 17 Aug 29 20:53 libedit.so -> libedit.so.0.0.58
lrwxrwxrwx 1 root root 17 Aug 29 20:53 libedit.so.0 -> libedit.so.0.0.58
-rwxr-xr-x 1 root root 762136 Aug 29 20:53 libedit.so.0.0.58
drwxr-xr-x 2 root root 4096 Aug 29 20:53 pkgconfig
drwxr-xr-x 3 root root 4096 Aug 11 01:26 python2.7
drwxr-xr-x 3 root root 4096 Aug 11 01:26 ruby

差し替え方法 その1

ldconfigの設定を変更する。
ただし、この方法は全てに波及してしまうので気をつける必要があります。

cat <<EOS | sudo tee /etc/ld.so.conf.d/libedit.conf
/usr/local/lib64
EOS

# キャッシュ更新
sudo ldconfig

sudo ldconfig -p | grep edit
libedit.so.0 (libc6,x86-64) => /usr/local/lib64/libedit.so.0
libedit.so.0 (libc6,x86-64) => /usr/lib64/libedit.so.0
libedit.so (libc6,x86-64) => /usr/local/lib64/libedit.so

ldd /usr/bin/mysql | grep edit
libedit.so.0 => /usr/local/lib64/libedit.so.0 (0x00007f459702f000)

問題なく日本語入力できました。

差し替え方法 その2

全体を差し替えてしまうのは怖いので、mysqlのCLIだけ差し替える場合はLD_LIBRARY_PATHで対応します。

alias mysql="LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH /usr/bin/mysql"
ldd /usr/bin/mysql | grep edit
libedit.so.0 => /usr/local/lib64/libedit.so.0 (0x00007fb179bec000)

これでも問題なく日本語が入力できました。

GKEのSSL証明書を使ってGCP/GAEの証明書を更新する

  • このエントリーをはてなブックマークに追加

GKEでLet’s Encryptを使ってSSL証明書の自動更新をしています。それとは別に、GKEのIngressとして作成していないロードバランサやGAEにもSSL証明書を使用しています。

GAEは証明書の自動更新をしてくれますが、ワイルドカード証明書には対応していません。

私が作っている環境はワイルドカード証明書を利用しているので、GKEで自動更新設定しているSSL証明書をGCP/GAEにも適用してみます。

GKEでSSL証明書の自動更新の設定をする

こっちは本題ではないので、かなり簡潔に書きます。HTTPではなくDNS01で行います。また、cert-managerは0.3以上じゃないとワイルドカード証明書に対応していません。

HELM INSTALL
curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get> get_helm.sh
chmod 700 get_helm.sh
./get_helm.sh
HELM UPDATE(既にインストール済みの場合はUpdateする)
helm init --upgrade

$HELM_HOME has been configured at /Users/tamtam180/.helm.

Tiller (the Helm server-side component) has been upgraded to the current version.
Happy Helming!
RBAC設定
kubectl create serviceaccount tiller --namespace kube-system
kubectl create clusterrolebinding tiller --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
Install Tiller
helm init --upgrade --service-account tiller
cert-manager
git clone https://github.com/kubernetes/charts
cd charts
helm install --set image.tag=v0.3.2 --name cert-manager --namespace kube-system stable/cert-manager
cert-managerがCloudDNSを操作できるようにする
GCLOUD_PROJECT=my-sample-project
gcloud iam service-accounts create cert-manager --display-name "cert-manager"
gcloud projects add-iam-policy-binding ${GCLOUD_PROJECT} --member serviceAccount:cert-manager@${GCLOUD_PROJECT}.iam.gserviceaccount.com --role roles/dns.admin
service accountのkeyを作る
gcloud iam service-accounts keys create cert-manager-key.json --iam-account cert-manager@${GCLOUD_PROJECT}.iam.gserviceaccount.com
service accountをsecretに登録する
kubectl create secret generic clouddns-service-account --from-file=cert-manager-key.json=cert-manager-key.json --namespace=kube-system

ClusterIssuerを作る

clusterissuer.yaml
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com # FIXME
privateKeySecretRef:
name: letsencrypt-prod
dns01:
providers:
- name: prod-dns
clouddns:
serviceAccountSecretRef:
name: clouddns-service-account
key: cert-manager-key.json
project: my-sample-project # FIXME
kubectl apply -f clusterissuer.yaml

Certificateを作る

ドメイン名はCloudDNSに登録している自分のドメインを指定します。
namespaceにdevを指定していますが、そこも自分の環境に合わせて調整してください。(私の場合はワイルドカード証明書を使っている関係で、dev環境やstaging環境に別のドメインを使っているため、環境ごとにnamesapceを分けています)

certificate.yaml
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: example-com-tls
namespace: dev
spec:
secretName: example-com-tls
commonName: example.com
dnsNames:
- "example.com"
- "*.example.com"
acme:
config:
- dns01:
provider: prod-dns
domains:
- "example.com"
- "*.example.com"
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
kubectl apply -f certificate.yaml

GKEのSSL証明書をGAEに設定する

GAEは既存の証明書をupdateコマンドで一発で更新できます。

gcloud app ssl-certificates list 2>/dev/nullで証明書の一覧が取得できます。

GAEの証明書のExpire時間を計算する
CERT_ID=xxxxx # 目的のIDにしてね
T=$(LANG=C date --date $(gcloud app ssl-certificates describe ${CERT_ID} | grep expireTime | sed -e "s/'//g" | cut -d " " -f 2) +'%s')
expr \( $T - $(date +'%s') \) / 86400

こんな感じで今の証明書があと何日で期限切れになるのか計算できます。cert-managerでは30日前に自動更新するようになっているので、30日とか25日あたりを閾値にすると良いと思います。

GAEの証明書を更新する

# 秘密鍵を取得する
kubectl get secret example-com-tls -o json --namespace=dev | jq -r '.data["tls.key"]' | base64 --decode > /tmp/tls.key
# 証明書を取得する
kubectl get secret example-com-tls -o json --namespace=dev | jq -r '.data["tls.crt"]' | base64 --decode > /tmp/tls.crt

証明書の有効期限が延長済みであることを確認するには、次のコマンドで証明書の有効期限を見れば良いです。

# 有効期限の表示
cat /tmp/tls.crt | openssl x509 -noout -enddate
notAfter=Oct 29 08:40:28 2018 GMT

# UNIX時間でほしい場合
date --date "$(cat /tmp/tls.crt | openssl x509 -noout -enddate | cut -d'=' -f2)" +"%s"
CERT_ID=xxxx
gcloud app ssl-certificates update "${CERT_ID}" \
--display-name "*.example.com lets" \ # GAEに登録するSSL証明書の名前。日付とか入れると良いかも
--certificate "/tmp/tls.crt" \
--private-key "/tmp/tls.key"

ロードバランサの証明書を更新する

※GKE(k8s)のIngressとして作成したロードバランサは、cert-managerで勝手に更新されます。そうではない野良のロードバランサが対象です。

GCP管理の証明書を一覧する
# この結果にはGKEで作成したものも含まれています
gcloud compute ssl-certificates list

残念ながらGAEのようなupdateコマンドがありません。
手順はSSL証明書を作成し、target-https-proxiesにその作成した証明書を設定します。

SSL証明書の更新
CERT_NAME="star-dev-example-com-$(date +'%Y-%m-%d')"
gcloud compute ssl-certificates create "${CERT_NAME}" \
--certificate "/tmp/tls.crt" \
--private-key "/tmp/tls.key"

TARGET_PROXY="star-example-com-target-proxy-2"
gcloud compute target-https-proxies update "${TARGET_PROXY}" \
--ssl-certificates "${CERT_NAME}"

これでGKEの環境で証明書を自動更新しつつ、ほかの環境にも展開できるようになりました。実運用はk8sのcronjobを定義して↑の内容をよしなに処理するスクリプトを作れば良いと思います。

Windows10のWSLで開発環境を整える

  • このエントリーをはてなブックマークに追加

サーバはLinux上で動かしたいけど、エディタはWindowsのものを使いたい。そんな環境を作ります。

まず、Windows10のストアからUbuntu18.04をインストールします。

ファイル共有

/etc/wsl.confで以下を設定します。
これはWindowsのドライブを自動でマウントしないようにする設定です。というのも、デフォルトパーミッションが777でやりとりされちゃうからです。あと、間違ってWSL側でWindowsのシステムファイルをrmしちゃうのも怖いので。

/etc/wsl.conf
[automount]
enabled = false

Windowsとファイル共有をするには、drvfsという形式でマウントしますが、これはドライブ単位でしかマウントできません。試しにsubstコマンドで仮想ドライブを作ってみましたが、エラーが出てmountできませんでした。
そういうわけで、Windows側のパーティションをちょっといじって、50GB程度の共有用のドライブを作ります。

/etc/fstabに自動マウントする設定を入れます。metadataは必須、uidgidは自分のユーザーを指定。umaskを設定することで、パーミッション755でやりとりされるようになります。

/etc/fstab
E: /mnt/wsl drvfs defaults,metadata,uid=1000,gid=1000,umask=022 0 0
dfの結果
$ df -h
Filesystem Size Used Avail Use% Mounted on
rootfs 217G 75G 142G 35% /
none 217G 75G 142G 35% /dev
none 217G 75G 142G 35% /run
none 217G 75G 142G 35% /run/lock
none 217G 75G 142G 35% /run/shm
none 217G 75G 142G 35% /run/user
E: 49G 342M 49G 1% /mnt/wsl

GHQのディレクトリをここに設定することで、WindowsとLinux両方でGitできるし、直接ファイルもいじれるし快適です。今はここにいろいろなレポジトリをcloneして、VSCodeやIntelliJ、EmEditorで編集しています。

参考:

Beep音がうるさい

~/.inputrcで以下を設定します。

set bell-style none

Windowsストア版のSlackを使うとプチフリする

  • このエントリーをはてなブックマークに追加

SlackのクライアントをWindowsストア版を使ったら、スタートメニューなどの表示が非常に遅くなりプチフリーズ状態になりました。

UWP(ユニバーサルWindowsプラットフォーム)のアプリ全てを巻き込んでフリーズするようです。
UWP版じゃないものを使えば大丈夫でした。

ひどい目にあった.. 😑

LG GRAM 購入

  • このエントリーをはてなブックマークに追加

Windows10のノートPCを購入しました。LgGramという機種です。

購入の決め手は、次の3点です。

  • メモリが16GB以上積めること
  • バッテリーが長持ちすること
  • 軽いこと

お値段は 155,300円から12,000円引いてもらって、143,300円。これにポイントが10%がついて14,330ポイントなので、実質 128,970円 です。ヨドバシで買いました。

メモリ

この機種は、増設メモリが1スロットあり、SSDも1つ増設することができます。そのため、16GBのモジュールを追加すれば24GBにすることができます。SSDも256GBに加えて、1TBなり2TBなり追加することができます。公式の増設サービスがありますが、それだとメモリ16GBのモジュールは扱っていないので、自分で分解して増設しました。

バッテリー

公称値は27時間となっています。実際は27時間は持ちませんし、増設することでその分消費電力が増えますがそれでも10時間は持つのではないでしょうか。
今仕事で使っているMacが3時間くらいしか持たないので、全然良いと思います。

軽い

1キロ未満。超軽いです。バッテリー入っているのかなと思うくらい軽いです。

だめなところ

キーボードがちょっと打ちにくいです。キーの大きさがすべて同じではなく、右側の記号のところが狭くなっています。文章書くのには困りませんが、プログラムを書くときは結構戸惑います。

増設しよう

※サポート対象外になりますので自己責任で。

増設のために購入したもの。ドスパラで買いました。SSDはWDではなくSAMSUNGにしましたが、理由としてはスペックシートを見た感じでは消費電力が低かったからです。

  • CFD D4N2400CM-16G (DDR4 PC4-19200 SO 16GB) / 19,780円
  • SAMSUNG 860EVO MZ-N6E1T0B/IT(M.2 2280 1TB) / 31,800円

SSDはSATA接続のものを買いました。NVMeもサポートしていますが、その場合はBootDiskが左右逆になります。元々のDiskがSATAなので、NVMeにすると既存のSSDからコピーしないといけなくて面倒なのと、SSDの方が消費電力が半分くらいなのでSATAのままです。

カバーの開け方はこちらの動画を参考にしました。

分解をします!

買った増設物

パチッとはめ込みます

こんな感じでパワーアップしました。

メモリ24GB

SSD1TB追加

Diskの性能を測ってみる

なんか書き込みが遅い…。けどまぁいいか。

rsyslogで処理が詰まらないようにする

  • このエントリーをはてなブックマークに追加

auditdのログをrsyslogでリモートホストに転送しようとしたら、ちょっとログが流れるだけでSSHが出来なくなったりとrsyslogが詰まるせいで、auditdも詰まり、プロセスが起動できない状態になりました。

出力プラグインがメッセージを提供できない場合、メッセージはI先行のメッセージキューに保存されます。キューがいっぱいになると、いっぱいでなくなるまで入力がブロックされます。これにより、ブロックされたキューを使用して新しいメッセーがログに記録されることが回避されます。個別のアクションキューが存在しないため、SSH ロギングが阻止され、SSH アクセスが阻止されるなどの重大な問題が発生することがあります。したがって、ネットワークを介して転送される、またはデータベースに転送される出力専用アクションキューを使用することが推奨されます。

https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/s1-working_with_queues_in_rsyslog

auditd自体の設定は何を記録したいかによるので、この記事では設定は省略して、rsyslogで処理が詰まらなくなる設定を紹介します。

Redhatのページによると、Mainキューで一度全てのログを受けて、ルール毎にActionキューに配置されて、出力されます。各キューにはワーカースレッドがあります。デフォルトの設定は、MainキューがFixedArray queue(10000), ActionキューがDirect queuesです。

/etc/rsyslog.conf

こんな感じに設定しました。
基本はインメモリーのキューを使います。
Mainのキューのサイズを10万に設定。Disk補助も使います。インメモリキューのサイズが5万を超えるとDiskを使い始めます。(Diskは遅いので差分の5万はメッセージバースト用です)。サイズが2万に下がるとDiskを使うのをやめます。

Actionも基本的には同じ戦略です。1桁少なくしています。こちらは確実に保存(転送)をしたいので、shutdown時にはDiskに書き出すようにして、Retryも延々と続ける設定です。

/etc/rsyslog.conf
$WorkDirectory /var/lib/rsyslog
$MainMsgQueueType LinkedList
$MainMsgQueueSize 100000
$MainMsgQueueHighWatermark 50000
$MainMsgQueueLowWatermark 20000
$MainMsgQueueSaveOnShutdown on
$MainMsgQueueFilename main_msg_pool
$MainMsgQueueMaxFileSize 10485760

$ActionQueueType LinkedList
$ActionQueueSize 10000
$ActionQueueHighWatermark 5000
$ActionQueueLowWatermark 2000
$ActionQueueFileName remote_spool
$ActionQueueMaxFileSize 10485760
$ActionQueueMaxDiskSpace 1g
$ActionQueueSaveOnShutdown on
$ActionResumeRetryCount -1

/etc/audisp/plugins.d/syslog.conf

active = yesにする事でaudit logをrsyslogで流すようにします

/etc/audisp/plugins.d/syslog.conf
active = yes
direction = out
path = builtin_syslog
type = builtin
args = LOG_INFO
format = string

/var/log/messagesにaudit logが流れてしまう

audit.logにlocal6を使っている場合、

/etc/rsyslog.conf ではこんな感じの設定になると思います。

/etc/rsyslog.conf
# auditd audit.log
$ModLoad imfile
$InputFileName /var/log/audit/audit.log
$InputFileTag tag_audit_log:
$InputFileStateFile audit_log
$InputFileSeverity info
$InputFileFacility local6
$InputRunFileMonitor

$template audit,"%timegenerated%,%syslogtag%,%hostname%,%msg%\n"
local6.* @@myhost.internal:514;audit
:syslogtag,contains,"tag_audit_log:" ~

# フィルタに local6.none を入れる
*.info;mail.none;authpriv.none;cron.none;local6.none /var/log/messages

↑のフィルタに local6.none を入れて、
/etc/audisp/plugins.d/syslog.confLOG_LOCAL6を入れます。

/etc/audisp/plugins.d/syslog.conf
active = yes
direction = out
path = builtin_syslog
type = builtin
args = LOG_INFO LOG_LOCAL6
format = string

あとは、rsyslogとauditdを再起動すればOKです。

参考

Redhatのページがとてもわかりやすいです。

JJUG CCC 2018 Springでレビューに関する話をしました

  • このエントリーをはてなブックマークに追加

発表スライドが映らなくてめっちゃ焦りました。スタッフの人がとても親切で何種類かの変換コネクタを用意してくれたりと、とても親切で助かりました。

お陰様で満席で立ち見も出て、見に来てくださった方々ありがとうございます。

私はオタクなのでとても早口になってしまい、またマイクも上手く使えずに反省点が多かったなと思います。(資料の枚数が少ないと、ゆったり喋るんですがあのペースで話さないと間に合わなかったorz)

何事に置いてもそうなのですが、対象となる組織、文化、背景、状況が異なれば大事にするべき観点は異なります。制度や仕組み、プログラムやパラメータなど、そのまま持っていくのでは無く、エッセンスを抽出して咀嚼してから必要な養分を取り込むという事が大事です

質問に関する補足です

その前に、
スライドに社名をいくつか出していますが、宣伝目的ではなくて、一般的な広告システムと違って組み合わせ最適化のフェーズ(Allocation)があるから処理が大変なんだよという事を言いたかったのです。宣伝はしてないです!!

レビューの観点には冪等性も大事やぞという指摘

その通りだと思います!!

撤退可能ってどういう事?

ロールバック可能な単位で設計やリリースをしましょうという事です。

具体的には、一度投入して問題があった時に例えば外部リソース(DB)の値を変に上書きしちゃって戻すに戻せないとか、ぶっつけ本番+お客さんにプレス打って戻しづらいとかが無いようにする事です。

ロールバック出来る事と、お金(リソース投入)でスケールする事の2つが担保されていると、失敗時の戦術も増え、比較的気軽に投入できます。

AtomicLongは何故問題なの?

LongAdderというクラスがあるので、そちらを使って欲しいのです。前提としては、readとwriteの比率のうち、writeが圧倒的に多い場合です。

パフォーマンスカウンタを例にすると、Writeは大量に発生しますが、Readは定期的な吸い上げで良いのでそういうケースに効果覿面です。

ELBの後ろにnginxがあるのは何故?

いくつか理由があります。

  • 同じドメインで静的コンテンツをいくつか配信している
  • Gzip処理をアプリケーションの外に逃がすため(アプリケーションの本来の責務ではないため)
  • もぐら叩き対策

もぐら叩きは私が勝手に名付けた現象です。負荷が高まったときにアプリケーションサーバのHTTPスレッドが枯渇すると、ELBのヘルスチェックにも応答が出来なくなってしまいます。アプリケーションサーバは2つのPortをListenしており、ServiceポートとAdminポートがあります。また、ServiceポートだけをInstanceの外部に公開しています。

ELBのヘルスチェックに失敗すると、ELBからInstanceが外れてしまいます。只でさえリソースが足りないのにELBから外れるとさらに状態がきつくなります。複数のInstanceがELBから抜ける、入るを繰り返し行うようになります。このようにシステム全体がなかなか復帰しない状態に陥っていまう事が多々ありました。

これを避けるために(被害を少しでも抑えるために)、

  • NGINXのレイヤーでヘルスチェックを終えてしまう
  • もしくはnginxのreverse proxyでadminポートのpingを叩くようにします。(adminの1つのendpointだけを外に公開する)

もちろん超高負荷時にはHTTPスレッドが枯渇している事に変わりはないので、アプリケーションサーバ側のバックログにも積めない状態になればnginxの408エラーが発生します。ただし、ELBから抜けることは無いので全体の処理量はそのままを維持することが出来、結果的に早く復帰します。

これを個人的にもぐら叩き対策と呼んでいます。

自前実装するのはなんで?

ソースが公開されているものは多いですが、遅かったり、該当のケースに適用しづらいのが殆どです。無ければ作れば良いじゃんの精神です。

例えばJavaのcommonsの実装なんて遅いので、そういうのは自前で実装し直します(主に僕の隣に座っている人が)。昔、”よくこれでcommonsを名乗れるな”と過激な発言をしていた事もありますが、もう大人になりました。

参考↓

ガチレビューするとリリース遅くならないの?

遅くはなりますが、参考までに結構前の資料ですがリリース頻度のグラフです。

レビューが細かすぎない?

オンライン処理、バッググラウンド処理、リクエストの最初の1回だけ、Filterや予測処理のループの中なのかで、ある程度指摘するレベルを変えています。
また局所最適化と思われるかもしれませんが、こういう細かい内容が積み重なってボディーブローのように性能を引きずります。それとは別にアルゴリズムの改善や、アーキテクチャの変更は随時行っています。そういう話はJavaとは関係ない話なのでセッションの中では省きました。

この内容で収益を支えるってどういうこと?

広告業界には、トラブル時に3倍返しという良くわからない習慣があります。(目先の謝罪よりも貸しを作って未来に繋げる仕事をする方が私は好きなので、こういう習慣は嫌いですけどね)
また、ピーク時に一発障害を出すだけで、売上げは大きく下がりますし信用も落とします。それに加えて3倍の損害金。レビューを通して技術力(とビジネス力)を底上げし、プロダクトの品質を保つことが結果として収益を支える事に繋がります。

ある程度の防御力を備える事で、多少の無茶が出来るようになります。

もし、どうやって収益を上げるか、どうすれば収益が倍増するかのお話を期待していた方には申し訳ないです。(そういう話はどうやって広告パフォーマンスを最適化するかという話なので、また分野は別になります。他の場所で別のメンバーが話すかもしれません。)

フェーズにおける戦い方

  • このエントリーをはてなブックマークに追加

仕事をする上で、会社/プロジェクト/チームのフェーズ/状況によって、また人のロールによって戦い方は変わってきます。

状況が違うのに、前と同じ戦い方をしていたらそれは戦術ミスとなります。上手くやってる人のやり方を真似しても上手くいきません。なぜなら状況が違うからです。

フェーズ

今そういうフェーズじゃないのに、なんでそのやり方しているんだろうというのがあります。また新規プロジェクトなのに、既存の大型Projectと同じやり方を持ち込もうとする人もいます。

例えば、大型Projectはお金があるけど新規Projectには予算が殆ど無い場合。お金をかけられないので、何でもかんでもお金で解決する事は出来ませんし、既存の大型Projectで何年もかけた叡智の水準をベースにする事は難しいです。
また、会社としてのリソース配分が決まっている状況下では、予算を大幅にオーバーするとそれはプロジェクトの弱点になったりもします。

会社の規模

会社が小さい時は、各々が何でもやらなければなりません。人が少ないと、成果は個人に対して結びつきやすいです。会社が大きくなり、チームが大きくなると、個人で戦う時代は終わりチームとして戦う時代がやってきます。

状況が変われば戦い方を変えなければなりませんが、この状況下でも個人で戦い続けようとする人は多いです。それは組織としてスケールしない戦い方をしているわけです。メンバーが何をやっているのか分からない/無関心であるチームは、チームである必要はあるのでしょうか?

ロール

個人に紐付くロールが変わっても戦い方は変わります。

一般的役職で言うと部長以上では、何をやっていくかを考える必要がありますが、マネージャーのように大枠のお題が与えられると思っていては物事は何も始まりません。

マネージャー,リード,メンバーによって戦い方は変わります。マネージャーが実務作業を抱えまくるのは簡単にブロッカーになるので最たる悪手です。

マネージャはチームの成果を最大化する事が責務である事が多いと思います。それなのに以前と同じようにマネージャが個人としての戦い方をしていると、一体なにをしているんだ?と思ってしまいます。

そのやり方、本当にチームとしてスケールするやり方ですか?
その行為、ただただメンバーが依存するだけで成長する機会を奪っているだけですよ?
何だったら自分のチームだけで無く、余所のチームの成長機会も奪っていますよ?

以下のページに良い事が書いてありました。

http://makonari.com/?p=275

強固な企業文化をつくるには、
リーダーが熱いスピーチをするのではなく、
社員が気付きを得られる環境を用意することのほうが重要です。

(ちなみに、熱いスピーチや叱咤激励で士気が高まって、うぉお!と動くのは依存なので、個を活かす企業文化つくりたいなら、程々にしておくべきではないでしょうか。何もしないよりは良いですが)