패키지 | NuGet |
---|---|
FluentDocker | |
마이크로소프트 테스트 | |
XUnit 테스트 |
이 라이브러리는 Fluent API를 사용하여 docker
및 docker-compose
상호 작용을 가능하게 합니다. Linux, Windows 및 Mac에서 지원됩니다. 또한 레거시 docker-machine
상호 작용도 지원합니다.
샘플 Fluent API 사용
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
Assert . AreEqual ( ServiceRunningState . Running , config . State . ToServiceState ( ) ) ;
}
그러면 포스트그레스가 실행되고 준비될 때까지 기다립니다. 작성을 사용하려면 다음과 같이 하세요.
참고: V2 동작을 사용하려면 AssumeComposeVersion(ComposeVersion.V2)을 사용하세요. 기본값은 여전히 V1입니다(올해 말에 기본값을 V2로 변경 예정).
var file = Path . Combine ( Directory . GetCurrentDirectory ( ) ,
( TemplateString ) "Resources/ComposeTests/WordPress/docker-compose.yml" ) ;
// @formatter:off
using ( var svc = new Builder ( )
. UseContainer ( )
. UseCompose ( )
. FromFile ( file )
. RemoveOrphans ( )
. WaitForHttp ( "wordpress" , "http://localhost:8000/wp-admin/install.php" )
. Build ( ) . Start ( ) )
// @formatter:on
{
// We now have a running WordPress with a MySql database
var installPage = await "http://localhost:8000/wp-admin/install.php" . Wget ( ) ;
Assert . IsTrue ( installPage . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ) ;
Assert . AreEqual ( 1 , svc . Hosts . Count ) ; // The host used by compose
Assert . AreEqual ( 2 , svc . Containers . Count ) ; // We can access each individual container
Assert . AreEqual ( 2 , svc . Images . Count ) ; // And the images used.
}
:bulb Linux 사용자를 위한 참고 사항: Docker에는 기본적으로 sudo가 필요하며 라이브러리는 기본적으로 실행 사용자가 docker 데몬과 통신하기 위해 sudo를 수행할 필요가 없다고 예상합니다. 자세한 설명은 Docker 데몬과 대화 장에서 확인할 수 있습니다.
Fluent API는 하나 이상의 서비스를 구축합니다. 각 서비스는 복합적일 수도 있고 단일적일 수도 있습니다. 따라서 여러 docker-compose 기반 서비스를 실행하고 각각을 단일 서비스로 관리하거나 각 docker-compose 서비스에서 모든 기본 서비스를 자세히 살펴보고 사용할 수 있습니다. 서비스를 직접 이용하는 것도 가능합니다.
var file = Path . Combine ( Directory . GetCurrentDirectory ( ) ,
( TemplateString ) "Resources/ComposeTests/WordPress/docker-compose.yml" ) ;
using ( var svc = new DockerComposeCompositeService ( DockerHost , new DockerComposeConfig
{
ComposeFilePath = new List < string > { file } , ForceRecreate = true , RemoveOrphans = true ,
StopOnDispose = true
} ) )
{
svc . Start ( ) ;
// We now have a running WordPress with a MySql database
var installPage = await $ "http://localhost:8000/wp-admin/install.php" . Wget ( ) ;
Assert . IsTrue ( installPage . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ) ;
}
위의 예에서는 단일 작성 파일에서 docker-compose 서비스를 생성합니다. 서비스가 삭제되면 모든 기본 서비스가 자동으로 중지됩니다.
라이브러리는 .NET 전체 4.51 프레임워크 이상, .NET 표준 1.6, 2.0에서 지원됩니다. 세 개의 얇은 레이어로 나누어져 있으며, 각 레이어에 액세스할 수 있습니다.
대부분의 서비스 방법은 확장 방법이며 서비스 자체에 내장되어 있지 않으므로 가볍고 사용자 정의가 가능합니다. 모든 것에 액세스할 수 있으므로 기능을 제공하기 위해 계층 1 명령을 사용하는 서비스에 대한 확장 방법을 쉽게 추가할 수 있습니다.
기여를 환영합니다. 아직 기여 지침은 없지만 Pull Request를 수행할 때 .editorconfig 를 준수하세요. 그렇지 않으면 빌드가 실패합니다. 올해 조만간 실제 지침을 업데이트하겠습니다.
모든 명령을 사용하려면 DockerUri
필요합니다. 로컬 또는 원격의 docker 데몬에 대한 Uri입니다. 검색 가능하거나 하드코딩될 수 있습니다. 로컬 DockerUri
검색은 다음을 수행할 수 있습니다.
var hosts = new Hosts ( ) . Discover ( ) ;
var _docker = hosts . FirstOrDefault ( x => x . IsNative ) ?? hosts . FirstOrDefault ( x => x . Name == "default" ) ;
잘라낸 예제는 docker-machine "default"를 호스트로 선택하지 않은 경우 기본 또는 docker 베타 "기본" 호스트를 확인합니다. docker-machine을 사용 중이고 머신이 존재하지 않거나 시작되지 않은 경우 "test-machine".Create(1024,20000000,1)
등을 사용하여 docker-machine을 쉽게 생성/시작할 수 있습니다. 그러면 1GB RAM, 20GB 디스크가 있고 하나의 CPU를 사용하는 "test-machine"이라는 도커 머신이 생성됩니다.
이제 Uri를 사용하여 명령을 사용하여 통신할 수 있습니다. 예를 들어 클라이언트 및 서버 Docker 바이너리의 버전을 얻으려면 다음을 수행하십시오.
var result = _docker . Host . Version ( _docker . Certificates ) ;
Debug . WriteLine ( result . Data ) ; // Will Print the Client and Server Version and API Versions respectively.
모든 명령은 CommandResponse를 반환하므로 response.Success
통해 성공 요인을 확인할 수 있습니다. 명령과 관련된 데이터가 있으면 response.Data
속성에 반환됩니다.
그런 다음 명령을 사용하여 컨테이너 삭제를 포함하여 시작 및 중지하는 것은 아래와 같이 간단합니다. 아래에서는 컨테이너를 시작하고 이에 대해 PS를 수행한 다음 삭제합니다.
var id = _docker . Host . Run ( "nginx:latest" , null , _docker . Certificates ) . Data ;
var ps = _docker . Host . Ps ( null , _docker . Certificates ) . Data ;
_docker . Host . RemoveContainer ( id , true , true , null , _docker . Certificates ) ;
Windows에서 실행하는 경우 Linux 또는 Windows 컨테이너를 실행하도록 선택할 수 있습니다. LinuxDaemon
또는 WindowsDaemon
사용하여 통신할 데몬을 제어합니다.
_docker . LinuxDaemon ( ) ; // ensures that it will talk to linux daemon, if windows daemon it will switch
일부 명령은 연속 스트림을 사용하여 이벤트나 로그를 원할 때 데이터 스트림을 반환합니다. 스트림은 백그라운드 작업에서 사용할 수 있으며 CancellationToken
지원합니다. 아래 예에서는 로그를 추적합니다.
using ( var logs = _docker . Host . Logs ( id , _docker . Certificates ) )
{
while ( ! logs . IsFinished )
{
var line = logs . TryRead ( 5000 ) ; // Do a read with timeout
if ( null == line )
{
break ;
}
Debug . WriteLine ( line ) ;
}
}
명령에 대한 유틸리티 메소드가 존재합니다. 네트워킹 등과 같은 다양한 형태로 제공됩니다. 예를 들어 로그를 끝까지 읽는 경우:
using ( var logs = _docker . Host . Logs ( id , _docker . Certificates ) )
{
foreach ( var line in logs . ReadToEnd ( ) )
{
Debug . WriteLine ( line ) ;
}
}
이 라이브러리의 최상위 계층은 머신, 이미지, 컨테이너를 정의하고 제어할 수 있는 Fluent API입니다. 예를 들어 Redis 서버에서 읽는 두 개의 nodejs 서버로 로드 밸런서를 설정하는 것은 다음과 같습니다(노드 이미지는 저장소에서 찾을 수 없는 경우 사용자 정의됩니다).
var fullPath = ( TemplateString ) @"${TEMP}/fluentdockertest/${RND}" ;
var nginx = Path . Combine ( fullPath , "nginx.conf" ) ;
Directory . CreateDirectory ( fullPath ) ;
typeof ( NsResolver ) . ResourceExtract ( fullPath , "index.js" ) ;
using ( var services = new Builder ( )
// Define custom node image to be used
. DefineImage ( "mariotoffia/nodetest" ) . ReuseIfAlreadyExists ( )
. From ( "ubuntu" )
. Maintainer ( "Mario Toffia <[email protected]>" )
. Run ( "apt-get update &&" ,
"apt-get -y install curl &&" ,
"curl -sL https://deb.nodesource.com/setup | sudo bash - &&" ,
"apt-get -y install python build-essential nodejs" )
. Run ( "npm install -g nodemon" )
. Add ( "emb:Ductus.FluentDockerTest/Ductus.FluentDockerTest.MultiContainerTestFiles/package.txt" ,
"/tmp/package.json" )
. Run ( "cd /tmp && npm install" )
. Run ( "mkdir -p /src && cp -a /tmp/node_modules /src/" )
. UseWorkDir ( "/src" )
. Add ( "index.js" , "/src" )
. ExposePorts ( 8080 )
. Command ( "nodemon" , "/src/index.js" ) . Builder ( )
// Redis Db Backend
. UseContainer ( ) . WithName ( "redis" ) . UseImage ( "redis" ) . Builder ( )
// Node server 1 & 2
. UseContainer ( ) . WithName ( "node1" ) . UseImage ( "mariotoffia/nodetest" ) . Link ( "redis" ) . Builder ( )
. UseContainer ( ) . WithName ( "node2" ) . UseImage ( "mariotoffia/nodetest" ) . Link ( "redis" ) . Builder ( )
// Nginx as load balancer
. UseContainer ( ) . WithName ( "nginx" ) . UseImage ( "nginx" ) . Link ( "node1" , "node2" )
. CopyOnStart ( nginx , "/etc/nginx/nginx.conf" )
. ExposePort ( 80 ) . Builder ( )
. Build ( ) . Start ( ) )
{
Assert . AreEqual ( 4 , services . Containers . Count ) ;
var ep = services . Containers . First ( x => x . Name == "nginx" ) . ToHostExposedEndpoint ( "80/tcp" ) ;
Assert . IsNotNull ( ep ) ;
var round1 = $ "http:// { ep . Address } : { ep . Port } " . Wget ( ) ;
Assert . AreEqual ( "This page has been viewed 1 times!" , round1 ) ;
var round2 = $ "http:// { ep . Address } : { ep . Port } " . Wget ( ) ;
Assert . AreEqual ( "This page has been viewed 2 times!" , round2 ) ;
}
위의 예에서는 노드 이미지에 대해 Dockerfile을 정의하고 빌드합니다. 그런 다음 바닐라 redis와 nginx를 사용합니다. 기존 Dockerfile을 사용하려는 경우 다음과 같이 수행할 수 있습니다.
using ( var services = new Builder ( )
. DefineImage ( "mariotoffia/nodetest" ) . ReuseIfAlreadyExists ( )
. FromFile ( "/tmp/Dockerfile" )
. Build ( ) . Start ( ) )
{
// Container either build to reused if found in registry and started here.
}
유창한 API는 docker-machine 정의부터 docker 인스턴스 집합까지 지원합니다. 예를 들어 Build()
완료되기 전에 컨테이너 내의 특정 포트나 프로세스를 기다리는 기능이 내장되어 있으므로 using 문 내에서 안전하게 사용할 수 있습니다. 대기 시간 초과 등에 대한 특정 관리가 있는 경우 언제든지 컨테이너를 빌드 및 시작하고 확장 메서드를 사용하여 컨테이너 자체에서 대기를 수행할 수 있습니다.
컨테이너를 생성하려면 시작 부분을 생략하면 됩니다. 예를 들어:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( ) )
{
Assert . AreEqual ( ServiceRunningState . Stopped , container . State ) ;
}
이 예에서는 postgres를 사용하여 컨테이너를 만들고 하나의 환경 변수를 구성합니다. using 문 내에서 IContainerService
시작할 수 있습니다. 따라서 빌드된 각 컨테이너는 IContainerService
에 래핑됩니다. IHostService.GetContainers(...)
사용하여 생성, 실행 및 종료된 컨테이너를 얻는 것도 가능합니다. IHostService
에서는 컨테이너를 생성하기 위해 로컬 저장소의 모든 이미지를 가져올 수도 있습니다.
단일 컨테이너를 실행하려면 Fluent 또는 컨테이너 서비스 시작 방법을 사용하세요. 예를 들어:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( ) ;
Assert . AreEqual ( ServiceRunningState . Running , container . State ) ;
Assert . IsTrue ( config . Config . Env . Any ( x => x == "POSTGRES_PASSWORD=mysecretpassword" ) ) ;
}
기본적으로 Dispose 메서드가 실행되면 컨테이너가 중지되고 삭제됩니다. 컨테이너를 archive에 유지하려면 Fluent API에서 KeepContainer()
사용하세요. Dispose()
호출되면 중지되지만 삭제되지는 않습니다. 폐기 후에도 계속 실행하는 것도 가능합니다.
포트를 명시적으로 또는 무작위로 노출하는 것이 가능합니다. 어느 쪽이든 코드에서 사용할 IP(머신의 경우)와 포트(임의 포트의 경우)를 확인할 수 있습니다. 예를 들어:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 40001 , 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
var endpoint = container . ToHostExposedEndpoint ( "5432/tcp" ) ;
Assert . AreEqual ( 40001 , endpoint . Port ) ;
}
여기서는 컨테이너 포트 5432를 호스트 포트 40001에 명시적으로 매핑합니다. container.ToHostExposedEndpoint(...)
사용에 유의하세요. 이는 Docker 컨테이너와 통신하기 위해 항상 작동하는 IP 및 포트를 확인하는 것입니다. 임의의 포트를 매핑하는 것도 가능합니다. 즉, Docker가 사용 가능한 포트를 선택하도록 할 수 있습니다. 예를 들어:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
var endpoint = container . ToHostExposedEndpoint ( "5432/tcp" ) ;
Assert . AreNotEqual ( 0 , endpoint . Port ) ;
}
여기서 유일한 차이점은 ExposePort(...)
사용하여 컨테이너를 구성할 때 하나의 인수만 사용된다는 것입니다. 그렇지 않은 경우에는 동일한 사용법이 적용되므로 코드에 대해 투명합니다.
예를 들어 연결을 시작하기 전에 특정 서비스가 언제 실행되고 있는지 알기 위해. 특정 포트가 열릴 때까지 기다릴 수도 있습니다. 예를 들어:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
Assert . AreEqual ( ServiceRunningState . Running , config . State . ToServiceState ( ) ) ;
}
위의 예에서는 컨테이너 포트 5432가 30초 이내에 열릴 때까지 기다립니다. 실패하면 예외가 발생하므로 컨테이너가 삭제되고 제거됩니다(Keep 컨테이너 등 구성이 없기 때문에).
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ , "127.0.0.1" )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
Assert . AreEqual ( ServiceRunningState . Running , config . State . ToServiceState ( ) ) ;
}
때로는 로컬 IP와 포트를 통해 컨테이너에 직접 도달하는 것이 불가능합니다. 예를 들어 컨테이너에는 루프백 인터페이스( 127.0.0.1 )에 노출된 포트가 있고 이것이 프로그램에서 컨테이너에 도달하는 유일한 방법입니다. 위의 예에서는 주소가 127.0.0.1 이 되도록 강제하지만 여전히 호스트 포트를 확인합니다. 기본적으로 FluentDocker는 컨테이너의 네트워크 검사를 사용하여 네트워크 구성을 결정합니다.
포트를 기다리는 것만으로는 충분하지 않은 경우도 있습니다. 때로는 컨테이너 프로세스가 기다리는 것이 훨씬 더 중요합니다. 따라서 Fluent API에는 프로세스 대기 메소드가 존재하고 컨테이너 객체에 대한 확장 메소드가 존재합니다. 예를 들어:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForProcess ( "postgres" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
Assert . AreEqual ( ServiceRunningState . Running , config . State . ToServiceState ( ) ) ;
}
위의 예에서 Build()
"postgres" 프로세스가 컨테이너 내에서 시작되면 제어를 반환합니다.
컨테이너를 활용하려면 때로는 컨테이너의 볼륨을 호스트에 마운트하거나 컨테이너에서 또는 컨테이너로 복사해야 하는 경우가 있습니다. 머신을 실행하는지 또는 기본적으로 Docker를 실행하는지에 따라 볼륨 매핑은 가상 머신에서 연결할 수 있어야 한다는 제약이 있습니다.
일반적인 사용 사례는 웹 서버가 도커 컨테이너에 콘텐츠를 제공하고 사용자가 호스트 파일 시스템의 파일을 편집하는 것입니다. 이러한 시나리오에서는 도커 컨테이너 볼륨을 호스트에 마운트해야 합니다. 예를 들어:
const string html = "<html><head>Hello World</head><body><h1>Hello world</h1></body></html>" ;
var hostPath = ( TemplateString ) @"${TEMP}/fluentdockertest/${RND}" ;
Directory . CreateDirectory ( hostPath ) ;
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "nginx:latest" )
. ExposePort ( 80 )
. Mount ( hostPath , "/usr/share/nginx/html" , MountType . ReadOnly )
. Build ( )
. Start ( )
. WaitForPort ( "80/tcp" , 30000 /*30s*/ ) )
{
File . WriteAllText ( Path . Combine ( hostPath , "hello.html" ) , html ) ;
var response = $ "http:// { container . ToHostExposedEndpoint ( "80/tcp" ) } /hello.html" . Wget ( ) ;
Assert . AreEqual ( html , response ) ;
}
위의 예에서는 nginx 컨테이너가 시작되고 '/usr/share/nginx/html'을 (임시 디렉토리의 임의) 호스트 경로에 마운트합니다. HTML 파일이 호스트 경로에 복사되고 nginx docker 컨테이너에 대한 HTTP 가져오기가 완료되면 동일한 파일이 제공됩니다.
때로는 컨테이너 간에 파일을 복사해야 하는 경우도 있습니다. 예를 들어 구성 파일을 복사하고 구성한 후 다시 복사합니다. 보다 일반적인 시나리오는 컨테이너가 시작되기 직전에 구성 파일을 컨테이너에 복사하는 것입니다. 다중 컨테이너 예제는 시작되기 직전에 nginx 구성 파일을 복사합니다. 따라서 이러한 간단한 작업을 위해 Dockerfile과 이미지를 수동으로 생성하는 것을 피할 수 있습니다. 대신 공식 또는 사용자 정의 이미지를 사용하고 구성을 복사한 후 실행하십시오.
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( )
. CopyFrom ( "/etc/conf.d" , fullPath ) )
{
var files = Directory . EnumerateFiles ( Path . Combine ( fullPath , "conf.d" ) ) . ToArray ( ) ;
Assert . IsTrue ( files . Any ( x => x . EndsWith ( "pg-restore" ) ) ) ;
Assert . IsTrue ( files . Any ( x => x . EndsWith ( "postgresql" ) ) ) ;
}
위의 예는 실행 중인 컨테이너의 호스트 경로(fullPath)에 디렉터리를 복사합니다. 여기에서는 확장 메서드를 사용하므로 유창한 API를 사용하지 않습니다(CopyFrom이 Start() 뒤에 있으므로). 시작하기 직전에 컨테이너에서 파일을 복사하려면 대신 Fluent API를 사용하세요.
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. CopyOnStart ( "/etc/conf.d" , fullPath )
. Build ( )
. Start ( ) )
{
}
아래 예에서는 파일이 컨테이너에 복사되는 훨씬 더 일반적인 시나리오를 보여줍니다. 이 예에서는 Fluent API 버전 대신 확장 메서드를 사용합니다. 복사 전과 복사 직후에 Diff 스냅샷을 생성합니다. 후자에는 hello.html이 있습니다.
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( )
. WaitForProcess ( "postgres" , 30000 /*30s*/ )
. Diff ( out before )
. CopyTo ( "/bin" , fullPath ) )
{
var after = container . Diff ( ) ;
Assert . IsFalse ( before . Any ( x => x . Item == "/bin/hello.html" ) ) ;
Assert . IsTrue ( after . Any ( x => x . Item == "/bin/hello.html" ) ) ;
}
IContainerService.Dispose()
에서 파일을 복사하는 것이 유용한 경우가 있습니다(컨테이너가 중지되기 직전). 따라서 이를 수행할 수 있도록 유창한 API가 존재합니다.
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. CopyOnDispose ( "/etc/conf.d" , fullPath )
. Build ( )
. Start ( ) )
{
}
컨테이너를 분석하기 위해 내보내기 확장 방식과 Fluent API 방식이 존재합니다. 가장 주목할만한 점은 IContainerService
가 삭제될 때 컨테이너를 내보낼 수 있다는 것입니다.
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExportOnDispose ( fullPath )
. Build ( )
. Start ( ) )
{
}
그러면 호스트(fullPath)에서 컨테이너 내보내기(tar 파일)가 생성됩니다. 분해(무게화)한 경우에는 대신 ExportExplodedOnDispose
메서드를 사용하세요. 물론 컨테이너의 확장 메서드를 사용하여 언제든지 컨테이너를 내보낼 수 있습니다.
단위 테스트와 관련하여 유용한 방법은 어떤 이유로 단위 테스트가 실패할 때 컨테이너 상태를 내보내는 것입니다. 따라서 특정 Lambda 조건이 충족될 때 내보내는 Fluent API가 존재합니다. 예를 들어:
var failure = false ;
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExportOnDispose ( fullPath , svc => failure )
. Build ( )
. Start ( ) )
{
failure = true ;
}
이 코드 조각은 실패 변수가 true로 설정되고 ExportOnDispose
식에 사용되므로 using 문이 컨테이너를 삭제할 때 컨테이너를 내보냅니다.
모든 서비스는 후크를 통해 확장될 수 있습니다. 서비스 상태가 Removing
인 경우 람다를 실행하도록 서비스 상태가 설정되면 서비스 상태가 설정될 때 서비스의 ExportOnDispose(path, lambda)
후크를 설치합니다. 즉석에서 후크를 설치하고 제거하는 것이 가능합니다. 동일한 ServiceRunningState
사용하여 동일한 서비스 인스턴스에 여러 후크가 등록된 경우 설치 순서대로 실행됩니다.
Starting
과 같이 서비스에서 상태가 설정(또는 실행)되려고 할 때 무언가를 실행하려는 경우 후크가 특히 좋습니다. Fluent API는 파일 복사, 내보내기 등과 같은 일부 상황에서 이를 활용합니다.
FluentDocker는 모든 docker 네트워크 명령을 지원합니다. NetworkRow
에 정의된 모든 네트워크와 일부 간단한 매개변수를 검색하는 _docker.NetworkLs()
를 통해 네트워크를 검색할 수 있습니다. 또한 _docker.NetworkInspect(network:"networkId")
를 통해 네트워크에 대한 더 자세한 정보(예: 네트워크에 있는 컨테이너 및 Ipam 구성)를 얻기 위해 검사할 수도 있습니다.
새 네트워크를 생성하려면 _docker.NetworkCreate("name_of_network")
를 사용하세요. 오버레이 네트워크 생성이나 IPam 구성 변경 등 모든 것을 사용자 정의할 수 있는 NetworkCreateParams
제공하는 것도 가능합니다. 네트워크를 삭제하려면 _docker.NetworkRm(network:"networkId")
을 사용하세요.
네트워크에 연결된 컨테이너가 있으면 네트워크는 삭제되지 않습니다.
네트워크가 생성되면 _docker.NetworkConnect("containerId","networkId")
를 사용하여 하나 이상의 컨테이너를 네트워크에 넣을 수 있습니다. 컨테이너는 동시에 여러 네트워크에 있을 수 있으므로 격리된 네트워크 간에 요청을 프록시할 수 있습니다. 네트워크에서 컨테이너의 연결을 끊으려면 _docker.NetworkDisconnect("containerId","networkId")
를 수행하면 됩니다.
다음 샘플은 컨테이너를 실행하고, 새 네트워크를 생성하고, 실행 중인 컨테이너를 네트워크에 연결합니다. 그런 다음 컨테이너 연결을 끊고 삭제한 후 네트워크를 삭제합니다.
var cmd = _docker . Run ( "postgres:9.6-alpine" , new ContainerCreateParams
{
PortMappings = new [ ] { "40001:5432" } ,
Environment = new [ ] { "POSTGRES_PASSWORD=mysecretpassword" }
} , _certificates ) ;
var container = cmd . Data ;
var network = string . Empty ;
var created = _docker . NetworkCreate ( "test-network" ) ;
if ( created . Success )
network = created . Data [ 0 ] ;
_docker . NetworkConnect ( container , network ) ;
// Container is now running and has address in the newly created 'test-network'
_docker . NetworkDisconnect ( container , id , true /*force*/ ) ;
_docker . RemoveContainer ( container , true , true ) ;
// Now it is possible to delete the network since it has been disconnected from the network
_docker . NetworkRm ( network : network ) ;
유창한 빌더를 사용하여 새로운 도커 네트워크를 구축하거나 기존 도커 네트워크를 재사용하는 것도 가능합니다. 그런 다음 컨테이너를 구축하는 동안 이를 참조할 수 있습니다. 둘 이상의 Docker 네트워크를 구축하고 한 번에 둘 이상의 네트워크에 컨테이너를 연결할 수 있습니다.
using ( var nw = new Builder ( ) . UseNetwork ( "test-network" ) )
{
using (
var container =
new DockerBuilder ( )
. WithImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExposePorts ( "5432" )
. UseNetwork ( nw )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( ) )
{
container . Create ( ) . Start ( ) ;
}
}
위의 코드 조각은 test-network 라는 새 네트워크를 만든 다음 test-network 에 연결된 컨테이너를 만듭니다. Dispose()
nw 에서 호출되면 네트워크가 제거됩니다. UseIpV4
또는 UseIpV6
통해 네트워크 내에서 고정 IP 컨테이너 할당을 수행하는 것도 가능합니다. 예를 들어:
using ( var nw = Fd . UseNetwork ( "unit-test-nw" )
. UseSubnet ( "10.18.0.0/16" ) . Build ( ) )
{
using (
var container =
Fd . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExposePort ( 5432 )
. UseNetwork ( nw )
. UseIpV4 ( "10.18.0.22" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var ip = container . GetConfiguration ( ) . NetworkSettings . Networks [ "unit-test-nw" ] . IPAddress ;
Assert . AreEqual ( "10.18.0.22" , ip ) ;
}
}
위의 예에서는 IP 범위 가 10.18.0.0/16 인 새로운 네트워크 unit-test-nw를 만듭니다. 새 용기에 사용된 것입니다. 컨테이너의 IP는 10.18.0.22 로 설정되고 UseIpV4
명령으로 인해 고정됩니다.
FluentDocker는 명령과 Fluent API 모두에서 도커 볼륨 관리를 지원합니다. 따라서 폐기, 재사용, 사용할 드라이버 등 컨테이너에 사용되는 용량을 완전히 제어할 수 있습니다.
var volume = _docker . VolumeCreate ( "test-volume" , "local" , opts : {
{ "type" , "nfs" } ,
{ "o=addr" , "192.168.1.1,rw" } ,
{ "device" , ":/path/to/dir" }
} ) ;
var cfg = _docker . VolumeInspect ( _certificates , "test-volume" ) ;
_docker . VolumeRm ( force : true , id : "test-volume" ) ;
위의 코드 조각은 NFS 유형의 test-volume 이라는 이름으로 새 볼륨을 생성합니다. 그런 다음 방금 생성된 볼륨을 검사하고 마지막으로 볼륨을 강제 삭제합니다.
Fluent API를 사용하여 볼륨을 생성하거나 사용하는 것도 가능합니다. 그런 다음 컨테이너를 만들 때 사용할 수 있습니다. 이는 볼륨 생성이 특별하거나 수명을 제어해야 할 때 특히 유용합니다.
using ( var vol = new Builder ( ) . UseVolume ( "test-volume" ) . RemoveOnDispose ( ) . Build ( ) )
{
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. MountVolume ( vol , "/var/lib/postgresql/data" , MountType . ReadWrite )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( ) ;
Assert . AreEqual ( 1 , config . Mounts . Length ) ;
Assert . AreEqual ( "test-volume" , config . Mounts [ 0 ] . Name ) ;
}
}
위 샘플은 test-volume 이라는 새 볼륨을 생성하며 IVolumeService
에서 Dispose()
호출될 때 삭제되도록 예약됩니다. 컨테이너가 생성되고 새로 생성된 볼륨을 읽기/쓰기 액세스 모드로 /var/lib/postgresql/data 에 마운트합니다. 컨테이너는 볼륨의 using
문의 범위 내에 있으므로 수명은 전체 컨테이너 수명에 걸쳐 삭제됩니다.
FluentDocker는 docker 이벤트 메커니즘에 연결하여 보내는 이벤트를 수신하는 기능을 지원합니다.
using ( var events = Fd . Native ( ) . Events ( ) )
{
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
FdEvent e ;
while ( ( e = events . TryRead ( 3000 ) ) != null )
{
if ( e . Type == EventType . Container && e . Action == EventAction . Start )
break ;
}
}
}
이벤트 리스너는 전역적이며 많은 EventAction
유형을 처리할 수 있습니다.
예를 들어
작업에 따라 EventAction.Kill
의 ContainerKillEvent
와 같이 이벤트 유형이 다를 수 있습니다. 모든 이벤트는 FdEvent
에서 파생됩니다. 이는 모든 공유 속성이 기본 이벤트에 있고 명시적인 속성이 파생 이벤트에 있음을 의미합니다.
예를 들어 'ContainerKillEvent'에는 다음 속성이 포함되어 있습니다.
public sealed class ContainerKillActor : EventActor
{
/// <summary>
/// The image name and label such as "alpine:latest".
/// </summary>
public string Image { get ; set ; }
/// <summary>
/// Name of the container.
/// </summary>
public string Name { get ; set ; }
/// <summary>
/// The signal that the container has been signalled.
/// </summary>
public string Signal { get ; set ; }
}
이 이벤트 루프는 이벤트를 선택하고 인스턴스화된 IService
인스턴스를 구동하는 데 사용될 수 있습니다. 또는 네트워크가 추가되거나 삭제되는 등의 상황에 대응해야 하는 경우.
전체 프레임워크에서는 System.Diagnostics.Debugger.Log
를 사용하여 자세한 로깅을 사용합니다. .net 코어의 경우 표준 Microsoft.Extensions.Logging.ILog
사용하여 로그합니다. 둘 다 Ductus.FluentDocker 범주를 사용하므로 로깅에 참여하거나 다른 로깅 대상을 구성하도록 구성될 수 있습니다.
.net 코어에서는 애플리케이션 구성 파일에 로깅 세그먼트를 제공할 수 있습니다.
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Ductus.FluentDocker": "None"
}
}
}
자세한 내용은 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1을 확인하세요. 전체 프레임워크의 경우 https://docs.microsoft.com/en-us/dotnet/framework/wcf/diagnostics/tracing/configuring-tracing에 설명된 전체 프레임워크에 대한 appconfig에 필요한 XML 을 확인하세요.
( Ductus.FluentDocker.Services
) Logging.Enabled()
또는 Logging.Disabled()
통해 로깅을 비활성화/활성화하는 빠른 방법이 있습니다. 그러면 강제로 로깅이 활성화/비활성화됩니다.
FluentDocker 의 기본 메커니즘을 재정의하여 WaitForPort
등 클라이언트 관점에서 컨테이너 IP를 확인하는 것이 가능합니다. 이는 ContainerBuilder
기반으로 재정의될 수 있습니다.
아래 샘플은 기본 동작을 재정의합니다. null
반환하면 기본 확인자가 시작됩니다.
using (
var container =
Fd . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExposePort ( 5432 )
. UseCustomResolver ( (
ports , portAndProto , dockerUri ) =>
{
if ( null == ports || string . IsNullOrEmpty ( portAndProto ) )
return null ;
if ( ! ports . TryGetValue ( portAndProto , out var endpoints ) )
return null ;
if ( null == endpoints || endpoints . Length == 0 )
return null ;
if ( CommandExtensions . IsNative ( ) )
return endpoints [ 0 ] ;
if ( CommandExtensions . IsEmulatedNative ( ) )
return CommandExtensions . IsDockerDnsAvailable ( )
? new IPEndPoint ( CommandExtensions . EmulatedNativeAddress ( ) , endpoints [ 0 ] . Port )
: new IPEndPoint ( IPAddress . Loopback , endpoints [ 0 ] . Port ) ;
if ( Equals ( endpoints [ 0 ] . Address , IPAddress . Any ) && null != dockerUri )
return new IPEndPoint ( IPAddress . Parse ( dockerUri . Host ) , endpoints [ 0 ] . Port ) ;
return endpoints [ 0 ] ;
} )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var state = container . GetConfiguration ( true /*force*/ ) . State . ToServiceState ( ) ;
Assert . AreEqual ( ServiceRunningState . Running , state ) ;
}
FluentAPI를 사용하여 docker-machine을 사용하지 않고 원격 docker 데몬과 통신하는 것은 제한적으로 지원됩니다. DockerHostService
인스턴스를 수동으로 생성하거나 HostBuilder
에서 FromUri
사용하면 됩니다.
using ( var container = Fd . UseHost ( ) .
FromUri ( Settings . DockerUri , isWindowsHost : true ) .
UseContainer ( ) .
Build ( ) )
{
}
위 샘플은 설정에서 사용자 지정 DockerUri
에 연결되며 Windows 컨테이너 Docker 데몬입니다.
DockerUri
를 사용하여 IHostService
생성하는 FromUri
입니다. 이 URI는 임의적입니다. 또한 다른 속성도 지원합니다( 아래 참조 ). public HostBuilder FromUri (
DockerUri uri ,
string name = null ,
bool isNative = true ,
bool stopWhenDisposed = false ,
bool isWindowsHost = false ,
string certificatePath = null ) { /*...*/ }
모든 매개변수에 대해 "합리적인" 기본값을 사용합니다. 대부분의 경우 uri 로 충분합니다. 예를 들어, 인증서 경로를 제공하지 않으면 DOCKER_CERT_PATH 환경에서 인증서를 가져오려고 시도합니다. 환경에서 찾을 수 없는 경우 기본값은 없음입니다.
IHostService
구현을 사용하는 UseHost
입니다. 라이브러리는 기존 Compose 파일을 사용하여 서비스를 렌더링하고 수명을 관리할 수 있도록 docker-compose를 지원합니다.
다음 샘플에는 MySql 및 WordPress를 실행하는 작성 파일이 있습니다. 따라서 단일 작성 서비스에는 그 아래에 두 개의 컨테이너 서비스가 있습니다. 기본적으로 Dispose()
호출되면 서비스를 중지하고 정리합니다. 이는 유창한 구성에서 KeepContainers()
로 재정의될 수 있습니다.
version : ' 3.3 '
services :
db :
image : mysql:5.7
volumes :
- db_data:/var/lib/mysql
restart : always
environment :
MYSQL_ROOT_PASSWORD : somewordpress
MYSQL_DATABASE : wordpress
MYSQL_USER : wordpress
MYSQL_PASSWORD : wordpress
wordpress :
depends_on :
- db
image : wordpress:latest
ports :
- " 8000:80 "
restart : always
environment :
WORDPRESS_DB_HOST : db:3306
WORDPRESS_DB_USER : wordpress
WORDPRESS_DB_PASSWORD : wordpress
volumes :
db_data :
위 파일은 전체 서비스를 연결하기 위한 docker-compose 파일입니다.
var file = Path . Combine ( Directory . GetCurrentDirectory ( ) ,
( TemplateString ) "Resources/ComposeTests/WordPress/docker-compose.yml" ) ;
using ( var svc = new Builder ( )
. UseContainer ( )
. UseCompose ( )
. FromFile ( file )
. RemoveOrphans ( )
. Build ( ) . Start ( ) )
{
var installPage = await "http://localhost:8000/wp-admin/install.php" . Wget ( ) ;
Assert . IsTrue ( installPage . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ) ;
Assert . AreEqual ( 1 , svc . Hosts . Count ) ;
Assert . AreEqual ( 2 , svc . Containers . Count ) ;
Assert . AreEqual ( 2 , svc . Images . Count ) ;
Assert . AreEqual ( 5 , svc . Services . Count ) ;
}
위의 코드 조각은 docker-compose 서비스를 원활하게 구성하고 설치 페이지를 호출하여 WordPress가 실제로 작동하는지 확인합니다.
복사, 내보내기, 대기 작업 등 단일 컨테이너가 지원하는 모든 작업을 수행하는 것도 가능합니다. 예를 들어:
var file = Path . Combine ( Directory . GetCurrentDirectory ( ) ,
( TemplateString ) "Resources/ComposeTests/WordPress/docker-compose.yml" ) ;
// @formatter:off
using ( new Builder ( )
. UseContainer ( )
. UseCompose ( )
. FromFile ( file )
. RemoveOrphans ( )
. WaitForHttp ( "wordpress" , "http://localhost:8000/wp-admin/install.php" , continuation : ( resp , cnt ) =>
resp . Body . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ? 0 : 500 )
. Build ( ) . Start ( ) )
// @formatter:on
{
// Since we have waited - this shall now always work.
var installPage = await "http://localhost:8000/wp-admin/install.php" . Wget ( ) ;
Assert . IsTrue ( installPage . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ) ;
}
위의 코드 조각은 wordpress docker compose 프로젝트를 실행하고 URL http://localhost:8000/wp-admin/install.php를 확인하여 본문에 특정 값을 반환합니다(이 경우 "https://wordpress.org"). /"). 그렇지 않은 경우 500을 반환하고 WaitForHttp
함수는 다시 호출하기 전에 500밀리초를 기다립니다. 이는 모든 사용자 정의 람다에서도 작동합니다. 대신 WaitFor
사용하세요. 따라서 사용 범위 내에서 계속하기 전에 데이터베이스를 쿼리하는 것이 가능합니다.
Linux 및 Mac 사용자의 경우 소켓을 인증하는 방법에 대한 몇 가지 옵션이 있습니다. FluentDocker는 sudo 없음, 비밀번호 없는 sudo (/etc/sudoer에서 사용자가 NOPASSWD로 추가됨) 또는 비밀번호가 있는 sudo를 지원합니다. 기본값은 FluentDocker가 sudo 없이 대화할 수 있을 것으로 예상하는 것입니다. 옵션은 전역이지만 런타임에 변경될 수 있습니다.
SudoMechanism . None . SetSudo ( ) ; // This is the default
SudoMechanism . Password . SetSudo ( "<my-sudo-password>" ) ;
SudoMechanism . NoPassword . SetSudo ( ) ;
docker 데몬과 통신하기 위해 sudo를 끄려면 docker 튜토리얼을 따라 사용자를 docker 그룹에 추가하는 마지막 단계를 수행하면 됩니다.
FluentDocker는 원격 Docker 데몬에 대한 연결을 지원합니다. 유창한 API는 다음을 지원합니다.
new Builder ( ) . UseHost ( ) . UseMachine ( ) . WithName ( "remote-daemon" )
여기서는 docker-machine 레지스트리에 이미 사전 설정된 항목이 필요합니다. 원격 데몬에 연결하기 위해 SSH 기반 docker-machine 레지스트리 전체를 정의하는 것도 가능합니다.
using (
var container =
new Builder ( ) . UseHost ( )
. UseSsh ( "192.168.1.27" ) . WithName ( "remote-daemon" )
. WithSshUser ( "solo" ) . WithSshKeyPath ( "${E_LOCALAPPDATA}/lxss/home/martoffi/.ssh/id_rsa" )
. UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( ) )
{
Assert . AreEqual ( ServiceRunningState . Stopped , container . State ) ;
}
이 예에서는 IP 주소가 192.168.1.27 이고 SSH 사용자가 solo인 SSH를 사용하는 Remote-daemon 이라는 새 docker-machine 레지스트리 항목을 생성합니다. Remote-daemon 이라는 항목이 이미 발견된 경우 이 항목을 다시 사용합니다. 그런 다음 원격 데몬에 대한 올바른 인증서와 URL이 포함된 IHostService를 가져옵니다. 따라서 원격 데몬에 docker 컨테이너를 생성하는 것이 가능하며, 이 경우 postgres 이미지가 됩니다. 컨테이너를 폐기할 때 평소와 같이 원격 도커에서 삭제합니다. IHostService는 연결을 인증하기 위해 필요한 모든 인증서를 선택해야 합니다.
위의 예에서는 이 docker-machine 레지스트리 항목을 생성합니다.
C:Usersmartoffi>docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
remote-daemon * generic Running tcp://192.168.1.27:2376 v18.06.1-ce
UseSsh(...)
사용하려면 비밀번호가 없는 SSH 터널을 설정해야 합니다. 또한 터널을 사용하는 사용자는 docker 데몬에 액세스할 수 있어야 합니다.
SSH 터널을 설정하고 사용자가 docker 데몬에 액세스할 수 있는지 확인하는 방법에 대한 이 튜토리얼을 따르십시오.
기본적으로 ssh-keygen -t rsa
사용하여 SSH 터널에 사용할 새 rsa 키를 만든 다음 ssh-copy-id {username}@{host}
를 통해 원격 호스트에 복사합니다.
두 번째 튜토리얼에 지정된 대로 /etc/sudoers를 편집합니다.
이 작업이 완료되면 이제 위에 지정된 일반 드라이버 또는 Fluent API를 통해 원격 Docker 데몬에 액세스할 수 있습니다. 예제에 지정된 것과 동일한 작업을 수동으로 수행하려면 다음과 같습니다.
C:Usersmartoffi>docker-machine.exe create --driver generic --generic-ip-address=192.168.1.27 --generic-ssh-key="%localappdata%/lxss/home/martoffi/.ssh/id_rsa" --generic-ssh-user=solo remote-daemon
Running pre-create checks...
Creating machine...
(remote-daemon) Importing SSH key...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with ubuntu(systemd)...
Installing Docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine.exe env remote-daemon
이제 레지스트리 항목이 생성되었으므로 터미널 도커에 대한 환경을 설정할 수 있습니다.
C:Usersmartoffi>docker-machine.exe env remote-daemon
SET DOCKER_TLS_VERIFY=1
SET DOCKER_HOST=tcp://192.168.1.24:2376
SET DOCKER_CERT_PATH=C:Usersmartoffi.dockermachinemachinesremote-daemon
SET DOCKER_MACHINE_NAME=remote-daemon
SET COMPOSE_CONVERT_WINDOWS_PATHS=true
REM Run this command to configure your shell:
REM @FOR /f "tokens=*" %i IN ('docker-machine.exe env remote-daemon') DO @%i
docker 클라이언트가 원격 docker 데몬을 사용하도록 하려면 이를 실행합니다.
@FOR /f "tokens=*" %i IN ('docker-machine.exe env remote-daemon') DO @%i
docker
바이너리를 사용하는 모든 명령은 이제 원격 docker 데몬에서 실행됩니다.
Hyper-V Docker 시스템을 통해 시스템을 생성하고 쿼리할 때 Hyper-V는 표준 사용자 모드에서 API 호출에 응답하지 않으므로 프로세스를 높여야 합니다.
이러한 활동을 기반으로 컨테이너의 상태를 보고하도록 Docker 컨테이너에 대한 상태 확인을 지정할 수 있습니다. 다음 예에서는 컨테이너가 종료되었는지 여부를 확인하는 상태 확인을 사용합니다. 상태 확인이 어떤 상태를 보고하는지 구성을 확인(강제 새로 고침 확인)할 수 있습니다.
using (
var container =
Fd . UseContainer ( )
. UseImage ( "postgres:latest" , force : true )
. HealthCheck ( "exit" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
AreEqual ( HealthState . Starting , config . State . Health . Status ) ;
}
Fluent API 및 ContainerCreateParams
통해 도커 컨테이너에 ulimit를 지정하여 열린 파일 수 등을 제한할 수 있습니다. 예를 들어 열린 파일 수를 2048(소프트 및 하드 모두)로 제한할 때 Fluent API를 사용하면 다음과 같이 보일 수 있습니다. .
using (
var container =
Fd . UseContainer ( )
. UseImage ( "postgres:latest" , force : true )
. UseUlimit ( Ulimit . NoFile , 2048 , 2048 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
// Do stuff
}
이 저장소에는 세 개의 너겟 패키지가 포함되어 있습니다. 하나는 유창한 액세스용, 하나는 ms-test 기본 클래스용, 다른 하나는 테스트 중에 사용할 xunit 기본 클래스용입니다. 예를 들어 단위 테스트에서는 postgres 컨테이너를 실행하고 db가 부팅될 때까지 기다릴 수 있습니다.
public class PostgresXUnitTests : IClassFixture < PostgresTestBase >
{
[ Fact ]
public void Test ( )
{
// We now have a running postgres
// and a valid connection string to use.
}
}
using (
var container =
new DockerBuilder ( )
. WithImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExposePorts ( "5432" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( ) )
{
container . Create ( ) . Start ( ) ;
}
예를 들어 postgres 테스트 베이스와 같은 추상 기본 클래스를 재사용하여 컨테이너에 대한 단위 테스트를 단순화하고 깔끔하게 만드는 것도 가능합니다.
[ TestClass ]
public class PostgresMsTests : PostgresTestBase
{
[ TestMethod ]
public void Test ( )
{
// We now have a running postgres
// and a valid connection string to use.
}
}
FluentDockerTestBase
를 사용하면 사용자 정의 Docker 지원 테스트를 쉽게 수행할 수 있도록 간단한 재정의가 가능합니다. 테스트 클래스를 만들고 FluentDockerTestBase
에서 파생시킨 다음 적합한 메서드를 재정의하세요. 예를 들어.
protected override DockerBuilder Build ( )
{
return new DockerBuilder ( )
. WithImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( $ "POSTGRES_PASSWORD= { PostgresPassword } " )
. ExposePorts ( "5432" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ ) ;
}
이렇게 하면 docker 이미지 postgres:latest를 사용하여 빌더가 생성되고 하나의 환경 문자열이 설정됩니다. 또한 postgres db 포트 5432가 호스트에 노출되어 컨테이너 내의 db에 연결할 수 있습니다. 마지막으로 포트 5432를 기다립니다. 이렇게 하면 db가 실행 중이고 제대로 부팅되었는지 확인할 수 있습니다. 이 예에서 시간 초과가 30초로 설정된 경우 예외가 발생하고 컨테이너가 중지되고 제거됩니다. 호스트 포트는 5432가 아닙니다! Container.GetHostPort("5432/tcp")
사용하여 호스트 포트를 가져옵니다. 호스트 IP는 Container.Host
속성으로 검색할 수 있으므로 컨테이너에서 앱과 통신할 때 사용됩니다.
컨테이너를 성공적으로 가져오고 생성하고 시작한 경우 콜백이 필요한 경우입니다.
protected override void OnContainerInitialized ( )
{
ConnectionString = string . Format ( PostgresConnectionString , Container . Host ,
Container . GetHostPort ( "5432/tcp" ) , PostgresUser ,
PostgresPassword , PostgresDb ) ;
}
이 예에서는 새로 가동된 컨테이너 내의 postgresql db에 대한 적절한 연결 문자열을 렌더링합니다. 이는 Npgsql, EF7, NHibernate, Marten 또는 기타 호환 도구를 사용하여 연결하는 데 사용할 수 있습니다. Docker 저장소에서 이미지를 가져오거나 컨테이너를 생성/시작할 수 없는 경우 이 메서드는 호출되지 않습니다.
종료 전 컨테이너 후크를 재정의하려는 경우.
protected virtual void OnContainerTearDown ( )
{
// Do stuff before container is shut down.
}
컨테이너 이름이 지정되지 않은 경우 제대로 삭제되지 않으면 도커 컨테이너가 계속 실행되므로 수동으로 제거해야 합니다. 테스트에서 여러 컨테이너를 실행해야 할 수 있으므로 이는 버그가 아닌 기능입니다. DockerContainer
클래스는 컨테이너의 인스턴스 ID를 관리하므로 해당 컨테이너와만 상호작용하고 다른 컨테이너와는 상호작용하지 않습니다.
새 컨테이너를 생성/시작할 때 컨테이너 이미지가 이미 존재하는지 먼저 로컬 저장소를 확인하고, 없으면 다운로드합니다. 시간이 좀 걸릴 수 있으며 활성화된 경우 디버그 로그만 있으므로 다운로드 프로세스를 모니터링할 수 있습니다.
처리되지 않은 예외가 발생하고 FailFast 즉 애플리케이션이 빠르게 종료되면 finally
절을 호출하지 않습니다 . 따라서 using
문 내에서 실패한 WaitForPort
컨테이너 서비스를 삭제 하지 않습니다 . 따라서 컨테이너는 계속 실행 중입니다. 이 문제를 해결하려면 전역 try...catch를 사용하거나 로컬에서 주입하세요.
try
{
using ( var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=postgres" )
. WaitForPort ( "5777/tcp" , 10000 ) // Fail here since 5777 is not valid port
. Build ( ) )
{
container . Start ( ) ; // FluentDockerException is thrown here since WaitForPort is executed
}
} catch { throw ; }
그러나 이는 WaitForPort
에서 발생한 FluentDockerException
으로 인해 애플리케이션 종료가 완료된 경우에만 해당됩니다. 그렇지 않으면 컨테이너가 적절하게 삭제되므로 try...catch
필요하지 않습니다.
이 문제는 Fd.Build
함수를 사용하여 해결할 수도 있습니다(자세한 내용은 빌더 확장 사용 참조 ).
Fd
클래스는 단일 컨테이너와 구성된 컨테이너를 빌드하고 실행하기 위한 편의 메서드를 제공하는 정적 클래스 입니다. 컨테이너를 구축하려면 다음을 사용하세요.
var build = Fd . Build ( c => c . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , TimeSpan . FromSeconds ( 30 ) ) ) ;
// This is the equivalent of
var build = new Builder ( ) . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , TimeSpan . FromSeconds ( 30 ) ) ;
그런 다음 예외가 포착되지 않은 경우에도 삭제가 보장되는 안전한 using
절 내에서 컨테이너를 시작하는 데 사용할 수 있습니다.
build . Container ( svc =>
{
var config = svc . GetConfiguration ( ) ;
// Do stuff...
} ) ;
Container
메서드가 실행된 후 이 경우 컨테이너는 중지되고 제거됩니다. 이는 다음과 같습니다.
// This is equivalent of
try
{
using ( var svc = build . Build ( ) )
{
svc . Start ( ) ;
var config = svc . GetConfiguration ( ) ;
// Do stuff...
}
}
catch
{
Log ( .. . ) ;
throw ;
}
다음을 통해 빌더와 실행을 결합하는 것도 가능합니다.
Fd . Container ( c => c . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , TimeSpan . FromSeconds ( 30 ) ) ,
svc =>
{
var config = svc . GetConfiguration ( ) ;
// Do stuff...
} ) ;
위의 예에서는 컨테이너를 빌드하고, 시작하고, 중지하고, 마지막으로 컨테이너를 삭제합니다. Exception
이 발생하더라도 Disposed
가 발생합니다. 물론 container
와 마찬가지로 composite
확장 방법을 사용하여 구성된 컨테이너를 사용하는 것도 가능합니다.