技術的な話題 に関する投稿を表示しています

needs-restarting

某所の某 CentOS7 なサーバー、たまに何も考えずにログインしては yum update -y して、更にたまに reboot してるけどこれもはや cron で自動でよいのではと思ったのでそうする。

そういえば、アップデート内容によっては再起動が必要かを調べてくれるツールがあったなあぼんやり思い出した。
調べてみたら needs-restarting だった。そのままだ。

yum-utils に入っているらしいので、それでいれる。

$ sudo yum search needs-restarting
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
...
============================================================================================================= Matched: needs-restarting =============================================================================================================
dnf-plugins-core.noarch : Core Plugins for DNF
python2-dnf-plugins-core.noarch : Core Plugins for DNF
yum-utils.noarch : Utilities based around the yum package manager

$ sudo yum install yum-utils
...
$ needs-restarting --help
Usage:
    needs-restarting: Report a list of process ids of programs that started
                    running before they or some component they use were updated.

Options:
  -h, --help        show this help message and exit
  -u, --useronly    show processes for my userid only
  -r, --reboothint  only report whether a full reboot is required (returns 1)
                    or not (returns 0)
  -s, --services    list the affected systemd services only

フルリブートが必要なのか、特定のサービスだけでいいのか、をオプションで切り替えられる模様。

こうしておいた。

$ cat /etc/cron.daily/yum
#!/bin/sh
yum update -y

$ cat /etc/cron.daily/needs-restarting-reboothint
#!/bin/sh
needs-restarting --reboothint

$ cat /etc/cron.daily/needs-restarting-services
#!/bin/sh
needs-restarting --services

確認した結果、全自動で再起動するようにしてもいいけど、何かが何かに引っかかって立ち上がらなくなったりして、まったく気付かないというのは少し怖いので、とりあえずは通知するだけにしておく。
cron はすでに通知…、というか MAILTO にかかれていて、本文に色々書かれていたので、このままで OK ということにしておく。

Kubernetes の Liveness と Readiness

簡単にまとめると Liveness に失敗したらザキ Readiness に失敗したらやけつく息。…いや逆にわからんような気もする。

Liveness は Pod が稼働できるかどうかの判定をする。判定に失敗した場合は Kubernetes がその Pod を作り直す。
Pod が提供するべき最低限の機能すら提供できない場合にこのチェックを失敗させるべきだろう。
たとえば自分のアプリケーションが待ち受けるサーバーを提供するんだけど、そこへの疎通が一切できないとか。それが最初から起きるならアプリケーションは起動しないだろうが、起動してから何かしらの理由でサーバが落ちるといったことが無いわけではないはず。

Readiness は Pod と疎通できるかの判定をする。判定に失敗すると Kubernetes が Service から切り離す。このとき Pod は生きたままになる。
デッカい初期データを読み込むなどと起動に時間のかかるようなアプリケーションだったとき、アプリケーションは無事に起動する(=Liveness をパスできる)が、リクエストを受け付けることができないので、そういった場合は Readiness を設定して、Serviceから切り離されるべきだろう。

 

あと、他のアプリケーションに依存している部分を Liveness や Readiness には入れないほうがよいはず。
その確認をいれていると、回り回って全ての依存関係が全滅するとか Liveness / Readiness のデッドロックが起こってしまいそうな予感がする。

A は B への疎通確認を Liveness にいれて、 B は C への疎通確認を Liveness にいれて、としたときに、仮に C が何かの影響で疎通できないような状態になると B が作り直され A が作り直され、結局 A → B → C とつながった機能が提供できなくなる。

接続できるけど Unavailable な状態、 HTTP でいうと 5xx なステータスを、アプリケーションあるいはプロキシするサイドカーな何かで返すほうがよいのでは?
ほら nginx も upstream につながらなくなっても nginx は終了せずに「upstream に繋がらないけど」というエラーが出るじゃん?
これを各 Pod でもできるようにしたほうがいいと思うのよ。

 

Liveness も Readiness もどちらも、監視までの時間、監視する間隔、判定失敗とするまでの許容する失敗回数といったオプション値を設定できる。
このあたりは公式ドキュメントを見るとまとまってるのでこまかい話はそっちを見るようにしたらよい。
Configure Liveness and Readiness Probes - Kubernetes

