初识Docker

环境为Ubuntu

image-20250106154457088

docker就是用于解决软件跨环境迁移的问题,docker简单来说就是装了软件及其运行环境的容器

image-20250106154451211

image-20250106155241251

ps:md,docker好几个源都不好用,我的/etc/docker/daemon.json配置如下

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
/*配置如下*/
{
"registry-mirrors": [
"https://hub.fast360.xyz",
"https://hub.rat.dev",
"https://hub.littlediary.cn",
"https://docker.kejilion.pro",
"https://dockerpull.cn",
"https://docker-0.unsee.tech",
"https://docker.tbedu.top",
"https://docker.1panelproxy.com",
"https://docker.melikeme.cn",
"https://cr.laoyou.ip-ddns.com",
"https://hub.firefly.store",
"https://docker.hlmirror.com",
"https://docker.m.daocloud.io",
"https://docker.1panel.live",
"https://image.cloudlayer.icu",
"https://docker.1ms.run"
],
"insecure-registries": [],
"debug": false,
"experimental": false,
"features": {
"buildkit": true
},
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}

Docker架构与容器化

image-20250106154444577

image-20250106154436075

比如想在docker主机上运行MySQL,在docker客户机上拉取MySQL镜像(docker pull MySQL),然后docker主机就会在应用市场/仓库中下载MySQL镜像;

客户机上运行MySQL,执行run命令(docker run MySQL),就会创建容器,容器中放着一个或者多个应用

image-20250106154423878

也可以制作自己的镜像,docker build xxx,会放在docker主机的image中,也可以将这个镜像上传到应用市场中,

docker push xxx

命令

命令-镜像操作

image-20250108101132008

eg:下载一个nginx

先查看仓库中有没有nginx的镜像,sudo docker search nginx(search不走配置的镜像源所以会超时,在镜像名前加上 register.liberx.info/可以解决超时问题)

然后拉到docker主机,sudo docker pull nginx,pull默认是下载最新版本,下载指定版本nginx:版本号

查看自己的镜像列表:sudo docker images/sudo docker image ls

命令-容器操作

image-20250108154811893

查看运行中/所有容器: sudo docker ps [-a]加上-a就会显示所有容器

image-20250108155909075

UP表示上线,EXITED表示结束运行

sudo docker start/stop/stats后面加的是容器名字或者容器的id(id不一定全部输完只要能区分就行,但是名字不能模糊匹配)

image-20250108160337579

删除容器:sudo docker rm [-f] 容器名/容器id,不加-f那么需要停止容器再删除,-f表示强制删除

可以用$传入id

sudo docker rm -f $(sudo docker ps -aq) -aq是显示所有容器的ID

命令-run,exec

运行的docker容器名字一坨,一run就阻塞终端,怎么办?

sudo docker run -d --name 想要指定的容器名 镜像名

image-20250108162414930

-d表示后台运行,–name(是两个杠)给容器指定一个名字

既然占用80端口了,计网里学的80是web服务端口,那么想必可以可以用浏览器访问吧

image-20250108161845673

虽然nginx运行了,还占用的80端口(web服务默认端口),但是在浏览器直接访问docker host的ip行不通

image-20250108161953081

为什么会这样呢?

image-20250108174937249

原因是占用的80端口只是容器的80端口而不是主机的80端口(前面说的容器就像一个更简化的虚拟机,内部的端口自然与外部主机的端口不一样),所以我们需要进行端口映射

-p 外部端口:内部端口(非常重要!!!!!)

image-20250108174845876

将主机的80端口映射到容器的80端口

这下访问到nginx的主页了

image-20250108174620804

怎么修改默认的这个页面呢?dockerhub中会描述镜像的默认初始页面(usr/share/nginx/html)(我上不去啊)

sudo docker exec -it 容器名 /bin/bash -it表示交互模式,/bin/bash表示以终端方式进入这个容器

image-20250108175646271

exit退出

命令-镜像操作

image-20250108225805297

sudo docker commit [选项] 选定容器 镜像

1
2
3
4
-a, --author:设置镜像作者信息。
-c, --change:应用 Dockerfile 指令来创建镜像。
-m, --message:提交时的说明信息。
-p, --pause:在提交之前暂停容器的运行(默认为 true)

比如将前面的运行nginx的容器提交为新镜像

sudo docker commit -m "commit-test" -a "wwwtty" myNginx committest

image-20250108232458971

镜像保存为一个tar文件

sudo docker save [-o 文件名.tar] 镜像名

sudo docker load -i tar包名字

load下来后就是一个镜像,直接run就可以启动容器

image-20250108234044536

image-20250109092226760

sudo docker login

需要有dockerhub账号

sudo docker tag 原镜像名 目标镜像名

sudo docker push 目标镜像名

存储

容器内部对文件修改困难(没有vi/vim),而且如果容器炸了或者容器重新启动就丢失修改

目录挂载

image-20250109100032943

外部的目录像u盘一样 -v 外部目录:内部目录

比如将~下的html目录作为外部目录,nginx默认页面所在目录作为内部目录

sudo docker run -d --name test -p 80:80 -v ~/html:/usr/share/nginx/html nginx

