Docker

Command

登录

docker login

查看信息

# version
docker version
docker info

# 查看系统信息
docker system info

# ps
docker ps

# 查看容器信息
docker inspect container_name/container_id

# 查看容器运行日志
docker logs container_name/container_id
# 查看尾部日志并跟踪
docker logs --tail 10 -f container_name/container_id

# 查看容器端口映射
docker port container_name

# 查看容器占用资源
docker stats

镜像

# search images
docker search python

# list images
docker image ls

# download image
docker pull python:latest

# commit image
docker commit -m "comment" -a "author" container_id my_hub/my_image:tag

# push image
docker login
docker tag my_image my_hub/my_image
docker push my_hub/my_image

# remove image
docker rmi my_image:tag
docker image prune

# save image
docker save -o my_image.tar my_hub/my_image

# load image
docker load --input my_image.tar

Build

docker image build

# build
docker build -t ${NAME}:${VERSION} .

# build with Dockerfile
docker build -t ${NAME}:${VERSION} -f path/to/Dockerfile ./

# build with host network
docker build --network=host -t ${NAME}:${VERSION} ./

# build with env args
docker build --build-arg env=$(ENV) -t ${NAME}:${VERSION} .

# build to target stage
docker build --target base -t ${NAME}:${VERSION} .

容器

# list containers
docker container ls
docker container ls -a

# start container
docker start container_name/container_id
docker stop container_name/container_id

# remove container
docker rm container_name
docker rm container_id
docker container prune

# 文件互传
docker cp /file/in/host container_name:/file/in/container # 将文件推入容器
docker cp container_name:/file/in/container /file/in/host # 从容器拉取文件

Run

# run in a new container
docker run -it --name='test' python bash

# run in a running container
docker exec -it container_name bash

# 后台运行
docker run -d python

# 自动重启
docker run -d --restart unless-stopped python

# 环境变量
docker run --env ENV1=env1 python

# 资源限制
docker run --cpus=".5" --memory=1g python

Data

# 创建数据卷
docker volume create my-vol

# 查看所有的数据卷
docker volume ls

# 查看数据卷信息
docker volume inspect my-vol

# 启动一个挂载数据卷的容器
docker run -it -v my-vol --name test ubuntu bash

# 删除数据卷
docker volume rm my-vol

# 清理数据卷
docker volume prune

# 挂载本地文件夹作为数据卷
docker run -it -v $(pwd)/test:/test --name test ubuntu bash

# 设置只读权限
docker run -it -v $(pwd)/test:/test:ro --name test ubuntu bash

Network

  1. 通过映射端口或者主机网络模式,使得容器可以对外提供服务
  2. 通过创建并加入容器网络,或者link(已废弃),使得容器可以互联
# 查看网络
docker network ls

# 创建网络
docker network create my-net

# 加入网络
docker run -it  --network=my-net --name test ubuntu
docker network connect my-net container_name

# 清理网络
docker network prune

# 映射容器的80端口到主机的8000端口
docker run -it -p 8000:80 --name test ubuntu

# 映射到主机的49000~49900中随机一个端口
docker run -it -P --name test ubuntu

# 映射容器的80端口到主机的8000端口,限定只有本地可以访问
docker run -it -p 127.0.0.1:8000:80 --name test ubuntu

# 以主机网络模式运行
docker run -it  --net=host --name test ubuntu
docker run -it  --network host --name test ubuntu

# 以桥接模式运行(默认模式)
docker run -it  --network bridge --name test ubuntu

# 创建连接
docker run -d --name my_db postgres
docker run -d -P --name web --link my_db:db webapp python app.py

