跳到主要内容

Docker 最佳实践

学习地址:Docker - 从入门到实践

  • Dockerfile
  • 分层存储
  • 镜像
  • 容器
  • 仓库

docker 命令总览,能够看懂这张图的命令就可以了,具体用法可以使用 --help 来查看。

一、基本概念

镜像

操作系统分为内核和用户空间。

Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等

docker pull 使用

$ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
docker pull ubuntu:18.04
等于
docker pull docker.io/libarary/ubuntu:18.04

docker run

docker run -it --rm ubuntu:18.04 bash
* -i 交互操作
* -t 终端
* --rm 容器退出之后立即删除
* -P 表示将 Dockerfile 中通过 EXPOSE 暴露出来的端口绑定到一个随机的端口
* -p 绑定指定端口

docker image

REPOSITORY | TAG | IMAGE ID | CREATE | SZIE

# 查看镜像、容器、数据卷所占用的空间。
docker system df

- docker image prune
- docker image ls -a
- docker image ls ubuntu
- docker image ls ubuntu:18.04
- docker image ls -f since=mongo:3.2 `since、before`
- docker image ls -f label=com.example.verison=0.1
- docker image ls -q ` # 只查看 image id `
- docker image ls --format "{{.ID}}:{{.Repository}}"
- docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"

docker image rm 386
docker image rm ubuntu
docler image ls --digests
docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
unstage delete
# 命令搭配使用
docker image rm $(docker image ls -q redis)
docker image rm $(docker image ls -q -f before=mongo:3.2)

docker commit 慎用

docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

docker run --name webserver -d -p 80:80 nginx
docker exec -it webserver bash # 进入到容器,修改内容

echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
exit
docker diff webserver
docker commit --author "wangzhy@cloud.com" --message "学习使用docker" webserver nginx:v2

docker history nginx:v2
docker run --name web2 -d -p 81:80 nginx:v2

docker save、docker load

$ docker save nginx:v3 -o 文件名称

$ docker save alpine | gzip > alpine-latest.tar.gz

$ docker load -i alpine-latest.tar.gz

如果我们结合这两个命令以及 ssh 甚至 pv 的话,利用 Linux 强大的管道,我们可以写一个命令完成从一个机器将镜像迁移到另一个机器,并且带进度条的功能:

docker save <镜像名> | bzip2 | pv | ssh <用户名>@<主机名> 'cat | docker load'

容器

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

检查本地是否存在指定的镜像,不存在就从 registry 下载 利用镜像创建并启动一个容器 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去 从地址池配置一个 ip 地址给容器 执行用户指定的应用程序 执行完毕后容器被终止

docker run ubuntu:18.04 /bin/echo 'Hello world'

docker run -t -i ubuntu:18.04 /bin/bash

docker run ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"

-d 后台允许

$ docker container ls

$ docker container logs [container ID or NAMES]

$ docker container ls -a

docker container start

docker container restart

docker container run

$ docker run -dit ubuntu
$ docker container ls
$ docker attach 243c

$ docker run -dit ubuntu
$ docker container ls
$ docker exec -i 69d1 bash
$ docker exec -it 69d1 bash

导出
$ docker container ls -a
test
$ docker export 7691a814370e > ubuntu.tar
导入
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0
$ docker image ls

$ docker import http://example.com/exampleimage.tgz example/imagerepo

删除
$ docker container rm trusting_newton
如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器

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

二、通过 Dockerfile 构建镜像

  1. 编写 Dockerfile 文件
  2. 执行构建镜像命令 docker build -t nginx:v3 . 这条命令有个小数点,表示当前目录,这是在指定上下文路径

Dockerfile 文件中包含的是一条条指令(每条指令会构建一层,因此需要注意下简化命令或者使用 && 将命令连接起来)

Dockerfile 的简单示例

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

FROM 指令 (必须要有,并且是第一条指令)

指定基础镜像,即在哪个镜像上面进行定制。

scratch 是空白镜像。 FROM scratch

MAINTAINER

记录维护者的信息,一般是 name + email 例如 wangzhy<491857242@qq.com>

RUN 指令

RUN 指令是用来执行命令行命令的。由于命令行的强大能力,RUN 指令在定制镜像时是最常用的指令之一

  1. RUN <命令>

  2. RUN ["可执行文件", "参数1", "参数2"]

