docker系列之入门篇

timg3254362.jpeg

0. 前言

docker是什么?
docker是用GO语言开发的应用容器引擎,基于容器化,沙箱机制的应用部署技术。可适用于自动化测试、打包,持续集成和发布应用程序等场景。

值得注意的是,docker现已改名为moby。

docker基于容器化,沙箱机制,可使你用较少的命令和脚本快速部署应用。一次构建,多处移植使用。再配合shell等脚本语言,可实现脚本化一键部署。

另外,docker大部分的工作都是依赖命令来执行的,简单易上手。

如火如荼的docker,现已被很多大公司所采用。同时docker也成为了实现serverless(无服务器架构)服务的基础架构。包括阿里云,亚马逊在内的云计算服务商都采用了docker来打造serverless服务平台。

如此同时,基于docker的微服务架构也如火如荼地出现。

由此可见,docker容器技术的重要性非同一般。

一,docker的运作

docker是沙箱机制,容器化部署技术,它主要作用在于通过运行容器来实现应用部署,而容器基于镜像运行。

简单地说,就是将你的项目和依赖包(基础镜像)打成一个带有启动指令的项目镜像,然后在服务器创建一个容器,让镜像在容器内运行,从而实现项目的部署。

服务器就是容器的宿主机,docker容器与宿主机之间是相互隔离的。

它不仅仅可以部署项目,还可以用于数据库搭建,nginx服务搭建,nodejs、php等编程语言环境搭建。

二,docker的三个概念

要学习docker,首先要理解它的三个概念:镜像(Image)、容器(Container)、仓库(Repository)。同时你还需要知道它们之间的相互关系。

为了方便理解,下面用通俗的话来描述它们。

  • 镜像
    docker镜像是使用Dockerfile脚本,将你的应用以及应用的依赖包构建而成的一个应用包,它通常带有该应用的启动命令。而这些命令会在容器启动时被执行,也就是说你的应用在启动容器时被启动。

镜像的创建,需要通过配置Dockerfile脚本,然后执行docker build命令来创建。

举个例子:

 docker build -t mydocker/node-server:v1 .

简单解释下:
后面的 “.” 表示当前目录下。
当执行docker build命令时,docker会寻找当前目录下的Dockerfile文件,
并根据Dockerfile脚本文件里的指令创建docker镜像

关于镜像的知识后面会做详细讲解。

  • 容器
    容器是使用 docker run --name 容器名 镜像 命令创建的,独立于宿主机(服务器)的沙箱,也可以理解为一个带有特殊结构的盒子,它在创建时会自动执行镜像自带的一些指令,从而实现该应用的运行。
    狭隘地讲,容器的主要作用就在于给镜像提供运行空间和环境,并执行镜像的指令。

  • 仓库
    顾名思义,仓库是用来存东西的,但不是存容器,而是存储docker镜像。你可以把你的docker镜像通过push命令推送到docker仓库,然后就可以在任何能使用docker命令的地方通过pull命令把这个镜像拉取下来。

三,docker镜像,容器,仓库三者关系

使用docker前,你必须先安装它。

docker的使用方式主要有docker命令,Dockerfile脚本,以及shell脚本三种。
换句话说,你可以把docker当做命令行工具来使用。

有一点上面已经讲解过,docker是沙箱机制,容器化部署技术,它主要作用在于通过运行容器来实现应用部署,而容器则基于镜像运行。

因此,首先要有镜像,有了镜像才能基于镜像创建容器,才能把镜像存储在仓库。

于是可以这么理解,镜像是基础,容器是镜像使用者,仓库是镜像的管理员。容器和仓库都是围绕着镜像来运作的,是对镜像的管理和使用。

另外,镜像,容器,仓库都分别有一套自己的docker命令,用于前期的构建以及后期的维护。

四,docker基本开发流程

基本的开发流程如下:

  • 1,寻找基础镜像
  • 2,基于基础镜像编写Dockerfile脚本
  • 3,根据Dockerfile脚本创建项目镜像
  • 4,将创建的镜像推送到docker仓库 (根据自身需要,可做可不做)
  • 5,基于项目镜像创建并运行docker容器 (实现最终部署)

这个流程通常只开发一次,后期只做简单的维护就好。
这是个基本流程,为什么是基本呢 ? 因为其中会穿插着很多细节,这些细节对后期的维护极为重要,比如:

  • 如何写shell脚本来执行docker命令?
  • 如何使用shell脚本+docker命令实现一键部署?
  • 如何规划测试环境脚本?
  • 如何规划正式环境脚本
    ……

