├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── defaults └── main.yml ├── files ├── docker-registry.init └── docker-registry.service ├── handlers └── main.yml ├── meta └── main.yml ├── tasks ├── base.yml ├── main.yml ├── nginx.yml └── redis.yml ├── templates ├── config.yml.j2 └── nginx.j2 └── tests ├── hosts └── test.yml /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .vagrant 3 | Vagrantfile -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: '2.7' 3 | env: 4 | - ANSIBLE_VERSION=1.7 5 | - ANSIBLE_VERSION=1.8 6 | before_install: 7 | - sudo apt-get update -qq 8 | - sudo apt-get install -y curl 9 | install: 10 | - pip install ansible==$ANSIBLE_VERSION 11 | - '{ echo ''[defaults]''; echo ''roles_path = ../''; } >> ansible.cfg' 12 | script: 13 | - ansible-playbook -i tests/hosts tests/test.yml --syntax-check 14 | - ansible-playbook -i tests/hosts tests/test.yml --connection=local --sudo 15 | - | 16 | ansible-playbook -i tests/hosts tests/test.yml --connection=local --sudo | grep -q 'changed=0.*failed=0' && (echo 'Idempotence test: pass' && exit 0) || (echo 'Idempotence test: fail' && exit 1) 17 | - curl http://localhost:5000 18 | notifications: 19 | slack: 20 | secure: FCNkjKkd2oesFcIsvFekb7GtWVniyT8a9fH1mvp9LOg3AJ1ZpDoOnuvuOXJTrNM8rmuqo+GAu9GzB1yCTmbjBlhyVAoeiV1N5qRctN4NbA5FohDRbKAY2Bgp4Q1nFRlGmjbdx45AZAzQNMD3Ni5Hfqiwf0voNS/qS8zYXtAG8rc= 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 CodingBunch 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ansible-docker-registry 2 | ========= 3 | 4 | [![Build Status](https://travis-ci.org/codingbunch/ansible-docker-registry.png?branch=master)](https://travis-ci.org/codingbunch/ansible-docker-registry) 5 | 6 | An Ansible role for installing a private Docker Registry 7 | 8 | Requirements 9 | ------------ 10 | 11 | TODO 12 | 13 | Role Variables 14 | -------------- 15 | 16 | ### Default 17 | 18 | * ```domain```: Specify a domain name (default: ```localhost```) 19 | * ```log_level```: Log level for the server (default: ```info```) 20 | * ```storage_type```: Accepts either ```local``` or ```s3``` (default: ```local```) 21 | * ```storage_path```: File path to store registry uploads (default: ```/var/docker-registry```) 22 | * ```use_nginx```: Install Nginx and configure docker-registry vhost (default: ```true```) 23 | * ```use_redis```: Install and configure Redis server as a cache (default: ```true```) 24 | * ```docker_registry_version```: Specify what version of docker_registry needs to be installed (default: ```none```) 25 | 26 | ### SSL Settings 27 | * ```registry_port```: Custom port to have nginx listen on (default: ```80```) 28 | * ```registry_ssl```: Enable SSL on nginx (default: ```false```) 29 | * ```registry_ssl_cert```: Specify a SSL certificate to use (default: ```/etc/ssl/certs/docker_registry.crt```) 30 | * ```registry_ssl_key```: Specify a SSL key to use (default: ```/etc/ssl/private/docker_registry.key```) 31 | * ```create_ssl_cert```: Create a self-signed certificate if ```registry_ssl_cert``` is not present on the system (default: ```true```) 32 | 33 | ### OpenSSL cert settings 34 | * ```openssl_bits```: Bit length of the private key (default: ```2048```) 35 | * ```openssl_countryName```: Country Code (default: ```ES```) 36 | * ```openssl_stateOrProvinceName```: State or Province (default: ```Madrid```) 37 | * ```openssl_localityName```: Locality name (default: ```Madrid```) 38 | * ```openssl_organizationName```: Full company name (default: ```MyCompany```) 39 | * ```openssl_organizationalUnitName```: Company sub-division or a product name (default: ```Docker Registry```) 40 | * ```openssl_commonName```: Your domain name, or in case of wildcard certificates, use an astrisk, like this: *.mycompany.com (default: ```mycompany.com```) 41 | 42 | ### S3 Storage 43 | 44 | * ```s3_region```: optional, will default to US Standard 45 | * ```s3_bucket```: also provides the value for boto_bucket 46 | * ```s3_storage_path``` 47 | * ```s3_access_key``` 48 | * ```s3_secret_key``` 49 | 50 | #### There are 2 ways to specify credentials for S3 storage: 51 | 52 | **1. Using access and secret keys:** 53 | 54 | By specifying ```s3_access_key``` and ```s3_secret_key```. 55 | 56 | **2. Using IAM instance profiles** 57 | 58 | By omitting ```s3_access_key``` and ```s3_secret_key``` it will use an IAM instance profile that has been attached to that virtual machine for [authorisation](http://docs.aws.amazon.com/IAM/latest/UserGuide/roles-usingrole-ec2instance.html). 59 | 60 | ### GCS Storage 61 | 62 | * ```gcs_bucket```: Value for the bucket deing used. 63 | * ```gcs_storage_path```: The path within th ebucket where to store data. (will be created if not exist). 64 | 65 | #### There are 3 ways to specify credentials for GCS storage: 66 | 67 | **1. Using Google Cloud Platform OAuth:** 68 | 69 | * ```gcs_oauth2```: oauth2 value (default ```false```) 70 | 71 | **2. Using Google Storage interoperability keys:** 72 | 73 | * ```gcs_access_key```: only used when gcs_oauth2 is false and ```use_gcs_default_credentials``` is undefined. 74 | * ```gcs_secret_key```: only used when gcs_oauth2 is false and ```use_gcs_default_credentials``` is undefined. 75 | 76 | **3. Using Google service account authorization:** 77 | 78 | * ```use_gcs_default_credentials```: use a service account that has been attached to that virtual machine for [authorisation](https://developers.google.com/identity/protocols/application-default-credentials) value (default ```undefined```) 79 | 80 | ## Note on SSL 81 | 82 | If using a self-signed certificate, or no SSL certificate for recent docker versions, you must start the docker daemon with ```--insecure-registry```. 83 | 84 | For boot2docker, see https://github.com/boot2docker/boot2docker#insecure-registry. 85 | 86 | 87 | Example Playbook 88 | ---------------- 89 | 90 | - hosts: docker-registry 91 | roles: 92 | - { role: codingbunch.ansible-docker-registry } 93 | 94 | License 95 | ------- 96 | 97 | MIT 98 | 99 | Author Information 100 | ------------------ 101 | 102 | CodingBunch 103 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for ansible-docker-registry 3 | domain: localhost 4 | log_level: info 5 | storage_type: local 6 | storage_path: /var/docker-registry 7 | use_nginx: true 8 | use_redis: true 9 | registry_port: 80 10 | 11 | # authenticated users 12 | use_auth: false 13 | auth_users: 14 | - { user: 'guest', passwd: '{PLAIN}secret' } 15 | 16 | # SSL Settings 17 | registry_ssl_port: 443 18 | registry_ssl: true 19 | registry_ssl_cert: /etc/ssl/certs/docker_registry.crt 20 | registry_ssl_key: /etc/ssl/private/docker_registry.key 21 | create_ssl_cert: true 22 | 23 | # OpenSSL cert settings 24 | openssl_bits: 2048 25 | openssl_countryName: ES 26 | openssl_stateOrProvinceName: Madrid 27 | openssl_localityName: Madrid 28 | openssl_organizationName: MyCompany 29 | openssl_organizationalUnitName: 'Docker Registry' 30 | openssl_commonName: mycompany.com 31 | 32 | # S3 Variables 33 | # s3_region 34 | # s3_bucket 35 | # s3_storage_path 36 | # s3_access_key 37 | # s3_secret_key -------------------------------------------------------------------------------- /files/docker-registry.init: -------------------------------------------------------------------------------- 1 | description "Docker Registry" 2 | version "0.9.1" 3 | author "Docker, Inc." 4 | 5 | start on runlevel [2345] 6 | stop on runlevel [016] 7 | 8 | respawn 9 | respawn limit 10 5 10 | 11 | script 12 | cd $REGISTRY_HOME 13 | exec gunicorn -k gevent --max-requests 100 -e SETTINGS_FLAVOR=prod \ 14 | --graceful-timeout 3600 -t 3600 -b localhost:5000 -w 8 \ 15 | --access-logfile /var/log/docker-registry/access.log \ 16 | --error-logfile /var/log/docker-registry/server.log \ 17 | docker_registry.wsgi:application 18 | end script -------------------------------------------------------------------------------- /files/docker-registry.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Registry server for Docker 3 | 4 | [Service] 5 | Type=simple 6 | Environment=DOCKER_REGISTRY_CONFIG=/etc/docker-registry/config.yml 7 | EnvironmentFile=-/etc/sysconfig/docker-registry 8 | WorkingDirectory=/usr/lib/python2.7/site-packages/docker-registry 9 | ExecStart=/usr/bin/gunicorn -k gevent --max-requests 100 -e SETTINGS_FLAVOR=prod \ 10 | --graceful-timeout 3600 -t 3600 -b localhost:5000 -w 8 \ 11 | --access-logfile /var/log/docker-registry/access.log \ 12 | --error-logfile /var/log/docker-registry/server.log \ 13 | docker_registry.wsgi:application 14 | Restart=on-failure 15 | 16 | [Install] 17 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for ansible-docker-registry 3 | 4 | - name: restart docker-registry 5 | service: name=docker-registry state=restarted 6 | 7 | - name: restart nginx 8 | service: name=nginx state=restarted -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: aalda 4 | description: Private registry server for Docker 5 | company: CodingBunch 6 | license: MIT 7 | min_ansible_version: 1.7 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - precise 12 | - quantal 13 | - raring 14 | - saucy 15 | - trusty 16 | - name: Debian 17 | versions: 18 | - wheezy 19 | categories: 20 | - cloud 21 | - development 22 | - packaging 23 | dependencies: [] 24 | version: 1.0.3 25 | -------------------------------------------------------------------------------- /tasks/base.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for ansible-docker-registry 3 | 4 | - name: Install base dependencies 5 | apt: name={{ item }} state=present update_cache=yes cache_valid_time=3600 6 | with_items: 7 | - build-essential 8 | - libevent-dev 9 | - libssl-dev 10 | - liblzma-dev 11 | - python-dev 12 | - python-pip 13 | - python-passlib 14 | - swig 15 | tags: base 16 | 17 | - name: Install latest version of Docker Registry 18 | pip: name=docker-registry state=present 19 | when: docker_registry_version is not defined 20 | tags: base 21 | 22 | - name: Install a specific version of Docker Registry 23 | pip: name=docker-registry state=present version={{ docker_registry_version }} 24 | when: docker_registry_version is defined 25 | tags: base 26 | 27 | - name: Create required directories 28 | file: dest={{ item }} state=directory 29 | with_items: 30 | - "{{ storage_path }}" 31 | - /var/log/docker-registry 32 | - /etc/docker-registry 33 | tags: base 34 | 35 | - name: Set configuration file 36 | template: > 37 | src=config.yml.j2 38 | dest=/etc/docker-registry/config.yml 39 | notify: restart docker-registry 40 | tags: base 41 | 42 | - name: Create symlink for config 43 | file: > 44 | src=/etc/docker-registry/config.yml 45 | dest=/usr/local/lib/python2.7/dist-packages/config/config.yml 46 | state=link 47 | tags: base 48 | 49 | - name: Install docker-registry init file 50 | copy: src=docker-registry.init dest=/etc/init/docker-registry.conf owner=root group=root mode=644 51 | tags: base 52 | 53 | - name: Install gcs backend driver. 54 | pip: name=https://github.com/GoogleCloudPlatform/docker-registry-driver-gcs/zipball/master 55 | when: storage_type == 'gcs' 56 | 57 | - name: Ensure Docker Registry is enabled and running 58 | service: name=docker-registry state=running enabled=yes 59 | tags: base 60 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for ansible-docker-registry 3 | 4 | - include: base.yml 5 | tags: base 6 | 7 | - include: nginx.yml 8 | when: use_nginx 9 | tags: nginx 10 | 11 | - include: redis.yml 12 | when: use_redis 13 | tags: redis -------------------------------------------------------------------------------- /tasks/nginx.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Nginx server 3 | apt: name={{ item }} state=present 4 | with_items: 5 | - nginx-extras 6 | - apache2-utils 7 | tags: nginx 8 | 9 | - name: Create authenticated users 10 | htpasswd: path=/etc/nginx/docker-registry.htpasswd name={{item.user}} password={{item.passwd}} crypt_scheme=plaintext 11 | with_items: auth_users 12 | when: use_auth 13 | tags: nginx 14 | 15 | - name: Verify ssl cert 16 | stat: path={{ registry_ssl_cert }} 17 | changed_when: false 18 | register: ssl_cert 19 | tags: nginx 20 | 21 | - name: Generate ssl cert 22 | shell: > 23 | openssl req -subj '/CN={{ domain }}/O={{openssl_organizationName}}/OU={{openssl_organizationalUnitName}}/C={{openssl_countryName}}/ST={{openssl_stateOrProvinceName}}/L={{openssl_localityName}}' 24 | -new -newkey rsa:{{openssl_bits}} -days 365 -nodes -x509 -keyout {{ registry_ssl_key }} -out {{ registry_ssl_cert }} 25 | when: create_ssl_cert and not ssl_cert.stat.exists 26 | tags: nginx 27 | 28 | - name: Install nginx vhost 29 | template: src=nginx.j2 dest=/etc/nginx/sites-available/docker-registry 30 | tags: nginx 31 | 32 | - name: Create symlink to vhost 33 | file: src=/etc/nginx/sites-available/docker-registry path=/etc/nginx/sites-enabled/docker-registry state=link 34 | notify: restart nginx 35 | tags: nginx 36 | 37 | - name: Remove default nginx vhost 38 | file: path=/etc/nginx/sites-enabled/default state=absent 39 | when: domain == 'localhost' 40 | notify: restart nginx 41 | tags: nginx 42 | 43 | - name: Ensure nginx is running 44 | service: name=nginx state=running enabled=yes 45 | tags: nginx 46 | -------------------------------------------------------------------------------- /tasks/redis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Redis server 3 | apt: name=redis-server state=present 4 | tags: redis 5 | 6 | - name: Ensure redis-server is running 7 | service: name=redis-server state=running enabled=yes 8 | tags: redis -------------------------------------------------------------------------------- /templates/config.yml.j2: -------------------------------------------------------------------------------- 1 | # All other flavors inherit the `common' config snippet 2 | common: &common 3 | issue: '"docker-registry server"' 4 | loglevel: _env:LOGLEVEL:{{ log_level }} 5 | # Enable debugging (additional informations in the output of the _ping endpoint) 6 | debug: _env:DEBUG:false 7 | standalone: true 8 | search_backend: sqlalchemy 9 | sqlalchemy_index_database: sqlite:///{{ storage_path }}/docker-registry.db 10 | storage_path: _env:STORAGE_PATH:{{ storage_path }} 11 | 12 | {% if use_redis %} 13 | # Enabling LRU cache for small files. This speeds up read/write on 14 | # small files when using a remote storage backend (like S3). 15 | cache: 16 | host: localhost 17 | port: 6379 18 | db: 0 19 | cache_lru: 20 | host: localhost 21 | port: 6379 22 | db: 0 23 | {% endif %} 24 | 25 | {% if storage_type == 'local' %} 26 | local: &local 27 | <<: *common 28 | storage: file 29 | {% endif %} 30 | 31 | {% if storage_type == 's3' %} 32 | s3: &s3 33 | <<: *common 34 | storage: s3 35 | s3_region: _env:AWS_REGION:{{ s3_region }} 36 | s3_bucket: _env:AWS_BUCKET:{{ s3_bucket }} 37 | s3_encrypt: _env:AWS_ENCRYPT:true 38 | s3_secure: _env:AWS_SECURE:true 39 | boto_bucket: _env:AWS_BUCKET:{{ s3_bucket }} 40 | {% if s3_access_key is defined and s3_secret_key is defined %} 41 | s3_access_key: _env:AWS_KEY:{{ s3_access_key }} 42 | s3_secret_key: _env:AWS_SECRET:{{ s3_secret_key }} 43 | {% else %} 44 | # No [s3_access_key|s3_secret_key] lets us use an IAM instance profile 45 | # that is attached to the virtual machine. See: 46 | # http://docs.aws.amazon.com/IAM/latest/UserGuide/roles-usingrole-ec2instance.html 47 | {% endif %} 48 | {% endif %} 49 | 50 | {% if storage_type == 'gcs' %} 51 | gcs: &gcs 52 | <<: *common 53 | storage: gcs 54 | boto_bucket: _env:GCS_BUCKET:{{ gcs_bucket }} 55 | {% if gcs_oauth2 is defined and gcs_oauth2 %} 56 | oauth2: {{ gcs_oauth2 }} 57 | {% elif use_gcs_default_credentials is defined %} 58 | # No oauth2 or [gcs_access_key|gcs_secret_key] let us use a service account 59 | # that is connected to the virtual machine instance see Google Application 60 | # Default Credentials (see 61 | # https://developers.google.com/identity/protocols/application-default-credentials) 62 | # to allow the docker registry to get its authorisation credentials by making 63 | # google api calls. 64 | {% else %} 65 | gs_access_key: _env:GCS_KEY:{{ gcs_access_key }} 66 | gs_secret_key: _env:GCS_SECRET:{{ gcs_secret_key }} 67 | {% endif %} 68 | storage_path: _env:STORAGE_PATH:{{ gcs_storage_path }} 69 | {% endif %} 70 | 71 | prod: 72 | <<: *{{storage_type }} 73 | -------------------------------------------------------------------------------- /templates/nginx.j2: -------------------------------------------------------------------------------- 1 | upstream docker-registry { 2 | server localhost:5000; 3 | } 4 | 5 | server { 6 | {% if registry_ssl %} 7 | listen {{ registry_ssl_port }}; 8 | {% else %} 9 | listen {{ registry_port }}; 10 | {% endif %} 11 | {% if domain != 'localhost' %} 12 | server_name {{ domain }}; 13 | {% endif %} 14 | 15 | proxy_set_header Host $http_host; # required for docker client's sake 16 | proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP 17 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 18 | proxy_connect_timeout 900; 19 | 20 | client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads 21 | 22 | # required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486) 23 | chunked_transfer_encoding on; 24 | 25 | {% if registry_ssl %} 26 | ssl on; 27 | {% if registry_ssl_cert %} 28 | ssl_certificate {{ registry_ssl_cert }}; 29 | {% endif %} 30 | {% if registry_ssl_key %} 31 | ssl_certificate_key {{ registry_ssl_key }}; 32 | {% endif %} 33 | {% endif %} 34 | 35 | location / { 36 | {% if use_auth %} 37 | auth_basic "Restricted"; 38 | auth_basic_user_file /etc/nginx/docker-registry.htpasswd; 39 | {% else %} 40 | auth_basic off; 41 | {% endif %} 42 | proxy_pass http://docker-registry; 43 | } 44 | 45 | location /_ping { 46 | auth_basic off; 47 | proxy_pass http://docker-registry; 48 | } 49 | 50 | location /v1/_ping { 51 | auth_basic off; 52 | proxy_pass http://docker-registry; 53 | } 54 | } -------------------------------------------------------------------------------- /tests/hosts: -------------------------------------------------------------------------------- 1 | [docker-registry] 2 | localhost -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: docker-registry 3 | gather_facts: yes 4 | roles: 5 | - ansible-docker-registry 6 | tasks: 7 | - wait_for: port=5000 delay=10 --------------------------------------------------------------------------------