Docker学习
Docker学习——基础篇
docker是什么
为什么会有docker出现
它对不同的运行环境(及环境配置文件)下出现的源代码部署运行差异给出了标准化的解决方案——系统平滑移植,容器虚拟化技术。
以前:
开发提交源代码-->运维部署;面临环境不一致(mysql版本,redis补丁,java版本不一致等);集群环境的安装复杂,易出错;3.扩容,缩容不便捷;
现在:
使用docker后,可以将开发跑通的源码+配置+环境+版本统一打包成镜像文件,使用docker引擎运行即可,保证了软件可以带环境安装。
镜像文件:运行文档+配置环境+运行环境+运行依赖包+操作系统发行版+内核
docker理念
一次镜像,处处运行。
将docker对应为VMware,将应用程序+环境的镜像文件对应对系统镜像文件。
一句话
解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术。
docker与虚拟机的比较
虚拟机是带环境安装的一种解决方案。(比如复制虚拟机)
虚拟机的缺点:资源占用多;冗余步骤多;启动慢;
Docker容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统(内核级别的虚拟化),而传统虚拟机则是在硬件层面实现虚拟化。
Docker的优点:启动速度快;占用体积小;
docker是鲸鱼(容器运行引擎),每个集装箱是容器实例(加载后的镜像),容器之间进程互相隔离,但是共用宿主机的内核。
- 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
- 容器内的应用进程直接运行于宿主机的内核,容器内没有自己的内核且也没有进行硬件虚拟。因此容器比传统虚拟机更为轻便,
- 每个容器之间互相隔离,每个容器有自己的文件系统,容器之间的进程不会相互影响,能区分计算资源。
docker能干嘛
技术职级的变化:coder-->programmer-->software engineer-->DevOps engineer
devops工程师:
一次构建,随处运行:
-
更快速的应用交付和部署
-
更便捷的升级和扩缩容
-
更简单的系统运维
-
更高效的计算资源利用(省钱)
![img]()
去哪下载
官网:www.docker.com
Docker安装
centos下的docker安装
Docker并非是一个通用的容器工具,它依赖于已存在并运行的Linux内核环境。
Docker实质上是在已经运行的Linux下制造了一个隔离的文件环境,因此它执行的效率几乎等同于所部属的Linux主机。
因此,Docker必须部署在Linux内核的系统上。
windows下,一般是用VMware+Centos+Docker的方式运行。
目前docker只支持64位的CentOS7,且内核版本3.8以上。
uname命令打印当前系统的相关信息(内核版本号,硬件架构,主机名,操作系统类型等)
-r, --kernel-release 输出内核发行号
[root@qianfeng01 ~]# cat /etc/redhat-release
CentOS Linux release 7.8.2003 (Core)
[root@qianfeng01 ~]# uname -r
3.10.0-1127.el7.x86_64
Docker的基本组成
镜像(image)+容器(container)+仓库(repository)
镜像(image)
类比于编程语言里的类。
镜像是一个只读的模板,可以用来创建Docker容器,一个镜像可以创建很多容器实例。
容器(container)
类比于类的实例对象。
应用程序或服务运行在容器里面,容器类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。镜像是静态的定义,容器是镜像运行时的实体。
容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。
每个容器都是相互隔离的、保证安全的平台。
可以把容器看做是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
鲸鱼背上的一个集装箱就是一个容器实例(集装箱可以被装上去,也可以被卸下来)。比如Redis r1 = docker run Redis镜像,可以有r1,r2,r3的容器,都来自于同一个镜像模板。
仓库(repository)
仓库是集中存放镜像的文件的场所。
docker公司提供的官方registry被称为docker hub,存放各种镜像模板的地方,也是最大的公开仓库。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
小总结
Docker本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似类与对象)
image文件可以看作是容器的模板。Docker根据image文件生成容器的实例。同一个image文件,可以生成多个同时运行的容器实例。
镜像文件:image文件生成的容器实例,本身也是一个文件。
容器实例:一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器。
仓库:就是放一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候再从仓库中拉下来就可以了。
Docker平台架构图
入门版
docker deamon就是真正执行各种docker命令的守护进程

入门版工作原理
Docker是一个Client-Server架构的系统,Docker的守护进程运行在主机上,然后通过socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。容器是一个运行时环境,也就是鲸鱼上的集装箱。

进阶版

底层通信原理简述
Docker是一个C/S模式的架构,后端是一个松耦合架构,众多模块各司其职。
Docker运行的基本流程为:
- 用户使用Docker Client与Docker Daemon建立通信,并发送请求给后者。
- Docker Daemon 作为Docker 架构中的主体部分,首先提供Docker Server 的功能使其可以接受Docker Client的请求。
- Docker Engine执行Docker内部的一系列工作,每一项工作都是以一个JOB的形式存在。
- job 的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动Graph driver 将下载镜像以Graph的形式存储。
- 当需要为Docker创建网络环境时,通过网络管理驱动Network driver创建并配置Docker容器网络环境。
- 当需要限制Docker容器运行资源或执行用户指令等操作时,则通过Exec driver来完成。
- Libcontainer 是一项独立的容器管理包,Network driver以及Exec driver都是通过Libcontainer 来实现具体对容器进行的操作。
CentOS 7下的安装
主要依据官网文档:https://docs.docker.com/engine/install/centos/
1.确认linux系统版本
cat /etc/redhat-release
2.卸载旧版本
没有可以省略
3.yum安装gcc相关
GNU Compiler Collection,缩写为GCC,指一套编程語言编译器。
- 保证能上外网
- yum -y install gcc
- yum -y install gcc-c++
4.安装需要的软件包
Install the yum-utils package (which provides the yum-config-manager utility) and set up the repository. 即先装工具包
- yum install -y yum-utils
5.设置stable镜像仓库
将docker仓库地址添加进去(这里使用阿里云镜像)
- yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
6.更新yum软件包索引
使得后续的下载速度变得更快
- yum makecache fast
7.安装Docker CE
安装docker-ce ( docker 引擎,Community Edition是社区版),docker-ce-cli( docker 引擎的命令行界面),守护进程containerd
- yum install docker-ce docker-ce-cli containerd.io
8.启动docker
通过系统进程的方式启动,然后查看进程是否启动成功
systemctl start docker
ps -ef|grep docker
[root@qianfeng01 ~]# systemctl start docker
[root@qianfeng01 ~]# ps -ef|grep docker
root 1902 1 1 17:18 ? 00:00:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 2025 1452 0 17:18 pts/0 00:00:00 grep --color=auto docker
9.测试
查看客户端及服务端版本,下面有客户端和服务端两种信息,表明docker是C/S架构了。
[root@qianfeng01 ~]# docker version
Client: Docker Engine - Community
Version: 20.10.17
API version: 1.41
Go version: go1.17.11
Git commit: 100c701
Built: Mon Jun 6 23:05:12 2022
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.17
API version: 1.41 (minimum version 1.12)
Go version: go1.17.11
Git commit: a89b842
Built: Mon Jun 6 23:03:33 2022
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.6
GitCommit: 10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
runc:
Version: 1.1.2
GitCommit: v1.1.2-0-ga916309
docker-init:
Version: 0.19.0
GitCommit: de40ad0
运行hello-world镜像,如果本地没有就会去远程仓库拉取下载到本地,然后再run起来,最后自动停止运行,容器自动终止。可以看下下面的具体信息。
[root@qianfeng01 ~]# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:53f1bbee2f52c39e41682ee1d388285290c5c8a76cc92b42687eecf38e0af3f0
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
10.卸载
- systemctl stop docker
- yum remove docker-ce docker-ce-cli containerd.io
- rm -rf /var/lib/docker
- rm -rf /var/lib/containerd
阿里云镜像加速
https://cr.console.aliyun.com/cn-hangzhou/instances
控制台-容器镜像服务-(创建个人实例-设置仓库密码1小2大)-镜像加速器-获取加速器地址(https://tcyyyhr9.mirror.aliyuncs.com)-按照下面的说明配置加速器

创建目录,-p 如果没有父目录则一并创建
<<-追加写文本到末尾,使用EOF表示写完。tee表示将写入的信息输出到控制台。
重载docker守护进程,重启docker,测试docker
[root@qianfeng01 ~]# mkdir -p /etc/docker
[root@qianfeng01 ~]# tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://tcyyyhr9.mirror.aliyuncs.com"]
}
EOF
{
"registry-mirrors": ["https://tcyyyhr9.mirror.aliyuncs.com"]
}
[root@qianfeng01 ~]# systemctl daemon-reload
[root@qianfeng01 ~]# systemctl restart docker
[root@qianfeng01 ~]# docker run hello-world
docker run干了什么事

底层——为什么docker会比vm虚拟机要快
(1)docker有着比虚拟机更少的抽象层
由于docker不需要Hypervisor(虚拟机)实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。
(2)docker利用的是宿主机的内核,而不需要加载操作系统OS内核
当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。进而避免引导、寻址、加载操作系统内核返回等比较耗时耗资源的过程,当新建一个虚拟机时,虚拟机软件需要加载OS,返回新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了返回过程,因此新建一个docker容器只需要几秒钟。


