Docker学习笔记

一、简介

Docker 是一个开源的应用容器引擎,它是由 Go 语言 开发实现的轻量级容器技术,它是基于 Linux 内核的 cgroupnamespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它隔离的进程,因此也称其为容器。

Docker 架构

二、概念

1. 概要理解

Docker支持安装各种软件并做好配置后编译成镜像,最终可以直接运行镜像产生一个或多个运行于宿主机内核上的容器。为了形象的理解容器,可以简单的理解它为运行在操作系统上的独立沙箱系统,这些沙箱系统内部的文件系统和Linux的非常相似,事实上确是如此,因为Docker就是融合了Linux内核而实现的虚拟化技术,由于具有沙箱的特性,所以各个容器之间是隔离运行的、独立的、互不影响的,程序员甚至不用担心容器内部损坏或者崩溃导致宿主机出错,因为只要删除掉这些有问题的容器,而再运行相关的镜像又会得到全新的容器了,只要不进行数据挂载,整个过程甚至干净不留残余。

2. 核心内容

docker主机(Host):或者称为docker宿主机,即安装了Docker程序的机器。

docker客户端(Client):连接docker主机进行操作的程序。

docker镜像(Images):是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

docker容器(Container):运行镜像后产生的实例称为容器,实质是运行于独立的 命名空间的进程。

docker仓库(Registry):集中存储、分发镜像的仓库服务系统,作用是允许用户上传、下载并管理镜像,包括公有仓库和私有仓库。

20180303165113

3. 对比传统虚拟机

特性虚拟机的架构容器的架构
启动分钟级秒级
性能弱于原生接近原生
硬盘使用一般为GB一般为MB
系统支持量一般几十个单机上千个容器

虚拟机的架构: 每个虚拟机都包括应用程序、必要的二进制文件和库以及一个完整的客户操作系统(Guest OS),尽管它们被分离,它们共享并利用主机的硬件资源,将近需要十几个 GB 的大小。

容器的架构: 容器包括应用程序及其所有的依赖,但与其他容器共享内核。它们以独立的用户空间进程形式运行在宿主机操作系统上。他们也不依赖于任何特定的基础设施,Docker 容器可以运行在任何计算机上,任何基础设施和任何云上。

4. 优势

(1)快速的启动时间

由于Docker容器直接运行于宿主内核,无需启动完整的操作系统即可运行,因此可以做到秒级、甚至毫秒级的启动时间,这大大的节约了开发、测试、部署的时间。

(2)一致的运行环境

开发过程中一个常见的问题是环境一致性问题。由于不同物理机的开发环境不一致,经常出现安装了相同的软件但却有不同的运行效果现象,甚至有的环境下还会出现bug。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,以至于不会再出现 “这段代码在我机器上没问题啊” 这类的问题。

(3)持续交付和部署

Docker可以一次创建或配置镜像,而可以在任意地方正常运行。即"一处构建,到处运行"。

(4)更方便的迁移

Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

(5)更轻量的维护和扩展

Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。

5. 分层存储

因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。

镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

6. 容器存储层

镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为 容器存储层

7. 数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,当容器内部的目录(文件)映射到宿主机的某目录(文件)时,那么就称这个宿主机的目录(文件)为数据卷。它绕过 UFS,可以提供很多有用的特性,如下:

  • 数据卷 可以在容器之间共享和重用
  • 数据卷 的修改会立马生效
  • 数据卷 的更新,不会影响镜像
  • 数据卷 默认会一直存在,即使容器被删除

注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷

按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。

数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。

三、Docker 安装和维护

Docker 分为 CE 和 EE 两大版本。CE 即社区版,EE 即企业版,强调安全,付费使用。

1. 包管理器安装 Docker

说明:下面以使用 CentOS7 的 Yum 包管理器安装 Docker 为例进行讲解,其他 Linux 发行版包管理器的安装说明可参考:https://docs.docker.com/engine/install/

1)安装旧版本(不推荐)

CentOS7 从默认的 Yum 源中安装的 Docker 版本是旧版本,旧版本的 Docker 称为 docker 或 docker-engine ,由于目前官方不在维护,故建议跳过以下去安装新版本。

1
2
3
4
5
6
7
8
9
10
#检查内核版本,必须是3.10及以上
uname -r
#安装docker
yum install docker -y
#启动docker
systemctl start docker
#开机启动docker
systemctl enable docker
#查看docker版本,验证安装是否成功(有client和service两部分表示docker安装启动都成功了)
docker -v

2)安装新版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#卸载旧版本,如果已安装这些程序,请卸载它们以及相关的依赖项
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

#安裝Docker源
wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
#yum install yum-utils -y
#yum-config-manager --add-repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo

#查看可安装的docker版本
yum list docker-ce --showduplicates

#安装docker-ce。
yum install docker-ce -y
#选择指定版本Docker安装
#yum install -y --setopt=obsoletes=0 docker-ce-<version>
#如:yum install -y --setopt=obsoletes=0 docker-ce-18.06.3.ce-3.el7

#查看docker版本,验证安装是否成功(有client和service两部分表示docker安装启动都成功了)
docker version

#启动并加入开机启动
systemctl start docker
systemctl enable docker

更多内容:

查看所有仓库中所有docker版本:yum list docker-ce --showduplicates | sort -r

安装指定版本:比如yum install docker-ce-17.12.0.ce

新版docker安装好后命令自动补全会有些缺失,所以可以选择安装命令补全依赖工具。

1
2
3
4
5
6
7
#docker自动补齐需要依赖工具bash-complete,安装好后会得到文件为 /usr/share/bash-completion/bash_completion
yum install -y bash-completion
#apt install -y bash-completion

#装好docker自动补齐依赖之后,要刷新下才能让其生效
source /usr/share/bash-completion/bash_completion
source /usr/share/bash-completion/completions/docker

3)内网环境集群安装

(1)yum离线安装

如果内网中有一台服务器能够访问外网,可使用一下方式安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#在内网中可访问外网的服务器中下载清华的镜像源文件(要求这台服务器依软件赖环境纯净,最好没有安装过任何软件。目的是这台服务器的所有软件依赖一定要在其他内网服务器中都存在。)
wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo

sed -i 's+download.docker.com+mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo

yum update


yum list docker-ce --showduplicates|sort -r

#下载docker安装的所有依赖到/tmp/docker-19.03目录
yum install --downloadonly --downloaddir=/tmp/docker-19.03 docker-ce-19.03.8-3.el7 docker-ce-cli-19.03.8-3.el7

#复制下载好的/tmp/docker-19.03到目标服务器(比如192.168.0.13)
scp -r /tmp/docker-19.03/ [email protected]:/tmp/

#登录进目标服务器之后进入文件夹安装
ssh [email protected]
cd /tmp/docker-19.03/
yum install *.rpm
#启动并加入开机启动
systemctl start docker
systemctl enable docker
(2)完全离线安装

如果内网中所有服务器都不能访问外网,可使用一下方式安装:

下载安装包

在客户机中访问docker官网下载安装包:

安装包各版本下载地址:https://download.docker.com/linux/static/stable/x86_64/

这里下载的版本是:https://download.docker.com/linux/static/stable/x86_64/docker-19.03.5.tgz

安装运行

下载好安装包后通过ftp等客户端工具上传安装包到目标服务器,然后通过ssh客户端工具登录到该目标服务器执行如下命令:

