docker使用入门

参考资料

  1. 初级指导: Docker overview | Docker Documentation

名词解释

  • 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
  • 容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
  • 仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。
  • 主机: 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
  • Docker Registry: Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用
  • PGP: Pretty Good Privacy 是一套用于消息加密、验证的应用程序. PGP 本身是商业应用程序;对应的开源软件为 GPG(GnuPG)
  • GPG: GNU Privacy Guard, 也是一种加密软件,它是 PGP 加密软件的开源替代程序. 在docker中主要用于容器的加密?
  • volume: 卷
  • Flask: 是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务
  • bind mount: linux命令,将一个目录挂载到另一个
  • CI / CD: Continuous Integrate, Continuous Deploy 持续集成和持续交付
  • Dockerfile: 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
  • Compose: 您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。 比Dockerfile集成度更高。或者说前者用来启动程序,后者用来生成镜像
  • overlay: 相当于创建一个虚拟网络,将主机,多个容器,甚至多个docker daemon均覆盖进来,彼此之间均可以通信
  • CNM: Container Network Model
  • bridge: 早期的交换机雏形,交换机可以认为是多个bridge的集合。转发二层帧,起到信号放大的作用。网卡相当于是一个网桥/交换机,两者在一个网段内。arp和ping均可以通过
  • automatic service discovery: 类似于DNS服务,使得 ping -c 2 alpine2 这样的操作可以实现
  • kvm: Kernel-based Virtual Machine,是一种内建于linux中的开源虚拟化技术
  • QEMU: 一种通用的开源计算机仿真器和虚拟器

安装

docker desktop

以下安装方法针对ubuntu

系统准备

在虚拟机上使用docker需要安装kvm

modprobe kvm
modprobe kvm_intel  
lsmod | grep kvm
ls -al /dev/kvm
sudo usermod -aG kvm $USER   # 添加用户态权限

安装新版qemu,文档要求5.2以上

wget https://download.qemu.org/qemu-7.1.0-rc3.tar.xz
tar xvJf qemu-7.1.0-rc3.tar.xz
cd qemu-7.1.0-rc3
./configure
make
sudo make install

configure需要安装ninja

sudo apt install re2c
git clone https://gitee.com/hclink_ai/ninja.git

cd ninja
python3 configure.py --bootstrap 

sudo cp ./ninja  /usr/bin
ninja --version

文件共享

 grep "$USER" /etc/subuid >> /dev/null 2&>1 || (echo "$USER:100000:65536" | sudo tee -a /etc/subuid)
 grep "$USER" /etc/subgid >> /dev/null 2&>1 || (echo "$USER:100000:65536" | sudo tee -a /etc/subgid)
 
 echo $USER
 cat /etc/subuid
 cat /etc/subgid
sudo add-apt-repository ppa:jacob/virtualisation
sudo apt update
sudo apt -y install libvirt-clients libvirt-daemon bridge-utils 
sudo apt install grub-common
grub2-editenv - set "$(grub2-editenv - list | grep kernelopts) intel_iommu=on"   # 开启IOMMU

sudo chmod a+rw /var/lib/docker

$ sudo service docker stop
$ sudo systemctl disable docker.service
$ sudo systemctl disable docker.socket

开始安装

安装前首先要清理原有的老文件

 sudo apt remove docker-desktop
 rm -r $HOME/.docker/desktop
 sudo rm /usr/local/bin/com.docker.cli
 sudo apt purge docker-desktop

如果没有gnome,需要先安装

sudo apt install gnome-terminal

开始安装Docker

sudo apt-get update
# 依赖
sudo apt-get install ca-certificates curl gnupg lsb-release
# GPG key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 设置repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装engine
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 需要update之后才行,比较大,有500多M
# 也可以通过deb安装 Docker Desktop
sudo apt-get install ./docker-desktop-<version>-<arch>.deb
# 80多M

生成凭据,否则无法登录

gpg --generate-key
pass init 7865BA9185AFA2C26C5B505669FC4F36530097C2

注册

登录

验证

docker compose version
docker --version
docker version

加入用户组

docker ps # 正常会显示权限错误,此时需要加入组
sudo gpasswd -a $USER docker 
newgrp docker

docker engine

可以使用脚本自动化安装

curl -sSL https://get.docker.com/ | sh

一般安装

sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt-get install ca-certificates curl gnupg lsb-release
sudo mkdir -p /etc/apt/keyrings

 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
 
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose
 

换源

阿里云的加速器:https://help.aliyun.com/document_detail/60750.html

网易加速器:http://hub-mirror.c.163.com

官方中国加速器:https://registry.docker-cn.com

ustc 的镜像:https://docker.mirrors.ustc.edu.cn

在 settings-> Docker Engine 中加入以下配置即可

