注意:
容器在整个应用程序生命周期工作流中提供以下优点:隔离性、可移植性、灵活性、可伸缩性和可控性。 最重要的优点是可在开发和运营之间提供隔离。
- **仓库:集中存放镜像文件的地方**
镜像构建完成后,可以很容易的在当前宿主上运行,但是, 如果需要在其他服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。所以说,镜像仓库是 Docker 用来集中存放镜像文件的地方,类似于我们之前常用的代码仓库。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。
我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
这里补充一下 Docker Registry 公开服务和私有 Docker Registry 的概念:Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。最常使用的 Registry 公开服务是官方的 Docker Hub ,这也是默认的 Registry,并拥有大量的高质量的官方镜像,网址为:hub.docker.com 。
在国内访问 Docker Hub 可能会比较慢,国内也有一些云服务商提供类似于 Docker Hub 的公开服务。
除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry 。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 Docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。
- **总结**
需要正确的理解仓库/镜像/容器这几个概念:
Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。
image文件可以看作是容器的模板。Docker 根据image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。
**镜像文件**
image 文件生成的容器实例,本身也是一个文件,称为镜像文件。
**容器实例**
一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器
**仓库**
就是放一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候再从仓库中拉下来就可以了。
### 2. Docker安装
#### 2.1 下载地址
桌面版:https://www.docker.com/products/docker-desktop
服务器版:https://docs.docker.com/engine/install/#server
#### 2.2 前提条件
目前,CentOS仅发行版本中的内核支持Docker。Docker运行在CentOS 7 (64-bit)上,要求系统为64位、Linux系统内核版本为3.8以上,这里选用Centos7.x。在Linux上运行`uname`命令查看系统内核版本。
```bash
[root@yaochuang ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
[root@yaochuang ~]# uname -r
3.10.0-1160.el7.x86_64
```
#### 2.3 Docker版本
Docker如今划分成了2个版本:
- Docker CE(社区版)
社区版并非阉割版,而是改了个名称;非常适合开发人员和运维团队构建容器APP。
- Docker EE(企业版)
企业版则提供了一些收费的高级特性。可在经过认证的操作系统和云提供商中使用。
> 一般都会选择Docker CE(社区版),因为CE版本是开源免费的。对于大多数企业公司都比较节约成本。
#### 2.4 Docker CE 安装方式
1. 设置Docker的存储库并从中进行安装,以便安装和升级任务。这是推荐的方法。
2. 有些用户下载RPM软件包并手动安装,并完全手动管理升级。这对于在无法访问互联网的系统上安装Docker等情况很有用。
3. 在测试和开发环境中,一些用户选择使用自动便利脚本来安装Docker。
> 这里我们就选择第一种方式进行安装,这也是推荐的做法。
>
> 至于其他的安装方式,可自行网上学习,这里不做过多讲解。
#### 2.5 安装
**A. CentOS7安装Docker官方文档**
https://docs.docker.com/engine/install/centos/
**B. 确定Linux版本必须是CentOS7及以上**
```bash
[root@localhost ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
[root@localhost ~]# uname -r
3.10.0-1160.el7.x86_64
```
**C. 卸载旧版本**
旧版本的 Docker 被称为`docker`或`docker-engine`. 如果安装了这些,请卸载它们以及相关的依赖项。
```bash
# 第一次安装不需要执行此命令
·
```
**D. 设置存储库**
安装所需的包。 yum-utils提供yum-config-manager实用程序,devicemapper存储驱动程序需要device-mapper-persistent-data和lvm2。
```bash
yum install -y yum-utils device-mapper-persistent-data lvm2
```
**E. 指定Docker镜像源**
> 默认下载Docker回去国外服务器下载,速度较慢,我们可以设置为阿里云镜像源,速度更快
```bash
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
```
**F. 安装最新版的Docker CE**
```bash
yum install docker-ce
```
稍作等待,当命令行最终输出“`Complete!`”或者“`完毕!`”即表示安装完成。
**G. 启动Docker**
```bash
systemctl start docker
```
**H. 验证Docker是否安装成功**
最简单的验证方式,就是使用 docker version 命令,类似于JDK的 java -version 一样的道理。
Docker安装成功的话,就可以直接在命令行运行Docker的命令的。
```bash
docker version
```
如果出现如下信息,说明Docker以及安装成功了!
```bash
[root@yaochuang ~]# docker version
Client: Docker Engine - Community
Version: 20.10.18
API version: 1.41
Go version: go1.18.6
Git commit: b40c2f6
Built: Thu Sep 8 23:14:08 2022
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.18
API version: 1.41 (minimum version 1.12)
Go version: go1.18.6
Git commit: e42327a
Built: Thu Sep 8 23:12:21 2022
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.8
GitCommit: 9cd3357b7fd7218e4aec3eae239db1f68a5a6ec6
runc:
Version: 1.1.4
GitCommit: v1.1.4-0-g5fd4c4d
docker-init:
Version: 0.19.0
GitCommit: de40ad0
```
【可选】:当然,你也可以通过运行 hello-world 映像来检验Docker是否安装成功:
```bash
docker run hello-world
```
> 上面的命令含义:下载官方提供的用于测试使用的hello-world镜像并将其运行到容器中,来检验Docker服务是否正常安装并运行。
>
> 执行上面的命令之后,Docker会自动下载hello-world镜像并自动运行到容器中,当命令行中出现“Hello from Docker!”的字样,说明已经成功运行了hello-world镜像,一切就OK了!
#### 2.6 Docker默认挂载目录
Docker CE安装成功之后,你可以发现 /var/lib 目录下有一个 docker 目录,你可以进入Docker目录查看Docker的一些结构,如下图所示:
其中有一个containers 目录,这个目录就是存放Docker容器的。上面我们有提及到 hello-world 镜像,如果你运行了 hello-world 镜像,那么这个镜像所生成的容器信息,就会存储在 containers 目录中。
#### 2.7 启动Docker
1、通过在命令行中执行以下命令,即可实现Docker服务的启动。当服务器重启的时候,Docker服务需要手工去启动。
```bash
systemctl start docker
```
2、自动启动
```bash
systemctl enable docker
```
#### 2.8 卸载Docker
```bash
systemctl stop docker
yum remove docker-ce
rm -rf /var/lib/docker
rm -rf /var/lib/containerd
```
#### 2.9 阿里云镜像加速器
登录阿里云:https://promotion.aliyun.com/ntms/act/kubernetes.html
针对Docker客户端版本大于 1.10.0 的用户
您可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器。
注意将`https://rxxxxxx0.mirror.aliyuncs.com`修改为自己的地址。
```bash
# 分步骤执行
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://rxxxxxx0.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
```
### 3. Docker常用命令
#### 3.1 启动命令
- 启动docker: systemctl start docker
- 停止docker: systemctl stop docker
- 重启docker: systemctl restart docker
- 查看docker状态: systemctl status docker
- 开机启动: systemctl enable docker
- 查看docker概要信息: docker info
- 查看docker总体帮助文档: docker --help
- 查看docker命令帮助文档: docker 具体命令--help
#### 3.2 镜像命令
1. 列出本地主机上的镜像:`docker [OPTIONS] images`
OPTIONS说明:
- -a :列出本地所有的镜像(含历史映像层)
- -q :只显示镜像ID。
```bash
[root@yaochuang ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 12 months ago 13.3kB
```
选项说明:
-
REPOSITORY:表示镜像的仓库源
- TAG:镜像的标签版本号
- IMAGE ID:镜像ID
- CREATED:镜像创建时间
- SIZE:镜像大小
2. docker search [OPTIONS] 镜像名字
```bash
docker search redis
```
OPTIONS说明:
- --limit : 只列出N个镜像,默认25个
- docker search --limit 5 redis
- NAME:镜像名称
- DESCRIPTION:镜像说明
- STARS:点赞数量
- OFFICIAL:是否是官方的
- AUTOMATED:是否是自动构建的
3. docker pull 下载镜像
```bash
[root@yaochuang ~]# docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
2b55860d4c66: Pull complete
Digest: sha256:20fa2d7bb4de7723f542be5923b06c4d704370f0390e4ae9e1c833544c1
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
[root@yaochuang ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 2dc39ba059dc 3 weeks ago 77.8MB
hello-world latest feb5d9fea6a5 12 months ago 13.3kB
```
4. docker system df 查看镜像/容器/数据卷所占的空间
```bash
[root@yaochuang ~]# docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 2 1 77.85MB 77.83MB (99%)
Containers 2 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
```
5. docker rmi 镜像ID
```bash
# 删除镜像
docker rmi -f 镜像ID
# 删除多个
docker rmi -f 镜像名1:TAG 镜像名2:TAG
# 删除全部
docker rmi -f $(docker images -qa)
```
6. 面试题:谈谈docker虚悬镜像是什么?
构建和删除镜像时出现错误,导致仓库名(REPOSITORY)、标签(TAG)都是
> 有镜像才能创建容器, 这是根本前提(下载一个CentOS或者ubuntu镜像演示)
```bash
docker pull centos
docker pull ubuntu
```
##### 3.3.1 新建 + 启动容器
> 运行容器需要指定具体镜像,如果镜像不存在,会直接下载
```bash
# 简单操作
docker run 镜像的标识|镜像名称[:tag]
# 常用的参数
docker run -d -p 宿主机端口:容器端口 --name 容器名称 镜像的标识|镜像名称[:tag]
docker run -d -p 8081:8080 --name tomcat b8
```
OPTIONS说明:
- --name="容器新名字"为容器指定一个名称;
- -d: 后台运行容器并返回容器ID,也即启动守护式容器(后台运行);
- -i:以交互模式运行容器,通常与 -t 同时使用;
- -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;
也即启动交互式容器(前台有伪终端,等待交互);
- -P: 随机端口映射,大写P
- -p: 指定端口映射,小写p
##### 3.3.2 启动交互式容器
```bash
# 使用镜像centos:latest以交互模式启动一个容器,在容器内执行/bin/bash命令。
docker run -it centos /bin/bash
```
参数说明:
- -i: 交互式操作。
- -t: 终端。
centos : centos 镜像。
/bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。
要退出终端,直接输入`exit`
**注意:有镜像才能创建容器,这是根本前提(下载一个Redis6.0.8镜像演示)**
在大部分的场景下,我们希望 docker 的服务是在后台运行的,我们可以过 -d 指定容器的后台运行模式。
```bash
docker run -d 容器名
#使用镜像centos:latest以后台模式启动一个容器
docker run -d centos
```
> 问题:然后docker ps -a 进行查看, 会发现容器已经退出。
> 注意:Docker容器后台运行,就必须有一个前台进程。
> 容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。
这个是docker的机制问题,比如你的web容器,我们以nginx为例,正常情况下,我们配置启动服务只需要启动响应的service即可。例如service nginx start。但是,这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用,这样的容器后台启动后,会立即自杀因为他觉得他没事可做了,所以,最佳的解决方案是,将你要运行的程序以前台进程的形式运行,常见就是命令行模式,表示我还有交互操作,别中断。
```bash
# 前台交互式启动
[root@dk ~]# docker run -it redis:6.0.8
# 后台守护式启动
[root@dk ~]# docker run -d redis:6.0.8
# 查看容器日志
[root@dk ~]# docker logs 容器ID
# 查看容器内运行的进程
[root@dk ~]# docker top 容器ID
# 查看容器内部细节
[root@dk ~]# docker inspect 容器ID
```
##### 3.3.3 列出正在运行的容器
```bash
docker ps [OPTIONS]
```
OPTIONS说明:
- -a :列出当前所有正在运行的容器+历史上运行过的
- -l :显示最近创建的容器。
- -n:显示最近n个创建的容器。
- -q :静默模式,只显示容器编号。
##### 3.3.4 退出容器
| 方式 | 结果 | 再次启动 |
| --------------------------- | ----------------------- | ---------------------- |
| exit(命令) | 退出后,这个容器也就消失了,容器销毁ps查不到 | docker start 容器名/容器ID |
| Ctrl + D(快捷方式) | 退出后,这个容器也就消失了,容器销毁ps查不到 | docker start 容器名/容器ID |
| 先按Ctrl + P,再按Ctrl + Q(快捷方式) | 退出容器,ps能查到,还在后台运行 | docker attach 容器名/容器ID |
##### 3.3.5 重启&停止&删除容器
```bash
# 重新启动容器
docker restart 容器ID或者容器名 # 启动停止运行的容器
docker stop 容器ID或者容器名 # 停止指定容器
docker rm 容器ID或者容器名 # 删除全部容器docker rm $(docker ps -qa)
docker kill 容器ID或容器名 # 强制停止容器
docker rm -f $(docker ps -qa) # 一次性删除多个容器实例
docker ps -a -q | xargs docker rm
```
##### 3.3.6 重新进入容器
首先启动容器:
```bash
# 以交互式模式在后台运行ubuntu
[root@dk ~]# docker run -itd ubuntu /bin/bash
659360a3583c2aa96dc420307af704c8efdf59723f4014704ed9f4c1431fc954
```
在使用 **-d** 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入:
- **docker attach:**直接进入容器启动命令的终端,不会启动新的进程。用exit退出,会导致容器的停止。
```bash
[root@dk ~]# docker attach 659360a3583c
```
- **docker exec**:推荐大家使用 docker exec 命令,因为此命令会退出容器终端,但不会导致容器的停止。
```bash
# 进入正在运行的容器并以命令行交互,只有容器正在运行才能执行成功!
[root@dk ~]# docker exec -it 659360a3583c /bin/bash
```
总结:一般用-d后台启动的程序,再用exec进入对应容器实例
##### 3.3.7 从容器内拷贝文件到主机上
语法格式如下:
> docker cp 容器ID:容器内路径 目的主机路径
```bash
# 1. 运行ubuntu
[root@dk ~]# docker run -itd ubuntu /bin/bash
61eb9ff76e82442175c128018eda7958f278b742adf3fb9e98b5d68039f34c52
# 2. 查看正在运行的容器
[root@dk ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
61eb9ff76e82 ubuntu "/bin/bash" 6 seconds ago Up 3 seconds my_shk
# 3. 重新进去容器
[root@dk ~]# docker exec -it 61eb9ff76e82 /bin/bash
# 4. 进入ubuntu容器后创建文件
root@61eb9ff76e82:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
root@61eb9ff76e82:/# cd home
root@61eb9ff76e82:/home# echo hello ubuntu >> ubuntu.txt
root@61eb9ff76e82:/home# cat ubuntu.txt
hello ubuntu
# 5. 退出ubuntu容器,此操作并不会真正退出容器。容器会继续在后台运行
root@61eb9ff76e82:/home# exit
exit
# 6. 执行复制的操作
[root@dk ~]# docker cp 61eb9ff76e82:/home/ubuntu.txt /home
# 7. 查看结果
[root@dk ~]# cat /home/ubuntu.txt
hello ubuntu
```
示例:
```bash
#将物理主机中的/data/index.html拷贝到容器bd96d72ed9c7:/web/目录下
[root@dk ~]# docker cp /data/index.html bd96d72ed9c7:/web/
#将物理主机中的/data/index.html拷贝到容器bd96d72ed9c7:/web/目录下并改名为index.php
[root@dk ~]# docker cp /data/index.html bd96d72ed9c7:/web/index.php
#拷贝容器bd96d72ed9c7:/web/目录到物理主机中的/data/目录下
[root@dk ~]# docker cp bd96d72ed9c7:/web /data/
```
##### 3.3.8 导出和导入容器
- 导出容器:如果要导出本地某个容器,可以使用 **docker export** 命令。
```bash
# 语法格式如下:
[root@dk ~]# docker export 容器ID > 文件名.tar
# 案例
# 1. 运行镜像创建容器
[root@dk ~]# docker run -itd ubuntu /bin/bash
450d0315404c5042a4f7ebad233b9ce5235897c6069e234e5c5b4cfaa816c025
# 2. 查看正在运行的容器
[root@dk ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
450d0315404c ubuntu "/bin/bash" 5 seconds ago Up 3 seconds gallant_raman
# 3. 导出容器
[root@dk ~]# docker export 450d0315404c > ubuntu.tar
# 4. 查看导出文件
[root@dk ~]# ls
anaconda-ks.cfg ubuntu.tar
```
- 导入容器快照:可以使用 docker import 从容器快照文件中再导入为镜像,以下实例将快照文件 ubuntu.tar 导入到镜像 test/ubuntu:v1:
```bash
# 语法格式如下:
[root@dk ~]# cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
# 案例
# 1. 可以先删除ubuntu镜像,也可以不删除。因为后面导入时重新设置了版本不冲突
[root@dk ~]# docker rmi -f ubuntu
# 2. 导入镜像
[root@dk ~]# cat /root/ubuntu.tar | docker import - test/ubuntu:v1
sha256:ede897f1dc208a1f738d4afd1639ebcfc34f91e49845f2ac5dc4f94a0a8ddfc9
# 3. 查看导入的镜像
[root@dk ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test/ubuntu v1 ede897f1dc20 6 seconds ago 77.8MB
tomcat latest ab37a470285a 27 hours ago 474MB
mysql 5.7 eef0fab001e8 3 days ago 495MB
mysql latest 2a04bf34fdf0 3 days ago 535MB
ubuntu latest a8780b506fa4 6 days ago 77.8MB
hello-world latest feb5d9fea6a5 13 months ago 13.3kB
centos latest 5d0da3dc9764 13 months ago 231MB
```
##### 3.3.9 常用命令总结
```bash
attach Attach to a running container # 当前 shell 下 attach 连接指定运行容器
build Build an image from a Dockerfile # 通过 Dockerfile 定制镜像
commit Create a new image from a container changes # 提交当前容器为新的镜像
cp Copy files/folders from the containers filesystem to the host path #从容器中拷贝指定文件或者目录到宿主机中
create Create a new container # 创建一个新的容器,同 run,但不启动容器
diff Inspect changes on a container's filesystem # 查看 docker 容器变化
events Get real time events from the server # 从 docker 服务获取容器实时事件
exec Run a command in an existing container # 在已存在的容器上运行命令
export Stream the contents of a container as a tar archive # 导出容器的内容流作为一个 tar 归档文件[对应 import ]
history Show the history of an image # 展示一个镜像形成历史
images List images # 列出系统当前镜像
import Create a new filesystem image from the contents of a tarball # 从tar包中的内容创建一个新的文件系统映像[对应export]
info Display system-wide information # 显示系统相关信息
inspect Return low-level information on a container # 查看容器详细信息
kill Kill a running container # kill 指定 docker 容器
load Load an image from a tar archive # 从一个 tar 包中加载一个镜像[对应 save]
login Register or Login to the docker registry server # 注册或者登陆一个 docker 源服务器
logout Log out from a Docker registry server # 从当前 Docker registry 退出
logs Fetch the logs of a container # 输出当前容器日志信息
port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT # 查看映射端口对应的容器内部源端口
pause Pause all processes within a container # 暂停容器
ps List containers # 列出容器列表
pull Pull an image or a repository from the docker registry server # 从docker镜像源服务器拉取指定镜像或者库镜像
push Push an image or a repository to the docker registry server # 推送指定镜像或者库镜像至docker源服务器
restart Restart a running container # 重启运行的容器
rm Remove one or more containers # 移除一个或者多个容器
rmi Remove one or more images # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]
run Run a command in a new container # 创建一个新的容器并运行一个命令
save Save an image to a tar archive # 保存一个镜像为一个 tar 包[对应 load]
search Search for an image on the Docker Hub # 在 docker hub 中搜索镜像
start Start a stopped containers # 启动容器
stop Stop a running containers # 停止容器
tag Tag an image into a repository # 给源中镜像打标签
top Lookup the running processes of a container # 查看容器中运行的进程信息
unpause Unpause a paused container # 取消暂停容器
version Show the docker version information # 查看 docker 版本号
wait Block until a container stops, then print its exit code # 截取容器停止时的退出状态值
```
### 4. Docker镜像
##### 4.1 什么是镜像?
- 是什么?
**镜像**是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。
- 分层的镜像
以我们的pull为例,在下载的过程中我们可以看到docker的镜像好像是在一层一层的在下载
- UnionFS(联合文件系统)
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
- Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是引导文件系统bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs (root file system) ,在bootfs之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
平时我们安装进虚拟机的CentOS都是好几个G,为什么docker这里才200M?
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别, 因此不同的发行版可以公用bootfs。
- 为什么 Docker 镜像要采用这种分层结构呢
镜像分层最大的一个好处就是共享资源,方便复制迁移,就是为了复用。比如说有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
##### 4.2 重点理解
Docker镜像层都是只读的,容器层是可写的。当容器启动时,一个新的可写层被加载到镜像的顶部。 这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
##### 4.3 Docker镜像commit操作案例
> docker commit提交容器副本使之成为一个新的镜像
```bash
# 语法格式为:
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名]
# 案例演示ubuntu安装vim
# 1. 从Hub上下载ubuntu镜像到本地并成功运行,原始的默认Ubuntu镜像是不带着vim命令的
[root@dk ~]# docker run -itd ubuntu /bin/bash
[root@dk ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5ad2545ddc9f ubuntu "/bin/bash" 8 seconds ago Up 5 seconds laughing_wilson
# 2. 外网连通的情况下,安装vim
[root@dk ~]# docker exec -it 5ad2545ddc9f /bin/bash
# 3. 更新包管理工具
root@5ad2545ddc9f:/# apt-get update
# 4. 安装vim
root@5ad2545ddc9f:/# apt-get -y install vim
# 5. 安装完成后commit我们自己的新镜像
[root@dk ~]# docker commit -m="add my images" -a="yaochuang" 5ad2545ddc9f yao/myubuntu:1.1
# 6. 查看自己的镜像,可以看到yao/myubuntu已经发布成功了
[root@dk ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
yao/myubuntu 1.1 5ea55b97dc36 5 seconds ago 177MB
test/ubuntu v1 ede897f1dc20 2 hours ago 77.8MB
tomcat latest ab37a470285a 29 hours ago 474MB
mysql 5.7 eef0fab001e8 4 days ago 495MB
mysql latest 2a04bf34fdf0 4 days ago 535MB
ubuntu latest a8780b506fa4 6 days ago 77.8MB
hello-world latest feb5d9fea6a5 13 months ago 13.3kB
centos latest 5d0da3dc9764 13 months ago 231MB
# 7. 重新运行自己发布的镜像文件
[root@dk ~]# docker run -itd yao/mybubuntu:1.1 /bin/bash
668079fcda31acaddd39396b6e013ac8fad90591ad9047f1b3df61d363d12717
# 8. 进入自己发布的容器
[root@dk ~]# docker exec -it 668079fcda31 /bin/bash
# 9. 此时再使用vim编辑器
root@668079fcda31:/# vim a
```
1. 官网是默认下载的Ubuntu没有vim命令
2. 我们自己commit构建的镜像,新增加了vim功能,可以成功使用。
**总结**
Docker中的镜像分层,支持通过扩展现有镜像,创建新的镜像。类似Java继承于一个Base基础类,自己再按需扩展。新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。
##### 4.4 生命周期
Docker 容器经历以下阶段:
- 创建容器
- 运行容器
- 暂停容器(可选)
- 取消暂停容器(可选)
- 启动容器
- 停止容器
- 重启容器
- 杀死容器
- 销毁容器
### 5. 本地镜像发布到阿里云
#### 5.1 本地镜像发布到阿里云流程
#### 5.2 镜像的生成方法
* 上一章节已经讲过操作
* 反复练习生成两个镜像centos + ubuntu
* 请大家务必多动手练习、
* 后面的DockerFile章节,第2种方法
#### 5.3 将本地镜像推送到阿里云
- 本地镜像素材原型
- 阿里云开发者平台
(1)打开链接:https://promotion.aliyun.com/ntms/act/kubernetes.html,登录阿里云开发者平台
* 创建镜像仓库
(1)登录阿里云
(2)选择容器镜像服务
(3)创建命名空间
(4)创建镜像仓库
(5)选择`管理`获取脚本信息
* 管理界面脚本
* 脚本实例,将镜像推送到Registry
```
$ docker login --username=no***@live.com registry.cn-hangzhou.aliyuncs.com
$ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu:[镜像版本号]
$ docker push registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu:[镜像版本号]
```
检查阿里云上的镜像仓库
#### 5.4 将阿里云上的镜像下载到本地
```bash
# 先删除本地已存在的镜像
[root@dk ~]# docker rmi -f 5ea55b97dc36
Untagged: yao/mybubuntu:1.1
Untagged: registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu:1.1
Untagged: registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu@sha256:102746278de36b7c9093a5b2b56abd6ffa66f656192127e912c7d4f070b46e2a
Deleted: sha256:5ea55b97dc36e138a84efaea893ab904c7c36e7963114eedb7ddeafb512c88a9
[root@dk ~]# docker pull registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu:1.1
```
### 6. 本地镜像发布到私有库
#### 6.1 本地镜像发布到私有库流程
#### 6.2 是什么?
1. 官方Docker Hub地址:https://hub.docker.com/,中国大陆访问太慢了且有被阿里云取代的趋势,不太主流。
2. Docker Hub、阿里云这样的公共镜像仓库可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要创建一个本地私人仓库供给团队使用,基于公司内部项目构建镜像。
3. Docker Registry是官方提供的工具,可以用于构建私有镜像仓库。
#### 6.3 将本地镜像推送到私有库
1. 下载镜像Docker Registry
```bash
[root@dk ~]# docker pull registry
Using default tag: latest
latest: Pulling from library/registry
213ec9aee27d: Pull complete
4583459ba037: Pull complete
6f6a6c5733af: Pull complete
b136d5c19b1d: Pull complete
fd4a5435f342: Pull complete
Digest: sha256:de4b7d974220231c29828d2a643b7dac3186746eb91f4d688438680bf0ebaf60
Status: Downloaded newer image for registry:latest
docker.io/library/registry:latest
```
2. 运行私有库Registry,相当于本地有个私有Docker Hub
```bash
# 此命令现在暂时无需了解,后面第7章再做说明
[root@dk ~]# docker run -d -p 5000:5000 -v /noyao/myregistry/:/tmp/registry --privileged=true registry
8756168da5688dbe17c61fcfed5f72b9e084ba404e872024d0201bcadce92e0d
```
默认情况,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器卷映射,方便于宿主机联调
4. 案例演示:创建一个新镜像,ubuntu安装ifconfig命令
> ifconfig(接口配置)是一个网络管理工具。它用于在Linux操作系统中配置和查看网络接口的状态。使用ifconfig可以分配IP地址、启用或禁用接口、管理ARP缓存、路由等。
- 从Hub上下载ubuntu镜像到本地并成功运行
```bash
[root@dk ~]# docker run -it ubuntu /bin/bash
```
- 原始的Ubuntu镜像是不带着ifconfig命令的
```bash
root@605767afd7aa:/# ipconfig
bash: ipconfig: command not found
root@605767afd7aa:/# apt-get update
root@605767afd7aa:/# apt-get install net-tools
root@605767afd7aa:/# ifconfig
eth0: flags=4163
* 启动我们的新镜像并和原来的对比
1. 官网是默认下载的Ubuntu没有ifconfig命令
2. 我们自己commit构建的新镜像,新增加了ifconfig功能,可以成功使用。
```bash
[root@dk ~]# docker images
[root@dk ~]# docker run -it ycubuntu:1.2 /bin/bash
root@6fbe42982759:/# ifconfig
```
* curl验证私服库上有什么镜像1
> **curl命令** 是一个利用URL规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称curl为下载工具。作为一款强力工具,curl支持包括HTTP、HTTPS、ftp等众多协议,还支持POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。做网页处理流程和数据检索自动化,curl可以助你一臂之力。
```bash
# -X:(HTTP)指定与服务器通信使用的请求方法,如:GET、PUT、POST、DELETE等,默认GET
[root@dk ~]# curl -XGET http://192.168.170.151:5000/v2/_catalog
{"repositories":[]}
```
可以看到目前私服库没有任何镜像上传过,继续看下面的步骤!
* 将新镜像ycubuntu:1.2修改符合私服规范的Tag
```bash
# 公式:
docker tag 镜像:Tag Host:Port/Repository:Tag
# 自己host主机IP地址,填写同学你们自己的,不要粘贴错误
# 使用命令 docker tag 将ycubuntu:1.2 这个镜像修改为192.168.170.151:5000/ycubuntu:1.2
[root@dk ~]# docker tag ycubuntu:1.2 192.168.170.151:5000/ycubuntu:1.2
```
* 修改配置文件使之支持http
```bash
# registry-mirrors 配置的是国内阿里提供的镜像加速地址,不用加速的话访问官网的会很慢。
# 2个配置中间有个逗号 ','别漏了,这个配置是json格式的。
[root@dk ~]# cat /etc/docker/daemon.json
{
"registry-mirrors":["https://aa25jngu.mirror.aliyuncs.com"],
"insecure-registries":["192.168.170.151:5000"]
}
```
上述理由:docker默认不允许http方式推送镜像,通过配置选项来取消这个限制。 修改完后重启docker。
```bash
# 重启docker
[root@dk ~]# systemctl restart docker
# 运行私有库
[root@dk ~]# docker run -d -p 5000:5000 -v /noyao/myregistry/:/tmp/registry --privileged=true registry
```
- push推送到私服库
```bash
[root@dk ~]# docker push 192.168.170.151:5000/ycubuntu:1.2
The push refers to repository [192.168.170.151:5000/ycubuntu]
d2126fedd4bd: Pushed
f4a670ac65b6: Pushed
1.2: digest: sha256:7cd62eb5e172572d793a343834adeaa722d2fa0f1990da54e9e7ceddfd04312c size: 741
```
* curl验证私服库上有什么镜像2
```bash
[root@dk ~]# curl -XGET http://192.168.170.151:5000/v2/_catalog
{"repositories":["ycubuntu"]}
```
* pull到本地并运行
```*bash
[root@dk ~]# docker pull 192.168.170.151:5000/ycubuntu:1.2
# 语法如下:
[root@dk ~]# docker run -it 镜像ID /bin/bash
# 镜像ID记得更换为自己的
[root@dk ~]# docker run -it 466572ffe865 /bin/bash
# 输入ifconfig查看信息
root@8097570fad7a:/# ifconfig
eth0: flags=4163
```bash
[root@dk ~]# docker run -d -p 5000:5000 -v /noyao/myregistry/:/tmp/registry --privileged=true registry
8756168da5688dbe17c61fcfed5f72b9e084ba404e872024d0201bcadce92e0d
```
默认情况,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器卷映射,方便与宿主机联调。
#### 7.3 是什么
卷(volume)就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或共享数据的特性;卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。
* 将docker容器内的数据保存进宿主机的磁盘中
* 运行一个带有容器卷存储功能的容器实例
```bash
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
```
#### 7.4 能做什么
将运用与运行的环境打包镜像,run后形成容器实例运行 ,但是我们对数据的要求希望是持久化的。Docker容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没有了。为了能保存数据在docker中我们使用卷。举个生活中例子帮助理解:假设我们有一台笔记本电脑,现在我需要备份笔记本电脑中的数据,那么我可以将数据保存外外置硬盘或U盘中,还可以备份到百度云盘或者阿里云盘。假设我现在有一台docker容器,里面有一些重要的数据,突然有一天有个人执行了docker stop或者docker rm -f,容器一旦被删除则里面的数据也会消失,这时候我们就需要用容器数据卷的方式完成数据持久化(备份重要的数据),docker可以实现容器的目录和主机的目录做一个映射,可以把容器内的数据备份+持久化到本地主机目录,这样就形成了对重要数据的保护,不容易丢失。
特点:
1. 数据卷可在容器之间共享或重用数据
2. 卷中的更改可以直接实时生效
3. 数据卷中的更改不会包含在镜像的更新中
4. 数据卷的生命周期一直持续到没有容器使用它为止
#### 7.5 数据卷案例
案例1:匿名卷
```bash
# 直接指定容器内的路径,会随机生成一个宿主机目录地址
docker run -d --name nx1 -p 80:80 -v /nginx/dir --privileged=true nginx
```
案例2:具名卷
```bash
# 指定数据卷的名称,本地卷在/var/lib/docker/volumes/数据卷的名称/_data
docker run -d -p 8080:80 --name nx2 --privileged=true -v nginx:/nginx/dir nginx
```
1. 宿主机与容器之间添加数据卷
```bash
# 1. 运行镜像并添加数据卷
[root@dk ~]# docker run -it --name yaoge --privileged=true -v /yc/myData:/tmp/ubuntu ubuntu /bin/bash
# 2. 在ubuntu中创建文件并写入内容
root@21cd326d8bfa:/# cd /tmp/ubuntu
root@21cd326d8bfa:/tmp/ubuntu# echo 'hello ubuntu' >> test.txt
# 3. 写入完毕后退出ubuntu
# 4. 在宿主机CentOS中查看/yc/myData/test.txt的文件内容
[root@dk ~]# cd /yc/myData/
[root@dk myData]# cat test.txt
hello ubuntu
# 5. 重写追加文字到test.txt文件中
[root@dk myData]# echo 'hello docker!!' >> test.txt
# 6. 查看运行的容器
[root@dk myData]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
21cd326d8bfa ubuntu "/bin/bash" About a minute ago Up About a minute yaoge
d5d543121c4b registry "/entrypoint.sh /etc…" 30 hours ago Up 30 hours 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp keen_ramanujan
# 7. 重新进去容器,并查看文件内容
[root@dk myData]# docker exec -it 21cd326d8bfa /bin/bash
root@21cd326d8bfa:/# cd /tmp/ubuntu
root@21cd326d8bfa:/tmp/ubuntu# cat test.txt
hello ubuntu
hello docker!!
# 8. 可以看到在宿主机中添加的内容也能够在ubuntu容器中看到了
```
查看数据卷是否挂载成功
```bash
# 查看运行的容器
[root@dk ~]# docker ps
[root@dk ~]# docker inspect d5d543121c4b
"Mounts": [
{
"Type": "bind",
"Source": "/noyao/myregistry",
"Destination": "/tmp/registry",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
```
2. 练习:容器和宿主机之间数据共享
* docker修改,主机同步获得
* 主机修改,docker同步获得
* docker容器stop,主机修改,docker容器重启看数据是否同步
总结:
1. 如果是开发或测试环境建议使用-v指定具体的目录地址
2. 如果是生产环境,建议使用具名卷;-v nginxhtml:容器内的地址;容器外的地址:/var/lib/docker/volumes/数据卷名称/_data
3. 如果要指定具体的路径,例如:/var/lib/mysql/mysql.sock,建议使用指定具体的目录
3. 读写规则映射添加说明
读写规则分为读写和只读两种,在默认情况下,就是容器可以读和写
```bash
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录-镜像名
```
下面我们进行只读的限制:容器实例内部被限制,容器只能读取不能写
```bash
# 此时如果宿主机写入内容,可以同步给容器内,容器可以读取到,但是不能写入。
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
```
4. 卷的继承和共享
我们在上一个容器yaoge的基础上进行配置,对于卷的继承,就是相当于使用原来的容器生成了一个新的一样的容器实例。
语法:
```bash
docker run -it --privileged=true --volumes-from 父类 --name yaoge ubuntu
```
容器yaoge2继承容器yaoge的卷规则
```bash
docker run -it --name=yaoge2 --privileged=true --volumes-from yaoge ubuntu bash
```
5. 多个MySQL实现数据共享(等待验证)
```bash
[root@localhost ~]# docker run -d -p 6603:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
[root@localhost ~]# docker run -d -p 6604:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
```
6. 总结
容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器为止。
但是一旦持久化到了本地,本地的数据是不会删除的!
### 8. Docker常规安装简介
#### 8.1 总体步骤
1. 搜索镜像
2. 拉取镜像
3. 查看镜像
4. 运行镜像
5. 服务端口映射
6. 停止容器
7. 移除容器
#### 8.2 安装Tomcat
1. 搜索Tomcat
```bash
[root@dk ~]# docker search tomcat
```
2. 从docker hub上拉取tomcat镜像到本地
```bash
[root@dk ~]# docker pull tomcat
```
3. 查看镜像
```bash
[root@dk ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ycubuntu 1.2 466572ffe865 2 days ago 119MB
test/ubuntu v1 ede897f1dc20 5 days ago 77.8MB
ubuntu latest a8780b506fa4 11 days ago 77.8MB
registry latest dcb3d42c1744 5 weeks ago 24.1MB
tomcat latest fb5657adc892 10 months ago 680MB
hello-world latest feb5d9fea6a5 13 months ago 13.3kB
centos latest 5d0da3dc9764 14 months ago 231MB
```
4. 运行镜像(使用tomcat镜像创建容器实例)
```bash
# -p 小写,主机端口:docker容器端口
# -P 大写,随机分配端口
# i:交互
# t:终端
# d:后台
[root@dk ~]# docker run -it -p 8080:8080 tomcat
```
5. 打开浏览器输入地址:http://192.168.170.151:8080/,打开时会报404错误,如下图所示:
```bash
# 1. 查看容器ID
[root@dk ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2432606c447f tomcat "catalina.sh run" 2 hours ago Up 2 hours 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp zen_gagarin
# 2. 重新进入容器
[root@dk ~]# docker exec -it 2432606c447f /bin/bash
# 3. 查看webapps文件夹
root@2432606c447f:/usr/local/tomcat# ls -l
total 132
-rw-r--r--. 1 root root 18994 Dec 2 2021 BUILDING.txt
-rw-r--r--. 1 root root 6210 Dec 2 2021 CONTRIBUTING.md
-rw-r--r--. 1 root root 60269 Dec 2 2021 LICENSE
-rw-r--r--. 1 root root 2333 Dec 2 2021 NOTICE
-rw-r--r--. 1 root root 3378 Dec 2 2021 README.md
-rw-r--r--. 1 root root 6905 Dec 2 2021 RELEASE-NOTES
-rw-r--r--. 1 root root 16517 Dec 2 2021 RUNNING.txt
drwxr-xr-x. 2 root root 4096 Dec 22 2021 bin
drwxr-xr-x. 1 root root 22 Nov 14 09:25 conf
drwxr-xr-x. 2 root root 4096 Dec 22 2021 lib
drwxrwxrwx. 1 root root 80 Nov 14 09:25 logs
drwxr-xr-x. 2 root root 159 Dec 22 2021 native-jni-lib
drwxrwxrwx. 2 root root 30 Dec 22 2021 temp
drwxr-xr-x. 2 root root 6 Dec 22 2021 webapps
drwxr-xr-x. 7 root root 81 Dec 2 2021 webapps.dist
drwxrwxrwx. 2 root root 6 Dec 2 2021 work
# 4. 删除webapps文件夹
root@2432606c447f:/usr/local/tomcat# rm -r webapps
# 5. 将webapps.dist文件夹改名为webapps
root@2432606c447f:/usr/local/tomcat# mv webapps.dist webapps
```
重新刷新浏览器,可以看到Tomcat可以正常访问了!
6. 免修改版本
```bash
[root@dk ~]# docker pull billygoo/tomcat8-jdk8
[root@dk ~]# docker run -d -p 8080:8080 --name mytomcat8 billygoo/tomcat8-jdk8
```
#### 8.3 安装MySQL
1. docker hub上面查找mysql镜像
```bash
[root@dk ~]# docker search mysql
```
2. 从docker hub上(阿里云加速器)拉取mysql镜像到本地标签为5.7
```bash
[root@dk ~]# docker pull mysql:5.7
```
3. 查看镜像
```bash
[root@dk ~]# docker images
```
4. 运行镜像(使用mysql5.7镜像创建容器)
```bash
# 1. 运行镜像方式1
[root@dk ~]# docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
# 运行镜像方式2
[root@dk ~]# docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
# 2. 查看有docker运行了哪些容器
[root@dk ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fe02e9067ea2 mysql:5.7 "docker-entrypoint.s…" 5 seconds ago Up 3 seconds 3306/tcp, 33060/tcp some-mysql
# 3. 重新进入容器
[root@dk ~]# docker exec -it fe02e9067ea2 /bin/bash
root@fe02e9067ea2:/# mysql -uroot -p
Enter password: (输入MYSQL_ROOT_PASSWORD上定义的密码)
```
登录成功后,建库建表插入数据。
```mysql
mysql> create database studentDB;
Query OK, 1 row affected (0.00 sec)
mysql> use studentDB;
Database changed
mysql> create table student(id int primary key auto_increment,name varchar(50));
Query OK, 0 rows affected (0.05 sec)
mysql> insert into student values(0,'aaa');
Query OK, 1 row affected (0.07 sec)
mysql> select * from student;
+----+------+
| id | name |
+----+------+
| 1 | aaa |
+----+------+
1 row in set (0.00 sec)
```
Win10使用外部工具SQLyog也可以连接MySQL,如果连接不上记得防火墙端口放行,或者在运行时使用的是默认端口,注意使用`docker ps`查看容器运行的端口是多少!
插入中文数据时会有乱码问题,如下图:
为什么报错?
- docker上默认字符集编码隐患
- client客户端的编码不是utf8
- server端的编码不是utf8
- database数据库的编码不是utf8
- 数据库的表的编码不是utf8
- 表中的列字段编码不是utf8
docker里面的mysql容器实例查看,内容如下:
```bash
SHOW VARIABLES LIKE 'character%'
```
删除此容器重新开始配置!
重新新建容器:
```bash
[root@dk ~]# docker run -d -p 3306:3306 --privileged=true -v /yaochuang/mysql/log:/var/log/mysql -v /yaochuang/mysql/data:/var/lib/mysql -v /yaochuang/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:5.7
1d25eb44bf8f77d63508bc086249b0b67b3df2f014757f7403f9c885ae2e8c3e
[root@dk ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1d25eb44bf8f mysql:5.7 "docker-entrypoint.s…" 7 seconds ago Up 4 seconds 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql
[root@dk ~]# cd /yaochuang/mysql/conf/
[root@dk conf]# vi my.cnf
[root@dk conf]# cat my.cnf
[mysqld]
# 添加此配置跳过登录
skip-grant-tables
collation_server=utf8_general_ci
character-set-server=utf8
[client]
default-character-set=utf8
[root@dk conf]# docker restart mysql
# 重新进入mysql
[root@dk ~]# docker exec -it mysql bash
# 不用输入密码,直接回车登录
root@23c664f57c61:/# mysql -uroot
# 注意:接下来的代码都是MySQL
mysql> use mysql;
# 必须先刷新权限
mysql> flush privileges;
# 修改密码
mysql> alter user 'root'@localhost identified by '123456';
# 修改完成后退出MySQL,退出容器
mysql> exit
root@23c664f57c61:/# exit
# 重新打开my.cnf文件,删除skip-grant-tables,然后重启mysql
# 测试:使用SQLyog连接MySQL,插入中文试试是否成功!
```
总结:
docker安装完MySQL并run出容器后,建议先修改字符集编码后再新建库-表-插数据。
问题:假如将当前容器实例删除,再重新来一次,之前建的studentDB实例还有吗?
5. 停止容器
```bash
[root@dk ~]# docker stop fe02e9067ea2
[root@dk ~]# docker rm -f fe02e9067ea2
```
6. 移除镜像
```bash
[root@dk ~]# docker rmi -f mysql:5.7
```
#### 8.4 安装Redis(待整理,考虑删除。)
Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 的 NoSQL 数据库,并提供多种语言的 API。
#### 8.5 安装Nginx
#### 8.6 安装PHP
#### 8.7 安装MongoDB
#### 8.8 安装Node.js
#### 8.9 安装Python
## 二、高级篇
### 1. Docker复杂安装详说
#### 1.1 安装MySQL主从复制
- 复习主从复制原理
- 主从搭建步骤
第1步:新建主服务器容器实例3307
```bash
# 注意:以下命令是整段,\为换行。此处应也应该要注意MYSQL_ROOT_PASSWORD设置密码时,可能后面会多一个空格
[root@dk ~]# docker run -p 3307:3306 --name mysql-master \
-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=123456 \
-d mysql:5.7
[root@dk ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
c1648630914c mysql:5.7 "docker-entrypoint.s…" 28 seconds ago Up 26 seconds 33060/tcp, 0.0.0.0:3307->3306/tcp, :::3307->3306/tcp
[root@dk ~]# docker exec -it mysql-master bash
# 检查 MYSQL_ROOT_PASSWORD 是否设置正确
root@c1648630914c:/# echo $MYSQL_ROOT_PASSWORD
# 尝试是否能够登陆成功
root@c1648630914c:/# mysql -uroot -p
```
第2步:进入/mydata/mysql-master/conf目录下新建my.cnf
```bash
[root@dk ~]# cd /mydata/mysql-master/conf
[root@dk conf]# cat my.cnf
[mysqld]
# 设置server_id,同一局域网中需要唯一
server-id=101
# 指定不需要同步的数据库名称
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
```
第3步:修改完配置后重启master实例
```bash
[root@dk conf]# docker restart mysql-master
```
第4步:重新进入mysql-master容器
```bash
[root@dk conf]# docker exec -it mysql-master bash
root@c1648630914c:/# mysql -uroot -p
```
第5步:master容器实例内创建数据同步用户
```mysql
mysql> CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
```
第6步:新建从服务器容器实例3308
```bash
[root@dk ~]# docker run -p 3308:3306 --name mysql-slave \
-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=123456 \
-d mysql:5.7
```
第7步:进入/mydata/mysql-slave/conf目录下新建my.cnf
````bash
[root@dk ~]# cd /mydata/mysql-slave/conf
[root@dk ~]# cat my.cnf
[mysqld]
# 设置server_id,同一局域网中需要唯一
server-id=102
# 指定不需要同步的数据库名称
binlog-ignore-db=mysql
# 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin=mall-mysql-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
````
第8步:修改完配置后重启slave实例
```bash
# 重启完成后记得查看后台是否重启成功
[root@dk conf]# docker restart mysql-slave
```
第9步:在主数据库中查看主从同步状态
```mysql
# 此处应当重新进入主库容器,重新进入数据库
mysql> show master status;
+-----------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------------+----------+--------------+------------------+-------------------+
| mall-mysql-bin.000001 | 617 | | mysql | |
+-----------------------+----------+--------------+------------------+-------------------
```
第10步:进入mysql-slave容器
```bash
[root@dk ~]# docker exec -it mysql-slave bash
root@75c733a68748:/# mysql -uroot -p
```
第11步:在从数据库中配置主从复制
参数说明:
- master_host:主数据库的IP地址;
- master_port:主数据库的运行端口;
- master_user:在主数据库创建的用于同步数据的用户账号;
- master_password:在主数据库创建的用于同步数据的用户密码;
- master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;
- master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;
- master_connect_retry:连接失败重试的时间间隔,单位为秒。
```mysql
mysql> change master to master_host='192.168.170.148',master_user='slave',master_password='123456',master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=1821, master_connect_retry=30;
```
第12步:在从数据库中开启主从同步
```bash
mysql> start slave;
```
第13步:在从数据库中查看主从同步状态
```mysql
mysql> show slave status \G;
```
第14步:查看从数据库状态发现已经同步
第15步:主从复制测试
主机新建库—使用库—新建表—插入数据
从机使用库—查看记录
#### 1.2 MySQL搭建双主双从
##### 1.2.2 准备工作
首先会创建一个自定义网络 mysql ,然后会启动 4 台 MySQL 容器实例,其中 2 台作为主机,2 台作为从机。
| 编号 | 主机名 | 备注 |
| ---- | ------ | ---------- |
| 1 | m1 | 主机 |
| 2 | s1 | 从 |
| 3 | m2 | 主机(备) |
| 4 | s2 | 从机(备) |
##### 1.2.3 自定义网络
创建自定义网络,以便容器之间可以通过主机名和端口互相通信:
```bash
docker network create mysql
```
##### 1.2.4 创建四台MySQL容器实例
新建 4 台 MySQL 容器实例,2 台容器实例为 MySQL 的主机,2 台容器实例为 MySQL 的从机:
```bash
docker run -d -p 3306:3306 --network mysql \
--name m1 \
-v m1:/etc/mysql/conf.d \
-v /var/m1/logs:/var/log/mysql \
-v /var/m1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai \
--restart=always mysql:8.0.26 \
--lower_case_table_names=1 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_general_ci \
--default-authentication-plugin=mysql_native_password
docker run -d -p 3307:3306 --network mysql \
--name s1 \
-v s1:/etc/mysql \
-v /var/s1/logs:/var/log/mysql \
-v /var/s1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai \
--restart=always mysql:8.0.26 \
--lower_case_table_names=1 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_general_ci \
--default-authentication-plugin=mysql_native_password
docker run -d -p 3308:3306 --network mysql \
--name m2 \
-v m2:/etc/mysql \
-v /var/m2/logs:/var/log/mysql \
-v /var/m2/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai \
--restart=always mysql:8.0.26 \
--lower_case_table_names=1 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_general_ci \
--default-authentication-plugin=mysql_native_password
docker run -d -p 3309:3306 --network mysql \
--name s2 \
-v s2:/etc/mysql \
-v /var/s2/logs:/var/log/mysql \
-v /var/s2/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e TZ=Asia/Shanghai \
--restart=always mysql:8.0.26 \
--lower_case_table_names=1 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_general_ci \
--default-authentication-plugin=mysql_native_password
```
##### 1.2.5 查看自定义网络
查看自定义网络是否有 MySQL 的主机和从机:
```bash
docker network ls
docker network inspect mysql
```
##### 1.2.6 查看卷
列出所有卷:
```bash
docker volume ls
```
查看卷的详情:
```bash
docker volume inspect m1
cd /var/lib/docker/volumes/m1/_data
```
> 其余信息,以此类推即可。
##### 1.2.7 主机搭建(m1)
修改主机 m1 容器实例的配置文件,并重启主机容器 m1 实例:
```bash
vim my.cnf
[mysqld]
# 以下是增加的配置
# [必须] 主服务器唯一ID,保证整个集群环境中唯一,取值范围在 1 - 2^32 -1 ,默认为 1
server-id=1
# 在作为从库的时候,有写入操作也需要更新二进制日志文件
log-slave-updates
```
重启容器:`docker restart m1`
##### 1.2.8 主机搭建(m2)
修改主机 m2 容器实例的配置文件,并重启主机容器 m2 实例:
```bash
vim my.cnf
[mysqld]
# 以下是增加的配置
# [必须] 主服务器唯一ID,保证整个集群环境中唯一,取值范围在 1 - 2^32 -1 ,默认为 1
server-id=3
# 在作为从库的时候,有写入操作也需要更新二进制日志文件
log-slave-updates
```
重启容器:`docker restart m2`
##### 1.2.9 从机搭建(s1)
修改从机 s1 容器实例的配置文件,并重启从机容器 s1 实例:
```bash
vim my.cnf
[mysqld]
# 以下是增加的配置
# [必须] 从服务器唯一ID
server-id=2
# [可选] 启用中继日志
relay-log=mysql-relay
```
重启容器:`docker restart s1`
##### 1.2.10 从机搭建(s2)
修改从机 s2 容器实例的配置文件,并重启从机容器 s2 实例:
```bash
vim my.cnf
[mysqld]
# 以下是增加的配置
# [必须] 从服务器唯一ID
server-id=4
# [可选] 启用中继日志
relay-log=mysql-relay
```
重启容器:`docker restart s2`
##### 1.2.11 双主:建立账户并授权
MySQL 主机(m1)创建账号并授权:
```sql
CREATE USER 'slave1'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'%';
ALTER USER 'slave1'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;
```
MySQL 主机(m2)创建账号并授权:
```sql
CREATE USER 'slave1'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'%';
ALTER USER 'slave1'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;
```
查看 MySQL 主机(m1)的状态,并记录 File 和 Position 的值:
```sql
SHOW MASTER STATUS;
```
查看 MySQL 主机(m2)的状态,并记录 File 和 Position 的值:
```bash
SHOW MASTER STATUS;
```
##### 1.2.12 双从:配置需要复制的主机
步骤 ①:从机上复制主机的命令
```sql
CHANGE MASTER TO
MASTER_HOST='主机的IP地址',
MASTER_USER='主机用户名',
MASTER_PORT='主机的端口号',
MASTER_PASSWORD='主机用户名的密码',
MASTER_LOG_FILE='binlog.具体数字',
MASTER_LOG_POS=具体值;
```
步骤②:配置从库1(s1),指定主机m1
步骤③:配置从库2(s2),指定主机m2
步骤④:启动从库1和从库2的主从同步
```sql
start slave;
```
步骤⑤:查看同步状态
> 到此为止,我们只是配置了两台主从复制,还没有配置主主复制,请看下面的操作。
##### 1.2.13 双主之间的相互复制
步骤 ① :查看 Master 的状态,并记录 File 和 Position 的值:
```bash
SHOW MASTER STATUS;
```
> 之所以再次执行,是为了防止 File 和 Position 有变化。
步骤 ②:m2 复制 m1,m1 复制 m2。
```bash
CHANGE MASTER TO
MASTER_HOST='主机的IP地址',
MASTER_USER='主机用户名',
MASTER_PORT='主机的端口号',
MASTER_PASSWORD='主机用户名的密码',
MASTER_LOG_FILE='binlog.具体数字',
MASTER_LOG_POS=具体值;
```
示例:m1
```bash
CHANGE MASTER TO
MASTER_HOST='m2',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='binlog.000003',
MASTER_LOG_POS=1114;
```
示例:m2
```bash
CHANGE MASTER TO
MASTER_HOST='m1',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='binlog.000003',
MASTER_LOG_POS=1114;
```
步骤 ③:启动 Master 同步
> 两个主库都要执行!
```sql
START SLAVE;
```
步骤④:查看同步状态
##### 1.2.14 测试
在 m1 和 m2 上执行 DDL 和 DML 语句,查看涉及到的数据库服务器的数据同步情况。
示例:m1
```sql
# 执行 DDL 操作
CREATE DATABASE IF NOT EXISTS dbtest CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci';
USE dbtest;
CREATE TABLE student(id INT,`name` VARCHAR(16));
```
示例:m2
```sql
# 执行 DML 操作
USE dbtest;
INSERT INTO student VALUES(1, 'xx');
INSERT INTO student VALUES(2, 'yy');
```
示例:m1
```sql
# 查看数据
USE dbtest;
SELECT * FROM student;
```
示例:m2
```sql
# 查看数据
USE dbtest;
SELECT * FROM student;
```
示例:s1
```sql
# 查看数据
USE dbtest;
SELECT * FROM student;
```
示例:s2
```bash
# 查看数据
USE dbtest;
SELECT * FROM student;
```
#### 1.3 安装redis集群
此处内容不做讲解
### 2. DockerFile解析
#### 2.1 是什么
Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
概念:
**官网:**
- https://docs.docker.com/engine/reference/builder/
**构建三步骤:**
- 编写Dockerfile文件
- docker build命令构建镜像
- docker run依镜像运行容器实例
#### 2.2 DockerFile构建过程解析
**Dockerfile内容基础知识**
1. 每条指令都必须为大写字母且后面要跟随至少一个参数
2. 指令按照从上到下,顺序执行
3. #表示注释
4. 每条指令都会创建一个新的镜像层并对镜像进行提交
**Docker执行Dockerfile的大致流程**
1. docker从基础镜像运行一个容器
2. 执行一条指令并对容器作出修改
3. 执行类似docker commit的操作提交一个新的镜像层
4. docker再基于刚提交的镜像运行一个新容器
5. 执行dockerfile中的下一条指令直到所有指令都执行完成
**总结**
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段:
* Dockerfile是软件的原材料
* Docker镜像是软件的交付品
* Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
1)Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
2)Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时会真正开始提供服务;
3)Docker容器,容器是直接提供服务的。
#### 2.3 DockerFile常用保留字指令
* 参考tomcat8的dockerfile入门
https://github.com/docker-library/tomcat
- 一般而言,Dockerfile 可以分为四个部分:`基础镜像信息`、`维护者信息`、`镜像操作指令`、`启动时执行指令`。
##### 2.3.1 FROM
> 基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是from
- 推荐使用 `alpine` 或 `slim` 之类的基础小镜像。
- `scratch` 镜像是一个空镜像,常常用于多阶段构建。
- 『问』:如何确定我们需要的基础镜像?
- 『答』:
- - ① 如果是 Java 应用,可以选择 Java 基础镜像或 Tomcat 基础镜像。
- - ② 如果是 JS 模块化应用,可以选择 nodejs 基础镜像。
- - ③ 每种语言都有自己的服务器或基础环境镜像,如:Python、Golang、Java、PHP 等。
##### 2.3.2 MAINTAINER & LABEL
> 镜像维护者的姓名和邮箱地址,已过期;可以使用 LABEL xxx=yyy 来代替。
>
> LABEL 用来标注镜像的一些说明信息,常常用来指定维护者的信息。
```bash
# 下面的这种格式是可以的
LABEL multi.label1="value1" multi.label2="value2" other="value3"
# 下面的这种格式也是可以的
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
```
##### 2.3.3 RUN
- RUN 指令在当前镜像层顶部的新层执行任何命令,并提交结果,生成新的镜像层。
- 生成的提交镜像将用于 Dockerfile 中的下一步,分层运行 RUN 指令并生成提交符合 Docker 的核心概念,就像 Git 管理源代码一样。
- `注意`:多个 RUN 指令并没有上下文的关系,换言之,多个 RUN 指令都是在同一个目录操作的。
* 容器构建时需要运行的命令,有两种格式
Shell格式:
```bash
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的shell名。
RUN yum -y install vim
```
exec格式
```bash
RUN["可执行文件","参数1","参数2"]
# 例如:
# RUN["./test.sh","dev","offline"] 等价于 RUN ./test.sh dev offline
```
RUN 是在docker build 时运行
- 在 RUN 中可以使用 `\` 将一条 RUN 指令继续到下一行。
```bash
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
# 等同于
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
```
* 案例
```bash
[root@dk ~]# vim Dockerfile
# 测试案例
FROM alpine
LABEL maintainer=yaoge
ENV msg='hello Docker'
RUN echo $msg
RUN ["echo","$msg"]
RUN /bin/sh -c 'echo $msg'
RUN ["/bin/sh","-c","echo $msg"]
CMD sleep 1000
```
运行
```bash
docker build -t test --force-rm --no-cache .
```
* 总结
- 1. 由于 `[]` 不是 shell 形式,所以不能输出变量信息,而是输出 `$msg` 。其他任何 `/bin/sh -c` 的形式都可以输出变量信息。
- 2. shell 是 `RUN /bin/sh -c
#### 2.4 案例
##### 2.4.1 构建Centos7镜像具备vim + ifconfig + jdk8
JDK下载地址:https://www.oracle.com/java/technologies/downloads/#java8
或者也可以安装之前我给你们的`jdk-8u333-linux-x64.tar.gz`,这里演示案例为`8u333`
第1步:创建文件夹/mywork,在此目录下创建文件Dockerfile
```bash
[root@dk ~]# mkdir /myfile
[root@dk ~]# touch /myfile/Dockerfile
[root@dk ~]# ll /myfile
总用量 144540
-rw-r--r-- 1 root root 791 11月 01 22:14 Dockerfile
-rw-r--r-- 1 root root 148003421 6月 10 07:52 jdk-8u333-linux-x64.tar.gz
```
第2步:编辑Dockerfile文件,文件内容如下:
注意:如果默认安装的是centos8,则会报一下错误:
第4步:测试
```bash
[root@dk ~]# docker run -it 93c5a653993f /bin/bash
```
##### 2.4.2 Dockerfile构建Tomcat
> 接下来,我们通过构建一个Tomcat镜像,来演示Dockerfile的使用方法
步骤①:准备jdk.tar.gz和tomcat.tar.gz两个文件
步骤②:编写Dockerfile文件
```bash
[root@dk df]# vim Dockerfile
#config file start#
FROM centos
LABEL maintainer="yaoge"
#添加并解压压缩包
ADD jdk-8u144-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-8.5.31.tar.gz /usr/local/
ADD index.jsp /usr/local/apache-tomcat-8.5.31/webapps/ROOT/
#配置环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_144
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.31/
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin
#设置tomcat监听端口
EXPOSE 8080
#设置运行时执行的启动命令
CMD /usr/local/apache-tomcat-8.5.31/bin/catalina.sh run
```
步骤③:构建镜像
```bash
docker build -t tomcat-web .
```
步骤④:运行容器测试
#### 2.5. 虚悬镜像
仓库名、标签都是`
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
### 3. Docker微服务实战(此章节不学)
docker微服务指的是使用一套小服务来开发单个应用的方式;每个服务运行在独立的进程中,一般采用轻量级的通讯机制互联,并且他们可以通过自动化的方式部署;微服务思想是将传统的单体系统按照业务拆分成多个职责单一并且可独立运行的接口服务。
#### 3.1 通过IDEA新建一个普通微服务模块
- 建Module:docker_boot
- 改POM,内容如下:
```xml
- 编写Dockerfile
```dockerfile
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER yao
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为yao_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar yao_docker.jar
# 运行jar包
RUN bash -c 'touch /yao_docker.jar'
ENTRYPOINT ["java","-jar","/yao_docker.jar"]
# 暴露6001端口作为微服务
EXPOSE 6001
```
- 将微服务jar包和Dockerfile文件上传到同一个目录下/mydocker
将docker_boot-0.0.1-SNAPSHOT.jar和Dockerfile文件放在同一个目录下,然后执行
```bash
[root@dk ~]# docker build -t yao_docker:1.6 .
```
- 运行容器
```bash
[root@dk ~]# docker run -d -p 6001:6001 yao_docker:1.6
```
- 访问测试
http://地址:6001/order/docker
### 4. Docker网络
```bash
# 先安装ifconfig命令
[root@dk ~]# yum install net-tools
```
#### 4.1 是什么
docker不启动,默认网络情况
桥接、NAT、仅主机、用户自定义。这里就不重复地说这些有啥区别了,但是咱们这节要说的docker网络模式与之有一定的相似之处。
- **Docker不启动时默认的网络情况**
```bash
[root@dk ~]# docker images
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
# 先关闭服务
[root@dk ~]# ifconfig
ens33: flags=4163
第3步:关闭u2实例,新建u3,查看ip变化
总结:docker容器内部的ip是有可能会发生改变的
- bridge是什么
Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
```bash
# 查看 bridge 网络的详细信息,并通过 grep 获取名称项
[root@dk ~]# docker network inspect bridge | grep name
"com.docker.network.bridge.name": "docker0",
[root@dk ~]# ifconfig | grep docker
docker0: flags=4163
代码:
```bash
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
```
两两匹配验证:
* host是什么
直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。
说明:
容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
案例:
```bash
[root@dk ~]# docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
WARNING: Published ports are discarded when using host network mode
db97ce1707f8d602db16108ee75fc57503a0cf4e4b0b6b8f1b8e2009d67edc88
```
**问题:**docker启动时总是遇见标题中的警告
**原因:**docker启动时指定--network=host或-net=host,如果还指定了-p映射端口,那这个时候就会有此警告,并且通过-p设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增。
**解决:**使用docker的其他网络模式,例如--network=bridge,这样就可以解决问题,或者直接无视。
```bash
[root@dk ~]# docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8
```
无之前的配对显示了,看容器实例内部
> 没有设置-p的端口映射了,如何访问启动的tomcat83??
http://宿主机IP:8080/
在CentOS里面用默认的火狐浏览器访问容器内的tomcat83看到访问成功,因为此时容器的IP借用主机的,所以容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。
* none 是什么
在none模式下,并不为Docker容器进行任何网络配置。 也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo。需要我们自己为Docker容器添加网卡、配置IP等。
禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)
案例:
```bash
[root@dk ~]# docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8
3af3372d8a3140b6436548bf0cb6adb5b9abff3a6b49962b354754497cbc74da
# 进入容器内部查看
[root@dk ~]# docker exec -it tomcat84 bash
root@3af3372d8a31:/usr/local/tomcat# ip add
1: lo:
- container 是什么
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
案例1:
```bash
[root@dk ~]# docker run -d -p 8085:8080 --name tomcat85 billygoo/tomcat8-jdk8
2cef03f7fe8020d19877708e1ae3ec69d0c9eeeac7cd72f37431e69854add531
[root@dk ~]# docker run -d -p 8086:8080 --network container:tomcat85 --name tomcat86 billygoo/tomcat8-jdk8
docker: Error response from daemon: conflicting options: port publishing and the container type network mode.
See 'docker run --help'.
```
相当于tomcat86和tomcat85公用同一个ip同一个端口,导致端口冲突。本案例用tomcat演示有坑,换其他镜像。
案例2:
Alpine操作系统是一个面向安全的轻型 Linux发行版,是一款独立的、非商业的通用 Linux 发行版,专为追求安全性、简单性和资源效率的用户而设计。 可能很多人没听说过这个 Linux 发行版本,但是经常用 Docker 的朋友可能都用过,因为他小,简单,安全而著称,所以作为基础镜像是非常好的一个选择,可谓是麻雀虽小但五脏俱全,镜像非常小巧,不到 6M的大小,所以特别适合容器打包。
```bash
[root@dk ~]# docker run -it --name alpine1 alpine /bin/sh
[root@dk ~]# docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh
```
运行结果,验证共用搭桥
假如此时关闭alpine1,再看看alpine2,会发现`38: eth0@if39: 消失了`
* 自定义网络
docker官网不建议使用此网络,最终将会被移除,所以这里不做讲解!
#### 4.5 Docker平台架构图解
从其架构和运行流程来看,Docker 是一个 C/S 模式的架构,后端是一个松耦合架构,众多模块各司其职。
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来实现具体对容器进行的操作。
### 5. Docker-compose容器编排
#### 5.1 是什么
Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器。
> Docker-Compose是Docker官方的开源项目, 负责实现对Docker容器集群的快速编排。
#### 5.2 能做什么
docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?
如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具
例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等。
Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。
可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。
#### 5.3 下载地址
* 官网
https://docs.docker.com/compose/compose-file/compose-file-v3/
* 官网下载
https://docs.docker.com/compose/install/
* 安装步骤
```bash
# 下载链接1
[root@dk ~]# curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 下载链接2
[root@dk ~]# curl -L https://get.daocloud.io/docker/compose/releases/download/v2.14.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
[root@dk ~]# chmod +x /usr/local/bin/docker-compose
[root@dk ~]# docker-compose --version
```
- 卸载
```bash
[root@dk ~]# sudo rm /usr/local/bin/docker-compose
```
#### 5.4 Compose核心概念
一个文件:`docker-compose.yml`
两个要素:
1. 服务(service)
一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器
2. 工程(project)
由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。
#### 5.5 Compose使用的三个步骤
1. 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
2. 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务。
3. 最后,执行docker-compose up命令 来启动并运行整个应用程序,完成一键部署上线
#### 5.6 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 # 停止服务
#### 5.7 Compose编排微服务
忽略,待整理,直接看5.8
#### 5.8 Compose编排案例
**什么是YAML**
YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。
YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。
YAML 的配置文件后缀为 .yml,如:docker-compose.yml 。
- 基本语法
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- #表示注释
案例1:使用Compose部署Tomcat + MySQL
第1步:准备镜像tomcat和mysql,如果已经有此镜像请忽略
```bash
[root@dk ~]# docker pull tomcat
[root@dk ~]# docker pull mysql:5.7
```
第2步:创建文件夹/composetest,并在该目录下创建文件docker-compose.yml。
```bash
[root@dk ~]# mkdir /composetest
[root@dk ~]# cd /composetest
[root@dk composetest]# cat docker-compose.yml
version: '3.1'
services:
tomcat:
restart: always
image: tomcat
container_name: tomcat
ports:
- 8080:8080
volumes:
- /usr/local/docker/tomcat/webapps:/usr/local/tomcat/webapps
environment:
TZ: Asia/Shanghai
mysql:
restart: always
image: mysql:5.7
container_name: mysql
ports:
- 3306:3306
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123456
command:
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
--max_allowed_packet=128M
--sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO"
volumes:
- /usr/local/docker/mysql:/var/lib/mysql
[root@dk composetest]# docker-compose up -d
# 如果出现done、done则表示成功!
```
第3步:访问tomcat地址
注意:此步骤会出现404错误,8.2章节的内容不再适合修改,因为删除webapps时已经有容器数据卷,所以删除webapps文件夹时会报错,应当把webapps.dist里的内容拷贝到webapps目录下。
第4步:检查MySQL是否可以正常登陆
- 案例2:
第1步:准备镜像mysql5.7和wordpress,不提供代码,代码自行编写。
```bash
[root@dk ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 10 months ago 141MB
wordpress latest c3c92cc3dcb1 11 months ago 616MB
mysql 5.7 c20987f18b13 11 months ago 448MB
```
第2步:创建yml文件
```yaml
[root@dk mywp]# cat docker-compose.yml
version: '3.1' #compose文件版本
services:
mysql: # 服务1:mysql
image: mysql:5.7 # 使用镜像 mysql:5.7版本
volumes:
- /use/local/docker/mysql:/var/lib/mysql # 数据持久化
restart: always # 容器服务宕机后总是重启
ports:
- "3307:3306"
environment: # 环境配置
MYSQL_ROOT_PASSWORD: root12345 #数据库root用户密码
MYSQL_DATABASE: wordpress #wordpress数据库名称
MYSQL_USER: wordpress #数据库用户名
MYSQL_PASSWORD: 123456 #数据库密码
wordpress: # 服务2:wordpress
depends_on: # wordpress服务启动时依赖mysql服务,所以会自动先启动mysql服务
- mysql
image: wordpress:latest # 使用镜像 wordpress:latest最新版
ports:
- "8080:80" #端口映射8000:80
restart: always
environment: # 环境
WORDPRESS_DB_HOST: mysql:3306 # wordpress连接db的3306端口
WORDPRESS_DB_USER: wordpress # 数据库用户为wordpress
WORDPRESS_DB_PASSWORD: 123456 # 数据库密码是123456
WORDPRESS_DB_NAME: wordpress # 数据库名字是wordpress
```
第3步:执行
```bash
[root@dk mywp]# docker-compose up -d
```
第4步:访问地址看结果
- 案例3:docker-compose + Jira
> JIRA是Atlassian公司出品的项目与事务跟踪工具,被广泛应用于缺陷跟踪、客户服务、需求收集、流程审批、任务跟踪、项目跟踪和敏捷管理等工作领域。
**分为以下x个步骤:**
> 1、先下载两个jar包,放入libs下
>
> 2、mysql、jira用于存放配置文件、日志文件、数据文件
>
> 3、编写docker-compose.yml文件
>
> 4、创建数据库
>
> 4、测试运行
```bash
[root@dk jira]# tree ./
./
├── docker-compose.yml
├── jira
├── libs
│ ├── atlassian-agent.jar
│ └── mysql-connector-java-5.7.36.jar
└── mysql
3 directories, 3 files
```
**编写docker-compose.yml文件:**
```yaml
[root@dk jira]# cat docker-compose.yml
version: '3.1'
services:
mysql:
container_name: mysql
image: mysql
restart: always
ports:
- 3308:3306
environment:
MYSQL_ROOT_PASSWORD: 123456
volumes:
- ./mysql/conf:/etc/mysql/conf.d
- ./mysql/logs:/var/log/mysql
- ./mysql/data:/var/lib/mysql
command:
--character-set-server=utf8mb4
jira:
container_name: jira
image: atlassian/jira-software:latest
restart: "no"
ports:
- 8080:8080
environment:
CATALINA_OPTS: -javaagent:/opt/atlassian/jira/atlassian-agent.jar
volumes:
- ./jira/data:/var/atlassian/application-data/jira
- ./libs/atlassian-agent.jar:/opt/atlassian/jira/atlassian-agent.jar
- ./libs/mysql-connector-java-8.0.27.jar:/opt/atlassian/jira/lib/mysql-connector-java.jar
```
**启动容器:**
```bash
[root@dk jira]# docker-compose up -d
```
**创建数据库:**
```mysql
CREATE DATABASE jiradb;
create user 'jira'@'%' identified by 'jira';
grant all privileges on jiradb.* to 'jira'@'%';
flush privileges;
```
**设置Jira:**
在浏览器上打开:http://IP:8080
1、点击右上角**Language**,设置中文
2、选择`我将设置它自己`,点击下一步
3、数据库设置,选择**其它数据库**,根据文本提示填入相应内容,点击测试连接,通过后点击下一步
4、设置应用程序的属性,采用默认值就行,直接点击下一步
5、设置许可证,如果本地有Java环境,可以在本地生成许可证,或者进入jira容器生成
执行命令,带上服务器ID,服务器ID写自己电脑上的,生成许可证:
```bash
root@61bf39400f8a:/opt/atlassian/jira# java -jar atlassian-agent.jar -d -m test@test.com -n BAT -p jira -o lewis2951 -s 服务器ID
```
6、设置管理员账户,根据自己的需求设置,设置Email地址
7、设置电子邮件通知,可以选择以后再说
8、完成部署
9、查看许可证
* 案例4:使用Docker-Compose部署nginx代理Tomcat集群,实现负载均衡
**大体步骤分为以下四步**
> 1、下载所需的文件tomcat,jdk
> 2、编写dockerfile来布署tomcat与java环境,生成镜像文件
> 3、编写docker-compose.yml配置文件,启动所有容器服务
> 4、测试负载均衡
```bash
[root@localhost java]# tree ./
./
├── docker-compose.yml
├── nginx
│ └── nginx.conf
├── tomcat
│ ├── apache-tomcat-8.5.79.tar.gz
│ ├── Dockerfile
│ └── jdk-8u333-linux-x64.tar.gz
└── webserver
├── tomcatA
│ └── index.jsp
└── tomcatB
└── index.jsp
5 directories, 7 files
[root@localhost java]# cat webserver/tomcatA/index.jsp
welcome to Tomcat-A server
[root@localhost java]# cat webserver/tomcatB/index.jsp
welcome to Tomcat-B server
# 编写docker-compose.yml文件
[root@master java]# cat docker-compose.yml
version: "3"
services:
nginx:
image: nginx:1.14
restart: always
ports:
- 80:80
links:
- tomcat1:tomcat1
- tomcat2:tomcat2
volumes:
- ./webserver:/webserver
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
environment:
TZ: Asia/Shanghai
depends_on:
- tomcat1
- tomcat2
tomcat1:
hostname: tomcat1
build: ./tomcat
volumes:
- ./webserver/tomcatA:/usr/local/apache-tomcat-8.5.79/webapps/ROOT
environment:
TZ: Asia/Shanghai
tomcat2:
hostname: tomcat2
build: ./tomcat
volumes:
- ./webserver/tomcatB:/usr/local/apache-tomcat-8.5.79/webapps/ROOT
environment:
TZ: Asia/Shanghai
#安装JAVA环境
[root@localhost java]# cat tomcat/Dockerfile
FROM centos
ADD jdk-8u333-linux-x64.tar.gz /usr/local
ENV JAVA_HOME /usr/local/jdk1.8.0_333
ADD apache-tomcat-8.5.79.tar.gz /usr/local
EXPOSE 8080
ENTRYPOINT ["/usr/local/apache-tomcat-8.5.31/bin/catalina.sh", "run"]
```
**编写nginx配置文件**
```bash
[root@localhost java]# cat ./nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
client_max_body_size 10m;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
upstream tomcat_client {
server tomcat1:8080 weight=1;
server tomcat2:8080 weight=1;
}
server {
server_name "";
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
location / {
proxy_pass http://tomcat_client;
proxy_redirect default;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
```
**启动所有容器服务**
```bash
[root@localhost java]# docker-compose up -d
```
**查看启动情况**
```bash
[root@localhost java]# docker-compose ps
```
**检测负载均衡**
```bash
[root@localhost java]# curl http://localhost
welcome to tomcat-A server
[root@localhost java]# curl http://localhost
welcome to tomcat-B server
[root@localhost java]# curl http://localhost
welcome to tomcat-A server
[root@localhost java]# curl http://localhost
welcome to tomcat-B server
```
**浏览器访问测试负载均衡**
在浏览器上输入服务器地址就可以测试了,多刷新几次试试!!
### 6. Docker轻量级可视化工具Portainer
#### 6.1 是什么
Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。
#### 6.2 安装
- 官网
https://www.portainer.io/
https://docs.portainer.io/v/ce-2.9/start/install/server/docker/linux
- docker命令安装
```bash
[root@dk ~]# 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
```
- 登录演示
第一次登录需创建admin,访问地址:xxx.xxx.xxx.xxx:9000
设置admin用户和密码后首次登陆(设置密码为12345678,以防忘记)
选择local选项卡后本地docker详细信息展示
上一步的图形展示,能想得起对应命令吗?
```bash
[root@dk ~]# docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 10 10 3.185GB 77.79MB (2%)
Containers 49 1 202.7MB 202.7MB (100%)
Local Volumes 13 9 1.094GB 828MB (75%)
Build Cache 0 0 0B 0B
```
#### 6.3 常用操作
点击镜像看效果,这里的删除、新建、导入导出都很明确。
咱们再回去看看刚刚首页上的Stack(左侧列表中):
可以看到这里是用来存放咱们compose编排的界面,继续点开看看:
常规操作
```bash
# 先docker ps看一眼有什么正在运行:
[root@dk ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
13fbca891039 mysql:5.7 "docker-entrypoint.s…" 6 seconds ago Up 4 seconds 3306/tcp, 33060/tcp serene_gauss
9dc6e689c519 portainer/portainer "/portainer" 27 minutes ago Up 26 minutes 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp portainer
```
演示Logs操作、演示监控信息、演示命令行(Exec Console)
添加一个容器实例,内容随便填写一下发布即可。
还可以配置容器数据卷
### 7. Docker容器监控值CAdvisor+InfluxDB+Granfana
#### 7.1 原生命令
通过`docker stats`命令可以很方便的看到当前宿主机上所有容器的CPU,内存以及网络流量等数据。docker stats统计结果只能是当前宿主机的全部容器,数据资料是实时的,没有地方存储、没有健康指标过线预警等功能。
#### 7.2 是什么
容器监控3剑客:CAdvisor监控收集+InfluxDB存储数据+Granfana展示图表
- CAdvisor监控收集
> CAdvisor是一个非常好用的容器监控工具,还可以和Prometheus整合,安装和使用都很简单。CAdvisor是一个容器资源监控工具,包括容器的内存CPU,网络IO,磁盘I0等监控,同时提供了一个WEB页面用于查看容器的实时运行状态。CAdvisor默认存储2分钟的数据, 而且只是针对单物理机。不过,CAdvisor提供了很多数据集成接口,支持InfluxDB,Redis,Kafka,Elasticsearch等集成,可以加上对应配置将监控数据发往这些数据库存储起来。
>
> CAdvisor功能主要有两点:
>
> 1. 展示Host和容器两个层次的监控数据
> 2. 展示历史变化数据
- InfluxDB存储数据
InfluxDB是用Go语言编写的一个开源分布式时序、事件和指标数据库,无序外部依赖。
CADvisor默认只在本机保存最近2分钟的数据,为了持久化存储数据和统一收集展示监控数据,需要将数据存储到InfluxDB中。InfluxDB是一个时序数据库,专门用于存储时序相关数据,很适合存储CADvisor的数据,而且,CADvisor本身已经提供了InfluxDB的集成方法,启动容器时指定配置即可。
主要功能:
1. 基于时间序列,支持与时间有关的相关函数(如最大、最小、求和等)
2. 可度量性:你可以实时对大量数据进行计算
3. 基于事件:它支持任意的事件数据
- Granfana展示图表
Grafana是一个开源的数据监控分析可视化平台,支持多种数据源配置和丰富的插件及模板功能,支持图表权限控制和报警。
主要功能:
1. 灵活丰富的图形化选项
2. 可以混合多种风格
3. 支持白天和夜间模式
4. 多个数据源
#### 7.3 Compose容器编排
第1步:新建目录
```bash
[root@dk ~]# mkdir /cig && cd /cig
```
第2步:新建docker-compose.yml文件,内容如下:
```yaml
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
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
```
第3步:输入网址测试,第一次加载较慢
```url
http://192.168.170.151:8080/containers/
```
第4步:
cadvisor也有基础的图形展现功能,这里主要用它来作数据采集
- 浏览influxdb存储服务,http://ip:8083/
- 浏览grafana展现服务,http://ip:3000
ip+3000端口的方式访问,默认帐户密码(admin/admin)
第5步:配置数据源—>选择influxdb数据源
点击Save & test,出现`Data source is working`则表示成功!
第6步:配置面板panel
到这里CAdvisor + InfluxDB + Grafana容器监控系统就部署完成了
### 8. Windows安装Docker
### 9. 项目实战
#### 9.1 OA办公系统
步骤1:拷贝o2server.zip文件,或者在Linux里使用wget命令下载压缩包,下载地址为:
https://download.o2oa.net/download/o2server-7.2.6-linux-x64.zip
步骤2:使用unzip命令解压安装包
步骤3:制作Dockerfile文件
- FROM为基础镜像名称,镜像中包括Tomcat、JDK、Linux操作系统
- PaaS平台不允许使用root用户启动应用,所以新建新用户 **yaoge**
- 将解压后的o2server拷贝到容器中
- 为新建的用户**yaoge**赋予o2server目录的管理权限,能够读、写、执行权限。
- 容器启动的时候将用户切换为 **yaoge**(1000为**yaoge**的用户编号,测试PaaS平台要求使用"user 用户编号"方式切换用户)
- 容器启动后的ENTRYPOINT为执行启动o2oa的脚本 start_linux.sh
```dockerfile
FROM tomcat:8.5
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asiz/Shanghai' > /etc/timezone
RUN groupadd yaoge
RUN useradd yaoge -g yaoge -u 1000
RUN mkdir -p /data/o2server
COPY o2server /data/o2server/
RUN chown -R yaoge:yaoge /data
ENTRYPOINT ["/bin/sh","-c","/data/o2server/start_linux.sh && tailf /etc/localtime"]
```
步骤4:docker build 制作镜像,镜像名为**oa**
步骤5:镜像制作完毕后将镜像存储为tar包进行保存,**gzip**没有请先安装,保存命令:
```bash
docker save oa | gzip > oa.tar
```
步骤6:docker run检查镜像是否能够正常启动
### 8. 其他问题
#### 8.1 停止警告
执行 `systemctl stop docker` 后提示“Warning: Stopping docker.service, but it can still be activated by: docker.socket”
解释:
这是docker在关闭状态下被访问自动唤醒机制,很人性化,即这时再执行任意docker命令会直接启动
注:如果真的不希望docker被访问自动唤醒,执行 `systemctl stop docker` 后再执行`systemctl stop docker.socket`即可
#### 8.2 Docker报错解决
- 【解决】Got permission denied while trying to connect to the Docker daemon socket at......dial unix /var/run/docker.sock: permission denied
> https://www.cnblogs.com/leozhanggg/p/11730189.html
- 【解决】error pulling image configuration: Get https:// ...... x509: certificate has expired or is not yet valid
> https://www.cnblogs.com/leozhanggg/p/12031433.html
- 【解决】image ... could not be accessed on a registry to record its digest.
> https://www.cnblogs.com/leozhanggg/p/12023531.html
- 【解决】http: server gave HTTP response to HTTPS client
> https://www.cnblogs.com/leozhanggg/p/12023456.html
- 【解决】OCI runtime exec failed......executable file not found in $PATH": unknown
> https://www.cnblogs.com/leozhanggg/
* 【解决】 WARNING: IPv4 forwarding is disabled. Networking will not work.
```bash
echo "net.ipv4.ip_forward=1" >>/usr/lib/sysctl.d/00-system.conf
# 重启网卡、docker
systemctl restart network && systemctl restart docker
```
### 9. 最终总结