jenkins搭建前端上線系統(tǒng)小試

概述

一次在跟同事討論中忽然萌生了自己手動搭建一套上線系統(tǒng)的想法,第一次上手,所以選用了業(yè)內(nèi)比較成熟的方案-jenkins。之前只是用過jenkins進(jìn)行過一些操作,并未自己從0到1完成搭建,本文記錄下自己整個過程中的遇到的一些問題與解決方案。

準(zhǔn)備知識

linux安裝軟件的方式(知道的可以直接跳過本節(jié)):

一般有三種方式:Linux系統(tǒng)中安裝軟件的幾種方式

  1. 源碼包安裝:
    下載源碼 -> 解壓 -> 運行configure配置等 -> make 編譯 -> make install 安裝

  2. rpm包安裝:
    RedHat Package Manager,由紅帽公司提出,建議統(tǒng)一的數(shù)據(jù)庫文件,詳細(xì)記錄軟件包的安裝、卸載等變化信息,能夠自動分析軟件包依賴關(guān)系。用RPM工具可以將二進(jìn)制程序進(jìn)行打包,包被稱為RPM包。RPM包并不是跨平臺的。

  3. yum源安裝:
    Yellow dog Updater, Modified, 是一個在Fedora和RedHat以及CentOS中的Shell前端軟件包管理器?;赗PM包管理,能夠從指定的服務(wù)器自動下載RPM包并且安裝,可以自動處理依賴性關(guān)系,并且一次安裝所有依賴的軟件包,無須繁瑣地一次次下載、安裝

linux啟動服務(wù)管理兩種方式service和systemctl:

service作為啟動init進(jìn)程的主命令存在一些歷史缺陷,Systemd就是他的升級版,他為系統(tǒng)的啟動和管理提供一套完整的解決方案。Systemd 并不是一個命令,而是一組命令,涉及到系統(tǒng)管理的方方面面:systemctl是 Systemd 的主命令,用于管理系統(tǒng)。

方案一: docker安裝

docker作為目前比較火的一個名詞,自己一直沒機會使用,了解到j(luò)enkins可以通過docker來安裝,于是,從docker開始,進(jìn)入了采坑之旅。

centos上安裝docker

自己的服務(wù)器為阿里云,版本如下(以下所有操作均是基于此臺機器):

uname -a
Linux iZ2zeb34hcp1ui0lowu4atZ 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

docker的安裝還是比較簡單的,參照阿里云官方文檔幾分鐘搞定:

添加yum源

  # yum install epel-release –y // 安裝并啟用 EPEL 源。
  # yum clean all
  # yum list

安裝并運行Docker

  # yum install docker-io –y
  # systemctl start docker // 啟動docker服務(wù)

檢查安裝結(jié)果。

  # docker info

關(guān)于 docker

"Docker" 的本質(zhì)其實是解決了應(yīng)用服務(wù)的 "隱私" 問題,實現(xiàn)進(jìn)程、內(nèi)存、文件、網(wǎng)絡(luò)之間相互隔離。也可以簡單把 Docker 理解成一種虛擬機,很多應(yīng)用服務(wù)可以像桌面軟件那樣一鍵安裝,免部署和環(huán)境配置。

前端為什么需要使用 Docker?

  1. 對于 Full Stack 工程師。Docker 可以提供一種簡單輕便的服務(wù)器編程環(huán)境,而且可以隨用隨刪、降低環(huán)境配置成本。
  2. 很多 FE 日常工作中需要跟 Nginx、MongoDB、MySQL 等服務(wù)器應(yīng)用打交道。用 Docker 可以很容易部署一個測試環(huán)境,學(xué)習(xí)和倒騰.

Docker 中的三個概念

Container - 容器
Image - 鏡像
Registry - 倉庫

可以像下面這張圖來類比:

1.jpg

docker 安裝jenkins

使用下面的 docker run 命令運行 jenkinsci/blueocean 鏡像作為Docker中的一個容器(如果本地沒有鏡像,這個命令會自動下載):

docker run \
  --rm \
  -u root \
  -p 8080:8080 \
  -v jenkins-data:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$HOME":/home \
  jenkinsci/blueocean

此時就進(jìn)入jenkins安裝流程,具體參照此處jenkins官網(wǎng)安裝示例

