從0到1實現先後端分離項目的gitlab-ci流程

從0到1把gitlab-ci弄好了, 完全拋棄travis-ci, 最大的坑仍是牆外的東西太慢了, 老是timeout
html

整個過程分爲以下幾步:前端

  • 如何在一個1核2G的雲服務器上搭建gitlab: 十分鐘搭建Gitlab
  • 使用gitlab-runner, 並選擇正確的executor
  • 如何構建前端鏡像
  • 如何構建後端鏡像
  • 編寫gitlab-ci.yml, 實現一個完整的前端後分離項目的構建部署

1. 使用gitlab-runner

gitlab-runnergitlab-ci是連體嬰, 主要爲gitlab-ci打工, 使用鏡像的安裝方式以下:node

docker run -d --name gitlab-runner --restart always \
 -v /srv/gitlab-runner/config:/etc/gitlab-runner \
 -v /var/run/docker.sock:/var/run/docker.sock \
 gitlab/gitlab-runner:latest
其中掛載卷 /srv/gitlab-runner/config/config.toml 包含了全部 runner 的配置信息. 經過掛載 /var/run/docker.sock:/var/run/docker.sock ,使得容器中的進程能夠經過它與Docker守護進程通訊

圖片


1.1 選擇Docker做爲runner的executorwebpack

在啓動了gitlab-runner容器後, 執行以下命令進入容器, 註冊runnernginx

docker exec -it gitlab-runner /bin/bash
root@492ce6ab72f9:/# gitlab-runner register

接下來須要填寫的信息以下:git

Please enter the gitlab-ci coordinator URL:
你的Gitlab地址: http(s)://gitlab.xxx.com
Please enter the gitlab-ci token for this runner:
你的Gitlab admin/runners頁面中的token
Please enter the gitlab-ci description for this runner:
填寫描述, 可有可無
Please enter the gitlab-ci tags for this runner (comma separated):
填寫標籤, 沒有標籤誰均可以用, 是shared-runner, 有標籤須要聲明纔可用, 回車就對了
Please enter the executor: docker-ssh, ssh, docker+machine, kubernetes, docker-ssh+machine, docker, parallels, shell, virtualbox:
選擇你的executor: Docker應該是我觀察到最經常使用的吧
Please enter the default Docker image (e.g. ruby:2.6):
選擇一個默認鏡像: 例如 docker:stable-alpine

不出意外, 就能在gitlab中看到了web

1.2 爲何使用docker做爲executorredis

參考官方文檔 - executormongodb

  • shell executor: 最簡單的executor, 全部依賴都必須手動安裝docker

    • 不能保證每次構建都是相同的環境

    • 不方便遷移

    • 須要手動配置

  • Virtual Machine Executor: 建立虛擬機用於構建, 若是你但願在不一樣的操做系統上構建, 能夠選擇它

    • 佔用資源大

    • 調試構建問題困難

    • 遷移較不方便

  • Docker Executor: 最佳的選擇QAQ

  • Docker Machine: Docker的拓展, 工做方式與Docker相似, 不過須要額外的一些安裝步驟

最後只嘗試了DockerDocker + Machine, 只是Docker + Machinerunner一直沒有鏈接成功

1.3 什麼是Docker in Docker(dind)

參考官方文檔 - dind

使用dind的背景是: 須要在容器內執行docker命令,

1.1中註冊好了一個docker executor以後, 只須要完成兩個操做, 便可使用

  • gitlab-ci.yml中添加:

imgage: docker:stable
service:
  - docker:dind
# 測試
before_script:
  - docker info
  • 確保config.toml中該runner中設置了privileged = true

1.4 可能碰見的問題: 拉取鏡像緩慢

講道理, 我沒有在官方文檔中找到: 當使用docker做爲runner executor時, 如何設置registry-mirror

若是使用docker + machine的話, 能夠在config.toml中設置:

圖片

進過測試, 此項配置顯然對docker無效...最終使用的辦法是:

圖片

1.5 構建階段一: 打包並上傳前端代碼

image: docker:stable

services:
  - docker:dind

stages:
  - build
  - make_image
  - deploy

cache:
  untracked: true
  paths:
    - backend/node_modules/
    - frontend/node_modules/
    - frontend/dist/

upload_to_oss:
  image: node:lts-alpine
  stage: build
  script:
    - cd frontend
    - npm install --registry https://registry.npm.taobao.org
    - npm run build
經過在 gitlab-ci 控制檯配置一些用於上傳至OSS的環境變量, 就能夠在這個階段實現以下兩個功能:
  • 打包前端代碼, 爲構建前端鏡像做準備
  • 結構webpack插件, 在打包結束後將靜態資源上傳至OSS

2. 如何構建先後端鏡像

2.1 構建前端鏡像

對於先後端分離的項目, 每每有一個特色: 須要經過nginx進行反向代理所以, 前端鏡像是以NGINX爲核心的, 須要準備2~3個文件

  • dist目錄: 前端的靜態資源
  • xxx.conf: nginx的部分配置, 例如: history路由的處理, 端口轉發

Dockerfile以下:

FROM nginx:stable-alpine
LABEL maintainer=Caaalabash
EXPOSE 800
COPY xxx.conf /etc/nginx.conf.d
COPY dist /etc/nginx/dist/
CMD nginx -g "daemon off;"

xxx.conf以下:

server {
  listen 800;
  server_name localhost;
  root /etc/nginx/dist;
  location / {
    try_files $uri $uri@router;
    index index.html;
  }
  location @router {
    rewrite ^.*$ /index.html last;
  }
  # 此處直接使用localhost進行轉發與後文採用的network_mode有關
  location /api {
    proxy_pass http://localhost:3000/api;
  }
}

