知了小站 - IT人的小站 - Docker容器 2024-07-30T20:58:00+08:00 Typecho https://izlzl.com/feed/atom/tag/docker/ <![CDATA[总结国内加速拉取 Docker 镜像的几种方法]]> https://izlzl.com/archives/1749.html 2024-07-30T20:58:00+08:00 2024-07-30T20:58:00+08:00 知了小站 https://izlzl.com 本文介绍多种方法加速Docker镜像拉取,包括利用Nginx、Cloudflare反向代理等方案,使国内服务器更高效获取海外Docker镜像。

目前仍可用的镜像(随时可能失效)

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
    "registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://huecker.io",
        "https://dockerhub.timeweb.cloud",
        "https://noohub.ru"
    ]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

使用 Nginx 反向代理

需要有一台国外服务器, 按下面添加 Nginx 配置即可,配置完成后,在 /etc/docker/daemon.json 中修改成你的域名,具体参考上方配置,然后重启 docker

server {
            listen 443 ssl;
            server_name 域名;

            ssl_certificate 证书地址;
            ssl_certificate_key 密钥地址;

            proxy_ssl_server_name on; # 启用SNI

            ssl_session_timeout 24h;
            ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
            ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;

            location / {
                    proxy_pass https://registry-1.docker.io;  # Docker Hub 的官方镜像仓库

                    proxy_set_header Host registry-1.docker.io;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Forwarded-Proto $scheme;

                    # 关闭缓存
                    proxy_buffering off;

                    # 转发认证相关的头部
                    proxy_set_header Authorization $http_authorization;
                    proxy_pass_header  Authorization;

                    # 对 upstream 状态码检查,实现 error_page 错误重定向
                    proxy_intercept_errors on;
                    # error_page 指令默认只检查了第一次后端返回的状态码,开启后可以跟随多次重定向。
                    recursive_error_pages on;
                    # 根据状态码执行对应操作,以下为301、302、307状态码都会触发
                    #error_page 301 302 307 = @handle_redirect;

                    error_page 429 = @handle_too_many_requests;
            }
            #处理重定向
            location @handle_redirect {
                    resolver 1.1.1.1;
                    set $saved_redirect_location '$upstream_http_location';
                    proxy_pass $saved_redirect_location;
            }
            # 处理429错误
            location @handle_too_many_requests {
                    proxy_set_header Host 替换为在CloudFlare Worker设置的域名;  # 替换为另一个服务器的地址
                    proxy_pass http://替换为在CloudFlare Worker设置的域名;
                    proxy_set_header Host $http_host;
            }
    }

使用 Cloudflare 反向代理

登录到 Cloudflare 控制台, 新建 worker, 在 worker.js 文件中输入以下代码, 注意需要自行修改代码中的域名

'use strict'

const hub_host = 'registry-1.docker.io'
const auth_url = 'https://auth.docker.io'
const workers_url = 'https://你的域名'
const workers_host = '你的域名'
const home_page_url = 'https://qninq.cn/file/html/dockerproxy.html'

/** @type {RequestInit} */
const PREFLIGHT_INIT = {
    status: 204,
    headers: new Headers({
        'access-control-allow-origin': '*',
        'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS',
        'access-control-max-age': '1728000',
    }),
}

/**
 * @param {any} body
 * @param {number} status
 * @param {Object<string, string>} headers
 */
function makeRes(body, status = 200, headers = {}) {
    headers['access-control-allow-origin'] = '*'
    return new Response(body, {status, headers})
}


/**
 * @param {string} urlStr
 */
function newUrl(urlStr) {
    try {
        return new URL(urlStr)
    } catch (err) {
        return null
    }
}


addEventListener('fetch', e => {
    const ret = fetchHandler(e)
        .catch(err => makeRes('cfworker error:\n' + err.stack, 502))
    e.respondWith(ret)
})


/**
 * @param {FetchEvent} e
 */
