一、前言 docker是一个将软件虚拟化的工具,可以在任意设备上,建立虚拟机跑软件,实现快速环境搭建运行
二、安装 1. ubuntu上安装docker 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 信任密钥 curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg # 设置密钥文件权限 sudo chmod a+r /etc/apt/trusted.gpg.d/docker.gpg # 添加安装源 echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/trusted.gpg.d/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 更新软件包索引 sudo apt update # 安装 Docker CE、CLI 和 containerd sudo apt install -y docker-ce docker-ce-cli containerd.io
三、配置 1. docker的默认网段 修改/etc/docker/daemon.json 默认的网段是172.17.0.0/12,size是16。代表分配的网桥段是172.[17-31].0.0/16 下面配置代表新建立的网桥分配段为172.31.[0-255].0/24 例如: 网桥1 172.22.31.1.0/24,网桥2 172.22.31.2.0/24 1 2 3 4 5 6 7 8 { "default-address-pools" : [ { "base" : "172.31.0.0/16" , "size" : 24 } ] }
注意 已经建立好的网桥不会清除,需要停止容器,删除对应网桥重新起容器绑定新的才可以 2. docker的网络模式 host: 不生成虚拟网卡和ip,使用宿主机的网络,无法使用端口映射 container: 和另一个容器共享ip和端口,compose配置network_mode: "container:[container name/id]" none: 关闭容器网络功能,容器无法联网 bridge: 创建一个网桥,虚拟出网卡和ip,通过docker0和iptables配置和主机通信 service: compose的概念,和service共享网络,配置network_mode: "service:[service name]" 四、常用命令 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 # # 查看所有容器 docker ps -a # 停止容器 docker stop [container_id] # 删除container docker rm [container_id] # 将某个容器变成自启动 docker update [container_id] --restart=always # 拷贝文件 docker cp [OPTIONS] [container_id]:[SRC_PATH] [DEST_PATH] docker cp [OPTIONS] [SRC_PATH] [container_id]:[DEST_PATH] # # 列出所有image docker image list # 清理所有没有被container引用的image # -a 所有 # -f 强制,不需要确认 docker image prune -af # 导入一个image docker load -i [image_file] # 导出一个image,使用image_id打包后导入名字会变成none docker save [image_id|image_name:tag] -o xxx.tar # 删除image docker rmi [image_id] # 重命名image docker tag [image_id] xxx # # 查看docker日志 # -f : 跟踪日志输出 # --since : 显示某个开始时间的所有日志 # -t : 显示时间戳 # --tail :仅列出最新N条容器日志 docker logs [OPTIONS] [container_name|container_id]
1. docker top 查看docker和宿主机的进程对照关系 1 2 3 4 5 # PID是宿主机进程id ,PPID是docker里面的id => docker top [container_name|container_id] UID PID PPID C STIME TTY TIME CMD nobody 26505 26486 0 2月16 ? 00:02:53 /usr/bin/python3.6 /usr/bin/supervisord -nc /etc/supervisord.conf nobody 26560 26505 0 2月16 ? 00:00:00 npm
2. docker network 查看docker网络情况 1 2 3 4 5 6 7 => docker network list NETWORK ID NAME DRIVER SCOPE f5efcaf6e2e2 bridge bridge local 237ebc86fb1b host host local d082a187db55 none null local # 删除一个网络,最好先停止容器,再删除网络 => docker network rm [network_id]
3. docker search 搜索镜像 1 2 3 4 5 6 7 => docker search kali NAME DESCRIPTION STARS OFFICIAL AUTOMATED kalilinux/kali-rolling Official Kali Linux Docker image (weekly sna… 786 kasmweb/kali-rolling-desktop Kali Rolling desktop for Kasm Workspaces 11 kalilinux/kali-bleeding-edge Same as kali-rolling with kali-bleeding-edge… 52 kalilinux/kali-last-release Image built from the last snapshot of the of… 55 ...
4. docker run 启动镜像 1 docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
4.1. 选项 1) 常用选项 -d: 后台运行-p [host-port:docker-port]: 端口映射--rm: 停止后删除容器--name [name]: 给容器起名字--network [network-mode]: 网络模式,也可以指定自己建立的网络-i: 启动后无需命令也不停止,就是compose的tty为true-v [host-dir:docker-dir]: 目录映射--restart=always: 开机自启动-e [xxx=val]: 设置环境变量示例
1 2 3 4 5 # 启动一个image # -d 后台运行 # -p host:docker 端口映射 # --rm 容器停止后删除容器 docker run -d -p 8085:8080 [image_name]:[tag]
2) cpu限制 --cpus=<value>: 限制cpu核心数,--cpus='1.5'代表使用一个半核--cpuset-cpus: 限制使用特定的cpu,0-3代表前4个核,1,3代表第二个和第4个--cpu-period: 设置每个容器进程的调度周期(CFS)下,范围1000-1000000对应单位us--cpu-quota: 设置每个周期内容器能使用的cpu时间,和--cpu-period配合使用,quota大于period代表使用多个核,不能小于1000示例
1 2 3 4 5 6 7 8 9 # 最大使用1.5个核 docker run -d golang:latest --cpus=1.5 # 限制使用0、2、3这三个核 docker run -d golang:latest --cpuset-cpus=0,2-3 # docker进程调度周期为50ms,每个周期docker使用25ms的cpu,也就是最多占用0.5个核 # quota大于period代表使用多个核 docker run -d golang:latest --cpu-period=50000 --cpu-quota=25000
3) 内存限制 -m: 指定内存使用限额,如200M--memory-swap: 设置内存+swap的限额,默认为-m的两倍,也就是内存和swap相同4) 启动后不退出 (1) 使用tail命令不退出 1 docker run xxx tail -f /dev/null
(2) 使用sleep命令不退出 1 docker run xxx sleep infinity
(3) 使用-i选项 5. docker build 基于dockerfile制作镜像 1 docker build [OPTIONS] PATH | URL | -
PATH指定的是工作目录,用于COPY等命令从相对目录拷贝文件
1) 选项 -f <dockerfile_path>: 指定dockerfile路径-t <image_name:version>: 指定镜像名和版本--no-cache: 不使用缓存,dockerfile没变不会新生成镜像,禁用缓存每次都新生成镜像五、dockerfile语法和编写 1. 命令解释 命令 解释 FROM 指定基础镜像,用于后续的指令构建。 MAINTAINER 指定Dockerfile的作者/维护者。(已弃用,推荐使用LABEL指令) LABEL 添加镜像的元数据,使用键值对的形式。 RUN 在构建过程中在镜像中执行命令。 CMD 指定容器创建时的默认命令。(可以被覆盖) ENTRYPOINT 设置容器创建时的主要命令。(不可被覆盖) EXPOSE 声明容器运行时监听的特定网络端口。 ENV 在容器内部设置环境变量。 ADD 将文件、目录或远程URL复制到镜像中。 COPY 将文件或目录复制到镜像中。 VOLUME 为容器创建挂载点或声明卷。 WORKDIR 设置后续指令的工作目录。 USER 指定后续指令的用户上下文。 ARG 定义在构建过程中传递给构建器的变量,可使用 “docker build” 命令设置。 ONBUILD 当该镜像被用作另一个构建过程的基础时,添加触发器。 STOPSIGNAL 设置发送给容器以退出的系统调用信号。 HEALTHCHECK 定义周期性检查容器健康状态的命令。 SHELL 覆盖Docker中默认的shell,用于RUN、CMD和ENTRYPOINT指令。
2. 示例 1) 使用golang:latest制作一个golang服务镜像 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 FROM golang:latestARG CODE_DIR="/code" RUN echo 'root:1' | chpasswd RUN mkdir -p ${CODE_DIR} && \ useradd -m -s /bin/bash test && \ chown -R test :test ${CODE_DIR} COPY --chown =test :test ./test.go ${CODE_DIR} USER test:testWORKDIR ${CODE_DIR} ENTRYPOINT ["go" , "run" , "test.go" ]
制作镜像命令
1 docker rmi test-con:v2 && docker build -t test-con:v2 -f Dockerfile --no-cache .
六、docker-compose.yml解释和编写 1. 基础用法示例 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 version: '3' services: sms_server: container_name: sms_server image: 'node:latest' ports: - "7878:7878" volumes: - .:/code:ro network_mode: "bridge" environment: MYSQL_ROOT_PASSWORD: "123456" GO111MODULE: 'on' GOPROXY: 'https://goproxy.cn' working_dir: /code command: ["node" , "server.js" ] restart: always privileged: false cap_add: - ALL cap_drop: - NET_ADMIN - SYS_ADMIN
2. service和container service是docker-compose的概念,container是docker的概念 一个service可以存在多个container,对应到docker ps就是多个container,docker ps无法查看service的名字 同一个service下面可以起多个相同的容器docker-compose up --scale sms_server=5不能指定container_name,指定就无法起,默认使用<当前路径名>_<service name>_<序号>起名 如果指定了端口映射,这里也会报错,因为端口被占用 3. cpu限制 services.<server_name>.deploy.resources.limits.cpus和docker run的--cpus对应,具体含义见上面解释1 2 3 4 5 6 7 8 9 10 11 12 13 version: '3' services: other-compile: container_name: other-compile image: 'cicd_2601/package-env:go1.20.2' deploy: resources: limits: cpus: 4 network_mode: 'host' tty: true privileged: false restart: always
七、实战 1. 启用一个sms_server服务 根据一个实例可以快速上手
1.1. 需求 将一个nodejs实现的服务器部署到docker上 代码在服务器硬盘,可以实现修改了代码,重启一个服务就可以生效 1.2. 服务器代码 很简单的一个监听7878端口http请求,统一返回一个json的设置cookie的代码,运行也只需要node server.js
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 const http = require ("http" );const url = require ("url" );let json = { "code" : "0" , "llll" : "test" , "aaaa" : "stest" , "token" : "a454as1bbvd5g5s15155" } let index = 0 ;http.createServer (function (req, res ) { res.setHeader ("Set-Cookie" , ["c=1;httponly" , "b=2" , "a=3" ]); res.writeHead (200 , { "Content-Type" : "application/json" , "Content-Length" : Buffer .from (JSON .stringify (json)).length }) res.write (JSON .stringify (json)) index++ console .log (`<h3>${index} </h3>` ) req.on ("data" , function (chunk ) { console .log (chunk.toString ()) }) res.end () }).listen (7878 , "0.0.0.0" ) console .log ("the server is starting" );
1.3. 编写docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 version: '3' services: sms_server: container_name: sms_server image: 'node:latest' ports: - "7878:7878" volumes: - .:/code:ro network_mode: "bridge" working_dir: /code command: ["node" , "server.js" ] restart: always
1.4. 运行 1 2 3 # 指定yml文件,-d后台运行 # --force-recreate 强制重建 sudo docker-compose -f server.yml up -d
2. 使用mysql 2.1. 安装 1 docker pull mysql:latest
2.2. 运行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 version: '3' services: mysql: container_name: mysql image: 'mysql:latest' volumes: - ./data/var_lib_mysql:/var/lib/mysql:rw ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: "123456" tty: true restart: always
3. 构建deepin开发环境 基于镜像168447636/deepin20
3.1. 初始化设置 1 2 3 4 5 6 7 # 安装必备软件 apt install zsh tmux git gdb iptables net-tools dnsutils apt-file locales wget curl iputils-ping apt autoremove # 更新apt-file apt-file update # 设置时区 ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
1 docker commit deepin_worker deepin_worker
3.2. docker-compose.yml编写 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 version: '3' services: deepin_worker: container_name: deepin_worker image: 'deepin_worker:latest' network_mode: 'bridge' volumes: - .:/code:rw environment: GO111MODULE: 'on' GOPROXY: 'http://goproxy.cn' GOSUMDB: 'off' working_dir: /code tty: true privileged: true restart: always
八、源码阅读 1. docker各个组件之间的关系 小技巧和踩坑记 1. docker容器内部无法使用iptables命令 容器本身是跟操作系统绑定,并非完整的系统,所以iptables其实操作的是宿主机的 如果是编译环境做测试用的,可以直接加上--privileged或--add-cap NET_ADMIN 2. docker pull报错Error: remote trust data does not exist for xxx 明确是信任的镜像,加上DOCKER_CONTENT_TRUST=false运行即可
1 DOCKER_CONTENT_TRUST=false docker pull xxx
3. docker修改镜像和容器存储目录 docker的默认目录在/var/lib/docker,如果根目录分配比较小的情况下会导致根目录占满
1 2 => docker info | grep -i root Docker Root Dir: /var/lib/docker
修改此目录按照下面的命令执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 先停止docker服务 => systemctl stop docker docker.socket # 拷贝到新目录 => cp -r /var/lib/docker /path/to/new # 修改docker配置文件 => vim /etc/docker/daemon.json # 修改如下内容 # { # "data-root" : "/path/to/new" # } # 重启docker即可 => systemctl start docker # 老的目录删掉 => rm -rf /var/lib/docker
和上面第2点是一样的,都是不信任导致,临时规避方式一样
1 DOCKER_CONTENT_TRUST=0 docker run xxx
5. docker信任一个证书 将证书下载到本地的文件中
1 echo | openssl s_client -connect domain:port | openssl x509 -outform PEM > domain.pem
让docker信任此证书需要先把证书拷贝到目录下
1 2 mkdir -p /etc/docker/cert.d/xxx cp domain.pem /etc/docker/cert.d/xxx/
然后在docker的配置文件里面配置上此域名
1 2 3 4 => cat /etc/docker/daemon.json { "insecure-registries": ["domain:port"] }
配置完直接重新加载docker配置即可