私活利器,docker快速部署node.js应用
http://cnodejs.org/topic/53f494d9bbdaa79d519c9a4a
最近研究了幾天docker的快速部署,感覺(jué)很有新意,非常輕量級(jí)和方便,打算在公司推廣一下,解放運(yùn)維,省得每次部署一臺(tái)新服務(wù)器都去跑安裝腳本了,對(duì)于我們開(kāi)發(fā)人員也是好事情,無(wú)需寫太多重復(fù)的部署文檔,直接將docker的images丟上服務(wù)器就可以運(yùn)行了。
可能還有一些同學(xué)不了解docker這個(gè)項(xiàng)目,docker是由go語(yǔ)言編寫的,一個(gè)快速部署的輕量級(jí)虛擬技術(shù)項(xiàng)目,他允許開(kāi)發(fā)人員將自己的程序和運(yùn)行環(huán)境一起打包,制作成一個(gè)docker的image(鏡像),這樣部署到服務(wù)器上,也只需要下載這個(gè)image就可以將程序跑起來(lái),免去每次都安裝各種依賴和環(huán)境的麻煩,還能夠做到應(yīng)用程序之間的隔離,因?yàn)槲覀児静渴鸬膒ython程序,這樣一來(lái)我也省去了每次都到新服務(wù)器配置 vitualenv了。
官網(wǎng)地址:http://www.docker.com/
如此看來(lái)docker非常適合做小巧的外包項(xiàng)目,免去每次都為客戶配置一遍運(yùn)行環(huán)境,費(fèi)時(shí)費(fèi)力。
一、安裝: 安裝很簡(jiǎn)單,直接進(jìn)入下載頁(yè)面,根據(jù)自己的操作系統(tǒng)下載相對(duì)應(yīng)的安裝包即可,下面說(shuō)一下windows安裝:
下載地址:https://docs.docker.com/installation/windows/
在安裝docker時(shí),會(huì)附帶安裝git和VirtualBox,所以可能安裝時(shí)間稍微長(zhǎng)一些,安裝完畢重啟系統(tǒng),以管理員身份進(jìn)入命令行,就可以使用進(jìn)入linux虛擬機(jī)命令 “boot2docker” ,由于docker目前的鏡像只針對(duì)linux,所以windows下面必須安裝虛擬機(jī)才能使用。
第一次使用boot2docker start,會(huì)出現(xiàn)錯(cuò)誤:
Failed to get machine "boot2docker-vm": machine does not exist沒(méi)關(guān)系,這是因?yàn)闆](méi)有boot2docker iso的鏡像所致,執(zhí)行:
$ boot2docker init就可以初始化鏡像,耐心等待下載并安裝完畢后,我們繼續(xù)執(zhí)行開(kāi)啟虛擬機(jī)。
$ boot2docker start在windows下是無(wú)法直接使用cmd窗口來(lái)操作linux系統(tǒng)的,所以我們需要進(jìn)入linux虛擬機(jī)來(lái)操作docker,執(zhí)行:
$ boot2docker ssh就可以進(jìn)入linux虛擬機(jī),如果要退出并關(guān)閉虛擬機(jī),執(zhí)行如下命令:
$ exit$ boot2docker stop調(diào)試時(shí)查看虛擬機(jī)ip地址,后面部署測(cè)試環(huán)境會(huì)用到:
$ boot2docker ip192.168.59.103我們可以通過(guò)ssh的ip地址192.168.59.103,用戶名 docker,密碼 tcuser,登錄到虛擬機(jī)中去。
如果不幸長(zhǎng)時(shí)間無(wú)法init成功,說(shuō)明鏡像被GFW擋住了,手動(dòng)去github上下載鏡像,地址為:
https://github.com/boot2docker/boot2docker/releases
如果還是無(wú)法下載成功,我是好心人,把1.1.2版本的boot2docker.iso鏡像丟到了百度云上:
http://pan.baidu.com/s/1c01qieG
下載完畢之后放到目錄:
C:\Users\你的用戶名\.boot2docker\boot2docker.iso然后再執(zhí)行
$ boot2docker init二、下載鏡像,安裝環(huán)境 我們先執(zhí)行如下命令,啟動(dòng)虛擬機(jī):
$ boot2docker start2014/08/18 21:22:41 Waiting for VM to be started... ........... 2014/08/18 21:23:21 Started. 2014/08/18 21:23:21 Docker client does not run on Windows for now. Please use 2014/08/18 21:23:21 "boot2docker" ssh 2014/08/18 21:23:21 to SSH into the VM instead.如果我們的物理機(jī)內(nèi)存低于4G,那么跑這個(gè)boot2docker可能需要手動(dòng)設(shè)置內(nèi)存占用大小:
$ boot2docker start -m=512我們利用命令就可以進(jìn)入linux虛擬機(jī)了
$ boot2docker ssh ## .## ## ## == ## ## ## ## === /""""""""""""""""\___/ === ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~ \______ o __/ \ \ __/ \____\______/ _ _ ____ _ _ | |__ ___ ___ | |_|___ \ __| | ___ ___| | _____ _ __ | '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__| | |_) | (_) | (_) | |_ / __/ (_| | (_) | (__| < __/ | |_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_| boot2docker: 1.1.2 master : 740106c - Thu Jul 24 03:24:10 UTC 2014執(zhí)行 docker 命令,會(huì)有一個(gè)命令列表,里面列出了所有 docker 支持的功能,列表如下:
Commands:attach Attach to a running container build Build an image from a Dockerfile commit Create a new image from a container's changes ... ...下面會(huì)對(duì)一些常用的命令進(jìn)行示例說(shuō)明,我們可以通過(guò)如下命令,先查找centos的鏡像,并把他下載下來(lái)
$ docker search centos #查找centos名字的鏡像$ docker pull centos #下載官方純凈版本的centos鏡像在調(diào)用search命令時(shí),你會(huì)看到有好多centos包,他們都是這樣的<username>/<image_name>,這些不在根目錄的鏡像都是非官方的,是其他用戶提交到docker hub上去的,耐心等待片刻我們就可以將centos的鏡像拉下來(lái)了。
如果臉黑,鏡像又不幸被墻,那么試試加上下面的hosts:
54.234.135.251 get.docker.io 54.234.135.251 cdn-registry-1.docker.io成功下載好centos鏡像之后,我們可以利用如下命令來(lái)查看鏡像列表:
$ docker imagesREPOSITORY TAG IMAGE ID CREATEDVIRTUAL SIZEcentos centos6 b1bd49907d55 2 weeks ago212.5 MB centos centos7 b157b77b1a65 2 weeks ago 243.7 MB centos latest b157b77b1a65 2 weeks ago 243.7 MB接下來(lái)我們就利用centos7這個(gè)鏡像輸出一段 hello world
$ docker run b15 /bin/echo 'Hello world' Hello world注意這里的 b15,他表示centos7這個(gè)images的id,不用全部打全,只要保證輸入的id前幾位能找到唯一鏡像即可,這點(diǎn)很贊。 稍微復(fù)雜一點(diǎn)的例子:
$ docker run -i -d -t b15 /bin/sh -c "while true; do echo hello world; sleep 1; done"-i表示同步container的stdin,-t表示同步container的輸出,-d表示deamon,以后臺(tái)啟動(dòng)這個(gè)container,執(zhí)行這個(gè)container是永遠(yuǎn)不會(huì)停止的,每一秒鐘都會(huì)輸出hello world。
至于什么是container,container和image的關(guān)系我們下一段再說(shuō),列出鏡像的歷史:
$docker history image_name三、安裝環(huán)境 在開(kāi)始第三段介紹之前,有必要說(shuō)幾個(gè)利用windows cmd窗口的小技巧。
1、如果想要使用標(biāo)記選中功能,你會(huì)發(fā)現(xiàn),當(dāng)我們進(jìn)入 boot2docker ssh 之后,鼠標(biāo)對(duì)窗口的右鍵是無(wú)效的,所以想要利用標(biāo)記選中窗口內(nèi)的文字得這么弄:“點(diǎn)擊左上角圖標(biāo)->編輯->標(biāo)記”,這樣就可以使用標(biāo)記了
2、如果從其他地方復(fù)制了命令,但是窗口沒(méi)有右鍵無(wú)法粘貼怎么辦?用和1相同的辦法:“點(diǎn)擊左上角圖標(biāo)->編輯->粘貼”。
3、坑爹的windws如果命令太長(zhǎng),在boot2docker ssh里換行會(huì)錯(cuò)位的,在“點(diǎn)擊左上角圖標(biāo)->屬性->布局->屏幕緩沖區(qū)大小和窗口大小”的數(shù)值,保證長(zhǎng)的命令也在一行內(nèi)就沒(méi)有問(wèn)題了,注意要重啟cmd窗口。
接下來(lái)我們簡(jiǎn)單說(shuō)明一下image和container的關(guān)系,image顧名思義就是鏡像的意思,我們把他理解為一個(gè)執(zhí)行環(huán)境(env),當(dāng)我們執(zhí)行了docker run命令之后,dock就會(huì)根據(jù)當(dāng)前的image創(chuàng)建一個(gè)新的container,container更像是一個(gè)操作或者程序運(yùn)行的一個(gè)沙箱,他們互相獨(dú)立,但是都運(yùn)行在image創(chuàng)建的執(zhí)行環(huán)境之上,根據(jù)上一段我們執(zhí)行了2個(gè)run的任務(wù),也就創(chuàng)建了2個(gè)獨(dú)立的container,我們通過(guò)命令
$ docker ps #查看當(dāng)前運(yùn)行的container$ docker ps -a #查看所有的container CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d1eca89869d0 centos:centos7 /bin/sh -c 'while tr 13 minutes ago Exited (0) 7 minutes ago distracted_mcclintock 0b71024c8a95 centos:centos7 /bin/sh -c 'while tr 15 minutes ago Exited (0) 7 minutes ago pensive_meitner 79a488c9cfb6 centos:centos7 console 22 minutes ago sick_babbage 06f43c19d10a centos:centos7 /bin/echo 'Hello wor 25 minutes ago Exited (0) 25 minutes ago berserk_einstein我們可以分別利用命令對(duì)image和container進(jìn)行刪除
$ docker rm #刪除container$ docker rmi #刪除image $ docker rm `docker ps -a -q` #刪除所有容器這樣執(zhí)行一次run就創(chuàng)建一個(gè)container,勢(shì)必會(huì)造成大量的無(wú)用的container,可能我不需要持久化保存container,如果在docker run命令加上 --rm=true 選項(xiàng),那當(dāng)這個(gè)container執(zhí)行完畢,將自動(dòng)自己刪除,保證了container數(shù)量不會(huì)泛濫增長(zhǎng)。
我們之前下載純凈版centos7是沒(méi)有任何第三方軟件的,包括wget,ping等命令都要通過(guò)yum工具來(lái)重新安裝,我們當(dāng)然不想每次都重新安裝這些東西,我不僅希望要把一些常用的庫(kù)安裝到的image中去,同時(shí)還希望把程序運(yùn)行的環(huán)境也安裝進(jìn)去,所以image更像是一個(gè)系統(tǒng)的模版。
你會(huì)發(fā)現(xiàn),當(dāng)你執(zhí)行如下命令,wget命令時(shí)安裝成功了,但是當(dāng)你下次執(zhí)行wget命令時(shí),又會(huì)報(bào)錯(cuò),說(shuō)找不到這個(gè)命令,到底是怎么回事呢?
$ docker run -t b15 yum install -y wget #通過(guò)yum工具安裝wget命令 #安裝完畢后,執(zhí)行wget會(huì)報(bào)沒(méi)有這個(gè)命令 $ docker run -t b15 wget http://www.baidu.com 2014/08/18 15:42:19 exec: "wget": executable file not found in $PATH為什么會(huì)出現(xiàn)這個(gè)問(wèn)題呢?答案就是我們上面所說(shuō)的那樣,每次執(zhí)行docker run都會(huì)去獨(dú)立的創(chuàng)建一個(gè)新的container來(lái)執(zhí)行程序,所以我們必須手動(dòng)把這些對(duì)container的更改提交成一個(gè)新的image,才能夠依據(jù)這個(gè)image執(zhí)行wget操作。
我們先把當(dāng)前所有的container都刪除,然后直接登錄到container的bash命令窗口中去,免得每次都去輸入docker run了
$ docker run -t -i b15 /bin/bash bash-4.2#這樣我們進(jìn)入了一個(gè)新的container,依據(jù)centos7作為模板,我們將要在其上面安裝wget工具,直接執(zhí)行
$ yum install -y wget安裝完畢之后,我們執(zhí)行exit退出container 輸入docker ps -a 我們找到剛才安裝過(guò)wget工具的container ID,我們要把這個(gè)container重新做成一個(gè)新的image模版,這個(gè)模版將帶wget命令。
$ docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES26cc82ad29af centos:centos7 /bin/bash 3 minutes ago Exited (0) 5 seconds ago desperate_mccarthy我們執(zhí)行如下命令,將一個(gè)安裝過(guò)軟件的container提交為一個(gè)image
$ docker commit 26 wget-centos7 60cd26c6ca1e753bf77aa913ed7b826767a678b75f6dd8421353f6c0899d3e5e我們查看當(dāng)前image鏡像的列表:
wget-centos7 latest 60cd26c6ca1e 37 seconds ago 304.6 MB centos centos6 b1bd49907d55 2 weeks ago 212.5 MB centos centos7 b157b77b1a65 2 weeks ago 243.7 MB centos latest b157b77b1a65 2 weeks ago 243.7 MB你會(huì)發(fā)現(xiàn)多了一行我們剛才提交的 wget-centos7 的image鏡像記錄,現(xiàn)在我們執(zhí)行這個(gè)鏡像的wget命令,看看會(huì)不會(huì)報(bào)錯(cuò)
$ docker run -t -i --rm=true 60 wget http://www.baidu.com --2014-08-18 15:54:55-- http://www.baidu.com/ Resolving www.baidu.com (www.baidu.com)... 180.97.33.108, 180.97.33.107 Connecting to www.baidu.com (www.baidu.com)|180.97.33.108|:80... connected. HTTP request sent, awaiting response... 200 OK執(zhí)行完畢后,docker自動(dòng)刪除這個(gè)container,并且至今報(bào)錯(cuò)的wget命令無(wú)法找到也不會(huì)出錯(cuò)了。
第二種安裝環(huán)境的辦法,類似腳本安裝,我們預(yù)先錄入好一系列安裝腳本,可以讓docker幫我們依次執(zhí)行這些安裝腳本,然后生成image,例如有安裝腳本Dockerfile:
# This is a commentFROM centosMAINTAINER doublespout <doublespout[@gmail](/user/gmail).com> RUN yum install -y wget我們執(zhí)行如下命令進(jìn)行創(chuàng)建images
$ mkdir wget$ cd wget$ vi Dockerfile #將上面內(nèi)容復(fù)制進(jìn)去 $ docker build -t="doublespout/wget" ./ #將看到安裝腳本的執(zhí)行輸出,安裝完成后,執(zhí)行 docker images 就可以看到我們剛才創(chuàng)建的鏡像了 docker imagescontainer 和 host 文件互相拷貝:
1、從container往host拷貝文件:
docker cp <container_id>:/root/hello.txt .2、從host往container里拷貝文件,比較麻煩一點(diǎn),首先停止Contaitner(當(dāng)然不停止也能拷貝)
docker stop <container_name_or_ID>然后執(zhí)行拷貝操作,執(zhí)行完成之后就能看到Contaitner里有這個(gè)文件拉
#執(zhí)行命令找到程序pidContainerID=$(docker inspect --format {{.Id}} <container_name_or_ID>) cp /tmp/tmp.txt /var/lib/docker/aufs/mnt/<ContainerID>/tmp/四、發(fā)布應(yīng)用程序 我們根據(jù)上一段的步驟,手動(dòng)將node.js環(huán)境裝好container并且發(fā)布成image,并保存"app.js"文件到"/var/nodejs/app.js",文件內(nèi)容為:
var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(1337); //注意這邊不能和官網(wǎng)示例那樣監(jiān)聽(tīng)127.0.0.1 console.log('Server running at http://0.0.0.0:1337/');執(zhí)行如下命令,運(yùn)行container:
$ docker run -d -i -p --name=nodeapp 1337:1337 fa node /var/nodejs/app.js #32bac9ed8ba055c935bd641d23097a36a573a243ee942358fd74dc4140308bc6其中fa是我創(chuàng)建的鏡像id,這個(gè)值因人而異,name參數(shù)是給這個(gè)container取名字,必須是唯一的。
-p參數(shù)就是類似端口映射的功能,將主機(jī)的端口1337映射到contianer的1337端口,我們可以運(yùn)行 docker ps,查看正在運(yùn)行的container,打開(kāi)瀏覽器,就能夠看到 hello world了。
用同樣的方法就可以將我們開(kāi)發(fā)好的應(yīng)用快速部署到生產(chǎn)服務(wù)器上去了。如果在docker run命令需要設(shè)置cpu的支持?jǐn)?shù)和權(quán)重值可以這樣:
-c, --cpu-shares=0 CPU shares (relative weight) --cpuset="" CPUs in which to allow execution (0-3, 0,1)另外一個(gè)小敲門,如果我想要進(jìn)入一個(gè)在運(yùn)行中的docker container時(shí),可以使用 docker attach,連上這個(gè)container的輸入和輸出。
五、將多個(gè) container 連接起來(lái)
我現(xiàn)在先下載一個(gè)redis數(shù)據(jù)庫(kù)image,這也是以后做項(xiàng)目的常規(guī)用法,數(shù)據(jù)庫(kù)單獨(dú)用一個(gè)image,程序一個(gè)image,利用docker的link屬性將他們連接起來(lái)。
$ docker pull redis #下載官方的redis鏡像,耐心等待一段時(shí)間接著我們執(zhí)行命令啟動(dòng)redis鏡像到一個(gè)container,開(kāi)啟redis-server持久化服務(wù)
$ docker run --name redis-server -d redis redis-server --appendonly yes然后我們?cè)賳?dòng)一個(gè)redis鏡像的container作為客戶端連接它
$ docker run -it --link redis-server:redis --rm redis /bin/bash redis[@7441b8880e4e](/user/7441b8880e4e):/data$ env #想要知道當(dāng)前我們?cè)谥鳈C(jī)還是container,注意$前面的host和name REDIS_PORT_6379_TCP_PROTO=tcp HOSTNAME=7441b8880e4e TERM=xterm REDIS_NAME=/boring_perlman/redis REDIS_PORT_6379_TCP_ADDR=172.17.0.34 #redis服務(wù)器ip REDIS_PORT_6379_TCP_PORT=6379 #redis服務(wù)器端口 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/data REDIS_PORT_6379_TCP=tcp://172.17.0.34:6379 SHLVL=1 REDIS_PORT=tcp://172.17.0.34:6379 HOME=/ _=/usr/bin/env $ redis-cli -h "$REDIS_PORT_6379_TCP_ADDR" -p "$REDIS_PORT_6379_TCP_PORT" 172.17.0.34:6379> set a 1 #成功連入redis數(shù)據(jù)庫(kù)服務(wù)器 OK 172.17.0.34:6379> get a "1" 172.17.0.34:6379>通過(guò)這樣的方法,我們就可以將發(fā)布的應(yīng)用程序和數(shù)據(jù)庫(kù)分開(kāi),單獨(dú)進(jìn)行管理,以后對(duì)數(shù)據(jù)庫(kù)進(jìn)行升級(jí)或者對(duì)程序進(jìn)行調(diào)整兩者都沒(méi)有沖突,系統(tǒng)環(huán)境變量我們可以通過(guò)程序的os模塊來(lái)獲得。
六、文件卷標(biāo)加載 比如我們想要一個(gè)日志文件保存目錄,如果直接寫入container,那樣image升級(jí)之后,日志文件處理就比較麻煩了,所以就需要將主機(jī)的文件卷標(biāo)掛載到container中去,掛載方法如下:
$ docker run --rm=true -i -t --name=ls-volume -v /etc/:/opt/etc/ centos ls /opt/etc boot2docker hostname ld.so.conf passwd- securetty sysconfig default hosts mke2fs.conf pcmcia services sysctl.conf fstab hosts.allow modprobe.conf profile shadow udev group hosts.deny motd profile.d shadow- version group- init.d mtab protocols shells gshadow inittab netconfig rc.d skel gshadow- issue nsswitch.conf resolv.conf ssl host.conf ld.so.cache passwd rpc sudoers如果想要掛載后的文件是只讀,需要在這樣掛載:
-v /etc/:/opt/etc/:ro #read only我們也可以掛載其他container中的文件系統(tǒng),需要用到 -volumes-from 參數(shù),我們先創(chuàng)建一個(gè)container,他共享/var/目錄給其他container。
$ docker run -d -i -t -p 1337:1337 --name nodedev -v /var/ fa node /var/nodejs/app.js然后我們啟動(dòng)一個(gè)ls-var的container來(lái)掛載nodedev共享的目錄:
$ docker run --rm=true -i -t --volumes-from nodedev --name=aaa1 centos ls /var adm db games kerberos local log nis opt run tmp yp cache empty gopher lib lock mail nodejs preserve spool var我們打印var目錄,會(huì)發(fā)現(xiàn)多了一個(gè)nodejs的目錄,就是從nodedev中的container掛載過(guò)來(lái)的。其實(shí)我們掛載其他container的路徑都是在根目錄上的。
七、發(fā)布到docker hub上去
我們做完鏡像,就需要將鏡像發(fā)布到docker hub上,供服務(wù)器下載然后運(yùn)行,這類似git倉(cāng)庫(kù),將自己開(kāi)發(fā)的東西丟到云服務(wù)器上,然后自己在其他機(jī)器或者其他開(kāi)發(fā)者可以下載鏡像,并且從這個(gè)鏡像開(kāi)始運(yùn)行程序或者再進(jìn)行2次制作鏡像。
我們需要先登錄docker帳號(hào),執(zhí)行:
$ docker login #輸入你在docker官網(wǎng)注冊(cè)的帳號(hào)和密碼就可以登錄了$ docker push <用戶名>/<鏡像名> #將你制作的鏡像提交到docker hub上非官方不允許直接提交根目錄鏡像,所以必須以<用戶名>/<鏡像名>這樣的方式提交,比如 doublespout/dev 這樣
八、總結(jié)
docker快速部署介紹完畢了,總結(jié)一下,要?jiǎng)?chuàng)建一個(gè)簡(jiǎn)單項(xiàng)目使用的步驟: 1、安裝配置docker
2、pull鏡像,安裝程序執(zhí)行環(huán)境
3、pull數(shù)據(jù)庫(kù)鏡像
4、開(kāi)發(fā)程序
5、push 程序的鏡像
6、服務(wù)器安裝配置docker
7、運(yùn)行數(shù)據(jù)庫(kù)鏡像
8、運(yùn)行程序鏡像,并且把數(shù)據(jù)庫(kù)鏡像link進(jìn)來(lái),并且掛載主機(jī)的日志目錄或其他上傳目錄。
使用了docker以后,環(huán)境配置只需一次,免去了開(kāi)發(fā)部署一套,測(cè)試部署一套,生產(chǎn)又部署一套的麻煩,以后程序搬家也是非常簡(jiǎn)單。
最后分享一個(gè)小敲門,如何像使用linux虛擬機(jī)那樣運(yùn)行一個(gè)container,比如我我們想要直接登錄container執(zhí)行多個(gè)任務(wù),又不想直接借助 docker run 命令,以后我們還想登錄到這個(gè)container來(lái)查看運(yùn)行情況,比如執(zhí)行top,ps -aux命令等等。
$ docker run -d -i -t -p 1337:1337 fa /bin/bash $ docker attach 58 bash-4.2#這樣我們就可以通過(guò)進(jìn)入container來(lái)調(diào)試程序了。但是一旦執(zhí)行ctrl+d或者exit,container就將退出,這個(gè)方法也只適用于開(kāi)發(fā)調(diào)試的時(shí)候。 最后請(qǐng)?zhí)?container 是一個(gè)小的vps的概念,詳見(jiàn)另一篇文章.
不要用ssh連接直接到你的container 常用的Dockerfile命令示例
74 回復(fù)總結(jié)
以上是生活随笔為你收集整理的私活利器,docker快速部署node.js应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Python pip 国内镜像大全及使用
- 下一篇: ACM题解系列之一:刘汝佳:《算法竞赛入