啟動服務(wù),輸入密碼繼續(xù)(安裝過程忘記截圖了,以下圖收集于網(wǎng)絡(luò));

1.png

啟動后安裝推薦插件:

2.png
3.png

安裝插件完成后,進(jìn)入設(shè)置初始賬號密碼:

4.png

設(shè)置完畢即可進(jìn)入jenkins主頁:

5.png

可能遇到的問題:
1.訪問接口出現(xiàn):Error:403 No valid crumb was included in the request
解決:關(guān)閉安全設(shè)置里面的-防止款站點請求偽造選項,具體參照此處。

jenkins 配置nodejs

進(jìn)入插件管理,安裝Nodejs Plugin:

6.png

進(jìn)入全局工具配置,配置項目中會用到的nodejs版本,可以配置多個

7.png

jenkins + github 配置項目

開始創(chuàng)建項目,選擇自由風(fēng)格:

8.png

開始配置項目(以github私有項目為例)

9.png

此處我們填寫完git地址后需要添加憑證,添加其他個人私有密鑰不知道為啥一直在下拉菜單中選不到,添加用戶名密碼則可以,暫時還不知道啥原因~

10.png
11.png

觸發(fā)器中我們選擇hook,push之后,自動觸發(fā)構(gòu)建

12.png

此處在github中也要做對應(yīng)配置才可出發(fā)hook功能:


16.png

也可以自定義配置,譬如,push 與 merge時觸發(fā),選擇Let me select individual events,勾選以下選項:

Deployments
Deployment statuses
Pull requests
Pushes

接下來我們配置拉去完代碼后需要執(zhí)行的腳本,centos上我們選擇shell腳本:

14.png

至此,一個簡單的配置已經(jīng)完畢,可以進(jìn)行自動構(gòu)建測試了!!

doker 模式下遇到的問題

我們隨便改一點東西,向master分支push代碼,觸發(fā)構(gòu)建;構(gòu)建任務(wù)正常觸發(fā),但執(zhí)行到shell腳本時卻出現(xiàn)了異常:

[test-project] $ /bin/sh -xe /tmp/jenkins6958996694563138608.sh
+ node -v
/tmp/jenkins6958996694563138608.sh: line 2: node: not found
Build step 'Execute shell' marked build as failure
Finished: FAILURE

通過docker exec -it 4c0fd5e5f2c5 bash 命令進(jìn)入容器內(nèi)部bash訪問:

    cd /var/jenkins_home/tools/jenkins.plugins.nodejs.tools.NodeJSInstallation/nodejs_10.15.3/

nodejs文件夾存在,但執(zhí)行 bin/node -v 報錯,提示 : No such file or directory

解決:終于在stackoverflow 翻到一個大佬的回答,原因如下:

This happens because the image doesn't contain libstdc++.so.6 as needed by nodejs
    
In other words, node: not found does not mean node is not installed (it is, it is executable and found in the $PATH).  
     
It means one of node dependencies is not found.

我們通過,手動安裝:

apk add --no-cache --update nodejs nodejs-npm

拉取的是Node.js Alpine 鏡像,這個鏡像做個優(yōu)化,并沒有內(nèi)置npm包(本人驗證 10.14.2是沒有的)需要手動再安裝npm;

再次構(gòu)建

/var/jenkins_home/tools/jenkins.plugins.nodejs.tools.NodeJSInstallation/nodejs_10.15.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin
+ node -v
v10.14.2
+ npm -v
6.4.1
Finished: SUCCESS

執(zhí)行成功,考慮到調(diào)試?yán)щy和后續(xù)還需要全局安裝vue-cli3.0 于是決定放棄docker方式,改用RPM方式再次安裝jenkins

RPM方式安裝jenkins

因為Jenkins是基于java的,所以需要先安裝jdk

安裝java環(huán)境

sudo yum install java

下載rpm包:

通過RedHat Linux RPM packages for Jenkins網(wǎng)站下載所需的rpm包,我安裝的是jenkins-2.195-1.1.noarch.rpm

安裝jenkins

sudo rpm -ih jenkins-2.73.2-1.1.noarch.rpm

自動安裝完成之后:

/usr/lib/jenkins/jenkins.war WAR包

