{"id":461162,"date":"2025-05-27T15:00:31","date_gmt":"2025-05-27T15:00:31","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=461162"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=461162","title":{"rendered":"<span>Docker Registry \u043d\u0430 Python \u0441 \u043d\u0443\u043b\u044f<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>\u0418\u043d\u0442\u0440\u043e<\/h3>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u0412 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u043c\u0438\u0440\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 docker \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u043a\u0440\u0430\u0435\u0443\u0433\u043e\u043b\u044c\u043d\u044b\u0445 \u043a\u0430\u043c\u043d\u0435\u0439 \u044d\u0440\u0433\u043e\u043d\u043e\u043c\u0438\u043a\u0438 \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430, \u043d\u0430\u0440\u044f\u0434\u0443 \u0441 git, \u0440\u0430\u0437\u043d\u043e\u0433\u043e \u0440\u043e\u0434\u0430 IDE \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430\u043c\u0438, \u0430 \u0434\u043b\u044f \u043a\u043e\u0433\u043e-\u0442\u043e &#8212; \u0438 GPT. \u0418, \u0445\u043e\u0442\u044c \u0432 \u0441\u0430\u043c\u043e\u043c \u043f\u043e \u0441\u0435\u0431\u0435 docker \u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u0442\u0430\u043a\u043e\u0433\u043e \u0443\u0436 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e (LXC, CRI-O, \u0447\u0438\u0441\u0442\u044b\u0439 containerd, \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043b\u0435\u0433\u043a\u0438\u0435 \u0438 \u0441\u0440\u0435\u0434\u043d\u0438\u0435 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u043a\u0438, \u0431\u0435\u0441\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b, \u0434\u043b\u044f \u043e\u0441\u043e\u0431\u044b\u0445 \u0446\u0435\u043d\u0438\u0442\u0435\u043b\u0435\u0439 &#8212; chroot. \u0422\u044b\u0441\u044f\u0447\u0438 \u0438\u0445), \u043e\u043d \u043f\u043e\u0434\u043a\u0443\u043f\u0430\u0435\u0442 \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0440\u0430\u0437\u0432\u0435\u0441\u0438\u0441\u0442\u043e\u0439 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 &#8212; \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 Docker \u0435\u0441\u0442\u044c \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u043e\u0432 \u043a\u043e\u0434\u0430 \u0438 IDE, \u043f\u0440\u043e \u043d\u0435\u0433\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u043c\u043d\u043e\u0433\u043e\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0435 \u043a\u043d\u0438\u0433\u0438, \u0441\u0442\u0430\u0442\u044c\u0438 \u0438 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u044b \u043e\u0442 \u0438\u043d\u0434\u0443\u0441\u043e\u0432, \u0430 \u043f\u043e \u0435\u0433\u043e \u0440\u0435\u0435\u0441\u0442\u0440\u0430\u043c (\u043e\u0442 Docker Hub \u0434\u043e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u043f \u043d\u0430 \u0433\u0438\u0442\u043b\u0430\u0431\u0435) \u0443\u0434\u043e\u0431\u043d\u043e \u0440\u0430\u0437\u043b\u043e\u0436\u0435\u043d \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0435\u0441\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043d\u0430 \u043f\u043b\u0430\u043d\u0435\u0442\u0435 \u0441\u043e\u0444\u0442.<\/p>\n<p>\u0412\u043e\u0442 \u043e \u0440\u0435\u0435\u0441\u0442\u0440\u0430\u0445 (registry) Docker \u0438 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c.<\/p>\n<p>\u0421\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435 \u0440\u0435\u0435\u0441\u0442\u0440 &#8212; \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e REST-\u0441\u0435\u0440\u0432\u0438\u0441 \u0438 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435. \u041e\u0431\u0440\u0430\u0437\u044b \u043f\u0440\u0438\u043b\u0435\u0442\u0430\u044e\u0442 \u0432 \u0440\u0435\u0435\u0441\u0442\u0440 \u0432 \u0432\u0438\u0434\u0435 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0445 \u0441\u043b\u043e\u0435\u0432 (\u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438 \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 Dockerfile, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0441\u043e\u0431\u0438\u0440\u0430\u043b\u0441\u044f \u043e\u0431\u0440\u0430\u0437) \u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e JSON-\u0444\u0430\u0439\u043b\u0430 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u0434\u043b\u044f nginx:1.27.3 (\u0434\u043b\u0438\u043d\u043d\u044b\u0435 SHA256-\u0445\u0435\u0448\u0438 \u0441\u043b\u043e\u0435\u0432 \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u044b \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0432\u043e\u0441\u043f\u0440\u0438\u044f\u0442\u0438\u044f)<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\">{ \"schemaVersion\": 2, \"mediaType\": \"application\/vnd.docker.distribution.manifest.v2+json\", \"config\": { \"mediaType\": \"application\/vnd.docker.container.image.v1+json\", \"digest\": \"sha256:c59e92...\" }, \"layers\": [ { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:d2eb42...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:ee083d...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:5afd65...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:8c2914...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:1e8aef...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:a982d0...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:ab571a...\" } ] }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041f\u044b\u0442\u043b\u0438\u0432\u044b\u0439 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044c (\u0432\u043f\u0440\u043e\u0447\u0435\u043c, \u043a\u0430\u043a \u0438 \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u0442\u043e\u0436\u0435) \u0441\u0440\u0430\u0437\u0443 \u0437\u0430\u043c\u0435\u0442\u0438\u0442, \u0447\u0442\u043e \u0432 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0435 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e. \u042d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0434\u043e\u043a\u0435\u0440\u0430, \u0441\u043b\u043e\u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 digest \u0437\u0430\u0442\u044f\u043d\u0443\u0442\u044c \u0438\u0437 \u0440\u0435\u0435\u0441\u0442\u0440\u0430 \u0438 \u0432 \u043a\u0430\u043a\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0441\u0448\u0438\u0442\u044c \u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0435. \u0427\u0443\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0435\u043d \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 (\u0431\u043b\u043e\u043a config). \u0418\u0437 \u043d\u0435\u0433\u043e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0442\u0440\u044f\u0445\u043d\u0443\u0442\u044c \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0431\u0443\u0434\u0443\u0449\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 (\u0434\u043b\u044f \u0447\u0435\u0433\u043e \u0431\u044b \u0432\u0430\u043c \u044d\u0442\u043e \u043d\u0438 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u043b\u043e\u0441\u044c)<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f nginx:1.27.3<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\">{ \"architecture\": \"amd64\", \"config\": { \"ExposedPorts\": { \"80\/tcp\": {} }, \"Env\": [ \"PATH=\/usr\/local\/sbin:\/usr\/local\/bin:\/usr\/sbin:\/usr\/bin:\/sbin:\/bin\", \"NGINX_VERSION=1.27.3\", \"NJS_VERSION=0.8.7\", \"NJS_RELEASE=1~bookworm\", \"PKG_RELEASE=1~bookworm\", \"DYNPKG_RELEASE=1~bookworm\" ], \"Entrypoint\": [ \"\/docker-entrypoint.sh\" ], \"Cmd\": [ \"nginx\", \"-g\", \"daemon off;\" ], \"Labels\": { \"maintainer\": \"NGINX Docker Maintainers \\u003cdocker-maint@nginx.com\\u003e\" }, \"StopSignal\": \"SIGQUIT\" }, \"created\": \"2024-11-26T18:42:08Z\", \"os\": \"linux\", \"rootfs\": { \"type\": \"layers\", \"diff_ids\": [ \"sha256:7914c8...\", \"sha256:2cdcae...\", \"sha256:0b2daf...\", \"sha256:a280e1...\", \"sha256:166490...\", \"sha256:1b78ff...\", \"sha256:e2eb04...\" ] } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0421\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0438\u0437 \u044d\u0442\u0438\u0445 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u043e\u0432, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u0437 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0438\u0437 \u0440\u0435\u0435\u0441\u0442\u0440\u0430 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0445 \u0441\u043b\u043e\u0435\u0432 \u0438 \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440. \u0412\u0441\u0435 \u0433\u0435\u043d\u0438\u0430\u043b\u044c\u043d\u043e\u0435 &#8212; \u043f\u0440\u043e\u0441\u0442\u043e.<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043b\u043e\u0438<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0421\u043b\u043e\u0438, \u043a\u0441\u0442\u0430\u0442\u0438, \u044d\u0442\u043e \u043f\u043e \u0441\u0432\u043e\u0435\u0439 \u0441\u0443\u0442\u0438 tar-gzip-\u0430\u0440\u0445\u0438\u0432\u044b, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435 \u0440\u0430\u0437\u043d\u0438\u0446\u0443 \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0434\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 (COPY, RUN \u0438 \u0442.\u0434.) \u0438 \u043f\u043e\u0441\u043b\u0435<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 Dockerfile \u0432\u0441\u0435 \u0442\u043e\u0433\u043e \u0436\u0435 nginx:1.27.3<\/p>\n<pre><code>COPY 10-listen-on-ipv6-by-default.sh \/docker-entrypoint.d<\/code><\/pre>\n<p>\u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0441\u043b\u043e\u0439, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u0438\u043d \u0444\u0430\u0439\u043b<\/p>\n<pre><code class=\"bash\"># tar -xvzf 8c2914db26a3b019cf8b9dcd9ffec286f7aa4db2165ee79bb214b6e66dd461c2 drwxr-xr-x 2 root root 4096 Feb  4 04:25 . drwxr-xr-x 3 root root 4096 May 26 14:48 .. -rwxr-xr-x 1 root root 2125 Feb  4 04:25 10-listen-on-ipv6-by-default.sh<\/code><\/pre>\n<p>\u041d\u0443 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e \u043d\u0435\u0442, \u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043e \u0447\u0435\u043c \u043d\u0430 \u0441\u043e\u0431\u0435\u0441\u0430\u0445 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c.<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043c\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 Docker Registry &#8212; \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430\u044f. \u041d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e, \u0447\u0442\u043e \u044f \u0440\u0435\u0448\u0438\u043b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 <a href=\"https:\/\/gitverse.ru\/icecloud\/ice-registry\" rel=\"noopener noreferrer nofollow\">opensource-\u043f\u0440\u043e\u0435\u043a\u0442<\/a>, \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0435\u0435\u0441\u0442\u0440\u0430. \u041d\u0443 \u0438, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043f\u0440\u043e\u0441\u0442\u043e \u0442\u0430\u043a \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u043d\u0430\u0434\u043e\u0435\u0441\u0442\u044c, \u044f \u0440\u0435\u0448\u0438\u043b \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u0434 \u043d\u0438\u043c \u043f\u0440\u043e\u0442\u0430\u0449\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437 1-2-3 \u0441\u0442\u0430\u0442\u044c\u0438 \u043d\u0430 \u0425\u0430\u0431\u0440\u0435, \u0432\u0434\u0440\u0443\u0433 \u043a\u043e\u043c\u0443-\u0442\u043e \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e (\u0438\u043b\u0438 \u0445\u043e\u0442\u044f \u0431\u044b \u043f\u043e\u043b\u0435\u0437\u043d\u043e).<\/p>\n<p>\u041d\u0430 \u0432\u0441\u044f\u043a\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439, \u0434\u0438\u0441\u043a\u043b\u0435\u0439\u043c\u0435\u0440: \u044d\u0442\u043e \u041d\u0415 \u0421\u0410\u041c\u042b\u0419 \u041e\u041f\u0422\u0418\u041c\u0410\u041b\u042c\u041d\u042b\u0419 \u0441\u043f\u043e\u0441\u043e\u0431 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u0435\u0441\u0442\u0440\u0430, \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0443\u0436\u0435\u043d \u0440\u0435\u0435\u0441\u0442\u0440 \u0441\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435. \u041b\u0443\u0447\u0448\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0441\u0435\u0431\u0435 \u0433\u0438\u0442\u043b\u0430\u0431, Nexus \u0438\u043b\u0438, \u043d\u0430 \u0445\u0443\u0434\u043e\u0439 \u043a\u043e\u043d\u0435\u0446, registry \u0441 \u0434\u043e\u043a\u0435\u0440\u0445\u0430\u0431\u0430. \u0422\u0430\u043c \u0443\u0436\u0435 \u0432\u0441\u0435 \u044d\u0442\u043e \u0435\u0441\u0442\u044c, \u0438 \u0434\u0430\u0436\u0435 \u0431\u043e\u043b\u044c\u0448\u0435, \u0430 \u0431\u043e\u043d\u0443\u0441\u043e\u043c &#8212; \u043a\u043e\u043c\u044c\u044e\u043d\u0438\u0442\u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u0435, \u043d\u0430 SO \u043a\u0443\u0447\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u0438 \u0434\u0430\u0436\u0435 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438 \u043f\u0440\u043e \u043d\u0438\u0445 \u0437\u043d\u0430\u044e\u0442 \u0438 \u043c\u043e\u0433\u0443\u0442 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043f\u043e\u0441\u043e\u0431\u0438\u0442\u044c. \u041c\u044b \u0436\u0435 \u0442\u0443\u0442 &#8212; \u043f\u043e\u043a\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441 \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 \u0446\u0435\u043b\u044c\u044e.<\/p>\n<p>\u0412\u0441\u0435, \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0434\u0438\u043b. \u041f\u043e\u0433\u043d\u0430\u043b\u0438<\/p>\n<h3>\u0421\u0442\u0435\u043a<\/h3>\n<p>\u042d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442 &#8212; \u043d\u0435 \u0438\u0437 \u0442\u0435\u0445, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u044f \u043e\u0431\u044b\u0447\u043d\u043e \u0438\u0437\u0443\u0447\u0430\u044e \u043d\u043e\u0432\u044b\u0435 \u044f\u0437\u044b\u043a\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438\u043b\u0438 \u043d\u043e\u0432\u044b\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0442\u0435\u043a \u043b\u0438\u0447\u043d\u043e \u0434\u043b\u044f \u043c\u0435\u043d\u044f &#8212; \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439<\/p>\n<pre><code>[tool.poetry.dependencies] python = \"&amp;gt;=3.11, &amp;lt;3.13\" fastapi = \"==0.115.12\" pydantic = {extras = [\"email\"], version = \"2.11.5\"} pydantic-settings = \"==2.9.1\" Hypercorn = \"==0.17.3\" uvloop = \"==0.21.0\" toml = \"==0.10.2\" python-ulid = \"==3.0.0\" aiofiles = \"==24.1.0\" orjson = \"==3.10.18\"<\/code><\/pre>\n<p>\u0418\u0437 \u044d\u0442\u043e\u0433\u043e \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c FastAPI \u0434\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u043c, \u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e &#8212; \u043c\u043e\u0439 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0439 \u0442\u0443\u043b\u0441\u0435\u0442, \u043d\u0435 \u043d\u0435\u0441\u0443\u0449\u0438\u0439 \u043d\u0430 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0434\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u043e\u0441\u043e\u0431\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438. \u041d\u043e, \u0434\u0443\u043c\u0430\u044e, \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c<\/p>\n<p>\u041d\u0443 \u0438 \u0432\u0435\u0440\u043d\u044b\u0439 <code>poetry<\/code> &#8212; \u0437\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0433\u043e \u043d\u0430 \u0440\u0430\u0437\u0440\u0443\u043b\u0438\u0432\u0430\u043d\u0438\u0438 \u0434\u0435\u0440\u0435\u0432\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/p>\n<h3>\u0421\u0442\u0430\u0440\u0442 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 \u0432\u0435\u0440\u0441\u0438\u0438 0.1.0 (\u0442\u043e \u0435\u0441\u0442\u044c, \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438)<\/p>\n<pre><code class=\"bash\">.\/ice-registry\/ \u251c\u2500\u2500 Dockerfile \u251c\u2500\u2500 LICENSE.md \u251c\u2500\u2500 README.md \u251c\u2500\u2500 config.py \u251c\u2500\u2500 deploy \u2502   \u2514\u2500\u2500 config \u2502       \u2514\u2500\u2500 nginx \u2502           \u2514\u2500\u2500 registry.conf \u251c\u2500\u2500 docker-compose-local.yaml \u251c\u2500\u2500 poetry.lock \u251c\u2500\u2500 pyproject.toml \u2514\u2500\u2500 registry     \u251c\u2500\u2500 __main__.py     \u251c\u2500\u2500 api     \u2502   \u251c\u2500\u2500 __init__.py     \u2502   \u251c\u2500\u2500 docker_v2     \u2502   \u2502   \u251c\u2500\u2500 __init__.py     \u2502   \u2502   \u251c\u2500\u2500 check_blob.py     \u2502   \u2502   \u251c\u2500\u2500 create_manifest.py     \u2502   \u2502   \u251c\u2500\u2500 create_upload.py     \u2502   \u2502   \u251c\u2500\u2500 finalize_upload.py     \u2502   \u2502   \u251c\u2500\u2500 get_blob.py     \u2502   \u2502   \u251c\u2500\u2500 get_manifest.py     \u2502   \u2502   \u251c\u2500\u2500 get_tags_list.py     \u2502   \u2502   \u251c\u2500\u2500 ping.py     \u2502   \u2502   \u251c\u2500\u2500 router.py     \u2502   \u2502   \u2514\u2500\u2500 update_upload.py     \u2502   \u2514\u2500\u2500 status     \u2502       \u251c\u2500\u2500 __init__.py     \u2502       \u251c\u2500\u2500 get_health.py     \u2502       \u2514\u2500\u2500 router.py     \u2514\u2500\u2500 utils         \u251c\u2500\u2500 __init__.py         \u251c\u2500\u2500 auth.py         \u2514\u2500\u2500 version.py<\/code><\/pre>\n<p>\u0418\u0442\u0430\u043a, \u0441 \u0447\u0435\u0433\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u043b\u044e\u0431\u043e\u0439 \u043f\u0440\u0438\u043b\u0438\u0447\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441? \u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u0441 \u0445\u0435\u043b\u0441\u0447\u0435\u043a\u0430 \u0438 \u0441\u0431\u043e\u0440\u043a\u0438<\/p>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u0440\u0443\u0447\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0432\u0441\u0435\u0433\u0434\u0430 <code>200 Ok<\/code>. \u0422\u0430\u043a\u043e\u0439 \u0432\u043e\u0442 \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u044b\u0439 healthcheck, \u0434\u0430<\/p>\n<pre><code class=\"python\"># registry\/api\/status\/get_health.py  from .router import router   @router.get(     \"\/health\",     summary=\"\u0421\u0442\u0430\u0442\u0443\u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\", ) async def get_health():     return True <\/code><\/pre>\n<p>\u0420\u043e\u0443\u0442\u0435\u0440 \u0434\u043b\u044f \u043c\u043e\u0434\u0443\u043b\u044f status \u0442\u043e\u0436\u0435 \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u044b\u0439<\/p>\n<pre><code class=\"python\"># registry\/api\/status\/router.py  from fastapi import APIRouter   router = APIRouter(tags=[\"status\"])  <\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u0432 \u044d\u0442\u043e\u043c \u043c\u043e\u0434\u0443\u043b\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0430\u044f \u0440\u0443\u0447\u043a\u0430 \u0441\u0442\u0430\u0442\u0443\u0441\u0430, \u0445\u0435\u043b\u0441\u0447\u0435\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0437\u0434\u043e\u0440\u043e\u0432\u044c\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430. \u041d\u043e \u043d\u0435 \u0441\u0435\u0433\u043e\u0434\u043d\u044f<\/p>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0443\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0442\u0430\u0449\u0438\u0442\u044c \u0440\u043e\u0443\u0442\u0435\u0440 \u043c\u043e\u0434\u0443\u043b\u044f status \u0432 \u043e\u0431\u0449\u0438\u0439 \u0440\u043e\u0443\u0442\u0435\u0440 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f&#8230;<\/p>\n<pre><code class=\"python\"># registry\/api\/__init__.py  from fastapi import APIRouter  from .status import router as status_router   router = APIRouter() router.include_router(status_router, prefix=\"\/status\")  <\/code><\/pre>\n<p>&#8230; \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c, \u043d\u0430\u043a\u043e\u043d\u0435\u0446, \u0441\u0430\u043c\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/p>\n<pre><code class=\"python\"># registry\/__main__.py  import asyncio import uvloop  from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware from hypercorn.asyncio import serve from hypercorn.config import Config from starlette.exceptions import HTTPException as StarletteHTTPException  from config import settings  from .api import router   def run():     app = FastAPI(         title=\"Ice Docker Registry\",         version=\"0.1.0\",         docs_url=\"\/openapi\" if settings.debug else None,         redoc_url=None,         root_path=settings.root_path,         debug=settings.debug,     )      app.include_router(router)      app.add_middleware(         CORSMiddleware,         allow_origins=[\"*\"],         allow_credentials=True,         allow_methods=[\"*\"],         allow_headers=[\"*\"],     )      @app.exception_handler(StarletteHTTPException)     async def http_exception_handler(request: Request, exc: StarletteHTTPException):         return JSONResponse(             status_code=exc.status_code,             headers=exc.headers,             content={                 \"meta\": {                     \"url\": str(request.url),                     \"client\": request.client.host,                 },                 \"status\": exc.status_code,                 \"method\": request.method,                 \"error_text\": exc.detail,             })      hypercorn_config = Config()     hypercorn_config.bind = [ settings.bind_host ]      uvloop.install()     asyncio.run((serve(app, hypercorn_config)))   if __name__ == \"__main__\":     run()  <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u0440\u0430\u0437\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0430\u0436\u043d\u044b\u0445 \u0432\u0435\u0449\u0435\u0439:<\/p>\n<ul>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0440\u043e\u0443\u0442\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>\u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f (\u0438 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0431\u0435\u0437\u0430\u043b\u0430\u0431\u0435\u0440\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0439 allow) \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a CORSMiddleware<\/p>\n<\/li>\n<li>\n<p>\u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430 <code>@app.exception_handler<\/code> \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u0449\u0438\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u0441\u0435\u0445 http-\u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 (\u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u0438 \u043f\u043e\u0442\u043e\u043c\u043a\u043e\u0432 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0433\u043e HTTPException). \u0412\u0430\u0436\u043d\u044b\u0439 \u0448\u0430\u0433, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f \u0434\u0435\u043b\u0430\u044e \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0435. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441 \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u0442\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\/\u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435 \u0437\u0430\u0434\u0443\u043c\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043e\u0448\u0438\u0431\u043e\u043a, \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0439\u0437\u0438\u0442\u044c HTTPException \u0432 \u043d\u0443\u0436\u043d\u044b\u0445 \u043c\u0435\u0441\u0442\u0430\u0445 \u043a\u043e\u0434\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0444\u0438\u0433 Hypercorn<\/p>\n<\/li>\n<li>\n<p>\u043d\u0443 \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0441\u0435 \u0442\u043e\u0433\u043e \u0436\u0435 Hypercorn (\u0430 \u0442\u0430\u043a\u0436\u0435 &#8212; \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0435\u0442\u043b\u0438 uvloop), \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435, \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u0434 \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0431\u043e\u0440\u043a\u043e\u0439 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c &#8212; \u0444\u0430\u0439\u043b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/p>\n<pre><code class=\"python\"># config.py  import os  from pydantic import Field, IPvAnyAddress from pydantic_settings import SettingsConfigDict, BaseSettings   class ServiceSettings(BaseSettings):     model_config = SettingsConfigDict(         env_file=os.getenv('ENV_FILE', '.env'),         env_file_encoding='utf-8',         env_nested_delimiter='__',     )      @property     def bind_host(self):         return f\"{self.bind_ip}:{self.bind_port}\"      debug: bool = False     bind_ip: IPvAnyAddress = Field(default=\"0.0.0.0\")     bind_port: int = 8080     root_path: str = \"\"   settings = ServiceSettings() <\/code><\/pre>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 &#8212; \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0435\u0449\u0435 \u043d\u0435 \u0443\u0441\u043f\u0435\u043b \u043e\u0431\u0440\u0430\u0441\u0442\u0438 \u043c\u044f\u0441\u043e\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430<\/p>\n<p>\u0418\u0442\u0430\u043a, \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0435\u0449\u0435 \u043d\u0435 \u043d\u0435\u0441\u0435\u0442 \u0432 \u0441\u0435\u0431\u0435 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u043d\u043e \u0435\u0433\u043e \u0443\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c<\/p>\n<pre><code>FROM python:3.12.3  ENV PYTHONUNBUFFERED=1  WORKDIR \/app  RUN pip install poetry  COPY poetry.lock \/app\/poetry.lock COPY pyproject.toml \/app\/pyproject.toml  RUN poetry config virtualenvs.create false &amp;&amp; poetry install --no-interaction  COPY .\/registry \/app\/registry COPY .\/config.py \/app\/config.py  HEALTHCHECK --interval=5s --timeout=10s --retries=3 CMD curl -sS localhost:8080\/status\/health || exit 1  CMD [ \"python\", \"-m\", \"registry\" ] <\/code><\/pre>\n<p>\u0412\u0441\u0435 \u043f\u0440\u043e\u0441\u0442\u043e, \u0438 \u0434\u0430\u0436\u0435 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0442\u044c\u0441\u044f \u043d\u0435 \u0431\u0443\u0434\u0435\u0442.<\/p>\n<p>\u0417\u0430\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u043c (\u0432 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435) <code>python:3.12.3<\/code>, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0442\u0443\u0434\u0430 poetry, \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c lock \u0438 toml \u0444\u0430\u0439\u043b\u044b \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c, \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c \u0438 \u0444\u0430\u0439\u043b \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439), \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0430\u0432\u0442\u043e\u0440\u0441\u043a\u0438\u0439 docker-healthcheck \u0438 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u043a\u0430\u043a \u0441\u0435\u0440\u0432\u0438\u0441 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c.<\/p>\n<p>\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c, \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0441\u0432\u0430\u0433\u0433\u0435\u0440, \u0440\u0430\u0434\u0443\u0435\u043c\u0441\u044f \u0446\u0435\u043b\u043e\u0439 \u043e\u0434\u043d\u043e\u0439 \u0440\u0443\u0447\u043a\u0435, \u0434\u0435\u043b\u0430\u044e\u0449\u0435\u0439 \u043d\u0438\u0447\u0435\u0433\u043e.<\/p>\n<details class=\"spoiler\">\n<summary>\u0428\u0430\u0433 \u0432 \u0441\u0442\u043e\u0440\u043e\u043d\u0443. Reverse Proxy<\/summary>\n<div class=\"spoiler__content\">\n<p>\u042f \u0432\u0441\u0435\u0433\u0434\u0430 \u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u043f\u0440\u043e\u043a\u0441\u0438 \u0432\u0441\u0435 \u0441\u0435\u0442\u0435\u0432\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u044e\u0442 \u043d\u0430\u0440\u0443\u0436\u0443 \u0441\u0432\u043e\u0438 \u043f\u043e\u0440\u0442\u044b. \u0422\u043e\u043c\u0443 \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438\u0447\u0438\u043d &#8212; \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u0443\u044e \u0431\u0430\u043b\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u043a\u0443, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u0432\u0430\u0439\u0442\u043b\u0438\u0441\u0442\u043e\u043c, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043b\u0435\u0433\u043a\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0435\u0432\u0440\u0430\u0439\u0442 \u0438 \u043f\u043e\u0434\u0446\u0435\u043f\u0438\u0442\u044c SSL. \u041d\u043e \u0432 \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u0441\u0442\u0435\u043f\u0435\u043d\u0438 \u044d\u0442\u043e \u0434\u0435\u043b\u043e \u043f\u0440\u0438\u0432\u044b\u0447\u043a\u0438. \u0415\u0441\u043b\u0438 \u0432\u0430\u0441 \u043d\u0435 \u0441\u043c\u0443\u0449\u0430\u0435\u0442, \u043a\u043e\u0433\u0434\u0430 \u0432\u0430\u0448\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0442\u043e\u0440\u0447\u0430\u0442 \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0433\u043e\u043b\u044b\u043c\u0438 \u0444\u0438\u043b\u0435\u0439\u043d\u044b\u043c\u0438 \u0447\u0430\u0441\u0442\u044f\u043c\u0438 &#8212; \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u0431\u043b\u043e\u043a<\/p>\n<p>\u041a\u043e\u043d\u0444\u0438\u0433 \u0434\u043b\u044f nginx \u043f\u0440\u043e\u0441\u0442 \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u043d\u0443 \u043b\u043e\u043a\u0430\u0446\u0438\u044e (\u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0444\u0440\u043e\u043d\u0442 &#8212; \u0431\u0443\u0434\u0443\u0442 \u0435\u0449\u0435 \u043b\u043e\u043a\u0430\u0446\u0438\u0438)<\/p>\n<pre><code># deploy\/config\/nginx\/registry.conf  upstream ice-registry-upstream {     server ice-registry:8080 fail_timeout=0; }  server {     listen                      80;     client_max_body_size        0;     charset                     utf-8;     gzip                        on;     gzip_types                  text\/plain application\/javascript image\/svg+xml;     error_log                   \/var\/log\/nginx\/service.error_log  debug;      location \/ {         proxy_pass                  http:\/\/ice-registry-upstream\/;         proxy_set_header            Host                $http_host;         proxy_set_header            X-Real-IP           $remote_addr;         proxy_set_header            X-Forwarded-For     $remote_addr;         proxy_set_header            X-Forwarded-Proto   http;         proxy_set_header            X-Forwarded-Ssl     off;         proxy_http_version          1.1;         proxy_request_buffering     off;         proxy_buffering off;         proxy_set_header Connection \"\";     } } <\/code><\/pre>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0444\u0438\u0433 \u0432 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u043c <code>docker-compose-local.yaml<\/code><\/p>\n<pre><code class=\"yaml\">services:   nginx:     image: nginx:1.27.3     container_name: nginx     hostname: nginx     command: \"\/bin\/sh -c 'while :; do sleep 6h &amp; wait $${!}; nginx -s reload; done &amp; nginx -g \\\"daemon off;\\\"'\"     volumes:       - .\/deploy\/config\/nginx:\/etc\/nginx\/conf.d     ports:       - 31002:80     networks:        - ice-registry-network     restart: on-failure:10   ice-registry:     build:       context: .       dockerfile: .\/Dockerfile     container_name: ice-registry     hostname: ice-registry     environment:       DEBUG: true     networks:        - ice-registry-network     restart: on-failure:10  networks:   ice-registry-network:     driver: bridge <\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0447\u0435\u0440\u0435\u0437 <code>docker compose -f docker-compose-local.yaml up -d --build --force-recreate<\/code>, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c &#8212; \u043d\u0430 31002 \u043f\u043e\u0440\u0442\u0443 \u043f\u043e\u0432\u0438\u0441\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0438\u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0428\u0430\u0433 \u0432 \u0434\u0440\u0443\u0433\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443. \u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0441\u0432\u0430\u0433\u0433\u0435\u0440\u0435<\/summary>\n<div class=\"spoiler__content\">\n<p>\u042f \u043b\u044e\u0431\u043b\u044e \u043f\u0440\u0438\u043d\u0446\u0438\u043f Single Source \u0432\u043e \u0432\u0441\u0435\u043c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u043c\u0435\u043d\u044f \u043a\u0440\u0430\u0439\u043d\u0435 \u0432\u0430\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0432 \u0441\u0432\u0430\u0433\u0433\u0435\u0440\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u043b\u0430\u0441\u044c \u0440\u0435\u0430\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0412 \u0441\u043b\u0443\u0447\u0430\u0435 poetry \u0432\u0435\u0440\u0441\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u0444\u0430\u0439\u043b\u0435 <code>pyproject.toml<\/code>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043e \u0432\u0441\u0435\u043c \u043c\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0432\u0441\u0435\u0433\u0434\u0430 \u0435\u0441\u0442\u044c \u0445\u0435\u043b\u043f\u0435\u0440, \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0449\u0438\u0439 \u0432\u044b\u0442\u0430\u0449\u0438\u0442\u044c \u0438\u0437 <code>pyproject.toml<\/code> \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0438 \u0440\u0430\u0437\u0434\u0430\u0442\u044c \u0435\u0435 \u0432\u0441\u0435\u043c \u0441\u0442\u0440\u0430\u0436\u0434\u0443\u0449\u0438\u043c.<\/p>\n<pre><code class=\"python\"># registry\/utils\/version.py  import toml  from pathlib import Path   def get_app_version() -&gt; str:     pyproject_toml_file = Path(__file__).parent.parent.parent \/ \"pyproject.toml\"      if pyproject_toml_file.exists() and pyproject_toml_file.is_file():         data = toml.load(pyproject_toml_file)          if \"tool\" in data and \"poetry\" in data[\"tool\"] and \"version\" in data[\"tool\"][\"poetry\"]:             return data[\"tool\"][\"poetry\"][\"version\"]              return \"unknown\" <\/code><\/pre>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0445\u0435\u043b\u043f\u0435\u0440 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/p>\n<pre><code class=\"python\"># registry\/__main__.py  ... from registry.utils import get_app_version ...   def run(): app = FastAPI(         title=\"Ice Docker Registry\",         version=get_app_version(),         docs_url=\"\/openapi\" if settings.debug else None,         redoc_url=None,         root_path=settings.root_path,         debug=settings.debug,     )     ... <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u0435\u0441\u043b\u0438 \u043f\u043e\u0434\u0432\u0438\u0433\u0430\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 <code>poetry version patch<\/code>, <code>poetry version minor<\/code> \u0438\u043b\u0438 <code>poetry version major<\/code>, \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u043b\u0435\u0437\u0442\u044c \u0432 <code>pyproject.toml<\/code> \u0438 \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e \u0440\u0443\u0447\u043a\u0430\u043c\u0438, \u0441\u0432\u0430\u0433\u0433\u0435\u0440 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e. \u041d\u0443 \u0438 \u043f\u043e\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0443\u0447\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e \u043d\u0430 \u0444\u0440\u043e\u043d\u0442<\/p>\n<\/div>\n<\/details>\n<h3>Docker Registry REST API<\/h3>\n<p>\u041d\u0443 \u0430 \u0442\u0435\u043f\u0435\u0440\u044c, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0433\u043b\u0430\u0432\u043d\u043e\u0435 &#8212; \u0431\u044d\u043a\u0435\u043d\u0434 \u0434\u043b\u044f \u0440\u0435\u0435\u0441\u0442\u0440\u0430.<\/p>\n<p>\u041f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432\u044b\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u0435:<\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u043b\u0438\u0447\u0438\u0435 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u044e\u0437\u0435\u0440 \u043f\u043e\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0434\u0438\u043d, \u0438 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0435\u0433\u043e \u043a\u0440\u0435\u0434\u044b \u0431\u0443\u0434\u0443\u0442 \u043d\u0430 \u0441\u0442\u0430\u0440\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f)<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0435\u0441\u0442\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0441\u043b\u043e\u0438 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435, \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u0441\u0442\u0430\u0440\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0430 \u0441\u0442\u0430\u0440\u0442\u0435 \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432. \u0424\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0447\u0435\u0440\u0435\u0437 API \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0432 \u043a\u0430\u043a\u043e\u0439-\u043d\u0438\u0431\u0443\u0434\u044c \u0438\u0437 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u0435\u0440\u0438\u0439<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432. \u0412 \u0444\u0430\u0439\u043b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u043e\u0432\u044b\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445<\/p>\n<pre><code class=\"python\"># config.py  ... docker_repos_path: str = \"\/data\/docker\/repos\" docker_default_repos: list[str] = [] docker_username: str = \"admin\" docker_password: str = \"admin\" ... <\/code><\/pre>\n<p>\u0433\u0434\u0435:<\/p>\n<ul>\n<li>\n<p><code>docker_repos_path<\/code> &#8212; \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u043a \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c<\/p>\n<\/li>\n<li>\n<p><code>docker_default_repos<\/code> &#8212; \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432<\/p>\n<\/li>\n<li>\n<p><code>docker_username<\/code> \u0438 <code>docker_username<\/code> &#8212; \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u041f\u0440\u043e\u0431\u0440\u043e\u0441\u0438\u043c \u0441\u0440\u0430\u0437\u0443 \u044d\u0442\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 (\u0430 \u0442\u0430\u043a\u0436\u0435 &#8212; volume) \u0432 <code>docker-compose-local.yaml<\/code>, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u0431\u044b\u0442\u044c<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"yaml\"># docker-compose-local.yaml  services:   ... ice-registry: volumes: - .\/.test:\/data\/docker\/repos environment: ... DOCKER_DEFAULT_REPOS: '[ \"test_repo_0\", \"test_repo_1\" ]' DOCKER_USERNAME: registry_user DOCKER_PASSWORD: registry_password_123  <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430 \u0441\u0442\u0430\u0440\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u043d\u0443\u0436\u043d\u044b\u0435 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438<\/p>\n<pre><code class=\"python\"># registry\/__main__.py  ... from pathlib import Path ...  def run():     Path(settings.docker_repos_path).mkdir(parents=True, exist_ok=True)     for docker_repo in settings.docker_default_repos:         repo_path = Path(settings.docker_repos_path) \/ docker_repo         Path(repo_path).mkdir(parents=True, exist_ok=True)      app = FastAPI(         title=\"Ice Docker Registry\",         version=get_app_version(),         docs_url=\"\/openapi\" if settings.debug else None,         redoc_url=None,         root_path=settings.root_path,         debug=settings.debug,     ) ... <\/code><\/pre>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0445\u0435\u043b\u043f\u0435\u0440 &#8212; <code>check_auth<\/code><\/p>\n<pre><code class=\"python\"># registry\/utils\/auth.py  import secrets  from fastapi import Depends, HTTPException from fastapi.security import HTTPBasic, HTTPBasicCredentials  from config import settings   security = HTTPBasic(     realm=\"Ice Registry\", )   def check_auth(     credentials: HTTPBasicCredentials = Depends(security) ):     exception = HTTPException(         status_code=401,         detail=\"Unauthorized\",         headers={             \"WWW-Authenticate\": 'Basic realm=\"Ice Registry\"'         },     )     if not credentials:         raise exception      user_is_valid = secrets.compare_digest(credentials.username, settings.docker_username)     password_is_valid   = secrets.compare_digest(credentials.password, settings.docker_password)     if not (user_is_valid and password_is_valid):         raise exception     return True  <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u043c\u0438 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 FastAPI \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u043c\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c <code>Authorization<\/code>-\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a, \u043f\u0440\u0438 \u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 &#8212; \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043a\u0440\u0435\u0434\u044b \u0441 \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f, \u0430 \u043f\u0440\u0438 \u043d\u0435\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 (\u043b\u0438\u0431\u043e \u0435\u0441\u043b\u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043a\u0440\u0435\u0434\u044b \u0441 \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043d\u0435 \u0441\u043e\u0448\u043b\u0438\u0441\u044c) &#8212; \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a <code>WWW-Authenticate<\/code> \u0438 401 \u043a\u043e\u0434. \u041f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u043e\u0442\u0432\u0435\u0442\u0430 docker \u043b\u0438\u0431\u043e \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u0435\u0449\u0435 \u0440\u0430\u0437 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441, \u043f\u043e\u043b\u044c\u0437\u0443\u044f\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0440\u0435\u043a\u0432\u0438\u0437\u0438\u0442\u0430\u043c\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u043b\u0438\u0431\u043e \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043e\u0448\u0438\u0431\u043a\u0443 <code>unauthorized: authentication required<\/code><\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u044b\u0439 API-\u043c\u043e\u0434\u0443\u043b\u044c <code>docker_v2<\/code>, \u0440\u043e\u0443\u0442\u0435\u0440 \u0434\u043b\u044f \u043d\u0435\u0433\u043e, \u0438 \u043f\u0435\u0440\u0432\u044b\u0439 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442<\/p>\n<pre><code class=\"python\"># registry\/api\/docker_v2\/router.py  from fastapi import APIRouter, Depends  from registry.utils import check_auth   router = APIRouter(     tags=[\"docker\"],     dependencies=[Depends(check_auth)] ) <\/code><\/pre>\n<pre><code class=\"python\"># registry\/api\/docker_v2\/ping.py  from .router import router   @router.get(     \"\/\",     summary=\"\u042d\u0445\u043e-\u043f\u0438\u043d\u0433 v2\" ) async def ping():     return {} <\/code><\/pre>\n<p>Docker \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u043a \u0440\u0435\u0435\u0441\u0442\u0440\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 GET-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0430\u0434\u0440\u0435\u0441 \/v2\/, \u043e\u0436\u0438\u0434\u0430\u044f \u043b\u0438\u0431\u043e 200 \u043e\u0442\u0432\u0435\u0442 (\u0440\u0435\u0435\u0441\u0442\u0440 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439, \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f), \u043b\u0438\u0431\u043e 401 \u0441 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u043c <code>WWW-Authenticate<\/code>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u0439 \u0442\u0438\u043f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (base, JWT \u0438 \u0442.\u0434.)<\/p>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438, \u043f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 <code>docker login ...<\/code> \u043e\u043d \u0442\u043e\u0436\u0435 \u0441\u0442\u0443\u0447\u0438\u0442\u0441\u044f \u043d\u0430 \u044d\u0442\u043e\u0442 \u0436\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442, \u043f\u044b\u0442\u0430\u044f\u0441\u044c \u043e\u0431\u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0440\u0435\u043a\u0432\u0438\u0437\u0438\u0442\u044b \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0430 \u043e\u0442\u0432\u0435\u0442 <code>200 Ok<\/code><\/p>\n<h3>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0437\u0430 \u0432 \u0440\u0435\u0435\u0441\u0442\u0440<\/h3>\n<p>\u0414\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0431\u0440\u0430\u0437\u0430 docker \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439:<\/p>\n<ul>\n<li>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 HEAD-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 <code>\/{repo}\/blobs\/{digest}<\/code>, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044f, \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d \u043b\u0438 \u0443\u0436\u0435 \u0434\u0430\u043d\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 \u0432 \u0440\u0435\u0435\u0441\u0442\u0440. \u0415\u0441\u043b\u0438 \u0440\u0435\u0435\u0441\u0442\u0440 \u043e\u0442\u0432\u0435\u0442\u0438\u0442 <code>200 Ok<\/code>, docker \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u044b\u0442\u0430\u0442\u044c\u0441\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0441\u043b\u043e\u0439. \u0414\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u0446\u0438\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 <code>\/{repo}\/blobs\/{digest}<\/code> \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u043c <code>404 Not Found<\/code><\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 POST-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 <code>\/v2\/{repo}\/blobs\/uploads\/<\/code>, \u043e\u0436\u0438\u0434\u0430\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 <code>202 Accepted<\/code> \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a <code>Location<\/code>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043f\u0443\u0442\u044c \u0441 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c ID \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 (<code>\/v2\/{repo}\/blobs\/uploads\/{upload_id}<\/code>)<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0432 \u043d\u0443\u0436\u043d\u044b\u0439 <code>Location<\/code>, docker \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0441\u043b\u043e\u044f, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044f PATCH-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 <code>\/v2\/{repo}\/blobs\/uploads\/{upload_id}<\/code>, \u0441 \u0447\u0430\u043d\u043a\u0430\u043c\u0438 \u0441\u043b\u043e\u044f \u0432 \u0442\u0435\u043b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0412 \u043e\u0442\u0432\u0435\u0442 \u043e\u043d \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u0441\u0435 \u0442\u043e\u0442 \u0436\u0435 \u043e\u0442\u0432\u0435\u0442 <code>202 Accepted<\/code><\/p>\n<\/li>\n<li>\n<p>\u041f\u043e \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0441\u043b\u043e\u044f docker \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0444\u0438\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u044e\u0449\u0438\u0439 PUT-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 <code>\/v2\/{repo}\/blobs\/uploads\/{upload_id}<\/code>, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u0432 query-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435 <code>digest<\/code> \u0441\u0432\u043e\u044e \u0432\u0435\u0440\u0441\u0438\u044e SHA256-\u0445\u0435\u0448\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f. \u0412 \u043e\u0442\u0432\u0435\u0442 \u0435\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 <code>201 Created<\/code> \u0438 \u043f\u0443\u0442\u044c \u043a \u0441\u043b\u043e\u044e \u0432\u0438\u0434\u0430 <code>\/v2\/{repo}\/blobs\/{digest}<\/code> \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u043b\u043e\u0435\u0432 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a docker \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043b \u0432\u0441\u0435 \u0441\u043b\u043e\u0438 \u0432 \u0440\u0435\u0435\u0441\u0442\u0440, \u0438 \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u043e\u043b\u0443\u0447\u0438\u043b <code>201 Created<\/code>, \u043e\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0432 \u0440\u0435\u0435\u0441\u0442\u0440 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u043e\u0431\u0440\u0430\u0437\u0430. \u041f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f:<\/p>\n<ul>\n<li>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f PUT-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0430\u0434\u0440\u0435\u0441 <code>\/{repo}\/manifests\/{tag}<\/code> (\u0433\u0434\u0435 tag &#8212; \u0442\u0435\u0433 \u043e\u0431\u0440\u0430\u0437\u0430), \u0441 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u043e\u043c \u0432 \u0442\u0435\u043b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0412 \u043e\u0442\u0432\u0435\u0442 \u043e\u043d \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 <code>201 Created<\/code> \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a <code>Docker-Content-Digest<\/code>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 SHA256-\u0445\u0435\u0448 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e docker \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0435\u0449\u0435 \u043e\u0434\u0438\u043d PUT-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0442\u043e\u0442 \u0436\u0435 \u0430\u0434\u0440\u0435\u0441, \u043d\u043e \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u0435\u0433\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 SHA256-\u0445\u0435\u0448 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430. Docker \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442, \u0447\u0442\u043e \u0440\u0435\u0435\u0441\u0442\u0440 \u043f\u0440\u0438\u043c\u0435\u0442 \u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u043e\u0442\u0432\u0435\u0442\u0438\u0442 \u043d\u0430 \u043e\u0431\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430<\/p>\n<\/li>\n<\/ul>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043a\u043e\u0434 \u0432\u0441\u0435\u0445 \u044d\u0442\u0438\u0445 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432:<\/p>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0434\u043b\u044f HEAD-\u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0422\u0443\u0442 \u0432\u0441\u0435 \u043f\u0440\u043e\u0441\u0442\u043e &#8212; \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0435\u0441\u0442\u044c \u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u0438 \u0435\u0441\u0442\u044c \u043b\u0438 \u0432 \u043d\u0435\u043c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0441\u043b\u043e\u0439. \u0415\u0441\u043b\u0438 \u043d\u0435\u0442 \u0442\u043e\u0433\u043e \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u043e\u0433\u043e &#8212; \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c <code>404 Not Found<\/code><\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from pathlib import Path  from fastapi import (     Path as FastAPIPath,     HTTPException,     Response, )  from config import settings  from .router import router   @router.head(     \"\/{repo}\/blobs\/{digest}\",     summary=\"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u043e\u044f \u043f\u043e \u043e\u0442\u043f\u0435\u0447\u0430\u0442\u043a\u0443\", ) async def check_blob(     digest: str,     repo: str = FastAPIPath(...), ):     repo_dir = Path(settings.docker_repos_path) \/ repo     if not repo_dir.exists():         raise HTTPException(404, detail=f\"Repository [{repo}] does not exist\")      repo_blobs_dir = repo_dir \/ \"blobs\"     blob_hex = digest.replace(\"sha256:\", \"\")     blob_path = repo_blobs_dir \/ blob_hex     if not blob_path.exists():         return Response(status_code=404)      return Response(status_code=200) <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a POST-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 python-ulid \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043a\u0430 \u043d\u0438\u0433\u0434\u0435, \u043a\u0440\u043e\u043c\u0435 tmp-\u0444\u0430\u0439\u043b\u0430 \u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 (\u043d\u043e \u0432 \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u0441\u0435\u0440\u0438\u044f\u0445 \u0431\u0443\u0434\u0435\u0442 \u0443\u0445\u043e\u0434\u0438\u0442\u044c \u0432 \u0411\u0414), \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u043e\u0439 tmp-\u0444\u0430\u0439\u043b \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u0443 <code>202 Accepted<\/code> \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441 \u043f\u0443\u0442\u0435\u043c, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0441\u043b\u043e\u0439<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from pathlib import Path  from fastapi import Path as FastAPIPath, Response, HTTPException from ulid import ULID  from config import settings  from .router import router   @router.post(     \"\/{repo}\/blobs\/uploads\/\",     summary=\"\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0441\u043b\u043e\u044f\" ) async def create_upload(     repo: str = FastAPIPath(...), ):     upload_id = ULID()     repo_dir = Path(settings.docker_repos_path) \/ repo     if not repo_dir.exists():         raise HTTPException(404, detail=f\"Repository [{repo}] does not exist\")      repo_blobs_dir = repo_dir \/ \"blobs\"     Path(repo_blobs_dir).mkdir(parents=True, exist_ok=True)      tmp_file_path = Path(repo_blobs_dir) \/ f\"{str(upload_id)}.tmp\"     tmp_file_path.touch()      return Response(         status_code=202,         headers={             \"Location\": f\"\/v2\/{repo}\/blobs\/uploads\/{str(upload_id)}\"         }     ) <\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a PATCH-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0441\u043b\u043e\u044f. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0438 tmp-\u0444\u0430\u0439\u043b\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0447\u0430\u043d\u043a \u0434\u0430\u043d\u043d\u044b\u0445, \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u0432 tmp-\u0444\u0430\u0439\u043b. \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c <code>202 Accepted<\/code> \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c \u043f\u0440\u0438\u043d\u044f\u0442\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/div>\n<\/details>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a PATCH-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0441\u043b\u043e\u044f. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0438 tmp-\u0444\u0430\u0439\u043b\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0447\u0430\u043d\u043a \u0434\u0430\u043d\u043d\u044b\u0445, \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u0432 tmp-\u0444\u0430\u0439\u043b. \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c <code>202 Accepted<\/code> \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c \u043f\u0440\u0438\u043d\u044f\u0442\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import aiofiles import os  from pathlib import Path  from fastapi import (     Path as FastAPIPath,     Response,     HTTPException,     Body, )  from config import settings  from .router import router  @router.patch(     \"\/{repo}\/blobs\/uploads\/{upload_id}\",     summary=\"\u0414\u043e\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0441\u043b\u043e\u044f\", ) async def update_upload(     upload_id: str,     repo: str = FastAPIPath(...),     chunk: bytes = Body(...), ):     repo_dir = Path(settings.docker_repos_path) \/ repo     if not repo_dir.exists():         raise HTTPException(404, detail=f\"Repository [{repo}] does not exist\")      repo_blobs_dir = repo_dir \/ \"blobs\"     tmp_file_path = Path(repo_blobs_dir) \/ f\"{str(upload_id)}.tmp\"     if not tmp_file_path.exists():         raise HTTPException(404, detail=f\"Upload [{upload_id}] does not exist\")      async with aiofiles.open(str(tmp_file_path), mode=\"ab\") as tmp_file:         await tmp_file.write(chunk)      offset = os.path.getsize(tmp_file_path)     return Response(         status_code=202,         headers = {             \"Location\": f\"\/v2\/{repo}\/blobs\/uploads\/{upload_id}\",             \"Range\": f\"0-{offset-1}\",         },     ) <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a PUT-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u0444\u0438\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0441\u043b\u043e\u044f. \u0421\u0447\u0438\u0442\u0430\u0435\u043c SHA256-\u0445\u0435\u0448 tmp-\u0444\u0430\u0439\u043b\u0430, \u0441\u0432\u0435\u0440\u044f\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0441 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u043c \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430. \u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u043e\u043a, \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u043e\u0432\u044b\u0432\u0430\u0435\u043c tmp-\u0444\u0430\u0439\u043b \u0432 \u0444\u0430\u0439\u043b \u0441\u043b\u043e\u044f, \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c <code>201 Created<\/code> \u0438 \u043f\u0443\u0442\u044c, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u044d\u0442\u043e\u0442 \u0441\u043b\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0447\u0430\u0442\u044c<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import hashlib import aiofiles import os  from pathlib import Path  from fastapi import (     Path as FastAPIPath,     Response,     HTTPException,     Query, )  from config import settings  from .router import router   @router.put(     \"\/{repo}\/blobs\/uploads\/{upload_id}\",     summary=\"\u0424\u0438\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043b\u043e\u044f\", ) async def finalize_upload(     upload_id: str,     repo: str = FastAPIPath(...),     digest: str = Query(..., description=\"sha256:&lt;hex&gt;\"), ):     repo_dir = Path(settings.docker_repos_path) \/ repo     if not repo_dir.exists():         raise HTTPException(404, detail=f\"Repository [{repo}] does not exist\")      repo_blobs_dir = repo_dir \/ \"blobs\"     tmp_file_path = Path(repo_blobs_dir) \/ f\"{str(upload_id)}.tmp\"     if not tmp_file_path.exists():         raise HTTPException(404, detail=f\"Upload [{upload_id}] does not exist\")      h = hashlib.sha256()     async with aiofiles.open(str(tmp_file_path), mode=\"rb\") as tmp_file:         while True:             block = await tmp_file.read(4096)             if not block:                 break             h.update(block)     real_digest = f\"sha256:{h.hexdigest()}\"      if real_digest != digest:         os.remove(str(tmp_file_path))         raise HTTPException(400, f\"Digest mismatch, real={real_digest}\")          blob_path = repo_blobs_dir \/ h.hexdigest()     os.replace(str(tmp_file_path), str(blob_path))      return Response(         status_code=201,         headers={             \"Location\": f\"\/v2\/{repo}\/blobs\/{real_digest}\"         },     ) <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a PUT-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c <code>Content-Type<\/code> \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 (\u043d\u0430\u0448 \u0440\u0435\u0435\u0441\u0442\u0440 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u044b v2, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u0436\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u043c, \u0430 \u043d\u0435 \u043f\u0440\u043e\u0448\u043b\u044b\u043c), \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c SHA256-\u0445\u0435\u0448 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043d\u0430 \u0434\u0438\u0441\u043a \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u043e\u0442\u0432\u0435\u0442 <code>201 Created<\/code> \u0438 SHA256-\u0445\u0435\u0448 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import aiofiles import hashlib import orjson  from pathlib import Path  from fastapi import (     Path as FastAPIPath,     HTTPException,     Body,     Response,     Header, )  from config import settings  from .router import router   @router.put(     \"\/{repo}\/manifests\/{tag}\",     summary=\"\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u0434\u043b\u044f \u0442\u0435\u0433\u0430\", ) async def create_manifest(     tag: str,     repo: str = FastAPIPath(...),     content_type: str = Header(..., alias=\"Content-Type\"),     manifest: dict = Body(...) ):     valid_types = {         \"application\/vnd.docker.distribution.manifest.v2+json\",         \"application\/vnd.docker.distribution.manifest.list.v2+json\",     }     if content_type not in valid_types:         raise HTTPException(400, detail=f\"Unsupported media type: {content_type}\")      repo_dir = Path(settings.docker_repos_path) \/ repo     if not repo_dir.exists():         raise HTTPException(404, detail=f\"Repository [{repo}] does not exist\")          repo_manifests_dir = repo_dir \/ \"manifests\"     Path(repo_manifests_dir).mkdir(parents=True, exist_ok=True)      manifest_bytes = orjson.dumps(manifest)     manifest_digest = f\"sha256:{hashlib.sha256(manifest_bytes).hexdigest()}\"      manifest_file_path = Path(repo_manifests_dir) \/ f\"{tag}.json\"     manifest_sha256_file_path = Path(repo_manifests_dir) \/ f\"{manifest_digest}.json\"     async with aiofiles.open(str(manifest_file_path), mode='wb') as manifest_file:         await manifest_file.write(manifest_bytes)     async with aiofiles.open(str(manifest_sha256_file_path), mode='wb') as manifest_file:         await manifest_file.write(manifest_bytes)      return Response(         status_code=201,         headers = {             \"Docker-Content-Digest\": manifest_digest,             \"Location\": f\"\/v2\/{repo}\/manifests\/{tag}\"         }     ) <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 PUT-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u043f\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0438 \u0432\u043c\u0435\u0441\u0442\u043e \u0447\u0435\u043b\u043e\u0432\u0435\u043a\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0433\u043e \u0442\u0435\u0433\u0430 \u0443\u043f\u0430\u0434\u0435\u0442 \u0432 \u044d\u0442\u043e\u0442 \u0436\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u043d\u0438\u0447\u0435\u0433\u043e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435 \u043d\u0430\u0434\u043e<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0435. \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u044b\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 <code>docker push ...<\/code> \u0444\u043b\u043e\u0443 \u043c\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u043b\u0438 \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435.<\/p>\n<h3>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0437\u0430 \u0438\u0437 \u0440\u0435\u0435\u0441\u0442\u0440\u0430<\/h3>\n<p>\u041d\u0430\u0441\u0442\u0430\u043b\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u044b\u0445 docker \u043f\u0440\u0438 <code>docker pull ...<\/code>. \u0422\u0443\u0442 \u0432\u0441\u0435 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e:<\/p>\n<ul>\n<li>\n<p>Docker \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u043e\u0431\u0440\u0430\u0437\u0430 \u0447\u0435\u0440\u0435\u0437 GET-\u0437\u0430\u043f\u0440\u043e\u0441 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430 <code>\/{repo}\/manifests\/{tag}<\/code>. \u041c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043b\u0438\u0431\u043e <code>404 Not Found<\/code>, \u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u0443 \u043d\u0430\u0441 \u043d\u0435\u0442, \u043b\u0438\u0431\u043e <code>200 Ok<\/code> \u0438 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u043e\u043c \u0432 \u0442\u0435\u043b\u0435 \u043e\u0442\u0432\u0435\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u0414\u0430\u043b\u0435\u0435 docker \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0438\u0437 \u0440\u0435\u0435\u0441\u0442\u0440\u0430 \u0441\u043b\u043e\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043d\u0435\u0442 \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u043c \u043a\u0435\u0448\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 GET-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 <code>\/{repo}\/blobs\/{digest}<\/code>. \u041d\u0430 \u044d\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043b\u0438\u0431\u043e <code>404 Not Found<\/code> (\u0438 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0441\u043b\u043e\u0435\u0432 \u0438 \u0441\u0431\u043e\u0440\u043a\u0430 \u043e\u0431\u0440\u0430\u0437\u0430 \u0441\u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f), \u043b\u0438\u0431\u043e \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u043d\u0430 \u0432\u044b\u0433\u0440\u0443\u0437\u043a\u0443 \u0441 \u0442\u0438\u043f\u043e\u043c <code>application\/octet-stream<\/code><\/p>\n<\/li>\n<\/ul>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043a\u043e\u0434 \u0432\u0441\u0435\u0445 \u044d\u0442\u0438\u0445 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432:<\/p>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a GET-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432\u044b\u0433\u0440\u0443\u0437\u043a\u0438 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430. \u041f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u0431\u0438\u0440\u0430\u0435\u043c \u0441 \u0434\u0438\u0441\u043a\u0430 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430, \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u043c \u0442\u0435\u0433\u043e\u043c, \u0438 \u043e\u0442\u0434\u0430\u0435\u043c, \u043f\u043e \u043f\u0443\u0442\u0438 \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0432 \u0435\u0433\u043e SHA256-\u0445\u0435\u0448<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\"># registry\/api\/docker_v2\/get_manifest.py  import hashlib  from pathlib import Path  from fastapi import (     Path as FastAPIPath,     HTTPException,     Response, )  from config import settings  from .router import router   @router.get(     \"\/{repo}\/manifests\/{tag}\",     summary=\"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u0434\u043b\u044f \u0442\u0435\u0433\u0430\", ) async def get_manifest(     tag: str,     repo: str = FastAPIPath(...), ):     repo_dir = Path(settings.docker_repos_path) \/ repo     if not repo_dir.exists():         raise HTTPException(404, detail=f\"Repository [{repo}] does not exist\")          repo_manifests_dir = repo_dir \/ \"manifests\"     manifest_file_path = Path(repo_manifests_dir) \/ f\"{tag}.json\"     if not manifest_file_path.exists():         raise HTTPException(404, detail=f\"Manifest for tag [{tag}] does not exist\")      manifest_bytes = open(str(manifest_file_path), \"r\").read().encode()     digest = f\"sha256:{hashlib.sha256(manifest_bytes).hexdigest()}\"      return Response(         content=manifest_bytes,         media_type=\"application\/vnd.docker.distribution.manifest.v2+json\",         headers={             \"Docker-Content-Digest\": digest,         }     ) <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a GET-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0441\u043b\u043e\u044f. \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u043d\u0430 \u0434\u0438\u0441\u043a\u0435 \u043d\u0443\u0436\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 \u0438 \u043e\u0442\u0434\u0430\u0435\u043c \u0435\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>StreamingResponse<\/code><\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\"># registry\/api\/docker_v2\/get_blob.py  from pathlib import Path  from fastapi import (     Path as FastAPIPath,     HTTPException, ) from fastapi.responses import StreamingResponse  from config import settings  from .router import router   @router.get(     \"\/{repo}\/blobs\/{digest}\",     summary=\"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u043e\u044f \u043f\u043e \u043e\u0442\u043f\u0435\u0447\u0430\u0442\u043a\u0443\", ) async def get_blob(     digest: str,     repo: str = FastAPIPath(...), ):     repo_dir = Path(settings.docker_repos_path) \/ repo     if not repo_dir.exists():         raise HTTPException(404, detail=f\"Repository [{repo}] does not exist\")      repo_blobs_dir = repo_dir \/ \"blobs\"     blob_hex = digest.replace(\"sha256:\", \"\")     blob_path = repo_blobs_dir \/ blob_hex     if not blob_path.exists():         raise HTTPException(404, detail=f\"BLOB [{digest}] does not exist\")      return StreamingResponse(         open(str(blob_path), \"rb\"),         media_type=\"application\/octet-stream\"     ) <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0435. \u0423 \u043d\u0430\u0441 \u0433\u043e\u0442\u043e\u0432 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 docker registry, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043c\u0435\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c, \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u044b \u0438 \u0441\u043b\u043e\u0438, \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u0430<\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432 docker-compose \u0438\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 <code>docker run<\/code>, \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043a\u0438\u0434\u0430\u0442\u044c \u0442\u0443\u0434\u0430 \u043e\u0431\u0440\u0430\u0437\u044b. \u0415\u0441\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0430 <a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>, \u0430\u0434\u0440\u0435\u0441 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0444\u0438\u0433 \u0434\u0435\u043c\u043e\u043d\u0430 docker (\u0432 \u0431\u043b\u043e\u043a <code>insecure-registries<\/code>), \u0438\u043d\u0430\u0447\u0435 docker \u0431\u0443\u0434\u0435\u0442 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c https. \u041b\u0438\u0431\u043e \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u043d\u0430 VPS, \u043a\u0443\u043f\u0438\u0442\u044c \u0438\u043b\u0438 \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0443 Lets Encrypt SSL-\u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0444\u0438\u0433 nginx \u0431\u043b\u043e\u043a SSL, \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0440\u0435\u0435\u0441\u0442\u0440\u043e\u043c \u0447\u0435\u0440\u0435\u0437 <code>docker push ...<\/code> \u0438 <code>docker pull ...<\/code><\/p>\n<p>\u0412\u0435\u0441\u044c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u043d\u0430 GitVerse (GitHub \u0438 Gitlab &#8212; \u0440\u0435\u0431\u044f\u0442\u0430 \u043d\u0435\u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0435, \u0430 \u043b\u0438\u0447\u043d\u044b\u0439 \u0433\u0438\u0442\u043b\u0430\u0431 \u0432\u044b\u0441\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043d\u0435 \u0445\u043e\u0447\u0435\u0442\u0441\u044f). \u041a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u0430\u044f \u043a\u0440\u0438\u0442\u0438\u043a\u0430 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442\u0441\u044f (\u043d\u0435\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u0430\u044f \u043d\u0435 \u0432\u043e\u0437\u0431\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f, \u043d\u043e \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \ud83d\ude42 ), \u043f\u0443\u043b\u043b-\u0440\u0435\u043a\u0432\u0435\u0441\u0442\u044b &#8212; \u0435\u0449\u0435 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442\u0441\u044f<\/p>\n<p>\u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c-\u043a\u0430\u043d\u0430\u043b \u0441\u0432\u043e\u0439 \u043d\u0435 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u044e. \u041d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0435 <s>\u043f\u0430\u0446\u0430\u043d\u044b<\/s> \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u0430 \u0437\u0430\u043b\u0438\u043f\u0430\u044e\u0442 \u0432 \u043b\u0438\u0441\u0442\u0438\u043d\u0433\u0430\u0445 \u043a\u043e\u0434\u0430 \u0438 \u0447\u0430\u0442\u044f\u0442\u0441\u044f \u0432 issue \ud83d\ude42<\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 gitverse &#8212; <a href=\"https:\/\/gitverse.ru\/icecloud\/ice-registry\" rel=\"noopener noreferrer nofollow\">https:\/\/gitverse.ru\/icecloud\/ice-registry<\/a><\/p>\n<p>\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u0435\u0440\u0438\u044f\u0445:<\/p>\n<ul>\n<li>\n<p>\u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c\u0438 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438<\/p>\n<\/li>\n<li>\n<p>\u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0430\u0432 \u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c<\/p>\n<\/li>\n<li>\n<p>\u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0441\u043b\u043e\u0435\u0432 (\u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0442\u0435 \u0441\u043b\u043e\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0433\u0434\u0435-\u0442\u043e \u0432 \u0440\u0435\u0435\u0441\u0442\u0440\u0435 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c)<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0432 Kafka<\/p>\n<\/li>\n<li>\n<p>\u0438 \u0442.\u0434.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><\/p>\n<div class=\"tm-article-poll-container\"><!--[--><\/p>\n<div class=\"tm-article-poll tm-article-poll_variant-bordered\">\n<div class=\"tm-notice tm-notice_positive tm-article-poll__notice\"><!----><\/p>\n<div class=\"tm-notice__inner\"><!----><\/p>\n<div class=\"tm-notice__content\" data-test-id=\"notice-content\"><!--[--><span>\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u043f\u0440\u043e\u0441\u0435. <a rel=\"nofollow\" href=\"\/kek\/v1\/auth\/habrahabr\/?back=\/ru\/articles\/913186\/&#038;hl=ru\">\u0412\u043e\u0439\u0434\u0438\u0442\u0435<\/a>, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430.<\/span><!--]--><\/div>\n<\/div>\n<\/div>\n<p><!--[--><\/p>\n<div class=\"tm-article-poll__header\">\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u0435\u043d \u043b\u0438 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u2014 \u0440\u0430\u0431\u043e\u0442\u0430 \u043d\u0430\u0434 opensource \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c \u0432 \u043f\u0440\u044f\u043c\u043e\u043c \u044d\u0444\u0438\u0440\u0435?<\/div>\n<div class=\"tm-article-poll__answers\"><!--[--><\/p>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent tm-article-poll__answer-percent_winning\">60% <\/span><span class=\"tm-article-poll__answer-label\">\u0414\u0430, \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e. \u041c\u043e\u0436\u043d\u043e \u0441\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0445\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/span><span class=\"tm-article-poll__answer-votes\">3<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress tm-article-poll__answer-progress_winning\" style=\"width: 60%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">40% <\/span><span class=\"tm-article-poll__answer-label\">\u041d\u0435\u0442, \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043a\u0430\u0436\u0438 \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442<\/span><span class=\"tm-article-poll__answer-votes\">2<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 40%\"><\/div>\n<\/div>\n<\/div>\n<p><!--]--><\/div>\n<div class=\"tm-article-poll__stats\"> \u041f\u0440\u043e\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043b\u0438 5 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.   \u0412\u043e\u0437\u0434\u0435\u0440\u0436\u0430\u043b\u0441\u044f 1 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c. <\/div>\n<p><!--]--><\/div>\n<p><!--]--><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/913186\/\"> https:\/\/habr.com\/ru\/articles\/913186\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>\u0418\u043d\u0442\u0440\u043e<\/h3>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u0412 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u043c\u0438\u0440\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 docker \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u043a\u0440\u0430\u0435\u0443\u0433\u043e\u043b\u044c\u043d\u044b\u0445 \u043a\u0430\u043c\u043d\u0435\u0439 \u044d\u0440\u0433\u043e\u043d\u043e\u043c\u0438\u043a\u0438 \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430, \u043d\u0430\u0440\u044f\u0434\u0443 \u0441 git, \u0440\u0430\u0437\u043d\u043e\u0433\u043e \u0440\u043e\u0434\u0430 IDE \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430\u043c\u0438, \u0430 \u0434\u043b\u044f \u043a\u043e\u0433\u043e-\u0442\u043e &#8212; \u0438 GPT. \u0418, \u0445\u043e\u0442\u044c \u0432 \u0441\u0430\u043c\u043e\u043c \u043f\u043e \u0441\u0435\u0431\u0435 docker \u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u0442\u0430\u043a\u043e\u0433\u043e \u0443\u0436 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e (LXC, CRI-O, \u0447\u0438\u0441\u0442\u044b\u0439 containerd, \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043b\u0435\u0433\u043a\u0438\u0435 \u0438 \u0441\u0440\u0435\u0434\u043d\u0438\u0435 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u043a\u0438, \u0431\u0435\u0441\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b, \u0434\u043b\u044f \u043e\u0441\u043e\u0431\u044b\u0445 \u0446\u0435\u043d\u0438\u0442\u0435\u043b\u0435\u0439 &#8212; chroot. \u0422\u044b\u0441\u044f\u0447\u0438 \u0438\u0445), \u043e\u043d \u043f\u043e\u0434\u043a\u0443\u043f\u0430\u0435\u0442 \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0440\u0430\u0437\u0432\u0435\u0441\u0438\u0441\u0442\u043e\u0439 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 &#8212; \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 Docker \u0435\u0441\u0442\u044c \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u043e\u0432 \u043a\u043e\u0434\u0430 \u0438 IDE, \u043f\u0440\u043e \u043d\u0435\u0433\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u043c\u043d\u043e\u0433\u043e\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0435 \u043a\u043d\u0438\u0433\u0438, \u0441\u0442\u0430\u0442\u044c\u0438 \u0438 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u044b \u043e\u0442 \u0438\u043d\u0434\u0443\u0441\u043e\u0432, \u0430 \u043f\u043e \u0435\u0433\u043e \u0440\u0435\u0435\u0441\u0442\u0440\u0430\u043c (\u043e\u0442 Docker Hub \u0434\u043e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u043f \u043d\u0430 \u0433\u0438\u0442\u043b\u0430\u0431\u0435) \u0443\u0434\u043e\u0431\u043d\u043e \u0440\u0430\u0437\u043b\u043e\u0436\u0435\u043d \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0435\u0441\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043d\u0430 \u043f\u043b\u0430\u043d\u0435\u0442\u0435 \u0441\u043e\u0444\u0442.<\/p>\n<p>\u0412\u043e\u0442 \u043e \u0440\u0435\u0435\u0441\u0442\u0440\u0430\u0445 (registry) Docker \u0438 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c.<\/p>\n<p>\u0421\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435 \u0440\u0435\u0435\u0441\u0442\u0440 &#8212; \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e REST-\u0441\u0435\u0440\u0432\u0438\u0441 \u0438 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435. \u041e\u0431\u0440\u0430\u0437\u044b \u043f\u0440\u0438\u043b\u0435\u0442\u0430\u044e\u0442 \u0432 \u0440\u0435\u0435\u0441\u0442\u0440 \u0432 \u0432\u0438\u0434\u0435 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0445 \u0441\u043b\u043e\u0435\u0432 (\u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438 \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 Dockerfile, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0441\u043e\u0431\u0438\u0440\u0430\u043b\u0441\u044f \u043e\u0431\u0440\u0430\u0437) \u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e JSON-\u0444\u0430\u0439\u043b\u0430 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u0434\u043b\u044f nginx:1.27.3 (\u0434\u043b\u0438\u043d\u043d\u044b\u0435 SHA256-\u0445\u0435\u0448\u0438 \u0441\u043b\u043e\u0435\u0432 \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u044b \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0432\u043e\u0441\u043f\u0440\u0438\u044f\u0442\u0438\u044f)<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\">{ \"schemaVersion\": 2, \"mediaType\": \"application\/vnd.docker.distribution.manifest.v2+json\", \"config\": { \"mediaType\": \"application\/vnd.docker.container.image.v1+json\", \"digest\": \"sha256:c59e92...\" }, \"layers\": [ { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:d2eb42...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:ee083d...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:5afd65...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:8c2914...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:1e8aef...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:a982d0...\" }, { \"mediaType\": \"application\/vnd.docker.image.rootfs.diff.tar.gzip\", \"digest\": \"sha256:ab571a...\" } ] }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041f\u044b\u0442\u043b\u0438\u0432\u044b\u0439 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044c (\u0432\u043f\u0440\u043e\u0447\u0435\u043c, \u043a\u0430\u043a \u0438 \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u0442\u043e\u0436\u0435) \u0441\u0440\u0430\u0437\u0443 \u0437\u0430\u043c\u0435\u0442\u0438\u0442, \u0447\u0442\u043e \u0432 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0435 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e. \u042d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0434\u043e\u043a\u0435\u0440\u0430, \u0441\u043b\u043e\u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 digest \u0437\u0430\u0442\u044f\u043d\u0443\u0442\u044c \u0438\u0437 \u0440\u0435\u0435\u0441\u0442\u0440\u0430 \u0438 \u0432 \u043a\u0430\u043a\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0441\u0448\u0438\u0442\u044c \u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0435. \u0427\u0443\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0435\u043d \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 (\u0431\u043b\u043e\u043a config). \u0418\u0437 \u043d\u0435\u0433\u043e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0442\u0440\u044f\u0445\u043d\u0443\u0442\u044c \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0431\u0443\u0434\u0443\u0449\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 (\u0434\u043b\u044f \u0447\u0435\u0433\u043e \u0431\u044b \u0432\u0430\u043c \u044d\u0442\u043e \u043d\u0438 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u043b\u043e\u0441\u044c)<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f nginx:1.27.3<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\">{ \"architecture\": \"amd64\", \"config\": { \"ExposedPorts\": { \"80\/tcp\": {} }, \"Env\": [ \"PATH=\/usr\/local\/sbin:\/usr\/local\/bin:\/usr\/sbin:\/usr\/bin:\/sbin:\/bin\", \"NGINX_VERSION=1.27.3\", \"NJS_VERSION=0.8.7\", \"NJS_RELEASE=1~bookworm\", \"PKG_RELEASE=1~bookworm\", \"DYNPKG_RELEASE=1~bookworm\" ], \"Entrypoint\": [ \"\/docker-entrypoint.sh\" ], \"Cmd\": [ \"nginx\", \"-g\", \"daemon off;\" ], \"Labels\": { \"maintainer\": \"NGINX Docker Maintainers \\u003cdocker-maint@nginx.com\\u003e\" }, \"StopSignal\": \"SIGQUIT\" }, \"created\": \"2024-11-26T18:42:08Z\", \"os\": \"linux\", \"rootfs\": { \"type\": \"layers\", \"diff_ids\": [ \"sha256:7914c8...\", \"sha256:2cdcae...\", \"sha256:0b2daf...\", \"sha256:a280e1...\", \"sha256:166490...\", \"sha256:1b78ff...\", \"sha256:e2eb04...\" ] } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0421\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0438\u0437 \u044d\u0442\u0438\u0445 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u043e\u0432, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u0437 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0438\u0437 \u0440\u0435\u0435\u0441\u0442\u0440\u0430 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0445 \u0441\u043b\u043e\u0435\u0432 \u0438 \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440. \u0412\u0441\u0435 \u0433\u0435\u043d\u0438\u0430\u043b\u044c\u043d\u043e\u0435 &#8212; \u043f\u0440\u043e\u0441\u0442\u043e.<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043b\u043e\u0438<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0421\u043b\u043e\u0438, \u043a\u0441\u0442\u0430\u0442\u0438, \u044d\u0442\u043e \u043f\u043e \u0441\u0432\u043e\u0435\u0439 \u0441\u0443\u0442\u0438 tar-gzip-\u0430\u0440\u0445\u0438\u0432\u044b, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435 \u0440\u0430\u0437\u043d\u0438\u0446\u0443 \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0434\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 (COPY, RUN \u0438 \u0442.\u0434.) \u0438 \u043f\u043e\u0441\u043b\u0435<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 Dockerfile \u0432\u0441\u0435 \u0442\u043e\u0433\u043e \u0436\u0435 nginx:1.27.3<\/p>\n<pre><code>COPY 10-listen-on-ipv6-by-default.sh \/docker-entrypoint.d<\/code><\/pre>\n<p>\u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0441\u043b\u043e\u0439, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u0438\u043d \u0444\u0430\u0439\u043b<\/p>\n<pre><code class=\"bash\"># tar -xvzf 8c2914db26a3b019cf8b9dcd9ffec286f7aa4db2165ee79bb214b6e66dd461c2 drwxr-xr-x 2 root root 4096 Feb  4 04:25 . drwxr-xr-x 3 root root 4096 May 26 14:48 .. -rwxr-xr-x 1 root root 2125 Feb  4 04:25 10-listen-on-ipv6-by-default.sh<\/code><\/pre>\n<p>\u041d\u0443 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e \u043d\u0435\u0442, \u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043e \u0447\u0435\u043c \u043d\u0430 \u0441\u043e\u0431\u0435\u0441\u0430\u0445 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c.<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043c\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 Docker Registry &#8212; \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430\u044f. \u041d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e, \u0447\u0442\u043e \u044f \u0440\u0435\u0448\u0438\u043b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 <a href=\"https:\/\/gitverse.ru\/icecloud\/ice-registry\" rel=\"noopener noreferrer nofollow\">opensource-\u043f\u0440\u043e\u0435\u043a\u0442<\/a>, \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0435\u0435\u0441\u0442\u0440\u0430. \u041d\u0443 \u0438, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043f\u0440\u043e\u0441\u0442\u043e \u0442\u0430\u043a \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u043d\u0430\u0434\u043e\u0435\u0441\u0442\u044c, \u044f \u0440\u0435\u0448\u0438\u043b \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u0434 \u043d\u0438\u043c \u043f\u0440\u043e\u0442\u0430\u0449\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437 1-2-3 \u0441\u0442\u0430\u0442\u044c\u0438 \u043d\u0430 \u0425\u0430\u0431\u0440\u0435, \u0432\u0434\u0440\u0443\u0433 \u043a\u043e\u043c\u0443-\u0442\u043e \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e (\u0438\u043b\u0438 \u0445\u043e\u0442\u044f \u0431\u044b \u043f\u043e\u043b\u0435\u0437\u043d\u043e).<\/p>\n<p>\u041d\u0430 \u0432\u0441\u044f\u043a\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439, \u0434\u0438\u0441\u043a\u043b\u0435\u0439\u043c\u0435\u0440: \u044d\u0442\u043e \u041d\u0415 \u0421\u0410\u041c\u042b\u0419 \u041e\u041f\u0422\u0418\u041c\u0410\u041b\u042c\u041d\u042b\u0419 \u0441\u043f\u043e\u0441\u043e\u0431 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u0435\u0441\u0442\u0440\u0430, \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0443\u0436\u0435\u043d \u0440\u0435\u0435\u0441\u0442\u0440 \u0441\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435. \u041b\u0443\u0447\u0448\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0441\u0435\u0431\u0435 \u0433\u0438\u0442\u043b\u0430\u0431, Nexus \u0438\u043b\u0438, \u043d\u0430 \u0445\u0443\u0434\u043e\u0439 \u043a\u043e\u043d\u0435\u0446, registry \u0441 \u0434\u043e\u043a\u0435\u0440\u0445\u0430\u0431\u0430. \u0422\u0430\u043c \u0443\u0436\u0435 \u0432\u0441\u0435 \u044d\u0442\u043e \u0435\u0441\u0442\u044c, \u0438 \u0434\u0430\u0436\u0435 \u0431\u043e\u043b\u044c\u0448\u0435, \u0430 \u0431\u043e\u043d\u0443\u0441\u043e\u043c &#8212; \u043a\u043e\u043c\u044c\u044e\u043d\u0438\u0442\u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u0435, \u043d\u0430 SO \u043a\u0443\u0447\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u0438 \u0434\u0430\u0436\u0435 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0438 \u043f\u0440\u043e \u043d\u0438\u0445 \u0437\u043d\u0430\u044e\u0442 \u0438 \u043c\u043e\u0433\u0443\u0442 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043f\u043e\u0441\u043e\u0431\u0438\u0442\u044c. \u041c\u044b \u0436\u0435 \u0442\u0443\u0442 &#8212; \u043f\u043e\u043a\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441 \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 \u0446\u0435\u043b\u044c\u044e.<\/p>\n<p>\u0412\u0441\u0435, \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0434\u0438\u043b. \u041f\u043e\u0433\u043d\u0430\u043b\u0438<\/p>\n<h3>\u0421\u0442\u0435\u043a<\/h3>\n<p>\u042d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442 &#8212; \u043d\u0435 \u0438\u0437 \u0442\u0435\u0445, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u044f \u043e\u0431\u044b\u0447\u043d\u043e \u0438\u0437\u0443\u0447\u0430\u044e \u043d\u043e\u0432\u044b\u0435 \u044f\u0437\u044b\u043a\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438\u043b\u0438 \u043d\u043e\u0432\u044b\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0442\u0435\u043a \u043b\u0438\u0447\u043d\u043e \u0434\u043b\u044f \u043c\u0435\u043d\u044f &#8212; \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439<\/p>\n<pre><code>[tool.poetry.dependencies] python = \"&amp;gt;=3.11, &amp;lt;3.13\" fastapi = \"==0.115.12\" pydantic = {extras = [\"email\"], version = \"2.11.5\"} pydantic-settings = \"==2.9.1\" Hypercorn = \"==0.17.3\" uvloop = \"==0.21.0\" toml = \"==0.10.2\" python-ulid = \"==3.0.0\" aiofiles = \"==24.1.0\" orjson = \"==3.10.18\"<\/code><\/pre>\n<p>\u0418\u0437 \u044d\u0442\u043e\u0433\u043e \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c FastAPI \u0434\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u043c, \u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e &#8212; \u043c\u043e\u0439 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0439 \u0442\u0443\u043b\u0441\u0435\u0442, \u043d\u0435 \u043d\u0435\u0441\u0443\u0449\u0438\u0439 \u043d\u0430 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0434\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u043e\u0441\u043e\u0431\u043e\u0439 \u0446\u0435\u043d\u043d\u043e\u0441\u0442\u0438. \u041d\u043e, \u0434\u0443\u043c\u0430\u044e, \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c<\/p>\n<p>\u041d\u0443 \u0438 \u0432\u0435\u0440\u043d\u044b\u0439 <code>poetry<\/code> &#8212; \u0437\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0433\u043e \u043d\u0430 \u0440\u0430\u0437\u0440\u0443\u043b\u0438\u0432\u0430\u043d\u0438\u0438 \u0434\u0435\u0440\u0435\u0432\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/p>\n<h3>\u0421\u0442\u0430\u0440\u0442 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 \u0432\u0435\u0440\u0441\u0438\u0438 0.1.0 (\u0442\u043e \u0435\u0441\u0442\u044c, \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438)<\/p>\n<pre><code class=\"bash\">.\/ice-registry\/ \u251c\u2500\u2500 Dockerfile \u251c\u2500\u2500 LICENSE.md \u251c\u2500\u2500 README.md \u251c\u2500\u2500 config.py \u251c\u2500\u2500 deploy \u2502   \u2514\u2500\u2500 config \u2502       \u2514\u2500\u2500 nginx \u2502           \u2514\u2500\u2500 registry.conf \u251c\u2500\u2500 docker-compose-local.yaml \u251c\u2500\u2500 poetry.lock \u251c\u2500\u2500 pyproject.toml \u2514\u2500\u2500 registry     \u251c\u2500\u2500 __main__.py     \u251c\u2500\u2500 api     \u2502   \u251c\u2500\u2500 __init__.py     \u2502   \u251c\u2500\u2500 docker_v2     \u2502   \u2502   \u251c\u2500\u2500 __init__.py     \u2502   \u2502   \u251c\u2500\u2500 check_blob.py     \u2502   \u2502   \u251c\u2500\u2500 create_manifest.py     \u2502   \u2502   \u251c\u2500\u2500 create_upload.py     \u2502   \u2502   \u251c\u2500\u2500 finalize_upload.py     \u2502   \u2502   \u251c\u2500\u2500 get_blob.py     \u2502   \u2502   \u251c\u2500\u2500 get_manifest.py     \u2502   \u2502   \u251c\u2500\u2500 get_tags_list.py     \u2502   \u2502   \u251c\u2500\u2500 ping.py     \u2502   \u2502   \u251c\u2500\u2500 router.py     \u2502   \u2502   \u2514\u2500\u2500 update_upload.py     \u2502   \u2514\u2500\u2500 status     \u2502       \u251c\u2500\u2500 __init__.py     \u2502       \u251c\u2500\u2500 get_health.py     \u2502       \u2514\u2500\u2500 router.py     \u2514\u2500\u2500 utils         \u251c\u2500\u2500 __init__.py         \u251c\u2500\u2500 auth.py         \u2514\u2500\u2500 version.py<\/code><\/pre>\n<p>\u0418\u0442\u0430\u043a, \u0441 \u0447\u0435\u0433\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u043b\u044e\u0431\u043e\u0439 \u043f\u0440\u0438\u043b\u0438\u0447\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441? \u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u0441 \u0445\u0435\u043b\u0441\u0447\u0435\u043a\u0430 \u0438 \u0441\u0431\u043e\u0440\u043a\u0438<\/p>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u0440\u0443\u0447\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0432\u0441\u0435\u0433\u0434\u0430 <code>200 Ok<\/code>. \u0422\u0430\u043a\u043e\u0439 \u0432\u043e\u0442 \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u044b\u0439 healthcheck, \u0434\u0430<\/p>\n<pre><code class=\"python\"># registry\/api\/status\/get_health.py  from .router import router   @router.get(     \"\/health\",     summary=\"\u0421\u0442\u0430\u0442\u0443\u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\", ) async def get_health():     return True <\/code><\/pre>\n<p>\u0420\u043e\u0443\u0442\u0435\u0440 \u0434\u043b\u044f \u043c\u043e\u0434\u0443\u043b\u044f status \u0442\u043e\u0436\u0435 \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u044b\u0439<\/p>\n<pre><code class=\"python\"># registry\/api\/status\/router.py  from fastapi import APIRouter   router = APIRouter(tags=[\"status\"])  <\/code><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u0432 \u044d\u0442\u043e\u043c \u043c\u043e\u0434\u0443\u043b\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0430\u044f \u0440\u0443\u0447\u043a\u0430 \u0441\u0442\u0430\u0442\u0443\u0441\u0430, \u0445\u0435\u043b\u0441\u0447\u0435\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0437\u0434\u043e\u0440\u043e\u0432\u044c\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430. \u041d\u043e \u043d\u0435 \u0441\u0435\u0433\u043e\u0434\u043d\u044f<\/p>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0443\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0442\u0430\u0449\u0438\u0442\u044c \u0440\u043e\u0443\u0442\u0435\u0440 \u043c\u043e\u0434\u0443\u043b\u044f status \u0432 \u043e\u0431\u0449\u0438\u0439 \u0440\u043e\u0443\u0442\u0435\u0440 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f&#8230;<\/p>\n<pre><code class=\"python\"># registry\/api\/__init__.py  from fastapi import APIRouter  from .status import router as status_router   router = APIRouter() router.include_router(status_router, prefix=\"\/status\")  <\/code><\/pre>\n<p>&#8230; \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c, \u043d\u0430\u043a\u043e\u043d\u0435\u0446, \u0441\u0430\u043c\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/p>\n<pre><code class=\"python\"># registry\/__main__.py  import asyncio import uvloop  from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware from hypercorn.asyncio import serve from hypercorn.config import Config from starlette.exceptions import HTTPException as StarletteHTTPException  from config import settings  from .api import router   def run():     app = FastAPI(         title=\"Ice Docker Registry\",         version=\"0.1.0\",         docs_url=\"\/openapi\" if settings.debug else None,         redoc_url=None,         root_path=settings.root_path,         debug=settings.debug,     )      app.include_router(router)      app.add_middleware(         CORSMiddleware,         allow_origins=[\"*\"],         allow_credentials=True,         allow_methods=[\"*\"],         allow_headers=[\"*\"],     )      @app.exception_handler(StarletteHTTPException)     async def http_exception_handler(request: Request, exc: StarletteHTTPException):         return JSONResponse(             status_code=exc.status_code,             headers=exc.headers,             content={                 \"meta\": {                     \"url\": str(request.url),                     \"client\": request.client.host,                 },                 \"status\": exc.status_code,                 \"method\": request.method,                 \"error_text\": exc.detail,             })      hypercorn_config = Config()     hypercorn_config.bind = [ settings.bind_host ]      uvloop.install()     asyncio.run((serve(app, hypercorn_config)))   if __name__ == \"__main__\":     run()  <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u0440\u0430\u0437\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0430\u0436\u043d\u044b\u0445 \u0432\u0435\u0449\u0435\u0439:<\/p>\n<ul>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0440\u043e\u0443\u0442\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>\u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f (\u0438 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0431\u0435\u0437\u0430\u043b\u0430\u0431\u0435\u0440\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0439 allow) \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a CORSMiddleware<\/p>\n<\/li>\n<li>\n<p>\u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u0430 <code>@app.exception_handler<\/code> \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u0449\u0438\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u0441\u0435\u0445 http-\u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 (\u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u0438 \u043f\u043e\u0442\u043e\u043c\u043a\u043e\u0432 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0433\u043e HTTPException). \u0412\u0430\u0436\u043d\u044b\u0439 \u0448\u0430\u0433, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f \u0434\u0435\u043b\u0430\u044e \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0435. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441 \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u0442\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\/\u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435 \u0437\u0430\u0434\u0443\u043c\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043e\u0448\u0438\u0431\u043e\u043a, \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0439\u0437\u0438\u0442\u044c HTTPException \u0432 \u043d\u0443\u0436\u043d\u044b\u0445 \u043c\u0435\u0441\u0442\u0430\u0445 \u043a\u043e\u0434\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0444\u0438\u0433 Hypercorn<\/p>\n<\/li>\n<li>\n<p>\u043d\u0443 \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0441\u0435 \u0442\u043e\u0433\u043e \u0436\u0435 Hypercorn (\u0430 \u0442\u0430\u043a\u0436\u0435 &#8212; \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0435\u0442\u043b\u0438 uvloop), \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435, \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u0434 \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0431\u043e\u0440\u043a\u043e\u0439 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c &#8212; \u0444\u0430\u0439\u043b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/p>\n<pre><code class=\"python\"># config.py  import os  from pydantic import Field, IPvAnyAddress from pydantic_settings import SettingsConfigDict, BaseSettings   class ServiceSettings(BaseSettings):     model_config =<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-461162","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/461162","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=461162"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/461162\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=461162"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=461162"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=461162"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}