文章目录
- Introduction
- Docker的兼容性问题
- Docker网络
- 构建一个最简单包,推送到Registry上
- Docker Cheatsheet
- Image
- 拉取镜像
- Docker镜像加速
- 本地导入导出Docker镜像
- 将运行中的容器变成镜像
- 列出镜像
- 在远程主机构建docker镜像
- Systemd
- 增加docker远程访问选项
- 远程构建镜像
- 支持中文
- 容器挂载NFS远程文件系统
- Linux OS 容器挂载
- Windows OS 容器(Linux模式容器)挂载
- Docker Compose
- bind mount
- 本地目录
- nfs
本文记录使用docker的常用技巧
Introduction
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
此图转载自:
简单来讲,之前开发一个Python应用,首先需要在机器上安装Python环境。
但是,使用docker,只需要一条命令就可以运行python应用,即:
docker run -p 4000:80 username/repository:tag
镜像里有python的运行时环境,还有一些依赖包。
Docker的兼容性问题
参考:How much does Docker virtualize?
这个问题首先出现在我将x86架构构建的镜像在arm架构上面。
Standard_init_linux.go:190: exec user process caused “exec format error
这涉及到Docker到底抽象到什么程度。Docker容器可以相同芯片架构上机器运行。也就是,你可以在x86_64架构的机器上,运行x86架构主机上构建的镜像。
docker只不过利用了linux的公用内核,即使这么多版本(ubuntu,centos,alpine,suse),但是它们的内核都是一样的。没有这一层共同的部分,要想实现通用可移植是不可能的。
即使windows上已经支持docker,也是需要一部分linux内核。可以参考Preview: Linux Containers on Windows
抛开docker,在x86_64编译的二进制文件无法运行于ARM架构的机器上。这是因为两种芯片指令集都不相同。docker的抽象仅限抽象linux内核之上的东西,那涉及到linux之下的东西是不能移植的。
我画一张图来表示docker的抽象的部分,仅仅是把你语言的运行时环境,依赖包都放进去而已。
开发docker的初衷也是因为开发环境和生产环境用的linux版本不同而发现的。随着docker声名鹊起好像抽象功能被放大了一样,其实那都是幻象。docker仅原生支持linux类型系统,像macOS(UNIX),windows,freeBSD这些是不行的;这些都是需要使用插入linux内核的方式去兼容的(docker公司好像在努力推进这个事情…)
Docker网络
参考:docker网络
构建一个最简单包,推送到Registry上
# 本地当前目录构建
docker build --tag=friendlyhello:v0.0.1 .
# 查看已构建包
docker image ls
# 运行已构建包
docker run -p 4000:80 friendlyhello
# 登陆
docker login
# 为本地已构建包贴标签
docker tag image username/repository:tag
docker tag friendlyhello gordon/get-started:part2
# 推送到docker registry
docker push username/repository:tag
# 拉取并在任何联网机器运行
docker run -p 4000:80 username/repository:tag
镜像的依赖关系,docker镜像构建时的.Dockerfile
都会指定本镜像基于的镜像(base image)。新的Docker镜像包含基础镜像的全部文件。比如可以基于ubuntu打包一个带jdk的ubuntu镜像ubuntu-jdk
,然后基于ubuntu-jdk
打包一个带有tomcat
的镜像。
Docker Cheatsheet
# 查看全部运行的容器
docker ps
# 前台运行容器
docker run <image_name>
#后台(daemon)运行容器
docker run -d <image_name>
# 切入容器内部
docker exec -it <container_name> bash
# 端口映射
docker run -d -p <host_port>:<container_port> <image_name>
Image
拉取镜像
docker pull <username>/<repository>:tag
比如:
Docker镜像加速
/etc/docker/daemon.json
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
本地导入导出Docker镜像
Docker镜像可以使用保存到.tar
包。
docker image save helloworld > helloworld.tar
tar包可以重新导入为docker镜像。
docker image load -i helloworld.tar
将运行中的容器变成镜像
有些场景下,你对容器做出一些通用性的更改,需要将运行中的容器变成镜像:
docker commit -a='author oneslide' -m='your custom message' container_name image_tag
如:
# 将运行中容器名(容器名在docker run 时使用--name指定)为 my-sample-app 的容器变成镜像,镜像的tag是oneslide/tomcat
docker commit -a='author oneslide' -m='your custom message' my-sample-app oneslide/tomcat
列出镜像
docker images --format "{{.Repository}}:{{.Tag}}"
输出:
quay.io/prometheus-operator/prometheus-config-reloader:v0.53.1
quay.io/prometheus-operator/prometheus-operator:v0.53.1
quay.io/prometheus/prometheus:latest
另外会有其他的变量,可以用于镜像的格式化输出:
Placeholder | Description |
.ID | The ID of your image |
.Repository | The image repository |
.Tag | The tag of your image |
.Size | The size of your image |
.CreatedSince | The time since your image was created |
.CreatedAt | The point in time when your image was created |
.Digest | The digest of your image (in short their UUIDs) |
仅列出镜像的ID
docker images -q
在远程主机构建docker镜像
Systemd
增加docker远程访问选项
systemd启动文件控制如何启动docker进程,默认情况下,docker进程只允许root用户通过unix local socket连接。需要修改其增加tcp远程访问的配置:
$ vim /usr/lib/systemd/system/docker.service
[Service]
Type=notify
# 这里增加tcp选项
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
远程构建镜像
然后你就可以远程构建镜像和推送:
# 远程构建镜像
docker -H tcp://192.168.10.20:2375 build -t sampleimg:v1 .
# 远程推送镜像,客户端机器和构建机器都要登录私库
docker -H tcp://192.168.10.20:2375 push 172.16.67.139/cicd/remotebuild:v1
支持中文
# centos 设置环境变量
ENV LANG="en_US.UTF-8"
容器挂载NFS远程文件系统
Linux OS 容器挂载
nfs直接挂载为本地目录就可以了
mount -t nfs <ip>:/<源主机目录> <本机目标目录>
直接使用-v
挂载即可。
Windows OS 容器(Linux模式容器)挂载
- nfs服务器创建一个所有人可读写的目录:
/docker-test2 *(rw,sync,insecure,no_subtree_check,no_root_squash)
运行exportfs -rv
生效
- 创建Named Volume
docker volume create --driver local --opt type=nfs \
--opt o=addr=192.168.10.8,hard,nolock,rw --opt device=:/源主机目录 mynfs
使用命名Volume方式挂载
# 此处nfs为volume name
docker run -d --name test -v mynfs:/etc/nginx/conf.d nginx
Docker Compose
bind mount
本地目录
Host volumes: For a host volume, defined with a path in your docker compose file like:
volumes:
- "./wordpress/uploads:/var/www/html/wp-content/uploads"
you will not receive any initialization of the host directory from the image contents. This is by design.
Named volumes: You can define a named volume that maps back to a local directory:
version: "2"
services:
your-service:
volumes:
- uploads:/var/www/html/wp-content/uploads
volumes:
uploads:
driver: local
driver_opts:
type: none
o: bind
device: /path/on/host/to/wordpress/uploads
This will provide the initialization properties of a named volume. When your host directory is empty empty, on container creation docker will copy the contents of the image at /var/www/html/wp-content/uploads
to /path/on/host/to/wordpress/uploads
.
这种模式下目录不会自动创建(e.g. /path/on/host/to/wordpress/uploads
)。
nfs
version: "3.9"
services:
web:
image: cicd-nginx:1.21.0
volumes:
- web:/etc/nginx/templates
ports:
- "8081:8083"
environment:
- NGINX_HOST=foobar.com
- NGINX_PORT=8083
volumes:
web:
driver: local
driver_opts:
type: nfs
o: addr=192.168.10.20,hard,nolock,rw
# 前面必须要加:
device: :/nginx/data