docker学习笔记

文章目录

一、Docker概述

1、Docker历史

更详细的关于Docker历史详见百度百科

Docker 公司起初是一家名为 dotCloud 的平台即服务(Platform-as-a-Service, PaaS)提供商。底层技术上,dotCloud平台利用了 Linux 容器技术。2013年,dotCloud 的 PaaS 业务并不景气,公司需要寻求新的突破。2013 年 3 月,dotCloud 公司的创始人之一,Docker 之父,28 岁的 Solomon Hykes 正式决定,将 Docker 项目开源。开源当月, Docker 0.1 版本发布。此后的每一个月, Docker 都会发布一个版本。到 2014 年 6 月 9 日, Docker 1.0 版本正式发布。而在Docker火了之后,dotCloud公司干脆把公司名字也改成了 Docker Inc

2、Docker介绍

Docker是基于Go语言实现的云开源项目。Docker的主要目标是“Build,Ship and Run Any App , Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。

3、Docker与虚拟机对比

  • 虚拟机属于虚拟化技术;而Docker这样的容器技术,也是虚拟化技术,属于轻量级的虚拟化。

  • 虚拟机虽然可以隔离出很多“子电脑”,但占用空间更大,启动更慢;而容器技术恰好没有这些缺点。它不需要虚拟出整个操作系统,只需要虚拟一个小规模的环境(类似“沙箱”)。

  • Docker启动时间很快,几秒钟就能完成,而且,它对资源的利用率很高(一台主机可以同时运行几千个Docker容器);此外,它占的空间很小,虚拟机一般要几GB到几十GB的空间,而容器只需要MB级甚至KB级。

  • 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。

  • 每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。

Docker官网:http://www.docker.com
Docker中文网站:https://www.docker-cn.com
Docker Hub官网:https://hub.docker.com (仓库)

4、Docker平台架构

Docker 运行的基本流程为:

  1. 用户是使用 Docker Client 与 Docker Daemon 建立通信,并发送请求给后者。

  2. Docker Daemon 作为 Docker 架构中的主体部分,首先提供 Docker Server 的功能使其可以接受 Docker Client 的请求。

  3. Docker Engine 执行 Docker 内部的一系列工作,每一项工作都是以一个 Job 的形式的存在。

  4. Job 的运行过程中,当需要容器镜像时,则从 Docker Registry 中下载镜像,并通过镜像管理驱动 Graph driver将下载镜像以Graph的形式存储。

  5. 当需要为 Docker 创建网络环境时,通过网络管理驱动 Network driver 创建并配置 Docker 容器网络环境。

  6. 当需要限制 Docker 容器运行资源或执行用户指令等操作时,则通过 Execdriver 来完成。

  7. Libcontainer是一项独立的容器管理包,Network driver以及Exec driver都是通过Libcontainer来实现具体对容器进行的操作。

二、Docker安装

1、Docker的基本组成

Docker架构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzbZXkJs-1658748239283)(https://docs.docker.com/engine/images/architecture.svg)]

  • Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是 image镜像文件。只有通过这个镜像文件才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。

  • image 文件生成的容器实例,本身也是一个文件,称为镜像文件。

  • 一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器

  • 仓库是放了一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候从仓库中拉下来就可以了。

2、Docker安装

2.1 Centos安装

Centos官网安装手册:https://docs.docker.com/engine/install/centos/

常规安装

# linux内核推荐3.8及以上,这里我使用了centos8
#查看内核版本
uname -r
#查看系统版本信息
cat /etc/os-release 

# yum安装gcc相关环境
yum -y install gcc 
yum -y install gcc-c++

# 卸载旧环境,第一次安装可忽略
yum remove docker \
    docker-client \
    docker-client-latest \
    docker-common \
    docker-latest \
    docker-latest-logrotate \
    docker-logrotate \
    docker-engine
# 或者 yum remove docker*

# 安装需要的软件包
yum install -y yum-utils

# 设置仓库镜像
# 官方源是国外的,用国内源代替
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 更新yum软件包索引
# centos8使用该命令,其他版本可能会有些许不同
yum makecache

# 安装Docker CE(社区版)
yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin

在线安装和离线安装

#==============在线安装======================
# 找到所有可用docker版本列表
yum list docker-ce --showduplicates | sort -r
# 安装指定版本,用上面的版本号替换<VERSION_STRING>
# yum install docker-ce-<VERSION_STRING>.x86_64 docker-ce-cli-<VERSION_STRING>.x86_64 containerd.io
yum install -y docker-ce-19.03.9  docker-ce-cli-19.03.9 containerd.io

#==============离线安装======================
# https://download.docker.com/linux/centos/7/x86_64/stable/Packages/
rpm -ivh xxx.rpm
# 可以下载 tar,解压启动即可
# https://docs.docker.com/engine/install/binaries/#install-daemon-and-client-binaries-on-linux

服务启动

#启动
systemctl start docker
#查看版本信息
docker version
#运行hello-word,在这里因为本地镜像没有会先拉远程镜像然后启动容器,如果没有找到就报错
docker run hello-world
#查看镜像
docker images

Docker卸载

#先停止docker
systemctl stop docker
#卸载docker
yum -y remove docker-ce docker-ce-cli containerd.io docker-compose-plugin
#删除目录
rm -rf /var/lib/docker
rm -rf /var/lib/containerd

2.2 Ubuntu安装

Ubuntu官网安装手册:https://docs.docker.com/engine/install/ubuntu/

通过脚本快速安装

# 文本处理的流编辑器 -i直接修改读取的文件内容,而不是输出到终端
# sed -i 's/原字符串/新字符串/' /home/1.txt
# 下面这个是修改ubuntu的源
sudo sed -i 's/cn.archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
# 更新
sudo apt update -y
sudo apt install curl
# 抓取docker安装脚本到一个文件中
curl -fsSL get.docker.com -o get-docker.sh
# 执行脚本,通过脚本下载 推荐设置阿里云镜像下载加速 默认管理员登陆不加sudo;有警告就忽略
sudo sh get-docker.sh --mirror Aliyun

#==========解决每次输入sudo问题===========
# 将当前用户加入到docker组,这样每次使用就不需要sudo了
sudo gpasswd -a ${USER} docker
# 更新用户组,这样才能生效
newgrp - docker
sudo service docker restart

3、阿里云镜像加速

进入阿里云搜索镜像容器服务,选择管理控制台,找到镜像工具下的镜像加速器,找到相应版本的,每个人都有自己的加速器地址

# 阿里云的镜像是从docker hub来的,我们配置了加速,默认是从阿里云(缓存)下载
#创建目录
sudo mkdir -p /etc/docker
#设置加速
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://raw0m61v.mirror.aliyuncs.com"]
}
EOF
#重载配置
sudo systemctl daemon-reload
#重启
sudo systemctl restart docker

#腾讯云的镜像地址
https://mirror.ccs.tencentyun.com
#网易的镜像地址
http://hub-mirror.c.163.com

三、Docker常用命令

1、帮助命令

官方文档:https://docs.docker.com/engine/reference/commandline/docker/

docker version  # 显示 Docker 版本信息
docker info    # 显示 Docker 系统信息,包括镜像和容器数
docker <命令> --help    # 帮助命令
# 容器监控
docker stats

docker相关基础命令

命令作用
attach绑定到运行中容器的标准输入, 输出,以及错误流(这样也能进入容器内容,但是一定小心,他们操作的就是控制台,控制台的退出命令会生效,比如redis,nginx…)
build从一个 Dockerfile 文件构建镜像
commit把容器的改变 提交创建一个新的镜像
cp容器和本地文件系统间 复制 文件/文件夹
create创建新容器,但并不启动(注意与docker run 的区分)需要手动启动。start\stop
diff检查容器里文件系统结构的更改【A:添加文件或目录 D:文件或者目录删除 C:文件或者目录更改】
events获取服务器的实时事件
exec在运行时的容器内运行命令
export导出容器的文件系统为一个tar文件。commit是直接提交成镜像,export是导出成文件方便传输
history显示镜像的历史
images列出所有镜像
import导入tar的内容创建一个镜像,再导入进来的镜像直接启动不了容器。/docker-entrypoint.sh nginx -g ‘daemon o?;’ docker ps --no-trunc 看下之前的完整启动命令再用他
info显示系统信息
inspect获取docker对象的底层信息
kill杀死一个或者多个容器
load从 tar 文件加载镜像
login登录Docker registry
logout退出Docker registry
logs获取容器日志;容器以前在前台控制台能输出的所有内容,都可以看到
pause暂停一个或者多个容器
port列出容器的端口映射
ps列出所有容器
pull从registry下载一个image 或者repository
push给registry推送一个image或者repository
rename重命名一个容器
restart重启一个或者多个容器
rm移除一个或者多个容器
rmi移除一个或者多个镜像
run创建并启动容器
save把一个或者多个镜像保存为tar文件
search去docker hub寻找镜像
start启动一个或者多个容器
stats显示容器资源的实时使用状态
stop停止一个或者多个容器
tag给源镜像创建一个新的标签,变成新的镜像
top显示正在运行容器的进程
unpausepause的反操作
update更新一个或者多个docker容器配置
version显示docker的相关信息
container管理容器
image管理镜像
network管理网络
volume管理卷

在这里插入图片描述

2、镜像相关命令

docker images

# 列出本地主机上的镜像 
docker images
# 同一个仓库源可以有多个 TAG,代表这个仓库源的不同版本,我们使用REPOSITORY:TAG 定义不同的镜像,如果你不定义镜像的标签版本,docker将默认使用 lastest 镜像!
# 可选项
-a     #列出本地所有镜像
-q     #只显示镜像id

docker search

#搜索镜像
docker search mysql
# docker search 某个镜像的名称, 对应DockerHub仓库中的镜像
# 可选项
--filter=stars=50      #列出收藏数不小于指定值的镜像。

docker pull

#拉取镜像,不写tag,默认是latest
docker pull mysql
# 指定版本下载
docker pull mysql:5.7

docker rmi

# 删除镜像
docker rmi -f 镜像id      #删除单个
docker rmi -f 镜像名:tag 镜像名:tag   #删除多个
docker rmi -f $(docker images -qa)   # 删除全部

docker tag

# 给镜像重新打标签
docker tag 原镜像:标签 新镜像名:标签

docker commit

# 一般运行中的容器会常年修改,我们要使用最终的新镜像,把新的镜像放到远程docker hub,方便后来在其他机器下载
docker commit -a shawn -m "first commit" mynginx4 mynginx:v4
# 会产生游离镜像

docker save、load

# 把busybox镜像保存成tar文件
docker save -o busybox.tar busybox:latest
# 把压缩包里面的内容直接导成镜像
docker load -i busybox.tar