/etc/sysconfig/jenkins 配置文件

/var/lib/jenkins/ 默認(rèn)的JENKINS_HOME目錄

/var/log/jenkins/jenkins.log Jenkins日志文件

啟動jenkins

sudo systemctl start jenkins.service

之后的操作與上一步docker安裝方式基本一摸一樣,唯一區(qū)別的就是器nodejs安裝方式

node調(diào)用centos全局配置

取消構(gòu)建環(huán)境配置下的:Provide Node & npm bin/ folder to PATH 即可

完善整個構(gòu)建流程

其實主要就是shell腳本的編寫,項目中配置如下(新建了另外一個項目vue-build-project):

node -v 
echo "開始安裝依賴..."
npm install
echo "開始打包..."
npm run build

base="/home/upload-upyun"
dest="vue-build-project"
cp -r ./dist  ${base}/${dest}
echo "開始上傳..."
node  ${base}/index.js ${dest}
rm -rf ${base}/${dest}

/home/upload-upyun/index.js 內(nèi)容如下:

const argPath=(process.argv.splice(2));//通過透傳參數(shù)獲取文件夾目錄
const uploadFolder=(argPath[0]?argPath[0]:'').replace(/^--/g,'');
const rootPath=(argPath[1]?argPath[1]:'').replace(/^--/g,''); //靜態(tài)資源服務(wù)器的上傳根路徑
const fs=require("fs");
const path=require("path");
const upyun = require("upyun/dist/upyun.common");
const localFileArr=[];
function readPathSync (p) {
  if(!fs.existsSync(p)) return;
  const stat = fs.statSync(p)
  if (stat.isDirectory()) {
    const ls = fs.readdirSync(p).map(file => path.join(p, file))
    for (let i = 0; i < ls.length; i++) {
      readPathSync(ls[i])
    }
  } else {
    localFileArr.push(p)
  }
}
const ypyConf={
    "serviceName" : "xxx",
    "operatorName" : "xxx",
    "password" :"xxx",
    "remotePath" rootPath,
};

  // 需要填寫本地路徑,云存儲路徑
  const remoteRoot = ypyConf.remotePath;
  const upService = new upyun.Service(ypyConf.serviceName, ypyConf.operatorName, ypyConf.password);
  const upClient = new upyun.Client(upService);
  const prefix=(path.join(__dirname))
  // 上傳參數(shù)
  // console.log(upClient)
  function uploadFile(localFile){
    const remoteFile= remoteRoot+(localFile.replace(prefix,'')).split(path.sep).join("/")
    upClient.putFile(remoteFile, fs.createReadStream(localFile), {
      'Date': new Date(),
      'Content-Length': fs.statSync(localFile).size,
    }).then(res => {
      if (res) {
        console.log(remoteFile+":上傳成功")
      } else {
        console.log(remoteFile+":上傳失敗")
      }
    }).catch(err => {
      console.log("上傳出現(xiàn)異常!")
    })
}
readPathSync(path.join(__dirname,uploadFolder));
localFileArr.map((item)=>{
  uploadFile(item)
})

最終構(gòu)建效果

16:21:48 [vue-build-project] $ /bin/sh -xe /tmp/jenkins8111626610056806132.sh
16:21:48 + node -v
16:21:48 v10.15.3
16:21:48 + echo 開始安裝依賴...
16:21:48 開始安裝依賴...
16:21:48 + echo 開始打包...
16:21:48 開始打包...
16:21:48 [vue-build-project] $ /bin/sh -xe /tmp/jenkins6507915611034625552.sh
16:21:48 + cd /var/lib/jenkins/workspace
16:21:48 + cd vue-build-project
16:21:48 + cp -r ./dist /home/upload-upyun/vue-build-project
16:21:48 + echo 開始上傳...
16:21:48 開始上傳...
16:21:48 + node /home/upload-upyun/index.js vue-build-project
16:21:48 /test/vue-build-project/favicon.ico:上傳成功
16:21:48 /test/vue-build-project/index.html:上傳成功
16:21:48 /test/vue-build-project/static/js/app.6e3674b9.js:上傳成功
16:21:48 /test/vue-build-project/static/css/app.e2713bb0.css:上傳成功
16:21:48 /test/vue-build-project/static/img/logo.82b9c7a5.png:上傳成功
16:21:48 /test/vue-build-project/static/js/chunk-vendors.a1771d7d.js:上傳成功
16:21:48 + rm -rf /home/upload-upyun/vue-build-project
16:21:48 Finished: SUCCESS

