k3sでGitHub Container Registryにおいたプライベートなコンテナイメージをつかう

内容を整理していて気づいたが、本記事には

  • (A) k3sでprivateなコンテナレジストリを使う
  • (B) Github Package Registry (GPR)からGithub Container Registry (GHCR *1 )へ移行する

という2つのトピックが混ざっていることに気が付いた。備忘録も兼ねて先にアウトラインを示しておく。

(A) k3sでprivateなコンテナレジストリを使う

(B) Github Package Registry (GPR)からGithub Container Registry (GHCR)へ移行する

  • Github Package RegistryとはGithub社で提供しているパッケージレジストリ
    • Github Actionsと組み合わることでコードベース管理からレジストリ登録までgithubで完結できる
  • Github Container Registryとは同じくGithub社が提供するコンテナレジストリ
    • オフィシャルアナウンス曰く、

      GitHub Container Registry improves how we handle containers within GitHub Packages. With the new capabilities introduced today, you can better enforce access policies, encourage usage of a standard base image, and promote innersourcing through easier sharing across the organization.

    • とのことだが、後述するとおり基本的にはGPRでDocker imageをホスティングするのと使い方は大きく変わらない。DockerHubやAmazon Elastic Container Registryなどと同じ位置づけ
    • まだpublic beta段階なので利用するには機能の有効化が必要
    • publicなコンテナイメージに対しては無料利用でき、public bataのフェーズの間はprivateなものも無料で利用できる。generally availableになったあともGPRと同じ課金モデルで提供予定とのこと
  • Github Actionをつかってdockerイメージをビルド、およびGHCRへイメージをpushするならdocker/build-push-actionを使うのが簡単

ここで今一度、takeawayを要約すると

  • Github Container Registry (GHCR)に置いたprivateコンテナイメージならk3sから利用できる
  • docker/build-push-action@v2を使ってGHCRにコンテナイメージをpushするには、(1)事前に機能の有効化を行い、(2)tagにはghcr.io/接頭辞としてつける

ということになる。

それでは次から具体的な方法を示していく。

ちなみにk3sはARMでも動くと謳っているが、本記事ではx86系のプロセッサで動く環境を前提とする*2。 本記事で利用したコードをこちらのリポジトリにまとめた。

github.com

1. Github Actionを用いたGHCRへのコンテナイメージ登録

流れとしては以下の4ステップ。

  • 1-a. 適当なプログラムとDockerイメージを生成するDockerfileの用意
  • 1-b. GHCRの有効化
  • 1-c. GHCRの認証用にPAT(Private Access Token)の発行
  • 1-d. GithubActionでdocker/build-push-action@v2を使うための設定

基本的にはリファレンスを張りつつ注意書き程度の補足を記すが、サンプルコードと合わせて確認してもらいたい。

1-a. 適当なプログラムとDockerイメージを生成するDockerfileの用意

Hello!の文字列と現在時刻を4秒ごとに履き続けるjsスクリプトをDockerイメージとして用意する。 長々と述べるよりもcommitログを見るほうがわかりやすいように思う。

1-b. GHCRの有効化

オフィシャルドキュメントにて画像添付で解説されているので参考にされたい。PersonalとOrganizationとで有効化ボタンが2箇所あるので注意。

なお、これを忘れると後述するGHCRへのコンテナイメージpushが失敗する。

1-c. GHCRの認証用にPAT(Private Access Token)の発行

オフィシャルでGITHUB_TOKENではなくPATを利用するように書かれている。

If you want to authenticate to GitHub Container Registry in a GitHub Actions workflow, then you must use a personal access token (PAT).

PATの作成方法はこちらから。

作成したPATをセキュアにGithub Actionに渡すには secrets という仕組みを利用する。

PATとは異なり、secretsはリポジトリごとあるいはOrganizationごとに登録する。登録した値をGithub Actionから${{ secrets.SuperSecret }} のようにして参照する。

1-d. GithubActionでdocker/build-push-action@v2を使うための設定

v1 から v2 へとアップデートした際に大きな仕様変更があり、v2では レジストリログインビルド環境セットアップが別actionに切り出された。 とはいえ設定項目が難しくなった訳でもないので素直にREADME.mdに従ってworkflowを書けば良い。

なお、v1にあった便利な tag_with_ref が使えなくなってしまった。そのためコンテナイメージへのタグ付けは他のアクションで賄う必要がある*3

