我們開發的流程一般是新建一個開發分支,然後開發,開發完打包部署到測試環境讓測試測。但是這裡有個痛點,尤其在大的團隊中,我們一般會很多人共用一臺測試機,這樣就導致了一個問題,一旦有人在使用這個機器,那麼其他人就無法使用該機器,因為切了分支就導致另外一個人的程式碼被切掉了,這樣就導致測試機器不夠用。而docker的出現,可以很好的解決這個問題。
備註:看本文需要對docker和gitlab-ci有所理解。關於這方面的知識不懂的,需要去學習下。
思路
我們在每個分支上開發準備提測的時候,就生成一個該分支特有docker映象,該映象基於nginx的,它會完成程式碼的打包和部署(到nginx上),以此來實現不同的分支程式碼在不同的docker容器上執行的目的,從而實現一個機器可以執行多個分支的程式碼的目的。
第一步: 編寫Dockerfile
因為我們需要用到nginx,所以我們的映象是基於nginx的,如果我們還要做到打包的操作,還需要用到node,假設我們的專案是在/home/test.liweiji.com下(當然這裡是一個git倉庫,我們需要先切換我們的分支並拉取我們自己的程式碼):
Dockerfile:
# 基於node映象
FROM node
# 設定工作路徑 類似cd /home/test.liweiji.com
WORKDIR /home/test.liweiji.com
# 打包到/home/test.liweiji.com/dist目錄下,當然是不是dist看自己專案
RUN npm install \
&& npm run build
# 基於nginx映象
FROM nginx
# 一般nginx的預設配置我們無法直接使用,所以需要用到自己的nginx配置
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
複製程式碼
nginx.conf
# 這裡根據你執行docker的使用者來定,設定不對,會出現403
user root;
events {
use epoll;
worker_connections 102400;
}
http {
# 這個不能少,不然會導致靜態樣式無法起效
include mime.types;
# 下面的配置根據自己的專案而定
server {
listen 80;
server_name test.liweiji.com;
location / {
add_header Access-Control-Allow-Origin *;
root /home/test.liweiji.com/dist;
break;
}
}
}
複製程式碼
第二步:生成映象並執行容器
我們登入到測試機,然後進入到/home/test.liweiji.com下
# 生成映象,不同的分支用不同的tag
docker build -t <image_name>:<tag> .
# 執行容器,埠對映大家靈活配置,這裡的例子是8080,不同的分支可以用不同的埠
docker run --name <container_name> -p 8080:80 -idt <image_name>:<tag>
複製程式碼
這樣我們就實現了不同的分支程式碼打包到不同的docker映象中了。我們可以使用docker ps檢視是不是在執行我們的docker容器了。
備註:如果沒有執行,則說明出錯了,但是docker run指令並不會告訴我們具體出錯的原因,我們可以需要通過docker logs <container_name>來檢視出錯資訊
通過上面兩步,我們就可以實現了打包不同的分支程式碼到不同的docker映象,讓測試通過不同的映象,測不同的分支程式碼。
但是這個方案還不夠完善,還需要開發者跑到測試機去切分支拉程式碼,打包映象執行容器。那麼有什麼方法讓這些操作自動完成呢,答案是肯定的,那就是gitlab-ci了。
利用gitlab-ci實現自動化
gitlab-ci可以讓我們提交程式碼後,觸發gitlab-runner去執行一系列的操作,比如安裝、打包、部署等。
所以,我們可以通過gitlab-ci來實現:當我們提交我們程式碼到遠端的時候,生成該分支的最新程式碼對應的映象,並執行該映象對應的容器,從而實現自動化。
第一步:註冊gitlab-runner
gitlab-ci真正的任務執行是由runner負責的,所以我們需要在特定的機器註冊runner,我們通過gitlab-ci-multi-runner來註冊runner。至於如何安裝gitlab-ci-multi-runner,這裡就不在講,大家去google下。
執行gitlab-ci-multi-runner register
來註冊runner,我們按照步驟一步一步輸入url、token、description、tag、executor等,我們根據我們gitlab網站上的setting->pipeline上找到url和token。 這裡的我們只執行shell命令,所以executor選擇shell。
第二步:編寫.gitlab-ci.yml
我們需要在.gitlab-ci.yml中定義我們要做的一系列操作,我們這裡需要上面所說的二個步驟:生成最新映象和執行容器。但是為了保證每次打包都是最新程式碼,我們需要有個清理的任務,所以有三個步驟:
# gitlab-cli各種變數檢視https://docs.gitlab.com/ee/ci/variables/
# 執行job的階段 按順序序列執行
stages:
- clean
- build
- run
# 清理映象
job1:
stage: clean
only:
- /^liweiji.*$/ # liweiji下的分支
tags:
- test
script:
- docker stop test:$CI_COMMIT_REF_NAME
- docker rm test:$CI_COMMIT_REF_NAME
- docker image rm test:$CI_COMMIT_REF_NAME
allow_failure: true #這裡要允許失敗,不然第一次清理是沒有映象會報錯,導致後面任務無法執行
# 自定義階段build的job流程
job2: # 自定義名字
stage: build # 指定這階段操作的名稱
only: # 指定那些分支會進入該處理流程
- /^liweiji.*$/ # liweiji下的分支
tags:
# 指定哪些runner執行script裡面的操作,因為我們上面註冊runner的時候輸入了test,所以這裡就是test,當然你想其他runner也執行,這裡就新增其他runner的tag
- test
script:
# docker build, $CI_COMMIT_REF_NAME是分支名,變數可以檢視https://docs.gitlab.com/ee/ci/variables/
- cd page/public-sale
- echo `docker build -t test:$CI_COMMIT_REF_NAME . | awk -F "Successfully built " '{print $2}'`
job3:
stage: run
only:
- /^liweiji.*$/ # liweiji下的分支
tags:
- test
script:
# $CI_COMMIT_REF_NAME是分支名
- docker run --name test:$CI_COMMIT_REF_NAME -p 8000:80 -idt test:$CI_COMMIT_REF_NAME
複製程式碼
我們的runner有兩種型別,Specific Runners和Shared Runners,我們一提交程式碼,如果.gitlab-ci.yml的job中不指定tag的話,則會執行Shared Runners,指定的話就執行tag對應的Specific Runners。
如果任務成功,我們就可以在我們gitlab網站的pipelines下看到我們任務的執行了。
注意:這裡其實正確的任務應該是生成映象後將映象上傳到映象倉庫,然後讓測試去拉取對應分支的映象執行容器的。我這裡這樣做是因為gitlab-runner和測試機在同個機器。
遇到問題
問題一:docker執行nginx報403
這個問題有兩個原因,一個是許可權問題,一個是首頁index.html不存在,我們遇到的是第一個原因。其實上面也講過,是因為我們用root許可權執行docker,但是nginx.conf裡面沒有配置user為root,所以nginx.conf的開頭需要這樣配置:
user root;
複製程式碼
問題二:樣式下載了但是未起效
這個原因也是nginx配置導致的,因為我們nginx自己重新配置了,所以有些配置沒做好。這裡nginx如果未配置mime.types的話,預設只有application/octet-stream,所以css資源能下載但是不能起效且有Resource interpreted as Stylesheet but transferred with MIME type text/plain的警告,解決辦法就是在nginx.conf的http標籤下新增include mime.types;
http {
include mime.types;
.....
}
複製程式碼
問題三:docker: command not found
這個原因是因為.gitlab-ci.yml中的job沒配置tags,導致share runner執行,而share runner中沒有配置docker。
問題四:su: user gitlab-runner does not exist
這個原因是我們的runner指定了user為gitlab-runner但是我們的機器並沒有建立這個使用者,所有有兩種方式解決:
- useradd gitlab-runner
- 殺掉gitlab-ci-multi-runner程式,並以其他使用者執行,這裡換成root執行
gitlab-ci-multi-runner run --working-directory /home/gitlab-runner --config /etc/gitlab-runner/config.toml --service root--syslog --user root
複製程式碼
總結
docker是一個獨立的容器,相互之間隔離,我們可以給各個docker映象部署nginx和程式碼,從而利用docker實現不同開發分支下不同的程式碼執行,從而實現一機多分支程式碼執行。gitlab-ci可以實現自動化任務,比如安裝、打包、部署等一系列任務,那麼我們就可以利用gitlab-ci來實現docker映象生成和容器執行的自動化,從而避免使用者手動操作。