遇到的問題

在npm install 這一步出現(xiàn)了一個問題困擾了我兩天,執(zhí)行過程中jenkins進(jìn)程會忽然掛掉,一直以為是配置的問題,后來忽然意識到可能是被centos給干掉了,查看日志:

grep "Out of memory" /var/log/messages

得到日志如下:

17.png

時間節(jié)點與我構(gòu)建時完全相同?。。?!,坑?。。。〔殚嗁Y料明白是觸發(fā)了 linux 的 OOM killer 機制

于是乎重啟服務(wù)器,只開啟Jenkins,重新構(gòu)建。我的阿里云服務(wù)器可憐的1G內(nèi)存在構(gòu)建過程中的內(nèi)存變化如下:

18.png

所以,都是貧窮惹的禍~

根據(jù)git diff 選擇性上傳文件

有些情況下,我們只需要上傳變動的文件,并不需要上傳所有文件,此時shell部分可以這樣編寫:

diff=`git diff --name-only HEAD~1 HEAD~0`
base="/home/upload-upyun"
dest="static-demo"
rm -rf ${base}/${dest}
# 創(chuàng)建目標(biāo)文件夾
mkdir -p ${base}/${dest}
# 循環(huán)復(fù)制變動文件(被刪除文件忽略,只做新增與覆蓋)
for line in $diff
do 
if [ -f $line ];then
  cp --parents -afv $line ${base}/${dest}
fi
done

echo "開始上傳..."
node  ${base}/index.js --${dest}
rm -rf ${base}/${dest}

jenkins的其他安裝方式

也可以通過以下方式安裝Jenkins,本文不再嘗試,效果與rpm方式理論應(yīng)該等同,具體可參照:

  1. yum進(jìn)行安裝:jenkins yum 安裝
  2. war包安裝:WAR包方式安裝Jenkins

Generic Webhook Trigger Plugin

上述的webhook是通過github-plugin 進(jìn)行觸發(fā),但對于其他git托管,如騰訊云,阿里云,碼云等其他第三方git服務(wù)提供平臺,并沒有對應(yīng)的插件,此時,就輪到 Generic Webhook Trigger Plugin 插件登場:

首先,進(jìn)行安裝,過程省略。安裝完畢后在項目配置頁面可以看到多出一個配置:

20.png

我們需要拿到webhook的地址,根據(jù)官方文檔可以知道,我們需要拿到Jenkins管理員的API token, 進(jìn)入管理用戶頁面,選擇admin生成一個:

21.png

最終得到的webhook地址為:http://xxx:8080/generic-webhook-trigger/invoke?token=11d85c3af55e018axxxxxx, 這里我們以騰訊云為例,配置下webhook:

22.png

我們再回到項目配置頁面,需要對該插件做一些配置, 默認(rèn)我們配置好webhook后,所有配置過該webhook的頁面,所有項目與分支的任意一個變動都可以觸發(fā)所有項目的構(gòu)建,這顯然不是我想要的,我們需要做一些區(qū)分,參照此文章

  1. 區(qū)分分支
24.png
  1. 區(qū)分項目(不同服務(wù)提供商字段會有差異,騰訊云是在repository下)
25.png
  1. 配置token
26.png
  1. 過濾字段匹配項目(此處為每個項目特有配置,區(qū)分好項目與分支)
27.png

我們手動構(gòu)建一次可以看到webhook返回結(jié)果如下:

