Jenkinsからserverspecを実行しようとしたら、ポート確認で失敗した話
目的
serverspecの手動実行時のチェック結果と、Jenkinsから実行したチェック結果に差異が生じました。
まったく同じサーバー、ユーザー、コマンドで実施したが結果が違ったため原因を調査する。
前提条件
バージョン
・rake:13.0.1
・ruby:2.7.0p0
・Jenkins:2.220
AWS上にJenkisサーバ、webサーバーをそれぞれ準備
・公開鍵を登録し、特定のユーザーのみパスワード無しで、sshログインできる
・2サーバ間のセキュリティは全アクセスを許可済み
serverspecのチェック内容
require 'spec_helper' describe package('nginx') do it { should be_installed } end describe service('nginx') do it { should be_enabled } it { should be_running } end describe port(80) do it { should be_listening } end describe file('/usr/share/nginx/html/index.html') do it { should be_file } it { should exist } its(:content) { should match /^Hello, development ansible!!$/ } end
詳細事象
wedサーバー上でrake spec
を実行したところ、以下のように0 failuresで実行できました。
/home/ec2-user/.rbenv/versions/2.7.0/bin/ruby -I/home/ec2-user/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-support-3.9.2/lib:/home/ec2-user/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/lib /home/ec2-user/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/exe/rspec --pattern spec/localhost/\*_spec.rb Package "nginx" is expected to be installed Service "nginx" is expected to be enabled is expected to be running Port "80" is expected to be listening File "/usr/share/nginx/html/index.html" is expected to be file is expected to exist content is expected to match /^Hello, development ansible!!$/ Finished in 0.09714 seconds (files took 0.55084 seconds to load) 7 examples, 0 failures
しかし、Jenkinsで同じユーザーでログインし、rake spec
を実行したところ以下の結果になりました。
[SSH] executing... /home/ansible/.rbenv/versions/2.7.0/bin/ruby -I/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-support-3.9.2/lib:/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/lib /home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/exe/rspec --pattern spec/localhost/\*_spec.rb failed /home/ansible/.rbenv/versions/2.7.0/bin/ruby -I/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-support-3.9.2/lib:/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/lib /home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/exe/rspec --pattern spec/localhost/\*_spec.rb Package "nginx" is expected to be installed Service "nginx" is expected to be enabled is expected to be running Port "80" is expected to be listening (FAILED - 1) File "/usr/share/nginx/html/index.html" is expected to be file is expected to exist content is expected to match /^Hello, production ansible!!$/ Failures: 1) Port "80" is expected to be listening On host `localhost' Failure/Error: it { should be_listening } expected Port "80" to be listening /bin/sh -c ss\ -tunl\ \|\ grep\ -E\ --\ :80\\\ # ./spec/localhost/web_spec.rb:13:in `block (2 levels) in <top (required)>' Finished in 0.11224 seconds (files took 0.54982 seconds to load) 7 examples, 1 failure Failed examples: rspec ./spec/localhost/web_spec.rb:13 # Port "80" is expected to be listening [SSH] completed [SSH] exit-status: 1
まったく同じコマンドを実行したはずですが、1 failureになってしまいました。
どうも80番ポートのチェックで失敗しているように見えます。
切り分け
実施コマンドの確認
エラーメッセージを確認すると以下のメッセージが表示されていることから、 ssコマンドが実行されていることがわかりました。
/bin/sh -c ss\ -tunl\ \|\ grep\ -E\ --\ :80\\\
webサーバーの対象ユーザーからssコマンドを実行してみます。
$ ss -tunl Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 0 0 0.0.0.0:725 0.0.0.0:* udp UNCONN 0 0 127.0.0.1:323 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:111 0.0.0.0:* udp UNCONN 0 0 [::]:725 [::]:* udp UNCONN 0 0 [::1]:323 [::]:* udp UNCONN 0 0 [fe80::4db:f6ff:fee3:8fec]%eth0:546 [::]:* udp UNCONN 0 0 [::]:111 [::]:* tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* tcp LISTEN 0 100 127.0.0.1:25 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:111 0.0.0.0:* tcp LISTEN 0 128 [::]:80 [::]:* tcp LISTEN 0 128 [::]:22 [::]:* tcp LISTEN 0 128 [::]:111 [::]:*
当然実行できました。
次に、Jenkisサーバからsshコマンドにssコマンドを渡して実行してみます。
$ ssh <対象ユーザー>@<webサーバーIP> "ss -tunl" bash: ss: command not found
失敗しました。。コマンドが存在しない???
コマンドの渡し方が間違っていたのか確認しましたが、あってそうです。
ssコマンドをフルパスで実行してみます。
$ which ss /sbin/ss $ ssh <対象ユーザー>@<webサーバーIP> "/sbin/ss -tunl" Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 0 0 0.0.0.0:725 0.0.0.0:* udp UNCONN 0 0 127.0.0.1:323 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:111 0.0.0.0:* udp UNCONN 0 0 [::]:725 [::]:* udp UNCONN 0 0 [::1]:323 [::]:* udp UNCONN 0 0 [fe80::4db:f6ff:fee3:8fec]%eth0:546 [::]:* udp UNCONN 0 0 [::]:111 [::]:* tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* tcp LISTEN 0 100 127.0.0.1:25 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:111 0.0.0.0:* tcp LISTEN 0 128 [::]:80 [::]:* tcp LISTEN 0 128 [::]:22 [::]:* tcp LISTEN 0 128 [::]:111 [::]:*
実行できました!
どうもパスが通っていないように感じますね。
普通にコマンド実行する場合とssh越しにコマンド実行する場合で、環境変数に差があるか確認してみます。
<通常実行時(webサーバ)>
$ env XDG_SESSION_ID=4 HOSTNAME= SHELL=/bin/bash TERM=xterm HISTSIZE=1000 USER=ansible LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36: SUDO_USER=ec2-user SUDO_UID=1000 USERNAME=root PATH=/sbin:/bin:/usr/sbin:/usr/bin MAIL=/var/spool/mail/ec2-user PWD=/home/ec2-user LANG=en_US.UTF-8 SHLVL=1 SUDO_COMMAND=/bin/su ansible HOME=/home/ansible LOGNAME=ansible LESSOPEN=||/usr/bin/lesspipe.sh %s SUDO_GID=1000 _=/bin/env
<ssh越しにenvコマンド実行時(Jenkinsサーバ)>
$ ssh <対象ユーザー>@<webサーバーIP> "env" XDG_SESSION_ID=44 SHELL=/bin/bash SSH_CLIENT= USER=ansible MAIL=/var/mail/ansible PATH=/usr/local/bin:/usr/bin PWD=/home/ansible LANG=en_US.UTF-8 SHLVL=1 HOME=/home/ansible LOGNAME=ansible SSH_CONNECTION= LESSOPEN=||/usr/bin/lesspipe.sh %s XDG_RUNTIME_DIR=/run/user/1001 _=/usr/bin/env
予想通り、PATHに差異があることがわかりました。
<通常実行時(webサーバ)> PATH=/sbin:/bin:/usr/sbin:/usr/bin <ssh越しにenvコマンド実行時(Jenkinsサーバ)> PATH=/usr/local/bin:/usr/bin
解決
問題を解決すべく、ssh時の環境変数について調べたところ以下のブログが見つかりました。
ssh-env - ssh実行時に環境変数を設定/変更したい - spikelet days
上記ブログに記載してある以下の方法を試します。
まずは、webサーバの /etc/ssh/sshd_config を編集します。
109行目あたりにある PermitUserEnvironment no を PermitUserEnvironment yes に変更します。
#変更前の設定内容を確認 $ sudo grep PermitUserEnvironment /etc/ssh/sshd_config #PermitUserEnvironment no #PermitUserEnvironmentをyesに変更 $ sudo vi /etc/ssh/sshd_config #変更されていることを確認 $ sudo grep PermitUserEnvironment /etc/ssh/sshd_config PermitUserEnvironment yes
次に、~/.ssh/environment に環境変数を書く (VAR=VAL 形式で一行一変数で) を実行します。
sshでログインしたいユーザーのホームディレクトリ配下にある ~/.ssh に移動し、
設定したい環境変数を記載した environment ファイルを作成します。
今回は、コマンドを実行するために PATH= の値を変更したいので、 PATH=/sbin:/bin:/usr/sbin:/usr/bin を記載します。
#ホームディレクトリは以下の.sshディレクトリに移動 $ cd ~/.ssh #設定する環境変数(PATH=/sbin:/bin:/usr/sbin:/usr/bin)を記載 $ vi environment #記載内容の確認 $ cat environment PATH=/sbin:/bin:/usr/sbin:/usr/bin
これで、設定完了です。
試しに、Jenkinsサーバからsshコマンド越しにserverspecを実行してみます。
ssh <ユーザー名>@<IPアドレス> "ss -tunl" NetidState Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 0 0 0.0.0.0:111 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:724 0.0.0.0:* udp UNCONN 0 0 127.0.0.1:323 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:* udp UNCONN 0 0 [::]:111 [::]:* udp UNCONN 0 0 [::]:724 [::]:* udp UNCONN 0 0 [::1]:323 [::]:* udp UNCONN 0 0 [fe80::4db:f6ff:fee3:8fec]%eth0:546 [::]:* tcp LISTEN 0 128 0.0.0.0:111 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* tcp LISTEN 0 100 127.0.0.1:25 0.0.0.0:* tcp LISTEN 0 128 [::]:111 [::]:* tcp LISTEN 0 128 [::]:80 [::]:* tcp LISTEN 0 128 [::]:22 [::]:*
実行できました!!
次は、いよいよJenkinsのGUIからjobを実行してみます。
Started by user root Running as SYSTEM Building in workspace /var/lib/jenkins/workspace/serverspec jobtest [SSH] script: cd /tmp/serverspec_sample ~/.rbenv/shims/rake spec [SSH] executing... /home/ansible/.rbenv/versions/2.7.0/bin/ruby -I/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-support-3.9.2/lib:/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/lib /home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/exe/rspec --pattern spec/localhost/\*_spec.rb Port "80" is expected to be listening Package "nginx" is expected to be installed Service "nginx" is expected to be enabled is expected to be running Port "80" is expected to be listening File "/usr/share/nginx/html/index.html" is expected to be file is expected to exist content is expected to match /^Hello, development ansible!!$/ Finished in 0.08618 seconds (files took 1.01 seconds to load) 8 examples, 0 failures [SSH] completed [SSH] exit-status: 0 Started calculate disk usage of build Finished Calculation of disk usage of build in 0 seconds Started calculate disk usage of workspace Finished Calculation of disk usage of workspace in 0 seconds Finished: SUCCESS
成功しました!!
余談
今回色々調べている中で、以下のようにパイプを使用して、ssh越しにコマンドを実行する方法があるのを知りました。
echo `ss -tunl` | ssh <ユーザー名>@<IPアドレス>
このコマンドだと、上記の解決を実施しなくてもコマンド実行できました。
これも環境変数が違うのかと思い、envコマンドを実行してみました。
$ echo "env" | ssh ansible@172.31.23.243 Pseudo-terminal will not be allocated because stdin is not a terminal. XDG_SESSION_ID=24 HOSTNAME=ansible-server.tk.com SHELL=/bin/bash HISTSIZE=1000 SSH_CLIENT=172.31.18.127 54540 22 USER=ansible MAIL=/var/spool/mail/ansible PATH=/home/ansible/.rbenv/shims:/home/ansible/.rbenv/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/home/ansible/.local/bin:/home/ansible/bin PWD=/home/ansible LANG=en_US.UTF-8 RBENV_SHELL=bash HISTCONTROL=ignoredups SHLVL=1 HOME=/home/ansible LOGNAME=ansible SSH_CONNECTION=172.31.18.127 54540 172.31.23.243 22 LESSOPEN=||/usr/bin/lesspipe.sh %s XDG_RUNTIME_DIR=/run/user/1001 _=/bin/env
今までのどれとも違う環境変数が出力されました。。
少し調べてみましたが、結論が出ませんでした。 時間のある時に調べて、原因がわかり次第追記しようと思います。
以上です。