这样访问服务器ip出来的就是自己预设的页面

卷映射

如果想通过修改外部文件就能实现对内部配置文件的修改,需要使用卷映射

为什么不能使用目录挂载呢,因为目录挂载如果目录不存在,会先在外部创建一个空文件夹,那么挂载空文件夹,相当于启动时配置项全是空的,就启动不了容器

-v 卷名:容器中配置文件路径

卷的位置 /var/docker/volumes/卷名

显示所有的卷:sudo docker volume ls

创建卷:sudo docker volume create 卷名

显示卷的详细信息:sudo docker volume inspect 卷名

网络

容器间访问

怎么实现一个容器访问另一个容器?

image-20250109213024273

1.可以通过run的-v端口映射

我有以下两个run起来的容器

image-20250109214140742

假设从test1访问test

sudo docker exec -it test1 bash

sudo curl 175.27.249.106:80

终端就会出现html

image-20250109215823532

但是这样做很奇怪,相当于从test1的88端口->docker host的80端口->test的80端口,绕了一大圈

有一个叫做docker0的默认网络

image-20250111130916309

image-20250111131302142

查看容器细节:sudo docker container inspect 容器名/sudo docker inspect 容器名

image-20250111131535673

接下来就可以通过显示出的IP地址进行访问(docker为每个容器分配唯一IP,通过ip+容器端口可以互相访问)

从test0访问test1

image-20250111134146225

1
2
ubuntu@VM-0-9-ubuntu:~$ sudo docker exec -it test0 bash
root@8656267b120d:/# curl http://172.17.0.3:80 #test1容器的ip和端口

image-20250111134308203

这样做也有缺点,ip是会变动的

image-20250111135401345

创建自定义网络

image-20250111135647105

创建mynet:sudo docker network create mynet

image-20250111135958433

将容器加入自己的网络下(–network 网络名) sudo docker run -d --name test0 -p 80:80 --network mynet nginx

sudo docker inspect test0:

image-20250111142033529

这样一来,容器间访问只需要进入容器->curl http://容器名:容器端口

Docker compose

image-20250111222853028

image-20250112104417975

上线:第一次创建并启动

下线:移除创建的容器以及相关资源

不用compose.yaml启动wordpress和mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#启动mysql
docker run -d -p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=wordpress \
-v mysql-data:/var/lib/mysql \
-v /app/myconf:/etc/mysql/conf.d \
--restart always --name mysql \
--network blog \
mysql:8.0

#启动wordpress
docker run -d -p 8080:80 \
-e WORDPRESS_DB_HOST=mysql \
-e WORDPRESS_DB_USER=root \
-e WORDPRESS_DB_PASSWORD=123456 \
-e WORDPRESS_DB_NAME=wordpress \
-v wordpress:/var/www/html \
--restart always --name wordpress-app \
--network blog \
wordpress:latest

compose.yaml格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
name: 项目名
services:
应用名:
container_name: 容器名(不指定默认:项目名_应用名)
environment:
环境配置(docker run的-e),格式如 - WORDPRESS_DB_HOST=mysql WORDPRESS_DB_HOST: mysql
networks:
- 网络名
ports:
- "端口映射"
restart:
always /……
volumes:
- 目录挂载或卷映射
networks:
网络名:
更详细的配置
volumes:
卷名:
更详细的配置

启动(上线)命令:sudo docker compose -f compose.yaml up -d

-d:后台运行

-f:指定yaml文件

下线命令:sudo docker compose -f compose.yaml down

如果更改了compose.yaml文件的配置,重新上线,没有被更改的应用会保持running状态;

下线并不会移除对应的卷

Dockerfile

dockerfile制作镜像

image-20250113092108980

image-20250113092724389

比如将app.jar打包成镜像

1
2
3
4
5
6
7
8
9
10
11
FROM openjdk:17

LABEL author=wwwtty

EXPOSE 8080 #容器暴露8080端口

COPY app.jar /app.jar #容器相当于一个新的操作系统要放在根目录下

ENTRYPOINT java -jar app.jar
#更推荐下面的写法
ENTRYPOINT ["java","-jar","/app.jar"]

制作镜像命令:docker build -f dockerfile -t 镜像名:版本 ./要指明当前的目录./

镜像分层存储

image-20250113104704713

image-20250113104744757

使用docker history 镜像名查看容器构建历史(过程自下而上)

也可以使用 docker image inspect 镜像名查看更详细的信息

官方的nginx镜像(docker history):

image-20250113110758704

(docker image inspect)展现的层级:

image-20250113111333347

进行目录挂载后的容器再commit的镜像:

image-20250113111013688

发现多了1.09kb

(docker image inspect)展现的层级:

image-20250113111401460

比官方的多了一层,多的一层就是挂载的目录

分层视角下的容器与镜像

image-20250113111738899

容器就是在镜像层(只读)加上一层读写层,对容器的修改都是在可读可写层进行,所以删除容器修改都会丢失(因为读写层没了)

这样的好处是容器隔离:

image-20250113112037023

可以用 docker ps -s/--size 查看读写层和只读层的关系

image-20250113112611189

SIZE列 前面是读写层大小括号内是镜像大小