はじめに
ふと思い立ち、自宅の Windows10 マシンの Docker Desktop 上 に Jenkins コンテナを立てた際に、Docker から NAS 上の SMB/CIFS 共有のドライブをマウントしようとして数時間ハマった。
この記事は基本的には先人が書き留めた情報をインターネットから拾い集めパッチワークしたものに過ぎないが、いずれ誰かの役に立つかも知れないので、ここにメモを残しておこうと思う。
やりたい事は以下の通り。
- WSL2 + Docker Desktop で Jenkins のコンテナを立てる。
- Jenkins で自作の Python スクリプトを定期実行する。
- Python スクリプトでは NAS 上のファイルを操作する。NAS は Synology の DiskStation DS418である。
docker-compose.yml 初期
version: '3'
services:
jenkins:
container_name: jenkins
image: jenkins/jenkins:latest
ports:
- 8080:8080
volumes:
- type: bind
source: ./jenkins_home
target: /var/jenkins_home
restart: always
Web で適当に検索して docker-compose.yml ファイルを作成する。
docker-compose.yml にこれを記述した後、PowerShell のコンソールから docker-compose up のコマンドを打てばそれだけで起動する。お手元のブラウザから localhost:8080 に接続すれば Jenkins が起動している事が確認できる。便利な時代になった。
- 色々サーバを立てているが、8080 番は空いていたので ports: の設定は変更していない。
- たまにある Windows の再起動後にコンテナが自動で立ち上がるよう、restart: always を指定している。
Jenkins の初期画面等の情報は Web 上に溢れているのでここでは割愛する。
タイムゾーンを変更する
environment:
# タイムゾーンを設定する
- JAVA_OPTS=-Duser.timezone=Asia/Tokyo
デフォルトのタイムゾーンは UTC になっている。使用する際に直感的に分かりにくいと思ったので、enviroment: にJAVA_OPTS=-Duser.timezone=Asia/Tokyo を指定して、タイムゾーンを JST に変更しておいた。→参考
Jenkins コンテナに python3 をインストール
# sudoがないのでrootで始める
user: root
# python3をインストールした後にjenkins.shで起動する
command: >
bash -c "
apt-get update &&
apt-get install --no-install-recommends -y python3 &&
apt-get install --no-install-recommends -y pip &&
/usr/local/bin/jenkins.sh"
該当部分の最終形は上記の通りとなった。
image: jenkins/jenkins:latest で指定している Jenkins 公式のイメージには Python がインストールされていない。ここは愚直にコンテナに対してpython3 をインストールしようと思ったが、ややハマった。
apt-get update を実行した後に apt-get で python3 と pip をインストールし、/usr/local/bin/jenkins.sh を叩いて Jenkins を起動している。この記述に辿り着くに辺り、ハマった点を以下に列挙する。
このコンテナには sudo がない
jenkins | bash: line 1: sudo: command not found
Jenkins 公式のイメージは最小限の構成であり sudo が入っていないので、sudo apt-get update が通らない。
root でやる
jenkins | Reading package lists…
jenkins | E: List directory /var/lib/apt/lists/partial is missing. - Acquire (13: Permission denied)
root でないと以下のように権限が足りず死ぬ。そのため user: root を指定し root で始めている。→参考
command: は追加ではなく書き換え