"registry-mirrors": [
    "http://hub-mirror.c.163.com"
  ]

如果是docker engine,换源方法如下

cd /etc/docker
vim daemon.json
# 添加源
{
  "registry-mirrors": [
    "https://hub-mirror.c.163.com",
    "https://ghcr.io",
    "https://mirror.baidubce.com"
  ]
}

service docker restart


使用

常用方法

查看类

docker images      # 查看镜像
docker ps          # 查看容器
docker ps -a       # 查看容器详细信息
docker --help      # 查看帮助
docker -v          # 查看版本
docker attach xx   # 查看xx容器运行情况

运行类

docker run xx     # 运行xx镜像
docker run xx && python3 hello.py -yy zz # 运行xx镜像并启动容器中的程序,后面可以加入命令行参数zz
docker run xx python3 hello.py     # 同上
docker run -dit xx # 以互动方式后台运行镜像
docker start xx   # 运行xx容器
docker stop  xx
docker restart xx

管理类

systemctl start docker   # 启动守护程序
systemctl stop docker
systemctl restart docker

docker system prune # 删除停止的容器,无用的网络,清缓存,删除无用的镜像
docker rm xx        # 删除指定容器,支持简写前几位进行模糊匹配
docker rmi xx       # 删除指定镜像

docker build -t xx .  # 在当前文件夹选择Dockerfile进行镜像构建,镜像名为xx
sudo docker login -u xxx -p xxx  # 上传前需要先登录
sudo docker tag storm moonfish/storm:v1.1 # 上传前要先重命名,格式为  账号/镜像名:版本号
sudo docker push moonfish/baikal:v1  # 上传
sudo docker pull moonfish/baikal:v1  # 下载

命令行参数

docker build

–network=host 相当于虚拟机中的host only选项,主机和容器共享网卡,名字也相同。

如果不输入参数,那么docker会自动构建一个桥接网络,容器会连接至虚拟网卡 docker0, 地址为127.17.0.1; 而容器会自动被赋予一个127网段的地址

docker run

  • -a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
  • -d: 后台运行容器,并返回容器ID;
  • -i: 以交互模式运行容器,通常与 -t 同时使用;
  • -P: 随机端口映射,容器内部端口随机映射到主机的端口
  • -p: 指定端口映射,格式为:主机(宿主)端口:容器端口
  • -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;提示符会变为#号
  • –name=“nginx-lb”: 为容器指定一个名称;
  • –dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;
  • -e username=“ritchie”: 设置环境变量
  • –env-file=[]: 从指定文件读入环境变量;
  • **-m 😗*设置容器使用内存最大值;
  • –link=[]: 添加链接到另一个容器;
  • –volume , -v: 绑定一个卷

docker exec

  • -d: Detached Mode: 后台运行
  • -e: 设置环境变量
  • -i: 展示容器输入信息STDIN
  • –privileged: 为命令提供一些扩展权限
  • -t: 命令行交互模式
  • -u: 设置用户名(format: <name|uid>[:<group|gid>])
  • -w: 指定容器内的目录

docker network

docker的网络管理调用的是iptable

docker network create -d bridge my-bridge-network  # 建立一个自定义的网络,指定为bridge模式;还可以为overlay模式
docker network create -d overlay my-multihost-network  # overlay模式,多个容器之间可以放入同一个网段,有docker统一管理
sysctl net.ipv4.conf.all.forwarding=1   # 让自定义网络能够与外界通信
sudo iptables -P FORWARD ACCEPT  # iptables 放通

other

docker create --name my-nginx --network my-net --publish 8080:80 nginx # 通过nginx镜像创建容器,指定名称,指定网络,指定端口映射
docker network connect my-net my-nginx  # 连接正在运行的容器与指定网络

镜像制作

python

代码

python依赖

cd /path/to/python-docker
pip3 install Flask
pip3 freeze | grep Flask >> requirements.txt

requirements.txt

scapy==2.4.5
pika==1.3.0
nmap==0.0.1

Dockerfile

# syntax=docker/dockerfile:1
FROM python:3.8-slim-buster
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .

FROM ubuntu:20.04
WORKDIR /app
RUN ["mkdir", "-p", "/var/log/stormEngine"]
RUN ["apt", "update"]
RUN ["apt", "-y", "install", "ethtool", "tcpreplay", "iputils-ping"]
COPY . /app
FROM ubuntu:20.04
WORKDIR /app
COPY . /app

ENV TZ=Asia/Shanghai \
    DEBIAN_FRONTEND=noninteractive