Docker常用命令
帮助启动类命令
启动docker:systemctl start docker
停止docker:systemctl stop docker
重启docker:systemctl restart docker
重启docker的daemon:systemctl daemon-reload
查看docker状态:systemctl status docker
开机自启动docker:systemctl enable docker
查看docker信息:docker info(类似于docker version)
查看docker总体帮助文档:docker --help
查看docker具体命令帮助文档:docker 命令 --help
镜像命令
docker images
含义:列出本地主机上的镜像
OPTIONS:
- -a:列出本地所有的镜像(含历史映像层)
- -q:只显示镜像ID
(所有的命令都是客户端发送给docker服务端(daemon)执行的,因此必须先启动docker才能执行docker的各类命令)
[root@qianfeng01 ~]# docker images
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
[root@qianfeng01 ~]# systemctl start docker
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 10 months ago 13.3kB
- REPOSITORY:表示镜像的仓库源,镜像名
- TAG:镜像的标签(版本号)
- IMAGE ID:镜像ID
- CREATED:镜像创建的时间
- SIZE:镜像的大小
同一仓库源可以有多个TAG版本,代表这个仓库源的不同版本,我们使用REPOSITORY:TAG来定义不同的镜像。如果不指定一个镜像的版本标签(版本号),例如你只使用ubantu,docker将默认使用ubantu:latest镜像
docker search 某xxx镜像的名字
含义:去远程仓库查找某镜像(配置阿里云镜像加速后,仓库地址就是阿里云的镜像仓库地址了。目前下载docker、下载docker镜像都是阿里云的源)
OPTIONS:
- --limit 5:限制展示的数量(默认是25个)
[root@qianfeng01 ~]# docker search redis
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
redis Redis is an open source key-value store that… 11150 [OK]
bitnami/redis Bitnami Redis Docker Image 227 [OK]
bitnami/redis-sentinel Bitnami Docker Image for Redis Sentinel 39 [OK]
bitnami/redis-cluster 34
rapidfort/redis-cluster RapidFort optimized, hardened image for Redi… 15
rapidfort/redis RapidFort optimized, hardened image for Redi… 15
circleci/redis CircleCI images for Redis 14 [OK]
[root@qianfeng01 ~]# docker search redis --limit 2
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
redis Redis is an open source key-value store that… 11150 [OK]
bitnami/redis Bitnami Redis Docker Image 227 [OK]
- NAME:镜像名称
- DESCRIPTION:镜像说明
- STARS:点赞数量
- OFFICIAL:是否是官方的
- AUTOMATED:是否是自动构建的
docker pull 某xxx镜像的名字
含义:下载仓库的镜像到本地
写法:docker pull 镜像名字[:版本号] 加冒号版本号就指定,不加默认下载latest版本
[root@qianfeng01 ~]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
a2abf6c4d29d: Pull complete
c7a4e4382001: Pull complete
4044b9ba67c9: Pull complete
c8388a79482f: Pull complete
413c8bb60be2: Pull complete
1abfd3011519: Pull complete
Digest: sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 7614ae9453d1 7 months ago 113MB
hello-world latest feb5d9fea6a5 10 months ago 13.3kB
[root@qianfeng01 ~]# docker pull redis:6.0.8
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 7614ae9453d1 7 months ago 113MB
hello-world latest feb5d9fea6a5 10 months ago 13.3kB
redis 6.0.8 16ecd2772934 21 months ago 104MB
docker system df 查看镜像/容器/数据卷所占的空间
含义:查看镜像/容器/数据卷所占的空间——面试考点
(Containers表示曾经启动过多少个容器;Local Volumes:本地数据卷;Build Cache:构建缓存;)
[root@qianfeng01 ~]# docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 3 1 216.9MB 216.9MB (99%)
Containers 2 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
docker rmi 某xxx镜像的名字/ID
含义:移除本地主机上的某镜像
OPTIONS:
- -f:强制删除
(可以rmi 镜像名字,也可以rmi 镜像ID,后者无需指定版本号,更准确;可以指定多个镜像,也可以删除全部,如下示例)
(docker 命令支持参数传递,比如docker rmi -f $(docker images -qa))
[root@qianfeng01 ~]# docker rmi hello-world
Error response from daemon: conflict: unable to remove repository reference "hello-world" (must force) - container 7207a97e4a68 is using its referenced image feb5d9fea6a5
[root@qianfeng01 ~]# docker rmi -f feb5d9fea6a5
Untagged: hello-world:latest
Untagged: hello-world@sha256:53f1bbee2f52c39e41682ee1d388285290c5c8a76cc92b42687eecf38e0af3f0
Deleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 7614ae9453d1 7 months ago 113MB
hello-world latest feb5d9fea6a5 10 months ago 13.3kB
redis 6.0.8 16ecd2772934 21 months ago 104MB
[root@qianfeng01 ~]# docker rmi redis:6.0.8 feb5d9fea6a5
[root@qianfeng01 ~]# docker images -qa
7614ae9453d1
feb5d9fea6a5
[root@qianfeng01 ~]# docker rmi -f $(docker images -qa)
Untagged: redis:latest
Untagged: redis@sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339
Deleted: sha256:7614ae9453d1d87e740a2056257a6de7135c84037c367e1fffa92ae922784631
Deleted: sha256:49c70179bc923a7d48583d58e2b6c21bde1787edf42ed1f8de9e9b96e2e88e65
Deleted: sha256:396e06df5d1120368a7a8a4fd1e5467cdc2dd4083660890df078c654596ddc1c
Deleted: sha256:434d118df2e9edb51238f6ba46e9efdfa21be68e88f54787531aa39a720a0740
Deleted: sha256:2047f09c412ff06f4e2ee8a25d105055e714d99000711e27a55072e640796294
Deleted: sha256:13d71c9ccb39b206211dd1900d06aa1984b0f5ab8abaa628c70b3eb733303a65
Deleted: sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f
Untagged: hello-world:latest
Untagged: hello-world@sha256:2498fce14358aa50ead0cc6c19990fc6ff866ce72aeb5546e1d59caac3d0d60f
Deleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412
docker的虚悬镜像是什么
定义:仓库名、标签都是None的镜像,俗称虚悬镜像dangling image;
docker在构建镜像的时候有时会生成,没有用,删掉即可。
长啥样:

思考——
类比于git pull,是不是有docker push,docker commit的命令呢
容器命令
1.下载镜像,有镜像才能创建容器
docker pull ubuntu
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
[root@qianfeng01 ~]# docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
7b1a6ab2e44d: Pull complete
Digest: sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest ba6acccedd29 9 months ago 72.8MB
[root@qianfeng01 ~]# docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 0 72.78MB 72.78MB (100%)
Containers 2 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
2.新建+启动容器
[root@qianfeng01 ~]# docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run a command in a new container
-i, --interactive Keep STDIN open even if not attached
-p, --publish list Publish a container's port(s) to the host
-P, --publish-all Publish all exposed ports to random ports
-t, --tty Allocate a pseudo-TTY
使用docker run命令即可根据镜像启动容器实例,用法:docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
OPTIONS说明(常用):有些是一个减号,有些是两个减号
| 选项【OPTIONS】 | 含义 |
|---|---|
| --name="新名字" | 为容器指定一个新名字,也即服务名,使用docker ps可以查看到 |
| -d | 后台运行容器并返回容器ID,也即启动守护式容器(后台运行) |
| -i | interactive,以交互式模式运行容器,通常与-t一起使用 |
| -t | tty,为容器重新分配一个伪输入终端,与-i一起使用==>也即启动交互式容器(前台有伪终端,等待交互) |
| -P | 随机端口映射,大写P |
| -p | 指定端口映射,小写p |

