Jekyll2022-11-10T13:09:31+00:00https://ryought.app/feed.xmlRyought::LabRyo Nakabayashi (ryought) Bioinformatics and web engineerRyo Nakabayashikickflipがしっかり張り付かないのをなんとかした話2019-11-18T03:00:00+00:002019-11-18T03:00:00+00:00https://ryought.app/2019/11/18/kickflip<p>すぐ剥がれてしまうBlueloungeのKickflipを、逆さまに持っても外れないようにするには、吸着テープを剥がしてmacに先に貼ってしまおう</p>
<hr />
<p><a href="https://www.amazon.co.jp/dp/B00LGLAFES/ref=cm_sw_em_r_mt_dp_U_fQWbEb8BZ3470">BlueloungeのKickflip</a>を使っていた。タイピングしやすい角度になるので重宝していたが、特に持ち運びの時など、 <strong>頻繁に外れる</strong> のが気になっていた。</p>
<p><img src="/assets/kick0.jpg" alt="kickflip" /></p>
<p>レビューを見る限り頻繁に外れるといっている人は少ないのでハズレを引いたのだと思う。
横から見ると、再利用可能と書いてある吸着テープとmacがうまく接していないところがある。</p>
<p><img src="/assets/kick1.jpg" alt="" /></p>
<p>上の写真は吸着面だが、奥の方など波打っている部分がある。この凸凹のせいで、しっかり接しておらず、すぐに剥がれてしまうのではないかと考えた。</p>
<h2 id="対処法">対処法</h2>
<p>もう剥がすつもりもないので強力両面テープで貼っても良いのだが、吸着テープを先にPCに貼るのが良かった。つまり</p>
<ol>
<li>
<p>吸着テープをkickflipの本体から剥がす</p>
<p>黒いゴムの板が取れる。kickflip本体とは両面テープか接着剤でくっついているようだ。
<img src="/assets/kick2.jpg" alt="" /></p>
</li>
<li>
<p>吸着テープをきれいに洗って、先にmacと吸着テープをくっつける</p>
<p>テープとPC底面に隙間があるのが悪いので、先にテープを載せてしまう。空気を入れないようにテープを載せるとぴったりくっつくのがわかる。
<img src="/assets/kick3.jpg" alt="" /></p>
</li>
<li>
<p>両面テープで、吸着テープとkickflip本体を貼り合わせる</p>
<p>お好みの両面テープを吸着テープの裏側に貼って、kickflipと接着させる。
今回は <strong>3M スコッチ はがせる両面テープ 強力薄手</strong> を使った。(このテープもいろいろ使えて便利)</p>
</li>
</ol>
<p>とすると剥がれなくなった。</p>Ryo Nakabayashiすぐ剥がれてしまうBlueloungeのKickflipを、逆さまに持っても外れないようにするには、吸着テープを剥がしてmacに先に貼ってしまおうOpenGL/GLXを使うXアプリケーションをkubernetes/dockerコンテナ内でheadlessに動かした話2019-07-14T03:00:00+00:002019-07-14T03:00:00+00:00https://ryought.app/2019/07/14/glx-docker-headless-gpu<p>OpenGLやGLXなどを使ってグラフィック用途でGPUを使うアプリケーションを、画面のないheadlessサーバー上で、しかもDocker内で動かす方法のまとめ。</p>
<p>コードはこちら: <a href="https://github.com/ryought/glx-docker-headless-gpu">ryought/glx-docker-headless-gpu</a></p>
<p>具体的には、Unityで作られたソフト本体の自動テスト環境やそれを使ったCI環境を、GPUインスタンスを含んだkubernetesクラスタ上に構築したい時に使える。</p>
<h2 id="background">Background</h2>
<p>自動テストサーバーを作るために、 https://github.com/lgsvl/simulator をKubernetes上で動かしたかった。</p>
<p>ECSもGPUインスタンスをサポートし始めたので、Docker内でグラフィック系アプリケーションをGPU機能込みで閉じ込めたい需要は割とあると思うので、まとめておく。</p>
<h2 id="method">Method</h2>
<p>とりあえず使いたい人向けに手順をまず書く。</p>
<h3 id="1-google-cloud-platform-compute-engineのインスタンスのセットアップ">1. Google Cloud Platform Compute Engineのインスタンスのセットアップ</h3>
<p>GPUインスタンス上で、GPUのドライバとnvidia-docker2をインストールする。</p>
<p>まず使うVMをGCP Compute Engineで立ち上げる。基本的にはNVIDIA GPUが搭載されているインスタンスを立ち上げて、GPUドライバ+nvidia-docker2をインストールすれば良いが、Marketplaceから入手できるDeep Learning VMをdeployするとインストール済みイメージが載った状態のVMにアクセスできる。</p>
<p>ここで使ったのは</p>
<ul>
<li>Deep Learning VM by Google</li>
<li>n1-highmem-2</li>
<li>1 x NVIDIA Tesla T4</li>
<li>100GB HDD</li>
<li>Tensorflow 1.14 frameworkを有効化(これはどれでも良さそう)</li>
<li>nvidiaドライバ込み</li>
</ul>
<p>それ以外は標準のままのインスタンス。us-west1-bに立ち上げた。起動時にwarningが出るが気にしない。ブラウザ上で設定したが、同様のコマンドは以下。</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcloud compute <span class="nt">--project</span><span class="o">=</span><span class="nv">$PROJECT_NAME</span> create <span class="nv">$INSTANCE_NAME</span> <span class="se">\</span>
<span class="nt">--zone</span><span class="o">=</span>us-west1-b <span class="se">\</span>
<span class="nt">--machine-type</span><span class="o">=</span>n1-highmem-2 <span class="se">\</span>
<span class="nt">--subnet</span><span class="o">=</span>default <span class="se">\</span>
<span class="nt">--network-tier</span><span class="o">=</span>PREMIUM <span class="se">\</span>
<span class="nt">--metadata</span><span class="o">=</span><span class="nv">framework</span><span class="o">=</span>TensorFlow:1.13,google-logging-enable<span class="o">=</span>0,google-monitoring-enable<span class="o">=</span>0,install-nvidia-driver<span class="o">=</span>True,status-config-url<span class="o">=</span>https://runtimeconfig.googleapis.com/v1beta1/projects/sever-rendering/configs/tensorflow-1-config,status-uptime-deadline<span class="o">=</span>600,status-variable-path<span class="o">=</span>status,title<span class="o">=</span>TensorFlow/Keras/Horovod.CUDA10.0,version<span class="o">=</span>27 <span class="se">\</span>
<span class="nt">--maintenance-policy</span><span class="o">=</span>TERMINATE <span class="se">\</span>
<span class="nt">--service-account</span><span class="o">=</span>471732791036-compute@developer.gserviceaccount.com <span class="se">\</span>
<span class="nt">--scopes</span><span class="o">=</span>https://www.googleapis.com/auth/compute,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/cloud.useraccounts.readonly,https://www.googleapis.com/auth/cloudruntimeconfig <span class="se">\</span>
<span class="nt">--accelerator</span><span class="o">=</span><span class="nb">type</span><span class="o">=</span>nvidia-tesla-t4,count<span class="o">=</span>1 <span class="se">\</span>
<span class="nt">--tags</span><span class="o">=</span>deeplearning-vm <span class="se">\</span>
<span class="nt">--image</span><span class="o">=</span>tf-1-14-cu100-20190619 <span class="se">\</span>
<span class="nt">--image-project</span><span class="o">=</span>click-to-deploy-images <span class="se">\</span>
<span class="nt">--boot-disk-size</span><span class="o">=</span>100GB <span class="se">\</span>
<span class="nt">--boot-disk-type</span><span class="o">=</span>pd-standard <span class="se">\</span>
<span class="nt">--boot-disk-device-name</span><span class="o">=</span>tensorflow-1-vm-1 <span class="se">\</span>
<span class="nt">--labels</span><span class="o">=</span>goog-dm<span class="o">=</span>tensorflow-1
</code></pre></div></div>
<p>ssh port forwardを使うので、sshクライアントからログインできるようにセットアップしておく。(コンソールからssh鍵を登録する。<code class="language-plaintext highlighter-rouge">ssh-keygen -t rsa -C "$USERNAME" -b 4096 -f ~/.ssh/$KEYNAME</code> コメントのユーザー名に指定したユーザーでしかログインできないので注意。)</p>
<p>sshログインして<code class="language-plaintext highlighter-rouge">nvidia-smi</code>を実行して、GPUが検出されることと、入っているドライババージョンを確認
上のVMでは、Tesla T4とドライババージョン410.104が入っていた。</p>
<h3 id="2-container立ち上げ">2. container立ち上げ</h3>
<p>X11サーバーとアプリケーションの入ったdocker containerをビルド、立ち上げる。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nvidia-docker build -t x11-GL-docker .
docker run --runtime=nvidia --privileged -it --rm \
-p 5900:5900 \ # or --net=host
-e BUSID=PCI:0:4:0 \
-e SCREEN_RESOLUTION=1280x1024 \
-e VNC_PASSWORD=passpass \
-v /home/ryonakabayashi/x11-docker/lgsvlsimulator-linux64-2019.05:/lg \
--name x11-GL-docker x11-GL-docker
</code></pre></div></div>
<p>run.shがdocker内で実行される。</p>
<p>5900ポートはVNC用。またホストマシンに一度ログインして、<code class="language-plaintext highlighter-rouge">nvidia-xconfig --query-gpu-info</code>を実行し、BUSIDを控えておく。</p>
<h3 id="3-port-forwarding-vnc-connection">3. port forwarding, VNC connection</h3>
<p>docker内で起動しているVNCサーバーに、ssh経由で接続する。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh -i "$KEYFILE" -L 5900:localhost:5900 $USERNAME@$GCP_PUBLIC_IP -N &
</code></pre></div></div>
<p>でトンネルを作り、VNCクライアント(macならFinder)で<code class="language-plaintext highlighter-rouge">vnc://localhost:5900</code>に接続する。</p>
<p><img src="/assets/lgsvl_on_docker.png" alt="img" /></p>
<p>リモートのdocker内でレンダリングした結果が手元のmacbookで確認できる環境がこれで整った。</p>
<h2 id="何をしているか">何をしているか</h2>
<p>2つの要素が組み合わさってできている。</p>
<ul>
<li>
<p>(A) headless GPU server上でGLXアプリを動かす</p>
<p>GPUを使える仮想ディスプレイを作る。ソフトウェアレンダリングで十分な場合は、<code class="language-plaintext highlighter-rouge">xorg xvfb x11vnc</code>などで仮想ディスプレイ環境を作れるが、GPUを使いたい場合はNVIDIAドライバの機能を使う必要がある。</p>
</li>
<li>
<p>(B) docker上でX11サーバーを動かす</p>
<p>ポピュラーな<code class="language-plaintext highlighter-rouge">DISPLAY</code> <code class="language-plaintext highlighter-rouge">/tmp/.X11-unix</code>を共有する方法ではなく、Xorgをコンテナ内で動かす方法。</p>
</li>
<li>
<p>(A+B) headless GPU server上のdocker上でX11サーバーとGLXアプリを動かす</p>
<p>上の2つを合わせれば完成。</p>
</li>
</ul>
<p>順番に解説する。</p>
<h3 id="a-run-glx-apps-on-headless-nvidia-gpu-server">(A) run GLX apps on headless NVIDIA GPU server</h3>
<p>これに関しては解説がいろいろある。</p>
<ul>
<li><a href="https://www.slideshare.net/T_S/headless-x11-demo">NVIDIA GPUで作るHeadless X11 Linux</a></li>
<li><a href="https://medium.com/@pigiuz/setting-up-a-hw-accelerated-desktop-on-aws-g2-instances-4b58718a4541">Setting up a HW accelerated desktop on AWS G2 instances</a></li>
<li><a href="http://xanxys.hatenablog.jp/entry/2014/05/17/135932">EC2(g2.2xlarge)でOpenGLを使う方法 - ⊥=⊥</a></li>
<li><a href="https://towardsdatascience.com/how-to-run-unity-on-amazon-cloud-or-without-monitor-3c10ce022639">How to run Unity on Amazon Cloud or without Monitor</a></li>
<li><a href="https://virtualgl.org/Documentation/HeadlessNV">VirtualGL | Documentation / Headless nVidia Mini How-To</a></li>
<li><a href="https://carla.readthedocs.io/en/latest/carla_headless/">Running without display and selecting GPUs - CARLA Simulator</a></li>
</ul>
<p>大まかな手順としては</p>
<ul>
<li>Xorg, Nvidia Driverのインストール</li>
<li>XorgがGPUを使うようにconfを編集</li>
</ul>
<p><code class="language-plaintext highlighter-rouge">nvidia-xconfig</code>を使うと、headless環境で仮想displayを使うためのXorg向けの設定を出力できる。しかしコマンド自体のオプションのマニュアルが不足しているので、release noteを読んで確認する必要がある。</p>
<p>今回はTesla T4を使う。<code class="language-plaintext highlighter-rouge">nvidia-xconfig --query-gpu-info</code>を見るとBUS idはPCI:0:4:0だったので</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nvidia-xconfig \
-a \
--virtual=1280x1024 \ # 仮想スクリーンの解像度
--allow-empty-initial-configuration \ # ディスプレイがなくてもXサーバーを起動する
--enable-all-gpus \ # GPUを有効化
--busid PCI:0:4:0 # GPUを見つけられるようにBUSIDを指定しておく
</code></pre></div></div>
<p>を実行した。これで出てきた/etc/X11/xorg.confはきちんと動いた。</p>
<p><code class="language-plaintext highlighter-rouge">--use-display-device=None</code>をつけるという記述があるが、<a href="https://docs.nvidia.com/datacenter/tesla/tesla-release-notes-410-104/index.html">Version 410.104(Linux)/412.29(Windows) :: NVIDIA Tesla Documentation</a> によると410.104からサポートされなくなったらしく、つけると起動しなくなるので注意。</p>
<p>詳細についてはrepositoryの記述も読んでください。</p>
<h3 id="b-x-on-docker">(B) X on Docker</h3>
<p>X11 GUIアプリケーションをdocker内で動かしたい時、いくつか選択肢がある。</p>
<ol>
<li>
<p>hostのx11 socketの共有 or hostで建てているx serverのアドレスを環境変数DISPLAYで渡す</p>
<p>docker run時に<code class="language-plaintext highlighter-rouge">-e DISPLAY=$DISPLAY</code>とか<code class="language-plaintext highlighter-rouge">-v /tmp/.X11-unix:/tmp/.X11-unix</code>とかを指定するのはこちらのアプローチ。やり方については検索すると出てくるので割愛。</p>
<p>x11 clientだけをcontainer内で動かし、host上で動いているx11 serverに接続して描画してもらう。dockerからは描画命令だけが来るので、例えばGPUを使って実際に描画するのはx11 server側、つまりcontainer外になる。それ以前に、デスクトップ環境を作っていないサーバー上で動かす場合、そのhost上でまずX11サーバーをセットアップする必要がある。</p>
<p>この方法でも、「ローカルのデスクトップ環境上で動かすが、ホストの環境を汚さないためにdocker内でGUIアプリを使いたい」場合や、「サーバーだけど直にX11をセットアップするのを厭わない」場合は十分。</p>
</li>
<li>
<p>docker内にx11 serverも建てる</p>
<p>x11 client, server共にdocker内で動いている。このままでは画面が見えないが、VNCやスクリーンショットの形で出力する。この場合は、実際に描画処理をしているのもコンテナになる。</p>
<ul>
<li>kubernetesやECSなどのコンテナベースのサービスを使っていて、Dockerコンテナの外側の環境に触れない場合</li>
<li>host上にX11サーバーをセットアップするのが面倒/できない場合</li>
</ul>
<p>だと、コンテナ内で完結する2の方法を取らざるを得なくなる。</p>
<p>例えば <a href="https://kunst1080.hatenablog.com/entry/2018/03/18/225102">DockerでXサーバを動かしてGUIを直接表示する - くんすとの備忘録</a> はこのアプローチを取っている。基本的にはコンテナ内にXserverとXclientを両方インストールし実行する。</p>
</li>
</ol>
<h3 id="ab-gpu-enabled-x-server-on-docker">(A+B) GPU-enabled X server on Docker</h3>
<p>普通のXアプリなら上の手順で良いが、OpenGLなどグラフィック用途でGPUを使うアプリケーションの場合、</p>
<ol>
<li>
<p>hostのx11 socket共有</p>
<p><a href="https://qiita.com/syoyo/items/22a0db4d49495020f1bd">NVIDIA Docker で HW accelerated な OpenGL(GLX) を動かす(2019 年版) - Qiita</a> でも触れられているように、 <a href="https://hub.docker.com/r/nvidia/opengl">nvidia/opengl - Docker Hub</a> などを使う方法がある。繰り返しになるがこの場合だとレンダリングしているのはホスト側になる。</p>
</li>
<li>
<p>docker内にx11 serverも建てる</p>
<p>この場合の手順について書かれた文章はほとんど見つけることができなかった。今回はこれをやりたい。</p>
<p>コンテナ内にインストールされたXserverがGPUを扱えるように、X用のドライバをインストールする必要がある。</p>
</li>
</ol>
<h4 id="グラフィック系ドライバのdocker内へのインストール">グラフィック系ドライバのdocker内へのインストール</h4>
<p>docker内でNVIDIA GPUを扱いたいときは、nvidia-docker2を使うのが普通。これはホストのGPUのデバイスファイルとそのドライバをコンテナと共有し、GPUをコンテナ内側でも使えるようにする技術だが、公式にはCUDA系やOpenGLの一部のみをサポートしていて、グラフィック系のGLX・Xorgからのレンダリング・vulkan等に対応していない( https://github.com/NVIDIA/nvidia-docker/issues/631 )ため、XorgがNVIDIA-GPUを使うためのドライバやGLXのextensionはコンテナと共有されない。</p>
<p>なので、nvidia-docker2の機能でGPUのデバイスファイルを使えるようにし、ドライバはコンテナ内部で1からインストールすることでGLXをコンテナ内で使えるようにできた。</p>
<p>コンテナ内部でドライバをインストールするDockerfileとして、公式の <a href="https://hub.docker.com/r/nvidia/driver">nvidia/driver - Docker Hub</a> とその解説wiki <a href="https://github.com/NVIDIA/nvidia-docker/wiki/Driver-containers-(Beta)">Driver containers (Beta) · NVIDIA/nvidia-docker Wiki</a> がある。これはNVIDIAのドライバを公式からダウンロードして、インストールスクリプトを実行しているDockerfileだが、前述のようにX系のドライバをインストールを省略している。 https://gitlab.com/nvidia/driver/blob/master/ubuntu16.04/Dockerfile#L43 を見ると</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> --x-prefix=/tmp/null \
--x-module-path=/tmp/null \
--x-library-path=/tmp/null \
--x-sysconfig-path=/tmp/null \
</code></pre></div></div>
<p>そこでXorgをインストールした後、ドライバインストール時にこのオプションをきちんと設定して、X用のグラフィックドライバをインストールさせる。これが終われば、ホストのGPUサーバー上でXorgを動かすのと全く同様に、dockerコンテナ内でXorgを(GPU込みで)動かすことができるようになる。</p>
<p>あとは(A)と同じで、(A)のコマンドで作ったxorg.confをコンテナ内に設置して、<code class="language-plaintext highlighter-rouge">Xorg &</code>でサーバーを走らせれば動く。</p>
<h2 id="各種ソフトウェア">各種ソフトウェア</h2>
<h3 id="xorg">xorg</h3>
<p>X11サーバーの1つの実装。</p>
<p><code class="language-plaintext highlighter-rouge">Xorg :0</code>とするとサーバーが立ち上がる。</p>
<p>例えば<code class="language-plaintext highlighter-rouge">x11-apps</code>を入れて<code class="language-plaintext highlighter-rouge">DISPLAY=:0 xeyes</code>とすると目玉のテストアプリケーションが立ち上がる。</p>
<p><a href="https://qiita.com/gm3d2/items/8346c76961d3fdb257b7">UbuntuでNVIDIAのディスプレイドライバが動作しない場合のチェック項目 - Qiita</a> が参考になる。</p>
<table>
<tbody>
<tr>
<td>lspci</td>
<td>grep NVIDIA</td>
</tr>
</tbody>
</table>
<ul>
<li>設定ファイル /etc/X11/xorg.conf</li>
<li>ログファイル /var/log/Xorg.0.log</li>
<li>/usr/lib/xorg/modules/drivers ドライバ</li>
<li>/usr/lib/xorg/modules/extensions glxのエクステンションなど</li>
</ul>
<h3 id="テスト用x11アプリ">テスト用X11アプリ</h3>
<ul>
<li>xeyes Xサーバーが動いているか?</li>
<li>glxgears, glxinfo GLXが使えるか?</li>
<li>vulkan-smoketest vulkanが使えるか?</li>
</ul>
<h3 id="x11vnc">x11vnc</h3>
<p>起動しているx11 serverの画面をそのままvncで飛ばすソフトウェア。実際のdisplayが繋がっている場合はその画面が、仮想displayの場合はその中身が転送される。</p>
<p>ずっと起動しておく -forever</p>
<p>ポート変更 x11vnc -rfbport 5566</p>
<h3 id="virtualgl">virtualGL</h3>
<ul>
<li>公式 https://virtualgl.org/About/Background</li>
<li>わかりやすいarchlinuxの解説 https://wiki.archlinux.jp/index.php/VirtualGL</li>
</ul>
<p>実際にアプリケーションを表示したいXサーバーのアドレスを<code class="language-plaintext highlighter-rouge">:0</code>、GPUにアクセスできるXサーバーのアドレスを<code class="language-plaintext highlighter-rouge">:1</code>とする。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DISPLAY=:0 VGL_DISPLAY=:1 vglrun glxgears
</code></pre></div></div>
<p>とした場合、アプリケーションのレンダリング命令が<code class="language-plaintext highlighter-rouge">:1</code>に行き、そのレンダリング結果が画像としてキャプチャされ、<code class="language-plaintext highlighter-rouge">:0</code>に送られる。これは通常通り</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DISPLAY=:0 glxgears
</code></pre></div></div>
<p>とした時に、GPUにアクセスできないXサーバー(例えばこれはssh -Xで転送されたクライアントのXサーバーなど)側にレンダリング命令が来てGPUを活用できない状況をパフォーマンス面で改善できるが、実際に<code class="language-plaintext highlighter-rouge">:0</code>から見える結果は全く同じになる。</p>
<h3 id="xvfb">xvfb</h3>
<p>仮想ディスプレイを作るソフト。
ただしGPUは使わない。ソフトレンダリングのみならこれで十分。</p>
<h3 id="nvidia-xconfig">nvidia-xconfig</h3>
<p>Xサーバーがnvidia gpuを使えるように</p>
<p>nvidia-xconfig –query-gpu-info</p>
<h3 id="x周りの要素技術についてのまとめ">X周りの要素技術についてのまとめ</h3>
<p><a href="http://ossan-engineer.blogspot.com/2016/12/">おっさんエンジニアの実験室: 12月 2016</a></p>Ryo NakabayashiOpenGLやGLXなどを使ってグラフィック用途でGPUを使うアプリケーションを、画面のないheadlessサーバー上で、しかもDocker内で動かす方法のまとめ。