# 一、Docker基础
# 1.1 Docker介绍
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。
由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
Docker的沙盒机制,每一个运行的docker容器都有其独立的运行环境及网络;从文件系统、网络互联到进程隔离。
# 1.2 Docker特点
更高效的利用系统资源
由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。
更快速的启动时间
传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。
持续交付和部署
对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。
而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。
更轻松的迁移
由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。
更轻松的维护和扩展
Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。
# 1.3 Docker架构
标题 | 说明 |
---|---|
镜像(Images) | Docker 镜像是用于创建 Docker 容器的模板。 |
容器(Container) | 容器是独立运行的一个或一组应用。 |
客户端(Client) | Docker 客户端通过命令行或者其他工具使用 Docker API (https://docs.docker.com/reference/api/docker_remote_api (opens new window)) 与 Docker 的守护进程通信。 |
主机(Host) | 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。 |
仓库(Registry) | Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com (opens new window)) 提供了庞大的镜像集合供使用。 |
Docker Machine | Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。 |
# 二、Docker安装
# 2.1 安装Docker(Ubuntu)
# 下载Docker
sudo apt-get update
# 安装必要的一些系统工具
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# 安装 GPG 证书
sudo curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# 写入软件源信息
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# 更新并安装 Docker CE
sudo apt-get -y update
sudo apt-get -y install docker-ce
2
3
4
5
6
7
8
9
10
以上命令会添加稳定版本的 Docker CE APT 镜像源,如果需要最新或者测试版本的 Docker CE 请将 stable 改为 edge 或者 test。从 Docker 17.06 开始,edge test 版本的 APT 镜像源也会包含稳定版本的 Docker。
# 启动 Docker CE
sudo systemctl enable docker
sudo systemctl start docker
sudo docker version
2
3
4
5
# 建立 docker 用户组
默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。
而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket。
出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。
因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。
建立 docker 组:
sudo groupadd docker
将当前用户加入 docker 组:
sudo usermod -aG docker $USER
退出当前终端并重新登录,进行如下测试。
sudo docker info
# Docker 镜像加速器
国内从 Docker Hub 拉取镜像有时会遇到困难,此时可以配置镜像加速器。
Docker 官方和国内很多云服务商都提供了国内加速器服务,例如:
编辑文件:
sudo vi /etc/docker/daemon.json
调整如下:
{
"registry-mirrors": [
"http://mirrors.aliyun.com"
]
}
2
3
4
5
注意,一定要保证该文件符合 json 规范,否则 Docker 将不能启动。
补充:设置自己的阿里云镜像加速器
{
"registry-mirrors": [
"https://zy2rj443.mirror.aliyuncs.com"
]
}
2
3
4
5
zy2rj443为阿里云为每个人分配的镜像加速器。
获取步骤:
1、在阿里云搜索 容器镜像服务;
2、点击镜像中心——镜像加速器;
重启docker服务
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo docker info
2
3
# 2.2 安装docker(CentOS7)
使用【uname -a】命令确定你是CentOS7及以上版本,输出如下:
Linux SERVER-235 3.10.0-957.el7.x86_64 #1 SMP Thu Nov 8 23:39:32 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
yum安装gcc相关
yum -y install gcc
yum -y install gcc-c++
2
安装需要的软件包
yum install -y yum-utils
设置stable镜像仓库
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
更新yum 软件包索引
yum makecache fast
安装DOCKER CE
yum install docker-ce docker-ce-cli containerd.io
启动docker,没有报错及成功
systemctl start docker
systemctl enable docker
docker version
2
3
# 安装docker-compose
二进制包安装
在 Linux 上的也安装十分简单,从 官方 GitHub Release 处直接下载编译好的二进制文件即可。
sudo curl -L https://github.com/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# 赋予可执行权限
sudo chmod +x /usr/local/bin/docker-compose
2
3
若使用非root账户,执行如下命令
sudo touch /usr/local/bin/docker-compose
# 赋予最大权限
sudo chmod 777 /usr/local/bin/docker-compose
sudo curl -L https://github.com/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
2
3
4
5
查看版本
sudo docker-compose version
卸载
如果是二进制包方式安装的,删除二进制文件即可。
sudo rm /usr/local/bin/docker-compose
# 三、Docker命令
# 3.1 Docker基础命令
启动docker
systemctl start docker
关闭docker
systemctl stop docker
重启docker
systemctl restart docker
docker设置随服务启动而自启动
systemctl enable docker
查看docker 运行状态
------如果是在运行中 输入命令后 会看到绿色的active
systemctl status docker
查看docker 版本号信息
docker version
docker info
2
3
# 3.2 Docker镜像命令
查看自己服务器中docker 镜像列表
docker images
搜索镜像
docker search 镜像名
docker search --filter=STARS=9000 mysql 搜索 STARS >9000的 mysql 镜像
2
拉取镜像
不加tag(版本号) 即拉取docker仓库中 该镜像的最新版本latest 加:tag 则是拉取指定版本
docker pull 镜像名
docker pull 镜像名:tag
2
删除镜像
------当前镜像没有被任何容器使用才可以删除
#删除一个
docker rmi -f 镜像名/镜像ID
#删除多个 其镜像ID或镜像用用空格隔开即可
docker rmi -f 镜像名/镜像ID 镜像名/镜像ID 镜像名/镜像ID
#删除全部镜像 -a 意思为显示全部, -q 意思为只显示ID
docker rmi -f $(docker images -aq)
#强制删除镜像
docker image rm 镜像名称/镜像ID
2
3
4
5
6
7
8
9
10
11
# 3.3 Docker清理命令
- 清理虚悬镜像image
docker image prune
- 清理 数据卷Volumes数据
# 查看Docker磁盘空间
docker system df
# 批量删除虚悬volume
docker volume rm $(docker volume ls -q)
# 查看所有volume
docker volume ls
2
3
4
5
6
# 3.4 Docker容器命令
查看正在运行容器列表
docker ps
查看所有容器
-----包含正在运行 和已停止的
docker ps -a
-- 过滤查询
docker ps --filter name=ccc
运行一个容器
# -it 表示 与容器进行交互式启动 -d 表示可后台运行容器 (守护式运行) --name 给要运行的容器 起的名字 /bin/bash 交互路径
docker run -it -d --name 要取的别名 镜像名:Tag /bin/bash
2
启动容器
docker start 容器ID/容器名
停止容器
# 先停止咱之前运行的 redis 容器
docker stop 容器名/容器ID
2
重启容器
docker restart 容器ID/容器名
删除容器
#删除一个容器
docker rm -f 容器名/容器ID
#删除多个容器 空格隔开要删除的容器名或容器ID
docker rm -f 容器名/容器ID 容器名/容器ID 容器名/容器ID
#删除全部容器
docker rm -f $(docker ps -aq)
2
3
4
5
6
7
8
进入容器方式
docker exec -it 容器名/容器ID /bin/bash
查看容器日志
docker logs -f --tail=要查看末尾多少行 默认all 容器ID
# 四、Docker网络
Docker的网络模块是可插拔式的,提供了3种网络模式可以选择。
- host模式
- container模式
- none模式
- bridge模式
生成容器时不指定网络模式下,默认使用bridge桥接模式。
通过docker network ls
这个命令来查看本机中所有的网络模式。
# 4.1 container模式
模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。
新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。
# 4.2 none模式
使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。
也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。这种网络模式下容器只有lo回环网络,没有其他网卡。
none模式可以在容器创建时通过–network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
# 4.3 Host模式
Docker中的host模式指定是容器与主机享受相同的network namespace。
默认Docker容器运行会分配独立的Network Namespace隔离子系统,而基于host模式,容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace,容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。(用的是宿主机的IP,也就是和宿主机共用一个IP地址,host模式不需要加-p进行端口映射,因为和宿主机共享网络IP和端口)
在这种情况下,我们访问主机端口就能访问我们的容器。比如说我们运行tomcat容器并且用-- network=host
来指定我们的网络模式为host,这样我们访问本机的8080端口就能访问到我们的tomcat容器。
# 4.4 Bridge模式
bridge模式是docker的默认网络模式,不写–net参数,就是bridge模式。
当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器都会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
# 五、Docker部署
# 六、Docker FAQ
# 6.1 Docker配置网络为host模式
问题描述:
在Linux上使用host模式遇到了在宿主机上能访问,但是通过其他机器无法访问,telnet也不通。
原因:
主要是因为host模式使用宿主机网络,因此需要在指定区域中主动开放防火墙端口;
而使用bridge、overlay模式时,这些接口默认都添加到firewalld的docker(ACCEPT)区域中。
解决方案:
第一种:关闭防火墙;
#在开机时禁用
systemctl disable firewalld.service
#停止
systemctl stop firewalld
2
3
4
第二种:若防火墙不能关闭,则开发端口;
例:CentOS7
# 开放5672端口
firewall-cmd --zone=public --add-port=5672/tcp --permanent
#更新防火墙规则
firewall-cmd --reload
#查看防火墙所有开放的端口
firewall-cmd --zone=public --list-ports
2
3
4
5
6
# 6.2 Docker部署时间差8小时问题
# 6.2.1 前置-时区
UTC Coordinated Universal Time 世界统一时间,世界标准时间,协调世界时
GMT Greenwish Mean Time 格林威治平时,
CTT China Shanghai Time 东八区时间,上海时间,北京时间
CST China Standard Time 中国沿海时间(北京时间)
UTC
是标准时间,是协调世界时;
GMT
是UTC
的民间名称。GMT=UTC
。
中国在东八区,用CTT
标识,也就是UTC+8
。CTT=UTC+8=GMT+8
。
linux查看时间命令
命令 | 说明 |
---|---|
date | 查看当前日期时间 |
date -R | 查看时区时间 |
date -u | 查看UTC时间 |
# date
Sat Dec 3 20:00:08 CST 2022
# date -R
Sat, 03 Dec 2022 20:00:08 +0800
# date -u
Sat Dec 3 12:00:08 UTC 2022
2
3
4
5
6
CST
标识中国北京时间UTC
标识世界标准时间
java查看ZoneId
在java.time.ZoneId
类中,配置了全球各区域的时间配置。
map.put("CTT", "Asia/Shanghai");
# 6.2.2 问题描述
在docker
启动容器,运行jar
程序,总会遇见各种各样的8小时时差问题(时区问题)。
- 容器时差:启动docker容器与挂载主机时间相差8小时;
- java程序运行时差:
- logback日志输出时差:
- mysql插入时差:使用LocalDateTime插入到数据库,时间少了8小时;
- 前后端交互序列化时差:前端、后端传递时间,相差8小时;
# 6.2.3 解决Docker主机与容器时差
容器时间与主机差8个小时,是因为主机的与容器的/etc/localtime
不一致。
解决方案:在构建镜像时,指定时间。
将主机的Shanghai
时区内容,复制到容器的/etc/localtime
文件。
FROM openjdk:8-jre
# 时间
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 挂载数据卷
。。。
2
3
4
5
效果:保证主机的时间与容器时间一致。如下表示配置成功,时间已保持一致。
主机:
# 在主机执行,查看时间date
root@k-docker:~# date
Sat Dec 3 17:48:41 CST 2022
2
3
容器内部:
# 在主机执行,进入容器内部
docker exec -it 9262e49aaf0c /bin/bash
# 在容器内部执行,查看时间date
root@k-docker:/# date
Sat Dec 3 17:47:36 CST 2022
2
3
4
5
6
CST
标识中国北京时间
# 6.2.4 解决java程序运行时差
解决方案一:SpringBoot
项目在启动类中配置时区。
public static void main(String[] args) {
// 启动类中,配置时区
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
SpringApplication.run(InspectionApplication.class, args);
}
2
3
4
5
解决方案二:通过jvm
参数配置时区。
-Duser.timezone=GMT+08
解决方案三:在启动容器时挂载时区。
因为Java获取时间是从/etc/timezone
里获取时区,所以将该文件挂载至容器内部,替换为上海时区。
若是通过docker-compose进行编排容器,则在
docker-compose.yml
中进行配置volumes
。version: '3.3' services: my-server: restart: unless-stopped image: my-server:1.1.0 container_name: my-server network_mode: host volumes: - ./logs:/logs:rw - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro
1
2
3
4
5
6
7
8
9
10
11若是通告docker run启动容器,则在启动命令中添加:
docker run -d -p 80:80 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone nginx
1说明:
如果挂载主机没有
/etc/timezone
文件,则新建:echo "Asia/shanghai" > /etc/timezone;
1
效果:保证主机的timezone
与容器的timezone
一致。
如下表示未配置,目前时区不一致。主机在上海时区,而容器内部是在
主机:
root@k-docker:~# cat /etc/timezone
Asia/Shanghai
2
容器:
root@k-docker:/# cat /etc/timezone
Etc/UTC
2
Asia/Shanghai
中国时区Etc/UTC
协调世界时
# 6.2.5 解决logback日志输出时差
解决方案一:在pattern
中的%d
日期配置中,添加CTT
,指定时区为东八区。
<appender name="logFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<!-- %p日志级别,%t线程名,%d日期,%c类的全名,%方法名,%L行号,%m输出的信息 -->
<pattern>
[%d{yyyy-MM-dd HH:mm:ss.SSS,CTT}] %-5level [%t] %class{36}.%M - %L - %msg%xEx%n
</pattern>
<!-- 记录日志的编码 -->
<charset>UTF-8</charset>
</encoder>
</appender>
2
3
4
5
6
7
8
9
10
# 6.2.6 解决mysql插入时差
解决方案一:在jdbc连接配置
url: jdbc:mysql://127.0.0.1:3306/database_test?serverTimezone=Asia/Shanghai
url: jdbc:mysql://127.0.0.1:3306/database_test?serverTimezone=CTT
2
3
# 6.2.7 前后端交互序列化时差
解决方案一:使用jackson
进行序列化时,如下统一配置时区。
@Bean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
/*
* 设置为东八区的时区。此处配置了,@JsonFormat注解则可省略时区配置;
*/
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
}
2
3
4
5
6
7
8
解决方案二:使用@JsonFormat
注解指定配置时区。
import com.fasterxml.jackson.annotation.JsonFormat;
public class Bean {
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date time;
}
2
3
4
5
6