docker prune

# 移除游离镜像 dangling:游离镜像(没有镜像名字的)
docker image prune

# 清理docker system
docker system prune

3、容器相关命令

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

==注意:有镜像才能创建容器==

新建容器并启动

#首先拉取一下镜像
docker pull centos
# 命令
docker run [OPTIONS] IMAGE [COMMAND][ARG...]
# 常用参数说明
--name="Name"    # 给容器指定一个名字
-d          # 后台方式运行容器,并返回容器的id!
-i          # 以交互模式运行容器,通过和 -t 一起使用
-t          # 给容器重新分配一个终端,通常和 -i 一起使用
-P          # 随机端口映射(大写)
-p          # 指定端口映射(小结),一般可以有四种写法

# 使用centos进行用交互模式启动容器,在容器内执行/bin/bash命令!
docker run -it centos /bin/bash
#退出容器可以使用exit

列出所有运行的容器

# 命令
docker ps [OPTIONS]
# 常用参数说明
-a      # 列出当前所有正在运行的容器 + 历史运行过的容器 
-l      # 显示最近创建的容器
-n=?    # 显示最近n个创建的容器
-q      # 静默模式,只显示容器编号。
-s      # 显示大小

退出容器

exit       # 容器停止退出 
ctrl+P+Q     # 容器不停止退出

创建容器

# 创建容器
docker create --name myredis -p 6379:6379 redis
docker create --name myredis -p 6379(主机的端口):6379(容器的端口) redis
# 用create创建的容器要用start启动

启动停止容器

docker start (容器id or 容器名)      # 启动容器  
docker restart (容器id or 容器名)    # 重启容器  
docker stop (容器id or 容器名)      # 停止容器
docker kill (容器id or 容器名)       # 强制停止容器
docker pause (容器id or 容器名)      # 暂停容器
docker unpause (容器id or 容器名)    # 启动已暂停的容器

删除容器

docker rm 容器id                  # 删除指定容器
docker rm -f $(docker ps -a -q)  # 删除所有容器
docker ps -a -q|xargs docker rm  # 删除所有容器

docker port

# 查看容器端口映射
docker port (容器名or容器id)
docker port myredis

docker rename

# 重命名容器
docker rename 容器名 新容器名
docker rename myredis redis

docker logs

# 查看容器运行日志
docker logs (容器名or容器id)
docker logs myredis

# 实时查看
docker logs -f myredis

# 查看最后100 行日志
docker logs -f --tail 100
docker logs -f --tail 100 myredis

# 查看最近30分钟的日志
docker logs --since 30m myredis

# 查看时间段日志
docker logs -t --since="2021-11-01" --until "2021-11-05" myredis

docker stats

# 查看容器资源的实时使用状态
docker stats (容器名or容器id)
# 查看镜像/容器/数据卷所占空间
docker system df

docker update

# 更新容器配置
# 更新容器为开机自启
docker update (容器名or容器id) --restart=always

docker inspect

# 查看镜像详细信息
docker inspect (容器名or容器id)

# 查看容器详细信息
docker container inspect (容器名or容器id)

# network volume等等都可以查看
docker inspect image/network/volume ....

进入运行的容器(docker exec)

# 进容器 绑定的是控制台. 可能导致容器停止。不要用这个
docker attach 
# 进入容器
docker exec -it 容器名或者容器id /bin/bash
# 0用户,以特权方式进入容器
docker exec -it -u 0:0 --privileged myredis /bin/bash

docker export、import

# 导出镜像
# 导出的文件被import导入以后变成镜像,并不能直接启动容器,需要知道之前的启动命令
docker export -o nginx.tar 容器id
docker import nginx.tar mynginx:v6
#(docker ps --no-trunc),然后再用下面启动。
docker run -d -P mynginx:v6 /docker-entrypoint.sh nginx -g 'daemon off;'
#或者docker image inspect 看之前的镜像,把之前镜像的Entrypoint的所有和
#Cmd的连接起来就能得到启动命令

4、Docker典型命令

4.1 容器状态

Created(新建)、Up(运行中)、Pause(暂停)、Exited(退出)

4.2 docker run

  • -d: 后台运行容器,并返回容器ID;

  • -i: 以交互模式运行容器,通常与 -t 同时使用;

  • -P: 随机端口映射,容器内部端口随机映射到主机的端口

  • -p:指定端口映射,格式为:主机(宿主)端口:容器端口

  • -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用

  • –name=“nginx-lb”:为容器指定一个名称;

  • –dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;

  • –dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;

  • -h “mars”: 指定容器的hostname;

  • -e username=“ritchie”: 设置环境变量;

  • –env-file=[]: 从指定文件读入环境变量;

  • –cpuset=“0-2” or --cpuset=“0,1,2”: 绑定容器到指定CPU运行;

  • -m :设置容器使用内存最大值;

  • –net=“bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;

  • –link=[]: 添加链接到另一个容器;

  • –expose=[]: 开放一个端口或一组端口;

  • –restart , 指定重启策略,可以写–restart=awlays 总是故障重启

  • –volume , -v: 绑定一个卷。一般格式 主机文件或文件夹:虚拟机文件或文件夹

启动容器

# 命令
docker run -d 容器名
# 例子
docker run -d centos # 启动centos,使用后台方式启动
# 问题: 使用docker ps 查看,发现容器已经退出了!
# 解释:Docker容器后台运行,就必须有一个前台进程,容器运行的命令如果不是那些一直挂起的命令,就会自动退出。
# 比如,你运行了nginx服务,但是docker前台没有运行应用,这种情况下,容器启动后,会立即自杀,因为他觉得没有程序了,所以最好的情况是,将你的应用使用前台进程的方式运行启动。
#因为没有后台命令,要启动容器必须使用前台进程启动,同时--rm参数表示如果存在该容器将自动删除
docker run -it --rm centos 
# -m限制容器内存,memory-reservation软限制或者叫保留,当docker检测到主机内存不足时激活
docker run -m 512m --memory-reservation=256m nginx
# 显示cpu
docker run --cpus=2 nginx
# 指定CPU分配的优先级。默认值是1024,数字越高优先级越高
docker run --cpus=2 --cpu-shares=2000 nginx
#这将以总是的重启策略运行redis容器,这样如果容器退出,Docker将重启它重试10次
docker run --restart=on-failure:10 redis

容器的重启策略

Docker容器的重启策略如下:

  • no,默认策略,在容器退出时不重启容器

  • on-failure,在容器非正常退出时(退出状态非0),才会重启容器

  • on-failure:3,在容器非正常退出时重启容器,最多重启3次

  • always,在容器退出时总是重启容器

  • unless-stopped,在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器

docker run的退出状态码如下:

  • 0,表示正常退出

  • 非0,表示异常退出(退出状态码采用chroot标准)

  • 125,Docker守护进程本身的错误

  • 126,容器启动后,要执行的默认命令无法调用

  • 127,容器启动后,要执行的默认命令不存在

  • 其他命令状态码,容器启动后正常执行命令,退出命令时该命令的返回状态码作为容器的退出状态码

4.3 其他常用命令

# 查看日志
docker logs -f -t --tail 容器id
# 容器监控
docker stats
# 查看容器中运行的进程信息,支持 ps 命令参数
docker inspect 容器id
# 进入正在运行的容器
docker exec -it [容器id or容器名] /bin/bash
# 从容器文件拷贝
docker cp 容器id:容器内路径 目的主机路径
# docker构建
docker build -t imageName -f DockerfileName . 

5、Docker简单实例

DockerHub:https://hub.docker.com

5.1 Docker安装nginx

#搜索stars大于等于1000的nginx镜像
docker search nginx -f=stars=1000
#拉取nginx最新镜像
docker pull nginx
#运行docker容器,指定容器名字以及映射端口,容器80映射到本机3500
docker run -d --name mynginx -p 3500:80 nginx
#测试是否成功
curl localhost:3500
#进入容器终端
docker exec -it mynginx /bin/bash
#寻找nginx,/usr/share/nginx里可以修改Index.html
whereis nginx

5.2 Docker安装tomcat

# 官方文档解释
# -it :交互模式
# --rm:容器启动成功并退出以后容器就自动移除,一般在测试情况下使用!
docker run -it --rm tomcat:9.0

# 1、下载最新版tomcat镜像
docker pull tomcat
# 2、启动
docker run -d -p 8555:8080 --name tomcat9 tomcat
# 3、进入容器
docker exec -it tomcat9 /bin/bash

四、Docker镜像和容器数据卷

1、Docker镜像

1.1 镜像加载原理

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

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

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

img

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等等。

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

1.2 镜像与容器

  • 容器和镜像之间的主要区别是可写顶层。

  • 在容器中添加新数据或修改现有数据的所有写操作都存储在此可写层中。

  • 删除容器后,可写层也会被删除。 基础图像保持不变。 因为每个容器都有其自己的可写容

    器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对同一基础映像的访问,

    但具有自己的数据状态。

在这里插入图片描述

1.3 Docker镜像挑选

  • busybox:是一个集成了一百多个最常用Linux命令和工具的软件。linux工具里的瑞士军刀

  • alpine:Alpine操作系统是一个面向安全的轻型Linux发行版经典最小镜像,基于busybox,功能比

    Busybox完善。

  • slim:docker hub中有些镜像有slim标识,都是瘦身了的镜像。也要优先选择

无论是制作镜像还是下载镜像,优先选择alpine类型

1.4 磁盘容量预估

多个容器可以共享部分或全部只读图像数据。从同一图像开始的两个容器共享100%的只读数据,而具有不同图像的两个容器(具有相同的层)共享这些公共层。 因此,不能只对虚拟大小进行总计。这高估了总磁盘使用量,可能是一笔不小的数目。

# size:用于每个容器的可写层的数据量(在磁盘上)
# virtual size:容器使用的用于只读图像数据的数据量加上容器的可写图层大小
docker ps -s

1.5 Copy On Write

  • 写时复制是一种共享和复制文件的策略,可最大程度地提高效率

  • 如果文件或目录位于映像的较低层中,而另一层(包括可写层)需要对其进行读取访问,则它仅使用现有文件

  • 另一层第一次需要修改文件时(在构建映像或运行容器时),将文件复制到该层并进行修改。 这样可以将I / O和每个后续层的大小最小化

1.6 镜像commit命令

#提交容器副本使之成为一个新的镜像!
docker commit 
# 语法
docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]

2、镜像的发布

2.1 发布到Docker Hub官网

#查看docker login 命令
docker login --help
#登录
docker login -u [用户名] -p [密码]
#发布,需要带标签
docker push mytomcat:latest
#若报错denied: requested access to the resource is denied
#问题:本地镜像名无帐号信息,解决加 tag即可,另外需要加上用户名才能push
docker tag [镜像id] shawn222/mytomcat:v1
#发布
docker push shawn222/mytomcat:v1
#下线
docker logout

