一、Docker入门

镜像与容器概念?

用重装系统来解释的话,镜像(images)就是我们要装的系统(win7、win10、manjaro 等 iso 文件),容器好比是 U 盘。我们可以把系统放进各种容量足够的 U 盘里,那么这个 U 盘就是一个容器,当我们需要重装系统的时候,就使用(启动)这个 U 盘(容器)。

因此,一个镜像可以去创建多个容器,各个容器之间互不干扰。

Q:docker 拉取的镜像为什么比我们直接下载的文件体积大?
A:一个镜像不仅仅是原来的软件包,它还包含了软件包运行所需的操作系统依赖、软件自身依赖等。所以随着我们的使用,使用的镜像越多,新的镜像下载会越来越快,因为有些依赖已经存在,后续的镜像如果对存在的依赖有使用的话,它会复用已经存在的依赖,而不会去再次下载。

二、准备

为了避免使用普通用户运行 docker 的相关命令时出现报错,我们可以在docker命令前加上sudo去运行,但是每次都加显然很麻烦。那么在安装完docker后,可以使用以下命令:

1
2
3
4
5
6
7
8
# 创建 docker 用户组
sudo groupadd docker
# 将当前普通用户加入 docker 组中
sudo gpasswd -a $USER docker
# 更新 docker 组
newgrp docker
# 测试命令
docker ps

镜像源配置

有两种方法修改镜像源,一种是命令行命令,另一种是修改相关文件。

1. 命令行设置国内镜像源

1
dockerd --registry-mirror=https://hub-mirror.c.163.com

2. 编辑配置文件设置镜像加速

/etc/docker/daemon.json
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"registry-mirrors" : [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
],
"insecure-registries" : [
"registry.docker-cn.com",
"hub-mirror.c.163.com",
"docker.mirrors.ustc.edu.cn"
],
"debug" : true,
"experimental" : true
}

设置完毕重启 docker 服务。

1
systemctl restart docker
加速源 url 说明
Docker 官方提供 https://registry.docker-cn.com 无需注册,已关闭,无法使用
中科大 https://docker.mirrors.ustc.edu.cn/ 无需注册(推荐)暂时无法使用,留意官网通告
阿里云 登录阿里云的容器hub服务,镜像加速器那一栏里会为你独立分配一个加速器地址 需要注册
网易云 https://hub-mirror.c.163.com 无需注册

三、常用命令

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 search name # 根据镜像名称查找镜像
docker pull 镜像名称:版本号 # 拉取镜像,注意对应的版本号
docker images -a # 列出本地所有的镜像
docker images --digests # 显示镜像的摘要信息
docker images --no-trunc # 显示完整的镜像信息
docker rmi name/id # 删除镜像,-f 选项强制删除
docker info # docker详细信息

# 容器操作
docker ps # 列出当前正在运行的容器
docker ps -a # 列出所有的容器
docker ps -q # 列出所有的容器ID
docker inspect name/id # 查看容器内部信息
docker stop name/id # 停止容器运行
docker start name/id # 启动容器
docker restart name/id # 重启容器
docker rm name/id # 删除容器,-f 选项强制删除

docker logs name/id # 查看容器服务运行日志
docker logs -f name/id # 实时监听服务运行日志
docker logs -t name/id # 为服务运行日志加入时间戳

# 复制容器内部的配置文件到宿主机
# docker cp name/id:源文件source 宿主机目录target
docker cp nginx:/etc/nginx/nginx.conf /data/docker-service/nginx/nginx.conf


# 进入容器
docker exec -it name/id bash

# 将容器打包成一个新的镜像
docker commit -m "描述信息" -a "作者信息" 容器名称或者ID tarName:tag
# 将镜像备份,备份为 .tar 文件
docker save 镜像名称:标签 -o fileName

以上的 name/id 均指容器创建时指定的名称或者容器的标识id(docker ps可以查看).

