mirror of https://github.com/portainer/portainer
commit
09a5534499
80
README.md
80
README.md
|
@ -1,79 +1,47 @@
|
|||
# Portainer
|
||||
|
||||
The easiest way to manage Docker.
|
||||
<p align="center">
|
||||
<img title="portainer" src='http://portainer.io/images/logo_alt.png' />
|
||||
</p>
|
||||
|
||||
[](https://microbadger.com/images/portainer/portainer "Latest version on Docker Hub")
|
||||
[](http://microbadger.com/images/portainer/portainer "Image size")
|
||||
[](http://portainer.readthedocs.io/en/stable/?badge=stable)
|
||||
[](http://portainer.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://gitter.im/portainer/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
Portainer is a lightweight management UI which allows you to **easily** manage your Docker host or Swarm cluster.
|
||||
**_Portainer_** is a lightweight management UI which allows you to **easily** manage your Docker host or Swarm cluster.
|
||||
|
||||
# Usage
|
||||
**_Portainer_** is meant to be as **simple** to deploy as it is to use. It consists of a single container that can run on any Docker for Linux engine. A Docker for Windows version is on its way !
|
||||
|
||||
It's really simple to deploy it using Docker:
|
||||
**_Portainer_** allows you to manage your Docker containers, images, volumes, networks and more ! It is compatible with the *standalone Docker* engine and with *Docker Swarm*.
|
||||
|
||||
```shell
|
||||
$ docker run -d -p 9000:9000 portainer/portainer -H tcp://<DOCKER_HOST>:<DOCKER_PORT>
|
||||
```
|
||||
## Demo
|
||||
|
||||
Just point it at your targeted Docker host and then access Portainer by hitting [http://localhost:9000](http://localhost:9000) with a web browser.
|
||||
<img src="http://portainer.io/images/screenshots/portainer.gif" width="77%"/>
|
||||
|
||||
If your target is a Docker Swarm cluster or a Docker cluster using *swarm mode*, just add the flag `--swarm`:
|
||||
You can try out the public demo instance: http://demo.portainer.io/ (login with the username **demo** and the password **tryportainer**).
|
||||
|
||||
```shell
|
||||
$ docker run -d -p 9000:9000 portainer/portainer -H tcp://<SWARM_HOST>:<SWARM_PORT> --swarm
|
||||
```
|
||||
Please note that the public demo cluster is **reset every 15min**.
|
||||
|
||||
If you don't specify any target, its default behaviour is to use a bind mount on the Docker socket so you can easily deploy it to manage your local Docker host:
|
||||
## Getting started
|
||||
|
||||
```shell
|
||||
$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer
|
||||
```
|
||||
* [Deploy Portainer](https://portainer.readthedocs.io/en/latest/deployment.html)
|
||||
* [Documentation](https://portainer.readthedocs.io)
|
||||
|
||||
Have a look at our [documentation](http://portainer.readthedocs.io/en/stable/deployment.html) for more deployment options.
|
||||
## Getting help
|
||||
|
||||
# Configuration
|
||||
* Issues: https://github.com/portainer/portainer/issues
|
||||
* FAQ: https://portainer.readthedocs.io/en/latest/faq.html
|
||||
* Gitter (chat): https://gitter.im/portainer/Lobby
|
||||
* Slack: http://portainer.io/slack/
|
||||
|
||||
Portainer is easy to tune using CLI flags.
|
||||
## Reporting bugs and contributing
|
||||
|
||||
## Hiding specific containers
|
||||
* Want to report a bug or request a feature? Please open [an issue](https://github.com/portainer/portainer/issues/new).
|
||||
* Want to help us build **_portainer_**? Follow our [contribution guidelines](https://portainer.readthedocs.io/en/latest/contribute.html) to build it locally and make a pull request. We need all the help we can get!
|
||||
|
||||
Portainer allows you to hide container with a specific label by using the `-l` flag.
|
||||
## Limitations
|
||||
|
||||
For example, take a container started with the label `owner=acme`:
|
||||
```shell
|
||||
$ docker run -d --label owner=acme nginx
|
||||
```
|
||||
|
||||
Simply add the `-l owner=acme` option on the CLI when starting Portainer:
|
||||
```shell
|
||||
$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer -l owner=acme
|
||||
```
|
||||
|
||||
## Use your own templates
|
||||
|
||||
Portainer allows you to rapidly deploy containers using `App Templates`.
|
||||
|
||||
By default [Portainer templates](https://raw.githubusercontent.com/portainer/templates/master/templates.json) will be used but you can also define your own templates.
|
||||
|
||||
Add the `--templates` flag and specify the external location of your templates when starting Portainer:
|
||||
|
||||
```shell
|
||||
$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer --templates http://my-host.my-domain/templates.json
|
||||
```
|
||||
|
||||
For more information about hosting your own template definitions and the format, see the [templates documentation](http://portainer.readthedocs.io/en/stable/templates.html).
|
||||
|
||||
Check our [documentation](http://portainer.readthedocs.io/en/stable/configuration.html) for more configuration options.
|
||||
|
||||
# FAQ
|
||||
|
||||
Be sure to check our [FAQ](http://portainer.readthedocs.io/en/stable/faq.html) if you are missing some information.
|
||||
|
||||
# Limitations
|
||||
|
||||
Portainer has full support for the following Docker versions:
|
||||
**_Portainer_** has full support for the following Docker versions:
|
||||
|
||||
* Docker 1.10 to Docker 1.12 (including `swarm-mode`)
|
||||
* Docker Swarm >= 1.2.3
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
// main is the entry point of the program
|
||||
func main() {
|
||||
kingpin.Version("1.9.2")
|
||||
kingpin.Version("1.9.3")
|
||||
var (
|
||||
endpoint = kingpin.Flag("host", "Dockerd endpoint").Default("unix:///var/run/docker.sock").Short('H').String()
|
||||
addr = kingpin.Flag("bind", "Address and port to serve Portainer").Default(":9000").Short('p').String()
|
||||
|
|
|
@ -188,4 +188,4 @@ angular.module('portainer', [
|
|||
.constant('DOCKER_PORT', '') // Docker port, leave as an empty string if no port is requred. If you have a port, prefix it with a ':' i.e. :4243
|
||||
.constant('CONFIG_ENDPOINT', 'settings')
|
||||
.constant('TEMPLATES_ENDPOINT', 'templates')
|
||||
.constant('UI_VERSION', 'v1.9.2');
|
||||
.constant('UI_VERSION', 'v1.9.3');
|
||||
|
|
|
@ -98,6 +98,9 @@
|
|||
<span ng-if="container.Ports.length == 0" >-</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!containers">
|
||||
<td colspan="8" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="containers.length == 0">
|
||||
<td colspan="8" class="text-center text-muted">No containers available.</td>
|
||||
</tr>
|
||||
|
|
|
@ -8,7 +8,6 @@ function ($scope, Container, ContainerHelper, Info, Settings, Messages, Config)
|
|||
$scope.sortReverse = false;
|
||||
$scope.state.selectedItemCount = 0;
|
||||
$scope.swarm_mode = false;
|
||||
$scope.containers = [];
|
||||
|
||||
$scope.order = function (sortType) {
|
||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||
|
@ -34,6 +33,10 @@ function ($scope, Container, ContainerHelper, Info, Settings, Messages, Config)
|
|||
return model;
|
||||
});
|
||||
$('#loadContainersSpinner').hide();
|
||||
}, function (e) {
|
||||
$('#loadContainersSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to retrieve containers");
|
||||
$scope.containers = [];
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -107,6 +107,9 @@
|
|||
<td>{{ image.VirtualSize|humansize }}</td>
|
||||
<td>{{ image.Created|getisodatefromtimestamp }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!images">
|
||||
<td colspan="5" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="images.length == 0">
|
||||
<td colspan="5" class="text-center text-muted">No images available.</td>
|
||||
</tr>
|
||||
|
|
|
@ -5,7 +5,6 @@ function ($scope, $state, Config, Image, Messages) {
|
|||
$scope.sortType = 'RepoTags';
|
||||
$scope.sortReverse = true;
|
||||
$scope.state.selectedItemCount = 0;
|
||||
$scope.images = [];
|
||||
|
||||
$scope.config = {
|
||||
Image: '',
|
||||
|
@ -98,6 +97,7 @@ function ($scope, $state, Config, Image, Messages) {
|
|||
}, function (e) {
|
||||
$('#loadImagesSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to retrieve images");
|
||||
$scope.images = [];
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
<td>{{ network.IPAM.Config[0].Subnet ? network.IPAM.Config[0].Subnet : '-' }}</td>
|
||||
<td>{{ network.IPAM.Config[0].Gateway ? network.IPAM.Config[0].Gateway : '-' }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!networks">
|
||||
<td colspan="8" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="networks.length == 0">
|
||||
<td colspan="8" class="text-center text-muted">No networks available.</td>
|
||||
</tr>
|
||||
|
|
|
@ -6,8 +6,6 @@ function ($scope, $state, Network, Config, Messages) {
|
|||
$scope.state.advancedSettings = false;
|
||||
$scope.sortType = 'Name';
|
||||
$scope.sortReverse = false;
|
||||
$scope.networks = [];
|
||||
|
||||
$scope.config = {
|
||||
Name: ''
|
||||
};
|
||||
|
@ -93,6 +91,7 @@ function ($scope, $state, Network, Config, Messages) {
|
|||
}, function (e) {
|
||||
$('#loadNetworksSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to retrieve networks");
|
||||
$scope.networks = [];
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,12 @@
|
|||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!services">
|
||||
<td colspan="4" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="services.length == 0">
|
||||
<td colspan="4" class="text-center text-muted">No services available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
angular.module('services', [])
|
||||
.controller('ServicesController', ['$scope', '$stateParams', '$state', 'Service', 'ServiceHelper', 'Messages',
|
||||
function ($scope, $stateParams, $state, Service, ServiceHelper, Messages) {
|
||||
|
||||
$scope.services = [];
|
||||
$scope.state = {};
|
||||
$scope.state.selectedItemCount = 0;
|
||||
$scope.sortType = 'Name';
|
||||
|
@ -77,6 +75,7 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Messages) {
|
|||
}, function(e) {
|
||||
$('#loadServicesSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to retrieve services");
|
||||
$scope.services = [];
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -77,11 +77,14 @@
|
|||
</rd-widget-header>
|
||||
<rd-widget-body classes="padding">
|
||||
<div class="template-list">
|
||||
<div ng-repeat="tpl in templates" class="container-template hvr-grow" id="template_{{ $index }}" ng-click="selectTemplate($index)">
|
||||
<div ng-repeat="tpl in templates" class="container-template hvr-underline-from-center" id="template_{{ $index }}" ng-click="selectTemplate($index)">
|
||||
<img class="logo" ng-src="{{ tpl.logo }}" />
|
||||
<div class="title">{{ tpl.title }}</div>
|
||||
<div class="description">{{ tpl.description }}</div>
|
||||
</div>
|
||||
<div ng-if="!templates" class="text-center text-muted">
|
||||
Loading...
|
||||
</div>
|
||||
<div ng-if="templates.length == 0" class="text-center text-muted">
|
||||
No templates available.
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
angular.module('templates', [])
|
||||
.controller('TemplatesController', ['$scope', '$q', '$state', '$filter', 'Config', 'Info', 'Container', 'ContainerHelper', 'Image', 'Volume', 'Network', 'Templates', 'Messages',
|
||||
function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper, Image, Volume, Network, Templates, Messages) {
|
||||
$scope.templates = [];
|
||||
$scope.selectedTemplate = null;
|
||||
$scope.formValues = {
|
||||
network: "",
|
||||
name: ""
|
||||
};
|
||||
$scope.templates = [];
|
||||
|
||||
var selectedItem = -1;
|
||||
|
||||
|
@ -161,6 +159,7 @@ function ($scope, $q, $state, $filter, Config, Info, Container, ContainerHelper,
|
|||
}, function (e) {
|
||||
$('#loadTemplatesSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to retrieve apps list");
|
||||
$scope.templates = [];
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,9 @@
|
|||
<td>{{ volume.Driver }}</td>
|
||||
<td>{{ volume.Mountpoint }}</td>
|
||||
</tr>
|
||||
<tr ng-if="!volumes">
|
||||
<td colspan="4" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="volumes.length == 0">
|
||||
<td colspan="4" class="text-center text-muted">No volumes available.</td>
|
||||
</tr>
|
||||
|
|
|
@ -5,8 +5,6 @@ function ($scope, $state, Volume, Messages) {
|
|||
$scope.state.selectedItemCount = 0;
|
||||
$scope.sortType = 'Name';
|
||||
$scope.sortReverse = true;
|
||||
$scope.volumes = [];
|
||||
|
||||
$scope.config = {
|
||||
Name: ''
|
||||
};
|
||||
|
@ -61,6 +59,7 @@ function ($scope, $state, Volume, Messages) {
|
|||
}, function (e) {
|
||||
$('#loadVolumesSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to retrieve volumes");
|
||||
$scope.volumes = [];
|
||||
});
|
||||
}
|
||||
fetchVolumes();
|
||||
|
|
|
@ -8,7 +8,7 @@ angular
|
|||
icon: '='
|
||||
},
|
||||
transclude: true,
|
||||
template: '<div class="widget-header"><div class="row"><span class="pull-left"><img class="custom-header-ico" ng-src="{{icon}}"></img> <span class="small text-muted"> {{title}} </span> </span><span class="pull-right col-xs-6 col-sm-4" ng-transclude></span></div></div>',
|
||||
template: '<div class="widget-header"><div class="row"><span class="pull-left"><img class="custom-header-ico" ng-src="{{icon}}"></img> <span class="text-muted"> {{title}} </span> </span><span class="pull-right col-xs-6 col-sm-4" ng-transclude></span></div></div>',
|
||||
restrict: 'E'
|
||||
};
|
||||
return directive;
|
||||
|
|
|
@ -217,16 +217,67 @@ input[type="radio"] {
|
|||
}
|
||||
|
||||
.custom-header-ico {
|
||||
max-width: 16px;
|
||||
max-height: 16px;
|
||||
max-width: 32px;
|
||||
max-height: 32px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.btn-responsive {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1107px) {
|
||||
.btn-responsive {
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.42857143;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Underline From Center */
|
||||
.hvr-underline-from-center {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
-webkit-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.hvr-underline-from-center:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
left: 50%;
|
||||
right: 50%;
|
||||
bottom: 0;
|
||||
background: #85898b;
|
||||
height: 2px;
|
||||
-webkit-transition-property: left, right;
|
||||
transition-property: left, right;
|
||||
-webkit-transition-duration: 0.3s;
|
||||
transition-duration: 0.3s;
|
||||
-webkit-transition-timing-function: ease-out;
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
.hvr-underline-from-center:hover:before, .hvr-underline-from-center:focus:before, .hvr-underline-from-center:active:before {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.container-template {
|
||||
font-size: 1em;
|
||||
width: 256px;
|
||||
height: 128px;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
margin: 15px;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
@ -259,20 +310,5 @@ input[type="radio"] {
|
|||
.container-template .description {
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.btn-responsive {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1107px) {
|
||||
.btn-responsive {
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.42857143;
|
||||
border-radius: 4px;
|
||||
}
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "portainer",
|
||||
"version": "1.9.2",
|
||||
"version": "1.9.3",
|
||||
"homepage": "https://github.com/portainer/portainer",
|
||||
"authors": [
|
||||
"Anthony Lapenna <anthony.lapenna at gmail dot com>"
|
||||
|
@ -35,7 +35,6 @@
|
|||
"bootstrap": "~3.3.6",
|
||||
"font-awesome": "~4.6.3",
|
||||
"filesize": "~3.3.0",
|
||||
"Hover": "2.0.2",
|
||||
"jquery": "1.11.1",
|
||||
"jquery.gritter": "1.7.4",
|
||||
"lodash": "4.12.0",
|
||||
|
|
|
@ -88,8 +88,7 @@ module.exports = function (grunt) {
|
|||
'bower_components/font-awesome/css/font-awesome.min.css',
|
||||
'bower_components/rdash-ui/dist/css/rdash.min.css',
|
||||
'bower_components/angular-ui-select/dist/select.min.css',
|
||||
'bower_components/xterm.js/dist/xterm.css',
|
||||
'bower_components/Hover/css/hover-min.css'
|
||||
'bower_components/xterm.js/dist/xterm.css'
|
||||
]
|
||||
},
|
||||
clean: {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"author": "Portainer.io",
|
||||
"name": "portainer",
|
||||
"homepage": "http://portainer.io",
|
||||
"version": "1.9.2",
|
||||
"version": "1.9.3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:portainer/portainer.git"
|
||||
|
|
Loading…
Reference in New Issue