1
2
3
4
5
6
7
8
9
10
#解压安装压缩包
tar xzvf docker-19.03.5.tgz
#拷贝安装
cp docker/* /usr/bin/
#运行服务
dockerd &

#停止服务启动
ps -ef|grep dockerd
kill -9 1958 #这里的1958改成ps出来的pid

配置开机启动

1
2
3
4
5
6
#关闭selinux策略
vi /etc/selinux/config
SELINUX=disabled

#重启系统
reboot

配置docker.socket

1
vi /etc/systemd/system/docker.socket
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Unit]

Description=Docker Socket for the API

PartOf=docker.service

[Socket]

# If /var/run is not implemented as a symlink to /run, you may need to

# specify ListenStream=/var/run/docker.sock instead.

ListenStream=/run/docker.sock

SocketMode=0660

SocketUser=root

SocketGroup=docker

[Install]

WantedBy=sockets.target

配置docker.service

1
vi /etc/systemd/system/docker.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
[Unit]

Description=Docker Application Container Engine

Documentation=https://docs.docker.com

After=network-online.target firewalld.service

Wants=network-online.target

[Service]

Type=notify

# the default is not to use systemd for cgroups because the delegate issues still

# exists and systemd currently does not support the cgroup feature set required

# for containers run by docker

ExecStart=/usr/bin/dockerd

ExecReload=/bin/kill -s HUP $MAINPID

# Having non-zero Limit*s causes performance problems due to accounting overhead

# in the kernel. We recommend using cgroups to do container-local accounting.

LimitNOFILE=infinity

LimitNPROC=infinity

LimitCORE=infinity

# Uncomment TasksMax if your systemd version supports it.

# Only systemd 226 and above support this version.

#TasksMax=infinity

TimeoutStartSec=0

# set delegate yes so that systemd does not reset the cgroups of docker containers

Delegate=yes

# kill only the docker process, not all processes in the cgroup

KillMode=process

# restart the docker process if it exits prematurely

Restart=on-failure

StartLimitBurst=3

StartLimitInterval=60s

[Install]

WantedBy=multi-user.target

启动服务

1
2
3
systemctl daemon-reload
systemctl start docker
systemctl enable docker

4)参考

2. 一键脚本安装 Docker

1
2
3
4
5
6
7
8
9
10
11
12
13
#使用官方源安装(国内直接访问较慢)
curl -fsSL https://get.docker.com | bash
#使用阿里源安装
#curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
#使用中国区Azure源安装
#curl -fsSL https://get.docker.com | bash -s docker --mirror AzureChinaCloud
#指定特定版本安装
#curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun --version 18.06.3

#设置开机自启
systemctl enable docker
#启动Docker
systemctl start docker

参考:

3. 配置 Docker 的镜像源

鉴于国内网络问题,Docker 的默认镜像源拉取 Docker 镜像十分缓慢,我们可以配置国内的镜像源来加速镜像下载(可以配置多个镜像源)。

测速:https://github.com/docker-practice/docker-registry-cn-mirror-test/actions

Docker 的镜像源配置文件路径(如果文件不存则需要手动创建):

  • Linux: /etc/docker/daemon.json
  • Windows:%programdata%\docker\config\daemon.json
1
2
3
4
5
6
7
8
9
10
11
12
13
cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn"
]
}
EOF

#重启 docker 服务
systemctl restart docker
#查看 Docker 配置是否修改生效(其中 “Registry Mirrors” 即为 Docker 的镜像源)
docker info

4. 配置 Docker 的默认存储路径(可选)

Docker 会使用 /var/lib/docker/ 目录作为默认存储目录,用以存放拉取的镜像和创建容器等的相关文件。由于该目录通常都位于系统盘,如果系统盘比较小,而镜像和容器使用得越多就会占用越多的系统盘空间,当系统盘不够时就会比较尴尬了,故而可能需要通过数据盘来转移镜像和容器内容,这就可能需要修改 Docker 的默认存储目录。假设 /data1 目录挂载点的磁盘空间比较大,需要将其设置为 Docker 的默认存储路径,可如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mkdir -p /data1/docker/lib

cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
],
"data-root": "/data1/docker/lib"
}
EOF

#重启 Docker 服务
systemctl restart docker
#查看 Docker 配置是否修改生效(其中 “Docker Root Dir” 即为 Docker 的存储路径)
docker info

5. Docker 服务管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#启动docker服务(提示:docker即docker.service,启动服务后会自动启动docker网络套接字守护进程,即docker.socket)
systemctl start docker

#重启docker服务
systemctl restart docker

#关闭docker服务
#如果该命令无法停止服务(报错提示:Warning: Stopping docker.service, but it can still be activated by: docker.socket,这个问题通常是由于容器使用了 --restart always 等重启策略导致服务无法关闭,解决办法就是还需要关闭 docker.socket 网络套接字守护进程)
systemctl stop docker
#关闭docker网络套接字守护进程
systemctl stop docker.socket

#开启开机自启动docker服务
systemctl enable docker
#禁止开机自启动docker服务
systemctl disable docker

6. 卸载 Docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
##卸载准备(可选)
#停止并删除所有正在运行的 Docker 容器
docker stop $(sudo docker ps -a -q)
docker rm $(sudo docker ps -a -q)
#删除所有 Docker 镜像
docker rmi $(sudo docker images -q)
#停止 Docker 服务
systemctl stop docker.socket
systemctl stop docker.service

##(1)yum 卸载 Docker
#删除 Docker 安装包
yum remove -y docker docker-ce docker-ce-cli containerd.io
yum autoremove -y
#删除残余文件
rm -rf /var/lib/docker #含镜像、容器、配置文件等内容
rm -rf /var/lib/containerd
rm -rf /etc/docker

#(2)apt 卸载 Docker
#删除 Docker 安装包
apt purge -y docker docker-ce docker-ce-cli containerd.io
#删除残余文件
rm -rf /var/lib/docker #含镜像、容器、配置文件等内容
rm -rf /var/lib/containerd
rm -rf /etc/docker

四、镜像管理

https://hub.docker.com/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#检索	(去https://hub.docker.com上查看镜像的详细信息。eg:docker search redis)
docker search 镜像关键字
#拉取 (name是镜像名,:tag是可选的,tag表示标签,多为软件的版本,默认是latest)
docker pull name[:tag]
#列出 (查看所有本地镜像)
docker images
#删除 (删除指定的本地镜像,image-id是镜像id)
docker rmi image-id

#查看容器/镜像详细信息
docker inspect image-name-or-id

#镜像导出
docker save -o 打包文件名.tar 镜像id
#或者
docker save 镜像id > 文件名.tar

#镜像导入
docker load -i 打包文件名.tar
#或者
docker load < 打包文件名.tar

镜像导出并导入案例:

1
2
3
4
5
6
#例如,以下导出方式将会让docker load导入时的镜像没有名字(即<none>)
docker save -o redis.tar 7864316753
#因此,推荐使用镜像的名字进行导出,此方式导入时就有镜像名称了
docker save -o redis:5.0.2
#另外,如果已经是<none>的名称了,也可以通过以下命令重命名镜像名称
docker tag [镜像id] [新镜像名称]:[新镜像标签]

五、容器管理

1. 管理命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#运行并创建一个容器
#-d:表示守护态运行,即后台状态运行
#--name:给容器自定义命名
#-p:表示端口映射
#格式:-p <host>:<host_port>:<container_port>/<protocol>
#<host> 是指宿主机的IP地址(可以使用 0.0.0.0 表示监听所有网络接口)
#<host_port> 是指宿主机上的端口,可指定为单个端口或者端口范围(如果不指定将自动映射到宿主机的一个随机端口)
#<container_port> 是指容器内部的端口,可指定为单个端口或者端口范围
#<protocol> 是要使用的传输协议,可指定为tcp或者udp(不指定默认为tcp)
#示例:-p 2222:22 -p 1194:1194/udp -p 127.0.0.1:8080:8080/tcp -p 9000-9100:9000:9100
#--volume(-v):数据卷或目录挂载,即挂载物理主机数据卷或者目录或文件到容器里
#--restart:设置docker守护进程对容器的重启策略,可选值如下:
#no(默认):容器退出后(无论是否正常退出),docker守护进程在重启时都不会自动启动该容器。
#always:容器退出后(无论是否正常退出),docker守护进程在重启时都会自动启动该容器。(提示:docker stop 关闭容器后并不会执行重启策略,仅是docker守护进程重启时,重启策略才会发挥作用)
#on-failure[:max-retries]:容器在非正常退出时(退出状态码不等于0,如:强制关机导致容器异常退出、手动关闭容器但容器且异常退出、容器内部程序执行错误导致异常退出等),docker守护进程在重启时会自动启动该容器。参数使用如:--restart=on-failure,可选地指定 max-retries 参数来限制最大重试次数。例如,--restart=on-failure:3 表示容器最多会重试 3 次,如果在重试次数耗尽之后容器仍然无法正常运行,它将停止。
#unless-stopped:容器在非正常退出时,除非手动关闭,docker守护进程将在重启时会自动启动该容器。
#补充说明:对于开机自启动的情况,可选 always 或 unless-stopped
docker run -d --name 容器名称 -p 主机端口:容器端口 -v 主机目录路径:容器目录路径 镜像名称

#关闭容器
docker stop 容器名称/容器id
#启动已经关掉的容器
docker start 容器名称/容器id
#重启容器
docker restart 容器名称/容器id

#删除指定容器
docker rm 容器名称/容器id
#强制删除当前所有容器
docker rm -f $(docker ps -a -q)

#显示容器列表(默认只显示运行中的容器)
docker ps
docker ps -s #-s:显示容器占用磁盘空间大小
#显示容器列表(所有,包括运行中和已停止的容器)
docker ps -a

#查看 Docker 容器里的环境变量
docker exec -it <container_name_or_id> env
#查看 Docker 容器里的指定环境变量
docker exec -it <container_name_or_id> sh -c 'echo $ENV_VARIABLE_NAME'

#执行容器内命令
docker exec -it 容器名称或容器id 命令
#如:docker exec -it nginx ip iddr
#进入容器内部
docker exec -it 容器名称或容器id bash
#从容器内部退出
exit

#从主机拷贝文件到容器
docker cp 主机文件或目录 容器名称或容器id:容器文件或目录
#从容器拷贝文件到主机
docker cp 容器名称或容器id:容器文件或目录 主机文件或目录

#查看容器/镜像详细信息
docker inspect 容器名称/容器id

#查看容器运行日志
docker logs 容器名称/容器id

#查看容器资源的使用情况,包括:CPU、内存、网络I/O、磁盘I/O 等资源占用情况
docker stats

##通过runlike去查看一个容器的docker run启动参数
# 安装runlike安装包
pip install runlike
# 可能需要退出终端会话重新连接才能生效
exit
# 查看容器运行命令
runlike -p <container_name> # 后面可以是容器名和容器id,-p参数是显示自动换行

#容器导出和导入
#将容器导出为镜像包
docker export -o 镜像包名称.tar 容器名称或容器id
#将镜像包导入为镜像
docker import 镜像包名称.tar 新镜像名称:新镜像标签


#修改容器配置
#注意只能修改一些配置项,并非所有配置都能修改,如数据卷、目录挂载等就修改不了
#常见参数如下:
# --cpu-shares=<权重>: 设置容器的 CPU 权重,影响 CPU 时间片的分配。(默认值为 1024,数值越高表示容器获得的 CPU 时间片越多)
# --cpu-period=<周期>: 设置 CPU 周期,用于控制 CPU 时间片的周期。
# --cpu-quota=<配额>: 设置 CPU 配额,限制 CPU 的使用。
# --blkio-weight=<权重>: 设置块 I/O 权重,用于限制容器的磁盘 I/O。
# --memory=<内存限制>: --memory可缩写-m,设置容器的内存限制。
# --memory-swap=<内存+交换限制>: 设置内存加交换限制,控制内存和交换空间的使用。
# --kernel-memory=<内核内存限制>: 设置容器可以使用的内核内存限制。
# --restart=<策略>: 配置容器的重启策略。可选策略包括 no、on-failure、always、unless-stopped 等。
docker update [OPTIONS] <容器名称或ID>
#docker update --restart always <容器名称或ID>
#docker update --restart=no <容器名称或ID>
#docker update --cpu-shares 512 <容器名称或ID>
#docker update -m 512M <容器名称或ID>

容器导出和导入案例:

1
2
3
4
5
6
7
8
9
10
#创建容器
docker run -itd --name centos7-test centos:centos7
#尝试修改容器
docker exec -it centos7-test touch /tmp/test
#将容器导出为镜像包
docker export -o centos7-test.tar centos7-test
#将镜像包导入为镜像
docker import centos7-test.tar centos7:centos7-test
#通过新镜像创建容器
docker run -itd --name centos7-test2 centos7:centos7-test

2. 设置容器与宿主机时间同步

1
2
3
4
5
##为了保证容器和宿主机之间的时间同步
#1、使用docker run运行容器时,添加如下参数-v /etc/localtime:/etc/localtime:ro 如下:
docker run -it -v /etc/localtime:/etc/localtime:ro centos
#2、如果容器已经运行则可以如下操作:
docker cp -L /usr/share/zoneinfo/Asia/Shanghai [容器名]:/etc/localtime

六、数据管理

由于docker容器和宿主机系统是隔离的,这会带来下面几个问题:

  • 不能在宿主机上很方便地访问容器中的文件
  • 无法在多个容器之间共享数据
  • 当容器删除时,容器中产生的数据将丢失

为了能够保存(持久化)数据以及共享容器间的数据,docker 引入了数据卷(volume) 机制。数据卷是存在于一个或多个容器中的特定文件或文件夹,它可以绕过默认的联合文件系统,以正常的文件或者目录的形式存在于宿主机上。生存周期独立于容器的生存周期的,所以删除容器后数据卷并不会丢失。

容器中主要有两种管理数据方式:数据卷(Data Volumes)数据卷容器(Data Volume Containers)

1. 数据卷

数据卷是一个可供容器使用的特殊目录,它绕过文件系统,可以提供很多有用的特性:

(1)数据卷 可以在容器之间共享和重用

(2)对 数据卷 的修改会立马生效

(3)对 数据卷 的更新,不会影响镜像

(4)数据卷 默认会一直存在,即使容器被删除

数据卷的使用类似 linux 下对目录或文件进行 mount 操作,目前Docker提供了三种不同的方式将数据卷从宿主机挂载到容器中:

  • 数据卷挂载:volume mount
  • 绑定挂载:bind mount
  • tmpfs mount

其中volumebind mount比较常用,tmpfs mount基本不会用.

1.1 数据卷挂载

数据卷挂载即将特定数据卷挂载到容器内部,数据卷挂载需要提前创建volume数据卷,然后再提供给容器绑定,新建的数据卷位于宿主机的/var/lib/docker/volumes目录里。这种挂载方式就是不需要手动指定宿主机目录并且能够通过docker volume命令来维护数据卷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#创建数据卷
docker volume create 数据卷名
#显示数据卷的详细信息
docker volume inspect 数据卷名
#列出所有的数据卷
docker volume ls
#删除未使用的数据卷(正被容器使用的数据卷无法删除)。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令
docker volume rm 数据卷名
#删除所有未使用的数据卷
docker volume prune

#运行容器并挂载数据卷
#type:挂载类型,包括volume(默认)和bind。source:数据卷名称(数据卷不存在则自动创建,如果不指定数据卷则自动创建随机数名称的数据卷)
#可以将多个数据卷挂载给一个容器;可以将一个数据卷挂载给多个容器,但是都共同使用同一个宿主机的数据卷目录
docker run -d --name 容器名称 --mount type=volume,source=数据卷名,target=容器内部目录或文件绝对路径 镜像名称或id
#可以使用-v(--volume)来简写,数据卷不存在则自动创建,如果不指定数据卷(如-v 容器内部目录或文件绝对路径)则自动创建随机数名称的数据卷
docker run -d --name 容器名称 -v 数据卷名:容器内部目录或文件绝对路径 镜像名称或id

##数据卷权限
#Docker挂载数据卷的默认权限是可读写rw,可以通过ro标记指定为只读,这样可以防止容器修改文件
#使用--mount时设置数据卷权限。不指定readonly则为读写
docker run -d --name 容器名称 --mount type=volume,source=数据卷名,target=容器内部目录或文件绝对路径,readonly 镜像名称或id
#使用-v(--volume)时设置数据卷权限
docker run -d --name 容器名称 -v 数据卷名:容器内部目录或文件绝对路径:rw 镜像名称或id
docker run -d --name 容器名称 -v 数据卷名:容器内部目录或文件绝对路径:ro 镜像名称或id

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker volume create my-vol
docker volume inspect my-vol
[
{
"CreatedAt": "2022-08-26T11:11:08+08:00",
"Driver": "local", #local表示宿主机。配置为vieux/sshfs(需要安装插件)可以把数据卷存储到其他云主机去,这里不做过多介绍。
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data", #数据卷所在宿主机位置
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]

docker run -d --name nginx --mount type=volume,source=my-vol,target=/usr/share/nginx/html nginx:1.22.0-alpine
#可以使用-v(--volume)来简写,如下:
#docker run -d --name nginx -v my-vol:/usr/share/nginx/html nginx:1.22.0-alpine

1.2 绑定挂载

绑定挂载即直接将主机文件系统中的目录或文件挂载到容器内部的方式,没有涉及到数据卷管理。当你使用绑定挂载时,主机上的目录或文件会直接映射到容器内部的路径,它们之间的更改会实时同步。注意这种方式不能使用 docker volume 命令来维护挂载目录或文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
#使用source表示自定义数据卷路径,source必须是绝对路径,且路径必须已经存在,否则报错
docker run -d --name 容器名称 --mount type=bind,source=宿主机目录或文件绝对路径,target=容器内部目录或文件绝对路径 镜像名称或id
#可以使用-v(--volume)来简写,如下:
#--mount和-v(--volume)有个区别,就是使用-v宿主机目录或文件不存在也能自动创建,而使用--mount不存在则会运行失败
docker run -d --name 容器名称 -v 宿主机目录或文件绝对路径:容器内部目录或文件绝对路径 镜像名称或id

##挂载目录或文件的权限
#Docker挂载目录或文件的默认权限是rw(即可读写),可以通过ro标记指定为只读,这样可以防止容器修改宿主机的目录或文件
#可使用--mount时设置挂载目录的权限,如只读(readonly)。
docker run -d --name 容器名称 --mount type=bind,source=宿主机目录或文件绝对路径,target=容器内部目录或文件绝对路径,readonly 镜像名称或id
#使用-v(--volume)时设置数据卷权限
docker run -d --name 容器名称 -v 宿主机目录或文件绝对路径:容器内部目录或文件绝对路径:rw 镜像名称或id
docker run -d --name 容器名称 -v 宿主机目录或文件绝对路径:容器内部目录或文件绝对路径:ro 镜像名称或id

示例:

1
2
3
mkdir -p /opt/nginx
docker run -d --name nginx --mount type=bind,source=/opt/nginx,target=/usr/share/nginx/html nginx:1.22.0-alpine
#docker run -d --name nginx -v /opt/nginx:/usr/share/nginx/html nginx:1.22.0-alpine

2. 数据卷容器

数据卷不仅可以设置在宿主机,同样也可以设置在其他容器中。这样即使容器挂掉或者被删除,也不会影响数据卷容器里已经同步的数据,同样的数据卷容器挂掉或者被删除也不会影响容器的数据。

1
2
3
4
#创建数据卷容器
docker run -d -v 数据卷名称或者宿主机目录或文件:容器目录或文件 --name=数据卷容器名称 镜像名称[:版本号]或ID
#挂载数据卷容器给其他容器
docker run -d --name=容器名称 --volumes-from 数据卷容器名称或ID 镜像名称[:版本号]或ID

示例:

1
2
3
4
5
docker run -d --name nginx-backup  -v /opt/data:/opt/data nginx:1.22.0-alpine
docker run -d --name nginx --volumes-from nginx-backup nginx:1.22.0-alpine
docker exec -it nginx touch /opt/data/test.txt
docker rm -f nginx
docker exec -it nginx-backup ls /opt/data

七、Docker 网络

Docker默认提供了3种网络模式,生成容器时不指定网络模式下默认使用bridge桥接模式。

1. host 模式

容器不会创建自己的网卡,配置 IP 等,而是使用宿主机的 IP 和端口

1
docker run -d --name 容器名称 --net=host   镜像名称

2. container 模式

容器不会创建自己的网卡,配置 IP 等,而是和一个指定的容器共享 IP和端口

1
docker run -d --name 容器名称 --net=container:容器名称或容器id   镜像名称

3. none 模式

关闭网络功能,不进行任何网络设置

1
docker run -d --name 容器名称 --net=none   镜像名称

4. bridge 模式(默认模式)

为每一个容器分配、设置 IP 等,并将容器连接到 docker0 虚拟网桥上,这是 docker run 创建容器时使用的默认模式。

当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥(网卡/路由器),此主机上启动的Docker容器都会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。

宿主机和Docker容器之间是可以进行网络连接的,同样的,Docker容器和容器之间只要是在一个虚拟网桥下,那么也可以直接进行网络连接。

1
2
#通过--net=bridge显示指定,默认就是bridge,也可以不设置
docker run -d --name 容器名称 --net=bridge 镜像名称

5. 自定义网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
##自定义创建名称为mynet的网络
#--driver bridge 桥接模式
#--subnet 192.168.0.0/16 子网掩码(网段)的主机IP可选范围为:192.168.0.1-192.168.255.254
#--gateway 192.168.0.1 网关
#其中192.168.0.1被设置为网关(即宿主机的虚拟网卡IP),故容器主机IP的可选范围为:192.168.0.2-192.168.255.254
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet

#删除网络
docker network rm mynet
#删除主机上全部未使用的网络
docker network prune

#查看当前Docker所有的网络模式
docker network ls

#查看自定义网络详细信息
docker network inspect mynet

#通过--net在启动容器时指定自定义网络
docker run -d --name 容器名称 --net mynet 镜像名称

#不同Docker网络之间的容器想要连接的话,需要把该容器注册到另一个容器所在的网络上
#如下假设centos01容器本来在默认的bridge(docker0)上,想要让其和mynet网络下的容器互通,则需要把centos01注册到mynet中。
#成功注册到mynet网络后,docker将会在centos01中创建一个网卡并且分配一个mynet网段内的IP,并且将新ip-主机名映射加入自己的hosts中
docker network connect mynet centos01
  • 默认docker0是无法Ping通容器名,而自定义网络可以Ping通容器名。
  • 在多个微服务部署到docker时,通过自定义网络方便统一的网络配置。

6. 多个容器之间通过容器名称互相访问

docker run 创建容器时默认使用的是 docker0 网络,容器可以通过另一个容器的 ip 来访问其他容器主机,但是没法通过容器 id 或者主机名来访问,因为容器的 hosts 文件中默认只有容器自己的 ip-主机名映射,而没有其他容器的。为了能够通过容器名称来访问其他容器,可以在容器的 hosts 文件中配置其他容器的 ip-主机名映射,这个过程可以使用 docker exec 命令进入容器内手动修改 hosts 文件实现,但这种方式会有点麻烦。还有另一解决方式是在 docker run 创建容器的时候通过指定 --link 参数来配置其他容器的ip-主机名映射。如下:

1
2
#创建centos02容器,并在其内部hosts中加入centos01容器的ip-主机名映射。
docker run -d -it --name centos02 --link centos01容器的 centos:centos7

docker run 创建容器时会自动把分配到的的 ip 和 hostname(即主机名,默认为容器id)写入到容器的hosts文件中。其中 主机名可以在 docker run 时指定 --hostname 来修改。

如上配置后就能让 centos02 能够访问到 centos01,但是 centos01 却不能访问 centos02,这是因为 centos01 的hosts 文件中没有加入 centos02 的 ip - hostname映射,除非使用 docker exec 命令进入 centos01 容器内修改其 hosts 文件,可见使用--link 参数配置的方式操作也很麻烦。

为了更加简便地实现 docker 中多个容器之间的互相访问,有一种更好的解决方案,那就是使用“docker自定义网络”,docker 自定义网络可以理解为“网卡”,docker 允许用户将多个容器配置到同一个自定义网络中,容器之间通过容器名称互相访问的时候,docker 将会根据自定义网络中的容器配置信息解析容器名称得到对应的 ip ,进而实现访问。

可以通过 docker inspect 自定义网络名称 命令来查看自定义网络中的容器配置信息,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
docker network ls
#查看自定义网络配置
docker inspect 自定义网络名称
#自定义网络中多个容器的配置信息如下:
[
{
"Name": "mynet",
"Containers": {
"0a348b84901337546e60bfcc7cd8592a81e244339337f1ceea9b578bba09d9dd": {
"Name": "nginx2",
"EndpointID": "867176b7354f17095eff8f5a047bbae062d3f80b4ad150f1711a13f0543ed549",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
},
"6bfc4ad47e7f878f14b5fa1c3a59029fc6b50a1e04cdb946bd45de4e052a7416": {
"Name": "nginx1",
"EndpointID": "4498313ad280f9e15c86f5c756a66dbdb39982f8469be50c2761e058b333bb31",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"a29f5b6558b22bf557a2b997c3c03284f013bc75da89065a983a2452f27bee0c": {
"Name": "nginx3",
"EndpointID": "85e61dc0d27a7d2a7be14c11e6566656817693981ee22b277328ee3116aafef4",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
}
}
]

将多个容器配置到同一个自定义网络中的操作如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#创建一个名称为mynet的自定义网络
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
#使用默认bridge(docker0)网络创建centos01
docker run -d --name centos01 centos:centos7
#使用自定义网络mynet创建centos02
docker run -d --name centos02 --net mynet centos:centos7
docker run -d --name centos01 centos:centos7
#使用自定义网络mynet创建centos03
docker run -d --name centos03 --net mynet centos:centos7
#测试centos01是否连通到centos03情况(发现连通成功)
docker exec -it centos01 ping centos03
#测试centos03是否连通到centos01情况(发现连通成功)
docker exec -it centos03 ping centos01
#测试centos01是否连通到centos02情况(发现不通)
docker exec -it centos01 ping centos02
#把该centos01注册到centos02所在的mynet网络上。
docker network connect mynet centos01
#测试centos01是否连通到centos02情况(发现连通成功)
docker exec -it centos01 ping centos02

7. 查看容器ip

两种方式:

1、由于docker run创建容器时就会给容器分配ip等信息了,所以可以直接使用docker inspect命令来过滤数据获取。

2、通过docker exec命令进入容器内部在使用如ifconfig等方式查看

1
2
3
4
5
6
7
8
9
10
##通过docker inspect查看容器ip
#查看容器ip
docker inspect --format='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名称或容器id
#显示所有容器 IP 地址
docker inspect --format='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)

##通过docker exec查看容器ip
docker exec -it 容器名称或容器id ifconfig
docker exec -it 容器名称或容器id ip iddr
docker exec -it 容器名称或容器id cat /etc/hosts

参考:https://blog.csdn.net/CSDN1csdn1/article/details/123961079

8. Docker与防火墙

1)bridge 网络模式的防火墙配置

Docker 默认使用的是 bridge 网络模式,在创建容器时会自动为容器配置 iptables 规则以进行网络安全控制。具体来说,Docker 会在主机上创建一个名为 docker0 的虚拟网桥,并将容器连接到该网桥中。同时,它还会为每个容器分配一个随机的 IP 地址,并通过 NAT 的方式将容器和宿主机的 IP 地址进行映射。

说明:

bridge 网络模式下,启动或停止 docker 容器时,docker 都会在 iptables 防火墙配置中自动创建或删除和容器相关的规则策略,而不需要人为手动进行配置。

由于 docker 使用的是iptables 命令维护防火墙配置,所以用户只能通过iptables 命令来查看 docker 相关的防火墙规则策略,而使用firewallufw 等上层防火墙工具则无法查看。

2)host 网络模式的防火墙配置

当 Docker 使用 host 网络模式时,Docker 容器将直接使用宿主机的网络环境,包括 IP 和端口。此时 Docker 不会再为容器自动进行 iptables 配置,而是直接使用宿主机的防火墙规则。

9. 反向代理到容器

1)简要概述

使用一些反向代理服务(如:Nginx)可以将用户请求代理到容器内部的端口监听服务上,从而可以给 docker 容器做负载均衡、网络安全防护等,这些都是常见的运维需求。其中网络安全防护的一些具体需求如下:

  • (1)通过限制外网访问 docker 容器从而实现多个 docker 微服务容器之间可以在内网中互相访问,但不允许外网直接访问。
  • (2) docker 中的 HTTP 服务容器未进行 SSL 加密而直接提供外网访问是非常不安全的,一般做法是先配置 docker 容器禁止外网访问而只允许内网访问,再使用 nginx 通过反向代理转发外网的 HTTPS 请求给 docker 容器中的 HTTP 服务。

下面是以上需求的具体实现方案:

2)限制外网访问 docker 容器

方式1:使用默认的 bridge 网络模式创建 docker 容器并指定端口映射时限制访问 IP。操作如下:

1
docker run -d --name 容器名称 -p 127.0.0.1:宿主机端口:容器端口  镜像名称

注意:docker会自动维护防火墙配置,通常不设置 ip 则容器对应的宿主机端口默认对所有 IP 访问不限制。

这种方式运行容器,容器程序的监听端口会占用宿主机的端口。

方式2:使用 host 网络模式创建 docker 容器,让容器监听端口直接使用宿主机端口。操作如下:

1
docker run -d --name 容器名称 --net=host  镜像名称

注意:使用 host 网络模式创建 docker 容器时, docker 不会进行 iptables 自动配置,而是直接使用宿主机的防火墙规则。

这种方式运行容器,容器程序的监听端口会占用宿主机的端口。

方式3:创建 docker 容器时不进行端口映射,而通过容器 IP 进行访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
#方法1:使用默认的bridge网络自动分配IP给容器(默认的bridge网络IP网段为172.17.0.0/16)
docker run -d --name 容器名称 镜像名称

#方法2:手动指定一个自定义网络的固定IP给容器
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker run -d --name 容器名称 --net=mynet --ip 192.168.0.100 镜像名称


#查看容器的ip
docker inspect 容器名称或ID | grep IPAddress
#或者:docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名称或ID
#查看默认的bridge网络的ip网段
docker network inspect bridge | grep Subnet

这种方式运行容器,容器程序的监听端口不会占用宿主机的端口。

注意:不能使用默认的 bridge 网络模式指定自定义 IP,否则会报错或设置无效。

3)通过nginx反向代理到docker的HTTP服务容器

(1)配置 nginx SSL证书和反向代理,如下:

配置nginx SSL证书并监听一个对外网访问的端口,并反向代理到docker容器的监听端口(设端口为:8080)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 443 ssl; #此处端口为nginx对外网暴露的端口
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
charset utf-8;

location ^~ / {
proxy_pass http://127.0.0.1:8080; #此处端口为docker的HTTP服务容器的监听端口
#proxy_pass http://172.17.0.100:8080;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 100m;
}
}
  • 假设docker run 运行 http 容器时限制 ip 映射端口为 -p 127.0.0.1:宿主机端口:容器端口,那么可以代理到 http://127.0.0.1:8080
  • 而如果不进行端口映射,则可以通过命令 docker inspect 容器名称或ID | grep IPAddress 查看容器的 ip ,然后反向代理到对应的地址,如:http://172.17.0.100:8080

(2)配置防火墙放行nginx对外网暴露的端口

1
2
3
4
5
6
##宿主机配置防火墙
#对于 UFW 防火墙:
ufw allow 443/tcp
#对于 FirewallD 防火墙:
firewall-cmd --zone=public --add-port=443/tcp --permanent
firewall-cmd --reload

最后外网访问的地址为:https://example.com(即:https://example.com:443

八、构建自定义镜像

方式1:上传程序到vps后构建

需求:构建一个能在tomcat容器里运行的war包程序镜像,并通过镜像创建一个实例容器。

由于依赖于tomcat容器,故需要先拉去tomcat镜像到本地。tomcat镜像主页:https://c.163yun.com/hub#/library/repository/info?repoId=3105

(1)拉取tomcat镜像到宿主机

1
docker pull hub.c.163.com/library/tomcat:latest

(2)上传war包

创建一个webdemo项目,并在这个webdemo项目的资源路径里添加一个index.jsp页面。

1
2
3
4
5
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>

然后打包得到webdemo.war文件。并上传这个webdemo.war文件到vps的/root/project目录

(3)编写Dockerfile文件

1
2
#在/root/project下编写一个Dockerfile文件
vim Dockerfile
1
2
3
4
5
6
#继承于某个镜像
from hub.c.163.com/library/tomcat
#镜像所有者信息
MAINTAINER qcmoke [email protected]
#把程序copy到容器镜像当中,前面的参数是本地路径,后面的参数是容器镜像路径(可以到镜像主页得到tomcat的webapp路径)
COPY /root/project /usr/local/tomcat/webapps

(4)构建生成镜像

1
2
#在Dockerfile文件所在的目录,即/root/project里执行如下命令
docker build -t webdemo:latest .

(5)查看本地镜像仓库是否构建成功

1
docker images
1
2
3
REPOSITORY                     TAG                 IMAGE ID            CREATED                  SIZE
docker.io/tomcat latest aeea3708743f Less than a second ago 529 MB
webdemo latest 10f75818641a 46 seconds ago 310 MB

(6)通过构建的镜像实例化一个容器并运行

1
docker run -d -p 8080:8080 webdemo:latest

(7)访问测试

访问index.jsp:http://192.168.222.131:8080/webdemo/index.jsp

image-20200215111413522

(8)其他

在用-d指定为后台启动的情况下,可以使用以下命令查看容器实时的日志情况:

1
docker logs -f 容器名称或者容器id

方式2:通过idea插件连接vps构建

(1)配置docker允许外网访问

在vps中配置docker允许外网访问

1
vim /usr/lib/systemd/system/docker.service

ExecStart属性原来值的最后追加

1
-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock

新版Docker CE配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
....
....
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
....
....

旧版Docker配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[Service]
....
....
ExecStart=/usr/bin/dockerd-current \
--add-runtime docker-runc=/usr/libexec/docker/docker-runc-current \
--default-runtime=docker-runc \
--exec-opt native.cgroupdriver=systemd \
--userland-proxy-path=/usr/libexec/docker/docker-proxy-current \
--init-path=/usr/libexec/docker/docker-init-current \
--seccomp-profile=/etc/docker/seccomp.json \
$OPTIONS \
$DOCKER_STORAGE_OPTIONS \
$DOCKER_NETWORK_OPTIONS \
$ADD_REGISTRY \
$BLOCK_REGISTRY \
$INSECURE_REGISTRY \
$REGISTRIES \
-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
....
....

(2)重新加载配置并重启Docker

1
2
systemctl daemon-reload
systemctl restart docker

(3)启动防火墙并开放2375端口

开放了防火墙2375端口用于远程连接

1
2
3
service firewalld start
firewall-cmd --permanent --add-port=2375/tcp
firewall-cmd --reload
  • 不需要另外开放容器映射到宿主机的端口。
  • 经操作发现,如果使用vmware的linux作为vps,不能直接关闭防火墙,开放相应的端口即可。

(4)创建一个springboot项目

image-20200216133115274

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--将其他模块的依赖一起打包生成可执行jar-->
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

DemoApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author qcmoke
*/
@RestController
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

