README

本文学习路线主要参照:
    https://www.bilibili.com/video/BV1og4y1q7M4
    https://www.bilibili.com/video/BV1kv411q7Qc

文章目录

一.Docker概述

1. Docker为什么出现?

传统开发中的问题:

  1. 一款产品:开发-上线 两套环境!应用环境,应用配置!

  2. 开发-运维问题:

    • 我在我的电脑上可以运行!
    • 版本更新,导致服务不可用!对于运维来说,考验就十分大?
    • 环境配置是十分的麻烦,每一个机器都要部署环境(集群Redis、ES、Hadoop.………)!费时费力。
    • 发布一个项目(jar +(Redis MySQL jdk ES)),项目能不能都带上环境安装打包!
    • 之前在服务器配置一个应用的环境 Redis MySQL jdk ES Hadoop,配置超麻烦了,不能够跨平台。Windows,最后发布到 Linux!

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

    Docker的思想就来自于集装箱!

    隔离:Docker核心思想!打包装箱!每个箱子是互相隔离的。Docker 通过隔离机制,可以将服务器利用到极致!

2. Docker的历史

  • 2010年,几个搞IT的年轻人,就在美国成立了一家公司 dotcloud,做一些 pass的云计算服务! LXC有关的容器技术!
    他们将自己的技术(容器化技术)命名 就是Docker!Docker刚刚诞生的时候,没有引起行业的注意!dotCloud,就活不下去!
  • 2013年,Docker开源!越来越多的人发现了docker的优点!火了,Docker每个月都会更新一个版本!
  • 2014年4月9日,Docker1.0发布!

Docker为什么这么火?

    因为它十分的轻巧!

    在容器技术出来之前,我们都是使用虚拟机技术!虚拟机:在window中装一个Vmware,通过这个软件我们可以虚拟出来一台或者多台电脑!笨重!
 虚拟机使用的是虚拟化技术,Docker的容器技术,也是一种虚拟化技术!

3. Docker简介

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

Docker Hub官网: https://hub.docker.com/

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

3.1 Docker的定义

    Docker是一个基于Go语言开发的开源的应用容器引擎;是一个轻量级容器技术;

    Docker支持将软件编译成一个镜像;然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以直接使用这个镜像;运行中的这个镜像称为容器,容器启动是非常快速的。

3.2 Docker VS 虚拟机

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

3.3 Docker的DevOps 优势

DevOps (开发、运维)

  1. 应用更快速的交付和部署

    • 传统:一堆帮助文档,安装程序
    • Docker:打包镜像发布测试,一键运行
  2. 更便捷的升级和扩缩容

    • 使用了Docker之后,我们部署应用就和搭积木一样!
    • 项目打包为一个镜像,扩展 服务器A!服务器B
  3. 更简单的系统运维

    • 在容器化之后,我们的开发,测试环境都是高度一致的。
  4. 更高效的计算资源利用

    • Docker是内核级别的虚拟化,可以再一个物理机上可以运行很多的容器实例!服务器的性能可以被压榨到机制!

4. Docker基本架构

镜像 image

  • Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。 就好似Java 中的类和对象,类就是镜像,容器就是对象!

容器 container

  • Docker 利用容器(Container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。
  • 它可以被启动、开始、停止、删除。每个容器都是相互隔离的,保证安全的平台。
  • 可以把容器看做是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。

仓库 repository

  • 仓库(Repository)是集中存放镜像文件的场所。
  • 仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
  • 仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
  • 最大的公开仓库是Docker Hub,存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云、网易云等。

架构图
在这里插入图片描述

  • Docker主机(Host):安装了Docker程序的机器(Docker直接安装在操作系统之上);
  • Docker客户端(Client):连接Docker主机进行操作;
  • Docker仓库(Registry):用来存放各种镜像,分为公有仓库和私有仓库,公有仓库默认是国外的Docker Hub;
  • Docker镜像(Images):由各种环境和软件好的模板,放在Docker仓库中,通过镜像可创建容器服务;
  • Docker容器(Container):镜像启动后的实例称为一个容器,容器是独立运行的一个或一组应用

5. Docker基本工作原理

  • Docker 是一个 Client-Server结构的系统,Docker的守护进程运行在主机上。通过Socket从客户端访问!
  • DockerServer 接收到Docker-Client的指令,就会执行这个命令!

在这里插入图片描述

二.Centos7安装Docker

1. 安装步骤

官方文档非常详细,地址: https://docs.docker.com/engine/install/centos/

#1、检查内核版本,必须是3.10及以上
cat /etc/os-release 
uname -r 

#2、卸载旧版本
yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
#3、安装依赖包
yum install -y yum-utils

#4、设置docker镜像仓库(官网给的默认仓库在国外,如下:不建议使用)
yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
#建议使用下面的阿里云docker镜像仓库
yum-config-manager \
    --add-repo \
    http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
#5、更新一下yum软件包索引
yum makecache fast

#6、安装docker(默认最新版本)    ce指社区版   ee指企业版
yum -y install docker-ce docker-ce-cli containerd.io  #如果安装指定版本的docker可去官网查看https://docs.docker.com/engine/install/centos/

#7、启动docker
systemctl start docker

#8、查看docker是否安装成功
docker version

#9、运行docker测试案例
docker run hello-world  # 使用 docker images 查看自动拉取到的hello-world镜像

docker run的运行原理如下图
在这里插入图片描述

2. 卸载docker

# 下载依赖
yum -y remove docker-ce docker-ce-cli containerd.io

# 删除资源
rm -rf /var/lib/docker  #docker的默认工作路径

3. 配置阿里云镜像加速

选配

mkdir -p /etc/docker

tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://2j8ru6sk.mirror.aliyuncs.com"]
}
EOF

systemctl daemon-reload
systemctl restart docker

三.Docker常用命令

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

1. 帮助命令

# 显示docker的版本信息
docker version
# 显示docker的系统信息,包括镜像和容器的数量
docker info
# 帮助命令
docker --help

2. 镜像命令

2.1 查看镜像

