Skip to content

Commit

Permalink
Merge pull request #1106 from samuelkarp/windows-merge
Browse files Browse the repository at this point in the history
Windows merge
  • Loading branch information
nmeyerhans authored Nov 22, 2017
2 parents 87ca4b3 + 73b72e5 commit e813337
Show file tree
Hide file tree
Showing 86 changed files with 2,624 additions and 245 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ out/
coverage/
_bin/
*.exe
*.exe~
*.zip
*.swp
*.orig
Expand All @@ -15,3 +16,4 @@ _bin/
/misc/windows-iam/devcon*
/misc/pause-container/pause
*-stamp
/.idea/
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
## Unreleased
* Feature - Support pulling from Amazon ECR with specified IAM role in task definition
* Feature - Enable support for task level CPU and memory constraints.
* Feature - Enable the ECS agent to run as a Windows service. [#1070](https://github.com/aws/amazon-ecs-agent/pull/1070)
* Enhancement - Support CloudWatch metrics for Windows. [#1077](https://github.com/aws/amazon-ecs-agent/pull/1077)
* Enhancement - Enforce memory limits on Windows. [#1069](https://github.com/aws/amazon-ecs-agent/pull/1069)
* Enhancement - Enforce CPU limits on Windows. [#1089](https://github.com/aws/amazon-ecs-agent/pull/1089)
* Enhancement - Simplify task IAM credential host setup. [#1105](https://github.com/aws/amazon-ecs-agent/pull/1105)

## 1.15.2
* Bug - Fixed a bug where container state information wasn't reported. [#1076](https://github.com/aws/amazon-ecs-agent/pull/1076)
Expand All @@ -11,6 +16,7 @@
* Bug - Fixed a bug where container state information wasn't reported. [#1067](https://github.com/aws/amazon-ecs-agent/pull/1067)
* Bug - Fixed a bug where a task can be blocked in creating state. [#1048](https://github.com/aws/amazon-ecs-agent/pull/1048)
* Bug - Fixed dynamic HostPort in container metadata. [#1052](https://github.com/aws/amazon-ecs-agent/pull/1052)
* Bug - Fixed bug on Windows where container memory limits are not enforced. [#1069](https://github.com/aws/amazon-ecs-agent/pull/1069)

## 1.15.0
* Feature - Support for provisioning tasks with ENIs.
Expand Down
66 changes: 56 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,72 @@ See also the Advanced Usage section below.

### On Windows Server 2016

On Windows Server 2016, the Amazon ECS Container Agent runs as a process on the
host. Unlike Linux, the agent may not run inside a container as it uses the
host's registry and the named pipe at `\\.\pipe\docker_engine` to communicate
with the Docker daemon.
On Windows Server 2016, the Amazon ECS Container Agent runs as a process or
service on the host. Unlike Linux, the agent may not run inside a container as
it uses the host's registry and the named pipe at `\\.\pipe\docker_engine` to
communicate with the Docker daemon.

#### As a Service
To install the service, you can do the following:

```powershell
PS C:\> # Set up directories the agent uses
PS C:\> New-Item -Type directory -Path ${env:ProgramFiles}\Amazon\ECS -Force
PS C:\> New-Item -Type directory -Path ${env:ProgramData}\Amazon\ECS -Force
PS C:\> New-Item -Type directory -Path ${env:ProgramData}\Amazon\ECS\data -Force
PS C:\> # Set up configuration
PS C:\> $ecsExeDir = "${env:ProgramFiles}\Amazon\ECS"
PS C:\> [Environment]::SetEnvironmentVariable("ECS_CLUSTER", "my-windows-cluster", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_LOGFILE", "${env:ProgramData}\Amazon\ECS\log\ecs-agent.log", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_DATADIR", "${env:ProgramData}\Amazon\ECS\data", "Machine")
PS C:\> # Download the agent
PS C:\> $agentVersion = "latest"
PS C:\> $agentZipUri = "https://s3.amazonaws.com/amazon-ecs-agent/ecs-agent-windows-$agentVersion.zip"
PS C:\> $zipFile = "${env:TEMP}\ecs-agent.zip"
PS C:\> Invoke-RestMethod -OutFile $zipFile -Uri $agentZipUri
PS C:\> # Put the executables in the executable directory.
PS C:\> Expand-Archive -Path $zipFile -DestinationPath $ecsExeDir -Force
PS C:\> Set-Location ${ecsExeDir}
PS C:\> # Set $EnableTaskIAMRoles to $true to enable task IAM roles
PS C:\> # Note that enabling IAM roles will make port 80 unavailable for tasks.
PS C:\> [bool]$EnableTaskIAMRoles = $false
PS C:\> if (${EnableTaskIAMRoles} {
>> .\hostsetup.ps1
>> }
PS C:\> # Install the agent service
PS C:\> New-Service -Name "AmazonECS" `
-BinaryPathName "$ecsExeDir\amazon-ecs-agent.exe -windows-service" `
-DisplayName "Amazon ECS" `
-Description "Amazon ECS service runs the Amazon ECS agent" `
-DependsOn Docker `
-StartupType Manual
PS C:\> sc.exe failure AmazonECS reset=300 actions=restart/5000/restart/30000/restart/60000
PS C:\> sc.exe failureflag AmazonECS 1
```

To run the service, you can do the following:
```powershell
Start-Service AmazonECS
```

#### As a Process

```powershell
PS C:\> # Set up directories the agent uses
PS C:\> New-Item -Type directory -Path $ProgramFiles\Amazon\ECS
PS C:\> New-Item -Type directory -Path $ProgramData\Amazon\ECS
PS C:\> New-Item -Type directory -Path ${env:ProgramFiles}\Amazon\ECS -Force
PS C:\> New-Item -Type directory -Path ${env:ProgramData}\Amazon\ECS -Force
PS C:\> New-Item -Type directory -Path ${env:ProgramData}\Amazon\ECS\data -Force
PS C:\> # Set up configuration
PS C:\> $ecsExeDir = "$env:ProgramFiles\Amazon\ECS"
PS C:\> $ecsExeDir = "${env:ProgramFiles}\Amazon\ECS"
PS C:\> [Environment]::SetEnvironmentVariable("ECS_CLUSTER", "my-windows-cluster", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_LOGFILE", "$ProgramData\Amazon\ECS\log\ecs-agent.log", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_DATADIR", "$ProgramData\Amazon\ECS\data", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_LOGFILE", "${env:ProgramData}\Amazon\ECS\log\ecs-agent.log", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_DATADIR", "${env:ProgramData}\Amazon\ECS\data", "Machine")
PS C:\> # Set this environment variable to "true" to enable IAM roles. Note that enabling IAM roles will make port 80 unavailable for tasks.
PS C:\> [Environment]::SetEnvironmentVariable("ECS_ENABLE_TASK_IAM_ROLE", "false", "Machine")
PS C:\> # Download the agent
PS C:\> $agentVersion = "latest"
PS C:\> $agentZipUri = "https://s3.amazonaws.com/amazon-ecs-agent/ecs-agent-windows-$agentVersion.zip"
PS C:\> $zipFile = "$env:TEMP\ecs-agent.zip"
PS C:\> $zipFile = "${env:TEMP}\ecs-agent.zip"
PS C:\> Invoke-RestMethod -OutFile $zipFile -Uri $agentZipUri
PS C:\> # Put the executables in the executable directory.
PS C:\> Expand-Archive -Path $zipFile -DestinationPath $ecsExeDir -Force
Expand Down
4 changes: 2 additions & 2 deletions agent/Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions agent/api/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ import (
)

const (
// DockerContainerMinimumMemoryInBytes is the minimum amount of
// memory to be allocated to a docker container
DockerContainerMinimumMemoryInBytes = 4 * 1024 * 1024 // 4MB
// defaultContainerSteadyStateStatus defines the container status at
// which the container is assumed to be in steady state. It is set
// to 'ContainerRunning' unless overridden
Expand Down
22 changes: 22 additions & 0 deletions agent/api/container_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// +build !windows

// Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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.

package api

const (
// DockerContainerMinimumMemoryInBytes is the minimum amount of
// memory to be allocated to a docker container
DockerContainerMinimumMemoryInBytes = 4 * 1024 * 1024 // 4MB
)
22 changes: 22 additions & 0 deletions agent/api/container_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// +build windows

// Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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.

package api

const (
// DockerContainerMinimumMemoryInBytes is the minimum amount of
// memory to be allocated to a docker container
DockerContainerMinimumMemoryInBytes = 256 * 1024 * 1024 // 256MB
)
62 changes: 54 additions & 8 deletions agent/api/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/aws/amazon-ecs-agent/agent/config"
"github.com/aws/amazon-ecs-agent/agent/credentials"
"github.com/aws/amazon-ecs-agent/agent/ecscni"
"github.com/aws/amazon-ecs-agent/agent/engine/dockerclient"
"github.com/aws/amazon-ecs-agent/agent/engine/emptyvolume"
"github.com/aws/amazon-ecs-agent/agent/utils/ttime"
"github.com/aws/aws-sdk-go/aws/arn"
Expand Down Expand Up @@ -414,11 +415,11 @@ func (task *Task) getEarliestKnownTaskStatusForContainers() TaskStatus {

// DockerConfig converts the given container in this task to the format of
// GoDockerClient's 'Config' struct
func (task *Task) DockerConfig(container *Container) (*docker.Config, *DockerClientConfigError) {
return task.dockerConfig(container)
func (task *Task) DockerConfig(container *Container, apiVersion dockerclient.DockerVersion) (*docker.Config, *DockerClientConfigError) {
return task.dockerConfig(container, apiVersion)
}

func (task *Task) dockerConfig(container *Container) (*docker.Config, *DockerClientConfigError) {
func (task *Task) dockerConfig(container *Container, apiVersion dockerclient.DockerVersion) (*docker.Config, *DockerClientConfigError) {
dockerVolumes, err := task.dockerConfigVolumes(container)
if err != nil {
return nil, &DockerClientConfigError{err.Error()}
Expand Down Expand Up @@ -447,8 +448,11 @@ func (task *Task) dockerConfig(container *Container) (*docker.Config, *DockerCli
ExposedPorts: task.dockerExposedPorts(container),
Volumes: dockerVolumes,
Env: dockerEnv,
Memory: dockerMem,
CPUShares: task.dockerCPUShares(container.CPU),
}

err = task.SetConfigHostconfigBasedOnVersion(container, config, nil, apiVersion)
if err != nil {
return nil, &DockerClientConfigError{"setting docker config failed, err: " + err.Error()}
}

if container.DockerConfig.Config != nil {
Expand All @@ -464,6 +468,43 @@ func (task *Task) dockerConfig(container *Container) (*docker.Config, *DockerCli
return config, nil
}

// SetConfigHostconfigBasedOnVersion sets the fields in both Config and HostConfig based on api version for backward compatibility
func (task *Task) SetConfigHostconfigBasedOnVersion(container *Container, config *docker.Config, hc *docker.HostConfig, apiVersion dockerclient.DockerVersion) error {
// Convert MB to B
dockerMem := int64(container.Memory * 1024 * 1024)
if dockerMem != 0 && dockerMem < DockerContainerMinimumMemoryInBytes {
seelog.Warnf("Task %s container %s memory setting is too low, increasing to %d bytes", task.Arn, container.Name, DockerContainerMinimumMemoryInBytes)
dockerMem = DockerContainerMinimumMemoryInBytes
}
cpuShare := task.dockerCPUShares(container.CPU)

// Docker copied Memory and cpu field into hostconfig in 1.6 with api version(1.18)
// https://github.com/moby/moby/commit/837eec064d2d40a4d86acbc6f47fada8263e0d4c
dockerAPIVersion, err := docker.NewAPIVersion(string(apiVersion))
if err != nil {
seelog.Errorf("Creating docker api version failed, err: %v", err)
return err
}

dockerAPIVersion_1_18 := docker.APIVersion([]int{1, 18})
if dockerAPIVersion.GreaterThanOrEqualTo(dockerAPIVersion_1_18) {
// Set the memory and cpu in host config
if hc != nil {
hc.Memory = dockerMem
hc.CPUShares = cpuShare
}
return nil
}

// Set the memory and cpu in config
if config != nil {
config.Memory = dockerMem
config.CPUShares = cpuShare
}

return nil
}

// dockerCPUShares converts containerCPU shares if needed as per the logic stated below:
// Docker silently converts 0 to 1024 CPU shares, which is probably not what we
// want. Instead, we convert 0 to 2 to be closer to expected behavior. The
Expand Down Expand Up @@ -512,8 +553,8 @@ func (task *Task) dockerConfigVolumes(container *Container) (map[string]struct{}
}

// DockerHostConfig construct the configuration recognized by docker
func (task *Task) DockerHostConfig(container *Container, dockerContainerMap map[string]*DockerContainer) (*docker.HostConfig, *HostConfigError) {
return task.dockerHostConfig(container, dockerContainerMap)
func (task *Task) DockerHostConfig(container *Container, dockerContainerMap map[string]*DockerContainer, apiVersion dockerclient.DockerVersion) (*docker.HostConfig, *HostConfigError) {
return task.dockerHostConfig(container, dockerContainerMap, apiVersion)
}

// ApplyExecutionRoleLogsAuth will check whether the task has excecution role
Expand All @@ -538,7 +579,7 @@ func (task *Task) ApplyExecutionRoleLogsAuth(hostConfig *docker.HostConfig, cred
return nil
}

func (task *Task) dockerHostConfig(container *Container, dockerContainerMap map[string]*DockerContainer) (*docker.HostConfig, *HostConfigError) {
func (task *Task) dockerHostConfig(container *Container, dockerContainerMap map[string]*DockerContainer, apiVersion dockerclient.DockerVersion) (*docker.HostConfig, *HostConfigError) {
dockerLinkArr, err := task.dockerLinks(container, dockerContainerMap)
if err != nil {
return nil, &HostConfigError{err.Error()}
Expand All @@ -564,6 +605,11 @@ func (task *Task) dockerHostConfig(container *Container, dockerContainerMap map[
VolumesFrom: volumesFrom,
}

err = task.SetConfigHostconfigBasedOnVersion(container, nil, hostConfig, apiVersion)
if err != nil {
return nil, &HostConfigError{err.Error()}
}

if container.DockerConfig.HostConfig != nil {
err := json.Unmarshal([]byte(*container.DockerConfig.HostConfig), hostConfig)
if err != nil {
Expand Down
Loading

0 comments on commit e813337

Please sign in to comment.