Docker笔记

我的Docker笔记,记录在此,以防忘记。

安装注意事项

安装文档

安装后需要将当前用户加入docker用户组,方可以普通用户权限执行docker:

sudo groupadd docker
sudo usermod -aG docker ${USER}
# 重新登录后生效,或
su -s ${USER}

修改容器镜像文件保存目录

sudo systemctl  stop docker
sudo vim /etc/docker/daemon.json
# 添加如下一行,注意修改想要使用的路径
# "graph":"/docke/image/path"
sudo systemctl start docker

不使用sudo运行docker

docker需要root权限才能运行,下面的命令把当前用户加入docker用户组,重新登录后再执行docker命令时就不需要再加sudo了。

sudo usermod -aG docker $USER

一些说明

  • docker命令行参数中,容器名与ID等效,通常可以互换使用。
  • Dockerfile的指令通常有与之对应的docker run参数。
  • 建议容器化的应用使用stdout/stderr输出日志,不建议通过文件方式打印日志。
  • 阅读文档:
    man docker
    man docker-run # 查看 docker run 的文档
    man docker-exe # 查看 docker exec 的文档
    
  • 查看帮助说明,使用--help选项,如:
    docker container prune --help
    

容器的操作

docker info 查看配置、系统信息等

使用docker info查看系统的信息和docker的当前配置。例如,要查询查询Docker镜像目录可以使用:

docker info |grep 'Docker Root Dir'

docker run - 通过镜像创建容器并运行

下面这条命令运行后会运行Ubuntu系统并进入其命令行:

docker run --name bob -i -t ubuntu /bin/bash

常用参数如下,更多详细的文档可通过man docker-run阅读:

  • --name 指定生成的容器名,如不指定docker会随机生成
  • --restart 有以下几种选项
    • always 始终自动重启
    • on-failure 仅在容器的exit code非0时重启
    • on-failure:5 仅在容器的exit code非0时重启,最多尝试5次
  • --expose=1000-5000 开放容器的端口。相当于Dockerfile里的EXPOSE。
  • -p 配置容器的端口与本地主机端口的映射关系
    • -p 8080:80 将容器的80端口映射至本地的8080端口。
    • -p 127.0.0.1::80 将容器的80端口映射至本地的随机端口,并限制为使用网口127.0.0.1,即只允许本主机访问。注意若不指定网口,默认为0.0.0.0
    • -P 开放并映射所有Dockerfile里EXPOSED参数配置的端口
  • -i 保持容器的STDIN开启
  • -t 为容器分配虚拟TTY,以进入交互式命令行
  • -v /localpath:/remotepath 参考VOLUME
  • -d 以后台服务方式运行
  • --network netname 加入指定的网络。默认为bridge,即通过NAT访问宿主网络。设为host时可直接使用宿主网络。可使用docker network -h命令查看docker容器的网络相关命令。
  • -h 设定容器的hostname。同一网络中,其它容器可通过hostname访问该容器,而不必直接用IP
  • --network-alias myservice 可使用本参数做简单的DNS负载均衡。

以启动MariaDB为例,若数据库尚未初始化,则会自动加载当前目录里的.sh, .sql, .sql.gz文件,参考docker mariadb

docker run   \
--name mydbname \
-v /my/data/path:/var/lib/mysql \
-v $(pwd):/docker-entrypoint-initdb.d \
-e MYSQL_PASSWORD=mypasswordtouse \
-e MYSQL_ROOT_PASSWORD=mypasswordtouse \
-e MYSQL_USER=cl \
-e MYSQL_DATABASE=flamingo \
-p 3306:3306 \
-d \
mariadb:10.5.8 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci

docker start / docker restart 启动/重启容器

可以使用容器名或容器ID启动或重启容器。

docker start bob

docker attach

对于正在运行的容器,本命令可以用容器创建时的参数再次创建会话:

docker attach bob

运行后会进入Ubuntu的BASH窗口。

docker exec 在容器中执行命令

此命令要求容器正在运行。

下面的命令在容器中创建/etc/sample文件:

docker exec bob touch /etc/sample

常用参数说明:

  • -d 以后台方式执行。如果需要运行阻塞型的命令可以使用此参数
  • -u tom 以指定用户运行命令。要求容器能找到此用户
  • -it 交互式命令,如进入容器的BASH窗口

docker stop / kill 停止/杀死 容器

以停止bob容器为例:

  • docker stop bob使用SIGTERM终止容器中的程序。
  • docker kill bob使用SIGKILL终止容器中的程序。

docker ps 查看本地容器列表