CMD

  1. shell 格式:CMD <命令>
  2. exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
  3. 参数列表格式:CMD ["参数1", "参数2"...] 。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。

每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条执行。如果在启动容器的时候指定了运行的命令,则会覆盖掉 CMD 制定的命令。

EXPOSE

暴露端口,使用 -P 会随机绑定 EXPOSE 暴露的端口。

ENV

指定环境变量。

COPY

复制文件到镜像中。

COPY package.json /usr/src/app/

COPY hom* /mydir/
COPY hom?.txt /mydir/

--chown=<user>:<group> 来改变文件的所属用户和用户组

COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/

ADD

同 COPY 一样,复制文件到镜像中,如果为压缩文件,会自动解压。

ENTRYPOINT

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:<ENTRYPOINT> "<CMD>"

WORKDIR

容器启动时,进入的目录。以及 RUN、CMD、ENTRYPOINT 等命令的工作目录。

...

构建镜像(根据 Dockerfile 文件)

docker build [选项] <上下文路径/URL/->

docker build -t nginx:v3 .

这里面的 . 指的是上下文路径。

镜像构建上下文 Context

Docker 在运行时分为

  1. Docker 引擎 一组 REST API
  2. 客户端工具 通过调用 REST API 操作 Docker 引擎

构建过程

当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后, 会将路径下的所有内容打包,然后上传给 Docker 引擎。 这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。 一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。 如果该目录下没有所需文件,那么应该把所需文件复制一份过来。 如果目录下有些东西确实不希望构建时传给 Docker 引擎, 那么可以用 .gitignore 一样的语法写一个 .dockerignore, 该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。

docker build 命令会将上下文目录下的内容打包交给 Docker 引擎以帮助构建镜像。

docker build 的一些用法

$ docker build -t hello-world https://github.com/docker-library/hello-world.git#master:amd64/hello-world

$ docker build http://server/context.tar.gz

$ docker build - < Dockerfile

$ cat Dockerfile | docker build -

$ docker build - < context.tar.gz

挂载目录

docker run --name myzbpt -d -p 8080:8080 \
-v /Users/wangzhy/docker/workdir/:/usr/local/workdir \
-v /Users/wangzhy/docker/webapps/:/usr/local/tomcat/webapps \
myzbpt

Docker Network

docker network ls 
  • bridge:为每一个容器分配、设置 IP 等,并将容器连接到一个 docker0 虚拟网桥,默认为该模式。
  • host:容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。
  • none:容器有独立的 Network namespace,但并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP 等。
  • container:新创建的容器不会创建自己的网卡和配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。

Docker MySQL

1.安装MySQL

# 下载镜像
docker pull mysql
# 启动镜像
docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql
# 登录
mysql -u root -p
# 重启数据库
systemctl restart mysqld

2.创建数据库

# 创建数据库
create database mybatis;
use mybatis;

# 创建用户表
create table user(
id_ varchar(30) primary key,
name_ varchar(200) default null,
pwd_ varchar(40) default null
)engine=innodb default charset=utf8 ;

# 插入数据
insert inti user(id_,name_,pwd_) values
('1','w','123456'),
('2','z','123456'),
('3','y','123456');

Docker mirror

配置腾讯云的镜像站

sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

Docker 安装和升级

1、install

2、upgrade

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

sudo apt-get upgrade deocker-desktop

Docker desktop

1、ubuntu 启动 docker-desktop

systemctl --user start docker-desktop
systemctl --user enable docker-desktop
systemctl --user status docker-desktop

2、windows 启动失败

1、问题1

System.Exception:
starting WSL engine: 2 errors occurred:
* starting keep alive in the data distro: waiting for wsl-keepalive to be ready: running wsl-keepalive in "docker-desktop-data": WSL engine terminated abruptly
* checking if isocache exists: CreateFile \\wsl$\docker-desktop-data\isocache\: The network name cannot be found.
计算机\HKEY_USERS\【SID】\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\

2、问题2

An unexpected error was encountered while executing a WSL command. Common causes include access rights issues, which occur after waking the computer or not being connected to your domain/active directory.

Docker compose

官方文档

