ReenterantReadWriteLock

멀티쓰레드 환경에서 메소드 단위가 아닌 그 안에 임의의 코드블럭 단위로 읽기는 여러 쓰레드가, 쓰기는 하나의 쓰레드 만 가능하게 하고 싶을때 ReenterantReadWriteLock 을 이용하여 구현할 수 있다.

예를 들어 어떤 데이터를 Map 에다가 마구마구 저장하다가 특정 시점에 Map 담긴 데이터를 외부로 전송하고 다시 Map 을 초기화 할 때 초기화 중간에 데이터가 들어 와 버리면 데이터가 유실 될 수 있기 때문에 Map 을 초기화할 시점에는 하나의 스레드만 실행히 가능하도록 Lock 을 걸 필요가 있다.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReentrantReadWriteLockTest {

    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    public void read() {

        readLock.lock();

        try {

            // map 을 가지고 쿵짝쿵짝.

        } finally {
            readLock.unlock();
        }
    }

    public void write() {

        writeLock.lock();

        try {

            // map 초기화

        } finally {
            writeLock.unlock();
        }
    }
}

write 메소드의 writeLock.lock() 이 걸리기 전 까지는 여러 쓰레드가 read 메소드를 실행이 가능하다가 write lock 이 걸리게 되면 readLock.lock() 부분에서 blocking 을 당하게 될 것 이다. 그 후 writeLock.unlock() 으로 write lock 이 해제가 되면 다시 blocking 되었던 쓰레드들이 정상적으로 실행되게 된다.

Advertisements

Extract DDL in Spring Boot + Spring Data JPA

Spring Boot + Spring Data JPA 사용 시 Entity 의 DDL를 추출하고 싶을 때 아래와 같이 하였다.

import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl;
import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;

import javax.persistence.spi.PersistenceUnitInfo;
import java.util.Properties;

public class JPASchemaExtractor  {

    @Autowired
    private LocalContainerEntityManagerFactoryBean lcemfb;

    public void extract_table_schema() throws Exception {

        final Properties prop = new Properties();
        prop.put(AvailableSettings.DIALECT, "org.hibernate.dialect.MySQL5InnoDBDialect");

        final PersistenceUnitInfo info = lcemfb.getPersistenceUnitInfo();
        final PersistenceUnitInfoDescriptor puid = new PersistenceUnitInfoDescriptor(info);
        final EntityManagerFactoryBuilderImpl emfbi = new EntityManagerFactoryBuilderImpl(puid, prop);

        final ServiceRegistry serviceRegistry = emfbi.buildServiceRegistry();
        final Configuration configuration = emfbi.buildHibernateConfiguration(serviceRegistry);

        final SchemaExport schemaExport = new SchemaExport(serviceRegistry, configuration);
        schemaExport.setDelimiter(";");

        schemaExport.execute(true, false, false, true);
    }
}

기본적으로는 Consol 에 DDL 이 남겨지고, schemaExport 에 output 파일을 지정하면 파일로 저장할 수 있다.

ThreadLocal, InheritableThreadLocal

parent thread 에서 child thread 를 생성할 때 ThreadLocal 을 사용한다면 기본적으로 parent 의 ThreadLocal 데이터는 child 에게 ‘상속하지 않는다’.

ThreadLocal 대신 InheritableThreadLocal 을 사용하면 생성되는 child 는 parent 의 ThreadLocal 데이터를 이어받게 된다.

이때, child 의 ThreadLocal 데이터를 변경할 경우 parent 의 ThreadLocal 데이터도 함께 영향을 받게 된다.

[Maven] Maven Options — pl, am, amd, B

-pl,--projects <arg> Build specified reactor projects instead of all projects

Multi-Project 구조에서 모든 프로젝트가 아닌 특정 프로젝트를 대상으로 mvn 을 수행하고 싶을 경우 ‘-pl’ 옵션을 사용하면 된다.

-am,--also-makeIf project list is specified, also build projects required by the list

‘-am’ 옵션은 ‘-pl’ 에 명시한 프로젝트를 참조하는 프로젝트 들도 같이 빌드 한다.

-amd,--also-make-dependentsIf project list is specified, also build projects that depend on projects on the list

