Skip to content

DockerのJenkinsコンテナからSMB/CIFSのNASをマウントする

これは2023-04-16に書いた古い記事です。内容が古いですがアーカイブとして残しておきます。

はじめに

ふと思い立ち、自宅のWindows10マシンのDocker Desktop on Windows上にJenkinsのコンテナを立てた際に、コンテナ側からNAS上のSMB/CIFS共有のドライブをマウントしようとして数時間ハマった。

この記事は基本的には先人が書き留めた情報をインターネットから拾い集めパッチワークしたものに過ぎないが、いずれ誰かの役に立つかも知れないので、ここにメモを残しておこうと思う。やりたい事は以下の通り。

  • Docker Desktop on Windowsで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が起動している事が確認できる。実に簡単だ。便利な時代になった。

  • Docker Desktop on Windowsでは他にも色々なサーバーを立てているが、ポート8080は空いていたのでports:の設定は変更していない。
  • Windowsの再起動後にコンテナが自動で立ち上がるよう、restart: alwaysを指定している。
  • Jenkinsの初期画面等の情報はWeb上に溢れているので、ここでは割愛する。

docker-compose.ymlの変更

やりたいことを実現するため、docker-compose.ymlに手を加えてゆく。

タイムゾーンを変更する

    environment:
        # タイムゾーンを設定する
        - JAVA_OPTS=-Duser.timezone=Asia/Tokyo

デフォルトのタイムゾーンはUTCになっている。

タイムゾーンが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がインストールされていない。必要ないからだ。command:にて指定する形で愚直にpython3をインストールしようと思ったが、ややハマった。

最初にapt-get updateを実行し、次にapt-get installでpython3とpipを順にインストールし、最後は/usr/local/bin/jenkins.shのシェルスクリプトを叩いてJenkinsを起動している。この最終系に辿り着くに辺り、ハマった点を以下に列挙する。

このコンテナにはsudoがない

jenkins | bash: line 1: sudo: command not found

Jenkins公式のイメージは最小限の構成であるためか、sudoコマンドが入っていなかったので、sudo apt-get updateが通らない。

最小限の構成のコンテナには、sudoがないのが普通なのだろうか?

sudoがないならrootでやる

jenkins | Reading package lists…
jenkins | E: List directory /var/lib/apt/lists/partial is missing. - Acquire (13: Permission denied)

rootでないと権限が足りず死ぬ。この記事を参考にして、user: rootを指定した。

command:は追加ではなく書き換え

前述の通り、やりたいのはpython3とpipのインストール後にJenkinsを起動することである。

だが、最後の/usr/local/bin/jenkins.shを叩かないと画像のような状態になってしまう。command:の指定によりデフォルトのコマンドの書き換えが行われるので、肝心のjenkins.shを叩かないと、python3とpipのインストールだけで終わった状態になってしまうのである。原因の調査、対応のヒントとしてはこの記事がとても参考になった。

「じゃあcommand:に何も指定していない場合に実行される、Jenkinsを起動するデフォルトのコマンドって何だよ?!」となったのだが、Webで調べてもイマイチ分からなかった。そのため自分で色々調べたが、多分jenkins.shで良い筈だ。

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
    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"

色々試して、この時点でdocker-compose.ymlは上記の通りとなった。Jenkinsのジョブでpython3が動く状態である。

絶対パスでマウントするには

    volumes:
      # ローカルのスクリプト配置場所
      - type: bind
        source: /c/dezikomoe_python
        target: /var/dezikomoe_python
        read_only: true

実行したいPythonのコードはホストマシン上に存在するため、Jenkinsコンテナからそれが見えるようにマウントする必要がある。

Webでサンプルを調べてみたのだが、volumes:の指定は相対パスで記述されているか、絶対パスで指定する場合でも/開始のものばかりだった。では和尚様、Windows環境にてドライブレターから絶対パスを指定する場合はどう書くのですか?

内容は少し古いがこの記事が参考になった。要するに c:\dezikomoe_pythonではなく/c/dezikomoe_pythonで記述すればOK。

なお、Jenkinsコンテナ側からPythonスクリプトを変更することはないので、念のためread_only: trueを指定している。

NASをマウントする

やっとこの記事の本題に辿り着いた。

ここからコンテナからNASにアクセスできるように設定を入れていく。当初はこの記事を見て、余裕そうだと思っていたのだが……

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 のエラーが出て怒られてしまった。うーむ……。Stack Overflowでも似たようなやり取りを見付けられたが、結論としては、ほぼ最初の記事の内容と変わらずであった。

"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の設定を確認すると、画像の通りSMB1・SMB2のみのサポートになっていた。ビンゴだ!

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のバージョンであるSMB2と合せている。エラーの根本的原因はこれで解消した。

また、当初は IP アドレスを指定していたが addr=DS418_01と組み合わせる事でホスト名で指定をしている。