Featured image of post docker学习

docker学习

本文阅读量

即使在小的帆也能远航。

只要学不死,就往死里学!

1、Docker概述

一次封装,到处运行

是什么

解决了运行环境和配置问题的软件容器,方便做持久集成并有助于整体发布的容器虚拟化技术。

1.1 docker为什么会出现?

问题:我在我的电脑上可以运行!版本更新导致服务不可用!对运维来说,考验就十分大?

环境配置是十分的麻烦,每一个机器都要部署环境(集群Redis、ES、Hadoop…)费时费力。

发布一个项目(jar+(Redis Mysql jdk ES)),项目能不能都带上环境安装打包!

之前在服务器配置的应用环境配置超麻烦,不能跨平台。

之前: 开发程序,运维来发包

现在:开发打包部署上线,一套流程做完!

Docker给以上的问题,提出了解决方案!

1.2 docker的历史

2010年,几个搞it的年轻人,就在美国成立了一家公司dotCloud

做一些pass的云计算服务!LXC有关的容器技术!

他们将自己的技术(容器话技术)命名就是Docker!

Docker刚刚诞生的时候,没有引起行业的注意!

开源

2013年,docker开源!

docker原来越多的人发现了docker的优点!火了,docker每月都会更新一个版本!

2014年4月9日,docker1.0发布!

Docker为什么那么火?十分的轻巧!

在容器技术出来之前,我们都是使用虚拟机技术!

虚拟机也是属于虚拟化技术,docker容器技术,也是一种虚拟技术!

1
2
vm  linux centos只生成镜像(一个电脑!) 隔离,需要启动多个虚拟机! 几个g   几分钟
docker:隔离,镜像(最核心的环境4m)十分小巧,运行镜像就可以了!小巧! 几m  kb  秒级启动!

聊聊docker

docker是基于go语言开发的!开源项目!

官网:https://www.docker.com/

文档:https://docs.docker.com/

仓库地址:https://hub.docker.com/

1.3 docker能干什么

之前的虚拟机技术!

缺点:

1、资源占用十分多

2、冗余步骤多

3、启动很慢!

容器虚拟化技术!

比较docker和虚拟机技术的不同

  • 传统虚拟机,虚拟出一套硬件,运行一套完整的操作系统,然后在这个系统上安装和运行软件
  • 容器内的应用直接运行在宿主机的内容,容器是没有自己的内核的,也没有虚拟我们的硬件,所以就轻便了
  • 每个容器间是互相隔离的,每个容器内容部都有一个属于自己的文件系统,互不影响。

DevOps(开发、运维)

应用更快速的交付和部署

传统:一堆帮助文档,安装程序

Docker:打包镜像发布测试,一键运行。

更便捷的升级和扩缩容

使用了Docker之后,我们部署应用就和搭积木一样!

更简单的系统运维

在容器话之后,我们的开发,测试环境都是高度一致的。

更高效的计算资源利用

Dokcer是内核级别的虚拟化,可以在一个物理机上运行很多的容器实例!服务器的性能可以被压榨到极致。

2、docker安装

2.1 docker的基本组成

镜像(image):

docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,tomcat镜像 => run => tomcat01容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。

==镜像是分层的==

容器(container):

docker利用容器技术,独立运行一个或一个组应用,通过镜像来创建的。

启动,停止,删除,基本命令!

木器就可以把这个容器理解为就是一个简单的linux系统

仓库(repository):

仓库就是存放镜像的地方!

仓库和仓库注册服务器(registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)

仓库分为公有仓库和私有仓库!

Docker hub(默认是国外的)

阿里云。。。都有容器服务器(配置镜像加速!)

2.2 安裝docker

环境准备

1、需要会一点点的linxu的基础

docker 要求系统64位,系统内核版本为2.6.32-431或者更高版本

2、centos7

环境查看

1
2
3
4
5
6
#系统内核是 3.10以上的
uname -r
3.10.0-1062.12.1.3lz.x86_64

#系统版本
cat /etc/os-release

帮助文档

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1、卸载旧的版本

