10

如何在 Jenkins 上用 Docker 跑 Rails + Rspec 做 CI

 2 years ago
source link: https://blog.niclin.tw/2019/08/18/using-jenkins-and-docker-to-run-rails-rspec-ci/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Nic Lin's Blog

喜歡在地上滾的工程師

基本上要跑 Rails CI 通常有很多第三方服務搭 integrate 可以做到,但如果你遇到和我的環境類似,也許你可以考慮自己把 Rails 專案用 Docker 包起來後丟到 Jenkins 上去跑。

至於為什麼會這樣做呢?這是我遇到的場景

  • Rails 啟動有 data dependency (Redis 上沒「特別的」資料格式啟動不了)
  • Rails 啟動有特規的套件要裝(apt-get install 沒有的東西、或是要使用低版本的套件之類的…)
  • 沒有要付費使用第三方的 CI
  • 只有已經架好的 Jenkins

如果要能夠讓 Jenkins 把 code 拉下來跑 Rspec 測試,那麼就要去主機上裝很多套件(Ruby / MySQL / Redis / etc),而且還有指定低版本問題,再加上主機也不是只有單獨跑這個專案而已,為了不想要有其他影響的可能,所以最後決定就是把這些問題包到 Docker 裡面,這樣一來就可以把環境的部分處理掉,也可以有效隔離 dependency。

這邊希望做到的是,Jenkins 從 Git Server 拉下專案,透過專案 Build image,然後用這個 image run test,所以會有一個場景是 Jenkins 上的 workspace 有了 Rails 專案,透過 Docker build 把 Rails 專案 sync 進容器的部分。

(Jenkins Pull Project) --> (Docker build image) --> (Run docker image)

如果 Docker Image 直接從 Ruby / MySQL / Redis 開始裝起,那每跑一次 Docker image 應該會久到爆炸,這不是一個有效率的作法,所以我們會把固定的環境先包成 Docker base,做一個基礎容器,然後再將 Rails 要用的容器堆疊在上面。

這樣一來,Jenkins 就可以在每次執行任務時,去拉下 Base Image 然後開始 Build Rails 專案,會節省掉要一直重新 Build Base Image 的部分。

所以我們計畫如下

  1. 先製作 Docker Base Image
  2. 包 test 專用 image
  3. 丟到 Jenkins 去跑

Docker Base

在這裡我做了一支 Image Base,主要安裝

  • GPG 指定版本號為 1 系列
  • Ruby 2.6.2
  • MySQL
  • Redis

Dockfile 看要放哪都可以,這個可以不用放在 Rails 目錄裡。

# Docker Base
FROM ruby:2.6.2
MAINTAINER NicLin

# We don't like apt-get warnings.
ENV DEBIAN_FRONTEND noninteractive

# Basic package
RUN apt-get update && apt-get install -y exiftool build-essential libpq-dev nodejs imagemagick gnupg1
RUN apt-get remove --auto-remove gpg
RUN cp /usr/bin/gpg1 /usr/bin/gpg

# === MySQL ===
RUN apt-get -y install mysql-server mysql-client --no-install-recommends

# === Redis ===
RUN apt-get -y -q install redis-server

# === SSDB ===
RUN wget --no-check-certificate https://github.com/ideawu/ssdb/archive/master.zip
RUN unzip master
RUN cd ssdb-master && make

docker build -t niclin:base .

這包在本地 Build 完後,就推到 Docker Hub 上了,如果有自己的 Server 也可以推自己的。

Test Image

現在我打算包一個「跑 Rspec」專用的 Image,他的生命週期就是

(Build Image) --> (Run Test Case) --> (Remove!)

這裡的 Dockfile 檔案就是直接放在 Rails 根目錄,因為這是每次 Jenkins 拉新的 code 下來要直接 build 的 docker image。

FROM 如果沒有指定自己的 server url,默認就是依照用戶名和 image 名稱直接去 docker hub 上抓

# Using Base Image
FROM niclin/base

MAINTAINER NicLin

# 設定一個程式起始的目錄
ENV APP_HOME /usr/src/app
RUN mkdir -p $APP_HOME

# 在這邊先加入 Gemfile 並 Bundle
COPY Gemfile $APP_HOME/Gemfile
COPY Gemfile.lock $APP_HOME/Gemfile.lock
RUN cd $APP_HOME && bundle install --jobs 4 --without development production

# 將現在(本地)所在專案目錄加入到 Docker Image 內
ADD . $APP_HOME

# 連結需要的檔案
RUN ln -s /usr/bin/convert /usr/local/bin/convert
RUN ln -s /usr/bin/exiftool /usr/local/bin/exiftool

# 加入 Jenkins workspace folder gpg key
# 這是我專案要用到的部分,一般人可能不需要就是了,可以跳過這裡
ADD ./gpg_home $APP_HOME/lib/gpg_home

# 連結需要的設定檔
RUN cp $APP_HOME/config/settings.yml.ci $APP_HOME/config/settings.yml
RUN cp $APP_HOME/config/storage.yml.ci $APP_HOME/config/storage.yml
RUN cp $APP_HOME/config/database.yml.ci $APP_HOME/config/database.yml

RUN cd $APP_HOME && RAILS_ENV=test bundle exec rake assets:precompile

# 設定 Docker 的工作目錄
WORKDIR $APP_HOME

ENTRYPOINT ["/bin/bash", "ci_script.sh"]

上面這隻 Docker image 要用到的東西有

  • config/settings.yml.ci
  • config/storage.yml.ci
  • config/database.yml.ci
  • ci_script.sh

上面 .ci 的部分就是我希望這些設定是只有給 CI server 用的,所以獨立出來。

ci_script.sh 的部分如下

service mysql start &&\
service redis-server start &&\
/ssdb-master/ssdb-server -d start /ssdb-master/ssdb.conf

RAILS_ENV=test bundle exec rake ci:initialize &&\
RAILS_ENV=test bundle exec rake db:create &&\
RAILS_ENV=test bundle exec rake db:migrate &&\
RAILS_ENV=test bundle exec rspec

至於這個啟動腳本就是在 Docker 啟動時直接做,所以放在 ENTRYPOINT 跑

  1. 啟動 MySQL
  2. 啟動 Redis
  3. 啟動 SSDB
  4. 跑 Redis data initialize
  5. 跑 database 建立
  6. 最後一步,跑 Rspec

如果要在自己電腦上先測試,可以在本地可以自己包一遍

docker build -t project:test .

然後直接執行看看會不會跑完整個任務

docker run -it project:test

Jenkins 設定

套件使用:

  • docker build and publish plugin
  1. 拉 Project
  2. shell 前置作業,看有沒有什麼固定檔案要先放的,如果沒有可以跳過此步
  3. 設定 Docker build, Skip publish
  4. shell 跑 docker run

小技巧:docker plugin 中的 build tag 打 $BUILD_NUMBER, 這樣他就會依照數字下去跑類似 project/test:142 ,然後最後一個 shell script 執行

docker run --rm project/test:$BUILD_NUMBER

這樣就可以確保跑完刪掉,一次性的測試,不會塞太多 Image 在 Jenkins Server


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK