В общем, дослоупочил я до момента осознания факта, что без докера дальше жить нельзя. Поскольку на хабе докера и без меня валяется куча невнятных образов, дабы не увеличивать энтропию, я решил сразу поднять свой докер с letsencrypt и мордой - Portus.

Подготовка

Вступление

Все контейнеры (кроме nginx) не будут слушать внешний мир. Portus и registry будут делить между собой один hostname, в данном примере - docker.force.fm. Разделение трафика между registry и Portus будет делать nginx. Конфиг nginx под это дело подрезан тут.

Letsencrypt сертификат пока мне было проще получать за пределами контейнеров, поэтому его не будет в итоговом Docker compose.

Docker-compose будет заниматься разворачиванием:

  1. Docker registry (distribution).  Сам реестр, в котором будут храниться образы. С хоста ему нужно пробросить ssl-сертификаты и конфиг. В виртуалке конфиг лежит в /etc/docker/registry/config.yml. Проверять наличие доступа у конкретного юзера registry будет при помощи token-авторизации через Portus. То есть все пароли хранятся Portus, никаких .htpasswd и т.д. не надо.
  2. База данных, mariadb. Нужна самому Portus'у. Все настройки через env.
  3. Nginx. Будет слушать внешний мир, ему нужно пробросить те же сертификаты, что и реестру и еще парочку конфигов.
  4. Сам Portus. Печально, что в их доках нет нормального семпла настроек, пришлось искать их в других образах, issues, коде и т.д. Пока настраивал, подрывало убрать оттуда апач, но потом я смирился.

Установка

sudo apt-get install \
     apt-transport-https \
     ca-certificates \
     curl \
     gnupg2 \
     software-properties-common -y
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"
sudo apt-get update
sudo apt-get install docker-ce

И docker-compose:

sudo apt-get install docker-compose

И Certbot:

sudo apt-get install certbot

Больше вроде как ничего не понадобится.

Ещё немного информации

Внутри контейнера Portus его запчасти уложены в /srv/Portus. Если что, нужные конфиги лежат там:

docker exec -it registry-portus /bin/sh
# Тут можно посмотреть то, что забирается из env для настроек БД.
cat /srv/Portus/config/database.yml
# env-секреты
cat /srv/Portus/config/secrets.yml
# Весь конфиг. Его лучше не трогать, потому что в config-local ...
cat /srv/Portus/config/config.yml
# ... Есть редефайны
cat /srv/Portus/config/config-local.yml

Настройка

Структура

/docker/
└── registry    
    ├── certs
    │   ├── cert.pem
    │   ├── chain.pem
    │   ├── dhparam.pem
    │   ├── fullchain.pem
    │   ├── privkey.pem    
    ├── config
    │   └── config.yml
    ├── data    
    ├── nginx
    │   ├── conf.d
    │   │   ├── letsencrypt.conf
    │   │   ├── proxy.conf
    │   │   ├── ssl.conf
    │   │   └── tuning.conf
    │   └── nginx.conf
    ├── portus    
    │   └── config-local.yml
    └── docker-compose.yml

Создадим всё это хозяйство:

mkdir -p /docker/registry/{certs,config,data,nginx/conf.d,portus}

Сертификаты

Генерируем сертификаты. Лежать они будут в /etc/letsencrypt/live/docker.force.fm/:

certbot certonly --agree-tos --standalone -d docker.force.fm --email admin@force.fm

Копируем сертификаты поближе:

cp /etc/letsencrypt/live/docker.force.fm/* /docker/registry/certs/

Сгенерируем еще dhparam:

openssl dhparam -out /docker/registry/certs/dhparam.pem 4096

Если всё очень долго, наверное нет ничего плохого в том, чтобы сгенерировать его на локалке, а потом унести на сервер через сысыха.

Конфиги

Общий docker-compose

version: '2'
services:
  srv:
    restart: always
    image: registry:2.6.1
    container_name: registry-srv
    depends_on:
      - web
    volumes:
      - ./config/config.yml:/etc/docker/registry/config.yml
      - ./data:/var/lib/registry
      - ./certs:/certs
  web:
    image: opensuse/portus:2.2
    container_name: registry-portus
    volumes:
      - "./certs/fullchain.pem:/certificates/portus-ca.crt:ro"
      - "./certs/privkey.pem:/secrets/portus.key:ro"
      - "./portus/config-local.yml:/srv/Portus/config/config-local.yml"
    links:
      - db
    environment:
      - PORTUS_SECRET_KEY_BASE='<длинный ключ сюда>'
      - DEBUG=false
      - RACK_ENV=production
      - RAILS_ENV=production
      - PORTUS_MACHINE_FQDN_VALUE=docker.force.fm
      - PORTUS_PORTUS_PASSWORD='ПАРОЛИСТЫЙПАРОЛЬ'
      - MARIADB_SERVICE_HOST=db
      - MARIADB_ADAPTER=mysql2
      - MARIADB_ENCODING=utf8
      - MARIADB_USER=portus
      - MARIADB_PASSWORD=portus
      - MARIADB_DATABASE=portus

  db:
    container_name: registry-mariadb
    image: mariadb:10
    environment:
      - MYSQL_ROOT_PASSWORD=portus
      - MYSQL_USER=portus
      - MYSQL_PASSWORD=portus
      - MYSQL_DATABASE=portus

  nginx:
    image: nginx
    container_name: registry-nginx
    ports:
      - "443:443"
    #  - "80:80"
    links:
      - srv
      - web
    volumes:
      - "./nginx/conf.d:/etc/nginx/conf.d"
      - "./nginx/nginx.conf:/etc/nginx/nginx.conf"
      - "./certs:/certs"

Нужно заменить в конфиге PORTUS_MACHINE_FQDN_VALUE и пароли. Пароль PORTUS_PORTUS_PASSWORD будет назначен пользователю portus, от имени которого будут происходить всякие системные операции.

Настройки distribution (registry)

version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
  delete:
    enabled: true
auth:
  token:
    realm: https://docker.force.fm/v2/token
    service: docker.force.fm
    issuer: docker.force.fm
    rootcertbundle: /certs/fullchain.pem
http:
  addr: :5000
  host: docker.force.fm:5000
  tls:
    certificate: /certs/fullchain.pem
    key: /certs/privkey.pem
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
notifications:
  endpoints:
    - name: portus
      url: https://docker.force.fm/v2/webhooks/events
      timeout: 500ms
      threshold: 5
      backoff: 1s

Здесь нужно обратить внимание на то что auth.token.service == auth.token.issuer, иначе не взлетит.

Настройки Portus

display_name:
  enabled: true
machine_fqdn:
  value: "docker.force.fm"
signup:
  enabled: false
delete:  # только для registry>=2.4, какая-то фигня с garbage collection
  enabled: true

email:
  from: "vasya@example.com"
  name: "Portus"
  reply_to: "mememe@example.com"  
  smtp:
    enabled: true
    address: "smtp.yandex.ru"
    port: 587
    user_name: "vasya@example.com"
    password: "azaza"
    domain: "yandex.ru"

Другого способа проверить валидность настроек мыла, кроме как через сброс пароля я не придумал и не увидел. Настройки приведены для yandex pdd.

Настройки nginx

Их я беззастенчиво стибрил отсюда. Там меняем your.registry.fqdn на актуальный fqdn. Там хорошие комментарии, думаю всем будет понятно. Чтобы не разрывать контекст приведу их тут в немного пожатом виде.

upstream registry {
	server	registry:5000;
}
upstream portus {
	server	portus:443;
}
map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
	'' 'registry/2.0';
}
server {
	listen		443 ssl default;
	server_name	your.registry.fqdn;	
	add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
	client_max_body_size 0;
	chunked_transfer_encoding on;
	# Redirect (most) requests to /v2/* to the Docker Registry
	location /v2/ {		
		if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
			return 404;
		}
		add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;
		proxy_pass			    https://registry;
		proxy_set_header  Host              $http_host;   # required for docker client's sake
		proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
		proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
		proxy_set_header  X-Forwarded-Proto $scheme;
		proxy_read_timeout                  900;

	}
	# Portus needs to handle /v2/token for authentication
	location = /v2/token {
		proxy_pass                          https://portus;
                proxy_set_header  Host              $http_host;   # required for docker client's sake
                proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
                proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
                proxy_set_header  X-Forwarded-Proto $scheme;
                proxy_read_timeout                  900;
        }
	# Portus needs to handle /v2/webhooks/events for notifications
	location = /v2/webhooks/events {
		proxy_pass                          https://portus;
                proxy_set_header  Host              $http_host;   # required for docker client's sake
                proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
                proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
                proxy_set_header  X-Forwarded-Proto $scheme;
                proxy_read_timeout                  900;
        }			
	# Portus handles everything else for the UI
	location / {
		proxy_pass                          https://portus;
		proxy_set_header  Host              $http_host;   # required for docker client's sake
                proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
                proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
                proxy_set_header  X-Forwarded-Proto $scheme;
                proxy_read_timeout                  900;
	}
}

Запуск

Ну, собственно:

docker-compose up

По ходу пьесы Portus будет ждать подъема Маши, создаст миграции и запустится. Теперь можно проверить docker login docker.force.fm. В ссылках справа линк на гх.

Немного скринов

night-crawler
Просмотров: 126
blog comments powered by Disqus