Docker日常操作


我们日常使用容器技术,主要且常用的操作就是涉及使用容器、操作镜像、访问仓库,需要融会贯通。本文主要参考《Docker — 从入门到实践》一书进行总结而来。

Docker日常操作

Docker日常操作


0. 常用操作

https://github.com/dennyzhang/cheatsheet-docker-A4

Docker日常操作

Docker日常操作

Docker日常操作

Docker日常操作


1. 使用镜像

Ubuntu/Debian上有UnionFS可以使用,如aufs或者overlay2,而CentOSRHEL的内核中没有相关驱动。因此对于这类系统,一般使用devicemapper驱动利用LVM的一些机制来模拟分层存储。这样的做法除了性能比较差外,稳定性一般也不好,而且配置相对复杂。

  • [1] 获取镜像 - pull
# 获取镜像的格式
# 仓库地址: 格式为域名或IP地址,默认为官方仓库地址
# 仓库名称: 格式为两段式,用户名/软件名,默认用户名为library官方镜像
docker pull [选项] [仓库地址:端口号]/仓库名称[:标签]
# 获取docker镜像
docker pull ubuntu:16.04

# 运行docker容器且退出后立即删除
docker run -it --rm <image:tag> bash
  • [2] 列出镜像 - ls
# 列出本地docker镜像
docker image ls

# 列出本地docker镜像,包括中间层镜像
docker image ls -a

# 列出本地镜像摘要
docker image ls --digests

# 列出本地部分docker镜像
docker image ls ubuntu

# 只列出docker镜像的ID信息,便于后续操作
docker image ls -q
docker image rm $(docker image ls -q -f before=mongo:3.2)
docker image ls -q -f before=mongo:3.2 | xargs docker image rm

# 筛选出redis之前/后构建的镜像列表
docker image ls -f before=redis
docker image ls -f since=redis

# 指定格式化输出docker镜像信息
docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
# 查看镜像、容器、数据卷所占用的空间
docker system df

# 显示悬空镜像
docker image ls -f dangling=true

# 删除悬空镜像
docker image prune
  • [3] 删除镜像 - rm
# 删除本地镜像格式
# 镜像可以是: 镜像短ID、镜像长ID、镜像名称、镜像摘要
docker image rm [选项] <镜像1> [<镜像2> ...]
# 镜像短ID
docker image rm cd6d8154f1e1

# 镜像名称
docker image rm centos:latest
  • [4] 镜像构建 - commit
# 如果不使用卷的话,任何文件修改都会被记录于容器存储层里
# 可使用commit命令,将容器的存储层保存下来成为一个镜像
# 用docker的commit命令就意味着所有对镜像的操作都是黑箱操作
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
# 创建一个nginx服务的容器
docker run --name webserver -d -p 80:80 nginx

# 进入容器修改页面显示内容
docker exec -it webserver bash

# 查看容器存储层的具体改动
docker diff webserver

# 保存存储层的内容成为镜像
docker commit -a "Escape" -m "Change Page" webserver nginx:v1.0

# 查看镜像内的历史记录
docker history nginx:v1.0

# 创建我们的新版本nginx服务的容器
docker run --name web2 -d -p 81:80 nginx:v1.0
  • [5] 定制镜像 - Dockerfile
# Dockerfile可以把每一层修改、安装、构建、操作的命令都写入一个脚本
# Dockerfile支持Shell类的行尾添加\的命令换行方式,以及行首#进行注释

# 以scratch为基础镜像的话,意味着你不以任何镜像为基础进行构建
# 使用Go微服务架构语言开发的应用很多会使用scratch这种方式来制作镜像
docker build [选项] <上下文路径/URL/->

# 容器build的原理
# Docker在运行时分为Docker引擎(提供API接口)和客户端工具(Docker命令)
# 通过API与Docker引擎交互,从而在引擎上完成各种功能,从而让远程调用变的简单
# 编写Dockerfile文件
FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

# 通过Dockerfile构建镜像
docker build -t redis:v1.0 .

# 通过URL构建镜像
docker build https://github.com/twang2218/gitlab-ce-zh.git#:11.1