RUN mkdir -p /var/log/stormEngine \
    && apt update \
    && apt install -y tzdata net-tools ethtool tcpreplay iputils-ping\
    && ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
    && echo ${TZ} > /etc/timezone \
    && dpkg-reconfigure --frontend noninteractive tzdata \
    && rm -rf /var/lib/apt/lists/* 
build
docker build --tag hello .
docker build -t hello .
run
docker run hello
push
sudo docker tag storm moonfish/storm:v1.1
sudo docker login -u moonfish -p xxx
sudo docker push moonfish/storm:v1.1

java

jdk安装
sudo wget https://download.oracle.com/java/18/latest/jdk-18_linux-x64_bin.tar.gz

sudo vi /etc/profile
# 添加以下3行
export JAVA_HOME=/home/ubuntu/Programm/openjdk-18.0.2.1_linux-x64_bin/jdk-18.0.2.1
export CLASSPATH=.:$JAVA_HOME/lib
export PATH=.:$JAVA_HOME/bin:$JAVA_HOME/lib:$PATH

source /etc/profile 

或者

sudo apt install openjdk-18-jre-headless  # version 18~36ea-1
代码

将代码打包为jar包,需要在pom.xml文件中加入插件,以下代码可以同时生成胖包和瘦包

<build>
        <plugins>
            <plugin>
                <!-- thin jar -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.2</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>main.MainApp</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <!-- add dependencies this jar -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.1.2</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <!-- fat jar -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>main.MainApp</mainClass><!--这里改成自己的主类位置-->
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
FROM amazoncorretto:17.0.4-alpine3.15
WORKDIR /app
COPY . /app
ENV PATH=$PATH:$JAVA_HOME/bin
ENV JRE_HOME=${JAVA_HOME}/jre
ENV CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib

FROM eclipse-temurin:11-jre-jammy
WORKDIR /app
COPY . /app
RUN ["mkdir", "-p", "/app/data"]
RUN ["mkdir", "-p", "/data/pcap_files"]
RUN ["cp", "/app/libpcap.so.1.9.1", "/usr/lib/x86_64-linux-gnu"]

加入libpcap.so.1.9.1

FROM eclipse-temurin:11-jre-jammy
WORKDIR /app
COPY . /app
ENV TZ=Asia/Shanghai \
    DEBIAN_FRONTEND=noninteractive
RUN mkdir -p /app/data /data/pcap_files \
    && cp /app/libpcap.so.1.9.1 /usr/lib/x86_64-linux-gnu \
    && apt update \
    && apt install -y tzdata \
    && ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
    && echo ${TZ} > /etc/timezone \
    && dpkg-reconfigure --frontend noninteractive tzdata \
    && rm -rf /var/lib/apt/lists/* 

C

需要将所有依赖的库都拷贝到/app目录下,系统会自动优先链接,以下命令可以查看

lld baikal
代码
FROM ubuntu:16.04
WORKDIR /app
COPY . /app
ENV TZ=Asia/Shanghai \
    DEBIAN_FRONTEND=noninteractive
RUN mkdir -p /usr/local/baikal /data/pcap_files \
    && apt update \
    && apt install -y tzdata \
    && ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
    && echo ${TZ} > /etc/timezone \
    && dpkg-reconfigure --frontend noninteractive tzdata \
    && rm -rf /var/lib/apt/lists/* 

多阶段构建

备注

  1. A 2018 analysis found that a typical Docker use case involves running eight containers per host

  2. 需要开启nested virtualization,否则docker desktop无法使用
    方法: 虚拟机设置->硬件->处理器->虚拟化 IntelVT-x/EPT
    另外

    sudo apt -y install libvirt-clients libvirt-daemon qemu
    sudo add-apt-repository ppa:jacob/virtualisation
    
  3. image是镜像,位置在/var/lib/docker,里面有(没有镜像,)容器和分层,都存储在这里; 镜像位置是:setting->advanced->disk image location中可以查看和修改

  4. and the volume’s contents exist outside the lifecycle of a given container.

  5. C语言如果使用静态编译,那么就不需要向容器中拷贝依赖库了,镜像也会变得非常小。构建时只需要 From scratch即可

  6. 如果是C的helloworld 镜像必须添加CMD,否则无法运行。 因为镜像中没哟bash程序

  7. “#” prompt 默认是root权限

  8. 若无特殊配置,不同host之间的容器无法通信; 除非使用overlay模式

  9. File sharing 可以在IDE中修改源代码,在容器中测试

  10. linux下docker desktop 会出现各种问题,最好用命令行操作

  11. 发布镜像时, 需要重命名为dockerhub用户名/镜像名,然后再push

  12. desktop 和 docker engine的配置并不通用, bug太多,无法使用

  13. 时区修改

    ENV TZ=Asia/Shanghai \
        DEBIAN_FRONTEND=noninteractive
    
    RUN apt update \
        && apt install -y tzdata \
        && ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
        && echo ${TZ} > /etc/timezone \
        && dpkg-reconfigure --frontend noninteractive tzdata \
        && rm -rf /var/lib/apt/lists/*