负责快速的部署分布式应用。

version: "3"

services:
webapp:
image: nginx
ports:
- 80:80
volumes:
- "/data"
version: '3'
services:
# confluence:
# image: "atlassian/confluence:8.5.2"
# volumes:
# - ./atlassian-agent.jar:/var/atlassian/atlassian-agent.jar
# - /home/wangzhy/dev/confluence/confluence/confluence:/usr/atlassian/confluence
# - /home/wangzhy/dev/confluence/application-data/confluence:/usr/atlassian/application-data
# ports:
# - "8090:8090"
# environment:
# - JAVA_OPTS="-javaagent:/var/atlassian/atlassian-agent.jar"
# restart: always
# docker pull atlassian/confluence-server:8.6-jdk17
confluence2:
image: "atlassian/confluence-server:8.6-jdk17"
volumes:
- ./atlassian-agent.jar:/var/atlassian/atlassian-agent.jar
- /home/wangzhy/dev/confluence/confluence/confluence:/usr/atlassian/confluence
- /home/wangzhy/dev/confluence/application-data/confluence:/usr/atlassian/application-data
ports:
- "8090:8090"
deploy:
resources:
limits:
cpus: 1
memory: 8888M
reservations:
cpus: 1
memory: 1024M
environment:
- JAVA_OPTS="-javaagent:/var/atlassian/atlassian-agent.jar"
restart: always
docker run -d --name confluence2 \
-p 8090:8090 \
-v $(pwd)/atlassian-agent.jar:/var/atlassian/atlassian-agent.jar \
-v /home/wangzhy/dev/confluence/confluence/confluence:/atlassian/confluence \
-v /home/wangzhy/dev/confluence/application-data/confluence:/atlassian/application-data \
-e JAVA_OPTS="-javaagent:/var/atlassian/atlassian-agent.jar" \
-e JVM_SUPPORT_RECOMMENDED_ARGS="-javaagent:/var/atlassian/atlassian-agent.jar" \
--memory=8888m \
--memory-reservation=1024m \
--cpus=1 \
--restart always \
atlassian/confluence-server:8.6-jdk17
docker exec -it confluence2 /bin/bash
cd /var/atlassian/
java -jar atlassian-agent.jar -d -m test@test.com -n BAT -p 'conf' -o http://localhost:8090 -s BCM8-MPK0-2M4F-V68T
/opt/atlassian/confluence/logs
docker cp confluence2:/opt/atlassian/confluence/bin/setenv.sh setenv.sh 

docker cp setenv.sh confluence2:/opt/atlassian/confluence/bin/setenv.sh
export JAVA_OPTS="-javaagent:/var/atlassian/atlassian-agent.jar ${JAVA_OPTS}"
export PATH="$PATH:/usr/share/vim"
/opt/java/openjdk/bin/java 
-Djava.util.logging.config.file=/opt/atlassian/confluence/conf/logging.properties
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-javaagent:/var/atlassian/atlassian-agent.jar
-Djdk.tls.ephemeralDHKeySize=2048
-Djava.protocol.handler.pkgs=org.apache.catalina.webresources
-Dorg.apache.catalina.security.SecurityListener.UMASK=0027
-Datlassian.plugins.startup.options=-fg
-Dorg.apache.tomcat.websocket.DEFAULT_BUFFER_SIZE=32768
-Dconfluence.context.path= -Dsynchrony.enable.xhr.fallback=true
-Datlassian.plugins.enable.wait=300 -Djava.awt.headless=true
-Xlog:gc*:file=/opt/atlassian/confluence/logs/gc-%t.log:tags,time,uptime,level:filecount=5,filesize=2M
-XX:G1ReservePercent=20 -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent -XX:+IgnoreUnrecognizedVMOptions
@/opt/atlassian/confluence/confluence/WEB-INF/jpms-args.txt -XX:ReservedCodeCacheSize=256m
-Xms1024m -Xmx1024m -Dconfluence.home=/var/atlassian/application-data/confluence
-DConfluenceHomeLogAppender.disabled=false -Dignore.endorsed.dirs=
-classpath /opt/atlassian/confluence/bin/bootstrap.jar:/opt/atlassian/confluence/bin/tomcat-juli.jar
-Dcatalina.base=/opt/atlassian/confluence -Dcatalina.home=/opt/atlassian/confluence
-Djava.io.tmpdir=/opt/atlassian/confluence/temp
org.apache.catalina.startup.Bootstrap start

