Featured image of post Docker 详解

Docker 详解

含代码和工程实践过程

官网地址

学习视频(上)

学习视频(下)

Docker 概念

什么是 Docker ?

  • 官方概念:
  1. Docker 是一个用于开发、发布和运行程序的开放平台
  2. Docker 能够将应用程序与基础架构分离,以便可以快速交付软件
  3. 使用 Docker,可以像管理应用程序一样管理基础架构
  4. 通过 docker 命令快速发布、测试和部署代码
  • 我的理解:
  1. 使用 docker 可以将打包代码和所需环境一起封装到一个集装箱
  2. 运行这个箱子便能一键部署环境

Docker 历史与发展

  1. 起源(Golang):2010年,美国成立的一家小公司docCloud。主要做一些PaaS的云计算服务,LXC有关的容器技术。后来他们将自己的容器技术命名为Docker,但是当时并没有引起行业的注意
  2. 开源( 源码地址 ):为了生存,这家公司在2013年将 Docker 开源,每个月更新一个版本,2014年4月9日,1.0发布。然后越来越多人发现它好用,慢慢火起来!
  3. 发展:现在,越来越多的公司使用 Docker 搭建和部署项目生产环境(不论是私有云、公有云、还是混合云)

Docker 架构

docker-arch

镜像(Images)

  • 类似于一个应用程序包,但是包含了运行所需的环境

容器(Container)

  • 通过运行镜像可以创建一个实例运行,该实例称为容器
  • 一个镜像可以创建多个容器
  • 可以对容器进行额外的操作,这些操作会被迭代,然后可以提交为新的镜像

仓库(Repository)

  • 所有镜像存放在仓库里,包括本地仓库和远程仓库
  • 远程下载(pull)的镜像和本地提交(commit)的镜像会保存在本地仓库,本地仓库的镜像可以推送(push)到远程仓库

守护进程(Daemon | Dockerd)

  • 接收客户端的指令,处理 docker 内部和远程仓库的资源

Docker 工作流程

docker-work

相关名词解释及概念

容器技术(LXC)与虚拟机技术(VM)

容器技术与虚拟机技术都属于虚拟化技术,只是虚拟对象不同 docker-container

  • 虚拟机技术:
  1. 将计算机硬件(包括CPU、内存、磁盘空间、网络适配器等)虚拟化,抽象、转换成一台或多台电脑的可分割、可组合配置环境
  2. 优点是虚拟出来的计算机像真实机器一样运行程序与软件实现
  3. 缺点是过于笨重,资源利用率没有发挥到极致,不可跨平台,启动慢等
  • 容器技术(操作系统层虚拟化):
  1. 将操作系统内核虚拟化,利用 linux 核心中的资源分离机制和 namespaces,创建独立的容器,在单一linux实体下运行,无视基础设施的差异部署到任何一个地方
  2. 容器实例运行在独立隔离的环境里,不需要虚拟机一样需要庞大的虚拟化类库
  3. 因此具有轻巧、资源高利用率、易部署、跨平台等特点

容器与镜像

安全性争议

2021年,安全公司Palo Alto Networks研究人员Aviv Sasson,在Docker Hub上发现的恶意容器映像档,分别来自10个不同账号,总下载次数超过2000万次,其中内含的挖矿软件

Docker 基础使用

安装

官方安装文档(linux、windows、macOS)

  • linux(centOS)下安装添加源
1
2
3
4
5
6
7
8
9
# 国外源(速度慢)
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
    
# 阿里源
sudo yum-config-manager \
    --add-repo \
    https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

基本命令

官网全部命令

帮助命令

1
2
3
4
5
6
7
8
# docker 版本信息
$ docker version

# docker 系统信息
$ docker info

# 查看 docker 所有命令
$ docker [command] --help

镜像命令

 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
# 查看所有镜像
$ docker images
$ docker images -a
$ docker images -aq

# 搜索远程仓库镜像
$ docker search mysql
$ docker search mysql --filter=STARS=500

# 下载镜像(默认 latest)
$ docker pull mysql
$ docker pull mysql:5.7

# 删除本地镜像
$ docker rmi 4181d485f650
$ docker rmi -f mysql:5.7

# 将一个容器制作为一个镜像
$ docker commit [可选参数] <id|container> <newImageName>[:tag]
-a 						作者
-m 						注释
-p						暂停当前容器
-c						使用 Dockerfile 指令创建镜像