async function fetchHandler(e) {
    const getReqHeader = (key) => e.request.headers.get(key);

    let url = new URL(e.request.url);

    if (url.pathname === '/') {
        // Fetch and return the home page HTML content with replacement
        let response = await fetch(home_page_url);
        let text = await response.text();
        text = text.replace(/{workers_host}/g, workers_host);
        return new Response(text, {
            status: response.status,
            headers: response.headers
        });
    }

    if (url.pathname === '/token') {
        let token_parameter = {
            headers: {
                'Host': 'auth.docker.io',
                'User-Agent': getReqHeader("User-Agent"),
                'Accept': getReqHeader("Accept"),
                'Accept-Language': getReqHeader("Accept-Language"),
                'Accept-Encoding': getReqHeader("Accept-Encoding"),
                'Connection': 'keep-alive',
                'Cache-Control': 'max-age=0'
            }
        };
        let token_url = auth_url + url.pathname + url.search
        return fetch(new Request(token_url, e.request), token_parameter)
    }

    url.hostname = hub_host;

    let parameter = {
        headers: {
            'Host': hub_host,
            'User-Agent': getReqHeader("User-Agent"),
            'Accept': getReqHeader("Accept"),
            'Accept-Language': getReqHeader("Accept-Language"),
            'Accept-Encoding': getReqHeader("Accept-Encoding"),
            'Connection': 'keep-alive',
            'Cache-Control': 'max-age=0'
        },
        cacheTtl: 3600
    };

    if (e.request.headers.has("Authorization")) {
        parameter.headers.Authorization = getReqHeader("Authorization");
    }

    let original_response = await fetch(new Request(url, e.request), parameter)
    let original_response_clone = original_response.clone();
    let original_text = original_response_clone.body;
    let response_headers = original_response.headers;
    let new_response_headers = new Headers(response_headers);
    let status = original_response.status;

    if (new_response_headers.get("Www-Authenticate")) {
        let auth = new_response_headers.get("Www-Authenticate");
        let re = new RegExp(auth_url, 'g');
        new_response_headers.set("Www-Authenticate", response_headers.get("Www-Authenticate").replace(re, workers_url));
    }

    if (new_response_headers.get("Location")) {
        return httpHandler(e.request, new_response_headers.get("Location"))
    }

    let response = new Response(original_text, {
        status,
        headers: new_response_headers
    })
    return response;

}


/**
 * @param {Request} req
 * @param {string} pathname
 */
function httpHandler(req, pathname) {
    const reqHdrRaw = req.headers

    // preflight
    if (req.method === 'OPTIONS' &&
        reqHdrRaw.has('access-control-request-headers')
    ) {
        return new Response(null, PREFLIGHT_INIT)
    }

    let rawLen = ''

    const reqHdrNew = new Headers(reqHdrRaw)

    const refer = reqHdrNew.get('referer')

    let urlStr = pathname

    const urlObj = newUrl(urlStr)

    /** @type {RequestInit} */
    const reqInit = {
        method: req.method,
        headers: reqHdrNew,
        redirect: 'follow',
        body: req.body
    }
    return proxy(urlObj, reqInit, rawLen, 0)
}


/**
 *
 * @param {URL} urlObj
 * @param {RequestInit} reqInit
 */
async function proxy(urlObj, reqInit, rawLen) {
    const res = await fetch(urlObj.href, reqInit)
    const resHdrOld = res.headers
    const resHdrNew = new Headers(resHdrOld)

    // verify
    if (rawLen) {
        const newLen = resHdrOld.get('content-length') || ''
        const badLen = (rawLen !== newLen)

        if (badLen) {
            return makeRes(res.body, 400, {
                '--error': `bad len: ${newLen}, except: ${rawLen}`,
                'access-control-expose-headers': '--error',
            })
        }
    }
    const status = res.status
    resHdrNew.set('access-control-expose-headers', '*')
    resHdrNew.set('access-control-allow-origin', '*')
    resHdrNew.set('Cache-Control', 'max-age=1500')

    resHdrNew.delete('content-security-policy')
    resHdrNew.delete('content-security-policy-report-only')
    resHdrNew.delete('clear-site-data')

    return new Response(res.body, {
        status,
        headers: resHdrNew
    })
}