name: deploy
on:
  push:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_PAT }} # secret経由でPATを渡す
      - name: Get smart tag  # このstepはオプショナル。v1の`tag_with_ref`と同等の機能
        id: prepare
        uses: Surgo/docker-smart-tag-action@v1
        with:
          docker_image: ghcr.io/${{ github.repository }}/test  # `ghcr.io`を接頭辞にすること
      - name: Build and push
        uses: docker/build-push-action@v2
        with:
          push: true
          context: .
          tags: ${{ steps.prepare.outputs.tag }}  # 前ステップ(prepare)で用意したタグ名を使う
          build-args: NODEJS_VERSION=14.10.0-slim
          cache-from: type=registry,ref=ghcr.io/${{ github.repository }}/test:develop
          cache-to: type=inline

2. Privateレジストリの設定とk3sのインストール

k3sでプライベートレジストリを利用するには、事前に所定のパスに設定ファイルを配備する*4。具体的には /etc/rancher/k3s/registries.yaml に以下の内容でファイルを置く。

$ cat /etc/rancher/k3s/registries.yaml
mirrors:
  ghcr:
    endpoint:
      - "https://ghcr.io"
configs:
  "ghcr.io":
    auth:
      username: sat0yu
      password: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

username にはGithubアカウント、passwordにはPATを指定する。ここで使うPATは read:packages 権限があれば十分で、GHCRにコンテナイメージをPushするときのものと異なっていても良い(むしろ別にしたほうが安全。)

オフィシャルドキュメントを見るとhttpsならTLSに関する設定が必須のように誤解しそうだが、上記のようにTLSフィールドを省略しても正常にイメージをpullできた。

registries.yaml が用意できたらk3sのインストールを行う。k3sのインストールはワンライナーで済む。

$ curl -sfL https://get.k3s.io | sh -
[INFO]  Finding release for channel stable
[INFO]  Using v1.20.0+k3s2 as release
[INFO]  Downloading hash https://github.com/rancher/k3s/releases/download/v1.20.0+k3s2/sha256sum-amd64.txt
[INFO]  Downloading binary https://github.com/rancher/k3s/releases/download/v1.20.0+k3s2/k3s
[INFO]  Verifying binary download
[INFO]  Installing k3s to /usr/local/bin/k3s
[INFO]  Creating /usr/local/bin/kubectl symlink to k3s
[INFO]  Creating /usr/local/bin/crictl symlink to k3s
[INFO]  Skipping /usr/local/bin/ctr symlink to k3s, command exists in PATH at /usr/bin/ctr
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s

k3sはkubectlが同梱していて、正常にインストール完了するとkubectlコマンドにsymlinkがはられる。 kubectl get nodeで動作を確認しておく。

$ ls -al /usr/local/bin/kubectl
lrwxrwxrwx 1 root root 3 Jan 18 14:06 /usr/local/bin/kubectl -> k3s

$ sudo kubectl get node
NAME   STATUS   ROLES                  AGE   VERSION
xxxx   Ready    control-plane,master   32s   v1.20.0+k3s2

3. k3sクラスタへのpodデプロイ

全ステップまでですでにk3sクラスタ*5を立ち上げた状態になっている。

マニフェストファイルに特別な記述は必要なく、適切なimageを指定すれば良い。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      containers:
        - name: test
          image: ghcr.io/sat0yu/k3s-with-private-container-images-in-gcr/test:master
          imagePullPolicy: Always
          command: ["yarn", "start"]

あとはマニフェストファイルをapplyしてk8sがコンテナイメージをpullしてデプロイが完了させる。

$ sudo kubectl apply -f deployment.yaml
deployment.apps/test configured

最後に期待通りコンテナが動いているか確認する

$ sudo kubectl get po
NAME                    READY   STATUS    RESTARTS   AGE
test-85b8b88944-grmsk   1/1     Running   0          2m22s

$ sudo kubectl logs -f -l app=test
hello! Mon Jan 18 2021 23:44:58 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:02 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:06 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:10 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:14 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:18 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:22 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:26 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:30 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:34 GMT+0000 (Coordinated Universal Time)
hello! Mon Jan 18 2021 23:45:38 GMT+0000 (Coordinated Universal Time)

