我的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
参数。
同时指定ENTRYPOINT
与CMD
时,后者将作为前者的命令行参数追加到前者执行。例如
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/
- 支持添加文件、目录及链接。
- 添加文件或目录时,要求这些文件必须在Dockerfile目录中。
- 通过目标名判断是添加文件还是目录,即以
/
结束时,为添加目录。 - 支持自动解压
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
的值为12
,v2
的值为默认值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 compose
和docker 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.txt
及psql_password.txt
创建两个secret:
docker stack deploy -c docker-compose.yml mydb
删除stack时,回同时删除创建的secret
docker stack rm mydb
Kubernetes
安装
Windows/Mac用户可通过Docker Desktop安装Kuebernetes,Linux用户安装microk8s或minikube。
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工具
- Portus 开源Docker Registry前端
- Docker Flow Proxy 适用于OSI Layer-7的代理
- ELK 日志记录,可视化查询
- Prometheus 可视化监控
- Portainer Docker容器管理的图形化界面