启动交互式容器(前台命令行)
使用镜像ubuntu:latest以交互模式启动一个容器,在容器内执行/bin/bash命令。
docker run -it ubuntu /bin/bash
-i:交互式操作;-t:终端;ubuntu:镜像;/bin/bash:放在镜像后面的是要执行的命令,这里我们希望有个交互式的shell,因此用的是/bin/bash;要退出终端,直接输入exit;
如果直接docker run ubuntu,是会启动失败的(但是不报错),所以启动容器的时候要养成启动会执行docker ps的查看习惯
[root@qianfeng01 ~]# docker run -it ubuntu /bin/bash
root@86a05d2dd44c:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
root@86a05d2dd44c:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:21 pts/0 00:00:00 /bin/bash
root 10 1 0 08:22 pts/0 00:00:00 ps -ef
root@86a05d2dd44c:/# exit
exit
3.列出当前所有正在运行的容器
使用docker ps命令查看当前正在运行的容器。
[root@qianfeng01 ~]# docker ps --help
Usage: docker ps [OPTIONS]
List containers
Options:
-a, --all Show all containers (default shows just running)
-f, --filter filter Filter output based on conditions provided
--format string Pretty-print containers using a Go template
-n, --last int Show n last created containers (includes all states) (default -1)
-l, --latest Show the latest created container (includes all states)
--no-trunc Don't truncate output
-q, --quiet Only display container IDs
-s, --size Display total file sizes
-a:表示列出当前所有正在运行的容器+历史上运行过的
-l:显示最近创建的容器
-n:显示最近创建的n个容器
-q:静默模式,只展示容器ID
# 终端窗口1
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@qianfeng01 ~]# docker run -it --name="bain" ubuntu /bin/bash
root@11babd2b6057:/#
# 终端窗口2
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
11babd2b6057 ubuntu "/bin/bash" 7 seconds ago Up 6 seconds bain(没有指定的话就是随机分配的名字elastic_franklin)
[root@qianfeng01 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a4eb22d1ca36 ubuntu "/bin/bash" 10 minutes ago Up 10 minutes bain
11babd2b6057 ubuntu "/bin/bash" 12 minutes ago Exited (130) 10 minutes ago elastic_franklin
86a05d2dd44c ubuntu "/bin/bash" 18 minutes ago Exited (0) 18 minutes ago great_wing
7207a97e4a68 feb5d9fea6a5 "/hello" 23 hours ago Exited (0) 23 hours ago optimistic_taussig
075377e4d7d9 feb5d9fea6a5 "/hello" 23 hours ago Exited (0) 23 hours ago crazy_euler
UP状态表示正在运行的,Exited表示已经退出的
4.退出容器
两种方式:
- exit——run进去容器,exit退出,容器停止
- ctrl+P+Q——run进去容器,ctrl+q+p退出,容器不停止
5.启停删容器相关
| 命令 | 作用 |
|---|---|
| docker start 容器ID或者容器名字 | 启动已经停止的容器 |
| docker restart 容器ID或者容器名字 | 重启容器 |
| docker stop 容器ID或者容器名字 | 停止容器 |
| docker kill 容器ID或者容器名字 | 强制停止容器 |
| docker rm 容器ID | 删除已经停止的容器,对于正在运行的容器可以使用 -f 强制删除 |
| docker stats | 实时查看各个容器占用的主机资源,cpu、mem、io等等 |

docker rm $(docker ps -aq)等价于docker ps -aq | xargs docker rm
使用linux的管道符,上一个命令的结果传给xargs,给下一个命令作为参数使用
[root@qianfeng01 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
50b677df7674 ubuntu "bash" 11 minutes ago Exited (0) 4 minutes ago bain1
a4eb22d1ca36 ubuntu "/bin/bash" 46 minutes ago Exited (137) 2 minutes ago bain
11babd2b6057 ubuntu "/bin/bash" 49 minutes ago Exited (130) 47 minutes ago elastic_franklin
86a05d2dd44c ubuntu "/bin/bash" 55 minutes ago Exited (0) 54 minutes ago great_wing
7207a97e4a68 feb5d9fea6a5 "/hello" 23 hours ago Exited (0) 23 hours ago optimistic_taussig
075377e4d7d9 feb5d9fea6a5 "/hello" 24 hours ago Exited (0) 24 hours ago crazy_euler
[root@qianfeng01 ~]# docker ps -aq
50b677df7674
a4eb22d1ca36
11babd2b6057
86a05d2dd44c
7207a97e4a68
075377e4d7d9
[root@qianfeng01 ~]# docker rm $(docker ps -aq)
50b677df7674
a4eb22d1ca36
11babd2b6057
86a05d2dd44c
7207a97e4a68
075377e4d7d9
[root@qianfeng01 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
重要演示——以redis:6.0.8为例
下载redis镜像到本地
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest ba6acccedd29 9 months ago 72.8MB
[root@qianfeng01 ~]# docker pull redis:6.0.8
6.0.8: Pulling from library/redis
bb79b6b2107f: Pull complete
1ed3521a5dcb: Pull complete
5999b99cee8f: Pull complete
3f806f5245c9: Pull complete
f8a4497572b2: Pull complete
eafe3b6b8d06: Pull complete
Digest: sha256:21db12e5ab3cc343e9376d655e8eabbdbe5516801373e95a8a9e66010c5b8819
Status: Downloaded newer image for redis:6.0.8
docker.io/library/redis:6.0.8
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest ba6acccedd29 9 months ago 72.8MB
redis 6.0.8 16ecd2772934 21 months ago 104MB
1.启动守护式容器(后台服务器)
目的:在大部分情况下,我们希望docker的服务是在后台运行的,我们可以通过-d选项指定容器的后台运行模式
使用:docker run -d 镜像名
问题:
使用docker run -d ubuntu以后台模式启动Ubuntu之后,使用docker ps -a查看,会发现容器已经退出。
[root@qianfeng01 ~]# docker run ubuntu
[root@qianfeng01 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1efea74b8e2 ubuntu "bash" 5 seconds ago Exited (0) 3 seconds ago interesting_haibt
[root@qianfeng01 ~]# docker run -d ubuntu
2eb97ee2abd888cd934bdfb3052d9c74bd729e3eaa8a28600aa2b92805519b19
[root@qianfeng01 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2eb97ee2abd8 ubuntu "bash" 2 seconds ago Exited (0) 1 second ago condescending_gauss
a1efea74b8e2 ubuntu "bash" 20 seconds ago Exited (0) 18 seconds ago interesting_haibt
Docker容器后台运行,必须有一个前台进程
容器运行的命令如果不是那些一直挂起的命令(比如top,tail),就是会自动退出的。这个是docker机制的问题,比如以Nginx为例,正常情况下我们配置启动服务只需要启动响应的service即可。例如 service nginx start。但是,这样做,nginx为后台运行模式运行,就导致docker前台没有没有运行的应用,这样的容器后台启动后,会因为没事做了而立即自杀。所以最佳的解决方案是:将你要运行的程序以前台进程的形式运行,常见的就是命令行模式(-it),表示我还有交互操作,不要中断。
正确的使用——前台交互式启动
命令:docker run -it redis:6.0.8
这种情况下只要按下ctrl c,redis容器就死掉了,对于mysql,redis等服务是不建议使用这种方式启动的。
[root@qianfeng01 ~]# docker run -it redis:6.0.8
1:C 21 Jul 2022 11:48:41.872 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 21 Jul 2022 11:48:41.872 # Redis version=6.0.8, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 21 Jul 2022 11:48:41.872 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.0.8 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 1
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
1:M 21 Jul 2022 11:48:41.873 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 21 Jul 2022 11:48:41.873 # Server initialized
1:M 21 Jul 2022 11:48:41.873 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:M 21 Jul 2022 11:48:41.873 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').
1:M 21 Jul 2022 11:48:41.874 * Ready to accept connections
^C1:signal-handler (1658404145) Received SIGINT scheduling shutdown...
1:M 21 Jul 2022 11:49:05.289 # User requested shutdown...
1:M 21 Jul 2022 11:49:05.289 * Saving the final RDB snapshot before exiting.
1:M 21 Jul 2022 11:49:05.290 * DB saved on disk
1:M 21 Jul 2022 11:49:05.290 # Redis is now ready to exit, bye bye...
[root@qianfeng01 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b05a6977e76 redis:6.0.8 "docker-entrypoint.s…" 2 minutes ago Exited (0) About a minute ago xenodochial_euler
2eb97ee2abd8 ubuntu "bash" 2 hours ago Exited (0) 2 hours ago condescending_gauss
正确的使用——后台守护式启动
命令:docker run -d redis:6.0.8
-d的后台启动,-it是前台交互式启动
[root@qianfeng01 ~]# docker run -d redis:6.0.8
a29aecbabe82b84d67c23b66dfd0a062f062f0abde04487ce3b59ba9e9456c5e
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a29aecbabe82 redis:6.0.8 "docker-entrypoint.s…" 3 seconds ago Up 3 seconds 6379/tcp charming_proskuriakova
PS.现在的程序员是:编码开发微服务+上线部署容器化+时时刻刻要监控=》devops
小总结:
docker run -d 镜像名:TAG 直接后台运行
docker run -it 镜像名:TAG /bin/bash(或其他命令行模式) 前台运行,可以通过ctrl q+p的方式退出,变成后台运行
2.查看容器日志
命令:docker logs 容器ID
这样就会把redis启动界面的打印日志都显示出来(如下)
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a29aecbabe82 redis:6.0.8 "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 6379/tcp charming_proskuriakova
[root@qianfeng01 ~]# docker logs a29aecbabe82
1:C 21 Jul 2022 11:54:04.374 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 21 Jul 2022 11:54:04.375 # Redis version=6.0.8, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 21 Jul 2022 11:54:04.375 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 21 Jul 2022 11:54:04.376 * Running mode=standalone, port=6379.
1:M 21 Jul 2022 11:54:04.376 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 21 Jul 2022 11:54:04.376 # Server initialized
1:M 21 Jul 2022 11:54:04.376 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:M 21 Jul 2022 11:54:04.376 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').
1:M 21 Jul 2022 11:54:04.377 * Ready to accept connections
3.查看容器内运行的进程
命令:docker top 容器ID
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a29aecbabe82 redis:6.0.8 "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 6379/tcp charming_proskuriakova
[root@qianfeng01 ~]# docker top a29aecbabe82
UID PID PPID C STIME TTY TIME CMD
polkitd 5152 5132 0 19:54 ? 00:00:00 redis-server *:6379
4.查看容器内部运行的细节
命令:docker inspect 容器ID
包含了容器的非常详细的信息,并且以json的格式呈现
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a29aecbabe82 redis:6.0.8 "docker-entrypoint.s…" 11 minutes ago Up 11 minutes 6379/tcp charming_proskuriakova
[root@qianfeng01 ~]# docker inspect a29aecbabe82
[
{
"Id": "a29aecbabe82b84d67c23b66dfd0a062f062f0abde04487ce3b59ba9e9456c5e",
"Created": "2022-07-21T11:54:04.019345151Z",
"Path": "docker-entrypoint.sh",
"Args": [
"redis-server"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 5152,
"ExitCode": 0,
"Error": "",
"StartedAt": "2022-07-21T11:54:04.35286116Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:16ecd277293476392b71021cdd585c40ad68f4a7488752eede95928735e39df4",
"ResolvConfPath": "/var/lib/docker/containers/a29aecbabe82b84d67c23b66dfd0a062f062f0abde04487ce3b59ba9e9456c5e/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/a29aecbabe82b84d67c23b66dfd0a062f062f0abde04487ce3b59ba9e9456c5e/hostname",
"HostsPath": "/var/lib/docker/containers/a29aecbabe82b84d67c23b66dfd0a062f062f0abde04487ce3b59ba9e9456c5e/hosts",
"LogPath": "/var/lib/docker/containers/a29aecbabe82b84d67c23b66dfd0a062f062f0abde04487ce3b59ba9e9456c5e/a29aecbabe82b84d67c23b66dfd0a062f062f0abde04487ce3b59ba9e9456c5e-json.log",
"Name": "/charming_proskuriakova",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "host",
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/b5b9ffac439e63345d4e1a619484ee1a9d593541d565dadd86ed68da95a6ad44-init/diff:/var/lib/docker/overlay2/c692da36824ac27b6298295c1344d684f135c1a51f088c3cdf798eb46b3e4627/diff:/var/lib/docker/overlay2/572d9f0ece324dbfb6215a41446e0099e81ee6b79723c70be17ce415e0e863ca/diff:/var/lib/docker/overlay2/4cb5ad26c7ef19205eff5b5aec6e91d31d1ac87346d6743e3b2a4f32eeb09884/diff:/var/lib/docker/overlay2/ef98c834e0a2f20246dcaaba8cc3641df5e92a90e27f7cc5c653329b43bd948a/diff:/var/lib/docker/overlay2/a928bfabb1af6c8d241cc0b7e056545be6f6efd26303b272b5460a8913b19021/diff:/var/lib/docker/overlay2/7d965298c735e69db82bf73a3c4843b2006c6208491bf54c62f386a96fe1b02a/diff",
"MergedDir": "/var/lib/docker/overlay2/b5b9ffac439e63345d4e1a619484ee1a9d593541d565dadd86ed68da95a6ad44/merged",
"UpperDir": "/var/lib/docker/overlay2/b5b9ffac439e63345d4e1a619484ee1a9d593541d565dadd86ed68da95a6ad44/diff",
"WorkDir": "/var/lib/docker/overlay2/b5b9ffac439e63345d4e1a619484ee1a9d593541d565dadd86ed68da95a6ad44/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "volume",
"Name": "39b5bbbb5653baaf21a625e632aaebb09c248b5c8bb71bbfebe60d36203770f9",
"Source": "/var/lib/docker/volumes/39b5bbbb5653baaf21a625e632aaebb09c248b5c8bb71bbfebe60d36203770f9/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
"Config": {
"Hostname": "a29aecbabe82",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"6379/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.12",
"REDIS_VERSION=6.0.8",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.0.8.tar.gz",
"REDIS_DOWNLOAD_SHA=04fa1fddc39bd1aecb6739dd5dd73858a3515b427acd1e2947a66dadce868d68"
],
"Cmd": [
"redis-server"
],
"Image": "redis:6.0.8",
"Volumes": {
"/data": {}
},
"WorkingDir": "/data",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "9c93dd0186e6b66dd885e7c29d787dbe06569f0a8cf7de80717bac5389dca364",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"6379/tcp": null
},
"SandboxKey": "/var/run/docker/netns/9c93dd0186e6",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "8357e6fbbf72835d235caa6debc6bcfabdc4afd82a7eea23f46ca9ab964c46ab",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "04f46ad7cc0b3073072eebbe8481dcd3a3acb9be22ff9036bc1cc060989fa3ba",
"EndpointID": "8357e6fbbf72835d235caa6debc6bcfabdc4afd82a7eea23f46ca9ab964c46ab",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]
5.进入正在运行的容器并以命令行形式交互-面试考点
方式1:docker exec -it 容器ID /bin/bash(或者其他命令行模式)
推荐使用!
-d:detach,分离、脱离,表示以后台方式运行命令
-i:interactive,交互的,表示以前台交互的方式输入命令运行
-t:tty,终端
[root@qianfeng01 ~]# docker exec --help
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Run a command in a running container
Options:
-d, --detach Detached mode: run command in the background
--detach-keys string Override the key sequence for detaching a container
-e, --env list Set environment variables
--env-file list Read in a file of environment variables
-i, --interactive Keep STDIN open even if not attached
--privileged Give extended privileges to the command
-t, --tty Allocate a pseudo-TTY
-u, --user string Username or UID (format: <name|uid>[:<group|gid>])
-w, --workdir string Working directory inside the container
通过redis-cli -p 6379 进入redis的命令行模式,操作redis,并通过exit退出redis;
再通过exit命令退出容器,并且不是停止容器形式的退出。
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a29aecbabe82 redis:6.0.8 "docker-entrypoint.s…" 30 minutes ago Up 30 minutes 6379/tcp charming_proskuriakova
[root@qianfeng01 ~]# docker exec -it a29aecbabe82 /bin/bash
root@a1b203b5efb3:/data# redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> exit
root@a29aecbabe82:/data# exit
exit
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a29aecbabe82 redis:6.0.8 "docker-entrypoint.s…" 30 minutes ago Up 30 minutes 6379/tcp charming_proskuriakova
方式2:docker attach 容器ID
[root@qianfeng01 ~]# docker attach --help
Usage: docker attach [OPTIONS] CONTAINER
Attach local standard input, output, and error streams to a running container
Options:
--detach-keys string Override the key sequence for detaching a container
--no-stdin Do not attach STDIN
--sig-proxy Proxy all received signals to the process (default true)
两种进入容器方式的区别:
exec:是在容器中打开新的终端,并且可以启动新的进程;用exit退出,不会导致容器的停止。——推荐使用
attach:是直接进入容器的终端,不会启动新的进程;用exit退出,会导致容器的停止
6.从容器内拷贝文件到主机上
文件方向:容器==>主机
命令:docker cp 容器ID:容器内路径 目的主机路径
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest ba6acccedd29 9 months ago 72.8MB
redis 6.0.8 16ecd2772934 21 months ago 104MB
[root@qianfeng01 ~]# docker run -it ubuntu /bin/bash
# ctrl p q 保证后台运行时退出
root@a22010165969:/# [root@qianfeng01 ~]#
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a22010165969 ubuntu "/bin/bash" 13 seconds ago Up 12 seconds infallible_curran
[root@qianfeng01 ~]# docker exec -it a22010165969 /bin/bash
root@a22010165969:/# touch /tmp/a.txt
root@a22010165969:/# exit
exit
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a22010165969 ubuntu "/bin/bash" About a minute ago Up About a minute infallible_curran
[root@qianfeng01 ~]# docker cp a22010165969:/tmp/a.txt /tmp
[root@qianfeng01 ~]# ls /tmp
a.txt vmware-root_663-4022243318
pyscript vmware-root_673-3988556249
7.导入和导出容器
目的:打包容器成镜像,给其他人使用,或者将当前状态的容器作一个整体的备份(先export在import)
export——导出容器的内容留作为一个tar归档文件
import——从tar包中的内容创建一个新的文件系统再导入为镜像
命令:
docker export 容器ID > 文件名.tar
cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
镜像用户/镜像名:镜像版本号 的作用在docker images时显示。
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a22010165969 ubuntu "/bin/bash" 17 minutes ago Up 17 minutes infallible_curran
[root@qianfeng01 ~]# docker export a22010165969 > bain_ubuntu.tar
[root@qianfeng01 ~]# ls
anaconda-ks.cfg bain_ubuntu.tar project py3_virtualenv softwares zookeeper.out
[root@qianfeng01 ~]# docker rm -f a22010165969
a22010165969
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@qianfeng01 ~]# cat bain_ubuntu.tar | docker import bain/bain_ubuntu:0.0.1
open bain/bain_ubuntu:0.0.1: no such file or directory
[root@qianfeng01 ~]# cat bain_ubuntu.tar | docker import - bain/bain_ubuntu:0.0.1
sha256:2dd253ec3b5c9e13f872dd476ff0557489760d5c7635466dc803a3f3308b0335
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
bain/bain_ubuntu 0.0.1 2dd253ec3b5c 9 seconds ago 72.8MB
ubuntu latest ba6acccedd29 9 months ago 72.8MB
redis 6.0.8 16ecd2772934 21 months ago 104MB
[root@qianfeng01 ~]# docker run -it 2dd253ec3b5c /bin/bash
root@9a632e4d60eb:/# ls /tmp
a.txt
root@9a632e4d60eb:/# [root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9a632e4d60eb 2dd253ec3b5c "/bin/bash" 16 seconds ago Up 15 seconds youthful_hertz
docker常用命令:

Docker镜像
镜像是什么:
镜像是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成docker容器实例。(类似类与类实例的关系)
镜像是分层的
当我们pull镜像的时候,可以看到是一层一层的下载的。并不只是pull一个完整的镜像下来,而是一层层pull多个下来,最终成为一个redis的镜像。
[root@qianfeng01 ~]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
a2abf6c4d29d: Pull complete
c7a4e4382001: Pull complete
4044b9ba67c9: Pull complete
c8388a79482f: Pull complete
413c8bb60be2: Pull complete
1abfd3011519: Pull complete
Digest: sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
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等。
docker=简易版的操作系统+应用程序及环境及配置;简易版的操作系统=bootfs+rootfs,Ubuntu仅70M左右,这也就是为什么docker轻量的原因,因为它只需要操作系统最低的要求。
对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令、工具和程序库就可以了,因为底层直接用host的kernel,自己只需要提供rootfs就行了。由此可见,不同的linux的发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以共用bootfs。(下图)
docker为什么要采用这种分层结构呢
镜像分层最大的好处就是共享资源,方便复制迁移,就是为了复用。——模块化,乐高积木。
比如说有多个镜像都从相同的base镜像构建而来,那么docker host只需要在磁盘上保存一份base镜像(下图,可以理解为ubuntu系统,其他的应用功能都是基于ubuntu基础镜像之上安装的),同时内存中也只需要加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。——多个容器可以使用相同的分层组件。
重点理解:
docker镜像层都是只读的,容器层是可写的。当容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称为容器层,容器层之下的都叫镜像层。所有对容器的改动--无论添加、删除、修改,都只会发生在容器层中。只有容器层是可写的,容器层以下的镜像层都是只读的。
这也就是为什么我停止容器后,想删除容器对应的镜像会删除失败,报冲突,因为容器是依赖镜像(而镜像本身是多层的,存在组件复用的)的,容器是部署镜像的复制品+启动。

docker镜像commit操作实例——从原始ubuntu系统安装vim命令后打包成新的镜像
docker commit 提交容器副本使之成为一个新的镜像
命令:docker commit -m "提交的信息" -a="作者" 容器ID 要创建的目标镜像名:版本号
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 7614ae9453d1 7 months ago 113MB
ubuntu latest ba6acccedd29 9 months ago 72.8MB
[root@qianfeng01 ~]# docker run -it ubuntu /bin/bash
root@bea65fd28740:/# vim a.txt
bash: vim: command not found
root@bea65fd28740:/# apt-get update
root@bea65fd28740:/# apt-get install -y vim
root@bea65fd28740:/# vim a.txt
root@bea65fd28740:/# cat a.txt
hello vim!
# ctrl p q 保证容器后台运行的退出
[root@qianfeng01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bea65fd28740 ubuntu "/bin/bash" 12 minutes ago Up 12 minutes laughing_nash
[root@qianfeng01 ~]# docker commit -m "vim add ubuntu" -a "bain" bea65fd28740 bain/bain_ubuntu:1.1
sha256:31f003b2ca9805eb2e15a4a938ab14987a72052ca2ad1a3d374cfaa8d26b23e9
[root@qianfeng01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
bain/bain_ubuntu 1.1 31f003b2ca98 8 seconds ago 178MB
redis latest 7614ae9453d1 7 months ago 113MB
ubuntu latest ba6acccedd29 9 months ago 72.8MB
# 可以看到,新的镜像比原始的镜像大了100多MB。成功的打包了带有新功能的镜像。
小总结:
docker中的镜像分层支持通过扩展现有的镜像创建新的镜像。类似类的继承,子类继承并拓展父类方法。新镜像是从base镜像一层一层叠加生成的,每安装一个软件,就在现有的镜像基础上增加一层。

本地镜像发布到阿里云
目的:初始的开发环境已经配置好了,让组内其他的小伙伴能够使用同一份配置环境,需要通过平台分享
流程
镜像仓库可以是建立在公有云仓库上,也可以存放在私有服务器仓库上。

镜像的生成方法
1.通过容器的commit命令,上面操作实例已有。
2.通过dockerfile生成,后面高级篇会讲。
将本地镜像推送到阿里云
1.确认自己的镜像已经生成

2.进入阿里云控制台-容器镜像服务
https://cr.console.aliyun.com/cn-hangzhou/instance/dashboard

3.创建命名空间-创建镜像仓库,全部选择公开。
命名空间:存放所有镜像的地方,类似git服务器
镜像仓库:存放镜像实例的地方,类似某个git地址



https://cr.console.aliyun.com/repository/cn-hangzhou/bainningking/bain_repo/details
页面命令会指导如何从阿里云推送及下载镜像。bainningking是仓库地址,bain_repo:1.0.0是具体镜像。

实际操作:

将阿里云上的镜像下载到本地
docker pull registry.cn-hangzhou.aliyuncs.com/bainningking/bain_repo:[镜像版本号]

本地镜像发布到私有仓库
目的:公有云仓库(阿里云docker或者docker hub)收费且不够安全,需要自己搭建私有仓库服务器,供公司内部使用。类似于公司自行搭建的gitlab。
docker registry是官方提供的工具,可以用于构建私有镜像仓库。
1.下载镜像docker registry
命令:docker pull registry

2.运行私有库registry,相当于本地有个私有的docker hub
命令:docker run -d -p 5000:5000 -v /bain/myregistry/:/tem/registry --privileged=true registry
-d, --detach Run container in background and print container ID,表示后台运行,类似redis
-p, --publish list Publish a container's port(s) to the host,指定虚拟机端口映射到容器端口,虚拟机端口在前,容器端口在后。比如外部访问虚拟机的8080,映射到容器的80,-p 8080:80(下图PORTS可见)
-P, --publish-all Publish all exposed ports to random ports,虚拟机容器端口随机映射
-v, --volume list Bind mount a volume,表示关联的数据卷
--privileged Give extended privileges to this container,赋予这个容器额外的权限
registry 镜像名
默认情况(不写-v的情况),仓库被创建在容器的/var/lib/registry目录下,建议自定义使用容器卷映射(即加上-v的参数),方便于宿主机联调

3.案例演示创建一个新镜像,ubuntu安装ifconfig命令
在ubuntu基础镜像的基础上安装ifconfig命令,并保持后台运行(ctrl p q退出)

执行提交命令:docker commit -m "提交的信息" -a="作者" 容器ID 要创建的目标镜像名:版本号**

验证镜像是否满足要求:停止原容器,启动新镜像,输入命令测试

这里如果通过exit退出会导致容器停止运行哈。这里做个小总结:
docker run -it 镜像ID /bin/bash + ctrl p q 运行镜像并进入容器,退出仍保持后台运行
docker run -d 镜像ID 直接后台运行,对于ubuntu会立即退出,对redis和mysql不会
docker exec -it 容器ID /bin/bash 进入已运行的容器,通过exit退出仍能保证后台运行
docker attach 容器ID 进入已运行的容器,通过exit退出会导致容器停止运行 ,ctrl p q 退出仍保持后台运行(不建议)
4.curl验证私服仓库上有什么镜像
命令:curl -XGET http://虚拟机IP:5000/v2/_catalog
通过访问虚拟机IP的5000端口,就会被映射到运行容器registry的端口上,通过Get请求查询到目前仓库内的镜像

5.将新镜像修改成符合私服规范的tag
命令:docker tag 待传输镜像:Tag host:port/待传输镜像新名字:新版本号,一般可保持不变
docker tag A B——将A的仓库名改为B,但是镜像ID不变
[root@qianfeng01 ~]# docker tag --help
Usage: docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
host是当前虚拟机的IP地址

6.修改配置文件使之支持http
docker默认是不允许未知IP通过http方式推送镜像的,需要通过修改相关配置来允许某些IP的http推送,这里是虚拟机本身。
命令:vim /etc/docker/deamon.json,新增"insecure-registries": ["192.168.10.101:5000"],重启docker保证生效,重启私服库。
注意这里是insecure-registries,如果按照尚硅谷的则是insecure-registry,配置会不生效。

7.push推送到私服库
命令:docker push 已经修改符合私服库规范名称的镜像名(一般是ip/域名:端口/仓库名/镜像名:版本号)

8.curl验证私服库上有什么镜像2

9.pull到本地并运行


Docker容器数据卷
1.注意事项
⚠️设置容器卷时一定要加入 --privileged=true,表示这个镜像运行成容器时的挂载的目录拥有虚拟机的root权限,这样就能访问主机对应的目录了。
如果不加这个参数,docker挂载主机目录访问时会出现open directory: Permission denied的错误。
因为centos7默认挂载目录是一个不安全的行为,加了这个参数容器内的root才拥有真正的root权限,否则container的root拥有的只是外部的一个普通用户权限,可能是不允许你挂载访问外部目录的。
在哪加这个参数呢:docker run -d -p 5000:5000 -v /bain/myregistry/:/tem/registry --privileged=true registry
命令:docker run -it --privileged=true -v 宿主机绝对路径:容器绝对路径 镜像名
-v 宿主机路径:容器路径,这个参数实现了宿主机某目录与容器内某目录的映射,实现互通互联。如果没有对应路径则会自动创建。
--privileged=true:配合-v,表示容器对宿主机目录的操作权限
2.容器数据卷能干什么
容器是在多层镜像之上新增一层可写的分层,在容器内新增的数据,如果在外部删除此容器,数据就会丢失。为了保证容器内操作的重要数据可以持久化存储,就需要使用数据卷对其进行备份操作,-v命令就是指定了一个目录映射,在容器内对应目录的文件都会持久化备份到宿主机的对应目录上。
有这个参数,docker cp命令和docker export命令作为容器数据转移和打包的功能就更灵活的被替代了。
数据卷特点:
-
数据卷可在容器之间共享或重用数据
-
卷中的更改可以直接实时生效
-
数据卷中的更改不会包含在镜像的更新中
-
数据卷的生命周期一直持续到没有容器使用它为止
3.容器数据卷是什么
卷就是目录或者文件,存在于一个或多个容器中,由docker挂载到容器(相当于给容器外挂了一个宿主机目录,这个目录会实时备份容器内映射的某目录,达到备份的目的,且因为属于外挂的目录,因此与容器生命周期无关),但不属于联合文件系统,因此能够绕过Union File System,提供一些用于持续存储或共享数据的特性。
卷的设计目的就是数据的持久化,完全独立于容器的生命周期,因此docker不会在容器删除时删除其挂载的数据卷。
类似于redis里面的rdb和aof文件。数据卷会将docker容器内的数据保存进宿主机的磁盘中,docker run -v 宿主机目录:容器目录 命令本质上是运行了一个带有外挂数据卷存储功能的容器实例。
4.容器卷案例
在容器卷内创建/修改文件,查看是否同步。

通过docker inspect 容器ID 命令查看数据卷是否挂载成功。可以看到源挂载目录(宿主机)和目标目录(容器)
"Mounts": [
{
"Type": "bind",
"Source": "/tmp/host_data",
"Destination": "/tmp/container_data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
容器停止时,向数据卷内写入数据,在容器启动时仍会最新的更新上去。(PS.相当于外部U盘,肯定的)

默认情况下,宿主机对于映射到容器的数据卷是可读可写的权限,即如下命令:
docker run -it --privileged=true -v 宿主机绝对路径:容器绝对路径:rw 镜像名
对于有些时候我们希望容器内的数据只能被宿主机修改,不应被容器修改,因为我的目的是单向传输文件,不希望容器对数据卷有写的权限。此时命令可以换成如下形式:
docker run -it --privileged=true -v 宿主机绝对路径:容器绝对路径:ro 镜像名,ro即read only,使得容器对于数据卷的权限为只读

对于多个容器,是可以挂载同一宿主机的目录下的。如果容器2想和容器1映射相同的数据卷规则(宿主机路径:容器路径:rw/ro),可以直接使用如下命令,省去查询inspect找到容器1的数据卷映射规则,在复制粘贴到容器2上的过程:
docker run -it --privileged=true -volumes-from 容器1 镜像名 /bin/bash
并且此时任一方对于数据卷的修改都会同步到三方的对应目录下(不管容器的状态是停止还是启动),因为本质上就是容器1和容器2同时使用了相同的外挂U盘。
Docker上常用软件的安装
1.总体步骤
搜索镜像(阿里云/docker hub)—拉取镜像(pull)—查看镜像(images)—启动镜像(run,注意服务端口映射)—停止容器(stop)—移除容器(rm)
2.安装tomcat
1.hub.docker.com(注册登录bainningking,不注册登录也行),查询搜索tomcat,点进去
2.查看安装命令和使用说明


3.下载镜像
docker search 镜像 只能搜到是否有该镜像,具体有哪些版本并没有罗列出来(但是官网可以看到),下载时需要自己指定。

docker pull不指定版本默认下载最新版本,指定版本则下载对应版本

4.运行镜像
docker run -it -p 8080:8080 tomcat,指定宿主机8080端口映射到容器8080端口,或者docker run -d -p 8080:8080 tomcat直接后台运行也可以

如果不指定,即使用docker run -it -P tomcat,则会随机分配宿主机端口给到容器的8080端口,通过docker ps 查看

默认是404,需要修改/usr/local/tomcat/webapps.dist为webapps才可以,新版会出现这个问题,旧版没问题

刷新页面后,成功访问

如不想修改,可以下载如下镜像使用,版本是tomcat8
docker pull billygoo/tomcat8-jdk8
docker run -d -p 8080:8080 billygoo/tomcat8-jdk8
3.安装mysql
A.简单版
1.搜索安装

2.启动
使用说明参考官网即可,注意替换相关参数

-
确保虚拟机本身没有启动mysql:ps -ef|grep mysql
-
确保虚拟机本身3306端口没有被占用:lsof -i:3306
-
按照使用说明运行容器:docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123123123 -d mysql:5.7,-e表示设置环境变量,这里root用户的密码被设置为123123123
-
进入容器:docker exec -it 容器ID /bin/bash
-
进入mysql:mysql -uroot -p,输入密码
![image-20220804095921397]()
-
进行测试操作:
show databases;
create database db01;
use db01;
create table t1(id int,name varchar(20));
insert into t1 values(1,'zzz');
select * from t1;

- 此时相当于linux虚拟机运行了一台mysql的容器实例,并且做好了端口映射,此时我们用外部物理机连接这个mysql,可以正常访问。


-
容器mysql的坑1:插入中文字符报错,因为docker上默认的字符集是拉丁字符集(通过命令show variables like 'character%';查看),这里的报错需通过navicat插入语句发现,通过命令行打不出来中文字,因为容器内部没有配置中文打字。
![image-20220804102139630]()
-
容器mysql的坑2:当我们删除容器实例后,容器内的数据库数据都没了。数据的备份及敏感数据的迁移问题没法解决
B.实战版——挂载数据卷,增加mysql字符集编码配置文件
1.新建mysql容器实例:命令对比,实际使用过程中我们只借助容器的mysql服务,对于其配置、日志、数据存储都通过数据卷的方式挂载到外部的宿主机的指定路径上面,这样方便我们从容器外部查看日志,修改配置,备份存储。完美!
简单版:
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123123123 mysql:5.7
实战版:
docker run -d -p 3306:3306 --privileged=true -v /bain/mysql/log:/var/log/mysql -v /bain/mysql/data:/var/lib/mysql -v /bain/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123123123 --name=mysql mysql:5.7
2.在mysql配置文件路径中新建my.cnf,配置mysql服务端和客户端的字符集
[client]
default_character_set=utf8
[mysqld]
collation_server=utf8_general_ci
character_set_server=utf8
3.重启容器(因为改了配置文件),进入mysql查看字符集编码:


4.插入中文测试:这里只能在navicat里插入,因为容器内没有设置中文语言,所以命令行里无法打出中文字符,也就没办法通过命令行插入数据。

5.结论
docker安装完mysql并run出容器后,建议先修改完字符集编码规则后再进行建库建表,插入数据的操作。
6.删除容器后,Mysql数据如何重新获取:重新启动带挂载命令的容器即可,配置及数据仍然保留。

4.安装redis
1.准备配置文件:redis容器默认是没有配置文件的,需要自行下载更改。https://redis.io/docs/manual/config/,image用的是6.0.8,因此配置文件也需要是对应版本的,如果不一致会报错。(踩坑)
修改内容:
bind 127.0.0.1 #注释掉这部分,这是限制redis只能本地访问
protected-mode no #默认yes,开启保护模式,限制为本地访问
daemonize no#默认no,改为yes意为以守护进程方式启动,可后台运行,除非kill进程,改为yes会使配置文件方式启动redis失败
2.运行redis:
docker run -d -p 6379:6379 --name=redis --privileged=true -v /bain/redis/redis.conf:/etc/redis/redis.conf -v /bain/redis/data:/data redis:6.0.8 redis-server /etc/redis/redis.conf
最后的两个参数表明redis服务端使用容器内的/etc/redis/redis.conf这个配置文件启动,而这个配置文件已经被我们映射到虚拟机上,因此我们可以通过修改虚拟机上的redis配置文件达到更改容器redis服务的效果。
3.确认配置文件是否生效:修改redis数据库数,查看是否生效。

PS. fing / -name '*xxx':查找根目录下的名字带有xxx结尾的文件;:noh取消查找后的高亮显示。
Docker学习——高级篇
一、组件复杂安装
A.mysql主从复制docker版安装
1.新建主服务器(master)容器实例3307:对外暴露是3307,对内仍是3306,注意端口映射。这里的容器配置文件路径映射没有到conf.d。
命令:
docker run -d -p 3307:3306 --privileged=true -v /mydata/mysql-master/log:/var/log/mysql -v /mydata/mysql-master/data:/var/lib/mysql -v /mydata/mysql-master/conf:/etc/mysql/ -e MYSQL_ROOT_PASSWORD=123123123 --name=mysql-master mysql:5.7
2.进入/mydata/mysql-master/conf目录新建配置文件my.cnf,并重启master实例
my.cnf:
[client]
## 设置客户端默认的字符集编码
default_character_set=utf8
[mysqld]
## 设置服务端字符集编码
collation_server=utf8_general_ci
character_set_server=utf8
## 设置server_id,同一局域网中需要唯一
server_id=101
## 指定不需要同步的数据库名称(mysql数据库是默认创建的4个数据库之一)
binlog-ignore-db=mysql
## 开启二进制日志功能
log-bin=mall-mysql-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间,默认值为0,表示不自动清理
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
docker restart mysql-master

3.进入mysql-master容器,并创建数据同步用户,这个用户专门用来同步数据的。
docker exec -it mysql-master /bin/bash
mysql -uroot -p
# 创建用户:
create user 'slave'@'%' identified by '123123123';
# 授权:
grant replication slave,replication client on *.* to 'slave'@'%';

4.新建从服务器(slave)容器实例3308
命令:
docker run -d -p 3308:3306 --privileged=true -v /mydata/mysql-slave/log:/var/log/mysql -v /mydata/mysql-slave/data:/var/lib/mysql -v /mydata/mysql-slave/conf:/etc/mysql/ -e MYSQL_ROOT_PASSWORD=123123123 --name=mysql-slave mysql:5.7
5.进入/mydata/mysql-slave/conf目录新建配置文件my.cnf,并重启slave实例
[client]
## 设置客户端默认的字符集编码
default_character_set=utf8
[mysqld]
## 设置服务端字符集编码
collation_server=utf8_general_ci
character_set_server=utf8
## 设置server_id,同一局域网中需要唯一
server_id=102
## 指定不需要同步的数据库名称(mysql数据库是默认创建的)
binlog-ignore-db=mysql
## 开启二进制日志功能,以备slave作为其他数据库实例的master时使用
log-bin=mall-mysql-bin-slave1-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间,默认值为0,表示不自动清理
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
## slave设置为只读(具有super权限的用户除外)
read_only=1
docker restart mysql-slave

6.在主数据库中查看主从同步状态(mysql命令行),结果用于第七步配置主服务器日志文件名和日志位置。
show master status;

7.进入mysql-slave容器,并从数据库中配置主从复制(通过具体的sql操作才能让从服务器认识主服务器)
docker exec -it mysql-slave /bin/bash
mysql -uroot -p
change master to master_host='主数据库IP',master_user='slave',master_password='123123123',master_port=3307,master_log_file='mall-mysql-bin.000001',master_log_pos=617,master_connect_retry=30;
# master_host:主数据库的IP地址;
# master_port:主数据库的运行端口;
# master_user:在主数据库创建的用于同步数据的用户账号;
# master_password:在主数据库创建的用于同步数据的用户账号的密码;
# master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取file参数;
# master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取pos参数;
# master_connect_retry:连接主数据库失败后重试的时间间隔,单位为秒;

8.查看主从同步状态,在从数据库中开启主从同步,并再次查看从数据库状态,是否已经同步
# 查看从数据库状态,\G表示以键值对的形式输出结果
# 这里主要查看slave-io-running和slave-sql-running的状态,由NO变为YES就成功了
show slave status \G;
start slave;
show slave status \G;


9.主从复制测试:主数据库创库,创表,插数据,看数据,从数据库看数据

B.Redis集群安装理论知识——分布式存储算法
面对的问题:1-2亿的数据量的缓存该如何设计?
思路:单机单台肯定不可能,必须使用分布式存储,如何使用redis落地分布式缓存呢?
答案:3种方法,适用数据量规模和难度依次递增:哈希取余分区 => 一致性哈希算法 => 哈希槽分区。
1️⃣哈希取余分区

原理:1亿条记录,按照键值对存储,根据hash算法计算出每个键的hash值,按照机器台数N取余,用来决定数据映射到哪一个节点上,使数据均匀的分配存储到N台机器上。
优点:简单粗暴,直接有效,只需要预估好数据,规划好节点数量,比如3台,10台,就能保证一段时间的数据支撑,使用hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的效果。
缺点:只能依赖于规划好的节点数量,如果要对节点数进行扩容或者缩容就比较麻烦,不管扩缩,都会导致节点数量有变动,映射关系需要重新进行计算,在服务器个数不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式发生变化:Hash(key) / N 会变成 Hash(key) / ?,此时hash地址经过某台redis机器宕机了,由于台数数量变化,会导致需要hash取余全部历史数据重新洗牌分配。
2️⃣一致性哈希算法分区
背景:一致性哈希算法由麻省理工在1997年提出,设计目标就是为了解决分布式缓存数据变动和映射问题,某个机器宕机了,分母数量改变了,自然取余就不OK了。
目的:当服务器数量发生变化时,尽量减少影响到客户端到服务器的映射关系。
步骤:算法构建一致性哈希环 => 服务器IP节点映射 => key落到服务器的落键规则。
step1:算法构建一致性哈希环
一致性hash算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间【0,232-1】(即hash值是一个32位无符号整型),这是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0=232),这样让它逻辑上形成了一个环形空间。整个空间按照顺时针方向组织,圆环的正上方的点代表0,向右依次旋转增加,直到232-1,我们把整个由232个点组成的圆环称为hash环。
step2:服务器IP节点映射
一致性哈希算法根据节点IP或者节点主机名计算出hash值,然后对2^32取模(求余数),求得每台主机在hash环上的位置,假如4个节点Node1,Node2,Node3,Node4,经过对应IP地址的hash函数计算(hash(ip)),数值落在hash环的位置如下图
step2:key落到服务器的落键规则
当我们需要存储一个KV键值对时,首先计算key的hash值,hash(key),得到其在hash环上的位置,然后从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,此时将该键值对存储在该节点上。

优点:
增加容错性:假设节点Node3宕机,此时节点1,2,3是不会受到影响的,只有node4到node3这一段的数据被重定位到node1了。一般地,在一致性hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿顺时针方向行走遇到的第一台服务器)之间的数据,其他不会受到影响。简单的说,就是1挂了,受影响的只会是3到1之间的数据,并且这些数据会转移到2进行存储。
增强扩展性:数据量增加了,需要新增节点node x,x的位置在1,2之间,那受到影响的就是1到x之间的数据,这些数据会被定位到x上面,不会导致数据全部重新洗牌。
缺点:
一致性hash环的数据倾斜问题:一致性hash算法在服务器节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上面)问题。例如系统中只有两台服务器:

3️⃣哈希槽分区
为什么出现:
为了解决一致性hash算法的数据倾斜问题,设计了hash槽,hash槽本质上就是一个数组,数组大小【0,2^14-1】,形成hash slot(hash槽)空间。
能做什么:
为了能使数据均匀分配,它在数据和节点之间又加了一层,把这层结构叫做hash槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里面放的是数据。槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。

多少个hash槽:
一个redis集群只能有16384个槽(redis之父规定的),编号0-16383(0-2^14-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几key就落入对应的槽里。slot = CRC16(key) % 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
哈希槽计算:
Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。如下代码,key之A 、B在Node2, key之C落在Node3上。

特点:
# 1.解耦了数据与节点之间的关系,数据的读写只需要计算出槽号即可,节点的扩容和缩容只需要重新均衡分配槽区间即可。简化了节点扩缩容的难度。
# 2.节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区和数据。
# 3.支持节点、槽、键之间的映射查询,用于数据路由、在线伸缩等场景。
C.Redis 3主3从集群安装实践
1.明确主从架构图,6台机器,两两配对,3主3从(实际是随机分配主从,主从端口对应不一定如下图):

2.启动6个redis容器实例:
🚨要区分run命令参数和镜像配置参数的区别,一个是在镜像前,一个是在镜像后
🚨以后参数带双中划线的不要带等号,就用空格表示后面填的是值。因为带等号有的情况下会报错,不带等号是默认情况,一定正确
docker run -d --name=redis-node-1 --net=host --privileged=true -v /mydata/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name=redis-node-2 --net=host --privileged=true -v /mydata/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name=redis-node-3 --net=host --privileged=true -v /mydata/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name=redis-node-4 --net=host --privileged=true -v /mydata/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name=redis-node-5 --net=host --privileged=true -v /mydata/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name=redis-node-6 --net=host --privileged=true -v /mydata/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
## docker run命令的参数:
# --net=host:使用宿主机的IP和端口,默认就是host
# --privileged=true:获取宿主机的root权限
## docker run 具体镜像后,镜像自带配置的参数:
# --cluster-enabled=yes:开启redis集群
# --appendonly=yes:开启持久化存储
# --port=6381:指定redis容器实例的端口号,因为--net=host,因此宿主机的6381与之对应,等价于-p 6381:6381

3.对6个redis实例配置集群关系,主从关系:
# 进入任意一个redis实例
docker exec -it redis-node-1 /bin/bash
# 执行如下命令创建redis集群
redis-cli --cluster create 192.168.10.101:6381 192.168.10.101:6382 192.168.10.101:6383 192.168.10.101:6384 192.168.10.101:6385 192.168.10.101:6386 --cluster-replicas 1
# --cluster create 节点1 …:指定redis集群的IP及端口,表示这些都是一个集群的。
# --cluster-replicas 副本数:指定集群中每个主节点对应的从节点数量,如果是2,那么6台机器就会自动被分配为2主4从。


4.查看集群状态,是否配置成功:
# 进入任意一个redis实例
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381(PS.不加端口或者错误端口会怎么样)
cluster info
cluster nodes
# cluster info:可以查看当前集群状态,槽分配,当前集群节点数量
# cluster nodes:可以查看对应端口的主从情况,即哪个端口是主,对应的从节点端口是哪个

5.数据读写存储测试:
这一步承接上一步,是通过redis-cli -p 6381命令进入redis的,这个命令是进入某一节点(单机版)的,写数据时如果key哈希取模后不符合当前节点的槽分区区间,是会写失败的。
keys *
set k1 v1
set k2 v2
set k3 v3
set k4 v4
keys *

上图中的moved表示是想写入到12706对应的槽分区节点内,但是客户端是单机启动的,移动不过去。
进入集群模式使用的命令需要加 -c:
# -c表示是客户端连接的是redis集群模式,不加-c默认客户端连接的是单机模式
redis-cli -p 6383 -c
# 清空数据库
flushall
keys *
# 注意节点机器(端口)的跳转/重定向(redirected)
set k1 v1
set k2 v2
set k3 v3
set k4 v4

6.集群检查信息
可以查看键值对分布式存储的情况,共有多少条数据,每个节点上多少个等。
# 可进入任意一台机器执行如下命令,返回的都是整个集群的信息
# 这里的--cluster无法换成-c,这里的主要命令是check,--cluster是针对check命令的。
redis-cli --cluster check 任一主节点所在IP:对应端口

7.主从容错切换迁移
当某台主节点挂掉了(比如6381),需要能自动的切换到对应从节点上(比如6385),保证服务可行性。
主从之间的切换是通过心跳机制感知的,所以不是说主节点挂了,从节点可以实时无缝感知到的,里面存在一个心跳时间差。
😕如果6381挂了,会发生什么?
# 这里承接步骤6
docker stop redis-node-1
# 这里等一会心跳机制生效
docker ps -a
docker exec -it redis-node-2 /bin/bash
# 以集群模式进入redis
redis-cli -p 6382 -c
# 查看节点状态
cluster nodes

😕如果6381又恢复运行了,那么此时的maser是恢复成原本的6381呢,还是切换之后的6385呢
# 主从关系是按部就班的切换的,即风水轮流转
docker start redis-node-1
docker exec -it redis-node-1 /bin/bash
cluster nodes

8.主从扩容案例
应对业务压力,横向扩容增加redis节点。由原本的3主3从,变为4主4从(增加6387主,6388从)


❓新增的主节点的槽位如何确定呢(哈希槽的重新分配-考点)
❓如何将新机器加入已有集群呢
# 1.新建6387/6388两个节点,并启动查看
docker run -d --name=redis-node-7 --net=host --privileged=true -v /mydata/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name=redis-node-8 --net=host --privileged=true -v /mydata/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
# 2.进入6387,并将其作为master节点加入已有集群
docker exec -it redis-node-7 /bin/bash
# 3.相当于新增节点向已有集群的主节点之一申请加入集群
redis-cli --cluster add-node 新增主节点IP:端口 已有集群主节点之一的IP:端口

# 4.检查集群情况,注意此时新节点虽已加入集群,但还未分配槽位,无法存储数据
redis-cli --cluster check 任一主节点所在IP:对应端口

# 5.向已有集群主节点申请重新分配槽号(洗牌reshard),填写新节点需要的槽位(由其他三个旧节点均匀的分给新节点,也可以指定某些旧节点),填写新节点的uuid
redis-cli --cluster reshard 已有集群主节点之一的IP:端口
# 可以指定哪些旧节点分槽位给新节点,也可以选择all,全部旧节点都分一部分出去,给出分配计划后,填写yes确认


新增主节点的槽号是集群已有的主节点各自匀出来一部分给到新节点,而不是重新分配(因为成本较高)
# 6.为6387主节点分配6388从节点
redis-cli --cluster add-node 新从节点IP:端口 新主节点IP:端口 --cluster-slave --cluster-master-id 新主节点的uuid
# 7.检查集群情况
redis-cli --cluster check 任一主节点所在IP:对应端口


9.主从缩容案例
❓删除节点时,槽位如何分配
❓删除节点时,是先删主节点还是先删从节点

# 1.删除从节点
redis-cli --cluster del-node 集群任一主节点IP:端口 从节点对应的uuid
# 2.转移(洗牌)无从节点的主节点的槽位给其他主节点(可以一次转移给同一节点,也可多次分配给不同节点),输入要分配的数量,分给哪个节点,从哪个节点上分
redis-cli --cluster reshard 集群任一主节点IP:端口
# 3.检查集群,查看槽位状态
redis-cli --cluster check
# 4.删除主节点
redis-cli --cluster del-node 集群任一主节点IP:端口 主节点对应的uuid
# 5.检查集群及数据状态,无误则缩容完成
redis-cli --cluster check



二、DockerFile解析
A.DockerFile是什么?
DockerFile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。本质就是个构建镜像的脚本。
B.DockerFile说明使用网站:
https://docs.docker.com/engine/reference/builder/
C.构建使用三部曲:
编写dockerfile文件 => docker build命令构建镜像 => docker run运行镜像
D.🎵 DockerFile文件编写基础知识:
- 每条保留字(或者叫关键字)指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- # 表示注释
- 每条指令都会创建一个新的镜像层并对镜像进行提交(commit)
E.🎵DockerFile执行大致流程:
- docker从基础镜像运行一个容器
- 执行一条指令并对容器作出修改
- 执行类似docker commit的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新容器
- 执行dockerfile中的下一条指令直到所有指令都执行完成
F.📘小总结:
从软件应用的角度来看,dockerfile,docker镜像,docker容器分别代表软件的三个不同阶段
- dockerfile是软件的原材料(面向开发),它定义了进程需要的一切东西,dockerfile涉及的内容包括执行代码或者文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务&内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等。
- docker镜像是交付物(交付标准),在用dockerfile定义了一个文件之后,docker build会产生一个docker镜像。
- docker容器(部署和运维)是镜像的运行态实例,容器是直接提供服务的。
三者缺一不可,合力充当docker体系的基石。

G.Dockerfile常用保留字指令
以官方tomcat镜像的dockerfile为例进行学习(https://hub.docker.com/_/tomcat给了链接):https://github.com/docker-library/tomcat/blob/2ae46bb6ec09c7d5f7558f013fd1ca878e8bc8d3/10.0/jdk8/corretto-al2/Dockerfile
🤞FROM
指定一个已经存在的镜像作为模板,表示当前的新镜像要基于哪个镜像创建的,dockerfile的第一行必须是from
🤞MAINTAINER
镜像维护者的姓名和邮箱地址
🤞RUN
容器构建时需要运行的命令(即在dockerfile build成镜像的过程中执行),有两种格式,shell和exec
# <命令行命令> 等同于在终端操作的shell命令。比如RUN apt-get -y install vim
RUN <命令行命令>
# 类似于可执行文件的带参数命令行,RUN ['./test.py', 'dev', '111'] 等价于 RUN ./test.py dev 111
RUN ["可执行文件","参数1","参数2"]
🤞EXPOSE
当前容器对外暴露的端口
EXPOSE 8080
🤞WORKDIR
指定在创建容器后,终端默认登录进来的工作目录,一个落脚点
🤞USER
指定该镜像用户以什么样的身份去执行,如果都不指定,默认是root
🤞ENV
用来在构建镜像过程中设置环境变量
# 只要声明,这个环境变量就可以在其他指令中直接使用这些环境变量。
ENV MY_PATH /usr/mytest
WORKDIR $MY_PATH
🤞ADD
将宿主机目录下的文件拷贝进镜像,且会自动处理URL和解压tar压缩包。ADD=COPY+解压。
🤞COPY
类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中<源路径>的文件/目录复制到新的一层的镜像内<目标路径>位置
COPY src dest
COPY ["src","dest"]
# <src源路径>:源文件/源目录
# <dest目标路径>:容器内的指定路径,该路径不用事先建好。
🤞VOLUME
容器数据卷,用于数据的保存和持久化
🤞CMD
指定容器启动后要做的事情,dockerfile可以有多个CMD指令,但只有最后一个生效,且CMD会被docker run 之后的参数替换
区分:CMD是在docker run的时候运行的;RUN是在docker build的时候运行的;
# CMD命令运行示例
CMD ['<executable','<param1>','param2',...]
# tomcat的dockerfile文件最后两行
EXPOSE 8080
CMD ["catalina.sh", "run"]
# 这个可以正常启动tomcat,因为它在容器启动后正常的执行了catalina.sh run这个命令
docker run -it -p 8080:8080 tomcat
# 这个tomcat是没有正常启动的,也无法正常访问,因为在容器启动后,它执行的命令变成了 /bin/bash 这个错误命令?
docker run -it -p 8080:8080 tomcat /bin/bash
🤞ENTRYPOINT
也是用来指定一个容器启动时要运行的命令,类似于CMD指令(同CMD指令,如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效),但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当作参数送给ENTRYPOINT指令指定的程序。
优点:在执行docker run的时候可以用户指定 ENTRYPOINT 运行所需的参数,而不是作者写死。
# ENTRYPOINT命令运行示例
ENTRYPOINT ['<executable','<param1>','param2',...]
# 如果ENTRYPOINT与CMD连着用,那么CMD命令的结果会作为参数传递给ENTRYPOINT的最后一个参数位置
# 如下例,容器执行docker run时,如果run不加参数,等价于容器创建后执行了 nginx -c /etc/nginx/nginx.conf
# 但是如果run后面加了参数,比如docker run -it nginx /bin/bash,那么/bin/bash就会把CMD的参数给替换掉,等于
# 容器启动后执行了 nginx -c /bin/bash 这个命令
ENTRYPOINT ["nginx", "-c"]
CMD ["/etc/nginx/nginx.conf"]
| 按照dockerfile编写执行(如上) | 传参运行(如上注释) | |
|---|---|---|
| Docker命令 | docker run nginx:test | docker run nginx:test -c /etc/nginx/new.conf |
| 衍生出的实际命令 | nginx -c /etc/nginx/nginx.conf | nginx -c /etc/nginx/new.conf |
📘小总结:

H.dockerfile实践:centos7+java8
需求:根据centos基础镜像,制作一个配有vim命令,ifconfig命令,以及jdk8环境的镜像。
jdk下载地址:https://www.oracle.com/java/technologies/downloads/
1.编写Dockerfile文件
⚠️Dockerfile文件名必须叫Dockerfile,首字母D一定要大写,且没有后缀。
mkdir /myfile/
cd /myfile/
vim Dockerfile
mv /root/softwares/jdk-8u221-linux-x64.tar.gz .
ls
Dockerfile jdk-8u221-linux-x64.tar.gz
以下为本次实践Dockerfile的内容
# 从基础镜像开始制作新镜像
FROM centos:latest
# 设置用户名和邮箱
MAINTAINER bain<bainningking@gmail.com>
# 指定环境变量,并将进入容器时的默认路径设置为/usr/local
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 因为最新版centos8已经不在维护,所以从官方镜像中移除了所有的包,现在这些包被转移到了 https://vault.centos.org
# 因此需要执行如下命令进行仓库源的更换,使用vault.centos.org代替原本的mirror.centos.org
RUN sed -i -e "s|mirrorlist=|#mirrorlist=|g" /etc/yum.repos.d/CentOS-*
RUN sed -i -e "s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-*
# 安装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
# ADD 是相对路径jar,把jdk-8u221-linux-x64.tar.gz添加到容器中并自动解压,这里安装包在当前路径
ADD jdk-8u221-linux-x64.tar.gz /usr/local/java/
# 配置java环境变量,这里也需要注意java解压后的文件夹名字
ENV JAVA_HOME /usr/local/java/jdk1.8.0_221
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命令,但是只有最后一个生效
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash
2.依据Dockerfile构建镜像centos-java8:1.0.0
# 运行此命令需保证当前目录下有Dockerfile(如果指定为.)且Dockerfile内ADD/COPY的文件存在(注意内部相对路径)
# 一定要注意docker build的最后一个参数表示Dockerfile所在的路径,“.”表示此文件在当前路径下
docker build -t centos-java8:1.0.0 .
docker build --help
Usage: docker build [OPTIONS] PATH | URL | -
-t, --tag list Name and optionally a tag in the 'name:tag' format
--target string Set the target build stage to build.
--ulimit ulimit Ulimit options (default [])

3.运行构建好的镜像
这里面run的最后参数到底是怎么和RUN命令作用的。。。
(在命令行里输入bash或者/bin/bash,仍为命令行)

4.虚悬镜像
概念:仓库名、标签都是<none>的镜像,俗称虚悬镜像dangling image,在删除镜像或者构建镜像时有可能会发生,可能会产生不良影响,需要删除掉。
这里用dockerfile写一个
FROM ubuntu
CMD 'action succeed'
# 这里构建时不指定镜像名和标签
docker build .

# 展示所有的虚悬镜像
docker image ls -f dangling=true
# 删除所有的虚悬镜像,prune是修剪的意思
docker image prune

三、Docker微服务实战
需要jar包,没学过java,难以生成这玩意,搁置。
四、Docker网络
A.回顾架构图,注意其中的网络部分

B.docker网络是个什么东西
1.启动docker前
虚拟机的网络部分:


主机的网络部分(ipconfig):

2.启动docker后
启动docker后,会产生一个名为docker0的虚拟网桥,docker就是通过这个网桥和宿主机直接进行网络连接,并且容器和容器之间的网络连接也是通过它来进行。
C.docker网络模式命令
当我们安装docker后,默认会自动创建3个网络:bridge,host,none,并且主流的网络模式也就有这三种,还有两种是容器的和自定义的。
每一个网络可以认为是一个网段,而有关联的容器服务一般会处于同一网段,因此才会有创建、删除网络等操作。
# docker管理网络的相关命令
[root@qianfeng01 ~]# docker network --help
Usage: docker network COMMAND
Manage networks
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
Run 'docker network COMMAND --help' for more information on a command.
# 列出网络
docker network ls

# 查看网络
docker network inspect bridge

❓docker网络能干嘛
docker网络主要负责管理docker容器间的网络互联和通信,以及容器与虚拟机之间的端口映射;除此之外,不同虚拟机之间的服务访问,也可借助docker网络的管理保证服务调用正常。(因为默认情况选容器重启后IP地址的是会发生变化的,但是服务名是不变的,因此容器IP变动的时候可以通过服务名直接进行网络通信)
D.docker的网络模式
| 网络模式 | 简介 |
|---|---|
| bridge,虚拟网桥模式 | 为每一个容器分配、设置IP,并将容器连接到一个叫docker0的虚拟网桥上面,创建网络时默认为该模式 |
| host,主机模式 | 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口 |
| none,空模式 | 容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接、IP等 |
| container,容器模式 | 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP和端口范围等 |
使用介绍:
- bridge模式:使用--network bridge指定,默认使用docker0作为虚拟网桥
- host模式:使用--network host指定
- none模式:使用--network none指定
- container模式:使用--network container:容器名/容器ID 指定
🥖实例说明默认网络规则
docker run -it --name u1 ubuntu bash
ctrl p q
docker run -it --name u2 ubuntu bash
ctrl p q
docker inspect u1|tail -20
docker inspect u2|tail -20
docker rm -f u2
docker run -it --name u3 ubuntu bash
ctrl p q
docker inspect u3|tail -20
# 此时会发现u3的IP与之前u2的IP相同,因此可以得出结论:默认情况下,容器的启停是会导致容器的IP发生变化的


1.1 bridge网桥模式(默认模式)
docker服务在启动的时候默认会创建一个docker0网桥(其上有一个docker0内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络(同一网段)。docker默认指定了docker0接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
[root@qianfeng01 ~]# docker inspect bridge|grep name
"com.docker.network.bridge.name": "docker0",
[root@qianfeng01 ~]# ifconfig|grep docker0
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
1.2 图解网桥模式
1.Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP(容器IP),同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
2.docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network。eth0,eth1,eth2……代表网卡一,网卡二,网卡三……,lo代表127.0.0.1,即localhost,inet addr用来表示网卡的IP地址.
3.网桥docker0创建一对对等虚拟设备接口一个叫veth(virtual eth,可以理解为虚拟网卡),另一个叫eth0,成对匹配。
3.1 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
3.2 每个容器实例内部也有一块网卡,每个接口叫eth0;
3.3 docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。
通过docker0这个网桥(类似交换机),将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。即虚拟机与容器之间的网络通信需要通过网桥,容器与容器之间的网络通信也需要通过网桥,容器与容器之间并不相通。

1.3 实例验证bidge模式
# 启动两个容器实例用以说明
docker run -d -p 8081:8080 --name t81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name t82 billygoo/tomcat8-jdk8
# 查看此时虚拟机的IP情况
ip addr|tail -n 8
# 查看此时容器的IP情况
docker exec -it t81 bash
ip addr|tail -n 8
docker exec -it t82 bash
ip addr|tail -n 8


2.1 host主机模式
直接使用宿主机的IP地址与外界进行通信,不再需要额外进行NAT(网桥转换,网络地址匹配啥的)转换。
2.2 图解主机模式
容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是直接使用宿主机的IP和端口。

2.3 实例验证host模式
# 如下命令会报警告信息,因为host模式与主机共用IP,因此是不存在端口映射的
# 此时容器端口号会以主机端口号为主,重复时则递增
docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
## WARNING: Published ports are discarded(丢失,丢弃) when using host network mode
# 如果使用host模式,就不用指定-p参数了,这样就不会产生告警了
docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8

查看不同网络模式下容器的网卡信息:



2.4 问题:此时83容器对外暴露的端口是多少
主机模式下,容器端口号会以主机端口号为主,重复时则递增,因为主机的8080端口未被占用,因此83容器借用的是主机的IP和8080端口。
此时容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。


3.1 none禁用网络模式
以禁用网络模式的方式启动docker,在none模式下,并不为Docker容器进行任何网络配置。 也就是说,这个Docker容器没有网卡、IP、路由等信息,需要我们自己为Docker容器添加网卡、配置IP等。此时容器只有lo标识(表示本地回环的127.0.0.1)
PS.一般网桥模式会有eth0和lo两个标识
3.2 实例验证none模式
docker run -it --name t85 --network none billygoo/tomcat8-jdk8 bash
ip addr

4.1 container容器网络模式
新建的容器和已存在的一个容器共享一个网络配置。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
ATT:因为共享端口,因此类似tomcat,redis这种需要指定端口映射的服务是没法用的,因为会端口冲突
4.2 图解容器网络模式

4.3 实例验证container网络模式
# 端口冲突示例
docker run -d -p 8086:8080 --name t86 billygoo/tomcat8-jdk8
docker run -d -p 8087:8080 --network container:t86 --name t87 billygoo/tomcat8-jdk8
# 以Alpine操作系统为例,Alpine操作系统是一个面向安全的轻型 Linux发行版
# 查看两个容器的IP信息,共用a1的网桥连接信息
docker run -it --name a1 alpine /bin/sh
ip addr
ctrl p q
docker run -it --network container:a1 --name a2 alpine /bin/sh
ip addr
ctrl p q
# 关闭a1后,查看a2的IP,此时a2没有eth0信息了
docker stop a1
docker exec -it a2 /bin/sh
ip addr

补充学习:
Alpine Linux 是一款独立的、非商业的通用 Linux 发行版,专为追求安全性、简单性和资源效率的用户而设计。 可能很多人没听说过这个 Linux 发行版本,但是经常用 Docker 的朋友可能都用过,因为他小,简单,安全而著称,所以作为基础镜像是非常好的一个选择,可谓是麻雀虽小但五脏俱全,镜像非常小巧,不到 6M的大小,所以特别适合容器打包。
不管什么模式,ctrl p q退出都是最保险的。(正确的是run -it用ctrl pq ,exec可以用it,run -d后台运行)
5.1 自定义网络模式
其实就是自己通过 docker network create 命令创建自定义的网络(仍是网桥模式),当创建容器指定--network为这个网络时,里面的容器是可以按照容器名互相ping通的,避免了容器启停导致IP变化的问题,IP可能会变,但是容器名是不会变的。
PS.默认的网桥模式只能Ping通IP,但是Ping不通容器名,自定义网络就是为了解决这个问题。
5.2 实例验证自定义网络模式
# 默认网桥模式
docker run -d -p 8081:8080 --name t1 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name t2 billygoo/tomcat8-jdk8
# 进入后互ping IP及容器名
docker exec -it t1 bash
ip addr
ping t2的IP
ping t2的容器名
docker exec -it t2 bash
ip addr
ping t1的IP
ping t1的容器名
# 自定义网络模式(仍为网桥驱动)
docker network create bain_network
docker run -d -p 8081:8080 --network bain_network --name t1 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --network bain_network --name t2 billygoo/tomcat8-jdk8
# 进入后互ping IP及容器名
docker exec -it t1 bash
ip addr
ping t2的IP
ping t2的容器名
docker exec -it t2 bash
ip addr
ping t1的IP
ping t1的容器名
总结:自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)
五、docker-compose容器编排
A.是什么
Compose 是 Docker公司推出的一个开源工具软件,负责实现对Docker容器集群的快速编排(指定容器启动的先后顺序,容器一键启动,容器一键关闭等)。只要定义一个 YAML 格式的配置文件docker-compose.yml,制定好多个容器之间的调用关系即可。
B.能干嘛
docker容器本身占用资源极少,所以使用的时候一般是将每个服务都单独的分割开来,但是如果某个应用需要同时部署多个服务,多个容器的管理启停就变得不方便了。Compose允许用户通过一个单独的docker-compose.yml文件来定义一组相关联的应用容器为一个项目(project),可以很容易地用一个配置文件管理一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。
C.去哪下
官网介绍:https://docs.docker.com/compose/compose-file/compose-file-v3/
官网下载:https://docs.docker.com/compose/install/,注意compose版本要与docker版本匹配
安装步骤:
# 通过curl网络下载
curl -L https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version
# 对应的卸载
rm /usr/local/bin/docker-compose

D.compose核心概念
1文件:docker-compose.yml,compose即组合,协作的意思。
2要素:
服务(service):即一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器,即业务服务和组件服务
工程(project):由一组关联的应用容器组成的一个完整业务单元,通过 docker-compose.yml 文件定义
E.compose使用步骤
- 编写dockerfile定义各个微服务应用并构建出对应的镜像文件
- 使用docker-compose.yml定义一个完整的业务单元,安排好整体应用中的各个容器服务,执行docker-compose config -q检查文件有无问题
- 执行docker-compose up命令来启动并运行整个应用程序,完成一键部署。PS.docker-compose up等价于多个docker run
F.compose常用命令
执行以下命令时,要保证在docker-compose.yml的文件路径下,否则需要使用-f指定yaml的文件路径
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 # 停止服务
G.以zookeeper集群的yaml文件为例进行说明
新建文件:docker-compose-zookeeper-cluster.yml
version: '3.7' # docker-copose的版本,目前都是3.*
networks: # 指定本docker-compose内所有容器要使用的自定义网络(只有自定义网络才能根据服务名互相ping通,默认网桥只能ping通ip)
docker_net:
external: true
services: # 主体部分,指定要编排的容器的服务名,可以自定义
zoo1:
image: zookeeper # 指定镜像名或者镜像ID
restart: unless-stopped # 指定此服务重启的条件——除非被停止了,如果是其他原因挂掉就要自动重启
hostname: zoo1 # 指定生成容器的主机名
container_name: zoo1 # 指定生成的容器名,--name,如果不指定,则默认按照文件夹名+镜像名+数字命名
ports:
- 2182:2181 # 指定端口映射,主机端口:容器端口
environment: # 指定环境变量
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=0.0.0.0:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181
volumes: # 指定容器卷
- ./zookeeper/zoo1/data:/data
- ./zookeeper/zoo1/datalog:/datalog
networks: # 指定容器所属的网络,是否与一级网络配置冲突?
- docker_net
启动集群
docker-compose -f docker-compose-zookeeper-cluster.yml up -d
六、Docker轻量级可视化工具Portainer
只要是软件系统内的服务,最终都需要监控和统计,达到可观测的目的
A.是什么
Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境(一般上k8s)。
B.去哪下
下载: https://docs.portainer.io/start/install/server/docker/linux
命令安装:
# -p指定了两个端口映射
# 镜像名为portainer/portainer
# 注意容器卷映射路径不要变
# --restart=always表示当docker重启后,部署在docker上的这个服务也随之重启,即docker启动时自动重启这个容器,这样这个服务才能时时刻刻监控着docker
docker pull portainer/portainer
docker run -d -p 8000:8000 -p 9000:9000 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
C.怎么用
访问:http://192.168.10.101:9000/
注册:用户名-admin,密码-123123123

登录:选择可视化展示本地docker环境(而不是默认的本地k8s环境)

查看相关信息,本质上就是各种docker命令的可视化展示,如docker system df。。等等等等,基本上命令行能做的都可以通过页面去实现。
包括docker inspect,docker log,各容器的最近1分钟的资源使用量,容器的命令行页面,容器的进程列表等等。

D.nginx安装示例
步骤:Container-Add container-配置信息-deploy-测试80端口



访问80端口,出现nginx欢迎页面表示安装成功

七、Docker重量级监控系统介绍:CIG,Cadvisor(监控)+Influxdb(存储)+Grafana(展示)
A.为什么要用CIG
不使用3剑客:原生的docker stats命令只能当前时刻的cpu、mem、网络IO等信息,无法存储、分析和告警。
使用CIG3剑客:CAdvisor监控收集 + InfluxDB存储数据 + Granfana展示图表 => 完整的docker监控系统
B.CIG是什么
CAdvisor:
CAdvisor是一款容器资源监控工具,包括cpu、mem、网络和磁盘的IO等性能资源,同时也提供了一个web页面用于查看容器的实时运行状态。CAdvisor默认存储2分钟的数据,而且只针对单物理机。不过CAdvisor提供了很多数据集成接口,支持InfluxDB,Redis,Kafka,ES等,可以加上对应配置将监控数据发往以上组件存储起来,本次案例使用的是InfluxDB。
CAdvisor主要功能:1.展示Host和容器两个层次的监控数据;2.展示历史变化数据;
InfluxDB:
InfluxDB是用Go语言编写的一个开源分布式的时序、事件和指标数据库,无需外部依赖。因为CAdvisor默认只在本机保存最近2分钟的数据,为了持久化存储数据和统一分析展示数据,需要将数据存储在InfluxDB中,且CAdvisor本身已经提供了InfluxDB的集成方法,在启动容器时指定配置即可。
InfluxDB的主要功能:1.存储时序数据,并支持相关聚合函数(最大,最小,求和等);2.可度量,可以实时对大量数据进行计算;3.支持存储事件数据。
Grafana:
Grafana是一款开源的数据监控分析可视化平台,支持多种数据源配置(包括InfluxDB,mysql,ES,OpenTSDB,Graphite等),拥有丰富的插件和模板功能,支持图表权限控制和报警。
Grafana的主要功能:1.灵活丰富的图形化选项;2.可以混合多种风格;3.支持白天和夜间模式;4.支持多个数据源
C.怎么搭建CIG
使用docker--compose一键编排生成,用容器服务监控容器本身,这合理吗。。
docker-compose-cig.yml
version: '3.1'
volumes:
grafana_data: {}
services:
influxdb:
image: tutum/influxdb:0.9
restart: always
environment:
- PRE_CREATE_DB=cadvisor # 预创建数据库cadvisor
ports:
- "8083:8083" # influxdb展示窗口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" # cadvisor展示窗口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 # 连接数据源influxdb,对应的主机为influxsrv
ports:
- "3000:3000" # grafana展示窗口3000
volumes:
- grafana_data:/var/lib/grafana
environment:
- HTTP_USER=admin
- HTTP_PASS=admin
- INFLUXDB_HOST=influxsrv # 指定influxdb的主机为influxsrv
- INFLUXDB_PORT=8086
- INFLUXDB_NAME=cadvisor # 指定influxdb的表名为cadvisor
- INFLUXDB_USER=root
- INFLUXDB_PASS=root
# 网络问题,先pull再编排,如果报超时可以多试几次
docker pull grafana/grafana
docker pull google/cadvisor
docker pull tutum/influxdb:0.9
docker-compose -f docker-compose-cig.yaml up -d

D.怎么使用CIG
访问3个服务对外暴露的图形化网页接口:IP+端口8080,8083,3000
CAdvisor:

InfluxDB:

Grafana:


E.Grafana展示
1.添加数据源
2.选择influxdb数据源

3.填写配置信息

4.配置仪表盘和面板,选择曲线图及展示条件

八、终章
end回顾,重复学习,重复实操。
九、补充
-
配置代理,使docker可以pull官网(外网)的镜像,因为有些镜像阿里云源是没有的(可以docker search看下)。。所以镜像不从阿里云下是有原因的,,人家没有,只能从官网下载了
# docker源的代理配置 mkdir -p /etc/systemd/system/docker.service.d vim /etc/systemd/system/docker.service.d/http-proxy.conf # 写入下面的配置 [Service] Environment="HTTPS_PROXY=http://192.168.10.100:7890" # 查看是否配置成功 systemctl show --property=Environment docker # 虚拟机的代理(不用这个应该也行。。待验证) vim /etc/profile,文本末尾添加 # proxy export http_proxy=http://192.168.10.100:7890 export https_proxy=http://192.168.10.100:7890 # 激活 source /etc/profile


2.查看安装命令和使用说明

浙公网安备 33010602011771号