# 通过压缩包构建镜像
docker build http://server/context.tar.gz
序号 命令 对应含义
1 FROM 指定基础镜像;必选且必须是第一条指令
2 RUN 执行命令;建议命令合并执行;不得超过 127 层
3 COPY 复制文件;可以使用通配符;保留源文件元数据信息
4 ADD 更高级的复制文件;URL 路径会自动下载;压缩包会自动解压
5 CMD 在启动时指定默认的容器主进程的启动命令;容器内没有后台服务的概念
6 ENTRYPOINT 入口点;与 CMD 一样都是在指定容器启动程序及参数;CMD 为参数
7 ENV 设置环境变量;后续的指令中可以使用;使用空格和等号为分隔符
8 ARG 构建参数;类似于 ENV 但不会在将来容器运行时看到;不要保存密码信息
9 VOLUME 定义匿名卷;任何向匿名卷中写入信息都会存储在匿名卷,而非存储层
10 EXPOSE 声明端口;在运行时并不会因为声明应用就会开启这个端口的服务
11 WORKDIR 指定工作目录;以后各层的当前目录就被改为指定的目录
12 USER 指定当前用户;该用户必须是事先建立好的;建议使用 gosu 命令
13 HEALTHCHECK 设置检查容器健康状况的命令;只可以出现一次
14 ONBUILD 在当前镜像构建时并不会被执行,去构建下一级镜像时才会被执行
# RUN vs CMD vs ENTRYPOINT

# 1.RUN: 执行命令并创建新的镜像层,经常用于安装软件包
#     Shell格式:RUN
#     Exec格式:RUN ["executable", "param1", "param2"]

# 2.CMD: 设置容器启动后默认执行的命令及其参数,但能够被docker的run命令后面跟的命令行参数替换
#     Exec格式:CMD ["executable","param1","param2"]
#     CMD ["param1","param2"] 为ENTRYPOINT提供额外的参数
#     Shell 格式:CMD command param1 param2

# 3.ENTRYPOINT: 配置容器启动时运行的命令,推荐使用Exec格式且可为可执行文件
#     Exec格式:ENTRYPOINT ["executable", "param1", "param2"]
#     Shell格式:ENTRYPOINT command param1 param2
# HEALTHCHECK [选项] CMD <命令>: 设置检查容器健康状况的命令
# HEALTHCHECK NONE: 如果基础镜像有健康检查指令使用这行可以屏蔽掉其健康检查指令

# 间隔时间: --interval=30s
# 超过时长: --timeout=30s
# 重试次数: --retries=3
# 间隔时间: --start-period=5s

# 查看健康检查命令的输出
docker inspect --format '{{json .State.Health}}' web | python -m json.tool
  • [6] 多阶段构建 - Dockerfile
FROM golang:1.9-alpine
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]
  • [7] 其它制作镜像的方式
# 1.使用Dockerfile生成镜像
docker build -t go/helloworld:v0.1 .

# 2. 可以修改镜像的标签
docker tag [容器ID] remgoote/helloworld:0.0.1
# 容器导出为文件
docker export [容器ID] > ubuntu.tar

# 导入容器文件
docker import [选项] <文件>|<URL>|- [<仓库名>[:<标签>]]
# 镜像导出为文件
docker save alpine -o alpine.tar
docker save alpine | gzip > alpine-latest.tar.gz

# 导入镜像文件
docker load -i alpine-latest.tar.gz

2. 操作容器

简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。

  • [1] 启动容器
# 启动一个容器,Docker在后台会运行如下步骤
- 1.检查本地是否存在指定的镜像,不存在就从公有仓库下载
- 2.利用镜像创建并启动一个容器
- 3.分配一个文件系统并在只读的镜像层外面挂载一层可读写层
- 4.从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 5.从地址池配置一个IP地址给容器
- 6.执行用户指定的应用程序
- 7.执行完毕后容器被终止
# 1.基于镜像新建一个容器并启动
# -i: 让容器的标准输入保持打开
# -t: 分配一个伪终端并绑定到容器的标准输入上
docker run -it ubuntu:18.04 /bin/bash
# 2.将在终止状态的容器重新启动
docker container start ubuntu:18.04
# 3.以守护态运行容器
docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo 1; done"

# 获取容器的输出信息
docker container logs [container ID or NAMES]
  • [2] 终止容器
# 终止一个正在运行的容器
# 容器中指定的应用终结时,容器也会自动终止
docker container stop ubuntu:18.04
docker container restart ubuntu:18.04

docker container pause ubuntu:18.04
docker container unpause ubuntu:18.04
  • [3] 进入容器
# [attach]命令
# 如果进入容器之后执行exit命令会导致容器的停止
docker attach 243c32535da7
# [exec]命令
# 不会因为exit命令导致容器的停止,推荐使用
docker exec -it 243c32535da7 bash
  • [4] 导出和导入容器
# 导出容器快照到本地文件
docker export 7691a814370e > ubuntu.tar
# 从容器快照文件中再导入为镜像
cat ubuntu.tar | docker import - test/nginx:v1.0