2.2 发布到阿里云

登录阿里云>找到容器镜像服务>创建命名空间>创建镜像仓库>点击进入这个镜像仓库

#登录
docker login --username=[账号] registry.cn-hangzhou.aliyuncs.com
#标记
docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/shawn222/ownwarehouse:[镜像版本号]
#发布
docker push registry.cn-hangzhou.aliyuncs.com/shawn222/ownwarehouse:[镜像版本号]

3、Docker Registry镜像私服

3.1 概述

docker registry官方API的地址:Registry API,然后还有使用手册:Docker Registry

官方的 Docker Hub 是一个用于管理公共镜像的地方,我们可以在上面找到我们想要的镜像,也可以把我们自己的镜像推送上去。但是,有时候我们的服务器无法访问互联网,或者你不希望将自己的镜像放到公网当中,那么你就需要 Docker Registry,它可以用来存储和管理自己的镜像

参考:搭建私有docker registry

3.2 本地基础使用

# 启动容器
 docker run -d -p 5000:5000 --restart=always --name registry registry:2
# 拉取镜像
docker pull ubuntu
# 重命名标签
docker image tag ubuntu localhost:5000/myfirstimage
# 推到私服
docker push localhost:5000/myfirstimage
# 拉取
docker pull localhost:5000/myfirstimage
# 删除
docker container stop registry && docker container rm -v registry

相关API的使用

# 查看API是否可用
curl -I -X GET localhost:5000/v2/
# 查看所有镜像
curl -X GET localhost:5000/v2/_catalog
# 获取一个镜像的manifest,<name>代表镜像名,reference可以使用tag或diges
curl -I -X GET localhost:5000/v2/<name>/manifests/<reference>
# 查看一个镜像是否存在
curl -I -X HEAD localhost:5000/v2/<name>/manifests/<reference>
# 获取某个镜像的标签列表
curl -XGET localhost:5000/v2/<name>/tags/list
# 下载单个镜像层
curl -X GET localhost:5000/v2/<name>/blobs/<digest>
# 删除一个镜像,不过删除一个镜像需要加一个header
curl -I -X DELETE localhost:5000/v2/<name>/manifests/<reference>
curl -I --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE localhost:5000/v2/<name>/manifests/<reference>
#同时还需要在配置中许可删除操作,修改配置文件,在storage下添加delete的许可
storage:
    delete:curl
        enabled: true

3.3 配置外网可访问registry

外网访问registry需要使用https协议做安全认证,不然docker pull/docker push都会失败。下面是几种配置方式

方法一:配置参数–insecure-registry

# (如果没有该文件就创建一个)
vim /etc/docker/daemon.json
# 打开后添加配置
# 修改配置文件使之支持http,不生效的话需要重启docker
{
  "registry-mirrors": ["https://raw0m61v.mirror.aliyuncs.com"],
  "insecure-registries": ["私服的IP地址或者域名:端口号"]
}

方法二:配置https服务

ssl证书,可以向CA机构申请。也可以配置自签名证书;使用CA证书访问远程registry使用域名访问,没有域名的可以vim /etc/hosts做实验

注意这里有些自签名ssl可能会失效,因为docker 版本20.10.8 版本编译使用的go 版本过高(>1.15.1)。是因为 go 1.15 版本开始废弃 CommonName 需要使用SAN证书,参考:https://www.dounaite.com/article/6278dadeac359fc91326a730.html

# ssl自签,自己颁发证书
mkdir -p /opt/data/certs && cd /opt/data/certs
# 生成私钥
openssl genrsa -out ca.key 2048
# 生成ca的根证书
openssl req -x509 -new -nodes -key ca.key -subj "/CN=hub.shawn22.xyz" -days 5000 -out ca.crt

# ca请求,注意改成自己的域名,这里我的是hub.shawn22.xyz
openssl req -new -sha256 \
-key ca.key \
-subj "/C=CN/ST=Beijing/L=Beijing/O=UnitedStack/OU=Devops/CN=hub.shawn22.xyz" \
-reqexts SAN \
-config <(cat /opt/data/openssl.cnf \
<(printf "[SAN]\nsubjectAltName=DNS:hub.shawn22.xyz")) \
-out server.csr

# 正式的证书,注意改域名
openssl x509 -req -days 365000 \
-in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(printf "subjectAltName=DNS:hub.shawn22.xyz") \
-out server.crt

# 运行docker 
docker run \
--restart=always \
--name registry \
-v /opt/data/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/server.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/ca.key \
-p 443:443 \
-v /opt/registry:/var/lib/registry \
registry:2

# 最后将证书文件server.crt复制到docker客户端的宿主机上/etc/docker/certs.d/[你的域名]/ca.crt;
# 然后重启docker客户端,注意这里指的是其他要访问私服的docker客户端


#===============================限制登录==============================
mkdir -p auth
docker run \
--entrypoint htpasswd \
httpd:2 -Bbn testuser testpassword > auth/htpasswd
# 运行
docker run \
--restart=always \
--name registry \
-v /opt/data/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v /opt/data/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/server.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/ca.key \
-p 443:443 \
-v /opt/registry:/var/lib/registry \
registry:2

方法三:nginx代理https(推荐)

使用nginx做https服务器,将数据转发给registry服务。这样,registry服务就不用配置ssl了

server {
    client_max_body_size 0;
    listen 443 ssl;
    server_name registry.yourhost.com;
    ssl_certificate   /mnt/cert/your-cert.pem;
    ssl_certificate_key  /mnt/cert/your-cert.key;
    ssl_session_timeout 10m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    location /v2/ {
      proxy_read_timeout      900;
      proxy_connect_timeout   300;
      proxy_redirect          off;

      proxy_set_header    Host                $http_host;
      proxy_set_header    X-Real-IP           $remote_addr;
      proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
      proxy_set_header  X-Forwarded-Proto $scheme;

      proxy_pass http://127.0.0.1:5000;
    }
}
  • 如果需要鉴权,在nginx.conf中配置访问权限
auth_basic "Registry realm"; 
auth_basic_user_file /mnt/htpasswd/registry.htpasswd;

3.4 Web registry

web界面的管理容器

docker run -itd -p 8080:8080 --name registry_web --link registry \
-e REGISTRY_URL=http://[ip]:5000/v2/ \
-e REGISTRY_NAME=[ip]:5000 \
-e REGISTRY_READONLY=false \
-e REGISTRY_TRUST_ANY_SSL=true \
hyper/docker-registry-web:latest

说明

  • -d,后台运行容器
  • -p 8080:8080,映射容器8080端口至宿主机8080端口
  • -restart always,设置重新启动策略,在docker重新启动时自动重新启动容器my-registry
  • -name my-registry 给容器命名
  • –link ,设置连接的仓库
  • -e REGISTRY_URL,设置仓库URL,如果使用证书创建容器,仓库URL以https开头
  • -e REGISTRY_NAME,设置仓库主机
  • -e REGISTRY_READONLY ,设置web界面是否显示读写、删除按钮
  • -e REGISTRY_TRUST_ANY_SSL,设置信任任何SSL的连接

当然还有harbor, Harbor是由VMWare公司开源的容器镜像仓库。事实上,Harbor是在Docker Registry上进行了相应的企业级扩展,可以参考:实战:使用harbor搭建Docker私有仓库

3、容器数据卷

3.1 容器挂载

容器的数据持久化,以及容器间的继承和数据共享,相当于双向绑定

  • Volumes(卷) :存储在主机文件系统的一部分中,该文件系统由Docker管理(在Linux上是"/var/lib/docker/volumes/")。 非Docker进程不应修改文件系统的这一部分。 卷是在Docker中持久存储数据的最佳方法

  • Bind mounts(绑定挂载) 可以在任何地方 存储在主机系统上。 它们甚至可能是重要的系统文件或目录。 Docker主机或Docker容器上的非Docker进程可以随时对其进行修改

  • **tmpfs mounts(临时挂载) **仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统

3.2 volume(卷)

如果将空卷装入存在文件或目录的容器中的目录中,则容器中的内容(复制)到该卷中;如果启动一个容器并指定一个尚不存在的卷,则会创建一个空卷

# ==============匿名卷使用==========
# docker将创建出匿名卷,并保存容器/etc/nginx下面的内容
# 匿名挂载的缺点,就是不好维护,通常使用命令 docker volume维护
# -v 宿主机:容器里的目录
docker run -dP -v /etc/nginx nginx

# ==============具名卷使用==========
# docker将创建出名为nginx的卷,并保存容器/etc/nginx下面的内容
docker run -dP -v nginx:/etc/nginx nginx

# 容器卷读写模式[:rw/ro]可以不填,即默认
docker run -it -v 宿主机绝对路径目录:容器内目录[:rw/ro] 镜像名


# =============== 多容器同步==========
#--volumes-from可以实现多个容器数据同步,举例
docker run -it --name docker03 --volumes-from docker01 centos:1.0


# ===============管理卷=============
# 创建卷名
docker volume create xxx
# 查看卷
docker volume ls
# 查看挂载的路径,也可以docker inspect 容器id 查看容器信息
docker volume inspect nginxconfig
# 移除无用卷,没有使用的卷
docker volume prune

3.3 bind mount

如果将绑定安装或非空卷安装到存在某些文件或目录的容器中的目录中,则这些文件或目录会被安装遮盖,就像将文件保存到Linux主机上的/mnt中一样,然后将USB驱动器安装到/mnt中。在卸载USB驱动器之前,/mnt的内容将被USB驱动器的内容遮盖。 被遮盖的文件不会被删除或更改,但是在安装绑定安装或卷时将无法访问
总结:外部目录覆盖内部容器目录内容,但不是修改。所以谨慎,外部空文件夹挂载方式也会导致容器内部是空文件夹

# bind mount和 volumes 的方式写法区别在于 
# 所有以/开始的都认为是 bind mount ,不以/开始的都认为是 volumes
docker run -dP -v /my/nginx:/etc/nginx nginx

# 警惕bind mount 方式,文件挂载没有在外部准备好内容而导致的容器启动失败问题
# 一行命令启动nginx,并且配置文件和html页面。需要知道卷的位置才能改 
docker run -d -P -v nginxconf:/etc/nginx/ -v nginxpage:/usr/share/nginx/html nginx

# 想要实现 docker run -d -P -v /root/nginxconf:/etc/nginx/ -v /root/nginxhtml:/usr/share/nginx/html --name=nginx999 nginx 
### 1、提前准备好东西 目录nginxconf,目录里面的配置文件都放里面,再调用命令 
### 2、docker cp nginxdemo:/etc/nginx /root/nginxconf #注意/的使用 
### 3、docker run -d -P -v /root/nginxconf:/etc/nginx/ -v /root/nginxhtml:/usr/share/nginx/html --name=nginx999 nginx

