前言
区块链技术因为比特币的疯狂而被世人所熟知,最近我司也要准备上区块链项目了。不过在区块链的技术选型上,我与同事产生了分歧。我之前是一个nodejs主义的捍卫者,本来计划上一个基于lisk的区块链项目,不过,在与同事们的交谈中,我了解到了一个新的区块链架构,hyperledge,区块链项目名为fabric。hyperledge fabric用golang开发,基于docker部署。为了能快速深入学习整个框架,我决定开始做技术积累,并写下我积累过程中的技术博客。本文是整个技术博客的开篇文章,我准备从docker入手,一点一点的分析、研究hyperledge这个区块链项目。鉴于对市面上相关技术类中文博客的了解,我打包票,接下来我将写一篇非常棒的关于docker的入门文章。我相信,只要掌握了这篇文章所讲的知识,对于大多数想要学习docker的人就足够了,特别是初入职场的开发们,读完这篇文章,就会让你成为半个docker专家。That's right,大家准备好了吗?让我们开始吧。
痛点
上初中那会,第一次安装win或者linux的时候,还真的是费了一番功夫。各种配置、各种驱动程序,麻烦的要死。当时我就在想,要是能有个一键安装的工具就好了。win、linux安装麻烦,估计也是大家的痛点,于是,歪果仁们发明了虚拟化技术,你可以拷贝着你自己的镜像到处使用,只需要挂载好你的镜像即可。再后来,有一个工具更加方便,都不需要你自己带着镜像了,直接通过仓库下载镜像,然后本地导入就能使用了,这个工具就是docker。
docekr的理念与早期的java很像,Build,Ship,and Run Any App,Anywhere。这条标语道出了只做一次的思想,这种只做一次的思想也是IT行业的共识。在云技术大行其道的今天,只做一次已经不单单是一种思想那么简单了,它早已成为IT行业的最佳实践。大家可以设想这样一个场景,云技术运维人员面对上万台服务器的运维场景,他们该如何运维呢?一台服务器、一台服务器的去做运维吗?肯定不会。他们会利用虚拟化技术,做好一个节点的运维工作,然后打包,并为其他同样的节点安装相同的镜像。docker是在众多虚拟化技术中脱颖而出的一款工具。利用docker的容器+虚拟化特性,可以进一步提高运维效率,大大的降低运维成本和难度。
在还没有云技术的年代,其实类似“云”的概念早已提出。人们利用vmware等工具,进行服务器的虚拟化工作,并取得了一定的成果。但是,这样的工具与操作系统内核的契合性不足,需要在操作系统上安装一个虚拟机系统,这样就造成了不必要的资源浪费。随着云技术的发展,硬件管理的问题逐步得到解决。但是,面对分布式、虚拟化、大数据的冲击,仅仅通过硬件升级和软件局部调优已经不能突破性能瓶颈了,这时,人们改变了DevOps思路,发明了docker这种与操作系统内核紧密联系的容器,最大限度的榨取了系统资源,从而突破了性能瓶颈,解决了一直以来困扰运维人员的技术难题。
从另外一个角度来说,在一台服务器上运行上百个虚拟机系统那简直是痴人说梦,但是,在一台服务器上运行上千个docker容器却已经成为现实。这也要归功于docker与操作系统的紧密联系。通过docker容器的这个特性,为运维工作带来无限可能。
docker
docker是由dotCloud开发的golang程序,是仅次于openstack的云计算开源项目,它的官网是docker.com,学习资料多半都来自于这个网站,github网站是github.com/docker,现在还有一个moby项目和dockerforge工具组件集。docker的主要目标是通过对应用组件的封装(packaging)、分发(distribution)、部署(deployment)、运行(runtime)等生命周期的管理,达到应用组件级别的一次封装到处运行。这里的应用组件,既可以是一个web应用,也可以是一整套数据库服务,甚至是一个操作系统或者编译器。docker引擎的基础是Linux容器(Linux Containers,LXC),这也是docker高效的原因所在。既然已经说了这么多容器,那么容器到底是什么呢?简单来说,容器是有效地将由单个操作系统管理的资源划分到孤立的组中,以便更好的在孤立的组之间平衡有冲突的资源使用需求的工具。与传统的虚拟化工具相比,docker容器(LXC)既不需要指令级模拟,也不需要即时编译,它可以在核心cpu本地运行指令,而不需要任何专门的解释机制。此外,也避免了准虚拟化(paravirtualization)和系统调用替换中的复杂性。
举个例子,例如使用LAMP来开发web app,需要配置大量的服务器资源,如果需要迁移,那么管理这些的成本都是空前庞大的。使用docker,可以通过容器对于已有的环境进行打包,迁移的话,只需要在新的服务器上启动需要的容器即可。这样开发和运维人员就可以只进行一次开发、配置,然后在任何服务器的docker中正常使用了。
docker特点归纳
特点 | 说明 |
---|---|
快速部署 | 开发人员首先在开发环境docker中快速构建开发环境,运维人员只需要在服务器安装好docker,就可以轻松实现在相同的环境下部署应用 |
高效利用资源 | 因为是基于linux内核的虚拟化工具 |
高适应性 | 在各种环境,如云,个人电脑等都可以安装 |
增量更新 | 使用dockerfile增量更新,配置小幅度修改即可 |
容器隔离 | 一台服务器可以起到n个容器 |
安全镜像 | 利用linux防护实现安全和镜像签名 |
docke安装
因为,我的服务器是CentOS因此,本处只做CentOS的安装介绍,其他的安装方法,请大家参考docker官方网站
在CentOS上安装docker
此处介绍Docker CE的安装方法,步骤如下:
步骤 | 行为 | 指令 |
---|---|---|
1 | 查看linux版本 | lsb_release -a |
2 | 创建和设置仓库 | |
3 | 安装docker | sudo yum -y install docker-ce |
4 | 启动docker | sudo systemctl start docker |
5 | 测试docker是否安装成功 | sudo docker run hello-world |
注意:创建和设置仓库的指令如下,方便大家直接复制粘贴
sudo yum install -y yum-utils sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo sudo yum makecache fast
docker核心概念
docker的核心概念是image、container和repository。下图展示了docker的核心概念以及彼此之间的关系。
docker的命令和参数
在深入学习docker之前,先来看看docker的命令和参数,看看docker都能干什么
命令
docker命令一般使用docker-cmd或者docker cmd的形式
cmd | 说明 |
---|---|
attach | 进入容器并读写容器内容,但是多个attach命令会同时操作一个容器 |
exec | 独立运行容器内shell |
build | 利用dockerfile创建镜像 |
commit | 利用容器构建镜像 |
create | 利用镜像创建一个不运行的容器 |
cp | 从容器中复制文件到宿主系统中 |
diff | 检查一个容器文件系统的修改 |
events | 从服务器端获取实时事件 |
export | 导出容器快照 |
history | 显示镜像历史 |
images | 展示镜像 |
ps | 展示容器 |
info | 显示相关系统信息 |
inspect | 显示容器详细的底层信息 |
login | 注册或登录到docker仓库服务器 |
logout | 从docker仓库服务器登出 |
logs | 获取容器的log信息 |
save | 备份本地容器,格式为tar |
load | 加载本地容器 |
pause | 暂停容器进程 |
unpause | 将进程从暂停状态恢复 |
port | 查找一个nat到一个私有网口的公共口 |
pull | 获取镜像或者仓库 |
push | 上传镜像或者仓库 |
restart | 重新启动容器 |
rm | 删除容器 |
rmi | 删除镜像 |
search | 搜索镜像 |
run | 创建并启动容器 |
start | 启动容器运行 |
stop | 停止容器运行 |
kill | 关闭运行的容器,包括进程和相关资源 |
tag | 修改镜像标签 |
top | 查看一个容器正在运行的进程信息 |
version | 展示docker版本信息 |
wait | 阻塞直到一个容器终止,然后输出它的退出符 |
参数
cmd | 说明 |
---|---|
-D=true orfalse | 使用debug模式,默认为false |
-H,--host=[unix:///var/run/docker.sock],tcp://[host:port] | 在daemon模式下绑定socket,通过host来指定地址 |
--api-enable-cors=true or false | 在远端api中启用cors头 |
-b="" | 将容器挂载到一个已经存在的网桥上 |
--bip="" | 让动态创建的docker0采用给定的CIDR地址与-b互斥 |
-d=true or false | 使用daemon模式 |
--dns="" | 为docker分配给定的dns |
-g="" | 指定docker运行时的root路径,默认为/var/lib/docker |
--icc=true or false | 启动容器间通信 |
--ip="" | 绑定端口时候的默认ip地址 |
--iptables=true or false | 禁止docker添加iptables规则 |
--mtu=VALUE | 指定容器网络的mtu,默认1500 |
-p="" | 指定daemon的pid文件路径,默认为/var/run/docker.pid |
--registry-mirror=:// | 指定一个注册服务器的镜像地址 |
-s="" | 强制docker运行时使用给定的存储驱动 |
-v=true or false | 输出版本信息 |
--selinux-enabled=true or false | 启用SELinux支持 |
下面我们就来看看这些命令和参数是如何应用在docker中的。
image
image是一个面向docker引擎的,包含了文件系统的只读模板,例如,包含完整ubuntu的ubuntu镜像,安装了apache的apache镜像等。下边,我们来看一下基于image的一系列操作:
docker+操作命令 | 说明 |
---|---|
pull | 获取镜像 |
run | 运行镜像 |
images | 查看镜像 |
tag | 变更标签 |
inspect | 查看镜像详细信息 |
search | 查找镜像 |
rmi | 删除镜像 |
ps -a | 查看本机运行的全部容器 |
commit | 创建镜像 |
save | 备份镜像,将会备份全部的镜像信息、历史记录、日志等,与export导出快照有很大区别 |
laod | 载入镜像 |
push | 上传镜像 |
下面我们结合着例子具体看一下操作命令应该如何使用:
获取镜像
镜像是docker运行容器的前提,在docker运行容器前,需要存在本地镜像,如果不存在,docker会先从用户配置的默认镜像仓库下载(如果没有设置的话,一般情况下是Docker Hub)
使用docker pull从网络上下载镜像格式为docker pull NAME[:TAG],如果不显示的制定TAG,则默认选择latest标签。
sudo docker pull ubuntu
sudo docker pull ubuntu:14.04
sudo docker pull dl.dockerpoll.com:5000/ubuntu
注意:下载过程中可以看出,镜像文件一般由若干层组成,行首的字符串代表了各层的ID。这些层是一个个的Layer,是AUFS(Advanced Union File System)中的重要概念,通过AUFS来实现增量保存和更新。
运行镜像
使用run就可以运行镜像了,前提也是镜像一定要存在,否则报错
sudo docker run -t -i ubuntu /bin/bash
sudo docker run ubuntu echo 'hello world'
参数说明
参数 | 说明 |
---|---|
-t | 为docker分配伪shell(pseudo-tty) |
-i | 打开容器的标准输入 |
-d | 后台守护进程,后边有详细说明 |
查看镜像
使用查看镜像命令可以列出本地主机上已经保存的镜像。
sudo docker images
表头 | 说明 |
---|---|
REPOSITORY | 镜像来源仓库 |
TAG | 版本标签 |
IMAGE ID | 唯一ID |
CREATED | 镜像在仓库中的创建时间 |
VIRTUAL SIZE | 镜像大小 |
变更标签
为了更好的管理标签,还可以使用标签变更命令
//格式
sudo docker tag image[:tag] [registry-host/][username/]image-name[:tag]
sudo docker tag ubuntu ubuntu:mytag
查看镜像详细信息
命令格式docker inspect [OPTIONS] NAME|ID [NAME|ID...]
sudo docker inspect ebcd9d4fca80
//如果想查看详细信息,可以使用下面这条指令
sudo docker inspect -f {{".Architecture"}} ebcd9d4fca80
查找镜像
通过search命令来查找存在于仓库中的镜像,该命令有几个参数,先来看一下参数
参数 | 说明 |
---|---|
--automated=false | 仅显示自动创建的镜像 |
--no-trunc=false | 输出信息不截断显示 |
-s,--stars=0 | 制定星级,显示设定星级以上的镜像 |
sudo docker search mysql
sudo docker search mysql --automated=false
sudo docker search mysql --no-trunc=false
sudo docker search mysql --filter=stars=0
删除镜像
通过标签或者ID来删除镜像
sudo docker rmi ubuntu:mytag
sudo docker rmi ubuntu:ebcd9d4fca80
注意,当某个镜像创造的容器存在时,是无法删除该镜像的,可以通过
sudo docker ps -a
来查看本机存在的全部容器。
如果想要删除这个镜像的话,可以使用-f
参数。sudo docker rmi -f ebcd9d4fca80
这样做,就会先删除容器,然后再删除镜像
注意:书中有一个sudo docker rm e81
,看不懂。p22/36
创建镜像
创建镜像有三种方法,基于已有镜像的容器创建、基于本地模板导入、基于Dockerfile创建
1.基于已有镜像的容器创建新的镜像
这个命令的格式为:sudo docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
参数包括:
参数 | 说明 |
---|---|
-a,--author="作者" | 添加作者信息 |
-m,--message="" | 提交信息 |
-p,--pause=0 | 提交时暂停容器运行 |
那么如何创建呢?按照下方的步骤走
1.先要有个镜像
2.用这个镜像做个容器
3.对这个容器镜像改变
4.使用commit命令创建新的镜像
//1.先要有个镜像
sudo docker pull ubuntu
sudo docker images
//2.用这个镜像做个容器,换句话说就是运行这个镜像,程序会自己建个容器
sudo docker run -ti ubuntu /bin/bash
//3.对这个容器做出改变,比如增加个文件啥滴,也可以试试不改变,看看会不会报错
touch test
ls
exit
sudo docker ps -a
sudo docker commit -m "a new image" -a "ryan" b156aa17c90c test
sudo docker images
注意,此处如果不小心执行了两次相同的命令,那么问题就来了,会产生箭头1的那个情况,出现<none>这个标签,箭头2是通过不同的镜像来覆盖,箭头3是不做任何改变通过镜像直接复制出一个新的镜像来
2.基于本地模板导入
直接通过操作系统的镜像文件来制作docker image,此处只做了解,不推荐使用,命令为
sudo cat ubuntu-14.04-x86.tar.gz |docker import - ubuntu:14
3.基于Dockerfile创建(重点)
前边两个是不太重要的镜像创建方法,因为,这样的镜像多半是别人创建好的,下边,我们来说一下用户自定义镜像创建方法,在这里,我们使用Dockerfile来快速创建。
Dockerfile是docker的脚本或者批处理脚本,需要你自己创建一个dockerfile文件,然后按照脚本语法要求写出镜像生成的流程语句。这个脚本包含了很多的预制指令,下面我们就来先说说这些指定
dockerfile的脚本指令
指令 | 说明 |
---|---|
form | 指定一个或者多个镜像来源 |
maintainer | 添加维护者信息 |
workdir | 为run、cmd、entrypoint等指令配置工作目录 |
run | 在默认shell下运行镜像,生成容器。用\换行 |
cmd | 指定启动时执行的命令格式 |
expose | 为容器设置暴露的端口 |
env | 设置环境变量 |
add | 将复制指定的<src>到容器中的<dest>中 |
copy | 将复制指定的<src>到容器中的<dest>中 |
entrypoint | 指定启动时执行的命令格式1,不被run参数覆盖 |
volume | 创建挂载点 |
user | 指定linux的运行用户或者uid |
onbuild | 配置当前所创建的镜像作为其他新创建镜像的基础镜像 |
from
第一条指令必须是from,如果在同一个dockerfile中创建多个镜像,可以使用多个from指令,格式为:
form image [:tage]
maintainer
指定维护者信息,格式为:
maintainer name
env
设置静态变量。格式为:
env key value
env god_v 99.0
run echo $god_v
run
在from指定的镜像上执行指令命令,并提交为新的镜像,格式为:
run comand
//or
run ["executable","param1","param2"...]
run ["/bin.bash","-c","echo hello"...]
cmd
如果没有在run中指定运行命令,那么就会执行dockerfile中的cmd命令。该命令有三种格式,但是美国dockerfile文档中只能有一条cmd,格式为:
cmd ["executable","param1","param2"...]
cmd "executable","param1","param2"...
cmd ["param1","param2"...]
entrypoint
除了不能被覆盖,其他与cmd功能一样。格式为:
entrypoint ["executable","param1","param2"...]
entrypoint command,"param1","param2"...
expose
告诉docker服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过-p,docker主机会自动分配一个端口转发到指定端口,使用-p,则可以具体指定哪个本地端口映射过来。格式为:
expose port [port...]
expose 22 80 8441
add
复制src到dest。格式为:
add src dest
copy
复制src到dest,如果不存在则自动创建。格式为:
copy src dest
volume
创建一个数据管理,为数据库或者其他数据文件服务。格式为:
volume ["/data"]
user
指定linux的运行用户或者uid。格式为:
user daemon
workdir
为run、cmd、entrypoint等指令配置工作目录。格式为:
workdir /path/to/workdir
workdir /a
workdir b
workdir c
run pwd
//show
/a/b/c
onbuild
配置当前所创建的镜像作为其他新创建镜像的基础镜像,使用onbuild构建的镜像,需要在镜像文件的尾部表明。格式为:
onbuild instruction
//可以使用如下内容先创建一个基础镜像
onbuild add ./app/src
onbuild run build --dir /app/src
//等价于
from 基础镜像
//自动执行
add ./app/src
run build --dir /app/src
构建dockerfile脚本
一般分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动时指令
#nginx
#
#version 0.0.1
from ubuntu
maintainer ryan
run apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
创建docker镜像
格式为:
sudo docker build [param...] path
//指定dockerfile路径为a,镜像标签为b
sudo docker build -t b a
执行dockerfile
执行完之后创建的cc镜像
备份镜像
将docker镜像备份到本地
sudo docker save -o ubuntu_14.04.tar ubuntu:14.04
载入镜像
从本地保存的镜像文件导入一个镜像
sudo docker load --input ubuntu_14.04.tar
//或者
sudo docker load < ubuntu_14.04.tar
上传镜像
将本地构建的镜像上传到默认的镜像仓库。(上传镜像这部分的演示,将会与后面的创建本地repository相结合)
sudo docker tag test:latest user/test:latest
sudo docker push user/test:latest
container
容器是镜像的一个运行实例,并提供可写文件层(时刻记住,镜像是只读模板)。docker的容器是直接提供应用服务的组件,也是docker实现快速的启动、停止以及提供高效服务性能的基础。
docker+操作命令 | 说明 |
---|---|
create | 新建容器 |
start | 启动容器 |
restart | 终止一个运行的容器,然后再启动它 |
run | 新建并启动容器,在镜像那一节,这条指令叫做运行镜像 |
logs | 查看日志 |
stop | 默认等待10秒后,停止容器运行 |
kill | 直接杀死容器进程,停止容器运行 |
attach | 同步进入容器,如果多个窗口进入的话,将会同步显示 |
exec | 独立进入容器,不同步容器信息 |
使用nsenter工具 | 类似于win桌面程序的一个shell,可以通过它连接到容器 |
rm | 删除容器 |
export | 导出容器快照,不保留历史信息,只记录导出时的容器状态 |
import | 导入容器快照 |
新建并启动容器
新建并启动容器有两种方法,一个是使用create + start,一个是直接使用run,换句或说,run与create + start是等价的。这个过程docker执行了查找镜像、创建容器、分配文件系统、为只读镜像挂载可写层、配置网桥接口桥接一个虚拟接口分配给容器、从地址池分配一个IP地址给容器、执行用户指定或者默认的应用程序、执行完毕后等待用户终止或继续操作容器。在进入容器的shell中,可以执行ls、ps、exit等命令。
sudo docker create -it ubuntu:latest
sudo docker ps -a
sudo docker start ubuntu:latest
//或者
sudo docker run ubuntu
docker守护进程
生产环境下,docker需要在linux守护进程(Daemonized)的状态下运行,通过增加-d参数来实现。
//-c参数是增加要执行的命令
sudo docker run -d ubuntu /bin/sh -c "while true;do echo hello world;sleep 1;done"
sudo docker ps -a
//logs可以是CONTAINER ID也可以是NAMES
sudo docker logs -f 3029eee9645e
终止容器
使用stop来终止容器,格式为sudo docker stop [-t|--time[=10]]
,这个命令会先向容器发送sigterm信号,等待一段时间后(默认为10秒),再发送sigkill信号终止容器。
sudo docker stop loving_mclean
还可以使用kill直接杀死进程,不过一般情况下,不要这样做。
进入容器
在使用-d参数启动后台进程时,可以通过attach(同步更新各个窗口的信息)、exec(不同步更新各个窗口的信息)、以及nsenter工具进入容器。
sudo docker run -idt ubuntu
sudo docker ps
//同步更新
sudo docker attach loving_mclean
//不同步更新
sudo docker exec loving_mclean
sudo docker exec -it loving_mclean /bin/sh
此处需要注意,使用exec要有两个参数
删除容器
使用rm来删除终止状态的容器,格式为sudo docker rm [options] container [container...]
参数 | 说明 |
---|---|
-f,--force=false | 强行终止并删除一个运行中的容器 |
-l,--link=false | 删除容器的连接,但保留容器 |
-v,--volumes=fasle | 删除容器挂载的数据卷 |
sudo docker rm hardcore_aryabhata keen_bhaskara
此处要注意,如果没有使用-f参数,不能删除没有停止的容器
导出容器快照
在容器运行或者停止的状态导出容器,格式为sudo docker export container > tag_tar_file
sudo docker export cocky_curie > cocky_curie.tar
导入容器快照
sudo docker import cocky_curie.tar
repository
registry是存放repository的具体服务器,每个registry上可以有多个repository,每个repository下面又会有多个image,例如前面我们看到的dl.dockerpool.com/ubuntu
,dl.dockerpool.com
就是registry的地址,ubuntu
就是repository名。一般情况下,一个repository内存放的是不同版本的同种image。repository还分公有和私有,之前的例子使用的都是公有repository。
repository又分为公共和私有两种,公共的有docker hub、docker pool等,用户可以将自己的镜像push自己的容器到repository,在用pull拉到这个镜像。下面我们来重点讲解一下docker hub和docker pool。
Docker Hub
docker官方的repository,可以执行docker login
进行注册和登录。注册成功后,本地用户目录的.dockercfg中将保持用户的认证信息。search、pull等操作不需要登录,push操作需要登录。
配置docker hub automated builds(自动创建)
automated build 使用户可以通过docker hub指定跟踪一个目标网站(如github、bitbucket等)上的项目,一旦项目发现新的提交,则自动执行创建。
配置自动创建的步骤如下:
1.创建并登陆docker hub,关联目标网站
2.在docker hub中配置一个automated builds
3.选取一个目标网站中的项目(需要包含dockerfile)和分支
4.指定dockerfile的位置,并提交创建
5.在docker hub的automated builds页面跟踪每次build的状态
Docker Pool
Docker Pool国内的docker社区,也提供镜像下载等服务,另外,该网站提供的镜像与官方镜像一致,可以作为墙后的备选方案。
创建私有repository并管理
执行命令sudo docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry
参数 | 说明 |
---|---|
-p | 监听端口 |
-v | 指定镜像存放的路径,不写该参数,镜像默认存放在/tmp/registry下,-v也是创建data volume的命令,可以创建多个data volumes |
这样本地repository就建立好了,之后可以通过其他服务器来建立上传下载功能了
container数据管理
container数据管理有两种形式:Data Volumes(数据卷)和Data Volume Containers(数据卷容器)
Data Volumes
Data Volumes是一个可供容器使用的特殊目录,它绕过文件系统,可以提供很多有用的特性,如:
特性 | 说明 |
---|---|
共享 | Data Volumes可以在容器间共享和重用 |
迅速 | 对Data Volumes的修改会瞬间生效 |
独立 | 更新不影响镜像,Data Volumes独立存在,持续供应容器使用 |
在容器中创建data volume 需要使用-v。多次使用-v可以创建多个数据卷。格式如下:
sudo docker run -d -P 8441 --name web -v /webapp training/webapp python app.py
Data Volume Containers
容器间共享数据,需要使用data volume containers
尾记
本文系统的讲解了docker怎么用,对于新手来说,算是非常好的入门文章了。关于docker如何用好的文章我会继续研究,继续分享的。感谢大家的阅读。
有疑问加站长微信联系(非本文作者)