CentOS 8(7以降)のネットワーク周りの設定について

■2020年10月15日 リンク切れ修正およびリンク追加


CenOS6でネットワーク周りの設定を行うには、当然のように /etc/sysconfig/network-scripts/* をいじっていましたが、CentOS7以降はNetwork Managerのコマンドを利用することが推奨されているようです。
いろいろググったり、Redhatのサイトで公開されているドキュメントを読み、なんとなくでコマンドを操作をしていたものをある程度理解できるようになってきました。

その備忘録として未来の自分のために残しておくエントリです。

ネットワーク周りの設定は究極的にいえば、素人的な使い方をしている限り、nmcli コマンドだけで十分かな、と思います。ホスト名もIPアドレスの設定も全部nmcliコマンドを叩けば殆どのケースでOKです。
あ、firewall関連は別ですけどね。firewall関連も firewall-cmd に集約され、バックエンドがiptablesであろうがnftablesだろうが設定方法自体(インターフェイス)は統一されている。
今後こういうフロントエンドとバックエンドを別々にしてフロントエンドは固定し、バックエンドをどんどん改良していく、というのが今風なんだろうな、と思います。

それはさておき。

大前提として、NetworkManagerをデフォルトで有効になっている環境です。そもそもNetworkManagerをsystemctlでenableにしていない環境は想定していません。
あしからず。ご了承のほど。

NetworkManagerの説明はRedHatのドキュメントを読め進めれば他にググって怪しげな情報に振り回される必要はありません。現に僕は思いっきり振り回された側の人間です💦

CentOSは RedHat Enterprise Linux のクローンなので大部分のドキュメントはCentOSにも当てはまるはずです。

第4章 NETWORKMANAGER によるネットワーク管理の開始

第8章 NMCLI の使用

最初ネットで情報を探していろいろ試してみましたが、書いていることがバラバラで全く要領がつかめず、頭の中が???状態でコマンドだけコピペしてきました。
その中で一番わからなくなったのは、nmcli コマンドでよく出てくる、インターフェイス名、デバイス名と接続名の関係が一体どいうことなのか全く理解できていませんでした。
ほとんどの記事でインターフェイス名と接続名を一緒にしているので全く???だったんです。

が、上記RedHatサイトの説明を読むと明快に説明されておりました。やっぱり公式のドキュメントをまず読むべきでした。

そして、nmcliコマンドで指定する device と connection の関係をまず理解することがはじめの第一歩です。
deviceは文字通りデバイス、ネットワークインターフェイスを指す。そしてconnectionは、このdeviceに適用するプロファイルのようなもの、と僕は理解した。
そして特定のdeviceに適切に設定されたプロファイルを適用することで通信ができるようになります。

基本的には一つのネットワークインターフェイス(通常はNICとかWiFiとか)に対して、複数のプロファイルを作り、その時々の環境によってプロファイルを切り替えてネットワークインターフェイスに適用する、という形になっているようです。

このプロファイルの名前こそ、接続名(connection.id)であり、con-nameというのは connection.id の別名であり、正式には connection.idというプロパティ、そしてconnection.idというプロパティ名を使うのが推奨されている、らしいです。

(追記:接続名とかいうとワケワカンネーし僕は混乱するので、接続名(con-name/connection.id)のことをプロファイル名と呼ぶようにしています。このエントリでも接続名の事をプロファイル名と書くようにしました。あしからず。)

ググると、LAN I/Fの設定のコマンドは以下のようになっていることが多いです。

# nmcli c add type ethernet con-name eth0 ifname eth0 

これは非常に分かりにくい、と思います。まず eth0 が2回も出てきてますが、この二つは全く別物です。分かっている人は分かるけど、NetworkManager自体よく知らない人が見てもこの2つのeth0が別物である、なんて知りようがありませんし、大抵の記事ではこの辺の説明がサラッと流されていることが多いです。(eth0のところはens01とかNICの命名規則によります)

あえてこれを正しく書き直すと、

# nmcli connection add connection.type ethernet connection.id eth0 connection.interface-name eth0

ここでこの二つのeth0が何を示すのか? を説明されている記事がほとんどなく、ググって検索下位の方に出てくる記事を辿るとやっと出てくる程度です。こんなの分かってて当然なんでしょうね・・・。
connection.type(別名 type)は、ethernetとかwifiとかvpnとかadslとかの文字通りの意味。ethernetやwifiもまた別名なんですけど、これは上記ドキュメントを参照。
connection.interface-name(別名 ifname)は、下記コマンドで確認できるDEVICE名のこと。

# nmcli device
DEVICE  TYPE      STATE     CONNECTION
eth0    ethernet  接続済み  xxxxxxx
lo      loopback  管理無し  --

ここで CONNECTIONを xxxxxx で伏字にしたのはワケがあります。大抵の記事ではこのCONNECTION名を DEVICE名と同じものにしていることが多いです。
コレ、たぶんOSインストール時に自動設定された情報なんでしょうね。おそらく。だけど、これが結局僕が理解する上で邪魔だった。なまじDEVICE名とCONNECTION名が同じになっているのでワケワカンネー状態に陥った原因だと思う。xxxxxxxは自分で任意の名前をつけるべきところです。
CONNECTION名は、上で何度も言ってきた プロファイル名と同じです。NetworkMangerではプロファイルを複数用意できる。
理由は、環境が変わる毎に設定ファイルをいじるのではなく、環境ごとに複数のプロファイルを用意しておき、その都度DEVICE(インターフェイス)に適切なプロファイルを適用することでラクしよう、ということだと思います。バックアップファイルをパカパカ作るより効率的ですし。
(まぁ、最終的には /etc/sysconfig/network-scripts/ 以下のファイル、その他が書き換わるんですけどね。。。精通しているパワーユーザーにとっては昔ながらのファイル修正とかの方がいいのかも?)

これが一番効率的だなと思うのは、無線LAN(wifi)を利用するときだと思います。無線接続時、接続ポイントによってSSIDとかパスワードとか暗号化方法とか、そういった無線LANの接続ポイント毎にプロファイルを用意しておくと、あとは nmcli コマンドで切り替えるだけで済むようになりますよねぇ。

そしてそのプロファイルは、connection.id(別名con-name)で指定するプロパティ、これは自分で勝手に名前をつけるべきもの。デフォルトでDEVICE名が使われています。分かっている人は別にいいんですけどね。。。僕は分かんなかったです。。。。

僕は HyperVでCentOS8を稼働していますが、この接続(プロファイル)の一覧を表示すると・・・

# nmcli connection
NAME      UUID                                  TYPE      DEVICE
external  fdfa3402-1a88-4e49-8877-ee44480fd405  ethernet  eth0
internal  c541d202-e756-4680-af82-fe9343de6b8c  ethernet  --

と、二つのプロファイルを用意しておいて、ケースに応じて切り替えて使ってます。
上記では externalが eth0 に適用されています。

internalプロファイルをeth0に適用するには、

# nmcli connection up internal connection.interface-name eth0
もしくは省略形を使うと・・・
# nmcli c up internal ifname eth0

と、するだけで再起動してもinternalが適用されます。いちいち down する必要もない。ただし、一つのデバイスに適用できるのは当然だけど一つのプロファイルだけ。
複数のデバイスがある場合で、プロファイルをスワップしたいときも nmcli connection up コマンドを二つ叩けば済みますしね。

nmcliで殆どのネットワーク設定が可能なので、複数のプロファイルを作ってDEVICEに適用する、というのは、以前のような/etc/sysconfig/network-scripts/*の各ファイルをいちいち編集するより非常に効率的な方法だと思います。

たとえば、違うdnsを切り替えたいとき、dnsの設定だけ違う別プロファイルを作っておき、nmcli connection up コマンドで切り替える。
たとえば、インターフェイスのゾーンを切り替えたいときもfirewall-cmdでもいいですけど、nmcliで切り替えたほうがカンタンなような気もします。

まぁ、素人考えですけどね。

プロファイルの各プロパティの値を変更するには・・・(めんどくさいので省略表記を利用します connection ⇒ c , modify ⇒ mod 等)

# nmcli c mod プロファイル名 プロパティー名 値

というように修正していく。具体的なプロパティー名は、show サブコマンドで確認できる。たとえば僕のテスト環境だと・・・

# nmcli c show external
connection.id:                          external
connection.uuid:                        fdfa3402-1a88-4e49-8877-ee44480fd405
connection.stable-id:                   --
connection.type:                        802-3-ethernet
connection.interface-name:              eth0
connection.autoconnect:                 はい
connection.autoconnect-priority:        0
connection.autoconnect-retries:         -1 (default)
connection.multi-connect:               0 (default)
connection.auth-retries:                -1
connection.timestamp:                   1584863103
connection.read-only:                   いいえ
connection.permissions:                 --
connection.zone:                        trusted
connection.master:                      --
connection.slave-type:                  --
connection.autoconnect-slaves:          -1 (default)
connection.secondaries:                 --
connection.gateway-ping-timeout:        0
connection.metered:                     不明
connection.lldp:                        default
connection.mdns:                        -1 (default)
connection.llmnr:                       -1 (default)
connection.wait-device-timeout:         -1
802-3-ethernet.port:                    --
802-3-ethernet.speed:                   0
802-3-ethernet.duplex:                  --
802-3-ethernet.auto-negotiate:          いいえ
802-3-ethernet.mac-address:             --
802-3-ethernet.cloned-mac-address:      --
802-3-ethernet.generate-mac-address-mask:--
802-3-ethernet.mac-address-blacklist:   --
802-3-ethernet.mtu:                     自動
802-3-ethernet.s390-subchannels:        --
802-3-ethernet.s390-nettype:            --
802-3-ethernet.s390-options:            --
802-3-ethernet.wake-on-lan:             default
802-3-ethernet.wake-on-lan-password:    --
ipv4.method:                            manual
ipv4.dns:                               8.8.8.8,8.8.4.4
ipv4.dns-search:                        --
ipv4.dns-options:                       --
ipv4.dns-priority:                      0
ipv4.addresses:                         10.0.0.153/24
ipv4.gateway:                           10.0.0.1
ipv4.routes:                            --
ipv4.route-metric:                      -1
ipv4.route-table:                       0 (unspec)
ipv4.routing-rules:                     --
ipv4.ignore-auto-routes:                いいえ
ipv4.ignore-auto-dns:                   いいえ
ipv4.dhcp-client-id:                    --
ipv4.dhcp-timeout:                      0 (default)
ipv4.dhcp-send-hostname:                はい
ipv4.dhcp-hostname:                     --
ipv4.dhcp-fqdn:                         --
ipv4.never-default:                     いいえ
ipv4.may-fail:                          いいえ
ipv4.dad-timeout:                       -1 (default)
ipv6.method:                            ignore
ipv6.dns:                               --
ipv6.dns-search:                        --
ipv6.dns-options:                       --
ipv6.dns-priority:                      0
ipv6.addresses:                         --
ipv6.gateway:                           --
ipv6.routes:                            --
ipv6.route-metric:                      -1
ipv6.route-table:                       0 (unspec)
ipv6.routing-rules:                     --
ipv6.ignore-auto-routes:                いいえ
ipv6.ignore-auto-dns:                   いいえ
ipv6.never-default:                     いいえ
ipv6.may-fail:                          はい
ipv6.ip6-privacy:                       -1 (unknown)
ipv6.addr-gen-mode:                     stable-privacy
ipv6.dhcp-duid:                         --
ipv6.dhcp-send-hostname:                はい
ipv6.dhcp-hostname:                     --
ipv6.token:                             --
proxy.method:                           none
proxy.browser-only:                     いいえ
proxy.pac-url:                          --
proxy.pac-script:                       --
GENERAL.NAME:                           external
GENERAL.UUID:                           fdfa3402-1a88-4e49-8877-ee44480fd405
GENERAL.DEVICES:                        eth0
GENERAL.STATE:                          アクティベート済み
GENERAL.DEFAULT:                        はい
GENERAL.DEFAULT6:                       いいえ
GENERAL.SPEC-OBJECT:                    --
GENERAL.VPN:                            いいえ
GENERAL.DBUS-PATH:                      /org/freedesktop/NetworkManager/ActiveConnection/2
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/Settings/1
GENERAL.ZONE:                           trusted
GENERAL.MASTER-PATH:                    --
IP4.ADDRESS[1]:                         10.0.0.153/24
IP4.GATEWAY:                            10.0.0.1
IP4.ROUTE[1]:                           dst = 10.0.0.0/24, nh = 0.0.0.0, mt = 100
IP4.ROUTE[2]:                           dst = 0.0.0.0/0, nh = 10.0.0.1, mt = 100
IP4.DNS[1]:                             8.8.8.8
IP4.DNS[2]:                             8.8.4.4

ってな感じになります。

externalプロファイルのdnsを変更するには・・・

# nmcli c mod external ipv4.dns 1.1.1.1
# nmcli c up external
接続が正常にアクティベートされました (D-Bus アクティブパス: /org/freedesktop/NetworkManager/ActiveConnection/3)
# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 1.1.1.1

upサブコマンドでexternal を up すると、即座に/etc/resolveが更新されていることが確認できます。
ほとんどの場合 プロパティーを変更してup すると更新されるみたい。

dnsを一時的に変更したい、というようなときは上記のようにプロファイルを修正すればいいけど、それにともなって gatewayも変更したい、ゾーンも変更したい・・・とかいう場合はプロファイルを修正するのではなく、新しいプロファイルを別につくってそれをdeviceに適用した方が断然効率的です。
その場合、既存のプロファイルをコピーするとラクかな、と思います。上記の例でexternal をコピーしてexternal2 としてそのプロファイルを修正していきます。

# nmcli c clone external external2
# nmcli c mod external2 connection.zone public ipv4.dns "8.8.8.8 8.8.4.4"
# nmcli c up external2
# nmcli d
DEVICE  TYPE      STATE     CONNECTION
eth0    ethernet  接続済み  external2
lo      loopback  管理無し  --

仮想マシンを違うPCから持ってきてインポートしたとき、大概ネットワーク関連を再設定するはめになるんですが、その都度ネットで調べて・・・という風にしてたんですが、NetworkManagerの仕組みをおおよそ理解することで、仮想マシンの引っ越しが躓かずにできるようになった。

編集可能な簡易グリッドビュー画面をイチから作ってみる

直接関係はありませんが、これ の続きです。

ここにきて、カンタンなグリッドを表示する仕事があって、それならセルの内容を書き換えるだけの簡易的なセル編集機能もあればなぁ、と家に帰ってから唐突に思いつき、テレビを見ながら数時間ぐらいで突貫工事で作った。

仕様的には・・・
(1)Excelファイル(もしくはUTF-8なCSVファイル)をブラウザに読込み、
(2)簡単なグリッドビュー表示を行い、
(3)セルをクリックしたら編集し、
(4)リターンキー押下で編集確定、
(5)エスケープキーで編集取消
という感じ。

これならHTMLとCSS、JavaScriptで百数十行ぐらいでいけそうです。
実際業務で使うには、変更内容を追跡し、サーバーへ送信してデータベースに反映して・・・というような事が考えられますが・・・そこまでやっとれん!w

まずは、見てくれ、画面デザインですが、あくまでサンプル的なものなので、ほとんど素のHTMLです。
CSSのGridなんて初めて書いてみました。普段はBootstrapのレイアウトグリッドを利用して画面を作っているんですが・・・CSSのグリッドって、なんか難解ですね・・・ドキュメントを一回読んだだけではオッサンには理解できません(笑) エクセルの読込には、みんな大好き SheetJS ライブラリを利用しました。

ただ、セルを結合しまくったりしてるような複雑なエクセルファイルとか読み込むとたぶん滅茶苦茶になります。そういう複雑なエクセルファイルを持っていないので試していないんだけど・・・

デモ画面はこちら

CSS自体はそんなに凝ったことしてないので、読めば大体やろうとしていることが分かるでしょう。

続いて、肝心のJavaScript ですが、ほとんど各種イベントの処理ぐらいで、これといって特筆するべき事はありません。
ところどころ jQuery 使って見苦しいのですが・・・jQueryだと明らかにタイプ量が減るので僕は多用しています。世間の流れとしてはjQueryは排除されつつあるのですが・・・やっぱりjQueryに慣れきってしまうと document.querySelector なんてタイプするのがすんげぇ面倒。jQueryだと $ の一文字ですよ?イベント登録も on の二文字ですよ? addEventListener とかタイプするのがアホらしくなりますけどね、オッサンは。

いつも利用させてもらってる疑似テストデータを利用してエクセルデータを流し込んでやると、こんな感じです。「こんな感じです」って言われてもね。。。

いちおう、エクセルファイルから簡易グリッドビューにデータを流し込み最低限の編集はできるようになりました。
・・・が、グリッドのHTML的な構造は見直す必要がありますよねぇ。

<div class="row body">
  <span class="cell"></span> ....延々とセルが続く
</div>

一つの要素(div.row.body)にすべてのセル(span.cell)を全部詰め込んで無理くりCSSでグリッド表示させてるだけなんで、<table>要素みたいに、行で分かれていないので、任意の行を選択したいときは、いちいち計算しないといけません。

ま、ちょっとだけグリッド表示したい、って時に使う用、と割り切る。

Windows10でこれだけ知ってれば飯食える(大嘘)基本キーボード操作

Linuxでターミナル中心にCUIでゴニョゴニョやってると、脳内がGUIからCUIになりつつあります。そりゃそうだよね、マウスでごちゃごちゃやるより、コマンド一発叩けば処理完了すんだから。

で、脳内がCUIに侵されつつある今、Windows10でもそれをやろう、というわけです。原点回帰です。MS-DOSは良かったな!的な。
Windows10ではすでにWSLがあるので、テキスト処理はもう快適です。コマンドプロンプトでの type とか findstr とかいう、意味わかんねーコマンドを叩く必要はありません。WSLで cat, grep できます。WSLで標準出力をクリップボードにも転送できるし、もちろん Windowsのコンソールプログラムにパイプで接続できますし、正直、昔のマイクロソフトはなんだったんだ?ってな具合です。

閑話休題。

本題です。脳内がCUIに侵されつつあるので、できるだけ Windowsでもマウスを触りたくないのです。特にエディタ(gVimとか)でガシガシコーディングしてると、キーボードから手を放したくないのです。

よく忘れるので、備忘録として残してます。

キーボードショットカット編
※ WIN は ウィンドウズキーでキーボードのWindowsのロゴがプリントされているキー。

( 1)WIN + SHIFT + S: スクショ
( 2)WIN + PAUSE: コントロールパネル⇒システム
( 3)WIN + . (ピリオド) :絵文字入力パレットを出す
( 4)WIN + , (カンマ) :押している間だけ 全ウィンドウを非表示(デスクトップ上のアイコン確認用途?)
( 5)WIN + +(プラス): 拡大鏡(拡大)
( 6)WIN + – (ハイフン):拡大鏡(縮小)
( 7)WIN + E :エクスプローラのE (エクスプローラ起動)
( 8)WIN + R :実行(Run) の R (コマンドを指定して実行ダイアログ)
( 9)WIN + D :デスクトップのD (全ウィンドウ表示・非表示の切り替え)
(10)WIN + I :INSTITUTE?の I (Windowsの設定を開く)
(11)WIN + G :GAMEのG (ゲーム用の情報FPSとか?を出す)
(12)WIN + V : なんのVだ? クリップボードの履歴表示

POWERSHELL編
※ コマンドの後に パイプで ogv に渡すとグリッドビューウィンドウで表示してくれる
 ( ogvは Out-GridView のエイリアス)

(1)gip LANインターフェイスのIPアドレス他の情報表示( Get-NetIPConfigurationのエイリアス)
   ipconfigコマンドよりこっちのほうがプロっぽいでしょワラ
(2)gin コンピュータの情報一覧 msinfo32のコンソール版?(Get-ComputerInfoのエイリアス)
(3)Get-NetAdapter (認識されているLANアダプター一覧)
(4)Get-NetFirewallRule (Windowsファイヤーウォール規則の一覧表示)

Powershellは.NET Fremeworkのほぼ100%にアクセスでき、NET Frameworkのクラスライブラリのほぼすべてのインスタンスを生成して使用することができる最強のシェルです。またレジストリを普通のファイル・ディレクトリのように扱えたりできて、マイクロソフトが提供するコマンドレット・ライブラリによってWindowsの設定、各種機能のインストールなど、Windowsのすべてを制御できるWindows=PowerShellといっても過言ではないでしょう。 水を飲むように、呼吸するように、Powershellを使えなければWindowsで飯を食うのは難しいでしょう。(そんなこたぁーねぇよ)

コマンドプロンプト編(POWERSHELL上でも可能)WSLなしでもOK
(1)where : unix系コマンドでいうところの which コマンドです。
(2)clip : 標準出力をパイプでつないでクリップボードにコピー。ex) dir | clip
(3)nbtstat -a コンピュータ名 :LAN内のNETBIOSコンピュータ名から情報を取得
(4)curl:ファイル送受信コマンド(最新のWindows10では標準で使えるようになってます)
(5)scp :リモートファイルコピーコマンド (最新のWindows10で OpenSSHがサポートされてます)

要望としてはnetcatも標準でサポートして欲しい。。。powershellではスクリプト組めばなんとかできそうだけどね。netcat相当のコマンドレットも提供して欲しいよ、標準で。

随時追加中。。。

11.6インチのサブモニタ

机の脇において使っていた、メーカー不明のフルセグ対応のポータブルテレビ(10インチ)が壊れてしまい、同じ大きさのテレビを探してたんですが、パナソニックとかシャープにはポータブルテレビがあるにはあるんですが、いずれも無線で信号を送る形式ばかり。
壊れたやつはHDMI入力もできて重宝してたので、HDMI入力ができるものを探してはみたんですが、なんか怪しげな中華製品が多く、どれもレビューがいまいち。

地デジBlulayレコーダーはすでに持っているので、地デジチューナー内蔵は諦めて、今度はHDMI入力ができる小型モニタを探し始めた。
・・・が、10インチ前後となるともう数が限られ、これまた怪しげな中華製品ばかり。しかもレビューもイマイチ。。。

こういうニッチ製品は大手メーカーは儲からないので作って売ってくれない。

で、こういうニッチ製品を多数作ってくれているCenturyの小型モニタに頼る。ヨドバシで29,800円で買う。23インチ前後のフルHD液晶モニタが安いやつだと19,800円ぐらいでいっぱいあるので・・・まぁ、それと比べるとちょっと高い感じがしますが、まぁ一般的にこのサイズのモニタはほとんど売れないと思うので需要と供給のバランスからみてもしょうがないんでしょうねぇ。

Century LCD-11600FHD2
FULL HD。1920×1080のみドットバイドット表示。型番に2とあったので、初代とどこが違うんだろう・・

ノングレア・IPSパネルみたいで、映りは非常にキレイです。ただ同時発色数が少ないのか夕焼けとかのグラデーション表示時、バンディングが出ます。まぁ気になるところはそれぐらいで画質面でこれといって不満はありません。

レコーダーでHDMIをつないでテレビやビデオのモニタとしてみる分には、スピーカーも内蔵されているので問題なし。
ただ、1点、分かっていたことですが、音量の調節が・・・すんげぇやりにくい・・・。パソコンとつないでサブモニタとして使うぶんには音量調節は関係ありませんが・・・、レコーダーとつないで単体で使用すると、音量調節がしにくいのは致命的・・・。レコーダー側のリモコンで音量調節できる機種(そんなのあるのかな?)だと問題ないんだけどねぇ。。。まぁ、音量調節だけ全面のフレームにつけてほしいなぁ・・・まぁ、そんなことしたらコストが跳ね上がるのはわかるんですけどね。。。

画面が思っていた以上に美しい発色なので満足してます😆

テーブルヘッダの固定

2020/12/25 間違い修正

これまで、tableタグ内で thead要素を固定してtbody要素をスクロール可能にするために、
table要素の外側の要素の高さを固定してposition:relative、overflow:autoと指定して、下記のような自作の簡易jQueryプラグインを使用していました。

実際のデモ

HTMLとCSS

jQueryのプラグインは適当に作成。汎用性はなし。

で、最近 display: sticky というのがあって便利だよ、というのを今更ながら知りまして、chrome,firefox用の画面にはこれを使うようしました。
MDNサイトで見ると「粘着位置指定要素 (stickily positioned element)」と言うそうで、要はイケてるサイトでよく見る、
下方スクロールしたら勝手にメニューなんかがページ上部に張り付く(固定される)挙動を簡単に実現できる CSSプロパティみたいです。

stickily positioned elementとは、 position の計算値が sticky である要素です。これは包含ブロックがフロールート (又はその中でスクロールするコンテナー) 内の指定されたしきい値 (例えば top に設定された auto 以外の値など) を達するまでは相対的な配置として扱われ、包含ブロックの反対の端が来るまでその位置に「粘着」するものとして扱われます。

これ読んでも、正直よく分かりません💦 すみません。

で、これをテーブルヘッダに利用しよう、というわけです。
ググると結構記事になっていて、要するに thead要素内の th要素に display:sticky position:sticky を指定して固定するようです。

実際のデモ

theadをstickyにするのが真っ当な感じですが、現在のブラウザのバージョンでは thead を固定することはできないみたい(無視される)。
それと、th要素をstickyで固定しても、th要素に適用されている border は固定されないみたいな挙動になる感じです(若干ずれてしまう?)

/* 実際のCSS */
.table-container {
  position: relative;
  overflow: auto;
  height: 100px;
  width: 80%;
  border: 1px solid #ccc;
}
.table-container table {
  width: 100%;
  border-collapse: collapse;
}
.table-container table > thead {
  background-color: #eee;
}
.table-container table > thead th {
  border-bottom: 2px solid #ccc !important;
  position: -webkit-sticky !important;
  position: sticky !important;
  z-index: 2 !important;
  top:0 !important;
}
.table-container table > tbody {
  background-color: white;
}
.table-container table tr > * {
  text-align: left;
  border-bottom: 1px solid #ccc;
}
.table-container table > tbody > tr:last-child > * {
  border-bottom: none !important;
}

HTMLはほとんど同じ 。最後のscript要素が要らないだけ。

<div class="table-container">
  <table>
    <thead>
      <tr>
        <th>カラム1</th>
        <th>カラム2</th>
        <th>カラム3</th>
        <th>カラム4</th>
        <th>カラム5</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>カラムデータ1</td>
        <td>カラムデータ2</td>
        <td>カラムデータ3</td>
        <td>カラムデータ4</td>
        <td>カラムデータ5</td>
      </tr>
       ...
       ...
    </tbody>
  </table>
</div>

ただし、Internet Explorerには全バージョンを通して非対応なので(display:stickyは単に無視される)、
かっこ悪いけどブラウザで判別し(navigator.userAgent)、IEの時のみjQueryプラグインを使用するように分岐するようにした。

たとえば、ウェブシステムで行をjavascriptで動的に増やしたり減らしたりするときは、行を追加したり削除したりする関数を一個作っておき、もしブラウザがIEなら 複製テーブル、複製元テーブル両方に行を追加したり、削除したり。なんにしてもメンドクサイが。
IEユーザーがいなくなれば、これも必要なくなるかなと。