swift 2.2 in docker on Linux

今日もDockerです。最近はまってます。
今回はdockerでswift 2.2の環境をLinux上で整えます。みんなもきっとserver-side swiftとかしたいよね!

はじめに

Appleの公約どおりswiftOSSで公開されました。
swift.org

Linuxでも動かせますがUbuntuを想定しているようです。Dockerであればディストリビューションに依らず開発・実行もできますし、swiftのバージョンごとに環境を作っておくこともできます。便利ですね。

前準備

Dockerfile

そのまま貼っておきます。大した内容ではありません。
なお、一部ファイルのread権限が不足してるためchmod -R +r /usr/lib/swiftを実行してます。

Dockerfile

FROM ubuntu:15.10
LABEL Description="swift compile environment"

RUN apt-get update && apt-get install -y wget clang libicu-dev libpython2.7-dev libxml2

ENV SWIFT_VERSION  swift-2.2-SNAPSHOT-2015-12-22-a
ENV SWIFT_PLATFORM ${SWIFT_VERSION}-ubuntu15.10
ENV SIWFT_URL https://swift.org/builds/ubuntu1510/${SWIFT_VERSION}/${SWIFT_PLATFORM}.tar.gz
RUN wget -q ${SIWFT_URL} ${SIWFT_URL}.sig
RUN gpg --keyserver hkp://pool.sks-keyservers.net \
      --recv-keys \
      '7463 A81A 4B2E EA1B 551F  FBCF D441 C977 412B 37AD' \
      '1BE1 E29A 084C B305 F397  D62A 9F59 7F4D 21A5 6D5F'