#2、需要的安装包
yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2
  
#3、设置镜像的仓库
yum-config-manager \
    --add-repo \
    http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
#更新yum软件的索引
yum makecache fast

#4、安装docker docker-ce 社区 ee企业版
yum install docker-ce docker-ce-cli containerd.io

#5、启动docker
systemctl start docker

#6、使用docker version 查看是否安装成功
docker version

#7、hello-word
docker run hello-world

#8、查看一下下载的这个hello-world
docker images

2.3 阿里云镜像加速

2.3.1 找到阿里云容器服务

2.3.2 找到镜像加速地址

2.3.3 配置使用

1
sudo mkdir -p /etc/docker

2.3.4 底层原理

Docker是怎么工作的?

docker是一个client-server结构的系统,Docker的守护进程运行在主机上。通过socker从客户端访问,守护进程从客户端接收命令并管理运行在主机上的容器。==容器是一个运行环境==

DockerServer接收到Docker-client的指令,就会执行这个命令!

docker为什么比vm块?

1、docker有着比虚拟机更少的抽象层。由于docker不需要hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在cpu、内存利用率上docker将会在效率上有明显优势。

2、docker利用的是宿主机的内核,vm需要Guest OS

当新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统内核,避免引寻,加载操作系统内核这个比较费时费资源的过程。虚拟机是加载Guest OS,分钟级别的,而docker是利用宿主机的操作系统,省略了加载Guest OS的过程,是秒级的。

docker vm
操作系统 与宿主机共享os 宿主机os上运行虚拟机os
存储大小 镜像小,便于存储与传输 镜像庞大(vmdk,vdi等)
运行性能 几乎无额外性能损失 操作系统额外的cpu、内存消耗
移植性 轻便、灵活,适应于linux 笨重,与虚拟机技术耦合度高
硬件亲和性 面向软件开发者 面向硬件运维者

3、docker常用命令

1
2
3
docker version #显示docker版本信息
docker info    #显示docker系统信息
docker --help  #帮助命令

帮助文档地址:

https://docs.docker.com/reference/

3.1 镜像命令

docker images

查看所有本地主机上的镜像

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
docker images
#REPOSITIORY  镜像的仓库源
#TAG          镜像的标签
#IMAGE ID     镜像的id
#CREATED      镜像的创建时间
#SIZE         镜像的大小

# 常用可选项
-a, --all      # 列出所有镜像
-q, --quiet    # 只显示镜像的id

搜索镜像

1
2
3
4
5
docker search mysql

# 常用可选项,通过搜索来过滤
--filter=STARS=3000      # 搜索出来的镜像就是STARS大于3000的
docker search mysql --filter=STARS=3000

docker pull

下载镜像

1
2
3
4
5
6
#下载最新版本
docker pull mysql
等价于
docker pull docker.io/library/mysql:latest
#下载指定版本
docker pull mysql:5.7

docker rmi

删除镜像

1
2
3
docker rmi -f IMAGE ID #删除指定的容器
docker rmi -f IMAGE ID IMAGE ID IMAGE ID #删除多个容器
docker rmi -f $(docker images -aq)  #删除全部容器

docker commit

提交容器副本使之成为一个新的镜像

1
# docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]

3.2 容器命令

说明:我们有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习

1
docker pull centos

新建容器并启动

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
docker run [可选参数] image

#参数说明
--name="Name"  容器名字 tomcat01 tomcat02,用来区分容器
-d             后台方式运行
-it            使用交互方式运行,进入容器查看内容
-p			   指定容器端口 -p 8080:8080
   -p ip:主机端口:容器端口
   -p 主机端口:容器端口(常用)
   -p 容器端口
-P              随机指定端口   

#启动并进入容器
docker run -it centos /bin/bash
docker run -it --name centos1153 centos /bin/bash

#从荣器退出主机
exit

列出所有运行的容器

1
2
3
4
5
6
7
8
# linux 
# ps -ef 列出当前linux系统正在运行的进程
#docker ps 命令
    #列出当前正在运行的容器
