本文內容仍是相對很淺的,Docker中關於分佈式,集羣的內容沒有涉及,因此本文推薦前端同窗看一看,後端同窗就不推薦了。本文中全部命令都是針對Ubuntu16.04,拷貝粘貼時請注意。(這一篇文章有些復讀機🎺,對docker感興趣的能夠直接看如下的參考資料)html
本文主要參考瞭如下資料前端
命令彙總,方面快速查詢node
# 建立鏡像 docker build -t [鏡像名] . # Docker鏡像列表 docker image ls # 刪除鏡像 docker rmi [id] # 刪除全部的鏡像 docker image rm $(docker image ls -a -q) # Docker容器列表 docker container ls docker container ls --all # 所有的中止的容器 docker container ls -aq # 刪除容器 docker rm [id] # 刪除全部的容器 docker container rm $(docker container ls -a -q) # 中止容器 docker container stop [id] # 啓動中止的容器 docker container start [id] # 強制關閉指定容器 docker container kill [id] # 重啓容器 docker container restart [id] # 進入容器內部 docker exec -it [容器id] bash # 運行容器,外部的4000端口映射到容器的80端口 docker run -p 4000:80 hello # 指定容器的名稱 --name docker run --name [name] -p 4000:80 [image] # 守護態運行容器(後臺運行,不須要在打開一個終端) docker run -d -p 4000:80 hello # 隨機映射本機的端口到容器的端口 docker run -d -P [image] # 映射全部的地址 docker run -d -p [宿主機端口]:[容器端口] [image] # 映射指定地址以及端口 docker run -d -p [ip]:[宿主機端口]:[容器端口] [image] # 映射指定地址的任意端口 docker run -d -p [ip]::[容器端口] [image] # 查看容器映射的端口 docker port [容器名|容器id] [容器的端口] # 標記鏡像 docker tag [鏡像名] [用戶名]/[存儲庫]:[標籤] # 上傳鏡像到DockerHub docker push [用戶名]/[存儲庫]:[標籤] # 從DockeerHub上獲取鏡像 docker pull [存儲庫]:[標籤] # 從存儲庫運行鏡像 docker run -p [用戶名]/[存儲庫]:[標籤] # 建立數據卷 docker volume create [數據卷名稱] # 查看全部的數據卷 docker volume ls # 查看數據卷的信息 docker volume inspect [數據卷名稱] # 刪除數據卷 docker volume rm [數據卷名稱] # 清理無主的數據卷 docker volume prune # 查看網絡列表 docker network ls
Docker的虛擬化是在系統層面實現的,虛擬機則是在硬件方面實現的。linux
Docker Images 是一個可執行的包。包含了運行應用程序的全部內容,代碼,運行時,環境變量,庫,配置文件。nginx
鏡像的構建是一層層構建的,前一層是後一層的基礎。每一層構建完成後,不會再改變。後面的修改的只會發生當前的鏡像層。好比刪除前一層的文件,並非真正的刪除。而是在後面的鏡像層中標記爲刪除,刪除的文件會一直存在鏡像中。git
分層的特性使得鏡像容易擴展和複用。好比在Docker Hub上提供的各類基礎鏡像。github
咱們在上面說過鏡像是分層的。咱們這裏利用commit命令深刻理解下鏡像的構成。web
咱們使用docker run --name webserver -d -p 4880:80 nginx構建nginx的容器。使用exec進入webserver容器,並進行了必定的修改。而docker commit命令能夠將咱們對容器存儲層的修改保存下來,成爲新的鏡像。新的鏡像由原有的鏡像,加上咱們更改的存儲層構成的。redis
Docker Containers 是鏡像運行的實例。可使用docker ps查看正在運行的容器列表。容器一樣也是多層存儲,以鏡像做爲基礎層,在基礎層上加一層容器運行的存儲層。mongodb
sudo apt-get remove docker docker-engine docker.io containerd runc
# 更新apt sudo apt-get update sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-common # 添加官方GPG密鑰 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # 將存儲庫添加到APT源 sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" # 更新apt sudo apt-get update # 安裝 sudo apt-get install docker-ce docker-ce-cli containerd.io
# 查看docker版本 docker version
# 容許hello-world鏡像,驗證是否正確安裝 docker run hello-world
在過去若是要編寫Python應用程序,須要在機器上安裝Python運行時,不只僅須要在你的開發機器配置環境,並且還須要在生產環境的機器上配置環境。若是使用Docker,能夠把Python運行時經過鏡像獲取,無需在不一樣的機器上重複安裝環境。能夠在應用程序,和Python運行時鏡像打包在一塊兒。確保在不一樣的機器上均可以正常運行。這些可移植的鏡像,由Dockerfile定義
Dockerfile定義了容器內的環境。容器與系統的其餘部分相隔離,所以須要將容器的端口映射到外部。由這個Dockerfile定義的應用程序的構建,運行在任何地方的行爲都徹底相同。
# 建立空文件夾,並在文件夾中建立Dockerfile文件 mkdir learn-docker cd learn-docker touch Dockerfile touch app.js
# 在Dockerfile寫入如下的內容 vim Dockerfile # 將node做爲父鏡像 FROM node # 將容器的工做目錄設置爲/app(當前目錄,若是/app不存在,WORKDIR會建立/app文件夾) WORKDIR /app # 將當前文件夾中的全部內容,複製到容器的/app中 COPY . /app # 安裝node包 RUN npm install # 容器對外暴露80端口 EXPOSE 80 # 環境變量 ENV NAME World # 容器啓動時運行app.js CMD ["node", "app.js"]
const express = require('express') const app = express() app.get('/', function (req, res) { res.send('hello world') }) app.listen(80, '0.0.0.0')
咱們並不須要在系統中安裝Python,Flask或者Redis。構建運行鏡像的時候也不須要安裝它們。雖然看起來咱們沒有使用Pyhone構建開發環境,可是咱們已經這樣作了。
使用docker build命令,構建鏡像。(--tag選項會對鏡像進行命名)
# 構建hellodocker的鏡像 docker build --tag=hellodocker . # 構建完成後,咱們查看鏡像列表 docker image ls
# 將服務器的4000端口映射到容器的80端口 docker run -p 3999:80 hellodocker # 查看正在運行的容器 docker container ls # curl測試,返回helloworld curl 0.0.0.0:3999
FROM指令用於指定鏡像的基礎鏡像。FROM scratch,能夠指定空的基礎鏡像。
Dockerfile中每個指令都會創建一層鏡像,不該該把RUN指令看成shell腳原本寫。
FROM scratch # 這回額外的建立7層鏡像,這是錯誤的行爲 RUN apt-get update RUN apt-get install -y gcc libc6-dev make wget RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" RUN mkdir -p /usr/src/redis RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 RUN make -C /usr/src/redis RUN make -C /usr/src/redis install # 正確的寫法應當是,使用&&將命令串連,簡化爲一層鏡像 RUN buildDeps='gcc libc6-dev make wget' \ && apt-get update \ && apt-get install -y $buildDeps \ && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \ && mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ && make -C /usr/src/redis \ && make -C /usr/src/redis install \ # 清除無用的緩存,避免Docker的臃腫 && rm -rf /var/lib/apt/lists/* \ && rm redis.tar.gz \ && rm -r /usr/src/redis \ && apt-get purge -y --auto-remove $buildDeps
COPY指令將當前目錄的文件,複製到image中。
源路徑指的是當前上下文的目錄。目標路徑能夠是容器內的絕對路徑路徑,也能夠是容器WORKDIR指定的工做目錄的相對路徑。
COPY [源路徑] [目標路徑]
CMD指定容器主進程的啓動命令。
# 使用node CMD ["node", "app.js"] # 使用pm2 # http://pm2.keymetrics.io/docs/usage/docker-pm2-nodejs/#docker-integration RUN npm install pm2 -g CMD ["pm2-runtime", "app.js"]
VOLUME指令能夠指定某個目錄爲匿名卷,任何對該目錄的寫操做,不會記錄到容器的存儲層。
對於數據庫,數據庫文件應保存到數據卷中
VOLUME /data
ENV指令用來設置環境變量,Dockerfile後面的指令或者代碼中,均可以使用該環境變量
# Dockerfile # 環境變量 ENV NAME World
// app.js const express = require('express') const app = express() app.get('/', function (req, res) { // 使用環境變量 res.send(`hello world${process.env.NAME}`) }) app.listen(80, '0.0.0.0')
EXPOSE指令用於聲明端口,可是EXPOSE聲明的端口要和docker run <宿主端口>:<容器端口>區分。EXPOSE指令僅僅是聲明,而不會自動進行端口映射。
WORKDIR用來指定當前目錄(工做目錄),Dockerfile不是shell腳本,這一點須要切記。
# 這是錯誤的示範 RUN cd /app RUN echo "hello" > world.txt
這裏並不會建立 /app/world.txt的文件。由於在Dockerfile中兩行RUN的執行環境是不一樣的。因此第一層的 cd /app 不會影響到第二層的當前目錄,正確的作法應當是。
WORKDIR /app RUN echo "hello" > world.txt
DockerHub相似於Github,由Docker官方維護的一個公共容器鏡像倉庫。咱們首先註冊,並登陸Docker Hub
# 登陸 docker login # 標記鏡像 # docker tag [鏡像名] [用戶名]/[存儲庫]:[標籤] docker tag hellodocker zhangyue9467/learn-docker:test
docker push zhangyue9467/learn-docker:test
Docker Hub倉庫中就會有咱們發佈的鏡像
使用Docker後,咱們不須要在其餘機器上安裝任何東西,就能夠運行它。只須要遠程拉取Docker的鏡像
docker run -p 3998:80 zhangyue9467/learn-docker:test
數據卷是一個可供一個或多個容器使用的特殊目錄, 數據卷中的數據能夠容器之間共享和重用。對數據卷的修改會立馬生效。
# 建立一個名爲vol的數據卷 docker volume create vol # 查看數據卷中的信息 docker volume inspect vol
Mountpoint中是數據卷掛載在宿主機的位置。咱們在Mountpoint字段對應的文件夾內建立一個文件
使用--mount,在啓動容器時掛載數據卷,容器啓動時能夠掛載多個數據卷。
# 啓動了name爲web的容器 # 使用vol數據卷,加載到容器的/webapp中 docker run -d -P \ --name web \ --mount source=vol,target=/webapp \ hello
進入web容器進行查看,vol數據卷中內容掛載到容器的/webapp目錄中
宿主機的路徑必須是絕對路徑,使用--mount若是主機目錄不存在Docker會報錯。Docker默認對主機目錄的權限是讀寫權限。
# 啓動了name爲web2的容器 # 使用本機/var/www/vol目錄做爲數據卷,加載到容器的/webapp中 docker run -d -P \ --name web2 \ --mount type=bind,source=/var/www/vol,target=/webapp \ hello
# /root/.bash_history 做爲卷 docker run -d -P \ --name web3 \ --mount type=bind,source=/root/.bash_history,target=/root/.bash_history \ hello
在容器內部能夠獲取外部的命令行的歷史記錄
# 映射任意端口到容器的端口 docker run -d -P [image] # 映射全部的地址 # docker run -d -p 5000:5000 web docker run -d -p [宿主機端口]:[容器端口] [image] # 映射指定地址以及端口 # docker run -d -p 127.0.0.1:5000:5000 web docker run -d -p [ip]:[宿主機端口]:[容器端口] [image] # 映射指定地址的任意端口 # docker run -d -p 127.0.0.1::5000 web docker run -d -p [ip]::[容器端口] [image]
# 查看容器映射的端口 docker port [容器名|容器id] [容器的端口]
容器內部擁有自身的網絡和ip,可使用docker inspect命令在"NetworkSettings"中獲取。
# 查看容器內部的ip信息 docker inspect [容器id]
使用自定義Docker網絡實現容器通訊。若是是多個容器可使用Docker Compose實現容器間的通信,Docker Compose默認全部容器都在同一個網絡中的。
# 建立網絡 docker network create -d bridge mynet # 將容器連接到網絡mynet中 docker run -d -p 5000:8888 --name busybox1 --network mynet hello docker run -d -p 5001:8889 --name busybox2 --network mynet hello2 # 進入容器busybox1內部,可使用curl或者ping,測試 # busybox2的ip地址 curl 172.19.0.3:8889 # ping busybox2
使用Dockerfile文件能夠很方便定義一個容器。但在平常的工做中一個項目可能須要多個容器(前端,後端,數據庫)。Compose容許用戶定義docker-compose.yml模版文件,來定義一組相關聯的容器爲一組項目。
Compose中兩個概念:
sudo curl -L https://github.com/docker/compose/releases/download/1.17.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
驗證docker composc的安裝
# 查看版本 docker-compose --version
更多命令請參考。
💡在介紹Compose命令以前,我以爲有必要明確一下,服務與容器的概念。我曾經混淆過它們的概念,詳細的解答請參考
在docker-compose中,docker-compose.ymal中定義的是服務, 下面定義了一個名爲web的服務。而web的服務會啓動一個名爲"[項目文件的名稱]_web"的容器。
# docker-compose.ymal version: '3' services: web: build: . ports: - "5000:3000"
在項目的目錄根目錄下運行build命令,構建鏡像
# 構建容器 docker-compose build
在項目的根目錄下運行ps命令,列出項目中的全部容器
docker-compose ps
up命令將會完成構建容器,建立服務,啓動服務,等一系列操做。能夠直接經過up命令啓動一個項目
# 在前臺啓動容器 docker-compose up # 在後臺啓動並運行項目(不須要強制退出控制檯了) docker-compose up -d
查看服務映射在宿主機上的端口
version: '3' services: web: build: . ports: - "5000:3000"
# 示例 # 0.0.0.0:5000 docker-compose port web[服務] 3000[容器端口]
更多指令請參考
version: '3' services: # web服務 web: # 容器的名稱 container_name: hello_compose # Dockerfile文件的位置(絕對路徑,相對docker-compose模版文件的路徑均可以) build: . # 暴露端口,但不映射到宿主機 expose: - "3000" # 暴露端口 [宿主端口]:[容器端口] ports: - "5000:3000" # 數據卷掛載的路徑 # https://forums.docker.com/t/making-volumes-with-docker-compose/45657 volumes: - [宿主機路徑]:[容器路徑] # db服務 db: # 容器使用的鏡像 image: "redis:alpine"
新建jenkins任務,將github上的項目拉取到線上雲服務器的空文件夾中。
接着定義Dockerfile自定義鏡像。使用FROM指令將nginx做爲父鏡像,使用COPY指令將上下文目錄的全部內容拷貝到容器的/var/www/hello_docker/目錄中。/var/www/hello_docker/是咱們在nginx配置中配置的靜態文件目錄。接着使用COPY指令將nginx的配置文件,拷貝到/etc/nginx/conf.d/目錄中。conf.d文件夾內的nginx配置文件的內容,會合併到nginx主配置文件中。緊接着使用RUN指令重啓nginx服務。
使用Dockerfile自定義咱們的鏡像後,須要經過build命令構建咱們的鏡像。因爲須要作到運維的自動化,直接啓動咱們的鏡像可能會產生錯誤(可能存在同名的鏡像)。咱們使用shell腳本判斷是否須要刪除以前的鏡像仍是直接啓動容器。最後使用run命令構建咱們的容器。
# Dockerfile FROM nginx COPY ./* /var/www/hello_docker/ COPY ./nginx/hello_docker.conf /etc/nginx/conf.d/ RUN service nginx restart
# nginx.conf server { listen 8888; server_name localhost; root /var/www/hello_docker; index index.html; expires 7d; }
容器構建完成後,咱們在本地沒法直接訪問容器映射的接口,咱們須要在☁️雲服務器配置nginx代理,訪問容器。
(咱們將轉發請求到容器映射的接口上)
前端的部署同以前的項目一致(這裏略過)。使用Dockerfile定義後端服務鏡像,使用FROM指令將node做爲父鏡像,使用RUN指令在全局安裝pm2,使用CMD指令, 使用pm2啓動後端的服務。
FROM node WORKDIR /server COPY . /server EXPOSE 8888 RUN npm install pm2 -g CMD ["pm2-runtime", "app.js"]
咱們直接使用docker-compose部署mongo數據庫。
須要注意的是,mongo數據存儲的位置,不建議直接將數據直接存儲到容器中。而是使用volumes,將容器內數據庫的存儲目錄掛載到宿主機的目錄中
version: '3.1' services: mongo: # 使用docker hub 的mongo鏡像 image: mongo # 容器重啓策略 restart: always # 容器啓動的參數 # ⚠️這裏存在一些問題還沒有解決 command: - '--auth' - '-f' - '/etc/mongod.conf' # 指定數據卷,配置文件以及數據存儲的位置 volumes: - '/etc/mongod.conf:/etc/mongod.conf' - '/var/lib/mongodb:/var/lib/mongodb' ports: - '37017:27017'
👻 這裏尚未作