AmazonLinuxでselenium + chromedriver + headlessするメモ
※2018/09/04追記:今はもっと簡単に導入できるので当記事みたいな苦労は不要。
※ここではselenium-server-standaloneを使ってるけど直近のselenium-webdriverで非remoteの場合はselenium-server-standaloneなしで動かした方がよさそう。
これまでAmazonLinuxでseleniumをheadlessモード(Xvfbを使った昔ながらのやり方ではなくchromeやfirefoxに機能として備わったheadlessモード)で動かす際、chromeもfirefoxも導入がややこしかったが、AmazonLinux2はCentOS7チックになったためgoogle-chromeのリポジトリを設定してyumするだけで入るようになった。
とりあえず備忘録として無印AmazonLinuxとAmazonLinux2での導入手順のメモを残しておく。
以下注記。
- Dockerfileで記す。
- dockerでのみ必要でAMIから生成した場合は不要な記述もある。
- Amazon Linux 2のタイムゾーン設定は未調査なのでこのままだとダメかも。
- 無印Amazon Linuxで使っていたintoli提供のchromeのRPMは2017/12/29現在なくなってるかも。
- PHP7でアプリを作ってる想定。
- MITM ProxyはPython2.7で動かす想定。
無印Amazon Linux (2017.09以前)の場合
FROM amazonlinux:2017.09 # タイムゾーン RUN echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock && \ echo 'UTC=false' >> /etc/sysconfig/clock && \ ln -snf /usr/share/zoneinfo/Japan /etc/localtime # remi RUN yum -y install http://rpms.famillecollet.com/enterprise/remi-release-6.rpm # 日本語の画面キャプチャをするためにIPAフォント、AWS CLIのレスポンス加工したいときのためにjq RUN yum -y install jq ipa-gothic-fonts ipa-mincho-fonts # アプリケーション用のユーザとグループとログ出力場所 RUN yum -y install shadow-utils && \ groupadd -g 800 appuser && \ useradd -u 800 -g appuser -s /sbin/nologin appuser && \ mkdir /var/log/appuser && \ chown appuser:appuser /var/log/appuser # PHP7 RUN yum -y install https://mirror.webtatic.com/yum/el6/latest.rpm && \ yum -y install php70w php70w-cli php70w-common php70w-devel php70w-fpm php70w-gd php70w-mbstring php70w-mcrypt php70w-mysqlnd php70w-opcache php70w-pdo php70w-pear php70w-xml php70w-pecl-redis php70w-pecl-imagick php70w-intl && \ yum -y install re2c gcc gcc-c++ libuuid-devel # composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # selenium-server-standalone用Java RUN yum -y install java-1.8.0-openjdk # selenium系 # docker imageにunzipなかったので入れておく RUN yum -y install unzip # chrome driver RUN curl -o /tmp/chromedriver.zip https://chromedriver.storage.googleapis.com/2.33/chromedriver_linux64.zip && unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/ # chrome(intoli提供のRPM。2017/12/29現在なくなってる?) RUN yum -y install https://intoli.com/blog/installing-google-chrome-on-centos/google-chrome-stable-60.0.3112.113-1.x86_64.rpm # selenium server RUN curl -o /usr/local/lib/selenium-server-standalone-3.6.0.jar http://selenium-release.storage.googleapis.com/3.6/selenium-server-standalone-3.6.0.jar # chrome libs (ConsoleKitとpolkitは相互依存してるので単一コマンドで入れる必要あり) RUN yum -y install http://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/ORBit2-2.14.17-6.el6_8.x86_64.rpm && \ yum -y install http://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/ConsoleKit-libs-0.4.1-6.el6.x86_64.rpm && \ yum -y install http://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/eggdbus-0.6-3.el6.x86_64.rpm && \ yum -y install http://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/ConsoleKit-0.4.1-6.el6.x86_64.rpm http://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/polkit-0.96-11.el6.x86_64.rpm && \ yum -y install http://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/GConf2-2.28.0-7.el6.x86_64.rpm # MITM Proxy # chromedriverのバグで相手サーバのオレオレ証明書の検証をスキップできないのでMITM Proxyを相手サーバの検証をスキップするモードで動かし、chromedriverにはMITM Proxyの証明書を信頼させる形で回避。 # Chrome M65で新オプション導入されるのでそれまでMITM Proxyでしのぐ。 # https://bugs.chromium.org/p/chromium/issues/detail?id=721739 # 最新のMITM Proxyはpython3用なので2.7系なら0.18.2を明示して導入。 # docker imageにpipなかったので入れておく RUN yum -y install python27-devel python27-pip RUN yum -y install libxml2-devel libxslt-devel openssl-devel && \ pip install "mitmproxy==0.18.2" # php.iniや自分のアプリの起動、MITM Proxyの起動スクリプトはここでコピー COPY etc/php.d/php.ini /etc/php.d/ COPY etc/init.d/selenium /etc/init.d/ COPY etc/init.d/mitmproxy /etc/init.d/ RUN chmod 755 /etc/init.d/selenium && \ chmod 755 /etc/init.d/mitmproxy # サービスの定期実行 RUN echo '*/10 * * * * /usr/bin/php -c /etc/php.ini /opt/apps/myapp/start.php >> /var/log/appuser/output.log 2>&1' >> /var/spool/cron/appuser && \ chkconfig crond on && \ chkconfig selenium on && \ chkconfig mitmproxy on
Amazon Linux 2 (2017.12以降)の場合
FROM amazonlinux:2017.12 # タイムゾーン(無印からコピーしてきて未検証のままなので効いてないかも) RUN echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock && \ echo 'UTC=false' >> /etc/sysconfig/clock && \ ln -snf /usr/share/zoneinfo/Japan /etc/localtime # remi(無印だとepelが入ってたが2だと入ってないっぽい) RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm RUN yum -y install http://rpms.famillecollet.com/enterprise/remi-release-7.rpm # 日本語の画面キャプチャをするためにIPAフォント、AWS CLIのレスポンス加工したいときのためにjq RUN yum -y install jq ipa-gothic-fonts ipa-mincho-fonts # アプリケーション用のユーザとグループとログ出力場所 RUN yum -y install shadow-utils && \ groupadd -g 800 appuser && \ useradd -u 800 -g appuser -s /sbin/nologin appuser && \ mkdir /var/log/appuser && \ chown appuser:appuser /var/log/appuser # PHP7 RUN yum -y install php70 php70-php-cli php70-php-common php70-php-devel php70-php-fpm php70-php-gd php70-php-mbstring php70-php-mcrypt php70-php-mysqlnd php70-php-opcache php70-php-pdo php70-php-pear php70-php-xml php70-php-pecl-redis php70-php-pecl-imagick php70-php-intl && \ yum -y install re2c gcc gcc-c++ libuuid-devel # composer # デフォルトでphp70にパスが通ってないためenableスクリプトを実行してphpへのパスを通す RUN source /opt/remi/php70/enable && \ curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # selenium-server-standalone用Java RUN yum -y install java-1.8.0-openjdk # selenium系 # docker imageにunzipなかったので入れておく RUN yum -y install unzip # chrome driver RUN curl -o /tmp/chromedriver.zip https://chromedriver.storage.googleapis.com/2.34/chromedriver_linux64.zip && unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/ # chrome(本当はファイルコピーがよいが手抜きでそのまま書いてしまった) RUN echo '[google-chrome]' > /etc/yum.repos.d/google-chrome.repo && \ echo 'name=google-chrome' >> /etc/yum.repos.d/google-chrome.repo && \ echo 'baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch' >> /etc/yum.repos.d/google-chrome.repo && \ echo 'enabled=1' >> /etc/yum.repos.d/google-chrome.repo && \ echo 'gpgcheck=1' >> /etc/yum.repos.d/google-chrome.repo && \ echo 'gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub' >> /etc/yum.repos.d/google-chrome.repo RUN yum -y install google-chrome-stable # selenium server RUN curl -o /usr/local/lib/selenium-server-standalone-3.6.0.jar http://selenium-release.storage.googleapis.com/3.6/selenium-server-standalone-3.6.0.jar # MITM Proxy # chromedriverのバグで相手サーバのオレオレ証明書の検証をスキップできないのでMITM Proxyを相手サーバの検証をスキップするモードで動かし、chromedriverにはMITM Proxyの証明書を信頼させる形で回避。 # Chrome M65で新オプション導入されるのでそれまでMITM Proxyでしのぐ。 # https://bugs.chromium.org/p/chromium/issues/detail?id=721739 # 最新のMITM Proxyはpython3用なので2.7系なら0.18.2を明示して導入。 # docker imageにpipなかったので入れておく RUN yum -y install python-devel python2-pip RUN yum -y install libxml2-devel libxslt-devel openssl-devel && \ pip install "mitmproxy==0.18.2" # php.iniや自分のアプリの起動、MITM Proxyの起動スクリプトはここでコピー # Amazon Linux 2はCentOS7同様systemdだが手抜きで無印と同じinit.dでやってる COPY etc/php.d/php.ini /etc/opt/remi/php70/php.ini COPY etc/init.d/selenium /etc/init.d/ COPY etc/init.d/mitmproxy /etc/init.d/ RUN chmod 755 /etc/init.d/selenium && \ chmod 755 /etc/init.d/mitmproxy # サービスの定期実行 # デフォルトでphp70にパスが通ってないためenableスクリプトを実行してphpへのパスを通す # Amazon Linux 2はCentOS7同様systemdだが手抜きで無印と同じchkconfigでやってる RUN echo '*/10 * * * * source /opt/remi/php70/enable && php /opt/apps/myapp/start.php >> /var/log/appuser/output.log 2>&1' >> /var/spool/cron/appuser && \ chkconfig crond on && \ chkconfig selenium on && \ chkconfig mitmproxy on
MITM Proxyのスクリプト
MITM Proxyは起動時に自身のキーペアを生成し公開鍵は「mitmproxy-ca-cert.pem」として出力する。
これを「/usr/share/pki/ca-trust-source/anchors」に格納して「update-ca-trust」コマンドを実行することでOS内で「mitmproxy-ca-cert.pem」が信頼できる証明書として登録される。
なお、MITM Proxyのキーペアは起動時に異なる内容で生成されるため、MITM Proxyのインストール時に「/usr/share/pki/ca-trust-source/anchors」にコピーしてもダメ。必ず起動の度にOSにそれを信頼させる必要がある。
start_mitmproxy.sh
#!/bin/bash # mitmproxy only for chromedriver bug. export PATH=$PATH:/usr/local/bin count=`ps -ef | grep [m]itmdump | wc -l` if [ $count -ne 0 ]; then echo "mitmproxy (mitmdump) is already started. Nothing to do." exit 0 fi nohup mitmdump -p 28080 --insecure > /dev/null 2>&1 & # mitmproxy generate mitmproxy-ca-cert.pem in FIRST START TIME (not a time of installed). sleep 3 cp ~/.mitmproxy/mitmproxy-ca-cert.pem /usr/share/pki/ca-trust-source/anchors update-ca-trust enable update-ca-trust extract
stop_mitmproxy.sh
#!/bin/bash # mitmproxy only for chromedriver bug. ps -ef | grep [m]itmdump | awk '{ print "kill -9", $2 }' | sh
Selenium Serverのスクリプト
PHPの任意のアプリmyappはユーザappuserのcronで定期実行される想定(複数の並行実行あり)。
selenium-server-standaloneとMITM Proxyは立ち上げっぱなし。
selenium-server-standaloneを落とす際にmyappがひとつも動いていないことを確認する処理と落ちている間はmyapp起動cronを止める処理を入れている(myapp自身の中にも/tmp/stop-selenium-server有無のチェックあり)。
start_selenium_server.sh
#!/bin/bash export PATH=$PATH:/usr/local/bin # it is heavy logs # -Dwebdriver.chrome.logfile=${CHROME_LOG} # -Dwebdriver.chrome.verboseLogging=true CHROME_LOG=/var/log/appuser/chromedriver.log-wor SELENIUM_LOG=/var/log/appuser/selenium.log-wor SELENIUM_SERVER_JAR=/usr/local/lib/selenium-server-standalone-3.6.0.jar count=`ps -ef | grep ${SELENIUM_SERVER_JAR} | grep -v grep | wc -l` if [ $count -ne 0 ]; then echo "Selenium Server is already started. Nothing to do." exit 0 fi nohup java -Dwebdriver.chrome.logfile=${CHROME_LOG} -Dselenium.LOGGER=${SELENIUM_LOG} -Dselenium.LOGGER.level=WARNING -jar ${SELENIUM_SERVER_JAR} -enablePassThrough false > /dev/null 2>&1 & echo "Remove the stop Selenium Server flag file." rm -rf /tmp/stop-selenium-server if [ -e /var/spool/cron/appuser ]; then echo "There is new appuser cron file. Remove old it." rm -rf /var/spool/cron/.appuser else echo "Enable appuser cron." mv -i /var/spool/cron/{.,}appuser fi
stop_selenium_server.sh
#!/bin/bash echo "Disable appuser cron." if [ -e /var/spool/cron/appuser ]; then mv -i /var/spool/cron/{,.}appuser fi echo "Create the stop Selenium Server flag file." touch /tmp/stop-selenium-server sleep 2 while : do count=`ps -ef | grep php | grep [M]yapp | grep -v "/bin/sh -c " | wc -l` if [ $count = 0 ]; then break fi echo "There are processes of myapp count[${count}]. Wait..." sleep 5 done echo "There is no process of myapp. OK." ps -ef | grep [s]elenium-server-standalone | awk '{ print "kill -9", $2 }' | sh