initial commit

This commit is contained in:
Bryan 2016-02-02 02:40:01 -08:00
parent 264b264c72
commit 1d5792df65
15 changed files with 438 additions and 1 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
builds

60
Dockerfile Normal file
View File

@ -0,0 +1,60 @@
# Copyright 2016 Bryan J. Hong
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM ubuntu:14.04
MAINTAINER bryan@turbojets.net
ENV DEBIAN_FRONTEND noninteractive
# Add Aptly repository
RUN echo "deb http://repo.aptly.info/ squeeze main" > /etc/apt/sources.list.d/aptly.list
RUN apt-key adv --keyserver keys.gnupg.net --recv-keys E083A3782A194991
# Add Nginx repository
RUN echo "deb http://nginx.org/packages/ubuntu/ trusty nginx" > /etc/apt/sources.list.d/nginx.list
RUN echo "deb-src http://nginx.org/packages/ubuntu/ trusty nginx" >> /etc/apt/sources.list.d/nginx.list
RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
# Update APT repository and install packages
RUN apt-get -q update \
&& apt-get -y install aptly \
bzip2 \
gnupg \
gpgv \
supervisor \
nginx
# Install GPG Generator
COPY assets/gpg_batch.sh /opt/gpg_batch.sh
# Install Aptly Configuration
COPY assets/aptly.conf /etc/aptly.conf
# Install Mirror Update Script
COPY assets/update_mirror.sh /opt/update_mirror.sh
# Install Nginx Config
COPY assets/nginx.conf.sh /opt/nginx.conf.sh
COPY assets/supervisord.nginx.conf /etc/supervisor/conf.d/nginx.conf
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
# Install Startup script
COPY assets/startup.sh /opt/startup.sh
# Bind mount location
VOLUME [ "/opt/aptly" ]
# Execute Startup script when container starts
ENTRYPOINT [ "/opt/startup.sh" ]

5
NOTICE Normal file
View File