AAABUw0ODAoPeJxtkF9rwjAUxd/zKQp7jsZatS0Epm0HBavOqg97i911DaRpSVKZ+/RL/8BgCCGQe
27OPff38qa4kzHlEN8hbjj3Q0Kc8ylyXOJ6KFLADK9lzAzQroLJHBMfJXcm2l6hNyY0oBh0oXjTV
85S8Iob+HQEL0BqcK4PpzSm0eF0+lNyARNeo736YpLrwaRTrSjqgomy1ib0SUBQUcvbhBWG34Ea1
QKKamnsO8kYF9SANq/dNSnqaujNDVMG1JipL22HCKdHAztWAY32WZYco3S9RdZFGpBMFpB8N1w9x
i39AJOVPWj8m8Z0m8Z5ssPb2YoE/nLh+WRpKeSg7qCsvPGOAU4/Lu944aUp9g6H5TDdOrIIZJepX
2B0fD7u0KqiZBr+wx4pXkDpjpWL8vb6R7v37Yft2uoKan87a9tJ8QzZyPRJ7JFij2OzPv0CSb6nw
jAtAhUAkvzM09I2m6xXImEKAsoICmoZ6R0CFCmyzc2AuEiPG7TFXl+O5SCit5f7X02go

Docker command

根据镜像启动一个容器

docker run --name nginx -d -p 8081:80 nginx:v1.1 

Dockerfile 脚本实例

FROM zbptv1.0
RUN yum install kde-l10n-Chinese -y \
&& yum install glibc-common -y \
&& localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
ENV LC_ALL = zh_CN.utf8

通过 Dockerfile 生成一个镜像

docker build -t zbpt:v1.1 .

生成一个容器

docker run --name zbpt -d -p 8080:8080 zbpt:v1.1

使用 save 导出镜像 ^ad306c

docker save -o zbptv1.1.tar zbpt:v1.1

使用 load 导入镜像

docker load -i zbptv1.1.tar

查看挂在的目录

docker inspect container_name | grep Mounts -A 20

host.docker.internal

在容器内部访问宿主机的服务

# docker run 命令需要加上 --add-host=host.docker.internal:host-gateway {id="1"}

docker-compose 中

version: '3.7'
services:
fpm:
build:
context: .
extra_hosts:
- "host.docker.internal:host-gateway"

Docker example

基于 ubuntu 安装 confluence

官方教程地址

Migrating Confluence Between Servers

Production Backup Strategy

准备工作

  1. 一个已经安装好 confluence 的环境
  2. 安装包
  3. 原始环境中的文件(放到 /home/wangzhy/docker/wiki/ 目录下)
    • atlassian-confluence-7.4.5-x64.bin (安装包)
    • /usr/local/atlassian/confluence/confluence/WEB-INF/lib 文件夹
    • /usr/local/atlassian/application-data/confluence/confluence.cfg.xml
    • /usr/local/atlassian/application-data/confluence/attachments
    • /usr/local/atlassian/application-data/confluence/config (可能没有)
    • /usr/local/atlassian/application-data/confluence/index
    • /usr/local/atlassian/confluence/conf/server.xml
  4. alias ls='ls -l --color=auto'
  5. 安装 vim (apt-get install -y vim)

镜像

# 下载镜像
docker pull ubuntu
# 启动容器,绑定端口,映射目录
docker run --name wiki -d -p 8090:8090 -v /home/wangzhy/docker/wiki:/usr/local/ -it ubuntu /bin/bash

docker run --name wiki_ubuntu -d -p 8090:8090 -it ubuntu /bin/bash

# 进入容器
docker exec -it 2bdf1b1f9930 /bin/bash
docker exec -it dd994b49fb6e /bin/bash

docker run --name wiki_mysql -d -p 8090:8090 -it wiki_mysql:v1 /bin/bash

安装 confluence

# 进入指定目录
cd /usr/local
# 安装
./atlassian-confluence-7.4.5-x64.bin
# 自定义安装目录为 /usr/local/wiki/confluence 和 /usr/local/wiki/application-data