3.4 volume和bind mount总结

volume

  1. 多个运行的容器之间共享数据,因为挂载点如果一样存储是一样的

  2. 当容器停止或者删除,该卷依然存在,数据不会丢失

  3. 多个容器可以同时挂载相同的卷

  4. 当明确删除卷时,卷才会被删除

  5. 将容器数据存储在远程主机上或者其他存储

  6. 将数据从一台docker主机迁移到另一台时,先停止容器,然后备份卷的目录(/var/lib/docker/volumes/)

bind mount

  1. 从主机共享配置文件到容器,默认情况下,挂载主机/etc/resolv.conf到每个容器,提供dns解析

  2. 在docker主机上开发环境和容器之间共享源代码,例如,可以将maven tag目录挂在到容器中,每次在docker主机上构建maven项目时,容器都可以访问构建的项目包

  3. 当docker主机的文件或者目录结构保证与容器所需的绑定挂在一致时

区别

volume 是docker的宿主机文件系统一部分,只有docker可以进行更改,其他进程不能修改;bind mounts 是挂载在宿主机文件系统的任意位置,除了docker所有进程都可以进行修改。

  • 如果自己开发测试,用 -v 绝对路径的方式

  • 如果是生产环境建议用卷

  • 除非特殊 /bin/docker 需要挂载主机路径的则操作绝对路径挂载

3.5 docker cp

# 把容器里面的复制出来
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
# 把外部的复制进去
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH

# ==========举例===============
# 本地文件复制到容器
docker cp index.html mynginx4:/usr/share/nginx/html
# 从容器中复制出来
docker cp mynginx4:/etc/nginx/nginx.conf nginx.conf
# testdir 文件夹,容器内会创建文件夹,并复制文件夹内的所有内容
docker cp testdir nginx:/usr/share/nginx/html

3.6 rw、ro 和 不指定的区别

  • 不指定(默认)

    • 文件:

      • 宿主机 修改该文件后容器里面看不到变化

      • 容器 里面修改该文件,宿主机也看不到变化

    • 文件夹:不管是宿主机还是容器内 修改、新增、删除文件 都会相互同步

  • ro

    • 文件:容器内不能修改,会提示read-only

    • 文件夹:容器内不能修改、新增、删除文件夹中的文件,会提示read-only

  • rw

    • 文件:不管是宿主机还是容器内修改,都会相互同步;但容器内不允许删除,会提示Device or resource busy;宿主机删除文件,容器内的不会被同步

    • 文件夹:不管是宿主机还是容器内修改、新增、删除文件,都会相互同步

五、DockerFile

dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本
文档地址:https://docs.docker.com/engine/reference/builder/

1、DockerFile构建

1.1 介绍

基础知识:
1、每条保留字指令都必须为大写字母且后面要跟随至少一个参数
2、指令按照从上到下,顺序执行
3、# 表示注释
4、每条指令都会创建一个新的镜像层,并对镜像进行提交
构建流程:
1、docker从基础镜像运行一个容器
2、执行一条指令并对容器做出修改
3、执行类似 docker commit 的操作提交一个新的镜像层
4、Docker再基于刚提交的镜像运行一个新容器
5、执行dockerfile中的下一条指令直到所有指令都执行完成!

1.2 DockerFile指令

**指令 **​说明
FROM指定基础镜像
MAINTAINER指定维护者信息,已经过时,可以使用LABEL maintainer=xxx 来替代
LABEL指定维护者信息 maintainer=xxx auth=xueqimiao a=b (语法 k=v )
RUN运行命令,代表镜像构建过程中运行的命令
CMD指定启动容器时默认的命令 容器启动时要执行的命令
ENTRYPOINT指定镜像的默认入口,运行命令
EXPOSE声明镜像内服务监听的端口
ENV指定环境变量,可以在docker run的时候使用-e改变 会被固话到image的config里面
ADD复制指定的src路径下的内容到容器中的dest路径下,src可以为url会自动下载,可以为tar文件,会自动解压
COPY复制本地主机的src路径下的内容到镜像中的dest路径下,但不会自动解压等
LABEL指定生成镜像的元数据标签信息
VOLUME创建数据卷挂载点
USER指定运行容器时的用户名或UID
WORKDIR配置工作目录,为后续的RUN、CMD、ENTRYPOINT指令配置工作目录
ARG指定镜像内使用的参数(如版本号信息等),可以在build的时候,使用–build-args改变
OBBUILD配置当创建的镜像作为其他镜像的基础镜像是,所指定的创建操作指令
STOPSIGNAL容器退出的信号值
HEALTHCHECK健康检查
SHELL指定使用shell时的默认shell类型

2、常用保留字

2.1 FROM与LABEL

FROM 指定基础镜像,最好挑一些apline,slim之类的基础小镜像.指定一个已经存在的镜像作为模板,第一条必须是from;scratch镜像是一个空镜像,常用于多阶段构建
LAVBELMAINTAINER标注镜像的一些说明信息。镜像维护者的姓名和邮箱地址

  • Java应用当然是java基础镜像(SpringBoot应用)或者Tomcat基础镜像(War应用)

  • JS模块化应用一般用nodejs基础镜像

  • 其他各种语言用自己的服务器或者基础环境镜像,如python、golang、java、php等

# 注释
# 基础镜像
FROM alpine
# 给镜像加标签
LABEL auth = shawn
#LABEL multi.label1="value1" multi.label2="value2" other="value3"
#LABEL multi.label1="value1" \
#  multi.label2="value2" \
#  other="value3"

# 运行的指令 默认是使用id=0的用户 也就是root 是这个基础系统的root用户
# 代表镜像构建过程中运行的命令
RUN echo 111

# 容器启动时要执行的命令
# 镜像启动如果要运行很长、很多命令 可以准备一个sh文件 让镜像启动运行sh文件(大多数镜像这么操作)
CMD sleep 10;echo success

2.2 RUN

  • RUN指令在当前镜像层顶部的新层执行任何命令,并提交结果,生成新的镜像层。

  • 生成的提交映像将用于Dockerfile中的下一步。 分层运行RUN指令并生成提交符合Docker的核心概念,就像源代码控制一样。

  • exec形式可以避免破坏shell字符串,并使用不包含指定shell可执行文件的基本映像运行RUN命令。可以使用SHELL命令更改shell形式的默认shell。 在shell形式中,您可以使用\(反斜杠)将一条RUN指令继续到下一行

  • RUN是在 docker build时运行

# shell格式
RUN yum -y install vim
# exec格式
RUN ["可执行文件","参数1","参数2"]
# --no-cache 不使用缓存构建
docker build --no-cache  -t myalpine:v111 -f D2 .

# =======================exec和shell总结=========================
# shell 是 /bin/sh -c <command>的方式
# exec ["/bin/sh","-c",command] 的方式== shell方式
# 由于[]不是shell形式,所以不能输出变量信息,而是输出$msg。其他任何/bin/sh -c 的形式都可以输出变量信息,
# exec 默认方式不会进行变量替换

举例

RUN <command> ( shell 形式, /bin/sh -c 的方式运行,避免破坏shell字符串)
RUN ["executable", "param1", "param2"] ( exec 形式)
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
#上面等于下面这种写法
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
RUN ["/bin/bash", "-c", "echo hello"]
# 测试案例
FROM alpine
LABEL maintainer=shawn xx=aa
ENV msg='hello shawn'
RUN echo $msg
# 这种方式不会打印参数
RUN ["echo","$msg"]
# 这样会打印
RUN /bin/sh -c 'echo $msg'
RUN ["/bin/sh","-c","echo $msg"]
CMD sleep 10000

2.3 CMD和ENTRYPOINT

  • Dockerfile中只能有一条CMD指令。 如果您列出多个CMD,则只有最后一个CMD才会生效

  • CMD的主要目的是为执行中的容器提供默认值。 这些默认值可以包含可执行文件,也可以省略可执行文件,在这种情况下,您还必须指定指令

  • 会被 之后的参数替换,是在** 时运行**​

  • RUN是在 docker build时运行

  • 类似于 CMD 指令,但ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序

  • 在执行docker run的时候可以指定 ENTRYPOINT 运行所需的参数

  • 如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效

# ====================CMD 的三种写法===================
CMD ["executable","param1","param2"] ( exec 方式, 首选方式)
CMD ["param1","param2"] (为ENTRYPOINT提供默认参数)
CMD command param1 param2 ( shell 形式)
# ====================ENTRYPOINT 的两种写法===================
ENTRYPOINT ["executable", "param1", "param2"] ( exec 方式, 首选方式)
ENTRYPOINT command param1 param2 (shell 形式)
["/bin/sh","-c"] = shell
# ENTRYPOINT + CMD = 容器的完整启动命令

# 一个示例
FROM alpine
LABEL maintainer=shawn
CMD ["1111"]
CMD ["2222"]
ENTRYPOINT ["echo"]

CMD为ENTRYPOINT组合使用

如果使用CMDENTRYPOINT指令提供默认参数,则CMDENTRYPOINT指令均应使用JSON数组格式指定。ENTRYPOINT可以和CMD一起用,一般是变参才会使用 ,这里的 等于是在给****传参。当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合会变成 <><>

FROM nginx