docker ps只显示当前正在运行的容器。常用下面几个参数:

  • docker ps -l 显示最近使用的容器
  • docker ps -n3 显示最近使用的3个容器
  • docker ps -a 显示所有容器

docker port 查看容器端口映射

docker port bob 8080

docker inspect 查看容器信息

返回容器的详情,支持Go语言的模板语言提取指定字段:

# 查看IP
docker inspect  -f '{{ .NetworkSettings.IPAddress }}' bob
# 查看文件映射
docker inspect -f "{{ .Config.Volumes }}"  bob

docker rm 删除容器

常用参数举例

  • docker rm tom 删除名称为tom的容器
  • docker rm -f tom 停止并删除名称为tom的容器
  • 停止并删除所有容器:
    docker rm -f `docker ps -a -q`
    

docker logs 查看容器日志

常用参数:

  • docker logs bob -f 持续显示容器的最近日志(与tail -f类似)
  • docker logs bob -f --tail 20 显示日志的最后20行
  • docker logs bob -tf 持续显示容器的最近日志并自动加载时间戳

docker top 查看容器的进程

docker top bob

docker stats 查看容器的资源使用情况

返回容器ID,名称,CPU,内存,磁盘,网络等使用情况

docker stats bob

docker网络配置

查看网络列表:

docker network ls
# NETWORK ID          NAME                DRIVER              SCOPE
# 809e95cacd28        bridge              bridge              local
# 323ddc9202ce        host                host                local
# 576aacb9247d        none                null                local

使用inspect指令查看指定网络的详情:

docker network inspect 809e95cacd28
# 查看哪些容器加入此网络
docker network inspect  -f '{{ .Containers }}' 809e95cacd28

镜像的操作

关于镜像的名称

镜像的命名规则为:[repo]:[tag]

ubuntu:16.04为Ubuntu 16.04版本的镜像。许多流行的镜像还提供多硬件平台版本,例如ubuntu:bionic-20191202@sha256:56ea270e0c0826ab6b155acf5130fbc59fa36703e982bddea3143261fca60b8d为Ubuntu 18.04 Arm版本的镜像(可在Docker Hub先找到系统版本,再从Tag列表里查找硬件版本对应的Tag)。

docker images 查看本地镜像列表

  • docker images 返回所有镜像列表
  • docker images ubuntu 返回所有ubuntu镜像列表

docker rmi 删除镜像

通过镜像ID删除镜像:docker rmi image_id

删除所有未使用的镜像:

docker image prune -a

docker pull 拉取远程镜像

将远程镜像下载到本地:

docker pull ubuntu:16.04

docker search 搜索远程镜像

在远程镜像仓库里搜索指定镜像:

docker search puppet

docker hisotry 查看镜像的生成步骤

docker history --no-trunc=true image_id

制作Docker镜像

要制作镜像,需要将Dockerfile与所需的其它文件放在同一个目录。

有几点需要注意:

  • Dockerfile里的指令只能访问同一目录及子目录里的文件。
  • 生成镜像时,除.dockerignore中指定的文件外,同一目录的其它所有文件都会打包入镜像。
  • 每一条具有修改效果的指令执行后,都会生成一个中间层镜像。在创建镜像时,这些中间层镜像会缓存。由于我们很难保证一次就把Dockerfile写好,生成镜像的命令很可能需要多次运行,利用缓存可以节省很多时间。
  • 生产环境使用时还需注意:
    • 指定基础镜像的版本号,不要使用默认的latest版本。
    • 使用ENV指定在基础镜像之上安装的其它软件的版本号。
    • 对于用户数据,指定Volume路径,防止删除容器时数据丢失。

进入Dockerfile目录,使用如下命令生成镜像:

docker build -t "my_repo/my_image:my_tag" ./

关于生成Docker镜像的几点说明:

  • 如果不指定tag,docker自动指定为latest
  • docker还支持使用远程git项目创建镜像,只需将Dockerfile所在的目录(此处为./)替换为Git项目地址,注意Dockefile必须在项目的根目录。
  • 可使用--no-cache选项禁用中间层缓存。

Dockerfile示例:

FROM ubuntu:bionic-20191202@sha256:56ea270e0c0826ab6b155acf5130fbc59fa36703e982bddea3143261fca60b8d
ENV CACHE_REFRESH_AT=2019-12-23
RUN  apt-get update
RUN  apt-get install -y apt-transport-https
COPY bionic.sources.list /etc/apt/sources.list
RUN  apt-get update
RUN  apt-get install -y libudev1 libudev-dev software-properties-common
RUN  apt-get install -y curl
RUN  echo 'export RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup' >> /etc/profile
COPY .cargo/ /root/.cargo/
RUN  curl https://sh.rustup.rs -sSf | sh -s -- -y
ENV  PATH="/root/.cargo/bin:${PATH}"
RUN  rustup install nightly
RUN  rustup default nightly
RUN  apt-get install -y build-essential