それと Liveness も Readiness も、どちらも時間や間隔の調整は必要なものの、基本的にはヘビーな処理が発生しないようにする必要がある。
Liveness や Readiness のチェックでかかる負荷によって、自分自身をを失敗させる原因にならならないようにするために。

brew まとめて更新する

とりあえず更新するようの便利セット。 fish 使ってるので function にしてるけど単に alias でもよいとおもう。

function brews
  brew update
  brew upgrade
  brew cask upgrade
  brew cleanup
  brew doctor
end

話変わるけどこういうことやるとポータビリティ上がってよさそう。

brew list > brew-list.txt
brew cask list > brew-cask-list.txt
mas list > mas-list.txt

やってみた。

brew list

apr
apr-util
aspell
autoconf
bash-completion
brotli
c-ares
clang-format
composer
curl-openssl
fish
fontconfig
freetds
freetype
gd
gdbm
gettext
ghq
git
git-chglog
glib
glide
gmp
go
graphviz
gts
icu4c
jansson
jasper
jemalloc
jo
jpeg
jq
kubectx
kubernetes-cli
kubetail
libev
libevent
libffi
libiconv
libidn
libmetalink
libpng
libpq
libssh2
libtiff
libtool
libyaml
libzip
make
mas
mcrypt
mhash
mysql
netpbm
nghttp2
oniguruma
openldap
openssl
pcre
pcre2
peco
php@7.1
pkg-config
pv
python
python@2
readline
rtmpdump
socat
sphinx-doc
sqlite
sshfs
stern
telepresence
the_silver_searcher
tidy-html5
torsocks
trash
tree
unixodbc
watch
webp
xz

brew cask list

charles
clipy
docker
firefox
goland
google-chrome
google-cloud-sdk
iterm2
java
minikube
osxfuse
phpstorm
sequel-pro
slack
station
visual-studio-code

mas list

540348655 Monosnap (3.5.7)
682658836 GarageBand (10.3.2)
408981434 iMovie (10.1.11)
409201541 Pages (8.0)
409183694 Keynote (9.0.1)
539883307 LINE (5.15.0)
409203825 Numbers (6.0)

なるたけ brew に載っておくと管理がお手軽でいいよ、と聞いてからできるだけ移した。

語彙崩壊してる

kubectl port-forward to Service (追記Deployment も可)

kubectl port-forward って pod に対してだけしかできないと思っていたが Service に対してもできたみたい。

$ kubectl port-forward svc/my-service 5000

このとき、ポートフォワードを開始するタイミングでだけ k8s の名前解決が行われて、フォワードする先を決定する。
サービスに対してフォワードしているわけではないので、フォワードしたポートに何度リクエストを送ってもロードバランスされない。

Issue にもコメントがあるが port-forward はもともとがそういう用途ではなく 1 つの pod に対してフォワードするので、サービスに対して完全にフォワードするには pod がいなくなったときに再接続したりバランスするようにしたりなんだり、多くの機能追加になるので、別にしようやという話がある。

kubectl port-forward should allow forwarding to a Service · Issue #15180 · kubernetes/kubernetes · GitHub


追記: Deployment も対象にできたのしらなかった。

Use Port Forwarding to Access Applications in a Cluster - Kubernetes

Withなんちゃらなオプション( Functional Option Pattern )

Go 文化なのかわからないけど New とか構造体作れる関数や初期化する関数へのオプションを設定するためのコールバック関数を設定するキメ文句をよく見るような気がする。

func WithInsecure() func(*Config) {
    return func(cfg *Config) {
        cfg.insecure = true
    }
}

func WithUserAgent(ua String) func(*Config) {
    return func(cfg *Config) {
        cfg.userAgent = ua
    }
}

func main() {
    req := NewRequest(
        WithInsecure(),
        WithUserAgent(),
    )
    req.Send()
}

こういう感じのやつ。

オプションを外のパッケージに対して隠せるっていうメリットがあるのかな。
流れに乗っておくと、雰囲気で利用できて便利そうだなーって思った。


追記:2019年3月4日
これは Functional Option Pattern だ、という話をまったく違う流れで社の Slack で知った。

Functional Option Pattern

Config 構造体でがんばって持つのもいいけど、文量増えてきたり、外に公開するような API であった場合にちょっと不便かなあと思っていたので、このデザインはいいなと思った。

envconfig の