‘-amd’ 옵션은 ‘-pl’ 에 명시한 프로젝트가 참조하고 있는 프로젝트 들을 같이 빌드 한다.

-B,--batch-modeRun in non-interactive (batch) mode

‘-B’ 옵션은 비-대화형 모드로 mvn 실행 중 대화형 입력 모드가 나오면 기본값으로 처리하고 넘어가도록 해준다.

예를 들어, A -> B -> C-> D-> E 와 같이 참조하고있는 프로젝트 에서

  • ‘-pl C -am’ 인 경우 C-D-E 프로젝트가 빌드
  • ‘-pl C -amd’ 인 경우 A-B-C 프로젝트가 빌드

Enum을 이용한 Logback의 프로그램적 설정 방법과 관리

Logback 사용할 시 기본적으로 설정을 아래와 같이 logback.xml 에서 선언적으로 사용을 하게 된다.

<configuration> 
  <appender name=”FILE” class=”xxx.RollingFileAppender”>
    <rollingPolicy class=”xxx.TimeBasedRollingPolicy”>
      <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory> 
    </rollingPolicy>
    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger{35} — %msg%n
      </pattern>
    </encoder>
  </appender>
 
  <root level=”DEBUG”>
    <appender-ref ref=”FILE” />
  </root>
</configuration>

xml를 이용한 선언적 방식이 설정을 분리하고 관리하는데에 장점이 있지만 설정하는 양이 많아질수록 xml을 크기는 점점 증가되어 오히려 관리에 대한 부담과 가독성을 떨어트리는 시점이 올 수 가 있다. 또한, 패스 경로나 파일명만 다르고 나머지는 동일한 설정들이 반복되는 현상도 발견할 수 있을 것이다.

이 때 Logback 설정을 선언적 방식이 아닌 소스 코드내에서의 프로그램적 방식을 이용하여 중복되는 설정을 간소화 할 수 있고, Enum 클래스와 약간의 컨벤션을 이용하여 Logback 설정을 여러 파일에 분산시키지 않고 한 곳에서 관리할 수 도 있다.

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import com.google.common.base.Joiner;
import org.slf4j.LoggerFactory;

public enum ProgrammticLoggers {

  auth, // 인증 관련 로그
  error // 에러 관련 로그
  ;

  private final Joiner joiner = Joiner.on("/");

  private final LoggerContext loggerContext 
             = (LoggerContext) LoggerFactory.getILoggerFactory();

  private final String PATTERN 
             = "%d{yyyy/MM/dd} %-5p %c{0}\t[ip=%X{IP}] %m%n";
 
  private final String LOG_ROOTPATH = "/logback/logs";

  private ProgrammticLoggers() {

    // 로그 파일 경로 생성
    final String filepath 
               = joiner.join(LOG_ROOTPATH, this.name()) + ".log";

    // Appender 설정
    RollingFileAppender rfAppender = new RollingFileAppender();
    rfAppender.setContext(loggerContext);
    rfAppender.setFile(filepath);

    // Policy 설정
    TimeBasedRollingPolicy rollingPolicy 
                           = new TimeBasedRollingPolicy();
    rollingPolicy.setContext(loggerContext);
    rollingPolicy.setParent(rfAppender);
    rollingPolicy.setFileNamePattern(filepath + ".%d{yyMMdd}.gz");
    rollingPolicy.setMaxHistory(30);
    rollingPolicy.start();

    // Encoder 설정
    PatternLayoutEncoder encoder = new PatternLayoutEncoder();
    encoder.setContext(loggerContext);
    encoder.setPattern(PATTERN);
    encoder.start();

    // Appender에 Policy와 Encoder를 연결
    rfAppender.setEncoder(encoder);
    rfAppender.setRollingPolicy(rollingPolicy);
    rfAppender.start();

    // 해당 enum의 이름으로 Logger를 만들고 Appender를 연결해준다.
    Logger logbackLogger = loggerContext.getLogger(this.name());
    logbackLogger.setLevel(Level.INFO);
    logbackLogger.setAdditive(false);
    logbackLogger.addAppender(rfAppender);
  }
}

위와 같이 Enum 클래스 생성자에 Logback 설정을 하게되면 각 enum 마다 그 enum의 이름으로 Logger가 생성 된다. 그 후 아래와 같이 Logger를 사용하면 된다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SomeClass {

  private final Logger logger 
       = LoggerFactory.getLogger(ProgrammticLoggers.auth.name());

  public void someMethod() {
    ...
    logger.error("Failed to login.");
    ...
  }
}