*1:GCRだとContainer Registry  |  Google Cloudと紛らわしい

*2:確認していないがDockerfileにあるnodeのベースイメージを適切なものに変更すればARMでも動くと思う

*3:サンプルコードではSurgo/docker-smart-tag-actionを利用した

*4:マニュアルによればk3s起動時に--private-registryオプションでパスを指定することも可能らしい

*5:k3sで立ち上げるk8sクラスタ?

Minikube 1.16.0 (Kubernetes 1.20.0) にアップグレードしたらReadinessProbeがfailした

背景と問題

  • Minikube 1.12.3をデフォルト設定 (Kubernetes v1.18.3)で利用していた
  • Readiness Probeとしてexecフィールドに内製スクリプトを指定していた(スクリプトの実行にかかる時間は約2秒)
  • 先日、Minikube 1.16.0にアップグレードした途端にReadiness Probeが失敗しだした

原因

kubernetes.io

学び

(dockershimのとき) timeoutSeconds フィールドが無視されていた

https://github.com/kubernetes/kubernetes/pull/94115 このPRによって timeoutSeconds が有効化されたのだけど、それと同時にexec型のreadiness/liveness probeにデフォルトで1秒タイムアウトが生まれた

readiness probe および liveness probeのログは journalctl -u kubelet 経由でアクセス可能

readiness/liveness probeのログは kubectl describe pod {YOUR_POD} の最下部にeventとして入ってくるが、情報量が少なくエラー追跡しづらい。せいぜいprobeの成功・失敗程度の情報しか得られず調査が進められなかった。

途中でprobeがkubeletによって実行される(ヘルスチェック対象のpodが実行する訳ではない)ことを思い出し、journalctlで見てみたところ DeadlineExceeded の文字列を発見。その後、https://kubernetes.io/blog/2020/12/08/kubernetes-1-20-release-announcement/#exec-probe-timeout-handling に行き当たり解決することができた。

xxx@xxxxx:~$ sudo journalctl -u kubelet -f
-- Logs begin at Thu 2020-11-05 03:45:00 UTC. --
Jan 12 06:11:47 rams kubelet[2763501]: E0112 06:11:47.967431 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jan 12 06:12:02 rams kubelet[2763501]: E0112 06:12:02.972673 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jan 12 06:12:17 rams kubelet[2763501]: E0112 06:12:17.967784 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jan 12 06:12:32 rams kubelet[2763501]: E0112 06:12:32.966427 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jan 12 06:12:47 rams kubelet[2763501]: E0112 06:12:47.966576 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jan 12 06:13:02 rams kubelet[2763501]: E0112 06:13:02.968304 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jan 12 06:13:17 rams kubelet[2763501]: E0112 06:13:17.965255 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jan 12 06:13:32 rams kubelet[2763501]: E0112 06:13:32.967139 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jan 12 06:13:47 rams kubelet[2763501]: E0112 06:13:47.964895 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded
Jan 12 06:14:02 rams kubelet[2763501]: E0112 06:14:02.969880 2763501 remote_runtime.go:392] ExecSync bc2ef37c1e3bcea3afe12a27c9b52340bb427e111b2e7d6245b12b0b22bb17a5 'yarn mqtt-healthchecker' from runtime service failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded

具体的には以下のいずれかで解決可能

蛇足

readiness probeのexecコマンドで使っていたのは拙作npmモジュール www.npmjs.com

システムの目的を忘れないこと

これは30日チャレンジの17日目(2019/09/25)に書かれた文章です

あらゆるシステムにおいて、ユーザが入力可能な値が増えるほどシステムの複雑性が高まる。 こうして見るとあえて文字に起こすほどのことでもないが、新規のシステムや機能を検討している段階において本主張はしばし見過ごされることがある。 あるいは「そもそもこの値はユーザから入力してもらう必要があるのか」という問いを立て、What‐何を創るかを洗練させることができるエンジニアは少ないのではなかろうか。

筆者はいま、来春リリースを見据えた新たなアプリケーションの開発に携わっており、本日もチームメンバーと機能の検討を行った。 議論の中で、ある機能をスコープから外すと一気に設計がシンプルになるモデルが発見された。

一言で説明すれば、それは時間割を表現するモデルで、機能としてはユーザごとに自らが作成した時間割にもとづく進捗管理を行う。 では一体どうして、時間割モデルをスコープから外すと大きく設計が簡潔になるのだろうか。

