なんかかきたい

プログラミングなどの個人的なメモやサークル「ゆきいろパラソル」の情報を載せてます

nginxの設定をrspecでテストする

前略。やや複雑な nginx.conf があります。

nginx の設定はたまに変更する必要がありますが、それなりに重要な位置に配置されたnginxだと設定を書き換える際に、既存の構成を壊さないか、あるいは意図した通りに動作するのか事前に調べておきたいということがあります。

本物のnginxを立ててログを調べてもよいですが、手元で再現できて、テストができると便利じゃないかということで、dockerでnginxのやや複雑な設定をテストする環境を作りました。

github.com

続きを読む

MySQLで定期的に SHOW PROCESSLIST を実行して長時間走っているクエリを探す

前略、大きいMySQLのデータベースがあります。すると、大きなデータベースから多くのデータを取得するため、非常に重いクエリが実行されることがあります。 そうするとデータベースが高負荷になるため重い処理を見つけたくなります。 (innodb_query_queued とかが増えて辛くなる)

MySQLには SHOW PROCESSLIST というものがあり、実行中のMySQLのプロセス一覧を見ることができ、 クエリの実行状況や実行時間をみることができるのでスロークエリログより便利なことがあります。 (スロークエリログはクエリが完了した場合に出力されるため実行中やクエリを中断した場合に見ることができない)

しかし、SHOW PROCESSLIST の出力は多いため毎回見るのは疲れます。そこで以下のようなスクリプトSHOW FULL PROCESSLIST の出力をフィルターすると便利です。

require 'mysql2'

MYSQL_USER = ''
MYSQL_PASS = ''
LONG_QUERY_THRESHOLD = 60

client = Mysql2::Client.new(host: 'localhost', username: MYSQL_USER, password: MYSQL_PASS)
client.query('SHOW FULL PROCESSLIST').each do |row|
  next unless row["Command"] == "Query"
  next if row["Time"] < LONG_QUERY_THRESHOLD

  puts ([Time.now] + row.values.map { |v| v.to_s&.gsub("\n", " ") }).join("\t")
end

実行コマンドがQueryかつ実行時間の閾値を超えた場合にのみ出力します。 複数回の出力をフィルターしないので、時間がかかっているクエリはなんども見ることになります。クエリに長い文字列が来ることもあるので、 実運用のことを考えるとある程度の長さで切った方がいいかもですね。

rsyncで圧縮転送したいけどpigzみたいに並列にならなくて困る

ので、tarとsshをパイプでつないで並列で圧縮しながら転送する君を作りました。

tar には指定したファイルのみをアーカイブに含める -T オプションがあるので、 rsync -nの結果をリストにして渡すことで、rsyncの対象になるファイルだけを転送できるようにしてます。

現場からは以上です。

Ansibleのテスト環境をDockerで作ると楽

以前はVagrantを使ってansibleのテスト環境は用意していたんだけど、 vagrantはsnapshotが取れる利点がありつつも、VMを使うのでちょっとというかそれなりに遅くて、 何度も実行するansibleのテスト環境にはちょっと不便だなーと思っていたのですが、 最近Dockerを使ってsshdを起動するだけの環境を用意すれば簡単にテスト環境が作れて便利だったので書いておきます。

Dockerfileを書く

ansibleを流す対象のイメージは DockerfileFROM に書いておけばよくて、 CentOSとかDebianとか好きなOSを選べばいいと思います。

Dockerfileを置いたディレクトリに id_rsa.pub を置いておくと authorized_keys にコピーされるというソリューションです。

FROM amazonlinux:latest

MAINTAINER cyrill

RUN yum install -y sudo shadow-utils
RUN useradd -g wheel ec2-user && echo "ec2-user:ec2-user" | chpasswd ;\
    mkdir /home/ec2-user/.ssh;\
    sed -i -e 's/^\(%wheel\s\+.\+\)/#\1/gi' /etc/sudoers ;\
    echo -e '\n%wheel ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
    echo -e '\nDefaults:root   !requiretty' >> /etc/sudoers && \
    echo -e '\nDefaults:%wheel !requiretty' >> /etc/sudoers