-a  #列出当前正在运行的容器+带出历史运行过的容器 
-n=?#显示最近创建的容器
-q  #只显示容器的编号
docker ps -n 2

退出容器

1
2
exit  #直接容器停止并退出
Ctrl + P + Q #容器不停止退出

删除容器

1
2
3
docker rm CONTAINER ID        #删除指定容器,不能删除正在运行的容器,需要结合-f进行删除
docker rm -f $(docker ps -aq) #删除所有容器
docker ps -a -q|xargs docker rm #删除所有的容器

启动和停止容器的操作

1
2
3
4
docker start 容器id
docker restart 容器id 
docker stop 容器id   
docker kill 容器id

3.3常用的其他命令

后台启动容器

1
2
3
4
5
6
7
8
9
# 命令 docker run -d 镜像名
docker run -d centos #停止了

#常见的坑:docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用就会自动停止

#nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就没有程序了

#可以使用以下方式保证不退出
docker run -d centos /bin/sh -c "while true;do echo hello zzyy;sleep 2;done"

查看日志

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
docker logs -tf --tail=100 CONTAINER ID

#自己编写一段shell脚本
docker run -d centos /bin/sh -c "while true;do echo snailsir;sleep 1;done"

#显示日志
-t            # 加入时间戳
-f            # 跟随最新的日志打印
--tail number #显示日志条数
docker logs -tf--tail 10  CONTAINER ID

查看容器中的进程信息

1
2
3
docker top CONTAINER ID

#PPID 进程id

查看容器的元数据

1
docker inspect CONTAINER ID

进入当前正在运行的容器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置

#方式1
docker exec -it CONTAINER ID baseShell

docker exec -id dce7bdld3d /bin/bash

#方式2
docker attach CONTAINER ID


#docker exec 与 docker attach区别
#docker exec 进入容器后开启一个新的终端,可以在里面操作(常用)
#docker attach 进入容器正在执行的终端(ctrl+p+q),不会启动新的进程

从容器内拷贝文件到主机上

1
2
3
4
5
docker cp 容器id:容器内路径 目的地主机路径

docker cp dcesde23dd2d:/home/test.java /home

#拷贝是一个手动过程,未来我们使用 -v 卷的技术,可以实现自动同步 

4、docker镜像

4.1是什么

是一种轻量级、可执行的独立软件包,==用来打包软件运行环境和基于运行环境开发的软件==,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。

unionFS (联合文件系统)

是一种分层、轻量级并且高性能的文件系统,它支持==对文件系统的修改作为一次提交来一层层的叠加==,同时可以将不同目录挂载到同一个虚拟文件系统下.union文件系统是docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

特性:

  • 一次加载多个文件系统,但从外面看来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

加载原理

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS。

UnionFS (boot file system)主要包含 bootloader和Kernel, bootloader主要是引导加载 kernel,Linux刚启动时会加载boot文件系统,在Docker镜像的最底层是 bootfs。这一层与我们典型的 Linux/Unⅸ系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由 boots转交给内核,此时系统也会卸载 boots。

rootfs( root file system),在bootfs之上,包含的就是典型 Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos等等。

为什么docker里的centos只有200m

对于一个精简的os,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的 kernel,自己只需要提供 rootfs就行了。由此可见对于不同的linux发行版, boofts基本是一致的, rootfs会有差别,因此不同的发行版可以公用 boots。

为什么采用这种分层结构

最大的好处-共享资源

比如:有多个镜像都从相同的base镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

4.2 特点

docker镜像都是只读的

当容器启动时,一个新的可写层被加载到镜像的顶部

这一层通常被称作“容器层”,“容器层之下的都叫“镜像层”。

5、容器数据卷

5.1 什么是容器数据卷

定义

命名的容器挂载数据卷,其他容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器

引申

问题: 如果数据都在容器中,那么我们容器删除了,数据就会丢失!

需求:数据可以持久化,数据存储在本地

引出:容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!

这就是卷技术!目录的挂载,将我们容器内的目录,挂载到linux上!

总结:容器的==持久化==和同步操作!容器间也是可以==数据共享==的!