前述の通り、やりたいのは python3 と pip のインストール後に Jenkins を起動する事である。
だが、最後の /usr/local/bin/jenkins.sh を叩かないと、Docker Desktop では画像のような状態になってしまう。command: の指定で実行されるのはデフォルトのコマンドを書き換える事なので、肝心のjenkins.sh を叩かないとインストールだけして終わった状態になってしまうのである。原因の調査、対応のヒントとしてはこの記事がとても参考になった。
「じゃあ command: に何も指定していない場合に実行される、Jenkins を起動するデフォルトのコマンドって何だよ?!」となったのだが、Web で調べてもイマイチ分からなかった。そのため自分で色々調べたが、多分 jenkins.sh で良い筈だ。
docker-compose.yml Python が動く状態
version: '3'
services:
jenkins:
container_name: jenkins
image: jenkins/jenkins:latest
ports:
- 8080:8080
volumes:
- type: bind
source: ./jenkins_home
target: /var/jenkins_home
restart: always
environment:
# タイムゾーンを設定する
- JAVA_OPTS=-Duser.timezone=Asia/Tokyo
# sudoがないのでrootで始める
user: root
# python3をインストールした後にjenkins.shで起動する
command: >
bash -c "
apt-get update &&
apt-get install --no-install-recommends -y python3 &&
apt-get install --no-install-recommends -y pip &&
/usr/local/bin/jenkins.sh"
この時点では上記の通りとなる。Jenkins のジョブで python3 が動く状態である。
絶対パスでマウントする
volumes:
# ローカルのスクリプト配置場所
- type: bind
source: /c/dezikomoe_python
target: /var/dezikomoe_python
read_only: true
実行したいPythonのコードはホストマシン上に存在するため追加でマウントする。
Web の情報を見ると volumes: の指定は相対パスで記述されているか、絶対パスの場合でも/開始が多い。では和尚様、Windows環境にてドライブレターから絶対パスを指定する場合は?内容は少し古いがこの記事が参考になった。
要するに c:\dezikomoe_python ではなく /c/dezikomoe_python で記述すればOK。
なお、Jenkinsコンテナ側からPythonスクリプトを変更する事は無いので read_only: true を指定している。
SMB/CIFS の NAS をマウントする → エラー
volumes:
# 処理対象ファイル
- ds418_01_photo:/var/photo:ro
volumes:
ds418_01_photo:
driver_opts:
type: cifs
device: //192.168.11.50/photo
o: username=●●●,password=■■■
やっと本題に辿り着いた。device: で接続先の NAS の IP アドレスを指定して、o: で認証情報を指定している。この記事を見て、あ~余裕そうだな~と思っていたのだが……
operation not supported
ERROR: for jenkins Cannot start service jenkins: error while mounting volume '/var/lib/docker/volumes/jenkins_ds418_01_photo/_data': failed to mount local volume: mount
//192.168.11.50/photo:/var/lib/docker/volumes/jenkins_ds418_01_photo/_data, data: username=●●●,password=■■■: operation not supported
いきなり死ぬ。想定外の operation not supported のエラーが出て怒られてしまった。うーむ……。Stackoverflowでも似たようなやり取りがあったが、結論としてはほぼ最初の記事の内容と変わらず。
“docker”+”SMB”+”operation not supported” 等で検索したが、目ぼしい情報は見付けられなかった。
そもそも cifs-utils でマウントできるのか?
command: >
bash -c "
apt-get update &&
apt-get install --no-install-recommends -y cifs-utils &&
mkdir /var/photo &&
mount -t cifs -o username=●●●,password=■■■ //192.168.11.50/photo /var/photo"
この時点で既に02:00を回っており寝てしまいたかったが、土曜日だったので問題の切り分けに進んだ。
”operation not supported” 以外のもう少し詳細なエラー情報が欲しい。そもそも通常の手段でマウントできるのか?を調べてみよう。cifs-utils を使うために command: の指定を一時的に上記のように変えて動かしてみた。
cifs-utils でもエラー
jenkins | Unable to apply new capability set.
cifs-utils をインストールして mount を実行する。すると、上記のエラーが出た。
Unable to apply new capability set
jenkins | mount error(95): Operation not supported
jenkins | Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)
“Unable to apply new capability set” で Google 検索するとこの記事がヒットした。記事に従って privileged: true を追加して動かすとエラー内容が変わった。
先程見た “Operation not supported” のメッセージはここから出ているようだ。解決まで後少しの所まで来ている気がする。
mount error(95): Operation not supported

“mount error(95): Operation not supported” を丸ごと検索すると、今度はこの記事がヒットした。SMB のバージョンの問題??
DS418 の設定を確認すると画像の通りになっていた。ビンゴだ!
docker-compose.yml 完成版
version: '3'
services:
jenkins:
container_name: jenkins
image: jenkins/jenkins:latest
ports:
- 8080:8080
volumes:
- type: bind
source: ./jenkins_home
target: /var/jenkins_home
# ローカルのスクリプト配置場所
- type: bind
source: /c/dezikomoe_python
target: /var/dezikomoe_python
read_only: true
# 処理対象ファイル
- ds418_01_photo:/var/photo:ro
restart: always
environment:
# タイムゾーンを設定する
- JAVA_OPTS=-Duser.timezone=Asia/Tokyo
# sudoがないのでrootで始める
user: root
# python3をインストールした後にjenkins.shで起動する
command: >
bash -c "
apt-get update &&
apt-get install --no-install-recommends -y python3 &&
apt-get install --no-install-recommends -y pip &&
/usr/local/bin/jenkins.sh"
volumes:
ds418_01_photo:
driver_opts:
type: cifs
device: //DS418_01/photo
o: username=●●●,password=■■■,addr=DS418_01,vers=2.0
完成版の docker-compose.yml は上記の通りとなった。
o: にてvers=2.0を明示的に指定する事で NAS 側の SMB のバージョンと合せている。本質的な原因はこれだった。
また、当初は IP アドレスを指定していたが addr=DS418_01 と組み合わせる事でホスト名の指定をしている。