@ -0,0 +1,5 @@
docker-aptly
Copyright 2016 Bryan J. Hong
This product contains software (https://github.com/bryanhong/docker-aptly) developed
by Bryan Hong (http://github.com/bryanhong) licensed under the Apache License.

View File

@ -1,2 +1,81 @@
# docker-aptly
#docker-aptly
Dockerfile and support scripts to run aptly in a container backed by nginx
from [aptly.info](http://aptly.info):
>aptly is a swiss army knife for Debian repository management: it allows you to mirror remote repositories, manage local package repositories, take snapshots, pull new versions of packages along with dependencies, publish as Debian repository.
##Requirements / Dependencies
* Docker 1.6 or higher, we are using the Docker syslog driver in this container and this feature made its debut in 1.6
* ```vars``` needs to be populated with the appropriate variables.
##Commands and variables
* ```vars```: Variables for Docker registry, the application, and aptly repository data location
* ```build.sh```: Build the Docker image locally
* ```run.sh```: Starts the Docker container, it the image hasn't been built locally, it is fetched from the repository set in vars
* ```push.sh```: Pushes the latest locally built image to the repository set in vars
* ```shell.sh```: get a shell within the container
##How this image/container works
####Data
All of aptly's data (including PGP keys and GPG keyrings) is bind mounted outside of the container to preserve it if the container is removed or rebuilt. Set the location for the bind mount in ```vars``` before starting the container. If you're going to host a mirror of Ubuntu's main repository, you'll need upwards of 35GB of free space as of Feb 2016, plan for growth.
####Networking
By default, Docker will map port 80 on the Docker host to port 80 within the container where nginx is configured to listen. You can change the external listening port in ```vars``` to map to any port you like.
####Security
The GPG password you set in ```vars``` is stored in plain text and is visible as an environment variable inside the container. It is set as an enviornment variable to allow for automation of repository updates without user interaction. The GPG password can be removed completely but it is safer to encrypt the GPG keyrings because they are bind mounted outside the container to avoid the necessity of regenerating/redistributing keys if the container is removed or rebuilt.
##Usage
####Configure the container
1. Configure application specific variables in ```vars```
####Build the image
1. Run ```./build.sh```
####Start the container
1. Run ```./run.sh```
2. Wait until the GPG keyrings are created (not 0 bytes) before proceeding (it can take a few minutes). They will be in the bind mount location you chose in ```vars```
####Create a mirror of Ubuntu's main repository
1. The initial download of the repository may take quite some time depending on your bandwidth limits, it may be in your best interest to open a tmux or screen session before proceeding.
2. Attach to the container ```./shell.sh```
3. By default, ```/opt/update_mirror.sh``` will automate the creation of an Ubuntu 14.04 Trusty repository, if you want a different release, modify the variables in the script.
4. Run ```/opt/update_mirror.sh```
5. If the script fails due to network disconnects etc, just re-run it.
When the script completes, you should have a functional mirror that you can point a client to.
####Point a host at the mirror
1. Fetch the public PGP key from your aptly repository and add it to your trusted repositories
```
wget http://FQDN.OF.APTLY/aptly_repo_key.pub
apt-key add aptly_repo_key.pub
```
2. Backup then replace /etc/apt/sources.list
```
cp /etc/apt/sources.list /etc/apt/sources.list.bak
echo "deb http://FQDN.OF.APTLY/ ubuntu main" > /etc/apt/sources.list
apt-get update
```
You should be able to install packages at this point!
Checkout the excellent aptly documentation [here](http://www.aptly.info/doc/overview/)
####Pushing your image to the registry
If you're happy with your container and ready to share with others, push your image up to a [Docker registry](https://docs.docker.com/docker-hub/) and save any other changes you've made so the image can be easily changed or rebuilt in the future.
1. Run ```./push.sh```
> NOTE: If your image will be used FROM other containers you might want to use ```./push.sh flatten``` to consolidate the AUFS layers into a single layer. Keep in mind, you may lose Dockerfile attributes when your image is flattened.

17
assets/aptly.conf Normal file
View File

@ -0,0 +1,17 @@
{
"rootDir": "/opt/aptly",
"downloadConcurrency": 4,
"downloadSpeedLimit": 0,
"architectures": [],
"dependencyFollowSuggests": false,
"dependencyFollowRecommends": false,
"dependencyFollowAllVariants": false,
"dependencyFollowSource": false,
"gpgDisableSign": false,
"gpgDisableVerify": false,
"downloadSourcePackages": false,
"ppaDistributorID": "ubuntu",
"ppaCodename": "",
"S3PublishEndpoints": {},
"SwiftPublishEndpoints": {}
}

17
assets/gpg_batch.sh Executable file
View File

@ -0,0 +1,17 @@
#! /bin/bash
cat << EOF > /opt/gpg_batch
%echo Generating a GPG key, might take a while
Key-Type: RSA
Key-Length: 2048
Subkey-Type: ELG-E
Subkey-Length: 1024
Name-Real: ${FULL_NAME}
Name-Comment: Aptly Repo Signing
Name-Email: ${EMAIL_ADDRESS}
Expire-Date: 0
Passphrase: ${GPG_PASSWORD}
%pubring /opt/aptly/aptly.pub
%secring /opt/aptly/aptly.sec
%commit
%echo done
EOF

12
assets/nginx.conf.sh Executable file
View File

@ -0,0 +1,12 @@
#! /bin/bash
cat << EOF > /etc/nginx/conf.d/default.conf
server {
root /opt/aptly/public;
server_name ${HOSTNAME};
location / {
autoindex on;
}
}
EOF

25
assets/startup.sh Executable file
View File

@ -0,0 +1,25 @@
#! /bin/bash
# If the repository GPG keypair doesn't exist, create it.
if [[ ! -f /opt/aptly/aptly.sec ]] || [[ ! -f /opt/aptly/aptly.pub ]]; then
/opt/gpg_batch.sh
gpg --batch --gen-key /opt/gpg_batch
fi
# Import Ubuntu keyrings
gpg --no-default-keyring \
--keyring /usr/share/keyrings/ubuntu-archive-keyring.gpg \
--export | \
gpg --no-default-keyring \
--keyring trustedkeys.gpg \
--import
# Aptly looks in /root/.gnupg for default keyrings
ln -sf /opt/aptly/aptly.sec /root/.gnupg/secring.gpg
ln -sf /opt/aptly/aptly.pub /root/.gnupg/pubring.gpg
# Generate Nginx Config
/opt/nginx.conf.sh
# Start Supervisor
/usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf

View File

@ -0,0 +1,5 @@
[program:nginx]
command=/usr/sbin/nginx
redirect_stderr=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0

61
assets/update_mirror.sh Executable file
View File

@ -0,0 +1,61 @@
#! /bin/bash
set -e
UBUNTU_RELEASE=trusty
UPSTREAM_URL="http://archive.ubuntu.com/ubuntu/"
REPOS=( ${UBUNTU_RELEASE} ${UBUNTU_RELEASE}-updates ${UBUNTU_RELEASE}-security )
# Export the GPG Public key
if [[ ! -f /opt/aptly/aptly_repo_key.pub ]]; then
gpg --export --armor > /opt/aptly/${HOSTNAME}_signing.key
fi
# Create repository mirrors if they don't exist
set +e
for repo in ${REPOS[@]}; do
aptly mirror list -raw | grep "^${repo}$"
if [[ $? -ne 0 ]]; then
echo "Creating mirror of ${repo} repository."
aptly mirror create \
-architectures=amd64 ${repo} ${UPSTREAM_URL} ${repo} main
fi
done
set -e
# Update all repository mirrors
for repo in ${REPOS[@]}; do
echo "Updating ${repo} repository mirror.."
aptly mirror update ${repo}
done
# Create snapshots of updated repositories
for repo in ${REPOS[@]}; do
echo "Creating snapshot of ${repo} repository mirror.."
aptly snapshot create ${repo}-`date +%Y%m%d%H` from mirror ${repo}
done
# Merge snapshots into a single snapshot with updates applied
echo "Merging snapshots into one.."
aptly snapshot merge -latest \
${UBUNTU_RELEASE}-merged-`date +%Y%m%d%H` \
${UBUNTU_RELEASE}-`date +%Y%m%d%H` \
${UBUNTU_RELEASE}-updates-`date +%Y%m%d%H` \
${UBUNTU_RELEASE}-security-`date +%Y%m%d%H`
# Publish the latest merged snapshot
set +e
aptly publish list -raw | awk '{print $2}' | grep "^${UBUNTU_RELEASE}$"
if [[ $? -eq 0 ]]; then
aptly publish switch \
-passphrase="${GPG_PASSWORD}" \
${UBUNTU_RELEASE} ${UBUNTU_RELEASE}-merged-`date +%Y%m%d%H`
else
aptly publish snapshot \
-passphrase="${GPG_PASSWORD}" \
-distribution=${UBUNTU_RELEASE} ${UBUNTU_RELEASE}-merged-`date +%Y%m%d%H`
fi
set -e
# Generate Aptly Graph
aptly graph
cp `ls -rt /tmp/aptly-graph*.png | tail -n1` /opt/aptly/public/aptly_graph.png

21
build.sh Executable file
View File

@ -0,0 +1,21 @@
#!/bin/bash
source vars
docker build -t "${REPO_NAME}/${APP_NAME}:${TAG}" .
# If the build was successful (0 exit code)...
if [ $? -eq 0 ]; then
echo
echo "Build of ${REPO_NAME}/${APP_NAME}:${TAG} completed OK"
echo
# log build details to builds file
echo "`date` => ${REPO_NAME}/${APP_NAME}:${TAG}" >> builds
# The build exited with an error.
else
echo "Build failed!"
exit 1
fi

51
push.sh Executable file
View File

@ -0,0 +1,51 @@
#!/bin/bash
source vars
#This will take the latest locally built image and push it to the repository as
#configured in vars and tag it as latest.
if [[ ! -f builds ]]; then
echo
echo "It appears that the Docker image hasn't been built yet, run build.sh first"
echo
exit 1
fi
LATESTIMAGE=`tail -1 builds | awk '{print $8}'`
# Flatten is here as an option and not the default because with the export/import
# process we lose Dockerfile attributes like PORT and VOLUMES. Flattening helps if
# we are concerned about hitting the AUFS 42 layer limit or creating an image that
# other containers source FROM
DockerExport () {
docker export ${APP_NAME} | docker import - ${REPO_NAME}/${APP_NAME}:latest
}
DockerPush () {
docker push ${REPO_NAME}/${APP_NAME}:latest
}
case "$1" in
flatten)
docker inspect ${APP_NAME} > /dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo "The ${APP_NAME} container doesn't appear to exist, exiting"
exit 1
fi
RUNNING=`docker inspect ${APP_NAME} | python -c 'import sys, json; print json.load(sys.stdin)[0]["State"]["Running"]'`
if [[ "${RUNNING}" = "True" ]]; then
echo "Stopping ${APP_NAME} container for export"
docker stop ${APP_NAME}
DockerExport
DockerPush
else
DockerExport
DockerPush
fi
;;
*)
docker tag -f ${LATESTIMAGE} ${REPO_NAME}/${APP_NAME}:latest
DockerPush
esac

33
run.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/bash
source vars
#If there is a locally built image present, prefer that over the
#one in the registry, we're going to assume you're working on changes
#to the image.
if [[ ! -f builds ]]; then
LATESTIMAGE=${REPO_NAME}/${APP_NAME}:latest
else
LATESTIMAGE=`tail -1 builds | awk '{print $8}'`
fi
echo
echo "Starting $APP_NAME..."
echo
echo -n "Container ID: "
docker run \
--detach=true \
--log-driver=syslog \
--name="${APP_NAME}" \
--restart=always \
-e FULL_NAME="${FULL_NAME}" \
-e EMAIL_ADDRESS="${EMAIL_ADDRESS}" \
-e GPG_PASSWORD="${GPG_PASSWORD}" \
-e HOSTNAME=${HOSTNAME} \
-v ${APTLY_DATADIR}:/opt/aptly \
-p ${DOCKER_HOST_PORT}:80 \
${LATESTIMAGE}
# Other useful options
# -p DOCKERHOST_PORT:CONTAINER_PORT \
# -e "ENVIRONMENT_VARIABLE_NAME=VALUE" \
# -v /DOCKERHOST/PATH:/CONTAINER/PATH \

12
shell.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
source vars
docker inspect ${APP_NAME} > /dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo "The ${APP_NAME} container doesn't appear to exist, exiting"
fi
CONTAINER_ID=`docker inspect ${APP_NAME} | python -c 'import sys, json; print json.load(sys.stdin)[0]["Id"]'`
docker exec -it ${CONTAINER_ID} /bin/bash

38
vars Normal file
View File

@ -0,0 +1,38 @@
#!/bin/bash
#### BEGIN APP SPECIFIC VARIABLES
# Name of the container
APP_NAME=aptly
# Docker Hub Username or internal registry (e.g. docker-registry.example.com:5000)
REPO_NAME="myusername"
# Name used on repository signing key
FULL_NAME="First Last"
# Email address of the repository key signer
EMAIL_ADDRESS=user@example.com
# Password used to encrypt the signing key
GPG_PASSWORD=repo1234
# The directory on the Docker host to store repository data
APTLY_DATADIR=/path/to/lots/ofspace
# FQDN of the Docker host that the aptly container will run on
HOSTNAME=aptly.example.com
# TCP port that aptly will be reachable on, set to something else if you already
# have a web server running on your Docker host
DOCKER_HOST_PORT=80
#### END APP SPECIFIC VARIABLES
#### BEGIN GENERIC VARIABLES
# Get an SHA sum of all files involved in building the image so the image can be tagged
# this will provide assurance that any image with the same tag was built the same way.
SHASUM=`find . -type f \
-not -path "*/.git/*" \
-not -path "*.gitignore*" \
-not -path "*builds*" \
-not -path "*run.sh*" \
-exec shasum {} + | awk '{print $1}' | sort | shasum | cut -c1-4`
TAG="`date +%Y%m%d`-${SHASUM}"
#### END GENERIC VARIABLES