logger를 사용하는 각 클래스에서 getLogger에 문자열을 매번 생성하지 않고 enum의 이름을 넘겨줌으로서 추 후 변경/관리에 용이하고 새로운 logger 설정을 추가할 때 Enum 클래스에 새로운 enum 한줄만 추가하면 된다. 만약 다른 Appender, Policy를 적용한 logger를 사용하고 싶을 때는 Enum 클래스에 맴버변수를 이용하여 각 enum의 속성을 설정하고 이를 바탕으로 프로그램적 설정방식의 이점을 이용하여 logger생성 로직이 있는 Enum클래스 생성자 안에서 동적으로 구성을 하면 될 것 이다.

[Docker] Container run 이야기.

Docker 테스트 중 경험했던 container run 과 관련된 내용을 정리 해 본다.

Container 실행하고 접속하고 나오고…

우선 docker 가 설치되어 있다는 가정하에 ubuntu 이미지를 내려 받아본다.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker pull ubuntu
ubuntu:latest: The image you are pulling has been verified
511136ea3c5a: Pull complete
d497ad3926c8: Pull complete
ccb62158e970: Pull complete
e791be0477f2: Pull complete
3680052c0f5c: Pull complete
22093c35d77b: Pull complete
5506de2b643b: Pull complete

그리고 ubuntu 이미지가 잘 받아졌는지 확인도 해 보자.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker images
REPOSITORY   TAG      IMAGE ID       CREATED      VIRTUAL SIZE
ubuntu       latest   5506de2b643b   7 days ago   197.8 MB

자, 이제 이미지를 받았으니 run 을 통해서 container 를 실행해 보자. 우선 -i 와 -t 옵션으로 생성된 container 와 대화형으로 입출력을 실행할 수 있다.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker run -i -t ubuntu
root@ff73f9aaee31:/#

이렇게 container 를 실행하면 그 container 에 접속한것을 볼 수 있는데 기본적인container 실행 명령어는 /bin/bash 쉘이다. -i 와 -t 옵션으로 대화형 명령 콘솔로 접근이 가능했던것이다. 이제 이 상태에서 다른 터미널 하나(2번 터미널)를 띄워 현재의 container 리스트를 확인 해 본다.

ubuntu@ip-172–31–21–107-#2:~$ sudo docker ps -a
CONTAINER ID IMAGE         COMMAND      CREATED        STATUS
ff73f9aaee31 ubuntu:latest “/bin/bash”  2 minutes ago  Up 2 seconds

container ID 는 ‘ff73f9aaee31’ 이며 ubuntu:latest 이미지로 container 를 생성하였으며 실행되고 있는 명령어는 ‘/bin/bash’, STATUS 부분에 보면 ‘Up’ 으로 현재 container 가 띄워져있다는 것을 확인할 수 있다.

그럼 container 에 접속하고 있는 1번 터미널에서 exit로 빠져 나와보자. 그리고 다시 2번 터미널에서 container 상태를 확인해 본다.

CONTAINER ID IMAGE         COMMAND     CREATED       STATUS
ff73f9aaee31 ubuntu:latest “/bin/bash” 2 minutes ago Exited(0)..ago

container 상태가 ‘Up’ 에서 ‘Exit’ 로 변하였다. 이는 container 에서 bash 쉘로 접속중인 상태에서 exit 명령어로 쉘을 종료 하였기 때문에 container 도 쉘의 정상적인 종료 메시지(exit 0)를 받고 자연스럽게 종료상태로 변하게 된것이다.

그럼 어떻게 다시 접속 할 수 있을까? 종료된 container 에 다시 쉘로 접속하기 위해서는 start 명령어로 container 를 다시 시작한 후 attach 명령어로 접속을 한다.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker start ff73f9aaee31
ff73f9aaee31
ubuntu@ip-172–31–21–107-#1:~$ sudo docker ps -a
CONTAINER ID IMAGE         COMMAND     CREATED        STATUS
ff73f9aaee31 ubuntu:latest “/bin/bash” 2 minutes ago  Up 2 seconds
ubuntu@ip-172–31–21–107-#1:~$ sudo docker attach ff73f9aaee31
root@ff73f9aaee31:/#