# 也可以通过指定URL或某个目录来导入
docker import http://example.com/exampleimage.tgz example/imagerepo

# docker load: 导入镜像存储文件到本地镜像库
# docker import: 导入一个容器快照到本地镜像库
# 两者的区别: 容器快照文件将丢弃所有的历史记录和元数据信息,而镜像存储文件将保存完整记录,体积也要大。
docker save app:0.0.1 -o app-0.0.1.tar
docker save app:0.0.1 | gzip > app-0.0.1.tar.gz
docker load -i app-0.0.1.tar.gz

# 从一个机器将镜像迁移到另一个机器,并且带进度条的功能
docker save app-0.0.1.tar.gz | bzip2 | pv | ssh <用户名>@<主机名> 'cat | docker load -i'
  • [5] 删除容器
# 删除一个处于终止状态的容器
docker container rm [container ID or NAMES]

# 删除一个运行中的容器
docker container rm -f [container ID or NAMES]

# 清理所有处于终止状态的容器
docker container prune

3. 访问仓库

实际上注册服务器(Registry)是管理仓库(Repository)的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。

Docker日常操作

  • [1] 公共仓库
# DockerHub是官方维护了一个公共仓库,其为Docker的默认安装源。
# 交互式的输入用户名及密码完成登录
docker login
docker logout

# 获取拉取远程镜像
docker search centos
docker search centos:latest

# 打标推送本地镜像
# 注意这里打标操作的username是自己dockerhub的名称,否则无法推送
docker tag ubuntu:18.04 username/ubuntu:base
docker push username/ubuntu:base
docker search username

# 高级搜索操作
docker search --limit 5 centos
docker search --filter=stars=1000 centos
docker search --filter=is-official=true centos
docker search --filter=is-automated=true centos
# [自动构建镜像]
# 允许用户通过指定跟踪一个目标网站上的项目,一旦项目发生新的提交或者创建新的标签
# 就会自动构建镜像并推送到DockerHub中,目前支持GitHub或BitBucket

# 配置自动创建的步骤
# - 1.创建并登录DockerHub及目标网站
# - 2.在目标网站中连接帐户到DockerHub
# - 3.在DockerHub中配置一个自动创建
# - 4.选取一个目标网站中的项目和分支,需要含Dockerfile文件
# - 5.指定Dockerfile文件的位置并提交创建
  • [2] 私有仓库
# 通过docker-registry部署私有仓库
docker run -d -p 5000:5000 --restart=always --name registry registry

# 通过-v参数来将镜像文件存放在本地的指定路径
docker run -d -p 5000:5000 -v /opt/data/registry:/var/lib/registry registry
# 在私有仓库上传/搜索/下载镜像 - 标记之后推送到仓库

# 标记本地镜像
docker tag ubuntu:18.04 127.0.0.1:5000/ubuntu:latest
# 上传标记镜像
docker push 127.0.0.1:5000/ubuntu:latest

# 查看仓库镜像
curl 127.0.0.1:5000/v2/_catalog
# 下载仓库镜像
docker pull 127.0.0.1:5000/ubuntu:latest
# Docker默认不允许非HTTPS方式推送镜像,需要配置才可以
$ vim /etc/docker/daemon.json
{
    "registry-mirror": [
        "https://registry.docker-cn.com"
    ],
    "insecure-registries": [
        "192.168.199.100:5000"
    ]
}
  • [3] 私有仓库的高级配置
#【1】搭建HTTPS私有仓库
# 使用openssl自行签发docker.domain.com的站点SSL证书
# 生成网站SSL私钥docker.domain.com.key和SSL证书docker.domain.com.crt
# 新建ssl文件夹并将docker.domain.com.key和docker.domain.com.crt这两个文件移入, 删除其他文件。

# 第一步: 创建CA私钥(即CA证书的私有Key)
# genrsa: 生成RSA私钥; 4096: 生成Key的长度
# -des3: 生成的Key使用des3进行加密; 加个这个参数需要输入密码
$ openssl genrsa -des3 -out "root-ca.key" 4096

# 第二步: 利用私钥创建CA根证书请求文件
# 生成CA的公钥部分,即我们所说的证书,会询问地区、组织、时区等
$ openssl req -new -x509 -days 365 \
    -key "root-ca.key" \
    -out "root-ca.csr" -sha256 \
    -subj '/C=CN/ST=Shanxi/L=Datong/O=Your Company Name/CN=Your Company Name Docker Registry CA'

# 第三步,配置CA根证书并新建root-ca.cnf文件
[root_ca]
basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
subjectKeyIdentifier=hash