RUN gpg --verify ${SWIFT_PLATFORM}.tar.gz.sig && tar xzf ${SWIFT_PLATFORM}.tar.gz
RUN cp -pr ${SWIFT_PLATFORM}/* / && rm -rf ${SWIFT_PLATFORM}* && \
    chmod -R +r /usr/lib/swift

RUN mkdir /project
WORKDIR /project

swift実行ヘルパー

dockerコンテナ上で実行できるけど、毎回docker runするとstoppedコンテナで溢れかえるし、docker execもなんだか煩わしい。
そこで、ヘルパースクリプトを用意しました。

追記:docker run --rmのことを忘れていた。。。まぁいいや。

コンテナがない場合は作成してから、与えられた引数でdocker execを起動してくれます。もしコンテナがstopしていてもstartしてくれますので、要はコンテナの状態管理は気にしなくていいです。

container-exec

#!/bin/sh

CID_FILE=.cid

function container_not_exist()
{
    if docker inspect `cat $CID_FILE` > /dev/null; then
        return 1
    fi
    return 0
}

function docker_run()
{
    docker run -d -ti -u=`id -u`:`id -g` --cidfile=\"$CID_FILE\" -v `pwd`:/project swift:2.2 bash
}

if [ $# -lt 1 ]; then
    echo Usage: $0 command ...
    exit 0
fi

if [ ! -e $CID_FILE  ]; then
    docker_run
elif container_not_exist; then
    rm -f $CID_FILE
    docker_run
fi

CID=`cat $CID_FILE`
RUNNING=`docker inspect --format="{{ .State.Running }}" $CID`
if [ $RUNNING = 'false' ]; then
    docker restart $CID
fi

docker exec -ti $CID $@

Dockerイメージ作成

上述のDockerfileとcontainer-execを用意しておく。

$ ls
Dockerfile    container-exec*
$ docker build -t swift:2.2 ./

...(略

$ docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
swift                 2.2                 b44d46edd478        3 hours ago         1.337 GB
ubuntu                15.10               2804d41e7f10        2 weeks ago         133.5 MB

REPL

まずはREPLから起動してみましょう。

$ ./container-exec swift
Welcome to Swift version 2.2-dev (LLVM 3ebdbb2c7e, Clang f66c5bb67b, Swift 17fe37d715). Type :help for assistance.
  1> 1+1
$R0: Int = 2
  2> var foo = "foo"
foo: String = "foo"
  3> var bar = "bar" 
bar: String = "bar"
  4> foo + bar 
$R1: String = "foobar"
  5> import Foundation 
  6> import Glibc 
  7> srandom(UInt32(NSDate().timeIntervalSince1970)) 
  8> random() 
$R2: Int = 684624134
  9> random() 
$R3: Int = 379490731
 10> random() 
$R4: Int = 522347634
 11> 

実行ファイル作成

実行ファイルをビルドしてみましょう。

$ mkdir sources
$ cat <<EOF > sources/main.swift
import Foundation

print("Hello, world!")
EOF
$ cat <<EOF > Package.swift
import PackageDescription
 
let package = Package(
    name: "HelloWorld"
)
EOF
$ ./container-exec swift build
Compiling Swift Module 'HelloWorld' (1 sources)
Linking Executable:  .build/debug/HelloWorld
$ ./container-exec .build/debug/HelloWorld
Hello, world!
$ 

以上!!!

Pulseaudio in docker

CentOS7 上でdockerを使ってpulseaudioを起動してみた。

環境

Centos7 でdockerとdocker-composeをインストールしておく。alsa関連もインストールされてることを確認。

$ yum list installed | grep alsa
alsa-firmware.noarch                  1.0.28-2.el7                     @anaconda
alsa-lib.x86_64                       1.0.28-2.el7                     @anaconda
alsa-plugins-pulseaudio.x86_64        1.0.27-3.el7                     @base    
alsa-tools-firmware.x86_64            1.0.28-2.el7                     @base    
alsa-utils.x86_64                     1.0.28-4.el7                     @base

今回の環境のサウンドカードはこちら。

$ cat /proc/asound/cards
 0 [HDMI           ]: HDA-Intel - HDA Intel HDMI
                      HDA Intel HDMI at 0xf7234000 irq 54
 1 [PCH            ]: HDA-Intel - HDA Intel PCH
                      HDA Intel PCH at 0xf7230000 irq 53

PCHで音がなることを確認。

# cat <<EOF > ~/.asoundrc
pcm.!default {
  type hw
  card 1
}
ctl.!default {
  type hw
  card 1
}
EOF
# play sample.wav

docker

とくに解説はなし。

$ mkdir -p docker/pulseaudio
$ cd docker
$
$ cat <<EOF > pulseaudio/Dockerfile
FROM centos:7

RUN yum -y install \
    alsa-utils \
    pulseaudio \
    pulseaudio-libs \
    pulseaudio-utils

RUN useradd pulseaudio
RUN usermod -aG audio,pulse,pulse-access pulseaudio

USER pulseaudio

ENTRYPOINT [ "pulseaudio" ]
CMD [ "--log-level=4", "--log-target=stderr", "-v" ]
EOF
$
$ cat <<EOF > pulseaudio/asound.conf
pcm.!default {
  type hw
  card 1
}
ctl.!default {
  type hw
  card 1
}
EOF
$
$ cat <<EOF > pulseaudio/clinet.conf
autospawn = no
daemon-binary = /bin/true 
EOF
$
$ cat <<EOF > pulseaudio/daemon.conf
exit-idle-time = -1
flat-volumes = yes
EOF
$
$ cat <<EOF > pulseaudio/default.pa
# Replace the *entire* content of this file with these few lines and
# read the comments

.fail
    # Set tsched=0 here if you experience glitchy playback. This will
    # revert back to interrupt-based scheduling and should fix it.
    #
    # Replace the device= part if you want pulse to use a specific device
    # such as "dmix" and "dsnoop" so it doesn't lock an hw: device.
    
    # INPUT/RECORD
    load-module module-alsa-source device="default" tsched=1
    
    # OUTPUT/PLAYBACK
    load-module module-alsa-sink device="default" tsched=1 
    
    # Accept clients -- very important
    load-module module-native-protocol-unix
    load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.0.0/24;10.0.0.0/8;172.0.0.0/8 auth-anonymous=1

.nofail
.ifexists module-x11-publish.so
    # Publish to X11 so the clients know how to connect to Pulse. Will
    # clear itself on unload.
    load-module module-x11-publish
.endif
EOF
$
$ cat <<EOF > docker-compose.yml
pulseaudio:
  build: pulseaudio
  container_name: pulseaudio
  command: pulseaudio -vvv --log-target=stderr
  volumes:
    - /etc/localtime:/etc/localtime:ro
    - /var/run/dbus:/var/run/dbus
    - /etc/machine-id:/etc/machine-id:ro
    - ./pulseaudio/asound.conf:/etc/asound.conf
    - ./pulseaudio/default.pa:/etc/pulse/default.pa
    - ./pulseaudio/client.conf:/etc/pulse/client.conf
    - ./pulseaudio/daemon.conf:/etc/pulse/daemon.conf
  ports:
    - "4713:4713"
  devices:
    - /dev/snd:/dev/snd
  environment:
    - HOME:/home/pulseaudio
  working_dir: /home/pulseaudio
EOF

docker起動

$ docker-compose up -d pulseaudio

鳴らす

pulseaudio経由で音がなることを確認。

# su -
# yum install pulseaudio-utils 
# exit
$ paplay -v -s localhost sample.wav

Netlink

ifconfigよりipコマンドやssコマンドが推奨されているけど、単に置き換えるだけでなく、そもそもkernelとのやりとりがioctlからnetlink推奨に変わったという背景があるみたい。知らなかった。
d.hatena.ne.jp

とは言え、ツールの実装は全く枯れてなくてこんな話もある。
d.hatena.ne.jp

近年はsystemdやらipコマンド群やらLinuxディストリビューション付属のツール群が大きく変わってきてるわけで、古いけど安定みたいな利点が薄れてるのかなぁ。もちろん、より管理しやすくなってる面もある。

カーネル開発のコントリビュータも増えてることからOSS開発者の母数も増えてるんじゃなかろうか。それゆえOSS開発スピードやプロジェクト数も増えてるんだろうけど、質や安定性、違いのあまりない選択肢の増加などが、OSS界隈が混沌としてきてる要因だろうとも思える。
jp.linux.com



閑話休題

netlinkはsocketベースのインターフェースなので、unix domain socketを用いるのとあまり大差ない。netlink socketはnl_pidで区別する。netlink socketを利用するプロセスのプロセスIDと同値を用いることが推奨されるが、別に強制ではない。そもそもプロセスが複数のnetlink socketを持てば足りないし。
ユーザ空間のみでIPCとしてnetlinkを利用するなら、IPソケットと互換なunix domain socketでいいんじゃないかな。

Mac OSX with Sambaの安定化

(追記) Sierraでやり方変わりました。
macOS Sierra - Samba安定化 - Pseudo Engineer


結論から言うと、SMB1で接続するのが一番安定する。

ことの発端

自鯖NFSは不安定でいっそQnapのNASを購入したら、なかなかというか、かなりいい感じ。Samba/NFS/AFP対応とのことで、AFPも試してみたがうまく認証が通らず使えない。なのでSamba Onlyに使っているのだが、OSXのFinderからアクセスするとやたら遅い。そこでなんとかならんもんか試行錯誤することにした。

SMBのバージョンを上げてみる

当初はSMB2で接続されていたのでQnapの設定を変えて、SMB3.0も有効にした。接続できるけどやはりFinderからは重い。なお接続したときのプロトコルバージョンはこれで確認できる。

$ smbutil statshares -a
QnapのSamba設定をいじくってみる

直接SSHでログインしてsmb.conf変えるのは危険かもしれないが仕方ない。以下を参考に直接smb.confに設定を追加し、sambaをrestartするも特に変化なし。そもそもQTS 4.1付属のSambaは4.0.3くらいだったからちょっと古いかも。
hvc-001.com

SMB1にバージョンダウン

接続をSMB1にダウンすると安定するみたい。実際やってみたら全然マシになった。
10.9: Switch the SMB stack to use SMB1 as default - Mac OS X Hints

現時点ではどうしようもない

みんな不満なのです。
www.reddit.com

Terminalからは快適

Terminalから掘っていく分にはSMBのヴァージョンに関わらず全く問題ない。Finderの挙動的がやはり問題なのだろう。フリーのいい感じのファイラーも探したが微妙ですね。じゃあ作るか(ぇ

Docker 仕組みメモ

今更感あるけどDockerの基礎技術について調べたことを自分用にメモdocs.docker.com

Dockerとは

Linux Containerによる仮想実行環境(コンテナ)を操作するツール。コンテナはホストOSから独立した環境で動作しているように見えるが、実際はホストOS上のプロセスに過ぎない。Linux Containerは割と古く2008年頃から開発している。でもVersion 1.0が提供されたのは最近で2014年。Linux ContainerはカーネルのNamespaceとcgroupsに依存している。

Namespaceは以下のようなリソースの参照を、プロセスをグループ化して隔離する機能。

Namespace   Constant        Isolates
IPC         CLONE_NEWIPC    System V IPC, POSIX message queues
Network     CLONE_NEWNET    Network devices, stacks, ports, etc.
Mount       CLONE_NEWNS     Mount points
PID         CLONE_NEWPID    Process IDs
User        CLONE_NEWUSER   User and group IDs
UTS         CLONE_NEWUTS    Hostname and NIS domain name

cgroupsは以下のようなリソースの利用を、プロセスをグループ化して制限できる機能。

CPU(使用率、割り当てコア、niceなど)、メモリ、Swap、物理IO帯域、ネットワークIO帯域、etc

コンテナの作成や実行だけならDockerがなくてもLXCでできる。しかしイメージ(コンテナのディスクイメージ)の作成やそのバージョン管理、またコンテナをexportとして他の環境へデプロイなどはDockerが得意とするところ。さらにDocker Hubを通じてイメージをインターネットで共有できる。

Dockerコンテナ

DockerコンテナはDockerイメージから作成された実行単位。start、stop、pauseもできるし、stopしたからと言ってコンテナ内で作成されたファイルが消えるわけではない。

Dockerイメージ

Dockerコンテナを作成するためのテンプレート。要はベースとなるディスクイメージ。実体はunion filesystemによる差分管理。初期のDockerはAUFSが利用されていた。今はdevice-mapperやbtrfs、overlayfsなんかが主に利用されている。なお、device-mapper(のdm-thin)はファイルシステムではなく、ブロックレベルでのoverlayを実現している。
https://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt

差分のbaseは空っぽのディスクイメージ。なのでDockerイメージはすべてsnapshot。

CentOS7のデフォルトはdevice-mapper。ローカルのファイル(/var/lib/docker/devicemapper/devicemapper)をループバックデバイスとしてマウントし、さらにthin provisioningでプールを構築して、イメージの差分保存に利用している。

補足:
device-mapperとは論理ブロックデバイスの中の各ブロックが、どの物理ブロックデバイス内の、どのブロックを指すかマッピングする機能。まさにメモリのページング(仮想メモリアドレスと実メモリアドレスのマッピング)のブロックデバイス版。これにより、例えばHDDとSSDを1つの大きなディスクに見せることもできる。
※実際は論理ブロックデバイス上のブロックが、さらに別の論理ブロックデバイス上のブロックを指すこともできる

docker-compose からは run じゃなくて up

docker-compose から run したあと、stopできずに悩んだ。

$ docker-compose run -d mosquitto
$ docker-compose stop mosquitto # ← エラー出さないので受け付けてるようにみえる
$ docker-compose ps
          Name                         Command               State   Ports 
--------------------------------------------------------------------------
mosquitto_mosquitto_run_1   /usr/sbin/mosquitto -c /et ...   Up 
$ # 止まってない。。。

この場合、docker ps -a でコンテナID調べて直接stopさせるしかない?

$ docker ps -a
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                    NAMES
accb9df6017d        mosquitto_mosquitto   "/usr/sbin/mosquitto "   2 minutes ago       Up 2 minutes                                 mosquitto_mosquitto_run_1
$ docker stop accb9df6017d
accb9df6017d
$ docker ps -a
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                      PORTS                    NAMES
accb9df6017d        mosquitto_mosquitto   "/usr/sbin/mosquitto "   3 minutes ago       Exited (0) 11 seconds ago                            mosquitto_mosquitto_run_1

docker-compose up で起動するのが正解。

$ docker-compose up -d
Starting mosquitto_mosquitto_1
$ docker-compose ps
        Name                       Command               State           Ports          
---------------------------------------------------------------------------------------
mosquitto_mosquitto_1   /usr/sbin/mosquitto -c /et ...   Up      0.0.0.0:1883->1883/tcp 
$ docker-compose stop
Stopping mosquitto_mosquitto_1 ... done
$ docker-compose ps
        Name                       Command               State    Ports 
-----------------------------------------------------------------------
mosquitto_mosquitto_1   /usr/sbin/mosquitto -c /et ...   Exit 0

CentOS 7 - teaming と bonding

nmtuiを触っていたらteamとbondの設定があった。WindowsではチーミングLinuxだとボンディングと呼ばれているだけに、何で両方あるのかと思ったら、teamはbondに変わる新しい実装らしい。
詳しくはこちら。
Chapter 5. Configure Network Teaming

APIが用意されているため、teamdなど、ユーザー空間から制御できたり、機能的にもbondよりいいみたいだけど詳しくはまだ見てない。