另外,有没有发现一点,基本上所有流程都与镜像有关。

五, Dockerfile脚本

经过上面的讲解你应该了解到,基本上大部分的工作都是围绕着镜像来做的。
所以,我们首先来学习docker镜像。

而创建镜像,首先得配置Dockerfile脚本。镜像是基于Dockerfile脚本来构建的。

1,基础镜像

在深入探讨镜像的创建、使用和管理之前,我们先来理解一个概念——基础镜像。

docker有这么一种机制,在构建镜像时,它可以依赖一个父镜像作为底层镜像,与当前正要被构建的镜像一起打包,从而构建成一个全新的镜像。而这个被用作依赖的父镜像,就是基础镜像。

为什么有这样的需要呢?

比如,通常我们开发一个nodejs应用,它不是随处可运行的,它的运行需要依赖操作系统环境和nodejs运行环境。

因此,一个单纯的node项目镜像是无法运行起来的,它需要依赖一个基础镜像,这个基础镜像就是nodejs镜像,nodejs镜像内包含了操作系统环境和nodejs环境。这样nodejs镜像+node项目构建出来的项目镜像才能完整地运行起来。

因此,在配置项目Dockerfile创建镜像脚本之前,需要先确立一个基础镜像。
基础镜像是如何使用的呢?主要是通过在Dockerfile脚本中使用From命令指定依赖一个父镜像(基础镜像)。

  • 基础镜像通常不需要自己创建,docker官方有提供很多基础镜像供用户使用,比如nodejs镜像,java镜像,mongodb镜像,nginx镜像等等。
  • 除了官方提供的,你还可以寻找第三方源提供的基础镜像。

关于Dockerfile,下面会仔细讲解。

2,Dockerfile脚本构成

docker镜像是通过配置Dockerfile脚本,然后执行docker build命令来创建的。
Dockerfile文件可通过nano Dockerfile命令或touch Dockerfile命令来创建。
Dockerfile脚本文件通常放在项目的根目录下。也可以放在其他文件夹,但执行docker build时需要指定该文件路径。


(1)Dockerfile脚本语法和结构

Dockerfile语法由两部分构成,分别是 注释指令+参数
使用 “#” 可实现注释。

Dockerfile脚本通常分为以下四部分:

  • 基础镜像信息
  • 维护者信息
  • 镜像操作指令
  • 容器启动时执行的指令

(2)Dockerfile脚本案例

下面直接来看个例子说明,这是我的开源博客后台sinn-server的Dockerfile脚本。
如下:

# 基础镜像信息
From registry.cn-hangzhou.aliyuncs.com/sessionboy/node:7.5

# 维护者信息
MAINTAINER sessionboy <postmaster@boyagirl.com>

# 镜像操作指令
COPY ./ /sinn-server
WORKDIR /sinn-server
RUN npm install
EXPOSE 8080

# 容器启动时执行的指令
ENTRYPOINT ["node","bin/run"]
  
(3)Dockerfile语法解剖

Dockerfile有十几条命令可用于创建镜像,下面根据四个不同功能模块来介绍这些命令。

A—基础镜像信息

基础镜像信息,只有一个From指令,指定依赖的基础镜像

  • From
    指定依赖的基础镜像,如果不存在就会从docker官方仓库Docker Hub寻找
 # docker官方镜像仓库
 # 指定docker官方仓库的最新版nodejs镜像作为基础镜像
 From node:latest     
 更多配置……
 # 第三方镜像仓库,比如阿里云的 “https://dev.aliyun.com/search.html ”
 # 指定标签为7.5的"registry.cn-hangzhou.aliyuncs.com/sessionboy/node"镜像作为基础镜像
 From registry.cn-hangzhou.aliyuncs.com/sessionboy/node:7.5   
 更多配置……
B—维护者信息

维护者信息也只有一个MAINTAINER指令,用于描述该镜像的维护者信息

  • MAINTAINER
# MAINTAINER指令
MAINTAINER sessionboy <postmaster@boyagirl.com>
C—镜像操作指令

镜像的操作指令有很多个,下面逐一讲解。

  • COPY
    COPY指令用于拷贝宿主机的源目录/文件到容器内的某个目录。接受两个参数,源目录路径和容器内目录路径。
# 将 “./”(当前目录) 下的文件拷贝到容器内的 “/sinn-server” 目录
COPY ./ /sinn-server
  • ADD
    功能和用法与COPY指令基本相同,不同在于使用ADD指令拷贝时,如果拷贝的是压缩文件,拷贝到容器中时会自动解压为目录。