@RequestMapping("/hello")
public Object hello() {
return "hello docker!";
}
}

application.yml

1
2
server:
port: 8080

Dockerfile

1
2
3
4
5
6
7
8
9
FROM openjdk:8u212-jre
#存放持久化数据的目录
VOLUME /tmp
#要复制到容器中的问件
ADD target/demo-boot-0.0.1-SNAPSHOT.jar /demo-boot.jar
#docker容器启动时执行的命令
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/demo-boot.jar"]
#启动端口号(容器中的)
EXPOSE 8080

(5)编译项目生成jar包

image-20200216134810561

(6)配置远程docker插件连接

默认新版的idea自带有这个插件,如果没有直接到插件中心安装即可。

image-20200216133850397

(7)然后配置DockerFile

image-20200216132351344

image-20200216134320278

(8)运行docker插件

运行插件部署镜像到vps,并通过镜像创建一个实例容器

image-20200216140432811

(9)访问测试

最后访问: http://192.168.222.132:8080/hello

image-20200216141102237

九、Docker Compose

1. 简介

Compose项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。

地址:https://github.com/docker/compose

使用一个 Dockerfile 模板文件,可以让用户很方便的定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。Compose 允许用户通过一个单独的 docker-compose.yml 模板文件来定义一组相关联的应用容器为一个项目。