替换数据

# 原本的安装目录
export OLD_INSTALL_HOME=/usr/local/atlassian/confluence
export OLD_DATA_HOME=/usr/local/atlassian/application-data
# 现在的安装目录
export NEW_INSTALL_HOME=/usr/local/wiki/confluence
export NEW_DATA_HOME=/usr/local/wiki/application-data

# 复制文件到新的安装目录
cp $OLD_DATA_HOME/confluence/confluence.cfg.xml $NEW_DATA_HOME/confluence/confluence.cfg.xml
cp -r $OLD_DATA_HOME/confluence/attachments $NEW_DATA_HOME/confluence/attachments
cp -r $OLD_DATA_HOME/confluence/index $NEW_DATA_HOME/confluence/index
cp $OLD_INSTALL_HOME/conf/server.xml $NEW_INSTALL_HOME/conf/server.xml

# 启动 confluence(启动会很慢)
cd $NEW_INSTALL_HOME/bin && ./startup.sh && $NEW_INSTALL_HOME/logs/catalina.out

到此,就迁移到 docker 成功了

打成一个镜像

# 基于容器打成一个镜像
docker commit wiki confluence
# 启动容器,查看是否满足要求
docker run --name confluence -d -p 8090:8090 -v /home/wangzhy/docker/wiki:/usr/local/ -it confluence /bin/bash
FROM confluence
CMD cd /usr/local/wiki/confluence/bin && ./startup.sh && tail -f /usr/local/wiki/confluence/logs/catalina.out
docker build -t wiki:stable .
docker run --name wiki_stable -d -p 8090:8090 -v /home/wangzhy/docker/wiki:/usr/local/wiki wiki:stable

tomcat-zbpt

生成一个 Image

FROM centos:7
ENV JAVA_OPTS="-Dfile.encoding=UTF8 -Dsun.jnu.encoding=GBK -Xms2048m -Xmx18432m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m -Deiworkdir=/usr/local/workdir -Djava.security.egd=file:/dev/urandom -Djava.awt.headless=true"
ENV JAVA_HOME /usr/local/java
ENV JRE_HOME /usr/local/java/jre
ENV PATH /usr/local/java/bin:$PATH
WORKDIR /usr/local/
ADD jdk-8u281-linux-x64.tar.gz /usr/local
ADD apache-tomcat-8.5.72.tar.gz /usr/local
RUN mv jdk1.8.0_281 java \
&& mv apache-tomcat-8.5.72 tomcat \
&& yum install kde-l10n-Chinese -y \
&& yum install glibc-common -y \
&& localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
# 修改时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENV LC_ALL=zh_CN.utf8
# COPY zbpt.war /usr/local/tomcat/webapps
EXPOSE 8080
VOLUME /opt/apache-tomcat-hbws/apache-tomcat-8.5.72/wkdir_hbws
ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]

创建一个容器并启动

docker run --name myzbpt -d -p 12580:8080 -v D:/docker/zbpt_test/workdir/:/opt/apache-tomcat-hbws/apache-tomcat-8.5.72/wkdir_hbws  -v D:/docker/zbpt_test/webapps/:/usr/local/tomcat/webapps/ myzbpt
FROM centos:7
ENV JAVA_OPTS="-Dfile.encoding=UTF8 -Dsun.jnu.encoding=GBK -Xms2048m -Xmx18432m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m -Deiworkdir=/usr/local/workdir -Djava.security.egd=file:/dev/urandom -Djava.awt.headless=true"
ENV JAVA_HOME /usr/local/java
ENV JRE_HOME /usr/local/java/jre
ENV PATH /usr/local/java/bin:$PATH
WORKDIR /usr/local/
ADD jdk-8u281-linux-x64.tar.gz /usr/local
ADD apache-tomcat-8.5.72.tar.gz /usr/local
RUN mv jdk1.8.0_281 java \
&& mv apache-tomcat-8.5.72 tomcat \
&& yum install kde-l10n-Chinese -y \
&& yum install glibc-common -y \
&& localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
# 修改时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENV LC_ALL=zh_CN.utf8
COPY zbpt.war /usr/local/tomcat/webapps
EXPOSE 6060
ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]