ADD id_rsa.pub /home/ec2-user/.ssh/authorized_keys
RUN yum install -y openssh-server openssh-clients && \
    ssh-keygen -q -b 1024 -N '' -t rsa -f /etc/ssh/ssh_host_rsa_key && \
    ssh-keygen -q -b 1024 -N '' -t dsa -f /etc/ssh/ssh_host_dsa_key && \
    ssh-keygen -q -b 521 -N '' -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key && \
    sed -i -r 's/.?UseDNS\syes/UseDNS no/' /etc/ssh/sshd_config && \
    sed -i -r 's/.?ChallengeResponseAuthentication.+/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config && \
    sed -i -r 's/.?PermitRootLogin.+/PermitRootLogin yes/' /etc/ssh/sshd_config && \
    echo "root:root" | chpasswd
RUN rm -rf /var/cache/yum/* && yum clean all

CMD ["/usr/sbin/sshd", "-D"]

最近気づいたんですが、AmazonLinuxのDocker Imageとか配布されてるんですね。 EC2環境のテストにも便利です。よいですね。

Makefileを書く

毎回 docker build して docker run してもいいんですが、 正直に言えばdockerのコマンドオプションは覚えやすい感じもないので、 シュッと実行できるようにMakefileを書きます。 シェルスクリプトでもいいのではというご意見もあるかなとは思いますが、 make とか make clean とかテスト環境を作ったり消したりするイメージには合ってる感じがして 割と気に入ってます。気にいらないタイプの方は他の好きなツールを使うといいと思います。

.DEFAULT: all

all: container run

container:
    docker build . -t ansible

run:
    docker run -d --name ansible -p 30022:22 ansible

attach:
    docker run -it --name ansible -p 30022:22 ansible /bin/bash

clean:
    docker stop ansible && docker rm ansible

こんな感じのMakefileを置いておいて、makeするとsshができる環境が用意されます。 テスト環境を捨てたいときは make clean して make すると最初からやり直せます。便利ですね。

ansibleのinventoryファイルを書く

docker run するとローカルでsshできる環境ができるので、 localhost:30022SSHするようにinventoryファイルを用意しておいてansibleではこれを指定して実行するようにします。

ansible-playbook -i inventory/local -K -k playbook.yml

終わりに

まあ便利なんですが、Docker環境に移行し切ったらansibleとかいらないのではとか思いつつ、まああってもいいやという感じで悩み中ですね。

お仕事ではMySQLのでかいDBをいい感じにやってるのであんまり関係ないんですが、シュッとかける話がこれくらいしかなかったので今回はこんなところで。

最近、ラ!関係の旅行で福岡と函館に行ってきたのですが、どちらも最の高という感想なので、みんなも行くといいと思います。

月末は沼津花火大会、来月はコミケ。あ、原稿やります。みんな来てくれ〜〜〜〜

現場からは以上です。

Webアプリケーションをデプロイしたくなったのでsupでやってみる

sup

pressly.github.io

Golangで書かれたシンプルなデプロイ要ツール。

機能がとてもシンプルで、SSHコマンドを手で書くよりは簡単に書ける程度で、 逆に機能が少なすぎて必要なことが書きづらいことも。

大雑把に現時点でサポートしている機能は以下のような感じ。

  • リモートでのコマンド実行(並列可能)
  • ローカルでのコマンド実行
  • ローカルからリモートへのアップロード
  • デプロイ対象の切り替え

Capistranoとかをイメージしていると機能の少なさにびっくりするが、 シンプルゆえに書きやすいところもあるし、 例に挙がっているdockerで使うならいい感じかもしれない。

で、そんなデプロイツールを無理やり感もありつつ、 Webアプリケーションのデプロイに使うとしたらどうするかを考えてみた。

あまりよい例ではなさそうなので、本運用には使えないかもしれない。

というか、ここまでやるならcapistranoを使えという話でもあるが、 諸事情でRubyを入れられないので...

gist91393876ec369f1f1701948b4a648771

時間を取るためにファイルを分けるのは正直微妙だし、変数的な何かは使えなかったのかという気はするけど、 そもそもドキュメントが微妙なので、コード読んで雰囲気でやったしまあこんなもんな気はする(本当か?)

あと、自前でシンボリックリンク張り返したり古いreleaseを消すのは面倒。もうちょっと何とかなってほしい気もする。

うん、微妙だな。なしなし。

nginxでupstreamをhttpsにしたいとき

ちょっとだけ知って置く必要がある設定があるので残しておく。あまり難しくはない。

証明書を更新しておく

CentOSならyumDebianならapt、その他はいい感じに。

yum install ca-certificate

nginx をビルドする

がんばってください。

SSLを使うことになるので、http_ssl_moduleは入れておく必要がある。

opensslをリンクする場合、新しいものを使うのがおすすめ。

nginx.confを設定する

クライアント証明書が必要な場合は、ちゃんとした証明書と検証用にtrustedな証明書を設定する。

nginx.conf

server {
  resolver     8.8.8.8;
  listen       8080;
  server_name  _;

  proxy_ssl_protocols TLSv1.2;
  proxy_ssl_ciphers   HIGH:!aNULL:!MD5;
  proxy_ssl_server_name on;
  proxy_ssl_name REPLACE_REMOTE_ADDR;

  proxy_ssl_password_file       /path/to/client_cert_password;
  proxy_ssl_certificate         /path/to/client_cert_pem_with_password;
  proxy_ssl_certificate_key     /path/to/client_cert_pem_with_password;
  proxy_ssl_trusted_certificate /path/to/ca-bundle.crt;

  proxy_set_header Host               $host;
  proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Host   $host;
  proxy_set_header X-Forwarded-Server $host;
  proxy_set_header X-Real-IP          $remote_addr;

  location / {
    proxy_pass https://REPLACE_REMOTE_ADDR;
  }
}

各設定値について

resolver

外に行く場合の名前解決に使うDNS。IP指定の場合はいらないかもしれない。適切なDNSを指定する。

listen

nginxがlistenするポート

server_name

_ なので何でもマッチする。default_serverにするといい気もする。

proxy_ssl_protocols

proxyで接続する際に使うプロトコル。TLS1.2をサポートしているサーバの場合はこの設定でいい。

proxy_ssl_ciphers

proxyで接続する際に使うcipher。リモート側でサポートしているもので強いものを使う。

proxy_ssl_server_name

SNI対応用。proxy_ssl_nameとセットで使う。

proxy_ssl_name

REPLACE_REMOTE_ADDR を実際に接続する先の名前に置き換える。

リモートサーバにつなぎに行くときの名前を置く。SNIを使っているサーバがぼちぼちあるので指定するのが無難。

proxy_ssl_password_file

プロキシに使うSSL証明書のパスワードファイル。

クライアント証明書がパスワード付きならここにパスワードファイルを設定する。

proxy_ssl_certificate

クライアント証明書を指定する。クライアント証明書が不要ならいらない気もする。

proxy_ssl_certificate_key

クライアント証明書を指定する。クライアント証明書が不要ならいらない気もする。

proxy_ssl_trusted_certificate

証明書の検証に使う信頼された証明書を指定する。

CentOSだと /etc/pki/tls/certs/ とかに置いてあるらしい。

ディストリビューションによって違うのでいい感じに設定するか、curlのをもってきてもいい。

proxy_set_header

いい感じに設定する

終わり

この辺触る人あんまりいなさそうなので地味なハマり方をして消耗する人はいそう。頑張って欲しい。

カスタムして作った CentOS7.4 をAWSの M5, C5 で動かすときに落ちた沼について書いておく

今回もそこそこの不幸があった。普通にセットアップする分には簡単な案件だけど、ちょっと面倒なオプションがついて、カスタムしてダレカの作ったAMIをいい感じに新しいタイプのインスタンスで動かしたいというのがきた。やれやれ。

前知識

新しい世代のEC2インスタンスタイプ M5/C5 が東京リージョンで利用可能になりました

NITRO世代(C5、M5)へのEC2インスタンスタイプ変更を試してみた(Amazon Linux編)

  • https://dev.classmethod.jp/cloud/aws/change-type-to-m5-nitro-generation/
    • 元々 M5 タイプに対応していないAMIをアップグレードで対応する場合のことがちょっとだけ書いてある。
    • 記事を読むと、ENAを有効にし、NVMeドライバが使えるようにすれば起動するように見える。
    • AmazonLinuxの例だが、CentOSでも同じようにできるはず。

EC2で稼働中のCentOS 7を新しいインスタンス M5/C5 で起動させる準備をする

  • http://info.m-up.com/news/?category=staffdiary&id=2000001333
    • もうちょっと泥臭い感じの内容
    • AWSのドキュメント通りにコマンドを流してだいたいうまく起動できた、と書いてある
    • 後半kernelを4系にあげてあるが、単に動かしたいだけなら3系でも問題はない
      • 新しいkernelを使うメリットは例えばnvmeドライバの改善、まあ気にしなくてもいいかもしれない

VPC 内の Linux インスタンスにおける Elastic Network Adapter (ENA) を使用した拡張ネットワーキングの有効化

起動しないM5インスタンス

ena supportのオプションはインスタンスで有効にした状態でスナップショットを取るとAMIにも引き継がれ、AMIから起動したEC2インスタンスもENAが有効になります。

OSの起動前に問題が発生した場合、システムログの出力がないため調査が難しいのですが、インスタンスを停止するとシステムログの出力が得られることがあるので、起動してしばらくしてもSSHができない場合は、インスタンスを停止してシステムログを確認する。

今回の場合、 dracut-initqueue timeout で停止しており、 switch_root でEBSに切り替えようとしているところでパーティションが見つけられずエラーになっていた。

ログを見た感じ、疑われたポイントは、fstab、grub、initramfs、nvmeドライバ。

EC2 HVMの仕組み

仮想化方式(HVM と PV)についてまとめ

HVMインスタンスの仕組みは、物理マシンに近く、ルートデバイスにあるブートローダーからkernelを起動する。裏側はXenだけど。

なのでシステムログをよく見ると

  • ブートローダ (grub) から grub2 を起動
  • /boot/grub2/grub.conf を読んで、 kernel vmlinuzinitrd initramfs.img みたいなことが行われて
  • initramfsから起動した小さなOSがルートデバイス/ へ switch_root
  • / からOSを起動

という流れになっているのがわかる。

dracut-initqueue timeout は initramfs のところで起きているのでその辺りを調べていく。

dracut

RHEL系のディストリビューションで使われているinitramfs生成ツール。

結論を書くと、dracutの設定にnvmeドライバの追記をしていなかったため、/を見つけられず起動に失敗していただけだった。

dracutにドライバを含めるには、 /etc/dracut.conf.d 以下にファイルを置く。例えば /etc/dracut.conf.d/nvme.conf として以下のファイルを作成する。

add_drivers+=" nvme "

その後、 dracut を実行する。

dracut -v -f

これで initramfs が再生成され、中に nvme ドライバが含まれるようになる。確認したい場合は、cpioとかを使って生成されたinitramfsを展開すれば確認できる。

終わりに

疲れた。勘弁してくれ。

まあ、この辺は以前にinitramfsとかブートローダとかnvmeドライバとかその辺を調べてあったから比較的早く解決できた気はする。

どっちかというとPXEBOOT周りを触ってる際に覚えたことだけど、事前知識がなかったらこの問題は相当難しい問題だったと思う。難of難。

AWS触るときにもこの辺りの知識が必要になるってのは意外だけどそんなもんかな。いや、普通にやってたらハマらないしこの辺触らないんだよな...