그럼 이제 2번 터미널에서 container 를 한 번 지워보자.

ubuntu@ip-172–31–21–107-#2:~$ sudo docker rm ff73f9aaee31
Error response from daemon: You cannot remove a running container. Stop the container before attempting removal or use -f
2014/10/31 09:12:02 Error: failed to remove one or more containers

안된단다. 왜냐하면 container 가 이미 실행중이거든. 그래도 지우고 싶으면 -f 옵션으로 지우라고 안내하고 있다. 하지만 우아하게 종료 후 삭제하도록 하겠다.

ubuntu@ip-172–31–21–107-#2:~$ sudo docker stop ff73f9aaee31
ff73f9aaee31
ubuntu@ip-172–31–21–107-#2:~$ sudo docker rm ff73f9aaee31
ff73f9aaee31
ubuntu@ip-172–31–21–107-#2:~$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

여기까지 docker image 를 가지고 container 를 실행하고, 접속하고, 삭제까지 해 보았다.

Apache 를 설치하고 실행해보자

그럼 기본적으로 container 를 생성하고 실행도 하고 접속도 해보았으니 apache 를 설치해서 웹서버를 하나 실행해보자. 다시 container를 하나 생성해야겠다. 이번에는 웹서버를 실행할 것이기 때문에 -p 옵션으로 port 를 지정을 해야한다.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker run -i -t -p 80:80 ubuntu
root@d448434e362d:/#

‘d448434e362d’ ID의 새로운 container 가 실행되었다. 앞서 얘기한것 처럼 apache 웹서버를 실행할것이기 때문에 ‘-p <host port>:<container port>’ 을 이용하여 host:80 으로 들어오는 request 를 container:80 으로 매핑시켜도록 설정한다. 이제 apache 를 설치해 보자.

root@d448434e362d:/# apt-get update
...blahblahblah...
root@d448434e362d:/# apt-get install -y apache2
...blahblahblah...
root@d448434e362d:/# service apache2 start
 * Starting web server apache2
root@d448434e362d:/# ps -ef | grep apache2
root 886 1 0 09:08 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 889 886 0 09:08 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 890 886 0 09:08 ? 00:00:00 /usr/sbin/apache2 -k start

container 안에 apache2 프로세스가 떠있는것을 확인하였고 실제 브라우저에서 웹서버가 실행 되었는지 확인해 보자.

정상적으로 서버는 실행 되었고, 그럼 2번 터미널로 container 상태를 확인해 보자.

ubuntu@ip-172–31–21–107-#2:~$ sudo docker ps -a
CONTAINER ID   IMAGE                COMMAND          CREATED 
d448434e362d   ubuntu:latest        “/bin/bash”      6 minutes ago 
STATUS         PORTS                NAMES
Up 6 minutes   0.0.0.0:80->80/tcp   dreamy_poincare

container 는 실행중이며 PORTS 에서 80(host) -> 80(container) 로 매핑하고 있다는 것을 확인할 수 있다. 그럼 정상적으로 apache 를 실행하였으니 접속한 container 에서 exit 나가 보자.

root@d448434e362d:/# exit
exit
ubuntu@ip-172–31–21–107-#1:~$

근데 이게 왠걸. 브라우저에서 다시 한번 확인하였더니 페이지가 안뜨는 것이다. container 상태를 다시 확인 해보니

ubuntu@ip-172–31–21–107-#1:~$ sudo docker ps -a
CONTAINER ID   IMAGE           COMMAND       CREATED 
d448434e362d   ubuntu:latest   “/bin/bash”   6 minutes ago 
STATUS                         PORTS         NAMES
Exited (0) 5 seconds ago                     dreamy_poincare

STATUS 가 ‘Exited’ 로 container 가 종료 되었다. 어떻게 된 것일까? 예상했을 수 도 있었겠지만 이전처럼 container 안에서 exit 로 빠져나오는 순간 bash 쉘은 종료가 되어 container 도 같이 종료가 되는 것이었다. 그럼 container 가 종료 되지 않고 접속해있던 container 에서 빠져 나오는 방법은 없을까?