2. 安装

在 Linux 上的也安装十分简单,从 官方 GitHub Release 处直接下载编译好的二进制文件即可。

例如,在 Linux 64 位系统上直接下载对应的二进制包。

1
2
3
4
curl -L https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
#旧版:curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version

因为 Docker Compose 存放在 GitHub,可能网络不太稳定。可以想办法下载后再上传到服务器,操作如下:

(1)手动下载 docker-compose 文件,下载地址:https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64

(2)复制并重命名下载的文件为docker-compose

1
>mv docker-compose-Linux-x86_64 /usr/local/bin/docker-compose

3. 卸载

如果是二进制包方式安装的,删除二进制文件即可。

1
rm /usr/local/bin/docker-compose

4. 编排文件

下面以编排 mysql、redis、nacos 容器为示例,配置如下:

在任意目录创建compose编排配置文件:docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
## 编排文件的版本
version: '3'
services: # compose管理的服务
redis:
image: redis # 指定镜像
container_name: redis-1 # 容器名称
restart: always # 开机自动启动
ports: # 端口映射
- 6379:6379
volumes: # 目录映射
- /opt/redis/conf:/usr/local/etc/redis
- /opt/redis/data:/data
command: redis-server --requirepass qcmoke123456 --appendonly yes #执行的命令; requirepass来指定远程登录密码; appendonly来指定是否开启持久化策略
#command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes

mysql:
image: mysql:5.7.24
container_name: mysql-1
ports:
- 3306:3306
environment: # 环境变量
MYSQL_ROOT_PASSWORD: qcmoke123456 # 设置数据库root密码
restart: always
volumes:
- /opt/mysql/data:/var/lib/mysql
#当 MySQL 服务启动时会以/etc/mysql/my.cnf为配置文件,而该文件会导入/etc/mysql/conf.d目录中所有以.cnf为后缀的文件。
#这些文件会拓展或覆盖 /etc/mysql/my.cnf 文件中的配置。因此创建以.cnf结尾的配置文件并挂载至MySQL容器中的/etc/mysql/conf.d目录即可。
#在本项目中只要在docker-compose up前上传my.cnf文件到宿主机的/opt/mysql/conf.d/my.cnf目录即可。如果在docker-compose up后的话,需要重启mysql容器才能重新加载到自定义的配置文件。
- /opt/mysql/conf.d:/etc/mysql/conf.d

nacos:
image: nacos/nacos-server
container_name: nacos-standalone
environment:
- MODE=standalone
ports:
- 8848:8848

注意:

  • 如果编排文件没有显式指定网络,则 Docker Compose 在默认情况下会自动创建一个新的 “bridge”(桥接)网络,并将Compose文件中的所有容器服务连接到该网络。这个默认的"bridge"网络在Compose文件中的每个服务之间提供了连接和通信的能力。通过这个网络,服务可以通过它们的服务名称相互访问,并使用容器的IP地址进行通信。
  • 如果在Compose文件中显式指定了网络,那么服务将连接到指定的网络。