特点

  1. 数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,这些数据会拷贝到新初始化的数据卷中
  2. 数据卷可在容器之间共享或重用数据
  3. 卷中的更改可以直接生效
  4. 数据卷中的更改不会包含在镜像的更新中
  5. 数据卷会一直存在,即使挂载数据卷的容器已经被删除

总的来说就是==数据持久化==和==数据共享==

5.2使用数据卷

方式一:直接使用命令来挂载 -v

1
2
3
4
5
docker run -it -v 主机目录:容器内目录

#docker run -it -v /home/ceshi:/home centos /bin/bash

#启动起来我们可以通过docker inspect 容器id查看信息

测试文件同步

好处: 我们以后修改只需要在本地修改即可,容器内会自动同步!

方式二 dockerfile编程

5.3 具名和匿名挂载

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 匿名挂载
-v 容器内路径!
docker run -d -P --name nginx01 -v /etc/nginx nginx

# 查看所有的 volume的情况
docker volume ls
local   8fa9sd8s9df8s9sd7asdf6dfg67dfhhgjgsfgv

# 这里发现,这种就是匿名挂载,我们在-v只写了容器内的路径,没有写容器外的路径!

#具名挂载
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx

docker volume ls
DRIVER             VOLUME NAME
local              juming-nginx

# 通过 -v 卷名:容器内路径
# 查看一下这个卷

所有的docker容器内的卷,没有指定目录的情况下都是在 /var/lib/docker/volumnes/xxxx/_data

我们通过具名挂载可以方便的找到我们的一个卷,大多数情况在使用的具名挂载

5.4.1 如何确定是具名挂载还是匿名挂载,还是指定路径挂载

1
2
3
-v   容器内路径   #匿名挂载
-v   卷名:容器内路径 #具名挂载
-v /宿主机路径::容器内路径  #指定路径挂载

拓展

1
2
3
4
5
#一旦这个设置了容器权限,容器对我们挂载出来的内容就有限定了
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx.ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx.rw nginx

# ro 说明这个路径只能通过宿主机来操作,容器内部是无法操作!

5.4 容器间传递共享

使用–volumes-from

6、Dockerfile

Dockerfile就是用来构建docker镜像来构建文件!(生成一个docker镜像并发布出去)就是一个命令参数脚本!

6.1 构建步骤:

1、编写一个dockerfile文件

2、docker build构建成为一个镜像

3、docker run 运行镜像

4、docker push 发布镜像(DockerHub,阿里云镜像仓库!)

6.2 基础知识

1、每个保留关键字(指令)都必须是大写字母,且后面要跟随至少一个参数

2、执行从上到下顺序执行

3、#表示注释

4、每一个指令都会创建提交一个新的镜像层,并提交!

5、docker是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单(之前是交jar ware包 现在是交一个docker镜像),docker镜像逐渐成为企业交付的标准,必须要掌握!

docker执行dockerfile的大致流程

  1. docker从基础镜像运行一个容器
  2. 执行一条指令并对容器做出修改
  3. 执行类似docker commit的操作,提交一个新的镜像层
  4. docker再基于刚提交的镜像运行一个新容器
  5. 执行dockerfile中的下一条指令直到所有指令都执行完成

步骤:开发,部署,运维。。。缺一不可

1、dockerfile构建文件,定义了一切的步骤,源代码(原材料)

2、dockerImages:通过dockerFile构建生成的镜像(最终发布和运行的产品)(软件交付品)

3、Docker容器,就是镜像运行起来提供服务器(软件的运行态)

6.3 dockerFile常用命令

FROM

基础镜像,当前镜像是基于哪个镜像的

格式:

1
2
3
from <image>
from <image>:<tag>
from <image>@<digest>

示例:

1
from mysql:5.6

MAINTAINER

维护者信息

示例:

1
MAINTAINER snailsir <snailsir@126.com>

RUN

镜像构建的时候需要运行的命令

格式:

1
RUN <command>

示例:

1
RUN ["/etc/execfile", "arg1", "arg1"]

RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定–no-cache参数,如:docker build –no-cache