그러기 위해서는 container 에 접속한 상태에서 ctrl +p & ctrl + q 를 누르면 container 의 종료 없이 container 에서 빠져 나올 수 있는 것을 확인할 수 있다.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker attach d448434e362d
root@d448434e362d:/#
root@d448434e362d:/# (press ctrl + p + q)
ubuntu@ip-172–31–21–107:~$

그 후 다시 container 의 상태를 확인 해 보면 여전히 실행중인것을 확인 할 수 있다.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED 
d448434e362d ubuntu:latest “/bin/bash” 6 minutes ago
STATUS PORTS NAMES
Up 12 seconds 0.0.0.0:80->80/tcp dreamy_poincare

실행을 하였지만…

그럼 docker 를 실제 서비스 환경에서 운영한다고 했을때 매번 ctrl + p, q 를 통해 접속한 container 에서 빠져나와야 하는 것일까? 만일 누군가 실수로 exit 를 해버린다면? 아마 바로 container 가 종료 되고 서비스도 종료 되어 장애로 이어질 수 있을 것이다. 그럼 이를 해결할 수 있는 방법은 없을까?

앞서 맨 처음 우리가 container 를 ‘sudo docker run -i -t ubuntu’ 로 실행하였는데, 이때 이미지명(ubuntu) 뒤에 container 실행 시 수행할 명령어를 명시할 수 있는데 생략하게 되면 기본적으로 ‘/bin/bash’ 가 실행된다. 따라서 container 실행 시 명령어로 /bin/bash 가 아닌 apache 실행 명령어를 명시해주면 되지 않을 까?

우선 apache 가 설치되어 있는 container 를 commit 을 통하여 이미지를 저장해보자.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker ps -a
CONTAINER ID   IMAGE           COMMAND       CREATED 
d448434e362d   ubuntu:latest   “/bin/bash”   2 days ago
STATUS                         PORTS         NAMES 
Exited (0) 2 minutes ago                     dreamy_poincare
ubuntu@ip-172–31–21–107-#1:~$ sudo docker commit d448434e362d ubuntu:apache
d7e4c2f8af99601388d747d30205b8fd0d0eb8e7985ddafc060bcd7047e1ef4f
ubuntu@ip-172–31–21–107-#1:~$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu apache d7e4c2f8af99 8 seconds ago 232.6 MB
ubuntu latest 5506de2b643b 10 days ago 197.8 MB

이제 apache 가 설치되어 있는 ubuntu 이미지로 apache 실행명령을 통한 container 를 실행해보자.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker run -p 80:80 ubuntu:apache /bin/bash -c ‘service apache2 start’
 * Starting web server apache2
ubuntu@ip-172–31–21–107:~$

오, 시작되었다고 메시지가 떴다. 브라우저로 확인해보자. 하지만 apache 페이지는 볼 수 없을 것이다. 왜냐하면 container 는 종료 상태이다. apache 를 실행 하였지만 실행 과 동시에 시작 명령어의 수행이 끝났기 때문에 쉘도 종료가 되고 자연스럽게 container 도 같이 종료가 되었다. apache 를 계속 동작하게 하려면 apache 를 background 가 아닌 foreground 로 실행 시켜 명령어가 종료되지 않고 계속 실행되게끔 해준다.

ubuntu@ip-172–31–21–107-#1:~$ sudo docker run -p 80:80 ubuntu:apache /bin/bash -c ‘/usr/sbin/apache2ctl -D FOREGROUND’
(prompt...)

브라우저에서 확인하니 정상적으로 페이지를 확인할 수 있다. 근데 문제가 생겼다. apache 는 실행되었는데 apache 를 실행한 터미널에서 빠져나갈수가 없다. exit 나 ctrl + pq 도 소용없었다. 우선 2번 터미널로 가서 해당 container 를 종료 시키자.

ubuntu@ip-172–31–21–107-#2:~$ sudo docker stop 7d9f5e334295
7d9f5e334295

1번 터미널도 정상적으로 돌아 왔다. 이대로는 정상적으로 사용할 수 없을 것 같다. 따라서 docker run 자체도 foreground 가 아닌 ‘-d’ 옵션으로 background 모드 실행을 해야 한다.

