Docker
官网
Docker的安装
去到官方文档https://docs.docker.com/get-docker/,选择对应的操作系统安装就可以了。
我的环境是CentOS 7.8
1 | # 下载需要的安装包 |
出现了docker版本的信息就代表安装成功了。
HelloWrold
1 | #创建hello-world容器 |
阿里云镜像加速
- 打开阿里云的官网。
- 打开容器镜像服务。
- 点击镜像中心下的镜像加速器。
- 选择版本就输入命令就可以配置好阿里云的镜像加速了。
这是阿里云的镜像加速,也可以找其他国内的源,不换源的话拉取镜像会很慢。
镜像
拉取镜像
1 | 拉取镜像 |
查看镜像
1 | 查看顶层镜像信息 |
查看镜像的历史
1 | docker history [容器名]:[TAG] |
搜索镜像
1 | 命令寻找镜像 |
删除镜像
1 | 通过镜像ID删除镜像 |
提交生成镜像
1 | 将自定义的容器提交成镜像 |
慎用docker commit。因为这样生成的黑箱镜像会对其他人的维护工作造成很大压力。生成镜像建议使用Dockerfile。
容器
新建并启动容器
1 | docker run [可选参数] image |
退出容器
1 | 通过命令来退出容器,在一些情况退出容器会使容器停止 |
查看运行的容器
1 | 查看运行的容器 |
进入容器
1 | 进入容器开启新终端,从中使用exit不会使容器停止 |
启动容器
1 | 启动容器 |
停止容器
1 | 停止容器 |
删除容器
1 | 删除指定ID的容器,不能删除正在运行的容器 |
查看日志
1 | 显示日志 |
查看容器信息
1 | 查看容器信息 |
拷贝容器内的文件到主机
1 | 将容器内的文件拷贝到主机的目录 |
清理所有处于终止状态的容器
1 | docker container prune |
部署Tomcat
拉取Tomcat的镜像
1
docker pull tomcat
启动容器
1
2
3
4启动容器
docker run -d --name tomcattest -p 5000:8080 tomcat
一次性的测试容器
docker run -it --rm tomcat打开公网ip的5000端口,或者服务器内部的localhost的5000端口就能看到页面了。因为下载的镜像是被处理过的,所以看不到index页面。
进入容器
1
2
3
4docker exec -it tomcattest /bin/bash
进入到webapps发现没有任何的网站信息,发现是被存储到了webapps.dist里了,所以要想看到index,就需要拷贝过来
cp -r ./webapps.dist/ROOT/ webapps
然后就能看见index页面了
数据卷
自定义路径挂载
1 | 将容器内目录挂载到本地目录上 |
匿名挂载
1 | 不指定本地目录的挂载 |
具名挂载
1 | 指定卷名不指定本地目录挂载 |
查看数据卷信息
1 | 查看当前数据卷的使用信息 |
删除数据卷
1 | docker volume rm [卷名] |
删除无主的数据卷
1 | docker volume prune |
DockerFile
一个简单的Dockerfile
建一个新的文件夹,然后在里面建一个Dockerfile文件,内容为
1
2FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html然后生成镜像
1
docker build -t nginx:v2 .
然后创建容器打开url,就能看到
Hello Docker!
了。
构建镜像
1 | docker build [选项] [镜像名]:[TAG] <上下文路径/URL/-> |
注意这个最后这个点,这个是表示指定当前目录为上下文路径,后面有些指令就是从上下文路径开始指引,不一定是Dockerfile的路径,为了方便才让Dockerfile文件与上下文路径一致。
指令
FROM
FROM用来指定基础的镜像,即用这个镜像来进行二次开发。
这个指令是必须的,而且必须是第一条。
Docker中有一个特殊的镜像,那就是
scratch
,表示一个空白的镜像。
MAINTAINER
这个指令用来表示作者的信息。
1 | MAINTAINER <username email> |
RUN
用来执行命令。
1 | # shell格式 |
当有多个命令的时候,尽量在一个RUN指令中完成,而不是使用多个RUN。因为一个指令就是建立一层,多层的结构就会显得十分的臃肿。
COPY
从构建上下文的目录中将一个文件复制到镜像内的目标路径当中。
1 | COPY [文件路径] [镜像内目标路径] |
如果源路径为文件夹,复制的时候不是直接复制该文件夹,而是将文件夹中的内容复制到目标路径。
ADD
ADD也是复制,与COPY差不多,但是能干更多的事情,比如复制的文件是tar压缩文件,会帮你自动解压到目标路径。
1 | ADD [文件路径] [镜像内目标路径] |
ADD有很多复杂的功能,所以只拷贝文件的时候尽量使用COPY,需要自动解压就用ADD。
WORKDIR
用来设置工作目录。
1 | WORKDIR <工作目录> |
工作目录是用来指定整个镜像的,因为每一层都是隔离的,所以不指定工作目录就会出现目录的混乱,即上一层使用了cd进入了新的目录,而下一层一不小心就当成是上一层的目录继续操作。
EXPOSE
暴露端口,类似之前的-p
参数,声明容器运行时容器提供服务端口。注意仅仅是声明,不会自动进行端口映射。
VOLUME
1 | VOLUME ["<路径1>", "<路径2>"...] |
这里的目录挂载之后就会在运行的时候自动挂载为匿名卷。
ENV
设置环境变量。
CMD
1 | # shell格式 |
ENTRYPOINT
ENTRYPOINT与CMD差不多,只不过在容器run后面加命令就会自动添加到ENTRYPOINT的参数去。
更多详细内容可以参考官网的Dockerfile reference。
发布镜像
Dockerhub
1 | 登陆dockerhub |
阿里云镜像
打开阿里云的容器镜像服务,创建命名空间和容器镜像,然后参考官方文档就能跟着发布了。
Docker网络
docker0
当Docker启动的时候就能看到docker会自主创建一个docker0
的网桥。
1 | ip a # 查看网络信息 |
docker0类似一个路由器,连接着各个容器。当创建一个容器的时候,也会创建一对的veth pair
接口,一个连接容器,一个连接docker0,这样就可以实现容器与docker0的通信和容器与容器的通信了(只限于ip)。这样Docker 就创建了在主机和所有容器之间一个虚拟共享网络。
因为docker0的存在,所以默认创建的容器之间是可以通过ip来通信的,即每个容器之间通过docker分配的ip是可以互相ping通,但是不能通过容器名来ping通,因为是没有解析的。
link
1 | 通过link可以简单的链接网络 |
自定义网络
1 | 之前创建容器的时候,默认是与docker0进行桥接模式,下面两个命令是一样的 |
1 | 自定义网络就修复了docker0的缺陷,容器能通过容器名来ping通 |
连通网络
1 | docker0网络下的容器通过容器名是不能Ping通另一个网络的容器的 |
DockerCompose
下载安装
1 | 官方文档下载 |
卸载
1 | 使用curl安装的 |
Docker Compose入门
创建一个新的目录用于DockerCompose
在目录中创建一个
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)创建一个
requirements.txt
1
2flask
redis建立
Dockerfile
1
2
3
4
5
6
7
8
9
10FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]创建
docker-compose.yml
定义服务1
2
3
4
5
6
7
8version: "3.8"
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"然后启动应用程序
1
docker-compose up
打开urlhttp://localhost:5000。就能看到计数的开始了。
这是官网的入门程序。地址为https://docs.docker.com/compose/gettingstarted/。
常用命令
查看项目中的所有容器
1 | docker-compose ps |
启动存在的服务容器
1 | docker-compose start [SERVISE] |
重启项目中的服务
1 | docker-compose restart [SERVISE] |
停止运行的容器
1 | docker-compose stop [SERVISE] |
删除所有停止的服务容器
1 | docker-compose rm [SERVISE] |
down
1 | 此命令将会停止 up 命令所启动的容器,并移除网络 |
up
1 | docker-compose up |
docker-compose.yml
1 | # 1. 版本,注意版本要匹配不然会报错 |
简单的微服务
用SpringBoot写上面类似的计数
创建springboot,导入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>创建一个请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.yww.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
public class HelloDocker {
StringRedisTemplate stringRedisTemplate;
public String hello(){
Long views = stringRedisTemplate.opsForValue().increment("views");
return "Hello Docker views:"+views;
}
}配置Springboot的配置文件
application.properties
1
28080 =
redis # 写服务名相当于域名 =编写
Dockerfile
1
2
3
4
5
6
7FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
ENTRYPOINT ["java","-jar","/app.jar"]编写
docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11version: '3.8'
services:
demoapp:
build: .
image: demoapp
depends_on:
- redis
ports:
- "8080:8080"
redis:
image: "library/redis:alpine"然后打包,将生成的Jar包上传到服务器上,然后运行,访问urlip:8080/hello就可以看到计数了。
1
docker-compose up
Docker swarm
swarm集群
初始化一个swarm集群
1 | 初始化一个swarm节点 |
加入节点
1 | 获取加入token |
查看节点信息
1 | docker node ls |
离开集群
1 | docker swarm leave |
Raft协议
Raft协议,也是一种一致性的算法,具体的算法内容就自行查阅。
要先知道的是managert节点分为三种状态,Leader
,Reachable
,Unreachable
。
现在以Docker集群来进行大概的情况讲解,假设现在swarm的集群有四个节点。
一个manager节点
因为只有manager节点能进行管理,所以在只有一个manager节点,三个worker节点的情况下,manager节点出现故障即状态为Unreachable
,该集群就无法进行管理,只能等待manager节点的修复。
两个manager节点
在这种情况下,其中一个manager节点状态为Unreachable
,另一个manager节点也会无法进行管理操作,所以两个manager节点是无法进行很好的管理的。
当故障的manager节点修复好在加入集群,状态就会变成Reachable
,而另外的那个就会变成Leader
。
三个manager节点
这种就是最起码的集群管理。在这种情况下,第一个manager节点出故障了,其他两个是能进行管理操作的,而不是像之前一样无法管理,当挂了两个就不能管理了。所以一个标准的集群式至少需要三个manager节点的,至少有两个manager节点可用,这样才能保证集群的正常运行。
服务
1 | 创建一个服务,命令类似run,创建的服务会随机分配给集群的节点 |
可以把集群看成是一个整体,因为服务的副本也是接的相同的端口,所以访问哪个节点的ip端口,都可以访问到这个服务。
附录
docker命令一图流。