部署完成后,点击设置->触发器->添加自定义域,绑定自己的域名即可。配置完成后,在 /etc/docker/daemon.json 中修改成你的域名,具体参考上方配置,然后重启 docker。

原文地址:https://blog.csun.site/posts/b3f62daf.html

]]>
<![CDATA[Docker容器 - 数据打包迁移全过程记录]]> https://izlzl.com/archives/1704.html 2023-02-23T18:09:00+08:00 2023-02-23T18:09:00+08:00 知了小站 https://izlzl.com 记录下Docker容器打包迁移的一些命令:

# 确认要迁移容器的名称
docker ps -a
# 打包容器为新的镜像
docker commit 容器名 镜像名
# 把镜像保存为tar文件 
docker save 镜像名 >文件名称.tar
# 将文件传输到另一台服务器,用SCP命令
scp -P 22 /文件路径/文件名称.tar root@接收文件的服务器的IP:/存放文件的目录
# 恢复镜像
docker load < 文件名称.tar

恢复镜像后,用同样的方法创建容器即可。

]]>
<![CDATA[记 Docker 运行 Logstash out of memory 问题]]> https://izlzl.com/archives/1667.html 2022-08-06T15:50:00+08:00 2022-08-06T15:50:00+08:00 知了小站 https://izlzl.com 最近在 Docker 容器上搭建 Zookeeper+Kafka+Logstash+Elasticsearch+Kibana 日志分析系统,在运行 Logstash 和 Elasticsearch 时遇到了如下错误:

library initialization failed - unable to allocate file descriptor table - out of memorylibrary
initialization failed - unable to allocate file descriptor table - out of memory

l6hlzlt2.png

在这里记录下解决方案。

解决方案

通过重写 Docker 的 ExecStart 的参数解决

sudo systemctl edit docker

进入后,添加或者修改对应参数

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --default-ulimit nofile=65536:65536 -H fd://

最后重启 Docker

sudo systemctl daemon-reload
sudo systemctl restart docker

参考:https://stackoverflow.com/questions/68776387/docker-library-initialization-failed-unable-to-allocate-file-descriptor-tabl

]]>
<![CDATA[Centos 中删除 docker0 虚拟网卡]]> https://izlzl.com/archives/1508.html 2022-04-21T14:32:00+08:00 2022-04-21T14:32:00+08:00 知了小站 https://izlzl.com Centos 删除 Docker 环境后,并不会将虚拟网卡docker0 删除。因此就需要手动处理下

ifconfig docker0 down

yum -y install bridge-utils

brctl delbr docker0

这样处理后,才算彻底删除了 Docker 环境。

注意:如果你没有删除删除 Docker 环境,那么下次系统启动 Docker Daemon 时还是会自动创建 docker0 网桥。

]]>
<![CDATA[进入 Docker 容器报错 OCI runtime exec failed: exec failed]]> https://izlzl.com/archives/1378.html 2021-10-23T11:03:00+08:00 2021-10-23T11:03:00+08:00 知了小站 https://izlzl.com 使用 docker exec -it 容器名/容器ID /bin/bash 进入容器报错

报错信息如下:

kv37xtro.png

OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "/bin/bash": stat /bin/bash: no such file or directory: unknown

查看相应镜像发现镜像是使用alpine制作的

再次输入命令

docker exec -it 容器名/容器ID /bin/sh

进入成功

]]>
<![CDATA[Centos 7 彻底卸载清除 Docker 环境]]> https://izlzl.com/archives/1278.html 2021-03-08T21:30:00+08:00 2021-03-08T21:30:00+08:00 知了小站 https://izlzl.com CentOS 7 彻底卸载 Docker 环境流程如下

杀死所有运行容器
docker kill $(docker ps -a -q)
删除所有Docker容器
docker rm $(docker ps -a -q)
删除所有Docker镜像
docker rmi $(docker images -q)
停止 docker 服务
systemctl stop docker
删除存储目录
rm -rf /etc/docker
rm -rf /run/docker
rm -rf /var/lib/dockershim
rm -rf /var/lib/docker

如果发现删除不掉,需要先 umount,如

