Node專案的Gitlab自動部署實踐(基於Docker)
準備工作
說明
公司最近準備了一臺新的開發伺服器,正好用以實踐docker的基本應用。docker的好處不再贅述,詳情可參考阮一峰的這篇入門。(關於Docker最好的中文介紹,沒有之一)。
公司目前主要使用了EggJs + ReactJS的技術組合,並且是前後端分離的。在使用docker以後,大致的部署方式如下:
環境
- 主機:阿里雲
- 系統:CentOS 7.5
工具
- Gitlab-Runner
- Docker
- Nginx
操作
在新購買的阿里雲主機上(域名要準備好,對映到主機的IP上),直接用root使用者鍵入以下命令:
root# yum install gitlab-ci-multi-runner root# yum install docker root# yum install nginx
以上三個命令,即已安裝好所需的軟體環境。餘下任務均可交由docker和gitlab-runner完成。
本文略過的內容
- gitlab的使用,看這裡
Nginx的配置
nginx在安裝完成後,需要對配置檔案進行修改,新增要代理的埠設定。配置檔案一般放在/etc/nginx/conf.d目錄下。
在/etc/nginx/nginx.conf中,有這麼一段程式碼:
...
include /etc/nginx/conf.d/*.conf;
...
意思是,所有在/etc/nginx/conf.d目錄下的配置檔案都會被自動載入。因此在該目錄下加入一個配置檔案如:service.conf。內容如下:
server { listen 80; server_name your.website.com; // 這裡寫你配置好的域名 // api訪問路徑(http://your.website.com/api/test) location /api/test { proxy_http_version 1.1; client_max_body_size 100m; client_body_buffer_size 128k; proxy_send_timeout 300; proxy_read_timeout 300; proxy_buffer_size 4k; proxy_buffers 16 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; proxy_connect_timeout 30s; proxy_redirect off; proxy_pass http://127.0.0.1:18001/; // api的docker轉發的內部埠 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } // web訪問路徑(http://your.website.com/web/test) location /web/test { alias /data/www/test; // web專案檔案放在此目錄下,可自行配置為你自己的目錄 index index.html; } // gitlab訪問路徑(http://your.website.com/gitlab), location /gitlab { proxy_http_version 1.1; client_max_body_size 100m; client_body_buffer_size 128k; proxy_send_timeout 300; proxy_read_timeout 300; proxy_buffer_size 4k; proxy_buffers 16 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; proxy_connect_timeout 30s; proxy_pass http://127.0.0.1:15080/; // gitlab的docker轉發的內部埠 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
conf配置完成後,重啟nginx
root# service nginx restart
gitlab的安裝和啟動(使用Docker)
通過docker方式安裝gitlab,比傳統方式簡單了不止100倍。只需簡單兩個命令即可開始使用:
安裝
root# docker pull gitlab/gitlab-ce // 這裡安裝的是ce版
這裡沒有指定版本,所以預設安裝的是最新版。你可以在這裡找到自己想要的版本並進行安裝:
root# docker pull gitlab/gitlab-ce:11.3.3-ce.0
啟動
使用簡單一條命令,即可執行並使用gitlab。首先編輯啟動指令碼gitlab-start.sh,例如放在/srv/docker中:
#! /bin/bash
docker run --name gitlab \
-d \
--restart always \
-p 15022:22 \ # 暴露給nginx的外部埠,
-p 15080:80 \ # 暴露給nginx的外部埠(與上面的nginx配置要一致)
-p 15433:433 \ # 暴露給nginx的外部埠,
-v /srv/gitlab/config:/etc/gitlab \ # gitlab的配置檔案
-v /srv/gitlab/logs:/var/log/gitlab \ # gitlab的日誌檔案
-v /srv/gitlab/data:/opt/lib/gitlab \ # gitlab的資料檔案
gitlab/gitlab-ce
使用bash執行該shell檔案,gitlab即可啟動
root# bash /srv/docker/gitlab-start.sh
現在訪問your.website.com/gitlab,應該就可以正常訪問gitlab服務了。就是這麼簡單!
使用Gitlab-Runner自動部署
api專案和web專案的執行方式有所區別,所以runner也有所區別。
- api專案 跑在docker容器中,僅需將檔案拷貝進docker即可,執行器是shell型別
- web專案 打包的靜態檔案需要docker環境,執行器是docker型別
web專案的自動部署
新增runner
執行runner的註冊命令,按照說明進行引數配置
root# gitlab-ci-multi-runner register
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/ci):
# http://your.website.com/gitlab
Please enter the gitlab-ci token for this runner:
# xxxxxxxx
Please enter the gitlab-ci description for this runner:
# web-deploy-runner
Please enter the gitlab-ci tags for this runner (comma separated):
# node-web-deploy
Registering runner... succeeded runner=avuSXASJ
Please enter the executor: docker-ssh, parallels, shell, ssh, virtualbox, docker+machine, docker-ssh+machine, docker:
# docker
Please enter the default Docker image (e.g. ruby:2.1):
# node:10
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
說明
- 前2個分別是要求輸入gitlab的url和對應的token,這兩個資訊在專案的Settings -> CI/CD -> Runners中有記錄
- 第3個要求輸入runner的名稱
- 第4個是要求輸入tags,runner通過專案gitlab-ci.yml中的tag標記來決定執行該CI過程。(見下一個步驟)
- runner的executor指定是docker,所以最後一個要求輸入docker的image,因為是node專案,指定該image為node:10(版本10的node,也可以指定其它版本)
新增完成後,runner就已經在服務中跑起來。只要gitlab的專案有提交,相關runner就根據tags來決定是否跑自動部署的命令。
新增.gitlab-ci.yml
在web專案的根目錄下,新增.gitlab-ci.yml檔案如下:
stages:
- deploy
cache:
key: ${CI_BUILD_REF_NAME}
paths:
- node_modules/
before_script:
- export PATH=/usr/local/bin:$PATH
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- ssh-add <(echo "$SSH_PRIVATE_KEY")
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
- '[[ -f /.dockerenv ]] && echo -e "Host *\\n\\tStrictHostKeyChecking no\\n\\n" > ~/.ssh/config'
# dev分支構建
dev-deploy:
stage: build
tags:
- node-web-deploy # 這個是runner的tags內容
only:
- dev # 僅僅dev分支會被對應的runner執行
script:
- npm install
- npm run build # web專案的打包命令
- scp -r dist/* [email protected]:/data/www/test-dev # 將打包好的dist拷貝到部署目錄
#uat分支構建
uat-deploy:
stage: build
tags:
- node-web-deploy # 這個是runner的tags內容
only:
- uat # 僅僅uat分支會被對應的runner執行
script:
- npm install
- npm run build # web專案的打包命令
- scp -r dist/* [email protected]:/data/www/test-uat # 將打包好的dist拷貝到部署目錄
說明
- 檔案中申明瞭兩個部署過程,分別針對dev和uat兩個分支,打包好以後拷貝到對應的部署目錄(nginx已經對映,看上面的配置檔案)
- before_script部分,是為scp命令做的準備。因為是在docker內部打包,無法直接拷貝到宿主目錄下,所以需要scp命令拷貝。
ssh的設定
如上說明2,因為是在docker內部打包,只能通過scp來拷貝打包好的檔案到宿主目錄下。因此需要配置SSH。
參考官方例子即可完成此步驟:
1. 生成SSH金鑰
官方文件推薦使用ed25519型別的SSH,我仍然用的RSA方式
root# ssh-keygen -o -t rsa -b 4096 -C "[email protected]"
按照提示,一路點選Enter即可完成配置。記得:不要新增passphrase。
2. 新增專案的Variables
在gitlab的專案設定(Settings -> CI/CD -> Variables)中,新增SSH_PRIVATE_KEY變數,變數的值為上一步生成的SSH金鑰對的私鑰。
root# vi ~/.ssh/id_rsa // 這裡儲存的就是私鑰,拷貝到SSH_PRIVATE_KEY的value欄位中
完成後,繼續新增SSH_KNOWN_HOSTS變數,變數的值為以下命令的輸出:
root# ssh-keyscan your.website.com
3. SSH免密設定
上面兩步驟設定完成後,Runner執行時仍然會報錯,原因就在於SSH登入雖然設定完成,但沒有設定登入免密。免密登入的要點就在於,要將SSH金鑰對的公鑰匯入到~/.ssh/authorized_keys檔案中。
對於本次實踐,gitlab-runner和gitlab其實是在同一臺伺服器上完成的。所以,將PUBLC_KEY匯入到本地的authorized_keys檔案中即可
root# cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
api專案的自動部署
新增runner
執行runner的註冊命令,按照說明進行引數配置。(具體說明見:web專案的自動部署)
root# gitlab-ci-multi-runner register
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/ci):
# http://your.website.com/gitlab
Please enter the gitlab-ci token for this runner:
# xxxxxxxx
Please enter the gitlab-ci description for this runner:
# api-deploy-runner
Please enter the gitlab-ci tags for this runner (comma separated):
# node-api-deploy
Registering runner... succeeded runner=avuSXASJ
Please enter the executor: docker-ssh, parallels, shell, ssh, virtualbox, docker+machine, docker-ssh+machine, docker:
# shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
新增.gitlab-ci.yml
在api專案的根目錄下,新增.gitlab-ci.yml檔案如下:
stages:
- deploy
cache:
key: ${CI_BUILD_REF_NAME}
paths:
- node_modules/
before_script:
- export PATH=/usr/local/bin:$PATH
- npm install
# ep-dev分支構建
deploy_dev:
stage: deploy
tags:
- node-api-deploy
only:
- dev
script:
- sudo docker cp ./ api-test:/home/service
- sudo docker restart api-test
# ep-release分支構建
deploy_uat:
stage: deploy
tags:
- node-api-deploy
only:
- uat
script:
- sudo docker cp ./ api-test:/home/service
- sudo docker restart api-test
說明
- 上面指令碼使用了docker命令,將新檔案拷貝到docker中並重啟docker服務
新增docker服務
上面兩部已經配置好了gitlab的CI/CD,CI/CD命令也提到要使用api專案對應的docker服務。這個docker服務需要我們提前打包一個image,docker裡要跑的正是api服務所需要的環境。
1. 編輯Dockerfile
在api專案的根目錄下,新增Dockerfile(沒有後綴名)如下:
FROM node:10
RUN mkdir -p /home/service
WORKDIR /home/service
COPY . /home/service
RUN npm install
EXPOSE 8102
CMD ["npm", "start"]
說明
- FROM 指使用的node環境對應的docker image
- WORKDIR 指docker環境中的工作目錄,執行的RUN命令都在此目錄下執行
- EXPOSE 指該api服務對外暴露的埠號,多個埠號可以寫多個EXPOSE
- CMD 則是docker在啟動這個image時執行的命令,eggjs專案預設在工作目錄下執行npm start
2. 打包docker
在根目錄下,執行以下命令即可打包一個image
root# docker build -t lynx/test .
打包完成後,執行docker images即可看到打包完成的docker映象。
3. 執行docker服務
與gitlab的使用方式類似,先編輯一個啟動指令碼/srv/docker/start-test.sh,內容如下:
#! /bin/bash
docker run -d --name nr-api-ep-dev \
-p 18001:8102 \
-v /data/api/test/logs:/home/service/logs \
lynx/test
使用bash執行該shell檔案,node服務即可啟動
root# bash /srv/docker/start-test.sh
啟動後,執行docker ps可以檢視已啟動的docker任務列表。
至此,web和api服務都已經自動部署完成。
連線其它docker服務
在api服務中,一定會用到sql或redis等第三方的服務。一種方式是將這個服務安裝到宿主環境中,另外一種方式是啟動redis的docker服務,將服務請求連線(link)過來,(docker-compose方式本文沒有涉及)。
使用redis
本文使用docker方式安裝redis,並啟動這個服務
root# docker pull redis // 安裝redis
root# docker run -d --name=redis -p 6379:6379 -v /srv/redis/data:/var/lib/redis redis:latest redis-server --appendonly yes // 啟動redis
api(Docker)連線redis(Docker)
如果api服務需要使用到redis,那麼將redis的地址定義為127.0.0.1是達不到目的的,因為兩個服務目前都是在docker環境中執行。
如上面的api的docker執行命令,加入--link選項
#! /bin/bash
docker run -d --name nr-api-ep-dev \
-p 18001:8102 \
--link redis:redis \
-v /data/api/test/logs:/home/service/logs \
lynx/test
為了提供服務,docker執行中可以使用引數--link。注意,在link了以後api服務中如果要使用redis,服務的地址要改為redis,而不是127.0.0.1或者localhost