ADD

将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget

格式

1
2
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]

实例:

1
2
3
4
ADD hom* /mydir/          # 添加所有以"hom"开头的文件
ADD hom?.txt /mydir/      # ? 替代一个单字符,例如:"home.txt"
ADD test relativeDir/     # 添加 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/    # 添加 "test" 到 /absoluteDir/

COPY

类似add,将我们文件拷贝到镜像中,只不过add会处理url和解压tar压缩包

CMD

构建容器后调用,也就是在容器启动时才进行调用,指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代(docker run之后的参数)

格式:

1
2
3
CMD ["executable","param1","param2"] (执行可执行文件,优先)
CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
CMD command param1 param2 (执行shell内部命令)

示例:

1
2
CMD echo "This is a test." | wc -
CMD ["/usr/bin/wc","--help"]

CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令

ENTRYPOINT

指定这个容器启动的时候要运行的命令,可以追加命令

格式:

1
2
ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)
ENTRYPOINT command param1 param2 (shell内部命令)

示例:

1
2
3
 FROM ubuntu
 ENTRYPOINT ["top", "-b"]
 CMD ["-c"]

ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。

LABEL

用于为镜像添加元数据

格式:

1
LABEL <key>=<value> <key>=<value> <key>=<value> ...

示例:

1
LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"

ENV

构建的时候设置环境变量!

格式:

1
2
ENV <key> <value>  #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
    ENV <key>=<value> ...  #可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行

示例:

1
2
3
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat=fluffy

EXPOSE

当前容器对外暴露的端口

格式:

1
EXPOSE <port> [<port>...]

示例:

1
2
EXPOSE 80 443
EXPOSE 8080

EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口

VOLUME

容器数据卷,用于数据保存和持久化工作,挂载到目录

格式:

1
VOLUME ["/path/to/dir"]

示例:

1
2
 VOLUME ["/data"]
 VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"

一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:

  1. 卷可以容器间共享和重用
  2. 容器并不一定要和其它容器共享卷
  3. 修改卷后会立即生效
  4. 对卷的修改不会对镜像产生影响
  5. 卷会一直存在,直到没有任何容器在使用它

WOEKDIR

指定在创建容器后,终端默认登陆进来工作目录,镜像的工作目录

格式:

1
WORKDIR /path/to/workdir

示例:

1
2
3
WORKDIR /a  (这时工作目录为/a)
WORKDIR b  (这时工作目录为/a/b)
WORKDIR c  (这时工作目录为/a/b/c)

通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录

USER

指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户

格式:

1
2
3
4
5
6
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group

示例:

1
USER www

使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过docker run运行容器时,可以通过-u参数来覆盖所指定的用户。

ARG

用于指定传递给构建运行时的变量

格式:

1
ARG <name>[=<default value>]

示例:

1
2
ARG site
ARG build_user=www

ONBUILD

用于设置镜像触发器,当构建一个被继承 DockerFile时候就会运行 ONBUILD 的指令。(父镜像在被子镜像继承后父镜像的onbuild被触发)触发指令

格式:

1
ONBUILD [INSTRUCTION]

示例:

1
2
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发

6.4 实战测试-centos

Docker Hub 中99%镜像都是从这个基础镜像过来的 FROM scratch,然后配置需要的软件和配置来进行的构建

创建一个自己的centos

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
FROM centos
MAINTAINER snailsir<snailsir@126.com>

ENV MYPATH /usr/local
WORKDIR $MYPATH

RUN yum -y install vim
RUN yum -y install net-tools

EXPOSE 80

CMD echo $MYPATH
CMD echo "----end----"
CMD /bin/bash

2、通过dockerFile构建镜像

1
docker build -f ./nginxfile -t self_nginx:v1

3、测试运行

1
docker run -it mycentos:0.1

补充:

1
2
# 查看镜像的构建历史,我们平时拿到一个镜像,可以研究一下它是怎么做的了?
docker history

CMD 和 ENTRYPOINT 区别

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 编写 dockerfile文件
FROM centos
CMD ["ls","-a"]