umount /var/lib/docker/devicemapper
卸载 docker

查看已安装的 docker 包

yum list installed | grep docker

卸载相关包

l1nn4gdy.png

yum remove docker-ce*
yum remove containerd.io.x86_64

原文地址:https://www.omicsclass.com/article/1184

]]>
<![CDATA[Typecho 迁移到 Docker 的过程记录]]> https://izlzl.com/archives/1237.html 2020-08-24T21:07:00+08:00 2020-08-24T21:07:00+08:00 知了小站 https://izlzl.com 前言

之前博客搭建在阿里云的机器上,现在快过期了,加上原机器带宽太小,就不打算续费了,现在就需要将 Typecho 博客迁移到新机器上。以前使用的是宝塔面板搭建的,现在不想把机器环境搞得乱七八糟,因此选择使用 Docker 来搭建 Typecho。

安装Docker

使用一键脚本安装Docker

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun && systemctl start docker && systemctl enable docker

如果提示 curl 命令不存在,就需要先安装 curl

# Centos
yum -y install curl

# Ubuntu、Debian
apt -y install curl

配置镜像加速

如果你机器是国内的机器,那么需要配置镜像加速

阿里云镜像获取地址:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors

image.png

修改 /etc/docker/daemon.json,文件不存在就创建一个

{
  "registry-mirrors": [
    "你的加速地址"
  ]
}

安装数据库

这里选择的是 Mariadb,可自行选择数据库镜像,这里配置参数就不做解释了,懂的都懂

docker run -d -v /home/mysql:/var/lib/mysql \
-p 3306:3306 -e MYSQL_ROOT_PASSWORD=密码 \
--privileged=true --restart=always --name mariadb mariadb

安装 PHP

安装官方的 php:7.2-fpm 镜像后,还需要进入容器安装 mysql pdo 才能使用 Mysql 数据库,比较麻烦,这里我自己在 php:7.2-fpm 的基础上构建了个带 mysql pdo 的镜像,可以选择性使用。

docker run -d -v /home/nginx/html:/var/www/html \
-p 8080:8080 --link mariadb:mariadb --name php dqjdda/php

安装 Nginx

docker run -d \
--link php:php \
--name nginx --restart always \
-p 80:80 -p 443:443 \
-e "TZ=Asia/Shanghai" \
-v /home/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /home/nginx/conf.d:/etc/nginx/conf.d \
-v /home/nginx/logs:/var/log/nginx \
-v /home/nginx/cert:/etc/nginx/cert \
-v /home/nginx/html:/var/www/html \
nginx

配置 Nginx

  • /home/nginx/conf.d 用于放配置文件
  • /home/nginx/logs 存放日志
  • /home/nginx/cert 存放证书
  • /home/nginx/html 存放网页

/home/nginx/conf.d 目录创建文件 blog.conf

server {

    listen 443 ssl http2;
    server_name  izlzl.com;
    gzip on;

    # 缓存SSL
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # 证书配置
    ssl_certificate       /etc/nginx/cert/ydyno.com/ydyno.com_chain.crt;
    ssl_certificate_key   /etc/nginx/cert/ydyno.com/ydyno.com_key.key;

    root /var/www/html/ydyno;
    index index.php;

    # 伪静态
    if (!-e $request_filename) {
       rewrite ^(.*)$ /index.php$1 last;
    }
  
   location ~ .*\.php(\/.*)*$ {
        include        fastcgi_params;
        fastcgi_param  PATH_INFO $fastcgi_path_info;
        fastcgi_param  PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_param  SCRIPT_NAME $fastcgi_script_name;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_index  index.php;
        fastcgi_pass   php:9000;
       }

   location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ {
       expires 5d;
   }

   # deny access to . files, for security
   location ~ /\. {
       log_not_found off;
       deny all;
   }
}

server {
    listen 80;
    server_name  izlzl.com ydyno.com;
    return 301 https://izlzl.com$request_uri;
}

迁移博客

直接将老服务器的博客目录移动到 /var/www/html/ydyno ,然后将数据库导出,然后导入到新库即可

]]>