前端的Docker入門與實踐

image

感謝 & 參考

本文內容仍是相對很淺的,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的基本概念

image

image

Docker的虛擬化是在系統層面實現的,虛擬機則是在硬件方面實現的。linux

鏡像

Docker Images 是一個可執行的包。包含了運行應用程序的全部內容,代碼,運行時,環境變量,庫,配置文件。nginx

鏡像的構成

鏡像的構建是一層層構建的,前一層是後一層的基礎。每一層構建完成後,不會再改變。後面的修改的只會發生當前的鏡像層。好比刪除前一層的文件,並非真正的刪除。而是在後面的鏡像層中標記爲刪除,刪除的文件會一直存在鏡像中。git

分層的特性使得鏡像容易擴展和複用。好比在Docker Hub上提供的各類基礎鏡像。github

image

commit

咱們在上面說過鏡像是分層的。咱們這裏利用commit命令深刻理解下鏡像的構成。web

咱們使用docker run --name webserver -d -p 4880:80 nginx構建nginx的容器。使用exec進入webserver容器,並進行了必定的修改。而docker commit命令能夠將咱們對容器存儲層的修改保存下來,成爲新的鏡像。新的鏡像由原有的鏡像,加上咱們更改的存儲層構成的。redis

容器

Docker Containers 是鏡像運行的實例。可使用docker ps查看正在運行的容器列表。容器一樣也是多層存儲,以鏡像做爲基礎層,在基礎層上加一層容器運行的存儲層。mongodb

Docker安裝

卸載舊版本的Docker

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

image

# 容許hello-world鏡像,驗證是否正確安裝
docker run hello-world

容器

在過去若是要編寫Python應用程序,須要在機器上安裝Python運行時,不只僅須要在你的開發機器配置環境,並且還須要在生產環境的機器上配置環境。若是使用Docker,能夠把Python運行時經過鏡像獲取,無需在不一樣的機器上重複安裝環境。能夠在應用程序,和Python運行時鏡像打包在一塊兒。確保在不一樣的機器上均可以正常運行。這些可移植的鏡像,由Dockerfile定義

Dockerfile

Dockerfile定義了容器內的環境。容器與系統的其餘部分相隔離,所以須要將容器的端口映射到外部。由這個Dockerfile定義的應用程序的構建,運行在任何地方的行爲都徹底相同。

示例

# 建立空文件夾,並在文件夾中建立Dockerfile文件
mkdir learn-docker
cd learn-docker
touch Dockerfile
touch app.js

Dockerfile

# 在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"]

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

image

運行應用程序

# 將服務器的4000端口映射到容器的80端口
docker run -p 3999:80 hellodocker

# 查看正在運行的容器
docker container ls

# curl測試,返回helloworld
curl 0.0.0.0:3999

image

image

Dockerfile指令詳解

🌟FROM

FROM指令用於指定鏡像的基礎鏡像。FROM scratch,能夠指定空的基礎鏡像。

🌟RUN

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

COPY指令將當前目錄的文件,複製到image中。

源路徑指的是當前上下文的目錄。目標路徑能夠是容器內的絕對路徑路徑,也能夠是容器WORKDIR指定的工做目錄的相對路徑。

COPY [源路徑] [目標路徑]

CMD

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指令能夠指定某個目錄爲匿名卷,任何對該目錄的寫操做,不會記錄到容器的存儲層。

對於數據庫,數據庫文件應保存到數據卷中

VOLUME /data

ENV

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指令用於聲明端口,可是EXPOSE聲明的端口要和docker run <宿主端口>:<容器端口>區分。EXPOSE指令僅僅是聲明,而不會自動進行端口映射。

WORKDIR

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?

DockerHub相似於Github,由Docker官方維護的一個公共容器鏡像倉庫。咱們首先註冊,並登陸Docker Hub

建立存儲庫

image

標記鏡像

# 登陸
docker login

# 標記鏡像
# docker tag [鏡像名] [用戶名]/[存儲庫]:[標籤]
docker tag hellodocker zhangyue9467/learn-docker:test

image

發佈鏡像