# 构建镜像
docker build -f dockerfile-cmd-test -t cmdtest

#run 运行,发现我们的ls -a 命令生效
docker run dd8e4401d72f

#想追加一个命令-l ls -al
docker run dd8e4401d72f -l
报错: "exec:\"-l\":executable file not found in $PATH"

#cmd的情况下, -l替换了 cmd ["ls","-a"]命令,-l不是命令所以报错!
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 编写 dockerfile文件
FROM centos
ENTRYPOINT ["ls","-l"]

# 构建镜像
docker build -f dockerfile-cmd-test -t cmdtest

#run 运行,发现我们的ls -a 命令生效
docker run dd8e4401d72f

#想追加一个命令-l ls -al 不报错
docker run dd8e4401d72f -l

6.5 实战测试-tomcat

1、准备镜像文件 tomcat压缩包,jdk的压缩包!

2、编写dockerfile文件, 官方命名Dockerfile,build会自动寻找这个文件,就不需要-f指定了!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
FROM centos
MAINTAINER snailsir<snailsir@126.com>

COPY readme.txt /usr/local/readme.txt

ADD jdk-Bull-linux-x64.tar.gz /usr/local/  #ADD 会自动解压
ADD apache-tomcat-9.0.22.tar.gz /usr/local/

RUM yum -y install vim

ENV MYPATH /usr/local
WORKDIR $MYPATH

ENV JAVA_HOME /usr/local/java1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.22
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.22
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin

EXPOSE 8080

CMD /usr/local/apache-tomcat-9.0.22/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.22/bin/logs/catalina.out

3、构建镜像

1
docker build -t diytomcat

4、启动镜像

1
docker run -d -p 9090:8080 --name snailsir -v /home/kuangsheng/build/tomcat/test:/usr/local/apache-tomcat-9.0.22/webapps/test -v /home/kuangsheng/build/tomcatlogs:/usr/local/apache-tomcat-9.0.22/logs diytomcat

5、访问测试

6、发布项目(由于做了卷挂载,我们直接在本地编写项目就可以发布了!)

6.6 发布自己的镜像

6.6.1 发布到dockerhub上

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#登录dockerhub
docker login -u snailsir

#提交镜像
docker push snailsir/diytomcat:1.0

#push镜像问题
docker push kuagnshen/diytomcat:1.0
An image does not exist locally with the tag:kuangsheng/diytomcat2
#解决,增加一个tag
docker tag f855234sdfeg kuangsheng/tomcat:1.0

docker push kuangshen/tomcat:1.0

6.6.2 发布到阿里云镜像仓库

6.7 小结

7、Docker网络

7.1 docker0

三个网络

问题: docker是如何处理容器网络访问的?

1
2
3
4
docker run -d -P --name tomcat01 tomcat

#查看容器的内部网络地址 ip addr  发现容器启动的时候回得到一个 eth0@if262 ip地址,docker分配的
docker exec -it tomcat01 ip addr

image-20200722155509193

1
2
3
4
#linux 能不能pingt通容器内部!
ping 127.18.0.2

#linux 可以ping 通docker 容器内部

原理

1、我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个网卡docker0

桥接模式,使用的技术是evth-pair技术!

再次测试ip addr

2、在启动一个容器测试

1
docker run -d -P --name tomcat02 tomcat

测试ip addr, 发现又多了一对网卡!

1
2
3
4
#我们发现这个容器带来网卡,都是一对对的
# evth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
# 正因为有这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备的
# OpenStac,Docker容器之间的连接,ovs的连接,都是使用evth-pair技术

3、我们来测试下tomcat01 和tomcat02 是否可以ping通!

1
2
3
docker exec -it tomcat02 ping 172.18.0.2

# 结论:容器和容器之间是可以互相ping通的!

网络模型图:

结论: tomcat01和tomcat02 是公用的一个路由器(docker0)

所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们容器分配一个默认的可用ip

小结

Docker使用的是linux的桥接,宿主机中的一个Docker容器的网桥 docker0.