# 第四步,签发根证书
$ openssl x509 -req -days 3650 -in "root-ca.csr" \
    -signkey "root-ca.key" -sha256 -out "root-ca.crt" \
    -extfile "root-ca.cnf" -extensions \
    root_ca

# 第五步,生成站点SSL私钥
$ openssl genrsa -out "docker.domain.com.key" 4096

# 第六步,使用私钥生成证书请求文件
$ openssl req \
    -new -key "docker.domain.com.key" \
    -out "site.csr" -sha256 \
    -subj '/C=CN/ST=Shanxi/L=Datong/O=Your Company Name/CN=docker.domain.com'

# 第七步,配置证书并新建site.cnf文件
[server]
authorityKeyIdentifier=keyid,issuer
basicConstraints = critical,CA:FALSE
extendedKeyUsage=serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
subjectAltName = DNS:docker.domain.com, IP:127.0.0.1
subjectKeyIdentifier=hash

# 第八步,签署站点SSL证书
$ openssl x509 -req -days 750 -in "site.csr" -sha256 \
    -CA "root-ca.crt" -CAkey "root-ca.key" -CAcreateserial \
    -out "docker.domain.com.crt" -extfile "site.cnf" -extensions server
#【2】配置私有仓库
# 私有仓库默认的config.yml配置文件

$ vim /etc/docker/registry/config.yml
version: 0.1
log:
    accesslog:
    disabled: true
    level: debug
    formatter: text
    fields:
    service: registry
    environment: staging
storage:
    delete:
    enabled: true
    cache:
    blobdescriptor: inmemory
    filesystem:
    rootdirectory: /var/lib/registry
auth:
    htpasswd:
    realm: basic-realm
    path: /etc/docker/registry/auth/nginx.htpasswd
http:
    addr: :443
    host: https://docker.domain.com
    headers:
    X-Content-Type-Options: [nosniff]
    http2:
    disabled: false
    tls:
    certificate: /etc/docker/registry/ssl/docker.domain.com.crt
    key: /etc/docker/registry/ssl/docker.domain.com.key
health:
    storagedriver:
    enabled: true
    interval: 10s
threshold: 3
#【3】生成http认证文件
# 将下面的username和password替换为你自己的用户名和密码

$ mkdir auth
$ docker run --rm --entrypoint htpasswd registry \
    -Bbn username password > auth/nginx.htpasswd
#【4】编辑docker-compose.yml文件
version: "3"

services:
  registry:
  image: registry
  ports:
    - "443:443"
  volumes:
    - ./:/etc/docker/registry
    - registry-data:/var/lib/registry

volumes:
  registry-data:
#【5】修改hosts配置
$ vim /etc/hosts
docker.domain.com 127.0.0.1
#【6】启动服务
$ docker-compose up -d
#【7】测试私有仓库功能

# 登录到私有仓库
$ docker login docker.domain.com

# 尝试推送、拉取镜像
$ docker pull ubuntu:18.04
$ docker tag ubuntu:18.04 docker.domain.com/username/ubuntu:18.04
$ docker push docker.domain.com/username/ubuntu:18.04
$ docker image rm docker.domain.com/username/ubuntu:18.04
$ docker pull docker.domain.com/username/ubuntu:18.04

# 如果我们退出登录,尝试推送镜像
$ docker logout docker.domain.com
$ docker push docker.domain.com/username/ubuntu:18.04
no basic auth credentials

# 发现会提示没有登录,不能将镜像推送到私有仓库中

4. 写在最后

书到用时方恨少,早知应该多努力!

  • 定时删除镜像中间层
$ crontab -l
0 0 * * 0    docker image prune -f
  • 重启自动启动容器
# 添加sleep是因为系统启动时候dockerd服务是在crond服务之后的,可能会导致reboot任务无法生效
$ crontab -l
@reboot    sleep 60 && \
    docker ps -a --format '{{.ID}}  {{.CreatedAt}}  {{.Names}}' | \
    grep -v 'xxx' | \
    sort -t' ' -k2 | \
    awk '{print $1}' | \
    xargs docker container start

@reboot    sleep 60 && \
    docker ps -a --format '{{.ID}}  {{.CreatedAt}}  {{.Names}}' | \
    egrep -v '_[[:digit:]]{6,}' | \
    sort -t' ' -k2 | \
    awk '{print $1}' | \
    xargs docker container start
# 另外,可以通过如下命令查看系统启动流程
$ systemd-analyze plot > systemd.svg

Docker日常操作


文章作者: Escape
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Escape !
  目录