Dockerfile的指令:RUN

执行一条命令,有以下两种方式:

  • RUN apt-get install -y apt-transport-https 本方式会通过容器的/bin/sh -c命令执行。
  • RUN ["apt-get", "install", "-y", "apt-transport-https"] 直接运行指定的命名

Dockerfile的指令:EXPOSE

开放指定的端口。也可以在运行时通过--expose参数指定,如docker run --expose ...

Dockerfile的指令:ENV

ENV A=1 B=2

相当于命令行的-e "A=1",通过ENV配置的环境变量对其后的命令(如RUN指定的命令)及生成的容器均有效。

Dockerfile的指令:CMD

示例

CMD /bin/bash

说明

  • 建议以数组方式使用
  • 若存在多次CMD配置,只有最后一次配置有效
  • 调用docker run时,通过指定命令覆盖CMD配置

Dockerfile的指令:ENTRYPOINT

指定容器启动时自动执行的命令。等效于容器启动时的--entrypoint参数。

同时指定ENTRYPOINTCMD时,后者将作为前者的命令行参数追加到前者执行。例如

ENTRYPOINT ["nginx"]
CMD ["-h"]

当然也可以在容器启动时通过传入参数:

docker run -it nginx -g "deamon off"

Dockerfile的指令:ADD

将指定内容添加到构建的镜像里。例如

ADD soft.lic /opt/app/soft.lic
ADD my_app.gz /opt/app/my_app/
  1. 支持添加文件、目录及链接。
  2. 添加文件或目录时,要求这些文件必须在Dockerfile目录中。
  3. 通过目标名判断是添加文件还是目录,即以/结束时,为添加目录。
  4. 支持自动解压tar, gzip bzip2, xz格式的压缩包。

Dockerfile的指令:COPY

复制文件或目录到指定目录里,若目录不存在,会自动创建。

复制conf.d/目录里的文件到/et/appache2/

COPY conf.d/ /et/appache2/

Dockerfile的指令:VOLUME

设置文件卷。通常这些目录会在容器间或容器与宿主间共享,生成镜像时,这些目录不会包含进去。

VOLUME ["/opt/a" "/opt/b"]

也可通过docker run-v指令使用,如

docker run -it --rm -v /opt/myapps/:/opt/myapps/ archlinux/base  /opt/myapps/myapp

Dockerfile的指令:WORKDIR

用于指定容器启动时默认进入的工作目录。

Dockerfile的指令:ARG

添加构建镜像时的可选参数。

ARG v1
ARG v2=myval

通过--build-arg <arg_name>=<arg_value>的方式使用:

docker build --build-arg v1=12 -t myimage .

在生成镜像时,v1的值为12v2的值为默认值myval

Dockerfile的指令:SHELL

设置后将使用指定的Shell执行后续的命令。默认值为["/bin/sh" "-c"]

Dockerfile的指令:HEALTHCHECK

配置假死应用检测。

HEALTHCHECK --intervals=10s --timeout=1m --retries=5 CMD curl http://localhost || exit 1

配置后可通过docker inspect --format="{{ .State.Health.Status}}" container_id 查看。

若存在多个HEALTHCHECK配置,则仅最后一个配置有效。

docker-compose.yml中使用,参考How to Implement Docker Health Checks

    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8622/debug/return-codes"]
      interval: 5m30s  # time between every two checks
      timeout: 10s # max time to wait to consider a check fails
      retries: 3 # max retries to before marking the container as unhealthy
      start_period: 60s # container initial start up time, failures during this period will not mark as unhealth

Dockerfile的指令:USER

指定默认用户,不指定时默认为root。可通过username, uid:gid等方式指定。

Dockerfile的指令:STOPSIGNAL

使用docker stop停止容器时,发送给容器的syscal信号,如9(即SIGKILL)。

可通过kill -l查看可选值。

Dockerfile的指令:ONBUILD

当生成的镜像被作为基础镜像,再用于生成其它镜像时执行。通常在做镜像模板时使用:

ONBUILD ADD . /app/src
ONBUILD RUN cd /app/src ; make

Dockerfile的指令:LABEL

key="val"的形式指定镜像的元数据,可多次使用,如

LABEL version="1.0"
LABEL maintainer="a"