Docker中的所有的网络接口都是虚拟的。虚拟的转发效率高!(内网传递文件!)

只要容器删除,对应的网桥一对就没了!

我们编写了一个微服务,database url=ip: 项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以名字来进行访问容器?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
docker exec -it tomcat02 ping tomcat01
ping: tomcat01:Name or service not knowm

# 解决
docker run -d -P --name tomcat03 --link tomcat02 tomcat
#校验
docker exec -it tomcat03 ping tomcat02

#反向可以ping通吗?不通
docker exec -it tomcat02 ping tomcat03

探究:inspect

其实这个tomcat03 就是在本地配置了tomcat02的配置?

1
2
3
4
5
# 查看 hosts配置,在这里原理发现!
docker exec -it tomcat03 cat /etc/hosts
127.0.0.1  localhost
127.18.0.3  tomcat02 312857784cd4
127.18.0.4  5ca72d80ebb0

–link 就是我们在hosts配置中增加了一个172.18.0.3 tomcat02 312857784cd4

我们现在玩docker已经不建议使用 –link了!

自定义网络!不使用docker0!

docker0问题:不支持容器名连接访问!

7.3 自定义网络

查看所有的docker网络

7.3.1 网络模式

bridge:桥接(默认) docker上搭桥 1 访问3 直接访问不了 可以在1和3之间搭建2 访问

none: 不配置网络

host: 和宿主机共享网络

container: 容器内网络连通(用的少!局限很大)

7.3.2 测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 我们直接启动的命令 --net bridge,而这个就是我们的coker0
docker run -d -P --name tomcat01 tomcat #最初启动方式
docker run -d -P --name tomcat01 --net bridge tomcat

#docker0 特点,默认域名不能访问,--link可以打通连接!

#我们可以自定义一个网络! 
# --driver bridge 桥接
# --subnet 192.168.0.0/16
# --gateway 192.168.0.1
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet

# 查看网络
docker network ls

#测试
docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat

#查看inspect
docker network inspect mynet

1
2
3
4
#测试 01 与02 是否能ping通
docker exec -it tomcat-net-01 192.168.0.3
# 不使用--link也能ping名字了!
docker exec -it tomcat-net-01 tomcat-net-02

我们自定义的网络docker都已经帮我们维护好了对应关系,推荐我们平时这样使用网络!

7.3.3好处

redis 不同的集群使用不同的网络,保证集群是安全和健康的

mysql 不同的集群使用不同的网络,保证集群是安全和健康的

7.4 网络连通

1
2
3
4
5
6
#测试打通 tomcat01 -> mynet
docker network connet mynet tomcat01

#连通之后就是将 tomcat01 放到了 mynet网络下
# 一个容器两个ip地址! 阿里云的公网ip与私网ip
docker network inspect mynet

1
2
3
4
docker exec -it tomcat01 ping tomcat-net-01
#cg
docker exec -it tomcat02 ping tomcat-net-01
# 不通

通过个脚本可以生成镜像,镜像是一层层的,脚本是一个个命令,每个命令都是一层!

1
2
3
4
5
6
7
8
9
# 创建一个dockerfile文件,名字可以随机,建议Dockerfile
FROM centos

VOLUME ["volume01", "volume02"]

CMD echo "----end----"
CMD /bin/bash

#这里的每个命令,就是镜像的一层

1
2
# 启动自己的容器
docker run -it 5d04f189a434 /bin/bash

这个卷和外部一定是有一个同步的目录!

查看一下卷挂载的路径

测试一下刚才的文件是否同步出去了!

这种方式我们未来使用的十分多,因为我们通常会构建自己的镜像!

假设构建镜像时没有挂载卷,要手动镜像关注 -v 卷名:容器内路径!

8、资源隔离与限制

8、实战

8.1mysql安装

安装5.7

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#获取镜像
docker pull mysql:5.7

#运行容器,需要做数据挂载! #安装mysql,需要配置密码的
#官方测试:

#启动我们的
-d 后台启动
-p 端口映射
-v 卷挂载
-e 环境配置
-name 容器名字
docker run --name mysql5 -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7

