镜像结构
镜像是分层结构,每一层称为一个Layer。
- BaseImage层:包含基本的系统函数库、环境变量、文件系统
- Entrypoint:入口,是镜像中应用启动的命令
- 其它层:在BaseImage基础上添加依赖、安装程序等,完成整个应用的安装和配置
以mysql5.7为例
Dockerfile
注意几点
- Dockerfile的本质是一个文件,通过指令描述镜像的构建过程
- Dockerfile的第一行必须是FROM,从一个基础镜像来构建
- 基础镜像可以是基本操作系统,如Ubuntu。也可以是其他人制作好的镜像,如java:8-alpine
测试下,创建一个web项目,写一个测试接口
@RestController
public class TestController {
@GetMapping("now")
public String test(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
打成jar包,上传服务器,我这里是放在/data/docker/java目录下
同目录下创建Dockerfile
# 指定基础镜像 这个镜像是基于linux系统构建的一个java镜像 帮我们省去了jdk安装配置的步骤
FROM java:8-alpine
# 将jar包拷贝到容器内
COPY ./docker-web.jar /tmp/app.jar
# 暴露端口 这里只是描述作用,为了方便查看暴露的端口 实际不会影响和改变项目里配置的端口
EXPOSE 8080
# 入口 java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
现在,构建镜像的必要条件已经完成
生成自定义镜像,docker build是构建命令,-t表示标签,myweb:1.0代表镜像名称,.代表Dockerfile的路径,我们放在了同一目录
docker build -t myweb:1.0 .
来看一下,可以看到,成功生成了一个镜像
接下来运行一下
docker run -d -p 8890:8080 testweb:1.0
容器运行成功
访问测试下
Docker网络
Docker容器是一个具有隔离性的虚拟系统,容器内可以有自己独立的网络空间,容器与容器之间相互隔离,通过Docker网络进行相互通信。
了解docker0
我们先查看下宿主机网卡
ip addr # 查看所有网卡
这里有3个网络
lo: 127.0.0.1 #本地回环接口,主要用于测试本机的网络功能。
eth0: 172.17.0.11 #以太网接口,也就是有线网卡。可以看成宿主机的内网ip
docker0: 172.18.0.1 #docker虚拟网桥 它是Docker容器的虚拟网络接口,用于连接容器和主机之间的网络。
docker0在我们安装docker的时候就会创建。docker0用来和宿主机之间通信。
Docker 是如何处理容器网络访问的?我们启动一个tomcat来看一下
进入容器,查看容器网卡信息
docker exec -it mt bash #进入容器
apt update
apt install -y iproute2 # 安装 ip addr
apt install -y iputils-ping # 安装 ping
ip addr # 查看容器网卡信息
退出容器,宿主机上可以ping通172.18.0.2
再次查看宿主机网卡信息,发现多出一个217,刚好和 mt 容器网卡216 相对应。
同样方式再启动一个tomcat容器mt2,端口8082,查看mt2容器网卡信息(进入容器提前安装iproute2、iputils-ping)
宿主机上可以ping通172.18.0.3
再次查看宿主机网卡
可以看到,我们每启动一个容器,就会多出一对网卡,同时他们被连接到docker0上,而docker0又和宿主机之间连通。
测试mt和mt2容器是否可以ping通
docker exec -it mt ping 172.18.0.2
可以看到,容器和容器之间是可以相互 ping 通的
在这里,我们可以认为docker0相当于一个路由器的作用,任何一个容器启动默认都是docker0网络。
docker默认会给容器分配一个可用ip,并把它同docke0相连。使用到的就是veth pair技术。
容器互联-link
现在我们考虑一个问题,如果需要部署一个微服务,是直接使用172.18.0.2、172.18.0.3这种ip做容器之间的连接访问吗?这样是不合理的,因为容器使用的是虚拟ip,每次更新启动ip都会变化。
那么是否可以通过容器名进行访问呢,我们测试下容器名之间是否能ping通
docker exec -it mt ping mt2
答案是不行的
我们再启动一个tomcat容器,并加上link参数,使mt3连接到mt2
docker run -it -d -p 8083:8080 --name mt3 --link mt2 tomcat
测试容器mt2和mt3是否能ping通名称
可以看到,mt3可以ping通mt2,反之不行。因为link是单向的。
docker exec -it mt3 cat /etc/hosts
查看容器mt3网卡信息也可以看到,--link 就是在hosts配置中增加ip与容器名的绑定。
不过我们现在不建议使用 --link了,推荐使用自定义网络。
自定义网络
docker0的特点:
-
它是默认的
-
域名访问不通
-
–link 域名通了,但是删了又不行
docker为我们提供了三种网络模式,使用如下命令可以查看
这其中默认使用的是 bridge,也就是我们的docker0网卡。
网络模式 | 配置 | 说明 |
bridge模式 | --net=bridge | 默认值,在Docker网桥docker0上为容器创建新的网络栈 |
none模式 | --net=none | 不配置网络,用户可以稍后进入容器,自行配置 |
container模式 | --net=container:name/id | 容器和另一个容器共享network namespace。 |
host模式 | --net=host | 容器和宿主机共享network namespace。 |
用户自定义 | --net=自定义网络 | 用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定义的网络 |
上面我们创建容器mt时,其实默认加了--net bridge
docker run -it -d -p 8081:8080 --name mt --net bridge tomcat
现在我们自定义一个docker网络
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
命令说明
- docker network create:docker网络创建命令
- --driver bridge:指定网络模式为bridge
- --subnet 192.168.0.0/16:指定子网网段
- --gateway 192.168.0.1:指定网关ip
- mynet:网络名称
查看docker网络
查看详情
下面我们使用自定义的网络启动 tomcat
docker run -d -p 8091:8080 --name tomcat-net-01 --net mynet tomcat
docker run -d -p 8092:8080 --name tomcat-net-02 --net mynet tomcat
查看容器
再次查看mynet网络,可以看到tomcat-net-01和tomcat-net-02
我们再来ping测试下,通过ip和容器名都可以访问。
网络连通
docker0和自定义网络肯定不通,我们使用自定义网络的好处就是网络隔离。
但是在实际的工作中,比如我们部署了mysql使用了一个网段。部署了tomcat使用了另一个网段,两个网段之间肯定是不能相互连通的,但是tomcat和mysql又需要相互连通,我们就要使用网络连通。原理图如下:
网络连通就是将一个容器和一个网段之间的连通。
比如我前面使用的默认docker0的tm,需要连接到mynet网络。
docker network connect [OPTIONS] NETWORK CONTAINER
测试下
查看mynet网络,可以看到容器mt也加入到了mynet网络,这样就实现了网络的连通。(现在容器mt在docker0和mynet两个网络中都能访问到)
总结:
-
veth pair是成对出现的一种虚拟网络设备接口,一端连着网络协议栈,一端彼此相连。
-
docker中默认使用docker0网络。
-
docker0相当于一个路由器的作用,任何一个容器启动默认都是docker0网络。
-
docker0是容器和虚拟机之间通信的桥梁。
-
推荐使用自定义网络,更好实现使用服务名的连通方式,避免ip改变的尴尬。
-
网络之间不能直接连通,网络连通是将一个容器和一个网络之间的连通,实现跨网络操作。
Docker Compose
什么是DockerCompose
- Docker Compos可以基础Compose文件帮我们快速的部署分布式应用,而无需一个个创建和运行容器
- Compose文件是一个文本文件,通过指令定义及集群中的每个容器如何运行
下面是一个简单的Compose文件的结构示例
更多用法可以参考官网学习
Docker Compose的安装很简单,只要在线下载或直接上传本地下载好的的docker-compose文件,放到/usr/local/bin目录下,然后修改文件权限,给docker-compose添加可执行权限。
Docker Compose的作用主要是帮助我们快速部署分布式应用,无需一个个微服务去构建镜像和部署。
实战部署下之前的cloud-demo微服务项目
先看下我们的项目结构,我们需要部署的微服务是user-service、order-service、gateway三个微服务(mysql的话我是直接使用宿主机服务器的mysql,大家也可以使用mysql镜像)
因为我们需要使用docker部署,所以不能再使用localhost了。
修改gateway、order-service、user-service中的nacos的地址,localhost改成容器服务名nacos
修改mysql的配置,改成线上地址、账号密码
pom.xml打包名改成app
<build>
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
执行maven打包
创建一个cloud-demo-docker文件夹,用于存在需要的配置文件及jar包等,目录结构如下
准备一个Dockerfile文件,内容如下
#基于java8镜像构建
FROM java:8-alpine
#拷贝微服务jar包到容器内
COPY ./app.jar /tmp/app.jar
分别复制到user-service,order-service,gateway文件夹下。并且3个微服务app.jar放到对应文件夹下。
编写docker-compose.yml
version: "3.2"
services:
nacos:
image: nacos/nacos-server
environment:
MODE: standalone
ports:
- "8848:8848"
userservice:
build: ./user-service
orderservice:
build: ./order-service
gateway:
build: ./gateway
ports:
- "10010:10010"
上传服务器,我是放在/data目录下,复制本地cloud_user和cloud_order数据到服务器上的mysql
部署
docker-compose up -d
等待打包完成,查看镜像和容器已成功创建,并且docker-compose会默认创建一个网络
查看日志
docker-compose logs -f
访问测试
镜像仓库
镜像仓库(Docker Registry)有公有的和私有的两种形式:
- 公有仓库:例如Docker官方的Docker Hub,国内也有一些云服务商提供类似于Docker Hub的公开服务,比如网易云镜像服务、DaoCloud镜像服务、阿里云镜像服务等。
- 除了使用公开仓库外,用户还可以在本地搭建私有Docker Registry。企业自己的镜像最好是采用私有Docker Registry来实现。
部署Docker Registry
version: '3.0'
services:
registry:
image: registry
volumes:
- ./registry-data:/var/lib/registry
ui:
image: joxit/docker-registry-ui:static
ports:
- 8888:80
environment:
- REGISTRY_TITLE=lsy的私有仓库
- REGISTRY_URL=http://registry:5000
depends_on:
- registry
另外,我们服务器一般采用的是http协议,默认不被Docker信任,所以需要做一个配置
# 打开要修改的文件
vi /etc/docker/daemon.json
# 添加内容 这里是服务器的ip+docker compose里配置的ui界面访问端口
"insecure-registries":["http://111.229.191.140:8888"]
# 重新加载
systemctl daemon-reload
# 重启docker
systemctl restart docker
将上面的docker-compose配置文件传到服务器上,我是放在/data/registry-ui
执行构建
构建完成,打开浏览器测试下
目前没有镜像。接下来,我们试着推送一个镜像到私有仓库
先查看下本地镜像
首先,我们需要重新tag本地镜像,名称前缀为私有仓库的地址:111.229.191.140:8888,以gateway镜像为例
docker tag cloud-demo-docker_gateway:latest 111.229.191.140:8888/gateway:1.0
可以看到,多了一个本地镜像,并且两个gateway的image id其实是一样的,相当于一个镜像副本
推送镜像
docker push 111.229.191.140:8888/gateway:1.0
可以看到,push成功。(PS:真慢,大家可以推送以个小一点的镜像)
去私有仓库刷新查看,可以看到,多了一个gateway镜像
点击还能查看镜像详情
拉取镜像
docker pull 111.229.191.140:8888/gateway:1.0
为了测试效果,先删除本地的111.229.191.140:8888/gateway:1.0镜像,再拉取
可以看到,拉取成功。(PS:这里因为本地已经有cloud-demo-docker_gateway镜像的文件层,所以拉取很快。)