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
# 镜像操作指令
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
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 .
下面是执行过程:
看到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
以下是执行过程,如图:
这样我们的容器就启动成功了。使用docker ps
或docker 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脚本配置。