#启动成功,本地连接下
#连接的ip可可以使用查看
docker inspect 5d04f189a434

安装mysql8

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
docker run --name mysql5 -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest

docker run --name mysql80 -d -p 3306:3306 -v /docker/mysql/mysql8/conf.d:/etc/mysql/conf.d -v /docker/mysql/mysql8/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
#发现启动不成功
# 查看日志
[snailsir@localhost docker]$ sudo docker logs -tf --tail=100 a8e66b9175eb
#错误
[ERROR] [MY-013236] [Server] The designated data directory /var/lib/mysql/ is unusable. You can remove all files that the server added to it.
# 更换数据保存目录
docker run --name mysql5 -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysqld -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest

# 修改数据保存目录 /home/mysql/conf/my.cnf
[mysqld]
datadir  = /var/lib/mysqld

#修改加密方式
docker exec -it mysql5 /bin/bash
mysql -uroot -p
use mysql;
select host, user, plugin from user;
alter user 'root'@'%' identified with mysql_native_password by 'your-password';
alter user 'root'@'localhost' identified with mysql_native_password by 'your-password';
flush privileges;

8.2 nginx安装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#1、获取镜像
docker pull nginx

#镜像配置文件目录
# /etc/nginx/nginx.conf  /etc/nginx/conf.d
#默认html路径 /usr/share/nginx/html
#镜像日志文件路径/var/log/nginx

#2、启动一个默认实例
docker run --name nginx -d nginx

#3、把容器内的文件拷贝出来
docker cp nginx:/etc/nginx/nginx.conf /docker/nginx/conf.d
docker cp nginx:/etc/nginx/conf.d/default.conf /docker/nginx/conf.d
docker cp nginx:/usr/share/nginx/html/50x.html /docker/www
docker cp nginx:/usr/share/nginx/html/index.html /docker/www
docker cp nginx:/var/log/nginx /docker/nginx/logs

#4、删除默认实例
docker stop nginx && docker rm nginx

#5、创建nginx新实例
docker run --name nginx-web -p 80:80 -v /docker/nginx/www:/usr/share/nginx/html -v /docker/nginx/conf.d:/etc/nginx/conf.d -v /docker/nginx/logs:/var/log/nginx -d nginx

#6、创建各个网站日志与代码地址
mkdir -p /docker/www/snailsir.cn
mkdir -p /docker/nginx/logs/snailsir.cn

nginx.conf默认文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

conf.d/default.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

snailsir.cn.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
server {
    listen       80;
    server_name  www.snailsir.cn;

    #charset koi8-r;
    access_log  /var/log/nginx/snailsir.cn/access.log  main;
    error_log  /var/log/nginx/snailsir.cn/error.log warn;

    location / {
        root   /usr/share/nginx/html/snailsir.cn;
        index  index.html index.htm;
    }
}

8.3 redis安装

单机版redis安装

配置文件 redis.conf

1
2
3
4
5
6
7
8
9
# 修改配置文件
bind 127.0.0.1 #注释掉这部分,这是限制redis只能本地访问
protected-mode no #默认yes,开启保护模式,限制为本地访问
daemonize no#默认no,改为yes意为以守护进程方式启动,可后台运行,除非kill进程,改为yes会使配置文件方式启动redis失败
dir  ./ #输入本地redis数据库存放文件夹(可选)
appendonly yes #redis持久化(可选)

#运行docker
docker run -p 6379:6379 --name redis -v /docker/redis/conf/redis.conf:/etc/redis/redis.conf -v /docker/redis/datas:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes

9、bug

9.1 问题1

描述

1
cannot connect to the docker daemon at unix:///var/run/docker.sock

原因

可能是上一次没有正常退出docker,所以docker没有正常启动,在响应的/var/run/路径下没有找到docker进程。

解决方法

1
service docker restart

或者

1
systemctl start docker

docker安装之后,已经安装了开机启动service文件,但还需要在设置下开机启动,才能在服务器重启是自动启动

1
systemctl enable docker
使用 Hugo 构建
主题 StackJimmy 设计