目录

Learning docker


Learning Docker

背景概要

  • $2005 \Rightarrow 2015$:Cloud(云技术)时代,为Container打下基础,带来土壤
  • $2015 \Rightarrow\ Now$:Container(容器)时代,最出名的代表就是Docker

Container的概念

  • 简述:复制了一遍你的运行环境,别人拿过来能直接跑对应的程序,不用再折腾配环境这种司马事情了

安装

Windows

  • Docker官网,下载桌面版

  • 注意安装前开启BIOS虚拟化

  • 如果开启wsl2能省去很多事情(

  • 最终验证:命令行运行

Linux

1
2
curl -fsSL get.docker.com -o get-docker.sh
sudo ./get-docker.sh
  • 最终验证:命令行运行
1
2
sudo systemctl start docker
docker version

创建与操作容器

  • 镜像(image):包含OS和应用的对象,类似虚拟机的模板(装系统用的那种,比如系统盘!),只能读
  • 容器(container):和镜像几乎一样,但是比镜像多了一层Read-Write Layer,这样就可以写了

创建容器

  • 先检测ClientServer是否都启动docker versiondocker桌面版要挂着)

  • 创建命令docker container run <image name>(容器里面是对应的image name)

  • 例子

1
2
3
4
# 建nginx的镜像,并运行
docker container run nginx
# 建ubuntu的镜像,并运行
docker container run ubuntu
  • Docker Hub:官方镜像社区,里面有大量制作好的镜像(创建容器就是上上面先拉取镜像,然后制作成镜像装在Docker里面)

查看正在运行的容器

1
2
3
4
PS C:\Users\ROG> docker container ls
CONTAINER ID   IMAGE                 COMMAND       CREATED        STATUS              PORTS     NAMES
9ed70676585e   l543306408/rros_lab   "/bin/bash"   8 hours ago    Up About a minute             rros_lab1
a99fc6e9cfe3   l543306408/rros_lab   "/bin/bash"   11 hours ago   Up 5 seconds                  rros_lab

查看所有容器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
PS C:\Users\ROG> docker container ps -a
CONTAINER ID   IMAGE                 COMMAND             CREATED        STATUS                         PORTS     NAMES
e64ddbdaadc8   rust-oslab:latest     "/bin/sh -c bash"   9 hours ago    Exited (0) About an hour ago             rust-oslab-1
9ed70676585e   l543306408/rros_lab   "/bin/bash"         9 hours ago    Exited (137) 43 seconds ago              rros_lab1
a99fc6e9cfe3   l543306408/rros_lab   "/bin/bash"         12 hours ago   Exited (137) 42 seconds ago              rros_lab

# 等价命令
PS C:\Users\ROG> docker container ls -a
CONTAINER ID   IMAGE                 COMMAND             CREATED        STATUS                       PORTS     NAMES
e64ddbdaadc8   rust-oslab:latest     "/bin/sh -c bash"   9 hours ago    Exited (0) 30 seconds ago              rust-oslab-1
9ed70676585e   l543306408/rros_lab   "/bin/bash"         10 hours ago   Exited (137) 7 minutes ago             rros_lab1
a99fc6e9cfe3   l543306408/rros_lab   "/bin/bash"         12 hours ago   Exited (137) 7 minutes ago             rros_lab

启动容器

1
docker container run <image name>

停止正在运行的容器

1
docker container stop <id> # id写前缀就行,一般前缀不会冲突

删除容器

1
docker container rm <id> # id写前缀就行,一般前缀不会冲突

  • 上面的命令可以省略container不过为了强调container平时不做删除(与image区分)

多容器操作

  • 一次停掉多个容器的思路
    • 查看所有容器的id
1
2
3
4
PS C:\Users\ROG> docker container ps -a -q
660040021c4d
9ed70676585e
a99fc6e9cfe3
  • 然后利用指令组合
1
2
3
4
PS C:\Users\ROG> docker container stop $(docker container ps -a -q)
660040021c4d
9ed70676585e
a99fc6e9cfe3
  • 命令组合的思路可以用到很多docker需要批量处理的情况下

  • 删除正在运行的容器会报错!

    • 强制删除:docker container rm <id> -f

端口映射与主/被动模式

端口映射

  • 目的:让容器能过在外网访问到
1
2
docker container run -p <"服务器的端口">:<"容器的端口"> nginx
docker container run -p 80:80 nginx
  • 检测是否启动
    • 浏览器中访问127.0.0.1:80