続きを読む

リファクタリングをしたいエンジニア

これは30日チャレンジの16日目(2019/09/24)に書かれた文章です

新規事業においては、プロダクトの方向性が変わったり、当初は想定していなかった機能が増えたり、What―何を創るのかという点の不確実性が大きい。 結果として、コンポーネントの責務範囲が曖昧になることでコード全体の品質が低下する。 類似のコードがそこかしこに偏在に、保守性が低く、バグの温床になりやすい。

経験や知識に裏打ちされた洞察でWhatの部分想像したり、適切なデザインパターンを採用することで、不確実性に起因するコード品質の低下を防ぐ努力はできる。 しかしながら、どれほど優れたエンジニアであってに、本質的に新規事業の未来を予見することは不可能だ。 むしろ刻々と変化するビジネスに追従・先回りできるだけの柔軟性と機敏性を保つことに心血を注ぐべきだろう。

一方で、「防げる」種類の問題も存在する。それはかの有名な名言「早すぎる最適化」である。 これはHow―どうやって創るのかに関する不確実性と関係している。 Howは定義からしてWhatに準じる概念であり、Whatが定まらない限りはHowの不確実性も収束しない。 しかし残念ながら、我々エンジニアという生き物はHowの不確実性を正しく認識できないらしい。

さまざまな理由が考えられるだろう。新しいデザインパターンを試してみたい、慣習に従うと数値型よりも文字列型のほうが便利だ、現状のままでも問題はないが煩雑としたコードベースを許せないなど、可能性はいくらでもある。 筆者が一番気に入っている比喩は手術好きの医者の例である。じつは一部の医者は「手術をしたい」と考えているのだそうだ。 自分が患者の身になったところを想像すれば、なんて恐ろしいことかと思うだろう。 しかし「手術が好き」で医者になった人間からすれば、当然手術をしたいと考えるし、そのような診断を下したとしても違和感のない話である。 同様にして、技術が好きでエンジニアになった人間が手術=リファクタリングをしたいと考えるのはごく自然なことだろう。 これが認知バイアスとなって働き、Howの不確実性を正しく認識することを妨げている可能性は否定できまい。

筆者が今、エンジニアとして新規事業の開発に携わっているという話はこれまでも何度か述べた。 じつは数週間前から細々と取り組んでいたリファクタリングがあったのだが、本日リファクタリングを元に戻すことが決定した。

主な理由は2つあった。

  • (1)リファクタリング過程において複数の機能実装間でコードの一貫性が欠ける
  • (2)不確実性の収束が期待できず後ほどまた別の変更が必要になる可能性が高い

(1)に関しては、近々多数のエンジニアがチームにジョインすることが決まっているため、コードベースの理解を妨げる要因を避けたいという外部状況も関係している。 そして注目すべきは(2)の理由で、まさに上で述べた議論に一致している。

今回、筆者が犯してしまった失敗は、WhatとHowの両方で不確実性が高い状態のままリファクタリングを進めてしまったことである。 「リファクタリングは(少なくともコードに改善する限りにおいて)良い習慣だ」という認識をもっており、不確実性の観点を十分に考慮に入れることができていなかった。 本経験をとおして、プロダクト開発における不確実性の考え方を一層深めることができた。

所属判定問題を解くBloomFilter

これは30日チャレンジの15日目(2019/09/23)に書かれた文章です

「ある要素aが集合Aに含まれているか否かを判定する」問題(以下、所属判定問題)が与えられたとき、どのような実装を考えるだろうか。 素朴な方法として「集合Aに含まれる要素を走査して判定する」方法が考えられるが、これはO(|A|)時間必要なため効率的だとは言えない。 本文では所属判定問題に対するひとつの解として、確率的データ構造BloomFilterをみていく。

なお、本文章で説明するBloomFilterをgolangで実装したのでコードと見比べてて読みすすめると理解が捗るかもしれない。

A BloomFilter implementation · GitHub

まずはじめに、確率的データ構造とは一体何を指すのだろうか。

続きを読む

進化可能なGraphQL Schema設計

これは30日チャレンジの13日目(2019/09/21)に書かれた文章です

GraphQLのスキーマ設計において進化可能なデザインを実践するためにxuorig氏が記事を残している。 本文はxuorig氏の記事の要約である。

