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

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 によるネットワーク管理の開始

最初ネットで情報を探していろいろ試してみましたが、書いていることがバラバラで全く要領がつかめず、頭の中が???状態でコマンドだけコピペしてきました。
その中で一番わからなくなったのは、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の仕組みをおおよそ理解することで、仮想マシンの引っ越しが躓かずにできるようになった。

LAMP環境からSQLServerインスタンスの接続ポート番号を取得する

制作中の業務用ウェブシステムがSQLServer縛りなので、ここ2~3年急にSQLServerをいじることが多くなってきています💦 別の大半の仕事は MySQLかSQLiteなのですが、やはり WindowsのデファクトスタンダードのDBMSはSQLServer、ツール類は断トツに整備されていじりやすい!

そんなことはともかく。いつも前置きが長いです。すみません。

通常SQLServerインスタンスへコンピューター名(or IPアドレス)\インスタンス名 で接続するので特に接続ポート番号(動的ポート)を意識せずとも接続できると思われます。

ただ、僕はLAMP環境メインでやってきているので、ASP.NETとかいうのは肌に合いませんし💦 今更関わろうとも思いません。(C#でのデスクトップアプリやツール類の作成は趣味でやっているのですが・・・)
Linux側からWindows Server+SQLServerにする場合、大抵の場合SQLServerインスタンスのポートを固定して、ファイヤーウォールでそのポート番号を開けて・・・というやり方が一番ラクですし、今開発中のものもそういう環境でやると思います。

で、ふと思ったのですが、動的ポートって「動的」って言うんだから変わることを前提にしないといけないなぁ・・・と漠然と思いまして、カンタンなSQLServerインスタンスの情報を取得する方法がないかと思いまして・・・このエントリはその時に調べた内容です。

Windowsクライアントメイン(大概がAccessランタイムアプリ)の職場環境だと、大抵の場合「名前付きインスタンス」で動的ポートになっていると思います。職場環境によってはポート固定への変更を頑なに拒まれることもあろうかと思いますし、結局のところ一体何番で接続していいのか困ることがあります。

とりあえず、ググるが、SQLServer Browserサービスに直接問合せることで、SQLServerインスタンスの情報を得られますよ~、的な、事しかわかりません。

マイクロソフトのドキュメントによると、SQLServer Browserへの問合せはSSRP(SQL Server Resolution Protocol)というプロトコルでアクセスするみたいです。
[MC-SQLR]: SQL Server Resolution Protocol

これを読むと、UDP/1434ポート(SQLServer Browserサービス)へ、下図のように、インスタンス名の前に0x04をつけて問い合わせると、セミコロン区切りで情報を投げてくれるそうです。

(上記URLのドキュメントより抜粋)

なんだ!カンタンじゃん!(ほんとかよ。。。)

もちろんですが、SQLServerホストのファイヤーウォールでUDP/1434ポートを開けておく必要がありますが、普通は空いているでしょう、さもないと外部クライアントから一切接続できなくなるし。

とりあえず、ターミナルからローカルで動いているSQLServer(Deverlopper edition)ホストへ netcat で叩いてみる。
インスタンス名は既定のインスタンス(MSSQLSERVER)。

$ echo -ne "\04mssqlserver" | nc -u localhost 1434
Y ServerName;XXXXXX;InstanceName;MSSQLSERVER;IsClustered;No;Version;14.0.1000.169;tcp;1433;;

一応ローカルで動かしているSQLServerは固定ポートで動かしているので 1433になってまつね。。。
これだけだとプロトコル・ポート番号とインスタンスのSQLServerのバージョンなどが取れます。IsClusteredは・・・なんなんでしょうね?無知ですみません。

結果のフォーマットは、先頭(0バイト目)に1バイトの識別記号?の\x05、1バイト目にペイロード長(終端ヌルを除く)を示す16ビット整数、3バイト目からペイロード・・・という風な感じ。ペイロードはセミコロン区切りで、名前1;値1;名前2;値2;….;; という風なフォーマット。これなら簡単に連想配列かなんかにパースできます。

んで、インスタンスの簡易情報を取得するコードを書いてみる。

<?php
/*************************************************************************************
SQLServer名前付きインスタンスの情報を得る。
File: query_sqlbrowser.php
*************************************************************************************/
function queryNamedInstance($host,$instanceName,$queryPort = 1434)
{
  static $timeout = 5;
  $fp = fsockopen('udp://'.$host,$queryPort,$errno,$errstr,$timeout);
  if($fp === false)
    throw new RuntimeException($errstr);

  $str = sprintf('%s%s%s',hex2bin('04'),$instanceName,hex2bin('00'));

  if(false === fwrite($fp,$str,strlen($str)))
    throw new RuntimeException(_('failed to fwrite to resouce pointer'));

  $rv = fread($fp,1500);
  fclose($fp);

  $head = substr($rv,0,1);
  list(,$len) = unpack('v',substr($rv,1,2));
  $data = explode(';',substr($rv,3,$len));
  $rv = array();
  $i = 0;
  while(array_key_exists($i,$data) && array_key_exists($i+1,$data))
  {
    if(!empty($data[$i]))
      $rv[$data[$i]] = $data[$i+1];

    $i+=2;
  }

  return $rv;
}

この関数を実行してみると

<?php
require 'query_sqlbrowser.php';
print_r(queryNamedInstance('IPアドレス','インスタンス名'));

/***** 出力 *****
Array
(
  [ServerName] => コンピュータ名
  [InstanceName] => インスタンス名
  [IsClustered] => No
  [Version] => 10.50.4000.0
  [tcp] => 49179
)
****************/

とりあえす、これで得られたポート番号をキャッシュしておき、接続試行がタイムアウトしたら、キャッシュ更新・・・という感じで組み込もうかと思います。

samba(linux)とgVim(windows)でハマる

おっさんは、忘れてしまうのが異常に早いので、忘れずに備忘録。
ドキュメント書いたことも忘れてしまう。意味ないじゃ~~~ん。

閑話休題。

開発マシンを新しくCentOS7にした時、ファイル共有(Samba4)の設定で罠にはまった。
気づいたのは、CentOS7上に共有ディレクトリを設定して、Windows10の gVim(Kaoriya)でdockerの Dockerfile と docker-compose.yaml 他諸々の自動化シェルスクリプトを編集してた時に気づきました。

「あれ? (Windows10の) gVimで編集して保存(:w)したとき、groupのパーミッションに実行ビットが勝手につきやがるな・・・」

↓の図で Dockerfile のパーミッションに見慣れない(+)記号と group のパーミッションに x が付きます。。。

/etc/samba/smb.conf の設定で、create_maskとかforce create mode とかいろいろ試行錯誤するも状況に変化なし。不思議なことに メモ帳で開いて、保存すると、上記のようなことは起こらない。
Windows版のvim/gVimの時だけそうなる。。。

で、見慣れない + 記号の意味を調べると、Windowsの拡張属性?みたいなものがくっつくと + 記号が出るみたい。。。
そこで、sambaのリファレンスからそれらしい設定を見つけた。

  • map archive
  • map system
  • map hidden
  • nt acl support

DOSでおなじみ、A,H,S属性をLinux側とマッピング?するオプションなのかな?
nt acl support はそのものずばり、WindowsのACLのサポートをするかどうか。
とりあえず、これらを片っ端から no に設定して samba を再起動したら直った・・・・と思ったけど、今度は実行ビットが立ったファイルをgVim(windows)で編集して保存すると、実行ビットがなくなる。。。
smb.confで、create mask を 644 にしているのでは???と思い、gVim の保存方法に問題があるんじゃねーの?って推定して、ググると、どうやら backupcopy 変数が関係しているらしい、とのこと。

:helpで調べると unix以外のvimでは backupcopyの初期値は auto だそうで、backup=yes を ~/_vimrc に追加すると、僕の期待した通りの挙動に一応なりました。

ってなわけで、Linux側で共有されたファイルをWindowsのgVimで編集するときは、:set backupcopy=yes で、/etc/samba/smb.conf の map **** と nt acl support を no に設定。

こっそりルーター化

これのCentOS7版

備忘録です。

開発用のサーバーを更新。CentOS8まで待とうかと少し思いましたが、まぁ、いいや。
ってことで、WiFi環境がないので、ポータブルのWiFiルータを開発用サーバーに接続して使えるように設定。

図A 前提

正直ネットワーク素人なので、zone:public に –add-masquerade するのがいいのかどうかわからんが、ちゃんとスマホからアクセスできるのでこれでいいか。

zoneをデフォルトのpublicではなく、work にした方が良かったのかもしれない。

#ゾーン publicとhomeに通信を許可するサービスをそれぞれに追加。
firewall-cmd --zone=public --add-service http --add-service https --add-service mysql --add-service dns --add-service samba --permanent
firewall-cmd --zone=home --add-service http --add-service https --add-service dns --permanent 

#eth1 は ELECOMのUSB接続タイプのNIC。これを home ゾーンに変更
nmcli c mod eth1 connection.zone home

#publicに対してNAPTをしかける
firewall-cmd --zone=public --add-masquerade --permanent

# リロード
firewall-cmd --reload

WSL(Ubuntu)-PHPからSQLServerへの道のり

2019年5月20日 若干修正


「CentOSからWindows上のSQLServerへの道のり」のつづきです。

LinuxからWindows上の SQLServerへのアクセス方法については、世間的にはあまり需要がないのでしょう。まぁ、Linuxとかのunix系のOSでは、SQLServerなんて採用しないし、逆も然り。。。困ったもんだ。。。

さて、Linuxの各ディストリビューション用のMicrosoft製のODBCドライバが提供されていて手順を踏むと、LinuxからSQLServerへODBC経由でアクセスすることができました。

今度は LAMP環境、PHPから文字化けさせずにCRUDすることができるか、ウェブアプリケーションをちゃんと実装できるか、確認です。とりあえずLinux各ディストリビューション用にマイクロソフト製のODBCドライバをインストールしていることが前提です。

PHPでたとえば、以下のようなコードでテーブルを作成し、SQLServer Management Studioとかで確認すると、日本語が全部文字化けします。
sqlcmdコマンドで日本語交じりのSQL文を発行すると化けないので、おそらく、PHPの問題でしょう。DSNで odbc: を使うのがだめなんでしょうね。odbcドライバをインストールしたんだから、当然odbcプレフィックスを使うものだと勘違いしてまして、これが罠でした・・・。日本語関係の処理が全くされない。。。この辺、よくわからん・・・。知識が圧倒的に足りない。SQLServerのODBCドライバとsqlsvr(PHP SQLServer用のドライバ?)の関係が全然わからない。PHPのマニュアル見てもよくわからない・・・。

// これが間違い
$pdo = new PDO('odbc:Driver={ODBC Driver 13 for SQL Server};Server=localhost;Database=Sample','dbuser','dbpass');
$pdo->exec('CREATE TABLE tbl_hoge(hoge_id int NOT NULL,hoge_txt nvarchar(100))');
$pdo->exec("INSERT INTO tbl_hoge values(1,N'日本語で挿入・・・なんかエロいな')");

接続文字列でエンコーディング関係のパラメータがあるのかなと、調べてみたけどないっぽい。さらにネットでの情報がほとんどなく。。。ヒットするのはFreeTDSを使ったものばかりで、多くはPHP5の情報ばかりで、PHP7以降のものは出てこず。。。

で、困ったときはグーグルで検索・・・ではなく、まずマイクロソフトの公式のドキュメントを漁りましょうってことですね(^^;
ググらなくても最近のマイクロソフトはちゃんとドキュメントを用意してくれてます。(MSも変わったな。。。)

Build an app using SQL Server


上記サイトの PHP ⇒ UBUNTU をクリックすると、Linux版 SQL Serverのセットアップから詳細な手順が書かれています。英語ですが、簡単な英文なので読みましょう!英語は喋れなくても、聴き取れなくても一向に問題はないけど、英語の読解能力は必須!ちゃんと読めるようにしましょう!!!
日本人がいくら優れたシステムを作れたとしても、英語で説明できなければ、世界には通用しませんし、この先世界中の開発者がこぞって日本語を理解してくれるとは到底思えないからです。

今までSQLServer用のODBCドライバとPHPでのSQLServer用のドライバ、それぞれが別々に説明がされてて、一体何がどうなってってるのか理解できなかったけど、上記サイトで Ubuntu + PHP では、この手順、Redhat系では、この手順、という風にまとめて手順が説明されているので、これで悩まなくて済むようになった。PHP7以降限定ですけどね。 まぁPHP5系はこの先消えていく(deprecated)のでまぁいいか、という感じ。

要するに ODBCドライバに加えて、peclでsqlsvr/pdo_sqlsvrをインストールすれば完了。やっと 非Windows環境でも労せずSQLServerへフツーにアクセスできるようになりました。

上記マイクロソフトのサイトには、Linux用のSQL Serverのセットアップから説明されていますが・・・SQLServerは Windows用のものをインストールしましょう。というか、開発マシンがWindowsの場合は、素直にWindows用のSQLServer Developper Editionをインストールした方がいいです。開発目的ならライセンスフリーですし。(サーバー環境で運用はできないけど)
開発マシンがMacやLinuxの方は・・・そもそもSQLServerに接続するような仕事してないでしょ?w

WSL(Ubuntu)でPHPをインストールしていない場合、aptで適当にphp7系をインストールしてください。その際、php-dev パッケージは必須です。
また、最新のsqlsrv,pdo-sqlsrvドライバはphp7.0をサポートしないので、最新に拘る人はphp7.1以上にするべし?php7.0をサポートするMicrosoftのsqlsrv/pdoドライバのバージョンは5.3です。最新は確か5.6だったかな?
システム要件:https://docs.microsoft.com/ja-jp/sql/connect/php/system-requirements-for-the-php-sql-driver

$ sudo apt install php php-cli php-dev php-... (必要なパッケージ)
# apacheで動作するには必要
$ sudo apt install libapache2-mod-php

1, 先に マイクロソフトのサイトからODBC Driver for SQLServer をインストールしておく(上述)

2, sqlsrv/pdo_sqlsrv のインストール (ubuntu 1804 の場合)

$ sudo apt install php-dev
$ sudo pecl install sqlsrv
$ sudo pecl install pdo_sqlsrv
$ sudo su 
# echo "extension=pdo_sqlsrv.so" > /etc/php/7.2/mods-available/pdo_sqlsrv.ini
# echo "extension=sqlsrv.so" > /etc/php/7.2/mods-available/sqlsrv.ini
# exit
$ sudo phpenmod -v 7.2 -s ALL  pdo_sqlsrv sqlsrv

apache2な人は再起動するべし。
参考リンク:the installation instructions on Microsoft Docs.

3, PHPで確認
PDOで、DSNのプレフィックスをodbc: ではなく sqlsrv: で。

$pdo = new PDO('sqlsrv:Server=localhost;Database=Sample','dbuser','dbpass');
$pdo->exec('CREATE TABLE tbl_サンプル(hoge_id int NOT NULL,hoge_txt nvarchar(100))');
$pdo->exec(sprintf('INSERT INTO tbl_hoge values(1,N%s)',$pdo->quote('日本語で挿入・・・なんかエロいな'));

参考リンク: https://github.com/Microsoft/msphpsql/tree/master/sample

文字列リテラルをNプレフィックスをつけずにINSERTとすると絵文字が化ける。文字列リテラルの場合は Nプレフィックスを忘れずに。。。
ただ、プリペアドステートメントを利用する場合、自動的にNプレフィックスはつけてくれるみたい。SQLServer Data Profilerで実際実行されたSQL文を確認すると、PDO::execでは自動的にNプレフィックスはつけてくれないみたい。まぁ当たり前といえば、そうなんだけど。。。

あと、東京オリンピックまでには、apt install php-sqsrv / yum install php-sqlsrv とか標準のリポジトリでインストールできるようにしてほしい。。。

\(^o^)/