kelseyhightower/envconfig: Golang library for managing configuration data from environment variables

ざっくり説明

環境変数を読み込んで構造体におとせるやつ。
構造体のタグに環境変数名をいれると、任意の環境変数をパースしたときに展開してくれる。
書かなくてもよしなに動く(すごい)

構造体は入れ子にできる

タグに必須有無やデフォルト値をかける。

使い方

リードミーに書いてあるので読むべし。
envconfig/README.md at master · kelseyhightower/envconfig

よしなにしてくれるが、毎回タグを書くようにしたほうが事故防止や分かりやすさからもいいと思う。

type (
    // Config of the app.
    Config struct {
        // Service config
        Environment string `envconfig:"ENVIRONMENT" required:"true"`
        LogLevel    string `envconfig:"LOG_LEVEL" default:"ERROR"`
        Version     string `envconfig:"VERSION"`
    }
)

なぜ使うか

ツール類なら flag パッケージで十分だと思う。
アプリケーションとしてだと環境変数に値をいれておいて設定を変えたい場合があるのでは。
DB の接続先とか。

Laravel のときにお世話になってた dotenv もそういう感じよね。
Configuration - Laravel - The PHP Framework For Web Artisans

Go でも dotenv が出来るやつある。
joho/godotenv: A Go port of Ruby's dotenv library (Loads environment variables from .env.)

これは構造体にシュッとならないので map 経由してよしなにやる必要ありそう。
envconfig は構造体にシュッとなって便利そうではある。

どうやって実装されているのか

これは完全に興味で羽を伸ばしてみる。

このソースだけで完結できる。意外と短い?
envconfig/envconfig.go at master · kelseyhightower/envconfig

Process がエントリポイントで、すぐに gatherInfo を呼ぶ。
この中では、引数のバリデーションと、渡された構造体を reflect パッケージを使って、タグや形、キーたる名前をパースしてる。要は構造体の情報から必要なものだけ集約したもの。それが varInfo のスライスになる。
このとき、構造体のタグに split_words がついていると、ベストエフォートでキャメルケースを、スネークケースに変換してキーを設定する。

構造体を持っている場合、それがデコード出来ない形式なら再帰的にパースを試みる。
がてことは割と雑多に構造体を放り込んでもよしなに動いてくれそうではある。

このようにして、読み込んだデータを格納する構造体についての分析が終わったら、いよいよ環境変数から値を読み込む。

すでに確認されているキー名で lookupEnv(os.getenv にあたる)をして、環境変数から読み込み、なければデフォルト値を試みて required の判定をする。
読み込んだ値は文字列でしかないので、構造体の形情報に合わせて変換していく。それを processField でやっている。

このとき、対象が Decoder か Setter を実装していると、それを使って構造体のフィールドを戻そうとする。あるいは TextUnmarshar,BinaryUnmarshaler でも。

サンプルだと IP アドレスな文字列から net.Ip 構造体に落としている。
たとえばだけど暗号化した値を渡して復号化することもできし json を入れて構造体に戻すこともできるし DB 接続の情報をいれて sql.DB のクライアントを作ったり API の接続情報から http.Client を作ることもできる。
もはや設定値から作るようなものなら何でも作れる。
やりすぎるとコード読む側が苦になりそうだけど…。

話をコードに戻すと、あとはエラーの場合がある程度で、以上。

Process がエントリポイントと最初に書いたけど MustProcess というのもある。
こっちはエラーになったら panic する。

regexp.MustCompile なんかと同じ。

おわり。

cron って +x ついてないと動かないのね

Let's Encrypt の証明書が切れるよーってメールが度々来ていて certbot の cron 設定しているはずなのになんだかなあと思ってその都度手動でやっていた。

cron はちゃんと動いている cron.daily は anacron に設定されている。

$ cat /var/log/cron* | grep daily | head
Dec 16 04:30:07 ... run-parts(/etc/cron.daily)[15097]: finished logrotate
...

$ cat /etc/anacrontab
...

#period in days   delay in minutes   job-identifier   command
1    5    cron.daily        nice run-parts /etc/cron.daily

...

どうやら+x がついていないのが原因っぽいなってことがわかった。

$ ls -al /etc/cron.daily/
...
-rw-------   1 root root   46 Nov 18 22:05 certbot
...
-rwx------   1 root root  219 Jul 15 21:16 logrotate
...