下面是以上编排文件中使用到的 mysql 配置文件:/opt/mysql/conf.d

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8

[mysqld]
# 设置3306端口
port = 3306
# 设置mysql的安装目录
# basedir=/var/lib/mysql
# 设置 mysql数据库的数据的存放目录,如果是MySQL 8+ 不需要以下配置,系统自己生成即可,否则有可能报错
# datadir=/var/lib/mysql/data
# 允许最大连接数
max_connections=20
# 服务端使用的字符集默认为8比特编码的latin1字符集
character-set-server=utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 最大连接数
max_connections=500

5. 操作命令

docker-compose.yml所在的目录执行如下命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#查看正在运行的容器
docker-compose ps

#启动容器
#尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。相关的服务都将会被自动启动,除非已经处于运行状态。
#注意:如果不指定SERVICE,则默认运行配置文件的所有容器服务
#以守护进程模式运行加-d选项
#添加--build表示启动前构建镜像(如果已经存在镜像则强制重新构建)
docker-compose up -d [SERVICE...]

#暂停容器
docker-compose pause [SERVICE...]

#恢复暂停的容器
docker-compose unpause [SERVICE...]

#停止并移除所有相关容器(此命令将会停止并移除up命令所启动的容器,并移除网络,但不会移除卷,如需移除卷,则需要加--volumes参数)
docker-compose down
#docker-compose down --volumes

#重启项目中的服务。
docker-compose restart [options] [SERVICE...]

#执行容器内部命令
docker-compose exec <service_name> <command>

#查看容器日志
docker-compose logs

十、Docker Swarm

Docker Swarm 是 Docker 的集群管理工具,或者说是资源管理工具。swarm 集群由管理节点(manager)和工作节点(work node)构成。

swarm mananger:负责整个集群的管理工作包括集群配置、服务管理等所有跟集群有关的工作。

work node:即图中的 available node,主要负责运行相应的服务来执行任务(task)。

注意:跟集群管理有关的任何操作,都是在管理节点上操作的。

准备6台linux服务器

manager-1:192.168.99.101

manager-2:192.168.99.102

manager-3:192.168.99.103

worker-1:192.168.99.201

worker-2:192.168.99.202

worker-3:192.168.99.203

1. 关闭所有服务器的防火墙

1
service firewalld stop

2. 初始化 swarm 集群

进行初始化的这台机器,就是集群的管理节点。

1
docker swarm init --advertise-addr 192.168.68.101

eg:

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# docker swarm init --advertise-addr 192.168.68.101
Swarm initialized: current node (w7xdf6see52t2t24x1ykedg5o) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join \
--token SWMTKN-1-04sl6nb0acbzzgvfrqycph4jtnyh0wcoix70gz44sjuaf66cwd-3xqtzzxymio3077xfki41od0e \
192.168.68.101:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

3. 添加worker节点

在manager节点下生成worker令牌,以让其他worker服务器加入

1
docker swarm join-token worker

eg:

1
2
3
4
5
6
[root@localhost ~]# docker swarm join-token worker
To add a worker to this swarm, run the following command:

docker swarm join \
--token SWMTKN-1-04sl6nb0acbzzgvfrqycph4jtnyh0wcoix70gz44sjuaf66cwd-3xqtzzxymio3077xfki41od0e \
192.168.68.101:2377

然后在三台worker节点运行:

1
2
3
docker swarm join \
--token SWMTKN-1-04sl6nb0acbzzgvfrqycph4jtnyh0wcoix70gz44sjuaf66cwd-3xqtzzxymio3077xfki41od0e \
192.168.68.101:2377

4. 添加manager节点

在manager节点下生成manager令牌,以让其他manager服务器加入

1
docker swarm join-token manager

eg:

1
2
3
4
5
6
[root@localhost ~]# docker swarm join-token manager
To add a manager to this swarm, run the following command:

docker swarm join \
--token SWMTKN-1-04sl6nb0acbzzgvfrqycph4jtnyh0wcoix70gz44sjuaf66cwd-4nbcolan6w4akrs3zsbvrkqxe \
192.168.68.101:2377

然后在三台manager节点运行如下命令:

1
2
3
docker swarm join \
--token SWMTKN-1-04sl6nb0acbzzgvfrqycph4jtnyh0wcoix70gz44sjuaf66cwd-4nbcolan6w4akrs3zsbvrkqxe \
192.168.68.101:2377

5. 查看所有管理的节点

manager-1运行命令查看所有管理的节点

1
docker node ls

eg:

1
2
3
4
5
6
7
8
[root@localhost ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
dif8w8hp6pevz9k0owqkg6hjl localhost.localdomain Ready Active
j9jqdioy91olyxd8dfghgm8qc localhost.localdomain Ready Active
mfv24dim85bi7sd9wl589rhdf localhost.localdomain Ready Active Reachable
q1qayx19ebfg7oi8dv7e3mzyh localhost.localdomain Ready Active
rbeb5mykx83qpr6u8afriqx9z localhost.localdomain Ready Active Reachable
w7xdf6see52t2t24x1ykedg5o * localhost.localdomain Ready Active Leader

6. 创建集群服务

1
docker service create -p 80:80 --name my-nginx nginx

这时候将会在任意节点中选择一台通过docker创建nginx服务。我们可以在集群下任意一台服务器(manager或者worker节点)访问nginx服务都能访问到,因为swarm创建的服务使用的是swarm集群网络。

7. 查看服务集群情况

1
docker service ls

eg:

1
2
3
[root@localhost ~]# docker service ls
ID NAME MODE REPLICAS IMAGE
v1hn945nmyds my-nginx replicated 1/1 nginx:latest

REPLICAS:副本数量

8. 集群服务扩容

对my-nginx服务扩容为5个

第一种方式:

1
docker service update --replicas 5 my-nginx

eg:

1
2
3
4
5
[root@localhost ~]# docker service update --replicas 5 my-nginx
my-nginx
[root@localhost ~]# docker service ls
ID NAME MODE REPLICAS IMAGE
v1hn945nmyds my-nginx replicated 5/5 nginx:latest

第二种方式:

1
docker service scale my-nginx=5
1
2
3
4
5
[root@localhost ~]# docker service scale my-nginx=5
my-nginx scaled to 5
[root@localhost ~]# docker service ls
ID NAME MODE REPLICAS IMAGE
v1hn945nmyds my-nginx replicated 5/5 nginx:latest

update更多的应用场景在于image的升级导致的运行中的服务需要更新等等

9. 删除集群服务

1
docker service rm my-nginx

这时候所有副本都会被删除

10. 修改服务

比如修改nginx服务镜像版本为1.9.5

1
$ docker service update --image nginx:1.9.5 my-nginx
1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# docker service ps my-nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
kzg8qncaal9a my-nginx.1 nginx:1.9.5 localhost.localdomain Running Running about a minute ago
er55pvmd8fn6 \_ my-nginx.1 nginx:latest localhost.localdomain Shutdown Shutdown about a minute ago
kv3t6poxh3rb my-nginx.2 nginx:1.9.5 localhost.localdomain Running Running about a minute ago
tuzkxmb0th6x \_ my-nginx.2 nginx:latest localhost.localdomain Shutdown Shutdown about a minute ago
edt0tg6tg120 my-nginx.3 nginx:1.9.5 localhost.localdomain Running Running about a minute ago
qfwlzj8hb3gl \_ my-nginx.3 nginx:latest localhost.localdomain Shutdown Shutdown about a minute ago
bb9kjj3n81yx my-nginx.4 nginx:1.9.5 localhost.localdomain Running Running about a minute ago
spx8ybz9wlph \_ my-nginx.4 nginx:latest localhost.localdomain Shutdown Shutdown about a minute ago
vs4akco7zpls my-nginx.5 nginx:1.9.5 localhost.localdomain Running Running about a minute ago
qtaqq0jout6k \_ my-nginx.5 nginx:latest localhost.localdomain Shutdown Shutdown about a minute ago

11. 服务回滚

1
docker service update --rollback

十一、运行常用 Docker 容器示例

步骤:软件镜像(安装程序)—> 运行镜像 —> 产生一个容器(正在运行的软件)。

1. 安装 nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#拉取(下载)镜像
docker pull nginx:latest
#首先简单运行一个nginx临时容器,用于复制容器内部的一些nginx相关重要文件到宿主机
docker run -d --name nginx nginx:latest
#创建映射目录
mkdir -p /opt/nginx/{conf,html,logs}
#复制容器内部的一些nginx相关重要文件到映射目录中(注意命令中的末尾的点,它表示复制目录中的所有内容而不是整个目录本身)
docker cp nginx:/etc/nginx/. /opt/nginx/conf
docker cp nginx:/usr/share/nginx/html/. /opt/nginx/html
docker cp nginx:/var/log/nginx/. /opt/nginx/logs
#删除nginx临时容器
docker rm -f nginx

#生产环境下运行nginx容器
#(1)使用host网络模式直接使用宿主机网络IP地址和端口(可以避免每次配置新的监听端口都需要重新docker run映射端口)
#(2)限制内存上限为200MB
#(3)指定时区并同步宿主机和容器的时间
#(4)映射几个主要的nginx相关目录
docker run -d \
--restart always \
--name nginx \
--net=host \
-m 200m \
-e TZ=Asia/Shanghai \
-v /etc/localtime:/etc/localtime:ro \
-v /opt/nginx/conf:/etc/nginx \
-v /opt/nginx/html:/usr/share/nginx/html \
-v /opt/nginx/logs:/var/log/nginx \
-v /opt/cache:/opt/cache:rw \
-v /opt/ssl-certs:/opt/ssl-certs:rw \
nginx:latest

#防火墙设置(开放nginx默认配置的80端口)
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --reload

访问地址如:http://192.168.60.201

提示:Docker 安装的 nginx 容器里的默认路径下的日志文件都只是符号链接,如下:

  • access.log -> /dev/stdout
  • error.log -> /dev/stderr

这样做是方便通过 docker logs nginx 查看。如需修改为真实文件,只需修改日志路径或者文件名称即可,当然更建议针对不同网站使用不同的日志路径。

2. 安装 tomcat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#1、搜索镜像
docker search tomcat

#2、拉取(下载)镜像
#docker pull tomcat
#这里使用的是网易镜像站的,网址:https://c.163yun.com/hub#/home
docker pull hub.c.163.com/library/tomcat

#3、根据镜像实例化一个容器并运行
# 选项:-d:表示后台运行;-p: 表示将主机的端口映射到容器的一个端口,如-p 主机端口:容器内部的端口。
# 启动一个做了端口映射的tomcat,假设虚拟机ip为192.168.222.132,那么启动容器后访问http://192.168.222.132:8080
docker run -d --name mytomcat -p 8080:8080 hub.c.163.com/library/tomcat

#4、查看运行中的容器
docker ps

#5、查看当前所有容器
docker ps -a

#6、 停止运行中的容器
docker stop mytomcat

#7、启动容器
docker start mytomcat

#8、删除一个容器(需要先停止运行中的容器)
docker rm mytomcat

#9、查看容器的日志 docker logs container-name或者container-id
docker logs mytomcat

更多命令参看https://docs.docker.com/engine/reference/commandline/docker/可以参考每一个镜像的文档

3. 安装 mysql

1
2
3
4
5
6
7
#获取镜像
docker pull mysql
#docker pull mysql:5.7 #指定版本用 :标签(版本号)

#运行容器
docker run -d -p 3306:3306 --name mysql02 -e MYSQL_ROOT_PASSWORD=123456 mysql
#docker run -d -p 3306:3306 --name mysql5_7 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

指定 mysql 配置文件运行 mysql 容器案例示例:

1
2
3
4
docker pull mysql:5.7
mkdir /opt/mysql/conf -p
mkdir /opt/mysql/data -p
vim /opt/mysql/conf/my.cnf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
## my.cnf
[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8

[mysqld]
# 设置3306端口
port = 3306
# 设置mysql的安装目录
# basedir=/var/lib/mysql
# 设置 mysql数据库的数据的存放目录,如果是MySQL 8+ 不需要以下配置,系统自己生成即可,否则有可能报错
datadir=/var/lib/mysql/data
# 允许最大连接数
max_connections=20
# 服务端使用的字符集默认为8比特编码的latin1字符集
character-set-server=utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 最大连接数
max_connections=500
1
2
3
#运行mysql实例
docker run -d --name mysql -p 3306:3306 -v /opt/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
docker run -d --name mysql -p 3306:3306 -v /opt/mysql/conf:/etc/mysql/conf.d -v /opt/mysql/data:/var/lib/mysql/data -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

4. 安装 rabbitmq

(1)获取镜像

1
2
#该版本包含了web控制页面
docker pull rabbitmq:management

(2)运行镜像

1
2
3
4
5
#方式一:默认用户名和密码都是guest
docker run -d --hostname my-rabbit --name rabbit -p 15672:15672 -p 5672:5672 rabbitmq:management

#方式二:设置用户名和密码
docker run -d --hostname my-rabbit --name rabbit -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=password -p 15672:15672 -p 5672:5672 rabbitmq:management

(3)访问rabbitmq管理页面页面 http://ip:15672

5. 安装 redis

创建redis配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mkdir -p /opt/redis/{conf,data}/
cat > /opt/redis/conf/redis.conf << 'EOF'
#节点端口
port 6379
#访问认证密码
requirepass 123456
#如果主节点开启了访问认证,从节点访问主节点需要认证的密码
masterauth 123456
#日志文件和序列化文件等默认存当前目录,可修改到指定目录
#dir ./
#保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
protected-mode no
#是否以守护线程的方式启动(后台启动),默认 no;
daemonize no
#是否开启 AOF 持久化模式,默认 no;
appendonly yes
#是否开启集群模式,默认 no;
cluster-enabled no
EOF

该原始文件可从官网下载

  • bind 127.0.0.1 注释掉这部分,这是限制redis只能本地访问
  • protected-mode no 默认yes,开启保护模式,限制为本地访问
  • daemonize no#默认no 改为yes意为以守护进程方式启动,可后台运行,除非kill进程,改为yes会使配置文件方式启动redis失败
  • databases 16 数据库个数(可选),我修改了这个只是查看是否生效。。
  • dir ./ 输入本地 redis 数据库存放文件夹(可选)
  • appendonly yes redis持久化(可选)
  • requirepass 123456 配置redis访问密码

运行redis镜像

1
2
3
4
5
docker run -d --name redis \
-v /opt/redis/conf/redis.conf:/etc/redis/redis.conf \
-v /opt/redis/data:/data \
-p 6379:6379 \
redis:6.2.7 redis-server /etc/redis/redis.conf --appendonly yes

配置防火墙

1
2
3
firewall-cmd --permanent --add-port=6379/tcp
firewall-cmd --reload
firewall-cmd --list-all

6. 安装 zookeeper

1
2
3
4
#-Xmx指定应用程能够使用的最大内存数
docker run -d --name zookeeper --restart always -e JVMFLAGS="-Xmx1024m" -p 2181:2181 zookeeper
docker exec -it zookeeper /bin/bash
/apache-zookeeper-3.6.2-bin/bin/zkCli.sh

https://hub.docker.com/r/wurstmeister/kafka/

7. 安装 kafka

提前安装并启动zookeeper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# KAFKA_BROKER_ID:kafka集群节点id(唯一)
# KAFKA_ZOOKEEPER_CONNECT:zookeeper连接地址
# KAFKA_ADVERTISED_LISTENERS:广播地址端口,即客户端访问的具体地址(会把这个地址端口注册给zookeeper,以备消费者获取)
# KAFKA_LISTENERS:外部代理地址和监听端口(限制访问ip以及服务端运行的端口)
# KAFKA_LOG_RETENTION_HOURS: 数据文件保留时间(非必选。缺省168小时,即7天)
# KAFKA_NUM_PARTITIONS:topic的分区数(非必选)
# KAFKA_DEFAULT_REPLICATION_FACTOR:分区的副本数(非必选)
docker run --name kafka \
-p 9092:9092 \
-v /opt/kafka:/kafka \
-v /etc/localtime:/etc/localtime:ro \
-e KAFKA_BROKER_ID=0 \
-e KAFKA_ZOOKEEPER_CONNECT=39.106.195.202:2181 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://39.106.195.202:9092 \
-e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 \
-d wurstmeister/kafka

8. 安装 Linux 系统

安装 centos 容器

简单用法
1
2
3
4
5
6
7
8
#运行容器
#-d 这个参数表示的是在后台运行,即 –deamon。
#-i 表示 interactive 可交互的,让容器的标准输入(stdin)保持打开
#-t 表示给容器分配一个伪终端。
#注意:运行容器时注意加-it参数,否则运行失败
docker run -itd --name centos7-test centos:centos7
#进入容器内部
docker exec -it centos7-test bash
使用 ssh 服务并支持简体中文
容器内部修改的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
##创建容器
#无服务初始化方式系统(可以不加 --privileged)
#保持后台运行通过supervisor、runit、dumb-init和s6-overlay等解决,而不是用Systemd
docker run -itd --name centos7-test centos:centos7
#host网络模式运行容器:
#docker run -itd --restart always --name centos7-test --net=host centos:centos7
#bridge 网络模式运行容器并映射端口:
#docker run -itd --restart always --name centos7-test -p 53371:22 -p 53372-53379:53372-53379 centos:centos7

#使用 Systemd 初始化系统方式管理服务(可选)
#使用 Systemd 管理服需要同时加 --privileged 和 /usr/sbin/init,否则报错:Failed to get D-Bus connection: Operation not permitted
docker run --privileged -itd --name centos7-test centos:centos7 /usr/sbin/init
#docker run --privileged -itd --restart always --name centos7-test -p 53371:22 -p 53372-53379:53372-53379 centos:centos7 /usr/sbin/init

docker exec -it centos7-test bash
#安装常用命令
yum install telnet net-tools curl wget vim -y
#安装openssh
yum install openssh openssh-clients openssh-server -y
#修改 sshd_config 配置文件
#找到 PermitRootLogin 并将其值修改为 yes,表示允许 root 管理员登录系统。将 PasswordAuthentication 的值改为 yes 表示使用帐号密码方式登录系统。
#注意:如果使用 host 网络模式创建容器,则需要将容器 ssh 程序的端口改为其他宿主机未被占用的端口,否则端口冲突导致 ssh 程序无法运行
vim /etc/ssh/sshd_config
#给容器的root用户设置密码
passwd root

##如果是无服务方式初始化系统,则如下操作:
ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key
/usr/sbin/sshd #每次重启容器时需要手动执行这条命令

##如果是使用 Systemd 初始化系统方式管理服务(可选),则如下操作:
#systemctl 相关命令:systemctl restart|stop|start|status sshd
#service 相关命令:service ssh restart|stop|start|status
#启动服务(通常安装后默认都已启动)
systemctl start sshd
#service sshd start #如果命令不存在,可安装相关依赖:yum install -y initscripts
#重启服务
systemctl restart sshd
#或:service sshd restart
#开机启动
systemctl enable sshd
#或:chkconfig sshd on


#通过ifconfig查看docker容器的ip,然后在宿主机上进行ssh登录。如果要在宿主机外的其他机器登录,则需要docker run时映射端口到容器的22端口,如-p 2222:22
ssh [email protected]


#安装简体中文语言包并修改配置以支持简体中文(CentOS官方的Docker镜像默认不支持简体中文)
#查看系统支持的语言:locale -a
#查看当前使用的语言包:locale
#参考:https://blog.51cto.com/u_8877512/2483145
yum install -y kde-l10n-Chinese glibc-common
#修改配置支持简体中文
#在 /etc/bashrc 文件末尾配置 export LC_ALL=en_US.UTF-8
#下面写那么复复杂主要是防止执行多次重复添加
#注意 en_US.UTF-8 只是支持解析简体中文,而如果希望将系统信息都使用特定地区的日期、时间格式或货币格式,则可以将 en_US.UTF-8 改为 zh_CN.UTF-8 ,而如果不需要这些与地区相关的设置,那么 en_US.UTF-8 就足够了。
#虽然按道理也可以改 /etc/locale.conf 配置文件实现,但实测仅改这个文件无法解决 ping 命令含中文字符的域名时会报错的问题
text_str='export LC_ALL="en_US.UTF-8"' ;\
file_str='/etc/bashrc' ;\
grep -v '^#' "$file_str" | grep -q "$text_str" || echo "$text_str" >> "$file_str"

#重载配置
source /etc/bashrc
#退出容器并重新进入即可生效
exit
docker exec -it centos7-test bash
编写 Dockerfile 封装镜像的方式

参考:

高特权用法
1
2
3
4
5
6
#创建具有更高特权容器
#--privileged(或 --privileged=true):表示开启特权
#使用该选项和参数,带有 --privileged 时,这个容器内的进程实质上就像直接运行在主机上一样,它会拥更高的权限,包括直接访问宿主机的硬件。
#否则执行高权限,如修改内核参数(只是可以修改内核参数而不是修改内核,如果修改宿主机的内核参数也会受到影响)、创建文件系统、创建分区、挂载分区、更改网络配置等时会报错(相关命令包括:如:sysctl、fdisk、mount 等)
##注意在使用官方的 centos 镜像创建容器使用 --privileged 时需要加在命令最后加 /usr/sbin/init,表示使用 systemd 作为第一启动程序,不加会报错。
docker run --privileged -itd --name centos7-test centos:centos7 /usr/sbin/init

安装 ubuntu 容器

简单用法
1
2
3
4
5
6
7
#运行容器(运行容器时注意加-it参数,否则运行失败)
#-d 这个参数表示的是在后台运行,即 –deamon。
#-i 表示 interactive 可交互的,以便可以从标准输入与容器交互。
#-t 表示给容器分配一个虚拟终端。
docker run -itd --name ubuntu-test ubuntu:20.04
#进入容器内部
docker exec -it ubuntu-test bash
使用 ssh 服务并支持简体中文
容器内部修改的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#运行容器(运行容器时注意加-it参数,否则运行失败)
#-d 这个参数表示的是在后台运行,即 –deamon。
#-i 表示 interactive 可交互的,以便可以从标准输入与容器交互。
#-t 表示给容器分配一个虚拟终端。
docker run -itd --name ubuntu-test ubuntu:20.04
#docker run -itd --restart always --name ubuntu-test -p 53371:22 -p 53372-53379:53372-53379 ubuntu:20.04
#进入容器内部
docker exec -it ubuntu-test bash
#更新软件源,这个步骤优先级最高,因为不更新很多组件安装不了
apt update
#安装常用工具
apt install -y telnet iputils-ping net-tools curl wget vim


#安装openssh
apt install -y openssh-server
#修改 sshd_config 配置文件
#找到 PermitRootLogin 并将其值修改为 yes,表示允许 root 管理员登录系统。将 PasswordAuthentication 的值改为 yes 表示使用帐号密码方式登录系统。
#注意:如果使用 host 网络模式创建容器,则需要将容器 ssh 程序的端口改为其他宿主机未被占用的端口,否则端口冲突导致 ssh 程序无法运行
vim /etc/ssh/sshd_config
#给容器的root用户设置密码
passwd root

#启动ssh
/usr/sbin/ssh

##服务管理方式(按道理以上方式在这里无效,但在Ubuntu中service命令启动和停止可以管理ssh进程运行,具体可看/etc/init.d/ssh脚本逻辑)
#service 相关命令:service ssh restart|stop|start|status
#启动服务(通常安装后默认都已启动)
#service ssh start
#重启服务
#service ssh restart
#启用开机自启动
#update-rc.d ssh defaults
#禁止开机自启动(如需)
#update-rc.d -f ssh remove
#通过ifconfig查看docker容器的ip,然后在宿主机上进行ssh登录。如果要在宿主机外的其他机器登录,则需要docker run时映射端口到容器的22端口,如-p 2222:22
ssh [email protected]


#安装简体中文语言包并修改配置以支持简体中文(Ubuntu官方的Docker镜像默认不支持简体中文)
#查看系统支持的语言:locale -a
#查看当前使用的语言包:locale
apt install -y language-pack-zh-hans
#修改配置支持简体中文
#在 /etc/bash.bashrc 文件末尾配置 export LC_ALL="C.UTF-8"
#下面写那么复复杂主要是防止执行多次重复添加
#注意 C.UTF-8 只是支持解析简体中文,而如果希望将系统信息都使用特定地区的日期、时间格式或货币格式,则可以将 C.UTF-8 改为 zh_CN.UTF-8 ,而如果不需要这些与地区相关的设置,那么 C.UTF-8 就足够了。
text_str='export LC_ALL="C.UTF-8"' ;\
file_str='/etc/bash.bashrc' ;\
grep -v '^#' "$file_str" | grep -q "$text_str" || echo "$text_str" >> "$file_str"

#重载配置
source /etc/bash.bashrc
#退出容器并重新进入即可生效
exit
docker exec -it ubuntu-test bash
编写 Dockerfile 封装镜像的方式

说明:由于官方的 Ubuntu 镜像没有内置 systemd 相关依赖包,所以默认无法使用 systemd 来管理 ssh 服务,但可以使用默认的 SysVinit 启动,简单来说就是使用 service 命令来管理 ssh 服务器(如:service ssh start)。

当然如果非要用 systemd 来管理 ssh 服务,官方的 Ubuntu 官方的镜像来创建容器并使之支持,操作过程会比较麻烦,如下:

1
2
3
4
5
>docker run -itd --name ubuntu-test ubuntu:20.04
>docker exec -it ubuntu-test bash
>apt update
>apt install -y systemd #安装失败,可以修改 apt 的软件源为国内源再重试
>ln -sf /lib/systemd/systemd /usr/sbin/init

但此时容器已经创建,没法再指定 /usr/sbin/init 为第一启动程序了,当然可以通过提交容器为新镜像的方式来重新运行,并在 docker run 命令最后加上 /usr/sbin/init,表示使用 systemd 作为第一启动程序,然后就能使用 systemd 了,但很明显这一系列操作太麻烦,所以不推荐。更推荐编写 Dockerfile 封装镜像的方式,参考:

高特权用法
1
2
3
4
5
#创建具有更高特权容器
#--privileged(或 --privileged=true):表示开启特权
#使用该选项和参数,带有 --privileged 时,这个容器内的进程实质上就像直接运行在主机上一样,它会拥更高的权限,包括直接访问宿主机的硬件。
#否则执行高权限,如修改内核参数(只是可以修改内核参数而不是修改内核,如果修改宿主机的内核参数也会受到影响)、创建文件系统、创建分区、挂载分区、更改网络配置等时会报错(相关命令包括:如:sysctl、fdisk、mount 等)
docker run --privileged -itd --name ubuntu-test ubuntu:20.04

更多 Linux 系统参考

十二、问题排错

(1)出现 Error response from daemon: oci runtime error: container_linux.go:247: start.... 报错提示。

原因是系统和docker版本不兼容。解决办法:执行yum update

(2)rying to pull repository docker.io/library/mysql ... Get https://registry-1.

解决办法:执行以下命令:

1
2
3
4
$ yum install bind-utils     #安装dig工具
$ dig @114.114.114.114 registry-1.docker.io
$ vi /etc/hosts
52.54.155.177 registry-1.docker.io

(3)[root@izuf6dskn3b7v2sly08bqtz ~]# docker pull mysql Using default tag: latest Trying to pull repository docker.io/library/mysql ... Get https://registry-1.docker.io/v2/: x509: certificate is valid for *.theranest.com, theranest.com, not registry-1.docker.io

解决办法:镜像加速解决

(3)执行docker-compose up -d后出现错误:ERROR: Failed to Setup IP tables: Unable to enable SKIP DNAT rule: (iptables failed: iptables --wait -t nat -I DOCKER -i br-950144d461c2 -j RETURN: iptables: No chain/target/match by that name. (exit status 1))

解决办法:重启docker试试

1
2
systemctl stop docker
systemctl start docker

(4)service network restart 重启网络后导致 docker 容器端口无法访问

解决办法:重启 Docker 服务并重启容器

1
2
systemctl restart docker
docker restart 容器名称/容器id

十三、参考

📚《Docker — 从入门到实践》



----------- 本文结束 -----------




如果你觉得我的文章对你有帮助,你可以打赏我哦~
0%