ubuntu@ip-172–31–21–107:~$ sudo docker run -d -p 80:80 ubuntu:apache /bin/bash -c ‘/usr/sbin/apache2ctl -D FOREGROUND’
a47aef40a2f0aa23b370f9a5668c9787a6664692e0e7c9aa506d74b18549d331
ubuntu@ip-172–31–21–107:~$ sudo docker ps -a
CONTAINER ID  IMAGE          COMMAND              CREATED 
a47aef40a2f0  ubuntu:apache  “/bin/bash -c ‘/usr/ 4 seconds ago
STATUS        PORTS                               NAMES 
Up 3 seconds  0.0.0.0:80->80/tcp                  distracted_darwin

이제 docker 도 apache 도 정상적으로 실행하였으니 container 에 접속하여 로그를 한번 살펴보아야 겠다

ubuntu@ip-172–31–21–107:~$ sudo docker attach a47aef40a2f0
(prompt…)

이런… 또 망했다. 아무것도 할 수 가 없다. attach 로 접속하여 이전처럼 /bin/bash 쉘이 실행될거라 기대하였지만 이 container 는 실행할 때 apache forground 실행 명령어로 했기 때문에 foreground 명령어 수행 상태로 접속하게 된 것이다. 그럼 또 어떻게 해야하는 것인가.

docker 1.3 부터 ‘exec’ 명령어가 추가 되어 host 에서 container 로 명령어를 수행할 수 있게되었다. 따라서 attach 대신 exec 에 대화형 모드로 container 안으로 접속 할 수 있다. 이 때 ‘/bin/bash’ 꼭 명시해 준다.

ubuntu@ip-172–31–21–107:~$ sudo docker exec -i -t a47aef40a2f0 /bin/bash
root@a47aef40a2f0:/#

그리고 exec 로 접속한 container 에서는 exit 로 빠져나와도 현재 run 상태인 container에는 아무런 영향을 주지 않는다.

root@a47aef40a2f0:/# exit
exit
ubuntu@ip-172–31–21–107:~$

정리하면 docker container 를 이용하여 apache 를 지속적으로 실행하고 서비스 하고 싶다면 apache 를 foreground 로 docker container 를 background 로 실행하고 exec 를 통하여 container 에 접속하면 된다.

container 에 service 를 자동적으로 또는 지속적으로 실행하는 방식에는 위의 방식 이외에 upstart, systemd, supervisor 등의 방식도 있는데 이는 다음 기회에 다루어 보기로 하겠다.

참고자료

[Docker] proxy 를 이용한 docker 설치 in ubuntu

간혹 사내망에서 docker 를 설치해야할 경우가 있는데 이 때에는 적절한 proxy 설정을 통해서 설치를 진행 하여야만 한다.
docker 홈페이지에서 아래와 같이 명령어 한줄로 설치하면 된다고 안내하고 있지만

curl -sSL https://get.docker.com/ubuntu/ | sudo sh

proxy 설정을 해 주어야 하기 때문에 위 스크립트 내용을 하나씩 수동으로 해줘야 한다.

 # Check that HTTPS transport is available to APT
 if [ ! -e /usr/lib/apt/methods/https ]; then
 apt-get update
 apt-get install -y apt-transport-https
 fi

 # Add the repository to your APT sources
 echo deb https://get.docker.com/ubuntu docker main > /etc/apt/sources.list.d/docker.list

 # Then import the repository key
 apt-key adv —keyserver hkp://keyserver.ubuntu.com:80 —recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9

 # Install docker
 apt-get update
 apt-get install -y lxc-docker

 #
 # Alternatively, just use the curl-able install.sh script provided at https://get.docker.com
 #

여기서 따로 proxy 설정을 해주어야 할 부분은 repository key 등록 부분과 apt 명령어 실행 부분이다.

우선 repository key 등록할 경우 proxy 설정 옵션을 추가해 준다.

sudo apt-key adv —keyserver-options http-proxy=http://proxy.server.com:3333 —keyserver hkp://keyserver.ubuntu.com:80 —recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9

그리고 나서 apt 명령어에 proxy 설정을 추가하기 위해서는 /etc/apt/apt.conf 파일에 아래와 같이 추가해 준다.

 Acquire::http::proxy "http://proxy.server.com:3333";
 Acquire::https::proxy "http://proxy.server.com:3333";

끝.