主动模式(默认)

  • 相当于命令行里面的子进程,会打印日志信息

  • 容易被误操作停掉(关掉命令行或者不小心Ctrl + c

  • 调试时使用

被动模式(后台运行)

1
docker container run -d -p 80:80 nginx
  • 不再打印日志

  • 不会因为关掉命令行而终止

  • 使用docker container ls就能查看到

转换成主动模式

1
docker attach <id>

detached模式下查看日志的方法

1
2
3
4
docker container run -d -p 80:80 nginx
docker container logs <id>
# id写镜像名字也行
docker container logs <image name>
  • 跟踪日志(变成终端的子进程,不会自动停止,Ctrl + C终止)
1
docker container logs -f <id>

交互模式

  • 先启动容器
1
2
3
4
5
6
7
PS C:\Users\ROG> docker container ls
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
PS C:\Users\ROG> docker container start rros_lab1
rros_lab1
PS C:\Users\ROG> docker container ls
CONTAINER ID   IMAGE                 COMMAND       CREATED        STATUS         PORTS     NAMES
9ed70676585e   l543306408/rros_lab   "/bin/bash"   21 hours ago   Up 3 seconds             rros_lab1
  • 进入容器终端
1
docker attach <container name>
/img/Learning Docker/docker attach.png
``
  • exit退出后容器也会关闭

如何exit后不关闭容器

  • 使用detached模式
1
docker exec -it <container name> <shell>(通常用sh或者bash)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
PS C:\Users\ROG> docker container ls
CONTAINER ID   IMAGE                 COMMAND       CREATED        STATUS          PORTS     NAMES
9ed70676585e   l543306408/rros_lab   "/bin/bash"   22 hours ago   Up 52 seconds             rros_lab1
PS C:\Users\ROG> docker exec -it rros_lab1 bash
root@9ed70676585e:/data/bupt-rtos# ls
20210704_ojeda_rust_support.mbx  arm64_ramdisk  rros
root@9ed70676585e:/data/bupt-rtos# exit
exit
PS C:\Users\ROG> docker container ls
CONTAINER ID   IMAGE                 COMMAND       CREATED        STATUS              PORTS     NAMES
9ed70676585e   l543306408/rros_lab   "/bin/bash"   22 hours ago   Up About a minute             rros_lab1

镜像获取/Image Registry

获取镜像的三个途径

  • pull from registry

  • Dockerfile (online)

  • 自有文件导入(offline)

  • 使用官方认证的镜像:安全


镜像的拉取和删除

  • 拉取:官网找命令
/img/Learning Docker/docker pull.png
``
1
docker pull wordpress
  • 新版本可以加上image前缀(强调,和container作区分)
1
docker image pull wordpress
  • 实际效果(默认最新版,如果要其他版本去tag里面找对应版本的命令)
/img/Learning Docker/docker pull busybox.png
``

查看现有镜像

1
docker image ls

查看镜像详细信息

1
docker image inspect <image id>
  • 关注对应的OS和架构

删除镜像

1
docker image rm <id>

image的导入/导出

  • image导出

  • 在要保存的位置执行

    1
    2
    3
    
    docker image save <image name>:<tag> -o <new image name>.image
    # example
    docker image save busybox:latest -o mybusybox.image
    
  • image导入

  • 也是在对应镜像的位置

    1
    2
    3
    
    docker image load -i <load image name>.image
    # 例子
    docker image load -i .\mybusybox.image
    

Dockerfile

  • Dockerfile:使用组合映像命令的文本文档,用于DIY自己的image

  • 例子ubuntu执行python程序
  • hello.py
1
print("Hello, my first docker!")
  • Dockerfile
1
2
3
4
5
FROM ubuntu:latest
RUN  apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y python3.9 python3-pip python3.9-dev
ADD hello.py /
CMD ["python3","hello.py"]
  • FROM:引入系统

  • RUN:安装环境(build的时候执行)

  • ADD:将本机文件加入到image中,默认加入到image的根目录

  • CMD:相当于使用shell命令(build后执行)


构建Dockerfile

1
docker image build -t <image name>:<tag(若忽略则默认latest)> <image path(通常用".",本地目录)>

  • 将镜像上传到Docker Hub上面

    • 规则:<Hub name>/<image name>
  • 更改名字使其能够上传

1
2
3
docker image tag <old name> <new name>
# example
docker image tag myimage username/myimage
  • 通过id/name上传
1
2
docker login # 输入用户名和密码登录
docker image push username/myimage

FROM语法

  • 引入基础镜像

  • 三个基本原则

    • 官方镜像优于非官方镜像(安全性)

    • 固定版本的Tag,而不是每次都使用latest(稳定)

    • 功能满足的情况下使用体积小的镜像(让自己的镜像变得简洁)


RUN命令

  • shell命令

1
2
3
4
5
6
7
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y wget
RUN wget https://github.com/ipinfo/cli/releases/download/ipinfo-2.0.1/ipinfo_2.0.1_linux_amd64.tar.gz
RUN tar zxf ipinfo_2.0.1_linux_amd64.tar.gz
RUN mv ipinfo_2.0.1_linux_amd64 /usr/bin/ipinfo
RUN rm -rf ipinfo_2.0.1_linux_amd64.tar.gz
  • 不建议这么写:每个RUN都会生成一个新的“层”,打包的体积会变大

    • rm -rf的命令也会无效:上面的文件已经嵌入到上一层里面了
  • 正确写法:改成&& \的组合

1
2
3
4
5
6
7
FROM ubuntu:latest
RUN apt-get update && \
    apt-get install -y wget && \
    wget https://github.com/ipinfo/cli/releases/download/ipinfo-2.0.1/ipinfo_2.0.1_linux_amd64.tar.gz && \
    tar zxf ipinfo_2.0.1_linux_amd64.tar.gz && \
    mv ipinfo_2.0.1_linux_amd64 /usr/bin/ipinfo && \
    rm -rf ipinfo_2.0.1_linux_amd64.tar.gz
  • 变成了只有一层,体积会变小

文件操作

  • COPY

    1
    
    COPY index.js /app/index.js
    
    • 将当前目录下的index.js文件拷贝到镜像中的/app/index.js(镜像中没有对应文件会自动创建)
  • ADD

    • 基本效果和COPY相同,但是会自动解压缩
    1
    
    ADD index.tar /app/
    
    • 运行后会发现容器/app/文件夹里面就是index.tar里面被压缩文件的内容
  • WORKDIR

    • 切换目录
    1
    2
    3
    
    FROM ubuntu:20.04
    WORKDIR /app
    COPY hello.py hello.py
    
    • 和上面效果相同,最后文件都在/app这个目录下
    • 相当于cd

  • 变量声明(类似宏定义,使得代码更简介)

  • 例子

1
2
3
4
5
6
7
FROM ubuntu:latest
RUN apt-get update && \
    apt-get install -y wget && \
    wget https://github.com/ipinfo/cli/releases/download/ipinfo-2.0.1/ipinfo_2.0.1_linux_amd64.tar.gz && \
    tar zxf ipinfo_2.0.1_linux_amd64.tar.gz && \
    mv ipinfo_2.0.1_linux_amd64 /usr/bin/ipinfo && \
    rm -rf ipinfo_2.0.1_linux_amd64.tar.gz

ENV

  • ENV key=value(中间不要加空格,有坑)
  • 使用value${key}$
  • 效果
1
2
3
4
5
6
7
8
FROM ubuntu:latest
ENV VERSION=2.0.1
RUN apt-get update && \
    apt-get install -y wget && \
    wget https://github.com/ipinfo/cli/releases/download/ipinfo-${VERSION}/ipinfo_${VERSION}_linux_amd64.tar.gz && \
    tar zxf ipinfo_${VERSION}_linux_amd64.tar.gz && \
    mv ipinfo_${VERSION}_linux_amd64 /usr/bin/ipinfo && \
    rm -rf ipinfo_${VERSION}_linux_amd64.tar.gz

ARG

  • 语法和ENV一样
    • ENV不同的地方
      • ENV声明的变量会带入镜像内部(比如Linuxenv命令,或者直接echo $key也能打出来),而ARG不会
      • ARG可以动态添加
1
docker image build -f Dockfile -t <image>:<tag> --build-arg VERSION=2.0.0 .

CMD

  • 三个基本原则

    • 容器启动时默认执行的命令
      • 如果没有,对应拉取的官方镜像会有默认的CMD,比如ubuntu会自带CMD["bash"]
    • 如果启动容器所用的命令是docker container run {commandline}
      • 比如docker container run -it <image name> <command> => docker container run -it ipinfo-base ipinfo version:对于容器ipinfo-base容器的默认CMD就是ipinfo version
    • 一个Dockerfile定义了多个CMD命令,只会执行最后一个
  • 两种写法

    1
    2
    
    CMD [ "python3","hello.py" ]
    CMD python3 hello.py
    
  • 清除不再使用的容器

1
docker system prune -f
  • 清除不再使用的镜像
1
docker image prune -a

ENTRYPOINT

  • CMD功能相似

  • 区别

    • CMD会被命令行启动模式后面接的命令覆盖掉,而ENTRYPOINT不会,一定会被执行
    • CMDENTRYPOINT可以联合使用
  • 1
    2
    3
    
    FROM ubuntu:21.04
    ENTRYPOINT ["echo"]
    CMD []
    
    • 命令行docker container run -rm -it demo-both "hello world"执行效果=> hello world

  • 数据持久化:VOLUME
    • 数据库,日志等如何存到本地?
1
2
3
4
5
6
7
8
9
FROM ubuntu:latest
ARG VERSION=2.0.1
RUN apt-get update && \
    apt-get install -y wget && \
    wget https://github.com/ipinfo/cli/releases/download/ipinfo-${VERSION}/ipinfo_${VERSION}_linux_amd64.tar.gz && \
    tar zxf ipinfo_${VERSION}_linux_amd64.tar.gz && \
    mv ipinfo_${VERSION}_linux_amd64 /usr/bin/ipinfo && \
    rm -rf ipinfo_${VERSION}_linux_amd64.tar.gz
VOLUME [ "/data" ]
  • 保留镜像下面的/data目录,没有会自动创建

  • 保存在本地的持久化数据使用docker volume系列的命令查看

1
2
3
4
docker volume ls
# -----
# -----
docker volume inspect <VOLUME NAME>(注意不能简写)
  • Mountpoint就是本地存储的位置

1
2
docker container run -d -v <VOLUME NAME>/<VOLUME PATH IN DOCKERFILE> <container name>
docker volume inspect <container name>
  • 这个时候VOLUME NAMEMountpoint就是你自己刚才指定的名字了,不会是一串大段的加密码了
  • 这个既可以让容器的数据保存的主机,反过来,如果主机对应的目录下面有文件,再次运行新的容器的时候,该容器可以复用主机对应部分的数据

  • Bind Mount自定义
    • Windows实现数据持久化
    • 用容器运行本机的程序,本机无需再需要安装环境
1
docker container run -it -v ${pwd}:/app node sh
  • 本机修改数据的时候会同步到容器中,相当于完全绑定

  • 网络端口映射
1
docker container run -d -p 80:80 <container name>
  • -p <主机端口>:<容器端口>:映射

  • 查看容器的端口

1
docker container inspect --format '{{.Config.ExposedPorts}}' <container id>
  • 主要关注Config.ExposedPorts下面的内容

Docker-compose

  • 相当于shell脚本,集成了命令

  • 两个部分

    • 配置yml文件
    • 运行文件的命令
  • 检测环境

1
docker-compose --version

yml文件的基本语法

  • docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: "3.8" # docker-compose的版本号

services:
  # 容器
  servicename:
    # 服务名字,这个名字也是内部 bridge网络可以使用的 DNS name
    image: # 镜像的名字
    command: # 可选,如果设置,则会覆盖默认镜像里的 CMD命令
    environment: # 可选,相当于 docker run里的 --env
    volumes: # 可选,相当于docker run里的 -v
    networks: # 可选,相当于 docker run里的 --network
    ports: # 可选,相当于 docker run里的 -p
  servicename2:


volumes: # 可选,相当于 docker volume create

networks: # 可选,相当于 docker network create
  • 实践
1
2
3
4
5
6
7
8
9
# docker container run -d -p 80:80 wordpress
version: '3.8'

services:
  my-wordpress:
    container_name: Jungle
    image: wordpress:latest
    ports:
      - 80:80
1
2
3
docker compose up # 主动,占用命令行
docker compose up -d # 被动,后台开启
docker compose stop # 停止对应的容器
  • compose镜像的构建和拉取
1
2
3
4
5
6
7
8
9
# docker container run -d -p 80:80 wordpress
version: '3.8'

services:
  my-wordpress:
    container_name: Jungle
    image: rust-oslab:latest
    ports:
      - 80:80
  • 只拉取镜像,不启动容器,下次docker compose up的时候会很快
1
docker compose pull

网络配置

  • 主要信息在配置文件里面的Networks部分

  • 内网地址:IPAddress

  • 网关:Gateway:通常是主机地址

  • 使用容器集群的时候要进行网络配置

    • 查看

      1
      
      docker network ls
      
    • 三种模式

      • bridge:桥接模式(网桥),为每一个容器分配和设置一个ip地址,并将容器连接到docker0上面(linux下使用ip addr查看)
      • host:使用主机的网络地址,容器自己没有ip和网关,全部依赖宿主
      • container:使用其他网络配置好了的容器的网络
      • null:不对网络进行任何配置,也可以自定义
1
docker run -it --name bbox01 busybox

  • 创建host类型的容器
1
docker run -it --name nginx1 --network host nginx
  • 创建null类型的容器
1
docker run -it --name bbox2 --network none busybox
  • container
1
2
3
4
# 启动一个新的容器
docker run -it --name bbox01 busybox
# container 模式
docker run -it --name bbox02 --network container:bbox1 busybox
  • 如果container对应的容器关闭,或者关闭之后开启container的容器的网都会断开