{
  "ref": "refs/heads/dev",
  "before": "6b53d96f0ee3dc5b1b60d389105d330641ac1612",
  "after": "9370030232017ff649a070ae6bf1bdd7522a037c",
  "created": false,
  "deleted": false,
  "compare": "\u003ca href\u003d\u0027https://coding.net/u/wangxx/p/jenkins-autoupload\u0027 target\u003d\u0027_blank\u0027\u003ejenkins-autoupload\u003c/a\u003e/git/compare/6b53d96f0ee3d...9370030232017",
  "commits": [{
    "id": "9370030232017ff649a070ae6bf1bdd7522a037c",
    "distinct": false,
    "message": "fix:添加文件\n",
    "timestamp": 1569831379000,
    "url": "\u003ca href\u003d\u0027https://coding.net/u/wangxx/p/jenkins-autoupload\u0027 target\u003d\u0027_blank\u0027\u003ejenkins-autoupload\u003c/a\u003e/git/commit/9370030232017ff649a070ae6bf1bdd7522a037c",
    "author": {
      "name": "wangxx",
      "email": "m.h.wang@foxmail.com",
      "username": "wangxx"
    },
    "committer": {
      "name": "wangxx",
      "email": "m.h.wang@foxmail.com",
      "username": "wangxx"
    },
    "added": ["dofun.png"],
    "removed": [],
    "modified": []
  }],
  "head_commit": {
    "id": "9370030232017ff649a070ae6bf1bdd7522a037c",
    "distinct": false,
    "message": "fix:添加文件\n",
    "timestamp": 1569831379000,
    "url": "\u003ca href\u003d\u0027https://coding.net/u/wangxx/p/jenkins-autoupload\u0027 target\u003d\u0027_blank\u0027\u003ejenkins-autoupload\u003c/a\u003e/git/commit/9370030232017ff649a070ae6bf1bdd7522a037c",
    "author": {
      "name": "wangxx",
      "email": "m.h.wang@foxmail.com",
      "username": "wangxx"
    },
    "committer": {
      "name": "wangxx",
      "email": "m.h.wang@foxmail.com",
      "username": "wangxx"
    },
    "added": ["dofun.png"],
    "removed": [],
    "modified": []
  },
  "pusher": {
    "name": "王xx",
    "email": "m.h.wang@foxmail.com",
    "username": "wangxx"
  },
  "sender": {
    "id": 2222972,
    "login": "wangxx",
    "avatar_url": "https://coding-net-production-static-ci.codehub.cn/e4ed6f51-7033-4c66-bb1c-d567795c88a9.jpg?imageMogr2/auto-orient/format/jpeg/cut/!640x640x0x0",
    "url": "https://dev.tencent.com/api/user/key/wangxx",
    "html_url": "https://dev.tencent.com/u/wangxx",
    "name": "王xx",
    "name_pinyin": "|wmh|wangxx"
  },
  "repository": {
    "id": 4733490,
    "name": "jenkins-autoupload",
    "full_name": "wangxx/jenkins-autoupload",
    "owner": {
      "id": 2222972,
      "login": "wangxx",
      "avatar_url": "https://coding-net-production-static-ci.codehub.cn/e4ed6f51-7033-4c66-bb1c-d567795c88a9.jpg?imageMogr2/auto-orient/format/jpeg/cut/!640x640x0x0",
      "url": "https://dev.tencent.com/api/user/key/wangxx",
      "html_url": "https://dev.tencent.com/u/wangxx",
      "name": "王xx",
      "name_pinyin": "|wmh|wangxx"
    },
    "private": true,
    "html_url": "\u003ca href\u003d\u0027https://dev.tencent.com/u/wangxx/p/jenkins-autoupload\u0027 target\u003d\u0027_blank\u0027\u003ejenkins-autoupload\u003c/a\u003e",
    "description": "測試jenkins自動化部署上傳",
    "fork": false,
    "url": "https://dev.tencent.com/api/user/wangxx/project/jenkins-autoupload",
    "created_at": 1568866930000,
    "updated_at": 1568866930000,
    "clone_url": "https://git.dev.tencent.com/wangxx/jenkins-autoupload.git",
    "ssh_url": "git@git.dev.tencent.com:wangxx/jenkins-autoupload.git",
    "default_branch": "master"
  }
}

至此,配置基本完畢。

權(quán)限部分配置

通過插件 Role-based Authorization Strategy 配置完成,安裝完插件,重啟Jenkins,會發(fā)現(xiàn)配置頁面多一個選項:

28.png

首先需要在全局安全配置頁面將授權(quán)策略改為:Role-Based Strategy

31.png

進(jìn)入 Manage Role 選項,配置用戶角色與所在組角色權(quán)限

29.png

進(jìn)入 Assign Roles 選項,為用戶分配角色(首先需要在用戶管理板塊創(chuàng)建用戶)