# 查看地主机上的镜像
[root@node02 ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              bf756fb1ae65        7 months ago        13.3kB

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

REPOSITORY 镜像的仓库源

TAG 镜像的标签

IMAGE ID 镜像的id

CREATED 镜像的创建时间

SIZE 镜像的大小

2.2 搜索镜像

docker search mysql

# 命令行可选项,举例
docker search mysql --dilter=STARS=3000  #搜索STARS大于3000的镜像

2.3 下载镜像

[root@node02 ~]# docker pull mysql
Using default tag: latest  # 如果不写tag,默认是latest
latest: Pulling from library/mysql
bf5952930446: Pull complete  # 分层下载,docker image的核心——联合文件系统        
8254623a9871: Pull complete 
938e3e06dac4: Pull complete 
ea28ebf28884: Pull complete 
f3cef38785c2: Pull complete 
894f9792565a: Pull complete 
1d8a57523420: Pull complete 
6c676912929f: Pull complete 
ff39fdb566b4: Pull complete 
fff872988aba: Pull complete 
4d34e365ae68: Pull complete 
7886ee20621e: Pull complete 
Digest: sha256:c358e72e100ab493a0304bda35e6f239db2ec8c9bb836d8a427ac34307d074ed # 签名(防伪)
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 镜像的真实地址   docker pull mysql  等价于 docker pull docker.io/library/mysql:latest

#指定tag(版本)下载
[root@node02 ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
bf5952930446: Already exists # 联合文件系统分层下载,在执行上面的 docker pull mysql 时已经下载过了
8254623a9871: Already exists 
938e3e06dac4: Already exists 
ea28ebf28884: Already exists 
f3cef38785c2: Already exists 
894f9792565a: Already exists 
1d8a57523420: Already exists 
5f09bf1d31c1: Pull complete 
1b6ff254abe7: Pull complete 
74310a0bf42d: Pull complete 
d398726627fd: Pull complete 
Digest: sha256:da58f943b94721d46e87d5de208dc07302a8b13e638cd1d24285d222376d6d84
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7

2.4 删除镜像

# 删除指定的镜像
docker rmi -f 镜像id  #根据IMAGE ID删除
# 删除多个镜像
docker rmi -f 镜像id 镜像id 。。。
# 删除全部镜像
docker rmi -f $(docker images -aq)

# 根据镜像的REPOSITORY删除镜像
docker images | grep "镜像名称" | awk '{print $1":"$2}' | xargs docker rmi

3. 容器命令

有了镜像才能创建容器,比如我们下载一个centos的docker镜像来测试学习

docker pull centos

3.1 启动容器

docker run [可选参数] 镜像名称

# 参数说明
--name="name"      容器名称,用来区分容器
-d                 后台方式运行
-it                使用交互式运行,用来进入容器查看内容
-p                 指定容器端口,有四种用法
    -p IP:主机端口:容器端口  #不常用
    -p 主机端口:容器端口     #最常用
    -p 容器端口             #直接写容器端口,不与主机端口关联
    容器端口                #可省掉-p,只写容器端口
    
# 启动容器并运行/bin/bash命令进入容器
[root@node02 ~]# docker run -it centos /bin/bash
WARNING: IPv4 forwarding is disabled. Networking will not work.

# 查看容器内的centos 【这是centos基础版本,很多命令都是不完善的!】
[root@9069a46e5b5b /]# ls
bin  etc   lib    lost+found  mnt  proc  run   srv  tmp  var
dev  home  lib64  media       opt  root  sbin  sys  usr

# 从容器中退会主机
[root@9069a46e5b5b ~]# exit
exit

3.2 查看容器

docker ps    # 列出正在运行的容器
  -a         # 可选参数,列出正在运行的容器 + 历史运行的容器
  -n=?       # 可选参数,仅列出前?行查询结果
  -q         # 可选参数,只显示容器的编号

举例:
docker ps
docker ps -a
docker ps -aq
docker ps -a -n=1

3.3 退出容器

# 还是以上述centos的容器为例
exit           # 退出并停止容器
Ctrl + P + Q   # 仅退出容器,容器并不会停止

3.4 删除容器

# 删除指定的容器,不能删除正在运行的容器
docker rm 容器id

# 删除所有容器【-f表示强制删除】  docker ps -a -q|xargs docker rm -f
docker rm -f $(docker ps -aq)

3.5 启停容器

docker start 容器id     # 启动容器
docker stop 容器id      # 关闭容器
docker restart 容器id   # 重启容器
docker kill 容器id      # 强制停止容器

4. 其他常用命令

4.1 后台启动容器

docker run -d 镜像名
[root@node02 ~]# docker run -d centos

注意:上述命令虽然成功执行了,但是要docker ps命令是查询不到此容器的,说名启动的这个容器又停止了

因此:docker容器使用后台运行时,必须要有一个前台应用使用,如果docker发现没有应用在使用这个容器,就会自动停止。

例如:Ngnix容器启动后,如果检测到容器没有提供服务,就会立刻停止此容器运行

4.2 查看容器日志

docker logs -f -t --tail 容器名

# 自己写一段shell脚本用centos容器运行
[root@node02 ~]# docker run -d centos /bin/sh -c "while true;do echo helloDocker;sleep 1;done"
WARNING: IPv4 forwarding is disabled. Networking will not work.
223677dd3986b57174e344c2b7f4d715e52db4e514fb4a8b2fb78ad1318c5882
[root@node02 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
223677dd3986        centos              "/bin/sh -c 'while t…"   7 seconds ago       Up 6 seconds                            angry_borg
[root@node02 ~]# docker logs -tf --tail 10 223677dd3986
2020-08-23T09:41:24.673130377Z helloDocker
2020-08-23T09:41:25.678436561Z helloDocker
... ...

-tf 含义是:显示日志

–tail 行数 含义是:要显示的日志行数

4.3 查看容器中的进程信息

docker top 容器id

# 例如
[root@node02 ~]# docker top 223677dd3986
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                5907                5890                0                   17:41               ?                   00:00:00            /bin/sh -c while true;do echo helloDocker;sleep 1;done
root                6221                5907                0                   17:45               ?                   00:00:00            /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1

4.4 查看容器的元数据

docker inspect 容器id

# 例如  查询到的是json格式的十分详细的容器信息
[root@node02 ~]# docker inspect 223677dd3986

4.5 进入正在运行的容器

# 命令1
docker exec -it 容器id bashshell
# 通常,容器都是使用后台方式运行,如果想要进容器修改一些配置,常用上面的命令行进入容器,举例如下:

[root@node02 ~]# docker run -d centos /bin/sh -c "while true;do echo helloDocker;sleep 1;done"
WARNING: IPv4 forwarding is disabled. Networking will not work.
4556168d774b1c86a86348a7c9264a1602d8bc7eb5ec4a669a2c2faebe0f4cb5
[root@node02 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
4556168d774b        centos              "/bin/sh -c 'while t…"   12 seconds ago      Up 11 seconds                           beautiful_haibt
[root@node02 ~]# docker exec -it 4556168d774b /bin/bash
[root@4556168d774b /]# ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 01:15 ?        00:00:00 /bin/sh -c while true;do echo helloDocker;sleep 1;done
root         42      0  0 01:15 pts/0    00:00:00 /bin/bash
root        249      1  0 01:18 ?        00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1
root        250     42  0 01:18 pts/0    00:00:00 ps -ef
# 命令2
docker attach 容器id

# 测试
[root@node02 ~]# docker attach 4556168d774b
helloDocker
helloDocker
helloDocker
... ...

命令区别:

  • docker exec 进入容器后后开启一个新的终端
  • docker attach 进入容器正在执行的终端

4.6 复制容器内文件到主机

# 命令 (复制文件或目录都可以)
docker cp 容器id:容器内文件路径 目的主机的路径
# 此命令是一个手动的过程,后面我们可以使用 卷 技术,实现自动同步。如将容器的/home与本机的/home做同步

# 测试
[root@node02 ~]# docker run -it centos /bin/bash
WARNING: IPv4 forwarding is disabled. Networking will not work.
[root@aed54c3c8382 /]# touch /home/hello.java
[root@aed54c3c8382 /]# ls /home
hello.java
# Ctrl + P + Q  仅退出容器,容器并不会停止
[root@node02 ~]# ls /home
[root@node02 ~]# docker cp aed54c3c8382:/home/hello.java /home  
[root@node02 ~]# ls /home
hello.java

5. 小结

在这里插入图片描述

命令行关键字英文注释译:中文
attachAttach to a running container当前 she11 下 attach 连接指定运行镜像
buitdBuild an image from a Dockerfile通过 Dockerfile 定制镜像
commitCreate a new image from a container changes提交当前容器为新的镜像
cpCopy files/folders from the containers filesystem to the host path从容器中拷贝指定文件或者目录到宿主机中
createCreate a new container创建一个新的容器,同run,但不启动容器
diffInspect changes on a container’s filesystem查看 docker 容器变化
eventsGet real time events from the server从 docker 服务获取容器实时事件
execRun a command in an existing container在已存在的容器上运行命令
exportStream the contents of a container as a tar archive导出容器的内容流作为一个 tar 归档文件
importCreate a new filesystem image from the contents of a tarball从tar包中的内容创建一个新的文件系统映像
historyShow the history of an image展示一个镜像形成历史
imagesList images列出系统当前镜像
infoDisplay system-wide information显示系统相关信息
inspectReturn low-level information on a container查看容器详细信息
killKill a running containerkill 指定 docker 容器
loadLoad an image from a tar archive从一个 tar 包中加载一个镜像[对应 save]
loginRegister or Login to the docker registry server注册或者登陆一个 docker 源服务器
logoutLog out from-a Docker registry server从当前 Docker registry 退出
logsFetch the logs of a container输出当前容器日志信息
portLookup the public-facing port which is NAT-ed to PRIVATE_PORT查看映射端口对应的容器内部源端口
pausePause all processes within a container暂停容器
psList containers列出容器列表
pullPull an image or a repository from the docker registry server从docker镜像源服务器拉取指定镜像或者库镜像
pushPush an image or a repository to the docker registry server推送指定镜像或者库镜像至docker源服务器
restartRestart a running container重启运行的容器
rmRemove one or more containers移陈一个或者多个容器
rmiRemove one or more images A移除一个或多个镜像
runRun a command in a new container创建一个新的容器并运行一个命令
saveSave an inage to a tar archive保存一个镜像为一个 tar 包[对应 load]
searchSearch for an image on the Docker Hub在 docker hub 中搜索镜像
startstart a stopped containers启动容器
stopStop a running containers停止容器
tagTag an image into a repository给源中镜像打标签
topLookup the running processes of a container查看容器中运行的进程信息
unpauseUnpause a paused container取消暂停容器
versionShow the docker version information查看 docker 版本号
waitBlock until a container stops, then print its exit code戴取容器停止时的退出状态值

四.常用镜像入门练习

1. Docker安装Nginx

# 搜索nginx镜像,建议去docker hup上去搜索
docker search nginx
# 下载镜像
docker pull nginx
# 查看ngnix镜像是否下载成功
docker images

# 启动nginx容器
[root@node02 ~]# docker run -d --name nginx01 -p 3344:80 nginx
WARNING: IPv4 forwarding is disabled. Networking will not work.
edb9fd24ddebbc324c89f29ffbc7e54c069bcfba853ac07b1f8cf45ccd2fea1a
# 查看nginx容器
[root@node02 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
edb9fd24ddeb        nginx               "/docker-entrypoint.…"   17 seconds ago      Up 16 seconds       0.0.0.0:3444->80/tcp   nginx01
# 查看nginx服务
[root@node02 ~]# curl localhost:3344

注意:仅在宿主机上可以访问到nginx容器,而其他内网机器无法访问 http:192.168.52.110:3344

修改以下配置后可解决

# 解决办法:
vim /usr/lib/sysctl.d/00-system.conf

# 添加如下代码:
net.ipv4.ip_forward=1

# 重启network服务
systemctl restart network

# 查看是否修改成功,如果返回为“net.ipv4.ip_forward = 1”则表示成功了
sysctl net.ipv4.ip_forward

思考问题:我们每次改动nginx配置文件都需要进入容器内部,会十分麻烦。如果可以在容器外部提供一个映射路径,达到在主机修改文件,容器内部也可以自动修改就会十分便捷。后文讲的 -v数据卷可以实现此需求。

2. Docker安装Tomcat

# 官方给的示例命令如下,该命令一般用来测试,用完就会自动删除掉此容器
docker run -it --rm tomcat:9.0
# 下载官方最新镜像
docker pull tomcat
# 后台启动tomcat
docker run -d -p 3355:8080 --name tomcat01 tomcat

浏览器访问 : http://192.168.52.110:3355

结果:可以访问的到,但是返回结果是404

原因如下:

[root@node02 ~]# docker exec -it tomcat01 /bin/bash
root@dcae39a6bff5:/usr/local/tomcat# ls -al    # 发现一: ll命令无法使用,说明镜像中支持的linux命令少了
total 124
drwxr-xr-x 1 root root    30 Aug  5 19:22 .
drwxr-xr-x 1 root root    20 Aug  5 19:18 ..
-rw-r--r-- 1 root root 18982 Jun 30 20:14 BUILDING.txt
-rw-r--r-- 1 root root  5409 Jun 30 20:14 CONTRIBUTING.md
-rw-r--r-- 1 root root 57092 Jun 30 20:14 LICENSE
-rw-r--r-- 1 root root  2333 Jun 30 20:14 NOTICE
-rw-r--r-- 1 root root  3255 Jun 30 20:14 README.md
-rw-r--r-- 1 root root  6898 Jun 30 20:14 RELEASE-NOTES
-rw-r--r-- 1 root root 16262 Jun 30 20:14 RUNNING.txt
drwxr-xr-x 2 root root  4096 Aug  5 19:23 bin
drwxr-xr-x 1 root root    22 Aug 25 01:37 conf
drwxr-xr-x 2 root root  4096 Aug  5 19:22 lib
drwxrwxrwx 1 root root   177 Aug 25 01:37 logs
drwxr-xr-x 2 root root   134 Aug  5 19:22 native-jni-lib
drwxrwxrwx 2 root root    30 Aug  5 19:22 temp
drwxr-xr-x 2 root root     6 Aug  5 19:22 webapps  # 去webapp下看项目信息
drwxr-xr-x 7 root root    81 Jun 30 20:12 webapps.dist
drwxrwxrwx 2 root root     6 Jun 30 20:09 work
root@dcae39a6bff5:/usr/local/tomcat# cd webapps
root@dcae39a6bff5:/usr/local/tomcat/webapps# ls          # 发现二: webapps下没有内容,所以返回的是404
root@dcae39a6bff5:/usr/local/tomcat/webapps# cp -r ../webapps.dist/* ./    # 复制后刷新web页面,可正常显示tomcat首页
root@dcae39a6bff5:/usr/local/tomcat/webapps# ls
ROOT  docs  examples  host-manager  manager

总结:以上问题发生的原因是, 阿里云镜像默认是最小镜像,所以镜像中不必要的内容都被剔除掉了,从而保证最小可运行环境

思考:我们以后要部署项目,如果每次都要进入容器会十分麻烦。如果可以在容器外部提供一个映射路径,webapps,我们在外部放置项目,就自动同步到内部了。

3. Docker安装Es + Kibana

docker rm -f $(docker ps -aq)
# 下载镜像
docker pull elasticsearch

# -e 修改环境配置(environment的缩写)
docker run -d --name elasticsearch01 -p 9200:9200 -p 9300:9300 -e ES_JAVA_OPTS="-Xms64m -Xmx512m" -e "discovery.type=single-node" elasticsearch

# 访问es容器
curl localhost:9200

# 查看docker的cpu和内存状况
docker stats

-e ES_JAVA_OPS="-Xms64m -Xmx512m" 增加对es的内存限制,es很占用内存

-e “discovery.type=single-node” 单节点部署环境

思考:kibana容器如何连接es容器

在这里插入图片描述

4. Docker安装Portainer

    Portainer是Docker的图形化界面管理工具。提供一个后台面板供我们操作、监控,平时不会用。

# 安装Portainer   -v 本地卷挂载,后文会讲   --privileged=true表示授权外网访问
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer

五.Docker镜像讲解

1. 镜像是什么

    镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
    所有的应用,直接打包docker镜像,就可以直接跑起来!

如何得到镜像:

  • 从远程仓库下载
  • 朋友拷贝给你
  • 自己制作一个镜像 DockerFile

2. Docker镜像加载原理

UnionFS(联合文件系统)

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

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

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

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

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

平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?

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

3. 分层理解

分层的镜像

我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载!

[root@node02 ~]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
bf5952930446: Already exists 
911b8422b695: Pull complete 
093b947e0ade: Pull complete 
5b1d5f59e382: Pull complete 
7a5f59580c0b: Pull complete 
f9c63997c980: Pull complete 
Digest: sha256:09c33840ec47815dc0351f1eca3befe741d7105b3e95bc8fdb9a7e4985b9e1e5
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest

思考:为什么Docker镜像要采用这种分层的结构呢?

    最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base
镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。

    查看镜像分层的方式可以通过 docker image inspect命令!分层信息保存在在layers属性中。

"Layers": [
                "sha256:d0f104dc0a1f9c744b65b23b3fd4d4d3236b4656e67f776fe13f8ad8423b955c",
                "sha256:09b6608896c0a00497d9e9c1b045f0c906203555740dee64473db943244059c2",
                "sha256:ab0653e928a7c1d4b2f1c8e527d735aa0ea8dcb8c50e7cefc7680cf09cf6f985",
                "sha256:57094a432b39be6fc8a3f533567e10c354705eec37e4f7a8b5a7041a4ec63fa2",
                "sha256:1b80269d908f58520f9f3f00d75e65907eafa0f5143d5fe2b6cafcc30b32bc83",
                "sha256:1bd654b55bb49880160a60ad3a75c4b14254656229d535e943d8fb88124f6177"
            ]

理解:

    所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。

    举一个简单的例子,假如基于Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。

    该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
在这里插入图片描述

    在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。
在这里插入图片描述

    上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。

    下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版本。

在这里插入图片描述

    这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。

    Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。

    Linux 上可用的存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs 以及ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。

    Docker在Windows上仅支持windowsfilter一种存储引擎,该引基于 NTFS 文件系统之上实现了分层和CoW[1]。

    下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。

在这里插入图片描述

特点

  1. Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
  2. 这一层就是我们通常说的容器层,容器之下的都叫镜像层!

4. commit镜像

命令

docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[tag]

测试:

[root@node02 ~]# docker rm -f $(docker ps -aq)
[root@node02 ~]# docker run -d -p 3355:8080 --name mytomcat tomcat
[root@node02 ~]# docker ps -aq
3a0d0432d1cd
[root@node02 ~]# docker exec -it mytomcat /bin/bash
root@3a0d0432d1cd:/usr/local/tomcat# cp -r webapps.dist/* webapps/
root@3a0d0432d1cd:/usr/local/tomcat# ls webapps
ROOT  docs  examples  host-manager  manager

[root@node02 ~]# docker commit -a="devin-kim" -m="tomcat webapps app docker images commit test" 3a0d0432d1cd mytomcat:1.0
sha256:67e6f4f39b6a0f9d66ac0142fd58811ff111c54bb427b6409b1d054be670643a
[root@node02 ~]# docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
mytomcat              1.0                 67e6f4f39b6a        5 seconds ago       652MB
nginx                 latest              4bb46517cac3        11 days ago         133MB
centos                latest              0d120b6ccaa8        2 weeks ago         215MB
tomcat                latest              2ae23eb477aa        2 weeks ago         647MB
portainer/portainer   latest              62771b0b9b09        4 weeks ago         79.1MB
hello-world           latest              bf756fb1ae65        7 months ago        13.3kB
elasticsearch         latest              5acf0e8da90b        23 months ago       486MB

六.容器数据卷

    如果数据存放在容器中,那么删除容器,数据也会丢失!所有,我么希望容器间有一个数据共享的技术,将Docker容器产生数据同步到本地。

    于是,就产生了卷技术,用来将容器内的目录挂载到Linux上面,容器间也可以共享数据。

1. 测试

docker rm -f $(docker ps -aq)
# 命令:  docker run -it -v 主机目录:容器目录 镜像名称 命令行
# 测试:
[root@node02 ~]# ls /home      # 宿主机的/home目录下没有内容
[root@node02 ~]# docker run -it -v /home/ceshi:/home centos /bin/bash     # 将宿主机的/home/ceshi目录与容器的/home目录映射
[root@a01f656d9ce6 /]# ls /home
[root@a01f656d9ce6 /]# 
Ctrl + P + Q   # 仅退出容器,容器并不会停止
[root@node02 ~]# ls /home   # 宿主机的/home目录下自动创建了ceshi这个目录
ceshi
[root@node02 ~]# docker ps -a
CONTAINER ID  IMAGE   COMMAND      CREATED        STATUS        PORTS  NAMES
a01f656d9ce6  centos  "/bin/bash"  8 minutes ago  Up 8 minutes         busy_babbage
[root@node02 ~]# docker inspect a01f656d9ce6  # 查看此容器的元数据信息中的Mounts属性如下面的代码块
[root@node02 ~]# touch /home/ceshi/master.java
[root@node02 ~]# ls /home/ceshi/
master.java
[root@node02 ~]# docker attach a01f656d9ce6
[root@a01f656d9ce6 /]# touch /home/slave.java
[root@a01f656d9ce6 /]# ls /home
master.java  slave.java
[root@a01f656d9ce6 /]# exit
exit
[root@node02 ~]# ls /home/ceshi/
master.java  slave.java
[root@node02 ~]# echo "hello,I am centos OS" >> /home/ceshi/master.java 
[root@node02 ~]# cat /home/ceshi/master.java 
hello,I am centos OS
[root@node02 ~]# docker start a01f656d9ce6
[root@node02 ~]# docker attach a01f656d9ce6
[root@a01f656d9ce6 /]# cat /home/master.java 
hello,I am centos OS
[root@a01f656d9ce6 /]# echo "hello,I am docker container" >> /home/slave.java
[root@a01f656d9ce6 /]# exit
exit
[root@node02 ~]# cat /home/ceshi/slave.java 
hello,I am docker container

查看此容器的元数据信息中的Mounts属性如下面的代码块

"Mounts": [
            {
                "Type": "bind",
                "Source": "/home/ceshi",
                "Destination": "/home",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ]

注意:即使容器被删除了,宿主机上的文件也不会被删除。

2. Docker安装MySQL

获取镜像

# docker rm -f $(docker ps -aq)
docker pull mysql:5.7

测试运行:

docker run -d --name mysql01 -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/datas:/var/lib/mysql  -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
mysql -hlocalhost -P3310 -uroot -p123456

3. 具名和匿名挂载

匿名挂载

# -P表示随机映射一个端口   -v后只指定了容器内的路径,没有指定宿主机的路径
docker run -d -P --name nginx01 -v /etc/nginx nginx

具名挂载

docker run -d -P --name nginx02 -v mynginx02:/etc/nginx nginx

运行查看区别

# docker volume --help  查看卷操作命令文档
[root@node02 mysql]# docker run -d -P --name nginx01 -v /etc/nginx nginx
0ececf091a21192a5b87402f934762d312888acdc1e3f0d9377a7fcfce4cdc83
[root@node02 mysql]# docker run -d -P --name nginx02 -v mynginx02:/etc/nginx nginx
66c3fd5a26d76f0cc2298b4ea70a7fb359ee2f00d8e8350a04255acdb9562f2c
[root@node02 mysql]# docker volume ls  # 查看所有volume(卷名)
DRIVER              VOLUME NAME
local               1b2981ff259408992e51580cd8d5330f0f930232baefc77acb04f3ac23d048ff
local               2e414e32e8f83f5959b86c9beb5f2711c768e73ab01585603187dc189096c939
local               5f7a668f05424aa0e87691fdbfc9fe7e038ad6262d69b7a4b6cf80a409be8b9a
local               865c4c392672a30b99a2e6e4a9360b03e35a97c23ac50cadf0540d0b5708a39b
local               afe808a7164ca387dfce697fbba8e8e5dcc10b88e7feefad3e51e13ff4924631
local               d8b0970a319db72b0f82794c8cdb610f404264daea41f4c923073e4868e6fc29
local               e917ce3cda341e304f25b79aa56251d71e0ef3279205b4d899c71ecd4fa0e8c8
local               mynginx02
[root@node02 mysql]# docker volume inspect mynginx02
[
    {
        "CreatedAt": "2020-08-25T16:30:02+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/mynginx02/_data",
        "Name": "mynginx02",
        "Options": null,
        "Scope": "local"
    }
]

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

我们可以通过具名挂载方便的招到我们的卷,不建议使用匿名挂载

扩展:

# 通过 -v 容器内路径:ro  或者是rw,改变读写权限
ro readonly    # 只读,ro说明这个路径只能通过宿主机来操作,容器内无法操作
rw readwrite   # 可读、可写(默认)

# 一旦设置了容器权限,容器对我们挂在出来的内容就有限定了
docker run -d -P --name nginx03 -v mynginx03:/etc/nginx:ro nginx
docker run -d -P --name nginx04 -v mynginx04:/etc/nginx:rw nginx

4. 初识DockerFile

DockerFile是用来构建docker镜像的命令脚本文件。通过这个脚本生成一个镜像

mkdir -p /home/docker-test-volume/
vim /home/docker-test-volume/docerfile01

文件内容如下:

from centos

VOLUME ["volume01","volume02"]

CMD  echo "-----end-----"

CMD /bin/bash

通过 DockerFile 定制镜像

[root@node02 home]# cd /home/docker-test-volume/
[root@node02 docker-test-volume]# docker build -f /home/docker-test-volume/docerfile01 -t mydf01-centos:1.0 .
Sending build context to Docker daemon  2.048kB
Step 1/4 : from centos
 ---> 0d120b6ccaa8
Step 2/4 : VOLUME ["volume01","wolume02"]
 ---> Running in 04869be72707
Removing intermediate container 04869be72707
 ---> 2781f855acbd
Step 3/4 : CMD  echo "-----end-----"
 ---> Running in 22b99a429339
Removing intermediate container 22b99a429339
 ---> baa228a16e1b
Step 4/4 : CMD /bin/bash
 ---> Running in 373d132a75f8
Removing intermediate container 373d132a75f8
 ---> 3aeb4218b1e4
Successfully built 3aeb4218b1e4
Successfully tagged mydf01-centos:1.0
[root@node02 docker-test-volume]# ls
docerfile01
[root@node02 docker-test-volume]# docker images   # mydf01-centos就是我们用dockerfile构建出来的镜像
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
mydf01-centos         1.0                 3aeb4218b1e4        17 seconds ago      215MB
mytomcat              1.0                 67e6f4f39b6a        4 hours ago         652MB
[root@node02 docker-test-volume]# docker run -it 3aeb4218b1e4 /bin/bash 
[root@45c6ca19e300 /]# ls -l
total 0
lrwxrwxrwx   1 root root   7 May 11  2019 bin -> usr/bin
drwxr-xr-x   5 root root 360 Aug 25 10:13 dev
drwxr-xr-x   1 root root  66 Aug 25 10:13 etc
drwxr-xr-x   2 root root   6 May 11  2019 home
lrwxrwxrwx   1 root root   7 May 11  2019 lib -> usr/lib
lrwxrwxrwx   1 root root   9 May 11  2019 lib64 -> usr/lib64
drwx------   2 root root   6 Aug  9 21:40 lost+found
drwxr-xr-x   2 root root   6 May 11  2019 media
drwxr-xr-x   2 root root   6 May 11  2019 mnt
drwxr-xr-x   2 root root   6 May 11  2019 opt
dr-xr-xr-x 128 root root   0 Aug 25 10:13 proc
dr-xr-x---   2 root root 162 Aug  9 21:40 root
drwxr-xr-x  11 root root 163 Aug  9 21:40 run
lrwxrwxrwx   1 root root   8 May 11  2019 sbin -> usr/sbin
drwxr-xr-x   2 root root   6 May 11  2019 srv
dr-xr-xr-x  13 root root   0 Aug 25 10:13 sys
drwxrwxrwt   7 root root 145 Aug  9 21:40 tmp
drwxr-xr-x  12 root root 144 Aug  9 21:40 usr
drwxr-xr-x  20 root root 262 Aug  9 21:40 var
drwxr-xr-x   2 root root   6 Aug 25 10:13 volume01    # 这是生成镜像时自动挂载的匿名数据卷目录
drwxr-xr-x   2 root root   6 Aug 25 10:13 volume02    # 对应dockerfile01中的 VOLUME ["volume01","volume02"]
[root@45c6ca19e300 /]# echo "I am volume01" >> /volume01/myfile.txt
[root@45c6ca19e300 /]# ls /volume01/
myfile.txt
[root@45c6ca19e300 /]# exit
exit
[root@node02 docker-test-volume]# docker inspect 45c6ca19e300  # 查看 Mounts 的属性值,如下代码块

查看到的卷挂载路径如下

"Mounts": [
            {
                "Type": "volume",
                "Name": "902971cdd33038d6ce43db14c00c56bbb17decfb1b13fcdacd64ee8c9bfc2c5c",
                "Source": "/var/lib/docker/volumes/902971cdd33038d6ce43db14c00c56bbb17decfb1b13fcdacd64ee8c9bfc2c5c/_data",
                "Destination": "volume02",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            },
            {
                "Type": "volume",
                "Name": "f7dc222ae6e0973da4b6680ba419b84d9c7f4bfaf7fddbe7955906779f4bee87",
                "Source": "/var/lib/docker/volumes/f7dc222ae6e0973da4b6680ba419b84d9c7f4bfaf7fddbe7955906779f4bee87/_data",
                "Destination": "volume01",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ]
# 查看文件是否已经同步到宿主机了
[root@node02 _data]# cat /var/lib/docker/volumes/f7dc222ae6e0973da4b6680ba419b84d9c7f4bfaf7fddbe7955906779f4bee87/_data/myfile.txt  
I am volume01

5. 数据卷容器

实现多个mysql容器间的数据的同步

docker rm -f $(docker ps -aq)
docker run -it --name docker01 mydf01-centos:1.0
docker run -it --name docker02 --volumes-from docker01 mydf01-centos:1.0
# 然后在 docker01的volume01目录下创建文件,也会同步到docker02的volume01目录下。删了docker01数据也不会丢失

docker run -d --name mysql02 -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql  -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
docker run -d --name mysql03 -p 3310:3306 --volumes-from mysql02  -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

结论:

  • 数据卷容器的声明周期一直持续到没有容器使用为止
  • 一旦持久化到本地,本地的数据是不会删除的

七.DockerFile

1. DockerFile简介

DockerFile是用来构建docker镜像的的文件!命令参数脚本。

构建步骤:

  1. 编写一个dockerfile文件
  2. docker build构建成为一个镜像
  3. docker run 运行镜像
  4. docker push发布镜像(如:DockerHub、阿里云镜像仓库)

step1:进入docker hub查找centos镜像,点击centos7这个tag

在这里插入图片描述

step2:跳转到了git hub中,内容是一个dockerfile

step3:获取到的dockerfile的内容如下

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

FROM scratch
ADD centos-7-x86_64-docker.tar.xz /

LABEL \
    org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20200809" \
    org.opencontainers.image.title="CentOS Base Image" \
    org.opencontainers.image.vendor="CentOS" \
    org.opencontainers.image.licenses="GPL-2.0-only" \
    org.opencontainers.image.created="2020-08-09 00:00:00+01:00"

CMD ["/bin/bash"]

从上述官方镜像都是基础包,很多功能都没有(如jdk,redis等),我们通常会自己搭建自己的镜像。

2. DockerFile构建过程

  1. 每个指令(又叫:保留关键字),都必须是大写字母
  2. 顺序执行
  3. 使用 # 表示注释
  4. 每一个指令都会创建提交一个新的镜像层

在这里插入图片描述

Dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单

Docker镜像已经 成为了企业交互的标准,所以必须要掌握这部分内容

Dockerfile:构建文件,定义了一切的步骤,类似于源代码

DockerImage:镜像,通过Dockerfile构建生成的镜像,最终发布和运行的产品,类似于原来的war包

DockerContrainer:容器,就是镜像运行起来提供服务

3. DockerFile的指令

以前都是使用别人的镜像,现在通过下面的命令生成自己的DockerFile,构建自己的镜像

指令说明
FROM设置镜像使用的基本镜像
MAINTAINER设置镜像的作者,常用:姓名+邮箱
RUN设置构建镜像时要运行的命令
ADD设置构建镜像时要复制到镜像中的文件,会自动解压
COPY设置构建镜像时要复制到镜像中的文件,不会自动解压
WORKDIR设置RUN CMD ENTRYPOINT COPY ADD指令的工作目录
VOLUME设置容器的挂载卷,挂载的主机目录
EXPOSE设置镜像暴露的端口
CMD设置容器启动时要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT设置容器启动时要运行的命令,可以追加命令
ONBUILD当构建一个被继承 DockerFile时,运行ONBUILD的指令,触发指令
ENV设置容器的环境变量
USER设置运行RUN CMD ENTRYPOINT的用户名
ARG设置编译镜像时加入的参数
LABEL设置镜像的标签
STOPSIGNAL设置容器的退出信号量

4. 实战测试

4.1 编写dockerfile文件

vim /home/dcokerfile-20200826-01
# 设置镜像使用的基本镜像
FROM centos 

# 设置镜像的作者,常用:姓名+邮箱
MAINTAINER devin-kim<13770473170@163.com>

# 设置容器的环境变量
ENV MYPATH /usr/local
# 设置RUN CMD ENTRYPOINT COPY ADD指令的工作目录
WORKDIR $MYPATH

# 设置构建镜像时要运行的命令
RUN yum -y install vim     
RUN yum -y install net-tools

# 设置镜像暴露的端口
EXPOSE 80

# 设置容器启动时要运行的命令,只有最后一个会生效,可被替代
CMD echo $MYPATH
CMD echo "--------end------"
CMD /bin/bash 

4.2 构建镜像

docker build -f /home/dcokerfile-20200826-01 -t my-centos-01:1.0 .

4.3 测试运行

[root@node02 ~]# docker run -it my-centos-01:1.0
[root@52b24dfb0b9b local]# pwd        # 直接进入到了工作目录,因为文件中定义了WORKDIR $MYPATH
/usr/local
[root@52b24dfb0b9b local]# vim aaa    # 可正常使用,说明 RUN yum -y install vim 生效了
[root@52b24dfb0b9b local]# ifconfig   # 可正常使用,说明 RUN yum -y install net-tools 生效了
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
        RX packets 8  bytes 656 (656.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

4.4 查看镜像构建过程

控制台日志显示了镜像的构建步骤,

[root@node02 ~]# docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
my-centos-01          1.0                 d91ba57f8da9        11 minutes ago      295MB
... ...
[root@node02 ~]# docker history d91ba57f8da9
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
d91ba57f8da9        12 minutes ago      /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/bin…   0B                  
a05dfbb78a0d        12 minutes ago      /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B                  
9e8f1d61cf65        12 minutes ago      /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B                  
c269cf868f07        12 minutes ago      /bin/sh -c #(nop)  EXPOSE 80                    0B                  
4359a7a1b9b8        12 minutes ago      /bin/sh -c yum -y install net-tools             22.8MB              
5aeac9e3857c        12 minutes ago      /bin/sh -c yum -y install vim                   57.2MB              
79ff8a171068        12 minutes ago      /bin/sh -c #(nop) WORKDIR /usr/local            0B                  
125ba0dad671        12 minutes ago      /bin/sh -c #(nop)  ENV MYPATH=/usr/local        0B                  
f0c86ca7f102        12 minutes ago      /bin/sh -c #(nop)  MAINTAINER devin-kim<1377…   0B                  
0d120b6ccaa8        2 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop) ADD file:538afc0c5c964ce0d…   215MB 

4.5 CMD和ENTRYPOINT的区别

CMD

vim /home/dcokerfile-cmd-test
FROM centos
CMD ["ls","-a"]
docker build -f /home/dcokerfile-cmd-test -t cmd-test:1.0 .
docker run cmd-test:1.0
# 运行下面的命令会报错
[root@node02 ~]# docker run cmd-test:1.0 -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
ERRO[0000] error waiting for container: context canceled 

错误原因: -l 命令替换掉了dockerfile中CMD ["ls","-a"]定义的 ls -a 命令,在centos客户端只执行-l,所以会报错。

ENTRYPOINT

vim /home/dcokerfile-entrypoint-test
FROM centos
ENTRYPOINT ["ls","-a"]
docker build -f /home/dcokerfile-entrypoint-test -t entrypoint-test:1.0 .
docker run entrypoint-test:1.0 
# 下面的命令可正常执行
docker run entrypoint-test:1.0 -l

正常执行是因为ENTRYPOINT可以实现将-lls -a做了拼接,实际执行的是ls -a -l

4.6 构建Tomcat镜像

下载jdk和tomcat安装包,可使用下面的链接。上传压缩包到/home/tomcat目录下

链接:https://pan.baidu.com/s/1ja7R-iqFz8U9K-SW_6G1lw
提取码:18bs

echo "I am readme file" >> /home/tomcat/readme.txt
vim /home/tomcat/Dockerfile
# 设置镜像使用的基本镜像
FROM centos 
# 设置镜像的作者,常用:姓名+邮箱
MAINTAINER devin-kim<13770473170@163.com>

# 复制宿主机此dockerfile目录下的readme.txt文件到容器/usr/local/readme.txt
COPY readme.txt /usr/local/readme.txt
# 设置构建镜像时要复制到镜像中的文件,会自动解压
ADD jdk-8u231-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-8.5.56.tar.gz /usr/local/

# 设置构建镜像时要运行的命令
RUN yum -y install vim

# 设置容器的环境变量
ENV MYPATH /usr/local
# 设置RUN CMD ENTRYPOINT COPY ADD指令的工作目录
WORKDIR $MYPATH
# java和tomcat的环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_231
ENV CLASSPATH $JAVA_HOME/lib/dt.jar;$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME  /usr/local/apache-tomcat-8.5.56
ENV CATALINA_BASH  /usr/local/apache-tomcat-8.5.56
ENV PATH $PATH;$JAVA_HOME/bin;$CATALINA_lib;$CATALINA_HOME/bin

# 设置镜像暴露的端口
EXPOSE 8080

# 设置容器启动时要运行的命令,只有最后一个会生效,可被替代
CMD /usr/local/apache-tomcat-8.5.56/bin/startup.sh && tail -F /usr/local/apache-tomcat-8.5.56/logs/catalina.out

构建镜像

cd /home/tomcat
docker build -t my-tomcat .

上述命令有多个细节:

  1. 在此dockerfile所在的目录执行的此命令
  2. 当dockerfile的文件名为Dockerfile是,可省略-f dockerfile文件名命令行
  3. 未指定my-tomcat的Tag,默认生成的tag是latest

启动容器

# 运行后浏览器可正常访问:   http://192.168.52.110:9090/
docker run -d -p 9090:8080 --name mytomcat-cn01 \
-v /home/jyx/test:/usr/local/apache-tomcat-8.5.56/webapps/test \
-v /home/jyx/tomcatlogs:/usr/local/apache-tomcat-8.5.56/logs \
my-tomcat

启动容器是设置了两个卷挂载,我们可以在宿主机的/home/jyx/tomcatlogs去查看tomcat的日志信息,在/home/jyx/test本地编写项目发布到容器中的tomcat

mkdir -p /home/jyx/test/WEB-INF
vim /home/jyx/test/WEB-INF/web.xml  # 文件内容为下一个代码块
vim /home/jyx/test/index.jsp  # 文件内容为下面第二个代码块

# 创建好文件后访问 http://192.168.52.110:9090/test/index.jsp

以下是web.xml文件的内容

<?xml version="1.0" encoding="UTF-8"?>
   <web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                               http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
           version="2.5">

</web-app>

以下是index.jsp文件的内容

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>首页</title>
</head>
<body>
    <div class="header">
        <div class="logo">web实践</div>
        <div class ="login">
            <a href ="javascript:void(0)">登录</a>
            <span>|</span> 
            <a href ="javascript:void(0)">故事</a>
        </div>
    </div>
</body>
</html>

/home/lxh/tomcatlogs目录下查看tomcat访问日志:catalina.out

tail -f /home/jyx/tomcatlogs/catalina.out

5. 发布镜像

将镜像提交到Docker Hub

# 1、在地址https://hub.docker.com/中注册一个自己的账号
# 2、在邮箱确认后,保证这个账号能正常登陆的。
# 3、使用注册的Docker ID和密码,登录docker,
docker login -u 627574775
# 4、给镜像添加版本号 命令: docker tag 镜像id dockerhub账号的id/镜像名称:版本号
docker tag 3b75b9eb8e10 627574775/my-tomcat:1.0
# 5、带上版本号后重新提交   # 最终还是失败了,报错:denied: requested access to the resource is denied。可能是网络问题,后面再解决
docker push 627574775/my-tomcat:1.0

注意:第4步,必须要吧dockerhub账号的id作为前缀加上/再加上镜像名称,要不会报错。

将镜像发布到阿里云镜像服务

  1. 登陆阿里云;

  2. 找到容器镜像服务;

  3. 新建一个命名空间,再创建一个镜像容器;

  4. 在镜像仓库菜单创建私有的本地仓库;

  5. 查看创建的仓库,里面的步骤十分详细。【注意:复制阿里云网页文字时,可能会存在特殊字符,最好先粘贴到nodepad++上面】

6. 小结

在这里插入图片描述

八.Docker网络

1. 理解docker0

# 学习前,先清空镜像库,重头开始学习
docker rmi -f $(docker images -aq)

查看宿主机ip地址发现了3个网卡,如下

在这里插入图片描述

然后,我们启动一个tomcat容器

docker run -d -P --name tomcat01 tomcat

查看tomcat容器的ip地址

[root@node02 tomcat]# docker exec -it tomcat01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
92: eth0@if93: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
 
 # 172.17.0.2这个网络地址和宿主机172.17.0.1在同一网段,说明这两个ip都是由docker0网卡分配的
 
 # 容器内ping 172.17.0.1  可以ping通
 # 宿主机ping 172.17.0.2  可以ping通
 # 容器内ping www.baidu.com  可以ping通

结论一: 只要装了docker,就会给宿主机安装docker0网卡。每启动一个docker容器,docker就会给docker容器分配一个ip。

tomcat01容器运行后,我们重新查看宿主机ip地址,会发现多了一个网卡,如下

[root@node02 tomcat]# 
[root@node02 tomcat]# ip addr  # 多出的网卡如下
93: veth51fac72@if92: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether ee:f6:28:36:83:cb brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::ecf6:28ff:fe36:83cb/64 scope link 
       valid_lft forever preferred_lft forever
# 对比这个网卡和tomcat01容器的网卡,发现很相似,如下:
# 92: eth0@if93   93: veth51fac72@if92

# 再启动一个tomcat02容器,发现宿主机又多了一个网卡
[root@node02 tomcat]# docker run -d -P --name tomcat02 tomcat
[root@node02 tomcat]# ip addr    # 又多出了一块网卡
95: veth094e059@if94: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 7e:e1:54:94:f7:4c brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::7ce1:54ff:fe94:f74c/64 scope link 
       valid_lft forever preferred_lft forever
[root@node02 tomcat]# docker exec -it tomcat02 ip addr 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
94: eth0@if95: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
 
 # tomcat01容器和tomcat02容器也是可以ping通的

总结:我们发现容器带来的网卡都是一对一对出现的,这是桥接模式中的evth-pair技术

evth-pair就是一对的虚拟设备接口,成对出现,一段连接着协议,一段彼此相连

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

在这里插入图片描述

2. 命令行参数 --link

**问题:**我们启动的两个tomcat容器服务的名字分别是:tomcat01、tomcat02,那么能不能在tomcat01容器t通过ping tomcat02与tomcat02通信呢?

测试:

[root@node02 ~]# docker exec -it tomcat01 /bin/bash
root@fd046d49f073:/usr/local/tomcat# ping tomcat02
ping: tomcat02: Name or service not known

结果:无法ping通,那么如何通过容器的服务名与tomcat02通信呢?

# 使用 --link 命令行参数从新启动一个容器tomcat03
[root@node02 ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
4798103ff67effc462c17846feaa6cb45b46715b3d35045c7eac5cbb4f3ef3d7
[root@node02 ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.042 ms
[root@node02 ~]# docker exec -it tomcat02 ping tomcat03 
ping: tomcat03: Name or service not known

启动tomcat03容器时添加--link tomcat02后tomcat03容器可以ping通tomcat02容器,但tomcat02容器无法ping通tomcat03容器,因为启动tomcat02容器的时候没有用–link配置。原因分析如下:

查看tomcat03的元数据信息,发现了其中的多了一个Links属性,如下

[root@node02 ~]# docker inspect tomcat03   # Link属性内容如下
"Links": [
                "/tomcat02:/tomcat03/tomcat02"
            ]

分别使用上面两个命令查看/etc/hosts文件,发现tomcat03容器的hosts文件多了一行配置172.17.0.3 tomcat02 393d9b624b57,所以,link命令行参数的本质是在容器的hosts文件中添加一行ip地址与容器服务名的映射,现在已不推荐这种使用方式了

docker exec -it tomcat02 cat /etc/hosts
docker exec -it tomcat03 cat /etc/hosts

3. 自定义网络

查看所有的docker网络

[root@node02 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
1ffc06514dd2        bridge              bridge              local
76598b4f6f5e        host                host                local
1b457516b387        none                null                local
# 查看 1ffc06514dd2        bridge 的详细元数据信息
[root@node02 ~]# docker network inspect 1ffc06514dd2

Docker支持的网络模式:

  1. bridge : 桥接(默认)
  2. none : 不配置网络
  3. host : 和宿主机共享网络
  4. container : 容器内网络连通(很少用,局限性大)

创建一个网络

# 先清空现有容器
docker rm -f $(docker ps -aq)

# 我们之前启动容器,没有指定 --net 命令行参数,即,使用的是 --net bridge 这个默认值,就是docker0
# dcoker0是有弊端的:默认情况下不能使用容器服务名相互通信,要写连通只能使用--link指定,很麻烦

# 所以,我们要自定义一个网络,默认就实现了容器间通过容器服务名通信
docker network create --driver bridge --subnet 10.90.0.0/16 --gateway 10.90.0.1  mynet

–driver bridge 网络模式,默认就是bridge

–subnet 192.168.0.0/16 子网

–gateway 192.168.0.1 网关

mynet 创建的网络的名称

查看创建的网络

docker network ls
docker network inspect mynet   
# 查看宿主机ip也多了一块网卡
[root@node02 ~]# ip addr
98: br-50257d18e2ae: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:c8:9e:55:a4 brd ff:ff:ff:ff:ff:ff
    inet 10.90.0.1/16 brd 10.90.255.255 scope global br-50257d18e2ae
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c8ff:fe9e:55a4/64 scope link 
       valid_lft forever preferred_lft forever

测试

docker rm -f $(docker ps -aq)
# 使用mynet网略启动
docker run -d -P --name tomcat-mynet-01 --net mynet tomcat
docker run -d -P --name tomcat-mynet-02 --net mynet tomcat
docker run -d -P --name tomcat-mynet-03 --net mynet tomcat

# 测试使用容器服务名相互通信  结果,可以相互ping通
docker exec -it tomcat-mynet-01 ping tomcat-mynet01-02
docker exec -it tomcat-mynet-01 ping tomcat-mynet01-03
docker exec -it tomcat-mynet-02 ping tomcat-mynet01-03

# 查看容hosts,无ip映射
docker exec -it tomcat-mynet-01 cat /etc/hosts

自定义网络的优点

不同的集群使用不同的网络可以实现集群间网路隔离,保证网络的安全。比如:redis集群使用10.90网段,mysql集群使用10.91网段,两个网段间默认是不能通信的,从而保证了两个集群间的网络安全。那么,如果就想让这两个网段间相互通信,又该怎么做呢?

4. 网络连通

目前宿主机上有docker0和mynet两个网络,接下来要实现的就是:让*使用docker0网络的容器* 和 使用mynet网络的容器相互通信。

[root@node02 ~]# docker network --help   # 注意有一个connect命令行参数
Usage:  docker network COMMAND
Manage networks
Commands:
  connect     Connect a container to a network  # 连接一个容器到一个网络
  ... ...

[root@node02 ~]# docker network connect --help  # 查看如何使用
Usage:  docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
      --alias strings           Add network-scoped alias for the container
      --driver-opt strings      driver options for the network
      --ip string               IPv4 address (e.g., 172.30.100.104)
      --ip6 string              IPv6 address (e.g., 2001:db8::33)
      --link list               Add link to another container
      --link-local-ip strings   Add a link-local address for the container

测试

# 使用docker0网络启动容器tomcat01
docker run -d -P --name tomcat01 tomcat

# 将tomcat01容器连接到mynet网络
docker network connect mynet tomcat01

# 连接测试,可以相互通信
docker exec -it tomcat-mynet-01 ping tomcat01
docker exec -it tomcat01 ping tomcat-mynet-01

# 查看mynet网络信息,发现"Containers"属性下多了一个容器,就是tomcat01,也就是说,mynet网络给tomcat01也分配了一个10.90网段的ip
docker network inspect mynet

# tomcat01容器多了一个10.90网段的网卡
docker exec -it tomcat01 ip addr

5. Docker安装Redis集群

部署要求:分片+高可用+负载均衡

集群规划:6台容器主从关系如下表

主节点容器名从节点容器名
redis-1redis-4
redis-2redis-5
redis-3redis-6

创建redis集群的专用网络

docker rm -f $(docker ps -aq)
docker network create redis --subnet 172.38.0.0/16

通过脚本创建六个redis服务的配置文件

for port in $(seq 1 6);\
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done

启动redis容器

for num in $(seq 1 6);\
do \
docker run -p 637${num}:6379 -p 1637${num}:16379 --name redis-${num} \
-v /mydata/redis/node-${num}/data:/data \
-v /mydata/redis/node-${num}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1${num} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
done

进入redis-1容器创建集群

# redis容器中没有/bin/bash脚本,要用/bin/sh进入
[root@node02 conf]# docker exec -it redis-1 /bin/sh

# 创建redis集群
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1  

# 进入redis集群客户端
/data # redis-cli -c

# 查看redis集群的基本信息
127.0.0.1:6379> cluster info

# 查看redis集群的节点信息
127.0.0.1:6379> cluster nodes

测试高可用

# 停掉resis-3容器,再查看redis集群的节点信息会发现有一个原本为slaver的redis容器变成了master
docker stop redis-3

查看docker容器的资源使用状况,发现redis集群很节省资源

[root@node02 ~]# docker stats
CONTAINER ID    NAME       CPU %    MEM USAGE / LIMIT      MEM %    NET I/O            BLOCK I/O    PIDS
13d19739c036    redis-6    0.17%    16.37MiB / 3.682GiB    0.43%    8.2MB / 8.2MB      0B / 0B      4
1d0e300740f1    redis-5    0.21%    12.35MiB / 3.682GiB    0.33%    8.12MB / 8.12MB    0B / 0B      4
4462a02cef92    redis-4    0.12%    12.38MiB / 3.682GiB    0.33%    8.12MB / 8.11MB    0B / 0B      4
69251ff3379b    redis-3    0.21%    12.25MiB / 3.682GiB    0.32%    2.49MB / 2.47MB    0B / 0B      4
4a4852b47daf    redis-2    0.15%    8.438MiB / 3.682GiB    0.22%    8.26MB / 8.14MB    0B / 0B      4
5d2395a8f25b    redis-1    0.16%    8.582MiB / 3.682GiB    0.23%    8.19MB / 8.21MB    0B / 0B      5

6. SpringBoot微服务打包成docker镜像

写一个简单的springboot接口测试程序上传到/home/app01目录下

docker rm -f $(docker ps -aq)
mkdir -p /home/app01/
cd /home/app01/

# 编写Dockerfile文件,下面的代码块是文件内容
vim /home/app01/Dockerfile
FROM java:8
COPY docker-test-0.1.0.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]

编译、运行镜像

cd /home/app01/
docker build -t myapp:0.1.0 .
docker run -d -p 9090:8080 --name datavis-web myapp:0.1.0

#测试
curl localhost:9090/test

九.Docker Compose

Docker Compose 批量容器编排服务

官方文档:https://docs.docker.com/compose/

1. 官网简介

    Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features.【 Compose是定义、运行多个容器的一个管理工具,通过yaml文件来配置你的服务,使用一个简单的命令就能启动所有的服务。】

    Compose works in all environments: production, staging, development, testing, as well as CI workflows. You can learn more about each case in Common Use Cases.【所有的环境都可使用Compose】

Using Compose is basically a three-step process:【使用compose主要有三个基本步骤】

  1. Define your app’s environment with a Dockerfile .【使用Dockerfile定义应用程序的环境】
  2. Define the services that make up your app in docker-compose.yml so they can be run together in an isolated environment.
  3. Run docker-compose up and Compose starts and runs your entire app.

2. 安装compose

docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -aq)

# 官方下载下载compose 下载很慢,不推荐使用
curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 从国内网站下载
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

# 给下载的文件授权
chmod +x /usr/local/bin/docker-compose

# 查看是否安装成功
docker-compose version

3. 官网快速入门案例

https://docs.docker.com/compose/gettingstarted/

mkdir -p /home/compose
cd /home/compose
# 创建一个文件名为app.py的Python应用,下个代码块是内容
vim app.py
import time

import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)

def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)

@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)
# 创建 requirements.txt 文件
vim requirements.txt # 文件内容如下
flask
redis
# 创建 Dockerfile 文件,下个代码块是内容
vim Dockerfile
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP app.py
ENV FLASK_RUN_HOST 0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
# 创建 docker-compose.yml 文件,下个代码块是内容
vim  docker-compose.yml
version: '3'
services:
  web:
    build: .
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"
#  使用Compose构建和运行服务,运行成功后可访问  curl localhost:5000
cd /home/compose # 要在yaml文件所在的目录下运行
docker-compose up  

总结上述步骤:

  1. 开发应用 app.py
  2. 使用Dockerfile将应用打包为镜像
  3. 配置docker-compose.yml文件,(定义整个服务、需要的环境:web和redis)
  4. 启动compose项目

观察compose启动的容器,容器名是默认生成的,而且还默认生成了一个网络

4. yaml文件编写规则

compose 文件是一个定义服务、网络和卷的YAML文件。【官方文档、百度上资料和示例文件很多】

version: "3"   #docker版本,会向下兼容
services:  #服务
  redis: #第一个服务
    image: redis:alpine  #镜像名: 仓库/标签:版本
    ports:   #暴露端口信息
      - "6379"
    #添加环境变量.可以使用数组或字典两种形式
    #任何布尔值:true, false, yes, no 需要用引号括起来,以确保它们不被YAML解析器转换为True或False 
    environment:
  db:  #第二个服务
    image: postgres:9.4
    # 定义全局挂载卷
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend
    deploy:
      placement:
        constraints: [node.role == manager]

5. Compose一键部署WP博客

https://docs.docker.com/compose/wordpress/

mkdir -p /home/wp
cd /home/wp
vim /home/wp/docker-compose.yml
version: '3.3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
volumes:
    db_data: {}
# 一键启动。启动后,访问8000端口
cd /home/wp
docker-compose up -d

十.Docker Swarm

官网:https://docs.docker.com/engine/swarm/

强烈建议参照:https://www.cnblogs.com/zhujingzhi/p/9792432.html 学习,课程讲的不太细致

虚拟机节点规划

主机名内网IP工作模式CPUMEM
node02.hadoop.com192.168.52.110manager2Core4GB
node03.hadoop.com192.168.52.120manager2Core4GB
node04.hadoop.com192.168.52.130worker2Core4GB

1. 工作模式

Docker Engine 1.12 introduces swarm mode that enables you to create a cluster of one or more Docker Engines called a swarm. A swarm consists of one or more nodes: physical or virtual machines running Docker Engine 1.12 or later in swarm mode.

There are two types of nodes: managers and workers.

在这里插入图片描述

If you haven’t already, read through the swarm mode overview and key concepts.

2. 搭建集群

帮助命令:docker swarm --help

在node02主机:初始化节点

# 注意控制台日志:如果要把一个worker节点加入到这个swarm中使用下面的命令 --> 等会在node04主机执行
[root@node02 ~]# docker swarm init --advertise-addr 192.168.52.110   
Swarm initialized: current node (cxc47cw7zz2mprrm55jtsdsux) is now a manager.

To add a worker to this swarm, run the following command:  
    docker swarm join --token SWMTKN-1-6ch6e4kzbos046etjeqt0fkh2fd4b59fpre4nres3z5mi6ea3d-2tfn5oi3vf080fdf98fd3hho4 192.168.52.110:2377
    
# 注意控制台日志:如果要把一个worker节点加入到这个swarm中,先使用 docker swarm join-token manager 命令,生成shell命令  --> 等会在node03主机执行
[root@node02 ~]# docker swarm join-token manager
To add a manager to this swarm, run the following command:
    docker swarm join --token SWMTKN-1-6ch6e4kzbos046etjeqt0fkh2fd4b59fpre4nres3z5mi6ea3d-amuelt21g29yon7p27drfbuba 192.168.52.110:2377
  1. 初始化节点: docker swarm init

  2. 加入一个节点: docker swarm join

  3. 获取节点令牌:docker swarm join-token managerdocker swarm join-token worker

在node04主机:将node04主机作为worker节点加入swarm

[root@node04 ~]# docker swarm join --token SWMTKN-1-6ch6e4kzbos046etjeqt0fkh2fd4b59fpre4nres3z5mi6ea3d-2tfn5oi3vf080fdf98fd3hho4 192.168.52.110:2377
This node joined a swarm as a worker.

在node03主机:将node03主机作为manager节点加入swarm

[root@node03 ~]# docker swarm join --token SWMTKN-1-6ch6e4kzbos046etjeqt0fkh2fd4b59fpre4nres3z5mi6ea3d-amuelt21g29yon7p27drfbuba 192.168.52.110:2377
This node joined a swarm as a manager.

在node01主机:查看节点状态

[root@node02 ~]# docker node ls      # docker swarm的管理命令只能在manager节点执行
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
cxc47cw7zz2mprrm55jtsdsux *   node02.hadoop.com   Ready               Active              Leader              19.03.12
5x2ky16u31b5gauxsvpbbf8ti     node03.hadoop.com   Ready               Active              Reachable           19.03.12
9c7v4stp9hymc78if7r4usbug     node04.hadoop.com   Ready               Active                                  19.03.12

3. Raft协议

    Raft协议是一种分布式一致性协议。结合docker swarm的使用简单理解为:只有保证集群中manager节点的存活数量>1,集群才可用,所以,通常集群中manager的数量要>=3。

测试:

  • 将node02主机上的docker停掉,此时存活的manager节点只有node03这一台,数量不足两台,swarm集群就不能使用了。

    [root@node02 ~]# systemctl stop docker
    
    [root@node03 ~]# docker node ls
    Error response from daemon: rpc error: code = Unknown desc = The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online.
    
    [root@node02 ~]# systemctl start docker   # 重启docker后发现leader节点成了node03
    
  • 将node04从swarm集群中移除,再作为一个manager节点重新加入

    [root@node04 ~]# docker swarm leave   # 如果是manager 要在使用 docker swarm leave --force
    [root@node03 ~]# docker node ls   #查看节点状态发现node04状态为Down
    [root@node03 ~]# docker swarm join-token manager
    [root@node04 ~]# docker swarm join --token SWMTKN-1-6ch6e4kzbos046etjeqt0fkh2fd4b59fpre4nres3z5mi6ea3d-amuelt21g29yon7p27drfbuba 192.168.52.120:2377
    This node joined a swarm as a manager.
    [root@node03 ~]# docker node ls   #查看节点状态发现node04状态为Down
    

4. 弹性扩缩容

先重建一下docker swarm集群:

  • 在三台主机分别执行docker swarm leave --force命令
  • 重复 10.2章节 - 搭建集群 的操作
[root@node02 ~]# docker service --help

Usage:  docker service COMMAND

Manage services

Commands:
  create      Create a new service
  inspect     Display detailed information on one or more services
  logs        Fetch the logs of a service or task
  ls          List services
  ps          List the tasks of one or more services
  rm          Remove one or more services
  rollback    Revert changes to a service's configuration
  scale       Scale one or multiple replicated services
  update      Update a service

Run 'docker service COMMAND --help' for more information on a command.

入门案例:

[root@node02 ~]# docker network create -d overlay nginx_net  # 这样swarm集群的所有节点有创建了这个网络
[root@node02 ~]# docker service create --network nginx_net -p 8888:80 --name nginx01 nginx

docker rundocker service 的区别:

  • docker run启动容器,不具有扩缩容器的功能;
  • docker service 启动服务,具有扩缩容器、滚动更新的功能
[root@node02 ~]# docker service ls   # 注意,只有一个副本(即,实际只启动了一个容器)
ID                  NAME          MODE         REPLICAS    IMAGE           PORTS
qyxg2tikq1fx        nginx01       replicated    1/1         nginx:latest    *:8888->80/tcp

# 分别在 3 台主机上使用docker ps 发现只有node04节点上运行了一个容器
[root@node03 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
b8f22f457cc2        nginx:latest        "/docker-entrypoint.…"   12 minutes ago      Up 12 minutes       80/tcp              nginx01.1.lvcbtaxpy86h0bggdlpkr0ruc

副本是用来干什么用的呢?

  • 当访问量特别大的时候,一台nginx容器压力很大,可能无法处理,大量请求。我们可以通过增加nginx01的副本数量来解决这个问题
[root@node03 ~]# docker service update --replicas 3 nginx01  # 可以使用这个命令再把副本数改为1,这就是动态扩缩容
nginx01
overall progress: 3 out of 3 tasks 
1/3: running   [==================================================>] 
2/3: running   [==================================================>] 
3/3: running   [==================================================>] 
verify: Service converged 
[root@node03 ~]# curl localhost:8888
^C
[root@node03 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
qyxg2tikq1fx        nginx01             replicated          3/3                 nginx:latest        *:8888->80/tcp

# 分别在 3 台主机上使用docker ps 发现三台主机运行了一个容器

总结:

  • 服务可以使用swarm的任意一个节点的IP + 端口号去访问。 <暂时没实现,暂未找到具体原因。。。>
  • 服务可以有多个副本,动态的去扩缩容,从而实现服务的高可用和服务器的高可用(如:阿里云)。未来可以使用k8s实现。

在这里插入图片描述
在这里插入图片描述

十一.Docker Stack

待学习

十二.Docker Secret

待学习

十三.Docker Config

待学习

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