ので+x をつけ、あとで様子を見ることにする。

$ sudo chmod u+x /etc/cron.daily/certbot

anacron は run-parts でプログラムを起動する、というかそう設定に書いてある。

第 21 章 システムタスクの自動化 - Red Hat Customer Portal

Linux 2.6 - man page for run-parts (linux section 8)

run-parts runs all the executable files named within constraints described below found in directory directory. Other files and directories are silently ignored.

だそうで cron.daily のディレクトリに何かしらのスクリプトなり実行ファイルを置いていても、実行することの出来ないものであれば無視されるのは正しい動き。
verbose オプションつけられたら cron 側のログ的なところにもうちょっと何かでてくれそうな気がする。
ちなみにこのサーバにいる run-parts にはオプションが list と test くらいしかなかった。多分 cron か anacron が古い…?

$ man run-parts
...
SYNOPSIS
       run-parts [--list|--test]<directory>
...

適当に yum update しても出てこない。
調べてみると久しく更新されていないっぽい。

crontabs-1.11-6.20121102git.el7.noarch.rpm CentOS 7 Download

パッケージに含まれる run-parts を更新していないってことなのかな、わからん。

まあいっか、ということで追いかけるのはおわり。

dd-trace-go を使って Datadog の Span をモリモリ作る

import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"

func hoge(ctx context.Context) (err error) {
    span, ctx := tracer.StartSpanFromContext(
        ctx,
        "do hoge",
    )
    defer func() { span.Finish(tracer.WithError(err)) }()

    err = somethings1(ctx)
    if err != nil {
        return
    }

    err = somethings2(ctx)
    if err != nil {
        return
    }

    return
}

こういう具合に tracer.StartSpanFromContext してあげるとよい。
StartSpanFormContext を使うと context に実行時の span を保持してくれて、親子関係を構築してくれる。

で、これをサードパーティなパッケージが絡んだところや、その他いろいろにモリモリ適用しようとすると大変そうだなあと思ったので、クロージャでシュッと StartSpanFormContext を書いてみた。

sters/wrapspan: for https://github.com/DataDog/dd-trace-go/

別に何も難しいことはやってない。


dd-trace-go に contrib パッケージがあるように、対象のソレをラップした構造体なり関数なりを作るほうが、そういうパッケージとして切り出せるしオプションの勝手もよいだろうしコードの見通しもよくなりそうだ、と、この記事を書いているときに思った。

Readme に書いた例だとこういう感じ。

func Xsomething1(ctx context.Context) (err error) {
    span, ctx := tracer.StartSpanFromContext(
        ctx,
        "span-1",
    )
    defer func() { span.Finish(tracer.WithError(err)) }()

    return X.something1(ctx)
}

func Xsomething2(ctx context.Context) (err error) {
    span, ctx := tracer.StartSpanFromContext(
        ctx,
        "span-2",
    )
    defer func() { span.Finish(tracer.WithError(err)) }()

    return X.something2(ctx)
}

func Xsomethings(ctx) (err error) {
    span, ctx := tracer.StartSpanFromContext(
        ctx,
        "span-somethings",
    )
    defer func() { span.Finish(tracer.WithError(err)) }()

    err = X.something1(ctx)
    if err != nil {
        return
    }

    err = X.something2(ctx)
    if err != nil {
        return
    }
}

よさそう。

Windows で dep ensure したときに File name too long が出るのを解決した

どうやら Windows はパスの文字数が 260 文字という制限があるらしい。

が、それをレジストリで設定変更できる。ここを 1 にしたらよいとのこと。

\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem

LongPathsEnabled = 1

Pro やら Enterprise やらだとグループポリシーの設定が必要そう。

Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem

Enable Win32 Long Paths

see: Long File Names?


そしてさらに git の設定が必要そう。

git config --system core.longpaths true

see: Sorce Tree で"Filename too long"のエラーが出た時の対処 - Qiita

WordPressでプラグイン無しmarkdownを使って記事を書けるようにする

以前やろうとしたときはうまく動かなかった記憶がある。ので諦めて自分で用意した話。

記事を書くときに Markdown で書き、何かしらの手段を講じて、実際に見えるときに HTML である状態を作ります。
それにはいくつかのアプローチが考えられます。