接下来以 MySQL 为例来体会一下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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 查找docker hub中是否有MySQL镜像,可以去 [docker hub](https://hub.docker.com/) 的官网搜索版本
docker search mysql

# 拉取MySQL8.0.20版本
docker pull mysql:8.0.20

# 查看拉取的镜像
docker images

# 创建并运行容器 MYSQL_ROOT_PASSWORD 该项在启动时必须指定,不然容器启动失败
# mysql:8.0.20 就是使用刚刚下载的镜像创建容器;如果不写会自动下载最新版本的镜像
docker run -d -p 9999:3306 --name mysql8 -v /docker-data/mysql/data:/var/lib/mysql -v /docker-data/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --restart=always mysql:8.0.20

# 查看 mysql8 容器是否运行
docker ps

# 查看容器信息
docker inspect mysql8

# 进入容器
docker exec -it mysql8 bash
# 进入 mysql 命令行,登录 mysql
mysql -u root -p
# 退出 bash,退出容器
exit

# 停止容器
docker stop mysql8

# 再次查看正在运行的容器
docker ps

# 查看本地所有的容器(启动成功和启动失败的都会显示)
docker ps -a
# 删除容器 (如果删除时需要删除卷,使用 -v 选项)
docker rm mysql8
# 再次查看本地所有的容器
docker ps -a

# 删除下载的 mysql8.0.20 镜像
docker rmi mysql:8.0.20
# 查看已下载的镜像
docker images
选项 说明
run 运行一个docker容器
–name 指定容器的名字 mysql8
-p 9999:3306 设置端口映射:宿主机映射端口:容器运行端口;客户端工具(例如navicat)连接时可以通过 9999 端口进行连接
-e MYSQL_ROOT_PASSWORD=123456 添加环境变量:初始化root用户的密码为 123456
-d 表示使用守护进程运行,即服务挂在后台运行
-v 数据卷映射,本地目录在前,容器内目录在后。删除容器后,映射的本地目录内容仍然存在
–restart=always 在 docker 服务启动后,自动运行该容器

如果已经运行的容器想要设置跟随 docker 服务启动,使用下面的命令
docker update --restart=always name/id

四、Docker进阶

1. Docker为什么提供网络功能?

Docker 允许通过外部访问容器或容器互联的方式来提供网络服务,方便了不同容器间进行通信。一般在使用 docker 网桥(bridge)实现容器与容器通信时,都是站在一个应用角度进行容器通信。

1
2
3
4
5
6
7
8
9
10
# 查看网桥配置
docker network ls
# 创建网桥
docker network create 自定义网桥名称
# 删除网桥
docker network rm 网桥名称或ID
# 查看网桥详情
docker inspect 网桥名称或ID
# 运行容器时使用 --network 网桥名称 指定该容器在那个网桥段
docker run -d --name mysql -p 3306:3306 --network ems mysql:8.0

网桥不会自动创建,如果要使用网桥,必须 先创建,再使用; 运行容器时若指定的网桥不存在,那么会导致这个容器运行失败。在容器启动时指定了网桥后,在这个网桥中的所有容器,可以直接使用容器名称与其它容器通信。类似于同一局域网进行联机对战。

1
2
3
4
docker run -d --name tomcat01 -p 8081:8080 --network ems tomcat:8.0-jre8
docker run -d --name tomcat02 -p 8082:8080 --network ems tomcat:8.0-jre8
# 之后可以通过名称去访问tomcat02的主页信息
curl http://tomcat02:8082

网络的好处:

  • 容器间通信: 容器可以相互通信,无需暴露端口到宿主机。
  • 服务发现: 容器可以通过服务名(如 mysql 或 redis)相互发现,而不需要知道对方的 IP 地址或端口号。
  • 网络隔离: 不同的服务可以属于不同的网络,从而实现网络隔离和安全性。

2. docker数据卷(volume)

  1. 数据卷的修改会立即影响到容器;
  2. 对数据的更新修改,不会影响镜像;
  3. 数据卷默认一直存在,即使容器被删除

实现容器与宿主机之间数据共享。数据卷就是上面创建mysql时的 -v 指定的映射目录,可以把容器内的数据持久化,这样删除容器时我们不必担心数据丢失。但是注意,这样做外部的改变和容器内部的改变会互相影响。

如果我们希望外部影响内部,但是内部操作不影响外部的话,在创建容器时我们可以这样指定: -v /usr/mysql/data:/var/lib/mysql:ro,其中 ro 代表只读(read only)。前面的映射目录我们也可以使用数据卷简称,这个简称可以随便起名,例如:-v aa:/var/lib/mysql,这样docker会自动帮我们创建 aa 存储目录,可以使用 docker volume inspect aa查看它的路径。

1
2
3
4
5
6
7
8
9
10
# 查看数据卷
docker volume ls
# 查看数据卷信息
docker volume inspect 卷名
# 创建数据卷
docker volume create 卷名
# 删除数据卷
docker volume rm 卷名
# 删除没有使用的数据卷,删除前需要我们手动确认
docker volume prune

五、Dockerfile

如果不清楚 Dockerfile 的相关指令含义,可以查阅官方文档,有清晰的介绍。Dockerfile 文件中的命令必须使用大写,通常以 FROM 指令开始。

下面使用 Dockerfile 创建 Java 镜像在容器运行作为一个简单的 Demo,其实,它也是很简单的。首先创建一个空的文件夹,在空文件夹中创建 DockerFile 文件,编辑:

1
2
3
4
5
6
7
8
9
10
11
12
# 以 openjdk:8-jre 为基础
FROM openjdk:8-jre
# 指定目录后,下面的命令都是基于此目录进行的
WORKDIR /application
# 基于 WORKDIR 的路径,/application/aa
WORKDIR aa
# 该文件上传后,直接改名为 app.jar
ADD app-0.0.1-SNAPSHOT.jar app.jar
# 暴露的端口
EXPOSE 8081
# 运行时执行的命令
ENTRYPOINT java -jar app.jar

构建自定义镜像:docker build -t demo:1.0 .,注意最后的 . 不要漏掉。
用自定义镜像启动容器:docker run -d -p 8081:8081 --name demo demo:1.0

六、docker-compose

docker-compose 和 Dockerfile 很相似,都是需要我们写一个文件,文件中按照特定的格式去写一些内容和指令,再使用命令去运行这个文件。一个 docker-compose 对应一个 stack。

和 docker 的不同:docker是面向容器的,而 docker-compose 是面向服务的,这也是两者本质区别。

另外,如果是 Window 系统,安装 Docker for Window 后就包括了 dcoker-compose 工具;如果是 Linux 系统,那么额外需要安装 docker-compose 插件。没错,docker-compose 是 docker 的一个插件。

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
# 查看帮助
docker-compose --help

# 启动服务的命令,默认前台启动;如果文件名称是 `docker-compose.yml` 可以省略,其它名称不可省略。
docker-compose up docker-compose.yml 或者直接 docker-compose up
docker-compose up docker-test.yml

# 后台启动
docker-compose up -d

# 停止所有 up 启动的服务,并移除网络
docker-compose down

# 进入指定的容器,注意写的是服务名称
docker-compose exec

# 列出当前 docker-compose 运行的所有容器
docker-compose ps

# 重启项目中服务,后面不写服务名代表重启所有服务。还有 start stop 指令,与之类似,略过。
docker-compose restart

# 强制删除(-f)并删除数据卷(-v,慎用-v选项)
docker-compose rm -f -v

# 查看每个服务容器内的进程
docker-compose top

# 唤醒服务
docker-compose unpause

# 暂停服务
docker-compose pause

# 查看服务日志
docker-compose 服务 logs

2. 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
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
version: "3.8" # 4.0以下
services:
tomcat: # 唯一服务名称
image: tomcat:8.0-jre8 # 使用哪个镜像创建
# 用来指定 dockerfile 所在目录。
# 和 image 的不同:先根据该文件构建镜像,之后再运行容器
build:
# dockerfile 文件所在的目录
context: demo
dockerfile: Dockerfile
container_name: tomcat # 指定容器名称
ports: # 宿主机与容器的的端口映射
- "8080:8081"
volumes: # 宿主机与容器的目录映射
#- /root/data:/usr/local/tomcat/
# 自定义映射
- tomcatwebapps:/usr/local/tomcat/webapps
networks: # 指定容器启动后使用的哪个网桥
- hello
mysql:
image: mysql:8.0
container_name: mysql
ports:
- "3306:3306"
volumes:
- /root/mysql/data:/var/lib/mysql
- /root/mysql/config:/etc/mysql
enviroment:
- MYSQL_ROOT_PASSWORD=root
# 可以把 environment 的内容写到配置文件中
env_file: # 配置文件必须以 .env 结束
- mysql.env
# 代表这个容器启动时依赖哪些模块,服务名称,不是容器名称
depends_on:
- tomcat
- redis
# 用来修改容器内部参数;因为如果不修改的话容器可能无法启动
sysctls:
- net.core.somaxconn=1024
- net.ipv4.tcp.syncookies=0
# 修改容器中系统内部进程数限制;根据当前容器运行服务要求更改
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
redis:
image: redis:6.2.5
container_name: redis
ports:
- "6379:6379"
volumes:
- redisdata:/data
# 启动时覆盖容器的默认命令
command: "redis-server --appendonly yes"

volumes:
tomcatwebapps: # 自定义映射卷标需要显式声明
# 是否确定使用指定卷标
# false 会使用 项目名 + 卷标名
external:
true
redisdata:

networks:
# 定义上面服务用到的网桥名称,默认就是 bridge
hello:
# 是否确定使用指定名称的网桥
# false 会使用 项目名 + 网桥名称
external:
true

同一网络中的服务可以使用服务名称进行通信

3. 使用portainer可视化工具

  1. 下载可视化工具:docker pull portainer/portainer
  2. 启动 portanier,需要开放两个端口,8000 为监听服务的端口,9000 为向外部提供服务的端口。
    docker -d -p 8000:8000 -p 9000:9000 --name portanier --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
  3. 浏览器访问 http://localhost:9000

4. 卸载 Docker

1
2
3
4
sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras

sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

本站由 江湖浪子 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。