前端的Docker入门与实践

感谢 & 参考

本文内容仍是相对很浅的,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'
复制代码
相关文章
相关标签/搜索