# 将镜像提交到 docker hub
# 1. 标记为自己的镜像
$ docker tag <image[:tag]> <username>/<image[:tag]>
# 2. 提交
$ docker push <username>/<image[:tag]>

容器命令

 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
# 新建容器并启动
$ docker run --name="nginx" -d -it -p 8080:80 nginx
$docker run [可选参数] <Image>
--name="Name" 容器名
-d 						后台方式运行(如果没有前台使用,自动停止)
-it						交互方式运行(进入容器查看内容)
-v						卷挂载
-e						配置环境
-p						端口映射
	主机端口:容器端口
  容器端口
 -P						随机指定端口

# 容器中退回主机并停止(ctrl+p+q 退出不停止)
$ exit

# 显示正在运行的容器
$ docker ps

# 显示所有的容器
$ docker ps -a

# 删除容器
$ docker rm <id|container>
$ docker ps -a -q|xargs docker rm
$ docker container prune # 删除所有停止运行的镜像

# 启动和停止容器
$ docker start <id|container>
$ docker restart <id|container>
$ docker stop <id|container>
$ docker kill <id|container>

# 进入容器并操作
$ docker exec -it <id|container> /bin/bash

# 进入容器查看运行终端
$ docker attach <id|container>

# 将容器内文件拷贝到主机
$ docker cp CONTAINER:SRC_PATH HOST_PATH 

# 将主机文件拷贝到容器内
$ docker cp HOST_PATH  CONTAINER:SRC_PATH

其他命令

1
2
3
4
5
6
7
8
# 查看日志
$ docker logs <id|container> [可选参数]
-f 				列出所有
-n				限定行数
-t				显示时间

# 查看元数据
$ docker inspect <image|container|network|...>

Docker 进阶

镜像原理

联合文件系统和分层迭代

docker-filesys

  • Docker 采用 AUFS(advanced muti-layerd unifaction filesystem,高级多层统一文件系统),可以实现联合挂载
  • 具体表现是可以如 git 一样对容器和镜像进行版本迭代,每新增一个镜像或操作
  • 因此,添加镜像时,分为多个包批量拉取,如果是本地镜像已有的部分,将会复用
  • 当镜像完全下载好后,多层文件会被压缩为一层
  • 如下:

docker-cmd-info1

docker-cmd-info2

容器数据卷

将 docker 内部的文件挂载到本机,实现本地可以操作 docker 内部数据,实现数据持久化

命令挂载

  • 使用命令挂载 mysql:
1
2
3
4
5
6
$ docker run -d -p 3310:3306 -v /mysql5.7/conf:/etc/mysql/conf.d -v /mysql5.7/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql5.7_01 mysql:5.7

# windows
$ docker run -d -p 3310:3306 -v H:\Docker\mysql5.7\conf:/etc/mysql/conf.d -v H:\Docker\mysql5.7\data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql5.7_01 mysql:5.7

$ docker run --name ipsec-vpn-server --env-file ./vpn.env --restart=always -v ikev2-vpn-data:/etc/ipsec.d -v /lib/modules:/lib/modules:ro -p 500:500/udp -p 4500:4500/udp -d --privileged  hwdsl2/ipsec-vpn-server

docker-mysql

  • docker inspect mysql5.7_01查看卷挂载

docker-cmd-info4

挂载分类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 匿名挂载
$ docker run --name <name> -P -d -v /etc/nginx  <image>

# 具名挂载
$ docker run --name <name> -P -d -v nginx_volume:/etc/nginx  <image>

# 指定路径挂载
$ docker run --name <name> -P -d -v /data/nginx:/etc/nginx  <image>

# 其中,具名和匿名挂载可通过以下方式查找路径
$ docker volume ls # 产看所有挂载卷
$ docker volume inspect <volume> # 查看指定挂载卷的元数据

Dockerfile

Dockerfile 是一个脚本文件,可以通过这个文件创建 docker 镜像

  • Dockerfile
1
2
3
4
5
6
# 通过 Dockerfile 文件创建镜像,最后有个 . 
$ docker build [可选参数] .
-t 					指定 name:tag
-f					指定 Dockerfile 文件路径。可省略,默认找当前目录下 Dockerfile
-o					输出镜像文件路径
-q					创建成功打印镜像id
  • .dockerignore