ENTRYPOINT ["nginx","-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参

# =======================效果=================
# 运行默认
docker run nginx:test 
# 实际运行命令
nginx -c /etc/nginx/nginx.conf  
# 自定义的
docker run nginx:test /etc/nginx/nginxnew.conf
# 实际运行命令
nginx -c /etc/nginx/nginxnew.conf

2.4 ARG和ENV

  • ARG指令定义了一个变量,用户可以在构建时使用--build-arg = 传递,docker build命令会将其传递给构建器

  • --build-arg 指定参数会覆盖Dockerfile 中指定的同名参数

  • 如果用户指定了 未在Dockerfile中定义的构建参数 ,则构建会输出警告

  • ARG只在构建期有效,运行期无效

  • 在构建阶段中所有后续指令的环境中使用,并且在许多情况下也可以内联替换。

  • 引号和反斜杠可用于在值中包含空格。

  • ENV可以使用key value的写法,但是这种不建议使用了,后续版本可能会删除

  • docker run --env可以修改这些值

  • 容器运行时ENV值可以生效

  • ENV在image阶段就会被解析并持久化(docker inspect image查看)

FROM alpine
ARG arg1=22222
ENV arg2=1111111
ENV runcmd=$arg1
RUN echo $arg1 $arg2 $runcmd
CMD echo $arg1 $arg2 $runcmd
# docker run test:0.1 输出1111111 22222

2.5 ADD和COPY

COPY详解

  • --chown功能仅在用于构建Linux容器的Dockerfiles上受支持,而在Windows容器上不起作用

  • COPY指令从 src 复制新文件或目录,并将它们添加到容器的文件系统中,路径为 dest

  • 可以指定多个 src 资源,但是文件和目录的路径将被解释为相对于构建上下文的源

  • 每个 src 都可以包含通配符,并且匹配将使用Go的filepath.Match规则进行

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
# 举例
#当前上下文,以home开始的所有资源
COPY hom* /mydir/ 
# ?匹配单个字符
COPY hom?.txt /mydir/ 

#所有复制的新文件都是uid(0)/gid(0)的用户,可以使用--chown改变
COPY --chown=55:mygroup files* /somedir/
COPY --chown=10:11 files* /somedir/

ADD详解

COPY用法,不过 ADD拥有自动下载远程文件和解压的功能

  • src 路径必须在构建的上下文中; 不能使用 ../something/something 这种方式,因为docker构建的第一步是将上下文目录(和子目录)发送到docker守护程序

  • 如果 src 是URL,并且 dest 不以斜杠结尾,则从URL下载文件并将其复制到 dest ;如果 dest 以斜杠结尾,将自动推断出url的名字(保留最后一部分),保存到 dest

  • 如果 src 是目录,则将复制目录的整个内容,包括文件系统元数据

FROM alpine
# 把上下文指定的内容添加到镜像中 如果是压缩包 自动解压 如果是远程文件 自动下载
# 把当前内容复制到 这个 alpine 小系统里面 /dest
# 如果是远程文件 自动下载 如果下载的还是压缩包 不会自动解压
ADD https://download.redis.io/releases/redis-6.2.4.tar.gz /dest/
# 自动解压 ADD *.tar.gz /app/
RUN ls
# RUN 指令并没有上下文关系 RUN cd /dest RUN ls 这样还是在跟目录下 并不是进入了dest 再ls
RUN cd /dest && ls -l

2.6 WORKDIR

  • 指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点

  • WORKDIR指令为Dockerfile中跟随它的所有 RUNCMDENTRYPOINTCOPYADD 指令设置工作目录。 如果WORKDIR不存在,即使以后的Dockerfile指令中未使用它也将被创建

  • WORKDIR指令可在Dockerfile中多次使用。 如果提供了相对路径,则它将相对于上一个WORKDIR指令的路径

FROM alpine
RUN pwd && ls -l
# 为以下所有的命令运行指定了基础目录
WORKDIR /app
RUN pwd && ls -l
# 可以为进入容器指定一个默认目录
WORKDIR abc
##比如我们的nginx镜像可以做成这样
#WORKDIR /usr/share/nginx/html
# /app/abc  多个WORKDIR可以嵌套
RUN pwd && ls -l
#复制到当前目录下
COPY *.txt   ./
RUN  pwd && ls -l
CMD ping baidu.com

2.7 VOLUME

把容器的某些文件夹映射到主机外部

#可以是JSON数组
VOLUME ["/var/log/"]  
#可以直接写
VOLUME /var/log  
#可以空格分割多个
VOLUME /var/log /var/db 
# 挂载容器内指定的文件夹,不存在会创建
# 指定了VOLUME,即使启动容器没有指定-v参数,我们也会自动进行匿名卷挂载
# 用 VOLUME 声明了卷,那么以后对于卷内容的修改会被丢弃,所以,一定在volume声明之前修改内容 

2.8 USER

USER指令设置运行映像时要使用的用户名(或UID)以及可选的用户组(或GID),以及Dockerfile中USER后面所有RUNCMDENTRYPOINT指令

USER <user>[:<group>]
USER <UID>[:<GID>]
# 举例
FROM alpine
# 开用户
# RUN adduser -u 1000 -g 1000
# 以后的所有命令会用 abc:abc 来执行。有可能没有执行权限
# 容器中的ROOT虽然不是linux宿主机的真实root,但是可以改掉这个镜像的所有
USER 1000:1000
# 把复制来的文件给用户所有权
COPY --chown=1000:1000   *.txt   /a.txt
RUN ls -l /
# 不是root不能写
RUN echo 2222 >> a.txt

2.9 EXPOSE

  • EXPOSE指令通知Docker容器在运行时在指定的网络端口上进行侦听。 可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认值为TCP

  • EXPOSE指令实际上不会发布端口。 它充当构建映像的人员和运行容器的人员之间的一种文档,即有关打算发布哪些端口的信息。 要在运行容器时实际发布端口,请在docker run上使用-p标志发布并映射一个或多个端口,或使用-P标志发布所有公开的端口并将其映射到高阶端口

EXPOSE <port> [<port>/<protocol>...]
EXPOSE [80,443]
EXPOSE 80/tcp
EXPOSE 80/udp

3、构建自定义mycentosjava8镜像

3.1 编写Dockerfile文件

FROM centos
MAINTAINER shawn 
  
ENV MYPATH /usr/local 
WORKDIR $MYPATH 
  
#安装vim编辑器 
RUN yum -y install vim 
#安装ifconfig命令查看网络IP 
RUN yum -y install net-tools 
#安装java8及lib库 
RUN yum -y install glibc.i686 
RUN mkdir /usr/local/java 

## jdk下载地址:https://www.oracle.com/java/technologies/downloads/#java8
## jdk镜像地址 https://mirrors.yangxingzhen.com/jdk
## jdk的包和Dockerfile放到同一目录

#ADD 是相对路径jar,把jdk-8u171-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置 
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/ 
#配置java环境变量 
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171 
ENV JRE_HOME $JAVA_HOME/jre 
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH 
ENV PATH $JAVA_HOME/bin:$PATH 
  
EXPOSE 80 
 
CMD echo $MYPATH 
CMD echo "success--------------ok" 
CMD /bin/bash 

3.2 构建镜像

#. 表示当前目录,如果名字是Dockerfile,则可以不需要命令-f dockerfile地址
docker build -f dockerfile地址 -t 新镜像名字:TAG .
#实例
docker build -f dockerfile -t centosjava8:1.0 .
#可以查看构建历史
docker history centosjava8

3.3 虚悬镜像

仓库名、标签都是<none>的镜像,俗称dangling image。在创建或删除出现的问题,最好不要出现

from ubuntu
CMD echo 'action is success' 

# ====命令=====
docker image ls -f dangling=true
docker image prune

3.4 构建自己的tomcat镜像

1、准备文件

# 准备好以下文件,创建Dockerfile文件,用这个文件名构建时默认不用指定文件了
-rw-r--r-- 1 root root  11486964 330 18:46 apache-tomcat-9.0.45.tar.gz
-rw-r--r-- 1 root root 189756259 417 15:58 jdk-8u161-linux-x64.tar.gz
-rw-r--r-- 1 root root         0 417 16:05 README.md

2、构建Dockerfile文件

FROM centos
MAINTAINER shawn<shawn955@163.com>
#拷贝
COPY README.md /usr/local/README.md
#把java与tomcat添加到容器中,会自动解压
ADD jdk-8u161-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.45.tar.gz /usr/local/
#设置工作访问时候的WORKDIR路径,登录落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
#安装vim编辑器
RUN yum -y install vim
#配置java与tomcat环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_161
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.45
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
#容器运行时监听的端口
EXPOSE 8080
#启动时运行tomcat
CMD /usr/local/apache-tomcat-9.0.45/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.45/bin/logs/catalina.out

3、构建镜像以及启动挂载

#构建镜像
docker build -t mytomcat .
#启动并挂载容器卷,最后只需要在本地宿主机上传java文件即可
docker run -d -p 9090:8080 --name mytomcat -v /home/tomcat/test:/usr/local/apache-tomcat-9.0.45/webapps/test -v /home/tomcat/build/tomcat/tomcat9logs/:/usr/local/apache-tomcat-9.0.45/logs --privileged=true mytomcat

4、多阶段构建

4.1 multi-stage builds

https://docs.docker.com/develop/develop-images/multistage-build/

多阶段构建解决的是如何让一个镜像变得更小; 多阶段构建的典型示例

#以下所有前提 保证Dockerfile和项目在同一个文件夹
# 第一阶段:环境构建;
FROM maven:3.5.0-jdk-8-alpine AS builder
WORKDIR /app
ADD ./ /app
RUN mvn clean package -Dmaven.test.skip=true

# 第二阶段,最小运行时环境,只需要jre
FROM openjdk:8-jre-alpine
# 修改时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
LABEL maintainer="91941@qq.com"
# 从上一个阶段复制内容
COPY --from=builder /app/target/*.jar /app.jar
ENV JAVA_OPTS=""
ENV PARAMS=""
# 运行jar包
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]

4.2 Images瘦身实践

  • 选择最小的基础镜像

  • 合并RUN环节的所有指令,少生成一些层

  • RUN期间可能安装其他程序会生成临时缓存,要自行删除

  • 使用 .dockerignore 文件,排除上下文中无需参与构建的资源,比如*.imltarget/*

  • 使用多阶段构建

  • 合理使用构建缓存加速构建。–no-cache

  • 学习更多Dockerfile的写法:https://github.com/docker-library/

# SpringBoot应用最终写法
FROM openjdk:8-jdk-alpine
LABEL maintainer="shawn955@163.com"
COPY target/*.jar /app.jar
# 时区设置
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime; echo 'Asia/Shanghai' >/etc/timezone && RUN touch /app.jar

# 记录一下最后保存进来的时间
# RUN touch /app.jar
ENV JAVA_OPTS=""
ENV PARAMS=""
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]
# 运行命令 docker run -e JAVA_OPTS="-Xmx512m -Xms33 -" -e PARAMS="--spring.profiles=dev --server.port=8080" -jar /app/app.jar

六、Docker网络

网络文档:https://docs.docker.com/network/

1、docker网络详解

1.1 网络介绍

docker网络作用:容器间的互联和通信以及端口映射;容器IP变动时候可以通过服务名直接网络通信而不受到影响。在同一个网络中相互之间的服务可以使用容器名进行访问

查看本地ip地址ip addr,docker 采用了veth-pair协议,docker每启动一个容器都会生成一对虚拟接口进行通信(这样一对接口叫veth pair);如同路由器一样进行与容器间的通信。

lo        127.0.0.1         # 本机回环地址
eth0      172.20.49.98/20   # 服务器私有ip地址
docker0   172.17.0.1/16     # docker网桥

1.2 网络常用命令

# 所有命令
docker network --help
# 创建网络
docker network create XXX网络名字
# 查看网络
docker network ls
# 查看网络源数据
docker network inspect  XXX网络名字
# 删除网络
docker network rm XXX网络名字

1.3 网络模式

Docker网络模式配置说明
host模式(常用)–net=host容器和宿主机共享Network namespace
container模式-net=container:NAME_or_ID容器和另外一个容器共享Network namespace。 kubernetes中的pod就是多个容器共享一个Network namespace
Docker网络模式–net=none容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等
bridge模式**(常用)** ​–net=bridge(默认为该模式),默认使用docker0
自定义模式-net=自定义用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定
  • bridge模式:使用–network bridge指定,默认使用docker0

  • host模式:使用–network host指定

  • none模式:使用–network none指定

  • container模式:使用–network container:NAME或者容器ID指定

注:docker容器内部的ip是有可能会发生改变的

2、docker四种经典网络

2.1 bridge

Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在 内核层 连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到 同一个物理网络 。Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码, 让主机和容器之间可以通过网桥相互通信

# 查看 bridge 网络的详细信息,并通过 grep 获取名称项 
docker network inspect bridge | grep name
ifconfig | grep docker
docker run -d -p 8081:8080   --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080   --name tomcat82 billygoo/tomcat8-jdk8
docker exec -it tomcat81 bash
ip addr
docker exec -it tomcat82 bash
ip addr
# 查看 eth0

  • Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。

  • docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network

  • 网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配

    • 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);

    • 每个容器实例内部也有一块网卡,每个接口叫eth0;

    • docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配
      在这里插入图片描述

2.2 host

直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。容器将 不会获得 一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。 容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。

# 无需进行端口映射,否则会警告
docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8
# 查看一下网络信息
docker inspect tomcat83 | tail -n 20
# 此时容器的IP借用主机的,所以容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。

在这里插入图片描述

2.3 none

在none模式下,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo需要我们自己为Docker容器添加网卡、配置IP等。禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)

docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8 

# 进入容器内部查看
docker exec -it tomcat84 | tail -n 20

2.4 container

新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。

在这里插入图片描述

3、自定义网络与网络连通

3.1 自定义网络

因为使用默认网络创建的容器不能ping通容器名,而使用自定义网络可以ping通

# 查看所有网络,默认桥接
docker network ls
# 默认我们不配置网络,也就相当于默认值 --net bridge  使用的docker0 
docker run -d -P --name tomcat01 --net bridge tomcat
docker run -d -P --name tomcat02 --net bridge tomcat
# docker0网络的特点
1.  它是默认的
2.  域名访问不通
3.  可以使用--link,但是有问题
# 在容器启动时使用--link已经过时,原理是在主机的域名和ip直接配置到了hosts文件中

创建自定义网络

# 创建
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
# 查看一下是否成功创建
docker network ls
docker network inspect mynet

# 使用自定义网络创建两个容器
docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat
# 进入容器内,发现使用容器名也可以ping通
docker exec -it tomcat-net-01 ping tomcat-net-02
# docker compose 中的网络默认就是自定义网络方式

3.2 网络连通

默认的docker01网络和自定义的网络无法连通,这就是网络隔离,若要一个网段的容器与另一个网段进行通信,则需要使用docker network connect命令

# 查看帮助文档,发现connect以及详情
docker network --help
docker network connect --help
# docker network connect [OPTIONS] NETWORK CONTAINER
# 运行以下命令,tomcat01即可与mynet通信,这相当于服务器的公网和私网
docker network connect mynet tomcat01
# 检查发现容器已被自定义网络记录
docker network inspect mynet
docker run --ip 192.168.0.3 --net 自定义网络

七、Docker Compose

1、Docker Compose简介

1.1 介绍

Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml, 写好多个容器之间的调用关系 。然后,只要一个命令,就能同时启动/关闭这些容器。Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。

Compose是一个用于定义和运行多容器Docker应用程序的工具,使用Compose可以使用YAML文件来配置应用程序的服务。然后使用一个命令,从配置中创建并启动所有服务。Compose适用于所有环境:生产、暂存、开发、测试以及CI工作流。

官网地址:https://docs.docker.com/compose/

文档手册地址:https://docs.docker.com/compose/compose-file/compose-file-v3/

安装地址:https://docs.docker.com/compose/install/

1.2 核心概念

一文件

docker-compose.yml

两要素

  • 服务(service) 一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器

  • 工程(project) 由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义

1.3 Compose使用的三个步骤

  • 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件

  • 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务

  • 最后,执行docker-compose up命令 来启动并运行整个应用程序,完成一键部署上线

2、Compoese安装与使用

2.1 Compose的安装与卸载

#因为compose是docker开源第三方软件,需要额外下载
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
#获取可执行权限
sudo chmod +x /usr/local/bin/docker-compose
#查看版本
docker-compose -v
# 如果是使用的 curl 方式来安装的
sudo rm /usr/local/bin/docker-compose

2.2 docker-compose常用命令

docker-compose up/down 和 restart 的区别

  • 只要xxx.yaml文件(默认是docker-compose.yaml文件)有任何修改,一定要执行docker-compose up才会生效,执行docker-compose restart是不会生效的;

  • 如果是code发生变化,执行docker-compose restart是有效的

#查看配置文件
docker-compose config 
#启动服务,这里文件必须是docker-compose.yml
docker-compose up 
#指定xxx.yaml文件,在后台启动该服务
docker-compose -f xxx.yaml up -d  
#停止正在运行的容器,可以通过docker-compose start再次启动
docker-compose -f xxx.yaml start
#停止和删除容器、网络、卷、镜像
docker-compose -f xxx.yaml down
#docker-compose restart [SERVICE…] 重启服务
docker-compose -f xxx.yaml restart

compose命令大全

docker-compose -h                           #  查看帮助 
docker-compose up                           #  启动所有 docker-compose服务 
docker-compose up -d                        #  启动所有 docker-compose服务 并后台运行 
docker-compose down                         #  停止并删除容器、网络、卷、镜像。 
docker-compose exec  yml里面的服务id     # 进入容器实例内部  docker-compose exec  docker-compose.yml文件中写的服务id  /bin/bash 
docker-compose ps                      # 展示当前docker-compose编排过的运行的所有容器 
docker-compose top                     # 展示当前docker-compose编排过的容器进程 
 
docker-compose logs  yml里面的服务id     #  查看容器输出日志 
docker-compose config     #  检查配置 
docker-compose config -q  #  检查配置,有问题才有输出 
docker-compose restart   #  重启服务 
docker-compose start     #  启动服务 
docker-compose stop      #  停止服务 

docker-compose up -d nginx                     # 构建建启动nignx容器
docker-compose exec nginx bash            # 登录到nginx容器中
docker-compose down                              # 删除所有nginx容器,镜像
docker-compose ps                                   # 显示所有容器
docker-compose restart nginx                   # 重新启动nginx容器
docker-compose run --no-deps --rm php-fpm php -v  # 在php-fpm中不启动关联容器,并容器执行php -v 执行完成后删除容器
docker-compose build nginx                     # 构建镜像        
docker-compose build --no-cache nginx   # 不带缓存的构建
docker-compose logs  nginx                     # 查看nginx的日志 
docker-compose logs -f nginx                   # 查看nginx的实时日志
 
docker-compose config  -q                # 验证(docker-compose.yml)文件配置,当配置正确时,不输出任何内容,当文件配置错误,输出错误信息。 
docker-compose events --json nginx       # 以json的形式输出nginx的docker日志
docker-compose pause nginx                 # 暂停nignx容器
docker-compose unpause nginx              # 恢复ningx容器
docker-compose rm nginx                    # 删除容器(删除前必须关闭容器)
docker-compose stop nginx                   # 停止nignx容器
docker-compose start nginx                  # 启动nignx容器

3、yaml配置文件规则

docker-compose.yml配置文件作为核心文件。详细配置文件官网:https://docs.docker.com/compose/compose-file/compose-file-v3/

#yaml文件是三层
version: '' #1、版本
services:  #2、服务
  服务1:   
    # 服务配置,和docker很像
    # 指定容器名称。默认将会使用项目名称_服务名称_序号,注指定容器名称后,该服务将无法进行扩展(scale),
    # 因为 Docker 不允许多个容器具有相同的名称
    container_name: 
    # 每个服务都必须通过 image 指令指定镜像或 build 指令(需要 Dockerfile)等来自动构建生成镜像
    # 指定为镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉取这个镜像
    image:
    # 命令类似于docker build .
    #build: .
    # 使用command 可以覆盖容器启动后默认执行的命令,类似CMD
    command: 
    # No是默认的重启策略,它在任何情况下都不会重启容器,当always被指定时,容器总是重新启动
    restart: always
    #有依赖关系先启动依赖,最后启动本服务,这里不会等待redis完全启动后在启动
    depends_on:
      - redis
    # 设置环境变量,可以使用数组或字典两种格式。只给定名称的变量会自动获取运行 Compose 主机上对应变量的值,可以用来防止泄露不必要的数据
    # 如果变量名称或者值中用到 true|false,yes|no 等表达布尔含义的词汇,最好放到引号里
    environment: 
      user: shawn
      #- user=shawn #两种写法
    # 暴露端口,但不映射到宿主机,只被其他容器连接到此容器时使用,仅可以指定容器内部的端口为参数
    expose:
      - "3000"
      - "8000"
    # 映射端口信息。宿主端口:容器端口,或者仅仅指定容器的端口(宿主将会随机选择端口)
    ports:
       - "3000"
       - "8000:8000"
       - "49100:22"
       - "127.0.0.1:8001:8001"
    # 使用该参数,container内的root拥有真正的root权限。否则,container内的root只是外部的一个普通用户权限。
    privileged: true
    # 类似Docker中的 --add-host参数,指定额外的host 名称映射信息
    extra_hosts:
       - "googledns:8.8.8.8"
       - "dockerhub:52.1.157.61"
    # 日志,默认是json-file,下面单个文件最大不超过50M,最多5个,滚动删除
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"
    # 配置容器内核参数
    sysctls:
      # 两种方式
      #net.core.somaxconn: 1024
      #net.ipv4.tcp_syncookies: 0
      - net.core.somaxconn=1024
      - net.ipv4.tcp_syncookies=0
    # 指定容器的ulimits限制值。例如,指定最大进程数为 65535,指定文件句柄数为 20000(软限制,应用可以随时修改,不能超过硬限制) 和 40000(系统硬限制,只能 root 用户提高)。
    ulimits:
      nproc: 65535
      nofile:
        soft: 20000
        hard: 40000
    # composev3.0使用,2.0不一样。由于做了资源限制, 并且没有使用swarm, 所以要加上--compatibility参数, 不然会报错
    deploy:
      # 资源限制
      resources:
         limits:
            cpus: "2.00"
            memory: 5G
         # 硬件资源紧张时会自动激活
         reservations:
            memory: 200M
    # 健康检查
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 15s
      timeout: 10s
      retries: 3
    #为容器分配一个伪终端,就相当于 docke run -t, 相当于把 /bin/bash 当做前台进程,没有前台进程的可以使用
    tty: true
    # 数据卷所挂载路径设置。可以设置宿主机路径(HOST:CONTAINER)或加上访问模式(HOST:CONTAINER:rw)
    volumes:
     # 第一种方式 路径直接挂载到本地,比较直观,但需要管理本地的路径(常用)
     # 可以相对路径也可以绝对,即宿主机的文件映射到容器内部,会覆盖,宿主机没有会创建
     #- cache/:/tmp/cache:ro
     #第二种方式
     # 卷 (volume) 常用,默认路径下/var/lib/docker/volumes/
      - type: volume
        source: mydata
        target: /data
        volume:
          # 用于在创建卷时禁用从容器中复制数据
          nocopy: true
      # 挂载 (bind)
      - type: bind
        source: ./static
        target: /opt/app/static
    #自定义的网络名称,这里需要选择下面创建的,还可以设置ipv4和ipv6
    networks: 
      - some-network
        # 网络上此服务的别名(备用主机名)。同一网络上的其他容器可以使用服务名称或此别名连接到其中一个服务的容器。
        aliases:
         - alias1
      - other-network
      
    #自定义的网络名称,与下面自定义网段配合使用
    #networks: 
      #app_net:
        #ipv4_address: 172.16.238.10
    ......
  服务2:
    ......
#3、其他配置 网络/卷、全局规则
# 这里的末尾都要加上:
volumes:
  #docker volume ls 查看卷,不知道数据存在本地什么位置
  mydata:
networks:
  some-network:
    # 这里使用docker network ls查看是能看到别名alias,否则就是默认的 文件夹名-网络名
    name: alias
  other-network:
#下面这种方法是使用默认外部网络,这个compose内所有容器都是用这个网络
#networks:
  #default:
  #  external:
  #    name: networks_h5-net

# 下面这种方法自定义网段,gateway可以不填有默认
#networks:
  #driver: bridge
  #app_net:
    #ipam:
      #driver: default
      #config:
        #- subnet: "172.16.238.0/24"
        #  gateway: 172.16.238.1
        #- subnet: "2001:3984:3989::/64"
        #  gateway: 2001:3984:3989::1
  
configs:

其他具体可以参考:Docker-compose指令详解 / docker compose 配置文件 .yml 全面指南

4、Compose体验

官网demo地址:https://docs.docker.com/compose/gettingstarted/

1、前期准备

mkdir composetest
cd composetest
#创建py文件
vim app.py
#在py文件里写入以下内容,这里是一个简单的web服务
import time
import redis
from flask import Flask
app = Flask(__name__)
#这里host主机使用了redis域名,因为docker在同一网络下,可以使用域名进行访问
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)

之后vim requirements.txt文件,写入

flask
redis

2、创建Dockerfile

# syntax=docker/dockerfile:1
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"]

3、定义Compose服务(docker-compose.yml)

version: "3.9"
services:
  web:
    build: .
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"

4、输入****运行Compose服务,停止服务

#这是生成后的四个文件
-rw-r--r-- 1 root root 683 612 19:56 app.py
-rw-r--r-- 1 root root 111 612 19:55 docker-compose.yml
-rw-r--r-- 1 root root 281 612 19:53 Dockerfile
-rw-r--r-- 1 root root  12 612 19:53 requirements.txt

到如图所示情况后,最后在浏览器输入

http://MACHINE_VM_IP:5000

即可访问(注意端口的开放)

5、Compose实战

快速搭建wordpress的demo:https://docs.docker.com/samples/wordpress/
对于springboot实战如下

1、创建springboot项目

<!-- pom.xml的依赖,要选择web和redis依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2、编写文件

server:
  port: 8080
spring:
  redis:
  #这里主机使用了名字,因为在docker联通的网络中,可以用主机名代替ip。
    host: redis

3、编写java文件

@RestController
public class ComposeController {

    @Autowired
    RedisTemplate<String, String> redisTemplate;

    @GetMapping("view")
    public String test01(){
        redisTemplate.opsForValue().increment("view");
        return "您已访问次数"+ redisTemplate.opsForValue().get("view")+"次";
    }
}

4、maven打包

image-20210614173849606

5、在根目录编写dockerfile文件

FROM java:8
COPY ./*.jar /app.jar
CMD ["--server=8080"]
EXPOSE 8808
ENTRYPOINT ["java","-jar","./app.jar"]

6、在根目录编写文件

version: "3.9"
services:
  shawnapp:
    build:
      context: .
      dockerfile: dockerfile
    image: shawnapp
    depends_on:
      - redis
    ports:
      - "8080:8080"
  redis:
    image: redis

7、上传部署,启动服务

docker-compose.ymldockerfile和jar包上传服务器,启动后即可访问

#前台启动
docker-compose  up 
#后台启动
docker-compose  up -d
# 重新构建
docker-compose  up --build 

6、docker-compose简单示例

version: "3.7"

services:
  redis:
    image: redis:5.0.5
    container_name: redis
    ports:
      - 6379:6379
    restart: always
    volumes:
      - /home/guest/learning/conf/redis.conf:/etc/redis.conf
      - /var/lib/redis:/data/
      - /etc/localtime:/etc/localtime:ro
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"
    command: redis-server /etc/redis.conf
    ulimits:
      nproc: 65535
      nofile:
        soft: 20000
        hard: 40000
  mysql:
    image: mysql:5.7.31
    container_name: mysql
    restart: always
    ports:
      - 3307:3306
    volumes:
      - /home/guest/learning/conf/my.cnf:/etc/mysql/my.cnf
      - /home/guest/learning/mysql_db:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: admin
      LANG: C.UTF-8
      LC_ALL: C.UTF-8
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"
    ulimits:
      nproc: 65535
      nofile:
        soft: 20000
        hard: 40000
  minio:
    image: minio/minio:latest
    container_name: minio
    restart: always
    ports:
      - 9001:9000
    volumes:
      - /home/guest/learning/data/minio:/data
      - /etc/localtime:/etc/localtime:ro
    command: "server /data"
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"
    ulimits:
      nproc: 65535
      nofile:
        soft: 20000
        hard: 40000

networks:
  default:
    external:
      name: learning

docker-entrypoint.sh是docker入口点脚本,在hub的github可以看到,比如mysql的入口脚本

#!/bin/bash
set -e

... code ...

exec "$@"

八、Docker Swarm

Docker Swarm 是 Docker 的集群管理工具。它将 Docker 主机池转变为单个虚拟 Docker 主机。 Docker Swarm 提供了标准的 Docker API,所有任何已经与 Docker 守护程序通信的工具都可以使用 Swarm 轻松地扩展到多个主机,Docker Swarm代码开源在https://github.com/docker/swarm。Docker Swarm 和 Docker Compose 一样,都是 Docker 官方容器编排项目,但不同的是,Docker Compose 是一个在单个服务器或主机上创建多个容器的工具,而 Docker Swarm 则可以在多个服务器或主机上创建容器集群服务,对于微服务的部署,Docker Swarm 会更加适合。

swarm分为管理节点和工作结点,所有操作都在manager节点上,并且集群的管理节点数量至少为3,manager节点使用raft协议进行管理。

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

1、服务器的购买和docker安装

因为至少购买4台云服务器,考虑经济成本,这里我选择了腾讯云的按量计费,云以及按使用流量计费,操作系统使用centos8,云服务器硬件配置为1核2G。

yum -y install gcc 
yum -y install gcc-c++
yum install -y yum-utils
# 官方源是国外的,用国内源代替
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache
yum install -y docker-ce docker-ce-cli containerd.io
#启动docker
systemctl start docker

四台云服务器可以一个命令同时执行,这里我使用了finalShell,打开四个不同的服务器后同时进行操作

2、Swarm集群搭建

#查看网络
docker network ls
#帮助命令
docker swarm --help
docker swarm init --help
#从帮助文档库看出需要init,这里使用内网进行搭建,可以ip addr命令查看,也可以登录云平台查看内网地址
#搭建主节点manager
docker swarm init --advertise-addr 10.206.0.14
#work节点在另外两台机器输入相应命令即可
#若想创建主节点,运行,之后复制相应命令到第四台机器即可加入
docker swarm join-token manager
#查看节点情况,这里完成了二主二从,这里即可灵活加入节点
docker node ls

搭建完主节点后,会自动进行提示如何加入工作节点和新的管理节点,在新的服务器上运行即可成为work或manager节点。

3、Raft协议

Raft协议:保证大多数节点存活;当存活节点数>1时能正常运行,故集群数量必须>3台,保证高可用

#双主双从时,当停止一个manager节点时,另一个也无法正常使用
#修改为三主一从,down掉一个manager节点时,集群仍可正常访问
systemctl stop docker
systemctl start docker
#离开节点
[root@shawn2 ~]# docker swarm leave
Node left the swarm.

4、Service服务实例

使用swarm的优点是可以弹性的进行扩缩容集群,因为可以使用名字作为host主机,可以随时增加或者缩减容器;而docker-compose 是单机项目

#容器启动!不具有扩缩容器
docker run  
#服务! 具有扩缩容器,可以滚动更新
docker service 
#查看命令帮助!要多使用
docker service --help
#和docker run类似的命令,创建服务,只能在主节点上创建服务,创建完可以进行访问
docker service create --replicas 1 -p 8888:80 --name mynginx nginx
#查看某个服务,可以查看节点分布在哪几个服务器上
docker service ps mynginx
#查看服务副本数
docker service ls
#查看服务日志
docker service logs mynginx
#扩容至5个副本以及缩小至2个副本,缩扩容都可用
docker service scale mynginx=5
docker service update --replicas 2 mynginx
#删除服务
docker service rm mynginx

在这里插入图片描述

5、服务、任务简单小结

Swarm
集群的管理和编号。 docker可以初始化一个 swarm 集群,其他节点可以加入。(管理、工作者)
Node
就是一个docker节点。多个节点就组成了一个网络集群。(管理、工作者)
Service

服务 (Services) 是指一组任务的集合,服务定义了任务的属性。服务有两种模式:

  • replicated services 按照一定规则在各个工作节点上运行指定个数的任务。

  • global services 每个工作节点上运行一个任务

两种模式通过 docker service create--mode 参数指定。

Task
任务 (Task)是Swarm中的最小的调度单位,目前来说就是一个单一的容器。

image-20210616114530443

6、Docker Stack

docker-compose.yml 来一次配置、启动多个容器,在 Swarm集群中也可以使用 compose 文件 (docker-compose.yml) 来配置、启动多个服务。而docker service create 一次只能部署一个服务,使用stack部署 docker-compose.yml 我们可以一次启动多个关联的服务。

首先创建docker-compose.yml文件,首先要启动swarm集群服务,其中的 visualizer 服务提供一个可视化页面,我们可以从浏览器中很直观的查看集群中各个服务的运行节点。

version: "3"
services:
  wordpress:
    image: wordpress
    ports:
      - 80:80
    networks:
      - overlay
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
    deploy:
      mode: replicated
      replicas: 3

  db:
    image: mysql
    networks:
       - overlay
    volumes:
      - db-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: somewordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    deploy:
      placement:
        constraints: [node.role == manager]

  visualizer:
    image: dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    stop_grace_period: 1m30s
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]

volumes:
  db-data:
networks:
  overlay:
#帮助命令
docker stack --help
#集群化方式部署cpmpose,-c指定某个文件
#命令启动后,浏览器输入 ip:8080即可查看各个节点的情况
docker stack deploy -c docker-compose.yml wordpress
#移除服务
docker stack rm wordpress

7、Docker Secret

用户可以在 Swarm 集群中安全地管理密码、密钥证书等敏感数据,并允许在多个 Docker 容器实例之间共享访问指定的敏感数据。

8、Docker Config

Config无需将配置文件放入镜像或挂载到容器中就可实现对服务的配置,提高了集群的通用性。

九、Docker监控与可视化

1、轻量级容器可视化—Portainer

Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便的管理Docker环境,包括单机环境和集群环境,下面我们将用Portainer来管理Docker容器中的应用
Portainer官网安装

普通docker运行

#新建卷
docker volume create portainer_data
#运行可视化容器
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:2.11.1
#访问即可
https://localhost:9443

docker-compose安装运行

version: '3'

services:
  portainer:
    image: portainer/portainer-ce
    container_name: myPortainer
    # 下面这个命令加了就是默认连接本地docker
    command: -H unix:///var/run/docker.sock
    restart: always
    ports:
      - 9000:9000
      - 8000:8000
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data

volumes:
  portainer_data:

2、Docker容器日志查看

Dozzle 是 一款基于接口来监视Docker日志的轻量级的应用,它不存储任何日志文件,而仅仅是实时监视你的容器日志。文档:https://github.com/amir20/dozzle

# 处于安全考虑,建议将挂载的权限设为 : read-only
docker run --name dozzle -d --volume=/var/run/docker.sock:/var/run/docker.sock:ro -p 9999:8080 amir20/dozzle:latest
#访问地址为 服务器ip:端口号

3、Docker容器监控

3.1 自带监控命令

使用Docker自带的docker stats命令可以很方便地看到主机上所有容器的 CPU、内存、网络 IO、磁盘 IO、PID 资源的使用情况。但是它的缺点也很明显,因为它只能获取本机数据,无法查看历史监控数据,没有可视化展示面板

docker stats

3.2 CAdvisor监控

https://github.com/google/cadvisor

CAdvisor 是谷歌开源的一款通用的容器监控解决方案。cAdvisor 不仅可以采集机器上所有运行的容器信息,还提供了基础的查询界面和 HTTP 接口,更方便与外部系统结合。所以,cAdvisor很快成了容器指标监控最常用组件,并且 Kubernetes 也集成了 cAdvisor 作为容器监控指标的默认工具

docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  google/cadvisor:latest

docker-compose文件

cadvisor:
    image:  google/cadvisor:latest
    container_name: monitoring_cadvisor
    restart: unless-stopped
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:rw
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    ports:
      - "8080:8080"

3.3 Prometheus监控

cAdvisor 已经内置了对 prometheus 的支持,我们只需要访问http://localhost:8080/metrics即可以获取到标准的 prometheus 监控样本的输出

# 修改prometheus.yml文件
- job_name: docker_cadvisor
  static_configs:
  - targets: ['192.168.11.15:8088']

4、CAdvisor+InfluxDB+Granfana(CIG容器监控)

4.1 简介

CAdvisor监控收集+InfluxDB存储数据+Granfana展示图表

4.2 CAdvisor

CAdvisor是一个容器资源监控,包括容器的内存,CPU,网络IO,磁盘IO等监控,同时提供了一个WEB页面用于查看容器的实时运行状态。CAdvisor默认存储2分钟的数据,而且只是针对单物理机。不过,CAdvisor提供了很多数据集成接口。支持InfluxDB, Redis, Kafka, Elasticsearch等集成可以加 上对应配置将监控数据发往这些数据库存储起来

CAdvisor功能主要有两点:

  • 展示Host和容器两个层次的监控数据

  • 展示历史变化数据。

4.3 InfluxDB

InfluxDB是用Go语言编写的一个开源分布式时序、事件和指标数据库无需外部依赖。CAdvisor默认只在本机保存最近2分钟的数据,为了持款化存储数据和统一收集展示监控数据,需要将数据存储到InfluxDB中。InfluxDB是一个时序数据库,专门用于存储时序相关数据,很适合存储CAdvisor的数据。而且, CAdvisor本身 已经提供了InfluxDB的集成方法,启动容器时指定配置即可。

InfluxDB主要功能

  • 基于时间序列,支持与时间有关的相关函数(如最大、最小求和等)

  • 可度量性:你可以实时对大量数据进行计算

  • 基于事件:它支持任意的事件数据

4.3 Granfana

Grafana是一个开源的数据监控分析可视化平台,支持多种数据源配置(支持的数据源包括InfluxDB,MySQL,Elasticsearch,OpenTSDB, Graphite等)和丰富的插件及模板功能支持图表权限控制和报警

Grafan主要特性:

  • 灵活丰富的图形化选项

  • 可以混合多种风格

  • 支持白天和夜间模式

  • 多个数据源

4.4 CIG安装

version: '3.1' 
  
volumes: 
  grafana_data: {} 
  
services: 
 influxdb: 
  image: tutum/influxdb:0.9 
  restart: always 
  environment: 
    - PRE_CREATE_DB=cadvisor 
  ports: 
    - "8083:8083" 
    - "8086:8086" 
  volumes: 
    - ./data/influxdb:/data 
  
 cadvisor: 
  image: google/cadvisor 
  links: 
    - influxdb:influxsrv 
  command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086 
  restart: always 
  ports: 
    - "8080:8080" 
  volumes: 
    - /:/rootfs:ro 
    - /var/run:/var/run:rw 
    - /sys:/sys:ro 
    - /var/lib/docker/:/var/lib/docker:ro 
  
 grafana: 
  user: "104" 
  image: grafana/grafana 
  user: "104" 
  restart: always 
  links: 
    - influxdb:influxsrv 
  ports: 
    - "3000:3000" 
  volumes: 
    - grafana_data:/var/lib/grafana 
  environment: 
    - HTTP_USER=admin 
    - HTTP_PASS=admin 
    - INFLUXDB_HOST=influxsrv 
    - INFLUXDB_PORT=8086 
    - INFLUXDB_NAME=cadvisor 
    - INFLUXDB_USER=root 
    - INFLUXDB_PASS=root 

启动docker-compose文件docker-compose up

# 浏览cAdvisor收集服务,http://ip:8080/
# 第一次访问慢,请稍等
#cadvisor也有基础的图形展现功能,这里主要用它来作数据采集
# 浏览influxdb存储服务,http://ip:8083/
# 浏览grafana展现服务,http://ip:3000
# ip+3000端口的方式访问,默认帐户密码(admin/admin)

配置步骤可以参考:https://blog.xueqimiao.com/docker/3f3228/


参考文章:

https://yeasy.gitbook.io/docker_practice/

https://www.bilibili.com/video/BV1og4y1q7M4

https://www.bilibili.com/video/BV1gr4y1U7CY

https://blog.xueqimiao.com/docker/
。CAdvisor默认只在本机保存最近2分钟的数据,为了持款化存储数据和统一收集展示监控数据,需要将数据存储到InfluxDB中。InfluxDB是一个时序数据库,专门用于存储时序相关数据,很适合存储CAdvisor的数据。而且, CAdvisor本身 已经提供了InfluxDB的集成方法,启动容器时指定配置即可。

InfluxDB主要功能

  • 基于时间序列,支持与时间有关的相关函数(如最大、最小求和等)

  • 可度量性:你可以实时对大量数据进行计算

  • 基于事件:它支持任意的事件数据

4.3 Granfana

Grafana是一个开源的数据监控分析可视化平台,支持多种数据源配置(支持的数据源包括InfluxDB,MySQL,Elasticsearch,OpenTSDB, Graphite等)和丰富的插件及模板功能支持图表权限控制和报警

Grafan主要特性:

  • 灵活丰富的图形化选项

  • 可以混合多种风格

  • 支持白天和夜间模式

  • 多个数据源

4.4 CIG安装

version: '3.1' 
  
volumes: 
  grafana_data: {} 
  
services: 
 influxdb: 
  image: tutum/influxdb:0.9 
  restart: always 
  environment: 
    - PRE_CREATE_DB=cadvisor 
  ports: 
    - "8083:8083" 
    - "8086:8086" 
  volumes: 
    - ./data/influxdb:/data 
  
 cadvisor: 
  image: google/cadvisor 
  links: 
    - influxdb:influxsrv 
  command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086 
  restart: always 
  ports: 
    - "8080:8080" 
  volumes: 
    - /:/rootfs:ro 
    - /var/run:/var/run:rw 
    - /sys:/sys:ro 
    - /var/lib/docker/:/var/lib/docker:ro 
  
 grafana: 
  user: "104" 
  image: grafana/grafana 
  user: "104" 
  restart: always 
  links: 
    - influxdb:influxsrv 
  ports: 
    - "3000:3000" 
  volumes: 
    - grafana_data:/var/lib/grafana 
  environment: 
    - HTTP_USER=admin 
    - HTTP_PASS=admin 
    - INFLUXDB_HOST=influxsrv 
    - INFLUXDB_PORT=8086 
    - INFLUXDB_NAME=cadvisor 
    - INFLUXDB_USER=root 
    - INFLUXDB_PASS=root 

启动docker-compose文件docker-compose up

# 浏览cAdvisor收集服务,http://ip:8080/
# 第一次访问慢,请稍等
#cadvisor也有基础的图形展现功能,这里主要用它来作数据采集
# 浏览influxdb存储服务,http://ip:8083/
# 浏览grafana展现服务,http://ip:3000
# ip+3000端口的方式访问,默认帐户密码(admin/admin)

配置步骤可以参考:https://blog.xueqimiao.com/docker/3f3228/


参考文章:

https://yeasy.gitbook.io/docker_practice/

https://www.bilibili.com/video/BV1og4y1q7M4

https://www.bilibili.com/video/BV1gr4y1U7CY

https://blog.xueqimiao.com/docker/

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值