入力画面で Markdown を使い WordPress へ保存するデータも Markdown にする

この場合 Post のメタデータとして Markdown であることを伝えて、テンプレートを表示する際に Markdown であった場合に PHP あるいは Javascript で Markdown から HTML に変換します。

PHP でやる場合 Composer でテーマ開発をする路線に乗り parsedown などを使うのがお手軽でしょう。
erusev/parsedown: Markdown Parser in PHP

そのほかにも Packagist を探すといくつかのライブラリが見つかります。
Packagist

Javascript でやる場合、テンプレートの出力を一度見えないようにしておいて HTML で整形できたら見せるようにすると綺麗です。
もちろんあえて Markdown を表示しておく、というのもアリかと。

ライブラリとしては marked があります。
markedjs/marked: A Markdown parser and compiler. Built for speed.

cdnjs を探すとほかにも見つかります。
cdnjs.com - The best FOSS CDN for web related libraries to speed up your websites!

PHP か Javascript かどちらを取るかというと、どっちでもよいと思います。
PHP の場合はサーバサイドの処理になるので、例えば nginx なり CDN なりで HTML にレンダリングしたものに対してキャッシュが利きます。
Javascript 上でやる場合、クライアントのリソースを使うので、大きなコンテンツであればあるほど HTML が表示されるまでに時間がかかってきます。
(ServiceWorker を使ってキャッシュを持つなどしてもよいかもしれません)

そしてこのパターンで PHP, Javascript 問わずに気を付けるべきは、もしも仮に第三者が記入できるエリアにも適用された場合に XSS などのバグおよび脆弱性につながりやすくことも挙げられます。
いやそもそもそういうことやりたいという事情はそんなにないか。

入力画面で Markdown を使い WordPress へ保存するデータを HTML にする

このアプローチは現在のごみばこいんでも扱っています。
WordPress 管理画面上の入力画面において Markdown で書いたものを HTML に変換するボタンを作っています。

具体的には以下のようなコードを functions.php に書いています。

// add media area buttons for admin
function hook_media_buttons() {
?>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.5.2/marked.min.js" integrity="sha256-zFUosuESzULu5P+SZdjRRtBZR8+1u5RZDlbt3Q5KL8U=" crossorigin="anonymous"></script>
    <button type="button" class="button Markdown-to-html">
        <span class="dashicons dashicons-thumbs-up"></span>
        Markdown to HTML
    </button>
    <script type="text/javascript">
        document.querySelector('.Markdown-to-html').addEventListener('click', () => {
            if (marked === null || marked === undefined) {
                alert("marked not loaded...");
                return;
            }

            let text = $('#content').val();

            // Markdown to html
            text = marked(text, {
                headerIds: false,
            });

            // More newline
            text = text
                .replace(
                    /(<\/(p|table|h\d|pre|ul|ol)>)\n?/g,
                    (_, a) => {
                        return `${a}\n\n`;
                    }
                )
                .replace(
                    /\n\n+/g,
                    '\n\n'
                );

            // Replace value
            $('#content').val(text);

            return false;
        });
    </script>
<?php
}
add_action('media_buttons', 'hook_media_buttons');

若干おまけもついていますが、これで Markdown を HTML にするボタンを記事編集ページに追加できます。
下書きを WordPress 上なり、別の場所なりで書いてから、実際に記事公開する前にポチっとやって様子を見てから公開するようにしています。公開アクションにフックして自動で変換されるようにすることもできると思います、今そうしていない理由は特にないです。

入力画面で Markdown を使わない

要は別の Markdown なエディタを使って、整形された HTML を WordPress にぶっこめば良いという話です。パッと思いつくのはこのあたり。

そのほか考えるとよさそうなところ

超速 web 体験を目指すなら、可能な限り前処理を済ませたほうがよいと思うので、クライアントの手元で動かすことをなるべく減らす = JavaScript 側でやるという選択肢ははずれてくるのではないでしょうか。
ServerSideRendering 的な話と捉えてもよいっぽい。保存するデータはどっち?という話は編集ページの利便性によってなんでもいいかなーと。

常に Markdown で書きたいなら Markdown で保存できるようにしたほうが良いだろうし。まあ何も気にせずプラグインぶっこむのがお手軽簡単でいいんじゃないですかね~~(結論)

1 2 3 4 5 6 7 8 9 10 11 12 13