blog.apollographql.com

GraphQLではAPIクライアント側が必要となるフィールドを明示するため、サーバー側が意図しない(突然フィールドを消すなど)限り、自然と後方互換性を保った開発ができるように設計されている。 また、将来的に削除したいフィールドには “Deprecated” の追加情報を与えることもできる。 そうすることで、スキーマ定義を通してクライアント側に古いフィールドを使い続けないよう促すことができる。

それでもなお、後方互換性を保ちながら開発を進めていくことは難しい。 xuorig氏の記事では、例を交えながら3つのTipsが提案されている。

続きを読む

読み書き禁止ファイルを変更できてしまう謎

これは30日チャレンジの9日目(2019/09/17)に書かれた文章です

Linuxで開発したことがあるエンジニアならpasswdコマンドを知っているだろう。 コマンド名が示すとおり、ログインパスワードを変更するコマンドだ。

ではパスワードを記したファイルは一体どこにあるのだろうか。 これも知ってるエンジニアは多いだろう。 /etc/passwd にはユーザー名、(暗号化された)パスワード、ログインシェルなどログイン時に必要な情報が一行ごとに保存されている。 また最近のLinuxディストリビューションでは、もはやパスワードは/etc/passwdに保存せずに/etc/shadowに移すことで安全性を高めているようだ。

無論、上記ファイルが一般ユーザから不要に読み書きされるようなことがあってはならない。 パーミッション設定によれば、/etc/passwd/etc/shadowともにrootのみ読み書きが許され、 /etc/shadowに至っては一般ユーザは読み込みさえ禁止されている。

root@be281876e6b0:/app# ls -al /etc/shadow /etc/passwd
-rw-r--r-- 1 root root   1236 Sep  4  2018 /etc/passwd
-rw-r----- 1 root shadow  652 Sep  4  2018 /etc/shadow

しかしここでひとつ疑問が浮かんでくる。 読み込みできないはずの/etc/shadowがどうして一般ユーザが実行したpasswdコマンド(/usr/bin/passwd)によって変更されるのだろうか。

一般に、ユーザが実行したプロセスは実行ユーザの権限をもってファイル読み書きを行う。 したがって、例えば読み込み権限のないファイルを表示しようとしても当然エラーが発生する。 それにもかかわらずpasswdコマンドで/etc/shadowの書き換えが可能なのだとしたら、なにか全く別の機構が隠れているのかもしれない。

sat0yu@b61abff74dc3:/$ ls -al hoge 
-rw------- 1 root root 0 Sep 17 14:19 hoge 
sat0yu@b61abff74dc3:/$ cat hoge 
cat: hoge: Permission denied

じつはこの隠れた機構こそが、本文を書くに至った今日の学びである。 答えから先に記すと、それは「セットユーザID (SUID)」と呼ばれる機構だ。

SUIDは読み/書き/実行と並ぶ特殊なアクセス権である。 このアクセス権をもつ実行ファイルは、実行ユーザではなく所有ユーザのアクセス権限で処理を行う。 すなわち、まさに我々がpasswdで抱えていた疑問に対する回答となる。

実際にpasswdコマンドのアクセス権限を確認してみると、通常は実行権限「x」が埋まる箇所(passwdコマンドの所有者権限)には代わりに「s」が記されている。

sat0yu@b61abff74dc3:/$ ls -al /usr/bin/passwd 
-rwsr-xr-x 1 root root 59680 May 17 2017 /usr/bin/passwd

SUIDはpasswdコマンドが代表格としてあげられるが、その他にもWebサーバーのような常時稼働する実行形式にも利用されることがある。 すなわち「起動時にポート80をListenするにはrootが必要だが、一度起動してしまえば権限を降格しても構わない」といったユースケースにも有用なのだ。

ちなみにSUIDは下記コマンドで設定することができる。 あまり頻繁に使うことはないかもしれないが、知識として持っておくと便利だろう。

chmod u+s file

ちなみに、本文を書くきっかけとなった今日の学びは「詳解UNIXプログラミング」から得た。 Webエンジニアとして普段からアプリケーションコードを書いていると、自ずと学びの範囲が狭まってきてしまう。 「詳解UNIXプログラミング」のような色褪せない名著は、知識の外縁を広げて新しい学びを得るための格好の材料となるだろう。