能夠看到xxx.conf只與業務相關, 不涉及https的處理, 這部分應該交由宿主機的nginx進行處理, 示例代碼以下:

宿主機nginx配置:

server {
  listen 80;
  server_name xxx.xxx.xxx;
  return 301 https://$server_name$request_uri;
}

server {
  listen 443 ssl http2 default_server;
  server_name xxx.xxx.xxx;
  ssl on;
  ssl_certificate   cert/blog.pem;
  ssl_certificate_key cert/blog.key;
  ssl_session_timeout 5m;
  ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;

  # 全部請求交給前端容器處理
  location / {
    proxy_pass http://127.0.0.1:8888;
  }
}

體如今gitlab-ci.yml

make_fe:
  stage: make_image
  script:
    - docker login -u="$DOCKER_USERNAME_ALI" -p="$DOCKER_PASSWORD_ALI" $DOCKER_REPOSITORY_ALI
    - docker build -t $DOCKER_REPOSITORY_ALI/calabash/blog:$CI_COMMIT_SHORT_SHA-FE ./frontend
    - docker push $DOCKER_REPOSITORY_ALI/calabash/blog:$CI_COMMIT_SHORT_SHA-FE

過程很簡單, 登陸私有鏡像倉庫, 構建鏡像, 上傳鏡像

2.2. 構建後端鏡像

對比前端就更更加簡單了, 就是一個普通的構建node鏡像流程~

FROM node:lts-alpine
LABEL maintainer=Caaalabash
WORKDIR /app
COPY package.json /app
RUN npm install --registry https://registry.npm.taobao.org
COPY . /app
ENV NODE_ENV=production
EXPOSE 3000
CMD ["npm""run""online"]
體如今 gitlab-ci.yml
make_be:
  stage: make_image
  script:
    - docker login -u="$DOCKER_USERNAME_ALI" -p="$DOCKER_PASSWORD_ALI" $DOCKER_REPOSITORY_ALI
    - docker build -t $DOCKER_REPOSITORY_ALI/calabash/blog:$CI_COMMIT_SHORT_SHA-BE ./backend
    - docker push $DOCKER_REPOSITORY_ALI/calabash/blog:$CI_COMMIT_SHORT_SHA-BE

3. 使用docker-compose在服務器上進行部署

在構建完鏡像後, 就進入最後一個階段: 部署

部署的核心是:

  • 將相關文件拷貝到服務器, 好比一個啓動腳本, 一個docker-compose.yml

  • 執行服務器腳本

體如今gitlab-ci.yml中爲:

deploy_test:
  image: caaalabash/alpine-ssh:latest
  stage: deploy
  before_script:
    - echo "$SSH_PRIVATE_KEY" > id_rsa_2048
    - chmod 0600 id_rsa_2048
    - eval $(ssh-agent)
    - ssh-add id_rsa_2048
  script:
    - scp -P $SSH_PORT deploy/* root@$DEPLOY_SERVER:$SERVER_FOLDER/deploy
    - ssh -p $SSH_PORT root@$DEPLOY_SERVER "chmod +x $SERVER_FOLDER/deploy/startup.sh"
    - ssh -p $SSH_PORT root@$DEPLOY_SERVER "$SERVER_FOLDER/deploy/startup.sh $CI_COMMIT_SHORT_SHA"

構建腳本只是: 中止/刪除舊容器/鏡像, docker-compose up

3.1 docker-compose.yml

簡化版的配置以下:

version: "3"
services:
  backend:
    image: calabash/blog:${VUE_BLOG_TAG}-BE
    container_name: blog-backend
    # 只須要暴露前端端口
    ports:
      - 8888:800
    network_mode: bridge
    restart: unless-stopped
    # 使用的mongodb redis服務
    external_links:
      - myMongoDB
      - myRedis
  frontend:
    image: calabash/blog:${VUE_BLOG_TAG}-FE
    container_name: blog-frontend
    restart: unless-stopped
    network_mode: "service:backend"
核心在於 frontend 中的配置:
 
 

network_mode"service:backend"

經過這個配置, 和後端容器共用一個網絡, 所以在nginx配置文件中, 能夠直接使用localhost進行轉發, 不過這樣作須要保證, 後端容器就緒後再啓動前端容器

3.2 保證後端容器啓動後才前端容器

總所周知, docker-compose沒有這個能力TAT, 最使用的方案有兩個

  • 分別啓動容器
  • wait-for-it.sh

此處使用wait-for-it.sh的簡化版操做修改前端Dockerfile爲:

FROM nginx:stable-alpine
LABEL maintainer=Caaalabash
EXPOSE 800
COPY wait-for-it.sh wait-for-it.sh
RUN chmod +x wait-for-it.sh
COPY blog.conf /etc/nginx/conf.d
COPY dist /etc/nginx/dist/
CMD ["./wait-for-it.sh"]

添加wait-for-it.sh

#!/bin/sh
while ! nc -z -v localhost 3000
do
  echo "wait backend"
  sleep 3
done
echo "done"
nginx -g "daemon off;"

便可

END

這波折騰雖然麻煩, 不過仍是完整的實現了一個先後端分離項目的Gitlab-ci配置, 達到了預期的目標, 雖然gitlab-ci.yml配置是很是好上手的, 除此以外

  • 體驗了一波gitlab-ci + gitlab-runner連體嬰

  • 使用私有鏡像倉庫!

  • 實踐了wait-for-it控制啓動流程的方案

沒想到試錯了5+次....完美成功!

圖片

相關文章
相關標籤/搜索