docker-compose:管理多个docker容器

安装文档:https://docs.docker.com/compose/install/

示例,通过docker-compose使用postgresql:

version: '3'
services:
  db:
    ports:
    - 5432:5432
    image: postgres:10.2-alpine
    volumes:
      - "./init.sql:/docker-entrypoint-initdb.d/init.sql"
      - "./pgdata:/var/lib/postgresql/data"
    environment:
      POSTGRES_PASSWORD: "mypass"
      POSTGRES_USER: "myuser"
      POSTGRES_DB: "mydb"

进入docker-compose.yml所在目录:

# 启动`docker-compose.yml`定义的容器
docker-compose up -d
# 查看进程
docker-compose ps
# 停止当前目录下docker-compose容器
docker-compose stop
# 停止并删除当前目录下docker-compose容器,(还可以通过-v / --rmi选项删除volume或生成的镜像)
docker-compose down
# 停止docker-compose的所有容器
docker-compose kill
docker-compose rm
# 查看日志
docker-compose logs

存在docker-compose.override.yml文件时,docker-compose自动合并此文件与docker-compose.yml。也可以使用config指令合并多个yaml文件:

docker-compose -f docker-compose.yml -f docker-compose.test.yml  config > output.xml

Docker Swarm

  • 在[A]主机上以启动Docker Swarm管理节点,
    docker swarm init --advertise-addr 192.168.123.200
    # 运行成功时会显示加入Docker Swarm集群的token
    
  • 在 [B] 主机上,使用上面返回的token加入Docker Swarm集群,
    docker swarm join --token SWMTKN-1-25p1bpq9gjuayw2k8enwal62tzbwk8fk3nl8cszgynmsf7knru-0b17mz4lu7evq7zhbj6uan3dh 192.168.123.200:2377
    
  • 在[A] 主机上查看当前集群的所有节点:
    docker node ls
    
  • 可在 管理节点 , 更改其它节点的角色:
    docker node update xps  --role manager
    
  • 可在 管理节点 , 查看加入节点的token:
    docker swarm join-token worker
    
  • 以冗余度为3创建docker服务:
    docker service  create --replicas 3 alpine ping baidu.com
    

可在同一Docker Swarm集群下所有节点里访问其中的服务。 Docker Swarm 会自动使用DNS轮询方式的负载均衡。需要注意:

Docker Swarm的负载均衡建立上OSI第 4 层 即DNS,而非 在OSI 第 3 层 即 TCP层,所以 Docker Swarm的负载均衡无法处理状态,这就要求运行的服务是无状态的。

下面的命令启动Elasticsearch服务, 并创建两个冗余。启动成功后,通过curl命令,可以确认响应来自不同的容器进程:

docker service create --name search --replicas 2 -p 9200:9200  -p 9300:9300  elasticsearch:7.9.3
curl http://IP:9200

在服务启动后,可使用下面的命令修改服务的冗余度:

docker service scale web=4

使用update更新服务的镜像参数等:

docker service update --image myapp:1.22.2 --env-add MY_EV=myval --publish-add 9090:80 --publish-rm 8080 myservicename

使用--publish-add--publish-rm选项修改开放的端口。

使用rollback回滚更新:

docker service rollback web

Docker stack

docker sevice用于管理单个服务,要管理多个服务,需要使用docker stack

docker stack 使用 docker-compose 类似的 yaml 配置文件:

实际上 docker composedocker stack 可使用同一份配置文件, docker compose 会自动忽略deploy 等配置项, docker statck 也会自动忽略build 等 配置项。

docker stack deploy -c example-voting-app-stack.yml  voteapp
# 列出 stack 里的容器
docker stack ps voteapp
# 列出 stack 里的服务
docker stack services voteapp
# 列出所有stack
docker stack ls

服务已经存在时,docker stack deploy将执行docker service update操作。

example-voting-app-stack.yml内容如下:

version: "3"
services:

  redis:
    image: redis:alpine
    ports:
      - "6379"
    networks:
      - frontend
    deploy:
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure
  db:
    image: postgres:9.4
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend
    environment:
      - POSTGRES_HOST_AUTH_METHOD=trust
    deploy:
      placement:
        constraints: [node.role == manager]
  vote:
    image: bretfisher/examplevotingapp_vote
    ports:
      - 5000:80
    networks:
      - frontend
    depends_on:
      - redis
    deploy:
      replicas: 2
      update_config:
        parallelism: 2
      restart_policy:
        condition: on-failure
  result:
    image: bretfisher/examplevotingapp_result
    ports:
      - 5001:80
    networks:
      - backend
    depends_on:
      - db
    deploy:
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

  worker:
    image: bretfisher/examplevotingapp_worker:java
    networks:
      - frontend
      - backend
    depends_on:
      - db
      - redis
    deploy:
      mode: replicated
      replicas: 1
      labels: [APP=VOTING]
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
        window: 120s
      placement:
        constraints: [node.role == manager]

  visualizer:
    image: dockersamples/visualizer
    ports:
      - "8080:8080"
    stop_grace_period: 1m30s
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]