Dockerfile

  • FROM: 继承镜像
  • COPY: 复制文件
    • 目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
    • 源路径可以是多个,甚至可以是通配符,例如COPY hom?.txt /mydir/
    • 如果源路径是文件夹,会把源文件夹下的文件拷贝过去,例:COPY dir1 dir2 ./等价于COPY dir1/* dir2/* ./
    • 使用--chown=<user>:<group>来改变文件的所属用户及所属组,例如:COPY --chown=55:mygroup files* /mydir/
  • WORKDIR: 工作路径
  • RUN: 执行命令
  • EXPOSE: 暴露端口,(本身只是提示哪个端口提供服务,实际需要运行时通过-p映射到主机端口才能访问)
  • ENTRYPOINT: 入口命令,后面传参时不会覆盖命令
  • CMD: 主命令,(不能用于启动后台service)
  • ENV: 设置环境变量

示例

FROM python:latest
ARG env=dev
COPY . /app
WORKDIR /app
RUN pip install web.py
RUN chmod 755 main.py
COPY .env.$env .env
CMD ["python", "main.py"]
EXPOSE 8080

Build Dockerfile

docker image build -t webpy:0.1 .

Run after build

docker run -p 8080:8080 -d --name webpy-test webpy:0.1

使用supervisord启动多个服务

配置文件配置服务

[supervisord]
nodaemon = true # 是否前台运行,true为前台运行

# 配置服务1
[program:openapi]
priority = 1 							# 优先级
command = uvicorn api.main:app --host 0.0.0.0 --port 30050 	# 启动命令
redirect_stderr = true 						# 是否重定向输出
stdout_logfile = /usr/src/app/logs/openapi.log 			# 重定向输出文件
stderr_logfile = /usr/src/app/logs/openapi.err			# 重定向错误文件

# 配置服务2
[program:python]
priority = 2
command = python ./python/main.py
redirect_stderr = true
stdout_logfile = /usr/src/app/logs/python.log
stderr_logfile = /usr/src/app/logs/python.err

编写Dockerfile

# 安装
RUN apt-get -y update \
    && apt-get -y install \
        supervisor \
    && apt-get clean

# 复制配置文件
COPY supervisord/supervisord.conf /root/supervisord.conf

# 启动
CMD ["/usr/bin/supervisord", "-c", "/root/supervisord.conf"]

使用心得

  • 快速上手新技术,将精力集中于语言本身而非环境配置
  • 将技术栈的常用框架docker化,快速进行新研究
  • 将自己代码或项目docker化,以后可以快速调用和交付

常用环境

Java

FROM maven:3-openjdk-8-slim AS BUILD
COPY pom.xml /tmp/
COPY src /tmp/src/
WORKDIR /tmp/
RUN mvn package

FROM openjdk:8-alpine AS RUNTIME
RUN apk update && apk add --no-cache libc6-compat
RUN ln -s /lib64/ld-linux-x86-64.so.2 /lib/ld-linux-x86-64.so.2
WORKDIR /app/
COPY --from=BUILD /tmp/target/logstreams-0.1-jar-with-dependencies.jar .
COPY src/main/resources ./resources
ENTRYPOINT ["java", "-cp", "*", "streams.LogStreams"]

Python

FROM python:3.10.12-slim
ENV TZ=Asia/Shanghai
ENV LANG zh_CN.UTF-8

WORKDIR /app
COPY ./requirements.txt /app/
RUN pip3 install -i https://pypi.mirrors.ustc.edu.cn/simple --upgrade pip && \
    pip3 install -i https://pypi.mirrors.ustc.edu.cn/simple --no-cache-dir -r requirements.txt

COPY ./src .
COPY .env ./

EXPOSE 8080
ENTRYPOINT ["python3", "main.py"]

Vue3 + Vite + Nginx

添加 Nginx 配置文件:

worker_processes 4;
events { worker_connections 1024; }

http {
    server {
        listen 8080;
        root  /usr/share/nginx/html/app;
        include /etc/nginx/mime.types;

        location /app/ {
            root  /usr/share/nginx/html;
            try_files $uri $uri/ /app/index.html;
        }
    }
}

Dockerfile:

FROM node:18-alpine AS build-stage
COPY package.json ./
RUN npm config set registry https://registry.npmmirror.com && npm install
COPY ./ .
RUN npm run build

FROM nginx:alpine as production-stage
ENV TZ=Asia/Shanghai
COPY nginx.conf /etc/nginx/nginx.conf
RUN rm -rf /usr/share/nginx/html/*
COPY --from=build-stage /dist /usr/share/nginx/html/app
EXPOSE 8080

# set the container to run Nginx in the foreground
ENTRYPOINT ["nginx", "-g", "daemon off;"]

Nuxt

Dockerfile

ARG NODE_VERSION=18.19.1
ARG PORT=3000

FROM node:${NODE_VERSION}-slim as base
ENV NODE_ENV=production
WORKDIR /src

# Build
FROM base as build

COPY --link package.json package-lock.json ./
RUN npm install --production=false

COPY --link . .

RUN npm run build
RUN npm prune

# Run
FROM base

ENV PORT=$PORT

COPY --from=build /src/.output /src/.output
# Optional, only needed if you rely on unbundled dependencies
# COPY --from=build /src/node_modules /src/node_modules

CMD [ "node", ".output/server/index.mjs" ]

Go

初始化项目

go mod init YOUR_PROJECT
go mod tidy

Dockerfile:

FROM golang:1.20.2
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

WORKDIR /app

# V1: go mod init in builder
COPY *.go ./
RUN go mod init godemo && go mod tidy
RUN CGO_ENABLED=0 GOOS=linux go build -o /godemo

# V2: go mod tidy locally and download Go modules in builder
#COPY go.mod go.sum ./
#RUN go mod download
#COPY *.go ./
#RUN CGO_ENABLED=0 GOOS=linux go build -o /godemo

# V3: copy excutable to container
#COPY build/godemo /

COPY .env ./

EXPOSE 8080
CMD ["/godemo"]

参考:Docker.Docs - Build your Go image

其他

给普通用户添加权限

sudo groupadd docker
sudo gpasswd -a ${USER} docker
sudo systemctl restart docker
sudo chmod a+rw /var/run/docker.sock

设置国内源

sudo vi /etc/docker/daemon.json

写入以下内容

{
  "registry-mirrors" : [
    "https://mirror.ccs.tencentyun.com"
  ]
}

重启docker服务

systemctl restart docker.service

远程构建

更改 docker 源只能解决 docker pull 时慢的问题,如果需要在构建阶段进行下载,例如 apt updatepip install 之类的操作则需要替换对应的源

替换 pip 的源相对简单,可以在 pip 命令时指定源,例如 pip install -i https://pypi.mirrors.ustc.edu.cn/simple requests

替换 apt 源则比较麻烦,因为不同的 base image 可能碰到不一样的问题。例如 python-slim 镜像需要替换 /etc/apt/sources.list 文件,添加国内源后会报公钥验证的错误,见 How can I write a Dockerfile based on Debian Slim in which ‘apt-get update’ doesn’t fail with public key errors?,根据网上教程添加公钥,又会报请先安装 gnupg:E: gnupg, gnupg2 and gnupg1 do not seem to be installed, but one of them is required for this operation,想要安装 gnupg,要先执行 apt-update 。我本来就是为了执行 apt-update,整闭环了。

使用 HTTP_PROXY/HTTPS_PROXY 环境变量设置代理,但是不支持 SOCKS 代理

使用 proxychains docker build ...,但是它只对 docker CLI 的客户端生效,构建实际发生在 dockerd 的服务端,因此该配置无效,参考 docker国内镜像加速无效的解决办法

因此考虑在远程(境外)服务器进行构建的方式,参考Can I build a Docker container from the CLI against a remote daemon?How to Use a Remote Docker Server to Speed Up Your Workflow

ssh-copy-id root@remote_host
export DOCKER_HOST=ssh://root@remote_host

# check docker server info
docekr info | grep Name

# build with remote docker server
docker build -t test:0.1 .

# unset environment
unset DOCKER_HOST

但是构建完还是需要 pull 回来

使用 GPU

Installing the NVIDIA Container Toolkit

curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | \
  sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo
sudo yum install -y nvidia-container-toolkit

sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

test

docker run --gpus all nvidia/cuda:latest nvidia-smi

修改存储目录

Relocating the Docker root directory

  1. Stop the Docker services
sudo systemctl stop docker
sudo systemctl stop docker.socket
sudo systemctl stop containerd
  1. Move data
sudo mkdir -p /data/docker
sudo mv /var/lib/docker /data/docker
  1. Change /etc/docker/daemon.json
{
  "data-root": "/data/docker"
}
  1. Restart & Verify
sudo systemctl start docker

docker info -f '{{ .DockerRootDir}}'

问题

pull或push时证书过期

x509:certificate has expired or is not yet valid

sudo vi /etc/docker/daemon.json

写入以下内容

{
  "insecure-registries" : [
    "host:port"
  ]
}

检查网络

Github - Docker Network Tools

docker run --rm -it jonlabelle/network-tools

pip 无法安装

Temporary failure in name resolution Errno -3 with Docker

在 docker build 时使用到的的 pip install 无法安装

解决方法,重启一下 dockerd 服务

sudo service docker restart

docker-compose

install

sudo apt install docker-compose

commands

# up
docker-compose up -d

# down
docker-compose down

# with file
docker-compose -f ${DOCKER_COMPOSE_FILE} up -d

Reference