docker push zhangyue9467/learn-docker:test

Docker Hub倉庫中就會有咱們發佈的鏡像

image

從DockerHub拉取並運行鏡像

使用Docker後,咱們不須要在其餘機器上安裝任何東西,就能夠運行它。只須要遠程拉取Docker的鏡像

docker run -p 3998:80 zhangyue9467/learn-docker:test

數據卷

什麼是數據卷?

數據卷是一個可供一個或多個容器使用的特殊目錄, 數據卷中的數據能夠容器之間共享和重用。對數據卷的修改會立馬生效。

建立數據卷

# 建立一個名爲vol的數據卷
docker volume create vol

# 查看數據卷中的信息
docker volume inspect vol

image

Mountpoint中是數據卷掛載在宿主機的位置。咱們在Mountpoint字段對應的文件夾內建立一個文件

image

啓動掛載了數據卷的容器

使用--mount,在啓動容器時掛載數據卷,容器啓動時能夠掛載多個數據卷。

# 啓動了name爲web的容器
# 使用vol數據卷,加載到容器的/webapp中

docker run -d -P \
    --name web \
    --mount source=vol,target=/webapp \
    hello

image

進入web容器進行查看,vol數據卷中內容掛載到容器的/webapp目錄中

image

掛載宿主機目錄做爲數據卷

宿主機的路徑必須是絕對路徑,使用--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

image

掛載本地文件做爲數據卷

# /root/.bash_history 做爲卷

docker run -d -P \
    --name web3 \
    --mount type=bind,source=/root/.bash_history,target=/root/.bash_history \
    hello

image

在容器內部能夠獲取外部的命令行的歷史記錄

網絡

外部訪問容器

# 映射任意端口到容器的端口
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] [容器的端口]

image

容器內部擁有自身的網絡和ip,可使用docker inspect命令在"NetworkSettings"中獲取。

# 查看容器內部的ip信息
docker inspect [容器id]

image

容器通訊

使用自定義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

image

Docker Compose

什麼是Compose?

使用Dockerfile文件能夠很方便定義一個容器。但在平常的工做中一個項目可能須要多個容器(前端,後端,數據庫)。Compose容許用戶定義docker-compose.yml模版文件,來定義一組相關聯的容器爲一組項目。

Compose中兩個概念:

  • 服務(service),一個應用的容器,能夠是多個相同鏡像的實例。
  • 項目(project),一組關聯的應用容器組成的完整業務單元, 在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命令

更多命令請參考

💡在介紹Compose命令以前,我以爲有必要明確一下,服務與容器的概念。我曾經混淆過它們的概念,詳細的解答請參考

在docker-compose中,docker-compose.ymal中定義的是服務, 下面定義了一個名爲web的服務。而web的服務會啓動一個名爲"[項目文件的名稱]_web"的容器。

# docker-compose.ymal

version: '3'
services:
  web:
    build: .
    ports:
     - "5000:3000"

build

在項目的目錄根目錄下運行build命令,構建鏡像

# 構建容器
docker-compose build

image

ps

在項目的根目錄下運行ps命令,列出項目中的全部容器

docker-compose ps

image

up

up命令將會完成構建容器,建立服務,啓動服務,等一系列操做。能夠直接經過up命令啓動一個項目

# 在前臺啓動容器
docker-compose up

# 在後臺啓動並運行項目(不須要強制退出控制檯了)
docker-compose up -d

port

查看服務映射在宿主機上的端口

version: '3'
services:
  web:
    build: .
    ports:
     - "5000:3000"
# 示例
# 0.0.0.0:5000
docker-compose port web[服務] 3000[容器端口]

Compose模板文件

更多指令請參考

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"

實戰

Docker部署前端應用

image

新建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代理,訪問容器。

image
(咱們將轉發請求到容器映射的接口上)

Docker部署Node服務

image

前端的部署同以前的項目一致(這裏略過)。使用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"]

image

Docker部署Mongo

咱們直接使用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'

DockerCompose部署前端,後端,數據庫

👻 這裏尚未作

相關文章
相關標籤/搜索