30.png

具體可參考jenkins配置用戶角色權(quán)限,根據(jù)不同權(quán)限顯示視圖、Job

總結(jié)

docker安裝方式比較傻瓜,而且整個插件安裝的過程都比較快,但是因為docker就像一個封閉的黑盒,很多東西與系統(tǒng)是隔離開的(廢話,設(shè)計初衷就是這樣),導(dǎo)致我這個菜鳥遇到問題想不到好的解決方案。嗯,還是RPM用起來更順手一些,遇到問題基本還能應(yīng)對。最后,不差錢的,請上大內(nèi)存服務(wù)器~

可能用到的linux命令

# 啟動docker
systemctl start docker
      
# 停止docker
systemctl stop docker 

# 列出docker內(nèi)正在運行的容器
docker ps

# 列出docker內(nèi)的容器
docker ps -a
      
# 啟動docker內(nèi)容器
docker start <id> 
docker restart <id>
docker stop <id>

# 進(jìn)入docker容器內(nèi)部執(zhí)行命令
docker exec -it <id> bash

# 查看jenkins啟動狀態(tài)
systemctl status jenkins.service 

# 查看jenkins日志
sudo tail -f /var/log/jenkins/jenkins.log  查看日志

補充:增加swap分區(qū)解決阿里云內(nèi)存不足

發(fā)現(xiàn)可以擴展Swap分區(qū),即交換區(qū),系統(tǒng)在物理內(nèi)存(這里應(yīng)該是運行內(nèi)存)不夠時,與Swap進(jìn)行交換,來解決內(nèi)存不足的問題。設(shè)置步奏如下:

  1. 首先創(chuàng)建用于交換分區(qū)的文件,并設(shè)置交換分區(qū)文件

     dd if=/dev/zero of=/var/swap bs=1024 count=4096000
    
  2. 創(chuàng)建 swap 文件

     mkswap /var/swap
    
  3. 加載這個文件

     swapon /var/swap
    

執(zhí)行以上命令可能會出現(xiàn):“不安全的權(quán)限 0644,建議使用 0600”提示,其實已經(jīng)激活了,可以忽略,修改權(quán)限:

    chmod -R 0600 /var/swap
  1. 設(shè)置系統(tǒng)啟動時自動掛載分區(qū)

     echo "/var/swap swap swap defaults 0 0" >> /etc/fstab
    
  2. 確定系統(tǒng)對SWAP分區(qū)的使用原則,當(dāng)swappiness內(nèi)容的值為0時,表示最大限度地使用物理內(nèi)存,物理內(nèi)存使用完畢后,才會使用SWAP分區(qū)。當(dāng)swappiness內(nèi)容的值為100時,表示積極地使用SWAP分區(qū),并且把內(nèi)存中的數(shù)據(jù)及時地置換到SWAP分區(qū)。liunx默認(rèn)為60,此處我們設(shè)置為默認(rèn)大小60

     echo 60 > /proc/sys/vm/swappiness
    

再次運用構(gòu)建命令,查看內(nèi)存變化,會發(fā)現(xiàn)swap區(qū)已經(jīng)得到了利用:

32.png

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 0 前言 記錄 jenkins 基于 docker 的前后端項目的構(gòu)建及部署全流程。后端項目案例使用 Spring...
    MrDcheng閱讀 1,400評論 0 1
  • 最近因為公司需要,所以搭建了一套持續(xù)集成的環(huán)境。再此記錄一下。如轉(zhuǎn)載,請標(biāo)明出處。 環(huán)境說明 虛擬機:centos...
    輝_ace閱讀 2,333評論 3 25
  • 萬師訪萬家 家校共教育 ——屏山中學(xué)積極開展“萬師訪萬家”家訪活動 為全面落實立德樹人的根本任務(wù),促進(jìn)家校共育,屏...
    凌霜傲雪1903閱讀 243評論 0 2
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,831評論 28 54
  • 人工智能是什么?什么是人工智能?人工智能是未來發(fā)展的必然趨勢嗎?以后人工智能技術(shù)真的能達(dá)到電影里機器人的智能水平嗎...
    ZLLZ閱讀 4,096評論 0 5

友情鏈接更多精彩內(nèi)容