1
2
3
4
# 通过 .dockerignore 忽略文件
*/temp* 							# 忽略根目录下一级目录所有含 temp 的文件
*/*/temp*							# 忽略根目录下二级目录所有含 temp 的文件
temp?									# ? 匹配一个字符
  • Dockerfile 内环境变量
1
2
3
4
5
FROM busybox
ENV FOO=/bar
WORKDIR ${FOO}   # WORKDIR /bar
ADD . $FOO       # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux

Dockerfile 指令

  • FROM
1
2
3
# 指定该镜像的基本镜像
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
  • RUN
1
2
3
4
# 运行一个操作系统命令,然后提交一个新镜像,作为下一次迭代的基本镜像
# 如果是 windows,将 /bin/bash -c 替换为 cmd /S /C
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
RUN ["/bin/bash", "-c", "echo hello"]
  • CMD
1
2
3
4
# 执行一个命令,只有最后一个生效,且 docker run 指定的时候会被替代
CMD echo "This is a test." | wc - 
CMD ["/usr/bin/wc","--help"]  # 默认,作为 ENTRYPOINT
CMD ["executable", "param1", "param2"]
  • ENTRYPOINT
1
2
3
# 与 CMD 类似,但会一个一个追加,且不会被 RUN 替代
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
  • MAINTAINER已废弃
1
2
MAINTAINER <name>
# 使用 LABEL org.opencontainers.image.authors="SvenDowideit@email" 替代
  • EXPOSE
1
2
# 端口映射
EXPOSE	EXPOSE 80/tcp    EXPOSE 80/udp
  • ENV
1
2
# 设置环境变量
ENV	ENV <key>=<value> ...
  • ADD
1
2
3
4
5
# 添加文件到镜像指定位置,可以是使用通配符?和*,可多个
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
ADD hom* /mydir/       
ADD hom?.txt /mydir/
  • COPY
1
2
3
# 复制文件到镜像指定位置,可以是使用通配符?和*,可多个
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
  • VOLUME
1
2
3
# 在镜像中创建一个或多个挂载点,可以将镜像作为数据卷容器共享数据
# 如果是 windows ,需要写成 windows 的文件目录形式
VOLUME	VOLUME ["/data",...]
  • USER
1
2
3
# 设置用户及分组,类似于 windows 的用户和分组
USER <user>[:<group>]
USER <UID>[:<GID>]
  • WORKDIR
1
2
# 运行容器后进入容器的工作目录,默认是根目录
WORKDIR /path/to/workdir
  • ARG
1
2
3
4
# 定义了一个变量
# 用户可以使用--build-arg <varname>=<value>标志在构建时使用 docker build 命令将该变量传递给构建器
# 如果用户指定了未在 Dockerfile 中定义的生成参数,则生成将输出警告
ARG	ARG <name>[=<default value>]
  • ONBUILD
1
2
3
# 向镜像添加了一个触发器指令,该指令将在以后用作另一个生成的基础时执行
ONBUILD ADD . /app/src 
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
  • STOPSIGNAL
1
2
3
# 设置将发送到容器以退出的系统调用信号,镜像默认的停止信号将会被重写
# 因此 docker run 和 docker create 时需要指定参数 --stop-signal
STOPSIGNAL signal
  • HEALTHCHECK
1
2
3
# 健康检查,默认值:--interval=30s --timeout=30s --start-period=0s --retries=3
HEALTHCHECK [options] CMD command
HEALTHCHECK NONE
  • SHELL
1
2
3
4
# 指定执行命令行的 shell 程序
SHELL ["executable", "parameters"]
SHELL ["powershell", "-command"]
SHELL ["cmd", "/S", "/C"]

Example

  • 构建一个centos镜像
1
2
3
4
5
6
FROM centos
ENV MYPATH=/user/local
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD /bin/bash
  • 打包 Spring Boot 到 Docker
1
2
3
4
5
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]

Docker 命令总结

docker-commands

Docker 高级

Docker 网络

1
2
3
4
5
6
7
8
# 查看所有 docker 网络,默认有3个
# 1、bridge 桥接(默认)
# 2、host 和主机共享
# 3、none 不配置
$ docker network ls

# 查看网络元数据
$ docker network inspect <network>
  1. Docker 中所有的网络都是虚拟的
  2. Docker 一运行就会创建一个网络,该网络以桥接的方式连接所有内部运行容器的网络
  3. Docker 内运行一个容器便会生成一对evth-pair网络,该网络默认会加到默认的桥接网络
  4. 默认情况下只能以地址方式 ping 通,但是通过docker run --name <container1> --link <container2> <image>可以通过容器名单向 ping通(不推荐)
  5. 使用自定义创建桥接网络可以实现以容器名 ping 通,原理是向容器元数据中添加了 ip 映射

docker-network

自定义网络连通

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
### 同个网络下 ping 通
---------------------------
# 1. 创建一个桥接网络
$ docker network create --driver bridge --subnet 172.168.0.0/16 --gateway 172.168.0.1 mynet

# 2. 运行容器并加入自定义网络
$ docker run -d -P --name nginx01 --net mynet nginx
$ docker run -d -P --name nginx02 --net mynet nginx

# 3.进入其中一个容器并 ping 另一个容器
$ docker exec -it nginx01 ping nginx02

### 不同网络下 ping 通
-------------------------------
# 1. 启动一个容器到默认网络
$ docker run -d -P --name nginx03 nginx

# 2. 加入自定义桥接网络
$ docker network connect mynet

docker-cmd-info5

docker-cmd-info3

Docker Compose

  • 什么是 Docker Compose?
    1. Compose 是一个定义和运行多容器 Docker 应用程序的工具
    2. 使用 YAML 文件配置应用程序服务
    3. 使用单个命令创建、启动所有服务
  • 如何使用?
    1. Dockerfile文件定义应用环境
    2. docker-compose.yml定义组成应用的服务
    3. docker compose up命令运行
  • 安装 Compose (windows和mac桌面版默认安装了)

.env 环境变量文件

如果你的 docker-compose.yml文件需要使用变量,需要创建一个.env配置文件

  • 例子:
1
2
# .env
TAG=v1.5
 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
version: '3.8'
services:
  web:
  	image: "webapp:${TAG}"
    build: .
    # $$ 转义一个 $
  	# command: "$$VAR_NOT_INTERPOLATED_BY_COMPOSE" 
    # 控制服务启动顺序
    command: ["./wait-for-it.sh", "db:5432", "--", "python", "app.py"]
    # 指定一些环境变量
    environment:
    - DEBUG=1 
    env_file:
    - .env.dev
  db:
  	image: "postgres:${POSTGRES_VERSION}"
  frontend:
    image: frontend
    profiles: ["frontend"]

  phpmyadmin:
    image: phpmyadmin
    depends_on:
      - db
    profiles:
      - debug

  backend:
    image: backend
  • 其他场景
  1. docker-compose --env-file ./config/.env.dev up 启动时指定环境变量文件
  2. docker-compose --env-file ./config/.env.dev config重写默认启动文件
  3. 如上- DEBUG=1运行时将会转化为docker run -e DEBUG=1
  4. 如上- web-variables.env运行时将会转化为docker run --env-file=.env.dev
  5. 如上设置了profile属性的服务默认情况下不启动,docker-compose run phpmyadmin显示指定时启动(不会启动db),或docker-compose --profile frontend --profile debug才启动
  6. YAML 文件支持不同环境的重写,如docker-compose.prod.yml继承默认的docker-compose.yml
  7. 使用 Compose 默认会自动创建一个网络,所有的服务启动会创建一个和服务名一样的网络并加入

开发环境到生产环境

  1. 移除所有程序代码的卷绑定
  2. 绑定主机与容器的端口映射
  3. 配置生产环境的变量
  4. 重新启动策略
  5. 额外的生产环境服务

因此可以创建一个docker-compose.prod.yml配置生产环境,然后两个配置文件一起启动:docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

YAML 文件的配置

官方相信配置文档

案例:搭建WordPress博客

  1. 创建一个项目目录WordPress
  2. 进入该目录cd WordPress
  3. 创建YAML文件docker-compose.yml,并配置
  4. 启动docker-compose up -d
  5. 浏览器访问http://localhost:8000
 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
version: "3.9"
    
services:
  db:
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: somewordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    
  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    volumes:
      - wordpress_data:/var/www/html
    ports:
      - "8000:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
volumes:
  db_data: {}
  wordpress_data: {}

Docker Swarm(已废弃)

用于 docker 集群的部署 现使用 Kubernetes替代, 官方文档

  • k8s笔记

Kubernetes(k8s)

Licensed under CC BY-NC-SA 4.0
最后更新于 2023-03-22 20:33:00