networks:
  frontend:
  backend:

volumes:
  db-data:

Docker secret

docker secret用于管理key-value形式的安全信息,如数据库密码等:

如果使用 Docker Swarm, secret将在所有节点里可见

# 生成key为sec_key的加密信息,value从文件读入
docker secret create sec_key psql_user.txt
# 生成key为another_sec的加密信息,value从命令行输入
echo "mydbpass" | docker secret  create another_sec -

当前docker的所有安全信息列表:

docker secret  ls

通过docker secret create创建的secret需要用docker secret rm手动删除。

Docker registry

Docker Registry 相当于Docker Hub的私有仓库,可使用docker运行docker registry服务:

docker container run --rm -d -p 3000:5000 -v "$(pwd)/registry-data:/var/lib/registry" --name reg registry

上传镜像:

docker pull hello-world
docker tag hello-world 127.0.0.1:3000/hello-world
docker push 127.0.0.1:3000/hello-world

从Registry下载镜像:

docker pull 127.0.0.1:3000/hello-world

Docker Registry 启用TLS

sudo mkdir  /etc/docker/certs.d
sudo mkdir /etc/docker/certs.d/127.0.0.1:3000
sudo cp $(pwd)/certs/domain.crt /etc/docker/certs.d/127.0.0.1:3000/ca.crt
sudo systemctl restart docker
mkdir certs
openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key -x509 -days 365 -out certs/domain.crt

# 使用TLS:
docker run -d -p 3000:5000 --name registry \
--restart unless-stopped \
-v $(pwd)/registry-data:/var/lib/registry \
-v $(pwd)/certs:/certs  \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt  \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry

Docker Registry 启用用户名与密码

mkdir auth
htpasswd -Bbn myuser mypass > auth/htpasswd

# 同时使用TLS及密码
docker run -d -p 3000:5000 --name registry \
  --restart unless-stopped \
  -v $(pwd)/registry-data:/var/lib/registry \
  -v $(pwd)/certs:/certs \
  -v $(pwd)/auth:/auth \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  -e REGISTRY_AUTH=htpasswd \
  -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
  -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
  registry

使用docker login通过用户名与密码访问此registry.

在 docker stack 里使用 docker secret

可以在yaml文件里指定secret,例如docker-compose.yml的内容为:

version: "3.1"

services:
  psql:
    image: postgres
    secrets:
      - psql_user
      - psql_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/psql_password
      POSTGRES_USER_FILE: /run/secrets/psql_user

secrets:
  psql_user:
    file: ./psql_user.txt
  psql_password:
    file: ./psql_password.txt
  pw-from-cmd:
    external: true
  • file标记的secret表示从文件读取secret值。
  • external标记的secret需要部署stack前创建。

部署stack时,会使用当前目录的psql_user.txtpsql_password.txt创建两个secret:

docker stack deploy -c docker-compose.yml  mydb

删除stack时,回同时删除创建的secret

docker stack rm mydb

Kubernetes

安装

Windows/Mac用户可通过Docker Desktop安装Kuebernetes,Linux用户安装microk8sminikube

minikube特别适于本地开发测试。下面以Arch Linux为例,介绍minikube的安装步骤。

  • 安装minikube包:
    yay -S --needed --noconfirm minikube
    
  • 配置代理,将前两行替换为你的代理地址:
    export http_proxy="http://127.0.0.1:12331"
    export https_proxy="http://127.0.0.1:12331"
    export NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,192.168.99.0/24,192.168.39.0/24
    
  • 启动minikube:
    minikube start
    
  • 运行nginx测试下:
    minikube kubectl -- run ngx --image nginx
    

在生产环境使用Docker

开发及测试环境使用docker-compose up运行服务,生产环境使用 Docker Swarm运行服务。

Docker Swarm对资源的占用比Kubernetes更少,如果没有功能上的特别需求,建议使用Docker Swarm.

Docker国内源

建议创建阿里云帐号,并使用阿里云镜像

修改后还需要重启docker服务:

sudo systemctl restart docker

其他Docker工具

文档

Comment