资源
视频教程
docker官网
docker镜像
docker免费练习课程
安装
- 注意:我们需要安装的是
Docker Engine
,而不是Docker Desktop
。 - ubuntu安装Docker官方文档
- 我们可以按照步骤安装,也可以拉到上方文档的下方,直接通过脚本安装:
# 在终端中运行以下指令,稍等片刻即可安装成功
curl -fsSL https://test.docker.com -o test-docker.sh
sudo sh test-docker.sh
sudo docker version # 检查是否安装成功
下载并运行镜像
- 从docker镜像网站搜索并下载所需镜像,比如whalesay
- 复制右侧的镜像安装指令:
# 若权限不足,需要sudo,稍等片刻即可安装成功
docker pull docker/whalesay # 默认安装 latest 版本
docker pull nginx:1.14-alpine # 安装指定版本
- 可通过镜像页面下面的说明,运行镜像:
docker run docker/whalesay cowsay DenisJD
docker 指令
1. 下载镜像
# 下载镜像,只下载不运行;或者可以跳过下载,直接在第一次运行时下载也可以;
docker pull [image_name]
2. 运行一个容器
# docker 运行指令; 第一次运行这个镜像时,如果没有安装,会自动安装再运行;如果已经安装了就直接运行;
docker run nginx # 表示想要基于 nginx 镜像运行一个容器;注意:同时运行两次这个指令,将会产生两个不同的容器!
docker run docker/whalesay cowsay "say something"
docker run --name webapp nginx:1.14-alpine # 通过指定版本的镜像运行一个容器,并且给这个容器命名为 webapp;这里的版本,在docker中称为TAG
sudo docker run docker.io/shadowthreed/custom-var # 这样可以指定运行其他仓库的docker镜像
# 容器和虚拟机不同:虚拟机运行后就一直处于运行状态,不管有没有任务需要执行;而容器是基于任务存在的,任务一旦结束,容器就结束了;
sudo docker run ubuntu # 立刻结束
sudo docker run -i ubuntu # 一直运行,并且可以输入 shell 指令并执行(Ubuntu镜像默认运行了一个bash,但是如果没有将用户终端重定向到容器内,bash将检测不到终端而直接终止运行)
sudo docker run ubuntu sleep 5 # 5s 后结束,当运行时后面跟了其他指令时,将代替Ubuntu镜像默认是bash指令
sudo docker run kodekloud/simple-prompt-docker # 默认不能输入
sudo docker run -i kodekloud/simple-prompt-docker # 将容器的标准输入重定向到用户终端
sudo docker run -it kodekloud/simple-prompt-docker # 将容器的标准输出也重定向到用户终端,但是貌似现在不需要 -t 了,-i已经包含了 -t 的功能好像?
3. 查看正在运行的容器
# 查看正在运行的docker,即其相关的一些信息;(ID和NAMES字段都是docker随机生成的,ID是唯一的哈希值)
docker ps
4. 查看本地所有容器
# 查看所有的docker镜像及其运行状态等信息;
docker ps -a
5. 停止正在运行的容器
# 停止某个正在运行的docker,可以通过 "docker ps" 中的 ID 或者 NAMES 字段指定docker镜像;
docker stop [NAMES]
docker stop [CONTAINER ID]
6. 删除容器
# 删除某个container;注意是删除容器不是删除镜像;需要先停止再删除;
docker rm [NAMES]
docker rm [CONTAINER ID]
docker container prune # 一次性删除所有停止状态的容器
7. 查看本地安装镜像
# 查看本地已经安装的docker image
docker images
8. 删除本地镜像
# 删除不需要的 image,删除之前需要保证没有基于这个image的容器正在运行或存在(通过"docker ps -a"确认;如果有,需要先停止,再删除容器后,才能删除镜像)
docker rmi [image_name] # 默认删除 latest 版本
docker rmi nginx:1.14-alpine # 当同时有多个不同版本的镜像在本地时,需要指定版本才能删除
9. 在容器中运行指令
# 通过一个终端运行一个Ubuntu镜像的容器并休眠
sudo docker run ubuntu sleep 1000 # 在第一个终端中运行
sudo docker ps # 在第二个终端中运行,可以看到上面运行的终端
sudo docker exec [NAMES] cat /etc/hosts # 在上面运行的容器中运行指令
10. 容器的前后台转换
# 上一个例子中,需要通过两个终端才能完成,是不是有点麻烦呢?
sudo docker run -d ubuntu sleep 1000 # 通过 -d 后台执行,也可以通过 "sudo docker run ubuntu sleep 1000 &" 实现
sudo docker ps # 查看正在运行的容器
sudo docker exec [NAMES] cat /etc/hosts # 在后台运行的容器中执行指令
sudo docker attach [NAMES] # 将后台运行的容器返回到前台
端口映射
- 运行webapp
- 运行后,可以发现其运行在5000端口,但是这个容器只是运行在内网中,用户通过这个信息是访问不了的;可以通过
sudo docker exec [NAMES] ip addr
查看其 IP 地址; - 如果用户需要从外部访问这个容器,就需要进行端口映射;注意这个顺序,是
-p container:host
,先容器端口,再是主机端口;
磁盘挂载
- 如果我们在一个容器中运行了一个MySQL数据库,并在里面存储了很多数据,万一我们需要删除这个容器时,容器中MySQL中的数据将随着容器一起被删除;如果想要将数据和容器解绑,就需要进行磁盘挂载;
- 通过将系统的一个路径挂载到容器的某个路径下,就可以将容器中的数据存储到系统中;
# 将系统中的 /opt/datadir 挂载到容器中的 /var/lib/mysql 路径下
docker run -v /opt/datadir:/var/lib/mysql mysql
查看容器详细信息
- 通过
docker ps
指令可以看到容器的 ID,NAMES 等信息,但想要查看更详细的信息,可以用inspect
指令:
docker inspect [NAMES] # 以 JSON 文件格式输出指定容器的详细信息
查看后台运行容器的日志
1.通过 -d
运行的容器将在后台运行,那如何看其运行日志呢?
docker logs [NAMES] # 查看后台运行的指定容器的日志
查看容器的变量以及设置变量
- 想象一下,有一个容器运行了一个网页,而这个网页有多种颜色可以设置;如果我们每次想要改变容器的颜色都需要该该网页的代码的话,是不是太麻烦了?我们可以把这个颜色通过一个系统变量引入,然后每次设置不同的变量值,就可以改变网页的颜色;
- 那么我们如何查看一个容器引用了哪些变量呢?
docker inspect [NAMES] # 输出的日志信息中,有一个 "Env" 字段,这个字段中的变量就是这个容器的变量。
- 创建容器时传入变量值
docker run -e APP_COLOR=blue [image_name] # 设置网页颜色为 blue
docker run -e APP_COLOR=green [image_name] # 设置网页颜色为 green
docker run -e APP_COLOR=yellow kodekloud/simple-webapp # 例子
创建自己的docker镜像
- 当在
doker hub
网站上找不到自己需要的镜像时,就需要自己创建。 - 首先需要确定这个镜像需要执行的功能,比如以下的例子,通过系统变量
DELAY_TIME
决定延时时间: - 创建一个文件夹,在里面创建两个文件:
app.py
和Dockerfile
。在app.py
中输入如下代码:
import os
import time
# 系统变量都是以字符串引入的
# 通过 try 防止用户的错误输入导致 int() 转换失败
try:
delay_time = int(os.environ.get('DELAY_TIME'))
except:
print("set DELAY_TIME as a number, please! Default 5s.")
delay_time = 5
print("Hello Docker!")
print("Sleep", delay_time, "s")
time.sleep(delay_time) # 休眠 5s
print("Sleep Ending")
可通过以下指令在终端中验证以上代码的功能
unset DELAY_TIME # 删除系统变量
python app.py
export DELAY_TIME=3s
python app.py
export DELAY_TIME=3
python app.py
- 然后在
Dockerfile
文件中输入如下内容,用于指导镜像的创建:
# Dockerfile 的注释以‘#’开始,但是不能跟在命令的后面!
# 创建一个基于 Ubuntu 系统的镜像;也可以创建基于其他镜像的镜像
FROM ubuntu
# 系统更新并安装python pip
RUN apt-get update
RUN apt-get -y install python3-pip
# 创建目录,并将本地的功能代码文件 app.py 拷贝到镜像系统的指定目录;也可以以文件夹为单位进行拷贝
RUN mkdir /app
COPY ./app.py /app
# 指定这个镜像将要执行的任务
CMD python3 /app/app.py
- 构建镜像
# build 后面跟 Dockerfile 的路径,-t 后面指定构建后的镜像名称(用户名/镜像名)
sudo docker build . -t shadowthreed/custom-var # 默认 tag 为 latest
sudo docker build . -t shadowthreed/custom-var:v0.1 # 指定 tag 为 v0.1
# 也可以通过以下指令重命名 TAG
sudo docker tag shadowthreed/custom-var:v0.2 shadowthreed/custom-var:latest # 其实相当于拷贝了一个新的镜像,只是 TAG 不同了,可以通过 'docker images' 发现,已经有两个 TAG 不一样的镜像了
# 构建完成后,可以通过 'sudo docker images' 查看其是否已经在本地镜像库中了
sudo docker images
# 并且可以基于自己构建的镜像创建一个容器,验证一下功能是否正常
sudo docker run shadowthreed/custom-var
sudo docker run -e DELAY_TIME=3 shadowthreed/custom-var
# 登录到 Docker Hub 网站
sudo docker login -u "username" -p "password" docker.io # 登录 docker hub,这样需要在指令中输入密码,不安全
sudo docker login docker.io # 这样直接回车后会交互式输入用户名和密码
# 将自己的镜像上传,下面的任一条指令都可以成功将镜像上传,但是为了更好的进行版本管理,
# 最好每次都将上传的新版本重新定义为 latest,并加上一个版本号;这样在下次上传的时候,latest将会被新版本覆盖,而旧版本还是有这个TAG在;
sudo docker push shadowthreed/custom-var # 默认上传 tag 为 latest 的镜像版本到已登录的账户
sudo docker push shadowthreed/custom-var:v0.1 # 指定上传 tag 为 v0.1 的镜像版本到已登录的账户
# 可以通过以下指令查看镜像各个模块的 size
sudo docker history shadowthreed/custom-var
Dockerfile
总结:
CMD
指定的指令可以被运行容器时后面添加的命令取代而不执行;ENTRYPOINT
指定的指令一定会被执行;而且其第一个参数必须的可执行的!- 可以通过
docker run --entrypoint [NEW_ENTRYPOINT_CMD] image_name
的格式重新指定ENTRYPOINT
CMD
和ENTRYPOINT
的区别,假设有如下几个Dockerfile
:
# 通过以下文件创建的镜像,将将会执行死命令 "sleep 5",无法更改
FROM ubuntu
CMD sleep 5
# 通过以下文件创建的镜像,可以在运行容器时在后面添加参数以指定sleep的时间,
# 比如:docker run [image_name] 5
# 但是当后面没有跟参数时,这个镜像就不能正常运行了;
FROM ubuntu
ENTRYPOINT ["sleep"]
# 通过以下文件创建的镜像,可以在运行容器时,在后面添加参数指定sleep的时间;
# 也可以不加,不加时将使用默认参数 5;
FROM ubuntu
ENTRYPOINT ["sleep"]
CMD ["5"]
Docker networking
Docker
中有 3 种网络设置,分别是bridge
,none
,host
;bridge
是由 Docker 在主机上创建的私有内部网络;所有的容器默认都连接到这个网络;并且能获取到一个IP地址;而且所有容器间可以通过这个IP地址相互访问;如果想要在外部访问这些容器,就需要用前面说的端口映射功能;host
是指将创建的容器直接使用主机的网络,这样的容器就可以不经过端口映射而直接被外部访问了;这也导致主机的这个端口将会一直被占用;none
自然就是不能联网了,表示这个容器不能和外部或者其他容器进行相互访问;- 当运行一个容器时,默认使用
bridge
,默认网络为172.17.0.0/16
;可通过docker inspect [contain_name]
查看; - 可以通过
docker run [image_name] --network=none
指定其他的网络; - 可以通过
docker inspect [contain_name]
产生信息中的Networks
字段查看当前容器的IP地址和网关等信息;
- 查看并创建
Docker
的网络
Docker
中各个容器相互访问
各个容器之间,既可以通过
IP
地址访问,也可以通过内建的DNS
服务器,用容器名访问;
10. 网络相关指令汇总
docker network ls # 查看 docker 所有可用网络
docker network inspect bridge # 查看某一个网络的详情
# 创建一个 bridge 类型的网络,网络名为 wp-mysql-network, 子网和网关如下:
docker network create --driver bridge --subnet 182.18.0.1/24 --gateway 182.18.0.1 wp-mysql-network
docker run -d --name alpine-2 --network none alpine # 以 none 网络运行一个容器
Docker file system
Docker
安装到本地后,文件存储在/var/lib/docker
文件夹下:
- 构建一个镜像到运行一个容器大致分为以下几层,Image Layers的内容会存储在cache中,以便后面重用,所有的容器都可以共用这些层。所有在第一次构建一个全新的镜像时,都会比较慢,而之后修改配置后重新构建,就能很快结束。正因为这些步骤是可重用的,也就要求这些层是不可修改的!而用户运行一个容器后进行的操作修改,都发生在Container Layer,这一层是可读可写的,但是这一层的所有内容将随着容器的删除而删除!
- 既然容器中的数据会随着容器删除而删除,那有什么办法可以把数据存储在容器之外吗?
有,通过挂载实现!有两种挂载方式,一种是bind
(可随意指定一个目录),一种是volume
(指定一个盘?)。
可以通过docker volume create data_volume
命令创建一个volume
,这个新创建的volume
路径在/var/lib/docker/volumes
目录下。然后通过docker run -v data_volume:/var/lib/mysql mysql
方式将主机的/var/lib/docker/volumes/data_volume
挂载到容器的/var/lib/mysql
目录下(当主机不存在data_volume
时,这个指令会自动创建data_volume
)。
通过以上方式只是将数据存在了容器外,但是有没有什么方式,能够使得容器还能使用我们现有的数据呢?
有,那就是通过bind
方式将现有的数据目录挂载到容器内,通过docker -v /data/mysql:/var/lib/mysql mysql
命令实现。
上面的-v
选项其实是旧的方式,现在新版本dockers
都使用更详细的--mount
选项,使用方式如下:
docker run --mount type=bind,source=/data/mysql,target=/var/lib/mysql mysql
而docker
支持的存储驱动也会随着依赖的底层系统不同有不同,会自动选择最佳性能的驱动。我安装的基于Ubuntu
的docker
环境就是使用的Overlay2
。