2020/03/02(月)Vagrant / CentOS 8 / gcc 9 (Visual Studio からの remote build/debug) / apache / ffmpeg / nginx-rtmp-module 環境構築メモ
自分のためのメモ
「ffmpegを呼び出してライブストリームをごにょごにょして独自形式のコンテナにMuxしてhttpでストリーミングするアプリケーション」を作りたくなったので、 Visual Studio で、C++ コンソールアプリケーション(Linux) としてプロジェクトを作って、CentOS 8上でリモートビルドしてApache に載せてCGIとして動かしてみるために、 vagrant で VMを用意して、CentOS 8 上に、g++ と apache と ffmpeg を準備します。
WSLは、そのうち……(ずっと、そのうち……って言ってる気がする)
install
- VirtualBox 入れる VirtualBox 6系はHyper-V上でも動くとか?
- VirtualBox The Extension Pack 入れる (注意: VirtualBox Personal Use and Evaluation License)
- Vagrant 入れる
- Vagrant のプラグインを入れる
vagrant plugin install vagrant-proxyconf # いろんなコマンドのproxy設定を横断的にやってくれるやつ。 proxy環境下では便利。
vagrant plugin install vagrant-vbguest #guest additions を自動で更新してくれるやつ
ゲストOSを準備する
mkdir vagrant/centos8
cd vagrant/centos8
vagrant init
Vagrantfile を適当に編集する。
#Vagrantfile
Vagrant.configure("2") do |config|
config.vm.box = "centos/8"
config.vm.network "forwarded_port", guest: 80, host: 8080
config.vm.network "forwarded_port", guest: 443, host: 8443
config.vm.provider "virtualbox" do |vb|
vb.cpus = 12 #morimori
vb.memory = 4096
end
# Enable provisioning with a shell script.
config.vm.provision "shell", inline: <<-SHELL
echo ---- start provisioning ----
dnf -y update
timedatectl set-timezone Asia/Tokyo # Timezone を Asia/Tokyo に
echo ---- end provisioning ----
SHELL
if Vagrant.has_plugin?("vagrant-proxyconf")
config.proxy.enabled = false
config.proxy.http = "http://hogehoge:8080"
config.proxy.https = "http://hogehoge:8080"
config.proxy.no_proxy = "localhost,127.0.0.1"
end
end
vagrant up
すると、仮想マシンインスタンスが作成され立ち上がる。
up 中に Guest Additions が入らないとかいうエラーが出るのは Kernelが古いかららしい。
一度 dnf -y update
すればいいらしいので、Vagrantfile の provision に書いてみたけど実行される前にエラーで止まるせい(?)で実行されてないっぽい。
マシンがあがった状態でもう一回 vagrant up
すると provision されてカーネル他が更新される。
この状態で vagrant reload
で、マシンを再起動すれば、先の vagrant-vbguest が Guest Additions を入れてくれる。
マシンの電源を切るには、vagrant halt
vagrant up
vagrant up
vagrant reload
gcc 9 を公式リポから入れる。
Visual Studio からのリモートビルドするには、ssh できて g++ gdb gdbserver が動けばいいんだとか。
gcc 8系とgcc 9系が利用できるらしいので、今回は 9系を準備してみる。
sudo su # rootになる。
dnf -y install gcc-toolset-9 gcc-toolset-9-gdb-gdbserver
echo -e '#!/bin/bash\\nsource scl_source enable gcc-toolset-9' > /etc/profile.d/enable-gcc-toolset-9.sh #デフォルトで gcc 9 が使われるようにする
ログインしなおして、 which gcc
が /opt/rh/gcc-toolset-9/root/usr/bin/gcc
に向いてれば成功。
vagrant up
でエラーが出たり、vagrant halt
でシャットダウンできなくなったりする。
ログイン時に自動的に scl_source
するようにすると、sudoが壊れる(語弊がある)せい。
結論から言えば、
sudo mv /opt/rh/gcc-toolset-9/root/usr/bin/sudo{,.bkup}
で解決する。
トラブルシュート
$ vagrant up
... (略) ...
==> default: Checking for guest additions in VM...
The following SSH command responded with a non-zero exit status.
Vagrant assumes that this means the command failed!
setup
Stdout from the command:
Stderr from the command:
$ vagrant halt
==> default: Attempting graceful shutdown of VM...
The following SSH command responded with a non-zero exit status.
Vagrant assumes that this means the command failed!
shutdown -h now
Stdout from the command:
Stderr from the command:
デバッグレベルのログを見る。参考: vagrant upでエラー 「command failed! setup」
VAGRANT_LOG=debug vagrant up 2> >(tee up.log)
流れる文字を眺めてると command not found
なんて文字が見えた気がしたので、less
して検索する。
シェルスクリプトの実行時に line 2: -E: command not found
とか出ているので、vagrant "-E: command not found"
でググると
Vagrant halt and up produces errors on Centos 6.7がヒットして、Centos 6.7 broken Sudoへのリンクが張ってあって、はー、なるほどという感じ。
$ which sudo
/opt/rh/gcc-toolset-9/root/usr/bin/sudo
/opt/rh/gcc-toolset-9/root/usr/bin/sudo
は /usr/bin/sudo
への wrapper script で、wrapするとき自動的に -E
をつけてくれちゃうせいで、むしろ明示的に -E
をつけた sudo -E whoami
などというコマンドが sudo -E ... -E whoami
に展開されて、 -E なんてコマンドないよ
というエラーが出てると。
うーん、個人的には、このような sudo は要らないので、剥がす。
sudo mv /opt/rh/gcc-toolset-9/root/usr/bin/sudo{,.bkup} # 先のコマンド
と、up
や halt
でエラーが出なくなる。
Visual Studio で、コンソールアプリケーション(Linux) を作って、リモートビルド・リモートでバッグができるか確認する。
Visual Studio からは、vagrant@localhost:2222
に 秘密鍵 .vagrant/machines/default/virtualbox/private_key
で接続する。
一回ビルドすれば、ヘッダファイルがVisual Studio側にコピーされてIntelliSenseが効くようになるし、リモートデバッグ時にはブレークポイント張ればブレークするし、ウォッチはSTLコンテナの中身までちゃんと見える。おお、これは結構いいのでは。
Apache httpd を 公式リポから入れる
dnfで入れます。最新版を使うならソースからだけど、さしあたり手元でCGIを動かしてみるだけなので
sudo dnf install httpd httpd-devel httpd-filesystem httpd-tools
- httpd: ほんたい
- httpd-devel: モジュールをソースからビルドするのとかに使う。
- httpd-filesytem: 基本的なApacheのディレクトリ構造を提供してくれる。
- httpd-tools: ab とか htdbm とか htpasswd とかが入ってる。
自動的に、 apache
ユーザ と apache
グループ が作られる。
sudo systemctl start httpd
sudo systemctl status httpd
Active: active (running)
になってれば。 Vagrantfile で、 Host 8080 -> Guest 80 の設定をしてあるので、ホストOSから http://localhost:8080/ にアクセスすれば、スタートページが見えるはず……。
ファイヤーウォールはデフォルトはOFFっぽい。使う場合に設定すればいいだろう。
sudo systemctl status firewalld
httpd
の設定は /etc/httpd/conf/httpd.conf
にあるので、これを書き換えていく。
ここでは開発環境が欲しいだけなので、/var/www/html
下を AllowOverride All
して、.htaccess おくでもいいかな。
sudo systemctl reload httpd
CGI の動作テスト
testを掘って、 .htaccess と index.cgi を置く。
sudo mkdir /var/www/html/test #testを掘る
sudo chown vagrant /var/www/html/test
cd /var/www/html/test
cat << __HTACCESS__ > .htaccess
DirectoryIndex index.cgi
Options +ExecCGI
AddHandler cgi-script cgi
__HTACCESS__
cat << __INDEXCGI__ > index.cgi
#!/usr/bin/bash
echo 'Status: 200 OK'
echo 'Content-Type: text/plain'
echo ''
cat << '__HTML__'
It wooooks.
__INDEXCGI__
chmod 755 index.cgi
ブラウザからアクセスしてみると、Internal Server Error. が出る。
sudo tail /var/log/httpd/error_log
すると、Permission denied
が出ている。あ、SELinuxか。
sudo setenforce 0
で、It wooooks が出れば、CGIの実行もOK。
C++で作ったCGIも、一応試しておく。
cat << __INDEXCGI__ > index.cpp
// index.cpp
#include <cstdio>
int main()
{
puts("Status: 200 OK");
puts("Content-Type: text/plain");
puts("");
puts("C++ CGI also wooooks.");
return 0;
}
__INDEXCGI__
g++ -o index.cgi index.cpp
ffmpeg をビルドする
ffmpeg は、 Nux Dextop のリポジトリから入れればいいよという記事がいっぱいあるけど、 ffmpeg 2系が降ってきちゃう? 最新版を使いたいのでソースから入れる。
https://trac.ffmpeg.org/wiki/CompilationGuide/Centosにインストールマニュアルがあるので、これに従えばよい。
けど、マニュアルに書いてあるのは、ユーザのホームディレクトリ以下にインストールする方法(root権限不要)であって(まぁこっちの方が需要あるのはわかるけど)、今回やりたいのは素直な /usr/local
へのインストールなので、コマンドを書き換えていく。
# 作業ディレクトリ。
mkdir ~/ffmpeg_sources
Get the Dependencies
sudo dnf install autoconf automake bzip2 bzip2-devel cmake freetype-devel gcc gcc-c++ git libtool make mercurial pkgconfig zlib-devel
g++ とか libtool は、gcc-toolset-9 入れてるので要らないんだけど。
NASM
cd ~/ffmpeg_sources &&
curl -O -L https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/nasm-2.14.02.tar.bz2 &&
tar xjvf nasm-2.14.02.tar.bz2 &&
cd nasm-2.14.02 &&
./autogen.sh &&
./configure &&
make -j &&
sudo make install
Yasm
cd ~/ffmpeg_sources &&
curl -O -L https://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz &&
tar xzvf yasm-1.3.0.tar.gz &&
cd yasm-1.3.0 &&
./configure &&
make -j &&
sudo make install
libx264
cd ~/ffmpeg_sources &&
git clone --depth 1 https://code.videolan.org/videolan/x264.git &&
cd x264 &&
./configure --enable-static &&
make -j &&
sudo make install
libx265
cd ~/ffmpeg_sources &&
hg clone https://bitbucket.org/multicoreware/x265 &&
cd ~/ffmpeg_sources/x265/build/linux &&
cmake -G "Unix Makefiles" ../../source &&
make -j &&
sudo make install
libfdk_aac
cd ~/ffmpeg_sources &&
git clone --depth 1 https://github.com/mstorsjo/fdk-aac &&
cd fdk-aac &&
autoreconf -fiv &&
./configure &&
make -j &&
sudo make install
libmp3lame
cd ~/ffmpeg_sources &&
curl -O -L https://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz &&
tar xzvf lame-3.100.tar.gz &&
cd lame-3.100 &&
./configure --enable-nasm &&
make -j &&
sudo make install
libopus
cd ~/ffmpeg_sources &&
curl -O -L https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz &&
tar xzvf opus-1.3.1.tar.gz &&
cd opus-1.3.1 &&
./configure &&
make -j &&
sudo make install
libvpx
cd ~/ffmpeg_sources &&
git clone --depth 1 https://chromium.googlesource.com/webm/libvpx.git &&
cd libvpx &&
./configure --disable-examples --disable-unit-tests --enable-vp9-highbitdepth --as=yasm --enable-pic &&
make -j &&
sudo make install
FFmpeg
cd ~/ffmpeg_sources &&
curl -O -L https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2 &&
tar xjvf ffmpeg-snapshot.tar.bz2 &&
cd ffmpeg &&
PKG_CONFIG_PATH="/usr/local/lib/pkgconfig" ./configure \\
--pkg-config-flags="--static" \\
--extra-libs=-lpthread \\
--extra-libs=-lm \\
--enable-gpl \\
--enable-libfdk_aac \\
--enable-libfreetype \\
--enable-libmp3lame \\
--enable-libopus \\
--enable-libvpx \\
--enable-libx264 \\
--enable-libx265 \\
--enable-nonfree &&
make -j &&
sudo make install
このままだと、ffmpeg: error while loading shared libraries: ...
と言われる。
/usr/local/lib にも .so を探しに行く設定をする。
sudo echo '/usr/local/lib' | sudo tee /etc/ld.so.conf.d/usr-local-lib.conf
sudo ldconfig
ffmpeg -codecs # 動くか確認
nginx
http://nginx.org/en/linux_packages.html に書いてあるとおりにやる。
# nginx.repo を追加する。
sudo tee /etc/yum.repos.d/nginx.repo << '__NGINX_REPO__'
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
__NGINX_REPO__
# 新しいバージョンを使いたいので mainline repository を有効にする。
sudo yum-config-manager --enable nginx-mainline
# インストール
sudo dnf install nginx
# 起動
sudo systemctl starat nginx
例によって http://localhost:8080/ で見えればOK。設定ファイルは /etc/nginx/nginx.conf
。
RTMPモジュールを入れてみる
モジュールを入れるにはソースからコンパイルする必要がある? nginx -V
すると configure のときのオプションが見える。
nginx -V
自分でコンパイルするなら、/usr/local に入れた方がいい気もするし、path系のオプションを除いてコンパイルしてみよ。
mkdir nginx-source
cd nginx-source
sudo dnf install openssl-devel pcre-devel
curl -O -L https://nginx.org/download/nginx-1.17.8.tar.gz && tar xf nginx-1.17.8.tar.gz
git clone https://github.com/sergey-dryabzhinsky/nginx-rtmp-module.git
cd nginx-1.17.8
./configure --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' \\
--add-module=../nginx-rtmp-module &&
make -j &&
sudo make install
もともとの ./configure オプションにあったパス回りの設定
--prefix=/etc/nginx
--sbin-path=/usr/sbin/nginx
--modules-path=/usr/lib64/nginx/modules
--conf-path=/etc/nginx/nginx.conf
--error-log-path=/var/log/nginx/error.log
--http-log-path=/var/log/nginx/access.log
--pid-path=/var/run/nginx.pid
--lock-path=/var/run/nginx.lock
--http-client-body-temp-path=/var/cache/nginx/client_temp
--http-proxy-temp-path=/var/cache/nginx/proxy_temp
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp
--http-scgi-temp-path=/var/cache/nginx/scgi_temp
デフォルトでは↓こうなるみたい。
nginx path prefix: "/usr/local/nginx"
nginx binary file: "/usr/local/nginx/sbin/nginx"
nginx modules path: "/usr/local/nginx/modules"
nginx configuration prefix: "/usr/local/nginx/conf"
nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
nginx pid file: "/usr/local/nginx/logs/nginx.pid"
nginx error log file: "/usr/local/nginx/logs/error.log"
nginx http access log file: "/usr/local/nginx/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
nginx http fastcgi temporary files: "fastcgi_temp"
nginx http uwsgi temporary files: "uwsgi_temp"
nginx http scgi temporary files: "scgi_temp"
そのまま systemctl を使うには、 /lib/systemd/system/nginx.service
をカスタムしてどうか。ユニットファイルのカスタムは /etc/systemd/system/
にコピーして行うのがお作法?
sudo cp /lib/systemd/system/nginx.service /etc/systemd/system/
書き換え後:
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStart=/usr/local/nginx/sbin/nginx
sudo systemctl start nginx
sudo systemctl status nginx
ブラウザからアクセスして見えてればOKっと。
# パッケージの方の nginx は紛らわしいので消しておく
sudo dnf remove nginx
RTMP
Vagrantfile に RTMP の port-forwarding を書き足す。vagrant reload
で再起動。
config.vm.network "forwarded_port", guest: 1935, host: 1935
/usr/local/nginx/conf/nginx.conf
に RTMP モジュールの設定をする。
rtmp {
server {
listen 1935;
chunk_size 8192;
application live {
live on;
record off;
meta copy;
}
}
}
ついでにリバプロも設定してみる。
http {
server {
... (略) ...
location /proxy/ {
proxy_pass http://127.0.0.1:8080/proxy/;
}
}
apache 側 /etc/httpd/conf/httpd.conf
の Listen port を変える。
Listen 8080
sudo systemctl reload httpd
sudo systemctl reload nginx
- ホストOSから、ブラウザで
http://localhost:8080/proxy/
にアクセスして Apacheの Not Found が出る。 - ホストOSから、OBSで
rtmp://localhost/live/abc
に 配信して、 VLCでrtmp://localhost/live/abc
が再生できる。
を確認して、OK。っと、ここまできてしまったけど、 SRPM というものを使ってビルドする方法もあるらしい。モジュールの git clone とかの手順も spec ファイルに書いておけば、やってくれるみたい。便利そう。
SRPMでもやってみる。
参考: nginxでsrpmからのconfigure変更 参考: nginx のモジュール追加リビルド (CentOS 7 EPEL 版) | 技術メモの壁
rpmbuild
コマンドが必要
# 環境
sudo dnf install rpm-build rpmdevtools
# 作業ユーザ
sudo su -
useradd builder
cd ~builder
su - builder
# 作業ディレクトリ
mkdir -p ~/nginx/rpmbuild &&
cd ~/nginx/rpmbuild
# ダウンロードと展開
echo '%_topdir %(echo $PWD)' > ~/.rpmmacros &&
rpmdev-setuptree &&
curl -O http://nginx.org/packages/mainline/centos/8/SRPMS/nginx-1.17.8-1.el8.ngx.src.rpm &&
rpm -i nginx-1.17.8-1.el8.ngx.src.rpm
# SPEC書き換え (後述)
vim SPECS/nginx.spec
BuildRequires に git
を足して、%prep
で git clone
しつつ BASE_CONFIGURE_ARGS
マクロに --add-module
を足す。%define
でやると、lazyに展開されるせいで、無限再帰マクロになって死ぬので %global
にする。
BuildRequires: git
... (略) ...
%prep
... (略) ...
git clone https://github.com/sergey-dryabzhinsky/nginx-rtmp-module.git
%global BASE_CONFIGURE_ARGS %{BASE_CONFIGURE_ARGS} --add-module=./nginx-rtmp-module
# ビルド
# RPMS/ にバイナリパッケージ、 SRPMS/ に ソースパッケージ がビルドできる
rpmbuild -ba SPECS/nginx.spec
# builder は sudoer ではないので、 logout して root に戻ってインストール
logout
dnf localinstall nginx/rpmbuild/RPMS/x86_64/nginx-1*
この場合は設定は /etc/nginx/nginx.conf
に書く。
# boot
sudo systemctl start nginx
sudo systemctl status nginx
うーん。
バージョンアップするごとに結局新しいパッケージのSRPMとってきて SPEC書き換えるとかやってると、ソースから入れるのとあんま変わらないかも。
モジュールを更新するためには、リビルドすればいいだけではあるけど……。
複数台サーバセットアップするときはRPMにして持っていけはするけど、それ以前に、ディスクイメージごと固めたり、VMのプロビジョニングレベルでスクリプト書くんでいい気がするしなぁ。
まぁ、パッケージ管理でインストールしたものと近しい構造でインストールされるというのが、一番大きなメリットかもしらん(?)
名前変えとかないと dnf update
で上書きされたりするんかな……?
これでひととおり
いかがでしたか?