# 将当前/server目录拷贝到容器内的/projects目录
ADD /server  /project
  • WORKDIR
    指定RUN、CMD、ENTRYPOINT 指令的工作目录

  • RUN
    RUN是核心指令,它接受命令作为参数并用于创建镜像。命令较多时可用""换行。

……
RUN ["/bin/bash", "-c", "echo hello"]
RUN rm ./tmp \
       npm install
……
  • USER
    USER命令用于设置运行容器的UID。
# 指定容器的UID为23541
USER  23541
  • VOLUME
    VOLUME命令用于让你的容器访问宿主机上的目录。一般用来存放数据库和需要保持的数据等
# 指定容器可访问宿主机(服务器)的“/data”目录和“/home”目录
VOLUME ["/data",'"/home"]
  • ONBUILD
    配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。

  • ENV
    ENV命令用于设置环境变量,在容器内被脚本或者程序调用。

# 设置环境变量 NODE_ENV 为 production
ENV NODE_ENV  production
  • EXPOSE
    EXPOSE用来指定端口,使容器内的应用可以通过端口和外界交互。
# 对外暴露8080端口
EXPOSE 8080
D—容器启动时执行的指令
  • CMD
    指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令,可被 docker run 提供的参数覆盖。
# 三种方式
CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式
CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用    
 CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;
  • ENTRYPOINT
    容器启动后执行的命令,与CMD命令不同的是,它不会被 docker run 提供的参数覆盖。
# 两种方式
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2(shell中执行)。

(4),案例分析

经过上面对Dockerfile脚本的解剖,相信你已对Dockerfile有了实际的了解。
我们接着上面讲过的例子,也就是我的开源博客后台sinn-server的Dockerfile脚本。
如下:

# 使用From指令指定基础镜像为registry.cn-hangzhou.aliyuncs.com/sessionboy/node7.5
From registry.cn-hangzhou.aliyuncs.com/sessionboy/node:7.5
# 使用MAINTAINER指令描述维护者信息
MAINTAINER sessionboy https://github.com/sessionboy/sinn-server
# 镜像操作指令
COPY ./ /sinn-server   // 把“./”(当前目录)下的所有文件拷贝到容器内的“/sinn-server”目录
WORKDIR /sinn-server   // 指定RUN、ENTRYPOINT、CMD指令的工作目录(容器内)
RUN npm install    // 使用RUN指令,指定执行"npm install"命令
EXPOSE 8080        // 对容器外暴露8080端口
# 容器启动时执行的指令
ENTRYPOINT ["node","bin/run"]  // 指定容器启动时执行“node bin/run”命令,启动node应用

六,docker镜像的创建、使用和管理

为了方便操作,docker提供了一套镜像的操作命令。

1,创建镜像

镜像是基于Dockerfile脚本,使用docker build命令创建的。上面已经对Dockerfile做了详细解剖。下面来说说如何使用Dockerfile脚本创建。
语法:

 docker build [OPTIONS] PATH

OPTIONS为对容器的配置项,内容有点多,可以到这里查看。一般不建议配置,使用默认的就好。
PATH为Dockerfile脚本所在的目录,通常放在项目根目录下,使用"."表示。
-t 标记,用来添加tag,指定新的镜像信息,比如镜像名称,标签。
下面,以sinn-server的脚本为例:

# 使用当前目录的Dockerfile脚本创建名为“registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn”的镜像,并标记为v1(标签,也可以理解为版本号)
docker build -t registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1 .

# 指定使用github.com/creack/docker-firefox的Dockerfile脚本创建镜像
docker build github.com/creack/docker-firefox

为了方便操作,我们在根目录下建立一个名为build.sh的shell脚本来执行这些命令。如下:

# build.sh
docker build -t registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1 .

下面是执行过程:

docker镜像构建过程1.png

docker镜像构建过程2.png

看到Successfully,说明镜像已经创建成功了。
使用命令docker images即可查看你本地所有的docker镜像。

2,本地镜像管理

命令如下:

  • 1,查看镜像:docker images
# 列出本地所有镜像
docker images
  • 2,删除镜像:docker rmi 镜像
# 删除镜像
docker rmi  registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn
  • 3,镜像重命名:docker tag 原镜像tag 新镜像tag
# 将镜像registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1重新标记为newImages:0.1
docker tag registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1  newImages:0.1
  • 4,查看镜像创建历史:docker history 镜像
# 查看镜像registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn的创建历史
docker history  registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn
  • 5,镜像归档:docker save 镜像
# 将镜像registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn保存成 sinn_v1.tar 归档文件
docker save -o sinn_v1.tar registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn
  • 6,基于归档文件创建镜像:docker import 归档文件 新镜像tag
docker import sinn_v1.tar new_sinn:v2

3,远程仓库镜像管理

命令如下:

  • 1,登录远程镜像仓库: docker login 用户信息 仓库地址
    如果未指定镜像仓库地址,则默认为官方仓库 Docker Hub
# 登录到官方Docker Hub仓库
docker login -u 用户名 -p 密码
# 登录到阿里云docker仓库
docker login --username=用户名  registry.cn-hangzhou.aliyuncs.com
  • 2,拉取镜像:docker pull 镜像
docker pull registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1
  • 3,推送镜像: docker push 镜像
docker push registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1
  • 4,搜索镜像: docker search 镜像名
# 从官方Docker Hub仓库搜索node镜像
docker search node

4,镜像的使用

镜像通过docker run命令使用的,这是关于容器部分的操作。

# 指定使用registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1镜像创建并启动容器
docker run --name  sinn-server -p 8080:8080 registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1

七,容器的创建、启动和管理

创建容器的前提是要有镜像,有了镜像之后我们就可以创建并启动容器了。

1,容器的创建和启动

通过docker run命令即可实现容器的创建,并也会启动容器,这个命令实际上就包含了创建和启动容器两部分工作。
因此,容器的创建和启动是一体的。

下面就来说说docker run命令的用法。


** docker run命令的用法 **

docker run命令如下:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
或
docker run 容器配置项 镜像 额外的配置

OPTIONS是一些给容器添加的配置项,比如指定容器的名称,IP,是否后台启动等等。
IMAGE则表示镜像。COMMAND则是一些额外的命令。

OPTIONS具体配置项如下:

# 摘自w3c菜鸟: http://www.runoob.com/docker/docker-run-command.html
-a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
-d: 后台运行容器,并返回容器ID。也可以理解为守护进程运行。
-i: 以交互模式运行容器,通常与 -t 同时使用;
-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
--name="nginx-lb": 为容器指定一个名称;
--dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;
--dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;
-h "mars": 指定容器的hostname;
-e username="ritchie": 设置环境变量;
--env-file=[]: 从指定文件读入环境变量;
--cpuset="0-2" or --cpuset="0,1,2": 绑定容器到指定CPU运行;
-m :设置容器使用内存最大值;
--net="bridge": 指定容器的网络连接类型,支持 bridge/host/none/container四种类型;
--link=[]: 添加链接到另一个容器;
--expose=[]: 开放一个端口或一组端口;
--restart=no/on-failure/always  是否开机自动启动 ,no表示不启动,always表示始终启动,on-failure表示容器推出状态非0时重启
-v  标记来创建一个数据卷并挂载到容器里,实现宿主机目录或文件与指定的容器内目录或文件同步映射。

注意: 有些是一个扛"-",有些是两个扛"--"

这么多配置项,除非特别需要,否则不必全都配置。但有些是经常使用或者必须的。比如: --name -d -e -v, 这些是常用的配置项,需要熟悉它们。

下面来举个例子,为了方便操作,我们通常把命令写在一个shell脚本里面,这样只需要执行shell脚本即可,不需要每次都手动输入。

以我的开源博客后台sinn-server的容器启动脚本release-sinn.sh为例:

# release-sinn.sh

docker run --name sinn-server -p 8888:8888 -d -e 'NODE_ENV=production' --restart=always registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1

# --name sinn-server   指定容器名为sinn-server
# -p 8888:8888   指定宿主机8888端口与容器内的8888端口形成映射
# -d   守护进程运行
# -e 'NODE_ENV=production'  设置环境变量NODE_ENV为production
# --restart=always 开机自启动
# registry.cn-hangzhou.aliyuncs.com/sessionboy/sinn:v1   指定的镜像

只需要执行release-sinn.sh脚本即可执行上面的命令,如下:

$ sh release-sinn.sh

以下是执行过程,如图:

容器的启动.png

这样我们的容器就启动成功了。使用docker psdocker ps -a命令即可查看所有正在运行的容器。

下面来讲讲容器的管理。

2,容器的管理

docker提供了很多关于容器的操作命令,包括删除容器,停止、重启容器等等。
下面列出一些经常使用的用于操作容器的命令:

  • 1,查看容器: docker ps
# 列出所有正在运行的容器
docker ps
  • 2,启动已停止的容器: docker start
# 启动一个或多少已经被停止的容器
docker start  [options]  [containers] 
  • 3,停止容器:docker stop
# 停止正在运行的容器 sinn-server
docker stop sinn-server
  • 4,重启容器:docker restart
# 重启正在运行的容器 sinn-server
docker restart  sinn-server
  • 5,删除容器: docker rm
#删除容器 sinn-server
docker rm  sinn-server

注意:删除容器命令是docker rm ,删除镜像命令是docker rmi

  • 6,杀掉正在运行的容器(包括进程): docker kill
# 杀掉容器 sinn-server
docker kill sinn-server
  • 7,进入容器: docker exec
    这个命令比较重要,因为通常我们需要进入容器内去操作一些东西。
docker exec -it 容器名称/ID  终端
# 例子: 进入ID为0d15561b9f10的容器
docker exec -it sinn-server bash
或
docker exec -it 0d15561b9f10 /bin/bash

注意: 进入容器后,如果想退出容器,只需要输入exit命令执行即可。

  • 8,查看容器日志:docker logs
# 查看容器sinn-server的日志
docker logs sinn-server
  • 9,容器与主机之间的数据拷贝:docker cp
    往容器里拷贝数据,或从容器内拷贝数据出来,偶尔会常用到
# 将主机的/data/user目录拷贝到容器sinn-server内的/data/user目录
docker cp /data/user sinn-server:/data/user
# 将容器sinn-server内的/data/user目录拷贝到主机的/data/user目录
docker cp sinn-server:/data/user /data/user

以上是关于容器操作的大部分内容。

八,使用docker搭建数据库

以往我们搭建数据库或nginx服务的时候,通常都需要手动去安装,然后做大量的配置。期间也经常会遇到各种各样的问题。

下面来介绍,如何在linux服务器用docker来快速搭建一个mongodb数据库。

1, 首先拉取mongodb镜像

# 从docker官方仓库拉取mongo镜像(你也可以用第三方的mongo镜像,或自己创建的mongo镜像)
docker pull mongo

2,启动mongo容器

建立shell脚本"my-mongo.sh"来运行mongodb容器启动命令

# my-mongo.sh
# 使用镜像"mongo"创建并启动容器"my-mongo"
docker run --name my-mongo -d -p 27017:27017 -v data/sinn-db:/data/db mongo
# -p 27017:27017    容器内27017端口与主机27017端口形成映射
# -d     守护进程运行
# --name my-mongo    指定容器名为my-mongo
# -v data/sinn-db:/data/db   将主机的"/data/sinn-db"目录挂载到容器内"/data/db"目录,作为mongodb数据存储目录

执行my-mongo.sh脚本即可:

 sh my-mongo.sh

容器启动成功后,mongodb已经启动。访问你IP的27017端口看到下面的提示,说明mongodb已经搭建成功。

It looks like you are trying to access MongoDB over HTTP on the native driver port.

至此,一个mongodb数据库就搭建好了,是不是很简单?

同样,使用docker搭建nginx也十分简单,只需要拉取nginx镜像,然后基于nginx镜像启动容器,启动时加一些nginx配置或指定nginx配置文件即可。

九,总结

docker很强大,除了部署项目外,还可用于搭建数据库,nginx服务等。它的用途十分广泛而强大,另外它的性能消耗也很低。操作也十分简单。

docker很容易学,它主要基于命令来操作,熟知这些命令以及Dockerfile脚本的配置,很快就能上手。

相信学会了docker之后,你会爱不释手。

本文的部分脚本,可参考我的开源博客后台sinn-server的docker脚本配置。

推荐阅读更多精彩内容

  • Docker — 云时代的程序分发方式 要说最近一年云计算业界有什么大事件?Google Compute Engi...
    ahohoho阅读 14,245评论 15 147
  • 转载自 http://blog.opskumu.com/docker.html 一、Docker 简介 Docke...
    极客圈阅读 9,722评论 0 122
  • docker基本概念 1. Image Definition 镜像 Image 就是一堆只读层 read-only...
    慢清尘阅读 6,881评论 1 19
  • 我的家庭条件一般,甚至可以说是不好,尤其是今年。爸爸辞职后以帮助其他包工头联系活路为业,紧紧张张的支撑着这个家...
    smile爱晴天阅读 163评论 0 0
  • 古风 叹黄秋园 温志龄 基层美协拒才俊,艺高招妒默无闻。 生前画作多珍宝,逝后七载始爆冷。 自幼习绘从巨擘,酷...
    碧野牧歌阅读 103评论 0 0